todoloo 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/exe/todoloo +4 -0
- data/lib/todoloo/cli.rb +27 -0
- data/lib/todoloo/error_handler.rb +40 -0
- data/lib/todoloo/file_scanner.rb +50 -0
- data/lib/todoloo/helpers.rb +24 -0
- data/lib/todoloo/parser.rb +159 -0
- data/lib/todoloo/task.rb +14 -0
- data/lib/todoloo/task_list/folded_string.rb +13 -0
- data/lib/todoloo/task_list.rb +66 -0
- data/lib/todoloo/transformer.rb +28 -0
- data/lib/todoloo/version.rb +1 -1
- data/lib/todoloo.rb +21 -0
- metadata +98 -6
- data/.ruby-version +0 -1
- data/Makefile +0 -8
- data/sig/todoloo.rbs +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 652fcb62bf1d496280e48cd4d10a1616f4626fdb44c69d3a59976962ae9ab141
|
4
|
+
data.tar.gz: 1dc37022a98ba6c1bd50b3eb790c740cec79f10681e5bafd6f04ed918364f35c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8433b47e15248d606b6c52ede9a9d42096136bd3faac4fb4f24b2698e92c0d6d6003605bfb7972aae479bd7758a24e398c78f39c8141fc4e1b8ea849fc45b35
|
7
|
+
data.tar.gz: 5d34072524e668e5acfaa679a040c271721b824246db544c6e4c45a23225261e0681ffcd73ecae419109d1fd4f02399c42b7cf6b9f13518f383e91ff1a046ad1
|
data/exe/todoloo
ADDED
data/lib/todoloo/cli.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require "thor"
|
2
|
+
|
3
|
+
class Todoloo::CLI < Thor
|
4
|
+
package_name "Todoloo"
|
5
|
+
|
6
|
+
desc "scan", "Scans all files that match the given globs and outputs a tasks.yml"
|
7
|
+
method_option :exclude, type: :array, aliases: "-e", desc: "List of path globs to exclude"
|
8
|
+
def scan
|
9
|
+
Todoloo::FileScanner
|
10
|
+
.new("**/*.rb", excludes: options[:exclude] || [], trace: true)
|
11
|
+
.scan
|
12
|
+
.write("tasks.yml")
|
13
|
+
end
|
14
|
+
|
15
|
+
desc "io", "Reads input from stdio and writes to stdout"
|
16
|
+
def io
|
17
|
+
Todoloo::TaskList.new.add(
|
18
|
+
Todoloo::Parser
|
19
|
+
.new
|
20
|
+
.parse_and_transform($stdin.read)
|
21
|
+
).write($stdout)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.exit_on_failure?
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Todoloo
|
2
|
+
class ErrorHandler
|
3
|
+
def initialize(kind)
|
4
|
+
@handler = new_handler_proc(kind)
|
5
|
+
end
|
6
|
+
|
7
|
+
def call(error, original_exception: nil)
|
8
|
+
# TODO(errors): Handle `original_exception`
|
9
|
+
@handler.call(error)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def new_handler_proc(kind)
|
15
|
+
case kind
|
16
|
+
when :raise
|
17
|
+
proc { |e| raise e }
|
18
|
+
when :stderr, :log, :trace
|
19
|
+
new_handler($stderr)
|
20
|
+
when String
|
21
|
+
File.open(kind, "w")
|
22
|
+
else
|
23
|
+
if kind.respond_to?(:write)
|
24
|
+
proc do |e|
|
25
|
+
if e.is_a?(Error)
|
26
|
+
kind.puts(e.message)
|
27
|
+
kind.puts(e.backtrace) if e.backtrace && !e.backtrace.empty?
|
28
|
+
else
|
29
|
+
kind.puts(e)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
elsif kind.respond_to?(:call)
|
33
|
+
kind
|
34
|
+
else
|
35
|
+
raise ArgumentError, "Unsupported type for ErrorHandler: #{kind}"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "concurrent-ruby"
|
2
|
+
require "parallel"
|
3
|
+
|
4
|
+
# File scanner for finding todos
|
5
|
+
class Todoloo::FileScanner
|
6
|
+
def initialize(pattern, excludes: [], trace: nil)
|
7
|
+
@parsers = Concurrent::Hash.new
|
8
|
+
|
9
|
+
@pattern = pattern
|
10
|
+
|
11
|
+
@excludes = excludes
|
12
|
+
|
13
|
+
@trace_output = case trace
|
14
|
+
when nil, false
|
15
|
+
nil
|
16
|
+
when true
|
17
|
+
$stderr
|
18
|
+
when String
|
19
|
+
File.open(trace, "w")
|
20
|
+
else
|
21
|
+
raise ArgumentError, "Invalid value #{trace} for argument trace"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Todoloo::TaskList]
|
26
|
+
def scan
|
27
|
+
Todoloo::TaskList.new.add(scan_files)
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def parser
|
33
|
+
(@parsers[Parallel.worker_number] ||= Todoloo::Parser.new)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [Array<Array<Task>>]
|
37
|
+
def scan_files
|
38
|
+
Parallel.map(Todoloo::Helpers.glob_paths(@pattern, excludes: @excludes)) do |path|
|
39
|
+
trace { "* Scan #{path}" }
|
40
|
+
|
41
|
+
parser.parse_and_transform(File.read(path), path: path)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def trace(&block)
|
46
|
+
return unless @trace_output
|
47
|
+
|
48
|
+
@trace_output.puts(block.call)
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Todoloo
|
2
|
+
module Helpers
|
3
|
+
extend self
|
4
|
+
# Returns an `Enumerator` over the paths that match `pattern`
|
5
|
+
#
|
6
|
+
# TODO We need to optimize how we traverse the file-system so that we can short-circuit the excludes instead
|
7
|
+
# of only filtering after finding them.
|
8
|
+
#
|
9
|
+
# @pattern [String]
|
10
|
+
# The file paths to glob
|
11
|
+
#
|
12
|
+
# @excludes [Array<String>]
|
13
|
+
# Optional list of file globs to exclude from the enumeration
|
14
|
+
#
|
15
|
+
# @return [Enumerator<String>]
|
16
|
+
def glob_paths(pattern, excludes: [])
|
17
|
+
Enumerator.new do |y|
|
18
|
+
Dir[pattern].lazy.each do |path|
|
19
|
+
y << path unless excludes.any? { |e| File.fnmatch(e, path) }
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,159 @@
|
|
1
|
+
module Todoloo
|
2
|
+
# Read https://kschiess.github.io/parslet/get-started.html
|
3
|
+
class Parser < Parslet::Parser
|
4
|
+
TASK_TYPES = %w[TODO NOTE FIXME HBD HACK XXX]
|
5
|
+
root :file
|
6
|
+
|
7
|
+
# Returns a list of tasks
|
8
|
+
def parse_and_transform(text, path: "")
|
9
|
+
tree = parse(text)
|
10
|
+
|
11
|
+
pp tree if Todoloo.debug?
|
12
|
+
|
13
|
+
Transformer.new.apply(tree).map do |hash|
|
14
|
+
line, column = hash.fetch(:type).line_and_column
|
15
|
+
|
16
|
+
Task.from_hash(hash.merge(path: path, line: line, column: column))
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def safe_parse(text)
|
21
|
+
parse(text)
|
22
|
+
rescue Parslet::ParseFailed => e
|
23
|
+
puts e.parse_failure_cause.ascii_tree
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Extension to automatically define `?` predicate rules
|
28
|
+
def self.rule(name, *args, **kwargs, &block)
|
29
|
+
if name.to_s.end_with?("?")
|
30
|
+
super
|
31
|
+
else
|
32
|
+
super # Define the original rule
|
33
|
+
super("#{name}?") { public_send(name).maybe }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Single char rules
|
38
|
+
rule(:lparen) { str("(") }
|
39
|
+
rule(:rparen) { str(")") }
|
40
|
+
|
41
|
+
rule(:eof) { any.absent? }
|
42
|
+
|
43
|
+
rule(:eol) { str("\n") }
|
44
|
+
|
45
|
+
rule(:space) { match(/[ \t]/) }
|
46
|
+
|
47
|
+
rule(:code_text) { match(/[^#\n]/) }
|
48
|
+
|
49
|
+
# FIXME: Cannot handle JS comments yet
|
50
|
+
rule(:comment_char) { str("#") } # || str("//") }
|
51
|
+
|
52
|
+
# Can read all the way without honoring anything special,
|
53
|
+
# since its use will only be when the comment has already started
|
54
|
+
rule(:comment_text) { match(/[^\n]/) }
|
55
|
+
|
56
|
+
# Parts
|
57
|
+
rule(:spacing) { space.repeat(1) }
|
58
|
+
|
59
|
+
rule(:code) { code_text.repeat(1).as(:code) }
|
60
|
+
|
61
|
+
rule(:line) { (comment | code >> comment | code).as(:line) }
|
62
|
+
|
63
|
+
rule(:file) { (line >> eol | eol | line >> eof).repeat.as(:file) }
|
64
|
+
|
65
|
+
rule(:comment) { (comment_start >> (task.as(:task) | line).maybe).as(:comment) }
|
66
|
+
|
67
|
+
rule(:comment_start) do
|
68
|
+
comment_char.capture(:comment_start) >> spacing?
|
69
|
+
end
|
70
|
+
|
71
|
+
# examples
|
72
|
+
# TODO(topic): My task
|
73
|
+
# TODO: My task
|
74
|
+
# TODO: My task
|
75
|
+
rule(:task) { task_type.as(:type).capture(:type) >> topics.repeat(0, 1).as(:topics) >> task_separator? >> spacing? >> description.repeat(0, 1).as(:description) }
|
76
|
+
|
77
|
+
rule(:description) { multiline_description | single_line_description }
|
78
|
+
|
79
|
+
rule(:description_text) { comment_text.repeat(1) }
|
80
|
+
|
81
|
+
rule(:single_line_description) { description_text.as(:text) }
|
82
|
+
|
83
|
+
rule(:multiline_description) do
|
84
|
+
(description_text >> comment_continuation.repeat(1)).as(:text)
|
85
|
+
end
|
86
|
+
|
87
|
+
MIN_INDENTATION_BEYOND_TYPE_START = 1
|
88
|
+
def comment_continuation
|
89
|
+
dynamic do |_source, context|
|
90
|
+
parser = match('[\n]')
|
91
|
+
|
92
|
+
comment_start = column_offset_of_capture(:comment_start, context)
|
93
|
+
|
94
|
+
# Match any non comment content before the comment starts
|
95
|
+
parser = ignore_code_text(parser, comment_start)
|
96
|
+
|
97
|
+
# Consume the characther that the comment opened with, like a `#` or `//`
|
98
|
+
parser >>= str(context.captures[:comment_start]).ignore
|
99
|
+
|
100
|
+
parser >>= (spacing? >> task_type).absent?
|
101
|
+
|
102
|
+
required_indentation = column_offset_of_capture(:type, context) - comment_start - context.captures[:comment_start].length + MIN_INDENTATION_BEYOND_TYPE_START
|
103
|
+
|
104
|
+
parser = indentation(parser, required_indentation)
|
105
|
+
|
106
|
+
# parser >>= task_type.absent?
|
107
|
+
|
108
|
+
parser >>= description_text
|
109
|
+
parser
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Calculates zero-based column offset for the given capture.
|
114
|
+
# Use only within dynamic blocks
|
115
|
+
def column_offset_of_capture(name, context)
|
116
|
+
case name
|
117
|
+
when :type
|
118
|
+
# FIXME: Not sure yet why this is even necessary...
|
119
|
+
_, col_index = context.captures[:type][:type].line_and_column
|
120
|
+
else
|
121
|
+
_, col_index = context.captures[name].line_and_column
|
122
|
+
end
|
123
|
+
|
124
|
+
col_index - 1
|
125
|
+
end
|
126
|
+
|
127
|
+
def ignore_code_text(parser, count)
|
128
|
+
if count.positive?
|
129
|
+
parser >> code_text.repeat(count, count).ignore
|
130
|
+
else
|
131
|
+
parser
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def indentation(parser, count)
|
136
|
+
return parser unless count.positive?
|
137
|
+
|
138
|
+
parser >> space.repeat(count, count)
|
139
|
+
end
|
140
|
+
|
141
|
+
rule(:task_type) do
|
142
|
+
TASK_TYPES
|
143
|
+
.map { |t| str(t) }
|
144
|
+
.reduce do |result, partial_matcher|
|
145
|
+
result | partial_matcher
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
rule(:topic) { match(/[^),]/).repeat(1).as(:topic) }
|
150
|
+
|
151
|
+
rule(:topic_rest) { (str(",") >> spacing.maybe >> topic).repeat(0, 1) }
|
152
|
+
|
153
|
+
# (topic1, topic2)
|
154
|
+
rule(:topics) { lparen >> topic >> topic_rest >> rparen }
|
155
|
+
|
156
|
+
rule(:task_separator) { str(":") }
|
157
|
+
rule(:task_separator?) { task_separator.maybe }
|
158
|
+
end
|
159
|
+
end
|
data/lib/todoloo/task.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
module Todoloo
|
2
|
+
class Task < Struct.new(:type, :topics, :description, :path, :line, :column)
|
3
|
+
def self.from_hash(hash)
|
4
|
+
new(
|
5
|
+
hash.fetch(:type),
|
6
|
+
hash.fetch(:topics),
|
7
|
+
hash.fetch(:description),
|
8
|
+
hash.fetch(:path),
|
9
|
+
hash.fetch(:line),
|
10
|
+
hash.fetch(:column)
|
11
|
+
)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require "psych"
|
2
|
+
# A string that is written to YAML as a folded string by default
|
3
|
+
class Todoloo::TaskList::FoldedString
|
4
|
+
def initialize(string)
|
5
|
+
@string = string
|
6
|
+
end
|
7
|
+
|
8
|
+
def encode_with(coder)
|
9
|
+
coder.style = Psych::Nodes::Scalar::LITERAL
|
10
|
+
coder.scalar = @string
|
11
|
+
coder.tag = nil
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Todoloo
|
4
|
+
class TaskList
|
5
|
+
def initialize
|
6
|
+
@tasks = []
|
7
|
+
end
|
8
|
+
|
9
|
+
def add(task)
|
10
|
+
if task.is_a?(Array)
|
11
|
+
task.each { |t| add(t) }
|
12
|
+
else
|
13
|
+
raise ArgumentError, "Task type must be Todoloo::Task: #{task.inspect}" unless task.is_a?(Todoloo::Task)
|
14
|
+
@tasks << task
|
15
|
+
end
|
16
|
+
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
# Structure:
|
21
|
+
# TOPIC:
|
22
|
+
# TYPE:
|
23
|
+
# - description:
|
24
|
+
# path:
|
25
|
+
def write(path, error: :raise)
|
26
|
+
output = YAML.dump(
|
27
|
+
converted_tasks(
|
28
|
+
error_handler: ErrorHandler.new(error)
|
29
|
+
)
|
30
|
+
)
|
31
|
+
|
32
|
+
if path.is_a?(String)
|
33
|
+
File.write(path, output)
|
34
|
+
else
|
35
|
+
path.write(output)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def converted_tasks(error_handler)
|
42
|
+
output = {"" => {}}
|
43
|
+
|
44
|
+
@tasks.each do |task|
|
45
|
+
output_task = {
|
46
|
+
"description" => FoldedString.new(task.description.to_s),
|
47
|
+
"path" => "#{task.path}:#{task.line}:#{task.column}"
|
48
|
+
}
|
49
|
+
|
50
|
+
task.topics.each do |topic|
|
51
|
+
by_type = output[topic.to_s] ||= {}
|
52
|
+
tasks = by_type[task.type.to_s.downcase] ||= []
|
53
|
+
tasks << output_task.dup
|
54
|
+
end
|
55
|
+
rescue Error => e
|
56
|
+
if task.respond_to?(:line) && task.respond_to?(:column)
|
57
|
+
error_handler.call("#{path}:#{task.line}:#{task.column}: Error: #{e}", original_exception: e)
|
58
|
+
else
|
59
|
+
error_handler.call("#{path}:??:??:Error: #{e}", original_exception: e)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
output
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Todoloo
|
2
|
+
class Transformer < Parslet::Transform
|
3
|
+
rule(file: subtree(:x)) { x.compact }
|
4
|
+
|
5
|
+
rule(lines: subtree(:x)) { x }
|
6
|
+
|
7
|
+
rule(line: {code: subtree(:x)}) { nil }
|
8
|
+
|
9
|
+
rule(line: {code: subtree(:x), comment: subtree(:y)}) { y }
|
10
|
+
|
11
|
+
# Discard lines with only code and comments without tasks
|
12
|
+
rule(line: {code: subtree(:x), comment: simple(:y)}) { nil }
|
13
|
+
|
14
|
+
rule(line: {comment: subtree(:x)}) { x }
|
15
|
+
|
16
|
+
rule(line: {comment: simple(:x)}) { nil }
|
17
|
+
|
18
|
+
rule(type: simple(:type), topics: sequence(:topics), description: sequence(:description)) do
|
19
|
+
{type: type, topics: topics, description: description.empty? ? "" : (raise "must be empty")}
|
20
|
+
end
|
21
|
+
|
22
|
+
rule(topic: simple(:x)) { x }
|
23
|
+
|
24
|
+
rule(task: subtree(:task)) { task }
|
25
|
+
|
26
|
+
rule([{text: simple(:x)}]) { x }
|
27
|
+
end
|
28
|
+
end
|
data/lib/todoloo/version.rb
CHANGED
data/lib/todoloo.rb
CHANGED
@@ -1,7 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "todoloo/version"
|
4
|
+
|
5
|
+
require "parslet"
|
6
|
+
|
7
|
+
require "zeitwerk"
|
8
|
+
|
9
|
+
loader = Zeitwerk::Loader.for_gem
|
10
|
+
|
11
|
+
loader.inflector.inflect "cli" => "CLI"
|
12
|
+
|
13
|
+
loader.setup # ready!
|
14
|
+
|
4
15
|
module Todoloo
|
5
16
|
class Error < StandardError; end
|
6
17
|
# Your code goes here...
|
18
|
+
|
19
|
+
class << self
|
20
|
+
attr_accessor :debug
|
21
|
+
|
22
|
+
def debug?
|
23
|
+
@debug
|
24
|
+
end
|
25
|
+
end
|
7
26
|
end
|
27
|
+
|
28
|
+
Todoloo.debug = false
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: todoloo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Coetzee
|
@@ -9,23 +9,115 @@ autorequire:
|
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
11
|
date: 2024-07-18 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: concurrent-ruby
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.3.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.3.3
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: parallel
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.25.1
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.25.1
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: parslet
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.0.0
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.0.0
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: psych
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 5.1.2
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 5.1.2
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: thor
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 1.3.1
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 1.3.1
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: zeitwerk
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
13
97
|
description: A tool for managing and saying bye to todos
|
14
98
|
email:
|
15
99
|
- chriscz93@gmail.com
|
16
|
-
executables:
|
100
|
+
executables:
|
101
|
+
- todoloo
|
17
102
|
extensions: []
|
18
103
|
extra_rdoc_files: []
|
19
104
|
files:
|
20
|
-
- ".ruby-version"
|
21
105
|
- CODE_OF_CONDUCT.md
|
22
106
|
- LICENSE.txt
|
23
|
-
- Makefile
|
24
107
|
- README.md
|
25
108
|
- Rakefile
|
109
|
+
- exe/todoloo
|
26
110
|
- lib/todoloo.rb
|
111
|
+
- lib/todoloo/cli.rb
|
112
|
+
- lib/todoloo/error_handler.rb
|
113
|
+
- lib/todoloo/file_scanner.rb
|
114
|
+
- lib/todoloo/helpers.rb
|
115
|
+
- lib/todoloo/parser.rb
|
116
|
+
- lib/todoloo/task.rb
|
117
|
+
- lib/todoloo/task_list.rb
|
118
|
+
- lib/todoloo/task_list/folded_string.rb
|
119
|
+
- lib/todoloo/transformer.rb
|
27
120
|
- lib/todoloo/version.rb
|
28
|
-
- sig/todoloo.rbs
|
29
121
|
homepage: https://github.com/chriscz/todoloo
|
30
122
|
licenses:
|
31
123
|
- MIT
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
3.2.2
|
data/Makefile
DELETED
data/sig/todoloo.rbs
DELETED