lhj-tools 0.1.4 → 0.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/lib/lhj/action/sh_helper.rb +138 -0
  3. data/lib/lhj/command/config/info.rb +47 -0
  4. data/lib/lhj/command/config.rb +11 -0
  5. data/lib/lhj/command/file_path.rb +20 -0
  6. data/lib/lhj/command/head_import.rb +19 -3
  7. data/lib/lhj/command/http.rb +14 -0
  8. data/lib/lhj/command/init.rb +2 -2
  9. data/lib/lhj/command/local/fetch.rb +1 -1
  10. data/lib/lhj/command/local/filter.rb +1 -1
  11. data/lib/lhj/command/local/local.rb +1 -1
  12. data/lib/lhj/command/local/local_upload.rb +1 -1
  13. data/lib/lhj/command/local/micro_service.rb +1 -1
  14. data/lib/lhj/command/oss/del.rb +18 -5
  15. data/lib/lhj/command/oss/list.rb +6 -2
  16. data/lib/lhj/command/oss/upload.rb +9 -5
  17. data/lib/lhj/command/refactor_rename.rb +18 -2
  18. data/lib/lhj/command/rename_image.rb +1 -1
  19. data/lib/lhj/command/sync_pod_repo.rb +151 -0
  20. data/lib/lhj/command/trans.rb +1 -1
  21. data/lib/lhj/command/yapi.rb +15 -14
  22. data/lib/lhj/command.rb +33 -0
  23. data/lib/lhj/tools/version.rb +1 -1
  24. data/lib/lhj/tools.rb +1 -0
  25. data/lib/lhj/tree/directory_renderer.rb +45 -0
  26. data/lib/lhj/tree/hash_walker.rb +70 -0
  27. data/lib/lhj/tree/node.rb +90 -0
  28. data/lib/lhj/tree/number_renderer.rb +30 -0
  29. data/lib/lhj/tree/path_walker.rb +107 -0
  30. data/lib/lhj/tree/tree.rb +112 -0
  31. data/lib/lhj/ui/errors/lhj_common_error.rb +19 -0
  32. data/lib/lhj/ui/errors/lhj_crash.rb +11 -0
  33. data/lib/lhj/ui/errors/lhj_error.rb +25 -0
  34. data/lib/lhj/ui/errors/lhj_exception.rb +19 -0
  35. data/lib/lhj/ui/errors/lhj_shell_error.rb +11 -0
  36. data/lib/lhj/ui/errors.rb +1 -0
  37. data/lib/lhj/ui/implementations/shell.rb +148 -0
  38. data/lib/lhj/ui/interface.rb +205 -0
  39. data/lib/lhj/ui/ui.rb +26 -0
  40. metadata +77 -2
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'node'
4
+ require_relative 'directory_renderer'
5
+ require_relative 'number_renderer'
6
+ require_relative 'hash_walker'
7
+ require_relative 'path_walker'
8
+
9
+ module Lhj
10
+ class Tree
11
+ # @api public
12
+ def self.[](data)
13
+ new(data)
14
+ end
15
+
16
+ # The list of nodes in this tree.
17
+ attr_reader :nodes
18
+
19
+ # Create a Tree
20
+ #
21
+ # @param [String,Dir,Hash] data
22
+ #
23
+ # @api public
24
+ def initialize(data = nil, options = {}, &block)
25
+ @data = data ? data.dup.freeze : nil
26
+ @walker = select_walker.new(options)
27
+ @nodes = []
28
+
29
+ if @data
30
+ @walker.traverse(data)
31
+ @nodes = @walker.nodes
32
+ end
33
+
34
+ @nodes_stack = []
35
+
36
+ instance_eval(&block) if block_given?
37
+
38
+ freeze
39
+ end
40
+
41
+ # Add node to this tree.
42
+ #
43
+ # @param [Symbol,String] name
44
+ # the name for the node
45
+ #
46
+ # @param [Node, LeafNode] type
47
+ # the type of node to add
48
+ #
49
+ # @example
50
+ # TTY::Tree.new do
51
+ # node '...' do
52
+ # node '...'
53
+ # end
54
+ # end
55
+ #
56
+ # @api public
57
+ def node(name, type = Node, &block)
58
+ parent = @nodes_stack.empty? ? Node::ROOT : @nodes_stack.last
59
+ level = [0, @nodes_stack.size - 1].max
60
+ prefix = ':pipe' * level
61
+ if parent.class == LeafNode
62
+ prefix = ':space' * level
63
+ end
64
+ node = type.new(name, parent.full_path, prefix, @nodes_stack.size)
65
+ @nodes << node
66
+
67
+ return unless block_given?
68
+
69
+ @nodes_stack << node
70
+ if block.arity.zero?
71
+ instance_eval(&block)
72
+ else
73
+ instance_eval(&(->(*_args) { block[node] }))
74
+ end
75
+ @nodes_stack.pop
76
+ end
77
+
78
+ # Add leaf node
79
+ #
80
+ # @api public
81
+ def leaf(name, &block)
82
+ node(name, LeafNode, &block)
83
+ end
84
+
85
+ # @api public
86
+ def render(options = {})
87
+ as = options.delete(:as) || :dir
88
+ renderer = select_renderer(as).new(nodes, options)
89
+ renderer.render
90
+ end
91
+
92
+ private
93
+
94
+ # @api private
95
+ def select_walker
96
+ if @data.is_a?(Hash) || @data.nil?
97
+ HashWalker
98
+ else
99
+ @data ||= Dir.pwd
100
+ PathWalker
101
+ end
102
+ end
103
+
104
+ def select_renderer(as)
105
+ case as
106
+ when :dir, :directory then DirectoryRenderer
107
+ when :num, :number then NumberRenderer
108
+ end
109
+ end
110
+ end
111
+ end
112
+
@@ -0,0 +1,19 @@
1
+ require_relative 'lhj_exception'
2
+
3
+ module Lhj
4
+ class Interface
5
+ # Super class for exception types that we do not want to record
6
+ # explicitly as crashes or user errors
7
+ class LhjCommonException < LhjException; end
8
+
9
+ # Raised when there is a build failure in xcodebuild
10
+ class LhjBuildFailure < LhjCommonException; end
11
+
12
+ # Raised when a test fails when being run by tools such as scan or snapshot
13
+ class LhjTestFailure < LhjCommonException; end
14
+
15
+ # Raise this type of exception when a failure caused by a third party
16
+ # dependency (i.e. xcodebuild, gradle, slather) happens.
17
+ class LhjDependencyCausedException < LhjCommonException; end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'lhj_exception'
2
+
3
+ module Lhj
4
+ class Interface
5
+ class LhjCrash < LhjException
6
+ def prefix
7
+ '[LHJ_CRASH]'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ require_relative 'lhj_exception'
2
+
3
+ module Lhj
4
+ class Interface
5
+ class LhjError < LhjException
6
+ attr_reader :show_github_issues
7
+ attr_reader :error_info
8
+
9
+ def initialize(show_github_issues: false, error_info: nil)
10
+ @show_github_issues = show_github_issues
11
+ @error_info = error_info
12
+ end
13
+
14
+ def prefix
15
+ '[USER_ERROR]'
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ class Exception
22
+ # def fastlane_should_report_metrics?
23
+ # return false
24
+ # end
25
+ end
@@ -0,0 +1,19 @@
1
+ module Lhj
2
+ class Interface
3
+ class LhjException < StandardError
4
+ def prefix
5
+ '[LHJ_EXCEPTION]'
6
+ end
7
+
8
+ def caused_by_calling_ui_method?(method_name: nil)
9
+ return false if backtrace.nil? || backtrace[0].nil? || method_name.nil?
10
+ first_frame = backtrace[0]
11
+ if first_frame.include?(method_name) && first_frame.include?('interface.rb')
12
+ true
13
+ else
14
+ false
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ require_relative 'lhj_exception'
2
+
3
+ module Lhj
4
+ class Interface
5
+ class LhjShellError < LhjException
6
+ def prefix
7
+ '[SHELL_ERROR]'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1 @@
1
+ Dir[File.dirname(__FILE__) + "/errors/*.rb"].each { |f| require_relative f }
@@ -0,0 +1,148 @@
1
+ require_relative '../interface'
2
+
3
+ module Lhj
4
+ # Shell is the terminal output of things
5
+ # For documentation for each of the methods open `interface.rb`
6
+ class Shell < Interface
7
+
8
+ def log
9
+ return @log if @log
10
+
11
+ $stdout.sync = true
12
+
13
+ # if !ENV.key?('DEBUG')
14
+ # $stdout.puts("Logging disabled while running tests. Force them by setting the DEBUG environment variable")
15
+ # @log ||= Logger.new(nil) # don't show any logs when running tests
16
+ # else
17
+ @log ||= Logger.new($stdout)
18
+ # end
19
+
20
+ @log.formatter = proc do |severity, datetime, progname, msg|
21
+ "#{format_string(datetime, severity)}#{msg}\n"
22
+ end
23
+
24
+ @log
25
+ end
26
+
27
+ def format_string(datetime = Time.now, severity = "")
28
+ return "[#{datetime.strftime('%H:%M:%S')}]: "
29
+ end
30
+
31
+ #####################################################
32
+ # @!group Messaging: show text to the user
33
+ #####################################################
34
+
35
+ def error(message)
36
+ log.error(message.to_s.red)
37
+ end
38
+
39
+ def important(message)
40
+ log.warn(message.to_s.yellow)
41
+ end
42
+
43
+ def success(message)
44
+ log.info(message.to_s.green)
45
+ end
46
+
47
+ def message(message)
48
+ log.info(message.to_s)
49
+ end
50
+
51
+ def deprecated(message)
52
+ log.error(message.to_s.deprecated)
53
+ end
54
+
55
+ def command(message)
56
+ log.info("$ #{message}".cyan)
57
+ end
58
+
59
+ def command_output(message)
60
+ actual = (encode_as_utf_8_if_possible(message).split("\r").last || "") # as clearing the line will remove the `>` and the time stamp
61
+ actual.split("\n").each do |msg|
62
+ prefix = msg.include?("▸") ? "" : "▸ "
63
+ log.info(prefix + "" + msg.magenta)
64
+ end
65
+ end
66
+
67
+ def verbose(message)
68
+ log.debug(message.to_s)
69
+ end
70
+
71
+ def header(message)
72
+ success(message)
73
+ end
74
+
75
+ def content_error(content, error_line)
76
+ error_line = error_line.to_i
77
+ return unless error_line > 0
78
+
79
+ contents = content.split(/\r?\n/).map(&:chomp)
80
+
81
+ start_line = error_line - 2 < 1 ? 1 : error_line - 2
82
+ end_line = error_line + 2 < contents.length ? error_line + 2 : contents.length
83
+
84
+ Range.new(start_line, end_line).each do |line|
85
+ str = line == error_line ? " => " : " "
86
+ str << line.to_s.rjust(Math.log10(end_line) + 1)
87
+ str << ":\t#{contents[line - 1]}"
88
+ error(str)
89
+ end
90
+ end
91
+
92
+ #####################################################
93
+ # @!group Errors: Inputs
94
+ #####################################################
95
+
96
+ def interactive?
97
+ interactive = true
98
+ interactive = false if $stdout.isatty == false
99
+ interactive = false if Helper.ci?
100
+ return interactive
101
+ end
102
+
103
+ def input(message)
104
+ verify_interactive!(message)
105
+ ask("#{format_string}#{message.to_s.yellow}").to_s.strip
106
+ end
107
+
108
+ def confirm(message)
109
+ verify_interactive!(message)
110
+ agree("#{format_string}#{message.to_s.yellow} (y/n)", true)
111
+ end
112
+
113
+ def select(message, options)
114
+ verify_interactive!(message)
115
+
116
+ important(message)
117
+ choose(*options)
118
+ end
119
+
120
+ def password(message)
121
+ verify_interactive!(message)
122
+
123
+ ask("#{format_string}#{message.to_s.yellow}") do |q|
124
+ q.whitespace = :chomp
125
+ q.echo = "*"
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ def encode_as_utf_8_if_possible(message)
132
+ return message if message.valid_encoding?
133
+
134
+ # genstrings outputs UTF-16, so we should try to use this encoding if it turns out to be valid
135
+ test_message = message.dup
136
+ return message.encode(Encoding::UTF_8, Encoding::UTF_16) if test_message.force_encoding(Encoding::UTF_16).valid_encoding?
137
+
138
+ # replace any invalid with empty string
139
+ message.encode(Encoding::UTF_8, invalid: :replace)
140
+ end
141
+
142
+ def verify_interactive!(message)
143
+ return if interactive?
144
+ important(message)
145
+ crash!("Could not retrieve response as fastlane runs in non-interactive mode")
146
+ end
147
+ end
148
+ end
@@ -0,0 +1,205 @@
1
+ require_relative 'errors'
2
+
3
+ module Lhj
4
+ # Abstract super class
5
+ class Interface
6
+ #####################################################
7
+ # @!group Messaging: show text to the user
8
+ #####################################################
9
+
10
+ # Level Error: Can be used to show additional error
11
+ # information before actually raising an exception
12
+ # or can be used to just show an error from which
13
+ # fastlane can recover (much magic)
14
+ #
15
+ # By default those messages are shown in red
16
+ def error(_message)
17
+ not_implemented(__method__)
18
+ end
19
+
20
+ # Level Important: Can be used to show warnings to the user
21
+ # not necessarily negative, but something the user should
22
+ # be aware of.
23
+ #
24
+ # By default those messages are shown in yellow
25
+ def important(_message)
26
+ not_implemented(__method__)
27
+ end
28
+
29
+ # Level Success: Show that something was successful
30
+ #
31
+ # By default those messages are shown in green
32
+ def success(_message)
33
+ not_implemented(__method__)
34
+ end
35
+
36
+ # Level Message: Show a neutral message to the user
37
+ #
38
+ # By default those messages shown in white/black
39
+ def message(_message)
40
+ not_implemented(__method__)
41
+ end
42
+
43
+ # Level Deprecated: Show that a particular function is deprecated
44
+ #
45
+ # By default those messages shown in strong blue
46
+ def deprecated(_message)
47
+ not_implemented(__method__)
48
+ end
49
+
50
+ # Level Command: Print out a terminal command that is being
51
+ # executed.
52
+ #
53
+ # By default those messages shown in cyan
54
+ def command(_message)
55
+ not_implemented(__method__)
56
+ end
57
+
58
+ # Level Command Output: Print the output of a command with
59
+ # this method
60
+ #
61
+ # By default those messages shown in magenta
62
+ def command_output(_message)
63
+ not_implemented(__method__)
64
+ end
65
+
66
+ # Level Verbose: Print out additional information for the
67
+ # users that are interested. Will only be printed when
68
+ # FastlaneCore::Globals.verbose? = true
69
+ #
70
+ # By default those messages are shown in white
71
+ def verbose(_message)
72
+ not_implemented(__method__)
73
+ end
74
+
75
+ # Print a header = a text in a box
76
+ # use this if this message is really important
77
+ def header(_message)
78
+ not_implemented(__method__)
79
+ end
80
+
81
+ # Print lines of content around specific line where
82
+ # failed to parse.
83
+ #
84
+ # This message will be shown as error
85
+ def content_error(content, error_line)
86
+ not_implemented(__method__)
87
+ end
88
+
89
+ #####################################################
90
+ # @!group Errors: Inputs
91
+ #####################################################
92
+
93
+ # Is is possible to ask the user questions?
94
+ def interactive?
95
+ not_implemented(__method__)
96
+ end
97
+
98
+ # get a standard text input (single line)
99
+ def input(_message)
100
+ not_implemented(__method__)
101
+ end
102
+
103
+ # A simple yes or no question
104
+ def confirm(_message)
105
+ not_implemented(__method__)
106
+ end
107
+
108
+ # Let the user select one out of x items
109
+ # return value is the value of the option the user chose
110
+ def select(_message, _options)
111
+ not_implemented(__method__)
112
+ end
113
+
114
+ # Password input for the user, text field shouldn't show
115
+ # plain text
116
+ def password(_message)
117
+ not_implemented(__method__)
118
+ end
119
+
120
+ #####################################################
121
+ # @!group Abort helper methods
122
+ #####################################################
123
+
124
+ # Pass an exception to this method to exit the program
125
+ # using the given exception
126
+ # Use this method instead of user_error! if this error is
127
+ # unexpected, e.g. an invalid server response that shouldn't happen
128
+ def crash!(exception)
129
+ raise LhjCrash.new, exception.to_s
130
+ end
131
+
132
+ # Use this method to exit the program because of an user error
133
+ # e.g. app doesn't exist on the given Developer Account
134
+ # or invalid user credentials
135
+ # or scan tests fail
136
+ # This will show the error message, but doesn't show the full
137
+ # stack trace
138
+ # Basically this should be used when you actively catch the error
139
+ # and want to show a nice error message to the user
140
+ def user_error!(error_message, options = {})
141
+ raise LhjError.new(show_github_issues: options[:show_github_issues], error_info: options[:error_info]), error_message.to_s
142
+ end
143
+
144
+ # Use this method to exit the program because of a shell command
145
+ # failure -- the command returned a non-zero response. This does
146
+ # not specify the nature of the error. The error might be from a
147
+ # programming error, a user error, or an expected error because
148
+ # the user of the Fastfile doesn't have their environment set up
149
+ # properly. Because of this, when these errors occur, it means
150
+ # that the caller of the shell command did not adequate error
151
+ # handling and the caller error handling should be improved.
152
+ def shell_error!(error_message, options = {})
153
+ raise LhjShellError.new(options), error_message.to_s
154
+ end
155
+
156
+ # Use this method to exit the program because of a build failure
157
+ # that's caused by the source code of the user. Example for this
158
+ # is that gym will fail when the code doesn't compile or because
159
+ # settings for the project are incorrect.
160
+ # By using this method we'll have more accurate results about
161
+ # fastlane failures
162
+ def build_failure!(error_message, options = {})
163
+ raise LhjBuildFailure.new(options), error_message.to_s
164
+ end
165
+
166
+ # Use this method to exit the program because of a test failure
167
+ # that's caused by the source code of the user. Example for this
168
+ # is that scan will fail when the tests fail.
169
+ # By using this method we'll have more accurate results about
170
+ # fastlane failures
171
+ def test_failure!(error_message)
172
+ raise LhjTestFailure.new, error_message
173
+ end
174
+
175
+ # Use this method to exit the program because of terminal state
176
+ # that is neither the fault of fastlane, nor a problem with the
177
+ # user's input. Using this method instead of user_error! will
178
+ # avoid tracking this outcome as a fastlane failure.
179
+ #
180
+ # e.g. tests ran successfully, but no screenshots were found
181
+ #
182
+ # This will show the message, but hide the full stack trace.
183
+ def abort_with_message!(message)
184
+ raise LhjCommonException.new, message
185
+ end
186
+
187
+ #####################################################
188
+ # @!group Helpers
189
+ #####################################################
190
+ def not_implemented(method_name)
191
+ require_relative 'ui'
192
+ UI.user_error!("Current UI '#{self}' doesn't support method '#{method_name}'")
193
+ end
194
+
195
+ def to_s
196
+ self.class.name.split('::').last
197
+ end
198
+ end
199
+ end
200
+
201
+ class String
202
+ def deprecated
203
+ self.bold.blue
204
+ end
205
+ end
data/lib/lhj/ui/ui.rb ADDED
@@ -0,0 +1,26 @@
1
+ module Lhj
2
+ class UI
3
+ class << self
4
+ attr_accessor(:ui_object)
5
+
6
+ def ui_object
7
+ require_relative 'implementations/shell'
8
+ @ui_object ||= Shell.new
9
+ end
10
+
11
+ def method_missing(method_sym, *args, &_block)
12
+ # not using `responds` because we don't care about methods like .to_s and so on
13
+ require_relative 'interface'
14
+ interface_methods = Lhj::Interface.instance_methods - Object.instance_methods
15
+ UI.user_error!("Unknown method '#{method_sym}', supported #{interface_methods}") unless interface_methods.include?(method_sym)
16
+
17
+ self.ui_object.send(method_sym, *args)
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ # Import all available implementations
24
+ Dir[File.dirname(__FILE__) + '/implementations/*.rb'].each do |file|
25
+ require_relative file
26
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhj-tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - lihaijian
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-01-20 00:00:00.000000000 Z
11
+ date: 2022-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: claide
@@ -118,6 +118,60 @@ dependencies:
118
118
  - - "~>"
