ned 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +95 -20
- data/bin/ned +1 -1
- data/lib/ned/command.rb +131 -8
- data/lib/ned/command_registry.rb +27 -0
- data/lib/ned/commands/append.rb +27 -13
- data/lib/ned/commands/backward.rb +21 -14
- data/lib/ned/commands/head.rb +33 -18
- data/lib/ned/commands/index.rb +34 -25
- data/lib/ned/commands/join.rb +41 -14
- data/lib/ned/commands/prepend.rb +27 -13
- data/lib/ned/commands/print.rb +13 -0
- data/lib/ned/commands/quote.rb +29 -13
- data/lib/ned/commands/read.rb +40 -0
- data/lib/ned/commands/sort.rb +45 -31
- data/lib/ned/commands/tail.rb +29 -20
- data/lib/ned/commands/uniq.rb +53 -18
- data/lib/ned/string.rb +14 -0
- data/lib/ned/strings.rb +1 -1
- data/lib/ned/version.rb +1 -3
- data/lib/ned.rb +181 -138
- data/lib/{ned → ned_old}/commands/grep.rb +0 -0
- data/lib/{ned → ned_old}/commands/only.rb +0 -0
- data/lib/{ned → ned_old}/commands/replace.rb +0 -0
- data/lib/{ned → ned_old}/commands/visual.rb +0 -0
- data/lib/ned_old.rb +193 -0
- data/ned.gemspec +2 -2
- data/scripts/generate_readme +6 -0
- data/spec_old/file_spec.rb +29 -0
- metadata +14 -12
- data/lib/ned/config.rb +0 -73
- data/lib/ned/context.rb +0 -10
- data/lib/ned/validators.rb +0 -19
- data/todo +0 -15
@@ -0,0 +1,40 @@
|
|
1
|
+
module Ned
|
2
|
+
class Read < Ned::Command
|
3
|
+
def initialize(inputs)
|
4
|
+
@inputs = inputs
|
5
|
+
end
|
6
|
+
|
7
|
+
def execute
|
8
|
+
line = @next || next_line
|
9
|
+
@next = next_line
|
10
|
+
line.ensure_trailing_newline if @next
|
11
|
+
line.nil? ? nil : line
|
12
|
+
end
|
13
|
+
|
14
|
+
def next_line
|
15
|
+
@current ||= @inputs.shift
|
16
|
+
return unless @current
|
17
|
+
|
18
|
+
line = @current.gets("\n")
|
19
|
+
return line if line
|
20
|
+
|
21
|
+
@current.close
|
22
|
+
@current = nil
|
23
|
+
|
24
|
+
next_line
|
25
|
+
end
|
26
|
+
|
27
|
+
def execute_all
|
28
|
+
lines = []
|
29
|
+
current = @inputs.shift
|
30
|
+
while current
|
31
|
+
lines.concat(current.readlines)
|
32
|
+
current.close
|
33
|
+
current = @inputs.shift
|
34
|
+
lines[-1].ensure_trailing_newline if !current.nil? && lines[-1]
|
35
|
+
end
|
36
|
+
|
37
|
+
lines
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/ned/commands/sort.rb
CHANGED
@@ -1,38 +1,52 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
1
|
module Ned
|
4
|
-
|
2
|
+
class Sort < Ned::AllCommand
|
5
3
|
DIGIT_MATCH = /^ *((?:0|-?[1-9][0-9]*)(?:[.][0-9]+)?)(.*)/
|
6
|
-
end
|
7
|
-
end
|
8
4
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
5
|
+
long_name 'sort'
|
6
|
+
short_name 's'
|
7
|
+
|
8
|
+
option_parser do |opts|
|
9
|
+
opts.banner = <<~EOF
|
10
|
+
Sort input lines.
|
11
|
+
|
12
|
+
Usage: ned sort [--numeric]
|
13
|
+
|
14
|
+
Options:
|
15
|
+
EOF
|
16
|
+
|
17
|
+
opts.on('-n', '--numeric', 'Sort numerically.') do |numeric|
|
18
|
+
raise OptionParser::ParseError.new('duplicate flag') if options[:numeric]
|
19
|
+
|
20
|
+
numeric
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on('-r', '--reverse', 'Reverse the sort order.') do |reverse|
|
24
|
+
raise OptionParser::ParseError.new('duplicate flag') if options[:reverse]
|
25
|
+
|
26
|
+
reverse
|
27
|
+
end
|
20
28
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
|
30
|
+
def execute_internal(lines)
|
31
|
+
ensure_trailing_newline
|
32
|
+
if options.fetch(:numeric, false)
|
33
|
+
sortable = lines.map do |line|
|
34
|
+
match = line.match(DIGIT_MATCH)
|
35
|
+
if match
|
36
|
+
[0, Float(match[1]), match[2], line]
|
37
|
+
else
|
38
|
+
[1, nil, nil, line]
|
39
|
+
end
|
32
40
|
end
|
41
|
+
lines = sortable.sort.map { |v| v[3] }
|
42
|
+
else
|
43
|
+
lines.sort!
|
33
44
|
end
|
34
|
-
|
45
|
+
|
46
|
+
lines.reverse! if options.fetch(:reverse, false)
|
47
|
+
lines
|
35
48
|
end
|
36
|
-
|
37
|
-
|
38
|
-
|
49
|
+
|
50
|
+
Ned::CommandRegistry.add(Sort)
|
51
|
+
end
|
52
|
+
end
|
data/lib/ned/commands/tail.rb
CHANGED
@@ -1,22 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
module Ned
|
2
|
+
class Tail < AllCommand
|
3
|
+
long_name 'tail'
|
4
|
+
short_name 't'
|
5
|
+
|
6
|
+
option_parser do |opts|
|
7
|
+
opts.banner = <<~EOF
|
8
|
+
Print only the last input lines.
|
9
|
+
|
10
|
+
Usage: ned tail [--num NUM]
|
11
|
+
|
12
|
+
Options:
|
13
|
+
EOF
|
14
|
+
|
15
|
+
opts.on('-n', '--num NUM', Integer, 'Number of input lines to print. Defaults to 10.') do |num|
|
16
|
+
raise OptionParser::ParseError.new('duplicate flag') if options[:num]
|
17
|
+
|
18
|
+
raise OptionParser::ParseError.new("invalid num: #{num}") if num < 0
|
19
|
+
|
20
|
+
num
|
21
|
+
end
|
14
22
|
end
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
23
|
+
|
24
|
+
def execute_internal(lines)
|
25
|
+
num = options.fetch(:num, 10)
|
26
|
+
num < lines.length ? lines[-num..-1] : lines
|
19
27
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
28
|
+
|
29
|
+
Ned::CommandRegistry.add(Tail)
|
30
|
+
end
|
31
|
+
end
|
data/lib/ned/commands/uniq.rb
CHANGED
@@ -1,20 +1,55 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
1
|
+
module Ned
|
2
|
+
class Uniq < Ned::AllCommand
|
3
|
+
long_name 'uniq'
|
4
|
+
short_name 'u'
|
5
|
+
|
6
|
+
option_parser do |opts|
|
7
|
+
opts.banner = <<~EOF
|
8
|
+
Filter out repeated input lines.
|
9
|
+
|
10
|
+
Usage: ned uniq [--count]
|
11
|
+
|
12
|
+
Options:
|
13
|
+
EOF
|
14
|
+
|
15
|
+
opts.on('-c', '--count', 'Precede each output line with the number of times the line occurred in the input, followed by a space.') do |count|
|
16
|
+
raise OptionParser::ParseError.new('duplicate flag') if options[:count]
|
17
|
+
|
18
|
+
count
|
16
19
|
end
|
17
20
|
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
|
22
|
+
def execute_internal(lines)
|
23
|
+
ensure_trailing_newline
|
24
|
+
|
25
|
+
previous = nil
|
26
|
+
count = 1
|
27
|
+
lines.each_with_index.flat_map do |line, i|
|
28
|
+
result = []
|
29
|
+
if previous && line != previous
|
30
|
+
if options.fetch(:count, false)
|
31
|
+
previous.insert(0, "#{count} ")
|
32
|
+
result << previous
|
33
|
+
else
|
34
|
+
result << previous
|
35
|
+
end
|
36
|
+
count = 1
|
37
|
+
elsif previous
|
38
|
+
count += 1
|
39
|
+
end
|
40
|
+
previous = line
|
41
|
+
if i == lines.length - 1
|
42
|
+
if options.fetch(:count, false)
|
43
|
+
line.insert(0, "#{count} ")
|
44
|
+
result << line
|
45
|
+
else
|
46
|
+
result << line
|
47
|
+
end
|
48
|
+
end
|
49
|
+
result
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
Ned::CommandRegistry.add(Uniq)
|
54
|
+
end
|
55
|
+
end
|
data/lib/ned/string.rb
ADDED
data/lib/ned/strings.rb
CHANGED
data/lib/ned/version.rb
CHANGED
data/lib/ned.rb
CHANGED
@@ -1,193 +1,236 @@
|
|
1
|
-
|
1
|
+
require 'optparse'
|
2
2
|
|
3
3
|
require 'ned/command'
|
4
|
-
require 'ned/
|
5
|
-
require 'ned/
|
4
|
+
require 'ned/command_registry'
|
5
|
+
require 'ned/string'
|
6
6
|
require 'ned/strings'
|
7
|
-
require 'ned/validators'
|
8
7
|
require 'ned/version'
|
9
8
|
|
10
9
|
Dir["#{__dir__}/ned/commands/**/*.rb"].sort.each { |f| require f }
|
11
10
|
|
11
|
+
# todo
|
12
|
+
# - uniq shouldn't be an all command
|
13
|
+
# - split into files
|
14
|
+
# - commands shouldn't have to implement execute_all, put this in the base or use a wrapper or something
|
15
|
+
# - implement help
|
16
|
+
# - add --version
|
17
|
+
# - write tests
|
18
|
+
# - add tail
|
19
|
+
# - add head
|
20
|
+
# - add replace
|
21
|
+
# - add grep
|
22
|
+
# - add grep -o or only
|
23
|
+
# - add grep -v or except
|
24
|
+
# - add index
|
25
|
+
# - add join
|
26
|
+
# - add count or something like wc
|
27
|
+
# - implement inline editing
|
28
|
+
# - implement highlighting
|
29
|
+
# - implement append
|
30
|
+
# - add ability for a command to turn one line into many
|
31
|
+
# - add ability for a command to turn one line into zero
|
32
|
+
# - handle newlines in quote, replace, etc.
|
33
|
+
# - add numeric sort
|
34
|
+
# - add reverse sort
|
35
|
+
# - maybe add append and prepend
|
36
|
+
# - figure out how to distrute as gem without bundle
|
37
|
+
# - use OptionParser on main args.
|
38
|
+
# - uniq should not need to be an AllCommand. Add ability to check whether there are more lines.
|
39
|
+
# - add support for a ~/.ned/ dir
|
40
|
+
# - implement highlight
|
41
|
+
# - add color
|
42
|
+
# - add support for more escape sequences, see https://github.com/ruby/ruby/blob/master/doc/syntax/literals.rdoc#label-Strings
|
43
|
+
# - add support for editing files in place
|
44
|
+
# - add ljust
|
45
|
+
# - maybe add ability to highlight columns, not just full lines
|
46
|
+
# - write readme
|
47
|
+
# - add support for to_set in uniq
|
48
|
+
# - add support for overriding existing commands
|
49
|
+
# - index shouldn't always be an all command. It should be based on the indices.
|
12
50
|
module Ned
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
Usage: ned [file] [options ...]
|
18
|
-
|
19
|
-
Options:
|
20
|
-
EOF
|
21
|
-
|
22
|
-
lines = [
|
23
|
-
[' --help', 'Print this message.'],
|
24
|
-
[' --version', 'Print version.']
|
25
|
-
]
|
26
|
-
max = lines[0][0].length
|
27
|
-
Ned::Config.all.each do |config|
|
28
|
-
next if config.hidden?
|
29
|
-
|
30
|
-
line = "#{config.short}, #{config.long} #{config.params}"
|
31
|
-
lines << [line, config.description]
|
32
|
-
max = [max, line.length].max
|
33
|
-
end
|
51
|
+
class Main
|
52
|
+
def initialize
|
53
|
+
@options = { }
|
34
54
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
55
|
+
@option_parser = OptionParser.new do |opts|
|
56
|
+
opts.banner = <<~EOF
|
57
|
+
Ned, a stream editor.
|
39
58
|
|
40
|
-
|
41
|
-
error_output.puts "fatal: #{message}\n\n"
|
42
|
-
help(error_output)
|
43
|
-
end
|
59
|
+
Usage: ned [[ned_arg ...] --] [[command] [: command ...]]
|
44
60
|
|
45
|
-
|
46
|
-
|
47
|
-
Ned::Strings.unescape!(arg.dup)
|
48
|
-
end
|
61
|
+
OPTIONS
|
62
|
+
EOF
|
49
63
|
|
50
|
-
|
51
|
-
|
52
|
-
return 0
|
53
|
-
end
|
64
|
+
opts.on('-i', '--inplace', 'Edit each file in place by processing each file independently.') do |inplace|
|
65
|
+
raise OptionParser::ParseError.new('duplicate flag') if @options[:inplace]
|
54
66
|
|
55
|
-
|
56
|
-
|
57
|
-
return 0
|
58
|
-
end
|
67
|
+
inplace
|
68
|
+
end
|
59
69
|
|
60
|
-
|
61
|
-
|
62
|
-
|
70
|
+
opts.on('-h', '--help [COMMAND]', 'Print this help.') do |help|
|
71
|
+
help || true
|
72
|
+
end
|
63
73
|
|
64
|
-
|
65
|
-
|
66
|
-
else
|
67
|
-
unless File.exist?(arg)
|
68
|
-
failure(error_output, "no such file: #{arg}")
|
69
|
-
return 1
|
74
|
+
opts.on('-v', '--version', 'Print Ned version.') do |version|
|
75
|
+
version
|
70
76
|
end
|
71
|
-
inputs << arg
|
72
77
|
end
|
73
78
|
end
|
74
79
|
|
75
|
-
|
76
|
-
|
77
|
-
return 1
|
78
|
-
end
|
80
|
+
def help
|
81
|
+
help = @option_parser.help
|
79
82
|
|
80
|
-
|
83
|
+
help << "\nCOMMANDS\n"
|
81
84
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
+
Ned::CommandRegistry.all.each do |command|
|
86
|
+
help << "\n#{command.long_name} (#{command.short_name})\n\n"
|
87
|
+
command.help.split("\n").each do |line|
|
88
|
+
help << " #{line}\n"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
help
|
85
92
|
end
|
86
93
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
94
|
+
def run(args, input, output, error)
|
95
|
+
args = args.map do |arg|
|
96
|
+
arg = arg.dup
|
97
|
+
Ned::Strings.unescape!(arg)
|
98
|
+
arg
|
99
|
+
end
|
91
100
|
|
92
|
-
|
101
|
+
ned_args = []
|
102
|
+
command_args = []
|
103
|
+
has_stdin = !input.tty?
|
93
104
|
|
94
|
-
|
95
|
-
|
96
|
-
|
105
|
+
if index = args.index('--')
|
106
|
+
ned_args.concat(args.shift(index))
|
107
|
+
args.shift
|
108
|
+
elsif args[0]&.start_with?('-')
|
109
|
+
ned_args = args
|
110
|
+
args = []
|
97
111
|
end
|
98
112
|
|
99
|
-
|
100
|
-
|
113
|
+
command_args.concat(args.shift(args.size))
|
114
|
+
|
115
|
+
begin
|
116
|
+
@option_parser.parse!(ned_args, into: @options)
|
117
|
+
rescue OptionParser::ParseError => e
|
118
|
+
error.puts "fatal: error while parsing options: #{e.message}"
|
101
119
|
return 1
|
102
120
|
end
|
103
121
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
if arg.start_with? '\\-'
|
114
|
-
arg = arg[1..-1]
|
122
|
+
if help_option = @options[:help]
|
123
|
+
if help_option.is_a? TrueClass
|
124
|
+
output.puts help
|
125
|
+
return 0
|
126
|
+
elsif help_option.is_a? String
|
127
|
+
command = Ned::CommandRegistry.find(help_option)
|
128
|
+
unless command
|
129
|
+
error.puts "fatal: \"#{help_option}\" is not a valid command"
|
130
|
+
return 1
|
115
131
|
end
|
116
|
-
|
132
|
+
output.puts command.help
|
133
|
+
return 0
|
117
134
|
end
|
118
135
|
end
|
119
136
|
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
137
|
+
if @options[:version]
|
138
|
+
output.puts "Ned version #{Ned::VERSION}"
|
139
|
+
return 0
|
140
|
+
end
|
141
|
+
|
142
|
+
raise 'in-place not implemented' if @options[:inplace]
|
143
|
+
|
144
|
+
inputs = ned_args.map do |current|
|
145
|
+
if current == '-'
|
146
|
+
input
|
125
147
|
else
|
126
|
-
|
127
|
-
|
148
|
+
unless File.exist? current
|
149
|
+
error.puts "fatal: file not found \"#{current}\""
|
150
|
+
return 1
|
151
|
+
end
|
152
|
+
File.new(current)
|
128
153
|
end
|
129
154
|
end
|
130
155
|
|
131
|
-
|
132
|
-
|
156
|
+
inputs << input if inputs.empty?
|
157
|
+
inputs.unshift input if !inputs.index(input) && has_stdin
|
133
158
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
IO.readlines(i, sep="\n")
|
159
|
+
command_args = command_args.inject([[]]) do |array, arg|
|
160
|
+
if arg == ':'
|
161
|
+
array << []
|
138
162
|
else
|
139
|
-
|
163
|
+
array[-1] << arg
|
140
164
|
end
|
165
|
+
array
|
141
166
|
end
|
142
|
-
process_all(commands, lines, output)
|
143
|
-
else
|
144
|
-
process_each(commands, inputs, output)
|
145
|
-
end
|
146
167
|
|
147
|
-
|
148
|
-
|
168
|
+
if command_args.size == 1 && command_args[0] == []
|
169
|
+
command_args.clear
|
170
|
+
end
|
149
171
|
|
150
|
-
|
151
|
-
|
172
|
+
if command_args.index([])
|
173
|
+
error.puts 'fatal: empty command found'
|
174
|
+
return 1
|
175
|
+
end
|
152
176
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
last = context.lines.length - 1
|
159
|
-
context.lines.each_with_index do |line, index|
|
160
|
-
output.print "#{line}#{index == last ? final_newline : "\n"}"
|
161
|
-
end
|
162
|
-
end
|
177
|
+
command_args.each do |array|
|
178
|
+
array.each_with_index do |arg, i|
|
179
|
+
array[i] = arg[1..-1] if arg == '\:'
|
180
|
+
end
|
181
|
+
end
|
163
182
|
|
164
|
-
|
165
|
-
|
183
|
+
command_args.each do |args|
|
184
|
+
unless Ned::CommandRegistry.find(args[0])
|
185
|
+
error.puts "fatal: \"#{args[0]}\" is not a valid command"
|
186
|
+
return 1
|
187
|
+
end
|
188
|
+
end
|
166
189
|
|
167
|
-
|
168
|
-
|
169
|
-
|
190
|
+
if inputs.count(input) > 1
|
191
|
+
error.puts 'fatal: can\'t read from stdin more than once'
|
192
|
+
return 1
|
193
|
+
end
|
170
194
|
|
171
|
-
|
172
|
-
|
173
|
-
|
195
|
+
if inputs.index(input) && @in_place
|
196
|
+
error.puts "fatal: option -i specified with stdin"
|
197
|
+
return 1
|
198
|
+
end
|
199
|
+
|
200
|
+
previous = Ned::Read.new(inputs)
|
201
|
+
command_args.each do |args|
|
202
|
+
command = Ned::CommandRegistry.find(args.shift).new(previous)
|
203
|
+
begin
|
204
|
+
command.parse(args)
|
205
|
+
raise OptionParser::ParseError.new("invalid arguments: #{args}") unless args.empty?
|
206
|
+
rescue OptionParser::ParseError => e
|
207
|
+
error.puts "fatal: error while parsing options for #{command.class.long_name}: #{e.message}"
|
208
|
+
return 1
|
209
|
+
end
|
174
210
|
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
211
|
+
if command.options[:help]
|
212
|
+
output.puts command.class.help
|
213
|
+
return 0
|
214
|
+
end
|
215
|
+
|
216
|
+
if command.options[:version]
|
217
|
+
output.puts "Ned version #{Ned::VERSION}"
|
218
|
+
return 0
|
219
|
+
end
|
220
|
+
|
221
|
+
previous = command
|
180
222
|
end
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
223
|
+
|
224
|
+
if inputs.index(input) && !has_stdin
|
225
|
+
error.puts 'fatal: can\'t read from stdin'
|
226
|
+
return 1
|
185
227
|
end
|
186
228
|
|
187
|
-
|
188
|
-
|
189
|
-
|
229
|
+
print = Ned::Print.new(previous, output)
|
230
|
+
|
231
|
+
while line = print.execute; end
|
190
232
|
|
191
|
-
|
233
|
+
0
|
234
|
+
end
|
192
235
|
end
|
193
236
|
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|