ssoroka-grepmate 1.0.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }