class Diakonos::Diakonos

Attributes

buffer_current[R]
buffers[R]
clipboard[R]
close_token_regexps[R]
closers[R]
column_markers[R]
diakonos_conf[R]
diakonos_home[R]
display_mutex[R]
do_display[RW]
functions_last[R]
hooks[R]
indenters[R]
indenters_next_line[R]
list_buffer[R]
list_filename[R]
readline[R]
script_dir[R]
session[R]
settings[R]
surround_pairs[R]
testing[R]
there_was_non_movement[R]
token_formats[R]
token_regexps[R]
unindenters[R]
win_line_numbers[R]
win_main[R]

Public Class Methods

new( argv = [] ) click to toggle source
# File lib/diakonos.rb, line 156
def initialize( argv = [] )
  @diakonos_home = File.expand_path( ( ENV[ 'HOME' ] || '' ) + '/.diakonos' )
  mkdir @diakonos_home
  @script_dir = "#{@diakonos_home}/scripts"
  mkdir @script_dir
  @extensions = ExtensionSet.new( File.join( @diakonos_home, 'extensions' ) )
  initialize_session

  @files = Array.new
  @read_only_files = Array.new
  @config_filename = nil
  parse_options argv

  # These requires are down here instead of up with the others so that
  # uninstall can be done without needing the curses lib (which may not
  # be installed).
  require 'diakonos/display'
  require 'diakonos/display/format'

  init_help

  @debug          = File.new( File.join( @diakonos_home, 'debug.log' ), 'w' )
  @list_filename  = File.join( @diakonos_home, 'listing.txt' )
  @diff_filename  = File.join( @diakonos_home, 'text.diff' )
  @help_filename  = File.join( @help_dir, 'about-help.dhf' )
  @error_filename = File.join( @diakonos_home, 'diakonos.err' )
  @about_filename = File.join( @diakonos_home, 'about.dhf' )

  @win_main         = nil
  @win_context      = nil
  @win_status       = nil
  @win_interaction  = nil
  @win_line_numbers = nil
  @buffers          = Array.new

  load_configuration

  @quitting         = false

  @x = 0
  @y = 0

  @buffer_stack           = Array.new
  @buffer_current         = nil

  @cursor_stack           = Array.new
  @cursor_stack_pointer   = nil

  @bookmarks              = Hash.new

  @macro_history          = nil
  @macro_input_history    = nil
  @macros                 = Hash.new
  @functions_last         = SizedArray.new(2)
  @playing_macro          = false

  @display_mutex          = Mutex.new
  @display_queue_mutex    = Mutex.new
  @display_queue          = nil
  @do_display             = true
  @context_line_mutex     = Mutex.new

  @tag_stack              = Array.new
  @last_search_regexps    = nil
  @there_was_non_movement = false
  @status_vars            = Hash.new

  # Readline histories
  @rlh_general  = Array.new
  @rlh_files    = Array.new
  @rlh_search   = Array.new
  @rlh_shell    = Array.new
  @rlh_help     = Array.new
  @rlh_sessions = Array.new

  @hooks = {
    :after_buffer_switch => [],
    :after_open          => [],
    :after_save          => [],
    :after_startup       => [],
  }
end

Public Instance Methods

about_write() click to toggle source
# File lib/diakonos/about.rb, line 4
    def about_write
      File.open( @about_filename, "w" ) do |f|
        inst = ::Diakonos::INSTALL_SETTINGS

        configs = @configs.map(&:to_s).join("\n")

        ext_loaded = @extensions.loaded_extensions.sort_by { |e|
          e.name.downcase
        }.map { |e|
          %{
### #{e.name} #{e.version}
#{e.description}
          }.strip
        }.join( "\n\n" )

        ext_not_loaded = @extensions.not_loaded_extensions.sort.map { |e|
          "### #{e} (NOT LOADED)"
        }.join( "\n" )

        installation_artifact = File.join(inst[:lib_dir], 'diakonos', 'installation.rb')
        if File.exist?(installation_artifact)
          install_time = File.mtime(installation_artifact)
        else
          install_time = "--"
        end

        f.puts %{
# About Diakonos

Licence:        MIT Licence
Copyright:      Copyright (c) 2004-#{ Time.now.year } Pistos

## Version

Version:        #{ ::Diakonos::VERSION }
Code Date:      #{ ::Diakonos::LAST_MODIFIED }
Install Time:   #{ install_time }
Ruby Version:   #{ ::RUBY_VERSION }

## Paths

Home dir:       #{ @diakonos_home }

### Installation

Prefix:             #{ inst[ :prefix ] }
Executable dir:     #{ inst[ :bin_dir ] }
Help dir:           #{ inst[ :help_dir ] }
System config dir:  #{ inst[ :conf_dir ] }
System library dir: #{ inst[ :lib_dir ] }

### Configuration Files

#{ configs }

## Extensions

#{ ext_loaded }

#{ ext_not_loaded }
        }.strip
      end
    end
actually_grep( regexp_source, *buffers ) click to toggle source
# File lib/diakonos/grep.rb, line 35
def actually_grep( regexp_source, *buffers )
  begin
    regexp = Regexp.new( regexp_source, Regexp::IGNORECASE )
    grep_results = buffers.map { |buffer| buffer.grep(regexp) }.flatten
    if settings[ 'grep.context' ] == 0
      join_str = "\n"
    else
      join_str = "\n---\n"
    end
    with_list_file do |list|
      list.puts grep_results.join( join_str )
    end
    list_buffer = open_list_buffer
    list_buffer.highlight_matches regexp
    display_buffer list_buffer
  rescue RegexpError
    # Do nothing
  end
end
buffer_number_to_name( buffer_number ) click to toggle source

@param [Integer] buffer_number should be 1-based, not zero-based. @return nil if no such buffer exists.

# File lib/diakonos/buffer-management.rb, line 31
def buffer_number_to_name( buffer_number )
  return nil  if buffer_number < 1

  b = @buffers[ buffer_number - 1 ]
  if b
    b.name
  end
end
buffer_to_number( buffer ) click to toggle source

@return [Integer] 1-based, not zero-based. @return nil if no such buffer exists.

# File lib/diakonos/buffer-management.rb, line 42
def buffer_to_number( buffer )
  i = @buffers.index( buffer )
  if i
    i + 1
  end
end
capture_keychain( c, context ) click to toggle source
# File lib/diakonos/keying.rb, line 188
def capture_keychain( c, context )
  if c == ENTER
    @capturing_keychain = false
    buffer_current.delete_selection
    str = keychain_str_for( context )
    buffer_current.insert_string str
    cursor_right( Buffer::STILL_TYPING, str.length )
  else
    keychain_pressed = context.concat [ c ]

    function_and_args = @modes[ 'edit' ].keymap.get_leaf( keychain_pressed )

    if function_and_args
      function, args = function_and_args
    end

    partial_keychain = @modes[ 'edit' ].keymap.get_node( keychain_pressed )
    if partial_keychain
      set_iline( "Part of existing keychain: " + keychain_str_for( keychain_pressed ) + "..." )
    else
      set_iline keychain_str_for( keychain_pressed ) + "..."
    end
    process_keystroke keychain_pressed
  end
end
capture_mapping( c, context ) click to toggle source
# File lib/diakonos/keying.rb, line 214
def capture_mapping( c, context )
  if c == ENTER
    @capturing_mapping = false
    buffer_current.delete_selection
    set_iline
  else
    keychain_pressed = context.concat [ c ]

    function_and_args = @modes[ 'edit' ].keymap.get_leaf( keychain_pressed )

    if function_and_args
      function, args = function_and_args
      set_iline "#{keychain_str_for( keychain_pressed )}  ->  #{function}( #{args} )"
    else
      partial_keychain = @modes[ 'edit' ].keymap.get_node( keychain_pressed )
      if partial_keychain
        set_iline( "Several mappings start with: " + keychain_str_for( keychain_pressed ) + "..." )
        process_keystroke keychain_pressed
      else
        set_iline "There is no mapping for " + keychain_str_for( keychain_pressed )
      end
    end
  end
