term_utils 0.3.2 → 0.5.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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -2
  3. data/COPYING +3 -3
  4. data/README.md +51 -16
  5. data/Rakefile +6 -0
  6. data/doc/TermUtils/AP/Article.html +57 -55
  7. data/doc/TermUtils/AP/ArticleResult.html +584 -0
  8. data/doc/TermUtils/AP/Flag.html +295 -78
  9. data/doc/TermUtils/AP/Parameter.html +891 -103
  10. data/doc/TermUtils/AP/ParameterResult.html +980 -0
  11. data/doc/TermUtils/{FF/Cursor/Context.html → AP/ParameterWalkerHooks.html} +60 -60
  12. data/doc/TermUtils/AP/ParseError.html +651 -19
  13. data/doc/TermUtils/AP/Parser.html +181 -121
  14. data/doc/TermUtils/AP/Result.html +201 -528
  15. data/doc/TermUtils/AP/Syntax.html +103 -393
  16. data/doc/TermUtils/AP/SyntaxError.html +9 -91
  17. data/doc/TermUtils/AP/Walker.html +686 -0
  18. data/doc/TermUtils/AP.html +49 -160
  19. data/doc/TermUtils/FF/Config.html +203 -35
  20. data/doc/TermUtils/FF/Context.html +585 -0
  21. data/doc/TermUtils/FF/Entry.html +626 -0
  22. data/doc/TermUtils/FF/Finder.html +850 -0
  23. data/doc/TermUtils/FF/{Cursor.html → FinderEntry.html} +473 -211
  24. data/doc/TermUtils/FF/FinderQuery.html +946 -0
  25. data/doc/TermUtils/FF/Query.html +402 -70
  26. data/doc/TermUtils/FF.html +135 -11
  27. data/doc/TermUtils/PropertyTreeNode.html +304 -190
  28. data/doc/TermUtils/Tab/Column.html +98 -96
  29. data/doc/TermUtils/Tab/Header.html +30 -30
  30. data/doc/TermUtils/Tab/Holder.html +81 -81
  31. data/doc/TermUtils/Tab/Printer.html +43 -43
  32. data/doc/TermUtils/Tab/Table.html +124 -128
  33. data/doc/TermUtils/Tab/TableError.html +7 -89
  34. data/doc/TermUtils/Tab.html +93 -86
  35. data/doc/TermUtils.html +10 -10
  36. data/doc/_index.html +62 -42
  37. data/doc/class_list.html +3 -3
  38. data/doc/css/style.css +3 -2
  39. data/doc/file.README.html +63 -26
  40. data/doc/file_list.html +2 -2
  41. data/doc/frames.html +2 -2
  42. data/doc/index.html +63 -26
  43. data/doc/js/app.js +14 -3
  44. data/doc/method_list.html +708 -236
  45. data/doc/top-level-namespace.html +7 -7
  46. data/lib/term_utils/ap/article.rb +15 -9
  47. data/lib/term_utils/ap/flag.rb +37 -20
  48. data/lib/term_utils/ap/parameter.rb +88 -19
  49. data/lib/term_utils/ap/parser.rb +143 -116
  50. data/lib/term_utils/ap/result.rb +208 -161
  51. data/lib/term_utils/ap/syntax.rb +53 -69
  52. data/lib/term_utils/ap.rb +79 -24
  53. data/lib/term_utils/ff/config.rb +22 -10
  54. data/lib/term_utils/{ap/no_such_value_error.rb → ff/entry.rb} +26 -8
  55. data/lib/term_utils/ff/finder.rb +255 -0
  56. data/lib/term_utils/ff/query.rb +94 -17
  57. data/lib/term_utils/ff.rb +12 -2
  58. data/lib/term_utils/property_tree_node.rb +47 -19
  59. data/lib/term_utils/tab.rb +106 -61
  60. data/lib/term_utils.rb +8 -1
  61. data/term_utils.gemspec +4 -4
  62. metadata +18 -17
  63. data/doc/TermUtils/AP/Element.html +0 -1025
  64. data/doc/TermUtils/AP/Level.html +0 -638
  65. data/doc/TermUtils/AP/NoSuchValueError.html +0 -217
  66. data/lib/term_utils/ap/element.rb +0 -78
  67. data/lib/term_utils/ap/level.rb +0 -57
  68. data/lib/term_utils/ap/parse_error.rb +0 -27
  69. data/lib/term_utils/ap/syntax_error.rb +0 -27
  70. data/lib/term_utils/ff/cursor.rb +0 -153
