ssoroka-grepmate 1.0.1 → 2.0.0

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.
@@ -0,0 +1,52 @@
1
+ # this file will be installed as ~/.grepmate
2
+ ---
3
+ # valid outputs are: text, html, textmate, file_and_line
4
+ default_output: html
5
+ search_rails_source: false
6
+ search_gems: false
7
+ # if you only want to search a specific rails version, uncomment and set this:
8
+ # :use_only_rails_version: 2.3.2
9
+ exclude_file_extensions:
10
+ - sql
11
+ - log
12
+ - tmp
13
+ - gz
14
+ - bz2
15
+ - tar
16
+ - zip
17
+ - db
18
+ - sqlite
19
+ - sqlite3
20
+ - csv
21
+ - DS_Store
22
+ - svn
23
+ - git
24
+ - tmproj
25
+ - sql
26
+ - dat
27
+ - gitignore
28
+ - gitmodules
29
+ - gemspec
30
+ - rdoc
31
+ - ico
32
+ - pdf
33
+ - xml
34
+ - html
35
+ - swf
36
+ - jpg
37
+ - png
38
+ - gif
39
+ include_dirs:
40
+ - .
41
+ exclude_dirs:
42
+ - .git
43
+ - .svn
44
+ - sphinx
45
+ - sql
46
+ - tmp
47
+ - log
48
+ - doc
49
+ - coverage
50
+ - flash
51
+ - images
52
+ - videos
data/grepmate.gemspec CHANGED
@@ -2,35 +2,62 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{grepmate}
5
- s.version = "1.0.1"
5
+ s.version = "2.0.0"
6
6
 
7
- s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
- s.authors = ["Steven Soroka"]
9
- s.date = %q{2008-12-06}
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Steven Soroka", "Zach Holt"]
9
+ s.date = %q{2009-07-23}
10
10
  s.default_executable = %q{grepmate}
11
11
  s.description = %q{Extremely fast search of rails projects or rails source for code, open in textmate or browser with html output}
12
12
  s.email = %q{ssoroka78@gmail.com}
13
13
  s.executables = ["grepmate"]
