raindown 0.6.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/chart/chart.rb +12 -1
- 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 +22 -20
- data/lib/raindown/endpoint/curl.rb +11 -7
- data/lib/raindown/endpoint/endpoint_runner.rb +13 -5
- data/lib/raindown/node_blocks.rb +31 -22
- data/lib/raindown/node_injector.rb +7 -10
- data/lib/raindown/nodes.rb +1 -0
- data/lib/raindown/pages.rb +1 -1
- data/lib/raindown/pre_renderer.rb +18 -0
- data/lib/raindown/stat.rb +53 -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/code_box.rb +143 -0
- data/lib/raindown/tui/endpoints.rb +2 -2
- data/lib/raindown/tui/events.rb +18 -1
- data/lib/raindown/tui/feelings/batch.rb +47 -0
- data/lib/raindown/tui/feelings/bins.rb +67 -0
- data/lib/raindown/tui/feelings/celebration/celebration.rb +28 -0
- data/lib/raindown/tui/feelings/celebration/fireworks.rb +75 -0
- data/lib/raindown/tui/feelings/celebration/rain.rb +66 -0
- data/lib/raindown/tui/feelings/celebration/scene.rb +40 -0
- data/lib/raindown/tui/feelings/cell_moves.rb +34 -0
- data/lib/raindown/tui/feelings/code_methods.rb +134 -0
- data/lib/raindown/tui/feelings/feelings_code_box.rb +40 -0
- data/lib/raindown/tui/feelings/feelings_log.rb +136 -0
- data/lib/raindown/tui/feelings/feelings_renderer.rb +183 -0
- data/lib/raindown/tui/feelings/feelings_view.rb +157 -0
- 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 +51 -0
- data/lib/raindown/tui/file_tree.rb +6 -5
- data/lib/raindown/tui/highlighter.rb +45 -0
- data/lib/raindown/tui/layout.rb +32 -18
- data/lib/raindown/tui/navigator/commands.rb +46 -2
- data/lib/raindown/tui/navigator/mouse.rb +43 -0
- data/lib/raindown/tui/navigator/navigator.rb +54 -25
- data/lib/raindown/tui/navigator/teleport.rb +128 -0
- data/lib/raindown/tui/navigator/views.rb +5 -3
- data/lib/raindown/tui/{workers/project.rb → project.rb} +38 -27
- data/lib/raindown/tui/{menu_overlay.rb → project_box.rb} +2 -2
- data/lib/raindown/tui/renderers/dashboard_renderer.rb +4 -4
- data/lib/raindown/tui/renderers/endpoint_renderer.rb +14 -10
- data/lib/raindown/tui/renderers/outline_renderer.rb +4 -4
- data/lib/raindown/tui/renderers/renderer.rb +4 -0
- data/lib/raindown/tui/row.rb +4 -2
- data/lib/raindown/tui/rows.rb +2 -1
- data/lib/raindown/tui/saves.rb +22 -0
- data/lib/raindown/tui/search_box.rb +86 -0
- data/lib/raindown/tui/terminal/input.rb +95 -7
- data/lib/raindown/tui/terminal/screen.rb +37 -29
- data/lib/raindown/tui/terminal/styles.rb +111 -26
- data/lib/raindown/tui/terminal/terminal_title.rb +31 -0
- data/lib/raindown/tui/text.rb +62 -12
- data/lib/raindown/tui/title.rb +1 -1
- data/lib/raindown/tui/views/dashboard_view.rb +7 -5
- data/lib/raindown/tui/views/endpoint_view.rb +4 -2
- data/lib/raindown/tui/views/list_view.rb +2 -0
- data/lib/raindown/tui/views/outline_view.rb +16 -6
- data/lib/raindown/tui.rb +19 -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 +60 -9
- data/lib/raindown/project_worker.rb +0 -48
- data/lib/raindown/supervisor.rb +0 -160
- data/lib/raindown/tui/workers/workers.rb +0 -53
- data/lib/raindown/watcher.rb +0 -57
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../projects'
|
|
4
|
+
require_relative 'worker'
|
|
5
|
+
|
|
6
|
+
module Raindown
|
|
7
|
+
# A Worker for each project in a folder: the folder's own, and every project inside it, each answering with what the
|
|
8
|
+
# block makes from its root, like the terminal UI's Project. A file's requests go to the worker of the project it's in,
|
|
9
|
+
# the nearest one above it, even when it's new, or else the folder's. Workers start when they're first asked something.
|
|
10
|
+
class Workers
|
|
11
|
+
attr_reader :root
|
|
12
|
+
|
|
13
|
+
def initialize(root:, fork: Worker.fork?, quiet: true, &answering)
|
|
14
|
+
@root = root
|
|
15
|
+
@fork = fork
|
|
16
|
+
@quiet = quiet
|
|
17
|
+
@answering = answering
|
|
18
|
+
@workers = {}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def for(path) = self[Projects.root(path) || root]
|
|
22
|
+
|
|
23
|
+
def [](project)
|
|
24
|
+
@workers[project] ||= Worker.new(root: project, fork: @fork, quiet: @quiet) { @answering.call(project) }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# The folder's project and those inside it, found when they're first asked for, and again after a restart.
|
|
28
|
+
def roots = @roots ||= [root, *Projects.roots(root)].uniq.sort
|
|
29
|
+
|
|
30
|
+
# A worker for each project, as they were found when the workers last started.
|
|
31
|
+
def each(&)
|
|
32
|
+
roots.map { self[it] }.each(&)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# The workers asked something, by their project's root.
|
|
36
|
+
def started = @workers.dup
|
|
37
|
+
|
|
38
|
+
# Stops the project's worker, so its next request starts it again, loading its nodes again, and returns whether it
|
|
39
|
+
# had started. Without a project, they all stop, and the projects are found again, like ones added since.
|
|
40
|
+
def restart(project = nil)
|
|
41
|
+
return restart_project(project) if project
|
|
42
|
+
|
|
43
|
+
stop
|
|
44
|
+
@roots = nil
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Stops the workers, so the next requests start them again.
|
|
48
|
+
def stop
|
|
49
|
+
@workers.each_value(&:stop)
|
|
50
|
+
@workers.clear
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def restart_project(project)
|
|
56
|
+
worker = @workers.delete(project)
|
|
57
|
+
return false unless worker
|
|
58
|
+
|
|
59
|
+
worker.stop
|
|
60
|
+
true
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/lib/raindown.rb
CHANGED
|
@@ -37,16 +37,16 @@ module Raindown
|
|
|
37
37
|
|
|
38
38
|
module_function
|
|
39
39
|
|
|
40
|
-
#
|
|
40
|
+
# Code is highlighted, unless the file's project changes it in .raindown/config.yml.
|
|
41
41
|
def render_file(path:, metadata: NoMetadata, bare: false, **options)
|
|
42
42
|
options = Config.render_options(path).merge(options)
|
|
43
43
|
render(markdown: File.read(path, encoding: Encoding::UTF_8), metadata:, bare: bare || BareDirectives.file?(path), **options)
|
|
44
44
|
end
|
|
45
45
|
|
|
46
46
|
# Front matter keys are variables, like {title} or <{ title }>.
|
|
47
|
-
#
|
|
47
|
+
# Bare directives like "toc:" show in bold in the HTML, and the rest stay hidden, as comments.
|
|
48
48
|
# Code blocks are highlighted, unless highlight_code is false for a site that highlights code in the browser.
|
|
49
|
-
def render(markdown:, metadata: NoMetadata, bare: false,
|
|
49
|
+
def render(markdown:, metadata: NoMetadata, bare: false, highlight_code: true)
|
|
50
50
|
bare ||= BareDirectives.front_matter?(markdown)
|
|
51
51
|
|
|
52
52
|
highlighting(highlight_code) do
|
|
@@ -55,7 +55,7 @@ module Raindown
|
|
|
55
55
|
html = assembled(markdown:, metadata:, bare:)
|
|
56
56
|
html = Nodes.render_placeholders(html, document: markdown, metadata:, bare:, html:)
|
|
57
57
|
|
|
58
|
-
Directives.render("#{html.chomp}\n"
|
|
58
|
+
Directives.render("#{html.chomp}\n")
|
|
59
59
|
end
|
|
60
60
|
end
|
|
61
61
|
end
|
data/lib/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: raindown
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- maedi
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2026-
|
|
10
|
+
date: 2026-11-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: antlers
|
|
@@ -135,6 +135,20 @@ dependencies:
|
|
|
135
135
|
- - ">="
|
|
136
136
|
- !ruby/object:Gem::Version
|
|
137
137
|
version: '0'
|
|
138
|
+
- !ruby/object:Gem::Dependency
|
|
139
|
+
name: rouge
|
|
140
|
+
requirement: !ruby/object:Gem::Requirement
|
|
141
|
+
requirements:
|
|
142
|
+
- - "~>"
|
|
143
|
+
- !ruby/object:Gem::Version
|
|
144
|
+
version: '5.1'
|
|
145
|
+
type: :runtime
|
|
146
|
+
prerelease: false
|
|
147
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
148
|
+
requirements:
|
|
149
|
+
- - "~>"
|
|
150
|
+
- !ruby/object:Gem::Version
|
|
151
|
+
version: '5.1'
|
|
138
152
|
- !ruby/object:Gem::Dependency
|
|
139
153
|
name: trees
|
|
140
154
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -183,6 +197,7 @@ files:
|
|
|
183
197
|
- lib/raindown.rb
|
|
184
198
|
- lib/raindown/actions.rb
|
|
185
199
|
- lib/raindown/bare_directives.rb
|
|
200
|
+
- lib/raindown/blueprints.rb
|
|
186
201
|
- lib/raindown/chart/bars.rb
|
|
187
202
|
- lib/raindown/chart/chart.rb
|
|
188
203
|
- lib/raindown/chart/columns.rb
|
|
@@ -209,25 +224,56 @@ files:
|
|
|
209
224
|
- lib/raindown/page.rb
|
|
210
225
|
- lib/raindown/pages.rb
|
|
211
226
|
- lib/raindown/pre_renderer.rb
|
|
212
|
-
- lib/raindown/project_worker.rb
|
|
213
227
|
- lib/raindown/projects.rb
|
|
214
228
|
- lib/raindown/rewriter.rb
|
|
215
|
-
- lib/raindown/
|
|
229
|
+
- lib/raindown/stat.rb
|
|
216
230
|
- lib/raindown/table_formatter.rb
|
|
217
231
|
- lib/raindown/toc.rb
|
|
218
232
|
- lib/raindown/tui.rb
|
|
233
|
+
- lib/raindown/tui/blueprints/blueprints_renderer.rb
|
|
234
|
+
- lib/raindown/tui/blueprints/blueprints_view.rb
|
|
235
|
+
- lib/raindown/tui/blueprints/canvas.rb
|
|
236
|
+
- lib/raindown/tui/blueprints/code_comments.rb
|
|
237
|
+
- lib/raindown/tui/blueprints/filter.rb
|
|
238
|
+
- lib/raindown/tui/blueprints/traced_code.rb
|
|
239
|
+
- lib/raindown/tui/blueprints/tracing.rb
|
|
219
240
|
- lib/raindown/tui/card.rb
|
|
241
|
+
- lib/raindown/tui/code_box.rb
|
|
220
242
|
- lib/raindown/tui/endpoint.rb
|
|
221
243
|
- lib/raindown/tui/endpoints.rb
|
|
222
244
|
- lib/raindown/tui/events.rb
|
|
245
|
+
- lib/raindown/tui/feelings/batch.rb
|
|
246
|
+
- lib/raindown/tui/feelings/bins.rb
|
|
247
|
+
- lib/raindown/tui/feelings/celebration/celebration.rb
|
|
248
|
+
- lib/raindown/tui/feelings/celebration/fireworks.rb
|
|
249
|
+
- lib/raindown/tui/feelings/celebration/rain.rb
|
|
250
|
+
- lib/raindown/tui/feelings/celebration/scene.rb
|
|
251
|
+
- lib/raindown/tui/feelings/cell_moves.rb
|
|
252
|
+
- lib/raindown/tui/feelings/code_methods.rb
|
|
253
|
+
- lib/raindown/tui/feelings/feelings_code_box.rb
|
|
254
|
+
- lib/raindown/tui/feelings/feelings_log.rb
|
|
255
|
+
- lib/raindown/tui/feelings/feelings_renderer.rb
|
|
256
|
+
- lib/raindown/tui/feelings/feelings_view.rb
|
|
257
|
+
- lib/raindown/tui/feelings/magnifier.rb
|
|
258
|
+
- lib/raindown/tui/feelings/motion.rb
|
|
259
|
+
- lib/raindown/tui/feelings/waffle/baked_waffle.rb
|
|
260
|
+
- lib/raindown/tui/feelings/waffle/balloons.rb
|
|
261
|
+
- lib/raindown/tui/feelings/waffle/confetti.rb
|
|
262
|
+
- lib/raindown/tui/feelings/waffle/sparkles.rb
|
|
263
|
+
- lib/raindown/tui/feelings/waffle/waffle.rb
|
|
264
|
+
- lib/raindown/tui/feelings/weekly_batch.rb
|
|
223
265
|
- lib/raindown/tui/file_tree.rb
|
|
266
|
+
- lib/raindown/tui/highlighter.rb
|
|
224
267
|
- lib/raindown/tui/layout.rb
|
|
225
|
-
- lib/raindown/tui/menu_overlay.rb
|
|
226
268
|
- lib/raindown/tui/navigator/commands.rb
|
|
227
269
|
- lib/raindown/tui/navigator/focus.rb
|
|
270
|
+
- lib/raindown/tui/navigator/mouse.rb
|
|
228
271
|
- lib/raindown/tui/navigator/navigator.rb
|
|
229
272
|
- lib/raindown/tui/navigator/project_menu.rb
|
|
273
|
+
- lib/raindown/tui/navigator/teleport.rb
|
|
230
274
|
- lib/raindown/tui/navigator/views.rb
|
|
275
|
+
- lib/raindown/tui/project.rb
|
|
276
|
+
- lib/raindown/tui/project_box.rb
|
|
231
277
|
- lib/raindown/tui/renderers/dashboard_renderer.rb
|
|
232
278
|
- lib/raindown/tui/renderers/endpoint_renderer.rb
|
|
233
279
|
- lib/raindown/tui/renderers/list_renderer.rb
|
|
@@ -235,22 +281,27 @@ files:
|
|
|
235
281
|
- lib/raindown/tui/renderers/renderer.rb
|
|
236
282
|
- lib/raindown/tui/row.rb
|
|
237
283
|
- lib/raindown/tui/rows.rb
|
|
284
|
+
- lib/raindown/tui/saves.rb
|
|
285
|
+
- lib/raindown/tui/search_box.rb
|
|
238
286
|
- lib/raindown/tui/terminal/input.rb
|
|
239
287
|
- lib/raindown/tui/terminal/ncurses_shim.rb
|
|
240
288
|
- lib/raindown/tui/terminal/quiet.rb
|
|
241
289
|
- lib/raindown/tui/terminal/screen.rb
|
|
242
290
|
- lib/raindown/tui/terminal/styles.rb
|
|
291
|
+
- lib/raindown/tui/terminal/terminal_title.rb
|
|
243
292
|
- lib/raindown/tui/text.rb
|
|
244
293
|
- lib/raindown/tui/title.rb
|
|
245
294
|
- lib/raindown/tui/views/dashboard_view.rb
|
|
246
295
|
- lib/raindown/tui/views/endpoint_view.rb
|
|
247
296
|
- lib/raindown/tui/views/list_view.rb
|
|
248
297
|
- lib/raindown/tui/views/outline_view.rb
|
|
249
|
-
- lib/raindown/tui/workers/project.rb
|
|
250
|
-
- lib/raindown/tui/workers/worker.rb
|
|
251
|
-
- lib/raindown/tui/workers/workers.rb
|
|
252
298
|
- lib/raindown/variables.rb
|
|
253
|
-
- lib/raindown/watcher.rb
|
|
299
|
+
- lib/raindown/watcher/changes.rb
|
|
300
|
+
- lib/raindown/watcher/file_watcher.rb
|
|
301
|
+
- lib/raindown/watcher/project.rb
|
|
302
|
+
- lib/raindown/watcher/supervisor.rb
|
|
303
|
+
- lib/raindown/workers/worker.rb
|
|
304
|
+
- lib/raindown/workers/workers.rb
|
|
254
305
|
- lib/version.rb
|
|
255
306
|
homepage: https://github.com/raindeer-rb/raindown
|
|
256
307
|
licenses: []
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative '../raindown'
|
|
4
|
-
require_relative 'rewriter'
|
|
5
|
-
|
|
6
|
-
module Raindown
|
|
7
|
-
# Writes back to one project's Markdown files as they're saved, like its TOCs, tables and endpoint responses,
|
|
8
|
-
# with the project's own nodes. Each line it shows starts with the project's name.
|
|
9
|
-
class ProjectWorker
|
|
10
|
-
attr_reader :root, :name
|
|
11
|
-
|
|
12
|
-
def initialize(root:, name: File.basename(root), bare: false, execute: false)
|
|
13
|
-
@root = root
|
|
14
|
-
@name = name
|
|
15
|
-
@bare = bare
|
|
16
|
-
@execute = execute
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Loads the project's nodes from its .raindown folder.
|
|
20
|
-
def start
|
|
21
|
-
Nodes.load(File.join(root, Nodes::FOLDER))
|
|
22
|
-
rescue StandardError, ScriptError => e
|
|
23
|
-
raise if ENV['RAINDOWN_DEBUG']
|
|
24
|
-
|
|
25
|
-
warn "[#{name}] raindown: #{Nodes::FOLDER}: #{e.message}"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Writing back saves the file again, and that save changes nothing, so it ends there.
|
|
29
|
-
def update(path)
|
|
30
|
-
changed = Rewriter.update(path:, bare: @bare, execute: @execute, metadata: Pages.for(path))
|
|
31
|
-
puts "[#{name}] Updated #{relative(path)}" if changed
|
|
32
|
-
rescue StandardError => e
|
|
33
|
-
raise if ENV['RAINDOWN_DEBUG']
|
|
34
|
-
|
|
35
|
-
# An error in the file is shown rather than stopping the watcher, so it can be fixed and saved again.
|
|
36
|
-
warn "[#{name}] raindown: #{relative(path)}: #{e.message}"
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Workers in this process have nothing to stop, unlike a ForkedWorker's process.
|
|
40
|
-
def stop; end
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
|
|
44
|
-
def relative(path)
|
|
45
|
-
File.expand_path(path).delete_prefix(File.join(root, ''))
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
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,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
|