data/lib/term_utils/ap.rb CHANGED
@@ -1,4 +1,6 @@
1
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2023 Thomas Baron
2
4
  #
3
5
  # This file is part of term_utils.
4
6
  #
@@ -13,21 +15,82 @@
13
15
  #
14
16
  # You should have received a copy of the GNU General Public License
15
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
16
- require 'term_utils/property_tree_node'
17
- require 'term_utils/ap/no_such_value_error'
18
- require 'term_utils/ap/parse_error'
19
- require 'term_utils/ap/syntax_error'
18
+
20
19
  require 'term_utils/ap/article'
21
20
  require 'term_utils/ap/flag'
22
- require 'term_utils/ap/element'
23
- require 'term_utils/ap/level'
24
21
  require 'term_utils/ap/parameter'
25
22
  require 'term_utils/ap/syntax'
26
23
  require 'term_utils/ap/result'
27
24
  require 'term_utils/ap/parser'
25
+
28
26
  module TermUtils
29
- # The Argument Parser module provides a way to parse command arguments.
27
+ # The Argument Parser module provides a way to parse arguments.
30
28
  module AP
29
+ # ParseError.
30
+ class ParseError < StandardError
31
+ # Constructs a new ParseError.
32
+ # @param props [Hash<Symbol, Object>]
33
+ def initialize(props = {})
34
+ super(self.class.create_message(props))
35
+ @props = props.dup
36
+ end
37
+
38
+ # Returns the properties associated to this one.
39
+ # @return [Hash<Symbol, Object>]
40
+ def props
41
+ @props.dup
42
+ end
43
+
44
+ # Returns the short message (i.e. the message without properties).
45
+ # @return [String] The short message.
46
+ def short_message
47
+ @props.fetch(:message)
48
+ end
49
+
50
+ # Tests whether this one has a syntax parameter ID.
51
+ # @return [Boolean]
52
+ def parameter?
53
+ @props.key?(:parameter)
54
+ end
55
+
56
+ # Returns the syntax parameter ID (if any).
57
+ # @return [Symbol, nil] The syntax parameter ID if any, `nil` otherwise.
58
+ def parameter
59
+ @props.fetch(:parameter, nil)
60
+ end
61
+
62
+ # Tests whether this one has a faulty argument.
63
+ # @return [Boolean]
64
+ def fault?
65
+ @props.key?(:fault)
66
+ end
67
+
68
+ # Returns the faulty argument (if any).
69
+ # @return [String, nil] The faulty argument if any, `nil` otherwise.
70
+ def fault
71
+ @props.fetch(:fault, nil)
72
+ end
73
+
74
+ # Creates a message based on given properties.
75
+ # @param props [Hash<Symbol, Object>]
76
+ # @return [String]
77
+ def self.create_message(props)
78
+ props = props.dup
79
+ msg = props.delete(:message) { |key| raise StandardError, "#{key} property is mandatory" }
80
+ return msg if props.empty?
81
+
82
+ vals = []
83
+ props.each do |key, val|
84
+ vals << "#{key}: \"#{val}\""
85
+ end
86
+ "#{msg} (#{vals.join(', ')})"
87
+ end
88
+ end
89
+
90
+ # SyntaxError.
91
+ class SyntaxError < StandardError
92
+ end
93
+
31
94
  # Creates a new Syntax.
32
95
  # @return [TermUtils::AP::Syntax]
33
96
  def self.create_syntax(&block)
@@ -35,25 +98,17 @@ module TermUtils
35
98
  block.call(new_syntax) if block
36
99
  new_syntax
37
100
  end
38
- # Parses a given command.
39
- # @param syntax [TermUtils::AP::Syntax]
40
- # @param command [String]
41
- # @param opts [Hash]
42
- # @return [TermUtils::AP::Result]
43
- # @raise [TermUtils::AP::ParseError]
44
- # @raise [TermUtils::AP::SyntaxError]
45
- def self.parse_command(syntax, command, opts = {})
46
- TermUtils::AP::Parser.new.parse_command(syntax, command, opts)
47
- end
101
+
48
102
  # Parses a given list of arguments.
49
- # @param syntax [TermUtils::AP::Syntax]
103
+ # @param syntax [Syntax]
50
104
  # @param arguments [Array<String>]
51
105
  # @param opts [Hash]