119
119
  - !ruby/object:Gem::Version
120
120
  version: '2.8'
121
+ - !ruby/object:Gem::Dependency
122
+ name: tty-spinner
123
+ requirement: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: 0.8.0
128
+ - - "<"
129
+ - !ruby/object:Gem::Version
130
+ version: 1.0.0
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 0.8.0
138
+ - - "<"
139
+ - !ruby/object:Gem::Version
140
+ version: 1.0.0
141
+ - !ruby/object:Gem::Dependency
142
+ name: colored
143
+ requirement: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: '0'
148
+ type: :runtime
149
+ prerelease: false
150
+ version_requirements: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: '0'
155
+ - !ruby/object:Gem::Dependency
156
+ name: excon
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - ">="
160
+ - !ruby/object:Gem::Version
161
+ version: 0.71.0
162
+ - - "<"
163
+ - !ruby/object:Gem::Version
164
+ version: 1.0.0
165
+ type: :runtime
166
+ prerelease: false
167
+ version_requirements: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - ">="
170
+ - !ruby/object:Gem::Version
171
+ version: 0.71.0
172
+ - - "<"
173
+ - !ruby/object:Gem::Version
174
+ version: 1.0.0
121
175
  - !ruby/object:Gem::Dependency
122
176
  name: faraday-cookie_jar
