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.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ pkg
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Steven Soroka
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.
data/README.rdoc ADDED
@@ -0,0 +1,60 @@
1
+ = Grepmate
2
+
3
+ Extremely fast search of rails projects or rails source for code, open in textmate or browser with html output
4
+
5
+ == Usage
6
+
7
+ NAME
8
+ grepmate
9
+
10
+ SYNOPSIS
11
+ grepmate [what_to_search_for*] [dir=dir] [options]+
12
+
13
+ PARAMETERS
14
+ what_to_search_for (-1 ~> what_to_search_for)
15
+ Search terms. Enclose in quotes to search for phrase. use with -e or
16
+ --regex option for regular expression support
17
+ dir=dir (-1 ~> dir)
18
+ Directories to search. Defaults to project dirs.
19
+ --case
20
+ Case sensitive search (default is off).
21
+ --textmate, -t
22
+ open matches in textmate
23
+ --html
24
+ Turn on html output (default)
25
+ --text
26
+ show matches as text, file and line number
27
+ --file_and_line, -f
28
+ output only match file and line number
29
+ --rails, -r
30
+ Search Rails source, in addition to whatever is named by dir parameter
31
+ etc.
32
+ --only_rails, -R
33
+ Search only Rails source, not the project. Takes precedence over dir
34
+ parameter
35
+ --gems, -g
36
+ Search all gems, in addition to whatever is named by dir parameter
37
+ etc.
38
+ --only_gems, -G
39
+ Search only gems, not the project. Takes precedence over dir parameter
40
+ --wait, -w
41
+ Wait between finds until TextMate file is closed. Only works with
42
+ textmate output (--textmate or -t).
43
+ --count, -c
44
+ Display only the number of matches.
45
+ --regex, -e
46
+ use regular expression search terms
47
+ --verbose, -v
48
+ Turn on verbose mode and explain everything
49
+ --help, -h
50
+
51
+ == Authors
52
+
53
+ - Steven Soroka
54
+ - Zach Holt
55
+
56
+ == To Do
57
+
58
+ - check ~/.grepmate for current rails repo to use instead of always using latest
59
+ - add include parameter to selectively include file extensions
60
+ - Allow global and cwd .gmconfig files to determine things like exclusions and inclusions.
data/Rakefile CHANGED
@@ -1,14 +1,52 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
- require 'echoe'
4
3
 
5
- Echoe.new('grepmate', '1.0.1') do |p|
6
- p.description = 'Extremely fast search of rails projects or rails source for code, open in textmate or browser with html output'
7
- p.url = 'http://github.com/ssoroka/grepmate'
8
- p.author = 'Steven Soroka'
9
- p.email = 'ssoroka78@gmail.com'
10
- p.ignore_pattern = ["tmp/*"]
11
- p.development_dependencies = ['main']
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "grepmate"
8
+ gem.summary = %Q{Extremely fast search of rails projects or rails source for code, open in textmate or browser with html output}
9
+ gem.description = gem.summary
10
+ gem.email = "ssoroka78@gmail.com"
11
+ gem.homepage = "http://github.com/ssoroka/grepmate"
12
+ gem.authors = ["Steven Soroka", 'Zach Holt']
13
+ gem.add_dependency 'main', '>= 2.8.3'
14
+ gem.add_dependency 'syntax', '>= 1.0.0'
15
+ gem.executables = ['grepmate']
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+
36
+ task :default => :spec
37
+
38
+ require 'rake/rdoctask'
39
+ Rake::RDocTask.new do |rdoc|
40
+ if File.exist?('VERSION.yml')
41
+ config = YAML.load(File.read('VERSION.yml'))
42
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
43
+ else
44
+ version = ""
45
+ end
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "grepmate #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
12
51
  end
13
52
 
14
- Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each{|f| load f }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
data/bin/grepmate CHANGED
@@ -1,300 +1,86 @@
1
1
  #!/usr/bin/env ruby
2
- # script/grepmate brought to you by Steven Soroka and Zach Holt
3
- # script/grepmate --help for help screen
4
2
  require 'rubygems'
5
3
  gem 'main', '>=2.8.0'