14
- s.extra_rdoc_files = ["bin/grepmate", "README"]
15
- s.files = ["bin/grepmate", "grepmate.gemspec", "Manifest", "Rakefile", "README"]
16
- s.has_rdoc = true
14
+ s.extra_rdoc_files = [
15
+ "LICENSE",
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "bin/grepmate",
25
+ "config/dot-grepmate",
26
+ "grepmate.gemspec",
27
+ "lib/.empty",
28
+ "lib/.git-empty",
29
+ "lib/grepmate.rb",
30
+ "lib/output/file_and_line.rb",
31
+ "lib/output/html.rb",
32
+ "lib/output/text.rb",
33
+ "lib/output/textmate.rb",
34
+ "spec/grepmate_spec.rb",
35
+ "spec/spec_helper.rb",
36
+ "tmp/grepmate.html"
37
+ ]
17
38
  s.homepage = %q{http://github.com/ssoroka/grepmate}
18
- s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Grepmate", "--main", "README"]
39
+ s.rdoc_options = ["--charset=UTF-8"]
19
40
  s.require_paths = ["lib"]
20
- s.rubyforge_project = %q{grepmate}
21
- s.rubygems_version = %q{1.3.1}
41
+ s.rubygems_version = %q{1.3.3}
22
42
  s.summary = %q{Extremely fast search of rails projects or rails source for code, open in textmate or browser with html output}
43
+ s.test_files = [
44
+ "spec/grepmate_spec.rb",
45
+ "spec/spec_helper.rb"
46
+ ]
23
47
 
24
48
  if s.respond_to? :specification_version then
25
49
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
- s.specification_version = 2
50
+ s.specification_version = 3
27
51
 
28
52
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
- s.add_development_dependency(%q<main>, [">= 0"])
53
+ s.add_runtime_dependency(%q<main>, [">= 2.8.3"])
54
+ s.add_runtime_dependency(%q<syntax>, [">= 1.0.0"])
30
55
  else
31
- s.add_dependency(%q<main>, [">= 0"])
56
+ s.add_dependency(%q<main>, [">= 2.8.3"])
57
+ s.add_dependency(%q<syntax>, [">= 1.0.0"])
32
58
  end
33
59
  else
34
- s.add_dependency(%q<main>, [">= 0"])
60
+ s.add_dependency(%q<main>, [">= 2.8.3"])
61
+ s.add_dependency(%q<syntax>, [">= 1.0.0"])
35
62
  end
36
63
  end
data/lib/.empty ADDED
File without changes
data/lib/.git-empty ADDED
@@ -0,0 +1 @@
1
+
data/lib/grepmate.rb ADDED
@@ -0,0 +1,187 @@
1
+ require 'rubygems'
2
+ begin
3
+ gem 'syntax'
4
+ rescue LoadError
5
+ end
6
+ require 'yaml'
7
+ require 'enumerator'
8
+ require File.join(File.dirname(__FILE__), 'output', 'html')
9
+ require File.join(File.dirname(__FILE__), 'output', 'textmate')
10
+ require File.join(File.dirname(__FILE__), 'output', 'text')
11
+ require File.join(File.dirname(__FILE__), 'output', 'file_and_line')
12
+
13
+ class Grepmate
14
+ attr_reader :params, :dirs, :query, :results
15
+ CONFIG_FILE = File.expand_path("~/.grepmate")
16
+
17
+ def initialize(params)
18
+ @params = params
19
+
20
+ load_config
21
+ end
22
+
23
+ def gem_path
24
+ `gem environment gemdir`.chomp
25
+ end
26
+
27
+ def gems_path
28
+ File.join(gem_path, 'gems')
29
+ end
30
+
31
+ def rails_version
32
+ `rails -v`.chomp.split(' ').last
33
+ end
34
+
35
+ def rails_path
36
+ vendor_rails = "vendor/rails"
37
+
38
+ if File.exist?(vendor_rails)
39
+ vendor_rails
40
+ else
41
+ Dir["#{gems_path}/acti*-#{rails_version}"]
42
+ end
43
+ end
44
+
45
+ def load_config
46
+ if !File.exist?(CONFIG_FILE)
47
+ # create
48
+ safe_puts "Creating config file #{CONFIG_FILE}..."
49
+ FileUtils.cp(File.expand_path(File.join(File.dirname(__FILE__), %w(.. config dot-grepmate))), CONFIG_FILE)
50
+ else
51
+ verbose("Using config file #{CONFIG_FILE}...")
52
+ end
53
+
54
+ # load
55
+ @config = YAML::load(File.read(CONFIG_FILE))
56
+ # set output type
57
+ @output = @config['default_output']
58
+ %w(html text textmate file_and_line).each{|out_type|
59
+ @output = out_type if params[out_type].value
60
+ }
61
+ verbose("Output mode set to #{@output}")
62
+
63
+ # set default search directories
64
+ unless params['dir'].values.any?
65
+ params['dir'].values = @config['include_dirs']
66
+ end
67
+
68
+ # exclude extensions
69
+ @exclude_exts = (@config['exclude_file_extensions'] || [])
70
+ verbose "Excluding file extensions: #{@exclude_exts.join(' ')}"
71
+ # dir exclusions
72
+ @exclude_dirs = (@config['exclude_dirs'] || [])
73
+ verbose "Excluding dirs: #{@exclude_dirs.join(' ')}"
74
+
75
+ @search_rails = params['rails'].given? ? params['rails'].value : @config['search_rails_source']
76
+ verbose "Searching rails source" if @search_rails
77
+ @search_gems = params['gems'].given? ? params['gems'].value : @config['search_gems']
78
+ verbose "Searching gems source" if @search_gems
79
+ end
80
+
81
+ # use default dirs, param dirs, or files/dirs from STDIN
82
+ def determine_directories_to_search
83
+ if STDIN.tty?
84
+ @dirs = if params['only_rails'].value || params['only_gems'].value
85
+ []
86
+ else
87
+ params['dir'].values || []
88
+ end
89
+ @dirs += Array(rails_path) if params['only_rails'].value || params['rails'].value
90
+ @dirs << gems_path if params['only_gems'].value || params['gems'].value
91
+ else
92
+ @dirs = []
93
+ input = STDIN.read
94
+
95
+ input.split("\n").each do |ln|
96
+ if ln =~ /^([^:]*)/ # Assume everything to the first colon is a file name
97
+ # filename = File.expand_path($1)
98
+ # if File.exist?(filename) # But actually check that it is
99
+ @dirs << $1# filename
100
+ # end
101
+ end
102
+ end
103
+ end
104
+ @dirs.uniq! # remove duplicates
105
+ verbose "including dirs: #{@dirs.join(' ')}"
106
+ @dirs.reject!{ |dir|
107
+ !File.exist?(dir) || @exclude_dirs.any?{ |exclude| dir =~ /(^|\/)#{exclude}(\/|$)/i }
108
+ }
109
+ verbose "rejected dirs, left with: #{@dirs.join(' ')}"
110
+ end
111
+
112
+ def determine_what_to_search_for
113
+ # funny bunny => 'funny' 'bunny'
114
+ # 'funny bunny' => 'funny bunny'
115
+ if params['regex'].value
116
+ exp = params['what_to_search_for'].value.dup
117
+ exp.gsub!(/^\//, '')
118
+ exp.gsub!(/\/$/, '')
119
+ @query = %("#{exp}")
120
+ verbose "Searching for: /#{exp}/"
121
+ else
122
+ verbose "Searching for: #{params['what_to_search_for'].values.inspect}"
123
+ @query = params['what_to_search_for'].values.map {|v| "\"#{v.gsub(/"/, '\\"')}\"" }.join(" ")
124
+ end
125
+ end
126
+
127
+ def find
128
+ determine_directories_to_search
129
+ determine_what_to_search_for
130
+
131
+ @results = []
132
+ if @dirs.any?
133
+ paths = `find #{@dirs.join(' ')}`.split("\n").map{|sp| sp.gsub(' ', '\\ ')} # escape spaces in found files
134
+ paths = paths.map!{|p| p.gsub(/^\.\//, '') }.uniq
135
+ paths.reject! { |path|
136
+ @exclude_exts.any?{ |exclude| path =~ /\.#{Regexp.escape(exclude)}$/i } ||
137
+ @exclude_dirs.any?{ |exclude| path =~ /(^|\/)#{Regexp.escape(exclude)}(\/|$)/i }
138
+ }
139
+
140
+ cmd = 'grep '
141
+ cmd << '-i ' unless params['case'].value
142
+ # 3 lines of context for html and text
143
+ cmd << '-C 3 ' if (@output == 'html') && !params['count'].value
144
+ cmd << '-E ' if params['regex']
145
+
146
+ # paths get too large for grep to handle, so limit the number it has to deal with at once.
147
+ paths.each_slice(100) {|sub_paths|
148
+ @results += `#{cmd} -n #{query} #{sub_paths.join(' ')}`.split("\n")
149
+ }
150
+ end
151
+ end
152
+
153
+ def display
154
+ if @results.empty?
155
+ safe_puts "Nothing found!"
156
+ exit
157
+ elsif params['count'].value
158
+ puts "Matches: #{@results.size}"
159
+ exit
160
+ end
161
+
162
+ output_class = case @output.to_s
163
+ when 'html'
164
+ Output::HTML
165
+ when 'text'
166
+ Output::Text
167
+ when 'textmate'
168
+ Output::Textmate
169
+ when 'file_and_line'
170
+ Output::FileAndLine
171
+ end
172
+
173
+ output_class.new(self).process
174
+ end
175
+
176
+ def safe_puts(msg)
177
+ if STDOUT.tty?
178
+ puts msg
179
+ end
180
+ end
181
+
182
+ def verbose(msg)
183
+ if @params['verbose'].value
184
+ puts msg
185
+ end
186
+ end
187
+ end
@@ -0,0 +1,17 @@
1
+ module Output
2
+ class FileAndLine
3
+ def initialize(grepmate)
4
+ @grepmate = grepmate
5
+ end
6
+
7
+ def process
8
+ @grepmate.results.each{|line|
9
+ if line
10
+ puts line.split(':')[0..1].join(':')
11
+ else
12
+ puts line
13
+ end
14
+ }
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,136 @@
1
+ module Output
2
+ class HTML
3
+ TEMP_FILE = File.expand_path(File.join(File.dirname(__FILE__), %w(.. .. tmp grepmate.html)))
4
+
5
+ def initialize(grepmate)
6
+ @grepmate = grepmate
7
+ end
8
+
9
+ def determine_javascript_highlight_text
10
+ @grepmate.params['what_to_search_for'].values.map {|v| "highlightSearchTerms('#{v.gsub(/'/, "\\'").gsub(/:/, "\\:")}', false)" }.join(";")
11
+ end
12
+
13
+ def process
14
+ syntax_installed = true
15
+ begin
16
+ require 'syntax/convertors/html'
17
+ rescue LoadError
18
+ syntax_installed = false
19
+ end
20
+
21
+ html = '<html><head>'
22
+ html << <<-CSS
23
+ <style type="text/css">body { background-color: #EEEEEE; } pre {
24
+ display: inline; } .ruby { font-family: Monaco; font-size: 10pt;
25
+ background-color: white } .ruby .normal {} .ruby .comment { color:
26
+ #005; font-style: italic; } .ruby .keyword { color: #A00; font-weight:
27
+ bold; } .ruby .method { color: #077; } .ruby .class { color: #074; }
28
+ .ruby .module { color: #050; } .ruby .punct { color: #447; font-weight:
29
+ bold; } .ruby .symbol { color: #099; } .ruby .string { color: #944;
30
+ background: #FFE; } .ruby .char { color: #F07; } .ruby .ident { color:
31
+ #004; } .ruby .constant { color: #07F; } .ruby .regex { color: #B66;
32
+ background: #FEF; } .ruby .number { color: #F99; } .ruby .attribute {
33
+ color: #7BB; } .ruby .global { color: #7FB; } .ruby .expr { color:
34
+ #227; } .ruby .escape { color: #277; } .ruby .highlight {
35
+ background-color: yellow; font-weight: 900; } td.lineno { text-align:
36
+ right; font-family: Monaco; font-size: 9pt; padding-right: 10px; }
37
+ td.filename { padding-top: 35px; font-family: Monaco; font-size: 14pt;
38
+ }</style>
39
+
40
+ <script type="text/javascript">
41
+ // http://www.nsftools.com/misc/SearchAndHighlight.htm
42
+
43
+ function doHighlight(bodyText,searchTerm,highlightStartTag,highlightEndTag)
44
+ {if((!highlightStartTag)||(!highlightEndTag)){highlightStartTag="<font style='color:blue; background-color:yellow;'>";highlightEndTag="</font>";}
45
+ var newText="";var i=-1;var lcSearchTerm=searchTerm.toLowerCase();var lcBodyText=bodyText.toLowerCase();while(bodyText.length>0){i=lcBodyText.indexOf(lcSearchTerm,i+1);if(i<0){newText+=bodyText;bodyText="";}else{if(bodyText.lastIndexOf(">",i)>=bodyText.lastIndexOf("<",i)){if(lcBodyText.lastIndexOf("/script>",i)>=lcBodyText.lastIndexOf("<script",i)){newText+=bodyText.substring(0,i)+highlightStartTag+bodyText.substr(i,searchTerm.length)+highlightEndTag;bodyText=bodyText.substr(i+searchTerm.length);lcBodyText=bodyText.toLowerCase();i=-1;}}}}
46
+ return newText;}
47
+ function highlightSearchTerms(searchText,treatAsPhrase,warnOnFailure,highlightStartTag,highlightEndTag)
48
+ {if(treatAsPhrase){searchArray=[searchText];}else{searchArray=searchText.split(" ");}
49
+ if(!document.body||typeof(document.body.innerHTML)=="undefined"){if(warnOnFailure){alert("Sorry, for some reason the text of this page is unavailable. Searching will not work.");}
50
+ return false;}
51
+ var bodyText=document.body.innerHTML;for(var i=0;i<searchArray.length;i++){bodyText=doHighlight(bodyText,searchArray[i],highlightStartTag,highlightEndTag);}
52
+ document.body.innerHTML=bodyText;return true;}</script></script>
53
+ CSS
54
+
55
+ # some searches can return stupid-huge result sets. Javascript will likely freeze or crash the browser in those cases.
56
+ if @grepmate.results.size < 1000
57
+ html << "</head><body onLoad=\"#{determine_javascript_highlight_text}\">"
58
+ else
59
+ html << "</head><body>"
60
+ end
61
+
62
+ syntax_converter = (syntax_installed ? Syntax::Convertors::HTML.for_syntax("ruby") : nil)
63
+ html << '<pre>sudo gem install syntax</pre> to get syntax highlighting<br><br>' unless syntax_installed
64
+
65
+ html << '<table cellspacing="0" cellpadding="2">'
66
+
67
+ last_file = ''
68
+ separator = false
69
+
70
+ html << @grepmate.results.map{ |line|
71
+ if line == '--'
72
+ separator = true
73
+ ''
74
+ elsif line =~ /^(.+?)-(\d+)-(.*)$/ || line =~ /^(.+?):(\d+):(.*)$/
75
+ file = $1
76
+ line = $2
77
+ context = $3
78
+
79
+ file_group = ''
80
+
81
+ if file == last_file
82
+ file_group << "<tr><td class=\"lineno\">...</td><td></td></tr>" if separator
83
+ else
84
+ parts = file.split(/\//)
85
+ path = ''
86
+ file_group << "<tr><td colspan=\"2\" class=\"filename\">"
87
+ parts.each do |part|
88
+ path << "/#{part}"
89
+ file_group << "/" unless part.equal?(parts.first)
90
+ if part.equal?(parts.last)
91
+ file_group << "<a href=\"txmt://open?url=file://#{file}\">#{part}</a>"
92
+ else
93
+ file_group << "<a href=\"file:/#{path}\">#{part}</a>"
94
+ end
95
+ end
96
+ file_group << "</td></tr>\n"
97
+ end
98
+
99
+ separator = false
100
+ last_file = file
101
+
102
+ file_group << "<tr><td class=\"lineno\"><a href=\"txmt://open?url=file://#{file}&line=#{line}\">#{line}</a></td>"
103
+
104
+ converted = begin
105
+ syntax_converter ? syntax_converter.convert(context =~ /^(.+?)\s*$/ ? $1 : context) : context
106
+ rescue Exception => e
107
+ "TOKENIZING ERROR: #{context} <!-- \n#{e.message}\n#{e.backtrace.join("\n")}\n -->"
108
+ end
109
+ file_group << "<td class=\"ruby\">#{converted}</td></tr>\n"
110
+
111
+ file_group
112
+ end
113
+ }.join
114
+
115
+ html << '</table></body></html>'
116
+
117
+ begin
118
+ FileUtils.touch(TEMP_FILE)
119
+ rescue Errno::EACCES => e
120
+ puts %(
121
+
122
+ Looks like there's a permission error for the file #{TEMP_FILE}. The grepmate gem expects to have write access to this file.
123
+
124
+ To fix, please do:
125
+ sudo chmod 0666 #{TEMP_FILE}
126
+
127
+ )
128
+ raise e
129
+ end
130
+ File.open(TEMP_FILE, 'w') { |f| f.write(html) }
131
+
132
+ system("open #{TEMP_FILE}")
133
+
134
+ end
135
+ end
136
+ end