dlundqvist-textmate 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/LICENSE +20 -0
  2. data/README.markdown +41 -0
  3. data/Rakefile +46 -0
  4. data/bin/textmate +264 -0
  5. metadata +68 -0
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Yehuda Katz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,41 @@
1
+ # textmate
2
+
3
+ A binary that provides package management for TextMate.
4
+
5
+ # Usage
6
+
7
+ `textmate [COMMAND] [*PARAMS]`
8
+
9
+ Textmate bundles are automatically reloaded after install or uninstall operations.
10
+
11
+ ## List available remote bundles
12
+
13
+ `textmate remote [SEARCH]`
14
+
15
+ List all of the available bundles in the remote repository, optionally filtering by `search`.
16
+
17
+ ## List installed bundles
18
+
19
+ `textmate list [SEARCH]`
20
+
21
+ List all of the bundles that are installed on the local system, optionally filtering by `search`.
22
+
23
+ ## Installing new bundles
24
+
25
+ `textmate install NAME [SOURCE]`
26
+
27
+ Installs a bundle from the remote repository. SOURCE filters known remote bundle locations.
28
+ For example, if you want to install the "Ruby on Rails" bundle off GitHub, you'd type the following:
29
+
30
+ `textmate install "Ruby on Rails" GitHub`
31
+
32
+ Available remote bundle locations are:
33
+ * Macromates Trunk
34
+ * Macromates Review
35
+ * GitHub
36
+
37
+ ## Uninstalling bundles
38
+
39
+ `textmate uninstall NAME`
40
+
41
+ Uninstalls a bundle from the local repository.
@@ -0,0 +1,46 @@
1
+ require 'rubygems'
2
+ require 'rake/gempackagetask'
3
+ require 'date'
4
+
5
+ GEM = "textmate"
6
+ GEM_VERSION = "0.9.6"
7
+ AUTHOR = "Yehuda Katz"
8
+ EMAIL = "wycats@gmail.com"
9
+ HOMEPAGE = "http://yehudakatz.com"
10
+ SUMMARY = "Command-line textmate package manager"
11
+
12
+ spec = Gem::Specification.new do |s|
13
+ s.name = GEM
14
+ s.version = GEM_VERSION
15
+ s.platform = Gem::Platform::RUBY
16
+ s.has_rdoc = true
17
+ s.extra_rdoc_files = ["README.markdown", "LICENSE"]
18
+ s.summary = SUMMARY
19
+ s.description = s.summary
20
+ s.author = AUTHOR
21
+ s.email = EMAIL
22
+ s.homepage = HOMEPAGE
23
+
24
+ s.add_dependency "thor", ">= 0.9.2"
25
+
26
+ s.require_path = 'bin' # Yes, it's a hack, but otherwise gem complains on install
27
+ s.autorequire = GEM
28
+ s.files = %w(LICENSE README.markdown Rakefile) + Dir.glob("{bin,specs}/**/*")
29
+ s.bindir = "bin"
30
+ s.executables = %w( textmate )
31
+ end
32
+
33
+ Rake::GemPackageTask.new(spec) do |pkg|
34
+ pkg.gem_spec = spec
35
+ end
36
+
37
+ desc "make a gemspec file"
38
+ task :make_spec do
39
+ File.open("#{GEM}.gemspec", "w") do |file|
40
+ file.puts spec.to_ruby
41
+ end
42
+ end
43
+
44
+ task :install => [:package] do
45
+ sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION} --no-rdoc --no-ri}
46
+ end
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "fileutils"
4
+ require "rubygems"
5
+ require "thor"
6
+ require "open-uri"
7
+ require "yaml"
8
+ require "cgi"
9
+
10
+ class TextmateInstaller < Thor
11
+
12
+ # CHANGED: renamed list to remote. Could there be a better name?
13
+ desc "search [SEARCH]", "Lists all the matching remote bundles"
14
+ def search(search_term = "")
15
+ search_term = Regexp.new(".*#{search_term}.*", "i")
16
+
17
+ REMOTE_LOCATIONS.each do |name,location|
18
+ puts "\n" << name.to_s << " Remote Bundles\n" << name.to_s.gsub(/./,'-') << '---------------'
19
+
20
+ results = case location[:scm]
21
+ when :svn
22
+ %x[svn list #{e_sh location[:url]}].map {|x| x.split(".")[0]}.select {|x| x =~ search_term}.join("\n")
23
+ when :git
24
+ 'git remotes not implemented yet'
25
+ when :github
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
+ }
33
+ end
34
+
35
+ puts results
36
+ end
37
+ end
38
+
39
+ desc "list [SEARCH]", "lists all the bundles installed locally"
40
+ def list(search_term = "")
41
+ search_term = Regexp.new(".*#{search_term}.*", "i")
42
+
43
+ local_bundle_paths.each do |name,bundles_path|
44
+ puts "\n" << name.to_s << " Bundles\n" << name.to_s.gsub(/./,'-') << '--------'
45
+ puts Dir["#{e_sh bundles_path}/*.tmbundle"].map {|x| x.split("/").last.split(".").first}.
46
+ select {|x| x =~ search_term}.join("\n")
47
+ end
48
+ end
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)
55
+ FileUtils.mkdir_p install_bundles_path
56
+ puts "Checking out #{bundle_name}..."
57
+
58
+ # CHANGED: It's faster to just try and fail for each repo than to search them all first
59
+ installed=false
60
+ REMOTE_LOCATIONS.each do |remote_name,location|
61
+ next unless remote_bundle(options["source"]) == location if options.key?("source")
62
+
63
+ cmd = case location[:scm]
64
+ when :git
65
+ 'echo "git remotes not implemented yet"'
66
+ when :svn
67
+ %[svn co "#{location[:url]}/#{url_escape bundle_name}.tmbundle" #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1]
68
+ when :github
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
98
+ end
99
+
100
+ res = %x{#{cmd}}
101
+
102
+ puts cmd, res.gsub(/^/,' ')
103
+
104
+ installed=true and break if res =~ /Checked out revision|Initialized empty Git repository/
105
+ end
106
+ abort 'Not Installed' unless installed
107
+
108
+ reload :verbose => true
109
+ end
110
+
111
+ desc "uninstall NAME", "uninstall a bundle"
112
+ def uninstall(bundle_name)
113
+ removed = false
114
+
115
+ puts "Removing bundle..."
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
118
+ # the same bundle in multiple bundle paths
119
+ local_bundle_paths.each do |name,bundles_path|
120
+ bundle_path = "#{bundles_path}/#{bundle_name}.tmbundle"
121
+ if File.exist? bundle_path
122
+ removed = true
123
+ %x[osascript -e 'tell application "Finder" to move the POSIX file "#{bundle_path}" to trash']
124
+ end
125
+ end
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
133
+ end
134
+
135
+ desc "reload", "Reloads TextMate Bundles"
136
+ method_options :verbose => :boolean
137
+ def reload(opts = {})
138
+ puts "Reloading bundles..." if opts[:verbose]
139
+ %x[osascript -e 'tell app "TextMate" to reload bundles']
140
+ puts "Done." if opts[:verbose]
141
+ end
142
+
143
+ private
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
+
148
+ # :'Bunch of Git Bundles' => {:scm => :git, :url => 'git://NotImplemented'},
149
+
150
+ :'GitHub' => {:scm => :github, :url => 'http://github.com/search?q=tmbundle'},
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]]
158
+ end
159
+
160
+ def local_bundle_paths
161
+ { :Application => '/Applications/Local/TextMate.app/Contents/SharedSupport/Bundles',
162
+ :User => "#{ENV["HOME"]}/Library/Application Support/TextMate/Bundles",
163
+ :System => '/Library/Application Support/TextMate/Bundles',
164
+ :'User Pristine' => "#{ENV["HOME"]}/Library/Application Support/TextMate/Pristine Copy/Bundles",
165
+ :'System Pristine' => '/Library/Application Support/TextMate/Pristine Copy/Bundles',
166
+ }
167
+ end
168
+
169
+ def install_bundles_path
170
+ local_bundle_paths[:'User Pristine']
171
+ end
172
+
173
+ # Copied from http://macromates.com/svn/Bundles/trunk/Support/lib/escape.rb
174
+ # escape text to make it useable in a shell script as one “word” (string)
175
+ def e_sh(str)
176
+ str.to_s.gsub(/(?=[^a-zA-Z0-9_.\/\-\x7F-\xFF\n])/, '\\').gsub(/\n/, "'\n'").sub(/^$/, "''")
177
+ end
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
+
184
+ CAPITALIZATION_EXCEPTIONS = %w[tmbundle on]
185
+ # Convert a GitHub repo name into a "normal" TM bundle name
186
+ # e.g. ruby-on-rails-tmbundle => Ruby on Rails.tmbundle
187
+ def normalize_github_repo_name(name)
188
+ name = name.gsub(/[\-\.]/, " ").split.each{|part| part.capitalize! unless CAPITALIZATION_EXCEPTIONS.include? part}.join(" ")
189
+ name[-9] = ?. if name =~ / tmbundle$/
190
+ name
191
+ end
192
+
193
+ # Does the opposite of normalize_github_repo_name
194
+ def denormalize_github_repo_name(name)
195
+ name = name.split(' ').each{|part| part.downcase!}.join(' ').gsub(' ', '-')
196
+ name += ".tmbundle" unless name =~ /.tmbundle$/
197
+ name
198
+ end
199
+
200
+ def find_github_bundles(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']}
212
+ end
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
+
228
+ end
229
+
230
+ # TODO: create a "monument to personal cleverness" by class-izing everything?
231
+ # class TextMateBundle
232
+ # def self.find_local(bundle_name)
233
+ #
234
+ # end
235
+ #
236
+ # def self.find_remote(bundle_name)
237
+ #
238
+ # end
239
+ # attr_reader :name
240
+ # attr_reader :location
241
+ # attr_reader :scm
242
+ # def initialize(name, location, scm)
243
+ # @name = name
244
+ # @location = location
245
+ # @scm = scm
246
+ # end
247
+ #
248
+ # def install!
249
+ #
250
+ # end
251
+ #
252
+ # def uninstall!
253
+ #
254
+ # end
255
+ #
256
+ #
257
+ # def installed?
258
+ # # List all the installed versions, and where they're at
259
+ # end
260
+ #
261
+ # # TODO: dirty? method to show if there are any deltas
262
+ # end
263
+
264
+ TextmateInstaller.start
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dlundqvist-textmate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.6
5
+ platform: ruby
6
+ authors:
7
+ - Yehuda Katz
8
+ autorequire: textmate
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-03 00:00:00 +08:00
13
+ default_executable: textmate
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: thor
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.11.5
24
+ version:
25
+ description: Command-line textmate package manager
26
+ email: wycats@gmail.com
27
+ executables:
28
+ - textmate
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README.markdown
33
+ - LICENSE
34
+ files:
35
+ - LICENSE
36
+ - README.markdown
37
+ - Rakefile
38
+ - bin/textmate
39
+ has_rdoc: true
40
+ homepage: http://yehudakatz.com
41
+ licenses: []
42
+
43
+ post_install_message:
44
+ rdoc_options: []
45
+
46
+ require_paths:
47
+ - bin
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: "0"
53
+ version:
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ requirements: []
61
+
62
+ rubyforge_project:
63
+ rubygems_version: 1.3.5
64
+ signing_key:
65
+ specification_version: 3
66
+ summary: Command-line textmate package manager
67
+ test_files: []
68
+