na 1.2.84 → 1.2.85

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1bdcec170985481e8d49edf6e599f0d1fba33bd250fcae7d0e900e24d3f8d8e4
4
- data.tar.gz: 7e9d815b0dee8abed829216771c8d4177a49629f37b54f76ff2000fede7172d9
3
+ metadata.gz: da5eb39ed44c356c713172455a60fe197de3d1d1f00dc2eedadeb24fe40c9dde
4
+ data.tar.gz: 5e58a4e4c17fac35a091b3cc49e7dca7aca06289b1fbc897be752bb4cdb93817
5
5
  SHA512:
6
- metadata.gz: c45055a0a92ad0cbcfcf670a9717b585a5fa0c2ddad5ffe84644a4a64fe3df564f7d0683fd9be47942c3d829f3148be1836039a57b82d108518a63b3d1de1e28
7
- data.tar.gz: 97d7a6616eae0ea5b967068a28154717aafd4c5f37aca62064bf86d2a82f719fe941aaacf98dd1158250e83d5d332f296eb99cb5d39d751852fc071e461d7ac7
6
+ metadata.gz: 053e5636721b5c9544e22099840ef279462cbba96bd8f561c6a37c3f5b5453d7872bf549da8e8f78009f2c0a417805711ee3c7eebe2dfdf05538bbad84f7aafc
7
+ data.tar.gz: 4626f2c6808056d9de394bd1eec109c0c56a8622015157ecc95bdf555523729254735550a775ae4542b5c20023d7bfc152152db0cf042661e07e5ec6ef1bc9ef
data/.rubocop_todo.yml CHANGED
@@ -1,6 +1,6 @@
1
1
  # This configuration was generated by
2
2
  # `rubocop --auto-gen-config`
3
- # on 2025-10-25 14:39:51 UTC using RuboCop version 1.75.7.
3
+ # on 2025-10-26 12:18:27 UTC using RuboCop version 1.75.7.
4
4
  # The point is for the user to remove these configuration records
5
5
  # one by one as the offenses are removed from the code base.
6
6
  # Note that changes in the inspected code, or installation of new
@@ -13,12 +13,7 @@ Lint/DuplicateBranch:
13
13
  - 'lib/na/action.rb'
14
14
  - 'lib/na/colors.rb'
15
15
 
16
- # Offense count: 1
17
- Lint/SelfAssignment:
18
- Exclude:
19
- - 'lib/na/string.rb'
20
-
21
- # Offense count: 1
16
+ # Offense count: 2
22
17
  # This cop supports safe autocorrection (--autocorrect).
23
18
  # Configuration parameters: AutoCorrect, AllowUnusedKeywordArguments, IgnoreEmptyMethods, IgnoreNotImplementedMethods, NotImplementedExceptions.
24
19
  # NotImplementedExceptions: NotImplementedError
@@ -40,9 +35,9 @@ Metrics/BlockLength:
40
35
  # Offense count: 4
41
36
  # Configuration parameters: CountComments, CountAsOne.
42
37
  Metrics/ClassLength:
43
- Max: 762
38
+ Max: 763
44
39
 
45
- # Offense count: 22
40
+ # Offense count: 23
46
41
  # Configuration parameters: AllowedMethods, AllowedPatterns.
47
42
  Metrics/CyclomaticComplexity:
48
43
  Max: 66
@@ -55,14 +50,14 @@ Metrics/MethodLength:
55
50
  # Offense count: 2
56
51
  # Configuration parameters: CountComments, CountAsOne.
57
52
  Metrics/ModuleLength:
58
- Max: 764
53
+ Max: 765
59
54
 
60
55
  # Offense count: 4
61
56
  # Configuration parameters: CountKeywordArgs, MaxOptionalParameters.
62
57
  Metrics/ParameterLists:
63
58
  Max: 19
64
59
 
65
- # Offense count: 21
60
+ # Offense count: 22
66
61
  # Configuration parameters: AllowedMethods, AllowedPatterns.
67
62
  Metrics/PerceivedComplexity:
68
63
  Max: 76
