milkode 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +18 -0
- data/HISTORY.rdoc +5 -0
- data/LICENSE.txt +20 -0
- data/README.rdoc +19 -0
- data/Rakefile +78 -0
- data/VERSION +1 -0
- data/bin/cdv +6 -0
- data/bin/cdview +10 -0
- data/bin/milk +10 -0
- data/lib/milkode/cdstk/cdstk.rb +508 -0
- data/lib/milkode/cdstk/cdstk_yaml.rb +106 -0
- data/lib/milkode/cdstk/cli_cdstk.rb +103 -0
- data/lib/milkode/cdstk/cli_cdstksub.rb +102 -0
- data/lib/milkode/cdview/cli_cdview.rb +43 -0
- data/lib/milkode/cdweb/app.rb +136 -0
- data/lib/milkode/cdweb/cli_cdweb.rb +111 -0
- data/lib/milkode/cdweb/config.ru +3 -0
- data/lib/milkode/cdweb/lib/coderay_wrapper.rb +94 -0
- data/lib/milkode/cdweb/lib/command.rb +67 -0
- data/lib/milkode/cdweb/lib/database.rb +260 -0
- data/lib/milkode/cdweb/lib/grep.rb +68 -0
- data/lib/milkode/cdweb/lib/mkurl.rb +56 -0
- data/lib/milkode/cdweb/lib/query.rb +81 -0
- data/lib/milkode/cdweb/lib/search_contents.rb +117 -0
- data/lib/milkode/cdweb/lib/search_files.rb +88 -0
- data/lib/milkode/cdweb/public/css/coderay.css +131 -0
- data/lib/milkode/cdweb/public/css/gren.css +75 -0
- data/lib/milkode/cdweb/public/images/directory.png +0 -0
- data/lib/milkode/cdweb/public/images/file.png +0 -0
- data/lib/milkode/cdweb/public/images/gren-icon-mini.png +0 -0
- data/lib/milkode/cdweb/public/images/gren-icon.png +0 -0
- data/lib/milkode/cdweb/views/filelist.haml +14 -0
- data/lib/milkode/cdweb/views/help.haml +16 -0
- data/lib/milkode/cdweb/views/index.haml +17 -0
- data/lib/milkode/cdweb/views/layout.haml +33 -0
- data/lib/milkode/cdweb/views/search.haml +12 -0
- data/lib/milkode/cdweb/views/view.haml +16 -0
- data/lib/milkode/common/dbdir.rb +39 -0
- data/lib/milkode/common/display_util.rb +62 -0
- data/lib/milkode/common/grenfiletest.rb +19 -0
- data/lib/milkode/common/grensnip.rb +37 -0
- data/lib/milkode/common/platform.rb +17 -0
- data/lib/milkode/common/string_snip.rb +61 -0
- data/lib/milkode/common/util.rb +144 -0
- data/lib/milkode/findgrep/findgrep.rb +408 -0
- data/lib/milkode/findgrep/result.rb +43 -0
- data/milkode.gemspec +159 -0
- data/test/data/abc.zip +0 -0
- data/test/data/nodir_abc.zip +0 -0
- data/test/data/nodir_abc_xpi.xpi +0 -0
- data/test/file_assert.rb +41 -0
- data/test/file_test_utils.rb +59 -0
- data/test/rake_test_loader.rb +7 -0
- data/test/runner.rb +11 -0
- data/test/test_bin_exec.rb +30 -0
- data/test/test_cdstk.rb +90 -0
- data/test/test_cdstk_yaml.rb +157 -0
- data/test/test_coderay_wrapper.rb +22 -0
- data/test/test_coderay_wrapper_data.rb +91 -0
- data/test/test_database.rb +78 -0
- data/test/test_dbdir.rb +60 -0
- data/test/test_gren_util.rb +34 -0
- data/test/test_helper.rb +2 -0
- data/test/test_mkurl.rb +30 -0
- data/test/test_query.rb +54 -0
- data/test/test_string_snip.rb +31 -0
- data/test/test_util.rb +40 -0
- metadata +300 -0
@@ -0,0 +1,408 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'find'
|
3
|
+
require 'milkode/findgrep/result'
|
4
|
+
require 'rubygems'
|
5
|
+
require 'termcolor'
|
6
|
+
require 'kconv'
|
7
|
+
require 'milkode/common/platform'
|
8
|
+
require 'milkode/common/grenfiletest'
|
9
|
+
require 'milkode/common/grensnip'
|
10
|
+
require 'groonga'
|
11
|
+
require 'milkode/common/util'
|
12
|
+
include Gren
|
13
|
+
require 'cgi'
|
14
|
+
|
15
|
+
module FindGrep
|
16
|
+
class FindGrep
|
17
|
+
Option = Struct.new(:keywordsNot,
|
18
|
+
:keywordsOr,
|
19
|
+
:directory,
|
20
|
+
:depth,
|
21
|
+
:ignoreCase,
|
22
|
+
:colorHighlight,
|
23
|
+
:isSilent,
|
24
|
+
:debugMode,
|
25
|
+
:filePatterns,
|
26
|
+
:suffixs,
|
27
|
+
:ignoreFiles,
|
28
|
+
:ignoreDirs,
|
29
|
+
:kcode,
|
30
|
+
:noSnip,
|
31
|
+
:dbFile,
|
32
|
+
:groongaOnly,
|
33
|
+
:isMatchFile,
|
34
|
+
:dispHtml)
|
35
|
+
|
36
|
+
DEFAULT_OPTION = Option.new([],
|
37
|
+
[],
|
38
|
+
".",
|
39
|
+
-1,
|
40
|
+
false,
|
41
|
+
false,
|
42
|
+
false,
|
43
|
+
false,
|
44
|
+
[],
|
45
|
+
[],
|
46
|
+
[],
|
47
|
+
[],
|
48
|
+
Platform.get_shell_kcode,
|
49
|
+
false,
|
50
|
+
nil,
|
51
|
+
false,
|
52
|
+
false,
|
53
|
+
false)
|
54
|
+
|
55
|
+
attr_reader :documents
|
56
|
+
|
57
|
+
def initialize(patterns, option)
|
58
|
+
@patterns = patterns
|
59
|
+
@option = option
|
60
|
+
@patternRegexps = strs2regs(patterns, @option.ignoreCase)
|
61
|
+
@subRegexps = strs2regs(option.keywordsNot, @option.ignoreCase)
|
62
|
+
@orRegexps = strs2regs(option.keywordsOr, @option.ignoreCase)
|
63
|
+
@filePatterns = (!@option.dbFile) ? strs2regs(option.filePatterns) : []
|
64
|
+
@ignoreFiles = strs2regs(option.ignoreFiles)
|
65
|
+
@ignoreDirs = strs2regs(option.ignoreDirs)
|
66
|
+
@result = Result.new(option.directory)
|
67
|
+
open_database if (@option.dbFile)
|
68
|
+
end
|
69
|
+
|
70
|
+
def open_database()
|
71
|
+
# データベース開く
|
72
|
+
dbfile = Pathname(File.expand_path(@option.dbFile))
|
73
|
+
|
74
|
+
if dbfile.exist?
|
75
|
+
Groonga::Database.open(dbfile.to_s)
|
76
|
+
puts "open : #{dbfile} open."
|
77
|
+
else
|
78
|
+
raise "error : #{dbfile.to_s} not found!!"
|
79
|
+
end
|
80
|
+
|
81
|
+
# ドキュメントを取
|
82
|
+
@documents = Groonga::Context.default["documents"]
|
83
|
+
end
|
84
|
+
|
85
|
+
def strs2regs(strs, ignore = false)
|
86
|
+
regs = []
|
87
|
+
|
88
|
+
strs.each do |v|
|
89
|
+
option = 0
|
90
|
+
option |= Regexp::IGNORECASE if (ignore)
|
91
|
+
regs << Regexp.new(v, option)
|
92
|
+
end
|
93
|
+
|
94
|
+
regs
|
95
|
+
end
|
96
|
+
|
97
|
+
def searchAndPrint(stdout)
|
98
|
+
unless (@option.dbFile)
|
99
|
+
searchFromDir(stdout, @option.directory, 0)
|
100
|
+
else
|
101
|
+
searchFromDB(stdout, @option.directory)
|
102
|
+
end
|
103
|
+
|
104
|
+
@result.time_stop
|
105
|
+
|
106
|
+
if (!@option.isSilent && !@option.dispHtml)
|
107
|
+
if (@option.debugMode)
|
108
|
+
stdout.puts
|
109
|
+
stdout.puts "--- search --------"
|
110
|
+
print_fpaths stdout, @result.search_files
|
111
|
+
stdout.puts "--- match --------"
|
112
|
+
print_fpaths stdout, @result.match_files
|
113
|
+
stdout.puts "--- ignore-file --------"
|
114
|
+
print_fpaths stdout, @result.ignore_files
|
115
|
+
stdout.puts "--- ignore-dir --------"
|
116
|
+
print_fpaths stdout, @result.prune_dirs
|
117
|
+
stdout.puts "--- unreadable --------"
|
118
|
+
print_fpaths stdout, @result.unreadable_files
|
119
|
+
end
|
120
|
+
|
121
|
+
unless (@option.colorHighlight)
|
122
|
+
stdout.puts
|
123
|
+
else
|
124
|
+
stdout.puts HighLine::REVERSE + "------------------------------------------------------------" + HighLine::CLEAR
|
125
|
+
end
|
126
|
+
|
127
|
+
@result.print(stdout)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def searchFromDB(stdout, dir)
|
132
|
+
# 全てのパターンを検索
|
133
|
+
table = @documents.select do |record|
|
134
|
+
expression = nil
|
135
|
+
|
136
|
+
# キーワード
|
137
|
+
@patterns.each do |word|
|
138
|
+
sub_expression = record.content =~ word
|
139
|
+
if expression.nil?
|
140
|
+
expression = sub_expression
|
141
|
+
else
|
142
|
+
expression &= sub_expression
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# パス
|
147
|
+
@option.filePatterns.each do |word|
|
148
|
+
sub_expression = record.path =~ word
|
149
|
+
if expression.nil?
|
150
|
+
expression = sub_expression
|
151
|
+
else
|
152
|
+
expression &= sub_expression
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
# 拡張子(OR)
|
157
|
+
se = suffix_expression(record)
|
158
|
+
expression &= se if (se)
|
159
|
+
|
160
|
+
# 検索式
|
161
|
+
expression
|
162
|
+
end
|
163
|
+
|
164
|
+
# タイムスタンプでソート
|
165
|
+
records = table.sort([{:key => "_score", :order => "descending"},
|
166
|
+
{:key => "timestamp", :order => "descending"}])
|
167
|
+
|
168
|
+
# データベースにヒット
|
169
|
+
stdout.puts "Found : #{records.size} records." unless (@option.dispHtml)
|
170
|
+
|
171
|
+
# 検索にヒットしたファイルを実際に検索
|
172
|
+
records.each do |record|
|
173
|
+
if (@option.groongaOnly)
|
174
|
+
searchGroongaOnly(stdout, record)
|
175
|
+
else
|
176
|
+
searchFile(stdout, record.path, record.path) if FileTest.exist?(record.path)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def and_expression(key, list)
|
182
|
+
sub = nil
|
183
|
+
|
184
|
+
list.each do |word|
|
185
|
+
e = key =~ word
|
186
|
+
if sub.nil?
|
187
|
+
sub = e
|
188
|
+
else
|
189
|
+
sub &= e
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
sub
|
194
|
+
end
|
195
|
+
|
196
|
+
def suffix_expression(record)
|
197
|
+
sub = nil
|
198
|
+
|
199
|
+
@option.suffixs.each do |word|
|
200
|
+
e = record.suffix =~ word
|
201
|
+
if sub.nil?
|
202
|
+
sub = e
|
203
|
+
else
|
204
|
+
sub |= e
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
sub
|
209
|
+
end
|
210
|
+
private :suffix_expression
|
211
|
+
|
212
|
+
|
213
|
+
def searchFromDir(stdout, dir, depth)
|
214
|
+
if (@option.depth != -1 && depth > @option.depth)
|
215
|
+
return
|
216
|
+
end
|
217
|
+
|
218
|
+
Dir.foreach(dir) do |name|
|
219
|
+
next if (name == '.' || name == '..')
|
220
|
+
|
221
|
+
fpath = File.join(dir,name)
|
222
|
+
fpath_disp = fpath.gsub(/^.\//, "")
|
223
|
+
|
224
|
+
# 除外ディレクトリならばパス
|
225
|
+
if ignoreDir?(fpath)
|
226
|
+
@result.prune_dirs << fpath_disp if (@option.debugMode)
|
227
|
+
next;
|
228
|
+
end
|
229
|
+
|
230
|
+
# 読み込み不可ならばパス
|
231
|
+
unless FileTest.readable?(fpath)
|
232
|
+
@result.unreadable_files << fpath_disp if (@option.debugMode)
|
233
|
+
next
|
234
|
+
end
|
235
|
+
|
236
|
+
# ファイルならば中身を探索、ディレクトリならば再帰
|
237
|
+
case File.ftype(fpath)
|
238
|
+
when "directory"
|
239
|
+
searchFromDir(stdout, fpath, depth + 1)
|
240
|
+
when "file"
|
241
|
+
searchFile(stdout, fpath, fpath_disp)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
private :searchFromDir
|
246
|
+
|
247
|
+
def print_fpaths(stdout, data)
|
248
|
+
stdout.print data.join("\n")
|
249
|
+
stdout.puts if data.count > 0
|
250
|
+
stdout.puts "total: #{data.count}"
|
251
|
+
stdout.puts
|
252
|
+
end
|
253
|
+
private :print_fpaths
|
254
|
+
|
255
|
+
def ignoreDir?(fpath)
|
256
|
+
FileTest.directory?(fpath) &&
|
257
|
+
(GrenFileTest::ignoreDir?(File.basename(fpath)) || ignoreDirUser?(fpath))
|
258
|
+
end
|
259
|
+
private :ignoreDir?
|
260
|
+
|
261
|
+
def ignoreDirUser?(fpath)
|
262
|
+
@ignoreDirs.any? {|v| v.match File.basename(fpath) }
|
263
|
+
end
|
264
|
+
private :ignoreDirUser?
|
265
|
+
|
266
|
+
def ignoreFile?(fpath)
|
267
|
+
!correctFileUser?(fpath) ||
|
268
|
+
GrenFileTest::ignoreFile?(fpath) ||
|
269
|
+
ignoreFileUser?(fpath) ||
|
270
|
+
GrenFileTest::binary?(fpath)
|
271
|
+
end
|
272
|
+
private :ignoreFile?
|
273
|
+
|
274
|
+
def correctFileUser?(fpath)
|
275
|
+
@filePatterns.empty? ||
|
276
|
+
@filePatterns.any? {|v| v.match File.basename(fpath) }
|
277
|
+
end
|
278
|
+
private :correctFileUser?
|
279
|
+
|
280
|
+
def ignoreFileUser?(fpath)
|
281
|
+
@ignoreFiles.any? {|v| v.match File.basename(fpath) }
|
282
|
+
end
|
283
|
+
private :ignoreFileUser?
|
284
|
+
|
285
|
+
def searchFile(stdout, fpath, fpath_disp)
|
286
|
+
@result.count += 1
|
287
|
+
@result.size += FileTest.size(fpath)
|
288
|
+
|
289
|
+
# 除外ファイル
|
290
|
+
if ignoreFile?(fpath)
|
291
|
+
@result.ignore_files << fpath_disp if (@option.debugMode)
|
292
|
+
return
|
293
|
+
end
|
294
|
+
|
295
|
+
@result.search_count += 1
|
296
|
+
@result.search_size += FileTest.size(fpath)
|
297
|
+
|
298
|
+
@result.search_files << fpath_disp if (@option.debugMode)
|
299
|
+
|
300
|
+
open(fpath, "r") do |file|
|
301
|
+
searchData(stdout, file2data(file), fpath_disp)
|
302
|
+
end
|
303
|
+
end
|
304
|
+
private :searchFile
|
305
|
+
|
306
|
+
def searchGroongaOnly(stdout, record)
|
307
|
+
file_size = record.content.size
|
308
|
+
|
309
|
+
@result.count += 1
|
310
|
+
@result.size += file_size
|
311
|
+
|
312
|
+
@result.search_count += 1
|
313
|
+
@result.search_size += file_size
|
314
|
+
|
315
|
+
@result.search_files << record.path if (@option.debugMode)
|
316
|
+
|
317
|
+
searchData(stdout, record.content, record.path)
|
318
|
+
end
|
319
|
+
private :searchGroongaOnly
|
320
|
+
|
321
|
+
def searchData(stdout, data, path)
|
322
|
+
match_file = false
|
323
|
+
|
324
|
+
data.each_with_index { |line, index|
|
325
|
+
result, match_datas = match?(line)
|
326
|
+
|
327
|
+
if ( result )
|
328
|
+
unless (@option.dispHtml)
|
329
|
+
header = "#{path}:#{index + 1}:"
|
330
|
+
line = GrenSnip::snip(line, match_datas) unless (@option.noSnip)
|
331
|
+
|
332
|
+
unless (@option.colorHighlight)
|
333
|
+
stdout.puts header + line
|
334
|
+
else
|
335
|
+
stdout.puts HighLine::BLUE + header + HighLine::CLEAR + GrenSnip::coloring(line, match_datas)
|
336
|
+
end
|
337
|
+
else
|
338
|
+
line_no = index + 1
|
339
|
+
line = GrenSnip::snip(line, match_datas) unless (@option.noSnip)
|
340
|
+
|
341
|
+
stdout.puts <<EOF
|
342
|
+
<h2><a href="../::view#{path}">#{path}</a></h2>
|
343
|
+
<pre>
|
344
|
+
#{line_no} : #{CGI.escapeHTML(line)}
|
345
|
+
</pre>
|
346
|
+
EOF
|
347
|
+
end
|
348
|
+
|
349
|
+
unless match_file
|
350
|
+
@result.match_file_count += 1
|
351
|
+
@result.match_files << path if (@option.debugMode)
|
352
|
+
match_file = true
|
353
|
+
break if (@option.isMatchFile)
|
354
|
+
end
|
355
|
+
|
356
|
+
@result.match_count += 1
|
357
|
+
end
|
358
|
+
}
|
359
|
+
end
|
360
|
+
private :searchData
|
361
|
+
|
362
|
+
def file2data(file)
|
363
|
+
data = file.read
|
364
|
+
|
365
|
+
if (@option.kcode != Kconv::NOCONV)
|
366
|
+
file_kcode = Kconv::guess(data)
|
367
|
+
|
368
|
+
if (file_kcode != @option.kcode)
|
369
|
+
# puts "encode!! #{fpath} : #{@option.kcode} <- #{file_kcode}"
|
370
|
+
data = data.kconv(@option.kcode, file_kcode)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
data = data.split("\n");
|
375
|
+
end
|
376
|
+
private :file2data
|
377
|
+
|
378
|
+
def match?(line)
|
379
|
+
match_datas = []
|
380
|
+
@patternRegexps.each {|v| match_datas << v.match(line)}
|
381
|
+
|
382
|
+
sub_matchs = []
|
383
|
+
@subRegexps.each {|v| sub_matchs << v.match(line)}
|
384
|
+
|
385
|
+
or_matchs = []
|
386
|
+
@orRegexps.each {|v| or_matchs << v.match(line)}
|
387
|
+
|
388
|
+
unless (@option.isMatchFile)
|
389
|
+
result = match_datas.all? && !sub_matchs.any? && (or_matchs.empty? || or_matchs.any?)
|
390
|
+
else
|
391
|
+
result = first_condition(match_datas, sub_matchs, or_matchs)
|
392
|
+
end
|
393
|
+
result_match = match_datas + or_matchs
|
394
|
+
result_match.delete(nil)
|
395
|
+
|
396
|
+
return result, result_match
|
397
|
+
end
|
398
|
+
private :match?
|
399
|
+
|
400
|
+
def first_condition(match_datas, sub_matchs, or_matchs)
|
401
|
+
unless match_datas.empty?
|
402
|
+
match_datas[0]
|
403
|
+
else
|
404
|
+
or_matchs[0]
|
405
|
+
end
|
406
|
+
end
|
407
|
+
end
|
408
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'find'
|
3
|
+
require 'milkode/common/util'
|
4
|
+
include Gren
|
5
|
+
|
6
|
+
module FindGrep
|
7
|
+
class Result
|
8
|
+
attr_accessor :count
|
9
|
+
attr_accessor :search_count
|
10
|
+
attr_accessor :match_file_count
|
11
|
+
attr_accessor :match_count
|
12
|
+
attr_accessor :size
|
13
|
+
attr_accessor :search_size
|
14
|
+
|
15
|
+
attr_accessor :search_files
|
16
|
+
attr_accessor :match_files
|
17
|
+
attr_accessor :unreadable_files
|
18
|
+
attr_accessor :prune_dirs
|
19
|
+
attr_accessor :ignore_files
|
20
|
+
|
21
|
+
def initialize(start_dir)
|
22
|
+
@start_dir = File.expand_path(start_dir)
|
23
|
+
@count, @search_count, @match_file_count, @match_count, @size, @search_size = 0, 0, 0, 0, 0, 0
|
24
|
+
@start_time = Time.now
|
25
|
+
@search_files, @match_files, @unreadable_files, @prune_dirs, @ignore_files = [], [], [], [], []
|
26
|
+
end
|
27
|
+
|
28
|
+
def time_stop
|
29
|
+
@end_time = Time.now
|
30
|
+
end
|
31
|
+
|
32
|
+
def time
|
33
|
+
@end_time - @start_time
|
34
|
+
end
|
35
|
+
|
36
|
+
def print(stdout)
|
37
|
+
stdout.puts "dir : #{@start_dir} (#{Util::time_s(time)})"
|
38
|
+
stdout.puts "files : #{@search_count} in #{@count} (#{Util::size_s(@search_size)} in #{Util::size_s(@size)})"
|
39
|
+
stdout.puts "match : #{@match_file_count} files, #{match_count} hit"
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|