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,698 @@
1
+ module Diakonos
2
+
3
+ class Buffer
4
+ attr_reader :name, :original_language, :language, :changing_selection, :read_only,
5
+ :tab_size, :selection_mode
6
+ attr_writer :desired_column, :read_only
7
+ attr_accessor :lines
8
+
9
+ TYPING = true
10
+ STOPPED_TYPING = true
11
+ STILL_TYPING = false
12
+ NO_SNAPSHOT = true
13
+ DO_DISPLAY = true
14
+ DONT_DISPLAY = false
15
+ READ_ONLY = true
16
+ READ_WRITE = false
17
+ ROUND_DOWN = false
18
+ ROUND_UP = true
19
+ PAD_END = true
20
+ DONT_PAD_END = false
21
+ MATCH_CLOSE = true
22
+ MATCH_ANY = false
23
+ START_FROM_BEGINNING = -1
24
+ DO_PITCH_CURSOR = true
25
+ DONT_PITCH_CURSOR = false
26
+ STRIP_LINE = true
27
+ DONT_STRIP_LINE = false
28
+ USE_INDENT_IGNORE = true
29
+ DONT_USE_INDENT_IGNORE = false
30
+ WORD_REGEXP = /\w+/
31
+
32
+ # Set name to nil to create a buffer that is not associated with a file.
33
+ # @param [Hash] options
34
+ # @option options [String] 'filepath'
35
+ # A file path (which is expanded internally)
36
+ # @option options [Boolean] 'read_only' (READ_WRITE)
37
+ # Whether the buffer should be protected from modification
38
+ # @option options [Hash] 'cursor'
39
+ # A Hash containing 'row' and/or 'col' indicating where the cursor should
40
+ # initially be placed. Defaults: 0 and 0
41
+ # @option options [Hash] 'display'
42
+ # A Hash containing 'top_line' and 'left_column' indicating where the view
43
+ # should be positioned in the file. Defaults: 0 and 0
44
+ # @see READ_WRITE
45
+ # @see READ_ONLY
46
+ def initialize( options = {} )
47
+ @name = options[ 'filepath' ]
48
+ @modified = false
49
+ @last_modification_check = Time.now
50
+
51
+ @buffer_states = Array.new
52
+ @cursor_states = Array.new
53
+ if @name.nil?
54
+ @lines = Array.new
55
+ @lines[ 0 ] = ""
56
+ else
57
+ @name = File.expand_path( @name )
58
+ if FileTest.exists? @name
59
+ @lines = IO.readlines( @name )
60
+ if ( @lines.length == 0 ) || ( @lines[ -1 ][ -1..-1 ] == "\n" )
61
+ @lines.push ""
62
+ end
63
+ @lines = @lines.collect do |line|
64
+ line.chomp
65
+ end
66
+ else
67
+ @lines = Array.new
68
+ @lines[ 0 ] = ""
69
+ end
70
+ end
71
+
72
+ @current_buffer_state = 0
73
+
74
+ options[ 'display' ] ||= Hash.new
75
+ @top_line = options[ 'display' ][ 'top_line' ] || 0
76
+ @left_column = options[ 'display' ][ 'left_column' ] || 0
77
+ @desired_column = @left_column
78
+ @mark_anchor = nil
79
+ @text_marks = Hash.new
80
+ @selection_mode = :normal
81
+ @last_search_regexps = Array( options['last_search_regexps'] ).map { |r| Regexp.new(r) }
82
+ @highlight_regexp = nil
83
+ @last_search = nil
84
+ @changing_selection = false
85
+ @typing = false
86
+ options[ 'cursor' ] ||= Hash.new
87
+ @last_col = options[ 'cursor' ][ 'col' ] || 0
88
+ @last_row = options[ 'cursor' ][ 'row' ] || 0
89
+ @last_screen_y = @last_row - @top_line
90
+ @last_screen_x = @last_col - @left_column
91
+ @last_screen_col = @last_screen_x
92
+ @read_only = options[ 'read_only' ] || READ_WRITE
93
+ @bookmarks = Array.new
94
+ @lang_stack = Array.new
95
+
96
+ configure
97
+
98
+ if @settings[ "convert_tabs" ]
99
+ tabs_subbed = false
100
+ @lines.collect! do |line|
101
+ new_line = line.expand_tabs( @tab_size )
102
+ tabs_subbed = ( tabs_subbed || new_line != line )
103
+ # Return value for collect:
104
+ new_line
105
+ end
106
+ @modified = ( @modified || tabs_subbed )
107
+ if tabs_subbed
108
+ $diakonos.set_iline "(spaces substituted for tab characters)"
109
+ end
110
+ end
111
+
112
+ @buffer_states[ @current_buffer_state ] = @lines
113
+ @cursor_states[ @current_buffer_state ] = [ @last_row, @last_col ]
114
+ end
115
+
116
+ def configure(
117
+ language = (
118
+ $diakonos.get_language_from_shabang( @lines[ 0 ] ) ||
119
+ $diakonos.get_language_from_name( @name ) ||
120
+ LANG_TEXT
121
+ )
122
+ )
123
+ reset_display
124
+ set_language language
125
+ @original_language = @language
126
+ end
127
+
128
+ def reset_display
129
+ @win_main = $diakonos.win_main
130
+ @win_line_numbers = $diakonos.win_line_numbers
131
+ end
132
+
133
+ def set_language( language )
134
+ @settings = $diakonos.settings
135
+ @language = language
136
+ @surround_pairs = $diakonos.surround_pairs[ @language ]
137
+ @token_regexps = $diakonos.token_regexps[ @language ]
138
+ @close_token_regexps = $diakonos.close_token_regexps[ @language ]
139
+ @token_formats = $diakonos.token_formats[ @language ]
140
+ @indenters = $diakonos.indenters[ @language ]
141
+ @indenters_next_line = $diakonos.indenters_next_line[ @language ]
142
+ @unindenters = $diakonos.unindenters[ @language ]
143
+ @preventers = @settings[ "lang.#{@language}.indent.preventers" ]
144
+ @closers = $diakonos.closers[ @language ] || Hash.new
145
+ @auto_indent = @settings[ "lang.#{@language}.indent.auto" ]
146
+ @indent_size = ( @settings[ "lang.#{@language}.indent.size" ] || 4 )
147
+ @indent_roundup = @settings[ "lang.#{@language}.indent.roundup" ].nil? ? true : @settings[ "lang.#{@language}.indent.roundup" ]
148
+ @indent_closers = @settings[ "lang.#{@language}.indent.closers" ].nil? ? false : @settings[ "lang.#{@language}.indent.closers" ]
149
+ @default_formatting = ( @settings[ "lang.#{@language}.format.default" ] || Curses::A_NORMAL )
150
+ @selection_formatting = ( @settings[ "lang.#{@language}.format.selection" ] || Curses::A_REVERSE )
151
+ @indent_ignore_charset = ( @settings[ "lang.#{@language}.indent.ignore.charset" ] || "" )
152
+ @tab_size = ( @settings[ "lang.#{@language}.tabsize" ] || DEFAULT_TAB_SIZE )
153
+ end
154
+
155
+ def [] ( arg )
156
+ @lines[ arg ]
157
+ end
158
+
159
+ def == (other)
160
+ return false if other.nil?
161
+ @name == other.name
162
+ end
163
+
164
+ def length
165
+ @lines.length
166
+ end
167
+
168
+ def to_a
169
+ @lines.dup
170
+ end
171
+
172
+ def modified?
173
+ @modified
174
+ end
175
+
176
+ def nice_name
177
+ @name || @settings[ "status.unnamed_str" ]
178
+ end
179
+
180
+ def replace_char( c )
181
+ row = @last_row
182
+ col = @last_col
183
+ take_snapshot TYPING
184
+ @lines[ row ][ col ] = c
185
+ set_modified
186
+ end
187
+
188
+ def insert_char( c )
189
+ row = @last_row
190
+ col = @last_col
191
+ take_snapshot( TYPING )
192
+ line = @lines[ row ]
193
+ @lines[ row ] = line[ 0...col ] + c.chr + line[ col..-1 ]
194
+ set_modified
195
+ end
196
+
197
+ def insert_string( str )
198
+ row = @last_row
199
+ col = @last_col
200
+ take_snapshot( TYPING )
201
+ line = @lines[ row ]
202
+ @lines[ row ] = line[ 0...col ] + str + line[ col..-1 ]
203
+ set_modified
204
+ end
205
+
206
+ def surround( text, parenthesis )
207
+ pattern, pair = @surround_pairs.select { |r, p| parenthesis =~ r }.to_a[ 0 ]
208
+
209
+ if pair.nil?
210
+ $diakonos.set_iline "No matching parentheses pair found."
211
+ nil
212
+ else
213
+ pair.map! do |paren|
214
+ parenthesis.gsub( pattern, paren )
215
+ end
216
+ pair[ 0 ] + text.join( "\n" ) + pair[ 1 ]
217
+ end
218
+ end
219
+
220
+ def join_lines_upward( row = @last_row, strip = DONT_STRIP_LINE )
221
+ return false if row == 0
222
+
223
+ take_snapshot
224
+
225
+ line = @lines.delete_at( row )
226
+ old_line = @lines[ row-1 ]
227
+
228
+ new_x_pos = old_line.length
229
+
230
+ if strip
231
+ line.strip!
232
+
233
+ # Only prepend a space if the line above isn't empty.
234
+ if ! old_line.strip.empty?
235
+ line = ' ' + line
236
+ new_x_pos += 1
237
+ end
238
+ end
239
+
240
+ @lines[ row-1 ] << line
241
+
242
+ cursor_to( row-1, new_x_pos )
243
+
244
+ set_modified
245
+ end
246
+
247
+ def join_lines( row = @last_row, strip = DONT_STRIP_LINE )
248
+ take_snapshot( TYPING )
249
+ next_line = @lines.delete_at( row + 1 )
250
+ return false if next_line.nil?
251
+
252
+ if strip
253
+ next_line = ' ' + next_line.strip
254
+ end
255
+ @lines[ row ] << next_line
256
+ set_modified
257
+ end
258
+
259
+ def close_code
260
+ line = @lines[ @last_row ]
261
+ @closers.each_value do |h|
262
+ h[ :regexp ] =~ line
263
+ lm = Regexp.last_match
264
+ if lm
265
+ str = case h[ :closer ]
266
+ when String
267
+ if lm[ 1 ].nil?
268
+ h[ :closer ]
269
+ else
270
+ lm[ 1 ].gsub(
271
+ Regexp.new( "(#{ Regexp.escape( lm[1] ) })" ),
272
+ h[ :closer ]
273
+ )
274
+ end
275
+ when Proc
276
+ h[ :closer ].call( lm ).to_s
277
+ end
278
+ r, c = @last_row, @last_col
279
+ paste str, !TYPING, @indent_closers
280
+ cursor_to r, c
281
+ if /%_/ === str
282
+ find [/%_/], direction: :down, replacement: '', auto_choice: CHOICE_YES_AND_STOP
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ def collapse_whitespace
289
+ if selection_mark
290
+ remove_selection DONT_DISPLAY
291
+ end
292
+
293
+ line = @lines[ @last_row ]
294
+ head = line[ 0...@last_col ]
295
+ tail = line[ @last_col..-1 ]
296
+ new_head = head.sub( /\s+$/, '' )
297
+ new_line = new_head + tail.sub( /^\s+/, ' ' )
298
+ if new_line != line
299
+ take_snapshot( TYPING )
300
+ @lines[ @last_row ] = new_line
301
+ cursor_to( @last_row, @last_col - ( head.length - new_head.length ) )
302
+ set_modified
303
+ end
304
+ end
305
+
306
+ def columnize( delimiter = /=>?|:|,/, num_spaces_padding = 1 )
307
+ take_snapshot
308
+
309
+ lines = self.selected_lines
310
+ column_width = 0
311
+ lines.each do |line|
312
+ pos = ( line =~ delimiter )
313
+ if pos
314
+ column_width = [ pos, column_width ].max
315
+ end
316
+ end
317
+
318
+ padding = ' ' * num_spaces_padding
319
+ one_modified = false
320
+
321
+ lines.each do |line|
322
+ old_line = line.dup
323
+ if line =~ /^(.+?)(#{delimiter.source})(.*)$/
324
+ pre = $1
325
+ del = $2
326
+ post = $3
327
+ if pre !~ /\s$/
328
+ del = " #{del}"
329
+ end
330
+ if post !~ /^\s/
331
+ del = "#{del} "
332
+ end
333
+ del.sub!( /^\s+/, ' ' * num_spaces_padding )
334
+ del.sub!( /\s+$/, ' ' * num_spaces_padding )
335
+ line.replace( ( "%-#{column_width}s" % pre ) + del + post )
336
+ end
337
+ one_modified ||= ( line != old_line )
338
+ end
339
+
340
+ if one_modified
341
+ set_modified
342
+ end
343
+ end
344
+
345
+ def comment_out
346
+ take_snapshot
347
+
348
+ one_modified = false
349
+ closer = @settings[ "lang.#{@language}.comment_close_string" ].to_s
350
+
351
+ self.selected_lines.each do |line|
352
+ next if line.strip.empty?
353
+ old_line = line.dup
354
+ line.gsub!( /^(\s*)/, "\\1" + @settings[ "lang.#{@language}.comment_string" ].to_s )
355
+ if ! closer.empty? && line !~ /#{Regexp.escape(closer)}$/
356
+ line << closer
357
+ end
358
+ one_modified ||= ( line != old_line )
359
+ end
360
+
361
+ if one_modified
362
+ set_modified
363
+ end
364
+ end
365
+
366
+ def uncomment
367
+ take_snapshot
368
+ comment_string = Regexp.escape( @settings[ "lang.#{@language}.comment_string" ].to_s )
369
+ comment_close_string = Regexp.escape( @settings[ "lang.#{@language}.comment_close_string" ].to_s )
370
+ one_modified = false
371
+ self.selected_lines.each do |line|
372
+ old_line = line.dup
373
+ comment_regexp = /^(\s*)#{comment_string}/
374
+ line.gsub!( comment_regexp, "\\1" )
375
+ if line !~ comment_regexp
376
+ line.gsub!( /#{comment_close_string}$/, '' )
377
+ end
378
+ one_modified ||= ( line != old_line )
379
+ end
380
+ if one_modified
381
+ set_modified
382
+ end
383
+ end
384
+
385
+ def carriage_return
386
+ take_snapshot
387
+ row = @last_row
388
+ col = @last_col
389
+ @lines = @lines[ 0...row ] +
390
+ [ @lines[ row ][ 0...col ] ] +
391
+ [ @lines[ row ][ col..-1 ] ] +
392
+ @lines[ (row+1)..-1 ]
393
+ cursor_to( row + 1, 0 )
394
+ if @auto_indent
395
+ parsed_indent undoable: false
396
+ end
397
+ set_modified
398
+ end
399
+
400
+ def line_at( y )
401
+ row = @top_line + y
402
+ if row < 0
403
+ nil
404
+ else
405
+ @lines[ row ]
406
+ end
407
+ end
408
+
409
+ def current_line
410
+ @lines[ @last_row ]
411
+ end
412
+
413
+ # Returns true iff the given column, x, is less than the length of the given line, y.
414
+ def in_line( x, y )
415
+ x + @left_column < line_at( y ).length
416
+ end
417
+
418
+ # Translates the window column, x, to a buffer-relative column index.
419
+ def column_of( x )
420
+ @left_column + x
421
+ end
422
+
423
+ # Translates the window row, y, to a buffer-relative row index.
424
+ def row_of( y )
425
+ @top_line + y
426
+ end
427
+
428
+ # Returns nil if the row is off-screen.
429
+ def row_to_y( row )
430
+ return nil if row.nil?
431
+ y = row - @top_line
432
+ y = nil if ( y < 0 ) || ( y > @top_line + $diakonos.main_window_height - 1 )
433
+ y
434
+ end
435
+
436
+ # Returns nil if the column is off-screen.
437
+ def column_to_x( col )
438
+ return nil if col.nil?
439
+ x = col - @left_column
440
+ x = nil if ( x < 0 ) || ( x > @left_column + Curses::cols - 1 )
441
+ x
442
+ end
443
+
444
+ def current_row
445
+ @last_row
446
+ end
447
+
448
+ def current_column
449
+ @last_col
450
+ end
451
+
452
+ def pan_view_to( left_column, do_display = DO_DISPLAY )
453
+ @left_column = [ left_column, 0 ].max
454
+ record_mark_start_and_end
455
+ display if do_display
456
+ end
457
+
458
+ # Returns the amount the view was actually panned.
459
+ def pan_view( x = 1, do_display = DO_DISPLAY )
460
+ old_left_column = @left_column
461
+ pan_view_to( @left_column + x, do_display )
462
+ @left_column - old_left_column
463
+ end
464
+
465
+ def pitch_view_to( new_top_line, do_pitch_cursor = DONT_PITCH_CURSOR, do_display = DO_DISPLAY )
466
+ old_top_line = @top_line
467
+
468
+ if new_top_line < 0
469
+ @top_line = 0
470
+ elsif new_top_line + $diakonos.main_window_height > @lines.length
471
+ @top_line = [ @lines.length - $diakonos.main_window_height, 0 ].max
472
+ else
473
+ @top_line = new_top_line
474
+ end
475
+
476
+ old_row = @last_row
477
+ old_col = @last_col
478
+
479
+ changed = ( @top_line - old_top_line )
480
+ if changed != 0 && do_pitch_cursor
481
+ @last_row += changed
482
+ end
483
+
484
+ height = [ $diakonos.main_window_height, @lines.length ].min
485
+
486
+ @last_row = NumberFitter.fit(
487
+ number: @last_row,
488
+ min: @top_line,
489
+ max: @top_line + height - 1,
490
+ )
491
+
492
+ if @last_row - @top_line < @settings["view.margin.y"]
493
+ @last_row = NumberFitter.fit(
494
+ number: @top_line + @settings["view.margin.y"],
495
+ min: @top_line,
496
+ max: @top_line + height - 1,
497
+ )
498
+ elsif @top_line + height - 1 - @last_row < @settings[ "view.margin.y" ]
499
+ @last_row = NumberFitter.fit(
500
+ number: @top_line + height - 1 - @settings[ "view.margin.y" ],
501
+ min: @top_line,
502
+ max: @top_line + height - 1,
503
+ )
504
+ end
505
+
506
+ @last_col = NumberFitter.fit(
507
+ number: @last_col,
508
+ min: @left_column,
509
+ max: [
510
+ @left_column + Curses::cols - 1,
511
+ @lines[@last_row].length
512
+ ].min
513
+ )
514
+
515
+ @last_screen_y = @last_row - @top_line
516
+ @last_screen_x = tab_expanded_column( @last_col, @last_row ) - @left_column
517
+
518
+ record_mark_start_and_end
519
+
520
+ if changed != 0
521
+ if ! @auto_anchored && ! @changing_selection && selecting?
522
+ remove_selection DONT_DISPLAY
523
+ end
524
+
525
+ highlight_matches
526
+ if $diakonos.there_was_non_movement
527
+ $diakonos.push_cursor_state( old_top_line, old_row, old_col )
528
+ end
529
+ end
530
+
531
+ display if do_display
532
+
533
+ changed
534
+ end
535
+
536
+ # Returns the amount the view was actually pitched.
537
+ def pitch_view( y = 1, do_pitch_cursor = DONT_PITCH_CURSOR, do_display = DO_DISPLAY )
538
+ pitch_view_to( @top_line + y, do_pitch_cursor, do_display )
539
+ end
540
+
541
+ def wrap_paragraph
542
+ start_row = end_row = cursor_row = @last_row
543
+ cursor_col = @last_col
544
+ until start_row == 0 || @lines[ start_row - 1 ].strip == ''
545
+ start_row -= 1
546
+ end
547
+ until end_row == @lines.size || @lines[ end_row ].strip == ''
548
+ end_row += 1
549
+ end
550
+
551
+ lines = []
552
+ line = ''
553
+ words = @lines[ start_row...end_row ].join( ' ' ).scan( /\S+/ )
554
+ words.each do |word|
555
+ if word =~ /^[a-z']+[.!?]$/
556
+ word = "#{word} "
557
+ end
558
+ if line.length + word.length + 1 > ( @settings[ "lang.#{@language}.wrap_margin" ] || 80 )
559
+ lines << line.strip
560
+ line = ''
561
+ end
562
+ line << " #{word}"
563
+ end
564
+ line.strip!
565
+ if ! line.empty?
566
+ lines << line
567
+ end
568
+ if @lines[ start_row...end_row ] != lines
569
+ take_snapshot
570
+ @lines[ start_row...end_row ] = lines
571
+ set_modified
572
+ cursor_to start_row + lines.length, lines[-1].length
573
+ end
574
+ end
575
+
576
+ def context
577
+ retval = Array.new
578
+ row = @last_row
579
+ clevel = indentation_level( row )
580
+ while row > 0 && clevel < 0
581
+ row = row - 1
582
+ clevel = indentation_level( row )
583
+ end
584
+ clevel = 0 if clevel < 0
585
+ while row > 0
586
+ row = row - 1
587
+ line = @lines[ row ]
588
+ if ! line.strip.empty? && ( line !~ @settings[ "lang.#{@language}.context.ignore" ] )
589
+ level = indentation_level( row )
590
+ if level < clevel && level > -1
591
+ retval.unshift line
592
+ clevel = level
593
+ break if clevel == 0
594
+ end
595
+ end
596
+ end
597
+ retval
598
+ end
599
+
600
+ def set_type( type )
601
+ return false if type.nil?
602
+ configure( type )
603
+ display
604
+ true
605
+ end
606
+
607
+ def word_under_cursor
608
+ pos = word_under_cursor_pos
609
+ return if pos.nil?
610
+
611
+ col1 = pos[ 0 ][ 1 ]
612
+ col2 = pos[ 1 ][ 1 ]
613
+ @lines[ @last_row ][ col1...col2 ]
614
+ end
615
+
616
+ def word_under_cursor_pos( options = {} )
617
+ or_after = options[:or_after]
618
+ @lines[ @last_row ].scan( WORD_REGEXP ) do |match_text|
619
+ last_match = Regexp.last_match
620
+ if (
621
+ last_match.begin( 0 ) <= @last_col && @last_col < last_match.end( 0 ) ||
622
+ or_after && last_match.begin(0) > @last_col
623
+ )
624
+ return [
625
+ [ @last_row, last_match.begin( 0 ) ],
626
+ [ @last_row, last_match.end( 0 ) ],
627
+ ]
628
+ end
629
+ end
630
+
631
+ nil
632
+ end
633
+
634
+ def word_before_cursor
635
+ word = nil
636
+
637
+ @lines[ @last_row ].scan( WORD_REGEXP ) do |match_text|
638
+ last_match = Regexp.last_match
639
+ if last_match.begin( 0 ) <= @last_col && @last_col <= last_match.end( 0 )
640
+ word = match_text
641
+ break
642
+ end
643
+ end
644
+
645
+ word
646
+ end
647
+ # TODO word_before_cursor_pos
648
+
649
+ # Returns an array of lines of the current paragraph.
650
+ def paragraph_under_cursor
651
+ ( first, _ ), ( last, _ ) = paragraph_under_cursor_pos
652
+ @lines[ first..last ]
653
+ end
654
+
655
+ # Returns the coordinates of the first and last line of the current
656
+ # paragraph.
657
+ def paragraph_under_cursor_pos
658
+ if @lines[ @last_row ] =~ /^\s*$/
659
+ return [
660
+ [ @last_row, 0 ],
661
+ [ @last_row, @lines[ @last_row ].length - 1 ]
662
+ ]
663
+ end
664
+
665
+ upper_boundary = 0
666
+ lower_boundary = @lines.size - 1
667
+
668
+ @last_row.downto( 0 ) do |i|
669
+ line = @lines[ i ]
670
+ if line =~ /^\s*$/
671
+ upper_boundary = i + 1
672
+ break
673
+ end
674
+ end
675
+
676
+ @last_row.upto( @lines.size - 1 ) do |i|
677
+ line = @lines[ i ]
678
+ if line =~ /^\s*$/
679
+ lower_boundary = i - 1
680
+ break
681
+ end
682
+ end
683
+
684
+ [
685
+ [ upper_boundary, 0 ],
686
+ [ lower_boundary, @lines[ lower_boundary ].length - 1 ]
687
+ ]
688
+ end
689
+ # TODO paragraph_before_cursor(_pos)?
690
+
691
+ def words( filter_regexp = nil )
692
+ w = @lines.join( ' ' ).scan( WORD_REGEXP )
693
+ filter_regexp ? w.grep( filter_regexp ) : w
694
+ end
695
+
696
+ end
697
+
698
+ end