7fe1656c7d
- Add test for editing a message and removing all content (empty content) - Add test for multiple consecutive edits to the same message (changes stack) - Add test for editing messages with special characters - Add test for very long message content - Add test for editing assistant messages with tool_calls - Add test for dropping all messages (empty history result) - Add test for unicode characters in message content - Add test for whitespace variations in rebase file - Add test for comments and empty lines mixed with commands - Add test for special characters and unicode in rebase content display - Fix apply_rebase to preserve original content when only role is changed - Add content_edited flag to RebaseLine to track explicit edits
84 lines
3.0 KiB
C++
84 lines
3.0 KiB
C++
#pragma once
|
|
#include "json_types.hpp"
|
|
#include <string>
|
|
#include <vector>
|
|
#include <expected>
|
|
|
|
// Forward declaration
|
|
struct Message;
|
|
|
|
enum class RebaseCommand { Pick, Edit, Drop };
|
|
|
|
// JSON serialization for RebaseCommand
|
|
inline void to_json(nlohmann::json& j, RebaseCommand cmd) {
|
|
switch (cmd) {
|
|
case RebaseCommand::Pick: j = "pick"; break;
|
|
case RebaseCommand::Edit: j = "edit"; break;
|
|
case RebaseCommand::Drop: j = "drop"; break;
|
|
}
|
|
}
|
|
|
|
template<>
|
|
inline std::expected<RebaseCommand, std::string> json_to<RebaseCommand>(const nlohmann::json& j) {
|
|
if (!j.is_string()) return std::unexpected("expected string for RebaseCommand");
|
|
std::string s = j.get<std::string>();
|
|
if (s == "pick") return RebaseCommand::Pick;
|
|
if (s == "edit") return RebaseCommand::Edit;
|
|
if (s == "drop") return RebaseCommand::Drop;
|
|
return std::unexpected("invalid RebaseCommand: '" + s + "'");
|
|
}
|
|
|
|
template<> struct has_json_to<RebaseCommand> : std::true_type {};
|
|
|
|
struct RebaseLine {
|
|
RebaseCommand command;
|
|
size_t index;
|
|
std::string role;
|
|
std::string content; // For edit commands, stores the new content
|
|
bool content_edited = false; // True if content was explicitly edited (even if empty)
|
|
};
|
|
|
|
DRH_DEFINE_TYPE_MIXED(RebaseLine, (command, index, role), (content))
|
|
|
|
class RebaseEngine {
|
|
public:
|
|
std::expected<std::vector<RebaseLine>, std::string> parse_rebase_file(
|
|
const std::string& content, size_t history_size) const;
|
|
|
|
std::expected<std::vector<Message>, std::string> apply_rebase(
|
|
const std::vector<RebaseLine>& lines,
|
|
const std::vector<Message>& history) const;
|
|
};
|
|
|
|
class HistoryEditor {
|
|
public:
|
|
// Build the rebase editor content from a message history
|
|
// Each message is formatted as: pick N [role] first_line_of_content
|
|
// Tool calls display as: [tool_call: function_name]
|
|
// Empty messages display as: (empty)
|
|
std::string build_rebase_content(const std::vector<Message>& history) const;
|
|
|
|
// Build the edit prompt for editing a single message
|
|
// Shows previous message as context, separator, and current content to edit
|
|
// For message 0, shows "(no previous message)" instead of previous message
|
|
std::string build_edit_prompt(const std::vector<Message>& history,
|
|
size_t edit_index,
|
|
const std::string& role) const;
|
|
|
|
// Full interactive edit flow
|
|
// 1. Build rebase content and spawn editor
|
|
// 2. Parse and validate rebase commands
|
|
// 3. For each edit command, build edit prompt and spawn editor
|
|
// 4. Apply all changes
|
|
// Returns modified history on success, error message on failure
|
|
std::expected<std::vector<Message>, std::string> edit_history_interactive(
|
|
const std::vector<Message>& history) const;
|
|
|
|
private:
|
|
// Extract the first line of content from a message
|
|
std::string get_first_line(const std::string& content) const;
|
|
|
|
// Format display text for a single message
|
|
std::string get_message_display(const Message& msg) const;
|
|
};
|