52
- # @return [TermUtils::AP::Result]
53
- # @raise [TermUtils::AP::ParseError]
54
- # @raise [TermUtils::AP::SyntaxError]
55
- def self.parse_arguments(syntax, arguments, opts = {})
56
- TermUtils::AP::Parser.new.parse_arguments(syntax, arguments, opts)
106
+ # @option opts [Boolean] :strict Whether the Syntax must be considered as strict.
107
+ # @return [Result]
108
+ # @raise [ParseError]
109
+ # @raise [SyntaxError]
110
+ def self.parse_arguments(syntax, arguments, opts = {}, &block)
111
+ TermUtils::AP::Parser.new.parse_arguments(syntax, arguments, opts, &block)
57
112
  end
58
113
  end
59
114
  end
@@ -1,6 +1,6 @@
1
- # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2023 Thomas Baron
4
4
  #
5
5
  # This file is part of term_utils.
6
6
  #
@@ -15,28 +15,40 @@
15
15
  #
16
16
  # You should have received a copy of the GNU General Public License
17
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
18
19
  module TermUtils
19
- # The ff module provides a way to find files.
20
20
  module FF
21
21
  # Represents a query configuration.
22
22
  class Config
23
23
  # @return [Array<Regexp>]
24
24
  attr_accessor :ignore_list
25
- # @return [Integer]
25
+ # @return [Integer, nil]
26
26
  attr_accessor :min_depth
27
- # @return [Integer]
27
+ # @return [Integer, nil]
28
28
  attr_accessor :max_depth
29
- # @return [Boolean]
30
- attr_accessor :sorted
29
+ # @return [Symbol, nil] Either `:forward`, `:reverse` or `nil` (default).
30
+ attr_accessor :sorting_mode
31
+ # @return [Proc, nil]
32
+ attr_accessor :sorting_compare
33
+
34
+ # Constructs a new Config.
31
35
  def initialize
32
36
  @ignore_list = []
33
37
  @min_depth = nil
34
38
  @max_depth = nil
35
- @sorted = false
39
+ @sorting_mode = nil
40
+ @sorting_compare = nil
41
+ end
42
+
43
+ # Returns whether the search is ordered.
44
+ # @return [Boolean]
45
+ def sorted?
46
+ (@sorting_mode == :forward) || (@sorting_mode == :reverse)
36
47
  end
48
+
37
49
  def initialize_copy(other)
38
- @ignore_list = other.ignore_list.dup
39
50
  super
51
+ @ignore_list = @ignore_list.dup
40
52
  end
41
53
  end
42
54
  end
@@ -1,6 +1,6 @@
1
- # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2023 Thomas Baron
4
4
  #
5
5
  # This file is part of term_utils.
6
6
  #
@@ -15,12 +15,30 @@
15
15
  #
16
16
  # You should have received a copy of the GNU General Public License
17
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
18
19
  module TermUtils
19
- module AP
20
- # No such value error.
21
- class NoSuchValueError < StandardError
22
- def initialize(msg)
23
- super
20
+ module FF
21
+ # Represents an Entry of a Query result.
22
+ class Entry
23
+ # @return [Integer]
24
+ attr_accessor :index
25
+ # @return [String]
26
+ attr_accessor :name
27
+ # @return [Array<String>]
28
+ attr_accessor :relative_path_comps
29
+ # @return [String]
30
+ attr_accessor :path
31
+
32
+ def initialize
33
+ @index = nil
34
+ @name = nil
35
+ @relative_path_comps = nil
36
+ @path = nil
37
+ end
38
+
39
+ # @return [Integer]
40
+ def depth
41
+ @relative_path_comps.length
24
42
  end
25
43
  end
26
44
  end
