ned 0.0.1
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 +7 -0
- data/.gitignore +2 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +32 -0
- data/README.md +24 -0
- data/bin/ned +7 -0
- data/lib/ned/command.rb +18 -0
- data/lib/ned/commands/append.rb +15 -0
- data/lib/ned/commands/backward.rb +14 -0
- data/lib/ned/commands/grep.rb +16 -0
- data/lib/ned/commands/head.rb +21 -0
- data/lib/ned/commands/index.rb +27 -0
- data/lib/ned/commands/join.rb +14 -0
- data/lib/ned/commands/only.rb +17 -0
- data/lib/ned/commands/prepend.rb +15 -0
- data/lib/ned/commands/quote.rb +15 -0
- data/lib/ned/commands/replace.rb +15 -0
- data/lib/ned/commands/sort.rb +38 -0
- data/lib/ned/commands/tail.rb +22 -0
- data/lib/ned/commands/uniq.rb +20 -0
- data/lib/ned/commands/visual.rb +22 -0
- data/lib/ned/config.rb +73 -0
- data/lib/ned/context.rb +10 -0
- data/lib/ned/strings.rb +29 -0
- data/lib/ned/validators.rb +19 -0
- data/lib/ned/version.rb +5 -0
- data/lib/ned.rb +193 -0
- data/ned.gemspec +26 -0
- data/scripts/generate_readme +8 -0
- data/scripts/release +31 -0
- data/scripts/test +3 -0
- data/todo +15 -0
- metadata +78 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 23d27cc1a81863250fe7b264e9199fb794c8d3e62fe879dab529f3201019e2f2
|
4
|
+
data.tar.gz: 6a1124febf6502f5a54693617da98eb869a0c181ae90aa678ec670937b26879d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 99fd8ede92eea52aed8a2308f6e05487219769c06a92104e7fe43fdbb96ea33aa833b598bbe5305a6741758657a7c0b16cfe01c7d96a38727c4166a83ec3fc78
|
7
|
+
data.tar.gz: 5f800186643b9638f32cdee823b64694fd0b4a60041af701e178d9c68967c63849726b0c29758899a483906c8a148103dda11495489d5219b9ae2949dff8e59d
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ned (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.5.0)
|
10
|
+
rspec (3.11.0)
|
11
|
+
rspec-core (~> 3.11.0)
|
12
|
+
rspec-expectations (~> 3.11.0)
|
13
|
+
rspec-mocks (~> 3.11.0)
|
14
|
+
rspec-core (3.11.0)
|
15
|
+
rspec-support (~> 3.11.0)
|
16
|
+
rspec-expectations (3.11.0)
|
17
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
18
|
+
rspec-support (~> 3.11.0)
|
19
|
+
rspec-mocks (3.11.1)
|
20
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
21
|
+
rspec-support (~> 3.11.0)
|
22
|
+
rspec-support (3.11.0)
|
23
|
+
|
24
|
+
PLATFORMS
|
25
|
+
x86_64-darwin-20
|
26
|
+
|
27
|
+
DEPENDENCIES
|
28
|
+
ned!
|
29
|
+
rspec
|
30
|
+
|
31
|
+
BUNDLED WITH
|
32
|
+
2.3.3
|
data/README.md
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# Ned
|
2
|
+
|
3
|
+
```
|
4
|
+
A stream editor.
|
5
|
+
|
6
|
+
Usage: ned [file] [options ...]
|
7
|
+
|
8
|
+
Options:
|
9
|
+
--help Print this message.
|
10
|
+
--version Print version.
|
11
|
+
-a, --append string Append the specified string to each line.
|
12
|
+
-b, --backward Invert all input lines.
|
13
|
+
-g, --grep pattern Search using the specified pattern.
|
14
|
+
-h, --head [num] Print the first n lines. Defaults to 10.
|
15
|
+
-i, --index start[..end] Print only the specified lines. Use -1 etc to index from end.
|
16
|
+
-j, --join [string] Join input lines, optionally with the specified string.
|
17
|
+
-o, --only pattern Search using the specified pattern. Only print matching part of line.
|
18
|
+
-p, --prepend string Prepend the specified string to each line.
|
19
|
+
-q, --quote [string] Quote each line, optionally with the specified string.
|
20
|
+
-r, --replace pattern replacement Regex replace.
|
21
|
+
-s, --sort ["num"|"alpha"] Sort. Defaults to alphabetical.
|
22
|
+
-t, --tail [num] Print the last n lines. Defaults to 10.
|
23
|
+
-u, --uniq Unique.
|
24
|
+
```
|
data/bin/ned
ADDED
data/lib/ned/command.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Ned
|
2
|
+
class Command
|
3
|
+
attr_reader :config, :params
|
4
|
+
|
5
|
+
def initialize(config, params)
|
6
|
+
@config = config
|
7
|
+
@params = params
|
8
|
+
end
|
9
|
+
|
10
|
+
def all?
|
11
|
+
config.all?(params)
|
12
|
+
end
|
13
|
+
|
14
|
+
def execute(context)
|
15
|
+
config.execute(context, *params)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'append',
|
5
|
+
description: 'Append the specified string to each line.',
|
6
|
+
short: '-a',
|
7
|
+
long: '--append',
|
8
|
+
params: 'string',
|
9
|
+
validator: Ned::Validators.num(1),
|
10
|
+
execute: lambda do |context, string|
|
11
|
+
context.lines = context.lines.flat_map do |line|
|
12
|
+
"#{line}#{string}".split("\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'backward',
|
5
|
+
description: 'Invert all input lines.',
|
6
|
+
short: '-b',
|
7
|
+
long: '--backward',
|
8
|
+
params: nil,
|
9
|
+
validator: Ned::Validators.none(),
|
10
|
+
execute: lambda do |context|
|
11
|
+
context.lines.reverse!
|
12
|
+
end,
|
13
|
+
all: true
|
14
|
+
)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'grep',
|
5
|
+
description: 'Search using the specified pattern.',
|
6
|
+
short: '-g',
|
7
|
+
long: '--grep',
|
8
|
+
params: 'pattern',
|
9
|
+
validator: Ned::Validators.num(1),
|
10
|
+
execute: lambda do |context, pattern|
|
11
|
+
regexp = Regexp.new(pattern)
|
12
|
+
context.lines = context.lines.flat_map do |line|
|
13
|
+
line.match(pattern) ? [line] : []
|
14
|
+
end
|
15
|
+
end
|
16
|
+
)
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'head',
|
5
|
+
description: 'Print the first n lines. Defaults to 10.',
|
6
|
+
short: '-h',
|
7
|
+
long: '--head',
|
8
|
+
params: '[num]',
|
9
|
+
validator: Ned::Validators.num(0, 1) do |num|
|
10
|
+
if num
|
11
|
+
Integer(num) rescue nil
|
12
|
+
else
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
end,
|
16
|
+
execute: lambda do |context, num=10|
|
17
|
+
context.lines = context.lines.flat_map do |line|
|
18
|
+
context.start_index < num ? [line] : []
|
19
|
+
end
|
20
|
+
end
|
21
|
+
)
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'index',
|
5
|
+
description: 'Print only the specified lines. Use -1 etc to index from end.',
|
6
|
+
short: '-i',
|
7
|
+
long: '--index',
|
8
|
+
params: 'start[..end]',
|
9
|
+
validator: Ned::Validators.num(1) do |param|
|
10
|
+
if param.index('..')
|
11
|
+
parts = param.split('..')
|
12
|
+
next unless parts.size == 2
|
13
|
+
|
14
|
+
[Integer(parts[0]), Integer(parts[1])] rescue nil
|
15
|
+
else
|
16
|
+
Integer(param) rescue nil
|
17
|
+
end
|
18
|
+
end,
|
19
|
+
execute: lambda do |context, start, ending = nil|
|
20
|
+
context.lines = (ending.nil? ? context.lines[start..start] : context.lines[start..ending]) || []
|
21
|
+
end,
|
22
|
+
all: lambda do |start, ending = nil|
|
23
|
+
true
|
24
|
+
# todo
|
25
|
+
#start < 0 || (!ending.nil? && ending < 0)
|
26
|
+
end
|
27
|
+
)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'join',
|
5
|
+
description: 'Join input lines, optionally with the specified string.',
|
6
|
+
short: '-j',
|
7
|
+
long: '--join',
|
8
|
+
params: '[string]',
|
9
|
+
validator: Ned::Validators.num(0, 1),
|
10
|
+
execute: lambda do |context, string=''|
|
11
|
+
context.lines = context.lines.join(string).split("\n")
|
12
|
+
end,
|
13
|
+
all: true
|
14
|
+
)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'only',
|
5
|
+
description: 'Search using the specified pattern. Only print matching part of line.',
|
6
|
+
short: '-o',
|
7
|
+
long: '--only',
|
8
|
+
params: 'pattern',
|
9
|
+
validator: Ned::Validators.num(1),
|
10
|
+
execute: lambda do |context, pattern|
|
11
|
+
regexp = Regexp.new(pattern)
|
12
|
+
context.lines = context.lines.flat_map do |line|
|
13
|
+
match = line.match(pattern)
|
14
|
+
match ? [match[0]] : []
|
15
|
+
end
|
16
|
+
end
|
17
|
+
)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'prepend',
|
5
|
+
description: 'Prepend the specified string to each line.',
|
6
|
+
short: '-p',
|
7
|
+
long: '--prepend',
|
8
|
+
params: 'string',
|
9
|
+
validator: Ned::Validators.num(1),
|
10
|
+
execute: lambda do |context, string|
|
11
|
+
context.lines = context.lines.flat_map do |line|
|
12
|
+
"#{string}#{line}".split("\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'quote',
|
5
|
+
description: 'Quote each line, optionally with the specified string.',
|
6
|
+
short: '-q',
|
7
|
+
long: '--quote',
|
8
|
+
params: '[string]',
|
9
|
+
validator: Ned::Validators.num(0, 1),
|
10
|
+
execute: lambda do |context, char='"'|
|
11
|
+
context.lines = context.lines.flat_map do |line|
|
12
|
+
"#{char}#{line}#{char}".split("\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'replace',
|
5
|
+
description: 'Regex replace.',
|
6
|
+
short: '-r',
|
7
|
+
long: '--replace',
|
8
|
+
params: 'pattern replacement',
|
9
|
+
validator: Ned::Validators.num(2),
|
10
|
+
execute: lambda do |context, pattern, string|
|
11
|
+
context.lines = context.lines.flat_map do |line|
|
12
|
+
line.gsub(Regexp.new(pattern), string).split("\n")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ned
|
4
|
+
module Sort
|
5
|
+
DIGIT_MATCH = /^ *((?:0|-?[1-9][0-9]*)(?:[.][0-9]+)?)(.*)/
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
Ned::Config.add(
|
10
|
+
name: 'sort',
|
11
|
+
description: 'Sort. Defaults to alphabetical.',
|
12
|
+
short: '-s',
|
13
|
+
long: '--sort',
|
14
|
+
params: '["num"|"alpha"]',
|
15
|
+
validator: Ned::Validators.num(0, 1) do |type|
|
16
|
+
if type
|
17
|
+
['num', 'alpha'].include?(type) ? [type] : nil
|
18
|
+
else
|
19
|
+
[]
|
20
|
+
end
|
21
|
+
end,
|
22
|
+
execute: lambda do |context, type='alpha'|
|
23
|
+
if type == 'alpha'
|
24
|
+
context.lines.sort!
|
25
|
+
else
|
26
|
+
sortable = context.lines.map do |line|
|
27
|
+
match = line.match(Ned::Sort::DIGIT_MATCH)
|
28
|
+
if match
|
29
|
+
[0, Float(match[1]), match[2], line]
|
30
|
+
else
|
31
|
+
[1, nil, nil, line]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
context.lines = sortable.sort.map { |v| v[3] }
|
35
|
+
end
|
36
|
+
end,
|
37
|
+
all: true
|
38
|
+
)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'tail',
|
5
|
+
description: 'Print the last n lines. Defaults to 10.',
|
6
|
+
short: '-t',
|
7
|
+
long: '--tail',
|
8
|
+
params: '[num]',
|
9
|
+
validator: Ned::Validators.num(0, 1) do |num|
|
10
|
+
if num
|
11
|
+
Integer(num) rescue nil
|
12
|
+
else
|
13
|
+
[]
|
14
|
+
end
|
15
|
+
end,
|
16
|
+
execute: lambda do |context, num=10|
|
17
|
+
if num < context.lines.length
|
18
|
+
context.lines = context.lines[-num..-1]
|
19
|
+
end
|
20
|
+
end,
|
21
|
+
all: true
|
22
|
+
)
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'uniq',
|
5
|
+
description: 'Unique.',
|
6
|
+
short: '-u',
|
7
|
+
long: '--uniq',
|
8
|
+
params: nil,
|
9
|
+
validator: Ned::Validators.none,
|
10
|
+
execute: lambda do |context|
|
11
|
+
context.lines = context.lines.each_with_index.flat_map do |line, index|
|
12
|
+
if index == 0 || context.lines[index - 1] != line
|
13
|
+
[line]
|
14
|
+
else
|
15
|
+
[]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end,
|
19
|
+
all: true
|
20
|
+
)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
Ned::Config.add(
|
4
|
+
name: 'visual',
|
5
|
+
short: '-v',
|
6
|
+
long: '--visual',
|
7
|
+
params: 'start[..end]',
|
8
|
+
description: 'Select a range to apply commands to. Use -1 etc to index from end.',
|
9
|
+
validator: Ned::Validators.num(1) do |param|
|
10
|
+
if param.index('..')
|
11
|
+
parts = param.split('..')
|
12
|
+
next unless parts.size == 2
|
13
|
+
|
14
|
+
[Integer(parts[0]), Integer(parts[1])] rescue nil
|
15
|
+
else
|
16
|
+
Integer(param) rescue nil
|
17
|
+
end
|
18
|
+
end,
|
19
|
+
execute: lambda do |context, start, ending = nil|
|
20
|
+
end,
|
21
|
+
hidden: true
|
22
|
+
)
|
data/lib/ned/config.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ned
|
4
|
+
class Config
|
5
|
+
class << self
|
6
|
+
attr_reader :all
|
7
|
+
end
|
8
|
+
|
9
|
+
@all = []
|
10
|
+
|
11
|
+
def self.add(name:, description:, short:, long:, params:, validator:, execute:, all: nil, hidden: false)
|
12
|
+
@all.each do |config|
|
13
|
+
if config.short.start_with?(short) || short.start_with?(config.short)
|
14
|
+
raise "Cannot add command #{name}. Flag conflicts with #{config.name}."
|
15
|
+
end
|
16
|
+
|
17
|
+
if config.long.start_with?(long) || long.start_with?(config.long)
|
18
|
+
raise "Cannot add command #{name}. Flag conflicts with #{config.name}."
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
@all << Config.new(
|
23
|
+
name: name,
|
24
|
+
description: description,
|
25
|
+
short: short,
|
26
|
+
long: long,
|
27
|
+
params: params,
|
28
|
+
validator: validator,
|
29
|
+
execute: execute,
|
30
|
+
all: all,
|
31
|
+
hidden: hidden
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :name, :description, :short, :long, :params
|
36
|
+
|
37
|
+
def initialize(name:, description:, short:, long:, params:, validator:, execute:, all: nil, hidden: false)
|
38
|
+
@name = name
|
39
|
+
@description = description
|
40
|
+
@short = short
|
41
|
+
@long = long
|
42
|
+
@params = params
|
43
|
+
@validator = validator
|
44
|
+
@execute = execute
|
45
|
+
@hidden = hidden
|
46
|
+
|
47
|
+
all = all || false
|
48
|
+
if [true, false].include? all
|
49
|
+
@all = lambda { |*params| all }
|
50
|
+
elsif all.is_a? Proc
|
51
|
+
@all = all
|
52
|
+
else
|
53
|
+
raise "Invalid all type for #{name}: #{all.class}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def validate(*args)
|
58
|
+
@validator.call(*args)
|
59
|
+
end
|
60
|
+
|
61
|
+
def all?(params)
|
62
|
+
@all.call(*params)
|
63
|
+
end
|
64
|
+
|
65
|
+
def hidden?
|
66
|
+
@hidden
|
67
|
+
end
|
68
|
+
|
69
|
+
def execute(*args)
|
70
|
+
@execute.call(*args)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
data/lib/ned/context.rb
ADDED
data/lib/ned/strings.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ned
|
4
|
+
class Strings
|
5
|
+
SEQUENCES = {
|
6
|
+
'n' => "\n",
|
7
|
+
't' => "\t",
|
8
|
+
'\\' => '\\'
|
9
|
+
}
|
10
|
+
|
11
|
+
def self.unescape!(string)
|
12
|
+
i = 0
|
13
|
+
while i < string.size - 1
|
14
|
+
if string[i] == '\\'
|
15
|
+
raise 'eof' if i == (string.size - 1)
|
16
|
+
c = string[i + 1]
|
17
|
+
r = SEQUENCES[c]
|
18
|
+
if r
|
19
|
+
string[i] = r
|
20
|
+
string[i + 1] = ''
|
21
|
+
end
|
22
|
+
end
|
23
|
+
i = i + 1
|
24
|
+
end
|
25
|
+
|
26
|
+
string
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ned
|
4
|
+
class Validators
|
5
|
+
def self.none
|
6
|
+
lambda { |*params| params.length == 0 ? [] : nil }
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.num(min, max = nil, &block)
|
10
|
+
lambda do |*params|
|
11
|
+
max = min unless max
|
12
|
+
|
13
|
+
return nil if params.length < min || params.length > max
|
14
|
+
|
15
|
+
block.nil? ? params : yield(*params)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/ned/version.rb
ADDED
data/lib/ned.rb
ADDED
@@ -0,0 +1,193 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'ned/command'
|
4
|
+
require 'ned/config'
|
5
|
+
require 'ned/context'
|
6
|
+
require 'ned/strings'
|
7
|
+
require 'ned/validators'
|
8
|
+
require 'ned/version'
|
9
|
+
|
10
|
+
Dir["#{__dir__}/ned/commands/**/*.rb"].sort.each { |f| require f }
|
11
|
+
|
12
|
+
module Ned
|
13
|
+
def self.help(error_output)
|
14
|
+
error_output.puts <<~EOF
|
15
|
+
A stream editor.
|
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
|
34
|
+
|
35
|
+
lines.each do |line|
|
36
|
+
error_output.puts " #{line[0].ljust(max)} #{line[1]}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.failure(error_output, message)
|
41
|
+
error_output.puts "fatal: #{message}\n\n"
|
42
|
+
help(error_output)
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.run(input: $stdin, output: $stdout, error_output: $stderr, args: ARGV)
|
46
|
+
args = args.map do |arg|
|
47
|
+
Ned::Strings.unescape!(arg.dup)
|
48
|
+
end
|
49
|
+
|
50
|
+
if args.include?('--help')
|
51
|
+
help(error_output)
|
52
|
+
return 0
|
53
|
+
end
|
54
|
+
|
55
|
+
if args.include?('--version')
|
56
|
+
output.puts("Ned version #{Ned::VERSION}")
|
57
|
+
return 0
|
58
|
+
end
|
59
|
+
|
60
|
+
inputs = []
|
61
|
+
while args.first && !args.first.match(/^-./)
|
62
|
+
arg = args.shift
|
63
|
+
|
64
|
+
if arg == '-'
|
65
|
+
inputs << input
|
66
|
+
else
|
67
|
+
unless File.exist?(arg)
|
68
|
+
failure(error_output, "no such file: #{arg}")
|
69
|
+
return 1
|
70
|
+
end
|
71
|
+
inputs << arg
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
if input.tty? && (inputs.empty? || (inputs.size == 1 && inputs[0] == input))
|
76
|
+
failure(error_output, 'no input')
|
77
|
+
return 1
|
78
|
+
end
|
79
|
+
|
80
|
+
inputs = inputs.unshift(input) if !input.tty? && !inputs.index(input)
|
81
|
+
|
82
|
+
if inputs.count(input) > 1
|
83
|
+
failure(error_output, "can't read from standard in twice, silly")
|
84
|
+
return 1
|
85
|
+
end
|
86
|
+
|
87
|
+
if inputs.size > 1
|
88
|
+
failure(error_output, "more than one input not yet supported")
|
89
|
+
return 1
|
90
|
+
end
|
91
|
+
|
92
|
+
commands = []
|
93
|
+
|
94
|
+
while arg = args.shift
|
95
|
+
config = Ned::Config.all.find do |config|
|
96
|
+
arg.start_with?(config.short) || arg == config.long || arg.start_with?("#{config.long}=")
|
97
|
+
end
|
98
|
+
|
99
|
+
unless config
|
100
|
+
failure(error_output, "invalid option: #{arg}")
|
101
|
+
return 1
|
102
|
+
end
|
103
|
+
|
104
|
+
params = []
|
105
|
+
|
106
|
+
if arg.start_with?("#{config.long}=")
|
107
|
+
params << arg["#{config.long}=".size..-1]
|
108
|
+
elsif arg.start_with?(config.short) && arg != config.short
|
109
|
+
params << arg[config.short.size..-1]
|
110
|
+
else
|
111
|
+
while !args.empty? && !args.first.start_with?('-')
|
112
|
+
arg = args.shift
|
113
|
+
if arg.start_with? '\\-'
|
114
|
+
arg = arg[1..-1]
|
115
|
+
end
|
116
|
+
params << arg
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
valid = config.validate(*params)
|
121
|
+
if valid.nil?
|
122
|
+
if config.params.nil?
|
123
|
+
failure(error_output, "invalid param for #{config.name}. found: #{params}, expected none")
|
124
|
+
return 1
|
125
|
+
else
|
126
|
+
failure(error_output, "invalid param for #{config.name}. found: #{params}, expected: #{config.params}")
|
127
|
+
return 1
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
commands << Ned::Command.new(config, valid)
|
132
|
+
end
|
133
|
+
|
134
|
+
if commands.any?(&:all?)
|
135
|
+
lines = inputs.flat_map do |i|
|
136
|
+
if i.is_a? String
|
137
|
+
IO.readlines(i, sep="\n")
|
138
|
+
else
|
139
|
+
i.readlines(sep="\n")
|
140
|
+
end
|
141
|
+
end
|
142
|
+
process_all(commands, lines, output)
|
143
|
+
else
|
144
|
+
process_each(commands, inputs, output)
|
145
|
+
end
|
146
|
+
|
147
|
+
0
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.process_all(commands, lines, output)
|
151
|
+
final_newline = lines[-1]&.[](-1) == "\n" ? "\n" : ''
|
152
|
+
|
153
|
+
lines = lines.map { |line| line.chomp("\n") }
|
154
|
+
context = Context.new(lines, 0)
|
155
|
+
commands.each do |command|
|
156
|
+
command.execute(context)
|
157
|
+
end
|
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
|
163
|
+
|
164
|
+
def self.process_each(commands, inputs, output)
|
165
|
+
input = inputs[0]
|
166
|
+
|
167
|
+
if input.is_a? String
|
168
|
+
input = File.open(input)
|
169
|
+
end
|
170
|
+
|
171
|
+
current = input.gets(sep="\n")
|
172
|
+
next_line = input.gets(sep="\n")
|
173
|
+
index = 0
|
174
|
+
|
175
|
+
while current
|
176
|
+
final_newline = current[-1]&.[](-1) == "\n" ? "\n" : ''
|
177
|
+
context = Context.new([current.chomp("\n")], index)
|
178
|
+
commands.each do |command|
|
179
|
+
command.execute(context)
|
180
|
+
end
|
181
|
+
index = context.start_index + context.lines.size
|
182
|
+
last = context.lines.length - 1
|
183
|
+
context.lines.each_with_index do |line, index|
|
184
|
+
output.print "#{line}#{index == last && next_line.nil? ? final_newline : "\n"}"
|
185
|
+
end
|
186
|
+
|
187
|
+
current = next_line
|
188
|
+
next_line = input.gets(sep="\n")
|
189
|
+
end
|
190
|
+
|
191
|
+
input.close
|
192
|
+
end
|
193
|
+
end
|
data/ned.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "lib/ned/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "ned"
|
7
|
+
spec.version = Ned::VERSION
|
8
|
+
spec.authors = ["Nick Dower"]
|
9
|
+
spec.email = ["nicholasdower@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = "A command line editor."
|
12
|
+
spec.description = "A command line editor"
|
13
|
+
spec.homepage = "https://github.com/nicholasdower/ned"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
17
|
+
spec.metadata["source_code_uri"] = "https://github.com/nicholasdower/ned"
|
18
|
+
spec.metadata["changelog_uri"] = "https://github.com/nicholasdower/ned/releases"
|
19
|
+
|
20
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
21
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
|
22
|
+
end
|
23
|
+
spec.bindir = 'bin'
|
24
|
+
spec.executables << 'ned'
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
end
|
data/scripts/release
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
set -e
|
4
|
+
|
5
|
+
if [ $# -ne 1 ]; then
|
6
|
+
echo "usage: $0 <version>" >&2
|
7
|
+
exit 1
|
8
|
+
fi
|
9
|
+
|
10
|
+
if [ -n "$(git status --porcelain)" ]; then
|
11
|
+
echo "error: stage or commit your changes." >&2
|
12
|
+
exit 1;
|
13
|
+
fi
|
14
|
+
|
15
|
+
NEW_VERSION=$1
|
16
|
+
CURRENT_VERSION=$(grep VERSION lib/ned/version.rb | cut -d'"' -f 2)
|
17
|
+
|
18
|
+
./scripts/test
|
19
|
+
|
20
|
+
echo "Updating from v$CURRENT_VERSION to v$NEW_VERSION. Press enter to continue."
|
21
|
+
read
|
22
|
+
|
23
|
+
sed -E -i '' "s/VERSION = \"[^\"]+\"/VERSION = \"$NEW_VERSION\"/g" lib/ned/version.rb
|
24
|
+
|
25
|
+
./scripts/generate_readme > README.md
|
26
|
+
|
27
|
+
gem build
|
28
|
+
gem push ned-$NEW_VERSION.gem
|
29
|
+
bundle install
|
30
|
+
git commit -a -m "v$NEW_VERSION Release"
|
31
|
+
open "https://github.com/nicholasdower/ned/releases/new?title=v$NEW_VERSION%20Release&tag=v$NEW_VERSION&target=$(git rev-parse HEAD)"
|
data/scripts/test
ADDED
data/todo
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
add support for a ~/.ned/ dir
|
2
|
+
implement highlight
|
3
|
+
add color
|
4
|
+
add support for more escape sequences, see https://github.com/ruby/ruby/blob/master/doc/syntax/literals.rdoc#label-Strings
|
5
|
+
add support for operating on one or more files
|
6
|
+
add support for editing files in place
|
7
|
+
handle --
|
8
|
+
add ljust
|
9
|
+
maybe add ability to highlight columns, not just full lines
|
10
|
+
write readme
|
11
|
+
add support for to_set
|
12
|
+
add support for overriding existing commands
|
13
|
+
add support for ommitting short or long flags
|
14
|
+
add support for uniq with count?
|
15
|
+
consider giving commands a way to operate on entire line with newline. currently can't replicate tr.
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ned
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Dower
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-05-29 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A command line editor
|
14
|
+
email:
|
15
|
+
- nicholasdower@gmail.com
|
16
|
+
executables:
|
17
|
+
- ned
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".gitignore"
|
22
|
+
- Gemfile
|
23
|
+
- Gemfile.lock
|
24
|
+
- README.md
|
25
|
+
- bin/ned
|
26
|
+
- lib/ned.rb
|
27
|
+
- lib/ned/command.rb
|
28
|
+
- lib/ned/commands/append.rb
|
29
|
+
- lib/ned/commands/backward.rb
|
30
|
+
- lib/ned/commands/grep.rb
|
31
|
+
- lib/ned/commands/head.rb
|
32
|
+
- lib/ned/commands/index.rb
|
33
|
+
- lib/ned/commands/join.rb
|
34
|
+
- lib/ned/commands/only.rb
|
35
|
+
- lib/ned/commands/prepend.rb
|
36
|
+
- lib/ned/commands/quote.rb
|
37
|
+
- lib/ned/commands/replace.rb
|
38
|
+
- lib/ned/commands/sort.rb
|
39
|
+
- lib/ned/commands/tail.rb
|
40
|
+
- lib/ned/commands/uniq.rb
|
41
|
+
- lib/ned/commands/visual.rb
|
42
|
+
- lib/ned/config.rb
|
43
|
+
- lib/ned/context.rb
|
44
|
+
- lib/ned/strings.rb
|
45
|
+
- lib/ned/validators.rb
|
46
|
+
- lib/ned/version.rb
|
47
|
+
- ned.gemspec
|
48
|
+
- scripts/generate_readme
|
49
|
+
- scripts/release
|
50
|
+
- scripts/test
|
51
|
+
- todo
|
52
|
+
homepage: https://github.com/nicholasdower/ned
|
53
|
+
licenses:
|
54
|
+
- MIT
|
55
|
+
metadata:
|
56
|
+
homepage_uri: https://github.com/nicholasdower/ned
|
57
|
+
source_code_uri: https://github.com/nicholasdower/ned
|
58
|
+
changelog_uri: https://github.com/nicholasdower/ned/releases
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '0'
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubygems_version: 3.3.3
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: A command line editor.
|
78
|
+
test_files: []
|