123
177
  requirement: !ruby/object:Gem::Requirement
@@ -218,8 +272,13 @@ extra_rdoc_files: []
218
272
  files:
219
273
  - README.md
220
274
  - bin/lhj
275
+ - lib/lhj/action/sh_helper.rb
221
276
  - lib/lhj/command.rb
277
+ - lib/lhj/command/config.rb
278
+ - lib/lhj/command/config/info.rb
279
+ - lib/lhj/command/file_path.rb
222
280
  - lib/lhj/command/head_import.rb
281
+ - lib/lhj/command/http.rb
223
282
  - lib/lhj/command/init.rb
224
283
  - lib/lhj/command/local/fetch.rb
225
284
  - lib/lhj/command/local/filter.rb
@@ -233,6 +292,7 @@ files:
233
292
  - lib/lhj/command/refactor_rename.rb
234
293
  - lib/lhj/command/rename_image.rb
235
294
  - lib/lhj/command/reverse_import.rb
295
+ - lib/lhj/command/sync_pod_repo.rb
236
296
  - lib/lhj/command/template.rb
237
297
  - lib/lhj/command/trans.rb
238
298
  - lib/lhj/command/view.rb
@@ -244,6 +304,21 @@ files:
244
304
  - lib/lhj/helper/trans_helper.rb
245
305
  - lib/lhj/tools.rb
246
306
  - lib/lhj/tools/version.rb
307
+ - lib/lhj/tree/directory_renderer.rb
308
+ - lib/lhj/tree/hash_walker.rb
309
+ - lib/lhj/tree/node.rb
310
+ - lib/lhj/tree/number_renderer.rb
311
+ - lib/lhj/tree/path_walker.rb
312
+ - lib/lhj/tree/tree.rb
313
+ - lib/lhj/ui/errors.rb
314
+ - lib/lhj/ui/errors/lhj_common_error.rb
315
+ - lib/lhj/ui/errors/lhj_crash.rb
316
+ - lib/lhj/ui/errors/lhj_error.rb
317
+ - lib/lhj/ui/errors/lhj_exception.rb
318
+ - lib/lhj/ui/errors/lhj_shell_error.rb
319
+ - lib/lhj/ui/implementations/shell.rb
320
+ - lib/lhj/ui/interface.rb
321
+ - lib/lhj/ui/ui.rb
247
322
  homepage:
248
323
  licenses:
249
324
  - MIT