use lasso::{Rodeo, Spur};
use ratatui::style::Style;
use std::collections::hash_map::DefaultHasher;
use std::collections::HashMap;
use std::hash::{Hash, Hasher};
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use crate::ai::orchestrator::RallyEvent;
use crate::ai::RallyState;
use crate::diff::LineType;
use crate::github::comment::{DiscussionComment, ReviewComment};
use crate::github::{ChangedFile, PullRequest};
#[derive(Debug, Clone)]
pub struct CommentPosition {
pub diff_line_index: usize,
pub comment_index: usize,
}
#[derive(Debug, Clone)]
pub struct JumpLocation {
pub file_index: usize,
pub line_index: usize,
pub scroll_offset: usize,
}
#[derive(Debug, Clone)]
pub struct SymbolPopupState {
pub symbols: Vec<(String, usize, usize)>,
pub selected: usize,
}
#[derive(Clone)]
pub struct InternedSpan {
pub content: Spur,
pub style: Style,
}
#[derive(Clone)]
pub struct CachedDiffLine {
pub spans: Vec<InternedSpan>,
pub line_type: LineType,
}
pub struct DiffCache {
pub file_index: usize,
pub patch_hash: u64,
pub lines: Vec<CachedDiffLine>,
pub interner: Rodeo,
pub highlighted: bool,
pub markdown_rich: bool,
}
impl DiffCache {
pub fn resolve(&self, spur: Spur) -> &str {
self.interner.resolve(&spur)
}
}
pub fn hash_string(s: &str) -> u64 {
let mut hasher = DefaultHasher::new();
s.hash(&mut hasher);
hasher.finish()
}
#[derive(Debug, Clone)]
pub struct MultilineSelection {
pub anchor_line: usize,
pub cursor_line: usize,
}
impl MultilineSelection {
pub fn start(&self) -> usize {
self.anchor_line.min(self.cursor_line)
}
pub fn end(&self) -> usize {
self.anchor_line.max(self.cursor_line)
}
}
#[derive(Debug, Clone)]
pub struct LineInputContext {
pub file_index: usize,
pub line_number: u32,
pub diff_position: u32,
pub start_line_number: Option<u32>,
}
#[derive(Debug, Clone)]
pub enum InputMode {
Comment(LineInputContext),
Suggestion {
context: LineInputContext,
original_code: String,
},
Reply {
comment_id: u64,
reply_to_user: String,
reply_to_body: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum AppState {
PullRequestList,
FileList,
DiffView,
TextInput,
CommentList,
Help,
AiRally,
SplitViewFileList,
SplitViewDiff,
PrDescription,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub(super) enum DiffViewVariant {
Fullscreen,
SplitPane,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LogEventType {
Info,
Thinking,
ToolUse,
ToolResult,
Text,
Review,
Fix,
Error,
}
#[derive(Debug, Clone)]
pub struct LogEntry {
pub timestamp: String,
pub event_type: LogEventType,
pub message: String,
}
impl LogEntry {
pub fn new(event_type: LogEventType, message: String) -> Self {
let now = chrono::Local::now();
Self {
timestamp: now.format("%H:%M:%S").to_string(),
event_type,
message,
}
}
}
#[derive(Debug, Clone)]
pub struct PermissionInfo {
pub action: String,
pub reason: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PauseState {
Running,
PauseRequested,
Paused,
}
#[derive(Debug, Clone)]
pub struct AiRallyState {
pub iteration: u32,
pub max_iterations: u32,
pub state: RallyState,
pub history: Vec<RallyEvent>,
pub logs: Vec<LogEntry>,
pub log_scroll_offset: usize,
pub selected_log_index: Option<usize>,
pub showing_log_detail: bool,
pub pending_question: Option<String>,
pub pending_permission: Option<PermissionInfo>,
pub pending_review_post: Option<crate::ai::orchestrator::ReviewPostInfo>,
pub pending_fix_post: Option<crate::ai::orchestrator::FixPostInfo>,
pub last_visible_log_height: usize,
pub pending_config_warning: Option<Vec<(String, String)>>,
pub pause_state: PauseState,
}
impl AiRallyState {
pub fn push_log(&mut self, entry: LogEntry) {
let was_at_tail = self.is_selection_at_tail();
self.logs.push(entry);
if was_at_tail {
self.selected_log_index = Some(self.logs.len().saturating_sub(1));
self.log_scroll_offset = 0; }
}
fn is_selection_at_tail(&self) -> bool {
match self.selected_log_index {
None => true, Some(idx) => {
idx >= self.logs.len().saturating_sub(1)
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ReviewAction {
Approve,
RequestChanges,
Comment,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(super) enum PendingApproveChoice {
Ignore,
Submit,
Cancel,
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum HelpTab {
#[default]
Keybindings,
Config,
}
#[derive(Debug, Clone, Copy, PartialEq, Default)]
pub enum CommentTab {
#[default]
Review,
Discussion,
}
#[derive(Debug, Clone)]
pub enum RefreshRequest {
PrRefresh { pr_number: u32 },
LocalRefresh,
}
#[derive(Debug, Clone)]
pub(super) enum MarkViewedResult {
Completed {
marked_paths: Vec<String>,
total_targets: usize,
error: Option<String>,
set_viewed: bool,
},
}
pub struct WatcherHandle {
pub(crate) active: Arc<AtomicBool>,
pub(crate) _thread: std::thread::JoinHandle<()>,
}
pub struct ViewSnapshot {
pub pr_number: Option<u32>,
pub selected_file: usize,
pub file_list_scroll_offset: usize,
pub selected_line: usize,
pub scroll_offset: usize,
pub diff_cache: Option<DiffCache>,
pub highlighted_cache_store: HashMap<usize, DiffCache>,
pub review_comments: Option<Vec<ReviewComment>>,
pub discussion_comments: Option<Vec<DiscussionComment>>,
pub local_file_signatures: HashMap<String, u64>,
pub local_file_patch_signatures: HashMap<String, u64>,
}
#[derive(Debug, Clone)]
pub enum DataState {
Loading,
Loaded {
pr: Box<PullRequest>,
files: Vec<ChangedFile>,
},
Error(String),
}