diakonos 0.8.11 → 0.9.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +433 -0
  3. data/LICENCE.md +675 -0
  4. data/bin/diakonos +6 -0
  5. data/diakonos-256-colour.conf +220 -0
  6. data/diakonos.conf +1802 -0
  7. data/help/about-help.dhf +31 -0
  8. data/help/clipboard.dhf +45 -0
  9. data/help/close-file.dhf +6 -0
  10. data/help/code-block-navigation.dhf +16 -0
  11. data/help/column-markers.dhf +15 -0
  12. data/help/config.dhf +69 -0
  13. data/help/cursor-stack.dhf +19 -0
  14. data/help/delete.dhf +41 -0
  15. data/help/extensions.dhf +125 -0
  16. data/help/file-type.dhf +24 -0
  17. data/help/key-mapping.dhf +127 -0
  18. data/help/line-numbers.dhf +22 -0
  19. data/help/macros.dhf +27 -0
  20. data/help/new-file.dhf +6 -0
  21. data/help/open-file.dhf +21 -0
  22. data/help/quit.dhf +7 -0
  23. data/help/resizing.dhf +19 -0
  24. data/help/ruby.dhf +17 -0
  25. data/help/save-file.dhf +10 -0
  26. data/help/scripting.dhf +92 -0
  27. data/help/search.dhf +103 -0
  28. data/help/shell.dhf +60 -0
  29. data/help/speed.dhf +23 -0
  30. data/help/support.dhf +15 -0
  31. data/help/switch-buffers.dhf +15 -0
  32. data/help/tabs.dhf +36 -0
  33. data/help/undo.dhf +9 -0
  34. data/help/uninstall.dhf +18 -0
  35. data/help/welcome.dhf +32 -0
  36. data/help/word-wrap.dhf +17 -0
  37. data/lib/diakonos/about.rb +69 -0
  38. data/lib/diakonos/bookmark.rb +46 -0
  39. data/lib/diakonos/buffer/bookmarking.rb +47 -0
  40. data/lib/diakonos/buffer/cursor.rb +335 -0
  41. data/lib/diakonos/buffer/delete.rb +170 -0
  42. data/lib/diakonos/buffer/display.rb +356 -0
  43. data/lib/diakonos/buffer/file.rb +157 -0
  44. data/lib/diakonos/buffer/indentation.rb +175 -0
  45. data/lib/diakonos/buffer/searching.rb +552 -0
  46. data/lib/diakonos/buffer/selection.rb +360 -0
  47. data/lib/diakonos/buffer/undo.rb +73 -0
  48. data/lib/diakonos/buffer-hash.rb +60 -0
  49. data/lib/diakonos/buffer-management.rb +59 -0
  50. data/lib/diakonos/buffer.rb +698 -0
  51. data/lib/diakonos/clipboard-klipper-dbus.rb +62 -0
  52. data/lib/diakonos/clipboard-klipper.rb +62 -0
  53. data/lib/diakonos/clipboard-osx.rb +59 -0
  54. data/lib/diakonos/clipboard-xclip.rb +60 -0
  55. data/lib/diakonos/clipboard.rb +47 -0
  56. data/lib/diakonos/config-file.rb +67 -0
  57. data/lib/diakonos/config.rb +381 -0
  58. data/lib/diakonos/core-ext/enumerable.rb +15 -0
  59. data/lib/diakonos/core-ext/hash.rb +60 -0
  60. data/lib/diakonos/core-ext/object.rb +6 -0
  61. data/lib/diakonos/core-ext/regexp.rb +6 -0
  62. data/lib/diakonos/core-ext/string.rb +122 -0
  63. data/lib/diakonos/ctag.rb +28 -0
  64. data/lib/diakonos/cursor.rb +27 -0
  65. data/lib/diakonos/display/format.rb +75 -0
  66. data/lib/diakonos/display.rb +336 -0
  67. data/lib/diakonos/extension-set.rb +49 -0
  68. data/lib/diakonos/extension.rb +34 -0
  69. data/lib/diakonos/finding.rb +40 -0
  70. data/lib/diakonos/functions/basics.rb +34 -0
  71. data/lib/diakonos/functions/bookmarking.rb +61 -0
  72. data/lib/diakonos/functions/buffers.rb +489 -0
  73. data/lib/diakonos/functions/clipboard.rb +70 -0
  74. data/lib/diakonos/functions/cursor.rb +264 -0
  75. data/lib/diakonos/functions/grepping.rb +83 -0
  76. data/lib/diakonos/functions/indentation.rb +71 -0
  77. data/lib/diakonos/functions/readline.rb +93 -0
  78. data/lib/diakonos/functions/search.rb +179 -0
  79. data/lib/diakonos/functions/selection.rb +98 -0
  80. data/lib/diakonos/functions/sessions.rb +78 -0
  81. data/lib/diakonos/functions/shell.rb +250 -0
  82. data/lib/diakonos/functions/tags.rb +65 -0
  83. data/lib/diakonos/functions/text-manipulation.rb +196 -0
  84. data/lib/diakonos/functions-deprecated.rb +77 -0
  85. data/lib/diakonos/functions.rb +292 -0
  86. data/lib/diakonos/grep.rb +98 -0
  87. data/lib/diakonos/help.rb +47 -0
  88. data/lib/diakonos/hooks.rb +13 -0
  89. data/lib/diakonos/installation.rb +19 -0
  90. data/lib/diakonos/interaction-handler.rb +216 -0
  91. data/lib/diakonos/interaction.rb +52 -0
  92. data/lib/diakonos/key-map.rb +62 -0
  93. data/lib/diakonos/keying.rb +442 -0
  94. data/lib/diakonos/line-mover.rb +42 -0
  95. data/lib/diakonos/list.rb +59 -0
  96. data/lib/diakonos/logging.rb +27 -0
  97. data/lib/diakonos/mode.rb +17 -0
  98. data/lib/diakonos/mouse.rb +18 -0
  99. data/lib/diakonos/number-fitter.rb +11 -0
  100. data/lib/diakonos/range.rb +31 -0
  101. data/lib/diakonos/readline/functions.rb +82 -0
  102. data/lib/diakonos/readline.rb +222 -0
  103. data/lib/diakonos/search.rb +58 -0
  104. data/lib/diakonos/sessions.rb +257 -0
  105. data/lib/diakonos/sized-array.rb +48 -0
  106. data/lib/diakonos/text-mark.rb +19 -0
  107. data/lib/diakonos/vendor/fuzzy_file_finder.rb +365 -0
  108. data/lib/diakonos/version.rb +25 -0
  109. data/lib/diakonos/window.rb +43 -0
  110. data/lib/diakonos.rb +592 -0
  111. metadata +160 -68
