Class: Diakonos::Diakonos

Inherits:
Object show all
Includes:
Functions
Defined in:
lib/diakonos.rb,
lib/diakonos/grep.rb,
lib/diakonos/list.rb,
lib/diakonos/help.rb,
lib/diakonos/mouse.rb,
lib/diakonos/hooks.rb,
lib/diakonos/about.rb,
lib/diakonos/keying.rb,
lib/diakonos/search.rb,
lib/diakonos/config.rb,
lib/diakonos/cursor.rb,
lib/diakonos/logging.rb,
lib/diakonos/display.rb,
lib/diakonos/sessions.rb,
lib/diakonos/interaction.rb,
lib/diakonos/buffer-management.rb,
lib/diakonos/functions-deprecated.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Functions

#about, #addNamedBookmark, #add_named_bookmark, #anchor_selection, #anchor_unanchored_selection, #backspace, #carriage_return, #change_session_setting, #chdir, #clear_matches, #close_buffer, #close_code, #collapseWhitespace, #collapse_whitespace, #columnize, #comment_out, #complete_word, #copy_selection, #cursor_bof, #cursor_bol, #cursor_bov, #cursor_down, #cursor_eof, #cursor_eol, #cursor_left, #cursor_return, #cursor_right, #cursor_tov, #cursor_up, #cut_selection, #delete, #delete_and_store_line, #delete_from, #delete_line, #delete_to, #delete_to_and_from, #delete_to_eol, #evaluate, #execute, #find, #find_again, #find_clip, #find_exact, #goToNamedBookmark, #goToNextBookmark, #goToPreviousBookmark, #go_block_inner, #go_block_next, #go_block_outer, #go_block_previous, #go_to_char, #go_to_char_previous, #go_to_line_ask, #go_to_named_bookmark, #go_to_next_bookmark, #go_to_pair_match, #go_to_previous_bookmark, #go_to_tag, #go_to_tag_under_cursor, #grep, #grep_buffers, #grep_dir, #grep_session_dir, #help, #indent, #insertSpaces, #insertTab, #insert_spaces, #insert_tab, #joinLines, #join_lines, #join_lines_upward, #list_buffers, #load_script, #merge_session_settings, #name_session, #open_file, #open_file_ask, #open_matching_files, #operateOnEachLine, #operateOnLines, #operateOnString, #operate_on_each_line, #operate_on_lines, #operate_on_string, #page_down, #page_up, #parsedIndent, #parsed_indent, #paste, #paste_shell_result, #play_macro, #pop_tag, #print_keychain, #print_mapped_function, #quit, #readline_abort, #readline_accept, #readline_backspace, #readline_complete_input, #readline_cursor_bol, #readline_cursor_down, #readline_cursor_eol, #readline_cursor_left, #readline_cursor_right, #readline_cursor_up, #readline_delete, #readline_delete_line, #readline_delete_word, #readline_grep_context_decrease, #readline_grep_context_increase, #readline_page_down, #readline_page_up, #removeNamedBookmark, #remove_named_bookmark, #remove_selection, #renumber_buffer, #repeat_last, #revert, #save_file, #save_file_as, #scroll_down, #scroll_up, #search_and_replace, #seek, #select_all, #select_block, #select_line, #select_word, #select_word_another, #select_wrapping_block, #selection_mode_block, #selection_mode_normal, #set_buffer_type, #set_read_only, #set_session_dir, #shell, #show_clips, #show_number_of_matches_found, #spawn, #sub_shell_variables, #surround_line, #surround_paragraph, #surround_selection, #surround_word, #suspend, #switch_to_buffer_number, #switch_to_next_buffer, #switch_to_previous_buffer, #toggleBookmark, #toggle_bookmark, #toggle_macro_recording, #toggle_selection, #toggle_session_setting, #uncomment, #undo, #unindent, #unundo, #wrap_paragraph

Constructor Details

#initialize(argv = []) ⇒ Diakonos

Returns a new instance of Diakonos


152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/diakonos.rb', line 152

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

  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
  @iline_mutex            = Mutex.new

  @tag_stack              = Array.new
  @last_search_regexps    = nil
  @iterated_choice        = nil
  @choice_iterations      = 0
  @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

Instance Attribute Details

#buffer_currentObject (readonly)

