hilfer 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +6 -0
- data/README.txt +49 -0
- data/bin/ec +9 -0
- data/bin/hilfer +260 -0
- data/bin/hilfer-icon.png +0 -0
- data/bin/sd +17 -0
- data/lib/hilfer/rails_locator.rb +150 -0
- data/lib/hilfer/scite_editor.rb +182 -0
- data/lib/hilfer/svn_colours.rb +58 -0
- data/lib/hilfer/tree_viewer.rb +805 -0
- metadata +75 -0
data/History.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
= hilfer
|
2
|
+
|
3
|
+
* http://www.semiosix.com/hilfer
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Directory browser with plenty of keyboard shortcuts and integration with scite
|
8
|
+
rails and subversion
|
9
|
+
|
10
|
+
== FEATURES/PROBLEMS:
|
11
|
+
|
12
|
+
See TODO list
|
13
|
+
|
14
|
+
== SYNOPSIS:
|
15
|
+
|
16
|
+
hilfer [directory]
|
17
|
+
|
18
|
+
== REQUIREMENTS:
|
19
|
+
|
20
|
+
ruby-gtk2
|
21
|
+
|
22
|
+
== INSTALL:
|
23
|
+
|
24
|
+
sudo gem install
|
25
|
+
|
26
|
+
== LICENSE:
|
27
|
+
|
28
|
+
(The MIT License)
|
29
|
+
|
30
|
+
Copyright (c) 2008 FIX
|
31
|
+
|
32
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
33
|
+
a copy of this software and associated documentation files (the
|
34
|
+
'Software'), to deal in the Software without restriction, including
|
35
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
36
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
37
|
+
permit persons to whom the Software is furnished to do so, subject to
|
38
|
+
the following conditions:
|
39
|
+
|
40
|
+
The above copyright notice and this permission notice shall be
|
41
|
+
included in all copies or substantial portions of the Software.
|
42
|
+
|
43
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
44
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
45
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
46
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
47
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
48
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
49
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/bin/ec
ADDED
data/bin/hilfer
ADDED
@@ -0,0 +1,260 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'yaml'
|
4
|
+
require 'pathname'
|
5
|
+
|
6
|
+
require 'hilfer/tree_viewer.rb'
|
7
|
+
|
8
|
+
require 'optparse'
|
9
|
+
|
10
|
+
# defaults
|
11
|
+
$options = { :debug => false, :hidden => false, :auto_sync => false, :quit_editor => false }
|
12
|
+
|
13
|
+
oparser = OptionParser.new
|
14
|
+
oparser.on( '-y', '--auto-sync', 'track editor current file changes') { |o| $options[:host] = true }
|
15
|
+
oparser.on( '-s', '--show-hidden', 'Show hidden files' ) { |o| $options[:user] = true }
|
16
|
+
oparser.on( '-D', '-d', '-v', '--debug' ) { |o| $options[:debug] = true }
|
17
|
+
oparser.on( '-q', '--quit-editor', 'close editor on shutdown' ) { |o| puts "o: #{o.inspect}"; $options[:quit_editor] = true }
|
18
|
+
|
19
|
+
oparser.on( '-h', '-?', '--help' ) do |o|
|
20
|
+
puts oparser.to_s
|
21
|
+
exit( 0 )
|
22
|
+
end
|
23
|
+
|
24
|
+
# remove parsed options
|
25
|
+
ARGV = args = oparser.parse( ARGV )
|
26
|
+
|
27
|
+
# this must come after options parsing or it tries to eat the options
|
28
|
+
require 'gtk2'
|
29
|
+
|
30
|
+
# NOTE no space after comma, trailing comma is so
|
31
|
+
# that bold, italic etc and font size can be specified
|
32
|
+
FIXED_FONT = "Lucida Console,Courier New,"
|
33
|
+
CONFIG_FILE = File.expand_path( '~/.hilfer' )
|
34
|
+
|
35
|
+
=begin rdoc
|
36
|
+
HilferItems are stored in the TreeModel
|
37
|
+
- item is the display name (ie filename and extension)
|
38
|
+
- path is the full pathname, expanded, unqualified etc etc
|
39
|
+
- last_child_used is the GtkTreeView path of the last child of a directory
|
40
|
+
node that had selection
|
41
|
+
- status is either :expanded or :collapsed
|
42
|
+
- populated is whether or not this directory node has been populated
|
43
|
+
from the filesystem. To aid in recursively populating the tree
|
44
|
+
=end
|
45
|
+
class HilferItem
|
46
|
+
attr_accessor :item, :path, :last_child_used, :status, :svn_status, :colour
|
47
|
+
attr_writer :populated
|
48
|
+
|
49
|
+
def initialize( item, path )
|
50
|
+
@item = item
|
51
|
+
@path = path
|
52
|
+
@dir = File.directory? path
|
53
|
+
@populated = false
|
54
|
+
@status = :collapsed
|
55
|
+
@colour = '#000'
|
56
|
+
end
|
57
|
+
|
58
|
+
def dir?
|
59
|
+
@dir
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
@item
|
64
|
+
end
|
65
|
+
|
66
|
+
# always return true for file nodes
|
67
|
+
def populated?
|
68
|
+
if !dir?
|
69
|
+
true
|
70
|
+
else
|
71
|
+
@populated
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# always return false for file nodes
|
76
|
+
def expanded?
|
77
|
+
if !dir?
|
78
|
+
false
|
79
|
+
else
|
80
|
+
@status == :expanded
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
=begin rdoc
|
87
|
+
Knows how to lauch gnome-terminal with mutiple tabs,
|
88
|
+
each opened on a different directory
|
89
|
+
=end
|
90
|
+
class GnomeTerminal
|
91
|
+
def launch( dirs )
|
92
|
+
# construct arguments and launch
|
93
|
+
args = [ "gnome-terminal" ]
|
94
|
+
dirs.uniq.each do |x|
|
95
|
+
# first one will be a window, subsequent ones are tabs
|
96
|
+
args << ( args.size == 1 ? '--window' : '--tab' )
|
97
|
+
args << "--working-directory=#{x}"
|
98
|
+
end
|
99
|
+
system( *args )
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
=begin rdoc
|
104
|
+
Just launches xterm. I couldn't figure out how to open it in different
|
105
|
+
directories.
|
106
|
+
|
107
|
+
use it by saying
|
108
|
+
|
109
|
+
$options[:terminal] = Xterm.new
|
110
|
+
|
111
|
+
before the call to TreeViewer.new
|
112
|
+
=end
|
113
|
+
class Xterm
|
114
|
+
def launch( dirs )
|
115
|
+
system( 'xterm &' )
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# yes, this actually does catch method calls polymorphically
|
120
|
+
# but you can't change the TreePath or TreeIter that are returned
|
121
|
+
class HilferModel < Gtk::TreeStore
|
122
|
+
def initialize( *args )
|
123
|
+
super
|
124
|
+
end
|
125
|
+
|
126
|
+
def ancestor?( iter, descendant )
|
127
|
+
print "testing #{iter[0]} against #{descendant}\n" if $options[:debug]
|
128
|
+
super
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
=begin rdoc
|
133
|
+
Read and write configuration to ~/.hilfer in yaml.
|
134
|
+
Currently only writes window position
|
135
|
+
=end
|
136
|
+
class HilferConfig
|
137
|
+
|
138
|
+
def initialize( file, window, count )
|
139
|
+
@file, @window = file, window
|
140
|
+
|
141
|
+
if File.file? CONFIG_FILE
|
142
|
+
config = YAML.load_file CONFIG_FILE
|
143
|
+
@window.set_default_size( *config[:size] )
|
144
|
+
@window.show_all
|
145
|
+
# move must be after show_all otherwise it
|
146
|
+
# gets overridden
|
147
|
+
@window.move( *config[:position] ) if count == 1
|
148
|
+
else
|
149
|
+
@window.set_default_size( 300,700 ).show_all
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# save config
|
154
|
+
def save
|
155
|
+
config = {
|
156
|
+
:path => @window.title,
|
157
|
+
:position => @window.position,
|
158
|
+
:size => @window.size
|
159
|
+
}
|
160
|
+
File.open( CONFIG_FILE, 'w' ) { |file| file.print config.to_yaml }
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
=begin rdoc
|
166
|
+
Layout of the Tree Viewer Window.
|
167
|
+
=end
|
168
|
+
class TreeViewerWindow
|
169
|
+
@@window_count = 0
|
170
|
+
|
171
|
+
def initialize( root_fs_path, editor )
|
172
|
+
@window = Gtk::Window.new
|
173
|
+
|
174
|
+
# initialize viewer with a path
|
175
|
+
@editor = editor
|
176
|
+
|
177
|
+
# location bar
|
178
|
+
@location = Gtk::Entry.new
|
179
|
+
|
180
|
+
@tv = TreeViewer.new( root_fs_path, editor, @window, @location )
|
181
|
+
|
182
|
+
# add scrollbars
|
183
|
+
@scroll = Gtk::ScrolledWindow.new( nil, nil )
|
184
|
+
@scroll.set_policy( Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC )
|
185
|
+
@scroll.add( @tv.view )
|
186
|
+
|
187
|
+
# expand and fill
|
188
|
+
@vbox = Gtk::VBox.new( false )
|
189
|
+
@vbox.pack_start( @location, false, false )
|
190
|
+
@vbox.pack_start( @scroll, true, true )
|
191
|
+
@vbox.focus_chain = [@scroll, @location]
|
192
|
+
|
193
|
+
icon_path = find_icon_path
|
194
|
+
if icon_path != nil
|
195
|
+
@window.icon = Gdk::Pixbuf.new( icon_path )
|
196
|
+
end
|
197
|
+
@window.title = 'Hilfer ' + @tv.root_item.path
|
198
|
+
@window.add( @vbox )
|
199
|
+
|
200
|
+
# read config. OK, the last parameter is a hack
|
201
|
+
# but we need it so that new windows aren't show in
|
202
|
+
# exactly the same position and it looks like nothing
|
203
|
+
# has happened
|
204
|
+
@@window_count += 1
|
205
|
+
@hc = HilferConfig.new( CONFIG_FILE, @window, @@window_count )
|
206
|
+
|
207
|
+
# set first line selected
|
208
|
+
@tv.select_first
|
209
|
+
|
210
|
+
# The program will directly end upon 'delete_event', ie window close
|
211
|
+
@window.signal_connect( 'delete_event' ) do
|
212
|
+
# unregister view from editor
|
213
|
+
@editor.unregister_view( @tv )
|
214
|
+
# finish
|
215
|
+
@@window_count -= 1
|
216
|
+
if @@window_count == 0
|
217
|
+
@hc.save
|
218
|
+
# this MUST come before cleanup, otherwise
|
219
|
+
# there will be no pipes to write the command to
|
220
|
+
@editor.quit
|
221
|
+
# delete command pipe files
|
222
|
+
@editor.cleanup
|
223
|
+
Gtk.main_quit
|
224
|
+
false
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
# to allow the sd command to find the correct editor instance
|
229
|
+
@window.signal_connect 'focus-in-event' do |widget,event|
|
230
|
+
@editor.write_pipe_name
|
231
|
+
false
|
232
|
+
end
|
233
|
+
|
234
|
+
# to allow the sd command to find the correct editor instance
|
235
|
+
@window.signal_connect 'focus-out-event' do |widget,event|
|
236
|
+
@editor.write_pipe_name
|
237
|
+
false
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def find_icon_path
|
242
|
+
$LOAD_PATH.grep( /hilfer/ ).each do |path|
|
243
|
+
png_file = Pathname.new( path ) + 'hilfer-icon.png'
|
244
|
+
return png_file.to_s if png_file.exist?
|
245
|
+
end
|
246
|
+
nil
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
|
251
|
+
################
|
252
|
+
# Main script
|
253
|
+
|
254
|
+
tvw = TreeViewerWindow.new(
|
255
|
+
File.expand_path( ARGV[0] || '.' ),
|
256
|
+
SciteEditor.new
|
257
|
+
)
|
258
|
+
|
259
|
+
# and off we go...
|
260
|
+
Gtk.main
|
data/bin/hilfer-icon.png
ADDED
Binary file
|
data/bin/sd
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#! /bin/bash
|
2
|
+
pipe=`cat /tmp/$USER.hilfer.scite`
|
3
|
+
director=/tmp/$USER.scite.director
|
4
|
+
wd=`pwd`
|
5
|
+
if [ -e $pipe ]; then
|
6
|
+
for name in $@; do
|
7
|
+
echo open:$wd/$name >$pipe;
|
8
|
+
done
|
9
|
+
else
|
10
|
+
test -e $pipe && /bin/rm $pipe
|
11
|
+
test -e $director && /bin/rm $director
|
12
|
+
/usr/bin/mkfifo $director
|
13
|
+
/usr/bin/scite \
|
14
|
+
-ipc.director.name=$director \
|
15
|
+
-ipc.scite.name=$pipe \
|
16
|
+
$@ &
|
17
|
+
fi
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
=begin
|
4
|
+
This handles various keypresses that jump to specific
|
5
|
+
locations in a Rails project. If a specific file isn't
|
6
|
+
selected, the jump will go to the directory. Otherwise
|
7
|
+
it will go to a specific file.
|
8
|
+
|
9
|
+
For example, if user_controller.rb is currently selected
|
10
|
+
and Ctrl-Shift-T is pressed, user_controller_test.rb will
|
11
|
+
be selected.
|
12
|
+
|
13
|
+
TODO fix multiple selections
|
14
|
+
=end
|
15
|
+
class RailsLocator
|
16
|
+
attr_accessor :treeviewer
|
17
|
+
|
18
|
+
def initialize( treeviewer )
|
19
|
+
@treeviewer = treeviewer
|
20
|
+
|
21
|
+
@controller_re = %r|^(.*?)/app/controllers/(.*?)/?(.*?)_controller.rb$|
|
22
|
+
@view_re = %r|^(.*?)/app/views/(.*?)/?([^/.]*?\..*?)?$|
|
23
|
+
@model_re = %r|^(.*?)/app/models/(.*?/?)([^/.]*?).rb$|
|
24
|
+
@unit_test_re = %r|^(.*?)/test/unit/(.*?/?)(.*?)_test.rb$|
|
25
|
+
@functional_test_re = %r|^(.*?)/test/functional/(.*?/?)(.*?)_controller_test.rb$|
|
26
|
+
@fixture_re = %r|^(.*?)/test/fixtures/(.*?/?)(.*?).yml$|
|
27
|
+
end
|
28
|
+
|
29
|
+
# this does the common code for each keypress
|
30
|
+
# the block should return a related path for
|
31
|
+
# the particular keypress on the currently selected
|
32
|
+
# item
|
33
|
+
def select_related_paths( widget, event, &block )
|
34
|
+
items = []
|
35
|
+
paths_to_select = []
|
36
|
+
widget.selection.selected_each do
|
37
|
+
|model, path, iter|
|
38
|
+
item = model.get_value( iter, 0 )
|
39
|
+
puts "item: #{item.path}" if $options[:debug]
|
40
|
+
new_rails_path = yield( item )
|
41
|
+
puts "new_rails_path #{new_rails_path}" if $options[:debug]
|
42
|
+
paths_to_select << rails_root + new_rails_path
|
43
|
+
end
|
44
|
+
|
45
|
+
treeviewer.select_fs_paths( paths_to_select, true )
|
46
|
+
end
|
47
|
+
|
48
|
+
def rails_root
|
49
|
+
treeviewer.root_item.path
|
50
|
+
end
|
51
|
+
|
52
|
+
def goto_rails_path( fs_path )
|
53
|
+
treeviewer.select_fs_path( rails_root + fs_path, true )
|
54
|
+
end
|
55
|
+
|
56
|
+
def handle_keypress( widget, event )
|
57
|
+
retval = true
|
58
|
+
case
|
59
|
+
# Shift-Ctrl-V - go to view, or view dir
|
60
|
+
when event.hardware_keycode == 55 && event.state.control_mask? && event.state.shift_mask?
|
61
|
+
select_related_paths( widget, event ) do |item|
|
62
|
+
case
|
63
|
+
when @controller_re.match( item.path )
|
64
|
+
'/app/views/' + $2 + $3
|
65
|
+
when @functional_test_re.match( item.path )
|
66
|
+
'/app/views/' + $2 + $3
|
67
|
+
else
|
68
|
+
'/app/views'
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Shift-Ctrl-C - go to controller, or controller dir
|
73
|
+
when event.hardware_keycode == 54 && event.state.control_mask? && event.state.shift_mask?
|
74
|
+
select_related_paths( widget, event ) do |item|
|
75
|
+
case
|
76
|
+
when md = @view_re.match( item.path )
|
77
|
+
'/app/controllers/' + $2 + '_controller.rb'
|
78
|
+
when md = @functional_test_re.match( item.path )
|
79
|
+
'/app/controllers/' + $2 + $3 + '_controller.rb'
|
80
|
+
else
|
81
|
+
'/app/controllers'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Shift-Ctrl-M - go to model, or model dir
|
86
|
+
when event.hardware_keycode == 58 && event.state.control_mask? && event.state.shift_mask?
|
87
|
+
select_related_paths( widget, event ) do |item|
|
88
|
+
case
|
89
|
+
when md = @unit_test_re.match( item.path )
|
90
|
+
'/app/models' + $2 + '/' + $3 + '.rb'
|
91
|
+
when md = @fixture_re.match( item.path )
|
92
|
+
'/app/models' + $2 + '/' + $3.singularize + '.rb'
|
93
|
+
else
|
94
|
+
'/app/models'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Shift-Ctrl-T - go to test, or test dir
|
99
|
+
when event.hardware_keycode == 28 && event.state.control_mask? && event.state.shift_mask?
|
100
|
+
select_related_paths( widget, event ) do |item|
|
101
|
+
case
|
102
|
+
when md = @model_re.match( item.path )
|
103
|
+
'/test/unit/' + $2 + $3 + '_test.rb'
|
104
|
+
when md = @view_re.match( item.path )
|
105
|
+
'/test/functional/' + $2 + '_controller_test.rb'
|
106
|
+
when md = @controller_re.match( item.path )
|
107
|
+
'/test/functional/' + $2 + $3 + '_controller_test.rb'
|
108
|
+
when md = @fixture_re.match( item.path )
|
109
|
+
'/test/unit/' + $2 + $3.singularize + '_test.rb'
|
110
|
+
else
|
111
|
+
'/test'
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Shift-Ctrl-H - go to helpers dir
|
116
|
+
when event.hardware_keycode == 43 && event.state.control_mask? && event.state.shift_mask?
|
117
|
+
goto_rails_path '/app/helpers'
|
118
|
+
|
119
|
+
# Shift-Ctrl-L - go to layouts dir
|
120
|
+
when event.hardware_keycode == 46 && event.state.control_mask? && event.state.shift_mask?
|
121
|
+
goto_rails_path '/app/views/layouts'
|
122
|
+
|
123
|
+
# Shift-Ctrl-I - go to migrations
|
124
|
+
when event.hardware_keycode == 31 && event.state.control_mask? && event.state.shift_mask?
|
125
|
+
goto_rails_path '/db/migrate'
|
126
|
+
|
127
|
+
# Shift-Ctrl-S - go to stylesheets
|
128
|
+
when event.hardware_keycode == 39 && event.state.control_mask? && event.state.shift_mask?
|
129
|
+
goto_rails_path '/public/stylesheets'
|
130
|
+
|
131
|
+
# Shift-Ctrl-F - go to fixtures
|
132
|
+
when event.hardware_keycode == 41 && event.state.control_mask? && event.state.shift_mask?
|
133
|
+
select_related_paths( widget, event ) do |item|
|
134
|
+
case
|
135
|
+
when md = @model_re.match( item.path )
|
136
|
+
'/test/fixtures' + $2 + '/' + $3.pluralize + '.yml'
|
137
|
+
when md = @unit_test_re.match( item.path )
|
138
|
+
'/test/fixtures' + $2 + '/' + $3.pluralize + '.yml'
|
139
|
+
else
|
140
|
+
'/test/fixtures'
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
else
|
145
|
+
retval = false
|
146
|
+
end
|
147
|
+
retval
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
# handle director interface to scite
|
2
|
+
class SciteEditor
|
3
|
+
|
4
|
+
# options can contain :debug
|
5
|
+
# view is a GtkTreeView
|
6
|
+
def initialize( options = {} )
|
7
|
+
@scite_pipe_name = "/tmp/#{ENV['USER']}.#{Process.pid}.scite"
|
8
|
+
@director_pipe_name = "/tmp/#{ENV['USER']}.#{Process.pid}.director"
|
9
|
+
|
10
|
+
# delete these if they already exist because we
|
11
|
+
# need to use their existence to determine if scite
|
12
|
+
# is running
|
13
|
+
File.exists?( @scite_pipe_name ) && File.unlink( @scite_pipe_name )
|
14
|
+
File.exists?( @director_pipe_name ) && File.unlink( @director_pipe_name )
|
15
|
+
|
16
|
+
# this is an array of TreeViewer objects
|
17
|
+
@views = []
|
18
|
+
# the command-line options
|
19
|
+
@options = options
|
20
|
+
end
|
21
|
+
|
22
|
+
def cleanup
|
23
|
+
FileUtils.rm @scite_pipe_name if File.exist? @scite_pipe_name
|
24
|
+
FileUtils.rm @director_pipe_name if File.exist? @director_pipe_name
|
25
|
+
FileUtils.rm pipe_name_file if File.exist? pipe_name_file
|
26
|
+
end
|
27
|
+
|
28
|
+
# send some command
|
29
|
+
def send( cmd, arg = '' )
|
30
|
+
launch
|
31
|
+
File.open( @scite_pipe_name, 'a' ) do |file|
|
32
|
+
file.print( cmd + ":" + arg.to_s, "\n" )
|
33
|
+
puts "sending: #{cmd}:#{arg.to_s}" if $options[:debug]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# open files in scite
|
38
|
+
def open_action( files )
|
39
|
+
files.each { |x| send "open", x.path }
|
40
|
+
send 'identity',0
|
41
|
+
end
|
42
|
+
|
43
|
+
def dump
|
44
|
+
%w{dyn local user base embed}.each {|x| send 'enumproperties', x}
|
45
|
+
#~ send 'askproperty','dyn:CurrentWord'
|
46
|
+
end
|
47
|
+
|
48
|
+
# fetch the current file in scite
|
49
|
+
def synchronize_path
|
50
|
+
send 'askfilename'
|
51
|
+
end
|
52
|
+
|
53
|
+
# shut down editor
|
54
|
+
def quit
|
55
|
+
send 'quit'
|
56
|
+
end
|
57
|
+
|
58
|
+
# insert text to editor, at current caret, or overwriting the current selection
|
59
|
+
def insert( arg )
|
60
|
+
value =
|
61
|
+
case
|
62
|
+
# note use of single quote - SciTE wants escaped characters
|
63
|
+
when arg.respond_to?( :join ); arg.join( '\\n' ) + '\\n'
|
64
|
+
else; arg.to_s
|
65
|
+
end
|
66
|
+
send 'insert', value
|
67
|
+
end
|
68
|
+
|
69
|
+
# send selection to all registered views
|
70
|
+
def set_selection ( fs_path )
|
71
|
+
@views.each { |v| v.synchronise_editor_path( fs_path ) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def register_view( view )
|
75
|
+
@views << view
|
76
|
+
end
|
77
|
+
|
78
|
+
def unregister_view( view )
|
79
|
+
@views.delete( view )
|
80
|
+
end
|
81
|
+
|
82
|
+
def pipe_name_file
|
83
|
+
@pipe_name_file ||= "/tmp/#{ENV['USER']}.hilfer.scite"
|
84
|
+
end
|
85
|
+
|
86
|
+
def write_pipe_name
|
87
|
+
File.open( pipe_name_file, 'w' ) do |f|
|
88
|
+
f.write @scite_pipe_name
|
89
|
+
f.write "\n"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
protected
|
94
|
+
|
95
|
+
# start up the editor if there isn't already one
|
96
|
+
# calling it when the editor is already open does nothing
|
97
|
+
def launch
|
98
|
+
# create the director pipe if it isn't there already
|
99
|
+
if !File.exists?( @director_pipe_name )
|
100
|
+
system( "mkfifo #{@director_pipe_name}" )
|
101
|
+
end
|
102
|
+
|
103
|
+
# scite already open, so don't launch another instance
|
104
|
+
return if File.exists?( @scite_pipe_name )
|
105
|
+
|
106
|
+
cmd = <<EOF
|
107
|
+
/usr/bin/scite
|
108
|
+
-ipc.director.name=#{@director_pipe_name}
|
109
|
+
-ipc.scite.name=#{@scite_pipe_name}
|
110
|
+
EOF
|
111
|
+
oneline = cmd.gsub( "\n", " ")
|
112
|
+
child_pid = fork
|
113
|
+
if child_pid.nil?
|
114
|
+
system( oneline )
|
115
|
+
|
116
|
+
# scite ended, so delete the pipes
|
117
|
+
File.unlink @director_pipe_name if File.exist? @director_pipe_name
|
118
|
+
File.unlink @scite_pipe_name if File.exist? @scite_pipe_name
|
119
|
+
|
120
|
+
# unfork, don't trigger a SystemException
|
121
|
+
exit! true
|
122
|
+
else
|
123
|
+
# start the listener thread
|
124
|
+
start_listener
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def start_listener
|
129
|
+
|
130
|
+
sleep 0.1 while !File.exists? @scite_pipe_name
|
131
|
+
|
132
|
+
Thread.new( self ) do |arg|
|
133
|
+
begin
|
134
|
+
arg.listen
|
135
|
+
rescue Errno::ENOENT
|
136
|
+
break
|
137
|
+
rescue Exception => e
|
138
|
+
print "listener thread ended: ", e.inspect, "\n"
|
139
|
+
e.backtrace.each { |x| print x, "\n" }
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
|
145
|
+
# respond to strings from scite
|
146
|
+
def listen
|
147
|
+
File.open( @director_pipe_name ).each( "\x0" ) do |line|
|
148
|
+
begin
|
149
|
+
line = line.slice(0...-1) if line[-1] = 0
|
150
|
+
print "heard #{line}\n" if $options[:debug]
|
151
|
+
case line
|
152
|
+
# scite sends one of these for each file opened
|
153
|
+
when /^opened:(.*)$/
|
154
|
+
set_selection( $1 )
|
155
|
+
|
156
|
+
# scite sends one of these each time the buffer is switched
|
157
|
+
when /^switched:(.*)$/
|
158
|
+
set_selection( $1 )
|
159
|
+
|
160
|
+
# response to askfilename, as sent by synchronize_path
|
161
|
+
when /^filename:(.*)$/
|
162
|
+
set_selection( $1 )
|
163
|
+
|
164
|
+
# the specified file has just been saved. Do nothing.
|
165
|
+
# TODO could check that it exists and add it if not.
|
166
|
+
when /^saved:(.*)$/
|
167
|
+
|
168
|
+
when /^closed$/
|
169
|
+
puts "SciTE closing"
|
170
|
+
break
|
171
|
+
|
172
|
+
# print it out
|
173
|
+
else
|
174
|
+
print "unknown: ", line, "\n" if $options[:debug]
|
175
|
+
end
|
176
|
+
rescue Exception => e
|
177
|
+
print "caught Exception in listen: ", e.inspect, "\n"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
end
|