term_utils 0.3.2 → 0.5.0

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