Returns the value of attribute buffer_current


3
4
5
# File 'lib/diakonos/buffer-management.rb', line 3

def buffer_current
  @buffer_current
end

#buffersObject (readonly)

Returns the value of attribute buffers


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

def buffers
  @buffers
end

#clipboardObject (readonly)

Returns the value of attribute clipboard


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

def clipboard
  @clipboard
end

#close_token_regexpsObject (readonly)

Returns the value of attribute close_token_regexps


14
15
16
# File 'lib/diakonos/config.rb', line 14

def close_token_regexps
  @close_token_regexps
end

#closersObject (readonly)

Returns the value of attribute closers


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

def closers
  @closers
end

#column_markersObject (readonly)

Returns the value of attribute column_markers


14
15
16
# File 'lib/diakonos/config.rb', line 14

def column_markers
  @column_markers
end

#diakonos_confObject (readonly)

Returns the value of attribute diakonos_conf


14
15
16
# File 'lib/diakonos/config.rb', line 14

def diakonos_conf
  @diakonos_conf
end

#diakonos_homeObject (readonly)

Returns the value of attribute diakonos_home


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

def diakonos_home
  @diakonos_home
end

#display_mutexObject (readonly)

Returns the value of attribute display_mutex


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

def display_mutex
  @display_mutex
end

#do_displayObject (readonly)

Returns the value of attribute do_display


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

def do_display
  @do_display
end

#functions_lastObject (readonly)

Returns the value of attribute functions_last


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

def functions_last
  @functions_last
end

#hooksObject (readonly)

Returns the value of attribute hooks


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

def hooks
  @hooks
end

#indentersObject (readonly)

Returns the value of attribute indenters


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

def indenters
  @indenters
end

#indenters_next_lineObject (readonly)

Returns the value of attribute indenters_next_line


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

def indenters_next_line
  @indenters_next_line
end

#list_bufferObject (readonly)

Returns the value of attribute list_buffer


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

def list_buffer
  @list_buffer
end

#list_filenameObject (readonly)

Returns the value of attribute list_filename


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

def list_filename
  @list_filename
end

#readlineObject (readonly)

Returns the value of attribute readline


51
52
53
# File 'lib/diakonos/interaction.rb', line 51

def readline
  @readline
end

#script_dirObject (readonly)

Returns the value of attribute script_dir


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

def script_dir
  @script_dir
end

#sessionObject (readonly)

Returns the value of attribute session


136
137
138
# File 'lib/diakonos/sessions.rb', line 136

def session
  @session
end

#settingsObject (readonly)

Returns the value of attribute settings


14
15
16
# File 'lib/diakonos/config.rb', line 14

def settings
  @settings
end

#surround_pairsObject (readonly)

Returns the value of attribute surround_pairs


14
15
16
# File 'lib/diakonos/config.rb', line 14

def surround_pairs
  @surround_pairs
end

#testingObject (readonly)

Returns the value of attribute testing


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

def testing
  @testing
end

#there_was_non_movementObject (readonly)

Returns the value of attribute there_was_non_movement


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

def there_was_non_movement
  @there_was_non_movement
end

#token_formatsObject (readonly)

Returns the value of attribute token_formats


14
15
16
# File 'lib/diakonos/config.rb', line 14

def token_formats
  @token_formats
end

#token_regexpsObject (readonly)

Returns the value of attribute token_regexps


14
15
16
# File 'lib/diakonos/config.rb', line 14

def token_regexps
  @token_regexps
end

#unindentersObject (readonly)

Returns the value of attribute unindenters


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

def unindenters
  @unindenters
end

#win_line_numbersObject (readonly)

Returns the value of attribute win_line_numbers


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

def win_line_numbers
  @win_line_numbers
end

#win_mainObject (readonly)

Returns the value of attribute win_main


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

def win_main
  @win_main
end

Instance Method Details

#about_writeObject


4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/diakonos/about.rb', line 4

def about_write
  File.open( @about_filename, "w" ) do |f|
    inst = ::Diakonos::INSTALL_SETTINGS

    configs = @configs.map { |h|
      source = h[:source] ? "(included by #{h[:source]})" : ''
      "#{h[:filename]}\t#{source}"
    }.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" )

    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:   #{ File.mtime( File.join( inst[ :lib_dir ], 'diakonos', 'installation.rb' ) ) }
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) ⇒ Object