data/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ### 1.2.85
2
+
3
+ 2025-10-26 08:43
4
+
5
+ #### IMPROVED
6
+
7
+ - YARD documentation
8
+ - Currently at 100% YARD documentation
9
+
10
+ #### FIXED
11
+
12
+ - Nil handler for trunc_middle
13
+ - Nil handler for hihglight_file
14
+
1
15
  ### 1.2.84
2
16
 
3
17
  2025-10-25 15:50
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- na (1.2.84)
4
+ na (1.2.85)
5
5
  chronic (~> 0.10, >= 0.10.2)
6
6
  git (~> 3.0.0)
7
7
  gli (~> 2.21.0)
data/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  _If you're one of the rare people like me who find this useful, feel free to
10
10
  [buy me some coffee][donate]._
11
11
 
12
- The current version of `na` is 1.2.84.
12
+ The current version of `na` is 1.2.85.
13
13
 
14
14
  `na` ("next action") is a command line tool designed to make it easy to see what your next actions are for any project, right from the command line. It works with TaskPaper-formatted files (but any plain text format will do), looking for `@na` tags (or whatever you specify) in todo files in your current folder.
15
15
 
@@ -76,7 +76,7 @@ SYNOPSIS
76
76
  na [global options] command [command options] [arguments...]
77
77
 
78
78
  VERSION
79
- 1.2.84
79
+ 1.2.85
80
80
 
81
81
  GLOBAL OPTIONS
82
82
  -a, --add - Add a next action (deprecated, for backwards compatibility)
data/lib/na/action.rb CHANGED
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NA
4
+ # Represents a single actionable item in a todo file, with tags, notes, and project context.
5
+ #
6
+ # @example Create a new action
7
+ # action = NA::Action.new('todo.txt', 'Inbox', [], '- Buy milk', 1)
4
8
  class Action < Hash
5
9
  attr_reader :file, :project, :tags, :line
6
10
  attr_accessor :parent, :action, :note
7
11
 
12
+ # @example
13
+ # action = NA::Action.new('todo.txt', 'Inbox', [], '- Buy milk', 1)
8
14
  def initialize(file, project, parent, action, idx, note = [])
9
15
  super()
10
16
 
@@ -25,6 +31,8 @@ module NA
25
31
  # @param remove_tag [Array<String>] Tags to remove
26
32
  # @param note [Array<String>] Notes to set
27
33
  # @return [void]
34
+ # @example
35
+ # action.process(priority: 5, finish: true, add_tag: ['urgent'], remove_tag: ['waiting'], note: ['Call Bob'])
28
36
  def process(priority: 0, finish: false, add_tag: [], remove_tag: [], note: [])
29
37
  string = @action.dup
30
38
 
@@ -54,6 +62,8 @@ module NA
54
62
  # String representation of the action
55
63
  #
56
64
  # @return [String]
65
+ # @example
66
+ # action.to_s #=> "{ project: 'Inbox', ... }"
57
67
  def to_s
58
68
  note = if @note.count.positive?
59
69
  "\n#{@note.join("\n")}"
@@ -127,7 +137,7 @@ module NA
127
137
 
128
138
  # Create the source filename string (optimized)
129
139
  filename = if needs_filename
