Class: Diakonos::Diakonos

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

Overview

These camel-cased method names are defined here to support legacy configuration files.

Constant Summary collapse

DOCK_SEPARATOR_CHAR =
''

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_definition, #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, #hover, #indent, #insertSpaces, #insertTab, #insert_spaces, #insert_tab, #joinLines, #join_lines, #join_lines_upward, #list_buffers, #load_script, #merge_session_settings, #move_lines, #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.



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# File 'lib/diakonos.rb', line 167

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

  @lsp_servers              = Hash.new
  @lsp_sessions             = Hash.new

  @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

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.



149
150
151
# File 'lib/diakonos.rb', line 149

def buffers
  @buffers
end

#clipboardObject (readonly)

Returns the value of attribute clipboard.



149
150
151
# File 'lib/diakonos.rb', line 149

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.



149
150
151
# File 'lib/diakonos.rb', line 149

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.



149
150
151
# File 'lib/diakonos.rb', line 149

def diakonos_home
  @diakonos_home
end

#display_mutexObject (readonly)

Returns the value of attribute display_mutex.



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

def display_mutex
  @display_mutex
end

#do_displayObject

Returns the value of attribute do_display.



148
149
150
# File 'lib/diakonos.rb', line 148

def do_display
  @do_display
end

#functions_lastObject (readonly)

Returns the value of attribute functions_last.



149
150
151
# File 'lib/diakonos.rb', line 149

def functions_last
  @functions_last
end

#hooksObject (readonly)

Returns the value of attribute hooks.



149
150
151
# File 'lib/diakonos.rb', line 149

def hooks
  @hooks
end

#indentersObject (readonly)

Returns the value of attribute indenters.



149
150
151
# File 'lib/diakonos.rb', line 149

def indenters
  @indenters
end

#indenters_next_lineObject (readonly)

Returns the value of attribute indenters_next_line.



149
150
151
# File 'lib/diakonos.rb', line 149

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.



149
150
151
# File 'lib/diakonos.rb', line 149

def list_filename
  @list_filename
end

#readlineObject (readonly)

Returns the value of attribute readline.



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

def readline
  @readline
end

#script_dirObject (readonly)

Returns the value of attribute script_dir.



149
150
151
# File 'lib/diakonos.rb', line 149

def script_dir
  @script_dir
end

#sessionObject (readonly)

Returns the value of attribute session.



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

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.



149
150
151
# File 'lib/diakonos.rb', line 149

def testing
  @testing
end

#there_was_non_movementObject (readonly)

Returns the value of attribute there_was_non_movement.



149
150
151
# File 'lib/diakonos.rb', line 149

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

#win_dockObject (readonly)

Returns the value of attribute win_dock.



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

def win_dock
  @win_dock
end

#win_line_numbersObject (readonly)

Returns the value of attribute win_line_numbers.



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

def win_line_numbers
  @win_line_numbers
end

#win_mainObject (readonly)

Returns the value of attribute win_main.



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

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
63
64
65
66
67
# 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:        GNU GPL v3
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 }
Session dir:    #{ @session.dir }

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



36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/diakonos/grep.rb', line 36

def actually_grep( regexp_source, *buffers )
  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

#buffer_number_to_name(buffer_number) ⇒ Object

Returns nil if no such buffer exists.

Parameters:

  • buffer_number (Integer)

    should be 1-based, not zero-based.

Returns:

  • nil if no such buffer exists.



32
33
34
35
36
37
# File 'lib/diakonos/buffer-management.rb', line 32

def buffer_number_to_name( buffer_number )
  return nil  if buffer_number < 1

  b = @buffers[ buffer_number - 1 ]
  b&.name
end

#buffer_to_number(buffer) ⇒ Integer

Returns:

  • (Integer)

    1-based, not zero-based.

  • nil if no such buffer exists.



41
42
43
44
45
46
# File 'lib/diakonos/buffer-management.rb', line 41

def buffer_to_number( buffer )
  i = @buffers.index( buffer )
  if i
    i + 1
  end
end

#capture_keychain(c, context) ⇒ Object



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

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



214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
# 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_displayObject



13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/diakonos/display.rb', line 13

def cleanup_display
  return  if @testing

  @win_context&.close
  @win_dock&.close
  @win_interaction&.close
  @win_line_numbers&.close
  @win_main&.close
  @win_status&.close

  Curses.close_screen
