VimMate 0.6.2
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/CHANGELOG +53 -0
- data/COPYING +20 -0
- data/README +198 -0
- data/Rakefile +72 -0
- data/TODO +9 -0
- data/bin/vimmate +179 -0
- data/lib/vimmatelib/config.rb +124 -0
- data/lib/vimmatelib/dummy_window.rb +39 -0
- data/lib/vimmatelib/file.png +0 -0
- data/lib/vimmatelib/file_green.png +0 -0
- data/lib/vimmatelib/file_orange.png +0 -0
- data/lib/vimmatelib/file_red.png +0 -0
- data/lib/vimmatelib/files.rb +230 -0
- data/lib/vimmatelib/files_menu.rb +325 -0
- data/lib/vimmatelib/files_window.rb +395 -0
- data/lib/vimmatelib/folder.png +0 -0
- data/lib/vimmatelib/folder_green.png +0 -0
- data/lib/vimmatelib/folder_orange.png +0 -0
- data/lib/vimmatelib/folder_red.png +0 -0
- data/lib/vimmatelib/icons.rb +104 -0
- data/lib/vimmatelib/main_window.rb +59 -0
- data/lib/vimmatelib/nice_singleton.rb +53 -0
- data/lib/vimmatelib/requirer.rb +68 -0
- data/lib/vimmatelib/search_window.rb +220 -0
- data/lib/vimmatelib/subversion.rb +157 -0
- data/lib/vimmatelib/terminals_window.rb +115 -0
- data/lib/vimmatelib/version.rb +29 -0
- data/lib/vimmatelib/vim_window.rb +84 -0
- data/lib/vimmatelib/vimmate16.png +0 -0
- data/lib/vimmatelib/vimmate32.png +0 -0
- data/lib/vimmatelib/vimmate48.png +0 -0
- data/setup.rb +1586 -0
- metadata +77 -0
@@ -0,0 +1,59 @@
|
|
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 'gtk2'
|
25
|
+
require 'vimmatelib/config'
|
26
|
+
require 'vimmatelib/icons'
|
27
|
+
|
28
|
+
module VimMate
|
29
|
+
|
30
|
+
# Represents the main window of the application
|
31
|
+
class MainWindow
|
32
|
+
|
33
|
+
# Create the MainWindow
|
34
|
+
def initialize
|
35
|
+
@gtk_main_window = Gtk::Window.new(Gtk::Window::TOPLEVEL)
|
36
|
+
gtk_window.signal_connect("delete_event") do
|
37
|
+
Gtk.main_quit
|
38
|
+
end
|
39
|
+
gtk_window.title = Config[:window_title]
|
40
|
+
gtk_window.set_default_size(Config[:window_width],
|
41
|
+
Config[:window_height])
|
42
|
+
gtk_window.set_icon_list(Icons.window_icons)
|
43
|
+
end
|
44
|
+
|
45
|
+
# The "window" for this object
|
46
|
+
def gtk_window
|
47
|
+
@gtk_main_window
|
48
|
+
end
|
49
|
+
|
50
|
+
# Show the window and start the main loop. Also starts the
|
51
|
+
# window given in parameters. (Used for VimWindow)
|
52
|
+
def start(start_window)
|
53
|
+
gtk_window.show_all
|
54
|
+
start_window.start
|
55
|
+
Gtk.main
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
@@ -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,68 @@
|
|
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
|
+
# This module contains methods to help when requiring files that
|
27
|
+
# could not be installed on the user computer.
|
28
|
+
module Requirer
|
29
|
+
|
30
|
+
# Runs the provided block if the file can be required. If it can't,
|
31
|
+
# return the value of to_return_on_error
|
32
|
+
def self.require_if(file, to_return_on_error = false)
|
33
|
+
begin
|
34
|
+
require file
|
35
|
+
rescue LoadError
|
36
|
+
to_return_on_error
|
37
|
+
else
|
38
|
+
yield
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Runs the provided block if the file cannot be required. If it can,
|
43
|
+
# return the value of to_return_on_success
|
44
|
+
def self.require_not_if(file, to_return_on_success = false)
|
45
|
+
begin
|
46
|
+
require file
|
47
|
+
rescue LoadError
|
48
|
+
yield
|
49
|
+
else
|
50
|
+
to_return_on_success
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Exit the program with a nice message if the specified file cannot
|
55
|
+
# be required
|
56
|
+
def self.require_exit(file)
|
57
|
+
require file
|
58
|
+
rescue LoadError
|
59
|
+
puts "Module #{file} is required to run this program"
|
60
|
+
exit
|
61
|
+
end
|
62
|
+
|
63
|
+
# Raise a LoadError if the provided block returns true
|
64
|
+
def self.raise_load_error_if
|
65
|
+
raise LoadError, "Requirer cannot continue" if yield
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,220 @@
|
|
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 'gtk2'
|
25
|
+
require 'set'
|
26
|
+
|
27
|
+
module VimMate
|
28
|
+
|
29
|
+
# A window that can be used to search for files
|
30
|
+
class SearchWindow
|
31
|
+
|
32
|
+
# Column for the file name
|
33
|
+
NAME = 0
|
34
|
+
# Column for the full path of the file
|
35
|
+
PATH = 1
|
36
|
+
|
37
|
+
# Create the SearchWindow
|
38
|
+
def initialize(file_tree)
|
39
|
+
@open_signal = Set.new
|
40
|
+
@menu_signal = Set.new
|
41
|
+
@file_tree = file_tree
|
42
|
+
@filter = /.*/
|
43
|
+
|
44
|
+
# File name, Path
|
45
|
+
@gtk_list_model = Gtk::ListStore.new(String, String)
|
46
|
+
@gtk_list_model.set_sort_column_id(PATH)
|
47
|
+
@gtk_filtered_list_model = Gtk::TreeModelFilter.new(@gtk_list_model)
|
48
|
+
@gtk_filtered_list_model.set_visible_func do |model, iter|
|
49
|
+
if iter[NAME] =~ @filter
|
50
|
+
true
|
51
|
+
else
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
@gtk_list_view = Gtk::TreeView.new(@gtk_filtered_list_model)
|
57
|
+
@gtk_list_view.selection.mode = Gtk::SELECTION_SINGLE
|
58
|
+
@gtk_list_view.headers_visible = false
|
59
|
+
|
60
|
+
# Double-click, Enter, Space: Signal to open the file
|
61
|
+
@gtk_list_view.signal_connect("row-activated") do |view, path, column|
|
62
|
+
path = @gtk_filtered_list_model.get_iter(path)[PATH]
|
63
|
+
@open_signal.each do |signal|
|
64
|
+
signal.call(path,
|
65
|
+
Config[:files_default_open_in_tabs] ? :tab_open : :open)
|
66
|
+
end
|
67
|
+
@gtk_entry.text = ""
|
68
|
+
end
|
69
|
+
|
70
|
+
# Left-click: Select and Signal to open the menu
|
71
|
+
@gtk_list_view.signal_connect("button_press_event") do |widget, event|
|
72
|
+
if event.kind_of? Gdk::EventButton and event.button == 3
|
73
|
+
path = @gtk_list_view.get_path_at_pos(event.x, event.y)
|
74
|
+
@gtk_list_view.selection.select_path(path[NAME]) if path
|
75
|
+
|
76
|
+
selected = @gtk_list_view.selection.selected
|
77
|
+
if selected
|
78
|
+
@menu_signal.each do |signal|
|
79
|
+
signal.call(selected[PATH])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Same thing as Left-click, but with the keyboard
|
86
|
+
@gtk_list_view.signal_connect("popup_menu") do
|
87
|
+
selected = @gtk_list_view.selection.selected
|
88
|
+
if selected
|
89
|
+
@menu_signal.each do |signal|
|
90
|
+
signal.call(selected[PATH])
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Add the columns
|
96
|
+
column = Gtk::TreeViewColumn.new
|
97
|
+
column.title = "Files"
|
98
|
+
|
99
|
+
# File name
|
100
|
+
text_cell_renderer = Gtk::CellRendererText.new
|
101
|
+
if Config[:files_use_ellipsis]
|
102
|
+
text_cell_renderer.ellipsize = Pango::Layout::EllipsizeMode::MIDDLE
|
103
|
+
end
|
104
|
+
column.pack_start(text_cell_renderer, true)
|
105
|
+
column.set_attributes(text_cell_renderer, :text => NAME)
|
106
|
+
|
107
|
+
@gtk_list_view.append_column(column)
|
108
|
+
|
109
|
+
# Put the tree view in a scroll window
|
110
|
+
@gtk_scrolled_window = Gtk::ScrolledWindow.new
|
111
|
+
@gtk_scrolled_window.set_policy(Gtk::POLICY_AUTOMATIC,
|
112
|
+
Gtk::POLICY_AUTOMATIC)
|
113
|
+
@gtk_scrolled_window.add(@gtk_list_view)
|
114
|
+
|
115
|
+
# Create a label to show the path of the file
|
116
|
+
gtk_label = Gtk::Label.new
|
117
|
+
gtk_label.ellipsize = Pango::Layout::EllipsizeMode::START
|
118
|
+
|
119
|
+
# When a selection is changed in the list view, we change the label
|
120
|
+
# to show the path of the file and which characters matches in the
|
121
|
+
# file name
|
122
|
+
@gtk_list_view.selection.signal_connect("changed") do
|
123
|
+
gtk_label.markup = ""
|
124
|
+
# Nothing to do if there are no selections or if the entry
|
125
|
+
# is empty
|
126
|
+
next if (selected_row = @gtk_list_view.selection.selected).nil?
|
127
|
+
next if @gtk_entry.text.empty?
|
128
|
+
# Build a regexp to add markup information on the file name
|
129
|
+
match = []
|
130
|
+
Regexp.escape(@gtk_entry.text).gsub(/\\.|./) {|c| match << c}
|
131
|
+
match_regexp = Regexp.new(match.join("|"), Config[:files_search_ignore_case])
|
132
|
+
file_name_markup = selected_row[NAME].gsub(match_regexp) do |c|
|
133
|
+
"<b><i>#{c}</i></b>"
|
134
|
+
end
|
135
|
+
# Join the path and the file name with the markup
|
136
|
+
gtk_label.markup = File.join(File.dirname(selected_row[PATH]), file_name_markup)
|
137
|
+
end
|
138
|
+
|
139
|
+
# Build a box to contain the entry for the filter
|
140
|
+
gtk_filter_box = Gtk::HBox.new
|
141
|
+
gtk_filter_box.spacing = 10
|
142
|
+
gtk_filter_box.border_width = 10
|
143
|
+
gtk_filter_box.add(@gtk_entry = Gtk::Entry.new)
|
144
|
+
|
145
|
+
# When the filter changes, create a new regex to filter the file names
|
146
|
+
@gtk_entry.signal_connect("changed") do
|
147
|
+
@filter = Regexp.new(".*" + Regexp.escape(@gtk_entry.text).gsub(/\\.|./) {|c| "#{c}.*"},
|
148
|
+
Config[:files_search_ignore_case])
|
149
|
+
# Unselect everything, filter and reselect the first row
|
150
|
+
@gtk_list_view.selection.unselect_all
|
151
|
+
@gtk_filtered_list_model.refilter
|
152
|
+
if first_row = @gtk_filtered_list_model.iter_first
|
153
|
+
@gtk_list_view.selection.select_iter(first_row)
|
154
|
+
end
|
155
|
+
# Scroll at the top
|
156
|
+
@gtk_scrolled_window.vscrollbar.value = @gtk_scrolled_window.vscrollbar.adjustment.lower
|
157
|
+
end
|
158
|
+
|
159
|
+
# When we press Enter in the entry, open the first file of the list
|
160
|
+
@gtk_entry.signal_connect("activate") do
|
161
|
+
next if (first_row = @gtk_filtered_list_model.iter_first).nil?
|
162
|
+
@open_signal.each do |signal|
|
163
|
+
signal.call(first_row[PATH],
|
164
|
+
Config[:files_default_open_in_tabs] ? :tab_open : :open)
|
165
|
+
end
|
166
|
+
@gtk_entry.text = ""
|
167
|
+
end
|
168
|
+
|
169
|
+
# Add the components in a box
|
170
|
+
@gtk_container_box = Gtk::VBox.new
|
171
|
+
@gtk_container_box.pack_start(gtk_filter_box, false, false)
|
172
|
+
@gtk_container_box.pack_start(@gtk_scrolled_window, true, true)
|
173
|
+
@gtk_container_box.pack_start(gtk_label, false, false)
|
174
|
+
|
175
|
+
# Process file tree event
|
176
|
+
@file_tree.add_refresh_signal do |method, file|
|
177
|
+
next if file.instance_of? ListedDirectory
|
178
|
+
case method
|
179
|
+
when :add
|
180
|
+
# Add the new file
|
181
|
+
new_row = @gtk_list_model.append
|
182
|
+
new_row[NAME] = file.name
|
183
|
+
new_row[PATH] = file.path
|
184
|
+
when :remove
|
185
|
+
# A file is removed. Find it and remove it
|
186
|
+
to_remove = []
|
187
|
+
@gtk_list_model.each do |model,path,iter|
|
188
|
+
if iter[PATH] == file.path
|
189
|
+
to_remove << Gtk::TreeRowReference.new(model, path)
|
190
|
+
break
|
191
|
+
end
|
192
|
+
end
|
193
|
+
to_remove.each do |element|
|
194
|
+
@gtk_list_model.remove(@gtk_list_model.get_iter(element.path))
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# The "window" for this object
|
201
|
+
def gtk_window
|
202
|
+
@gtk_container_box
|
203
|
+
end
|
204
|
+
|
205
|
+
# Add a block that will be called when the user choose to open a file
|
206
|
+
# The block take two argument: the path to the file to open, and a
|
207
|
+
# symbol to indicate the kind: :open, :split_open, :tab_open
|
208
|
+
def add_open_signal(&block)
|
209
|
+
@open_signal << block
|
210
|
+
end
|
211
|
+
|
212
|
+
# Add a block that will be called when the user choose to open the
|
213
|
+
# menu. The block takes one argument: the path to the file to open.
|
214
|
+
def add_menu_signal(&block)
|
215
|
+
@menu_signal << block
|
216
|
+
end
|
217
|
+
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
@@ -0,0 +1,157 @@
|
|
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 'svn/core'
|
25
|
+
require 'svn/client'
|
26
|
+
require 'svn/wc'
|
27
|
+
require 'svn/repos'
|
28
|
+
require 'vimmatelib/config'
|
29
|
+
require 'vimmatelib/nice_singleton'
|
30
|
+
require 'vimmatelib/requirer'
|
31
|
+
|
32
|
+
module VimMate
|
33
|
+
|
34
|
+
# Do not load Subversion if it's disabled
|
35
|
+
Requirer.raise_load_error_if do
|
36
|
+
not Config[:subversion_enabled]
|
37
|
+
end
|
38
|
+
|
39
|
+
# This class helps the integration of the Subversion version control system
|
40
|
+
class Subversion
|
41
|
+
include NiceSingleton
|
42
|
+
|
43
|
+
UNKNOWN = -1
|
44
|
+
NONE = 1
|
45
|
+
UNVERSIONED = 2
|
46
|
+
NORMAL = 3
|
47
|
+
ADDED = 4
|
48
|
+
MISSING = 5
|
49
|
+
DELETED = 6
|
50
|
+
REPLACED = 7
|
51
|
+
MODIFIED = 8
|
52
|
+
MERGED = 9
|
53
|
+
CONFLICTED = 10
|
54
|
+
IGNORED = 11
|
55
|
+
OBSTRUCTED = 12
|
56
|
+
EXTERNAL = 13
|
57
|
+
INCOMPLETE = 14
|
58
|
+
|
59
|
+
STATUS_TEXT = {
|
60
|
+
UNKNOWN => "",
|
61
|
+
NONE => "None",
|
62
|
+
UNVERSIONED => "Unversioned",
|
63
|
+
NORMAL => "Normal",
|
64
|
+
ADDED => "Added",
|
65
|
+
MISSING => "Missing",
|
66
|
+
DELETED => "Deleted",
|
67
|
+
REPLACED => "Replaced",
|
68
|
+
MODIFIED => "Modified",
|
69
|
+
MERGED => "Merged",
|
70
|
+
CONFLICTED => "Conflicted",
|
71
|
+
IGNORED => "Ignored",
|
72
|
+
OBSTRUCTED => "Obstructed",
|
73
|
+
EXTERNAL => "External",
|
74
|
+
INCOMPLETE => "Incomplete",
|
75
|
+
}.freeze
|
76
|
+
|
77
|
+
# Create the Subversion class. Cannot be called directly
|
78
|
+
def initialize
|
79
|
+
end
|
80
|
+
|
81
|
+
# Get the status of the specified file. The file must be a full path.
|
82
|
+
def status(path)
|
83
|
+
ret_status = UNKNOWN
|
84
|
+
begin
|
85
|
+
# Arguments: File, Revision, Recursive, Any files, Update
|
86
|
+
new_client.status(path, "HEAD", true, true, false) do |path, status|
|
87
|
+
ret_status = status.text_status if status.text_status > ret_status
|
88
|
+
end
|
89
|
+
rescue Svn::Error::WC_NOT_DIRECTORY
|
90
|
+
end
|
91
|
+
ret_status
|
92
|
+
end
|
93
|
+
|
94
|
+
# Get the text that represents the status of the file
|
95
|
+
def status_text(path)
|
96
|
+
STATUS_TEXT[status(path)]
|
97
|
+
end
|
98
|
+
|
99
|
+
# Add the specified file (full path) to Subversion
|
100
|
+
def add(path)
|
101
|
+
cleanup(path)
|
102
|
+
new_client.add(path)
|
103
|
+
rescue
|
104
|
+
false
|
105
|
+
else
|
106
|
+
true
|
107
|
+
end
|
108
|
+
|
109
|
+
# Remove the specified file (full path) from Subversion's control
|
110
|
+
def remove(path)
|
111
|
+
cleanup(path)
|
112
|
+
new_client.remove(path)
|
113
|
+
rescue
|
114
|
+
false
|
115
|
+
else
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
# Revert the specified file (full path) to what it was before the local
|
120
|
+
# modifications
|
121
|
+
def revert(path)
|
122
|
+
cleanup(path)
|
123
|
+
new_client.revert(path)
|
124
|
+
rescue
|
125
|
+
false
|
126
|
+
else
|
127
|
+
true
|
128
|
+
end
|
129
|
+
|
130
|
+
# Move the specified file (full path) to a new file name
|
131
|
+
def move(path, new_path)
|
132
|
+
cleanup(path)
|
133
|
+
new_client.move(path, new_path)
|
134
|
+
rescue
|
135
|
+
false
|
136
|
+
else
|
137
|
+
true
|
138
|
+
end
|
139
|
+
|
140
|
+
# Cleanup the subversion path when something is locked
|
141
|
+
def cleanup(path)
|
142
|
+
new_client.cleanup(File.directory?(path) ? path : File.dirname(path))
|
143
|
+
rescue
|
144
|
+
false
|
145
|
+
else
|
146
|
+
true
|
147
|
+
end
|
148
|
+
|
149
|
+
private
|
150
|
+
|
151
|
+
# Create a new subversion client
|
152
|
+
def new_client
|
153
|
+
Svn::Client::Context.new
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
157
|
+
end
|