#!/usr/bin/env ruby require "fileutils" require "rubygems" require "thor" class TextmateInstaller < Thor # CHANGED: renamed list to remote. Could there be a better name? desc "remote [SEARCH]", "Lists all the matching remote bundles" def remote(limit = "") limit = Regexp.new(".*#{limit}.*", "i") remote_bundle_locations.each do |name,location| puts "\n" << name.to_s << " Remote Bundles\n" << name.to_s.gsub(/./,'-') << '---------------' results = %x[svn list #{e_sh location[:url]}] if location[:scm]==:svn puts results.map {|x| x.split(".")[0]}.select {|x| x =~ limit}.join("\n") if results puts 'git remotes not implemented yet' if location[:scm]==:git end end desc "list", "lists all the bundles installed locally" def list() local_bundle_paths.each do |name,bundles_path| puts "\n" << name.to_s << " Bundles\n" << name.to_s.gsub(/./,'-') << '--------' puts Dir["#{e_sh bundles_path}/*.tmbundle"].map {|x| x.split("/").last.split(".").first}.join("\n") end end # TODO: Add a DESTINATION option to decide where to install. Maybe make some sort of ~/.textmate-cli config file? desc "install NAME [SOURCE]", "install a bundle" def install(bundle_name, remote_bundle_location_name=nil) # TODO: Add an option to remove all other versions of the same bundle FileUtils.mkdir_p install_bundles_path puts "Checking out #{bundle_name}..." # CHANGED: It's faster to just try and fail for each repo than to search them all first installed=false remote_bundle_locations.each do |remote_name,location| next unless remote_name.to_s.downcase.include? remote_bundle_location_name.to_s.downcase if remote_bundle_location_name cmd = 'echo "git remotes not implemented yet"' if location[:scm]==:git cmd = %[svn co #{e_sh location[:url]}/#{e_sh bundle_name}.tmbundle #{e_sh install_bundles_path}/#{e_sh bundle_name}.tmbundle 2>&1] if location[:scm]==:svn res = %x{#{cmd}} puts cmd, res.gsub(/^/,' ') #if verbose # TODO: Implement a verbose mode to toggle showing all the gory details installed=true and break if res =~ /Checked out revision|Initialized empty Git repository/ end abort 'Not Installed' unless installed # TODO: Offer suggestions for alternate bundles with similar names. Maybe let them choose puts "Reloading Bundles..." reload_textmate! puts "Done." end desc "uninstall NAME", "uninstall a bundle" def uninstall(bundle_name) puts "Removing bundle..." # FIXME: Move deleted bundles to the trash instead of rm_rf-ing them? # When moving to the trash, maybe move the bundle into a trash/disabled_bundles subfolder # named as the bundles_path key. Just in case there are multiple versions of # the same bundle in multiple bundle paths local_bundle_paths.each do |name,bundles_path| FileUtils.rm_rf("#{bundles_path}/#{bundle_name}.tmbundle") end puts "Reloading bundles..." reload_textmate! puts "Done." end private def reload_textmate! %x[osascript -e 'tell app "TextMate" to reload bundles'] end def remote_bundle_locations { :'Marcomates Trunk' => {:scm => :svn, :url => 'http://macromates.com/svn/Bundles/trunk/Bundles'}, :'Marcomates Review' => {:scm => :svn, :url => 'http://macromates.com/svn/Bundles/trunk/Review/Bundles'}, # TODO: Add Git support to remote_bundle_locations. Define some sort of standard way of listing git repos, checkout how rubygems does it # :'Bunch of Git Bundles' => {:scm => :git, :url => 'git://NotImplemented'}, # TODO: Add GitHub support as a remote_bundle_location # This will require fetching the html of the search page, scanning for urls and converting them to git urls # :'GitHub' => {:scm => :github, :url => 'http://github.com/search?q=tmbundle'}, } # TODO: Add some way to add more custom remotes end def local_bundle_paths { :Application => '/Applications/TextMate.app/Contents/SharedSupport/Bundles', :User => "#{ENV["HOME"]}/Library/Application Support/TextMate/Bundles", :System => '/Library/Application Support/TextMate/Bundles', :'User Pristine' => "#{ENV["HOME"]}/Library/Application Support/TextMate/Pristine Copy", :'System Pristine' => '/Library/Application Support/TextMate/Pristine Copy', } end def install_bundles_path #TODO: Add some way for the user to configure where they'd prefer to install bundles local_bundle_paths[:'User Pristine'] end # Copied from http://macromates.com/svn/Bundles/trunk/Support/lib/escape.rb # escape text to make it useable in a shell script as one “word” (string) def e_sh(str) str.to_s.gsub(/(?=[^a-zA-Z0-9_.\/\-\x7F-\xFF\n])/, '\\').gsub(/\n/, "'\n'").sub(/^$/, "''") end end # TODO: create a "monument to personal cleverness" by class-izing everything? # class TextMateBundle # def self.find_local(bundle_name) # # end # # def self.find_remote(bundle_name) # # end # attr_reader :name # attr_reader :location # attr_reader :scm # def initialize(name, location, scm) # @name = name # @location = location # @scm = scm # end # # def install! # # end # # def uninstall! # # end # # # def installed? # # List all the installed versions, and where they're at # end # # # TODO: dirty? method to show if there are any deltas # end TextmateInstaller.start