@@ -0,0 +1,255 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2023 Thomas Baron
4
+ #
5
+ # This file is part of term_utils.
6
+ #
7
+ # term_utils is free software: you can redistribute it and/or modify
8
+ # it under the terms of the GNU General Public License as published by
9
+ # the Free Software Foundation, version 3 of the License.
10
+ #
11
+ # term_utils is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ module TermUtils
20
+ module FF
21
+ # Represents filesystem Entry.
22
+ class FinderEntry
23
+ # @return [Integer]
24
+ attr_accessor :index
25
+ # @return [String]
26
+ attr_accessor :name
27
+ # Identifies the type of stat. The return string is one of: “file”,
28
+ # “directory”, “characterSpecial”, “blockSpecial”, “fifo”, “link”,
29
+ # “socket”, or “unknown”.
30
+ # @return [String]
31
+ attr_accessor :kind
32
+ # Returns the numeric user id of the owner of stat.
33
+ # @return [Integer]
34
+ attr_accessor :uid
35
+ # Returns the numeric group id of the owner of stat.
36
+ # @return [Integer]
37
+ attr_accessor :gid
38
+ # Returns an integer representing the permission bits of stat. The meaning
39
+ # of the bits is platform dependent.
40
+ # @return [Integer]
41
+ attr_accessor :mode
42
+ # Returns the size of stat in bytes.
43
+ # @return [Integer]
44
+ attr_accessor :size
45
+ # @return [Array<String>]
46
+ attr_accessor :path_parts
47
+ # @return [String]
48
+ attr_accessor :path
49
+
50
+ def initialize
51
+ @index = nil
52
+ @name = nil
53
+ @kind = nil
54
+ @path_parts = nil
55
+ @path = nil
56
+ end
57
+
58
+ def directory?
59
+ @kind == 'directory'
60
+ end
61
+
62
+ def file?
63
+ @kind == 'file'
64
+ end
65
+
66
+ # @return [Integer]
67
+ def depth
68
+ @path_parts.length
69
+ end
70
+ end
71
+
72
+ # Represents a Query.
73
+ class FinderQuery
74
+ # @return [Integer]
75
+ attr_accessor :min_depth
76
+ # @return [Integer, nil]
77
+ attr_accessor :max_depth
78
+ # @return [Boolean]
79
+ attr_accessor :use_stat
80
+ # @return [String, Array<String>, nil]
81
+ attr_accessor :entry_kind
82
+
83
+ def initialize
84
+ @min_depth = 0
85
+ @max_depth = nil
86
+ @use_stat = false
87
+ @entry_kind = nil
88
+ @filters = []
89
+ end
90
+
91
+ def filter(kind, &block)
92
+ raise StandardError, 'wrong filter kind' unless %i[enter skip include exclude].include?(kind)
93
+ raise StandardError, "missing #{kind} block" if block.nil?
94
+
95
+ @filters << { kind: kind, block: block }
96
+ end
97
+
98
+ # Wether to enter a directory.
99
+ def enter_directory(&block)
100
+ filter(:enter, &block)
101
+ end
102
+
103
+ # Wether not to enter a directory.
104
+ def skip_directory(&block)
105
+ filter(:skip, &block)
106
+ end
107
+
108
+ # Wether to include an entry into the results.
109
+ def include_entry(&block)
110
+ filter(:include, &block)
111
+ end
112
+
113
+ # Wether to exclue an entry from the results.
114
+ def exclude_entry(&block)
115
+ filter(:exclude, &block)
116
+ end
117
+
118
+ def each_filter(&block)
119
+ @filters.each(&block)
120
+ end
121
+ end
122
+
123
+ # Represents the find method engine.
124
+ class Finder
125
+ attr_reader :query
126
+
127
+ def initialize(paths)
128
+ @paths = paths.dup
129
+ @query = FinderQuery.new
130
+ end
131
+
132
+ def exec
133
+ @paths.each_with_object([]) do |path, obj|
134
+ ctx = { entries: obj, basedir: path }
135
+ list_start(ctx)
136
+ end
137
+ end
138
+
139
+ def list_start(ctx)
140
+ entry = FinderEntry.new.tap do |e|
141
+ e.index = ctx[:entries].length
142
+ e.name = File.basename(ctx[:basedir])
143
+ if @query.use_stat
144
+ st = File.stat(ctx[:basedir])
145
+ e.kind = st.ftype
146
+ e.uid = st.uid
147
+ e.gid = st.gid
148
+ e.mode = st.mode
149
+ e.size = st.size
150
+ end
151
+ e.path_parts = []
152
+ e.path = ctx[:basedir]
153
+ end
154
+ ctx[:entries] << entry if match?(entry) && include?(entry)
155
+ return unless enter?(entry)
156
+
157
+ list(ctx)
158
+ end
159
+
160
+ def list(ctx)
161
+ path_parts = ctx.fetch(:path_parts, [])
162
+ absolute_path =
163
+ if path_parts.empty?
164
+ ctx[:basedir]
165
+ else
166
+ "#{ctx[:basedir]}/#{path_parts.join('/')}"
167
+ end
168
+ entries = Dir.entries(absolute_path)
169
+ entries.each do |name|
170
+ next if %w[. ..].include?(name)
171
+
172
+ entry = FinderEntry.new.tap do |e|
173
+ e.index = ctx[:entries].length
174
+ e.name = name
175
+ if @query.use_stat
176
+ st = File.stat("#{absolute_path}/#{name}")
177
+ e.kind = st.ftype
178
+ e.uid = st.uid
179
+ e.gid = st.gid
180
+ e.mode = st.mode
181
+ e.size = st.size
182
+ end
183
+ e.path_parts = path_parts.dup + [name]
184
+ e.path = "#{ctx[:basedir]}/#{e.path_parts.join('/')}"
185
+ end
186
+ ctx[:entries] << entry if match?(entry) && include?(entry)
187
+ if enter?(entry)
188
+ list(ctx.merge({ path_parts: entry.path_parts }))
189
+ end
190
+ end
191
+ end
192
+
193
+ def match?(entry)
194
+ # (1of2) Depth
195
+ return false if entry.depth < @query.min_depth
196
+ return false if !@query.max_depth.nil? && entry.depth > @query.max_depth
197
+
198
+ # (2of2) Entry kind.
199
+ if @query.entry_kind.nil?
200
+ true
201
+ elsif @query.entry_kind.is_a?(String)
202
+ entry.kind == @query.entry_kind
203
+ elsif @query.entry_kind.is_a?(Array)
204
+ @query.entry_kind.include?(entry.kind)
205
+ else
206
+ false
207
+ end
208
+ end
209
+
210
+ def enter?(entry)
211
+ unless @query.use_stat
212
+ return File.directory?(entry.path)
213
+ end
214
+
215
+ return false unless entry.directory?
216
+
217
+ @query.each_filter do |f|
218
+ case f[:kind]
219
+ when :enter
220
+ return true if f[:block].call(entry)
221
+ when :skip
222
+ return false if f[:block].call(entry)
223
+ end
224
+ end
225
+ true
226
+ end
227
+
228
+ def include?(entry)
229
+ @query.each_filter do |f|
230
+ case f[:kind]
231
+ when :include
232
+ return true if f[:block].call(entry)
233
+ when :exclude
234
+ return false if f[:block].call(entry)
235
+ end
236
+ end
237
+ true
238
+ end
239
+ end
240
+
241
+ # Finds files.
242
+ # @param paths [Array<String>]
243
+ # @return [Array<Hash>]
244
+ def self.find(*paths, &block)
245
+ fdr =
246
+ if paths.empty?
247
+ TermUtils::FF::Finder.new(['.'])
248
+ else
249
+ TermUtils::FF::Finder.new(paths)
250
+ end
251
+ block&.call(fdr.query)
252
+ fdr.exec
253
+ end
254
+ end
255
+ end
@@ -1,6 +1,6 @@
1
- # frozen-string-literal: true
2
- #
3
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2023 Thomas Baron
4
4
  #