35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# 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) ⇒ Object

Returns nil if no such buffer exists.

Parameters:

  • buffer_number (Fixnum)

    should be 1-based, not zero-based.

Returns:

  • nil if no such buffer exists.


31
32
33
34
35
36
37
38
# 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) ⇒ Fixnum

Returns:

  • (Fixnum)

    1-based, not zero-based.

  • nil if no such buffer exists.


42
43
44
45
46
47
# 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) ⇒ Object


190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
# File 'lib/diakonos/keying.rb', line 190

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


216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# File 'lib/diakonos/keying.rb', line 216

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_displayObject


8
9
10
11
12
13
14
15
16
17
18
# File 'lib/diakonos/display.rb', line 8

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_sessionObject


251
252
253
254
255
# 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_flagObject


306
307
308
# File 'lib/diakonos.rb', line 306

def clear_non_movement_flag
  @there_was_non_movement = false
end

#close_help_bufferObject


11
12
13
14
# File 'lib/diakonos/help.rb', line 11

def close_help_buffer
  close_buffer @help_buffer
  @help_buffer = nil
end

#close_list_buffer(opts = {}) ⇒ Object


10
11
12
13
# File 'lib/diakonos/list.rb', line 10

def close_list_buffer( opts = {} )
  close_buffer  @list_buffer, opts
  @list_buffer = nil
end

#current_list_itemObject


23
24
25
26
27
# 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) ⇒ Object


4
5
6
7
8
# 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) ⇒ Object


10
11
12
13
# 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_contextObject


89
90
91
92
93
94
95
# 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) ⇒ Object


285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
# 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) ⇒ Object


550
551
552
553
554
555
556
557
558
559
560
561
# File 'lib/diakonos.rb', line 550

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

#fetch_conf(location = "v#{VERSION}") ⇒ Object


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/diakonos/config.rb', line 16

def fetch_conf( location = "v#{VERSION}" )
  require 'open-uri'
  found = false
  puts "Fetching configuration from #{location}..."

  begin
    open( "http://github.com/Pistos/diakonos/tree/#{location}/diakonos.conf?raw=true" ) do |http|
      text = http.read
      if text =~ /key/ && text =~ /colour/ && text =~ /lang/
        found = true
        File.open( @diakonos_conf, 'w' ) do |f|
          f.puts text
        end
      end
    end
  rescue SocketError, OpenURI::HTTPError => e
    $stderr.puts "Failed to fetch from #{location}."
  end

  found
end

#find_(options = {}) ⇒ Fixnum

Returns the number of replacements made

Returns:

  • (Fixnum)

    the number of replacements made


5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
# 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_char(prompt) ⇒ Object


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

def get_char( prompt )
  set_iline prompt
  char = @win_main.getch
  set_iline
  char
end

#get_choice(prompt, choices, default = nil) ⇒ Object

choices should be an array of CHOICE_* constants. default is what is returned when Enter is pressed.


97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
# File 'lib/diakonos/interaction.rb', line 97

def get_choice( prompt, choices, default = nil )
  retval = @iterated_choice
  if retval
    @choice_iterations -= 1
    if @choice_iterations < 1
      @iterated_choice = nil
      @do_display = true
    end
    return retval
  end

  @saved_main_x = @win_main.curx
  @saved_main_y = @win_main.cury

  msg = prompt + " "
  choice_strings = choices.collect do |choice|
    CHOICE_STRINGS[ choice ]
  end
  msg << choice_strings.join( ", " )

  if default
    set_iline msg
  else
    show_message msg
  end

  while retval.nil?
    ch = @win_interaction.getch
    if ch
      c = ch.ord
    else
      next
    end

    case c
    when Curses::KEY_NPAGE
      page_down
    when Curses::KEY_PPAGE
      page_up
    else
      if @message_expiry && Time.now < @message_expiry
        interaction_blink
        show_message msg
      else
        case c
        when ENTER
          retval = default
        when '0'.ord..'9'.ord
          if @choice_iterations < 1
            @choice_iterations = ( c - '0'.ord )
          else
            @choice_iterations = @choice_iterations * 10 + ( c - '0'.ord )
          end
        else
          choices.each do |choice|
            if CHOICE_KEYS[ choice ].include? c
              retval = choice
              break
            end
          end
        end

        if retval.nil?
          interaction_blink( msg )
        end
      end
    end
  end

  terminate_message
  set_iline

  if @choice_iterations > 0
    @choice_iterations -= 1
    @iterated_choice = retval
    @do_display = false
  end

  retval
