Module: Hunkify::UI
- Defined in:
- lib/hunkify/ui.rb
Class Method Summary collapse
- .confirm_plan(plan) ⇒ Object
- .move_hunk(commits_data, hunks_by_id, context: nil) ⇒ Object
- .new_commit(commits_data, hunks_by_id, context: nil) ⇒ Object
- .prefill_readline(prompt, default) ⇒ Object
- .print_grouping(commits_data, hunks_by_id) ⇒ Object
- .print_header ⇒ Object
- .print_hunk(hunk, max_lines: 25) ⇒ Object
- .print_hunks_overview(hunks) ⇒ Object
- .prompt_edit_commit(commit_data, _index, _total) ⇒ Object
- .prompt_new_message(hunk_ids, hunks_by_id, context:) ⇒ Object
- .reassign_loop(commits_data, hunks_by_id, context: nil) ⇒ Object
- .reinsert_hunk(commits_data, hunk_id) ⇒ Object
Class Method Details
.confirm_plan(plan) ⇒ Object
229 230 231 232 233 234 235 236 237 238 |
# File 'lib/hunkify/ui.rb', line 229 def confirm_plan(plan) puts Color.bold("๐ Final plan:") plan.each_with_index do |(msg, _), i| puts " #{Color.dim("#{i + 1}.")} #{Color.green(msg)}" end puts print " #{Color.yellow("[Enter]")} Run #{Color.yellow("[q]")} Cancel: " input = $stdin.gets.chomp.downcase exit 0 if input == "q" end |
.move_hunk(commits_data, hunks_by_id, context: nil) ⇒ Object
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
# File 'lib/hunkify/ui.rb', line 125 def move_hunk(commits_data, hunks_by_id, context: nil) print " Hunk id? " hunk_id = $stdin.gets.chomp.to_i unless hunks_by_id.key?(hunk_id) puts Color.red(" โ Unknown hunk id.") return end print_hunk(hunks_by_id[hunk_id]) print " Target commit (1-#{commits_data.size}, 'new', or 'c' to cancel): " target = $stdin.gets.chomp.downcase if target.empty? || target == "c" || target == "cancel" puts Color.dim(" Cancelled.") return end commits_data.each { |c| c["hunk_ids"].delete(hunk_id) } if target == "new" msg = ([hunk_id], hunks_by_id, context: context) if msg.nil? || msg.empty? puts Color.red(" โ Empty message, reassignment cancelled.") reinsert_hunk(commits_data, hunk_id) return end commits_data << {"message" => msg, "hunk_ids" => [hunk_id], "reasoning" => "manual"} else idx = target.to_i - 1 unless (0...commits_data.size).cover?(idx) puts Color.red(" โ Invalid target.") reinsert_hunk(commits_data, hunk_id) return end commits_data[idx]["hunk_ids"] << hunk_id end commits_data.reject! { |c| c["hunk_ids"].empty? } puts print_grouping(commits_data, hunks_by_id) end |
.new_commit(commits_data, hunks_by_id, context: nil) ⇒ Object
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 |
# File 'lib/hunkify/ui.rb', line 168 def new_commit(commits_data, hunks_by_id, context: nil) print " Hunk ids (space-separated): " ids = $stdin.gets.chomp.split.map(&:to_i) ids = ids.select { |id| hunks_by_id.key?(id) } if ids.empty? puts Color.red(" โ No valid hunk ids.") return end msg = (ids, hunks_by_id, context: context) if msg.nil? || msg.empty? puts Color.red(" โ Empty message, cancelled.") return end commits_data.each { |c| c["hunk_ids"] -= ids } commits_data << {"message" => msg, "hunk_ids" => ids, "reasoning" => "manual"} commits_data.reject! { |c| c["hunk_ids"].empty? } puts print_grouping(commits_data, hunks_by_id) end |
.prefill_readline(prompt, default) ⇒ Object
190 191 192 193 194 195 196 197 198 |
# File 'lib/hunkify/ui.rb', line 190 def prefill_readline(prompt, default) Readline.pre_input_hook = lambda do Readline.insert_text(" #{default}") Readline.redisplay Readline.pre_input_hook = nil end result = Readline.readline(prompt, false) result&.sub(/\A /, "") end |
.print_grouping(commits_data, hunks_by_id) ⇒ Object
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/hunkify/ui.rb', line 29 def print_grouping(commits_data, hunks_by_id) puts Color.bold("๐ค AI-proposed grouping:") puts commits_data.each_with_index do |c, i| hunk_list = c["hunk_ids"].map { |id| Color.dim("[#{id}]") }.join(" ") files = c["hunk_ids"].map { |id| hunks_by_id[id]&.file_path }.compact.uniq puts " #{Color.bold(Color.blue("Commit #{i + 1}"))} #{hunk_list}" puts " #{Color.green(c["message"])}" puts " #{Color.dim("โณ " + c["reasoning"])}" puts " #{Color.dim("Files: " + files.join(", "))}" puts end end |
.print_header ⇒ Object
10 11 12 13 14 15 16 17 |
# File 'lib/hunkify/ui.rb', line 10 def print_header version = defined?(Hunkify::VERSION) ? Hunkify::VERSION : "" puts puts " #{Color.magenta("โฒ")} #{Color.bold("hunkify")} #{Color.dim("v#{version}")}" puts " #{Color.dim("โ" * 40)}" puts " #{Color.dim("Atomic commits, grouped by Claude.")}" puts end |
.print_hunk(hunk, max_lines: 25) ⇒ Object
88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
# File 'lib/hunkify/ui.rb', line 88 def print_hunk(hunk, max_lines: 25) added = hunk.lines.count { |l| l.start_with?("+") } removed = hunk.lines.count { |l| l.start_with?("-") } title = "Hunk [#{hunk.id}] ยท #{hunk.file_path} #{Color.green("+#{added}")} #{Color.red("-#{removed}")}" # visible width: strip ANSI to compute padding visible = title.gsub(/\e\[[0-9;]*m/, "") inner_w = [visible.length + 4, 60].max puts puts Color.dim(" โญโ ") + title + " " + Color.dim("โ" * [inner_w - visible.length - 4, 1].max) + Color.dim("โฎ") puts Color.dim(" โ ") + Color.dim(hunk.hunk_header) lines = hunk.lines truncated = lines.size > max_lines shown = truncated ? lines.first(max_lines) : lines shown.each do |line| colored = if line.start_with?("+") Color.green(line) elsif line.start_with?("-") Color.red(line) else line end puts Color.dim(" โ ") + colored end if truncated puts Color.dim(" โ โฆ #{lines.size - max_lines} more line(s)") end puts Color.dim(" โฐ" + "โ" * (inner_w + 2) + "โฏ") puts end |
.print_hunks_overview(hunks) ⇒ Object
19 20 21 22 23 24 25 26 27 |
# File 'lib/hunkify/ui.rb', line 19 def print_hunks_overview(hunks) puts Color.bold("๐ #{hunks.size} hunk(s) detected:") hunks.each do |h| added = h.lines.count { |l| l.start_with?("+") } removed = h.lines.count { |l| l.start_with?("-") } puts " #{Color.dim("[#{h.id}]")} #{Color.cyan(h.file_path)} #{Color.green("+#{added}")} #{Color.red("-#{removed}")}" end puts end |
.prompt_edit_commit(commit_data, _index, _total) ⇒ Object
209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 |
# File 'lib/hunkify/ui.rb', line 209 def prompt_edit_commit(commit_data, _index, _total) print " #{Color.yellow("[Enter]")} Confirm #{Color.yellow("[e]")} Edit message #{Color.yellow("[s]")} Skip #{Color.yellow("[q]")} Quit: " input = $stdin.gets.chomp.downcase case input when "" commit_data["message"] when "e" new_msg = prefill_readline(" โ๏ธ New message: ", commit_data["message"]) new_msg.to_s.empty? ? commit_data["message"] : new_msg when "s" nil when "q" puts Color.red("\n Cancelled.") exit 0 else commit_data["message"] end end |
.prompt_new_message(hunk_ids, hunks_by_id, context:) ⇒ Object
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/hunkify/ui.rb', line 66 def (hunk_ids, hunks_by_id, context:) hunks = hunk_ids.map { |id| hunks_by_id[id] }.compact print Color.dim(" Asking Claude for a suggestion... ") begin suggestion = AnthropicAPI.(hunks, context: context) rescue => e puts Color.red("failed (#{e.})") suggestion = nil end puts if suggestion && !suggestion.empty? puts " #{Color.dim("suggested:")} #{Color.green(suggestion)}" print " #{Color.yellow("[Enter]")} Accept #{Color.yellow("[type]")} Override: " else print " Commit message: " end input = $stdin.gets.chomp input.empty? ? suggestion : input end |
.reassign_loop(commits_data, hunks_by_id, context: nil) ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
# File 'lib/hunkify/ui.rb', line 45 def reassign_loop(commits_data, hunks_by_id, context: nil) loop do print " #{Color.yellow("[m]")} Move hunk #{Color.yellow("[n]")} New commit #{Color.yellow("[Enter]")} Continue #{Color.yellow("[q]")} Quit: " input = $stdin.gets.chomp.downcase case input when "" return when "q" puts Color.red("\n Cancelled.") exit 0 when "m" move_hunk(commits_data, hunks_by_id, context: context) when "n" new_commit(commits_data, hunks_by_id, context: context) else puts Color.dim(" (unknown command)") end end end |
.reinsert_hunk(commits_data, hunk_id) ⇒ Object
200 201 202 203 204 205 206 207 |
# File 'lib/hunkify/ui.rb', line 200 def reinsert_hunk(commits_data, hunk_id) # Hunk was removed in advance; put it back somewhere safe. if commits_data.any? commits_data.first["hunk_ids"] << hunk_id else commits_data << {"message" => "unassigned", "hunk_ids" => [hunk_id], "reasoning" => "manual"} end end |