end

#cleanup_sessionObject



250
251
252
253
254
# File 'lib/diakonos/sessions.rb', line 250

def cleanup_session
  if @session && Session.pid_session?(@session.filename) && File.exist?(@session.filename)
    File.delete @session.filename
  end
end

#clear_non_movement_flagObject



394
395
396
# File 'lib/diakonos.rb', line 394

def clear_non_movement_flag
  @there_was_non_movement = false
end

#close_help_bufferObject



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

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

#create_buffers_from_filesObject




400
401
402
403
404
# File 'lib/diakonos.rb', line 400

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

#current_list_itemObject



23
24
25
# File 'lib/diakonos/list.rb', line 23

def current_list_item
  @list_buffer&.set_selection_current_line
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



90
91
92
93
94
95
96
# File 'lib/diakonos/grep.rb', line 90

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



308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
# File 'lib/diakonos/display.rb', line 308

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 StandardError => 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



653
654
655
656
657
658
659
660
661
662
663
664
# File 'lib/diakonos.rb', line 653

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

Returns the number of replacements made.

Returns:

  • (Integer)

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

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



48
49
50
# File 'lib/diakonos/interaction.rb', line 48

def get_choice(*args)
  @interaction_handler.get_choice(*args)
end

#get_language_from_name(name) ⇒ Object



568
569
570
571
572
573
574
575
# File 'lib/diakonos.rb', line 568

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



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

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



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

def get_token_regexp( hash, arg, match )
  language = match[ 1 ]
  token_class = match[ 2 ]
  case_insensitive = match[ 3 ]
  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



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
# 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:,
    &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) ⇒ Object



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

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

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



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
# 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
      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, typing: true)
    when 'input'
      @readline.paste s
    end
  end

  if ch
    process_keystroke( [], mode, ch )
  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.



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

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

Returns true iff c began a UTF-8 byte sequence.

Parameters:

  • c (Integer)

    The ordinal (number) of a character

  • mode (String)

Returns:

  • (Boolean)

    true iff c began a UTF-8 byte sequence



274
275
276
277
278
279
280
# 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_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



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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/diakonos/display.rb', line 26

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' ]
    line_numbers_width = settings[ 'view.line_numbers.width' ] + Buffer::COLUMNS_FOR_DIAGNOSTICS

    @win_line_numbers = ::Diakonos::Window.new( main_window_height, line_numbers_width, 0, 0 )
    @win_main = ::Diakonos::Window.new(
      main_window_height,
      Curses.cols - line_numbers_width,
      0,
      line_numbers_width
    )
  else
    @win_main = ::Diakonos::Window.new( main_window_height, Curses.cols, 0, 0 )
    @win_line_numbers = nil
  end
  if @dock_lines && ! @dock_lines.empty?
    @win_dock = ::Diakonos::Window.new(dock_height, Curses.cols, main_window_height, 0)
  else
    @win_dock = 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 )
    @win_line_numbers&.keypad( true )
    @win_context&.keypad( true )
  end

  if ! @testing
    @win_main.timeout = @settings['async_update_interval'] || 100
  end

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

  @win_interaction.refresh
  @win_main.refresh
  @win_line_numbers&.refresh

  @buffers&.each(&:reset_display)
end

#initialize_sessionObject



137
138
139
140
141
# File 'lib/diakonos/sessions.rb', line 137

def initialize_session
  @session_dir = "#{@diakonos_home}/sessions"
  mkdir @session_dir
  @session = Session.new("#{@session_dir}/#{Process.pid}")
end

#keychain_str_for(array) ⇒ Object



180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/diakonos/keying.rb', line 180

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) ⇒ ConfigFile, ConfigFileUnreadable



168
169
170
171
172
173
174
175
176
177
# File 'lib/diakonos/config.rb', line 168

def legitimize_config_filename!(prospective_filename, including_config_file)
  if File.exist?(prospective_filename)
    ConfigFile.new(
      File.realpath(prospective_filename),
      including_config_file
    )
  else
    ConfigFileUnreadable.new(prospective_filename, including_config_file)
  end
end

#list_item_selected?Boolean

Returns:

  • (Boolean)


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

def list_item_selected?
  @list_buffer&.selecting?
end