end

#get_language_from_name(name) ⇒ Object


464
465
466
467
468
469
470
471
# File 'lib/diakonos.rb', line 464

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


473
474
475
476
477
478
479
480
# File 'lib/diakonos.rb', line 473

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


149
150
151
152
153
154
155
156
157
158
159
# File 'lib/diakonos/config.rb', line 149

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

completion_array is the array of strings that tab completion can use

Parameters:

  • options (defaults to: {})

    :initial_text, :completion_array, :history, :do_complete, :on_dirs


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/diakonos/interaction.rb', line 55

def get_user_input( prompt, options = {}, &block )
  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 )

  if @playing_macro
    retval = @macro_input_history.shift
  else
    cursor_pos = set_iline( prompt )
    @readline = Readline.new( self, @win_interaction, cursor_pos, options, &block )

    while ! @readline.done?
      process_keystroke Array.new, 'input'
    end
    retval = @readline.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
  end
  retval
end

#grep_(regexp_source, *buffers) ⇒ Object


55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 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_mouse_eventObject


3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 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_filesObject

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.


213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# 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

#increase_grep_contextObject


84
85
86
87
88
# 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_helpObject


3
4
5
6
# 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_displayObject


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/diakonos/display.rb', line 20

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 )

  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_sessionObject


138
139
140
141
142
# 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

88
89
90
91
92
93
# File 'lib/diakonos/interaction.rb', line 88

def interaction_blink( message = nil )
  terminate_message
  set_iline @settings[ 'interaction.blink_string' ]
  sleep @settings[ 'interaction.blink_duration' ]
  set_iline message if message
end

#keychain_str_for(array) ⇒ Object


177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/diakonos/keying.rb', line 177

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

#list_item_selected?Boolean

Returns:

  • (Boolean)

19
20
21
# File 'lib/diakonos/list.rb', line 19

def list_item_selected?
  @list_buffer && @list_buffer.selecting?
end

#load_configurationObject Also known as: loadConfiguration


38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
# File 'lib/diakonos/config.rb', line 38

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."
          $stdout.print "Would you like to download one right now from the Diakonos repository? (y/n)"; $stdout.flush
          answer = $stdin.gets

          case answer
          when /^y/i
            if ! fetch_conf
              fetch_conf 'master'
            end
          end
        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 = []
  @config_problems = []
  parse_configuration_file @global_diakonos_conf
  parse_configuration_file @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
  else
    @clipboard = Clipboard.new( @settings[ "max_clips" ] )
  end
  @log = File.open( @logfilename, "a" )

  if @buffers
    @buffers.each do |buffer|
      buffer.configure
    end
  end
end

#load_session(session_file) ⇒ Object


144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# 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_tagsObject


524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
# File 'lib/diakonos.rb', line 524

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


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

def log( string )
  @log.puts string
  @log.flush
end

#log_backtraceObject


15
16
17
18
19
20
21
22
23
# 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_heightObject


110
111
112
113
114
115
116
117
118
119
# File 'lib/diakonos/display.rb', line 110

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_widthObject


121
122
123
# File 'lib/diakonos/display.rb', line 121

def main_window_width
  Curses::cols
end

#map_key(arg, keymap = @modes['edit'].keymap) ⇒ Object


161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
# File 'lib/diakonos/config.rb', line 161

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


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# 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) ⇒ Object


231
232
233
234
235
# File 'lib/diakonos.rb', line 231

def mkdir( dir )
  if ! FileTest.exists?( dir )
    Dir.mkdir dir
  end
end

#next_list_itemObject


44
45
46
47
48
49
# 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_bufferObject


8
9
10
# File 'lib/diakonos/help.rb', line 8

def open_help_buffer
  @help_buffer = open_file( @help_filename )
end

#open_help_document(selected_string) ⇒ Object


39
40
41
42
43
44
# 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_bufferObject


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

def open_list_buffer
  @list_buffer = open_file( @list_filename )
