ellipses 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. checksums.yaml +7 -0
  2. data/BEN/304/260OKU.md +137 -0
  3. data/CHANGELOG.md +9 -0
  4. data/LICENSE.md +675 -0
  5. data/README.md +1 -0
  6. data/bin/src +12 -0
  7. data/bin/srv +12 -0
  8. data/ellipses.gemspec +39 -0
  9. data/lib/ellipses-client.rb +4 -0
  10. data/lib/ellipses-server.rb +4 -0
  11. data/lib/ellipses.rb +5 -0
  12. data/lib/ellipses/client.rb +17 -0
  13. data/lib/ellipses/client/application.rb +77 -0
  14. data/lib/ellipses/client/cli.rb +24 -0
  15. data/lib/ellipses/client/cli/compile.rb +21 -0
  16. data/lib/ellipses/client/cli/decompile.rb +21 -0
  17. data/lib/ellipses/client/cli/init.rb +23 -0
  18. data/lib/ellipses/client/cli/update.rb +19 -0
  19. data/lib/ellipses/client/cli/validate.rb +21 -0
  20. data/lib/ellipses/client/cli/version.rb +17 -0
  21. data/lib/ellipses/client/command.rb +69 -0
  22. data/lib/ellipses/client/commands.rb +23 -0
  23. data/lib/ellipses/client/commands/include.rb +22 -0
  24. data/lib/ellipses/client/commands/reject.rb +17 -0
  25. data/lib/ellipses/client/commands/select.rb +17 -0
  26. data/lib/ellipses/client/commands/substitute.rb +23 -0
  27. data/lib/ellipses/client/commands/translate.rb +15 -0
  28. data/lib/ellipses/client/config.rb +33 -0
  29. data/lib/ellipses/client/directive.rb +39 -0
  30. data/lib/ellipses/client/error.rb +10 -0
  31. data/lib/ellipses/client/lines.rb +154 -0
  32. data/lib/ellipses/client/meta.rb +79 -0
  33. data/lib/ellipses/client/meta_file.rb +56 -0
  34. data/lib/ellipses/client/parser.rb +74 -0
  35. data/lib/ellipses/client/repository.rb +101 -0
  36. data/lib/ellipses/client/source.rb +69 -0
  37. data/lib/ellipses/error.rb +5 -0
  38. data/lib/ellipses/server.rb +10 -0
  39. data/lib/ellipses/server/application.rb +85 -0
  40. data/lib/ellipses/server/cli.rb +21 -0
  41. data/lib/ellipses/server/cli/dump.rb +20 -0
  42. data/lib/ellipses/server/cli/validate.rb +21 -0
  43. data/lib/ellipses/server/cli/version.rb +17 -0
  44. data/lib/ellipses/server/error.rb +7 -0
  45. data/lib/ellipses/server/meta.rb +69 -0
  46. data/lib/ellipses/server/meta_file.rb +39 -0
  47. data/lib/ellipses/server/repository.rb +47 -0
  48. data/lib/ellipses/server/symbols.rb +146 -0
  49. data/lib/ellipses/support.rb +3 -0
  50. data/lib/ellipses/support/deflate_path.rb +14 -0
  51. data/lib/ellipses/support/digest.rb +13 -0
  52. data/lib/ellipses/support/entropy.rb +17 -0
  53. data/lib/ellipses/support/expand_path.rb +13 -0
  54. data/lib/ellipses/support/intersperse_arrays.rb +14 -0
  55. data/lib/ellipses/support/prefixize_non_blank.rb +16 -0
  56. data/lib/ellipses/support/refinements.rb +20 -0
  57. data/lib/ellipses/support/sanitize_path.rb +101 -0
  58. data/lib/ellipses/support/search_path.rb +19 -0
  59. data/lib/ellipses/support/shell.rb +85 -0
  60. data/lib/ellipses/support/to_range.rb +15 -0
  61. data/lib/ellipses/support/ui.rb +55 -0
  62. data/lib/ellipses/support/updatelines.rb +19 -0
  63. data/lib/ellipses/support/writelines.rb +11 -0
  64. data/lib/ellipses/version.rb +5 -0
  65. 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,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ellipses
4
+ module Client
5
+ module Commands
6
+ class Translate < Command
7
+ command 'tr', argc: 2
8
+
9
+ def call(input)
10
+ input.map { |line| line.tr(*argv) }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ 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,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Ellipses
4
+ module Client
5
+ Error = Class.new Ellipses::Error
6
+
7
+ AmbiguousError = Class.new Error
8
+ UnfoundError = Class.new Error
9
+ end
10
+ 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