130
- path = @file.sub(%r{^\./}, '').sub(/#{Dir.home}/, '~')
140
+ path = @file ? @file.sub(%r{^\./}, '').sub(/#{Dir.home}/, '~') : ''
131
141
  if File.dirname(path) == '.'
132
142
  fname = NA.include_ext ? File.basename(path) : File.basename(path, ".#{extension}")
133
143
  fname = "./#{fname}" if NA.show_cwd_indicator
data/lib/na/array.rb CHANGED
@@ -1,10 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ##
4
+ # Extensions to Ruby's Array class for todo management and formatting.
5
+ #
6
+ # @example Remove bad elements from an array
7
+ # ['foo', '', nil, 0, false, 'bar'].remove_bad #=> ['foo', 'bar']
3
8
  class ::Array
4
9
  # Like Array#compact -- removes nil items, but also
5
10
  # removes empty strings, zero or negative numbers and FalseClass items
6
11
  #
7
12
  # @return [Array] Array without "bad" elements
13
+ # @example
14
+ # ['foo', '', nil, 0, false, 'bar'].remove_bad #=> ['foo', 'bar']
8
15
  def remove_bad
9
16
  compact.map { |x| x.is_a?(String) ? x.strip : x }.select(&:good?)
10
17
  end
@@ -15,6 +22,8 @@ class ::Array
15
22
  # @param indent [Integer] Indentation spaces
16
23
  # @param color [String] Color code to apply
17
24
  # @return [Array, String] Wrapped and colorized lines
25
+ # @example
26
+ # ['foo', 'bar'].wrap(80, 2, '{g}') #=> "\n{g} • foo{x}\n{g} • bar{x}"
18
27
  def wrap(width, indent, color)
19
28
  return map { |l| "#{color} #{l.wrap(width, 2)}" } if width < 60
20
29
 
data/lib/na/benchmark.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NA
4
+ # Provides benchmarking utilities for measuring code execution time.
5
+ #
6
+ # @example Measure a block of code
7
+ # NA::Benchmark.measure('sleep') { sleep(1) }
4
8
  module Benchmark
5
9
  class << self
6
10
  attr_accessor :enabled, :timings
@@ -18,6 +22,8 @@ module NA
18
22
  #
19
23
  # @param label [String] Label for the measurement
20
24
  # @return [Object] Result of the block
25
+ # @example
26
+ # NA::Benchmark.measure('sleep') { sleep(1) }
21
27
  def measure(label)
22
28
  return yield unless @enabled
23
29
 
@@ -31,6 +37,8 @@ module NA
31
37
  # Output a performance report to STDERR
32
38
  #
33
39
  # @return [void]
40
+ # @example
41
+ # NA::Benchmark.report
34
42
  def report
35
43
  return unless @enabled
36
44
 
data/lib/na/colors.rb CHANGED
@@ -3,6 +3,9 @@
3
3
  # Cribbed from <https://github.com/flori/term-ansicolor>
4
4
  module NA
5
5
  # Terminal output color functions.
6
+ #
7
+ # @example Clear the color template cache
8
+ # NA::Color.clear_template_cache
6
9
  module Color
7
10
  # Regexp to match excape sequences
8
11
  ESCAPE_REGEX = /(?<=\[)(?:(?:(?:[349]|10)[0-9]|[0-9])?;?)+(?=m)/.freeze
@@ -199,6 +202,10 @@ module NA
199
202
  @template_cache ||= {}
200
203
  end
201
204
 
205
+ # Clears the cached compiled color templates.
206
+ # @return [void]
207
+ # @example
208
+ # NA::Color.clear_template_cache
202
209
  def clear_template_cache
203
210
  @template_cache = {}
204
211
  end
@@ -231,15 +238,15 @@ module NA
231
238
  # i: italic, x: reset (remove background, color,
232
239
  # emphasis)
233
240
  #
234
- # Also accepts {#RGB} and {#RRGGBB} strings. Put a b
241
+ # Also accepts `{#RGB}` and `{#RRGGBB}` strings. Put a b
235
242
  # before the hash to make it a background color
236
243
  #
237
244
  # @example Convert a templated string
238
- # Color.template('{Rwb}Warning:{x} {w}you look a
239
- # little {g}ill{x}')
245
+ # Color.template('\\{Rwb\\}Warning:\\{x\\} \\{w\}\you look a
246
+ # little \\{g\\}ill\\{x\\}')
240
247
  #
241
248
  # @example Convert using RGB colors
242
- # Color.template('{#f0a}This is an RGB color')
249
+ # Color.template('\\{#f0a\\}This is an RGB color')
243
250
  #
244
251
  # @param input [String, Array] The template
245
252
  # string. If this is an array, the
data/lib/na/editor.rb CHANGED
@@ -3,8 +3,12 @@
3
3
  require 'English'
4
4
 
5
5
  module NA
6
+ # Provides editor selection and argument helpers for launching text editors.
6
7
  module Editor
7
8
  class << self
9
+ # Returns the default editor command, checking environment variables and available editors.
10
+ # @param prefer_git_editor [Boolean] Prefer GIT_EDITOR over EDITOR
11
+ # @return [String, nil] Editor command or nil if not found
8
12
  def default_editor(prefer_git_editor: true)
9
13
  editor ||= if prefer_git_editor
10
14
  ENV['NA_EDITOR'] || ENV['GIT_EDITOR'] || ENV.fetch('EDITOR', nil)
@@ -29,10 +33,15 @@ module NA
29
33
  nil
30
34
  end
31
35
 
36
+ # Returns the default editor command with its arguments.
37
+ # @return [String] Editor command with arguments
32
38
  def editor_with_args
33
39
  args_for_editor(default_editor)
34
40
  end
35
41
 
42
+ # Returns the editor command with appropriate arguments for file opening.
43
+ # @param editor [String] Editor command
44
+ # @return [String] Editor command with arguments
36
45
  def args_for_editor(editor)
37
46
  return editor if editor =~ /-\S/
38
47
 
data/lib/na/hash.rb CHANGED
@@ -1,9 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ##
4
+ # Extensions to Ruby's Hash class for symbolizing keys and deep freezing values.
5
+ #
6
+ # @example Symbolize all keys in a hash
7
+ # { 'foo' => 1, 'bar' => { 'baz' => 2 } }.symbolize_keys #=> { :foo => 1, :bar => { :baz => 2 } }
3
8
  class ::Hash
4
9
  # Convert all keys in the hash to symbols recursively
5
10
  #
6
11
  # @return [Hash] Hash with symbolized keys
12
+ # @example
13
+ # { 'foo' => 1, 'bar' => { 'baz' => 2 } }.symbolize_keys #=> { :foo => 1, :bar => { :baz => 2 } }
7
14
  def symbolize_keys
8
15
  each_with_object({}) { |(k, v), hsh| hsh[k.to_sym] = v.is_a?(Hash) ? v.symbolize_keys : v }
9
16
  end
@@ -12,6 +19,8 @@ class ::Hash
12
19
  # Freeze all values in a hash
13
20
  #
14
21
  # @return Hash with all values frozen
22
+ # @example
23
+ # { foo: { bar: 'baz' } }.deep_freeze
15
24
  def deep_freeze
16
25
  chilled = {}
17
26
  each do |k, v|
@@ -1,17 +1,26 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ##
4
+ # Monkeypatches for GLI CLI framework to support paginated help output.
5
+ #
6
+ # @example Show help for a command
7
+ # GLI::Commands::Help.new.show_help({}, {}, [], $stdout, $stderr)
3
8
  module GLI
9
+ ##
10
+ # Command extensions for GLI CLI framework.
4
11
  module Commands
5
12
  # Help Command Monkeypatch for paginated output
6
13
  class Help < Command
7
14
  # Show help output for GLI commands with paginated output
8
15
  #
9
- # @param global_options [Hash] Global CLI options
16
+ # @param _global_options [Hash] Global CLI options
10
17
  # @param options [Hash] Command-specific options
11
18
  # @param arguments [Array] Command arguments
12
19
  # @param out [IO] Output stream
13
20
  # @param error [IO] Error stream
14
21
  # @return [void]
22
+ # @example
23
+ # GLI::Commands::Help.new.show_help({}, {}, [], $stdout, $stderr)
15
24
  def show_help(_global_options, options, arguments, out, error)
16
25
  NA::Pager.paginate = true
17
26
 
@@ -8,10 +8,17 @@ module NA
8
8
  attr_accessor :verbose, :extension, :include_ext, :na_tag, :command_line, :command, :globals, :global_file,
9
9
  :cwd_is, :cwd, :stdin, :show_cwd_indicator
10
10
 
11
+ # Returns the current theme hash for color and style settings.
12
+ # @return [Hash] The theme settings
11
13
  def theme
12
14
  @theme ||= NA::Theme.load_theme
13
15
  end
14
16
 
17
+ # Print a message to stderr, optionally exit or debug.
18
+ # @param msg [String] The message to print
19
+ # @param exit_code [Integer, Boolean] Exit code or false for no exit
20
+ # @param debug [Boolean] Only print if verbose
21
+ # @return [void]
15
22
  def notify(msg, exit_code: false, debug: false)
16
23
  return if debug && !NA.verbose
17
24
 
@@ -23,6 +30,8 @@ module NA
23
30
  Process.exit exit_code if exit_code
24
31
  end
25
32
 
33
+ # Returns a map of priority levels to numeric values.
34
+ # @return [Hash{String=>Integer}] Priority mapping
26
35
  def priority_map
27
36
  {
28
37
  'h' => 5,
@@ -128,6 +137,11 @@ module NA
128
137
  end
129
138
  end
130
139
 
140
+ # Shift project indices after a given index by a length.
141
+ # @param projects [Array<NA::Project>] Projects to shift
142
+ # @param idx [Integer] Index after which to shift
143
+ # @param length [Integer] Amount to shift
144
+ # @return [Array<NA::Project>] Shifted projects
131
145
  def shift_index_after(projects, idx, length = 1)
132
146
  projects.map do |proj|
133
147
  proj.line = proj.line - length if proj.line > idx
@@ -194,9 +208,8 @@ module NA
194
208
  #
195
209
  # @param target [String] Path to the todo file
196
210
  # @param project [String] Project name
197
- # @param projects [Array<NA::Project>] Existing projects
198
211
  # @return [NA::Project] The new project
199
- def insert_project(target, project, _projects)
212
+ def insert_project(target, project)
200
213
  path = project.split(%r{[:/]})
201
214
  todo = NA::Todo.new(file_path: target)
202
215
  built = []
@@ -602,6 +615,19 @@ module NA
602
615
  end
603
616
  end
604
617
 
618
+ # Find files matching criteria and containing actions.
619
+ # @param options [Hash] Options for file search
620
+ # @option options [Integer] :depth Search depth
621
+ # @option options [Boolean] :done Include done actions
622
+ # @option options [String] :file_path File path
623
+ # @option options [Boolean] :negate Negate search
624
+ # @option options [Boolean] :hidden Include hidden files
625
+ # @option options [String] :project Project name
626
+ # @option options [String] :query Query string
627
+ # @option options [Boolean] :regex Use regex
628
+ # @option options [String] :search Search string
629
+ # @option options [String] :tag Tag to filter
630
+ # @return [Array<String>] Matching files
605
631
  def find_files_matching(options = {})
606
632
  defaults = {
607
633
  depth: 1,
@@ -690,6 +716,10 @@ module NA
690
716
  end
691
717
  end
692
718
 
719
+ # Find a directory with an exact match from a list.
720
+ # @param dirs [Array<String>] Directories to search
721
+ # @param search [Array<Hash>] Search tokens
722
+ # @return [Array<String>] Matching directories
693
723
  def find_exact_dir(dirs, search)
694
724
  terms = search.filter { |s| !s[:negate] }.map { |t| t[:token] }.join(' ')
695
725
  out = dirs
@@ -878,6 +908,12 @@ module NA
878
908
  File.open(file, 'w') { |f| f.puts dirs.join("\n") }
879
909
  end
880
910
 
911
+ # List projects in a todo file or matching query.
912
+ # @param query [Array] Query tokens
913
+ # @param file_path [String, nil] File path
914
+ # @param depth [Integer] Search depth
915
+ # @param paths [Boolean] Show full paths
916
+ # @return [void]
881
917
  def list_projects(query: [], file_path: nil, depth: 1, paths: true)
882
918
  files = if NA.global_file
883
919
  [NA.global_file]
@@ -906,6 +942,9 @@ module NA
906
942
  end
907
943
  end
908
944
 
945
+ # List todo files matching a query.
946
+ # @param query [Array] Query tokens
947
+ # @return [void]
909
948
  def list_todos(query: [])
910
949
  dirs = if query
911
950
  match_working_dir(query, distance: 2, require_last: false)
@@ -922,6 +961,10 @@ module NA
922
961
  puts NA::Color.template(dirs.join("\n"))
923
962
  end
924
963
 
964
+ # Save a search definition to the database.
965
+ # @param title [String] The search title
966
+ # @param search [String] The search string
967
+ # @return [void]
925
968
  def save_search(title, search)
926
969
  file = database_path(file: 'saved_searches.yml')
927
970
  searches = load_searches
data/lib/na/project.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NA
4
+ # Represents a project section in a todo file, with indentation and line tracking.
5
+ #
6
+ # @example Create a new project
7
+ # project = NA::Project.new('Inbox', 0, 1, 5)
4
8
  class Project < Hash
5
9
  attr_accessor :project, :indent, :line, :last_line
6
10
 
@@ -11,6 +15,8 @@ module NA
11
15
  # @param line [Integer] Starting line number
12
16
  # @param last_line [Integer] Ending line number
13
17
  # @return [void]
18
+ # @example
19
+ # project = NA::Project.new('Inbox', 0, 1, 5)
14
20
  def initialize(project, indent = 0, line = 0, last_line = 0)
15
21
  super()
16
22
  @project = project
@@ -22,6 +28,8 @@ module NA
22
28
  # String representation of the project
23
29
  #
24
30
  # @return [String]
31
+ # @example
32
+ # project.to_s #=> "{ project: 'Inbox', ... }"
25
33
  def to_s
26
34
  { project: @project, indent: @indent, line: @line, last_line: @last_line }.to_s
27
35
  end
data/lib/na/string.rb CHANGED
@@ -1,15 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # Special handling for nil strings
4
+ class ::NilClass
5
+ # Always returns an empty string for nil.
6
+ # @return [String]
7
+ def highlight_filename
8
+ ''
9
+ end
10
+
11
+ # Always returns an empty string for nil.
12
+ # @param max [Integer] Maximum allowed length
13
+ # @return [String]
14
+ def trunc_middle(max)
15
+ ''
16
+ end
17
+ end
18
+
19
+ # Matches day names (e.g., mon, tue, wednesday)
20
+ # @return [Regexp]
3
21
  REGEX_DAY = /^(mon|tue|wed|thur?|fri|sat|sun)(\w+(day)?)?$/i.freeze
22
+
23
+ # Matches clock times (e.g., 12:30 pm, midnight)
24
+ # @return [String]
4
25
  REGEX_CLOCK = '(?:\d{1,2}+(?::\d{1,2}+)?(?: *(?:am|pm))?|midnight|noon)'
26
+
27
+ # Matches time strings using REGEX_CLOCK
28
+ # @return [Regexp]
5
29
  REGEX_TIME = /^#{REGEX_CLOCK}$/i.freeze
6
30
 
7
31
  # String helpers
8
32
  class ::String
9
33
  # Insert a comment character at the start of every line
10
34
  # @param char [String] The character to insert (default #)
11
- def comment(_char = '#')
12
- split("\n").map { |l| "# #{l}" }.join("\n")
35
+ def comment(char = '#')
36
+ split("\n").map { |l| "#{char} #{l}" }.join("\n")
13
37
  end
14
38
 
15
39
  # Tests if object is nil or empty
@@ -25,6 +49,10 @@ class ::String
25
49
  line =~ /^#/ || line.strip.empty?
26
50
  end
27
51
 
52
+ # Returns the contents of the file, or raises if missing.
53
+ # Handles directories and NA extension.
54
+ # @return [String] Contents of the file
55
+ # @raise [RuntimeError] if the file does not exist
28
56
  def read_file
29
57
  file = File.expand_path(self)
30
58
  raise "Missing file #{file}" unless File.exist?(file)
@@ -64,11 +92,15 @@ class ::String
64
92
  !action? && self =~ /:( +@\S+(\([^)]*\))?)*$/
65
93
  end
66
94
 
95
+ # Returns the project name if matched, otherwise nil.
96
+ # @return [String, nil]
67
97
  def project
68
98
  m = match(/^([ \t]*)([^\-][^@:]*?): *(@\S+ *)*$/)
69
99
  m ? m[2] : nil
70
100
  end
71
101
 
102
+ # Returns the action text with leading dash and whitespace removed.
103
+ # @return [String]
72
104
  def action
73
105
  sub(/^[ \t]*- /, '')
74
106
  end
@@ -84,6 +116,8 @@ class ::String
84
116
  # Colorize the dirname and filename of a path
85
117
  # @return [String] Colorized string
86
118
  def highlight_filename
119
+ return '' if nil?
120
+
87
121
  dir = File.dirname(self).shorten_path.trunc_middle(TTY::Screen.columns / 3)
88
122
  file = NA.include_ext ? File.basename(self) : File.basename(self, ".#{NA.extension}")
89
123
  "#{NA.theme[:dirname]}#{dir}/#{NA.theme[:filename]}#{file}{x}"
@@ -135,14 +169,15 @@ class ::String
135
169
  # @param max [Integer] Maximum allowed length of the string
136
170
  # @return [String] Truncated string with middle replaced if necessary
137
171
  def trunc_middle(max)
138
- return '' if self.nil?
139
- return self unless length > max
140
-
141
- half = (max / 2).floor - 3
142
- chars = chars
143
- pre = chars.slice(0, half)
144
- post = chars.reverse.slice(0, half).reverse
145
- "#{pre.join}[...]#{post.join}"
172
+ return '' if nil?
173
+
174
+ return self unless length > max
175
+
176
+ half = (max / 2).floor - 3
177
+ cs = chars
178
+ pre = cs.slice(0, half)
179
+ post = cs.reverse.slice(0, half).reverse
180
+ "#{pre.join}[...]#{post.join}"
146
181
  end
147
182
 
148
183
  # Wrap the string to a given width, indenting each line and preserving tag formatting.
data/lib/na/theme.rb CHANGED
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NA
4
+ # Provides theme and color template helpers for todo CLI output.
5
+ #
6
+ # @example Get theme help text
7
+ # NA::Theme.template_help
4
8
  module Theme
5
9
  class << self
6
10
  # Returns a help string describing available color placeholders for themes.
7
11
  # @return [String] Help text for theme placeholders
12
+ # @example
13
+ # NA::Theme.template_help
8
14
  def template_help
9
15
  <<~EOHELP
10
16
  Use {X} placeholders to apply colors. Available colors are:
@@ -28,6 +34,8 @@ module NA
28
34
  # Writes the help text and theme YAML to the theme file.
29
35
  # @param template [Hash] Additional theme settings to merge
30
36
  # @return [Hash] The merged theme configuration
37
+ # @example
38
+ # NA::Theme.load_theme(template: { action: '{r}' })
31
39
  def load_theme(template: {})
32
40
  NA::Benchmark.measure('Theme.load_theme') do
33
41
  # Default colorization, can be overridden with full or partial template variable
data/lib/na/todo.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NA
4
+ # Represents a parsed todo file, including actions, projects, and file management.
5
+ #
6
+ # @example Parse a todo file
7
+ # todo = NA::Todo.new(file_path: 'todo.txt')
4
8
  class Todo
5
9
  attr_accessor :actions, :projects, :files
6
10
 
@@ -8,6 +12,8 @@ module NA
8
12
  #
9
13
  # @param options [Hash] Options for parsing todo files
10
14
  # @return [void]
15
+ # @example
16
+ # todo = NA::Todo.new(file_path: 'todo.txt')
11
17
  def initialize(options = {})
12
18
  @files, @actions, @projects = parse(options)
13
19
  end
@@ -26,6 +32,8 @@ module NA
26
32
  # @option options [Boolean] :require_na Require @na tag
27
33
  # @option options [String] :file_path file path to parse
28
34
  # @return [Array] files, actions, projects
35
+ # @example
36
+ # files, actions, projects = todo.parse(file_path: 'todo.txt')
29
37
  def parse(options)
30
38
  NA::Benchmark.measure('Todo.parse') do
31
39
  defaults = {
data/lib/na/version.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ ##
4
+ # Main module for the na gem, providing version information.
3
5
  module Na
4
- VERSION = '1.2.84'
6
+ ##
7
+ # Current version of the na gem.
8
+ VERSION = '1.2.85'
5
9
  end
data/src/_README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  _If you're one of the rare people like me who find this useful, feel free to
10
10
  [buy me some coffee][donate]._
11
11
 
12
- The current version of `na` is <!--VER-->1.2.83<!--END VER-->.
12
+ The current version of `na` is <!--VER-->1.2.84<!--END VER-->.
13
13
 
14
14
  `na` ("next action") is a command line tool designed to make it easy to see what your next actions are for any project, right from the command line. It works with TaskPaper-formatted files (but any plain text format will do), looking for `@na` tags (or whatever you specify) in todo files in your current folder.
15
15
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: na
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.84
4
+ version: 1.2.85
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brett Terpstra