end

#parse_configuration_file(filename_, including_filename = nil) ⇒ Object

Returns an Array of problem descriptions (Strings)

Parameters:

  • filename_ (String)

    the config file to parse

  • including_filename (String) (defaults to: nil)

    the config file which calls include on this one

Returns:

  • an Array of problem descriptions (Strings)


194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
# File 'lib/diakonos/config.rb', line 194

def parse_configuration_file( filename_, including_filename = nil )
  return  if filename_.nil?
  begin
    filename = File.realpath( filename_ )
  rescue Errno::ENOENT
    s = "Configuration file #{filename_.inspect} was not found"
    if including_filename
      s << " (referenced from #{including_filename.inspect}"
    end
    @config_problems << s
    return
  end
  return  if @configs.any? { |c| c[:filename] == filename }
  @configs << { filename: filename, source: including_filename }

  IO.readlines( filename ).each_with_index do |line,line_number|
    line.chomp!
    # Skip comments
    next  if line[ 0 ] == ?#

    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_problems << "Configuration file #{filename.inspect} has an error on line #{line_number+1}"
        next
      end
    end

    command = command.downcase

    @setting_strings[ command ] = arg

    case command
    when "include"
      parse_configuration_file( File.expand_path( arg ), filename )
    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) ⇒ Object


237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
# File 'lib/diakonos.rb', line 237

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_itemObject


37
38
39
40
41
42
# File 'lib/diakonos/list.rb', line 37

def previous_list_item
  if @list_buffer
    cursor_up
    @list_buffer[ @list_buffer.current_row ]
  end
end

295
296
297
298
299
300
301
302
303
304
# File 'lib/diakonos.rb', line 295

def print_usage
  puts "Usage: #{$0} [options] [file] [file...]"
  puts "\t--help\tDisplay usage"
  puts "\t-c <config file>\tLoad this config file instead of ~/.diakonos/diakonos.conf"
  puts "\t-e, --execute <Ruby code>\tExecute Ruby code (such as Diakonos commands) after startup"
  puts "\t-m, --open-matching <regular expression>\tOpen all matching files under current directory"
  puts "\t-ro <file>\tLoad file as read-only"
  puts "\t-s, --load-session <session identifier>\tLoad a session"
  puts "\t--uninstall[-without-confirmation]\tUninstall Diakonos"
end

#process_keystroke(context = [], mode = 'edit', ch = nil) ⇒ Object

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


247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
# File 'lib/diakonos/keying.rb', line 247

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

  # UTF-8
  if 194 <= c && c <= 244
    if 194 <= c && c <= 223
      # 2-byte character
      byte_array = [c, @modes[mode].window.getch.ord]
    elsif 224 <= c && c <= 239
      # 3-byte character
      byte_array = [
        c,
        @modes[mode].window.getch.ord,
        @modes[mode].window.getch.ord,
      ]
    elsif 240 <= c && c <= 244
      # 4-byte character
      byte_array = [
        c,
        @modes[mode].window.getch.ord,
        @modes[mode].window.getch.ord,
        @modes[mode].window.getch.ord,
      ]
    end

    char = byte_array.pack('C*').force_encoding('utf-8')
    type_character char, mode
    return
  end

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

    if context.empty? && typeable?( c )
      type_character ch, mode

      # Handle X windows paste
      s = ""
      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
        if 194 <= c && c <= 244
          if 194 <= c && c <= 223
            # 2-byte character
            byte_array = [c, @modes[mode].window.getch.ord]
          elsif 224 <= c && c <= 239
            # 3-byte character
            byte_array = [
              c,
              @modes[mode].window.getch.ord,
              @modes[mode].window.getch.ord,
            ]
          elsif 240 <= c && c <= 244
            # 4-byte character
            byte_array = [
              c,
              @modes[mode].window.getch.ord,
              @modes[mode].window.getch.ord,
              @modes[mode].window.getch.ord,
            ]
          end

          char = byte_array.pack('C*').force_encoding('utf-8')
          s << char
        elsif 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

      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.search_area = nil
        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) ⇒ Object


10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 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

#redrawObject


102
103
104
105
106
107
108
# File 'lib/diakonos/display.rb', line 102

def redraw
  load_configuration
  initialize_display
  update_status_line
  update_context_line
  display_buffer buffer_current
