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.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nodes/endpoint_node.rb +3 -2
  3. data/lib/raindown/actions.rb +5 -3
  4. data/lib/raindown/bare_directives.rb +14 -27
  5. data/lib/raindown/blueprints.rb +255 -0
  6. data/lib/raindown/chart/chart.rb +12 -1
  7. data/lib/raindown/cli.rb +39 -54
  8. data/lib/raindown/config.rb +2 -3
  9. data/lib/raindown/directives.rb +30 -13
  10. data/lib/raindown/document.rb +22 -20
  11. data/lib/raindown/endpoint/curl.rb +11 -7
  12. data/lib/raindown/endpoint/endpoint_runner.rb +13 -5
  13. data/lib/raindown/node_blocks.rb +31 -22
  14. data/lib/raindown/node_injector.rb +7 -10
  15. data/lib/raindown/nodes.rb +1 -0
  16. data/lib/raindown/pages.rb +1 -1
  17. data/lib/raindown/pre_renderer.rb +18 -0
  18. data/lib/raindown/stat.rb +53 -0
  19. data/lib/raindown/tui/blueprints/blueprints_renderer.rb +93 -0
  20. data/lib/raindown/tui/blueprints/blueprints_view.rb +146 -0
  21. data/lib/raindown/tui/blueprints/canvas.rb +58 -0
  22. data/lib/raindown/tui/blueprints/code_comments.rb +71 -0
  23. data/lib/raindown/tui/blueprints/filter.rb +77 -0
  24. data/lib/raindown/tui/blueprints/traced_code.rb +56 -0
  25. data/lib/raindown/tui/blueprints/tracing.rb +129 -0
  26. data/lib/raindown/tui/code_box.rb +143 -0
  27. data/lib/raindown/tui/endpoints.rb +2 -2
  28. data/lib/raindown/tui/events.rb +18 -1
  29. data/lib/raindown/tui/feelings/batch.rb +47 -0
  30. data/lib/raindown/tui/feelings/bins.rb +67 -0
  31. data/lib/raindown/tui/feelings/celebration/celebration.rb +28 -0
  32. data/lib/raindown/tui/feelings/celebration/fireworks.rb +75 -0
  33. data/lib/raindown/tui/feelings/celebration/rain.rb +66 -0
  34. data/lib/raindown/tui/feelings/celebration/scene.rb +40 -0
  35. data/lib/raindown/tui/feelings/cell_moves.rb +34 -0
  36. data/lib/raindown/tui/feelings/code_methods.rb +134 -0
  37. data/lib/raindown/tui/feelings/feelings_code_box.rb +40 -0
  38. data/lib/raindown/tui/feelings/feelings_log.rb +136 -0
  39. data/lib/raindown/tui/feelings/feelings_renderer.rb +183 -0
  40. data/lib/raindown/tui/feelings/feelings_view.rb +157 -0
  41. data/lib/raindown/tui/feelings/magnifier.rb +68 -0
  42. data/lib/raindown/tui/feelings/motion.rb +93 -0
  43. data/lib/raindown/tui/feelings/waffle/baked_waffle.rb +88 -0
  44. data/lib/raindown/tui/feelings/waffle/balloons.rb +81 -0
  45. data/lib/raindown/tui/feelings/waffle/confetti.rb +55 -0
  46. data/lib/raindown/tui/feelings/waffle/sparkles.rb +61 -0
  47. data/lib/raindown/tui/feelings/waffle/waffle.rb +62 -0
  48. data/lib/raindown/tui/feelings/weekly_batch.rb +51 -0
  49. data/lib/raindown/tui/file_tree.rb +6 -5
  50. data/lib/raindown/tui/highlighter.rb +45 -0
  51. data/lib/raindown/tui/layout.rb +32 -18
  52. data/lib/raindown/tui/navigator/commands.rb +46 -2
  53. data/lib/raindown/tui/navigator/mouse.rb +43 -0
  54. data/lib/raindown/tui/navigator/navigator.rb +54 -25
  55. data/lib/raindown/tui/navigator/teleport.rb +128 -0
  56. data/lib/raindown/tui/navigator/views.rb +5 -3
  57. data/lib/raindown/tui/{workers/project.rb → project.rb} +38 -27
  58. data/lib/raindown/tui/{menu_overlay.rb → project_box.rb} +2 -2
  59. data/lib/raindown/tui/renderers/dashboard_renderer.rb +4 -4
  60. data/lib/raindown/tui/renderers/endpoint_renderer.rb +14 -10
  61. data/lib/raindown/tui/renderers/outline_renderer.rb +4 -4
  62. data/lib/raindown/tui/renderers/renderer.rb +4 -0
  63. data/lib/raindown/tui/row.rb +4 -2
  64. data/lib/raindown/tui/rows.rb +2 -1
  65. data/lib/raindown/tui/saves.rb +22 -0
  66. data/lib/raindown/tui/search_box.rb +86 -0
  67. data/lib/raindown/tui/terminal/input.rb +95 -7
  68. data/lib/raindown/tui/terminal/screen.rb +37 -29
  69. data/lib/raindown/tui/terminal/styles.rb +111 -26
  70. data/lib/raindown/tui/terminal/terminal_title.rb +31 -0
  71. data/lib/raindown/tui/text.rb +62 -12
  72. data/lib/raindown/tui/title.rb +1 -1
  73. data/lib/raindown/tui/views/dashboard_view.rb +7 -5
  74. data/lib/raindown/tui/views/endpoint_view.rb +4 -2
  75. data/lib/raindown/tui/views/list_view.rb +2 -0
  76. data/lib/raindown/tui/views/outline_view.rb +16 -6
  77. data/lib/raindown/tui.rb +19 -7
  78. data/lib/raindown/watcher/changes.rb +33 -0
  79. data/lib/raindown/watcher/file_watcher.rb +58 -0
  80. data/lib/raindown/watcher/project.rb +47 -0
  81. data/lib/raindown/watcher/supervisor.rb +111 -0
  82. data/lib/raindown/{tui/workers → workers}/worker.rb +37 -29
  83. data/lib/raindown/workers/workers.rb +63 -0
  84. data/lib/raindown.rb +4 -4
  85. data/lib/version.rb +1 -1
  86. metadata +60 -9
  87. data/lib/raindown/project_worker.rb +0 -48
  88. data/lib/raindown/supervisor.rb +0 -160
  89. data/lib/raindown/tui/workers/workers.rb +0 -53
  90. 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
- # Directives show in bold and code is highlighted, unless the file's project changes them in .raindown/config.yml.
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
- # Directives like "toc:" export to HTML as bold or comments depending on hide_directives config.
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, hide_directives: false, highlight_code: true)
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", hide: hide_directives)
58
+ Directives.render("#{html.chomp}\n")
59
59
  end
60
60
  end
61
61
  end
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Raindown
4
- VERSION = '0.6.0'
4
+ VERSION = '0.8.0'
5
5
  end
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.6.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-27 00:00:00.000000000 Z
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/supervisor.rb
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
@@ -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
@@ -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