vimmate 0.8.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.autotest +10 -0
- data/CHANGELOG +108 -0
- data/COPYING +20 -0
- data/README +221 -0
- data/Rakefile +31 -0
- data/TODO +21 -0
- data/bin/vimmate +105 -0
- data/config/environment.rb +35 -0
- data/controllers/file_filter_controller.rb +101 -0
- data/controllers/file_popup_menu_controller.rb +40 -0
- data/controllers/vim_controller.rb +28 -0
- data/controllers/vim_mate_controller.rb +76 -0
- data/images/file.png +0 -0
- data/images/file_green.png +0 -0
- data/images/file_orange.png +0 -0
- data/images/file_red.png +0 -0
- data/images/folder.png +0 -0
- data/images/folder_green.png +0 -0
- data/images/folder_orange.png +0 -0
- data/images/folder_red.png +0 -0
- data/images/processing.png +0 -0
- data/images/svn_added.png +0 -0
- data/images/svn_conflict.png +0 -0
- data/images/svn_deleted.png +0 -0
- data/images/svn_locked.png +0 -0
- data/images/svn_modified.png +0 -0
- data/images/svn_normal.png +0 -0
- data/images/svn_readonly.png +0 -0
- data/images/vimmate16.png +0 -0
- data/images/vimmate32.png +0 -0
- data/images/vimmate48.png +0 -0
- data/lib/active_window/active_column.rb +218 -0
- data/lib/active_window/active_tree_store/columns.rb +88 -0
- data/lib/active_window/active_tree_store/extentions.rb +81 -0
- data/lib/active_window/active_tree_store/index.rb +53 -0
- data/lib/active_window/active_tree_store.rb +26 -0
- data/lib/active_window/application.rb +137 -0
- data/lib/active_window/controller.rb +58 -0
- data/lib/active_window/dot_file.rb +29 -0
- data/lib/active_window/filtered_active_tree_store.rb +113 -0
- data/lib/active_window/listed_item.rb +127 -0
- data/lib/active_window/signal.rb +46 -0
- data/lib/active_window.rb +8 -0
- data/lib/config_window.rb +90 -0
- data/lib/file_tree_store.rb +74 -0
- data/lib/filtered_file_tree_store.rb +34 -0
- data/lib/gtk_thread_helper.rb +73 -0
- data/lib/listed_directory.rb +45 -0
- data/lib/listed_file.rb +67 -0
- data/lib/try.rb +9 -0
- data/lib/vim/buffers.rb +18 -0
- data/lib/vim/integration.rb +38 -0
- data/lib/vim/netbeans.rb +154 -0
- data/lib/vim/source.vim +18 -0
- data/lib/vim_mate/config.rb +132 -0
- data/lib/vim_mate/dummy_window.rb +14 -0
- data/lib/vim_mate/files_menu.rb +110 -0
- data/lib/vim_mate/icons.rb +156 -0
- data/lib/vim_mate/nice_singleton.rb +53 -0
- data/lib/vim_mate/plugins/inotify/init.rb +4 -0
- data/lib/vim_mate/plugins/inotify/lib/INotify.rb +208 -0
- data/lib/vim_mate/plugins/inotify/lib/directory.rb +58 -0
- data/lib/vim_mate/plugins/subversion/init.rb +7 -0
- data/lib/vim_mate/plugins/subversion/lib/file.rb +59 -0
- data/lib/vim_mate/plugins/subversion/lib/menu.rb +96 -0
- data/lib/vim_mate/plugins/subversion/lib/subversion.rb +157 -0
- data/lib/vim_mate/plugins.rb +6 -0
- data/lib/vim_mate/requirer.rb +68 -0
- data/lib/vim_mate/search_window.rb +227 -0
- data/lib/vim_mate/tags_window.rb +167 -0
- data/lib/vim_mate/terminals_window.rb +163 -0
- data/lib/vim_mate/version.rb +29 -0
- data/lib/vim_mate/vim_widget.rb +143 -0
- data/spec/active_window/active_column_spec.rb +41 -0
- data/spec/active_window/active_tree_store_spec.rb +312 -0
- data/spec/active_window/controller_spec.rb +6 -0
- data/spec/lib/file_tree_store_spec.rb +40 -0
- data/spec/lib/listed_directory_spec.rb +26 -0
- data/spec/lib/listed_file_spec.rb +53 -0
- data/spec/nice_singleton_spec.rb +23 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +10 -0
- data/views/vim_mate.glade +500 -0
- data/vimmate.gemspec +138 -0
- metadata +146 -0
@@ -0,0 +1,110 @@
|
|
1
|
+
=begin
|
2
|
+
= VimMate: Vim graphical add-on
|
3
|
+
Copyright (c) 2006 Guillaume Benny
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
=end
|
23
|
+
|
24
|
+
require 'fileutils'
|
25
|
+
require 'set'
|
26
|
+
module VimMate
|
27
|
+
|
28
|
+
# The pop-up menu used in the file tree
|
29
|
+
class FilesMenu
|
30
|
+
|
31
|
+
# Open a dialog to enter a new folder name to create
|
32
|
+
def menu_new_folder
|
33
|
+
dialog = Gtk::FileChooserDialog.new("New folder",
|
34
|
+
@parent_window.gtk_window,
|
35
|
+
Gtk::FileChooser::ACTION_CREATE_FOLDER,
|
36
|
+
nil,
|
37
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
38
|
+
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT])
|
39
|
+
dialog.set_icon_list(Icons.window_icons)
|
40
|
+
dialog.current_folder = if File.directory? @last_path
|
41
|
+
@last_path
|
42
|
+
else
|
43
|
+
File.dirname(@last_path)
|
44
|
+
end
|
45
|
+
dialog.run
|
46
|
+
dialog.destroy
|
47
|
+
menu_refresh
|
48
|
+
end
|
49
|
+
|
50
|
+
# Open a dialog to enter a new name for a file or directory
|
51
|
+
def menu_rename
|
52
|
+
dialog = Gtk::FileChooserDialog.new("Rename #{File.basename(@last_path)}",
|
53
|
+
@parent_window.gtk_window,
|
54
|
+
Gtk::FileChooser::ACTION_SAVE,
|
55
|
+
nil,
|
56
|
+
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL],
|
57
|
+
[Gtk::Stock::SAVE, Gtk::Dialog::RESPONSE_ACCEPT])
|
58
|
+
dialog.set_icon_list(Icons.window_icons)
|
59
|
+
dialog.current_folder = File.dirname(@last_path)
|
60
|
+
if dialog.run == Gtk::Dialog::RESPONSE_ACCEPT
|
61
|
+
begin
|
62
|
+
File.rename(@last_path, dialog.filename)
|
63
|
+
rescue SystemCallError
|
64
|
+
$stderr.puts "Cannot rename from #{@last_path} to #{dialog.filename}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
dialog.destroy
|
68
|
+
menu_refresh
|
69
|
+
end
|
70
|
+
|
71
|
+
# Open a dialog and ask the user if he really wants to delete the target
|
72
|
+
# file or directory. Note that for safety, directories can only be removed
|
73
|
+
# if they are empty.
|
74
|
+
def menu_delete
|
75
|
+
name = File.basename(@last_path)
|
76
|
+
dialog = Gtk::MessageDialog.new(@parent_window.gtk_window,
|
77
|
+
Gtk::MessageDialog::Flags::MODAL,
|
78
|
+
Gtk::MessageDialog::Type::QUESTION,
|
79
|
+
Gtk::MessageDialog::ButtonsType::YES_NO,
|
80
|
+
if File.directory? @last_path
|
81
|
+
"Delete directory #{name} ?"
|
82
|
+
else
|
83
|
+
"Delete file #{name} ?"
|
84
|
+
end)
|
85
|
+
dialog.set_icon_list(Icons.window_icons)
|
86
|
+
if dialog.run == Gtk::Dialog::RESPONSE_YES
|
87
|
+
begin
|
88
|
+
if File.directory? @last_path
|
89
|
+
FileUtils.rmdir(@last_path)
|
90
|
+
else
|
91
|
+
FileUtils.rm(@last_path)
|
92
|
+
end
|
93
|
+
rescue
|
94
|
+
$stderr.puts "Cannot remove #{@last_path}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
dialog.destroy
|
98
|
+
menu_refresh
|
99
|
+
end
|
100
|
+
|
101
|
+
# Signals that the file tree must be refreshed
|
102
|
+
def menu_refresh
|
103
|
+
@refresh_signals.each do |signal|
|
104
|
+
signal.call
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
@@ -0,0 +1,156 @@
|
|
1
|
+
=begin
|
2
|
+
= VimMate: Vim graphical add-on
|
3
|
+
Copyright (c) 2006 Guillaume Benny
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
=end
|
23
|
+
|
24
|
+
module VimMate
|
25
|
+
|
26
|
+
# Manages the icons that can be loaded from the disk
|
27
|
+
class Icons
|
28
|
+
include NiceSingleton
|
29
|
+
|
30
|
+
# The filenames for the icons of the windows
|
31
|
+
WINDOW_ICON_FILENAME = 'vimmate%d.png'.freeze
|
32
|
+
# The size for the icons of the windows
|
33
|
+
WINDOW_ICON_SIZES = [16, 32, 48].freeze
|
34
|
+
|
35
|
+
Overlays = %w(scm progress type)
|
36
|
+
|
37
|
+
# Create the Icons class. Cannot be called directly
|
38
|
+
def initialize
|
39
|
+
@gtk_window_icons = []
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get an array of icons for the windows
|
43
|
+
def window_icons
|
44
|
+
# Load them
|
45
|
+
load_window_icons
|
46
|
+
@gtk_window_icons.freeze
|
47
|
+
# Once loaded, we only need a reader
|
48
|
+
self.class.send(:define_method, :window_icons) do
|
49
|
+
@gtk_window_icons
|
50
|
+
end
|
51
|
+
# Return the value
|
52
|
+
window_icons
|
53
|
+
end
|
54
|
+
|
55
|
+
def method_missing(meth, *args, &block)
|
56
|
+
if meth.to_s =~ /_overlayed_with_/
|
57
|
+
overlay_icon(meth, *args)
|
58
|
+
elsif meth.to_s =~ /_icon$/
|
59
|
+
build_icon(meth)
|
60
|
+
else
|
61
|
+
raise NoMethodError, "method not found: #{meth}"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def free_position
|
66
|
+
@free_overlays ||= Overlays.dup
|
67
|
+
@free_overlays.pop
|
68
|
+
end
|
69
|
+
|
70
|
+
def by_name(icon_name)
|
71
|
+
send (icon_name =~ /_icon$/) ? icon_name : "#{icon_name}_icon"
|
72
|
+
end
|
73
|
+
private
|
74
|
+
|
75
|
+
# Auto-define a method with _icon for each icon's name
|
76
|
+
def build_icon(meth)
|
77
|
+
if meth.to_s =~ /^(.*)_icon$/
|
78
|
+
name = $1
|
79
|
+
path = File.join(Config.images_path, "#{name}.png")
|
80
|
+
if File.exists? path
|
81
|
+
begin
|
82
|
+
icon = Gdk::Pixbuf.new(path)
|
83
|
+
icon.freeze
|
84
|
+
# Once loaded, we only need a reader
|
85
|
+
self.class.send(:define_method, meth) do
|
86
|
+
icon
|
87
|
+
end
|
88
|
+
return icon
|
89
|
+
rescue StandardError => e
|
90
|
+
$stderr.puts e.to_s
|
91
|
+
$stderr.puts "Problem loading #{name} icon #{path}"
|
92
|
+
raise e
|
93
|
+
end
|
94
|
+
else
|
95
|
+
raise "Icon not found: #{path}"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def overlay_with(original_name,overlay_name=nil,position='south')
|
101
|
+
if overlay_name.nil?
|
102
|
+
original_name
|
103
|
+
else
|
104
|
+
"#{original_name}_#{position}_overlayed_with_#{overlay_name}"
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def overlay_icon(meth)
|
109
|
+
if meth.to_s =~ /^(.*)_(#{Overlays.join('|')})_overlayed_with_(.*)$/
|
110
|
+
original = $1
|
111
|
+
original_icon = by_name original
|
112
|
+
where = $2
|
113
|
+
overlay = $3
|
114
|
+
overlay_icon = by_name overlay
|
115
|
+
case where
|
116
|
+
when 'progress'
|
117
|
+
x = y = 1
|
118
|
+
when 'tr'
|
119
|
+
x = 7; y = 1
|
120
|
+
when 'scm'
|
121
|
+
x = 1; y = 7
|
122
|
+
when 'br'
|
123
|
+
x = y = 7
|
124
|
+
end
|
125
|
+
overlayed = original_icon.dup
|
126
|
+
overlayed.composite!(
|
127
|
+
overlay_icon,
|
128
|
+
x, y, # start region to render
|
129
|
+
8, 8, # width / height
|
130
|
+
x, y, # offset
|
131
|
+
0.5, 0.5, # scale
|
132
|
+
Gdk::Pixbuf::INTERP_BILINEAR, # interpolation
|
133
|
+
255 # alpha
|
134
|
+
)
|
135
|
+
self.class.send(:define_method, meth) do
|
136
|
+
overlayed
|
137
|
+
end
|
138
|
+
return overlayed
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Load the icons for the windows
|
143
|
+
def load_window_icons
|
144
|
+
WINDOW_ICON_SIZES.each do |size|
|
145
|
+
file = File.join(Config.lib_path, WINDOW_ICON_FILENAME % [size])
|
146
|
+
begin
|
147
|
+
@gtk_window_icons << Gdk::Pixbuf.new(file) if File.exist? file
|
148
|
+
rescue StandardError => e
|
149
|
+
$stderr.puts e.to_s
|
150
|
+
$stderr.puts "Problem loading window icon #{file}"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
=begin
|
2
|
+
= VimMate: Vim graphical add-on
|
3
|
+
Copyright (c) 2006 Guillaume Benny
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
6
|
+
this software and associated documentation files (the "Software"), to deal in
|
7
|
+
the Software without restriction, including without limitation the rights to
|
8
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
9
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
10
|
+
so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
22
|
+
=end
|
23
|
+
|
24
|
+
require 'singleton'
|
25
|
+
|
26
|
+
module VimMate
|
27
|
+
|
28
|
+
# A nicer singleton implementation. When a class mixes this module,
|
29
|
+
# it becomes a singleton and you don't have to use 'instance' to
|
30
|
+
# access the singleton's class method. For example:
|
31
|
+
# class Hello
|
32
|
+
# include VimMate::NiceSingleton
|
33
|
+
#
|
34
|
+
# def hello
|
35
|
+
# "hello"
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# Hello.hello # => "hello"
|
40
|
+
#
|
41
|
+
module NiceSingleton
|
42
|
+
def self.included(other)
|
43
|
+
if other.class == Class
|
44
|
+
other.send(:include, Singleton)
|
45
|
+
class << other
|
46
|
+
def method_missing(method, *args, &block)
|
47
|
+
self.instance.send(method, *args)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
# Version 0.3.0 (2005-09-27) by James Le Cuirot <chewi@ffaura.com>
|
2
|
+
# masks updated
|
3
|
+
# syscalls instead of /dev/inotify for linux-2.6.13 (are the archs correct?)
|
4
|
+
# start/stop methods added for threading
|
5
|
+
# ignore_dir_recursively method added
|
6
|
+
# Events class removed : not necessary
|
7
|
+
# (wd <=> dir) hashed both ways : needed for ignore
|
8
|
+
# default watch mask is IN_ALL_EVENTS
|
9
|
+
# UnsupportedPlatformError class added to deal with unsupported CPUs and OSes
|
10
|
+
#
|
11
|
+
# Version 0.2.3 (2005-01-18) by oxman
|
12
|
+
# function ignore_dir : was added
|
13
|
+
#
|
14
|
+
# Version 0.2.2 (2005-01-18) by oxman
|
15
|
+
# cleaning code (big thanks to gnome at #ruby-lang)
|
16
|
+
# rename next_event in each_event (thanks kig)
|
17
|
+
#
|
18
|
+
# Version 0.2.1 (2005-01-18) by oxman
|
19
|
+
# class Events : use real mask
|
20
|
+
#
|
21
|
+
# Version 0.2.0 (2005-01-18) by oxman
|
22
|
+
# function watch_dir : only watch
|
23
|
+
# function next_event : was added
|
24
|
+
# function watch_dir_recursively : was added
|
25
|
+
#
|
26
|
+
# Version 0.1.1 (2005-01-17) by oxman
|
27
|
+
# Correct IN_ var for inotify 0.18
|
28
|
+
|
29
|
+
module INotify
|
30
|
+
require 'rbconfig'
|
31
|
+
|
32
|
+
class UnsupportedPlatformError < RuntimeError
|
33
|
+
end
|
34
|
+
|
35
|
+
case Config::CONFIG["arch"]
|
36
|
+
|
37
|
+
when /i[3-6]86-linux/
|
38
|
+
INOTIFY_INIT = 291
|
39
|
+
INOTIFY_ADD_WATCH = 292
|
40
|
+
INOTIFY_RM_WATCH = 293
|
41
|
+
|
42
|
+
when /x86_64-linux/
|
43
|
+
INOTIFY_INIT = 253
|
44
|
+
INOTIFY_ADD_WATCH = 254
|
45
|
+
INOTIFY_RM_WATCH = 255
|
46
|
+
|
47
|
+
when /powerpc(64)?-linux/
|
48
|
+
INOTIFY_INIT = 275
|
49
|
+
INOTIFY_ADD_WATCH = 276
|
50
|
+
INOTIFY_RM_WATCH = 277
|
51
|
+
|
52
|
+
when /ia64-linux/
|
53
|
+
INOTIFY_INIT = 1277
|
54
|
+
INOTIFY_ADD_WATCH = 1278
|
55
|
+
INOTIFY_RM_WATCH = 1279
|
56
|
+
|
57
|
+
when /s390-linux/
|
58
|
+
INOTIFY_INIT = 284
|
59
|
+
INOTIFY_ADD_WATCH = 285
|
60
|
+
INOTIFY_RM_WATCH = 286
|
61
|
+
|
62
|
+
when /alpha-linux/
|
63
|
+
INOTIFY_INIT = 444
|
64
|
+
INOTIFY_ADD_WATCH = 445
|
65
|
+
INOTIFY_RM_WATCH = 446
|
66
|
+
|
67
|
+
when /sparc(64)?-linux/
|
68
|
+
INOTIFY_INIT = 151
|
69
|
+
INOTIFY_ADD_WATCH = 152
|
70
|
+
INOTIFY_RM_WATCH = 156
|
71
|
+
|
72
|
+
when /arm-linux/
|
73
|
+
INOTIFY_INIT = 316
|
74
|
+
INOTIFY_ADD_WATCH = 317
|
75
|
+
INOTIFY_RM_WATCH = 318
|
76
|
+
|
77
|
+
when /sh-linux/
|
78
|
+
INOTIFY_INIT = 290
|
79
|
+
INOTIFY_ADD_WATCH = 291
|
80
|
+
INOTIFY_RM_WATCH = 292
|
81
|
+
|
82
|
+
else raise UnsupportedPlatformError, Config::CONFIG["arch"]
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
Mask = Struct::new(:value, :name)
|
87
|
+
|
88
|
+
Masks = {
|
89
|
+
:IN_ACCESS => Mask::new(0x00000001, 'access'),
|
90
|
+
:IN_MODIFY => Mask::new(0x00000002, 'modify'),
|
91
|
+
:IN_ATTRIB => Mask::new(0x00000004, 'attrib'),
|
92
|
+
:IN_CLOSE_WRITE => Mask::new(0x00000008, 'close_write'),
|
93
|
+
:IN_CLOSE_NOWRITE => Mask::new(0x00000010, 'close_nowrite'),
|
94
|
+
:IN_OPEN => Mask::new(0x00000020, 'open'),
|
95
|
+
:IN_MOVED_FROM => Mask::new(0x00000040, 'moved_from'),
|
96
|
+
:IN_MOVED_TO => Mask::new(0x00000080, 'moved_to'),
|
97
|
+
:IN_CREATE => Mask::new(0x00000100, 'create'),
|
98
|
+
:IN_DELETE => Mask::new(0x00000200, 'delete'),
|
99
|
+
:IN_DELETE_SELF => Mask::new(0x00000400, 'delete_self'),
|
100
|
+
:IN_UNMOUNT => Mask::new(0x00002000, 'unmount'),
|
101
|
+
:IN_Q_OVERFLOW => Mask::new(0x00004000, 'q_overflow'),
|
102
|
+
:IN_IGNORED => Mask::new(0x00008000, 'ignored'),
|
103
|
+
}
|
104
|
+
|
105
|
+
Masks.each {|key, value|
|
106
|
+
const_set(key, value)
|
107
|
+
}
|
108
|
+
|
109
|
+
OrMasks = {
|
110
|
+
:IN_CLOSE => Mask::new(IN_CLOSE_WRITE.value | IN_CLOSE_NOWRITE.value, 'close'),
|
111
|
+
:IN_MOVE => Mask::new(IN_MOVED_FROM.value | IN_MOVED_TO.value, 'moved'),
|
112
|
+
:IN_ALL_EVENTS => Mask::new(IN_ACCESS.value | IN_MODIFY.value | IN_ATTRIB.value | IN_CLOSE_WRITE.value | IN_CLOSE_NOWRITE.value | IN_OPEN.value | IN_MOVED_FROM.value | IN_MOVED_TO.value | IN_DELETE.value | IN_CREATE.value | IN_DELETE_SELF.value, 'all_events')
|
113
|
+
}
|
114
|
+
|
115
|
+
OrMasks.each {|key, value|
|
116
|
+
const_set(key, value)
|
117
|
+
}
|
118
|
+
|
119
|
+
AllMasks = Masks.merge OrMasks
|
120
|
+
|
121
|
+
require 'find'
|
122
|
+
|
123
|
+
class INotify
|
124
|
+
def initialize
|
125
|
+
@wd_dir = Hash.new
|
126
|
+
@dir_wd = Hash.new
|
127
|
+
@io = IO.open(syscall(INOTIFY_INIT))
|
128
|
+
end
|
129
|
+
|
130
|
+
def close
|
131
|
+
@io.close
|
132
|
+
end
|
133
|
+
|
134
|
+
def watch_dir (dir, option = IN_ALL_EVENTS)
|
135
|
+
wd = syscall(INOTIFY_ADD_WATCH, @io.fileno, dir, option.value)
|
136
|
+
|
137
|
+
if wd >= 0
|
138
|
+
@dir_wd[dir] = wd
|
139
|
+
@wd_dir[wd] = dir
|
140
|
+
end
|
141
|
+
|
142
|
+
return wd
|
143
|
+
rescue Errno::EACCES => e
|
144
|
+
STDERR.puts e.message
|
145
|
+
end
|
146
|
+
|
147
|
+
def ignore_dir (dir)
|
148
|
+
syscall(INOTIFY_RM_WATCH, @io.fileno, @dir_wd[dir])
|
149
|
+
end
|
150
|
+
|
151
|
+
def watch_dir_recursively (dir, option = IN_ALL_EVENTS)
|
152
|
+
Find.find(dir) { |sub_dir| watch_dir(sub_dir, option) if (File::directory?(sub_dir) == true) }
|
153
|
+
end
|
154
|
+
|
155
|
+
def ignore_dir_recursively (dir)
|
156
|
+
Find.find(dir) { |sub_dir| ignore_dir(sub_dir) if (File::directory?(sub_dir) == true) }
|
157
|
+
end
|
158
|
+
|
159
|
+
def next_events
|
160
|
+
begin
|
161
|
+
read_cnt = @io.read(16)
|
162
|
+
wd, mask, cookie, len = read_cnt.unpack('lLLL')
|
163
|
+
read_cnt = @io.read(len)
|
164
|
+
filename = read_cnt.unpack('Z*')
|
165
|
+
end while (mask & IN_Q_OVERFLOW.value) != 0
|
166
|
+
|
167
|
+
events = Array.new
|
168
|
+
|
169
|
+
AllMasks.each_value do |m|
|
170
|
+
next if m.value == IN_ALL_EVENTS.value
|
171
|
+
events.push Event.new(@wd_dir[wd].to_s, filename.to_s, m.name.to_s, cookie) if (m.value & mask) != 0
|
172
|
+
end
|
173
|
+
|
174
|
+
return events
|
175
|
+
end
|
176
|
+
|
177
|
+
def each_event
|
178
|
+
loop { next_events.each { |event| yield event } }
|
179
|
+
end
|
180
|
+
|
181
|
+
def start
|
182
|
+
@thread = Thread.new { loop { next_events.each { |event| yield event } } }
|
183
|
+
end
|
184
|
+
|
185
|
+
def stop
|
186
|
+
@thread.exit
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
class Event
|
191
|
+
attr_reader :path, :filename, :type, :cookie
|
192
|
+
|
193
|
+
def initialize (path, filename, type, cookie)
|
194
|
+
@path = path
|
195
|
+
@filename = filename
|
196
|
+
@type = type
|
197
|
+
@cookie = cookie
|
198
|
+
end
|
199
|
+
|
200
|
+
def dump
|
201
|
+
"path: " + @path.to_s + ", filename: " + @filename.to_s + ", type: " + @type.to_s + ", cookie: " + @cookie.to_s
|
202
|
+
end
|
203
|
+
|
204
|
+
def to_s
|
205
|
+
dump
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module VimMate
|
2
|
+
module Plugin
|
3
|
+
module INotifyDirectory
|
4
|
+
Mask = INotify::Mask.new(
|
5
|
+
INotify::IN_MODIFY.value |
|
6
|
+
INotify::IN_DELETE.value |
|
7
|
+
INotify::IN_CREATE.value |
|
8
|
+
INotify::IN_MOVED_TO.value |
|
9
|
+
INotify::IN_MOVED_FROM.value |
|
10
|
+
0, 'filechange')
|
11
|
+
|
12
|
+
Exclusions = [ /(swp|~|rej|orig)$/, /\/\.?#/, /^\./ ]
|
13
|
+
def self.included(base)
|
14
|
+
base.class_eval do
|
15
|
+
include InstanceMethods
|
16
|
+
extend ClassMethods
|
17
|
+
alias_method_chain :initialize, :inotify
|
18
|
+
start_inotify_watcher
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
def initialize_with_inotify(*args)
|
25
|
+
initialize_without_inotify(*args)
|
26
|
+
if directory?
|
27
|
+
self.class.inotify_watcher.watch_dir(full_path, Mask)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ClassMethods
|
33
|
+
def inotify_watcher
|
34
|
+
@@inotify_watcher ||= INotify::INotify.new
|
35
|
+
end
|
36
|
+
|
37
|
+
def start_inotify_watcher
|
38
|
+
inotify_watcher.start do |event|
|
39
|
+
next if ignore_file_changes? event.filename
|
40
|
+
path = File.join(event.path, event.filename)
|
41
|
+
case event.type
|
42
|
+
when 'modify'
|
43
|
+
ActiveWindow::Signal.emit_file_modified(path)
|
44
|
+
when /^delete|moved_from$/
|
45
|
+
ActiveWindow::Signal.emit_file_deleted(path)
|
46
|
+
when /^create|moved_to$/
|
47
|
+
ActiveWindow::Signal.emit_file_created(path)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def ignore_file_changes?(filename)
|
53
|
+
Exclusions.any? { |exclusion| filename =~ exclusion }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
#VimMate::Requirer.require_if('lib/subversion') do
|
2
|
+
# require 'lib/file'
|
3
|
+
# require 'lib/menu'
|
4
|
+
# VimMate::ListedFile.class_eval { include VimMate::Plugin::SubversionFile }
|
5
|
+
# VimMate::ListedDirectory.class_eval { include VimMate::Plugin::SubversionFile }
|
6
|
+
# VimMate::FilesMenu.class_eval { include VimMate::Plugin::SubversionMenu }
|
7
|
+
#end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# All stuff for SVN handling of files
|
2
|
+
|
3
|
+
module VimMate
|
4
|
+
module Plugin
|
5
|
+
module SubversionFile
|
6
|
+
def self.included(base)
|
7
|
+
base.class_eval do
|
8
|
+
include InstanceMethods
|
9
|
+
modify_icon :svn do |file|
|
10
|
+
Icons.overlay_with file.icon_name_without_svn, file.svn_icon, 'scm'
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
module InstanceMethods
|
15
|
+
# Refresh the file. If the file status has changed, send a refresh
|
16
|
+
# signal
|
17
|
+
def refresh
|
18
|
+
status = Subversion.status(full_path)
|
19
|
+
if @last_status != status
|
20
|
+
@last_status = status
|
21
|
+
ListedTree.refreshed self
|
22
|
+
end
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
# Return the icon for this file depending on the file status
|
28
|
+
def svn_icon
|
29
|
+
status = Subversion.status(full_path)
|
30
|
+
if @last_status != status
|
31
|
+
@last_status = status
|
32
|
+
end
|
33
|
+
case status
|
34
|
+
when Subversion::UNVERSIONED, Subversion::EXTERNAL,
|
35
|
+
Subversion::IGNORED, Subversion::UNKNOWN
|
36
|
+
nil
|
37
|
+
when Subversion::NONE, Subversion::NORMAL
|
38
|
+
"svn_normal"
|
39
|
+
when Subversion::ADDED, Subversion::REPLACED
|
40
|
+
'svn_added'
|
41
|
+
when Subversion::DELETED, Subversion::MISSING
|
42
|
+
'svn_deleted'
|
43
|
+
when Subversion::MODIFIED
|
44
|
+
'svn_modified'
|
45
|
+
when Subversion::CONFLICTED
|
46
|
+
'svn_conflict'
|
47
|
+
when Subversion::MERGED, Subversion::OBSTRUCTED, Subversion::INCOMPLETE
|
48
|
+
'svn_readonly' # FIXME for now, have no better
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Return the status text for this file depending on the file status
|
53
|
+
def status
|
54
|
+
Subversion.status_text(full_path)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|