end

#refresh_allObject


346
347
348
349
350
351
352
353
354
355
356
# File 'lib/diakonos/display.rb', line 346

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


3
4
5
# 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) ⇒ Object


7
8
9
10
11
# 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

#save_session(session_file = @session.filename) ⇒ Object


162
163
164
165
166
167
168
169
170
171
172
# 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_itemObject


29
30
31
32
33
34
35
# 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) ⇒ Object


174
175
176
177
178
179
180
# 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_startupObject


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
# 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(string = "") ⇒ Object

Display text on the interaction line.


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

def set_iline( string = "" )
  return  if @testing
  return  if @readline

  @iline = string
  Curses::curs_set 0
  @win_interaction.setpos( 0, 0 )
  @win_interaction.addstr( "%-#{Curses::cols}s" % @iline )
  @win_interaction.refresh
  Curses::curs_set 1
  string.length
end

#set_iline_if_empty(string) ⇒ Object


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

def set_iline_if_empty( string )
  if @iline.nil? || @iline.empty?
    set_iline string
  end
end

#set_status_variable(identifier, value) ⇒ Object


145
146
147
# 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| ... } ⇒ Object

Yields:

  • (diff_buffer)

49
50
51
52
53
54
55
56
# 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" ]) ⇒ Object


482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
# File 'lib/diakonos.rb', line 482

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

#show_message(message, non_interaction_duration = @settings[ 'interaction.choice_delay' ]) ⇒ Object


322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
# File 'lib/diakonos/display.rb', line 322

def show_message( message, non_interaction_duration = @settings[ 'interaction.choice_delay' ] )
  terminate_message

  @message_expiry = Time.now + non_interaction_duration
  @message_thread = Thread.new do
    time_left = @message_expiry - Time.now
    while time_left > 0
      set_iline "(#{time_left.round}) #{message}"
      @win_main.setpos( @saved_main_y, @saved_main_x )
      sleep 1
      time_left = @message_expiry - Time.now
    end
    set_iline message
    @win_main.setpos( @saved_main_y, @saved_main_x )
  end
end

#showing_list?Boolean

Returns:

  • (Boolean)

15
16
17
# File 'lib/diakonos/list.rb', line 15

def showing_list?
  @list_buffer
end

#startObject



312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
# File 'lib/diakonos.rb', line 312

def start
  require 'diakonos/window'

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

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

  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"

  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

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

  handle_stale_session_files

  @files.each do |file_info|
    @buffers << Buffer.new( file_info )
  end

  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 @config_problems.any?
    File.open( @error_filename, "w" ) do |f|
      f.puts "There are #{@config_problems.size} problems with the configuration file(s):"
      @config_problems.each do |p|
        f.puts p
      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) ⇒ Object


508
509
510
511
512
513
514
# File 'lib/diakonos.rb', line 508

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_macroObject


516
517
518
519
520
521
522
# File 'lib/diakonos.rb', line 516

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

Returns true iff the buffer was successfully switched to

Parameters:

  • the (Buffer)

    Buffer to switch to

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

    a customizable set of options

Options Hash (opts):

  • :do_display (Boolean)

    Whether or not to update the display after closure

Returns:

  • (Boolean)

    true iff the buffer was successfully switched to


9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 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

#terminate_messageObject


339
340
341
342
343
344
# File 'lib/diakonos/display.rb', line 339

def terminate_message
  if @message_thread && @message_thread.alive?
    @message_thread.terminate
    @message_thread = nil
  end
end

#type_character(c, mode = 'edit') ⇒ Object


410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/diakonos/keying.rb', line 410

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

Returns:

  • (Boolean)

241
242
243
# File 'lib/diakonos/keying.rb', line 241

def typeable?( char )
  char > 31 && char < 255 && char != BACKSPACE
end

#uninstall(confirm = true) ⇒ Object


429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# File 'lib/diakonos.rb', line 429

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_lineObject


237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# 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

    @iline_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_lineObject


223
224
225
226
227
228
229
230
231
232
233
234
235
# 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

#with_list_fileObject


51
52
53
54
55
# File 'lib/diakonos/list.rb', line 51

def with_list_file
  File.open( @list_filename, "w" ) do |f|
    yield f
  end
end