diakonos 0.8.11 → 0.9.9

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 +439 -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 +70 -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 +362 -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 +382 -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 +491 -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 +223 -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 +386 -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,362 @@
1
+ module Diakonos
2
+
3
+ class Buffer
4
+
5
+ attr_reader :top_line, :left_column
6
+
7
+ def find_opening_match( line, match_close = true, bos_allowed = true )
8
+ open_index = line.length
9
+ open_token_class = nil
10
+ open_match_text = nil
11
+ match = nil
12
+ match_text = nil
13
+ @token_regexps.each do |token_class,regexp|
14
+ if match = regexp.match( line )
15
+ if match.length > 1
16
+ index = match.begin 1
17
+ match_text = match[ 1 ]
18
+ whole_match_index = match.begin 0
19
+ else
20
+ whole_match_index = index = match.begin( 0 )
21
+ match_text = match[ 0 ]
22
+ end
23
+ if ( ! regexp.uses_bos ) || ( bos_allowed && ( whole_match_index == 0 ) )
24
+ if index < open_index
25
+ if ( ( ! match_close ) || @close_token_regexps[ token_class ] )
26
+ open_index = index
27
+ open_token_class = token_class
28
+ open_match_text = match_text
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ if open_match_text
36
+ # $diakonos.debug_log "OPEN\t" + [ open_index, open_token_class, open_match_text ].inspect
37
+ end
38
+ [ open_index, open_token_class, open_match_text ]
39
+ end
40
+
41
+ private def find_closing_match(line_segment, regexp, bos_allowed = true)
42
+ close_match_text = nil
43
+ close_index = nil
44
+
45
+ line_segment.scan(regexp) do |m|
46
+ match = Regexp.last_match
47
+ if match.length > 1
48
+ index = match.begin 1
49
+ match_text = match[1]
50
+ else
51
+ index = match.begin 0
52
+ match_text = match[0]
53
+ end
54
+ if ( ! regexp.uses_bos ) || ( bos_allowed && ( index == 0 ) )
55
+ close_index = index
56
+ close_match_text = match_text
57
+ break
58
+ end
59
+ end
60
+
61
+ if close_match_text
62
+ # $diakonos.debug_log "CLOSE\t" + [close_index, close_match_text].inspect
63
+ end
64
+ [close_index, close_match_text]
65
+ end
66
+
67
+ # Prints text to the screen, truncating where necessary.
68
+ # Returns nil if the string is completely off-screen.
69
+ # write_cursor_col is buffer-relative, not screen-relative
70
+ def truncate_off_screen( string, write_cursor_col )
71
+ retval = string
72
+
73
+ # Truncate based on left edge of display area
74
+ if write_cursor_col < @left_column
75
+ retval = retval[ (@left_column - write_cursor_col)..-1 ]
76
+ write_cursor_col = @left_column
77
+ end
78
+
79
+ if retval
80
+ # Truncate based on right edge of display area
81
+ if write_cursor_col + retval.length > @left_column + Curses::cols - 1
82
+ new_length = ( @left_column + Curses::cols - write_cursor_col )
83
+ if new_length <= 0
84
+ retval = nil
85
+ else
86
+ retval = retval[ 0...new_length ]
87
+ end
88
+ end
89
+ end
90
+
91
+ retval == "" ? nil : retval
92
+ end
93
+
94
+ # Worker function for painting only part of a row.
95
+ def paint_single_row_mark( row, text_mark, string, curx, cury )
96
+ expanded_col = tab_expanded_column( text_mark.start_col, row )
97
+ if expanded_col < @left_column + Curses::cols
98
+ left = [ expanded_col - @left_column, 0 ].max
99
+ right = tab_expanded_column( text_mark.end_col, row ) - @left_column
100
+ if left < right
101
+ @win_main.setpos( cury, curx + left )
102
+ @win_main.addstr string[ left...right ]
103
+ end
104
+ end
105
+ end
106
+
107
+ def paint_marks( row )
108
+ string = @lines[ row ][ @left_column ... @left_column + Curses::cols ]
109
+ return if string.nil? || string == ""
110
+ string = string.expand_tabs( @tab_size )
111
+ cury = @win_main.cury
112
+ curx = @win_main.curx
113
+
114
+ @text_marks.values.flatten.reverse_each do |text_mark|
115
+ next if text_mark.nil?
116
+
117
+ @win_main.attrset text_mark.formatting
118
+
119
+ case @selection_mode
120
+ when :normal
121
+ if ( (text_mark.start_row + 1) .. (text_mark.end_row - 1) ) === row
122
+ @win_main.setpos( cury, curx )
123
+ @win_main.addstr string
124
+ elsif row == text_mark.start_row && row == text_mark.end_row
125
+ paint_single_row_mark( row, text_mark, string, curx, cury )
126
+ elsif row == text_mark.start_row
127
+ expanded_col = tab_expanded_column( text_mark.start_col, row )
128
+ if expanded_col < @left_column + Curses::cols
129
+ left = [ expanded_col - @left_column, 0 ].max
130
+ @win_main.setpos( cury, curx + left )
131
+ @win_main.addstr string[ left..-1 ]
132
+ end
133
+ elsif row == text_mark.end_row
134
+ right = tab_expanded_column( text_mark.end_col, row ) - @left_column
135
+ @win_main.setpos( cury, curx )
136
+ @win_main.addstr string[ 0...right ]
137
+ else
138
+ # This row not in selection.
139
+ end
140
+ when :block
141
+ if(
142
+ text_mark.start_row <= row && row <= text_mark.end_row ||
143
+ text_mark.end_row <= row && row <= text_mark.start_row
144
+ )
145
+ paint_single_row_mark( row, text_mark, string, curx, cury )
146
+ end
147
+ end
148
+ end
149
+ end
150
+
151
+ def paint_column_markers
152
+ $diakonos.column_markers.each_value do |data|
153
+ column = data[ :column ]
154
+ next if column.nil?
155
+ next if column > Curses::cols - @left_column || column - @left_column < 0
156
+
157
+ num_lines_to_paint = [ $diakonos.main_window_height, @lines.size - @top_line ].min
158
+ ( 0...num_lines_to_paint ).each do |row|
159
+ @win_main.setpos( row, column - @left_column )
160
+ @win_main.attrset data[ :format ]
161
+ @win_main.addstr( @lines[ @top_line + row ][ column + @left_column ] || ' ' )
162
+ end
163
+ end
164
+ end
165
+
166
+ def print_string( string, formatting = ( @token_formats[ @continued_format_class ] || @default_formatting ) )
167
+ return if ! @pen_down
168
+ return if string.nil?
169
+
170
+ @win_main.attrset formatting
171
+ @win_main.addstr string
172
+ end
173
+
174
+ # This method assumes that the cursor has been set up already at
175
+ # the left-most column of the correct on-screen row.
176
+ # It merely unintelligently prints the characters on the current curses line,
177
+ # refusing to print characters of the in-buffer line which are offscreen.
178
+ def print_line( line )
179
+ i = 0
180
+ substr = nil
181
+ index = nil
182
+ while i < line.length
183
+ substr = line[ i..-1 ]
184
+ if @continued_format_class
185
+ close_index, close_match_text = find_closing_match(
186
+ substr,
187
+ @close_token_regexps[@continued_format_class],
188
+ i == 0
189
+ )
190
+
191
+ if close_match_text.nil?
192
+ print_string truncate_off_screen( substr, i )
193
+ print_padding_from( line.length )
194
+ i = line.length
195
+ else
196
+ end_index = close_index + close_match_text.length
197
+ print_string truncate_off_screen( substr[ 0...end_index ], i )
198
+ @continued_format_class = nil
199
+ i += end_index
200
+ end
201
+ else
202
+ first_index, first_token_class, first_word = find_opening_match( substr, MATCH_ANY, i == 0 )
203
+
204
+ if @lang_stack.length > 0
205
+ prev_lang, close_token_class = @lang_stack[-1]
206
+ close_index, close_match_text = find_closing_match(
207
+ substr,
208
+ $diakonos.close_token_regexps[prev_lang][close_token_class],
209
+ i == 0
210
+ )
211
+
212
+ if close_match_text && close_index <= first_index
213
+ # Print any remaining text in the embedded language
214
+ s = substr[0...close_index]
215
+ print_string( truncate_off_screen(s, i) )
216
+ i += s.length
217
+
218
+ @lang_stack.pop
219
+ set_language prev_lang
220
+
221
+ print_string(
222
+ truncate_off_screen(
223
+ substr[
224
+ close_index...(close_index + close_match_text.length)
225
+ ],
226
+ i
227
+ ),
228
+ @token_formats[close_token_class]
229
+ )
230
+ i += close_match_text.length
231
+
232
+ # Continue printing from here.
233
+ next
234
+ end
235
+ end
236
+
237
+ if first_word
238
+ if first_index > 0
239
+ # Print any preceding text in the default format
240
+ print_string truncate_off_screen( substr[ 0...first_index ], i )
241
+ i += substr[ 0...first_index ].length
242
+ end
243
+ print_string( truncate_off_screen( first_word, i ), @token_formats[ first_token_class ] )
244
+ i += first_word.length
245
+ if @close_token_regexps[ first_token_class ]
246
+ if change_to = @settings[ "lang.#{@language}.tokens.#{first_token_class}.change_to" ]
247
+ @lang_stack.push [ @language, first_token_class ]
248
+ set_language change_to
249
+ else
250
+ @continued_format_class = first_token_class
251
+ end
252
+ end
253
+ else
254
+ print_string truncate_off_screen( substr, i )
255
+ i += substr.length
256
+ break
257
+ end
258
+ end
259
+ end
260
+
261
+ print_padding_from i
262
+ end
263
+
264
+ def print_padding_from( col )
265
+ return if ! @pen_down
266
+
267
+ if col < @left_column
268
+ remainder = Curses::cols
269
+ else
270
+ remainder = @left_column + Curses::cols - col
271
+ end
272
+
273
+ if remainder > 0
274
+ print_string( " " * remainder )
275
+ end
276
+ end
277
+
278
+ def display
279
+ @continued_format_class = nil
280
+ @pen_down = true
281
+ @lang_stack = []
282
+
283
+ # First, we have to "draw" off-screen, in order to check for opening of
284
+ # multi-line highlights.
285
+
286
+ # So, first look backwards from the @top_line to find the first opening
287
+ # regexp match, if any.
288
+ index = @top_line - 1
289
+ @lines[ [ 0, @top_line - @settings[ "view.lookback" ] ].max...@top_line ].reverse_each do |line|
290
+ open_index = -1
291
+ open_token_class = nil
292
+ open_match_text = nil
293
+
294
+ open_index, open_token_class, open_match_text = find_opening_match( line )
295
+
296
+ if open_token_class
297
+ @pen_down = false
298
+ @lines[ index...@top_line ].each do |line|
299
+ print_line line
300
+ end
301
+ @pen_down = true
302
+
303
+ break
304
+ end
305
+
306
+ index = index - 1
307
+ end
308
+
309
+ # Draw each on-screen line.
310
+ y = 0
311
+ @lines[ @top_line...($diakonos.main_window_height + @top_line) ].each_with_index do |line, row|
312
+ if @win_line_numbers
313
+ @win_line_numbers.setpos( y, 0 )
314
+ @win_line_numbers.attrset @settings[ 'view.line_numbers.format' ]
315
+ n = ( @top_line+row+1 ).to_s
316
+ @win_line_numbers.addstr(
317
+ @settings[ 'view.line_numbers.number_format' ] % [
318
+ n[ -[ @settings[ 'view.line_numbers.width' ], n.length ].min..-1 ]
319
+ ]
320
+ )
321
+ end
322
+ @win_main.setpos( y, 0 )
323
+ print_line line.expand_tabs( @tab_size )
324
+ @win_main.setpos( y, 0 )
325
+ paint_marks @top_line + row
326
+ y += 1
327
+ end
328
+
329
+ # Paint the empty space below the file if the file is too short to fit in one screen.
330
+ ( y...$diakonos.main_window_height ).each do |y|
331
+ if @win_line_numbers
332
+ @win_line_numbers.setpos( y, 0 )
333
+ @win_line_numbers.attrset @settings[ 'view.line_numbers.format' ]
334
+ @win_line_numbers.addstr( ' ' * @settings[ 'view.line_numbers.width' ] )
335
+ end
336
+
337
+ @win_main.setpos( y, 0 )
338
+ @win_main.attrset @default_formatting
339
+ linestr = " " * Curses::cols
340
+ if @settings[ "view.nonfilelines.visible" ]
341
+ linestr[ 0 ] = ( @settings[ "view.nonfilelines.character" ] || "~" )
342
+ end
343
+
344
+ @win_main.addstr linestr
345
+ end
346
+
347
+ paint_column_markers
348
+
349
+ if @win_line_numbers
350
+ @win_line_numbers.refresh
351
+ end
352
+ @win_main.setpos( @last_screen_y , @last_screen_x )
353
+ @win_main.refresh
354
+
355
+ if @language != @original_language
356
+ set_language( @original_language )
357
+ end
358
+ end
359
+
360
+ end
361
+
362
+ end
@@ -0,0 +1,157 @@
1
+ module Diakonos
2
+
3
+ class Buffer
4
+
5
+ def save( filename = nil, prompt_overwrite = DONT_PROMPT_OVERWRITE )
6
+ if filename
7
+ name = File.expand_path( filename )
8
+ else
9
+ name = @name
10
+ end
11
+
12
+ if @read_only && FileTest.exist?( @name ) && FileTest.exist?( name ) && ( File.stat( @name ).ino == File.stat( name ).ino )
13
+ $diakonos.set_iline "#{name} cannot be saved since it is read-only."
14
+ else
15
+ @read_only = false
16
+ if name.nil?
17
+ $diakonos.save_file_as
18
+ else
19
+ proceed = true
20
+
21
+ if prompt_overwrite && FileTest.exist?( name )
22
+ proceed = false
23
+ choice = $diakonos.get_choice(
24
+ "Overwrite existing '#{name}'?",
25
+ [ CHOICE_YES, CHOICE_NO ],
26
+ CHOICE_NO
27
+ )
28
+ case choice
29
+ when CHOICE_YES
30
+ proceed = true
31
+ when CHOICE_NO
32
+ proceed = false
33
+ end
34
+ end
35
+
36
+ if file_modified?
37
+ proceed = ! $diakonos.revert( "File has been altered externally. Load on-disk version?" )
38
+ end
39
+
40
+ if proceed
41
+ save_copy name
42
+ @name = name
43
+ @last_modification_check = File.mtime( @name )
44
+ saved = true
45
+
46
+ if @name =~ /#{$diakonos.diakonos_home}\/.*\.conf/
47
+ $diakonos.load_configuration
48
+ $diakonos.initialize_display
49
+ end
50
+
51
+ @modified = false
52
+
53
+ display
54
+ $diakonos.update_status_line
55
+ end
56
+ end
57
+ end
58
+
59
+ saved
60
+ end
61
+
62
+ # Returns true on successful write.
63
+ def save_copy( filename )
64
+ return false if filename.nil?
65
+
66
+ name = File.expand_path( filename )
67
+
68
+ if @settings['save_backup_files']
69
+ begin
70
+ FileUtils.cp name, name+'~', preserve: true
71
+ rescue Errno::ENOENT
72
+ # Do nothing if file didn't exist yet
73
+ end
74
+ end
75
+
76
+ File.open( name, "w" ) do |f|
77
+ @lines[ 0..-2 ].each do |line|
78
+ if @settings[ 'strip_trailing_whitespace_on_save' ]
79
+ line.rstrip!
80
+ end
81
+ f.puts line
82
+ end
83
+
84
+ line = @lines[ -1 ]
85
+ if @settings[ 'strip_trailing_whitespace_on_save' ]
86
+ line.rstrip!
87
+ end
88
+ if line != ""
89
+ # No final newline character
90
+ if @settings[ "eof_newline" ]
91
+ line << "\n"
92
+ @lines << ''
93
+ end
94
+ f.print line
95
+ end
96
+
97
+ if @settings[ 'strip_trailing_whitespace_on_save' ]
98
+ if @last_col > @lines[ @last_row ].size
99
+ cursor_to @last_row, @lines[ @last_row ].size
100
+ end
101
+ end
102
+ end
103
+ end
104
+
105
+ # Check if the file which is being edited has been modified since
106
+ # the last time we checked it.
107
+ # @return true if file has been modified
108
+ # @return false if file has not been modified
109
+ def file_modified?
110
+ modified = false
111
+
112
+ if @name
113
+ begin
114
+ mtime = File.mtime( @name )
115
+
116
+ if mtime > @last_modification_check
117
+ modified = true
118
+ @last_modification_check = mtime
119
+ end
120
+ rescue Errno::ENOENT
121
+ # Ignore if file doesn't exist
122
+ end
123
+ end
124
+
125
+ modified
126
+ end
127
+
128
+ # Compares MD5 sums of buffer and actual file on disk.
129
+ # Returns true if there is no file on disk.
130
+ def file_different?
131
+ if @name && File.exist?( @name )
132
+ Digest::MD5.hexdigest(
133
+ @lines.join( "\n" )
134
+ ) != Digest::MD5.hexdigest(
135
+ File.read( @name )
136
+ )
137
+ else
138
+ true
139
+ end
140
+ end
141
+
142
+ def set_modified( do_display = DO_DISPLAY, use_md5 = DONT_USE_MD5 )
143
+ if @read_only
144
+ $diakonos.set_iline "Warning: Modifying a read-only file."
145
+ end
146
+
147
+ @modified = use_md5 ? file_different? : true
148
+ clear_matches
149
+ if do_display
150
+ $diakonos.update_status_line
151
+ display
152
+ end
153
+ end
154
+
155
+ end
156
+
157
+ end
@@ -0,0 +1,175 @@
1
+ module Diakonos
2
+
3
+ class Buffer
4
+
5
+ def tab_expanded_column( col, row )
6
+ delta = 0
7
+ line = @lines[ row ]
8
+ for i in 0...col
9
+ if line[ i ] == "\t"
10
+ delta += ( @tab_size - ( (i+delta) % @tab_size ) ) - 1
11
+ end
12
+ end
13
+ col + delta
14
+ end
15
+
16
+ def set_indent( row, level, opts = {} )
17
+ do_display = opts.fetch( :do_display, true )
18
+ undoable = opts.fetch( :undoable, true )
19
+ cursor_eol = opts.fetch( :cursor_eol, false )
20
+
21
+ @lines[ row ] =~ /^([\s#{@indent_ignore_charset}]*)(.*)$/
22
+ current_indent_text = ( $1 || "" )
23
+ rest = ( $2 || "" )
24
+ current_indent_text.gsub!( /\t/, ' ' * @tab_size )
25
+ indentation = @indent_size * [ level, 0 ].max
26
+ if current_indent_text.length >= indentation
27
+ indent_text = current_indent_text[ 0...indentation ]
28
+ else
29
+ indent_text = current_indent_text + " " * ( indentation - current_indent_text.length )
30
+ end
31
+ if @settings[ "lang.#{@language}.indent.using_tabs" ]
32
+ num_tabs = 0
33
+ indent_text.gsub!( / {#{@tab_size}}/ ) { |match|
34
+ num_tabs += 1
35
+ "\t"
36
+ }
37
+ indentation -= num_tabs * ( @tab_size - 1 )
38
+ end
39
+
40
+ take_snapshot( TYPING ) if do_display && undoable
41
+ @lines[ row ] = indent_text + rest
42
+ if do_display
43
+ cursor_to(
44
+ row,
45
+ cursor_eol ? @lines[row].length : indentation
46
+ )
47
+ end
48
+ set_modified do_display
49
+ end
50
+
51
+ def indentation_level( row, use_indent_ignore = USE_INDENT_IGNORE )
52
+ line = @lines[ row ]
53
+
54
+ if use_indent_ignore
55
+ if line =~ /^[\s#{@indent_ignore_charset}]*$/ || line == ""
56
+ level = 0
57
+ elsif line =~ /^([\s#{@indent_ignore_charset}]+)[^\s#{@indent_ignore_charset}]/
58
+ whitespace = $1.expand_tabs( @tab_size )
59
+ level = whitespace.length / @indent_size
60
+ if @indent_roundup && ( whitespace.length % @indent_size > 0 )
61
+ level += 1
62
+ end
63
+ else
64
+ level = 0
65
+ end
66
+ else
67
+ level = 0
68
+ if line =~ /^([\s]+)/
69
+ whitespace = $1.expand_tabs( @tab_size )
70
+ level = whitespace.length / @indent_size
71
+ if @indent_roundup && ( whitespace.length % @indent_size > 0 )
72
+ level += 1
73
+ end
74
+ end
75
+ end
76
+
77
+ level
78
+ end
79
+
80
+ # @param starting_row [Integer]
81
+ # @param next_line_check [Boolean]
82
+ # @return [Integer]
83
+ def nearest_basis_row_from(starting_row, next_line_check = true)
84
+ row = starting_row-1
85
+
86
+ if @lines[row] =~ @indenters_next_line || @lines[row] =~ @indenters
87
+ return row
88
+ end
89
+
90
+ loop do
91
+ return nil if row.nil? || row < 0
92
+
93
+ while (
94
+ @lines[row] =~ /^[\s#{@indent_ignore_charset}]*$/ ||
95
+ @lines[row] =~ @settings[ "lang.#{@language}.indent.ignore" ] ||
96
+ @lines[row] =~ @settings[ "lang.#{@language}.indent.not_indented" ]
97
+ )
98
+ row = nearest_basis_row_from(row)
99
+ return nil if row.nil?
100
+ end
101
+
102
+ if next_line_check
103
+ row_before = nearest_basis_row_from(row, false)
104
+ if row_before && @lines[row_before] =~ @indenters_next_line
105
+ row = row_before
106
+ next
107
+ end
108
+ end
109
+
110
+ break
111
+ end
112
+
113
+ row
114
+ end
115
+
116
+ def parsed_indent( opts = {} )
117
+ row = opts.fetch( :row, @last_row )
118
+ do_display = opts.fetch( :do_display, true )
119
+ undoable = opts.fetch( :undoable, true )
120
+ cursor_eol = opts.fetch( :cursor_eol, false )
121
+
122
+ if row == 0 || @lines[ row ] =~ @settings[ "lang.#{@language}.indent.not_indented" ]
123
+ level = 0
124
+ else
125
+ basis_row = nearest_basis_row_from(row)
126
+
127
+ if basis_row.nil?
128
+ level = 0
129
+ else
130
+ # @lines[basis_row] += " // x"
131
+ level = indentation_level(basis_row)
132
+
133
+ prev_line = @lines[basis_row]
134
+ line = @lines[row]
135
+
136
+ if @preventers
137
+ prev_line = prev_line.gsub( @preventers, "" )
138
+ line = line.gsub( @preventers, "" )
139
+ end
140
+
141
+ indenter_index = (prev_line =~ @indenters)
142
+ nl_indenter_index = (prev_line =~ @indenters_next_line)
143
+
144
+ if nl_indenter_index && basis_row == row-1
145
+ level += 1
146
+ elsif indenter_index
147
+ level += 1
148
+ unindenter_index = (prev_line =~ @unindenters)
149
+ if unindenter_index && unindenter_index != indenter_index
150
+ level -= 1
151
+ end
152
+ end
153
+
154
+ if line =~ @unindenters
155
+ level -= 1
156
+ end
157
+ end
158
+ end
159
+
160
+ set_indent row, level, do_display: do_display, undoable: undoable, cursor_eol: cursor_eol
161
+ end
162
+
163
+ def indent( row = @last_row, do_display = DO_DISPLAY )
164
+ level = indentation_level( row, DONT_USE_INDENT_IGNORE )
165
+ set_indent row, level + 1, do_display: do_display
166
+ end
167
+
168
+ def unindent( row = @last_row, do_display = DO_DISPLAY )
169
+ level = indentation_level( row, DONT_USE_INDENT_IGNORE )
170
+ set_indent row, level - 1, do_display: do_display
171
+ end
172
+
173
+ end
174
+
175
+ end