ellipses 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|