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 +1 -0
- data/LICENSE +20 -0
- data/README.rdoc +60 -0
- data/Rakefile +47 -9
- data/VERSION +1 -0
- data/bin/grepmate +44 -258
- data/config/dot-grepmate +52 -0
- data/grepmate.gemspec +41 -14
- data/lib/.empty +0 -0
- data/lib/.git-empty +1 -0
- data/lib/grepmate.rb +187 -0
- data/lib/output/file_and_line.rb +17 -0
- data/lib/output/html.rb +136 -0
- data/lib/output/text.rb +11 -0
- data/lib/output/textmate.rb +22 -0
- data/spec/grepmate_spec.rb +117 -0
- data/spec/spec_helper.rb +11 -0
- data/tmp/grepmate.html +83 -0
- metadata +41 -20
- data/Manifest +0 -5
- data/README +0 -54
data/config/dot-grepmate
ADDED
@@ -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 = "
|
5
|
+
s.version = "2.0.0"
|
6
6
|
|
7
|
-
s.required_rubygems_version = Gem::Requirement.new(">=
|
8
|
-
s.authors = ["Steven Soroka"]
|
9
|
-
s.date = %q{
|
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 = [
|
15
|
-
|
16
|
-
|
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 = ["--
|
39
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
19
40
|
s.require_paths = ["lib"]
|
20
|
-
s.
|
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 =
|
50
|
+
s.specification_version = 3
|
27
51
|
|
28
52
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
29
|
-
s.
|
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>, [">=
|
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>, [">=
|
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
|
data/lib/output/html.rb
ADDED
@@ -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
|