task-tree 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,319 @@
1
+ require 'rubygems'
2
+ require 'tty-prompt'
3
+ require 'tree'
4
+ require 'json'
5
+ require 'open3'
6
+ require 'tty'
7
+
8
+ module TaskTree
9
+ class Tasky
10
+ def initialize(options)
11
+ @output = options.output || 'default.json'
12
+ @font_path = options.figlet_font || './fonts/larry3d'
13
+ @prompt = TTY::Prompt.new
14
+ @tree_root = Tree::TreeNode.new('__', '__')
15
+ @current_node = @tree_root
16
+ @screen = TTY::Screen
17
+ @animations = options.animations || false
18
+ end
19
+
20
+ def verify_dependencies?
21
+ out, err, = Open3.capture3('which figlet')
22
+ if err.include?('no figlet') || err.include?('not found')
23
+ puts ''
24
+ puts '---------------------------------------------------------------'
25
+ puts '--> unfortunately you need to install figlet for this to work '
26
+ puts '---------------------------------------------------------------'
27
+ return false
28
+ end
29
+ true
30
+ end
31
+
32
+ def start
33
+ return unless verify_dependencies?
34
+ restore_state
35
+ print_current
36
+
37
+ action = :quit
38
+ loop do
39
+ action = @prompt.expand('Next action?') do |q|
40
+ q.choice key: 'a', name: 'add', value: :add
41
+ q.choice key: 'u', name: 'up', value: :up
42
+ q.choice key: 'd', name: 'down', value: :down
43
+ q.choice key: 'n', name: 'next', value: :next
44
+ q.choice key: 'p', name: 'previous', value: :previous
45
+ q.choice key: 'P', name: 'print', value: :print
46
+ q.choice key: 'c', name: 'complete', value: :complete
47
+ q.choice key: 'q', name: 'quit', value: :quit
48
+ q.choice key: 'x', name: 'quit without saving', value: :exit
49
+ q.choice key: 's', name: 'start pommodoro timer', value: :pommo
50
+ q.choice key: 'l', name: 'clean display', value: :clean
51
+ q.choice key: '$', name: 'last', value: :last_sibling
52
+ q.choice key: '_', name: 'first', value: :first_sibling
53
+ end
54
+ handle_action(action)
55
+ break if action == :quit || action == :exit
56
+ end
57
+ save_state if action == :quit
58
+ end
59
+
60
+ def handle_action(action)
61
+ case action
62
+ when :clean
63
+ print_current
64
+ @prompt.keypress('_')
65
+ when :add
66
+ prompt_and_add_task
67
+ print_current(action)
68
+ save_state
69
+ when :up
70
+ move_up
71
+ print_current(action)
72
+ when :down
73
+ move_down
74
+ print_current(action)
75
+ when :print
76
+ print_tree
77
+ print_current(action)
78
+ when :complete
79
+ set_as_complete
80
+ print_current(action)
81
+ save_state
82
+ when :previous
83
+ move_previous
84
+ print_current(action)
85
+ when :next
86
+ move_next
87
+ print_current(action)
88
+ when :last_sibling
89
+ move_last
90
+ print_current
91
+ when :first_sibling
92
+ move_first
93
+ print_current
94
+ when :pommo
95
+ when :pommo
96
+ start_pommo
97
+ end
98
+ end
99
+
100
+ def start_pommo
101
+ minutes = @prompt.ask('how many minutes?', convert: :int)
102
+ bar = TTY::ProgressBar.new('[:bar]', total: minutes*6)
103
+ (minutes*6).times do
104
+ sleep(10)
105
+ bar.advance(1)
106
+ # TODO: check for quit or pause?
107
+ end
108
+ end
109
+
110
+ def print_tree
111
+ @tree_root.print_tree
112
+ end
113
+
114
+ def select_with_menu
115
+ selected = @prompt.select("Current level:") do |menu|
116
+ @current_node.children.each do |c|
117
+ menu.choice c.name, c
118
+ end
119
+ end
120
+ @current_node = selected
121
+ end
122
+
123
+ def set_as_complete
124
+ @current_node.content = 'complete'
125
+ move_next
126
+ end
127
+
128
+ def move_up
129
+ @current_node = @current_node.parent if !@current_node.parent.nil?
130
+ end
131
+
132
+ def move_down
133
+ @current_node = @current_node.first_child unless @current_node.first_child.nil?
134
+ if @current_node.content == 'complete'
135
+ move_up unless move_next || move_previous
136
+ end
137
+ end
138
+
139
+ def move_previous(prev=@current_node)
140
+ return false if prev.is_only_child? || prev.is_first_sibling?
141
+ prev = prev.previous_sibling
142
+ if prev.content == 'complete'
143
+ prev = move_previous(prev)
144
+ else
145
+ @current_node = prev
146
+ end
147
+ true
148
+ end
149
+
150
+ def move_next(nxt=@current_node)
151
+ return false if nxt.is_only_child? || nxt.is_last_sibling?
152
+ nxt = nxt.next_sibling
153
+ if nxt.content == 'complete'
154
+ nxt = move_next(nxt)
155
+ else
156
+ @current_node = nxt
157
+ end
158
+ true
159
+ end
160
+
161
+ def move_last(current=@current_node)
162
+ last = current.last_sibling
163
+ if last.content == 'complete'
164
+ last = move_previous(last)
165
+ else
166
+ @current_node = last
167
+ end
168
+ end
169
+
170
+ def move_first(current=@current_node)
171
+ first = current.first_sibling
172
+ if first.content == 'complete'
173
+ first = move_next(first)
174
+ else
175
+ @current_node = first
176
+ end
177
+ end
178
+
179
+ def prompt_and_add_task
180
+ new_task = @prompt.ask('what to add?')
181
+ return if new_task.nil? || new_task.empty?
182
+ new_node = Tree::TreeNode.new(new_task, new_task)
183
+ @current_node << new_node
184
+ @current_node = new_node
185
+ end
186
+
187
+ def print_current(direction=nil)
188
+ if !@animations
189
+ direction = nil
190
+ end
191
+ empty_row = ""
192
+ @previous_page = @screen.height.times.map { empty_row } if @previous_page.nil?
193
+ page = @previous_page
194
+ task = draw_task(@current_node.content)
195
+ task_lines = task.split("\n")
196
+ case direction
197
+ when :down
198
+ blank = 0
199
+ @screen.height.times do |line_num|
200
+ start_task = @screen.height - task_lines.length
201
+ line = line_num >= start_task ? task_lines[line_num - start_task] : empty_row
202
+ blank +=1 if line == empty_row
203
+ puts page
204
+ sleep 0.01
205
+ # add after
206
+ page.push(line)
207
+ # remove first
208
+ page = page[1..-1]
209
+ end
210
+ when :up
211
+ task_lines.reverse!
212
+ @screen.height.times do |line_num|
213
+ line = task_lines.length > line_num ? task_lines[line_num] : empty_row
214
+ puts page
215
+ sleep 0.01
216
+ # add before
217
+ page.unshift(line)
218
+ # remove last
219
+ page = page[0..-2]
220
+ end
221
+ when :next
222
+ blank_lines = (@screen.height-task_lines.length).times.map { empty_row }
223
+ right_page = blank_lines + task_lines
224
+ joined = join_page(page, right_page)
225
+ speed = 3
226
+ ((@screen.width * 0.8).to_i / speed).times do |col_num|
227
+ joined = remove_page_col(joined, speed: speed)
228
+ print_from_left(joined)
229
+ # TODO: need to regulate speed here, moving previous is very slow
230
+ sleep 0.005
231
+ end
232
+ page = right_page
233
+ when :previous
234
+ blank_lines = (@screen.height-task_lines.length).times.map { empty_row }
235
+ left_page = blank_lines + task_lines
236
+ joined = join_page(left_page, page)
237
+ speed = 3
238
+ ((@screen.width * 0.666).to_i / speed).times do |col_num|
239
+ joined = remove_page_col(joined, reverse: true, speed: speed)
240
+ print_from_right(joined)
241
+ sleep 0.003
242
+ end
243
+ page = left_page
244
+ puts page
245
+ else
246
+ task = draw_task(@current_node.content)
247
+ task_lines = task.split("\n")
248
+ blank_lines = (@screen.height-task_lines.length).times.map { empty_row }
249
+ page = blank_lines + task_lines
250
+ puts task
251
+ end
252
+ @previous_page = page
253
+ end
254
+
255
+ def join_page(left, right)
256
+ return nil if left.length != right.length
257
+ joined = []
258
+ left.length.times do |i|
259
+ joined.push(left[i] + right[i]) unless left.empty?
260
+ end
261
+ joined
262
+ end
263
+
264
+ def print_from_left(page)
265
+ to_print = []
266
+ end_num = @screen.width-1
267
+ page.each { |line| to_print.push(line[0..end_num]) }
268
+ puts to_print
269
+ end
270
+
271
+ def print_from_right(page)
272
+ to_print = []
273
+ page.each { |line| to_print.push(line[-@screen.width..-1]) }
274
+ puts to_print
275
+ end
276
+
277
+ def remove_page_col(page, options={})
278
+ new_page = []
279
+ speed = options[:speed] || 1
280
+ page.each do |line|
281
+ if line.empty?
282
+ new_page.push(line)
283
+ else
284
+ if options[:reverse]
285
+ new_page.push(line[0..-speed])
286
+ else
287
+ new_page.push(line[speed..-1])
288
+ end
289
+ end
290
+ end
291
+ new_page
292
+ end
293
+
294
+ def draw_task(task)
295
+ width = @screen.width - 2
296
+ text = `figlet -f #{@font_path} -c -w #{width} #{task}`
297
+ text
298
+ end
299
+
300
+ def save_state
301
+ File.write(@output, @tree_root.to_json)
302
+ end
303
+
304
+ def restore_state
305
+ return unless File.exist?(@output)
306
+ data = File.read(@output)
307
+ parsed_data = JSON.parse(data)
308
+ @tree_root = load_tree(parsed_data)
309
+ @current_node = @tree_root
310
+ end
311
+
312
+ def load_tree(json_hash)
313
+ node = Tree::TreeNode.new(json_hash['name'], json_hash['content'])
314
+ json_hash['children'].each { |h| node << load_tree(h) } unless json_hash['children'].nil?
315
+ node
316
+ end
317
+ end
318
+
319
+ end
@@ -0,0 +1,3 @@
1
+ module TaskTree
2
+ VERSION = "0.1.0"
3
+ end
data/task-tree.gemspec ADDED
@@ -0,0 +1,29 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "tasktree/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "task-tree"
8
+ spec.version = TaskTree::VERSION
9
+ spec.authors = ["Ewoudt Kellerman"]
10
+ spec.email = ["ewoudt.kellerman@gmail.com"]
11
+
12
+ spec.summary = %q{Task Tree is a CLI tool that helps you keep track of your tasks in a tree structure}
13
+ spec.homepage = "https://github.com/hellola/task-tree"
14
+ spec.license = 'MIT'
15
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
16
+ f.match(%r{^(test|spec|features)/})
17
+ end
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency "bundler", "~> 1.16"
23
+ spec.add_dependency "rake", "~> 10.0"
24
+ spec.add_dependency "tty"
25
+ spec.add_dependency "tty-screen"
26
+ spec.add_dependency "rubytree"
27
+ spec.add_dependency "commander"
28
+ spec.add_development_dependency "byebug"
29
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: task-tree
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Ewoudt Kellerman
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-11-16 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: tty
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
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: tty-screen
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rubytree
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: commander
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'
97
+ - !ruby/object:Gem::Dependency
98
+ name: byebug
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description:
112
+ email:
113
+ - ewoudt.kellerman@gmail.com
114
+ executables:
115
+ - task-tree
116
+ extensions: []
117
+ extra_rdoc_files: []
118
+ files:
119
+ - ".gitignore"
120
+ - Gemfile
121
+ - Gemfile.lock
122
+ - LICENSE.txt
123
+ - README.md
124
+ - Rakefile
125
+ - bin/console
126
+ - bin/setup
127
+ - exe/task-tree
128
+ - fonts/larry3d.flf
129
+ - lib/tasktree/tasky.rb
130
+ - lib/tasktree/version.rb
131
+ - task-tree.gemspec
132
+ homepage: https://github.com/hellola/task-tree
133
+ licenses:
134
+ - MIT
135
+ metadata: {}
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ required_rubygems_version: !ruby/object:Gem::Requirement
146
+ requirements:
147
+ - - ">="
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ requirements: []
151
+ rubyforge_project:
152
+ rubygems_version: 2.5.2
153
+ signing_key:
154
+ specification_version: 4
155
+ summary: Task Tree is a CLI tool that helps you keep track of your tasks in a tree
156
+ structure
157
+ test_files: []