todo-curses 0.0.1
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 +7 -0
- data/.gitignore +5 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +49 -0
- data/README.rdoc +69 -0
- data/Rakefile +44 -0
- data/bin/todo-curses +55 -0
- data/features/step_definitions/todo_steps.rb +6 -0
- data/features/support/env.rb +15 -0
- data/features/todo.feature +8 -0
- data/lib/todo/todo_viewer.rb +283 -0
- data/lib/todo/version.rb +3 -0
- data/lib/todo-txt/list.rb +87 -0
- data/lib/todo-txt/logger.rb +21 -0
- data/lib/todo-txt/task.rb +339 -0
- data/lib/todo-txt.rb +7 -0
- data/lib/todo.rb +6 -0
- data/reset.sh +2 -0
- data/run.sh +2 -0
- data/test/default_test.rb +14 -0
- data/test/test_helper.rb +9 -0
- data/todo-curses.gemspec +23 -0
- data/todo.rdoc +5 -0
- metadata +130 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 2dd49a99988df6a2152060b4120949b41d8594f3
|
4
|
+
data.tar.gz: 54d30df0e6700095ba7603ff6846391124527a1c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 32d463bbd95f1c0cb53f9fe5e6b867d571fd665d9d76717ef50e324382591f037664058a0c77ff8898593f6069af8b4058c5fee086bfa07049cdd3564cb77b48
|
7
|
+
data.tar.gz: 84291c4338fae4c798d3c73b57ae18b031f42dcfc238b49a73e9f32a3fed4383b161481811300ed9de2714ad678a7a6e4ff6d22bf1c95102738979941c545b4c
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
todo (0.0.1)
|
5
|
+
gli (= 2.13.4)
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
aruba (0.6.1)
|
11
|
+
childprocess (>= 0.3.6)
|
12
|
+
cucumber (>= 1.1.1)
|
13
|
+
rspec-expectations (>= 2.7.0)
|
14
|
+
builder (3.2.2)
|
15
|
+
childprocess (0.5.5)
|
16
|
+
ffi (~> 1.0, >= 1.0.11)
|
17
|
+
cucumber (1.3.18)
|
18
|
+
builder (>= 2.1.2)
|
19
|
+
diff-lcs (>= 1.1.3)
|
20
|
+
gherkin (~> 2.12)
|
21
|
+
multi_json (>= 1.7.5, < 2.0)
|
22
|
+
multi_test (>= 0.1.1)
|
23
|
+
diff-lcs (1.2.5)
|
24
|
+
ffi (1.9.10)
|
25
|
+
gherkin (2.12.2)
|
26
|
+
multi_json (~> 1.3)
|
27
|
+
gli (2.13.4)
|
28
|
+
multi_json (1.11.2)
|
29
|
+
multi_test (0.1.1)
|
30
|
+
ncursesw (1.4.9)
|
31
|
+
rake (10.4.2)
|
32
|
+
rdoc (4.2.0)
|
33
|
+
rspec-expectations (3.3.1)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.3.0)
|
36
|
+
rspec-support (3.3.0)
|
37
|
+
|
38
|
+
PLATFORMS
|
39
|
+
ruby
|
40
|
+
|
41
|
+
DEPENDENCIES
|
42
|
+
aruba
|
43
|
+
ncursesw
|
44
|
+
rake
|
45
|
+
rdoc
|
46
|
+
todo!
|
47
|
+
|
48
|
+
BUNDLED WITH
|
49
|
+
1.11.2
|
data/README.rdoc
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
= TODO-Curses
|
2
|
+
|
3
|
+
This is a super crude first pass at a terminal application for
|
4
|
+
todo.txt files. A lot of the features are based on how
|
5
|
+
{todotxt.net}[todotxt.net] handles things. I really liked the overall
|
6
|
+
design of the application, just not the fact that it didn't run in my
|
7
|
+
linux terminal. To solve this, I decided to roll my own using Ruby and
|
8
|
+
Ncurses. There was already a pretty robust library for handling todo.txt
|
9
|
+
files, and Ncurses was something I'd been meaning to learn for a while.
|
10
|
+
|
11
|
+
No doubt there's a ton of nasty code in here. Please help me along if you're
|
12
|
+
interested. I'm sure there's a lot that can be refactored.
|
13
|
+
|
14
|
+
== Current features
|
15
|
+
|
16
|
+
- Open todo.txt files and view a scrollable list of items
|
17
|
+
- Move to the next item with `j`
|
18
|
+
- Move to the prev item with `k`
|
19
|
+
- Create new items with `n`
|
20
|
+
- Toggle done / not done state with `x`
|
21
|
+
- Move priority down with `shift+j`
|
22
|
+
- Move priority up with `shift+k`
|
23
|
+
- Completed tasks are archived to done.txt on exit
|
24
|
+
|
25
|
+
== Planned features
|
26
|
+
|
27
|
+
- Use ctrl instead of shift for priority change
|
28
|
+
- Color code priorities
|
29
|
+
- Add a spacer between priority groups
|
30
|
+
- Priority view with `ctrl+1`
|
31
|
+
- Project view with `ctrl+2`
|
32
|
+
- Strip out application wrapper; not needed
|
33
|
+
- Prep for release as a gem
|
34
|
+
- If no argument is given, open the default file. Default tbd.
|
35
|
+
|
36
|
+
== Ideas for later
|
37
|
+
|
38
|
+
Shift+J: Cycle through displays (Priority, project, etc.)
|
39
|
+
F: filter tasks (free-text, one filter condition per line)
|
40
|
+
T: append text to selected tasks
|
41
|
+
O or Ctrl+O: open todo.txt file
|
42
|
+
C or Ctrl+N: new todo.txt file
|
43
|
+
|
44
|
+
== Things not included
|
45
|
+
|
46
|
+
A: archive tasks
|
47
|
+
Ctrl+C: copy task to clipboard
|
48
|
+
Ctrl+Shift+C: copy task to edit field
|
49
|
+
Win+Alt+T: hide/unhide windows
|
50
|
+
0: clear filter
|
51
|
+
1-9: apply numbered filter preset
|
52
|
+
|
53
|
+
== Keyboard shortcut ideas
|
54
|
+
|
55
|
+
N: new task
|
56
|
+
J: next task
|
57
|
+
K: prev task
|
58
|
+
X: toggle task completion
|
59
|
+
D or Del or Backspace: delete task (with confirmation)
|
60
|
+
E or F2: update task
|
61
|
+
I: set priority
|
62
|
+
. or F5: reload tasks from file
|
63
|
+
?: show help
|
64
|
+
Shift+K: increase priority
|
65
|
+
Shift+J: decrease priority
|
66
|
+
Alt+Left/Right: clear priority
|
67
|
+
Ctrl+Alt+Up: increase due date by 1 day
|
68
|
+
Ctrl+Alt+Down: decrease due date by 1 day
|
69
|
+
Ctrl+Alt+Left/Right: remove due date
|
data/Rakefile
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'rubygems'
|
3
|
+
require 'rubygems/package_task'
|
4
|
+
require 'rdoc/task'
|
5
|
+
require 'cucumber'
|
6
|
+
require 'cucumber/rake/task'
|
7
|
+
Rake::RDocTask.new do |rd|
|
8
|
+
rd.main = "README.rdoc"
|
9
|
+
rd.rdoc_files.include("README.rdoc","lib/**/*.rb","bin/**/*")
|
10
|
+
rd.title = 'Your application title'
|
11
|
+
end
|
12
|
+
|
13
|
+
spec = eval(File.read('todo.gemspec'))
|
14
|
+
|
15
|
+
Gem::PackageTask.new(spec) do |pkg|
|
16
|
+
end
|
17
|
+
CUKE_RESULTS = 'results.html'
|
18
|
+
CLEAN << CUKE_RESULTS
|
19
|
+
desc 'Run features'
|
20
|
+
Cucumber::Rake::Task.new(:features) do |t|
|
21
|
+
opts = "features --format html -o #{CUKE_RESULTS} --format progress -x"
|
22
|
+
opts += " --tags #{ENV['TAGS']}" if ENV['TAGS']
|
23
|
+
t.cucumber_opts = opts
|
24
|
+
t.fork = false
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Run features tagged as work-in-progress (@wip)'
|
28
|
+
Cucumber::Rake::Task.new('features:wip') do |t|
|
29
|
+
tag_opts = ' --tags ~@pending'
|
30
|
+
tag_opts = ' --tags @wip'
|
31
|
+
t.cucumber_opts = "features --format html -o #{CUKE_RESULTS} --format pretty -x -s#{tag_opts}"
|
32
|
+
t.fork = false
|
33
|
+
end
|
34
|
+
|
35
|
+
task :cucumber => :features
|
36
|
+
task 'cucumber:wip' => 'features:wip'
|
37
|
+
task :wip => 'features:wip'
|
38
|
+
require 'rake/testtask'
|
39
|
+
Rake::TestTask.new do |t|
|
40
|
+
t.libs << "test"
|
41
|
+
t.test_files = FileList['test/*_test.rb']
|
42
|
+
end
|
43
|
+
|
44
|
+
task :default => [:test,:features]
|
data/bin/todo-curses
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
begin # XXX: Remove this begin/rescue before distributing your app
|
4
|
+
require 'todo'
|
5
|
+
rescue LoadError
|
6
|
+
STDERR.puts "In development, you need to use `bundle exec bin/todo` to run your app"
|
7
|
+
STDERR.puts "At install-time, RubyGems will make sure lib, etc. are in the load path"
|
8
|
+
STDERR.puts "Feel free to remove this message from bin/todo now"
|
9
|
+
exit 64
|
10
|
+
end
|
11
|
+
|
12
|
+
include GLI::App
|
13
|
+
include Ncurses
|
14
|
+
include Ncurses::Form
|
15
|
+
|
16
|
+
program_desc 'A simple interface for managing todo.txt files.'
|
17
|
+
|
18
|
+
subcommand_option_handling :normal
|
19
|
+
arguments :strict
|
20
|
+
|
21
|
+
desc 'Starts an interactive editor for the given todo.txt list.'
|
22
|
+
arg_name 'Describe arguments to list here'
|
23
|
+
command :list do |c|
|
24
|
+
c.action do |global_options,options,args|
|
25
|
+
if args.size != 1 then
|
26
|
+
printf("usage: #{$0} file\n");
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
|
30
|
+
TodoViewer.new(ARGV[0])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
pre do |global,command,options,args|
|
35
|
+
# Pre logic here
|
36
|
+
# Return true to proceed; false to abort and not call the
|
37
|
+
# chosen command
|
38
|
+
# Use skips_pre before a command to skip this block
|
39
|
+
# on that command only
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
post do |global,command,options,args|
|
44
|
+
# Post logic here
|
45
|
+
# Use skips_post before a command to skip this
|
46
|
+
# block on that command only
|
47
|
+
end
|
48
|
+
|
49
|
+
on_error do |exception|
|
50
|
+
# Error logic here
|
51
|
+
# return false to skip default error handling
|
52
|
+
true
|
53
|
+
end
|
54
|
+
|
55
|
+
exit run(ARGV)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'aruba/cucumber'
|
2
|
+
|
3
|
+
ENV['PATH'] = "#{File.expand_path(File.dirname(__FILE__) + '/../../bin')}#{File::PATH_SEPARATOR}#{ENV['PATH']}"
|
4
|
+
LIB_DIR = File.join(File.expand_path(File.dirname(__FILE__)),'..','..','lib')
|
5
|
+
|
6
|
+
Before do
|
7
|
+
# Using "announce" causes massive warnings on 1.9.2
|
8
|
+
@puts = true
|
9
|
+
@original_rubylib = ENV['RUBYLIB']
|
10
|
+
ENV['RUBYLIB'] = LIB_DIR + File::PATH_SEPARATOR + ENV['RUBYLIB'].to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
After do
|
14
|
+
ENV['RUBYLIB'] = @original_rubylib
|
15
|
+
end
|
@@ -0,0 +1,283 @@
|
|
1
|
+
# TODO: Refactor into proper size functions
|
2
|
+
# A curses based todo.txt file viewer
|
3
|
+
class TodoViewer
|
4
|
+
|
5
|
+
# Run the ncurses application
|
6
|
+
def interact
|
7
|
+
while true
|
8
|
+
result = true
|
9
|
+
c = Ncurses.getch
|
10
|
+
case c
|
11
|
+
when 'J'.ord
|
12
|
+
result = priority_down
|
13
|
+
when 'K'.ord
|
14
|
+
result = priority_up
|
15
|
+
when 'x'.ord
|
16
|
+
toggle_item_completion
|
17
|
+
when 'n'.ord
|
18
|
+
new_item
|
19
|
+
when 'j'.ord
|
20
|
+
result = scroll_down
|
21
|
+
when 'k'.ord
|
22
|
+
result = scroll_up
|
23
|
+
# when '\s'.ord # white space
|
24
|
+
# for i in 0..(@screen.getmaxy - 2)
|
25
|
+
# if( ! scroll_down )
|
26
|
+
# if( i == 0 )
|
27
|
+
# result = false
|
28
|
+
# end
|
29
|
+
# break
|
30
|
+
# end
|
31
|
+
# end
|
32
|
+
# when Ncurses::KEY_PPAGE
|
33
|
+
# for i in 0..(@screen.getmaxy - 2)
|
34
|
+
# if( ! scroll_up )
|
35
|
+
# if( i == 0 )
|
36
|
+
# result = false
|
37
|
+
# end
|
38
|
+
# break
|
39
|
+
# end
|
40
|
+
# end
|
41
|
+
when 'h'.ord
|
42
|
+
while( scroll_up )
|
43
|
+
end
|
44
|
+
when 'l'.ord
|
45
|
+
while( scroll_down )
|
46
|
+
end
|
47
|
+
when 'q'.ord
|
48
|
+
break
|
49
|
+
else
|
50
|
+
@screen.mvprintw(0,0, "[unknown key `#{Ncurses.keyname(c)}'=#{c}] ")
|
51
|
+
end
|
52
|
+
if( !result )
|
53
|
+
Ncurses.beep
|
54
|
+
end
|
55
|
+
# TODO: Catch ctrl+c for graceful exit
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO: Confirm exit
|
59
|
+
clean_done_tasks
|
60
|
+
close_ncurses
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
# Create a new fileviewer, and view the file.
|
66
|
+
def initialize(filename)
|
67
|
+
@log = Logger.new 'log.txt'
|
68
|
+
@log.debug 'Run Started'
|
69
|
+
init_curses
|
70
|
+
load_file(filename)
|
71
|
+
interact
|
72
|
+
# TODO: Save a copy of the todo.txt list to backup file.
|
73
|
+
end
|
74
|
+
|
75
|
+
# Perform the curses setup
|
76
|
+
def init_curses
|
77
|
+
@screen = Ncurses.initscr
|
78
|
+
Ncurses.nonl
|
79
|
+
Ncurses.cbreak
|
80
|
+
Ncurses.noecho
|
81
|
+
@screen.scrollok(true)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Loads the given file as a todo.txt array. Sets the view to the top
|
85
|
+
# and redraws the list view.
|
86
|
+
# @param filename [String] path to the text file to be loaded
|
87
|
+
def load_file(filename)
|
88
|
+
@done_file = File.dirname(filename) + '/done.txt'
|
89
|
+
@list = Todo::List.new filename
|
90
|
+
@list.sort! { |x,y| y <=> x } # Reverse sort
|
91
|
+
items = []
|
92
|
+
last_priority = nil
|
93
|
+
last_selection = @menu.current_item.user_object if @menu
|
94
|
+
current_selection = nil
|
95
|
+
|
96
|
+
# Build the menu item list
|
97
|
+
@list.each do |item|
|
98
|
+
# Insert dividers on priority change
|
99
|
+
if item.priority != last_priority
|
100
|
+
divider_priority = item.priority.nil? ? 'N/A' : item.priority.to_s
|
101
|
+
divider = Ncurses::Menu::ITEM.new(divider_priority, '')
|
102
|
+
items << divider
|
103
|
+
last_priority = item.priority
|
104
|
+
end
|
105
|
+
|
106
|
+
# Build the todo menu item
|
107
|
+
menu_item = Ncurses::Menu::ITEM.new(item.to_s, '') # name, description
|
108
|
+
menu_item.user_object = item
|
109
|
+
items << menu_item
|
110
|
+
|
111
|
+
# Set the current selection
|
112
|
+
current_selection = menu_item if item.to_s == last_selection.to_s
|
113
|
+
end
|
114
|
+
|
115
|
+
# Build the final menu object
|
116
|
+
# TODO: Possible memory leak from resetting object over top?
|
117
|
+
@menu = Ncurses::Menu::MENU.new items
|
118
|
+
@menu.set_menu_win(@screen)
|
119
|
+
@menu.set_menu_sub(@screen.derwin(@screen.getmaxx, @screen.getmaxy, 0, 0))
|
120
|
+
@menu.set_menu_format(@screen.getmaxy, 1)
|
121
|
+
|
122
|
+
# Set dividers to non-interactive
|
123
|
+
@menu.menu_items.select{ |i| i.user_object.nil? }.each do |divider|
|
124
|
+
divider.item_opts_off Menu::O_SELECTABLE
|
125
|
+
end
|
126
|
+
|
127
|
+
# Show the menu
|
128
|
+
@screen.clear
|
129
|
+
@menu.post_menu
|
130
|
+
|
131
|
+
# Set selection position
|
132
|
+
@menu.set_current_item current_selection if current_selection
|
133
|
+
@menu.menu_driver(Ncurses::Menu::REQ_DOWN_ITEM) if @menu.current_item.user_object.nil?
|
134
|
+
|
135
|
+
# Refresh
|
136
|
+
@screen.refresh
|
137
|
+
end
|
138
|
+
|
139
|
+
# Moves the current selection's priority up by one unless it is at Z.
|
140
|
+
def priority_up
|
141
|
+
item = @menu.current_item.user_object
|
142
|
+
item.priority_inc
|
143
|
+
save_list
|
144
|
+
end
|
145
|
+
|
146
|
+
# Moves the current selection's priority down by one unless it is at A.
|
147
|
+
def priority_down
|
148
|
+
item = @menu.current_item.user_object
|
149
|
+
item.priority_dec
|
150
|
+
save_list
|
151
|
+
end
|
152
|
+
|
153
|
+
# Scroll the display up by one line
|
154
|
+
# @return [Boolean] true if the action completed successfully.
|
155
|
+
def scroll_up
|
156
|
+
# Move to the next item if it's not the first in the list
|
157
|
+
unless @menu.menu_items[0].user_object.nil? &&
|
158
|
+
@menu.current_item.item_index < 2
|
159
|
+
result = @menu.menu_driver(Ncurses::Menu::REQ_UP_ITEM)
|
160
|
+
end
|
161
|
+
# Move to the next item if it's not a divider
|
162
|
+
result = @menu.menu_driver(Ncurses::Menu::REQ_UP_ITEM) unless @menu.current_item.user_object
|
163
|
+
return true if result == E_OK
|
164
|
+
false
|
165
|
+
end
|
166
|
+
|
167
|
+
# Scroll the display down by one line
|
168
|
+
# @return [Boolean] true if the action completed successfully.
|
169
|
+
def scroll_down
|
170
|
+
result = @menu.menu_driver(Ncurses::Menu::REQ_DOWN_ITEM)
|
171
|
+
result = @menu.menu_driver(Ncurses::Menu::REQ_DOWN_ITEM) unless @menu.current_item.user_object
|
172
|
+
return true if result == E_OK
|
173
|
+
false
|
174
|
+
end
|
175
|
+
|
176
|
+
# Collects a new todo item from the user and saves
|
177
|
+
# it to the text file.
|
178
|
+
def new_item
|
179
|
+
field = FIELD.new(1, @screen.getmaxx-1, 2, 1, 0, 0)
|
180
|
+
field.set_field_back(A_UNDERLINE)
|
181
|
+
fields = [field]
|
182
|
+
my_form = FORM.new(fields);
|
183
|
+
my_form.user_object = "My identifier"
|
184
|
+
|
185
|
+
# Calculate the area required for the form
|
186
|
+
rows = Array.new()
|
187
|
+
cols = Array.new()
|
188
|
+
my_form.scale_form(rows, cols);
|
189
|
+
|
190
|
+
# Create the window to be associated with the form
|
191
|
+
my_form_win = WINDOW.new(rows[0] + 3, cols[0] + 14, 1, 1);
|
192
|
+
my_form_win.keypad(TRUE);
|
193
|
+
|
194
|
+
# Set main window and sub window
|
195
|
+
my_form.set_form_win(my_form_win);
|
196
|
+
my_form.set_form_sub(my_form_win.derwin(rows[0], cols[0], 2, 12));
|
197
|
+
|
198
|
+
my_form.post_form();
|
199
|
+
|
200
|
+
# Print field types
|
201
|
+
my_form_win.mvaddstr(4, 2, "New item")
|
202
|
+
my_form_win.wrefresh();
|
203
|
+
|
204
|
+
stdscr.refresh();
|
205
|
+
|
206
|
+
new_item_text = capture_text_field_input(my_form_win, my_form, field)
|
207
|
+
|
208
|
+
# Save results
|
209
|
+
save_new_item(new_item_text)
|
210
|
+
|
211
|
+
# Clean up
|
212
|
+
my_form.unpost_form
|
213
|
+
my_form.free_form
|
214
|
+
|
215
|
+
field.free_field
|
216
|
+
# fields.each {|f| f.free_field()}
|
217
|
+
end
|
218
|
+
|
219
|
+
# Adds a new item to the list and saves the file
|
220
|
+
# @param task [String] the task to be added
|
221
|
+
# @return [Todo::List] the updated list
|
222
|
+
def save_new_item(task)
|
223
|
+
@list << Todo::Task.new(task)
|
224
|
+
save_list
|
225
|
+
@list
|
226
|
+
end
|
227
|
+
|
228
|
+
# Saves the current state of the list. Overrides the current file.
|
229
|
+
# Reloads the newly saved file.
|
230
|
+
def save_list
|
231
|
+
File.open(@list.path, 'w') { |file| file << @list.join("\n") }
|
232
|
+
load_file @list.path
|
233
|
+
end
|
234
|
+
|
235
|
+
# Marks the currently selected menu item as complete and saves the list.
|
236
|
+
def toggle_item_completion
|
237
|
+
@menu.current_item.user_object.toggle!
|
238
|
+
save_list
|
239
|
+
end
|
240
|
+
|
241
|
+
# Saves done tasks to done.txt and removes them from todo.txt
|
242
|
+
def clean_done_tasks
|
243
|
+
done_tasks = @list.select { |task| !task.completed_on.nil? }
|
244
|
+
File.open(@done_file, 'a') { |file|
|
245
|
+
file << "\n"
|
246
|
+
file << done_tasks.join("\n")
|
247
|
+
}
|
248
|
+
remaining_tasks = @list.select { |task| task.completed_on.nil? }
|
249
|
+
File.open(@list.path, 'w') { |file| file << remaining_tasks.join("\n") }
|
250
|
+
end
|
251
|
+
|
252
|
+
# put the screen back in its normal state
|
253
|
+
def close_ncurses
|
254
|
+
Ncurses.echo()
|
255
|
+
Ncurses.nocbreak()
|
256
|
+
Ncurses.nl()
|
257
|
+
Ncurses.endwin()
|
258
|
+
end
|
259
|
+
|
260
|
+
# Captures text input into a form and returns the resulting string.
|
261
|
+
# @param window [Window] the form window
|
262
|
+
# @param form [FORM] the form to be captured
|
263
|
+
# @param field [FIELD] the form to be captured
|
264
|
+
# @return [String] the captured input
|
265
|
+
def capture_text_field_input(window, form, field)
|
266
|
+
# Capture typing...
|
267
|
+
while((ch = window.getch()) != 13) # return is ascii 13
|
268
|
+
case ch
|
269
|
+
when KEY_LEFT
|
270
|
+
form.form_driver(REQ_PREV_CHAR);
|
271
|
+
when KEY_RIGHT
|
272
|
+
form.form_driver(REQ_NEXT_CHAR);
|
273
|
+
when KEY_BACKSPACE
|
274
|
+
form.form_driver(REQ_DEL_PREV);
|
275
|
+
else
|
276
|
+
# If this is a normal character, it gets Printed
|
277
|
+
form.form_driver(ch);
|
278
|
+
end
|
279
|
+
end
|
280
|
+
form.form_driver REQ_NEXT_FIELD # Request next to set 0 buffer in field
|
281
|
+
Ncurses::Form.field_buffer(field, 0)
|
282
|
+
end
|
283
|
+
end
|
data/lib/todo/version.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
module Todo
|
2
|
+
class List < Array
|
3
|
+
# Initializes a Todo List object with a path to the corresponding todo.txt
|
4
|
+
# file. For example, if your todo.txt file is located at:
|
5
|
+
#
|
6
|
+
# /home/sam/Dropbox/todo/todo.txt
|
7
|
+
#
|
8
|
+
# You would initialize this object like:
|
9
|
+
#
|
10
|
+
# list = Todo::List.new "/home/sam/Dropbox/todo/todo-txt"
|
11
|
+
#
|
12
|
+
# Alternately, you can initialize this object with an array of strings or
|
13
|
+
# tasks. If the array is of strings, the strings will be converted into
|
14
|
+
# tasks. You can supply a mixed list of string and tasks if you wish.
|
15
|
+
#
|
16
|
+
# Example:
|
17
|
+
#
|
18
|
+
# array = Array.new
|
19
|
+
# array.push "(A) A string task!"
|
20
|
+
# array.push Todo::Task.new("(A) An actual task!")
|
21
|
+
#
|
22
|
+
# list = Todo::List.new array
|
23
|
+
def initialize list
|
24
|
+
if list.is_a? Array
|
25
|
+
# No file path was given.
|
26
|
+
@path = nil
|
27
|
+
|
28
|
+
# If path is an array, loop over it, adding to self.
|
29
|
+
list.each do |task|
|
30
|
+
# If it's a string, make a new task out of it.
|
31
|
+
if task.is_a? String
|
32
|
+
self.push Todo::Task.new task
|
33
|
+
# If it's a task, just add it.
|
34
|
+
elsif task.is_a? Todo::Task
|
35
|
+
self.push task
|
36
|
+
end
|
37
|
+
end
|
38
|
+
elsif list.is_a? String
|
39
|
+
@path = list
|
40
|
+
|
41
|
+
# Read in lines from file, create Todo::Tasks out of them and push them
|
42
|
+
# onto self.
|
43
|
+
File.open(list) do |file|
|
44
|
+
file.each_line { |line| self.push Todo::Task.new line }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# The path to the todo.txt file that you supplied when you created the
|
50
|
+
# Todo::List object.
|
51
|
+
def path
|
52
|
+
@path
|
53
|
+
end
|
54
|
+
|
55
|
+
# Filters the list by priority and returns a new list.
|
56
|
+
#
|
57
|
+
# Example:
|
58
|
+
#
|
59
|
+
# list = Todo::List.new "/path/to/list"
|
60
|
+
# list.by_priority "A" #=> Will be a new list with only priority A tasks
|
61
|
+
def by_priority priority
|
62
|
+
Todo::List.new self.select { |task| task.priority == priority }
|
63
|
+
end
|
64
|
+
|
65
|
+
# Filters the list by context and returns a new list.
|
66
|
+
#
|
67
|
+
# Example:
|
68
|
+
#
|
69
|
+
# list = Todo::List.new "/path/to/list"
|
70
|
+
# list.by_context "@context" #=> Will be a new list with only tasks
|
71
|
+
# containing "@context"
|
72
|
+
def by_context context
|
73
|
+
Todo::List.new self.select { |task| task.contexts.include? context }
|
74
|
+
end
|
75
|
+
|
76
|
+
# Filters the list by project and returns a new list.
|
77
|
+
#
|
78
|
+
# Example:
|
79
|
+
#
|
80
|
+
# list = Todo::List.new "/path/to/list"
|
81
|
+
# list.by_project "+project" #=> Will be a new list with only tasks
|
82
|
+
# containing "+project"
|
83
|
+
def by_project project
|
84
|
+
Todo::List.new self.select { |task| task.projects.include? project }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Todo
|
4
|
+
module Logger
|
5
|
+
def self.included base
|
6
|
+
base.extend(self)
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.logger= new_logger
|
10
|
+
@@logger = new_logger
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.logger
|
14
|
+
@@logger ||= ::Logger.new(STDOUT)
|
15
|
+
end
|
16
|
+
|
17
|
+
def logger
|
18
|
+
Todo::Logger.logger
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,339 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Todo
|
4
|
+
class Task
|
5
|
+
include Comparable
|
6
|
+
include Todo::Logger
|
7
|
+
|
8
|
+
# The regular expression used to match contexts.
|
9
|
+
def self.contexts_regex
|
10
|
+
/(?:\s+|^)@\w+/
|
11
|
+
end
|
12
|
+
|
13
|
+
# The regex used to match projects.
|
14
|
+
def self.projects_regex
|
15
|
+
/(?:\s+|^)\+\w+/
|
16
|
+
end
|
17
|
+
|
18
|
+
# The regex used to match priorities.
|
19
|
+
def self.priority_regex
|
20
|
+
/(?:^|\s+)\(([A-Za-z])\)\s+/
|
21
|
+
end
|
22
|
+
|
23
|
+
# The regex used to match creation date.
|
24
|
+
def self.created_on_regex
|
25
|
+
/(?:^|-\d{2}\s|\)\s)(\d{4}-\d{2}-\d{2})\s/
|
26
|
+
end
|
27
|
+
|
28
|
+
# The regex used to match completion.
|
29
|
+
def self.done_regex
|
30
|
+
/^x\s+(\d{4}-\d{2}-\d{2})\s+/
|
31
|
+
end
|
32
|
+
|
33
|
+
# The regex used to match due date.
|
34
|
+
def self.due_on_regex
|
35
|
+
/(?:due:)(\d{4}-\d{2}-\d{2})(?:\s+|$)/i
|
36
|
+
end
|
37
|
+
|
38
|
+
# Creates a new task. The argument that you pass in must be the string
|
39
|
+
# representation of a task.
|
40
|
+
#
|
41
|
+
# Example:
|
42
|
+
#
|
43
|
+
# task = Todo::Task.new("(A) A high priority task!")
|
44
|
+
def initialize task
|
45
|
+
@orig = task
|
46
|
+
@completed_on = get_completed_date #orig.scan(self.class.done_regex)[1] ||= nil
|
47
|
+
@priority, @created_on = orig_priority, orig_created_on
|
48
|
+
@due_on = get_due_on_date
|
49
|
+
@contexts ||= orig.scan(self.class.contexts_regex).map { |item| item.strip }
|
50
|
+
@projects ||= orig.scan(self.class.projects_regex).map { |item| item.strip }
|
51
|
+
end
|
52
|
+
|
53
|
+
# Returns the original content of the task.
|
54
|
+
#
|
55
|
+
# Example:
|
56
|
+
#
|
57
|
+
# task = Todo::Task.new "(A) @context +project Hello!"
|
58
|
+
# task.orig #=> "(A) @context +project Hello!"
|
59
|
+
attr_reader :orig
|
60
|
+
|
61
|
+
# Returns the task's creation date, if any.
|
62
|
+
#
|
63
|
+
# Example:
|
64
|
+
#
|
65
|
+
# task = Todo::Task.new "(A) 2012-03-04 Task."
|
66
|
+
# task.created_on
|
67
|
+
# #=> <Date: 2012-03-04 (4911981/2,0,2299161)>
|
68
|
+
#
|
69
|
+
# Dates _must_ be in the YYYY-MM-DD format as specified in the todo.txt
|
70
|
+
# format. Dates in any other format will be classed as malformed and this
|
71
|
+
# attribute will be nil.
|
72
|
+
attr_reader :created_on
|
73
|
+
|
74
|
+
# Returns the task's completion date if task is done.
|
75
|
+
#
|
76
|
+
# Example:
|
77
|
+
#
|
78
|
+
# task = Todo::Task.new "x 2012-03-04 Task."
|
79
|
+
# task.completed_on
|
80
|
+
# #=> <Date: 2012-03-04 (4911981/2,0,2299161)>
|
81
|
+
#
|
82
|
+
# Dates _must_ be in the YYYY-MM-DD format as specified in the todo.txt
|
83
|
+
# format. Dates in any other format will be classed as malformed and this
|
84
|
+
# attribute will be nil.
|
85
|
+
attr_reader :completed_on
|
86
|
+
|
87
|
+
# Returns the task's due date, if any.
|
88
|
+
#
|
89
|
+
# Example:
|
90
|
+
#
|
91
|
+
# task = Todo::Task.new "(A) This is a task. due:2012-03-04"
|
92
|
+
# task.due_on
|
93
|
+
# #=> <Date: 2012-03-04 (4911981/2,0,2299161)>
|
94
|
+
#
|
95
|
+
# Dates _must_ be in the YYYY-MM-DD format as specified in the todo.txt
|
96
|
+
# format. Dates in any other format will be classed as malformed and this
|
97
|
+
# attribute will be nil.
|
98
|
+
attr_reader :due_on
|
99
|
+
|
100
|
+
# Returns the priority, if any.
|
101
|
+
#
|
102
|
+
# Example:
|
103
|
+
#
|
104
|
+
# task = Todo::Task.new "(A) Some task."
|
105
|
+
# task.priority #=> "A"
|
106
|
+
#
|
107
|
+
# task = Todo::Task.new "Some task."
|
108
|
+
# task.priority #=> nil
|
109
|
+
attr_reader :priority
|
110
|
+
|
111
|
+
# Returns an array of all the @context annotations.
|
112
|
+
#
|
113
|
+
# Example:
|
114
|
+
#
|
115
|
+
# task = Todo:Task.new "(A) @context Testing!"
|
116
|
+
# task.context #=> ["@context"]
|
117
|
+
attr_reader :contexts
|
118
|
+
|
119
|
+
# Returns an array of all the +project annotations.
|
120
|
+
#
|
121
|
+
# Example:
|
122
|
+
#
|
123
|
+
# task = Todo:Task.new "(A) +test Testing!"
|
124
|
+
# task.projects #=> ["+test"]
|
125
|
+
attr_reader :projects
|
126
|
+
|
127
|
+
# Gets just the text content of the todo, without the priority, contexts
|
128
|
+
# and projects annotations.
|
129
|
+
#
|
130
|
+
# Example:
|
131
|
+
#
|
132
|
+
# task = Todo::Task.new "(A) @test Testing!"
|
133
|
+
# task.text #=> "Testing!"
|
134
|
+
def text
|
135
|
+
@text ||= orig.
|
136
|
+
gsub(self.class.done_regex, '').
|
137
|
+
gsub(self.class.priority_regex, '').
|
138
|
+
gsub(self.class.created_on_regex, '').
|
139
|
+
gsub(self.class.contexts_regex, '').
|
140
|
+
gsub(self.class.projects_regex, '').
|
141
|
+
gsub(self.class.due_on_regex, '').
|
142
|
+
strip
|
143
|
+
end
|
144
|
+
|
145
|
+
# Returns the task's creation date, if any.
|
146
|
+
#
|
147
|
+
# Example:
|
148
|
+
#
|
149
|
+
# task = Todo::Task.new "(A) 2012-03-04 Task."
|
150
|
+
# task.date
|
151
|
+
# #=> <Date: 2012-03-04 (4911981/2,0,2299161)>
|
152
|
+
#
|
153
|
+
# Dates _must_ be in the YYYY-MM-DD format as specified in the todo.txt
|
154
|
+
# format. Dates in any other format will be classed as malformed and this
|
155
|
+
# method will return nil.
|
156
|
+
#
|
157
|
+
# Deprecated
|
158
|
+
def date
|
159
|
+
logger.warn("Task#date is deprecated, use created_on instead.")
|
160
|
+
|
161
|
+
@created_on
|
162
|
+
end
|
163
|
+
|
164
|
+
# Returns whether a task's due date is in the past.
|
165
|
+
#
|
166
|
+
# Example:
|
167
|
+
#
|
168
|
+
# task = Todo::Task.new("This task is overdue! due:#{Date.today - 1}")
|
169
|
+
# task.overdue?
|
170
|
+
# #=> true
|
171
|
+
def overdue?
|
172
|
+
return true if !due_on.nil? && due_on < Date.today
|
173
|
+
false
|
174
|
+
end
|
175
|
+
|
176
|
+
# Returns if the task is done.
|
177
|
+
#
|
178
|
+
# Example:
|
179
|
+
#
|
180
|
+
# task = Todo::Task.new "x 2012-12-08 Task."
|
181
|
+
# task.done?
|
182
|
+
# #=> true
|
183
|
+
#
|
184
|
+
# task = Todo::Task.new "Task."
|
185
|
+
# task.done?
|
186
|
+
# #=> false
|
187
|
+
def done?
|
188
|
+
!@completed_on.nil?
|
189
|
+
end
|
190
|
+
|
191
|
+
# Completes the task on the current date.
|
192
|
+
#
|
193
|
+
# Example:
|
194
|
+
#
|
195
|
+
# task = Todo::Task.new "2012-12-08 Task."
|
196
|
+
# task.done?
|
197
|
+
# #=> false
|
198
|
+
#
|
199
|
+
# task.do!
|
200
|
+
# task.done?
|
201
|
+
# #=> true
|
202
|
+
# task.created_on
|
203
|
+
# #=> <Date: 2012-12-08 (4911981/2,0,2299161)>
|
204
|
+
# task.completed_on
|
205
|
+
# #=> # the current date
|
206
|
+
def do!
|
207
|
+
@completed_on = Date.today
|
208
|
+
@priority = nil
|
209
|
+
end
|
210
|
+
|
211
|
+
# Marks the task as incomplete and resets its original priority.
|
212
|
+
#
|
213
|
+
# Example:
|
214
|
+
#
|
215
|
+
# task = Todo::Task.new "x 2012-12-08 2012-03-04 Task."
|
216
|
+
# task.done?
|
217
|
+
# #=> true
|
218
|
+
#
|
219
|
+
# task.undo!
|
220
|
+
# task.done?
|
221
|
+
# #=> false
|
222
|
+
# task.created_on
|
223
|
+
# #=> <Date: 2012-03-04 (4911981/2,0,2299161)>
|
224
|
+
# task.completed_on
|
225
|
+
# #=> nil
|
226
|
+
def undo!
|
227
|
+
@completed_on = nil
|
228
|
+
@priority = orig_priority
|
229
|
+
end
|
230
|
+
|
231
|
+
# Toggles the task from complete to incomplete or vice versa.
|
232
|
+
#
|
233
|
+
# Example:
|
234
|
+
#
|
235
|
+
# task = Todo::Task.new "x 2012-12-08 Task."
|
236
|
+
# task.done?
|
237
|
+
# #=> true
|
238
|
+
#
|
239
|
+
# task.toggle!
|
240
|
+
# task.done?
|
241
|
+
# #=> false
|
242
|
+
#
|
243
|
+
# task.toggle!
|
244
|
+
# task.done?
|
245
|
+
# #=> true
|
246
|
+
def toggle!
|
247
|
+
done? ? undo! : do!
|
248
|
+
end
|
249
|
+
|
250
|
+
# Returns this task as a string.
|
251
|
+
#
|
252
|
+
# Example:
|
253
|
+
#
|
254
|
+
# task = Todo::Task.new "(A) 2012-12-08 Task"
|
255
|
+
# task.to_s
|
256
|
+
# #=> "(A) 2012-12-08 Task"
|
257
|
+
def to_s
|
258
|
+
priority_string = priority ? "(#{priority}) " : ""
|
259
|
+
done_string = done? ? "x #{completed_on} " : ""
|
260
|
+
created_on_string = created_on ? "#{created_on} " : ""
|
261
|
+
contexts_string = contexts.empty? ? "" : " #{contexts.join ' '}"
|
262
|
+
projects_string = projects.empty? ? "" : " #{projects.join ' '}"
|
263
|
+
due_on_string = due_on.nil? ? "" : " due:#{due_on}"
|
264
|
+
"#{done_string}#{priority_string}#{created_on_string}#{text}#{contexts_string}#{projects_string}#{due_on_string}"
|
265
|
+
end
|
266
|
+
|
267
|
+
# Compares the priorities of two tasks.
|
268
|
+
#
|
269
|
+
# Example:
|
270
|
+
#
|
271
|
+
# task1 = Todo::Task.new "(A) Priority A."
|
272
|
+
# task2 = Todo::Task.new "(B) Priority B."
|
273
|
+
#
|
274
|
+
# task1 > task2
|
275
|
+
# # => true
|
276
|
+
#
|
277
|
+
# task1 == task2
|
278
|
+
# # => false
|
279
|
+
#
|
280
|
+
# task2 > task1
|
281
|
+
# # => false
|
282
|
+
def <=> other_task
|
283
|
+
if self.priority.nil? and other_task.priority.nil?
|
284
|
+
0
|
285
|
+
elsif other_task.priority.nil?
|
286
|
+
1
|
287
|
+
elsif self.priority.nil?
|
288
|
+
-1
|
289
|
+
else
|
290
|
+
other_task.priority <=> self.priority
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
# Decreases the priority until Z.
|
295
|
+
def priority_dec
|
296
|
+
return if @priority.nil?
|
297
|
+
@priority = @priority.next if @priority.ord < 90
|
298
|
+
end
|
299
|
+
|
300
|
+
# Increases the priority until A. If it's nil, it sets it to A.
|
301
|
+
def priority_inc
|
302
|
+
if @priority.nil?
|
303
|
+
@priority = 'A'
|
304
|
+
else
|
305
|
+
@priority = (@priority.ord-1).chr if @priority.ord > 65
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
private
|
310
|
+
|
311
|
+
def orig_priority
|
312
|
+
@orig.match(self.class.priority_regex)[1] if @orig =~ self.class.priority_regex
|
313
|
+
end
|
314
|
+
|
315
|
+
def orig_created_on
|
316
|
+
begin
|
317
|
+
if @orig =~ self.class.created_on_regex
|
318
|
+
date = @orig.match self.class.created_on_regex
|
319
|
+
return Date.parse(date[1]) unless date.nil?
|
320
|
+
end
|
321
|
+
rescue; end
|
322
|
+
nil
|
323
|
+
end
|
324
|
+
|
325
|
+
def get_completed_date
|
326
|
+
begin
|
327
|
+
return Date.parse(self.class.done_regex.match(@orig)[1])
|
328
|
+
rescue; end
|
329
|
+
nil
|
330
|
+
end
|
331
|
+
|
332
|
+
def get_due_on_date
|
333
|
+
begin
|
334
|
+
return Date.parse(self.class.due_on_regex.match(@orig)[1])
|
335
|
+
rescue; end
|
336
|
+
nil
|
337
|
+
end
|
338
|
+
end
|
339
|
+
end
|
data/lib/todo-txt.rb
ADDED
data/lib/todo.rb
ADDED
data/reset.sh
ADDED
data/run.sh
ADDED
data/test/test_helper.rb
ADDED
data/todo-curses.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# Ensure we require the local version and not one we might have installed already
|
2
|
+
require File.join([File.dirname(__FILE__),'lib','todo','version.rb'])
|
3
|
+
spec = Gem::Specification.new do |s|
|
4
|
+
s.name = 'todo-curses'
|
5
|
+
s.version = Todo::VERSION
|
6
|
+
s.author = 'Loren Rogers'
|
7
|
+
s.email = 'loren@lorentrogers.com'
|
8
|
+
s.homepage = 'http://www.lorentrogers.com'
|
9
|
+
s.platform = Gem::Platform::RUBY
|
10
|
+
s.summary = 'An interactive terminal application for managing todo.txt files.'
|
11
|
+
s.files = `git ls-files`.split("
|
12
|
+
")
|
13
|
+
s.require_paths << 'lib'
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.extra_rdoc_files = ['README.rdoc','todo.rdoc']
|
16
|
+
s.rdoc_options << '--title' << 'todo-curses' << '--main' << 'README.rdoc' << '-ri'
|
17
|
+
s.bindir = 'bin'
|
18
|
+
s.executables << 'todo-curses'
|
19
|
+
s.add_development_dependency('rake')
|
20
|
+
s.add_development_dependency('rdoc')
|
21
|
+
s.add_development_dependency('aruba')
|
22
|
+
s.add_runtime_dependency('gli','2.13.4')
|
23
|
+
end
|
data/todo.rdoc
ADDED
metadata
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: todo-curses
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Loren Rogers
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-01-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rdoc
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: aruba
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: gli
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 2.13.4
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 2.13.4
|
69
|
+
description:
|
70
|
+
email: loren@lorentrogers.com
|
71
|
+
executables:
|
72
|
+
- todo-curses
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files:
|
75
|
+
- README.rdoc
|
76
|
+
- todo.rdoc
|
77
|
+
files:
|
78
|
+
- ".gitignore"
|
79
|
+
- Gemfile
|
80
|
+
- Gemfile.lock
|
81
|
+
- README.rdoc
|
82
|
+
- Rakefile
|
83
|
+
- bin/todo-curses
|
84
|
+
- features/step_definitions/todo_steps.rb
|
85
|
+
- features/support/env.rb
|
86
|
+
- features/todo.feature
|
87
|
+
- lib/todo-txt.rb
|
88
|
+
- lib/todo-txt/list.rb
|
89
|
+
- lib/todo-txt/logger.rb
|
90
|
+
- lib/todo-txt/task.rb
|
91
|
+
- lib/todo.rb
|
92
|
+
- lib/todo/todo_viewer.rb
|
93
|
+
- lib/todo/version.rb
|
94
|
+
- reset.sh
|
95
|
+
- run.sh
|
96
|
+
- test/default_test.rb
|
97
|
+
- test/test_helper.rb
|
98
|
+
- todo-curses.gemspec
|
99
|
+
- todo.rdoc
|
100
|
+
homepage: http://www.lorentrogers.com
|
101
|
+
licenses: []
|
102
|
+
metadata: {}
|
103
|
+
post_install_message:
|
104
|
+
rdoc_options:
|
105
|
+
- "--title"
|
106
|
+
- todo-curses
|
107
|
+
- "--main"
|
108
|
+
- README.rdoc
|
109
|
+
- "-ri"
|
110
|
+
require_paths:
|
111
|
+
- lib
|
112
|
+
- lib
|
113
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ">="
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
119
|
+
requirements:
|
120
|
+
- - ">="
|
121
|
+
- !ruby/object:Gem::Version
|
122
|
+
version: '0'
|
123
|
+
requirements: []
|
124
|
+
rubyforge_project:
|
125
|
+
rubygems_version: 2.4.8
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: An interactive terminal application for managing todo.txt files.
|
129
|
+
test_files: []
|
130
|
+
has_rdoc: true
|