end
cleanup_display() click to toggle source
# File lib/diakonos/display.rb, line 10
def cleanup_display
  return  if @testing

  @win_main.close          if @win_main
  @win_status.close        if @win_status
  @win_interaction.close   if @win_interaction
  @win_context.close       if @win_context
  @win_line_numbers.close  if @win_line_numbers

  Curses::close_screen
end
cleanup_session() click to toggle source
# File lib/diakonos/sessions.rb, line 251
def cleanup_session
  if @session && Session.pid_session?(@session.filename) && File.exists?(@session.filename)
    File.delete @session.filename
  end
end
clear_non_movement_flag() click to toggle source
# File lib/diakonos.rb, line 314
def clear_non_movement_flag
  @there_was_non_movement = false
end
close_help_buffer() click to toggle source
# File lib/diakonos/help.rb, line 11
def close_help_buffer
  close_buffer @help_buffer
  @help_buffer = nil
end
close_list_buffer( opts = {} ) click to toggle source
# File lib/diakonos/list.rb, line 10
def close_list_buffer( opts = {} )
  close_buffer  @list_buffer, opts
  @list_buffer = nil
end
create_buffers_from_files() click to toggle source

# File lib/diakonos.rb, line 320
def create_buffers_from_files
  @files.each do |file_info|
    @buffers << Buffer.new(file_info)
  end
end
current_list_item() click to toggle source
# File lib/diakonos/list.rb, line 23
def current_list_item
  if @list_buffer
    @list_buffer.set_selection_current_line
  end
end
cursor_stack_remove_buffer( buffer ) click to toggle source
# File lib/diakonos/cursor.rb, line 4
def cursor_stack_remove_buffer( buffer )
  @cursor_stack.delete_if { |frame|
    frame[ :buffer ] == buffer
  }
end
debug_log( string ) click to toggle source
# File lib/diakonos/logging.rb, line 10
def debug_log( string )
  @debug.puts( Time.now.strftime( "[%a %H:%M:%S] #{string}" ) )
  @debug.flush
end
decrease_grep_context() click to toggle source
# File lib/diakonos/grep.rb, line 89
def decrease_grep_context
  current = settings['grep.context']
  if current > 0
    @session.settings['grep.context'] = current - 1
    merge_session_settings
  end
end
display_buffer( buffer ) click to toggle source
# File lib/diakonos/display.rb, line 285
def display_buffer( buffer )
  return  if @testing
  return  if ! @do_display

  Thread.new do

    if ! @display_mutex.try_lock
      @display_queue_mutex.synchronize do
        @display_queue = buffer
      end
    else
      begin
        Curses::curs_set 0
        buffer.display
        Curses::curs_set 1
      rescue Exception => e
        $diakonos.log( "Display Exception:" )
        $diakonos.log( e.message )
        $diakonos.log( e.backtrace.join( "\n" ) )
        show_exception e
      end

      @display_mutex.unlock

      @display_queue_mutex.synchronize do
        if @display_queue
          b = @display_queue
          @display_queue = nil
          display_buffer b
        end
      end
    end

  end

end
escape_quotes( str ) click to toggle source
# File lib/diakonos.rb, line 570
def escape_quotes( str )
  temp = ''
  str.each_byte do |b|
    if b == 39
      temp << 39
      temp << 92
      temp << 39
    end
    temp << b
  end
  temp
end
find_( options = {} ) click to toggle source

@return [Integer] the number of replacements made

# File lib/diakonos/search.rb, line 5
def find_( options = {} )
  regexp_source, replacement = options.values_at( :regexp_source, :replacement )
  return  if regexp_source.nil? || regexp_source.empty?

  rs_array = regexp_source.newline_split
  regexps = Array.new
  exception_thrown = nil

  rs_array.each do |source|
    begin
      warning_verbosity = $VERBOSE
      $VERBOSE = nil
      regexps << Regexp.new(
        source,
        options[:case_sensitive] ? nil : Regexp::IGNORECASE
      )
      $VERBOSE = warning_verbosity
    rescue RegexpError => e
      if ! exception_thrown
        exception_thrown = e
        source = Regexp.escape( source )
        retry
      else
        raise e
      end
    end
  end

  if replacement == ASK_REPLACEMENT
    replacement = get_user_input( "Replace with: ", history: @rlh_search )
  end

  if exception_thrown && ! options[:quiet]
    set_iline "Searching literally; #{exception_thrown.message}"
  end

  # The execution order of the #find and the @last_search_regexps assignment is likely deliberate
  num_replacements = buffer_current.find(
    regexps,
    :direction          => options[:direction],
    :replacement        => replacement,
    :starting_row       => options[:starting_row],
    :starting_col       => options[:starting_col],
    :quiet              => options[:quiet],
    :show_context_after => @settings[ 'find.show_context_after' ],
    :starting           => true
  )
  @last_search_regexps = regexps

  num_replacements
end
get_choice(*args) click to toggle source
# File lib/diakonos/interaction.rb, line 48
def get_choice(*args)
  @interaction_handler.get_choice *args
end
get_language_from_name( name ) click to toggle source
# File lib/diakonos.rb, line 484
def get_language_from_name( name )
  @filemasks.each do |language,filemask|
    if name =~ filemask
      return language
    end
  end
  nil
end
get_language_from_shabang( first_line ) click to toggle source
# File lib/diakonos.rb, line 493
def get_language_from_shabang( first_line )
  @bangmasks.each do |language,bangmask|
    if first_line =~ bangmask
      return language
    end
  end
  nil
end
get_token_regexp( hash, arg, match ) click to toggle source
# File lib/diakonos/config.rb, line 120
def get_token_regexp( hash, arg, match )
  language = match[ 1 ]
  token_class = match[ 2 ]
  case_insensitive = ( match[ 3 ] != nil )
  hash[ language ] = ( hash[ language ] || Hash.new )
  if case_insensitive
    hash[ language ][ token_class ] = Regexp.new( arg, Regexp::IGNORECASE )
  else
    hash[ language ][ token_class ] = Regexp.new arg
  end
end
get_user_input( prompt, options = {}, &block ) click to toggle source

completion_array is the array of strings that tab completion can use @param options :initial_text, :completion_array, :history, :do_complete, :on_dirs

# File lib/diakonos/interaction.rb, line 9
def get_user_input( prompt, options = {}, &block )
  if @playing_macro
    return @macro_input_history.shift
  end

  options[ :history ] ||= @rlh_general
  options[ :initial_text ] ||= ""
  options[ :do_complete ] ||= DONT_COMPLETE
  options[ :on_dirs ] ||= :go_into_dirs
  will_display_after_select = options.fetch( :will_display_after_select, false )

  cursor_pos = set_iline( prompt )
  @readline = Readline.new(
    list_manager: self,
    keystroke_processor: self,
    testing: @testing,
    window: @win_interaction,
    start_pos: cursor_pos,
    options: options,
    &block
  )

  retval = @readline.get_input
  if will_display_after_select
    close_list_buffer  do_display: ! retval
  else
    close_list_buffer
  end
  options[ :history ][ -1 ] = @readline.input
  @readline = nil

  if @macro_history
    @macro_input_history.push retval
  end
  set_iline

  retval
end
grep_( regexp_source, *buffers ) click to toggle source
# File lib/diakonos/grep.rb, line 55
def grep_( regexp_source, *buffers )
  original_buffer = buffer_current
  if buffer_current.changing_selection
    selected_text = buffer_current.copy_selection[ 0 ]
  end
  starting_row, starting_col = buffer_current.last_row, buffer_current.last_col

  selected = get_user_input(
    "Grep regexp: ",
    history: @rlh_search,
    initial_text: regexp_source || selected_text || "",
    will_display_after_select: true
  ) { |input|
    next  if input.length < 2
    actually_grep input, *buffers
  }

  if selected
    spl = selected.split( "| " )
    if spl.size > 1
      open_file spl[-1]
    else
      original_buffer.cursor_to starting_row, starting_col
    end
  else
    original_buffer.cursor_to starting_row, starting_col
  end
end
handle_gui_paste(mode) click to toggle source

Handle paste from a GUI (like x.org). i.e. Shift-Insert

