textmate 0.9.2 → 0.9.6
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/Rakefile +3 -2
- data/bin/textmate +137 -56
- metadata +8 -5
data/Rakefile
CHANGED
@@ -3,7 +3,7 @@ require 'rake/gempackagetask'
|
|
3
3
|
require 'date'
|
4
4
|
|
5
5
|
GEM = "textmate"
|
6
|
-
GEM_VERSION = "0.9.
|
6
|
+
GEM_VERSION = "0.9.6"
|
7
7
|
AUTHOR = "Yehuda Katz"
|
8
8
|
EMAIL = "wycats@gmail.com"
|
9
9
|
HOMEPAGE = "http://yehudakatz.com"
|
@@ -22,7 +22,8 @@ spec = Gem::Specification.new do |s|
|
|
22
22
|
s.homepage = HOMEPAGE
|
23
23
|
|
24
24
|
s.add_dependency "thor", ">= 0.9.2"
|
25
|
-
|
25
|
+
|
26
|
+
s.require_path = 'bin' # Yes, it's a hack, but otherwise gem complains on install
|
26
27
|
s.autorequire = GEM
|
27
28
|
s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{bin,specs}/**/*")
|
28
29
|
s.bindir = "bin"
|
data/bin/textmate
CHANGED
@@ -5,88 +5,133 @@ require "rubygems"
|
|
5
5
|
require "thor"
|
6
6
|
require "open-uri"
|
7
7
|
require "yaml"
|
8
|
+
require "cgi"
|
8
9
|
|
9
10
|
class TextmateInstaller < Thor
|
10
|
-
|
11
|
+
|
11
12
|
# CHANGED: renamed list to remote. Could there be a better name?
|
12
|
-
desc "
|
13
|
-
def
|
13
|
+
desc "search [SEARCH]", "Lists all the matching remote bundles"
|
14
|
+
def search(search_term = "")
|
14
15
|
search_term = Regexp.new(".*#{search_term}.*", "i")
|
15
|
-
|
16
|
-
|
16
|
+
|
17
|
+
REMOTE_LOCATIONS.each do |name,location|
|
17
18
|
puts "\n" << name.to_s << " Remote Bundles\n" << name.to_s.gsub(/./,'-') << '---------------'
|
18
|
-
|
19
|
+
|
19
20
|
results = case location[:scm]
|
20
21
|
when :svn
|
21
22
|
%x[svn list #{e_sh location[:url]}].map {|x| x.split(".")[0]}.select {|x| x =~ search_term}.join("\n")
|
22
23
|
when :git
|
23
24
|
'git remotes not implemented yet'
|
24
25
|
when :github
|
25
|
-
find_github_bundles(search_term).map{|result|
|
26
|
+
find_github_bundles(search_term).map {|result|
|
27
|
+
"%s (by %s)" %
|
28
|
+
[
|
29
|
+
normalize_github_repo_name(result['name']).split('.').first,
|
30
|
+
result['username']
|
31
|
+
]
|
32
|
+
}
|
26
33
|
end
|
27
|
-
|
34
|
+
|
28
35
|
puts results
|
29
36
|
end
|
30
37
|
end
|
31
|
-
|
38
|
+
|
32
39
|
desc "list [SEARCH]", "lists all the bundles installed locally"
|
33
40
|
def list(search_term = "")
|
34
41
|
search_term = Regexp.new(".*#{search_term}.*", "i")
|
35
|
-
|
42
|
+
|
36
43
|
local_bundle_paths.each do |name,bundles_path|
|
37
44
|
puts "\n" << name.to_s << " Bundles\n" << name.to_s.gsub(/./,'-') << '--------'
|
38
45
|
puts Dir["#{e_sh bundles_path}/*.tmbundle"].map {|x| x.split("/").last.split(".").first}.
|
39
46
|
select {|x| x =~ search_term}.join("\n")
|
40
47
|
end
|
41
48
|
end
|
42
|
-
|
43
|
-
desc "install NAME
|
44
|
-
|
49
|
+
|
50
|
+
desc "install NAME", "Install a bundle. Source must be one of trunk, review, or github. \n" \
|
51
|
+
"If multiple gems with the same name exist, you will be prompted to \n" \
|
52
|
+
"choose from the available list."
|
53
|
+
method_option :source
|
54
|
+
def install(bundle_name)
|
45
55
|
FileUtils.mkdir_p install_bundles_path
|
46
56
|
puts "Checking out #{bundle_name}..."
|
47
|
-
|
57
|
+
|
48
58
|
# CHANGED: It's faster to just try and fail for each repo than to search them all first
|
49
59
|
installed=false
|
50
|
-
|
51
|
-
next unless
|
52
|
-
|
60
|
+
REMOTE_LOCATIONS.each do |remote_name,location|
|
61
|
+
next unless remote_bundle(options["source"]) == location if options.key?("source")
|
62
|
+
|
53
63
|
cmd = case location[:scm]
|
54
64
|
when :git
|
55
65
|
'echo "git remotes not implemented yet"'
|
56
66
|
when :svn
|
57
|
-
%[svn co #{
|
67
|
+
%[svn co "#{location[:url]}/#{url_escape bundle_name}.tmbundle" #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
|
58
68
|
when :github
|
59
|
-
|
60
|
-
|
69
|
+
repos = find_github_bundles(denormalize_github_repo_name(bundle_name))
|
70
|
+
|
71
|
+
# Handle possible multiple Repos with the same name
|
72
|
+
case repos.size
|
73
|
+
when 0
|
74
|
+
'echo "Sorry, no such bundle found"'
|
75
|
+
when 1
|
76
|
+
git_clone(repos.first["username"], repos.first["name"])
|
77
|
+
else
|
78
|
+
puts "Multiple bundles with that name found. Please choose which one you want to install:"
|
79
|
+
repos.each_with_index {|repo, idx|
|
80
|
+
puts "%d: %s by %s" %
|
81
|
+
[
|
82
|
+
idx + 1,
|
83
|
+
normalize_github_repo_name(repo['name']),
|
84
|
+
repo['username']
|
85
|
+
]
|
86
|
+
}
|
87
|
+
print "Your choice: "
|
88
|
+
|
89
|
+
# Since to_i defaults to 0, we have to use Integer
|
90
|
+
choice = Integer(STDIN.gets.chomp) rescue nil
|
91
|
+
until choice && (0...repos.size).include?( choice - 1 ) do
|
92
|
+
print "Sorry, invalid choice. Please enter a valid number or Ctrl+C to stop: "
|
93
|
+
choice = Integer(STDIN.gets.chomp) rescue nil
|
94
|
+
end
|
95
|
+
|
96
|
+
git_clone(repos[choice - 1]["username"], repos[choice - 1]["name"])
|
97
|
+
end
|
61
98
|
end
|
62
|
-
|
99
|
+
|
63
100
|
res = %x{#{cmd}}
|
64
|
-
|
101
|
+
|
65
102
|
puts cmd, res.gsub(/^/,' ')
|
66
|
-
|
103
|
+
|
67
104
|
installed=true and break if res =~ /Checked out revision|Initialized empty Git repository/
|
68
105
|
end
|
69
106
|
abort 'Not Installed' unless installed
|
70
|
-
|
107
|
+
|
71
108
|
reload :verbose => true
|
72
109
|
end
|
73
110
|
|
74
111
|
desc "uninstall NAME", "uninstall a bundle"
|
75
112
|
def uninstall(bundle_name)
|
113
|
+
removed = false
|
114
|
+
|
76
115
|
puts "Removing bundle..."
|
77
|
-
# When moving to the trash, maybe move the bundle into a trash/disabled_bundles subfolder
|
78
|
-
# named as the bundles_path key. Just in case there are multiple versions of
|
116
|
+
# When moving to the trash, maybe move the bundle into a trash/disabled_bundles subfolder
|
117
|
+
# named as the bundles_path key. Just in case there are multiple versions of
|
79
118
|
# the same bundle in multiple bundle paths
|
80
119
|
local_bundle_paths.each do |name,bundles_path|
|
81
120
|
bundle_path = "#{bundles_path}/#{bundle_name}.tmbundle"
|
82
121
|
if File.exist? bundle_path
|
122
|
+
removed = true
|
83
123
|
%x[osascript -e 'tell application "Finder" to move the POSIX file "#{bundle_path}" to trash']
|
84
124
|
end
|
85
125
|
end
|
86
|
-
|
87
|
-
|
126
|
+
|
127
|
+
unless removed
|
128
|
+
say "There is no bundle by that name in the system", :red
|
129
|
+
exit
|
130
|
+
else
|
131
|
+
reload :verbose => true
|
132
|
+
end
|
88
133
|
end
|
89
|
-
|
134
|
+
|
90
135
|
desc "reload", "Reloads TextMate Bundles"
|
91
136
|
method_options :verbose => :boolean
|
92
137
|
def reload(opts = {})
|
@@ -94,18 +139,24 @@ class TextmateInstaller < Thor
|
|
94
139
|
%x[osascript -e 'tell app "TextMate" to reload bundles']
|
95
140
|
puts "Done." if opts[:verbose]
|
96
141
|
end
|
97
|
-
|
142
|
+
|
98
143
|
private
|
99
|
-
|
100
|
-
{ :'Macromates Trunk' => {:scm => :svn, :url => 'http://
|
101
|
-
:'Macromates Review' => {:scm => :svn, :url => 'http://
|
102
|
-
|
144
|
+
REMOTE_LOCATIONS =
|
145
|
+
{ :'Macromates Trunk' => {:scm => :svn, :url => 'http://svn.textmate.org/trunk/Bundles'},
|
146
|
+
:'Macromates Review' => {:scm => :svn, :url => 'http://svn.textmate.org/trunk/Review/Bundles'},
|
147
|
+
|
103
148
|
# :'Bunch of Git Bundles' => {:scm => :git, :url => 'git://NotImplemented'},
|
104
|
-
|
149
|
+
|
105
150
|
:'GitHub' => {:scm => :github, :url => 'http://github.com/search?q=tmbundle'},
|
106
151
|
}
|
152
|
+
|
153
|
+
SHORT_LOCATIONS = {:trunk => :"Macromates Trunk", :review => :"Macromates Review", :github => :"GitHub"}
|
154
|
+
|
155
|
+
def remote_bundle(name)
|
156
|
+
name = name.to_sym
|
157
|
+
REMOTE_LOCATIONS[name] || REMOTE_LOCATIONS[SHORT_LOCATIONS[name]]
|
107
158
|
end
|
108
|
-
|
159
|
+
|
109
160
|
def local_bundle_paths
|
110
161
|
{ :Application => '/Applications/TextMate.app/Contents/SharedSupport/Bundles',
|
111
162
|
:User => "#{ENV["HOME"]}/Library/Application Support/TextMate/Bundles",
|
@@ -114,46 +165,76 @@ class TextmateInstaller < Thor
|
|
114
165
|
:'System Pristine' => '/Library/Application Support/TextMate/Pristine Copy/Bundles',
|
115
166
|
}
|
116
167
|
end
|
117
|
-
|
168
|
+
|
118
169
|
def install_bundles_path
|
119
170
|
local_bundle_paths[:'User Pristine']
|
120
171
|
end
|
121
|
-
|
172
|
+
|
122
173
|
# Copied from http://macromates.com/svn/Bundles/trunk/Support/lib/escape.rb
|
123
174
|
# escape text to make it useable in a shell script as one “word” (string)
|
124
175
|
def e_sh(str)
|
125
176
|
str.to_s.gsub(/(?=[^a-zA-Z0-9_.\/\-\x7F-\xFF\n])/, '\\').gsub(/\n/, "'\n'").sub(/^$/, "''")
|
126
177
|
end
|
127
|
-
|
178
|
+
|
179
|
+
def url_escape(str)
|
180
|
+
chars = ((33...47).to_a + (94...96).to_a + (123...126).to_a).map {|c| c.chr }.join + "\\[\\]\\\\"
|
181
|
+
str = str.to_s.gsub(%r{[#{chars}]}) {|m| CGI.escape(m) }
|
182
|
+
end
|
183
|
+
|
128
184
|
CAPITALIZATION_EXCEPTIONS = %w[tmbundle on]
|
129
185
|
# Convert a GitHub repo name into a "normal" TM bundle name
|
130
186
|
# e.g. ruby-on-rails-tmbundle => Ruby on Rails.tmbundle
|
131
187
|
def normalize_github_repo_name(name)
|
132
|
-
name = name.gsub(
|
188
|
+
name = name.gsub(/[\-\.]/, " ").split.each{|part| part.capitalize! unless CAPITALIZATION_EXCEPTIONS.include? part}.join(" ")
|
133
189
|
name[-9] = ?. if name =~ / tmbundle$/
|
134
190
|
name
|
135
191
|
end
|
136
|
-
|
192
|
+
|
137
193
|
# Does the opposite of normalize_github_repo_name
|
138
194
|
def denormalize_github_repo_name(name)
|
139
|
-
name
|
140
|
-
name
|
195
|
+
name = name.split(' ').each{|part| part.downcase!}.join(' ').gsub(' ', '-')
|
196
|
+
name += ".tmbundle" unless name =~ /.tmbundle$/
|
197
|
+
name
|
141
198
|
end
|
142
|
-
|
199
|
+
|
143
200
|
def find_github_bundles(search_term)
|
144
|
-
|
201
|
+
# Until GitHub fixes http://support.github.com/discussions/feature-requests/11-api-search-results,
|
202
|
+
# we need to account for multiple pages of results:
|
203
|
+
page = 1
|
204
|
+
repositories = YAML.load(open("http://github.com/api/v1/yaml/search/tmbundle?start_value=#{page}"))['repositories']
|
205
|
+
results = []
|
206
|
+
until repositories.empty?
|
207
|
+
results += repositories.find_all{|result| result['name'].match(search_term)}
|
208
|
+
page += 1
|
209
|
+
repositories = YAML.load(open("http://github.com/api/v1/yaml/search/tmbundle?start_value=#{page}"))['repositories']
|
210
|
+
end
|
211
|
+
results.sort{|a,b| a['name'] <=> b['name']}
|
145
212
|
end
|
146
|
-
|
213
|
+
|
214
|
+
def git_clone(repo, name)
|
215
|
+
bundle_name = normalize_github_repo_name(name)
|
216
|
+
|
217
|
+
path = "#{install_bundles_path}/#{bundle_name}"
|
218
|
+
escaped_path = "#{e_sh(install_bundles_path)}/#{e_sh(bundle_name)}"
|
219
|
+
|
220
|
+
if File.directory?(path)
|
221
|
+
say "Sorry, that bundle is already installed. Please uninstall it first.", :red
|
222
|
+
exit
|
223
|
+
end
|
224
|
+
|
225
|
+
%[git clone "git://github.com/#{url_escape(repo)}/#{url_escape(name)}.git" #{escaped_path} 2>&1]
|
226
|
+
end
|
227
|
+
|
147
228
|
end
|
148
229
|
|
149
230
|
# TODO: create a "monument to personal cleverness" by class-izing everything?
|
150
231
|
# class TextMateBundle
|
151
232
|
# def self.find_local(bundle_name)
|
152
|
-
#
|
233
|
+
#
|
153
234
|
# end
|
154
|
-
#
|
235
|
+
#
|
155
236
|
# def self.find_remote(bundle_name)
|
156
|
-
#
|
237
|
+
#
|
157
238
|
# end
|
158
239
|
# attr_reader :name
|
159
240
|
# attr_reader :location
|
@@ -163,20 +244,20 @@ end
|
|
163
244
|
# @location = location
|
164
245
|
# @scm = scm
|
165
246
|
# end
|
166
|
-
#
|
247
|
+
#
|
167
248
|
# def install!
|
168
|
-
#
|
249
|
+
#
|
169
250
|
# end
|
170
|
-
#
|
251
|
+
#
|
171
252
|
# def uninstall!
|
172
|
-
#
|
253
|
+
#
|
173
254
|
# end
|
174
|
-
#
|
175
|
-
#
|
255
|
+
#
|
256
|
+
#
|
176
257
|
# def installed?
|
177
258
|
# # List all the installed versions, and where they're at
|
178
259
|
# end
|
179
|
-
#
|
260
|
+
#
|
180
261
|
# # TODO: dirty? method to show if there are any deltas
|
181
262
|
# end
|
182
263
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: textmate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yehuda Katz
|
@@ -9,11 +9,12 @@ autorequire: textmate
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
12
|
+
date: 2009-09-19 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: thor
|
17
|
+
type: :runtime
|
17
18
|
version_requirement:
|
18
19
|
version_requirements: !ruby/object:Gem::Requirement
|
19
20
|
requirements:
|
@@ -37,11 +38,13 @@ files:
|
|
37
38
|
- bin/textmate
|
38
39
|
has_rdoc: true
|
39
40
|
homepage: http://yehudakatz.com
|
41
|
+
licenses: []
|
42
|
+
|
40
43
|
post_install_message:
|
41
44
|
rdoc_options: []
|
42
45
|
|
43
46
|
require_paths:
|
44
|
-
-
|
47
|
+
- bin
|
45
48
|
required_ruby_version: !ruby/object:Gem::Requirement
|
46
49
|
requirements:
|
47
50
|
- - ">="
|
@@ -57,9 +60,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
60
|
requirements: []
|
58
61
|
|
59
62
|
rubyforge_project:
|
60
|
-
rubygems_version: 1.
|
63
|
+
rubygems_version: 1.3.5
|
61
64
|
signing_key:
|
62
|
-
specification_version:
|
65
|
+
specification_version: 3
|
63
66
|
summary: Command-line textmate package manager
|
64
67
|
test_files: []
|
65
68
|
|