#load_configurationObject Also known as: loadConfiguration



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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/diakonos/config.rb', line 24

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.exist?( @diakonos_conf )
      if FileTest.exist?( @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.exist?( @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" )

  @buffers&.each(&:configure)
end

#load_session(session_file) ⇒ Object



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

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



627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/diakonos.rb', line 627

def load_tags
  @tags = Hash.new
  if buffer_current&.name
    path = File.expand_path( File.dirname( buffer_current.name ) )
    tagfile = path + "/tags"
  else
    tagfile = "./tags"
  end

  if ! FileTest.exist? 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 =~ %r{^/.*/$}
        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
# File 'lib/diakonos/logging.rb', line 15

def log_backtrace
  raise StandardError
rescue StandardError => e
  e.backtrace[ 1.. ].each do |x|
    debug_log x
  end
end

#main_window_heightObject



132
133
134
135
136
137
138
139
140
# File 'lib/diakonos/display.rb', line 132

def main_window_height
  retval = Curses.lines - (INPUT_LINE_HEIGHT + STATUS_LINE_HEIGHT)
  if @settings['context.visible'] && ! @settings['context.combined']
    retval -= CONTEXT_LINE_HEIGHT
  end
  retval -= dock_height

  retval
end

#main_window_widthObject



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

def main_window_width
  Curses.cols
end

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



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

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



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

def matching_help_documents( str )
  docs = []

  if str =~ %r{^/(.+)$}
    regexp = ::Regexp.last_match(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



253
254
255
256
257
# File 'lib/diakonos.rb', line 253

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

#next_list_itemObject



42
43
44
45
46
47
# File 'lib/diakonos/list.rb', line 42

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



40
41
42
43
44
45
# File 'lib/diakonos/help.rb', line 40

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_config_file = ConfigFileNull.new) ⇒ Object

Parameters:

  • filename (String)

    the config file to parse

  • including_config_file (ConfigFile) (defaults to: ConfigFileNull.new)

    the config file which calls include on this one



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
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
# File 'lib/diakonos/config.rb', line 181

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 = ::Regexp.last_match(1), @setting_strings[ ::Regexp.last_match(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

    # rubocop:disable Lint/RedundantSplatExpansion
    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 = ::Regexp.last_match(1)

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

      pair_key = args.shift

      if pair_key =~ %r{^/.+/$}
        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 = ::Regexp.last_match(1)
      token_class = ::Regexp.last_match(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:,
        fg:,
        bg:,
      }
    when /^lang\.(.+?)\.indent\.indenters(\.case_insensitive)?$/
      case_insensitive = ::Regexp.last_match(2)
      if case_insensitive
        @indenters[ ::Regexp.last_match(1) ] = Regexp.new( arg, Regexp::IGNORECASE )
      else
        @indenters[ ::Regexp.last_match(1) ] = Regexp.new arg
      end
    when /^lang\.(.+?)\.indent\.indenters_next_line(\.case_insensitive)?$/
      case_insensitive = ::Regexp.last_match(2)
      if case_insensitive
        @indenters_next_line[ ::Regexp.last_match(1) ] = Regexp.new( arg, Regexp::IGNORECASE )
      else
        @indenters_next_line[ ::Regexp.last_match(1) ] = Regexp.new arg
      end
    when /^lang\.(.+?)\.indent\.unindenters(\.case_insensitive)?$/
      case_insensitive = ::Regexp.last_match(2)
      if case_insensitive
        @unindenters[ ::Regexp.last_match(1) ] = Regexp.new( arg, Regexp::IGNORECASE )
      else
        @unindenters[ ::Regexp.last_match(1) ] = Regexp.new arg
      end
    when /^lang\.(.+?)\.indent\.(?:preventers|ignore|not_indented)(\.case_insensitive)?$/,
        /^lang\.(.+?)\.context\.ignore(\.case_insensitive)?$/
      case_insensitive = ::Regexp.last_match(2)
      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 = ::Regexp.last_match(2)
      if case_insensitive
        @indent_triggers[::Regexp.last_match(1)] = Regexp.new( arg, Regexp::IGNORECASE )
      else
        @indent_triggers[::Regexp.last_match(1)] = Regexp.new arg
      end
    when /^lang\.(.+?)\.filemask$/
      @filemasks[ ::Regexp.last_match(1) ] = Regexp.new arg
    when /^lang\.(.+?)\.bangmask$/
      @bangmasks[ ::Regexp.last_match(1) ] = Regexp.new arg
    when /^lang\.(.+?)\.closers\.(.+?)\.(.+?)$/
      @closers[ ::Regexp.last_match(1) ] ||= Hash.new
      @closers[ ::Regexp.last_match(1) ][ ::Regexp.last_match(2) ] ||= Hash.new
      @closers[ ::Regexp.last_match(1) ][ ::Regexp.last_match(2) ][ ::Regexp.last_match(3).to_sym ] = (
        case ::Regexp.last_match(3)
        when 'regexp'
          Regexp.new arg
        when 'closer'
          begin
            if arg =~ /^\{.+\}$/
              eval("Proc.new " + arg)  # rubocop:disable Security/Eval
            else
              arg
            end
          rescue StandardError => 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[ ::Regexp.last_match(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$/,
        /^lang\..+?\.lsp\.command$/,
        /^extension\.(.+?)\.(.+?)/,
        "view.line_numbers.diagnostic_marker",
        "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)$/,
        "async_update_interval",
        "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[ ::Regexp.last_match(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 *[
      'close_forgotten_buffers_after',
      'context.delay',
      'interaction.blink_duration',
      'interaction.choice_delay',
    ]
      @settings[ command ] = arg.to_f
    when 'fuzzy_file_find.ignore'
      @fuzzy_ignores << arg
    end
    # rubocop:enable Lint/RedundantSplatExpansion
  end
end

#parse_options(argv) ⇒ Object



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

def parse_options( argv )
  @post_load_script = ""
  while argv.any?
    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(confirm: 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



35
36
37
38
39
40
# File 'lib/diakonos/list.rb', line 35

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


383
384
385
386
387
388
389
390
391
392
# File 'lib/diakonos.rb', line 383

def print_usage
  puts "Usage: #{$PROGRAM_NAME} [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_asyncObject



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

def process_async
  @lsp_sessions.each_value(&:process_queue)
  show_lsp_diagnostic_for_current_line
end

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

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

Returns:

  • (Symbol)

    :waiting_for_keystroke if no character was read



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

def process_keystroke( context = [], mode = 'edit', ch = nil )
  ch ||= @modes[ mode ].window.getch
  if ch.nil?
    # Preserve partial keychains
    return :waiting_for_keystroke
  end

  if ch == Curses::KEY_MOUSE
    handle_mouse_event
    return
  end

  if @dock_lines
    hide_dock
  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

      @macro_history&.push to_eval

      begin
        if buffer_current.search_area? && ! ( /^(?:find|readline)/ === to_eval )
          buffer_current.clear_search_area
        end
        eval(to_eval, nil, "eval")  # rubocop:disable Security/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 StandardError => 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
        waiting_for_keystroke = :waiting_for_keystroke
        while waiting_for_keystroke == :waiting_for_keystroke
          waiting_for_keystroke = process_keystroke(keychain_pressed, mode)
        end
      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:,
    row:,
    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



124
125
126
127
128
129
130
# File 'lib/diakonos/display.rb', line 124

def redraw
  load_configuration
  initialize_display
  update_status_line
  update_context_line
  display_buffer buffer_current
end

#refresh_allObject



344
345
346
347
348
349
350
351
# File 'lib/diakonos/display.rb', line 344

def refresh_all
  @win_main.refresh
  @win_context&.refresh
  @win_status.refresh
  @win_interaction.refresh
  @win_dock&.refresh
  @win_line_numbers&.refresh
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: }
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

#run_scriptsObject



516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/diakonos.rb', line 516

def run_scripts
  scripts = @extensions.scripts + Dir[ "#{@script_dir}/*" ]
  scripts.each do |script|
    require script
  rescue StandardError => e
    show_exception(
      e,
      [
        "There is a syntax error in the script.",
        "An invalid hook name was used.",
      ]
    )
  end
end

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



161
162
163
164
165
166
167
168
169
170
171
# File 'lib/diakonos/sessions.rb', line 161

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



27
28
29
30
31
32
33
# File 'lib/diakonos/list.rb', line 27

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



173
174
175
176
177
178
179
# File 'lib/diakonos/sessions.rb', line 173

def session_filepath_for( session_id )
  if session_id && session_id !~ %r{/}
    "#{@session_dir}/#{session_id}"
  else
    session_id
  end
end

#session_startupObject



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

def session_startup
  @stale_session_files = []

  if @session_to_load
    @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 = "") ⇒ Object



161
162
163
# File 'lib/diakonos/display.rb', line 161

def set_iline(s = "")
  @interaction_handler.set_iline s
end

#set_iline_if_empty(s) ⇒ Object



165
166
167
# File 'lib/diakonos/display.rb', line 165

def set_iline_if_empty(s)
  @interaction_handler.set_iline_if_empty s
end

#set_initial_ilineObject



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

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



169
170
171
# File 'lib/diakonos/display.rb', line 169

def set_status_variable( identifier, value )
  @status_vars[ identifier ] = value
end

#show_buffer_file_diff(buffer = @buffer_current) {|diff_buffer| ... } ⇒ Object

Yields:

  • (diff_buffer)


48
49
50
51
52
53
54
55
# File 'lib/diakonos/buffer-management.rb', line 48

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_dock(lines:) ⇒ Object



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

def show_dock(lines:)
  @dock_lines = lines
  initialize_display
  render_dock
  update_status_line
  update_context_line

  buffer_current.cursor_to(
    buffer_current.last_row,
    buffer_current.last_col,
    Buffer::DONT_DISPLAY,
  )
  display_buffer buffer_current
end

#show_exception(exception, probable_causes = ["Unknown"]) ⇒ Object



586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
# File 'lib/diakonos.rb', line 586

def show_exception(exception, probable_causes = ["Unknown"])
  File.open( @error_filename, "w" ) do |f|
    f.puts "Diakonos Error:"
    f.puts
    f.puts "#{exception.class}: #{exception.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 exception.backtrace
  end
  open_file @error_filename
rescue StandardError => e
  debug_log "EXCEPTION: #{e.message}"
  debug_log "\t#{e.backtrace}"
end

#showing_list?Boolean

Returns:

  • (Boolean)


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

def showing_list?
  @list_buffer
end

#startObject



406
407
408
409
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
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
# File 'lib/diakonos.rb', line 406

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(&:reset_display)

  set_initial_iline

  run_scripts

  @hooks.each_value do |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  # rubocop:disable Security/Eval
    rescue StandardError => 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
    ensure_language_lsp(buffer: b, language: b.original_language)
    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
      process_async
      close_forgotten_buffers

      @win_main.refresh
    end
  rescue SignalException => e
    debug_log "Terminated by signal (#{e.message})"
  end

  @lsp_servers.each_value(&:stop)

  cleanup_display
  cleanup_session

  @debug.close
end

#start_recording_macro(name = nil) ⇒ Object



610
611
612
613
614
615
616
617
# File 'lib/diakonos.rb', line 610

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



619
620
621
622
623
624
625
# File 'lib/diakonos.rb', line 619

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
28
# 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') ⇒ Object



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
444
# File 'lib/diakonos/keying.rb', line 415

def type_character( c, mode = 'edit' )
  @macro_history&.push "type_character #{c.inspect}, #{mode.inspect}"
  @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
    elsif (
      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

#typeable?(char) ⇒ Boolean

Returns:

  • (Boolean)


239
240
241
# File 'lib/diakonos/keying.rb', line 239

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

#uninstall(confirm: true) ⇒ Object



531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
# File 'lib/diakonos.rb', line 531

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."
    puts "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
      # Don't delete directories with contents
    end
    if File.exist? d
      warn "(#{d} not removed)"
    end
  end

  exit 0
end

#update_context_lineObject



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

def update_context_line
  return  if @testing
  return  if @win_context.nil?

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

    Curses.curs_set 0
    @win_context.setpos( 0, 0 )
    chars_printed = 0
    if context.any?
      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
      context_subset = context[ 0...truncation ]
      context_subset = context_subset.collect { |line|
        line.strip[ 0...max_length ]
      }

      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_lineObject



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

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

Returns nil if c is not the beginning of a multi-byte sequence.

Parameters:

  • c (Integer)

    The first byte of a UTF-8 byte sequence

Returns:

  • (String)

    nil if c is not the beginning of a multi-byte sequence



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



49
50
51
52
53
# File 'lib/diakonos/list.rb', line 49

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