task-tree 0.1.0

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