ellipses 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/BEN/304/260OKU.md +137 -0
- data/CHANGELOG.md +9 -0
- data/LICENSE.md +675 -0
- data/README.md +1 -0
- data/bin/src +12 -0
- data/bin/srv +12 -0
- data/ellipses.gemspec +39 -0
- data/lib/ellipses-client.rb +4 -0
- data/lib/ellipses-server.rb +4 -0
- data/lib/ellipses.rb +5 -0
- data/lib/ellipses/client.rb +17 -0
- data/lib/ellipses/client/application.rb +77 -0
- data/lib/ellipses/client/cli.rb +24 -0
- data/lib/ellipses/client/cli/compile.rb +21 -0
- data/lib/ellipses/client/cli/decompile.rb +21 -0
- data/lib/ellipses/client/cli/init.rb +23 -0
- data/lib/ellipses/client/cli/update.rb +19 -0
- data/lib/ellipses/client/cli/validate.rb +21 -0
- data/lib/ellipses/client/cli/version.rb +17 -0
- data/lib/ellipses/client/command.rb +69 -0
- data/lib/ellipses/client/commands.rb +23 -0
- data/lib/ellipses/client/commands/include.rb +22 -0
- data/lib/ellipses/client/commands/reject.rb +17 -0
- data/lib/ellipses/client/commands/select.rb +17 -0
- data/lib/ellipses/client/commands/substitute.rb +23 -0
- data/lib/ellipses/client/commands/translate.rb +15 -0
- data/lib/ellipses/client/config.rb +33 -0
- data/lib/ellipses/client/directive.rb +39 -0
- data/lib/ellipses/client/error.rb +10 -0
- data/lib/ellipses/client/lines.rb +154 -0
- data/lib/ellipses/client/meta.rb +79 -0
- data/lib/ellipses/client/meta_file.rb +56 -0
- data/lib/ellipses/client/parser.rb +74 -0
- data/lib/ellipses/client/repository.rb +101 -0
- data/lib/ellipses/client/source.rb +69 -0
- data/lib/ellipses/error.rb +5 -0
- data/lib/ellipses/server.rb +10 -0
- data/lib/ellipses/server/application.rb +85 -0
- data/lib/ellipses/server/cli.rb +21 -0
- data/lib/ellipses/server/cli/dump.rb +20 -0
- data/lib/ellipses/server/cli/validate.rb +21 -0
- data/lib/ellipses/server/cli/version.rb +17 -0
- data/lib/ellipses/server/error.rb +7 -0
- data/lib/ellipses/server/meta.rb +69 -0
- data/lib/ellipses/server/meta_file.rb +39 -0
- data/lib/ellipses/server/repository.rb +47 -0
- data/lib/ellipses/server/symbols.rb +146 -0
- data/lib/ellipses/support.rb +3 -0
- data/lib/ellipses/support/deflate_path.rb +14 -0
- data/lib/ellipses/support/digest.rb +13 -0
- data/lib/ellipses/support/entropy.rb +17 -0
- data/lib/ellipses/support/expand_path.rb +13 -0
- data/lib/ellipses/support/intersperse_arrays.rb +14 -0
- data/lib/ellipses/support/prefixize_non_blank.rb +16 -0
- data/lib/ellipses/support/refinements.rb +20 -0
- data/lib/ellipses/support/sanitize_path.rb +101 -0
- data/lib/ellipses/support/search_path.rb +19 -0
- data/lib/ellipses/support/shell.rb +85 -0
- data/lib/ellipses/support/to_range.rb +15 -0
- data/lib/ellipses/support/ui.rb +55 -0
- data/lib/ellipses/support/updatelines.rb +19 -0
- data/lib/ellipses/support/writelines.rb +11 -0
- data/lib/ellipses/version.rb +5 -0
- metadata +264 -0
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ellipses
|
4
|
+
module Client
|
5
|
+
module Commands
|
6
|
+
class Reject < Command
|
7
|
+
command 'reject', argc: 1
|
8
|
+
|
9
|
+
include Command::Mixins::SetupPattern
|
10
|
+
|
11
|
+
def call(input)
|
12
|
+
input.grep_v param.pattern
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ellipses
|
4
|
+
module Client
|
5
|
+
module Commands
|
6
|
+
class Select < Command
|
7
|
+
command 'select', argc: 1
|
8
|
+
|
9
|
+
include Command::Mixins::SetupPattern
|
10
|
+
|
11
|
+
def call(input)
|
12
|
+
input.grep param.pattern
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ellipses
|
4
|
+
module Client
|
5
|
+
module Commands
|
6
|
+
class Substitute < Command
|
7
|
+
command 'sub', argc: 2
|
8
|
+
|
9
|
+
include Command::Mixins::SetupPattern
|
10
|
+
|
11
|
+
def setup
|
12
|
+
super
|
13
|
+
|
14
|
+
param.replace = argv.last
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(input)
|
18
|
+
input.map { |line| line.gsub param.pattern, param.replace }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Ellipses
|
6
|
+
module Client
|
7
|
+
class Config
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :@config, *%i[paths]
|
11
|
+
|
12
|
+
def initialize(**options)
|
13
|
+
@config = build OpenStruct.new(options.compact)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
|
18
|
+
def default_paths
|
19
|
+
environment = %w[ELLIPSES_PATH SRCPATH].find { |env| ENV.key? env }
|
20
|
+
paths = environment ? ENV[environment].split(':') : []
|
21
|
+
|
22
|
+
['.', *paths]
|
23
|
+
end
|
24
|
+
|
25
|
+
def build(config)
|
26
|
+
config.paths = default_paths if !config.paths || config.paths.empty?
|
27
|
+
config.paths.compact!
|
28
|
+
|
29
|
+
config
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ellipses
|
4
|
+
module Client
|
5
|
+
class Directive
|
6
|
+
Error = Class.new Error
|
7
|
+
|
8
|
+
attr_reader :commands, :server
|
9
|
+
|
10
|
+
def initialize(lexemes, server)
|
11
|
+
@server = server
|
12
|
+
@commands = build(lexemes)
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute(**kwargs)
|
16
|
+
output = []
|
17
|
+
commands.each do |command|
|
18
|
+
output = command.call(input = output, **kwargs) || input
|
19
|
+
end
|
20
|
+
output
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def build(lexemes)
|
26
|
+
lexemes.map do |lexeme|
|
27
|
+
name = lexeme.token
|
28
|
+
|
29
|
+
raise Error, "No such command available: #{name}" unless Commands.available?(name)
|
30
|
+
|
31
|
+
proto = Commands.proto(name)
|
32
|
+
raise Error, "Wrong number of arguments: #{lexeme.argv}" unless proto.valid?(lexeme.argv)
|
33
|
+
|
34
|
+
proto.klass.new(lexeme.argv, server)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module Ellipses
|
6
|
+
module Client
|
7
|
+
class Lines < SimpleDelegator
|
8
|
+
Surround = Struct.new :before, :after, keyword_init: true do
|
9
|
+
def range(i)
|
10
|
+
Range.new(i - before, i + after)
|
11
|
+
end
|
12
|
+
|
13
|
+
def center
|
14
|
+
before
|
15
|
+
end
|
16
|
+
|
17
|
+
def sensible?(i)
|
18
|
+
before <= i || after >= i
|
19
|
+
end
|
20
|
+
|
21
|
+
def sensible!(i)
|
22
|
+
return true if sensible?(i)
|
23
|
+
|
24
|
+
raise ArgumentError, "Insensible index #{i} for surround: #{before}, #{after}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def length
|
28
|
+
@length ||= before + after + 1
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private_constant :Surround
|
33
|
+
|
34
|
+
class Chunk
|
35
|
+
attr_reader :content, :center, :origin
|
36
|
+
|
37
|
+
def initialize(content, center:, origin:)
|
38
|
+
@content = content
|
39
|
+
@center = center
|
40
|
+
@origin = origin
|
41
|
+
|
42
|
+
raise ArgumentError, "Invalid center of array: #{center}" unless center < content.length
|
43
|
+
end
|
44
|
+
|
45
|
+
def insertion
|
46
|
+
@insertion ||= Meta::Insertion.new before: center,
|
47
|
+
after: content.length - center - 1,
|
48
|
+
signature: content[center],
|
49
|
+
digest: Support.digest(*content)
|
50
|
+
end
|
51
|
+
|
52
|
+
def size
|
53
|
+
content.size
|
54
|
+
end
|
55
|
+
|
56
|
+
def origin_range
|
57
|
+
(origin...(origin + size))
|
58
|
+
end
|
59
|
+
|
60
|
+
def to_a
|
61
|
+
content
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
private_constant :Chunk
|
66
|
+
|
67
|
+
def initialize(array_of_lines)
|
68
|
+
super
|
69
|
+
|
70
|
+
@delegate_sd_obj = array_of_lines
|
71
|
+
end
|
72
|
+
|
73
|
+
def surrounding_chunk(i, before: 0, after: 0)
|
74
|
+
return unless i < length
|
75
|
+
|
76
|
+
range = (surround = Surround.new(before: before, after: after)).range(i)
|
77
|
+
Chunk.new self[range], center: surround.center, origin: range.begin
|
78
|
+
end
|
79
|
+
|
80
|
+
def insertion_by_surround(i, before: 0, after: 0)
|
81
|
+
return unless i < length
|
82
|
+
|
83
|
+
signature = self[i]
|
84
|
+
digest = Support.digest(*self[Surround.new(before: before, after: after).range(i)])
|
85
|
+
|
86
|
+
Meta::Insertion.new before: before, after: after, signature: signature, digest: digest
|
87
|
+
end
|
88
|
+
|
89
|
+
def insertion_by_entropy
|
90
|
+
return if empty?
|
91
|
+
|
92
|
+
signature = self[center = index_of_maximum_entropy]
|
93
|
+
before = center
|
94
|
+
after = length - center - 1
|
95
|
+
digest = Support.digest(*self)
|
96
|
+
|
97
|
+
Meta::Insertion.new before: before, after: after, signature: signature, digest: digest
|
98
|
+
end
|
99
|
+
|
100
|
+
def likes(insertion)
|
101
|
+
likes = []
|
102
|
+
|
103
|
+
each_with_index do |line, i|
|
104
|
+
next unless line == insertion.signature
|
105
|
+
next unless (chunk = surrounding_chunk(i, before: insertion.before, after: insertion.after))
|
106
|
+
next unless chunk.insertion == insertion
|
107
|
+
|
108
|
+
likes << chunk
|
109
|
+
end
|
110
|
+
|
111
|
+
likes
|
112
|
+
end
|
113
|
+
|
114
|
+
def uniq_like_chunk!(insertion)
|
115
|
+
raise UnfoundError, "No chunks found like: #{insertion}" if likes(insertion).empty?
|
116
|
+
raise AmbiguousError, "Multiple chunks found like: #{insertion}" unless (likes = likes(insertion)).size == 1
|
117
|
+
|
118
|
+
likes.first
|
119
|
+
end
|
120
|
+
|
121
|
+
def substitute_uniq_like_chunk!(insertion, *lines)
|
122
|
+
uniq_like_chunk!(insertion).tap do |chunk|
|
123
|
+
substitute_within(chunk.origin_range, *lines)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def index_of_maximum_entropy
|
130
|
+
each_index.max_by { |i| Support.entropy(self[i]) }
|
131
|
+
end
|
132
|
+
|
133
|
+
def substitute_at(index, *lines)
|
134
|
+
slice!(index).tap { insert(index, *lines) }
|
135
|
+
end
|
136
|
+
|
137
|
+
def substitute_within(index_or_range, *lines)
|
138
|
+
slice!(range = Support.to_range(index_or_range)).tap { insert(range.begin, *lines) }
|
139
|
+
end
|
140
|
+
|
141
|
+
class << self
|
142
|
+
def [](object = nil)
|
143
|
+
case object
|
144
|
+
when self then object
|
145
|
+
when Array then new(object)
|
146
|
+
when Enumerator then new(object.to_a)
|
147
|
+
when NilClass then new([])
|
148
|
+
else new([*object])
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module Ellipses
|
6
|
+
module Client
|
7
|
+
class Meta < SimpleDelegator
|
8
|
+
Error = Class.new Error
|
9
|
+
|
10
|
+
using Support::Refinements::Struct::FromHashWithoutBogusKeys
|
11
|
+
|
12
|
+
Source = Struct.new :source, :series, keyword_init: true do
|
13
|
+
def to_json(*args)
|
14
|
+
to_h.to_json(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.from_hash(hash)
|
18
|
+
new source: hash[:source], series: Series.from_array(hash[:series])
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Series < SimpleDelegator
|
23
|
+
def initialize(array_of_locks)
|
24
|
+
super
|
25
|
+
|
26
|
+
@delegate_sd_obj = array_of_locks
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_json(*args)
|
30
|
+
to_a.to_json(*args)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.from_array(array_of_hashes)
|
34
|
+
array_of_locks = array_of_hashes.map { |hash| Lock.from_hash hash }
|
35
|
+
new array_of_locks
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
Lock = Struct.new :directive, :insertion, keyword_init: true do
|
40
|
+
def to_json(*args)
|
41
|
+
to_h.to_json(*args)
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.from_hash(hash)
|
45
|
+
new directive: hash[:directive], insertion: Insertion.from_hash(hash[:insertion])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
Insertion = Struct.new :before, :after, :signature, :digest, keyword_init: true do
|
50
|
+
def to_s
|
51
|
+
signature
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_json(*args)
|
55
|
+
to_h.to_json(*args)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.from_hash(hash)
|
59
|
+
new(**hash)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def initialize(array_of_sources)
|
64
|
+
super
|
65
|
+
|
66
|
+
@delegate_sd_obj = array_of_sources
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_json(*args)
|
70
|
+
to_a.to_json(*args)
|
71
|
+
end
|
72
|
+
|
73
|
+
def self.from_array(array_of_hashes)
|
74
|
+
array_of_sources = array_of_hashes.map { |hash| Source.from_hash(hash) }
|
75
|
+
new array_of_sources
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Ellipses
|
6
|
+
module Client
|
7
|
+
class MetaFile
|
8
|
+
FILES = %w[.local/var/src.lock src.lock .src.lock].freeze
|
9
|
+
EMPTY = "[]\n"
|
10
|
+
|
11
|
+
def self.create(directory)
|
12
|
+
Support.dir!(directory)
|
13
|
+
|
14
|
+
selected = FILES.find { |path| ::Dir.exist? File.join(directory, ::File.dirname(path)) }
|
15
|
+
file = ::File.join(directory, selected)
|
16
|
+
|
17
|
+
::File.write(file, EMPTY) unless ::File.exist? file
|
18
|
+
|
19
|
+
new
|
20
|
+
end
|
21
|
+
|
22
|
+
attr_reader :directory, :alternatives
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
@directory, @file = Support.search_path FILES
|
26
|
+
end
|
27
|
+
|
28
|
+
def file
|
29
|
+
loaded!
|
30
|
+
@file
|
31
|
+
end
|
32
|
+
|
33
|
+
def loaded?
|
34
|
+
@file && ::File.exist?(@file)
|
35
|
+
end
|
36
|
+
|
37
|
+
def loaded!
|
38
|
+
raise 'Lockfile not located' if @file.nil?
|
39
|
+
|
40
|
+
raise "Lockfile not found: #{@file}" unless ::File.exist? @file
|
41
|
+
end
|
42
|
+
|
43
|
+
def read
|
44
|
+
Meta.from_array JSON.load_file(file, symbolize_names: true)
|
45
|
+
end
|
46
|
+
|
47
|
+
def write(meta)
|
48
|
+
Support.updatelines(file, meta.empty? ? EMPTY : JSON.pretty_generate(meta))
|
49
|
+
end
|
50
|
+
|
51
|
+
def to_s
|
52
|
+
file
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|