6
- require 'main' # don't think this line is needed. gem should require it
7
-
8
- def gem_path
9
- `gem environment gemdir`.chomp
10
- end
11
-
12
- def gems_path
13
- File.join(gem_path, 'gems')
14
- end
15
-
16
- def rails_version
17
- `rails -v`.chomp.split(' ').last
18
- end
19
-
20
- def rails_path
21
- vendor_rails = "vendor/rails"
22
-
23
- if File.exist?(vendor_rails)
24
- vendor_rails
25
- else
26
- Dir["#{gems_path}/acti*-#{rails_version}"]
27
- end
28
- end
4
+ require 'main'
5
+ require File.join(File.dirname(__FILE__), %w(.. lib grepmate))
29
6
 
30
7
  Main {
31
- option( 'case' ) {
8
+ option('case') {
9
+ cast :bool
10
+ description 'Case sensitive search (default is off).'
11
+ }
12
+
13
+ option('textmate', 't') {
14
+ cast :bool
15
+ description 'open matches in textmate'
16
+ }
17
+
18
+ option('html') {
32
19
  cast :bool
33
- description 'Case sensitive search (exclude for case insensitive).'
20
+ description 'Turn on html output (default)'
34
21
  }
35
22
 
36
- option( 'no_html', 'H' ) {
23
+ option('text') {
37
24
  cast :bool
38
- description 'Turn off HTML output.'
25
+ description 'show matches as text, file and line number'
39
26
  }
40
27
 
41
- option( 'only_rails', 'R' ) {
28
+ option('file_and_line', 'f') {
42
29
  cast :bool
43
- description 'Search Rails source, not the project. Takes precedence over dir='
30
+ description 'output only match file and line number'
44
31
  }
45
32
 
46
- option( 'rails', 'r' ) {
33
+ option('rails', 'r') {
47
34
  cast :bool
48
- description 'Search Rails source, in addition to whatever is named by dir= dir= etc.'
35
+ description 'Search Rails source, in addition to whatever is named by dir parameter etc.'
49
36
  }
50
37
 
38
+ option('only_rails', 'R') {
39
+ cast :bool
40
+ description 'Search only Rails source, not the project. Takes precedence over dir parameter'
41
+ }
42
+
51
43
  option('gems', 'g') {
52
44
  cast :bool
53
- description "Search all gems, in addition to whatever is named by dir= dir= etc."
45
+ description "Search all gems, in addition to whatever is named by dir parameter etc."
54
46
  }
55
47
 
56
48
  option('only_gems', 'G') {
57
49
  cast :bool
58
- description "Search only gems, not the project. Takes precedence over dir="
50
+ description "Search only gems, not the project. Takes precedence over dir parameter"
59
51
  }
60
52
 
61
- option( 'wait', 'w' ) {
53
+ option('wait', 'w') {
62
54
  cast :bool
63
- description 'Wait between finds until TextMate is closed. Only works with --no_html.'
55
+ description 'Wait between finds until TextMate file is closed. Only works with textmate output (--textmate or -t).'
64
56
  }
65
57
 
66
- option( 'count', 'c' ) {
58
+ option('count', 'c') {
67
59
  cast :bool
68
60
  description 'Display only the number of matches.'
69
61
  }
70
62
 
71
- keyword( 'dir', 'd' ) {
72
- arity -1
73
- default 'app', 'config', 'db', 'lib', 'spec', 'public', 'vendor/plugins', 'test/selenium_fixtures', 'test/selenium'
74
- description 'Directories to search. Defaults to project.'
63
+ option('regex', 'e') {
64
+ description 'use regular expression search terms'
75
65
  }
76
-
77
- keyword( 'exclude' ) {
78
- arity -1
79
- cast { |v| Regexp.new( v ) }
80
- default '\.sql$', '\.log$', '\.tmp$', '\.gz$', '\.bz2$', '\.tar$', '\.db$', '\.sqlite$', '\.sqlite3$', '\.csv$', '\.DS_Store$', '\.svn', '\.git', '\.CSV$', 'grepmate\.html$'
81
- description 'Exclude paths matching these patterns.'
66
+
67
+ option('verbose', 'v') {
68
+ description 'Turn on verbose mode and explain everything'
82
69
  }
83
70
 
84
- keyword( 'tempfile' ) {
85
- arity 1
86
- default '/tmp/grepmate_out.html'
87
- description "Where to store search results for HTML output. Terminate with .html"
71
+ keyword('dir', 'd') {
72
+ arity -1
73
+ description 'Directories to search. Defaults to project dirs.'
88
74
  }
89
-
90
- argument( 'what_to_search_for' ) {
75
+
76
+ argument('what_to_search_for') {
91
77
  arity -1
92
- description "What to search for. Enclose in quotes to search for phrase."
78
+ description "Search terms. Enclose in quotes to search for phrase. use with -e or --regex option for regular expression support"
93
79
  }
94
80
 
95
-
96
- def determine_directories_to_search
97
- if @stdin.tty?
98
- dirs = if params['only_rails'].value || params['only_gems'].value
99
- []
100
- else
101
- params['dir'].values
102
- end
103
- dirs += Array(rails_path) if params['only_rails'].value || params['rails'].value
104
- dirs << gems_path if params['only_gems'].value || params['gems'].value
105
- else
106
- dirs = []
107
- input = @stdin.read
108
-
109
- input.split( "\n" ).each do |ln|
110
- if ln =~ /^([^:]*)/ # Assume everything to the first colon is a file name
111
- filename = File.expand_path( $1 )
112
- if File.exist?( filename ) # But actually check that it is
113
- dirs << filename
114
- end
115
- end
116
- end
117
- end
118
- dirs.map{ |dir| File.expand_path( dir ) }.uniq
119
- end
120
-
121
- def determine_what_to_search_for
122
- # funny bunny => 'funny' 'bunny'
123
- # 'funny bunny' => 'funny bunny'
124
- params['what_to_search_for'].values.map {|v| "\"#{v.gsub( /"/, '\\"' )}\"" }.join( " " )
125
- end
126
-
127
- def determine_javascript_highlight_text
128
- # funny bunny => 'funny' 'bunny'
129
- # 'funny bunny' => 'funny bunny'
130
- params['what_to_search_for'].values.map {|v| "highlightSearchTerms('#{v.gsub( /'/, "\\'" ).gsub( /:/, "\\:" )}', false)" }.join( ";" )
131
- end
132
-
133
- def find( what_to_find, opts={} )
134
- directories = opts[:in]
135
-
136
- paths = `find #{directories.join(' ')}`.split( "\n" )
137
-
138
- paths.delete_if { |path| params['exclude'].values.any?{ |exclude| path =~ exclude } }
139
-
140
- cmd = 'grep '
141
- cmd << '-i ' unless params['case'].value
142
- # 3 lines of context unless we're not displaying html, or we're counting, or we're piping into another process
143
- cmd << '-C 3 ' if !params['no_html'].value && !params['count'].value && @stdout.tty?
144
-
145
- # paths get too large for grep to handle, so limit the number it has to deal with at once.
146
- results = []
147
- paths.each_slice(100) {|paths|
148
- results += `#{cmd} -n #{what_to_find} #{paths.join(" ")}`.split( "\n" )
149
- }
150
- results
151
- end
152
-
153
- def display( results )
154
- if results.empty?
155
- puts "Nothing found!"
156
- exit
157
- elsif params['count'].value
158
- puts "Matches: #{results.size}"
159
- exit
160
- end
161
-
162
- if @stdout.tty?
163
- if params['no_html'].value
164
- display_textmate results
165
- else
166
- display_html results
167
- end
168
- else
169
- puts results
170
- end
171
- end
172
-
173
- def display_textmate( results )
174
- print "Found #{results.size} matches. "
175
- if results.size > 20
176
- puts "Display? [Y/n]..."
177
- exit if $stdin.gets.chomp.downcase == 'n'
178
- end
179
-
180
- results.each { |f|
181
- file, line = f.split( ':' )
182
- system( "mate #{'-w ' if params['wait'].value}-l #{line} #{file}" )
183
- }
184
- end
185
-
186
- def display_html( results )
187
- syntax_installed = true
188
- begin
189
- require 'syntax/convertors/html'
190
- rescue LoadError
191
- syntax_installed = false
192
- end
193
-
194
- html = '<html><head>'
195
- html << <<-CSS
196
- <style type="text/css">body { background-color: #EEEEEE; } pre {
197
- display: inline; } .ruby { font-family: Monaco; font-size: 10pt;
198
- background-color: white } .ruby .normal {} .ruby .comment { color:
199
- #005; font-style: italic; } .ruby .keyword { color: #A00; font-weight:
200
- bold; } .ruby .method { color: #077; } .ruby .class { color: #074; }
201
- .ruby .module { color: #050; } .ruby .punct { color: #447; font-weight:
202
- bold; } .ruby .symbol { color: #099; } .ruby .string { color: #944;
203
- background: #FFE; } .ruby .char { color: #F07; } .ruby .ident { color:
204
- #004; } .ruby .constant { color: #07F; } .ruby .regex { color: #B66;
205
- background: #FEF; } .ruby .number { color: #F99; } .ruby .attribute {
206
- color: #7BB; } .ruby .global { color: #7FB; } .ruby .expr { color:
207
- #227; } .ruby .escape { color: #277; } .ruby .highlight {
208
- background-color: yellow; font-weight: 900; } td.lineno { text-align:
209
- right; font-family: Monaco; font-size: 9pt; padding-right: 10px; }
210
- td.filename { padding-top: 35px; font-family: Monaco; font-size: 14pt;
211
- }</style>
212
-
213
- <script type="text/javascript">
214
- // http://www.nsftools.com/misc/SearchAndHighlight.htm
215
-
216
- function doHighlight(bodyText,searchTerm,highlightStartTag,highlightEndTag)
217
- {if((!highlightStartTag)||(!highlightEndTag)){highlightStartTag="<font style='color:blue; background-color:yellow;'>";highlightEndTag="</font>";}
218
- 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;}}}}
219
- return newText;}
220
- function highlightSearchTerms(searchText,treatAsPhrase,warnOnFailure,highlightStartTag,highlightEndTag)
221
- {if(treatAsPhrase){searchArray=[searchText];}else{searchArray=searchText.split(" ");}
222
- 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.");}
223
- return false;}
224
- var bodyText=document.body.innerHTML;for(var i=0;i<searchArray.length;i++){bodyText=doHighlight(bodyText,searchArray[i],highlightStartTag,highlightEndTag);}
225
- document.body.innerHTML=bodyText;return true;}</script></script>
226
- CSS
227
-
228
- html << "</head><body onLoad=\"#{determine_javascript_highlight_text}\">"
229
-
230
- syntax_converter = ( syntax_installed ? Syntax::Convertors::HTML.for_syntax( "ruby" ) : nil )
231
- html << '<pre>sudo gem install syntax</pre> to get syntax highlighting<br><br>' unless syntax_installed
232
-
233
- html << '<table cellspacing="0" cellpadding="2">'
234
-
235
- last_file = ''
236
- separator = false
237
-
238
- html << results.map{ |line|
239
- if line == '--'
240
- separator = true
241
- ''
242
- elsif line =~ /^(.+?)-(\d+)-(.*)$/ || line =~ /^(.+?):(\d+):(.*)$/
243
- file = $1
244
- line = $2
245
- context = $3
246
-
247
- file_group = ''
248
-
249
- if file == last_file
250
- file_group << "<tr><td class=\"lineno\">...</td><td></td></tr>" if separator
251
- else
252
- parts = file.split( /\// )
253
- path = ''
254
- file_group << "<tr><td colspan=\"2\" class=\"filename\">"
255
- parts.each do |part|
256
- path << "/#{part}"
257
- file_group << "/" unless part.equal?( parts.first )
258
- if part.equal?( parts.last )
259
- file_group << "<a href=\"txmt://open?url=file://#{file}\">#{part}</a>"
260
- else
261
- file_group << "<a href=\"file:/#{path}\">#{part}</a>"
262
- end
263
- end
264
- file_group << "</td></tr>\n"
265
- end
266
-
267
- separator = false
268
- last_file = file
269
-
270
- file_group << "<tr><td class=\"lineno\"><a href=\"txmt://open?url=file://#{file}&line=#{line}\">#{line}</a></td>"
271
-
272
- converted = begin
273
- syntax_converter ? syntax_converter.convert( context =~ /^(.+?)\s*$/ ? $1 : context ) : context
274
- rescue Exception => e
275
- "TOKENIZING ERROR: #{context} <!-- \n#{e.message}\n#{e.backtrace.join("\n")}\n -->"
276
- end
277
- file_group << "<td class=\"ruby\">#{ converted }</td></tr>\n"
278
-
279
- file_group
280
- end
281
- }.join
282
-
283
- html << '</table></body></html>'
284
-
285
- FileUtils.makedirs( File.dirname( params['tempfile'].value ) )
286
- FileUtils.touch( params['tempfile'].value )
287
- File.open( params['tempfile'].value, 'w' ) { |f| f.write(html) }
288
-
289
- system( "open #{params['tempfile'].value}" )
290
- end
291
-
292
81
  def run
293
- haystack = determine_directories_to_search
294
- needle = determine_what_to_search_for
295
-
296
- results = find needle, :in => haystack
297
-
298
- display results
82
+ grepmate = Grepmate.new(params)
83
+ grepmate.find
84
+ grepmate.display
299
85
  end
300
86
  }