textmate 0.9.2 → 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/Rakefile +3 -2
  2. data/bin/textmate +137 -56
  3. 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.2"
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"
@@ -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 "remote [SEARCH]", "Lists all the matching remote bundles"
13
- def remote(search_term = "")
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
- remote_bundle_locations.each do |name,location|
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| normalize_github_repo_name(result['name']).split('.').first}
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 [SOURCE]", "install a bundle"
44
- def install(bundle_name, remote_bundle_location_name=nil)
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
- remote_bundle_locations.each do |remote_name,location|
51
- next unless remote_name.to_s.downcase.include? remote_bundle_location_name.to_s.downcase if remote_bundle_location_name
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 #{e_sh location[:url]}/#{e_sh bundle_name}.tmbundle #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
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
- repo = find_github_bundles(denormalize_github_repo_name(bundle_name)).first
60
- %[git clone #{e_sh repo['url'].sub('http', 'git') + '.git'} #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
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
- reload :verbose => true
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
- def remote_bundle_locations
100
- { :'Macromates Trunk' => {:scm => :svn, :url => 'http://macromates.com/svn/Bundles/trunk/Bundles'},
101
- :'Macromates Review' => {:scm => :svn, :url => 'http://macromates.com/svn/Bundles/trunk/Review/Bundles'},
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("-", " ").split.each{|part| part.capitalize! unless CAPITALIZATION_EXCEPTIONS.include? part}.join(" ")
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 += " tmbundle" unless name =~ / tmbundle$/
140
- name.split(' ').each{|part| part.downcase!}.join(' ').gsub(' ', '-')
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
- YAML.load(open('http://github.com/api/v1/yaml/search/tmbundle'))['repositories'].find_all{|result| result['name'].match(search_term)}
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.2
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: 2008-05-28 00:00:00 +02:00
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
- - lib
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.1.1
63
+ rubygems_version: 1.3.5
61
64
  signing_key:
62
- specification_version: 2
65
+ specification_version: 3
63
66
  summary: Command-line textmate package manager
64
67
  test_files: []
65
68