5
5
  # This file is part of term_utils.
6
6
  #
@@ -15,51 +15,128 @@
15
15
  #
16
16
  # You should have received a copy of the GNU General Public License
17
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
18
19
  module TermUtils
19
- # The ff module provides a way to find files.
20
20
  module FF
21
+ # Query search context.
22
+ Context = Struct.new('Context', :config, :base_path, :block, :index_seq, :result)
21
23
  # Represents a file system query.
22
24
  class Query
25
+ # Constructs a new Query.
23
26
  def initialize
24
27
  @config = TermUtils::FF::Config.new
25
28
  end
29
+
26
30
  def initialize_copy(other)
27
- @config = other.config.dup
28
31
  super
32
+ @config = @config.dup
29
33
  end
30
- # Adds a Regexp to ignore.
34
+
35
+ # Adds a Regexp for ignoring file names.
31
36
  # @param regexp [Regexp]
32
- # @return [TermUtils::FF::Query]
37
+ # @return [Query]
33
38
  def ignore(regexp)
34
39
  @config.ignore_list << regexp
35
40
  self
36
41
  end
42
+
37
43
  # Sets a minimum depth.
38
44
  # @param depth [Integer]
39
- # @return [TermUtils::FF::Query]
45
+ # @return [Query]
40
46
  def min_depth(depth)
41
47
  @config.min_depth = depth
42
48
  self
43
49
  end
50
+
44
51
  # Sets a maximum depth.
45
52
  # @param depth [Integer]
46
- # @return [TermUtils::FF::Query]
53
+ # @return [Query]
47
54
  def max_depth(depth)
48
55
  @config.max_depth = depth
49
56
  self
50
57
  end
