dorian 2.4.0 → 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +15 -0
- data/.github/workflows/ci.yml +38 -0
- data/.gitignore +30 -0
- data/.node-version +1 -0
- data/.prettierignore +2 -0
- data/.rubocop.yml +129 -0
- data/.ruby-version +1 -0
- data/.tool-versions +3 -0
- data/Gemfile +20 -0
- data/Gemfile.lock +186 -0
- data/LICENSE +19 -0
- data/README.md +24 -0
- data/VERSION +1 -1
- data/bin/bundle +123 -0
- data/bin/bundle-audit +31 -0
- data/bin/bundler-audit +31 -0
- data/bin/rspec +31 -0
- data/bin/rubocop +31 -0
- data/dorian.gemspec +35 -0
- data/lib/dorian/bin.rb +739 -81
- data/package-lock.json +39 -0
- data/package.json +11 -0
- data/samples/books.json +33 -0
- data/samples/books.jsonl +3 -0
- data/samples/config.yml +27 -0
- data/samples/config_2.yml +22 -0
- data/samples/maths.js +1 -0
- data/samples/numbers.raw +10 -0
- data/samples/people.csv +6 -0
- data/samples/people.yml +28 -0
- data/samples/query.sql +7 -0
- data/samples/user.json +31 -0
- data/spec/dorian_spec.rb +276 -0
- data/spec/spec_helper.rb +3 -0
- data/vendor/groovy-beautify/LICENSE.MD +21 -0
- data/vendor/groovy-beautify/README.md +65 -0
- data/vendor/groovy-beautify/dist/__tests__/index.test.d.ts +1 -0
- data/vendor/groovy-beautify/dist/cjs/index.js +573 -0
- data/vendor/groovy-beautify/dist/cjs/package.json +3 -0
- data/vendor/groovy-beautify/dist/es/index.js +682 -0
- data/vendor/groovy-beautify/dist/es/package.json +3 -0
- data/vendor/groovy-beautify/dist/formatter/FormatRule.d.ts +14 -0
- data/vendor/groovy-beautify/dist/formatter/Formatter.d.ts +11 -0
- data/vendor/groovy-beautify/dist/formatter/GroovyFormatRules.d.ts +3 -0
- data/vendor/groovy-beautify/dist/formatter/__tests__/formatter.test.d.ts +1 -0
- data/vendor/groovy-beautify/dist/index.d.ts +5 -0
- data/vendor/groovy-beautify/dist/parser/GroovyParseRules.d.ts +3 -0
- data/vendor/groovy-beautify/dist/parser/ParseRule.d.ts +23 -0
- data/vendor/groovy-beautify/dist/parser/Parser.d.ts +29 -0
- data/vendor/groovy-beautify/dist/parser/__tests__/parser.test.d.ts +1 -0
- data/vendor/groovy-beautify/dist/parser/types.d.ts +6 -0
- data/vendor/groovy-beautify/dist/utils/text.d.ts +4 -0
- data/vendor/groovy-beautify/package.json +55 -0
- data/vendor/prettier/LICENSE +4379 -0
- data/vendor/prettier/README.md +109 -0
- data/vendor/prettier/bin/prettier.cjs +71 -0
- data/vendor/prettier/doc.d.ts +243 -0
- data/vendor/prettier/doc.js +1545 -0
- data/vendor/prettier/doc.mjs +1503 -0
- data/vendor/prettier/index.cjs +825 -0
- data/vendor/prettier/index.d.ts +941 -0
- data/vendor/prettier/index.mjs +25899 -0
- data/vendor/prettier/internal/cli.mjs +4366 -0
- data/vendor/prettier/package.json +198 -0
- data/vendor/prettier/plugins/acorn.d.ts +6 -0
- data/vendor/prettier/plugins/acorn.js +6618 -0
- data/vendor/prettier/plugins/acorn.mjs +6456 -0
- data/vendor/prettier/plugins/angular.d.ts +8 -0
- data/vendor/prettier/plugins/angular.js +2435 -0
- data/vendor/prettier/plugins/angular.mjs +2375 -0
- data/vendor/prettier/plugins/babel.d.ts +18 -0
- data/vendor/prettier/plugins/babel.js +14271 -0
- data/vendor/prettier/plugins/babel.mjs +13986 -0
- data/vendor/prettier/plugins/estree.d.ts +1 -0
- data/vendor/prettier/plugins/estree.js +9487 -0
- data/vendor/prettier/plugins/estree.mjs +9279 -0
- data/vendor/prettier/plugins/flow.d.ts +5 -0
- data/vendor/prettier/plugins/flow.js +51477 -0
- data/vendor/prettier/plugins/flow.mjs +51219 -0
- data/vendor/prettier/plugins/glimmer.d.ts +5 -0
- data/vendor/prettier/plugins/glimmer.js +8534 -0
- data/vendor/prettier/plugins/glimmer.mjs +8421 -0
- data/vendor/prettier/plugins/graphql.d.ts +5 -0
- data/vendor/prettier/plugins/graphql.js +2421 -0
- data/vendor/prettier/plugins/graphql.mjs +2347 -0
- data/vendor/prettier/plugins/html.d.ts +8 -0
- data/vendor/prettier/plugins/html.js +8182 -0
- data/vendor/prettier/plugins/html.mjs +8077 -0
- data/vendor/prettier/plugins/markdown.d.ts +7 -0
- data/vendor/prettier/plugins/markdown.js +9068 -0
- data/vendor/prettier/plugins/markdown.mjs +8977 -0
- data/vendor/prettier/plugins/meriyah.d.ts +5 -0
- data/vendor/prettier/plugins/meriyah.js +5953 -0
- data/vendor/prettier/plugins/meriyah.mjs +5800 -0
- data/vendor/prettier/plugins/postcss.d.ts +7 -0
- data/vendor/prettier/plugins/postcss.js +9244 -0
- data/vendor/prettier/plugins/postcss.mjs +9046 -0
- data/vendor/prettier/plugins/typescript.d.ts +5 -0
- data/vendor/prettier/plugins/typescript.js +38058 -0
- data/vendor/prettier/plugins/typescript.mjs +37732 -0
- data/vendor/prettier/plugins/yaml.d.ts +5 -0
- data/vendor/prettier/plugins/yaml.js +7425 -0
- data/vendor/prettier/plugins/yaml.mjs +7329 -0
- data/vendor/prettier/standalone.d.ts +33 -0
- data/vendor/prettier/standalone.js +3984 -0
- data/vendor/prettier/standalone.mjs +3938 -0
- data/vendor/sql-formatter.js +18762 -0
- metadata +205 -2
data/lib/dorian/bin.rb
CHANGED
@@ -6,14 +6,82 @@ require "dorian/eval"
|
|
6
6
|
require "dorian/progress"
|
7
7
|
require "dorian/to_struct"
|
8
8
|
require "git"
|
9
|
+
require "hexapdf"
|
9
10
|
require "json"
|
11
|
+
require "mini_racer"
|
10
12
|
require "net/http"
|
11
13
|
require "parallel"
|
14
|
+
require "shellwords"
|
15
|
+
require "syntax_tree"
|
16
|
+
require "syntax_tree/erb"
|
17
|
+
require "syntax_tree/haml"
|
18
|
+
require "syntax_tree/xml"
|
19
|
+
require "syntax_tree/json"
|
20
|
+
require "tempfile"
|
21
|
+
require "terminal-table"
|
12
22
|
require "uri"
|
13
23
|
require "yaml"
|
14
24
|
|
15
25
|
class Dorian
|
16
26
|
class Bin
|
27
|
+
RUBY_EXTENSIONS = %w[
|
28
|
+
.rb
|
29
|
+
.arb
|
30
|
+
.axlsx
|
31
|
+
.builder
|
32
|
+
.fcgi
|
33
|
+
.gemfile
|
34
|
+
.gemspec
|
35
|
+
.god
|
36
|
+
.jb
|
37
|
+
.jbuilder
|
38
|
+
.mspec
|
39
|
+
.opal
|
40
|
+
.pluginspec
|
41
|
+
.podspec
|
42
|
+
.rabl
|
43
|
+
.rake
|
44
|
+
.rbuild
|
45
|
+
.rbw
|
46
|
+
.rbx
|
47
|
+
.ru
|
48
|
+
.ruby
|
49
|
+
.schema
|
50
|
+
.spec
|
51
|
+
.thor
|
52
|
+
.watchr
|
53
|
+
].freeze
|
54
|
+
|
55
|
+
RUBY_FILENAMES = %w[
|
56
|
+
.irbrc
|
57
|
+
.pryrc
|
58
|
+
.simplecov
|
59
|
+
Appraisals
|
60
|
+
Berksfile
|
61
|
+
Brewfile
|
62
|
+
Buildfile
|
63
|
+
Capfile
|
64
|
+
Cheffile
|
65
|
+
Dangerfile
|
66
|
+
Deliverfile
|
67
|
+
Fastfile
|
68
|
+
Gemfile
|
69
|
+
Guardfile
|
70
|
+
Jarfile
|
71
|
+
Mavenfile
|
72
|
+
Podfile
|
73
|
+
Puppetfile
|
74
|
+
Rakefile
|
75
|
+
rakefile
|
76
|
+
Schemafile
|
77
|
+
Snapfile
|
78
|
+
Steepfile
|
79
|
+
Thorfile
|
80
|
+
Vagabondfile
|
81
|
+
Vagrantfile
|
82
|
+
buildfile
|
83
|
+
].freeze
|
84
|
+
|
17
85
|
VERSION = File.read(File.expand_path("../../VERSION", __dir__))
|
18
86
|
|
19
87
|
DEFAULT_IO = :raw
|
@@ -51,8 +119,7 @@ class Dorian
|
|
51
119
|
alias: :o
|
52
120
|
},
|
53
121
|
parallel: {
|
54
|
-
alias: :p
|
55
|
-
default: true
|
122
|
+
alias: :p
|
56
123
|
},
|
57
124
|
parallel_type: {
|
58
125
|
alias: :pt,
|
@@ -206,6 +273,83 @@ class Dorian
|
|
206
273
|
arguments.delete("ls")
|
207
274
|
@command = :ls
|
208
275
|
command_ls
|
276
|
+
when :strip
|
277
|
+
arguments.delete("strip")
|
278
|
+
@command = :strip
|
279
|
+
command_strip
|
280
|
+
when :rstrip
|
281
|
+
arguments.delete("rstrip")
|
282
|
+
@command = :rstrip
|
283
|
+
command_rstrip
|
284
|
+
when :lstrip
|
285
|
+
arguments.delete("lstrip")
|
286
|
+
@command = :lstrip
|
287
|
+
command_lstrip
|
288
|
+
when :merge
|
289
|
+
arguments.delete("merge")
|
290
|
+
@command = :merge
|
291
|
+
command_merge
|
292
|
+
when :pluck
|
293
|
+
arguments.delete("pluck")
|
294
|
+
@command = :pluck
|
295
|
+
command_pluck
|
296
|
+
when :shuffle
|
297
|
+
arguments.delete("shuffle")
|
298
|
+
@command = :shuffle
|
299
|
+
command_shuffle
|
300
|
+
when :rename
|
301
|
+
arguments.delete("rename")
|
302
|
+
@command = :rename
|
303
|
+
command_rename
|
304
|
+
when :replace
|
305
|
+
arguments.delete("replace")
|
306
|
+
@command = :replace
|
307
|
+
command_replace
|
308
|
+
when :sort
|
309
|
+
arguments.delete("sort")
|
310
|
+
@command = :sort
|
311
|
+
command_sort
|
312
|
+
when :table
|
313
|
+
arguments.delete("table")
|
314
|
+
@command = :table
|
315
|
+
command_table
|
316
|
+
when :then
|
317
|
+
arguments.delete("then")
|
318
|
+
@command = :then
|
319
|
+
@ruby = arguments.delete_at(0)
|
320
|
+
command_then
|
321
|
+
when :times
|
322
|
+
arguments.delete("times")
|
323
|
+
@command = :times
|
324
|
+
command_times
|
325
|
+
when :uniq
|
326
|
+
arguments.delete("uniq")
|
327
|
+
@command = :uniq
|
328
|
+
command_uniq
|
329
|
+
when :write
|
330
|
+
arguments.delete("write")
|
331
|
+
@command = :write
|
332
|
+
command_write
|
333
|
+
when :release
|
334
|
+
arguments.delete("release")
|
335
|
+
@command = :release
|
336
|
+
command_release
|
337
|
+
when :top
|
338
|
+
arguments.delete("top")
|
339
|
+
@command = :top
|
340
|
+
command_top
|
341
|
+
when :tree
|
342
|
+
arguments.delete("tree")
|
343
|
+
@command = :tree
|
344
|
+
command_tree
|
345
|
+
when :format
|
346
|
+
arguments.delete("format")
|
347
|
+
@command = :format
|
348
|
+
command_format
|
349
|
+
when :pretty
|
350
|
+
arguments.delete("pretty")
|
351
|
+
@command = :pretty
|
352
|
+
command_pretty
|
209
353
|
else
|
210
354
|
arguments.delete("read")
|
211
355
|
@command = :read
|
@@ -213,12 +357,217 @@ class Dorian
|
|
213
357
|
end
|
214
358
|
end
|
215
359
|
|
360
|
+
def command_pretty
|
361
|
+
command_format
|
362
|
+
end
|
363
|
+
|
364
|
+
def command_format
|
365
|
+
context = MiniRacer::Context.new
|
366
|
+
context.attach("puts", proc { |string| puts(string) })
|
367
|
+
context.attach("warn", proc { |string| warn(string) })
|
368
|
+
context.attach("read", proc { |path| File.read(path) })
|
369
|
+
context.attach(
|
370
|
+
"write",
|
371
|
+
proc { |path, content| File.write(path, content) }
|
372
|
+
)
|
373
|
+
|
374
|
+
root = File.expand_path("../../", __dir__)
|
375
|
+
|
376
|
+
prettier_path = File.join(root, "vendor/prettier/standalone.js")
|
377
|
+
prettier_js = File.read(prettier_path)
|
378
|
+
context.eval(prettier_js)
|
379
|
+
|
380
|
+
sql_path = File.join(root, "vendor/sql-formatter.js")
|
381
|
+
sql_js = File.read(sql_path)
|
382
|
+
context.eval("self = {};")
|
383
|
+
context.eval(sql_js)
|
384
|
+
context.eval("sqlFormatter = self.sqlFormatter;")
|
385
|
+
|
386
|
+
groovy_path = File.join(root, "vendor/groovy-beautify/dist/cjs/index.js")
|
387
|
+
groovy_js = File.read(groovy_path)
|
388
|
+
context.eval("module = { exports: {} };")
|
389
|
+
context.eval("exports = module.exports;")
|
390
|
+
context.eval(groovy_js)
|
391
|
+
context.eval("groovyBeautify = module.exports;")
|
392
|
+
|
393
|
+
plugins = %w[babel estree typescript html postcss markdown]
|
394
|
+
|
395
|
+
plugins.each do |plugin|
|
396
|
+
path = File.join(root, "vendor/prettier/plugins/#{plugin}.js")
|
397
|
+
js = File.read(path)
|
398
|
+
context.eval("module = { exports: {} };")
|
399
|
+
context.eval("exports = module.exports;")
|
400
|
+
context.eval(js)
|
401
|
+
context.eval("#{plugin} = module.exports;")
|
402
|
+
end
|
403
|
+
|
404
|
+
context.eval("plugins = [#{plugins.join(", ")}];")
|
405
|
+
|
406
|
+
context.eval(<<~JS)
|
407
|
+
format = async (path, parser) => {
|
408
|
+
try {
|
409
|
+
const before = read(path);
|
410
|
+
let after;
|
411
|
+
|
412
|
+
if (parser === "sql") {
|
413
|
+
after = sqlFormatter.format(before);
|
414
|
+
} else if (parser === "groovy") {
|
415
|
+
after = groovyBeautify(before);
|
416
|
+
} else {
|
417
|
+
after = await prettier.format(before, { parser, plugins });
|
418
|
+
}
|
419
|
+
|
420
|
+
if (before != after) {
|
421
|
+
puts(path);
|
422
|
+
write(path, after);
|
423
|
+
}
|
424
|
+
} catch (e) {
|
425
|
+
warn(`failed to parse ${path}: ${e.message.split("\\n")[0]}`);
|
426
|
+
}
|
427
|
+
};
|
428
|
+
JS
|
429
|
+
|
430
|
+
if files.any?
|
431
|
+
each(files) { |file| format(file, context:) }
|
432
|
+
else
|
433
|
+
each(
|
434
|
+
Git.open(".").ls_files.map(&:first)
|
435
|
+
) { |file| format(file, context:) }
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
def command_release
|
440
|
+
File.delete(*Dir["*.gem"])
|
441
|
+
system("gem build")
|
442
|
+
system("gem push *.gem")
|
443
|
+
File.delete(*Dir["*.gem"])
|
444
|
+
end
|
445
|
+
|
446
|
+
def command_top
|
447
|
+
shell = arguments[0] || File.basename(ENV.fetch("SHELL", nil)) || "bash"
|
448
|
+
limit = arguments[1] || 10
|
449
|
+
|
450
|
+
history =
|
451
|
+
case shell.to_s.downcase
|
452
|
+
when "fish"
|
453
|
+
File
|
454
|
+
.read("#{Dir.home}/.local/share/fish/fish_history")
|
455
|
+
.lines
|
456
|
+
.grep(/^- cmd: /)
|
457
|
+
.map { |line| line.split("- cmd: ", 2).last.strip }
|
458
|
+
when "bash"
|
459
|
+
File.read("#{Dir.home}/.bash_history").lines.map(&:strip)
|
460
|
+
when "zsh"
|
461
|
+
File.read("#{Dir.home}/.zsh_history").lines.map(&:strip)
|
462
|
+
else
|
463
|
+
raise NotImplementedError, shell
|
464
|
+
end
|
465
|
+
|
466
|
+
table(
|
467
|
+
history
|
468
|
+
.map { |line| line.split.first }
|
469
|
+
.tally
|
470
|
+
.to_a
|
471
|
+
.sort_by(&:last)
|
472
|
+
.reverse
|
473
|
+
.map
|
474
|
+
.with_index do |(command, command_count), index|
|
475
|
+
{
|
476
|
+
"#" => index + 1,
|
477
|
+
:count => command_count,
|
478
|
+
:percent =>
|
479
|
+
"#{(command_count * 100 / history.size.to_f).round(3)}%",
|
480
|
+
:command => command
|
481
|
+
}
|
482
|
+
end
|
483
|
+
.first(limit)
|
484
|
+
)
|
485
|
+
end
|
486
|
+
|
487
|
+
def command_tree
|
488
|
+
space = " "
|
489
|
+
right = "└── "
|
490
|
+
down = "│ "
|
491
|
+
down_and_right = "├── "
|
492
|
+
|
493
|
+
git_ls_files = ->(path) { Git.open(".").ls_files(path).map(&:first) }
|
494
|
+
|
495
|
+
group =
|
496
|
+
lambda do |files|
|
497
|
+
files
|
498
|
+
.group_by { |file| file.split("/").first }
|
499
|
+
.transform_values do |values|
|
500
|
+
group.call(
|
501
|
+
values
|
502
|
+
.map { |value| value.split("/")[1..].join("/") }
|
503
|
+
.reject(&:empty?)
|
504
|
+
)
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
print =
|
509
|
+
lambda do |key:, values:, index: 0, size: 1, prefix: ""|
|
510
|
+
key = "#{key}/" if values.any?
|
511
|
+
last = index + 1 == size
|
512
|
+
right_prefix = last ? right : down_and_right
|
513
|
+
puts prefix + right_prefix + key
|
514
|
+
values.each.with_index do |(value_key, value_values), value_index|
|
515
|
+
print.call(
|
516
|
+
key: value_key,
|
517
|
+
values: value_values,
|
518
|
+
index: value_index,
|
519
|
+
size: values.size,
|
520
|
+
prefix: prefix + (last ? space : down)
|
521
|
+
)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
keys = (arguments + files)
|
526
|
+
keys = ["."] unless keys.any?
|
527
|
+
|
528
|
+
keys.each do |key|
|
529
|
+
files =
|
530
|
+
git_ls_files
|
531
|
+
.call(key)
|
532
|
+
.map { |file| parsed.arguments.any? ? file.sub(key, "") : file }
|
533
|
+
values = group.call(files)
|
534
|
+
key = "#{key}/" if values.any? && key != "." && key[-1] != "/"
|
535
|
+
puts key
|
536
|
+
values.each.with_index do |(value_key, value_values), value_index|
|
537
|
+
print.call(
|
538
|
+
key: value_key,
|
539
|
+
values: value_values,
|
540
|
+
index: value_index,
|
541
|
+
size: values.size
|
542
|
+
)
|
543
|
+
end
|
544
|
+
end
|
545
|
+
end
|
546
|
+
|
216
547
|
def files
|
217
548
|
parsed.files
|
218
549
|
end
|
219
550
|
|
551
|
+
def command_times
|
552
|
+
map(everything, &:to_i).sum.times { |index| puts index + 1 }
|
553
|
+
end
|
554
|
+
|
220
555
|
def command_eval
|
221
|
-
each(everything) { |thing| outputs(evaluates(ruby: thing)
|
556
|
+
each(everything) { |thing| outputs(evaluates(ruby: thing)) }
|
557
|
+
end
|
558
|
+
|
559
|
+
def command_then
|
560
|
+
each(stdin_files + files) do |input|
|
561
|
+
outputs(evaluates(it: reads(File.read(input))), file: input)
|
562
|
+
end
|
563
|
+
|
564
|
+
each(stdin_arguments + arguments) do |input|
|
565
|
+
outputs(evaluates(it: reads(input)))
|
566
|
+
end
|
567
|
+
end
|
568
|
+
|
569
|
+
def command_table
|
570
|
+
table(map(everything) { |thing| lines(reads(thing)) }.inject(&:+))
|
222
571
|
end
|
223
572
|
|
224
573
|
def command_chat
|
@@ -258,6 +607,39 @@ class Dorian
|
|
258
607
|
puts message
|
259
608
|
end
|
260
609
|
|
610
|
+
def command_replace
|
611
|
+
from, to = arguments
|
612
|
+
|
613
|
+
each(stdin_files + stdin_arguments + files) do |file|
|
614
|
+
next if File.directory?(file)
|
615
|
+
|
616
|
+
File.write(file, File.read(file).gsub(from, to))
|
617
|
+
end
|
618
|
+
end
|
619
|
+
|
620
|
+
def command_rename
|
621
|
+
from, to = arguments
|
622
|
+
files = stdin_files + stdin_arguments + self.files
|
623
|
+
(files - directories).each { |file| rename(file, file.gsub(from, to)) }
|
624
|
+
directories.each { |dir| rename(dir, dir.gsub(from, to)) }
|
625
|
+
end
|
626
|
+
|
627
|
+
def directories
|
628
|
+
(stdin_files + files).select { |file| File.directory?(file) }
|
629
|
+
end
|
630
|
+
|
631
|
+
def rename(old, new)
|
632
|
+
return if old == new
|
633
|
+
|
634
|
+
puts "#{old} -> #{new}"
|
635
|
+
File.rename(old, new)
|
636
|
+
end
|
637
|
+
|
638
|
+
def command_write
|
639
|
+
content = read_stdin.join
|
640
|
+
each(files + arguments) { |file| File.write(file, content) }
|
641
|
+
end
|
642
|
+
|
261
643
|
def command_read
|
262
644
|
each(stdin_files + files) do |input|
|
263
645
|
outputs(reads(File.read(input)), file: input)
|
@@ -266,6 +648,36 @@ class Dorian
|
|
266
648
|
each(stdin_arguments + arguments) { |input| outputs(reads(input)) }
|
267
649
|
end
|
268
650
|
|
651
|
+
def command_strip
|
652
|
+
each(stdin_files + files) do |input|
|
653
|
+
outputs(lines(reads(File.read(input)), strip: :strip), file: input)
|
654
|
+
end
|
655
|
+
|
656
|
+
each(stdin_arguments + arguments) do |input|
|
657
|
+
outputs(lines(reads(input), strip: :strip))
|
658
|
+
end
|
659
|
+
end
|
660
|
+
|
661
|
+
def command_rstrip
|
662
|
+
each(stdin_files + files) do |input|
|
663
|
+
outputs(lines(reads(File.read(input)), strip: :rstrip), file: input)
|
664
|
+
end
|
665
|
+
|
666
|
+
each(stdin_arguments + arguments) do |input|
|
667
|
+
outputs(lines(reads(input), strip: :rstrip))
|
668
|
+
end
|
669
|
+
end
|
670
|
+
|
671
|
+
def command_lstrip
|
672
|
+
each(stdin_files + files) do |input|
|
673
|
+
outputs(lines(reads(File.read(input)), strip: :lstrip), file: input)
|
674
|
+
end
|
675
|
+
|
676
|
+
each(stdin_arguments + arguments) do |input|
|
677
|
+
outputs(lines(reads(input), strip: :lstrip))
|
678
|
+
end
|
679
|
+
end
|
680
|
+
|
269
681
|
def everything
|
270
682
|
read_stdin_files + stdin_arguments + read_files + arguments
|
271
683
|
end
|
@@ -335,7 +747,9 @@ class Dorian
|
|
335
747
|
|
336
748
|
homefile = "#{Dir.home}/#{file}"
|
337
749
|
dotfile = File.expand_path("#{dir.chomp("/")}/#{file}")
|
338
|
-
|
750
|
+
if File.exist?(homefile) || File.symlink?(homefile)
|
751
|
+
File.delete(homefile)
|
752
|
+
end
|
339
753
|
FileUtils.mkdir_p(File.dirname(homefile))
|
340
754
|
FileUtils.ln_s(dotfile, homefile, verbose: true)
|
341
755
|
end
|
@@ -370,6 +784,43 @@ class Dorian
|
|
370
784
|
end
|
371
785
|
end
|
372
786
|
|
787
|
+
def command_merge
|
788
|
+
outputs(map(everything) { |thing| lines(reads(thing)) }.inject(&:+))
|
789
|
+
end
|
790
|
+
|
791
|
+
def command_sort
|
792
|
+
outputs(
|
793
|
+
map(everything) { |thing| lines(reads(thing)) }
|
794
|
+
.inject(&:+)
|
795
|
+
.sort_by do |line|
|
796
|
+
result = pluck(line).from_deep_struct
|
797
|
+
result.is_a?(Hash) ? result.values : result
|
798
|
+
end
|
799
|
+
)
|
800
|
+
end
|
801
|
+
|
802
|
+
def command_uniq
|
803
|
+
outputs(
|
804
|
+
map(everything) { |thing| lines(reads(thing)) }
|
805
|
+
.inject(&:+)
|
806
|
+
.uniq { |line| pluck(line) }
|
807
|
+
)
|
808
|
+
end
|
809
|
+
|
810
|
+
def command_pluck
|
811
|
+
outputs(
|
812
|
+
map(everything) do |thing|
|
813
|
+
map(lines(reads(thing))) { |line| pluck(line) }
|
814
|
+
end.inject(&:+)
|
815
|
+
)
|
816
|
+
end
|
817
|
+
|
818
|
+
def command_shuffle
|
819
|
+
outputs(
|
820
|
+
map(everything) { |thing| lines(reads(thing)) }.inject(&:+).shuffle
|
821
|
+
)
|
822
|
+
end
|
823
|
+
|
373
824
|
def command_tally
|
374
825
|
each(everything) do |input|
|
375
826
|
outputs(
|
@@ -378,7 +829,7 @@ class Dorian
|
|
378
829
|
if ruby.to_s.empty?
|
379
830
|
element
|
380
831
|
else
|
381
|
-
evaluates(it: element, returns: true, stdout: false)
|
832
|
+
evaluates(it: element, returns: true, stdout: false)
|
382
833
|
end
|
383
834
|
end.tally
|
384
835
|
)
|
@@ -393,11 +844,13 @@ class Dorian
|
|
393
844
|
end
|
394
845
|
|
395
846
|
def command_append
|
396
|
-
outputs(everything
|
847
|
+
outputs(map(everything) { |input| lines(reads(input)) }.inject(&:+))
|
397
848
|
end
|
398
849
|
|
399
850
|
def command_prepend
|
400
|
-
outputs(
|
851
|
+
outputs(
|
852
|
+
map(everything.reverse) { |input| lines(reads(input)) }.inject(&:+)
|
853
|
+
)
|
401
854
|
end
|
402
855
|
|
403
856
|
def command_select
|
@@ -487,12 +940,14 @@ class Dorian
|
|
487
940
|
end.join
|
488
941
|
when :json
|
489
942
|
pretty? ? JSON.pretty_generate(content) : content.to_json
|
490
|
-
when :jsonl
|
491
|
-
map(content, &:to_json).join("\n")
|
943
|
+
when :jsonl
|
944
|
+
"#{map(content, &:to_json).join("\n")}\n"
|
492
945
|
when :raw
|
493
946
|
content
|
494
947
|
when :yaml
|
495
948
|
content.to_yaml
|
949
|
+
when :yamll
|
950
|
+
"#{map(map(content, &:to_yaml), &:to_json).join("\n")}\n"
|
496
951
|
else
|
497
952
|
abort "#{output.inspect} not supported"
|
498
953
|
end
|
@@ -508,12 +963,16 @@ class Dorian
|
|
508
963
|
end
|
509
964
|
when :json
|
510
965
|
JSON.parse(content).to_deep_struct
|
511
|
-
when :jsonl
|
966
|
+
when :jsonl
|
512
967
|
map(content.lines) { |line| JSON.parse(line) }.to_deep_struct
|
513
968
|
when :raw
|
514
969
|
content
|
515
970
|
when :yaml
|
516
971
|
YAML.safe_load(content).to_deep_struct
|
972
|
+
when :yamll
|
973
|
+
map(content.lines) do |line|
|
974
|
+
YAML.safe_load(JSON.parse(line))
|
975
|
+
end.to_deep_struct
|
517
976
|
else
|
518
977
|
abort "#{input.inspect} not supported"
|
519
978
|
end
|
@@ -722,9 +1181,9 @@ class Dorian
|
|
722
1181
|
end
|
723
1182
|
end
|
724
1183
|
|
725
|
-
def lines(input)
|
1184
|
+
def lines(input, strip: :rstrip)
|
726
1185
|
if input.is_a?(String)
|
727
|
-
input.lines.map(
|
1186
|
+
input.lines.map(&strip)
|
728
1187
|
elsif deep?
|
729
1188
|
deep_lines(input)
|
730
1189
|
else
|
@@ -827,7 +1286,7 @@ class Dorian
|
|
827
1286
|
end
|
828
1287
|
|
829
1288
|
def match?(element, ruby: @ruby)
|
830
|
-
!!evaluates(ruby:, it: element, stdout: false, returns: true)
|
1289
|
+
!!evaluates(ruby:, it: element, stdout: false, returns: true)
|
831
1290
|
end
|
832
1291
|
|
833
1292
|
def token(file)
|
@@ -934,77 +1393,65 @@ class Dorian
|
|
934
1393
|
Tiktoken.encoding_for_model("gpt-4o")
|
935
1394
|
end
|
936
1395
|
|
937
|
-
def match_filetypes?(path)
|
938
|
-
return true
|
939
|
-
return true unless
|
940
|
-
|
941
|
-
ruby_extensions = %w[
|
942
|
-
.rb
|
943
|
-
.arb
|
944
|
-
.axlsx
|
945
|
-
.builder
|
946
|
-
.fcgi
|
947
|
-
.gemfile
|
948
|
-
.gemspec
|
949
|
-
.god
|
950
|
-
.jb
|
951
|
-
.jbuilder
|
952
|
-
.mspec
|
953
|
-
.opal
|
954
|
-
.pluginspec
|
955
|
-
.podspec
|
956
|
-
.rabl
|
957
|
-
.rake
|
958
|
-
.rbuild
|
959
|
-
.rbw
|
960
|
-
.rbx
|
961
|
-
.ru
|
962
|
-
.ruby
|
963
|
-
.schema
|
964
|
-
.spec
|
965
|
-
.thor
|
966
|
-
.watchr
|
967
|
-
]
|
968
|
-
|
969
|
-
ruby_filenames = %w[
|
970
|
-
.irbrc
|
971
|
-
.pryrc
|
972
|
-
.simplecov
|
973
|
-
Appraisals
|
974
|
-
Berksfile
|
975
|
-
Brewfile
|
976
|
-
Buildfile
|
977
|
-
Capfile
|
978
|
-
Cheffile
|
979
|
-
Dangerfile
|
980
|
-
Deliverfile
|
981
|
-
Fastfile
|
982
|
-
Gemfile
|
983
|
-
Guardfile
|
984
|
-
Jarfile
|
985
|
-
Mavenfile
|
986
|
-
Podfile
|
987
|
-
Puppetfile
|
988
|
-
Rakefile
|
989
|
-
rakefile
|
990
|
-
Schemafile
|
991
|
-
Snapfile
|
992
|
-
Steepfile
|
993
|
-
Thorfile
|
994
|
-
Vagabondfile
|
995
|
-
Vagrantfile
|
996
|
-
buildfile
|
997
|
-
]
|
998
|
-
|
1396
|
+
def match_filetypes?(path, filetypes: arguments)
|
1397
|
+
return true if filetypes.none?
|
1398
|
+
return true unless filetypes.intersect?(%w[rb ruby])
|
999
1399
|
return false if Dir.exist?(path)
|
1000
|
-
return true if
|
1001
|
-
return true if
|
1400
|
+
return true if RUBY_FILENAMES.include?(path)
|
1401
|
+
return true if RUBY_EXTENSIONS.include?(File.extname(path))
|
1002
1402
|
return false unless File.exist?(path)
|
1003
1403
|
|
1004
|
-
first_line =
|
1005
|
-
|
1404
|
+
first_line = File.open(path, &:gets).to_s
|
1405
|
+
first_line = first_line.encode("UTF-8", invalid: :replace)
|
1006
1406
|
|
1007
|
-
/\A#!.*ruby\z/.match?(first_line)
|
1407
|
+
return true if /\A#!.*ruby\z/.match?(first_line)
|
1408
|
+
|
1409
|
+
false
|
1410
|
+
end
|
1411
|
+
|
1412
|
+
def pluck(element)
|
1413
|
+
element = element.from_deep_struct
|
1414
|
+
|
1415
|
+
keys =
|
1416
|
+
if arguments.any?
|
1417
|
+
arguments
|
1418
|
+
elsif element.is_a?(Hash)
|
1419
|
+
element.keys
|
1420
|
+
elsif element.is_a?(Array)
|
1421
|
+
(0...(element.size)).map(&:to_s)
|
1422
|
+
else
|
1423
|
+
"it"
|
1424
|
+
end
|
1425
|
+
|
1426
|
+
results =
|
1427
|
+
keys.map do |argument|
|
1428
|
+
if element.is_a?(Array) && argument.to_i.to_s == argument
|
1429
|
+
element[argument.to_i]
|
1430
|
+
elsif element.is_a?(Hash) && element.key?(argument)
|
1431
|
+
{ argument => element[argument] }
|
1432
|
+
else
|
1433
|
+
evaluates(ruby: argument, it: element.to_deep_struct)
|
1434
|
+
end
|
1435
|
+
end
|
1436
|
+
|
1437
|
+
if results.all?(Hash)
|
1438
|
+
results.inject(&:merge).to_deep_struct
|
1439
|
+
else
|
1440
|
+
results
|
1441
|
+
.map { |result| result.is_a?(Hash) ? result.values.first : result }
|
1442
|
+
.to_deep_struct
|
1443
|
+
end
|
1444
|
+
end
|
1445
|
+
|
1446
|
+
def table(data)
|
1447
|
+
is_hashes = data.first.from_deep_struct.is_a?(Hash)
|
1448
|
+
headings = is_hashes ? data.first.to_h.keys : nil
|
1449
|
+
rows = is_hashes ? data.map(&:values) : data.map { |row| wrap(row) }
|
1450
|
+
if headings
|
1451
|
+
puts Terminal::Table.new(headings:, rows:)
|
1452
|
+
else
|
1453
|
+
puts Terminal::Table.new(rows:)
|
1454
|
+
end
|
1008
1455
|
end
|
1009
1456
|
|
1010
1457
|
def evaluates(
|
@@ -1028,7 +1475,218 @@ class Dorian
|
|
1028
1475
|
rails:,
|
1029
1476
|
fast:,
|
1030
1477
|
returns:
|
1031
|
-
)
|
1478
|
+
).returned
|
1479
|
+
end
|
1480
|
+
|
1481
|
+
def sort(object)
|
1482
|
+
object = object.from_deep_struct
|
1483
|
+
|
1484
|
+
if object.is_a?(Hash)
|
1485
|
+
object
|
1486
|
+
.to_a
|
1487
|
+
.sort_by(&:first)
|
1488
|
+
.to_h
|
1489
|
+
.transform_values { |value| sort(value) }
|
1490
|
+
elsif object.is_a?(Array)
|
1491
|
+
object.map { |element| sort(element) }
|
1492
|
+
else
|
1493
|
+
object
|
1494
|
+
end
|
1495
|
+
end
|
1496
|
+
|
1497
|
+
def filetype(path)
|
1498
|
+
ext = File.extname(path).to_s.downcase
|
1499
|
+
return :directory if Dir.exist?(path)
|
1500
|
+
return :symlink if File.symlink?(path)
|
1501
|
+
return :ruby if RUBY_FILENAMES.include?(File.basename(path))
|
1502
|
+
return :ruby if RUBY_EXTENSIONS.include?(ext)
|
1503
|
+
return :json if ext == ".json"
|
1504
|
+
return :jsonl if ext == ".jsonl"
|
1505
|
+
return :yaml if ext == ".yaml"
|
1506
|
+
return :yaml if ext == ".yml"
|
1507
|
+
return :yamll if ext == ".yamll"
|
1508
|
+
return :yamll if ext == ".ymll"
|
1509
|
+
return :csv if ext == ".csv"
|
1510
|
+
return :js if ext == ".js"
|
1511
|
+
return :js if ext == ".mjs"
|
1512
|
+
return :js if ext == ".cjs"
|
1513
|
+
return :ts if ext == ".ts"
|
1514
|
+
return :css if ext == ".css"
|
1515
|
+
return :html if ext == ".html"
|
1516
|
+
return :html if ext == ".htm"
|
1517
|
+
return :haml if ext == ".haml"
|
1518
|
+
return :slim if ext == ".slim"
|
1519
|
+
return :erb if ext == ".erb"
|
1520
|
+
return :fish if ext == ".fish"
|
1521
|
+
return :sql if ext == ".sql"
|
1522
|
+
return :tex if ext == ".tex"
|
1523
|
+
return :md if ext == ".md"
|
1524
|
+
return :md if ext == ".markdown"
|
1525
|
+
return :png if ext == ".png"
|
1526
|
+
return :jpeg if ext == ".jpg"
|
1527
|
+
return :jpeg if ext == ".jpeg"
|
1528
|
+
return :ico if ext == ".ico"
|
1529
|
+
return :webp if ext == ".webp"
|
1530
|
+
return :heic if ext == ".heic"
|
1531
|
+
return :pdf if ext == ".pdf"
|
1532
|
+
return :raw if ext == ".raw"
|
1533
|
+
return :env if path == ".env"
|
1534
|
+
return :env if path.start_with?(".env.")
|
1535
|
+
return :sh if path == "Dockerfile"
|
1536
|
+
return :sh if ext == ".sh"
|
1537
|
+
return :enc if ext == ".enc"
|
1538
|
+
return :enc if ext == ".keystore"
|
1539
|
+
return :pro if ext == ".pro"
|
1540
|
+
return :txt if ext == ".txt"
|
1541
|
+
return :bat if ext == ".bat"
|
1542
|
+
return :xcconfig if ext == ".xcconfig"
|
1543
|
+
return :pbxproj if ext == ".pbxproj"
|
1544
|
+
return :xml if ext == ".xml"
|
1545
|
+
return :xml if ext == ".plist"
|
1546
|
+
return :xml if ext == ".storyboard"
|
1547
|
+
return :xml if ext == ".xcscheme"
|
1548
|
+
return :xml if ext == ".xcworkspacedata"
|
1549
|
+
return :xml if ext == ".xcprivacy"
|
1550
|
+
return :xml if ext == ".entitlements"
|
1551
|
+
return :kotlin if ext == ".kt"
|
1552
|
+
return :groovy if ext == ".gradle"
|
1553
|
+
return :groovy if ext == ".properties"
|
1554
|
+
return :binary if ext == ".jar"
|
1555
|
+
return :objectivec if ext == ".h"
|
1556
|
+
return :objectivec if ext == ".mm"
|
1557
|
+
return :objectivec if ext == ".m"
|
1558
|
+
return unless File.exist?(path)
|
1559
|
+
|
1560
|
+
first_line = File.open(path, &:gets).to_s
|
1561
|
+
first_line = first_line.encode("UTF-8", invalid: :replace).strip
|
1562
|
+
return :ruby if first_line == "#!/usr/bin/env ruby"
|
1563
|
+
return :sh if first_line == "#!/bin/bash"
|
1564
|
+
return :sh if first_line == "#!/bin/sh"
|
1565
|
+
return :sh if first_line == "#!/bin/bash -e"
|
1566
|
+
return :sh if first_line == "#!/usr/bin/env sh"
|
1567
|
+
|
1568
|
+
nil
|
1569
|
+
end
|
1570
|
+
|
1571
|
+
def format(path, context:)
|
1572
|
+
return if File.symlink?(path)
|
1573
|
+
return if File.directory?(path)
|
1574
|
+
return unless File.exist?(path)
|
1575
|
+
|
1576
|
+
before = File.read(path)
|
1577
|
+
|
1578
|
+
case filetype(path)
|
1579
|
+
when :directory
|
1580
|
+
when :ruby
|
1581
|
+
after = SyntaxTree.format(before)
|
1582
|
+
when :haml
|
1583
|
+
after = SyntaxTree::Haml.format(before)
|
1584
|
+
when :erb
|
1585
|
+
after = SyntaxTree::ERB.format(before)
|
1586
|
+
when :xml
|
1587
|
+
after = SyntaxTree::XML.format(before)
|
1588
|
+
when :json
|
1589
|
+
after = JSON.pretty_generate(JSON.parse(before))
|
1590
|
+
when :jsonl
|
1591
|
+
after = before.lines.map { |line| JSON.parse(line).to_json }.join("\n")
|
1592
|
+
when :csv
|
1593
|
+
after =
|
1594
|
+
CSV.generate { |csv| CSV.parse(before).each { |row| csv << row } }
|
1595
|
+
when :yaml
|
1596
|
+
after = sort(YAML.safe_load(before)).to_yaml
|
1597
|
+
when :yamll
|
1598
|
+
after =
|
1599
|
+
before
|
1600
|
+
.lines
|
1601
|
+
.map do |line|
|
1602
|
+
sort(YAML.safe_load(JSON.parse(line))).to_yaml.to_json
|
1603
|
+
end
|
1604
|
+
.join("\n") + "\n"
|
1605
|
+
when :js
|
1606
|
+
context.eval("format(#{path.to_json}, 'babel')")
|
1607
|
+
when :ts
|
1608
|
+
context.eval("format(#{path.to_json}, 'typescript')")
|
1609
|
+
when :html
|
1610
|
+
context.eval("format(#{path.to_json}, 'html')")
|
1611
|
+
when :md
|
1612
|
+
context.eval("format(#{path.to_json}, 'markdown')")
|
1613
|
+
when :sql
|
1614
|
+
context.eval("format(#{path.to_json}, 'sql')")
|
1615
|
+
when :groovy
|
1616
|
+
context.eval("format(#{path.to_json}, 'groovy')")
|
1617
|
+
when :css
|
1618
|
+
context.eval("format(#{path.to_json}, 'css')")
|
1619
|
+
when :sh
|
1620
|
+
if system("command -v shfmt > /dev/null 2>&1")
|
1621
|
+
command = ["shfmt", "--indent", "4", path].shelljoin
|
1622
|
+
stdout, stderr, status = Open3.capture3(command)
|
1623
|
+
raise stderr unless stderr.empty? && status.success?
|
1624
|
+
|
1625
|
+
after = stdout
|
1626
|
+
else
|
1627
|
+
warn "run: `brew install shfmt` for #{path}"
|
1628
|
+
end
|
1629
|
+
when :pdf
|
1630
|
+
doc = HexaPDF::Document.open(path)
|
1631
|
+
doc.trailer.info.each_key { |key| doc.trailer.info.delete(key) }
|
1632
|
+
doc.write(path, update_fields: false)
|
1633
|
+
after = File.read(path)
|
1634
|
+
when :tex
|
1635
|
+
if system("command -v latexindent > /dev/null 2>&1")
|
1636
|
+
command = ["latexindent", path, "--logfile", "/dev/null"].shelljoin
|
1637
|
+
stdout, stderr, status = Open3.capture3(command)
|
1638
|
+
raise stderr unless stderr.empty? && status.success?
|
1639
|
+
|
1640
|
+
after = stdout.gsub("\t", " ")
|
1641
|
+
else
|
1642
|
+
warn "run: `brew install latexindent` for #{path}"
|
1643
|
+
end
|
1644
|
+
when :objectivec
|
1645
|
+
if system("command -v clang-format > /dev/null 2>&1")
|
1646
|
+
command = ["clang-format", path].shelljoin
|
1647
|
+
stdout, stderr, status = Open3.capture3(command)
|
1648
|
+
raise stderr unless stderr.empty? && status.success?
|
1649
|
+
|
1650
|
+
after = stdout.gsub("\t", " ")
|
1651
|
+
else
|
1652
|
+
warn "run: `brew install clang-format` for #{path}"
|
1653
|
+
end
|
1654
|
+
when :kotlin
|
1655
|
+
if system("command -v ktlint > /dev/null 2>&1")
|
1656
|
+
command = ["ktlint", "-F", path].shelljoin
|
1657
|
+
stdout, stderr, status = Open3.capture3(command)
|
1658
|
+
raise stderr unless stderr.empty? && status.success?
|
1659
|
+
|
1660
|
+
after = File.read(path)
|
1661
|
+
else
|
1662
|
+
warn "run: `brew install ktlint` for #{path}"
|
1663
|
+
end
|
1664
|
+
when :raw, :directory, :symlink, :env, :enc, :txt, :pro, :binary, :slim,
|
1665
|
+
:fish, :bat, :xcconfig, :pbxproj, :jpeg, :png, :webp, :heic, :ico
|
1666
|
+
# nothing to do
|
1667
|
+
else
|
1668
|
+
case File.basename(path)
|
1669
|
+
when ".gitignore", ".node-version", ".prettierignore", ".ruby-version",
|
1670
|
+
".tool-versions", "Gemfile.lock", "LICENSE", "VERSION", ".rspec",
|
1671
|
+
"Procfile", "Procfile.dev", "Podfile.lock", ".xcode.env", "CNAME",
|
1672
|
+
"TODO", ".gitmodules", ".asdfrc", "config", ".dotignore", ".gemrc",
|
1673
|
+
".gitconfig", ".gitmessage", ".hushlogin", ".psqlrc", ".vimrc",
|
1674
|
+
"DIRECTORIES"
|
1675
|
+
# nothing to do
|
1676
|
+
when ".keep"
|
1677
|
+
File.write(path, "")
|
1678
|
+
else
|
1679
|
+
puts "unhandled: #{path}"
|
1680
|
+
end
|
1681
|
+
end
|
1682
|
+
|
1683
|
+
if after && before != after
|
1684
|
+
puts path
|
1685
|
+
File.write(path, after)
|
1686
|
+
end
|
1687
|
+
rescue StandardError => e
|
1688
|
+
warn "failed to parse #{path}: #{e.message}"
|
1689
|
+
binding.irb
|
1032
1690
|
end
|
1033
1691
|
end
|
1034
1692
|
end
|