# File lib/diakonos/keying.rb, line 283
def handle_gui_paste(mode)
  s = ""
  ch = nil

  loop do
    ch = nil
    begin
      Timeout::timeout(0.02) do
        ch = @modes[mode].window.getch
      end
    rescue Timeout::Error => e
      break
    end
    break  if ch.nil?

    c = ch.ord
    utf_8_char = self.utf_8_bytes_to_char(c, mode)

    if utf_8_char
      s << utf_8_char
    elsif self.typeable?(c)
      s << c
    elsif c == ENTER && mode == 'edit'
      s << "\n"
    else
      break
    end
  end

  if ! s.empty?
    case mode
    when 'edit'
      buffer_current.paste s, Buffer::TYPING
    when 'input'
      @readline.paste s
    end
  end

  if ch
    process_keystroke( [], mode, ch )
  end
end
handle_mouse_event() click to toggle source
# File lib/diakonos/mouse.rb, line 3
def handle_mouse_event
  event = Curses::getmouse
  return  if event.nil?

  if event.bstate & Curses::BUTTON1_CLICKED > 0
    buffer_current.cursor_to(
      buffer_current.top_line + event.y,
      buffer_current.left_column + event.x,
      Buffer::DO_DISPLAY
    )
  else
    $diakonos.debug_log "button state = #{'0x%x' % event.bstate}, "
  end
end
handle_stale_session_files() click to toggle source

We have to do this separately and later (as opposed to inside session_startup) because we have to wait for the display to get initialized in order to prompt the user for input, etc.

# File lib/diakonos/sessions.rb, line 213
def handle_stale_session_files
  return  if @testing
  return  if @stale_session_files.empty?

  session_buffers = []
  @stale_session_files.each_with_index do |session_file,index|
    session_buffers << open_file( session_file )

    choice = get_choice(
      "#{@stale_session_files.size} unclosed session(s) found.  Open the above files?  (session #{index+1} of #{@stale_session_files.size})",
      [ CHOICE_YES, CHOICE_NO, CHOICE_DELETE ],
      index > 0 ?  CHOICE_NO : nil
    )

    case choice
    when CHOICE_YES
      load_session session_file
      if @session
        File.delete session_file
        break
      end
    when CHOICE_DELETE
      File.delete session_file
    end
  end

  if session_buffers.empty? && @files.empty? && @settings[ 'session.default_session' ]
    session_file = session_filepath_for( @settings[ 'session.default_session' ] )
    if File.exist? session_file
      load_session session_file
    end
  end

  session_buffers.each do |buffer|
    close_buffer buffer
  end
end
handle_utf_8(c, mode) click to toggle source

@param [Integer] c The ordinal (number) of a character @param [String] mode @return [Boolean] true iff c began a UTF-8 byte sequence

# File lib/diakonos/keying.rb, line 274
def handle_utf_8(c, mode)
  utf_8_char = utf_8_bytes_to_char(c, mode)
  if utf_8_char
    self.type_character utf_8_char, mode
    true
  end
end
increase_grep_context() click to toggle source
# File lib/diakonos/grep.rb, line 84
def increase_grep_context
  current = settings['grep.context']
  @session.settings['grep.context'] = current + 1
  merge_session_settings
end
init_help() click to toggle source
# File lib/diakonos/help.rb, line 3
def init_help
  @help_dir = INSTALL_SETTINGS[ :help_dir ]
  @help_tags = `grep -h Tags #{@help_dir}/* | cut -d ' ' -f 2-`.split.uniq
end
initialize_display() click to toggle source
# File lib/diakonos/display.rb, line 22
def initialize_display
  if ! @testing
    cleanup_display

    Curses::init_screen
    Curses::nonl
    Curses::raw
    Curses::noecho
    if @settings['mouse']
      Curses::mousemask(Curses::ALL_MOUSE_EVENTS)
    end

    if Curses::has_colors?
      Curses::start_color
      Curses::use_default_colors

      # -1 means use the terminal's current/default background, which may even have some transparency
      background_colour = settings['colour.background'] || -1
      Curses::init_pair( Curses::COLOR_BLACK, Curses::COLOR_BLACK, background_colour )
      Curses::init_pair( Curses::COLOR_RED, Curses::COLOR_RED, background_colour )
      Curses::init_pair( Curses::COLOR_GREEN, Curses::COLOR_GREEN, background_colour )
      Curses::init_pair( Curses::COLOR_YELLOW, Curses::COLOR_YELLOW, background_colour )
      Curses::init_pair( Curses::COLOR_BLUE, Curses::COLOR_BLUE, background_colour )
      Curses::init_pair( Curses::COLOR_MAGENTA, Curses::COLOR_MAGENTA, background_colour )
      Curses::init_pair( Curses::COLOR_CYAN, Curses::COLOR_CYAN, background_colour )
      Curses::init_pair( Curses::COLOR_WHITE, Curses::COLOR_WHITE, background_colour )
      @colour_pairs.each do |cp|
        Curses::init_pair( cp[ :number ], cp[ :fg ], cp[ :bg ] )
      end
    end
  end

  if settings[ 'view.line_numbers' ]
    @win_line_numbers = ::Diakonos::Window.new( main_window_height, settings[ 'view.line_numbers.width' ], 0, 0 )
    @win_main = ::Diakonos::Window.new( main_window_height, Curses::cols - settings[ 'view.line_numbers.width' ], 0, settings[ 'view.line_numbers.width' ] )
  else
    @win_main = ::Diakonos::Window.new( main_window_height, Curses::cols, 0, 0 )
    @win_line_numbers = nil
  end
  @win_status = ::Diakonos::Window.new( 1, Curses::cols, Curses::lines - 2, 0 )
  @win_status.attrset @settings[ 'status.format' ]
  @win_interaction = ::Diakonos::Window.new( 1, Curses::cols, Curses::lines - 1, 0 )

  @interaction_handler = InteractionHandler.new(
    win_main: @win_main,
    win_interaction: @win_interaction,
    cursor_manager: self,
    testing: @testing,
    choice_delay: @settings['interaction.choice_delay'],
    blink_string: @settings['interaction.blink_string'],
    blink_duration: @settings['interaction.blink_duration']
  )

  if @settings['context.visible']
    if @settings['context.combined']
      pos = 1
    else
      pos = 3
    end
    @win_context = ::Diakonos::Window.new( 1, Curses::cols, Curses::lines - pos, 0 )
  else
    @win_context = nil
  end

  if ! @testing
    @win_main.keypad( true )
    @win_status.keypad( true )
    @win_interaction.keypad( true )
    if @win_line_numbers
      @win_line_numbers.keypad( true )
    end
    if @win_context
      @win_context.keypad( true )
    end
  end

  @modes[ 'edit' ].window = @win_main
  @modes[ 'input' ].window = @win_interaction

  @win_interaction.refresh
  @win_main.refresh
  if @win_line_numbers
    @win_line_numbers.refresh
  end

  if @buffers
    @buffers.each do |buffer|
      buffer.reset_display
    end
  end
end
initialize_session() click to toggle source
# File lib/diakonos/sessions.rb, line 138
def initialize_session
  @session_dir = "#{@diakonos_home}/sessions"
  mkdir @session_dir
  @session = Session.new("#{@session_dir}/#{Process.pid}")
end
keychain_str_for( array ) click to toggle source
# File lib/diakonos/keying.rb, line 175
def keychain_str_for( array )
  chain_str = ""
  array.each do |key|
    key_str = Keying.key_string_for( key )
    if key_str
      chain_str << key_str + " "
    else
      chain_str << key.to_s + " "
    end
  end
  chain_str.strip
end
legitimize_config_filename!(prospective_filename, including_config_file) click to toggle source

@return [ConfigFile, ConfigFileUnreadable]

# File lib/diakonos/config.rb, line 163
def legitimize_config_filename!(prospective_filename, including_config_file)
  if File.exists?(prospective_filename)
    ConfigFile.new(
      File.realpath(prospective_filename),
      including_config_file
    )
  else
    ConfigFileUnreadable.new(prospective_filename, including_config_file)
  end
end
list_item_selected?() click to toggle source
# File lib/diakonos/list.rb, line 19
def list_item_selected?
  @list_buffer && @list_buffer.selecting?
