dlundqvist-textmate 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.
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
+