raindown 0.7.0 → 0.8.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.
- checksums.yaml +4 -4
- data/lib/nodes/endpoint_node.rb +3 -2
- data/lib/raindown/actions.rb +5 -3
- data/lib/raindown/bare_directives.rb +14 -27
- data/lib/raindown/blueprints.rb +255 -0
- data/lib/raindown/cli.rb +39 -54
- data/lib/raindown/config.rb +2 -3
- data/lib/raindown/directives.rb +30 -13
- data/lib/raindown/document.rb +21 -15
- data/lib/raindown/endpoint/endpoint_runner.rb +13 -5
- data/lib/raindown/node_blocks.rb +22 -16
- data/lib/raindown/node_injector.rb +7 -10
- data/lib/raindown/pages.rb +1 -1
- data/lib/raindown/pre_renderer.rb +18 -0
- data/lib/raindown/tui/blueprints/blueprints_renderer.rb +93 -0
- data/lib/raindown/tui/blueprints/blueprints_view.rb +146 -0
- data/lib/raindown/tui/blueprints/canvas.rb +58 -0
- data/lib/raindown/tui/blueprints/code_comments.rb +71 -0
- data/lib/raindown/tui/blueprints/filter.rb +77 -0
- data/lib/raindown/tui/blueprints/traced_code.rb +56 -0
- data/lib/raindown/tui/blueprints/tracing.rb +129 -0
- data/lib/raindown/tui/{feelings/code_box.rb → code_box.rb} +44 -30
- data/lib/raindown/tui/endpoints.rb +2 -2
- data/lib/raindown/tui/events.rb +3 -1
- data/lib/raindown/tui/feelings/bins.rb +5 -5
- data/lib/raindown/tui/feelings/{celebration.rb → celebration/celebration.rb} +5 -2
- data/lib/raindown/tui/feelings/{scene.rb → celebration/scene.rb} +4 -1
- data/lib/raindown/tui/feelings/cell_moves.rb +34 -0
- data/lib/raindown/tui/feelings/feelings_code_box.rb +40 -0
- data/lib/raindown/tui/feelings/feelings_log.rb +22 -19
- data/lib/raindown/tui/feelings/feelings_renderer.rb +94 -95
- data/lib/raindown/tui/feelings/feelings_view.rb +28 -32
- data/lib/raindown/tui/feelings/magnifier.rb +68 -0
- data/lib/raindown/tui/feelings/motion.rb +93 -0
- data/lib/raindown/tui/feelings/waffle/baked_waffle.rb +88 -0
- data/lib/raindown/tui/feelings/waffle/balloons.rb +81 -0
- data/lib/raindown/tui/feelings/waffle/confetti.rb +55 -0
- data/lib/raindown/tui/feelings/waffle/sparkles.rb +61 -0
- data/lib/raindown/tui/feelings/waffle/waffle.rb +62 -0
- data/lib/raindown/tui/feelings/weekly_batch.rb +24 -5
- data/lib/raindown/tui/layout.rb +25 -18
- data/lib/raindown/tui/navigator/commands.rb +32 -1
- data/lib/raindown/tui/navigator/mouse.rb +43 -0
- data/lib/raindown/tui/navigator/navigator.rb +40 -44
- data/lib/raindown/tui/navigator/{search.rb → teleport.rb} +14 -12
- data/lib/raindown/tui/navigator/views.rb +2 -3
- data/lib/raindown/tui/{workers/project.rb → project.rb} +33 -22
- data/lib/raindown/tui/{menu_overlay.rb → project_box.rb} +1 -1
- data/lib/raindown/tui/renderers/renderer.rb +1 -0
- data/lib/raindown/tui/saves.rb +22 -0
- data/lib/raindown/tui/{search_overlay.rb → search_box.rb} +27 -14
- data/lib/raindown/tui/terminal/input.rb +58 -7
- data/lib/raindown/tui/terminal/screen.rb +31 -26
- data/lib/raindown/tui/terminal/styles.rb +55 -28
- data/lib/raindown/tui/terminal/terminal_title.rb +31 -0
- data/lib/raindown/tui/text.rb +35 -12
- data/lib/raindown/tui/views/dashboard_view.rb +2 -2
- data/lib/raindown/tui/views/endpoint_view.rb +3 -3
- data/lib/raindown/tui/views/list_view.rb +1 -1
- data/lib/raindown/tui/views/outline_view.rb +5 -4
- data/lib/raindown/tui.rb +17 -7
- data/lib/raindown/watcher/changes.rb +33 -0
- data/lib/raindown/watcher/file_watcher.rb +58 -0
- data/lib/raindown/watcher/project.rb +47 -0
- data/lib/raindown/watcher/supervisor.rb +111 -0
- data/lib/raindown/{tui/workers → workers}/worker.rb +37 -29
- data/lib/raindown/workers/workers.rb +63 -0
- data/lib/raindown.rb +4 -4
- data/lib/version.rb +1 -1
- metadata +37 -17
- data/lib/raindown/project_worker.rb +0 -48
- data/lib/raindown/supervisor.rb +0 -160
- data/lib/raindown/tui/feelings/waffle.rb +0 -43
- data/lib/raindown/tui/workers/workers.rb +0 -53
- data/lib/raindown/watcher.rb +0 -57
- /data/lib/raindown/tui/feelings/{fireworks.rb → celebration/fireworks.rb} +0 -0
- /data/lib/raindown/tui/feelings/{rain.rb → celebration/rain.rb} +0 -0
data/lib/raindown/supervisor.rb
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'project_worker'
|
|
4
|
-
require_relative 'projects'
|
|
5
|
-
|
|
6
|
-
module Raindown
|
|
7
|
-
# Watches every Raindown project in a directory, like ~/dev, and writes back to each Markdown file as it's saved.
|
|
8
|
-
#
|
|
9
|
-
# Each project's nodes load in a process of its own, forked once the shared code has loaded, so starting a project
|
|
10
|
-
# costs almost nothing and two projects can have nodes of the same name. One listener watches the directory and sends
|
|
11
|
-
# each changed file to its project's process. Without fork, as on Windows, the projects share this process.
|
|
12
|
-
class Supervisor
|
|
13
|
-
# Watching skips these folders as they hold dependencies and builds, which Projects ignores too.
|
|
14
|
-
IGNORE = %r{(?:\A|/)(?:#{Projects::IGNORED.join('|')})/}
|
|
15
|
-
|
|
16
|
-
attr_reader :directory, :workers
|
|
17
|
-
|
|
18
|
-
def initialize(directory:, bare: false, execute: false)
|
|
19
|
-
@directory = File.expand_path(directory)
|
|
20
|
-
@bare = bare
|
|
21
|
-
@execute = execute
|
|
22
|
-
@workers = {}
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def start
|
|
26
|
-
require 'listen'
|
|
27
|
-
$stdout.sync = true
|
|
28
|
-
preload
|
|
29
|
-
|
|
30
|
-
Projects.roots(directory).each { worker(it) }
|
|
31
|
-
puts "Watching #{workers.size} #{workers.size == 1 ? 'project' : 'projects'} in #{directory} for changes..."
|
|
32
|
-
|
|
33
|
-
trap_interrupt
|
|
34
|
-
listener.start
|
|
35
|
-
sleep
|
|
36
|
-
ensure
|
|
37
|
-
stop
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
# Sends each changed file to its project, starting a worker for a new project, or restarting one when its nodes change.
|
|
41
|
-
def handle(paths)
|
|
42
|
-
paths.each do |path|
|
|
43
|
-
root = Projects.root(path) || next
|
|
44
|
-
|
|
45
|
-
if Projects.node?(path, root:)
|
|
46
|
-
restart(root)
|
|
47
|
-
elsif Projects.markdown?(path, root:)
|
|
48
|
-
worker(root).update(path)
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def stop
|
|
54
|
-
workers.each_value(&:stop)
|
|
55
|
-
workers.clear
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
private
|
|
59
|
-
|
|
60
|
-
def listener
|
|
61
|
-
Listen.to(directory, ignore: IGNORE) { |modified, added, _removed| handle(modified + added) }
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def worker(root)
|
|
65
|
-
workers[root] ||= begin
|
|
66
|
-
worker = ProjectWorker.new(root:, name: name(root), bare: @bare, execute: @execute)
|
|
67
|
-
fork? ? ForkedWorker.new(worker).tap(&:start) : worker.tap(&:start)
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Without fork the projects share this process, where nodes can't be unloaded.
|
|
72
|
-
def restart(root)
|
|
73
|
-
return warn("[#{name(root)}] Restart raindown to load the node changes") unless fork?
|
|
74
|
-
|
|
75
|
-
worker = workers.delete(root) || return
|
|
76
|
-
worker.stop
|
|
77
|
-
puts "[#{worker.name}] Reloading nodes"
|
|
78
|
-
worker(root)
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# A project in the directory is named by its path from there, and the project the directory is in by its folder.
|
|
82
|
-
def name(root)
|
|
83
|
-
return File.basename(root) unless root.start_with?(File.join(directory, ''))
|
|
84
|
-
|
|
85
|
-
root.delete_prefix(File.join(directory, ''))
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# Loaded before forking, so every project's process shares them instead of loading its own.
|
|
89
|
-
def preload
|
|
90
|
-
return unless fork?
|
|
91
|
-
|
|
92
|
-
require 'lownode'
|
|
93
|
-
require 'dirload'
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
def fork?
|
|
97
|
-
Process.respond_to?(:fork)
|
|
98
|
-
end
|
|
99
|
-
|
|
100
|
-
# Ctrl+C reaches every process in the terminal, so the workers stop with the watcher.
|
|
101
|
-
def trap_interrupt
|
|
102
|
-
trap('INT') { exit }
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
# A project's worker in a forked process, which reads the files to update from a pipe until the pipe closes.
|
|
107
|
-
# The process ends with exit! as it's forked mid-call, so exit would run the watcher's ensure and stop its siblings.
|
|
108
|
-
class ForkedWorker
|
|
109
|
-
def initialize(worker)
|
|
110
|
-
@worker = worker
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
def name
|
|
114
|
-
@worker.name
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def start
|
|
118
|
-
@writer&.close
|
|
119
|
-
reader, @writer = IO.pipe
|
|
120
|
-
@pid = fork do
|
|
121
|
-
@writer.close
|
|
122
|
-
trap('INT') { exit!(0) }
|
|
123
|
-
@worker.start
|
|
124
|
-
while (path = reader.gets)
|
|
125
|
-
@worker.update(path.chomp)
|
|
126
|
-
end
|
|
127
|
-
exit!(0)
|
|
128
|
-
end
|
|
129
|
-
reader.close
|
|
130
|
-
end
|
|
131
|
-
|
|
132
|
-
# A worker that stopped, such as from a crash, starts again for the next file.
|
|
133
|
-
def update(path, restarted: false)
|
|
134
|
-
start unless alive?
|
|
135
|
-
@writer.puts(path)
|
|
136
|
-
@writer.flush
|
|
137
|
-
rescue Errno::EPIPE
|
|
138
|
-
raise if restarted
|
|
139
|
-
|
|
140
|
-
start
|
|
141
|
-
update(path, restarted: true)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
def stop
|
|
145
|
-
@writer&.close
|
|
146
|
-
Process.kill('TERM', @pid) if alive?
|
|
147
|
-
Process.wait(@pid)
|
|
148
|
-
rescue Errno::ECHILD, Errno::ESRCH
|
|
149
|
-
nil
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
private
|
|
153
|
-
|
|
154
|
-
def alive?
|
|
155
|
-
@pid && Process.waitpid(@pid, Process::WNOHANG).nil?
|
|
156
|
-
rescue Errno::ECHILD
|
|
157
|
-
false
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
end
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Raindown
|
|
4
|
-
module TUI
|
|
5
|
-
# The waffle after the rain with a pat of butter, when a batch of code is refined, and the week's work is done:
|
|
6
|
-
# ╔═══╦═══╦═══╦═══╦═══╗
|
|
7
|
-
# ║ ║ ║ ║ ║ ║
|
|
8
|
-
# ╠═══╬═══╬═══╬═══╬═══╣
|
|
9
|
-
# ║ ║ ║███║ ║ ║
|
|
10
|
-
# ╠═══╬═══╬═══╬═══╬═══╣
|
|
11
|
-
# ║ ║ ║ ║ ║ ║
|
|
12
|
-
# ╚═══╩═══╩═══╩═══╩═══╝
|
|
13
|
-
module Waffle
|
|
14
|
-
EDGE = :code_number
|
|
15
|
-
BUTTER = :button
|
|
16
|
-
WAFFLE = [
|
|
17
|
-
[['╔═══╦═══╦═══╦═══╦═══╗', EDGE]],
|
|
18
|
-
[['║ ║ ║ ║ ║ ║', EDGE]],
|
|
19
|
-
[['╠═══╬═══╬═══╬═══╬═══╣', EDGE]],
|
|
20
|
-
[['║ ║ ║', EDGE], ['███', BUTTER], ['║ ║ ║', EDGE]],
|
|
21
|
-
[['╠═══╬═══╬═══╬═══╬═══╣', EDGE]],
|
|
22
|
-
[['║ ║ ║ ║ ║ ║', EDGE]],
|
|
23
|
-
[['╚═══╩═══╩═══╩═══╩═══╝', EDGE]],
|
|
24
|
-
[['100% refined', :feelings]],
|
|
25
|
-
[['The work is mysterious and important.', :feelings]]
|
|
26
|
-
].freeze
|
|
27
|
-
|
|
28
|
-
private
|
|
29
|
-
|
|
30
|
-
def waffle
|
|
31
|
-
top = [(height - WAFFLE.length) / 2, 0].max
|
|
32
|
-
lines = [*Array.new(top, []), *WAFFLE, *Array.new(height, [])].first(height)
|
|
33
|
-
lines.map { centred(it) }
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# The parts in the middle of the width.
|
|
37
|
-
def centred(parts)
|
|
38
|
-
left = [(width - parts.sum { display_width(it[0]) }) / 2, 0].max
|
|
39
|
-
padded([segment('', :blank, width: left), *parts.map { |text, style| segment(text, style, width: display_width(text)) }])
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
end
|
|
43
|
-
end
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../../projects'
|
|
4
|
-
require_relative 'worker'
|
|
5
|
-
|
|
6
|
-
module Raindown
|
|
7
|
-
module TUI
|
|
8
|
-
# A Worker for each project in the terminal UI's folder: the folder's own, and every project inside it.
|
|
9
|
-
# A file's requests go to the worker of the project it's in, the nearest one above it, even when it's new.
|
|
10
|
-
# Workers start when they're first asked something.
|
|
11
|
-
class Workers
|
|
12
|
-
attr_reader :root, :roots
|
|
13
|
-
|
|
14
|
-
def initialize(root:, fork: Worker.fork?)
|
|
15
|
-
@root = root
|
|
16
|
-
@fork = fork
|
|
17
|
-
@workers = {}
|
|
18
|
-
find_roots
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def for(path)
|
|
22
|
-
worker(Projects.root(path) || root)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# A worker for each project, as they were found when the workers last started.
|
|
26
|
-
def each(&)
|
|
27
|
-
roots.map { worker(it) }.each(&)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Stops the workers, so the next requests start them again, loading their projects' nodes again,
|
|
31
|
-
# and finds the projects again, like ones added since.
|
|
32
|
-
def restart
|
|
33
|
-
stop
|
|
34
|
-
@workers.clear
|
|
35
|
-
find_roots
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def stop
|
|
39
|
-
@workers.each_value(&:stop)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def worker(project)
|
|
45
|
-
@workers[project] ||= Worker.new(root: project, fork: @fork)
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def find_roots
|
|
49
|
-
@roots = [root, *Projects.roots(root)].uniq.sort
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|
data/lib/raindown/watcher.rb
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../raindown'
|
|
4
|
-
require_relative 'rewriter'
|
|
5
|
-
|
|
6
|
-
module Raindown
|
|
7
|
-
# Watches one file and writes back to it as it's saved, like its TOC, tables and endpoint responses.
|
|
8
|
-
class Watcher
|
|
9
|
-
attr_reader :path, :bare, :execute
|
|
10
|
-
|
|
11
|
-
def initialize(path:, bare: false, execute: false)
|
|
12
|
-
@path = path
|
|
13
|
-
@bare = bare
|
|
14
|
-
@execute = execute
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def start
|
|
18
|
-
require 'listen'
|
|
19
|
-
|
|
20
|
-
$stdout.sync = true
|
|
21
|
-
|
|
22
|
-
safely { rewrite }
|
|
23
|
-
puts "Watching #{path} for changes..."
|
|
24
|
-
|
|
25
|
-
listener.start
|
|
26
|
-
sleep
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
private
|
|
30
|
-
|
|
31
|
-
def listener
|
|
32
|
-
directory = File.dirname(File.expand_path(path))
|
|
33
|
-
filename = File.basename(path)
|
|
34
|
-
|
|
35
|
-
Listen.to(directory) do |modified, added, _removed|
|
|
36
|
-
next unless (modified + added).any? { |f| File.basename(f) == filename }
|
|
37
|
-
|
|
38
|
-
# Writing back saves the file again, and that save changes nothing, so it ends there.
|
|
39
|
-
safely { puts "Updated #{path}" if rewrite }
|
|
40
|
-
end
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# An error in the file is shown rather than stopping the watcher, so it can be fixed and saved again.
|
|
44
|
-
def safely
|
|
45
|
-
yield
|
|
46
|
-
rescue StandardError => e
|
|
47
|
-
raise if ENV['RAINDOWN_DEBUG']
|
|
48
|
-
|
|
49
|
-
warn "raindown: #{path}: #{e.message}"
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
# The files' metadata is loaded again each time, so new and renamed files are listed.
|
|
53
|
-
def rewrite
|
|
54
|
-
Rewriter.update(path:, bare:, execute:, metadata: Pages.for(path))
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
File without changes
|
|
File without changes
|