51
- # Sets whether results shall be sorted.
52
- # @param sorted [Boolean]
53
- # @return [TermUtils::FF::Query]
54
- def sort(sorted = true)
55
- @config.sorted = sorted
58
+
59
+ # Sets the sorting mode.
60
+ # @param mode [Symbol] Either `:forward`, `:reverse` or `nil` (default).
61
+ # @return [Query]
62
+ def sort(mode = :forward, &block)
63
+ @config.sorting_mode = mode
64
+ @config.sorting_compare = block
56
65
  self
57
66
  end
67
+
58
68
  # Executes this one.
59
69
  # @param path [String]
60
- # @return [TermUtils::FF::Cursor]
61
- def exec(path)
62
- TermUtils::FF::Cursor.new(@config.dup, path).bootstrap
70
+ # @return [Array<Entry>]
71
+ def exec(path, &block)
72
+ ctx = Context.new
73
+ ctx.config = @config.dup
74
+ ctx.config.min_depth = 0 if ctx.config.min_depth.nil?
75
+ ctx.base_path = path
76
+ ctx.block = block
77
+ ctx.index_seq = 0
78
+ ctx.result = []
79
+ first_entry = TermUtils::FF::Entry.new
80
+ first_entry.name = path
81
+ first_entry.relative_path_comps = []
82
+ first_entry.path = path
83
+ if ctx.config.min_depth == 0
84
+ first_entry.index = ctx.index_seq
85
+ ctx.index_seq += 1
86
+ ctx.block.call(first_entry) if ctx.block
87
+ ctx.result << first_entry
88
+ end
89
+ if File.directory?(first_entry.path) && (ctx.config.max_depth.nil? || ctx.config.max_depth > 0)
90
+ TermUtils::FF::Query.search(ctx, first_entry)
91
+ end
92
+ ctx.result
93
+ end
94
+
95
+ # Searches a directory.
96
+ # @param ctx [Context]
97
+ # @param parent_entry [entry]
98
+ # @return [nil]
99
+ def self.search(ctx, parent_entry)
100
+ entries = Dir.entries(parent_entry.path)
101
+ unless ctx.config.sorting_mode.nil?
102
+ if ctx.config.sorting_compare.nil?
103
+ entries.sort!
104
+ else
105
+ entries.sort!(&ctx.config.sorting_compare)
106
+ end
107
+ if ctx.config.sorting_mode == :reverse
108
+ entries.reverse!
109
+ end
110
+ end
111
+ entries.each do |name|
112
+ next if %w[. ..].include? name
113
+ next unless accept_entry_name?(ctx, name)
114
+
115
+ new_entry = TermUtils::FF::Entry.new
116
+ new_entry.name = name
117
+ new_entry.relative_path_comps = parent_entry.relative_path_comps.dup.push(name)
118
+ new_entry.path = "#{ctx.base_path}/#{new_entry.relative_path_comps.join('/')}"
119
+ if ctx.config.min_depth <= new_entry.depth
120
+ new_entry.index = ctx.index_seq
121
+ ctx.index_seq += 1
122
+ ctx.block.call(new_entry) if ctx.block
123
+ ctx.result << new_entry
124
+ end
125
+ if File.directory?(new_entry.path) && (ctx.config.max_depth.nil? || ctx.config.max_depth > new_entry.depth)
126
+ TermUtils::FF::Query.search(ctx, new_entry)
127
+ end
128
+ end
129
+ end
130
+
131
+ # Tests whether a given entry should be accepted.
132
+ # @param ctx [Context]
133
+ # @param name [String]
134
+ # @return [Boolean]
135
+ def self.accept_entry_name?(ctx, name)
136
+ ctx.config.ignore_list.each do |e|
137
+ return false if e.match?(name)
138
+ end
139
+ true
63
140
  end
64
141
  end
65
142
  end
data/lib/term_utils/ff.rb CHANGED
@@ -1,4 +1,6 @@
1
- # Copyright (C) 2019 Thomas Baron
1
+ # frozen_string_literal: true
2
+
3
+ # Copyright (C) 2023 Thomas Baron
2
4
  #
3
5
  # This file is part of term_utils.
4
6
  #
@@ -13,6 +15,14 @@
13
15
  #
14
16
  # You should have received a copy of the GNU General Public License
15
17
  # along with term_utils. If not, see <https://www.gnu.org/licenses/>.
18
+
19
+ module TermUtils
20
+ # Provides ways to find files.
21
+ module FF
22
+ end
23
+ end
24
+
16
25
  require 'term_utils/ff/config'
17
- require 'term_utils/ff/cursor'
26
+ require 'term_utils/ff/entry'
18
27
  require 'term_utils/ff/query'
28
+ require 'term_utils/ff/finder'