end
loadConfiguration()
Alias for: load_configuration
load_configuration() click to toggle source
# File lib/diakonos/config.rb, line 16
def load_configuration
  # Set defaults first

  conf_dir = INSTALL_SETTINGS[ :conf_dir ]
  @global_diakonos_conf = "#{conf_dir}/diakonos.conf"
  if ! @testing
    @diakonos_conf = @config_filename || "#{@diakonos_home}/diakonos.conf"

    if ! FileTest.exists?( @diakonos_conf )
      if FileTest.exists?( @global_diakonos_conf )
        puts "No personal configuration file found."
        puts "Would you like to copy the system-wide configuration file (#{@global_diakonos_conf}) to use"
        $stdout.print "as a basis for your personal configuration (recommended)? (y/n)"; $stdout.flush
        answer = $stdin.gets
        if answer =~ /^y/i
          FileUtils.cp @global_diakonos_conf, @diakonos_conf
        end
      else
        if @testing
          File.open( @diakonos_conf, 'w' ) do |f|
            f.puts File.read( './diakonos.conf' )
          end
        else
          puts "diakonos.conf not found in any of:"
          puts "  #{conf_dir}"
          puts "  #{@diakonos_home}"
          puts "At least one configuration file must exist."
          puts "You download one from https://git.sr.ht/~pistos/diakonos/blob/master/diakonos.conf"
        end

        if ! FileTest.exists?( @diakonos_conf )
          puts "Terminating due to lack of configuration file."
          exit 1
        end
      end
    end
  end

  @logfilename         = @diakonos_home + "/diakonos.log"
  @modes = {
    'edit'  => Mode.new,
    'input' => Mode.new,
  }
  @token_regexps       = Hash.new { |h,k| h[ k ] = Hash.new }
  @close_token_regexps = Hash.new { |h,k| h[ k ] = Hash.new }
  @token_formats       = Hash.new { |h,k| h[ k ] = Hash.new }
  @column_markers      = Hash.new { |h,k| h[ k ] = Hash.new }
  @indenters           = Hash.new
  @indenters_next_line = Hash.new
  @unindenters         = Hash.new
  @indent_triggers     = Hash.new
  @filemasks           = Hash.new
  @bangmasks           = Hash.new
  @closers             = Hash.new
  @surround_pairs      = Hash.new { |h,k| h[ k ] = Hash.new}
  @fuzzy_ignores       = Array.new

  @settings = Hash.new
  @setting_strings = Hash.new
  # Setup some defaults
  @settings[ "context.format" ] = Curses::A_REVERSE
  @settings['fuzzy_file_find.recursive'] = true

  @modes[ 'edit' ].keymap[ Curses::KEY_RESIZE ] = [ "redraw", nil ]
  @modes[ 'edit' ].keymap[ RESIZE2 ] = [ "redraw", nil ]

  @colour_pairs = Array.new

  @configs = Set.new
  parse_configuration_file @global_diakonos_conf
  parse_configuration_file @diakonos_conf  if @diakonos_conf

  languages = @surround_pairs.keys | @token_regexps.keys | @close_token_regexps.keys | @token_formats.keys

  languages.each do |language|
    @surround_pairs[ language ] = @surround_pairs[ 'all' ].merge( @surround_pairs[ language ] )
    @token_regexps[ language ] = @token_regexps[ 'all' ].merge( @token_regexps[ language ] )
    @close_token_regexps[ language ] = @close_token_regexps[ 'all' ].merge( @close_token_regexps[ language ] )
    @token_formats[ language ] = @token_formats[ 'all' ].merge( @token_formats[ language ] )
  end

  merge_session_settings

  case @settings[ 'clipboard.external' ]
  when 'klipper', 'klipper-dcop'
    @clipboard = ClipboardKlipper.new
  when 'klipper-dbus'
    @clipboard = ClipboardKlipperDBus.new
  when 'xclip'
    @clipboard = ClipboardXClip.new
  when 'osx'
    @clipboard = ClipboardOSX.new
  else
    @clipboard = Clipboard.new( @settings[ "max_clips" ] )
  end
  @log = File.open( @logfilename, "a" )

  if @buffers
    @buffers.each do |buffer|
      buffer.configure
    end
  end
end
Also aliased as: loadConfiguration
load_session( session_file ) click to toggle source
# File lib/diakonos/sessions.rb, line 144
def load_session( session_file )
  cleanup_session
  @session = Session.from_yaml_file(session_file)
  if @session
    @files.concat @session.buffers
    rlh = @session.readline_histories
    if rlh
      @rlh_general  = rlh['general'] || @rlh_general
      @rlh_files    = rlh['files'] || @rlh_files
      @rlh_search   = rlh['search'] || @rlh_search
      @rlh_shell    = rlh['shell'] || @rlh_shell
      @rlh_help     = rlh['help'] || @rlh_help
      @rlh_sessions = rlh['sessions'] || @rlh_sessions
    end
    merge_session_settings
  end