@@ -0,0 +1,381 @@
1
+ module Diakonos
2
+
3
+ BOL_ZERO = 0
4
+ BOL_FIRST_CHAR = 1
5
+ BOL_ALT_ZERO = 2
6
+ BOL_ALT_FIRST_CHAR = 3
7
+
8
+ EOL_END = 0
9
+ EOL_LAST_CHAR = 1
10
+ EOL_ALT_END = 2
11
+ EOL_ALT_LAST_CHAR = 3
12
+
13
+ class Diakonos
14
+ attr_reader :token_regexps, :close_token_regexps, :token_formats, :diakonos_conf, :column_markers, :surround_pairs, :settings
15
+
16
+ def load_configuration
17
+ # Set defaults first
18
+
19
+ conf_dir = INSTALL_SETTINGS[ :conf_dir ]
20
+ @global_diakonos_conf = "#{conf_dir}/diakonos.conf"
21
+ if ! @testing
22
+ @diakonos_conf = @config_filename || "#{@diakonos_home}/diakonos.conf"
23
+
24
+ if ! FileTest.exists?( @diakonos_conf )
25
+ if FileTest.exists?( @global_diakonos_conf )
26
+ puts "No personal configuration file found."
27
+ puts "Would you like to copy the system-wide configuration file (#{@global_diakonos_conf}) to use"
28
+ $stdout.print "as a basis for your personal configuration (recommended)? (y/n)"; $stdout.flush
29
+ answer = $stdin.gets
30
+ if answer =~ /^y/i
31
+ FileUtils.cp @global_diakonos_conf, @diakonos_conf
32
+ end
33
+ else
34
+ if @testing
35
+ File.open( @diakonos_conf, 'w' ) do |f|
36
+ f.puts File.read( './diakonos.conf' )
37
+ end
38
+ else
39
+ puts "diakonos.conf not found in any of:"
40
+ puts " #{conf_dir}"
41
+ puts " #{@diakonos_home}"
42
+ puts "At least one configuration file must exist."
43
+ puts "You download one from https://git.sr.ht/~pistos/diakonos/blob/master/diakonos.conf"
44
+ end
45
+
46
+ if ! FileTest.exists?( @diakonos_conf )
47
+ puts "Terminating due to lack of configuration file."
48
+ exit 1
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ @logfilename = @diakonos_home + "/diakonos.log"
55
+ @modes = {
56
+ 'edit' => Mode.new,
57
+ 'input' => Mode.new,
58
+ }
59
+ @token_regexps = Hash.new { |h,k| h[ k ] = Hash.new }
60
+ @close_token_regexps = Hash.new { |h,k| h[ k ] = Hash.new }
61
+ @token_formats = Hash.new { |h,k| h[ k ] = Hash.new }
62
+ @column_markers = Hash.new { |h,k| h[ k ] = Hash.new }
63
+ @indenters = Hash.new
64
+ @indenters_next_line = Hash.new
65
+ @unindenters = Hash.new
66
+ @indent_triggers = Hash.new
67
+ @filemasks = Hash.new
68
+ @bangmasks = Hash.new
69
+ @closers = Hash.new
70
+ @surround_pairs = Hash.new { |h,k| h[ k ] = Hash.new}
71
+ @fuzzy_ignores = Array.new
72
+
73
+ @settings = Hash.new
74
+ @setting_strings = Hash.new
75
+ # Setup some defaults
76
+ @settings[ "context.format" ] = Curses::A_REVERSE
77
+ @settings['fuzzy_file_find.recursive'] = true
78
+
79
+ @modes[ 'edit' ].keymap[ Curses::KEY_RESIZE ] = [ "redraw", nil ]
80
+ @modes[ 'edit' ].keymap[ RESIZE2 ] = [ "redraw", nil ]
81
+
82
+ @colour_pairs = Array.new
83
+
84
+ @configs = Set.new
85
+ parse_configuration_file @global_diakonos_conf
86
+ parse_configuration_file @diakonos_conf if @diakonos_conf
87
+
88
+ languages = @surround_pairs.keys | @token_regexps.keys | @close_token_regexps.keys | @token_formats.keys
89
+
90
+ languages.each do |language|
91
+ @surround_pairs[ language ] = @surround_pairs[ 'all' ].merge( @surround_pairs[ language ] )
92
+ @token_regexps[ language ] = @token_regexps[ 'all' ].merge( @token_regexps[ language ] )
93
+ @close_token_regexps[ language ] = @close_token_regexps[ 'all' ].merge( @close_token_regexps[ language ] )
94
+ @token_formats[ language ] = @token_formats[ 'all' ].merge( @token_formats[ language ] )
95
+ end
96
+
97
+ merge_session_settings
98
+
99
+ case @settings[ 'clipboard.external' ]
100
+ when 'klipper', 'klipper-dcop'
101
+ @clipboard = ClipboardKlipper.new
102
+ when 'klipper-dbus'
103
+ @clipboard = ClipboardKlipperDBus.new
104
+ when 'xclip'
105
+ @clipboard = ClipboardXClip.new
106
+ when 'osx'
107
+ @clipboard = ClipboardOSX.new
108
+ else
109
+ @clipboard = Clipboard.new( @settings[ "max_clips" ] )
110
+ end
111
+ @log = File.open( @logfilename, "a" )
112
+
113
+ if @buffers
114
+ @buffers.each do |buffer|
115
+ buffer.configure
116
+ end
117
+ end
118
+ end
119
+
120
+ def get_token_regexp( hash, arg, match )
121
+ language = match[ 1 ]
122
+ token_class = match[ 2 ]
123
+ case_insensitive = ( match[ 3 ] != nil )
124
+ hash[ language ] = ( hash[ language ] || Hash.new )
125
+ if case_insensitive
126
+ hash[ language ][ token_class ] = Regexp.new( arg, Regexp::IGNORECASE )
127
+ else
128
+ hash[ language ][ token_class ] = Regexp.new arg
129
+ end
130
+ end
131
+
132
+ def map_key( arg, keymap = @modes['edit'].keymap )
133
+ return if arg.nil?
134
+
135
+ if / / === arg
136
+ keystrings, function_and_args = arg.split( / {2,}/, 2 )
137
+ else
138
+ keystrings, function_and_args = arg.split( /;/, 2 )
139
+ end
140
+
141
+ keystrokes = Array.new
142
+ keystrings.split( /\s+/ ).each do |ks_str|
143
+ codes = Keying.keycodes_for( ks_str )
144
+ if codes.empty?
145
+ puts "Unknown keystring: #{ks_str}"
146
+ else
147
+ keystrokes.concat codes
148
+ end
149
+ end
150
+
151
+ if function_and_args.nil?
152
+ keymap.delete_key_path( keystrokes )
153
+ else
154
+ function, function_args = function_and_args.split( /\s+/, 2 )
155
+ keymap.set_key_path(
156
+ keystrokes,
157
+ [ function, function_args ]
158
+ )
159
+ end
160
+ end
161
+
162
+ # @return [ConfigFile, ConfigFileUnreadable]
163
+ def legitimize_config_filename!(prospective_filename, including_config_file)
164
+ if File.exists?(prospective_filename)
165
+ ConfigFile.new(
166
+ File.realpath(prospective_filename),
167
+ including_config_file
168
+ )
169
+ else
170
+ ConfigFileUnreadable.new(prospective_filename, including_config_file)
171
+ end
172
+ end
173
+
174
+ # @param [String] filename the config file to parse
175
+ # @param [ConfigFile] including_config_file the config file which calls include on this one
176
+ def parse_configuration_file( filename, including_config_file = ConfigFileNull.new )
177
+ config_file = self.legitimize_config_filename!(filename, including_config_file)
178
+
179
+ @configs << config_file
180
+
181
+ config_file.each_line_with_index do |line, line_number|
182
+ if line =~ /^\s*(\S+)\s*=\s*(\S+)\s*$/
183
+ # Inheritance
184
+ command, arg = $1, @setting_strings[ $2 ]
185
+ end
186
+
187
+ if arg.nil?
188
+ command, arg = line.split( /\s+/, 2 )
189
+ next if command.nil?
190
+ if arg.nil?
191
+ config_file.problems << "error on line #{line_number+1}"
192
+ next
193
+ end
194
+ end
195
+
196
+ command = command.downcase
197
+
198
+ @setting_strings[ command ] = arg
199
+
200
+ case command
201
+ when "include"
202
+ self.parse_configuration_file( File.expand_path( arg ), config_file )
203
+ when 'load_extension'
204
+ @extensions.load( arg ).each do |conf_file|
205
+ parse_configuration_file conf_file
206
+ end
207
+ when /^lang\.(.+?)\.surround\.pair$/
208
+ language = $1
209
+
210
+ args = arg.split( /"\s+"/ )
211
+ args.map! do |s|
212
+ s.gsub( /(?<!\\)"/, '' ).gsub( /\\"/, '"' )
213
+ end
214
+
215
+ pair_key = args.shift
216
+
217
+ if pair_key =~ /^\/.+\/$/
218
+ pair_key = Regexp.new( pair_key[ 1..-2 ] )
219
+ else
220
+ pair_key = Regexp.new( "^#{Regexp.escape(pair_key)}$" )
221
+ end
222
+
223
+ pair_parens = args
224
+ @surround_pairs[ language ][ pair_key ] = pair_parens
225
+ when 'key'
226
+ map_key arg
227
+ when 'mkey'
228
+ mode, arg_ = arg.split( /\s+/, 2 )
229
+ map_key arg_, @modes[mode].keymap
230
+ when 'key.after'
231
+ function, args = arg.split( /\s+/, 2 )
232
+ map_key args, @modes['edit'].keymap_after[function]
233
+ when /^lang\.(.+?)\.tokens\.([^.]+)(\.case_insensitive)?$/, /^lang\.(.+?)\.tokens\.([^.]+)\.open(\.case_insensitive)?$/
234
+ get_token_regexp( @token_regexps, arg, Regexp.last_match )
235
+ when /^lang\.(.+?)\.tokens\.([^.]+)\.close(\.case_insensitive)?$/
236
+ get_token_regexp( @close_token_regexps, arg, Regexp.last_match )
237
+ when /^lang\.(.+?)\.tokens\.(.+?)\.format$/
238
+ language = $1
239
+ token_class = $2
240
+ @token_formats[ language ][ token_class ] = Display.to_formatting( arg )
241
+ when /^lang\.(.+?)\.format\..+$/
242
+ @settings[ command ] = Display.to_formatting( arg )
243
+ when /^colou?r$/
244
+ number, fg, bg = arg.split( /\s+/ )
245
+ number = number.to_i
246
+ fg = Display.to_colour_constant( fg )
247
+ bg = Display.to_colour_constant( bg )
248
+ @colour_pairs << {
249
+ :number => number,
250
+ :fg => fg,
251
+ :bg => bg
252
+ }
253
+ when /^lang\.(.+?)\.indent\.indenters(\.case_insensitive)?$/
254
+ case_insensitive = ( $2 != nil )
255
+ if case_insensitive
256
+ @indenters[ $1 ] = Regexp.new( arg, Regexp::IGNORECASE )
257
+ else
258
+ @indenters[ $1 ] = Regexp.new arg
259
+ end
260
+ when /^lang\.(.+?)\.indent\.indenters_next_line(\.case_insensitive)?$/
261
+ case_insensitive = ( $2 != nil )
262
+ if case_insensitive
263
+ @indenters_next_line[ $1 ] = Regexp.new( arg, Regexp::IGNORECASE )
264
+ else
265
+ @indenters_next_line[ $1 ] = Regexp.new arg
266
+ end
267
+ when /^lang\.(.+?)\.indent\.unindenters(\.case_insensitive)?$/
268
+ case_insensitive = ( $2 != nil )
269
+ if case_insensitive
270
+ @unindenters[ $1 ] = Regexp.new( arg, Regexp::IGNORECASE )
271
+ else
272
+ @unindenters[ $1 ] = Regexp.new arg
273
+ end
274
+ when /^lang\.(.+?)\.indent\.(?:preventers|ignore|not_indented)(\.case_insensitive)?$/,
275
+ /^lang\.(.+?)\.context\.ignore(\.case_insensitive)?$/
276
+ case_insensitive = ( $2 != nil )
277
+ if case_insensitive
278
+ @settings[ command ] = Regexp.new( arg, Regexp::IGNORECASE )
279
+ else
280
+ @settings[ command ] = Regexp.new arg
281
+ end
282
+ when /^lang\.(.+?)\.indent\.triggers(\.case_insensitive)?$/
283
+ case_insensitive = ( $2 != nil )
284
+ if case_insensitive
285
+ @indent_triggers[$1] = Regexp.new( arg, Regexp::IGNORECASE )
286
+ else
287
+ @indent_triggers[$1] = Regexp.new arg
288
+ end
289
+ when /^lang\.(.+?)\.filemask$/
290
+ @filemasks[ $1 ] = Regexp.new arg
291
+ when /^lang\.(.+?)\.bangmask$/
292
+ @bangmasks[ $1 ] = Regexp.new arg
293
+ when /^lang\.(.+?)\.closers\.(.+?)\.(.+?)$/
294
+ @closers[ $1 ] ||= Hash.new
295
+ @closers[ $1 ][ $2 ] ||= Hash.new
296
+ @closers[ $1 ][ $2 ][ $3.to_sym ] = case $3
297
+ when 'regexp'
298
+ Regexp.new arg
299
+ when 'closer'
300
+ begin
301
+ if arg =~ /^\{.+\}$/
302
+ eval( "Proc.new " + arg )
303
+ else
304
+ arg
305
+ end
306
+ rescue Exception => e
307
+ show_exception(
308
+ e,
309
+ [ "Failed to process Proc for #{command}.", ]
310
+ )
311
+ end
312
+ end
313
+ when "context.visible", "context.combined", "eof_newline", "view.nonfilelines.visible",
314
+ /^lang\.(.+?)\.indent\.(?:auto|roundup|using_tabs|closers)$/,
315
+ "found_cursor_start", "convert_tabs", 'delete_newline_on_delete_to_eol',
316
+ 'suppress_welcome', 'strip_trailing_whitespace_on_save', 'save_backup_files',
317
+ 'find.return_on_abort', 'fuzzy_file_find', 'fuzzy_file_find.recursive', 'view.line_numbers',
318
+ 'find.show_context_after', 'view.pairs.highlight', 'open_as_first_buffer', 'mouse'
319
+ @settings[ command ] = arg.to_b
320
+ when "context.format", "context.separator.format", "status.format", 'view.line_numbers.format',
321
+ 'view.non_search_area.format'
322
+ @settings[ command ] = Display.to_formatting( arg )
323
+ when /view\.column_markers\.(.+?)\.format/
324
+ @column_markers[ $1 ][ :format ] = Display.to_formatting( arg )
325
+ when "logfile"
326
+ @logfilename = File.expand_path( arg )
327
+ when "context.separator", /^lang\..+?\.indent\.ignore\.charset$/,
328
+ /^lang\.(.+?)\.tokens\.([^.]+)\.change_to$/,
329
+ /^lang\.(.+?)\.column_delimiters$/,
330
+ "view.nonfilelines.character",
331
+ 'diff_command', 'session.default_session',
332
+ 'clipboard.external'
333
+ @settings[ command ] = arg
334
+ when /^lang\..+?\.comment_(?:close_)?string$/, 'view.line_numbers.number_format',
335
+ "status.filler", "status.left", "status.right",
336
+ "status.modified_str", "status.unnamed_str", "status.selecting_str",
337
+ "status.read_only_str", 'interaction.blink_string'
338
+ @settings[ command ] = arg.gsub( /^["']|["']$/, '' )
339
+ when "status.vars"
340
+ @settings[ command ] = arg.split( /\s+/ )
341
+ when /^lang\.(.+?)\.indent\.size$/, /^lang\.(.+?)\.(?:tabsize|wrap_margin)$/,
342
+ "context.max_levels", "context.max_segment_width", "max_clips", "max_undo_lines",
343
+ "view.margin.x", "view.margin.y", "view.scroll_amount", "view.lookback", 'grep.context',
344
+ 'view.line_numbers.width', 'fuzzy_file_find.max_files', 'colour.background'
345
+ @settings[ command ] = arg.to_i
346
+ when "view.jump.x", "view.jump.y"
347
+ @settings[ command ] = [ arg.to_i, 1 ].max
348
+ when /view\.column_markers\.(.+?)\.column/
349
+ @column_markers[ $1 ][ :column ] = [ arg.to_i, 1 ].max
350
+ when "bol_behaviour", "bol_behavior"
351
+ case arg.downcase
352
+ when "zero"
353
+ @settings[ "bol_behaviour" ] = BOL_ZERO
354
+ when "first-char"
355
+ @settings[ "bol_behaviour" ] = BOL_FIRST_CHAR
356
+ when "alternating-zero"
357
+ @settings[ "bol_behaviour" ] = BOL_ALT_ZERO
358
+ else # default
359
+ @settings[ "bol_behaviour" ] = BOL_ALT_FIRST_CHAR
360
+ end
361
+ when "eol_behaviour", "eol_behavior"
362
+ case arg.downcase
363
+ when "end"
364
+ @settings[ "eol_behaviour" ] = EOL_END
365
+ when "last-char"
366
+ @settings[ "eol_behaviour" ] = EOL_LAST_CHAR
367
+ when "alternating-last-char"
368
+ @settings[ "eol_behaviour" ] = EOL_ALT_FIRST_CHAR
369
+ else # default
370
+ @settings[ "eol_behaviour" ] = EOL_ALT_END
371
+ end
372
+ when "context.delay", 'interaction.blink_duration', 'interaction.choice_delay'
373
+ @settings[ command ] = arg.to_f
374
+ when 'fuzzy_file_find.ignore'
375
+ @fuzzy_ignores << arg
376
+ end
377
+ end
378
+ end
379
+
380
+ end
381
+ end
@@ -0,0 +1,15 @@
1
+ module Enumerable
2
+ # Returns [array-index, string-index, string-index] triples for each match.
3
+ def grep_indices( regexp )
4
+ array = Array.new
5
+ each_with_index do |element,index|
6
+ element.scan( regexp ) do |match_text|
7
+ match = Regexp.last_match
8
+ strindex = match.begin( 0 )
9
+ array.push [ index, strindex, strindex + match[ 0 ].length ]
10
+ end
11
+ end
12
+ array
13
+ end
14
+ end
15
+
@@ -0,0 +1,60 @@
1
+ class Hash
2
+ # path is an array of hash keys
3
+ # This method deletes a path of hash keys, with each step in the path
4
+ # being a recursively deeper key in a hash tree.
5
+ # Returns the possibly modified hash.
6
+ def delete_key_path( path )
7
+ if path.length > 1
8
+ subtree = self[ path[ 0 ] ]
9
+ if subtree.respond_to?( :delete_key_path )
10
+ subtree.delete_key_path( path[ 1..-1 ] )
11
+ if subtree.empty?
12
+ delete( path[ 0 ] )
13
+ end
14
+ end
15
+ elsif path.length == 1
16
+ delete( path[ 0 ] )
17
+ end
18
+
19
+ self
20
+ end
21
+
22
+ def set_key_path( path, leaf )
23
+ if path.length > 1
24
+ node = self[ path[ 0 ] ]
25
+ if ! node.respond_to?( :set_key_path )
26
+ node = self[ path[ 0 ] ] = Hash.new
27
+ end
28
+ node.set_key_path( path[ 1..-1 ], leaf )
29
+ elsif path.length == 1
30
+ self[ path[ 0 ] ] = leaf
31
+ end
32
+
33
+ self
34
+ end
35
+
36
+ def get_node( path )
37
+ node = self[ path[ 0 ] ]
38
+ if path.length > 1
39
+ if node && node.respond_to?( :get_node )
40
+ return node.get_node( path[ 1..-1 ] )
41
+ end
42
+ elsif path.length == 1
43
+ return node
44
+ end
45
+
46
+ nil
47
+ end
48
+
49
+ def get_leaf( path )
50
+ node = get_node( path )
51
+ if node.respond_to?( :get_node )
52
+ # Only want a leaf node
53
+ nil
54
+ else
55
+ node
56
+ end
57
+ end
58
+
59
+ end
60
+
@@ -0,0 +1,6 @@
1
+ class Object
2
+ def deep_clone
3
+ Marshal::load( Marshal.dump( self ) )
4
+ end
5
+ end
6
+
@@ -0,0 +1,6 @@
1
+ class Regexp
2
+ def uses_bos
3
+ source[ 0 ] == ?^
4
+ end
5
+ end
6
+
@@ -0,0 +1,122 @@
1
+ class String
2
+
3
+ def to_b
4
+ case downcase
5
+ when "true", "t", "1", "yes", "y", "on", "+"
6
+ true
7
+ else
8
+ false
9
+ end
10
+ end
11
+
12
+ def expand_tabs( tab_size )
13
+ s = dup
14
+ while s.sub!( /\t/ ) { |match_text|
15
+ match = Regexp.last_match
16
+ index = match.begin( 0 )
17
+ " " * ( tab_size - ( index % tab_size ) )
18
+ }
19
+ end
20
+ s
21
+ end
22
+
23
+ def newline_split
24
+ retval = split( /\\n/ )
25
+ if self =~ /\\n$/
26
+ retval << ""
27
+ end
28
+ if retval.length > 1
29
+ retval[ 0 ] << "$"
30
+ retval[ 1..-2 ].collect do |el|
31
+ "^" << el << "$"
32
+ end
33
+ retval[ -1 ] = "^" << retval[ -1 ]
34
+ end
35
+ retval
36
+ end
37
+
38
+ # Works like normal String#index except returns the index
39
+ # of the first matching regexp group if one or more groups are specified
40
+ # in the regexp. Both the index and the matched text are returned.
41
+ def group_index( regexp, offset = 0 )
42
+ if regexp.class != Regexp
43
+ return index( regexp, offset )
44
+ end
45
+
46
+ i = nil
47
+ match_text = nil
48
+ working_offset = 0
49
+ loop do
50
+ index( regexp, working_offset )
51
+ match = Regexp.last_match
52
+ if match
53
+ i = match.begin( 0 )
54
+ match_text = match[ 0 ]
55
+ if match.length > 1
56
+ # Find first matching group
57
+ 1.upto( match.length - 1 ) do |match_item_index|
58
+ if match[ match_item_index ]
59
+ i = match.begin( match_item_index )
60
+ match_text = match[ match_item_index ]
61
+ break
62
+ end
63
+ end
64
+ end
65
+
66
+ break if i >= offset
67
+ else
68
+ i = nil
69
+ break
70
+ end
71
+ working_offset += 1
72
+ end
73
+
74
+ [ i, match_text ]
75
+ end
76
+
77
+ # Works like normal String#rindex except returns the index
78
+ # of the first matching regexp group if one or more groups are specified
79
+ # in the regexp. Both the index and the matched text are returned.
80
+ def group_rindex( regexp, offset = length )
81
+ if regexp.class != Regexp
82
+ return rindex( regexp, offset )
83
+ end
84
+
85
+ i = nil
86
+ match_text = nil
87
+ working_offset = length
88
+ loop do
89
+ rindex( regexp, working_offset )
90
+ match = Regexp.last_match
91
+ if match
92
+ i = match.end( 0 ) - 1
93
+ match_text = match[ 0 ]
94
+ if match.length > 1
95
+ # Find first matching group
96
+ 1.upto( match.length - 1 ) do |match_item_index|
97
+ if match[ match_item_index ]
98
+ i = match.end( match_item_index ) - 1
99
+ match_text = match[ match_item_index ]
100
+ break
101
+ end
102
+ end
103
+ end
104
+
105
+ if match_text == ""
106
+ # Assume that an empty string means that it matched $
107
+ i += 1
108
+ end
109
+
110
+ break if i <= offset
111
+ else
112
+ i = nil
113
+ break
114
+ end
115
+ working_offset -= 1
116
+ end
117
+
118
+ [ i, match_text ]
119
+ end
120
+
121
+ end
122
+
@@ -0,0 +1,28 @@
1
+ module Diakonos
2
+
3
+ class CTag
4
+ attr_reader :file, :command, :kind, :rest
5
+
6
+ def initialize( file, command, kind, rest )
7
+ @file = file
8
+ @command = command
9
+ @kind = kind
10
+ @rest = rest
11
+ end
12
+
13
+ def to_s
14
+ "#{@file}:#{@command} (#{@kind}) #{@rest}"
15
+ end
16
+
17
+ def == ( other )
18
+ (
19
+ other &&
20
+ @file == other.file &&
21
+ @command == other.command &&
22
+ @kind == other.kind &&
23
+ @rest == other.rest
24
+ )
25
+ end
26
+ end
27
+
28
+ end
@@ -0,0 +1,27 @@
1
+ module Diakonos
2
+ class Diakonos
3
+
4
+ def cursor_stack_remove_buffer( buffer )
5
+ @cursor_stack.delete_if { |frame|
6
+ frame[ :buffer ] == buffer
7
+ }
8
+ end
9
+
10
+ def push_cursor_state( top_line, row, col, clear_stack_pointer = CLEAR_STACK_POINTER )
11
+ new_state = {
12
+ buffer: buffer_current,
13
+ top_line: top_line,
14
+ row: row,
15
+ col: col
16
+ }
17
+ if ! @cursor_stack.include? new_state
18
+ @cursor_stack << new_state
19
+ if clear_stack_pointer
20
+ @cursor_stack_pointer = nil
21
+ end
22
+ clear_non_movement_flag
23
+ end
24
+ end
25
+
26
+ end
27
+ end