ratatui_ruby 1.0.0 → 1.0.1
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/ext/ratatui_ruby/Cargo.lock +1 -1
- data/ext/ratatui_ruby/Cargo.toml +1 -1
- data/lib/ratatui_ruby/version.rb +1 -1
- metadata +1 -232
- data/.builds/ruby-3.2.yml +0 -54
- data/.builds/ruby-3.3.yml +0 -54
- data/.builds/ruby-3.4.yml +0 -54
- data/.builds/ruby-4.0.0.yml +0 -54
- data/.pre-commit-config.yaml +0 -16
- data/.rubocop.yml +0 -10
- data/AGENTS.md +0 -146
- data/CHANGELOG.md +0 -710
- data/README.md +0 -187
- data/README.rdoc +0 -302
- data/Rakefile +0 -11
- data/Steepfile +0 -49
- data/doc/concepts/application_architecture.md +0 -321
- data/doc/concepts/application_testing.md +0 -193
- data/doc/concepts/async.md +0 -190
- data/doc/concepts/custom_widgets.md +0 -247
- data/doc/concepts/debugging.md +0 -401
- data/doc/concepts/event_handling.md +0 -162
- data/doc/concepts/interactive_design.md +0 -146
- data/doc/contributors/auditing/parity.md +0 -239
- data/doc/contributors/design/ruby_frontend.md +0 -420
- data/doc/contributors/design/rust_backend.md +0 -422
- data/doc/contributors/design.md +0 -11
- data/doc/contributors/developing_examples.md +0 -400
- data/doc/contributors/documentation_style.md +0 -121
- data/doc/contributors/index.md +0 -21
- data/doc/contributors/todo/align/api_completeness_audit-finished.md +0 -375
- data/doc/contributors/todo/align/api_completeness_audit-unfinished.md +0 -206
- data/doc/contributors/todo/align/terminal.md +0 -647
- data/doc/contributors/todo/future_work.md +0 -169
- data/doc/contributors/upstream_requests/tab_rects.md +0 -173
- data/doc/contributors/upstream_requests/title_rects.md +0 -132
- data/doc/custom.css +0 -22
- data/doc/getting_started/quickstart.md +0 -291
- data/doc/getting_started/why.md +0 -93
- data/doc/images/app_all_events.png +0 -0
- data/doc/images/app_cli_rich_moments.gif +0 -0
- data/doc/images/app_color_picker.png +0 -0
- data/doc/images/app_debugging_showcase.gif +0 -0
- data/doc/images/app_debugging_showcase.png +0 -0
- data/doc/images/app_login_form.png +0 -0
- data/doc/images/app_stateful_interaction.png +0 -0
- data/doc/images/verify_quickstart_dsl.png +0 -0
- data/doc/images/verify_quickstart_layout.png +0 -0
- data/doc/images/verify_quickstart_lifecycle.png +0 -0
- data/doc/images/verify_readme_usage.png +0 -0
- data/doc/images/widget_barchart.png +0 -0
- data/doc/images/widget_block.png +0 -0
- data/doc/images/widget_box.png +0 -0
- data/doc/images/widget_calendar.png +0 -0
- data/doc/images/widget_canvas.png +0 -0
- data/doc/images/widget_cell.png +0 -0
- data/doc/images/widget_center.png +0 -0
- data/doc/images/widget_chart.png +0 -0
- data/doc/images/widget_gauge.png +0 -0
- data/doc/images/widget_layout_split.png +0 -0
- data/doc/images/widget_line_gauge.png +0 -0
- data/doc/images/widget_list.png +0 -0
- data/doc/images/widget_map.png +0 -0
- data/doc/images/widget_overlay.png +0 -0
- data/doc/images/widget_popup.png +0 -0
- data/doc/images/widget_ratatui_logo.png +0 -0
- data/doc/images/widget_ratatui_mascot.png +0 -0
- data/doc/images/widget_rect.png +0 -0
- data/doc/images/widget_render.png +0 -0
- data/doc/images/widget_rich_text.png +0 -0
- data/doc/images/widget_scroll_text.png +0 -0
- data/doc/images/widget_scrollbar.png +0 -0
- data/doc/images/widget_sparkline.png +0 -0
- data/doc/images/widget_style_colors.png +0 -0
- data/doc/images/widget_table.png +0 -0
- data/doc/images/widget_tabs.png +0 -0
- data/doc/images/widget_text_width.png +0 -0
- data/doc/index.md +0 -39
- data/doc/troubleshooting/async.md +0 -4
- data/doc/troubleshooting/terminal_limitations.md +0 -131
- data/doc/troubleshooting/tui_output.md +0 -197
- data/examples/app_all_events/README.md +0 -114
- data/examples/app_all_events/app.rb +0 -98
- data/examples/app_all_events/model/app_model.rb +0 -159
- data/examples/app_all_events/model/event_color_cycle.rb +0 -43
- data/examples/app_all_events/model/event_entry.rb +0 -94
- data/examples/app_all_events/model/msg.rb +0 -39
- data/examples/app_all_events/model/timestamp.rb +0 -56
- data/examples/app_all_events/update.rb +0 -75
- data/examples/app_all_events/view/app_view.rb +0 -80
- data/examples/app_all_events/view/controls_view.rb +0 -54
- data/examples/app_all_events/view/counts_view.rb +0 -61
- data/examples/app_all_events/view/live_view.rb +0 -72
- data/examples/app_all_events/view/log_view.rb +0 -57
- data/examples/app_all_events/view.rb +0 -9
- data/examples/app_cli_rich_moments/README.md +0 -81
- data/examples/app_cli_rich_moments/app.rb +0 -189
- data/examples/app_color_picker/README.md +0 -156
- data/examples/app_color_picker/app.rb +0 -76
- data/examples/app_color_picker/clipboard.rb +0 -86
- data/examples/app_color_picker/color.rb +0 -193
- data/examples/app_color_picker/controls.rb +0 -92
- data/examples/app_color_picker/copy_dialog.rb +0 -168
- data/examples/app_color_picker/export_pane.rb +0 -128
- data/examples/app_color_picker/harmony.rb +0 -58
- data/examples/app_color_picker/input.rb +0 -176
- data/examples/app_color_picker/main_container.rb +0 -180
- data/examples/app_color_picker/palette.rb +0 -111
- data/examples/app_debugging_showcase/README.md +0 -119
- data/examples/app_debugging_showcase/app.rb +0 -318
- data/examples/app_login_form/README.md +0 -58
- data/examples/app_login_form/app.rb +0 -109
- data/examples/app_stateful_interaction/README.md +0 -35
- data/examples/app_stateful_interaction/app.rb +0 -328
- data/examples/timeout_demo.rb +0 -45
- data/examples/verify_quickstart_dsl/README.md +0 -55
- data/examples/verify_quickstart_dsl/app.rb +0 -49
- data/examples/verify_quickstart_layout/README.md +0 -77
- data/examples/verify_quickstart_layout/app.rb +0 -73
- data/examples/verify_quickstart_lifecycle/README.md +0 -68
- data/examples/verify_quickstart_lifecycle/app.rb +0 -62
- data/examples/verify_readme_usage/README.md +0 -49
- data/examples/verify_readme_usage/app.rb +0 -42
- data/examples/verify_website_managed/README.md +0 -48
- data/examples/verify_website_managed/app.rb +0 -36
- data/examples/verify_website_menu/README.md +0 -60
- data/examples/verify_website_menu/app.rb +0 -84
- data/examples/verify_website_spinner/README.md +0 -44
- data/examples/verify_website_spinner/app.rb +0 -34
- data/examples/widget_barchart/README.md +0 -58
- data/examples/widget_barchart/app.rb +0 -240
- data/examples/widget_block/README.md +0 -44
- data/examples/widget_block/app.rb +0 -258
- data/examples/widget_box/README.md +0 -54
- data/examples/widget_box/app.rb +0 -255
- data/examples/widget_calendar/README.md +0 -48
- data/examples/widget_calendar/app.rb +0 -115
- data/examples/widget_canvas/README.md +0 -31
- data/examples/widget_canvas/app.rb +0 -130
- data/examples/widget_cell/README.md +0 -45
- data/examples/widget_cell/app.rb +0 -112
- data/examples/widget_center/README.md +0 -33
- data/examples/widget_center/app.rb +0 -118
- data/examples/widget_chart/README.md +0 -50
- data/examples/widget_chart/app.rb +0 -220
- data/examples/widget_gauge/README.md +0 -50
- data/examples/widget_gauge/app.rb +0 -229
- data/examples/widget_layout_split/README.md +0 -53
- data/examples/widget_layout_split/app.rb +0 -260
- data/examples/widget_line_gauge/README.md +0 -50
- data/examples/widget_line_gauge/app.rb +0 -219
- data/examples/widget_list/README.md +0 -58
- data/examples/widget_list/app.rb +0 -384
- data/examples/widget_map/README.md +0 -48
- data/examples/widget_map/app.rb +0 -95
- data/examples/widget_overlay/README.md +0 -45
- data/examples/widget_overlay/app.rb +0 -250
- data/examples/widget_popup/README.md +0 -45
- data/examples/widget_popup/app.rb +0 -106
- data/examples/widget_ratatui_logo/README.md +0 -43
- data/examples/widget_ratatui_logo/app.rb +0 -104
- data/examples/widget_ratatui_mascot/README.md +0 -43
- data/examples/widget_ratatui_mascot/app.rb +0 -95
- data/examples/widget_rect/README.md +0 -53
- data/examples/widget_rect/app.rb +0 -222
- data/examples/widget_render/README.md +0 -46
- data/examples/widget_render/app.rb +0 -186
- data/examples/widget_render/app.rbs +0 -41
- data/examples/widget_rich_text/README.md +0 -44
- data/examples/widget_rich_text/app.rb +0 -193
- data/examples/widget_scroll_text/README.md +0 -46
- data/examples/widget_scroll_text/app.rb +0 -109
- data/examples/widget_scrollbar/README.md +0 -46
- data/examples/widget_scrollbar/app.rb +0 -155
- data/examples/widget_sparkline/README.md +0 -51
- data/examples/widget_sparkline/app.rb +0 -277
- data/examples/widget_style_colors/README.md +0 -43
- data/examples/widget_style_colors/app.rb +0 -83
- data/examples/widget_table/README.md +0 -57
- data/examples/widget_table/app.rb +0 -279
- data/examples/widget_tabs/README.md +0 -50
- data/examples/widget_tabs/app.rb +0 -183
- data/examples/widget_text_width/README.md +0 -44
- data/examples/widget_text_width/app.rb +0 -117
- data/migrate_to_buffer.rb +0 -145
- data/mise.toml +0 -8
- data/tasks/autodoc/examples.rb +0 -87
- data/tasks/autodoc/member.rb +0 -58
- data/tasks/autodoc/name.rb +0 -21
- data/tasks/autodoc.rake +0 -21
- data/tasks/bump/cargo_lockfile.rb +0 -21
- data/tasks/bump/changelog.rb +0 -47
- data/tasks/bump/header.rb +0 -32
- data/tasks/bump/history.rb +0 -32
- data/tasks/bump/links.rb +0 -69
- data/tasks/bump/manifest.rb +0 -33
- data/tasks/bump/ruby_gem.rb +0 -49
- data/tasks/bump/sem_ver.rb +0 -40
- data/tasks/bump/unreleased_section.rb +0 -56
- data/tasks/bump.rake +0 -51
- data/tasks/doc.rake +0 -887
- data/tasks/example_viewer.html.erb +0 -172
- data/tasks/extension.rake +0 -14
- data/tasks/license/headers_md.rb +0 -223
- data/tasks/license/headers_rb.rb +0 -210
- data/tasks/license/license_utils.rb +0 -130
- data/tasks/license/snippets_md.rb +0 -315
- data/tasks/license/snippets_rdoc.rb +0 -150
- data/tasks/license.rake +0 -91
- data/tasks/lint.rake +0 -170
- data/tasks/rdoc_config.rb +0 -29
- data/tasks/resources/build.yml.erb +0 -60
- data/tasks/resources/index.html.erb +0 -141
- data/tasks/resources/rubies.yml +0 -7
- data/tasks/sourcehut.rake +0 -110
- data/tasks/steep.rake +0 -11
- data/tasks/terminal_preview/app_screenshot.rb +0 -45
- data/tasks/terminal_preview/crash_report.rb +0 -54
- data/tasks/terminal_preview/example_app.rb +0 -27
- data/tasks/terminal_preview/launcher_script.rb +0 -48
- data/tasks/terminal_preview/preview_collection.rb +0 -60
- data/tasks/terminal_preview/preview_timing.rb +0 -24
- data/tasks/terminal_preview/safety_confirmation.rb +0 -58
- data/tasks/terminal_preview/saved_screenshot.rb +0 -56
- data/tasks/terminal_preview/system_appearance.rb +0 -13
- data/tasks/terminal_preview/terminal_window.rb +0 -138
- data/tasks/terminal_preview/window_id.rb +0 -16
- data/tasks/terminal_preview.rake +0 -30
- data/tasks/test.rake +0 -33
- data/tasks/website/index_page.rb +0 -30
- data/tasks/website/version.rb +0 -127
- data/tasks/website/version_menu.rb +0 -68
- data/tasks/website/versioned_documentation.rb +0 -83
- data/tasks/website/website.rb +0 -53
- data/tasks/website.rake +0 -28
data/migrate_to_buffer.rb
DELETED
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
#
|
|
6
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
7
|
-
#++
|
|
8
|
-
|
|
9
|
-
# !/usr/bin/env ruby
|
|
10
|
-
|
|
11
|
-
# Migration script to refactor widget renderers from Frame to Buffer
|
|
12
|
-
|
|
13
|
-
require "fileutils"
|
|
14
|
-
|
|
15
|
-
WIDGETS_DIR = "/Users/kerrick/Developer/ratatui_ruby/ext/ratatui_ruby/src/widgets"
|
|
16
|
-
|
|
17
|
-
# Files to update (excluding block.rs which is already done)
|
|
18
|
-
WIDGET_FILES = %w[
|
|
19
|
-
barchart.rs
|
|
20
|
-
calendar.rs
|
|
21
|
-
canvas.rs
|
|
22
|
-
center.rs
|
|
23
|
-
chart.rs
|
|
24
|
-
clear.rs
|
|
25
|
-
cursor.rs
|
|
26
|
-
gauge.rs
|
|
27
|
-
layout.rs
|
|
28
|
-
line_gauge.rs
|
|
29
|
-
list.rs
|
|
30
|
-
overlay.rs
|
|
31
|
-
paragraph.rs
|
|
32
|
-
ratatui_logo.rs
|
|
33
|
-
ratatui_mascot.rs
|
|
34
|
-
scrollbar.rs
|
|
35
|
-
sparkline.rs
|
|
36
|
-
table.rs
|
|
37
|
-
tabs.rs
|
|
38
|
-
].freeze
|
|
39
|
-
|
|
40
|
-
def migrate_file(filepath)
|
|
41
|
-
puts "Migrating #{File.basename(filepath)}..."
|
|
42
|
-
|
|
43
|
-
content = File.read(filepath)
|
|
44
|
-
original_content = content.dup
|
|
45
|
-
|
|
46
|
-
# 1. Update function signature
|
|
47
|
-
content.gsub!("pub fn render(frame: &mut Frame,", "pub fn render(buffer: &mut Buffer,")
|
|
48
|
-
content.gsub!("pub fn render_ratatui_mascot(frame: &mut Frame,", "pub fn render_ratatui_mascot(buffer: &mut Buffer,")
|
|
49
|
-
|
|
50
|
-
# 2. Update imports - Add Buffer, remove Frame
|
|
51
|
-
# Handle various import patterns
|
|
52
|
-
content.gsub!(/use ratatui::\{([^}]*),\s*Frame\s*\};/, 'use ratatui::{\1};')
|
|
53
|
-
content.gsub!(/use ratatui::\{Frame,\s*([^}]*)\};/, 'use ratatui::{\1};')
|
|
54
|
-
content.gsub!("use ratatui::Frame;", "")
|
|
55
|
-
|
|
56
|
-
# Add Buffer import if not present
|
|
57
|
-
unless content.match?(/use ratatui::(?:\{[^}]*)?buffer::Buffer/)
|
|
58
|
-
# Find the ratatui use statement and add buffer::Buffer
|
|
59
|
-
content.gsub!("use ratatui::{", "use ratatui::{buffer::Buffer, ")
|
|
60
|
-
content.gsub!("use ratatui::layout::Rect;", "use ratatui::{buffer::Buffer, layout::Rect};")
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
# 3. Update widget.render calls
|
|
64
|
-
# frame.render_widget(widget, area) → widget.render(area, buffer)
|
|
65
|
-
content.gsub!(/frame\.render_widget\(([^,]+),\s*([^)]+)\)/, '\1.render(\2, buffer)')
|
|
66
|
-
|
|
67
|
-
# 4. Update direct buffer access
|
|
68
|
-
content.gsub!("frame.buffer_mut()", "buffer")
|
|
69
|
-
|
|
70
|
-
# 5. Update recursive render_node calls
|
|
71
|
-
content.gsub!("render_node(frame,", "render_node(buffer,")
|
|
72
|
-
|
|
73
|
-
# 5.5. Update stateful widget rendering
|
|
74
|
-
# frame.render_stateful_widget(widget, area, state) → StatefulWidget::render(widget, area, buffer, state)
|
|
75
|
-
content.gsub!(/frame\.render_stateful_widget\(([^,]+),\s*([^,]+),\s*([^)]+)\)/, 'StatefulWidget::render(\1, \2, buffer, \3)')
|
|
76
|
-
|
|
77
|
-
# Add StatefulWidget import if stateful widgets are used
|
|
78
|
-
if content.match?(/StatefulWidget::render/) && !content.match?(/use ratatui::widgets::StatefulWidget/) && content.match?(/use ratatui::/)
|
|
79
|
-
content.sub!("use ratatui::{", "use ratatui::{widgets::StatefulWidget, ")
|
|
80
|
-
end
|
|
81
|
-
|
|
82
|
-
# 6. Clean up any double-added Buffer imports and duplicate Widget imports
|
|
83
|
-
content.gsub!(/buffer::Buffer,\s*buffer::Buffer,/, "buffer::Buffer,")
|
|
84
|
-
content.gsub!(/widgets::Widget,\s*widgets::Widget/, "widgets::Widget")
|
|
85
|
-
content.gsub!(/(use ratatui::\{[^}]*widgets::Widget[^}]*),\s*widgets::Widget/, '\1')
|
|
86
|
-
|
|
87
|
-
# 7. Fix Widget trait usage - make sure Widget is imported where .render is called
|
|
88
|
-
# Add Widget to imports if calling .render on widgets
|
|
89
|
-
if content.match?(/\.render\(area,\s*buffer\)/) && !content.match?(/use ratatui::widgets::Widget/) && content.match?(/use ratatui::\{/) && !content.match?(/widgets::Widget/)
|
|
90
|
-
content.sub!("use ratatui::{", "use ratatui::{widgets::Widget, ")
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# 8. For files that don't have proper Buffer imports yet, add them
|
|
94
|
-
if !content.match?(/use ratatui::\{[^}]*buffer::Buffer/) && content.match?(/use ratatui::/)
|
|
95
|
-
# Try to add to first ratatui import
|
|
96
|
-
content.sub!("use ratatui::", "use ratatui::{buffer::Buffer};\nuse ratatui::")
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# 9. Special case: cursor.rs uses frame.set_cursor_position which doesn't exist on Buffer
|
|
100
|
-
# Cursor widget needs special handling - it can't work with just Buffer
|
|
101
|
-
if File.basename(filepath) == "cursor.rs"
|
|
102
|
-
puts " ⚠ cursor.rs requires special handling - skipping set_cursor_position"
|
|
103
|
-
# This widget can't be fully migrated as set_cursor_position is Frame-only
|
|
104
|
-
# Will need manual fix or different approach
|
|
105
|
-
end
|
|
106
|
-
# Only write if content changed
|
|
107
|
-
if content != original_content
|
|
108
|
-
File.write(filepath, content)
|
|
109
|
-
puts " ✓ Updated #{File.basename(filepath)}"
|
|
110
|
-
true
|
|
111
|
-
else
|
|
112
|
-
puts " - No changes needed for #{File.basename(filepath)}"
|
|
113
|
-
false
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def main
|
|
118
|
-
puts "Starting widget renderer migration..."
|
|
119
|
-
puts "=" * 60
|
|
120
|
-
|
|
121
|
-
updated_count = 0
|
|
122
|
-
|
|
123
|
-
WIDGET_FILES.each do |filename|
|
|
124
|
-
filepath = File.join(WIDGETS_DIR, filename)
|
|
125
|
-
|
|
126
|
-
unless File.exist?(filepath)
|
|
127
|
-
puts " ⚠ File not found: #{filename}"
|
|
128
|
-
next
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Create backup
|
|
132
|
-
backup_path = "#{filepath}.premigration"
|
|
133
|
-
FileUtils.cp(filepath, backup_path) unless File.exist?(backup_path)
|
|
134
|
-
|
|
135
|
-
updated_count += 1 if migrate_file(filepath)
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
puts "=" * 60
|
|
139
|
-
puts "Migration complete!"
|
|
140
|
-
puts " Files updated: #{updated_count}/#{WIDGET_FILES.length}"
|
|
141
|
-
puts "\nBackups saved with .premigration extension"
|
|
142
|
-
puts "Run 'bundle exec rake compile' to verify the changes"
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
main if __FILE__ == $PROGRAM_NAME
|
data/mise.toml
DELETED
data/tasks/autodoc/examples.rb
DELETED
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
module Autodoc
|
|
9
|
-
class Examples
|
|
10
|
-
def self.sync
|
|
11
|
-
new.sync
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def sync
|
|
15
|
-
Dir.glob("{README.md,doc/**/*.md,examples/*/README.md}").each do |readme_path|
|
|
16
|
-
sync_readme(readme_path)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
private def sync_readme(readme_path)
|
|
21
|
-
content = File.read(readme_path)
|
|
22
|
-
dir = File.dirname(readme_path)
|
|
23
|
-
|
|
24
|
-
new_content = content.gsub(/<!-- SYNC:START:([^ ]+) -->.*?<!-- SYNC:END -->/m) do
|
|
25
|
-
marker_info = $1
|
|
26
|
-
source_rel_path, segment_id = marker_info.split(":")
|
|
27
|
-
|
|
28
|
-
# Support both repo-root-relative paths (no leading ./) and file-relative paths
|
|
29
|
-
source_path = if source_rel_path.start_with?("./", "../")
|
|
30
|
-
File.join(dir, source_rel_path)
|
|
31
|
-
else
|
|
32
|
-
source_rel_path # Already relative to repo root
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
unless File.exist?(source_path)
|
|
36
|
-
warn "Warning: Source file not found: #{source_path}"
|
|
37
|
-
next $&
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
source_content = File.read(source_path)
|
|
41
|
-
extracted_content = if segment_id
|
|
42
|
-
extract_segment(source_content, segment_id, source_path)
|
|
43
|
-
else
|
|
44
|
-
source_content
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Detect language from extension
|
|
48
|
-
ext = File.extname(source_path).delete(".")
|
|
49
|
-
lang = (ext == "rb") ? "ruby" : ext
|
|
50
|
-
|
|
51
|
-
# Build replacement
|
|
52
|
-
"<!-- SYNC:START:#{marker_info} -->\n```#{lang}\n#{extracted_content}```\n<!-- SYNC:END -->"
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
if new_content != content
|
|
56
|
-
puts "Syncing #{readme_path}..."
|
|
57
|
-
File.write(readme_path, new_content)
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
def extract_segment(content, segment_id, source_path)
|
|
62
|
-
start_marker = /#\s*\[SYNC:START:#{segment_id}\]/
|
|
63
|
-
end_marker = /#\s*\[SYNC:END:#{segment_id}\]/
|
|
64
|
-
|
|
65
|
-
lines = content.lines
|
|
66
|
-
start_idx = lines.find_index { |l| l =~ start_marker }
|
|
67
|
-
end_idx = lines.find_index { |l| l =~ end_marker }
|
|
68
|
-
|
|
69
|
-
if start_idx && end_idx
|
|
70
|
-
"#{unindent(lines[(start_idx + 1)...end_idx].join).strip}\n"
|
|
71
|
-
else
|
|
72
|
-
warn "Warning: Segment '#{segment_id}' not found in #{source_path}"
|
|
73
|
-
content # Fallback to full content or error? Let's fallback to original for now.
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
def unindent(text)
|
|
78
|
-
lines = text.lines
|
|
79
|
-
# Don't unindent if empty or just one line
|
|
80
|
-
return text if lines.empty?
|
|
81
|
-
|
|
82
|
-
# Find common leading whitespace
|
|
83
|
-
indentation = lines.grep(/\S/).map { |l| l[/^\s*/].length }.min || 0
|
|
84
|
-
lines.map { |l| (l.length > indentation) ? l[indentation..-1] : "#{l.strip}\n" }.join
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
data/tasks/autodoc/member.rb
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
module Autodoc
|
|
9
|
-
module Member
|
|
10
|
-
class Delegate < Data.define(:name)
|
|
11
|
-
def rbs
|
|
12
|
-
" def #{name}: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped"
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def rdoc
|
|
16
|
-
[
|
|
17
|
-
" # :method: #{name}",
|
|
18
|
-
" # :call-seq: #{name}(*args, **kwargs, &block)",
|
|
19
|
-
" #",
|
|
20
|
-
" # Delegates to RatatuiRuby.#{name}.",
|
|
21
|
-
" #",
|
|
22
|
-
]
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
class Factory < Data.define(:name, :const_name)
|
|
27
|
-
def rbs
|
|
28
|
-
" def #{name}: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped"
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def rdoc
|
|
32
|
-
[
|
|
33
|
-
" # :method: #{name}",
|
|
34
|
-
" # :call-seq: #{name}(*args, **kwargs, &block)",
|
|
35
|
-
" #",
|
|
36
|
-
" # Factory for RatatuiRuby::#{const_name}.new.",
|
|
37
|
-
" #",
|
|
38
|
-
]
|
|
39
|
-
end
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
class Helper < Data.define(:name, :class_method, :const_name)
|
|
43
|
-
def rbs
|
|
44
|
-
" def #{name}: (*untyped args, **untyped kwargs) ?{ (*untyped) -> untyped } -> untyped"
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def rdoc
|
|
48
|
-
[
|
|
49
|
-
" # :method: #{name}",
|
|
50
|
-
" # :call-seq: #{name}(*args, **kwargs, &block)",
|
|
51
|
-
" #",
|
|
52
|
-
" # Helper for RatatuiRuby::#{const_name}.#{class_method}.",
|
|
53
|
-
" #",
|
|
54
|
-
]
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
end
|
|
58
|
-
end
|
data/tasks/autodoc/name.rb
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
module Autodoc
|
|
9
|
-
class Name < Data.define(:string)
|
|
10
|
-
def snake
|
|
11
|
-
string.to_s
|
|
12
|
-
.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
13
|
-
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
14
|
-
.downcase
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
def to_s
|
|
18
|
-
string.to_s
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
data/tasks/autodoc.rake
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2025 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
require_relative "autodoc/examples"
|
|
9
|
-
|
|
10
|
-
namespace :autodoc do
|
|
11
|
-
desc "Update all automatically generated documentation"
|
|
12
|
-
task all: [:examples]
|
|
13
|
-
|
|
14
|
-
desc "Sync code snippets in example READMEs with source files"
|
|
15
|
-
task :examples do
|
|
16
|
-
Autodoc::Examples.sync
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
desc "Update all automatically generated documentation"
|
|
21
|
-
task autodoc: "autodoc:all"
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
# Lockfiles need to be refreshed by a tool after Manifests are changed.
|
|
9
|
-
class CargoLockfile < Data.define(:path, :dir, :name)
|
|
10
|
-
def exists?
|
|
11
|
-
File.exist?(path)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def refresh
|
|
15
|
-
return unless exists?
|
|
16
|
-
|
|
17
|
-
Dir.chdir(dir) do
|
|
18
|
-
system("cargo update -p #{name} --offline")
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
end
|
data/tasks/bump/changelog.rb
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
require_relative "links"
|
|
9
|
-
require_relative "unreleased_section"
|
|
10
|
-
require_relative "history"
|
|
11
|
-
require_relative "header"
|
|
12
|
-
|
|
13
|
-
# Changelog manages the project's CHANGELOG.md file.
|
|
14
|
-
class Changelog
|
|
15
|
-
# Creates a new Changelog for the file at the given path.
|
|
16
|
-
def initialize(path: "CHANGELOG.md")
|
|
17
|
-
@path = path
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Releases a new version in the changelog.
|
|
21
|
-
# This moves the unreleased changes to a new version heading and resets the unreleased section.
|
|
22
|
-
def release(new_version)
|
|
23
|
-
content = File.read(@path)
|
|
24
|
-
|
|
25
|
-
header = Header.parse(content)
|
|
26
|
-
unreleased = UnreleasedSection.parse(content)
|
|
27
|
-
links = Links.from_markdown(content)
|
|
28
|
-
|
|
29
|
-
raise "Could not parse CHANGELOG.md" unless header && unreleased && links
|
|
30
|
-
|
|
31
|
-
history = History.parse(content, header.length, unreleased.to_s.length, links.to_s)
|
|
32
|
-
|
|
33
|
-
links.release(new_version)
|
|
34
|
-
history.add(unreleased.as_version(new_version))
|
|
35
|
-
|
|
36
|
-
File.write(@path, "#{header}#{UnreleasedSection.fresh}\n\n#{history}\n#{links}")
|
|
37
|
-
nil
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
def commit_message(version)
|
|
41
|
-
content = File.read(@path)
|
|
42
|
-
unreleased = UnreleasedSection.parse(content)
|
|
43
|
-
return nil unless unreleased
|
|
44
|
-
|
|
45
|
-
"chore: release v#{version}\n\n#{unreleased.commit_body}"
|
|
46
|
-
end
|
|
47
|
-
end
|
data/tasks/bump/header.rb
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
# Header manages the header section of the changelog.
|
|
9
|
-
class Header
|
|
10
|
-
PATTERN = /^(.*?)(?=## \[Unreleased\])/m
|
|
11
|
-
|
|
12
|
-
# Extracts the header section from the given content.
|
|
13
|
-
def self.parse(content)
|
|
14
|
-
match = content.match(PATTERN)
|
|
15
|
-
new(match[1]) if match
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# Creates a new Header from the given content.
|
|
19
|
-
def initialize(content)
|
|
20
|
-
@content = content.dup
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
# Returns the length of the header content.
|
|
24
|
-
def length
|
|
25
|
-
@content.length
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Returns the current state of the header as a string.
|
|
29
|
-
def to_s
|
|
30
|
-
@content
|
|
31
|
-
end
|
|
32
|
-
end
|
data/tasks/bump/history.rb
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
# History manages the versioned history of the changelog.
|
|
9
|
-
class History
|
|
10
|
-
# Extracts the history section from the given content, between unreleased and links.
|
|
11
|
-
def self.parse(content, header_length, unreleased_length, links_text)
|
|
12
|
-
start = header_length + unreleased_length
|
|
13
|
-
text = "#{content[start...(content.index(links_text))].strip}\n"
|
|
14
|
-
new(text)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
# Creates a new History from the given content.
|
|
18
|
-
def initialize(content)
|
|
19
|
-
@content = content.dup
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# Adds a new versioned section to the history.
|
|
23
|
-
def add(section)
|
|
24
|
-
@content = "#{"#{section}\n\n#{@content}".strip}\n"
|
|
25
|
-
nil
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Returns the current state of the history as a string.
|
|
29
|
-
def to_s
|
|
30
|
-
@content
|
|
31
|
-
end
|
|
32
|
-
end
|
data/tasks/bump/links.rb
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
# Manages the version comparison links at the botton of the changelog.
|
|
9
|
-
#
|
|
10
|
-
# Release automation needs to update links. Manually calculating git diff URLs
|
|
11
|
-
# for every release is tedious and error-prone. SourceHut does not have
|
|
12
|
-
# standard comparison views, complicating matters further.
|
|
13
|
-
#
|
|
14
|
-
# This class manages the collection of links. It parses them from the markdown.
|
|
15
|
-
# It generates the correct tree links for SourceHut. It properly shifts the
|
|
16
|
-
# "Unreleased" pointer.
|
|
17
|
-
#
|
|
18
|
-
# Use it to update the changelog during a release.
|
|
19
|
-
class Links
|
|
20
|
-
PATTERN = /^(\[Unreleased\]: .*)$/m
|
|
21
|
-
UNRELEASED_PATTERN = %r{^\[Unreleased\]: (.*?/refs/)HEAD$}
|
|
22
|
-
|
|
23
|
-
# Creates a Links object from the full markdown content.
|
|
24
|
-
#
|
|
25
|
-
# [content] String. The full text of the changelog.
|
|
26
|
-
def self.from_markdown(content)
|
|
27
|
-
match = content.match(PATTERN)
|
|
28
|
-
return unless match
|
|
29
|
-
|
|
30
|
-
new(match[1].strip)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Returns the raw text of the links.
|
|
34
|
-
attr_reader :text
|
|
35
|
-
|
|
36
|
-
# Creates a new Links object.
|
|
37
|
-
#
|
|
38
|
-
# [text] String. The raw text of the links section.
|
|
39
|
-
def initialize(text)
|
|
40
|
-
@text = text.dup
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
# Releases a new version.
|
|
44
|
-
#
|
|
45
|
-
# Updates the "Unreleased" link to point to the new head. Adds a new link for
|
|
46
|
-
# the just-released version pointing to its specific tag.
|
|
47
|
-
#
|
|
48
|
-
# [version] String. The new version number (e.g., <tt>"0.5.0"</tt>).
|
|
49
|
-
def release(version)
|
|
50
|
-
return unless base_url
|
|
51
|
-
|
|
52
|
-
new_unreleased = "[Unreleased]: #{base_url}HEAD" # .../HEAD
|
|
53
|
-
new_version_link = "[#{version}]: #{base_url}v#{version}" # .../v1.0.0
|
|
54
|
-
|
|
55
|
-
@text.sub!(UNRELEASED_PATTERN, "#{new_unreleased}\n#{new_version_link}")
|
|
56
|
-
self
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Returns the string representation of the links.
|
|
60
|
-
def to_s
|
|
61
|
-
@text
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
# The base URL for the repository's references.
|
|
65
|
-
private def base_url
|
|
66
|
-
match = @text.match(UNRELEASED_PATTERN)
|
|
67
|
-
match[1] if match
|
|
68
|
-
end
|
|
69
|
-
end
|
data/tasks/bump/manifest.rb
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
# Manifests hold a copy of the version number and should be changed manually.
|
|
9
|
-
# Use Regexp lookarounds in `pattern` to match the version number.
|
|
10
|
-
class Manifest < Data.define(:path, :pattern, :primary)
|
|
11
|
-
def read
|
|
12
|
-
File.read(path)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def initialize(path:, pattern:, primary: false)
|
|
16
|
-
super
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def version
|
|
20
|
-
content = read
|
|
21
|
-
match = content.match(pattern)
|
|
22
|
-
raise "Version missing in manifest #{path}" unless match
|
|
23
|
-
|
|
24
|
-
SemVer.parse(match[0])
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
def write(version)
|
|
28
|
-
return unless File.exist?(path)
|
|
29
|
-
|
|
30
|
-
new_content = read.gsub(pattern, version.to_s)
|
|
31
|
-
File.write(path, new_content)
|
|
32
|
-
end
|
|
33
|
-
end
|
data/tasks/bump/ruby_gem.rb
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
class RubyGem
|
|
9
|
-
def initialize(manifests:, lockfile:, changelog:)
|
|
10
|
-
raise ArgumentError, "Must have exactly one primary manifest" unless manifests.count(&:primary) == 1
|
|
11
|
-
@manifests = manifests
|
|
12
|
-
@lockfile = lockfile
|
|
13
|
-
@changelog = changelog
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def version
|
|
17
|
-
@manifests.find(&:primary).version
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def bump(segment)
|
|
21
|
-
target = version.next(segment)
|
|
22
|
-
commit_message = @changelog.commit_message(target)
|
|
23
|
-
|
|
24
|
-
puts "Bumping #{segment}: #{version} -> #{target}"
|
|
25
|
-
@changelog.release(target)
|
|
26
|
-
@manifests.each { |manifest| manifest.write(target) }
|
|
27
|
-
@lockfile.refresh
|
|
28
|
-
|
|
29
|
-
puts_commit_message(commit_message)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def set(version_string)
|
|
33
|
-
target = SemVer.parse(version_string)
|
|
34
|
-
commit_message = @changelog.commit_message(target)
|
|
35
|
-
|
|
36
|
-
puts "Setting version: #{version} -> #{target}"
|
|
37
|
-
@changelog.release(target)
|
|
38
|
-
@manifests.each { |manifest| manifest.write(target) }
|
|
39
|
-
@lockfile.refresh
|
|
40
|
-
|
|
41
|
-
puts_commit_message(commit_message)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private def puts_commit_message(message)
|
|
45
|
-
puts "=" * 80
|
|
46
|
-
puts message
|
|
47
|
-
puts "=" * 80
|
|
48
|
-
end
|
|
49
|
-
end
|
data/tasks/bump/sem_ver.rb
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
#--
|
|
4
|
-
# SPDX-FileCopyrightText: 2026 Kerrick Long <me@kerricklong.com>
|
|
5
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
6
|
-
#++
|
|
7
|
-
|
|
8
|
-
# See https://semver.org/spec/v2.0.0.html
|
|
9
|
-
class SemVer
|
|
10
|
-
SEGMENTS = [:major, :minor, :patch].freeze
|
|
11
|
-
|
|
12
|
-
def self.parse(string)
|
|
13
|
-
require "rubygems"
|
|
14
|
-
# Extract prerelease suffix (e.g., "-beta.1", "-alpha.2", "-rc.1")
|
|
15
|
-
base, prerelease = string.split("-", 2)
|
|
16
|
-
segments = Gem::Version.new(base).segments.fill(0, 3).first(3)
|
|
17
|
-
new(segments, prerelease:)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
def initialize(segments, prerelease: nil)
|
|
21
|
-
@segments = segments
|
|
22
|
-
@prerelease = prerelease
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
def next(segment)
|
|
26
|
-
index = SEGMENTS.index(segment)
|
|
27
|
-
raise ArgumentError, "Invalid segment: #{segment}" unless index
|
|
28
|
-
|
|
29
|
-
new_segments = @segments.dup
|
|
30
|
-
new_segments[index] += 1
|
|
31
|
-
new_segments.fill(0, (index + 1)..2)
|
|
32
|
-
|
|
33
|
-
SemVer.new(new_segments)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
def to_s
|
|
37
|
-
base = @segments.join(".")
|
|
38
|
-
@prerelease ? "#{base}-#{@prerelease}" : base
|
|
39
|
-
end
|
|
40
|
-
end
|