end
load_tags() click to toggle source
# File lib/diakonos.rb, line 544
def load_tags
  @tags = Hash.new
  if buffer_current && buffer_current.name
    path = File.expand_path( File.dirname( buffer_current.name ) )
    tagfile = path + "/tags"
  else
    tagfile = "./tags"
  end

  if ! FileTest.exists? tagfile
    set_iline "(tags file not found)"
  else
    IO.foreach( tagfile ) do |line_|
      line = line_.chomp
      # <tagname>\t<filepath>\t<line number or regexp>\t<kind of tag>
      tag, file, command, kind, rest = line.split( /\t/ )
      command.gsub!( /;"$/, "" )
      if command =~ /^\/.*\/$/
        command = command[ 1...-1 ]
      end
      @tags[ tag ] ||= Array.new
      @tags[ tag ].push CTag.new( file, command, kind, rest )
    end
  end
end
log( string ) click to toggle source
# File lib/diakonos/logging.rb, line 5
def log( string )
  @log.puts string
  @log.flush
end
log_backtrace() click to toggle source
# File lib/diakonos/logging.rb, line 15
def log_backtrace
  begin
    raise Exception
  rescue Exception => e
    e.backtrace[ 1..-1 ].each do |x|
      debug_log x
    end
  end
end
main_window_height() click to toggle source
# File lib/diakonos/display.rb, line 122
def main_window_height
  # One line for the status line
  # One line for the input line
  # One line for the context line
  retval = Curses::lines - 2
  if @settings['context.visible'] && ! @settings['context.combined']
    retval = retval - 1
  end
  retval
end
main_window_width() click to toggle source
# File lib/diakonos/display.rb, line 133
def main_window_width
  Curses::cols
end
map_key( arg, keymap = @modes['edit'].keymap ) click to toggle source
# File lib/diakonos/config.rb, line 132
def map_key( arg, keymap = @modes['edit'].keymap )
  return  if arg.nil?

  if /  / === arg
    keystrings, function_and_args = arg.split( / {2,}/, 2 )
  else
    keystrings, function_and_args = arg.split( /;/, 2 )
  end

  keystrokes = Array.new
  keystrings.split( /\s+/ ).each do |ks_str|
    codes = Keying.keycodes_for( ks_str )
    if codes.empty?
      puts "Unknown keystring: #{ks_str}"
    else
      keystrokes.concat codes
    end
  end

  if function_and_args.nil?
    keymap.delete_key_path( keystrokes )
  else
    function, function_args = function_and_args.split( /\s+/, 2 )
    keymap.set_key_path(
      keystrokes,
      [ function, function_args ]
    )
  end
end
matching_help_documents( str ) click to toggle source
# File lib/diakonos/help.rb, line 16
def matching_help_documents( str )
  docs = []

  if str =~ %r{^/(.+)$}
    regexp = $1
    files = Dir[ "#{@help_dir}/*" ].select{ |f|
      File.open( f ) { |io| io.grep( /#{regexp}/i ) }.any?
    }
  else
    terms = str.gsub( /[^a-zA-Z0-9-]/, ' ' ).split.join( '|' )
    file_grep = `egrep -i -l '^Tags.*\\b(#{terms})\\b' #{@help_dir}/*`
    files = file_grep.split( /\s+/ )
  end

  files.each do |file|
    File.open( file ) do |f|
      docs << ( "%-300s | %s" % [ f.gets.strip, file ] )
    end
  end

  docs.sort { |a,b| a.gsub( /^# (?:an?|the) */i, '# ' ) <=> b.gsub( /^# (?:an?|the) */i, '# ' ) }
end
mkdir( dir ) click to toggle source
# File lib/diakonos.rb, line 239
def mkdir( dir )
  if ! FileTest.exists?( dir )
    Dir.mkdir dir
  end
end
next_list_item() click to toggle source
# File lib/diakonos/list.rb, line 44
def next_list_item
  if @list_buffer
    cursor_down
    @list_buffer[ @list_buffer.current_row ]
  end
end
open_help_buffer() click to toggle source
# File lib/diakonos/help.rb, line 8
def open_help_buffer
  @help_buffer = open_file( @help_filename )
end
open_help_document( selected_string ) click to toggle source
# File lib/diakonos/help.rb, line 39
def open_help_document( selected_string )
  help_file = selected_string.split( "| " )[ -1 ]
  if File.exist? help_file
    open_file help_file
  end
end
open_list_buffer() click to toggle source
# File lib/diakonos/list.rb, line 6
def open_list_buffer
  @list_buffer = open_file( @list_filename )
end
parse_configuration_file( filename, including_config_file = ConfigFileNull.new ) click to toggle source

@param [String] filename the config file to parse @param [ConfigFile] including_config_file the config file which calls include on this one

# File lib/diakonos/config.rb, line 176
def parse_configuration_file( filename, including_config_file = ConfigFileNull.new )
  config_file = self.legitimize_config_filename!(filename, including_config_file)

  @configs << config_file

  config_file.each_line_with_index do |line, line_number|
    if line =~ /^\s*(\S+)\s*=\s*(\S+)\s*$/
      # Inheritance
      command, arg = $1, @setting_strings[ $2 ]
    end

    if arg.nil?
      command, arg = line.split( /\s+/, 2 )
      next  if command.nil?
      if arg.nil?
        config_file.problems << "error on line #{line_number+1}"
        next
      end
    end

    command = command.downcase

    @setting_strings[ command ] = arg

    case command
    when "include"
      self.parse_configuration_file( File.expand_path( arg ), config_file )
    when 'load_extension'
      @extensions.load( arg ).each do |conf_file|
        parse_configuration_file conf_file
      end
    when /^lang\.(.+?)\.surround\.pair$/
      language = $1

      args = arg.split( /"\s+"/ )
      args.map! do |s|
        s.gsub( /(?<!\\)"/, '' ).gsub( /\\"/, '"' )
      end

      pair_key = args.shift

      if pair_key =~ /^\/.+\/$/
        pair_key = Regexp.new( pair_key[ 1..-2 ] )
      else
        pair_key = Regexp.new( "^#{Regexp.escape(pair_key)}$" )
      end

      pair_parens = args
      @surround_pairs[ language ][ pair_key ] = pair_parens
    when 'key'
      map_key arg
    when 'mkey'
      mode, arg_ = arg.split( /\s+/, 2 )
      map_key arg_, @modes[mode].keymap
    when 'key.after'
      function, args = arg.split( /\s+/, 2 )
      map_key args, @modes['edit'].keymap_after[function]
    when /^lang\.(.+?)\.tokens\.([^.]+)(\.case_insensitive)?$/, /^lang\.(.+?)\.tokens\.([^.]+)\.open(\.case_insensitive)?$/
      get_token_regexp( @token_regexps, arg, Regexp.last_match )
    when /^lang\.(.+?)\.tokens\.([^.]+)\.close(\.case_insensitive)?$/
      get_token_regexp( @close_token_regexps, arg, Regexp.last_match )
    when /^lang\.(.+?)\.tokens\.(.+?)\.format$/
      language = $1
      token_class = $2
      @token_formats[ language ][ token_class ] = Display.to_formatting( arg )
    when /^lang\.(.+?)\.format\..+$/
      @settings[ command ] = Display.to_formatting( arg )
    when /^colou?r$/
      number, fg, bg = arg.split( /\s+/ )
      number = number.to_i
      fg = Display.to_colour_constant( fg )
      bg = Display.to_colour_constant( bg )
      @colour_pairs << {
        :number => number,
        :fg => fg,
        :bg => bg
      }
    when /^lang\.(.+?)\.indent\.indenters(\.case_insensitive)?$/
      case_insensitive = ( $2 != nil )
      if case_insensitive
        @indenters[ $1 ] = Regexp.new( arg, Regexp::IGNORECASE )
      else
        @indenters[ $1 ] = Regexp.new arg
      end
    when /^lang\.(.+?)\.indent\.indenters_next_line(\.case_insensitive)?$/
      case_insensitive = ( $2 != nil )
      if case_insensitive
        @indenters_next_line[ $1 ] = Regexp.new( arg, Regexp::IGNORECASE )
      else
        @indenters_next_line[ $1 ] = Regexp.new arg
      end
    when /^lang\.(.+?)\.indent\.unindenters(\.case_insensitive)?$/
      case_insensitive = ( $2 != nil )
      if case_insensitive
        @unindenters[ $1 ] = Regexp.new( arg, Regexp::IGNORECASE )
      else
        @unindenters[ $1 ] = Regexp.new arg
      end
    when /^lang\.(.+?)\.indent\.(?:preventers|ignore|not_indented)(\.case_insensitive)?$/,
        /^lang\.(.+?)\.context\.ignore(\.case_insensitive)?$/
      case_insensitive = ( $2 != nil )
      if case_insensitive
        @settings[ command ] = Regexp.new( arg, Regexp::IGNORECASE )
      else
        @settings[ command ] = Regexp.new arg
      end
    when /^lang\.(.+?)\.indent\.triggers(\.case_insensitive)?$/
      case_insensitive = ( $2 != nil )
      if case_insensitive
        @indent_triggers[$1] = Regexp.new( arg, Regexp::IGNORECASE )
      else
        @indent_triggers[$1] = Regexp.new arg
      end
    when /^lang\.(.+?)\.filemask$/
      @filemasks[ $1 ] = Regexp.new arg
    when /^lang\.(.+?)\.bangmask$/
      @bangmasks[ $1 ] = Regexp.new arg
    when /^lang\.(.+?)\.closers\.(.+?)\.(.+?)$/
      @closers[ $1 ] ||= Hash.new
      @closers[ $1 ][ $2 ] ||= Hash.new
      @closers[ $1 ][ $2 ][ $3.to_sym ] = case $3
      when 'regexp'
        Regexp.new arg
      when 'closer'
        begin
          if arg =~ /^\{.+\}$/
            eval( "Proc.new " + arg )
          else
            arg
          end
        rescue Exception => e
          show_exception(
            e,
            [ "Failed to process Proc for #{command}.", ]
          )
        end
      end
    when "context.visible", "context.combined", "eof_newline", "view.nonfilelines.visible",
        /^lang\.(.+?)\.indent\.(?:auto|roundup|using_tabs|closers)$/,
        "found_cursor_start", "convert_tabs", 'delete_newline_on_delete_to_eol',
        'suppress_welcome', 'strip_trailing_whitespace_on_save', 'save_backup_files',
        'find.return_on_abort', 'fuzzy_file_find', 'fuzzy_file_find.recursive', 'view.line_numbers',
        'find.show_context_after', 'view.pairs.highlight', 'open_as_first_buffer', 'mouse'
      @settings[ command ] = arg.to_b
    when "context.format", "context.separator.format", "status.format", 'view.line_numbers.format',
        'view.non_search_area.format'
      @settings[ command ] = Display.to_formatting( arg )
    when /view\.column_markers\.(.+?)\.format/
      @column_markers[ $1 ][ :format ] = Display.to_formatting( arg )
    when "logfile"
      @logfilename = File.expand_path( arg )
    when "context.separator", /^lang\..+?\.indent\.ignore\.charset$/,
        /^lang\.(.+?)\.tokens\.([^.]+)\.change_to$/,
        /^lang\.(.+?)\.column_delimiters$/,
        "view.nonfilelines.character",
        'diff_command', 'session.default_session',
        'clipboard.external'
      @settings[ command ] = arg
    when /^lang\..+?\.comment_(?:close_)?string$/, 'view.line_numbers.number_format',
        "status.filler", "status.left", "status.right",
        "status.modified_str", "status.unnamed_str", "status.selecting_str",
        "status.read_only_str", 'interaction.blink_string'
      @settings[ command ] = arg.gsub( /^["']|["']$/, '' )
    when "status.vars"
      @settings[ command ] = arg.split( /\s+/ )
    when /^lang\.(.+?)\.indent\.size$/, /^lang\.(.+?)\.(?:tabsize|wrap_margin)$/,
        "context.max_levels", "context.max_segment_width", "max_clips", "max_undo_lines",
        "view.margin.x", "view.margin.y", "view.scroll_amount", "view.lookback", 'grep.context',
        'view.line_numbers.width', 'fuzzy_file_find.max_files', 'colour.background'
      @settings[ command ] = arg.to_i
    when "view.jump.x", "view.jump.y"
      @settings[ command ] = [ arg.to_i, 1 ].max
    when /view\.column_markers\.(.+?)\.column/
      @column_markers[ $1 ][ :column ] = [ arg.to_i, 1 ].max
    when "bol_behaviour", "bol_behavior"
      case arg.downcase
      when "zero"
        @settings[ "bol_behaviour" ] = BOL_ZERO
      when "first-char"
        @settings[ "bol_behaviour" ] = BOL_FIRST_CHAR
      when "alternating-zero"
        @settings[ "bol_behaviour" ] = BOL_ALT_ZERO
      else # default
        @settings[ "bol_behaviour" ] = BOL_ALT_FIRST_CHAR
      end
    when "eol_behaviour", "eol_behavior"
      case arg.downcase
      when "end"
        @settings[ "eol_behaviour" ] = EOL_END
      when "last-char"
        @settings[ "eol_behaviour" ] = EOL_LAST_CHAR
      when "alternating-last-char"
        @settings[ "eol_behaviour" ] = EOL_ALT_FIRST_CHAR
      else # default
        @settings[ "eol_behaviour" ] = EOL_ALT_END
      end
    when "context.delay", 'interaction.blink_duration', 'interaction.choice_delay'
      @settings[ command ] = arg.to_f
    when 'fuzzy_file_find.ignore'
      @fuzzy_ignores << arg
    end
  end
end
parse_options( argv ) click to toggle source
# File lib/diakonos.rb, line 245
def parse_options( argv )
  @post_load_script = ""
  while argv.length > 0
    arg = argv.shift
    case arg
    when '-c', '--config'
      @config_filename = argv.shift
      if @config_filename.nil?
        print_usage
        exit 1
      end
    when '-e', '--execute'
      post_load_script = argv.shift
      if post_load_script.nil?
        print_usage
        exit 1
      else
        @post_load_script << "\n#{post_load_script}"
      end
    when '-h', '--help'
      print_usage
      exit 1
    when '-m', '--open-matching'
      regexp = argv.shift
      files = `egrep -rl '#{regexp}' *`.split( /\n/ )
      if files.any?
        @files.concat( files.map { |f| Session.file_hash_for f } )
        script = "\nfind '#{regexp}', case_sensitive: true"
        @post_load_script << script
      end
    when '-ro'
      filename = argv.shift
      if filename.nil?
        print_usage
        exit 1
      else
        h = Session.file_hash_for( filename )
        h[ 'read_only' ] = true
        @read_only_files.push h
      end
    when '-s', '--load-session'
      @session_to_load = session_filepath_for( argv.shift )
    when '--test', '--testing'
      @testing = true
    when '--uninstall'
      uninstall
    when '--uninstall-without-confirmation'
      uninstall false
    when '--version'
      puts "Diakonos #{::Diakonos::VERSION} (#{::Diakonos::LAST_MODIFIED})"
      exit 0
    else
      # a name of a file to open
      @files.push Session.file_hash_for( arg )
    end
  end
end
previous_list_item() click to toggle source
# File lib/diakonos/list.rb, line 37
def previous_list_item
  if @list_buffer
    cursor_up
    @list_buffer[ @list_buffer.current_row ]
  end
end
print_usage() click to toggle source
process_keystroke( context = [], mode = 'edit', ch = nil ) click to toggle source

context is an array of characters (bytes) which are keystrokes previously typed (in a chain of keystrokes)

# File lib/diakonos/keying.rb, line 328
def process_keystroke( context = [], mode = 'edit', ch = nil )
  ch ||= @modes[ mode ].window.getch
  return  if ch.nil?

  if ch == Curses::KEY_MOUSE
    handle_mouse_event
    return
  end

  c = ch.ord

  self.handle_utf_8(c, mode) and return

  if @capturing_keychain
    capture_keychain c, context
  elsif @capturing_mapping
    capture_mapping c, context
  else

    if context.empty? && typeable?( c )
      self.type_character ch, mode
      self.handle_gui_paste(mode)
      return
    end

    keychain_pressed = context.concat [ c ]

    function_and_args = (
      @modes[mode].keymap_after[@function_last].get_leaf( keychain_pressed ) ||
      @modes[mode].keymap.get_leaf( keychain_pressed )
    )

    if function_and_args
      function, args = function_and_args
      @function_last = function

      if mode != 'input' && ! @settings[ "context.combined" ]
        set_iline
      end

      if args
        to_eval = "#{function}( #{args} )"
      else
        to_eval = function
      end

      if @macro_history
        @macro_history.push to_eval
      end

      begin
        if buffer_current.search_area? && ! ( /^(?:find|readline)/ === to_eval )
          buffer_current.clear_search_area
        end
        eval to_eval, nil, "eval"
        @functions_last << to_eval  unless to_eval == "repeat_last"
        if ! @there_was_non_movement
          @there_was_non_movement = !( /^((cursor|page|scroll)_?(up|down|left|right)|find|seek)/i === to_eval )
        end
      rescue Exception => e
        debug_log e.message
        debug_log e.backtrace.join( "\n\t" )
        show_exception e
      end
    else
      partial_keychain = @modes[ mode ].keymap.get_node( keychain_pressed )
      if partial_keychain
        if mode != 'input'
          set_iline( keychain_str_for( keychain_pressed ) + "..." )
        end
        process_keystroke keychain_pressed, mode
      elsif mode != 'input'
        set_iline "Nothing assigned to #{keychain_str_for( keychain_pressed )}"
      end
    end
  end
end
push_cursor_state( top_line, row, col, clear_stack_pointer = CLEAR_STACK_POINTER ) click to toggle source
# File lib/diakonos/cursor.rb, line 10
def push_cursor_state( top_line, row, col, clear_stack_pointer = CLEAR_STACK_POINTER )
  new_state = {
    buffer: buffer_current,
    top_line: top_line,
    row: row,
    col: col
  }
  if ! @cursor_stack.include? new_state
    @cursor_stack << new_state
    if clear_stack_pointer
      @cursor_stack_pointer = nil
    end
    clear_non_movement_flag
  end
end
redraw() click to toggle source
# File lib/diakonos/display.rb, line 114
def redraw
  load_configuration
  initialize_display
  update_status_line
  update_context_line
  display_buffer buffer_current
end
refresh_all() click to toggle source
# File lib/diakonos/display.rb, line 322
def refresh_all
  @win_main.refresh
  if @win_context
    @win_context.refresh
  end
  @win_status.refresh
  @win_interaction.refresh
  if @win_line_numbers
    @win_line_numbers.refresh
  end
end
register_proc( the_proc, hook_name, priority = 0 ) click to toggle source
# File lib/diakonos/hooks.rb, line 3
def register_proc( the_proc, hook_name, priority = 0 )
  @hooks[ hook_name ] << { :proc => the_proc, :priority => priority }
end
run_hook_procs( hook_id, *args ) click to toggle source
# File lib/diakonos/hooks.rb, line 7
def run_hook_procs( hook_id, *args )
  @hooks[ hook_id ].each do |hook_proc|
    hook_proc[ :proc ].call( *args )
  end
end
run_scripts() click to toggle source
# File lib/diakonos.rb, line 432
def run_scripts
  scripts = @extensions.scripts + Dir[ "#{@script_dir}/*" ]
  scripts.each do |script|
    begin
      require script
    rescue Exception => e
      show_exception(
        e,
        [
          "There is a syntax error in the script.",
          "An invalid hook name was used."
        ]
      )
    end
  end
end
save_session( session_file = @session.filename ) click to toggle source
# File lib/diakonos/sessions.rb, line 162
def save_session( session_file = @session.filename )
  return  if session_file.nil?
  return  if @testing && Session.pid_session?(session_file)

  @session.set_buffers(@buffers)
  @session.set_readline_histories(@rlh_general, @rlh_files, @rlh_search, @rlh_shell, @rlh_help, @rlh_sessions)

  File.open( session_file, 'w' ) do |f|
    f.puts @session.to_yaml
  end
end
select_list_item() click to toggle source
# File lib/diakonos/list.rb, line 29
def select_list_item
  if @list_buffer
    line = @list_buffer.set_selection_current_line
    display_buffer @list_buffer
    line
  end
end
session_filepath_for( session_id ) click to toggle source
# File lib/diakonos/sessions.rb, line 174
def session_filepath_for( session_id )
  if session_id && session_id !~ %r{/}
    "#{@session_dir}/#{session_id}"
  else
    session_id
  end
end
session_startup() click to toggle source
# File lib/diakonos/sessions.rb, line 182
def session_startup
  @stale_session_files = []

  if @session_to_load
    pid_session = @session
    @session = nil
    session_path = session_filepath_for( @session_to_load )
    load_session session_path
    if ! @session
      @session = Session.new(session_path)
    end
  else
    session_files = Dir[ "#{@session_dir}/*" ].grep( %r{/\d+$} )
    session_files.each do |sf|
      pid = sf[ %r{/(\d+)$}, 1 ].to_i

      # Check if the process is still alive
      begin
        Process.kill 0, pid
      rescue Errno::ESRCH, Errno::EPERM
        if Session.pid_session?(sf)
          @stale_session_files << sf
        end
      end
    end
  end
end
set_iline(s = "") click to toggle source
# File lib/diakonos/display.rb, line 137
def set_iline(s = "")
  @interaction_handler.set_iline s
end
set_iline_if_empty(s) click to toggle source
# File lib/diakonos/display.rb, line 141
def set_iline_if_empty(s)
  @interaction_handler.set_iline_if_empty s
end
set_initial_iline() click to toggle source
# File lib/diakonos.rb, line 423
def set_initial_iline
  if ENV['COLORTERM'] == 'gnome-terminal'
    help_key = 'Shift-F1'
  else
    help_key = 'F1'
  end
  set_iline "Diakonos #{VERSION} (#{LAST_MODIFIED})   #{help_key} for help  F12 to configure  Ctrl-Q to quit"
end
set_status_variable( identifier, value ) click to toggle source
# File lib/diakonos/display.rb, line 145
def set_status_variable( identifier, value )
  @status_vars[ identifier ] = value
end
show_buffer_file_diff( buffer = @buffer_current ) { |diff_buffer| ... } click to toggle source
# File lib/diakonos/buffer-management.rb, line 49
def show_buffer_file_diff( buffer = @buffer_current )
  current_text_file = @diakonos_home + '/current-buffer'
  buffer.save_copy( current_text_file )
  `#{@settings[ 'diff_command' ]} #{current_text_file} #{buffer.name} > #{@diff_filename}`
  diff_buffer = open_file( @diff_filename )
  yield diff_buffer
  close_buffer diff_buffer
end
show_exception( e, probable_causes = [ "Unknown" ] ) click to toggle source
# File lib/diakonos.rb, line 502
def show_exception( e, probable_causes = [ "Unknown" ] )
  begin
    File.open( @error_filename, "w" ) do |f|
      f.puts "Diakonos Error:"
      f.puts
      f.puts "#{e.class}: #{e.message}"
      f.puts
      f.puts "Probable Causes:"
      f.puts
      probable_causes.each do |pc|
        f.puts "- #{pc}"
      end
      f.puts
      f.puts "----------------------------------------------------"
      f.puts "If you can reproduce this error, please report it at"
      f.puts "https://github.com/Pistos/diakonos/issues !"
      f.puts "----------------------------------------------------"
      f.puts e.backtrace
    end
    open_file @error_filename
  rescue Exception => e2
    debug_log "EXCEPTION: #{e.message}"
    debug_log "\t#{e.backtrace}"
  end
end
showing_list?() click to toggle source
# File lib/diakonos/list.rb, line 15
def showing_list?
  @list_buffer
end
start() click to toggle source
# File lib/diakonos.rb, line 326
def start
  require 'diakonos/window'

  create_buffers_from_files
  @files = []
  @read_only_files.each do |file|
    @buffers << Buffer.new( file )
  end
  if ! @testing
    session_startup
  end
  create_buffers_from_files
  @files = []
  if @buffers.empty?
    @buffers << Buffer.new
  end

  initialize_display
  @buffers.each do |buffer|
    buffer.reset_display
  end

  set_initial_iline

  run_scripts

  @hooks.each do |hook_name, hook|
    hook.sort { |a,b| a[ :priority ] <=> b[ :priority ] }
  end

  handle_stale_session_files

  create_buffers_from_files

  session_buffer_number = @session.buffer_current || 1
  if ! switch_to_buffer_number( session_buffer_number )
    debug_log "Failed to switch to buffer #{session_buffer_number.inspect}"
    switch_to_buffer_number 1
  end

  run_hook_procs :after_startup
  if @post_load_script
    begin
      eval @post_load_script
    rescue Exception => e
      show_exception(
        e,
        [ "There is an error in the post-load script:\n#{@post_load_script}" ]
      )
    end
  end

  @buffers.each do |b|
    run_hook_procs :after_open, b
    b.cursor_to( b.last_row, b.last_col, Buffer::DONT_DISPLAY )
  end
  buffer_current.cursor_to( buffer_current.last_row, buffer_current.last_col, Buffer::DONT_DISPLAY )

  if @configs.any? { |c| c.problems.any? }
    File.open( @error_filename, "w" ) do |f|
      f.puts "There are problems with the configuration file(s):"
      @configs.each do |c|
        next  if c.problems.empty?

        f.puts
        f.print "#{c}:\n\t"
        f.puts c.problems.join("\n\t")
      end
    end
    open_file @error_filename
  end

  if ! @testing && ! @settings[ 'suppress_welcome' ]
    open_file "#{@help_dir}/welcome.dhf"
  else
    conflict_regexp_source = '^<{4,}'
    if seek(conflict_regexp_source)
      find conflict_regexp_source
    end
  end

  begin
    # Main keyboard loop.
    while ! @quitting
      process_keystroke
      @win_main.refresh
    end
  rescue SignalException => e
    debug_log "Terminated by signal (#{e.message})"
  end

  cleanup_display
  cleanup_session

  @debug.close
end
start_recording_macro( name = nil ) click to toggle source
# File lib/diakonos.rb, line 528
def start_recording_macro( name = nil )
  return if @macro_history
  @macro_name = name
  @macro_history = Array.new
  @macro_input_history = Array.new
  set_iline "Started macro recording."
end
stop_recording_macro() click to toggle source
# File lib/diakonos.rb, line 536
def stop_recording_macro
  @macro_history.pop  # Remove the stop_recording_macro command itself
  @macros[ @macro_name ] = [ @macro_history, @macro_input_history ]
  @macro_history = nil
  @macro_input_history = nil
  set_iline "Stopped macro recording."
end
switch_to( buffer, opts = {} ) click to toggle source

@param [Buffer] the Buffer to switch to @option opts [Boolean] :do_display

Whether or not to update the display after closure

@return [Boolean] true iff the buffer was successfully switched to

# File lib/diakonos/buffer-management.rb, line 9
def switch_to( buffer, opts = {} )
  return false  if buffer.nil?
  do_display = opts.fetch( :do_display, true )

  @buffer_stack -= [ @buffer_current ]
  if @buffer_current
    @buffer_stack.push @buffer_current
  end
  @buffer_current = buffer
  @session.buffer_current = buffer_to_number( buffer )
  run_hook_procs( :after_buffer_switch, buffer )
  if do_display
    update_status_line
    update_context_line
    display_buffer buffer
  end

  true
end
type_character( c, mode = 'edit' ) click to toggle source
# File lib/diakonos/keying.rb, line 406
def type_character( c, mode = 'edit' )
  if @macro_history
    @macro_history.push "type_character #{c.inspect}, #{mode.inspect}"
  end
  @there_was_non_movement = true

  case mode
  when 'edit'
    buffer_current.delete_selection Buffer::DONT_DISPLAY
    buffer_current.insert_string c
    cursor_right Buffer::STILL_TYPING
    if c =~ @indent_triggers[buffer_current.language]
      buffer_current.parsed_indent cursor_eol: true
    end
  when 'input'
    if ! @readline.numbered_list?
      @readline.paste c
    else
      if(
        showing_list? &&
        ( (48..57).include?( c.ord ) || (97..122).include?( c.ord ) )
      )
        line = list_buffer.to_a.select { |l|
          l =~ /^#{c}  /
        }[ 0 ]

        if line
          @readline.list_sync line
          @readline.finish
        end
      end
    end
  end
end
typeable?( char ) click to toggle source
# File lib/diakonos/keying.rb, line 239
def typeable?( char )
  char > 31 && char < 255 && char != BACKSPACE
end
uninstall( confirm = true ) click to toggle source
# File lib/diakonos.rb, line 449
def uninstall( confirm = true )
  inst = ::Diakonos::INSTALL_SETTINGS[ :installed ]

  if confirm
    puts inst[ :files ].sort.join( "\n" )
    puts
    puts inst[ :dirs ].sort.map { |d| "#{d}/" }.join( "\n" )
    puts
    puts "The above files will be removed.  The above directories will be removed if they are empty.  Proceed?  (y/n)"
    answer = $stdin.gets
    case answer
    when /^y/i
      puts "Deleting..."
    else
      puts "Uninstallation aborted."
      exit 1
    end
  end

  inst[ :files ].each do |f|
    FileUtils.rm f
  end
  inst[ :dirs ].sort { |d1,d2| d2.length <=> d1.length }.each do |d|
    begin
      FileUtils.rmdir d
    rescue Errno::ENOTEMPTY
    end
    if File.exists? d
      $stderr.puts "(#{d} not removed)"
    end
  end

  exit 0
end
update_context_line() click to toggle source
# File lib/diakonos/display.rb, line 237
def update_context_line
  return  if @testing
  return  if @win_context.nil?

  @context_thread.exit  if @context_thread
  @context_thread = Thread.new do
    context = buffer_current.context

    Curses::curs_set 0
    @win_context.setpos( 0, 0 )
    chars_printed = 0
    if context.length > 0
      truncation = [ @settings[ "context.max_levels" ], context.length ].min
      max_length = [
        ( Curses::cols / truncation ) - @settings[ "context.separator" ].length,
        ( @settings[ "context.max_segment_width" ] || Curses::cols )
      ].min
      line = nil
      context_subset = context[ 0...truncation ]
      context_subset = context_subset.collect do |line|
        line.strip[ 0...max_length ]
      end

      context_subset.each do |line|
        @win_context.attrset @settings[ "context.format" ]
        @win_context.addstr line
        chars_printed += line.length
        @win_context.attrset @settings[ "context.separator.format" ]
        @win_context.addstr @settings[ "context.separator" ]
        chars_printed += @settings[ "context.separator" ].length
      end
    end

    @context_line_mutex.synchronize do
      @win_context.attrset @settings[ "context.format" ]
      @win_context.addstr( " " * ( Curses::cols - chars_printed ) )
      @win_context.refresh
    end
    @display_mutex.synchronize do
      @win_main.setpos( buffer_current.last_screen_y, buffer_current.last_screen_x )
      @win_main.refresh
    end
    Curses::curs_set 1
  end

  @context_thread.priority = -2
end
update_status_line() click to toggle source
# File lib/diakonos/display.rb, line 223
def update_status_line
  return  if @testing

  str = build_status_line
  if str.length > Curses::cols
    str = build_status_line( str.length - Curses::cols )
  end
  Curses::curs_set 0
  @win_status.setpos( 0, 0 )
  @win_status.addstr str
  @win_status.refresh
  Curses::curs_set 1
end
utf_8_bytes_to_char(c, mode) click to toggle source

@param [Integer] c The first byte of a UTF-8 byte sequence @return [String] nil if c is not the beginning of a multi-byte sequence

# File lib/diakonos/keying.rb, line 245
def utf_8_bytes_to_char(c, mode)
  if Keying::UTF_8_2_BYTE_BEGIN <= c && c <= Keying::UTF_8_2_BYTE_END
    # 2-byte character
    byte_array = [c, @modes[mode].window.getch.ord]
  elsif Keying::UTF_8_3_BYTE_BEGIN <= c && c <= Keying::UTF_8_3_BYTE_END
    # 3-byte character
    byte_array = [
      c,
      @modes[mode].window.getch.ord,
      @modes[mode].window.getch.ord,
    ]
  elsif Keying::UTF_8_4_BYTE_BEGIN <= c && c <= Keying::UTF_8_4_BYTE_END
    # 4-byte character
    byte_array = [
      c,
      @modes[mode].window.getch.ord,
      @modes[mode].window.getch.ord,
      @modes[mode].window.getch.ord,
    ]
  else
    return nil
  end

  byte_array.pack('C*').force_encoding('utf-8')
end
with_list_file() { |f| ... } click to toggle source
# File lib/diakonos/list.rb, line 51
def with_list_file
  File.open( @list_filename, "w" ) do |f|
    yield f
  end
end

Protected Instance Methods

build_status_line( truncation = 0 ) click to toggle source
# File lib/diakonos/display.rb, line 149
def build_status_line( truncation = 0 )
  var_array = Array.new
  @settings[ "status.vars" ].each do |var|
    case var
    when "buffer_number"
      var_array.push buffer_to_number( buffer_current )
    when "col"
      var_array.push( buffer_current.last_screen_col + 1 )
    when "filename"
      name = buffer_current.nice_name
      var_array.push name[ ([ truncation, name.length ].min)..-1 ]
    when "modified"
      if buffer_current.modified?
        var_array.push @settings[ "status.modified_str" ]
      else
        var_array.push ""
      end
    when "num_buffers"
      var_array.push @buffers.length
    when "num_lines"
      var_array.push buffer_current.length
    when "row", "line"
      var_array.push( buffer_current.last_row + 1 )
    when "read_only"
      if buffer_current.read_only
        var_array.push @settings[ "status.read_only_str" ]
      else
        var_array.push ""
      end
    when "selecting"
      if buffer_current.changing_selection
        var_array.push @settings[ "status.selecting_str" ]
      else
        var_array.push ""
      end
    when 'selection_mode'
      case buffer_current.selection_mode
      when :block
        var_array.push 'block'
      else
        var_array.push ''
      end
    when 'session_name'
      var_array.push @session.name
    when "type"
      var_array.push buffer_current.original_language
    when /^@/
      var_array.push @status_vars[ var ]
    end
  end
  str = nil
  begin
    status_left = @settings[ "status.left" ]
    field_count = status_left.count "%"
    status_left = status_left % var_array[ 0...field_count ]
    status_right = @settings[ "status.right" ] % var_array[ field_count..-1 ]
    filler_string = @settings[ "status.filler" ]
    fill_amount = (Curses::cols - status_left.length - status_right.length) / filler_string.length
    if fill_amount > 0
      filler = filler_string * fill_amount
    else
      filler = ""
    end
    str = status_left + filler + status_right
  rescue ArgumentError, TypeError => e
    debug_log e
    debug_log e.backtrace[ 0 ]
    debug_log "var_array: #{var_array.inspect}"
    str = "%-#{Curses::cols}s" % "(status line configuration error)"
  end
  str
end