diakonos 0.8.11 → 0.9.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG +439 -0
- data/LICENCE.md +675 -0
- data/bin/diakonos +6 -0
- data/diakonos-256-colour.conf +220 -0
- data/diakonos.conf +1802 -0
- data/help/about-help.dhf +31 -0
- data/help/clipboard.dhf +45 -0
- data/help/close-file.dhf +6 -0
- data/help/code-block-navigation.dhf +16 -0
- data/help/column-markers.dhf +15 -0
- data/help/config.dhf +69 -0
- data/help/cursor-stack.dhf +19 -0
- data/help/delete.dhf +41 -0
- data/help/extensions.dhf +125 -0
- data/help/file-type.dhf +24 -0
- data/help/key-mapping.dhf +127 -0
- data/help/line-numbers.dhf +22 -0
- data/help/macros.dhf +27 -0
- data/help/new-file.dhf +6 -0
- data/help/open-file.dhf +21 -0
- data/help/quit.dhf +7 -0
- data/help/resizing.dhf +19 -0
- data/help/ruby.dhf +17 -0
- data/help/save-file.dhf +10 -0
- data/help/scripting.dhf +92 -0
- data/help/search.dhf +103 -0
- data/help/shell.dhf +60 -0
- data/help/speed.dhf +23 -0
- data/help/support.dhf +15 -0
- data/help/switch-buffers.dhf +15 -0
- data/help/tabs.dhf +36 -0
- data/help/undo.dhf +9 -0
- data/help/uninstall.dhf +18 -0
- data/help/welcome.dhf +32 -0
- data/help/word-wrap.dhf +17 -0
- data/lib/diakonos/about.rb +70 -0
- data/lib/diakonos/bookmark.rb +46 -0
- data/lib/diakonos/buffer/bookmarking.rb +47 -0
- data/lib/diakonos/buffer/cursor.rb +335 -0
- data/lib/diakonos/buffer/delete.rb +170 -0
- data/lib/diakonos/buffer/display.rb +362 -0
- data/lib/diakonos/buffer/file.rb +157 -0
- data/lib/diakonos/buffer/indentation.rb +175 -0
- data/lib/diakonos/buffer/searching.rb +552 -0
- data/lib/diakonos/buffer/selection.rb +360 -0
- data/lib/diakonos/buffer/undo.rb +73 -0
- data/lib/diakonos/buffer-hash.rb +60 -0
- data/lib/diakonos/buffer-management.rb +59 -0
- data/lib/diakonos/buffer.rb +698 -0
- data/lib/diakonos/clipboard-klipper-dbus.rb +62 -0
- data/lib/diakonos/clipboard-klipper.rb +62 -0
- data/lib/diakonos/clipboard-osx.rb +59 -0
- data/lib/diakonos/clipboard-xclip.rb +60 -0
- data/lib/diakonos/clipboard.rb +47 -0
- data/lib/diakonos/config-file.rb +67 -0
- data/lib/diakonos/config.rb +382 -0
- data/lib/diakonos/core-ext/enumerable.rb +15 -0
- data/lib/diakonos/core-ext/hash.rb +60 -0
- data/lib/diakonos/core-ext/object.rb +6 -0
- data/lib/diakonos/core-ext/regexp.rb +6 -0
- data/lib/diakonos/core-ext/string.rb +122 -0
- data/lib/diakonos/ctag.rb +28 -0
- data/lib/diakonos/cursor.rb +27 -0
- data/lib/diakonos/display/format.rb +75 -0
- data/lib/diakonos/display.rb +336 -0
- data/lib/diakonos/extension-set.rb +49 -0
- data/lib/diakonos/extension.rb +34 -0
- data/lib/diakonos/finding.rb +40 -0
- data/lib/diakonos/functions/basics.rb +34 -0
- data/lib/diakonos/functions/bookmarking.rb +61 -0
- data/lib/diakonos/functions/buffers.rb +491 -0
- data/lib/diakonos/functions/clipboard.rb +70 -0
- data/lib/diakonos/functions/cursor.rb +264 -0
- data/lib/diakonos/functions/grepping.rb +83 -0
- data/lib/diakonos/functions/indentation.rb +71 -0
- data/lib/diakonos/functions/readline.rb +93 -0
- data/lib/diakonos/functions/search.rb +179 -0
- data/lib/diakonos/functions/selection.rb +98 -0
- data/lib/diakonos/functions/sessions.rb +78 -0
- data/lib/diakonos/functions/shell.rb +250 -0
- data/lib/diakonos/functions/tags.rb +65 -0
- data/lib/diakonos/functions/text-manipulation.rb +196 -0
- data/lib/diakonos/functions-deprecated.rb +77 -0
- data/lib/diakonos/functions.rb +292 -0
- data/lib/diakonos/grep.rb +98 -0
- data/lib/diakonos/help.rb +47 -0
- data/lib/diakonos/hooks.rb +13 -0
- data/lib/diakonos/installation.rb +19 -0
- data/lib/diakonos/interaction-handler.rb +216 -0
- data/lib/diakonos/interaction.rb +52 -0
- data/lib/diakonos/key-map.rb +62 -0
- data/lib/diakonos/keying.rb +442 -0
- data/lib/diakonos/line-mover.rb +42 -0
- data/lib/diakonos/list.rb +59 -0
- data/lib/diakonos/logging.rb +27 -0
- data/lib/diakonos/mode.rb +17 -0
- data/lib/diakonos/mouse.rb +18 -0
- data/lib/diakonos/number-fitter.rb +11 -0
- data/lib/diakonos/range.rb +31 -0
- data/lib/diakonos/readline/functions.rb +82 -0
- data/lib/diakonos/readline.rb +223 -0
- data/lib/diakonos/search.rb +58 -0
- data/lib/diakonos/sessions.rb +257 -0
- data/lib/diakonos/sized-array.rb +48 -0
- data/lib/diakonos/text-mark.rb +19 -0
- data/lib/diakonos/vendor/fuzzy_file_finder.rb +386 -0
- data/lib/diakonos/version.rb +25 -0
- data/lib/diakonos/window.rb +43 -0
- data/lib/diakonos.rb +592 -0
- metadata +160 -68
@@ -0,0 +1,82 @@
|
|
1
|
+
module Diakonos
|
2
|
+
|
3
|
+
class Readline
|
4
|
+
|
5
|
+
def abort
|
6
|
+
@input = nil
|
7
|
+
@done = true
|
8
|
+
end
|
9
|
+
|
10
|
+
def accept( item )
|
11
|
+
if item && @on_dirs == :go_into_dirs && File.directory?( item )
|
12
|
+
complete_input
|
13
|
+
else
|
14
|
+
@done = true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def backspace
|
19
|
+
if cursor_left
|
20
|
+
delete
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def cursor_bol
|
25
|
+
@input_cursor = 0
|
26
|
+
sync
|
27
|
+
end
|
28
|
+
|
29
|
+
def cursor_eol
|
30
|
+
@input_cursor = @input.length
|
31
|
+
sync
|
32
|
+
end
|
33
|
+
|
34
|
+
def cursor_left
|
35
|
+
return false if @input_cursor < 1
|
36
|
+
@input_cursor -= 1
|
37
|
+
sync
|
38
|
+
true
|
39
|
+
end
|
40
|
+
|
41
|
+
def cursor_right
|
42
|
+
return if @input_cursor >= @input.length
|
43
|
+
@input_cursor += 1
|
44
|
+
sync
|
45
|
+
end
|
46
|
+
|
47
|
+
def delete
|
48
|
+
return if @input_cursor >= @input.length
|
49
|
+
@input = @input[ 0...@input_cursor ] + @input[ (@input_cursor + 1)..-1 ]
|
50
|
+
sync
|
51
|
+
end
|
52
|
+
|
53
|
+
def delete_line
|
54
|
+
@input = ""
|
55
|
+
sync
|
56
|
+
end
|
57
|
+
|
58
|
+
def delete_word
|
59
|
+
head = @input[ 0...@input_cursor ]
|
60
|
+
chopped = head.sub( /\w+\W*$/, '' )
|
61
|
+
@input = chopped + @input[ @input_cursor..-1 ]
|
62
|
+
@input_cursor -= head.length - chopped.length
|
63
|
+
sync
|
64
|
+
end
|
65
|
+
|
66
|
+
def history_up
|
67
|
+
return if @history_index < 1
|
68
|
+
@history[ @history_index ] = @input
|
69
|
+
@history_index -= 1
|
70
|
+
@input = @history[ @history_index ]
|
71
|
+
end
|
72
|
+
|
73
|
+
def history_down
|
74
|
+
return if @history_index > @history.length - 2
|
75
|
+
@history[ @history_index ] = @input
|
76
|
+
@history_index += 1
|
77
|
+
@input = @history[ @history_index ]
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
module Diakonos
|
2
|
+
|
3
|
+
class Readline
|
4
|
+
|
5
|
+
attr_reader :input
|
6
|
+
|
7
|
+
# completion_array is the array of strings that tab completion can use
|
8
|
+
# The block returns true if a refresh is needed?
|
9
|
+
# @param options :initial_text, :completion_array, :history, :do_complete, :on_dirs
|
10
|
+
def initialize(
|
11
|
+
list_manager:,
|
12
|
+
keystroke_processor:,
|
13
|
+
testing: false,
|
14
|
+
window:,
|
15
|
+
start_pos:,
|
16
|
+
options: {},
|
17
|
+
&block
|
18
|
+
)
|
19
|
+
@list_manager = list_manager
|
20
|
+
@keystroke_processor = keystroke_processor
|
21
|
+
@testing = testing
|
22
|
+
@window = window
|
23
|
+
@start_pos = start_pos
|
24
|
+
@window.setpos( 0, start_pos )
|
25
|
+
@initial_text = options[ :initial_text ] || ''
|
26
|
+
@completion_array = options[ :completion_array ]
|
27
|
+
@list_filename = @list_manager.list_filename
|
28
|
+
|
29
|
+
@history = options[ :history ] || []
|
30
|
+
@history << @initial_text
|
31
|
+
@history_index = [ @history.length - 1, 0 ].max
|
32
|
+
|
33
|
+
@do_complete = options[ :do_complete ] || ::Diakonos::DONT_COMPLETE
|
34
|
+
@on_dirs = options[ :on_dirs ] || :go_into_dirs
|
35
|
+
@numbered_list = options[ :numbered_list ]
|
36
|
+
|
37
|
+
@block = block
|
38
|
+
|
39
|
+
# ---
|
40
|
+
|
41
|
+
@input = @initial_text.dup
|
42
|
+
if ! @input.empty?
|
43
|
+
call_block
|
44
|
+
end
|
45
|
+
|
46
|
+
@icurx = @window.curx
|
47
|
+
@icury = @window.cury
|
48
|
+
@view_y = 0
|
49
|
+
if ! @testing
|
50
|
+
@window.addstr @initial_text
|
51
|
+
end
|
52
|
+
@input_cursor = @initial_text.length
|
53
|
+
@opened_list_file = false
|
54
|
+
|
55
|
+
if @do_complete
|
56
|
+
complete_input
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_input
|
61
|
+
while ! done?
|
62
|
+
@keystroke_processor.process_keystroke(Array.new, 'input')
|
63
|
+
end
|
64
|
+
input
|
65
|
+
end
|
66
|
+
|
67
|
+
def call_block
|
68
|
+
if @block
|
69
|
+
@block.call( @input )
|
70
|
+
@window.refresh
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def set_input( input = '' )
|
75
|
+
if numbered_list? && input =~ /^\w /
|
76
|
+
input = input[ 3..-1 ]
|
77
|
+
end
|
78
|
+
@input = input
|
79
|
+
end
|
80
|
+
|
81
|
+
def done?
|
82
|
+
@done
|
83
|
+
end
|
84
|
+
|
85
|
+
def finish
|
86
|
+
@done = true
|
87
|
+
end
|
88
|
+
|
89
|
+
def list_sync( line )
|
90
|
+
return if line.nil?
|
91
|
+
set_input line
|
92
|
+
cursor_write_input
|
93
|
+
end
|
94
|
+
|
95
|
+
def numbered_list?
|
96
|
+
@numbered_list
|
97
|
+
end
|
98
|
+
|
99
|
+
def sync
|
100
|
+
if @input_cursor > @input.length
|
101
|
+
@input_cursor = @input.length
|
102
|
+
elsif @input_cursor < @view_y
|
103
|
+
@view_y = @input_cursor
|
104
|
+
end
|
105
|
+
|
106
|
+
diff = ( @input_cursor - @view_y ) + 1 - ( Curses::cols - @start_pos )
|
107
|
+
if diff > 0
|
108
|
+
@view_y += diff
|
109
|
+
end
|
110
|
+
|
111
|
+
@window.setpos( @window.cury, @start_pos + @input_cursor - @view_y )
|
112
|
+
redraw_input
|
113
|
+
call_block
|
114
|
+
end
|
115
|
+
|
116
|
+
def paste( s )
|
117
|
+
@input = @input[ 0...@input_cursor ] + s + @input[ @input_cursor..-1 ]
|
118
|
+
@input_cursor += s.length
|
119
|
+
sync
|
120
|
+
end
|
121
|
+
|
122
|
+
def redraw_input
|
123
|
+
# TODO: Gracefully handle when `input` is nil
|
124
|
+
input = @input[ @view_y...(@view_y + Curses::cols) ]
|
125
|
+
|
126
|
+
curx = @window.curx
|
127
|
+
cury = @window.cury
|
128
|
+
@window.setpos( @icury, @icurx )
|
129
|
+
@window.addstr "%-#{ Curses::cols - curx }s%s" % [
|
130
|
+
input,
|
131
|
+
" " * [ ( Curses::cols - input.length ), 0 ].max
|
132
|
+
]
|
133
|
+
@window.setpos( cury, curx )
|
134
|
+
@window.refresh
|
135
|
+
end
|
136
|
+
|
137
|
+
# Redisplays the input text starting at the start of the user input area,
|
138
|
+
# positioning the cursor at the end of the text.
|
139
|
+
def cursor_write_input
|
140
|
+
if @input
|
141
|
+
@input_cursor = @input.length
|
142
|
+
@window.setpos( @window.cury, @icurx + @input.length )
|
143
|
+
redraw_input
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def complete_input
|
148
|
+
if @completion_array && @input.length > 0
|
149
|
+
len = @input.length
|
150
|
+
matches = @completion_array.find_all { |el| el[ 0...len ] == @input && len <= el.length }
|
151
|
+
else
|
152
|
+
path = File.expand_path( @input || '.' )
|
153
|
+
if FileTest.directory? path
|
154
|
+
path << '/'
|
155
|
+
end
|
156
|
+
matches = Dir.glob( ( path + "*" ).gsub( /\*\*/, "*" ) )
|
157
|
+
if @on_dirs == :accept_dirs
|
158
|
+
matches = matches.select { |m| File.directory? m }
|
159
|
+
end
|
160
|
+
end
|
161
|
+
matches.sort!
|
162
|
+
|
163
|
+
if matches.length == 1
|
164
|
+
@input = matches[ 0 ]
|
165
|
+
cursor_write_input
|
166
|
+
File.open( @list_filename, "w" ) do |f|
|
167
|
+
if @completion_array.nil?
|
168
|
+
f.puts "(unique)"
|
169
|
+
else
|
170
|
+
f.puts @input
|
171
|
+
end
|
172
|
+
end
|
173
|
+
if @completion_array.nil? && FileTest.directory?( @input )
|
174
|
+
@input << "/"
|
175
|
+
cursor_write_input
|
176
|
+
if @on_dirs != :accept_dirs
|
177
|
+
complete_input
|
178
|
+
end
|
179
|
+
end
|
180
|
+
elsif matches.length > 1
|
181
|
+
common = matches[ 0 ]
|
182
|
+
File.open( @list_filename, "w" ) do |f|
|
183
|
+
i = nil
|
184
|
+
matches.each do |match|
|
185
|
+
f.print match
|
186
|
+
if FileTest.directory?( match )
|
187
|
+
f.print '/'
|
188
|
+
end
|
189
|
+
f.puts
|
190
|
+
|
191
|
+
if match[ 0 ] != common[ 0 ]
|
192
|
+
common = nil
|
193
|
+
break
|
194
|
+
end
|
195
|
+
|
196
|
+
up_to = [ common.length - 1, match.length - 1 ].min
|
197
|
+
i = 1
|
198
|
+
while ( i <= up_to ) && ( match[ 0..i ] == common[ 0..i ] )
|
199
|
+
i += 1
|
200
|
+
end
|
201
|
+
common = common[ 0...i ]
|
202
|
+
end
|
203
|
+
end
|
204
|
+
if common.nil?
|
205
|
+
File.open( @list_filename, "w" ) do |f|
|
206
|
+
f.puts "(no matches)"
|
207
|
+
end
|
208
|
+
else
|
209
|
+
@input = common
|
210
|
+
cursor_write_input
|
211
|
+
end
|
212
|
+
else
|
213
|
+
File.open( @list_filename, "w" ) do |f|
|
214
|
+
f.puts "(no matches)"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
@list_manager.open_list_buffer
|
218
|
+
@window.setpos( @window.cury, @window.curx )
|
219
|
+
end
|
220
|
+
|
221
|
+
end
|
222
|
+
|
223
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Diakonos
|
2
|
+
class Diakonos
|
3
|
+
|
4
|
+
# @return [Integer] the number of replacements made
|
5
|
+
def find_( options = {} )
|
6
|
+
regexp_source, replacement = options.values_at( :regexp_source, :replacement )
|
7
|
+
return if regexp_source.nil? || regexp_source.empty?
|
8
|
+
|
9
|
+
rs_array = regexp_source.newline_split
|
10
|
+
regexps = Array.new
|
11
|
+
exception_thrown = nil
|
12
|
+
|
13
|
+
rs_array.each do |source|
|
14
|
+
begin
|
15
|
+
warning_verbosity = $VERBOSE
|
16
|
+
$VERBOSE = nil
|
17
|
+
regexps << Regexp.new(
|
18
|
+
source,
|
19
|
+
options[:case_sensitive] ? nil : Regexp::IGNORECASE
|
20
|
+
)
|
21
|
+
$VERBOSE = warning_verbosity
|
22
|
+
rescue RegexpError => e
|
23
|
+
if ! exception_thrown
|
24
|
+
exception_thrown = e
|
25
|
+
source = Regexp.escape( source )
|
26
|
+
retry
|
27
|
+
else
|
28
|
+
raise e
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
if replacement == ASK_REPLACEMENT
|
34
|
+
replacement = get_user_input( "Replace with: ", history: @rlh_search )
|
35
|
+
end
|
36
|
+
|
37
|
+
if exception_thrown && ! options[:quiet]
|
38
|
+
set_iline "Searching literally; #{exception_thrown.message}"
|
39
|
+
end
|
40
|
+
|
41
|
+
# The execution order of the #find and the @last_search_regexps assignment is likely deliberate
|
42
|
+
num_replacements = buffer_current.find(
|
43
|
+
regexps,
|
44
|
+
:direction => options[:direction],
|
45
|
+
:replacement => replacement,
|
46
|
+
:starting_row => options[:starting_row],
|
47
|
+
:starting_col => options[:starting_col],
|
48
|
+
:quiet => options[:quiet],
|
49
|
+
:show_context_after => @settings[ 'find.show_context_after' ],
|
50
|
+
:starting => true
|
51
|
+
)
|
52
|
+
@last_search_regexps = regexps
|
53
|
+
|
54
|
+
num_replacements
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,257 @@
|
|
1
|
+
module Diakonos
|
2
|
+
class Session
|
3
|
+
attr_reader :filename, :settings, :name, :dir, :buffers, :readline_histories
|
4
|
+
attr_accessor :buffer_current
|
5
|
+
|
6
|
+
def initialize(filepath, data = nil)
|
7
|
+
if data.nil?
|
8
|
+
@filename = File.expand_path(filepath)
|
9
|
+
@settings = Hash.new
|
10
|
+
|
11
|
+
basename = File.basename(filepath)
|
12
|
+
if ! Session.pid_session?(filepath)
|
13
|
+
@name = basename
|
14
|
+
end
|
15
|
+
|
16
|
+
@buffers = []
|
17
|
+
@buffer_current = 1
|
18
|
+
self.dir = Dir.getwd
|
19
|
+
else
|
20
|
+
@filename = data['filename']
|
21
|
+
@settings = data['settings']
|
22
|
+
@name = data['name']
|
23
|
+
@buffers = data['buffers']
|
24
|
+
@buffer_current = data['buffer_current'] || 1
|
25
|
+
self.dir = data['dir']
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_yaml
|
30
|
+
{
|
31
|
+
'filename' => @filename,
|
32
|
+
'settings' => @settings,
|
33
|
+
'name' => @name,
|
34
|
+
'buffer_current' => @buffer_current,
|
35
|
+
'dir' => @dir,
|
36
|
+
'buffers' => @buffers,
|
37
|
+
'readline_histories' => @readline_histories
|
38
|
+
}.to_yaml
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_buffers(buffers)
|
42
|
+
@buffers = buffers.reject { |buffer|
|
43
|
+
buffer.name.nil?
|
44
|
+
}.collect { |buffer|
|
45
|
+
{
|
46
|
+
'filepath' => buffer.name,
|
47
|
+
'read_only' => buffer.read_only,
|
48
|
+
'cursor' => {
|
49
|
+
'row' => buffer.last_row,
|
50
|
+
'col' => buffer.last_col,
|
51
|
+
},
|
52
|
+
'display' => {
|
53
|
+
'top_line' => buffer.top_line,
|
54
|
+
'left_column' => buffer.left_column
|
55
|
+
},
|
56
|
+
'last_search_regexps' => buffer.last_search_regexps.map { |r| r.to_s },
|
57
|
+
}
|
58
|
+
}.compact
|
59
|
+
end
|
60
|
+
|
61
|
+
def set_readline_histories(rlh_general, rlh_files, rlh_search, rlh_shell, rlh_help, rlh_sessions)
|
62
|
+
@readline_histories = {
|
63
|
+
'general' => rlh_general,
|
64
|
+
'files' => rlh_files,
|
65
|
+
'search' => rlh_search,
|
66
|
+
'shell' => rlh_shell,
|
67
|
+
'help' => rlh_help,
|
68
|
+
'sessions' => rlh_sessions,
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
def dir=(new_dir)
|
73
|
+
@dir = new_dir
|
74
|
+
Dir.chdir new_dir
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Session] The Session created from the YAML data in the specified file
|
78
|
+
# or nil on failure to load
|
79
|
+
def self.from_yaml_file(yaml_filename)
|
80
|
+
return nil if ! File.exist?(yaml_filename)
|
81
|
+
session = nil
|
82
|
+
|
83
|
+
File.open(yaml_filename) do |f|
|
84
|
+
loaded = YAML::load(f) or break
|
85
|
+
|
86
|
+
if(
|
87
|
+
loaded[ 'filename' ] &&
|
88
|
+
loaded[ 'settings' ] &&
|
89
|
+
loaded[ 'settings' ].respond_to?( :values ) &&
|
90
|
+
loaded.has_key?( 'name' ) &&
|
91
|
+
(
|
92
|
+
loaded[ 'files' ] &&
|
93
|
+
loaded[ 'files' ].respond_to?( :each ) ||
|
94
|
+
loaded[ 'buffers' ] &&
|
95
|
+
loaded[ 'buffers' ].respond_to?( :each )
|
96
|
+
)
|
97
|
+
)
|
98
|
+
# Convert old sessions
|
99
|
+
if loaded[ 'files' ]
|
100
|
+
loaded[ 'buffers' ] = loaded[ 'files' ].map { |f|
|
101
|
+
Session.file_hash_for f
|
102
|
+
}
|
103
|
+
loaded.delete 'files'
|
104
|
+
end
|
105
|
+
|
106
|
+
session = Session.new(loaded['filename'], loaded)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
session
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.pid_session?(path)
|
114
|
+
%r{/\d+$} === path
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.file_hash_for(filepath)
|
118
|
+
filepath, line_number = ::Diakonos.parse_filename_and_line_number(filepath)
|
119
|
+
{
|
120
|
+
'filepath' => filepath,
|
121
|
+
'read_only' => false,
|
122
|
+
'cursor' => {
|
123
|
+
'row' => line_number || 0,
|
124
|
+
'col' => 0,
|
125
|
+
},
|
126
|
+
'display' => {
|
127
|
+
'top_line' => 0,
|
128
|
+
'left_column' => 0
|
129
|
+
},
|
130
|
+
}
|
131
|
+
end
|
132
|
+
|
133
|
+
end
|
134
|
+
|
135
|
+
class Diakonos
|
136
|
+
attr_reader :session
|
137
|
+
|
138
|
+
def initialize_session
|
139
|
+
@session_dir = "#{@diakonos_home}/sessions"
|
140
|
+
mkdir @session_dir
|
141
|
+
@session = Session.new("#{@session_dir}/#{Process.pid}")
|
142
|
+
end
|
143
|
+
|
144
|
+
def load_session( session_file )
|
145
|
+
cleanup_session
|
146
|
+
@session = Session.from_yaml_file(session_file)
|
147
|
+
if @session
|
148
|
+
@files.concat @session.buffers
|
149
|
+
rlh = @session.readline_histories
|
150
|
+
if rlh
|
151
|
+
@rlh_general = rlh['general'] || @rlh_general
|
152
|
+
@rlh_files = rlh['files'] || @rlh_files
|
153
|
+
@rlh_search = rlh['search'] || @rlh_search
|
154
|
+
@rlh_shell = rlh['shell'] || @rlh_shell
|
155
|
+
@rlh_help = rlh['help'] || @rlh_help
|
156
|
+
@rlh_sessions = rlh['sessions'] || @rlh_sessions
|
157
|
+
end
|
158
|
+
merge_session_settings
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def save_session( session_file = @session.filename )
|
163
|
+
return if session_file.nil?
|
164
|
+
return if @testing && Session.pid_session?(session_file)
|
165
|
+
|
166
|
+
@session.set_buffers(@buffers)
|
167
|
+
@session.set_readline_histories(@rlh_general, @rlh_files, @rlh_search, @rlh_shell, @rlh_help, @rlh_sessions)
|
168
|
+
|
169
|
+
File.open( session_file, 'w' ) do |f|
|
170
|
+
f.puts @session.to_yaml
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def session_filepath_for( session_id )
|
175
|
+
if session_id && session_id !~ %r{/}
|
176
|
+
"#{@session_dir}/#{session_id}"
|
177
|
+
else
|
178
|
+
session_id
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def session_startup
|
183
|
+
@stale_session_files = []
|
184
|
+
|
185
|
+
if @session_to_load
|
186
|
+
pid_session = @session
|
187
|
+
@session = nil
|
188
|
+
session_path = session_filepath_for( @session_to_load )
|
189
|
+
load_session session_path
|
190
|
+
if ! @session
|
191
|
+
@session = Session.new(session_path)
|
192
|
+
end
|
193
|
+
else
|
194
|
+
session_files = Dir[ "#{@session_dir}/*" ].grep( %r{/\d+$} )
|
195
|
+
session_files.each do |sf|
|
196
|
+
pid = sf[ %r{/(\d+)$}, 1 ].to_i
|
197
|
+
|
198
|
+
# Check if the process is still alive
|
199
|
+
begin
|
200
|
+
Process.kill 0, pid
|
201
|
+
rescue Errno::ESRCH, Errno::EPERM
|
202
|
+
if Session.pid_session?(sf)
|
203
|
+
@stale_session_files << sf
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# We have to do this separately and later (as opposed to inside #session_startup)
|
211
|
+
# because we have to wait for the display to get initialized in order to
|
212
|
+
# prompt the user for input, etc.
|
213
|
+
def handle_stale_session_files
|
214
|
+
return if @testing
|
215
|
+
return if @stale_session_files.empty?
|
216
|
+
|
217
|
+
session_buffers = []
|
218
|
+
@stale_session_files.each_with_index do |session_file,index|
|
219
|
+
session_buffers << open_file( session_file )
|
220
|
+
|
221
|
+
choice = get_choice(
|
222
|
+
"#{@stale_session_files.size} unclosed session(s) found. Open the above files? (session #{index+1} of #{@stale_session_files.size})",
|
223
|
+
[ CHOICE_YES, CHOICE_NO, CHOICE_DELETE ],
|
224
|
+
index > 0 ? CHOICE_NO : nil
|
225
|
+
)
|
226
|
+
|
227
|
+
case choice
|
228
|
+
when CHOICE_YES
|
229
|
+
load_session session_file
|
230
|
+
if @session
|
231
|
+
File.delete session_file
|
232
|
+
break
|
233
|
+
end
|
234
|
+
when CHOICE_DELETE
|
235
|
+
File.delete session_file
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
if session_buffers.empty? && @files.empty? && @settings[ 'session.default_session' ]
|
240
|
+
session_file = session_filepath_for( @settings[ 'session.default_session' ] )
|
241
|
+
if File.exist? session_file
|
242
|
+
load_session session_file
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
session_buffers.each do |buffer|
|
247
|
+
close_buffer buffer
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def cleanup_session
|
252
|
+
if @session && Session.pid_session?(@session.filename) && File.exist?(@session.filename)
|
253
|
+
File.delete @session.filename
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
class SizedArray < Array
|
2
|
+
attr_reader :capacity
|
3
|
+
|
4
|
+
def initialize( capacity = 10, *args )
|
5
|
+
@capacity = capacity
|
6
|
+
super( *args )
|
7
|
+
end
|
8
|
+
|
9
|
+
def resize
|
10
|
+
if size > @capacity
|
11
|
+
slice!( (0...-@capacity) )
|
12
|
+
end
|
13
|
+
end
|
14
|
+
private :resize
|
15
|
+
|
16
|
+
def concat( other_array )
|
17
|
+
super( other_array )
|
18
|
+
resize
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def fill( *args )
|
23
|
+
retval = super( *args )
|
24
|
+
resize
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def <<( item )
|
29
|
+
retval = super( item )
|
30
|
+
if size > @capacity
|
31
|
+
retval = shift
|
32
|
+
end
|
33
|
+
retval
|
34
|
+
end
|
35
|
+
|
36
|
+
def push( item )
|
37
|
+
self << item
|
38
|
+
end
|
39
|
+
|
40
|
+
def unshift( item )
|
41
|
+
retval = super( item )
|
42
|
+
if size > @capacity
|
43
|
+
retval = pop
|
44
|
+
end
|
45
|
+
retval
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'diakonos/range'
|
2
|
+
|
3
|
+
module Diakonos
|
4
|
+
|
5
|
+
class TextMark
|
6
|
+
attr_reader :formatting
|
7
|
+
include RangeDelegator
|
8
|
+
|
9
|
+
def initialize( range, formatting )
|
10
|
+
@range = range
|
11
|
+
@formatting = formatting
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
"(#{@range.start_row},#{@range.start_col})-(#{@range.end_row},#{@range.end_col}) #{formatting}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|