diff --git a/crates/owlen-cli/src/main.rs b/crates/owlen-cli/src/main.rs index 35a9310..5c460d6 100644 --- a/crates/owlen-cli/src/main.rs +++ b/crates/owlen-cli/src/main.rs @@ -223,12 +223,11 @@ fn run_config_doctor() -> Result<()> { if needs_env_update { cloud.api_key_env = Some(OLLAMA_API_KEY_ENV.to_string()); } - if let Some(ref value) = original_api_key_env { - if value.eq_ignore_ascii_case(LEGACY_OLLAMA_CLOUD_API_KEY_ENV) - || value.eq_ignore_ascii_case(LEGACY_OWLEN_OLLAMA_CLOUD_API_KEY_ENV) - { - cloud.api_key_env = Some(OLLAMA_API_KEY_ENV.to_string()); - } + if let Some(ref value) = original_api_key_env + && (value.eq_ignore_ascii_case(LEGACY_OLLAMA_CLOUD_API_KEY_ENV) + || value.eq_ignore_ascii_case(LEGACY_OWLEN_OLLAMA_CLOUD_API_KEY_ENV)) + { + cloud.api_key_env = Some(OLLAMA_API_KEY_ENV.to_string()); } if cloud.api_key_env != original_api_key_env { changes @@ -338,20 +337,19 @@ fn run_config_doctor() -> Result<()> { if ensure_default_enabled { let default_id = config.general.default_provider.clone(); - if let Some(default_cfg) = config.providers.get(&default_id) { - if !default_cfg.enabled { - if let Some(new_default) = config - .providers - .iter() - .filter(|(id, cfg)| cfg.enabled && *id != &default_id) - .map(|(id, _)| id.clone()) - .min() - { - config.general.default_provider = new_default.clone(); - changes.push(format!( - "default provider '{default_id}' was disabled; switched default to '{new_default}'" - )); - } else { + if let Some(default_cfg) = config.providers.get(&default_id) && !default_cfg.enabled { + if let Some(new_default) = config + .providers + .iter() + .filter(|(id, cfg)| cfg.enabled && *id != &default_id) + .map(|(id, _)| id.clone()) + .min() + { + config.general.default_provider = new_default.clone(); + changes.push(format!( + "default provider '{default_id}' was disabled; switched default to '{new_default}'" + )); + } else { let entry = core_config::ensure_provider_config_mut(&mut config, "ollama_local"); if !entry.enabled { @@ -371,7 +369,6 @@ fn run_config_doctor() -> Result<()> { } } } - } match config.mcp.mode { McpMode::Legacy => { diff --git a/crates/owlen-core/src/providers/ollama.rs b/crates/owlen-core/src/providers/ollama.rs index 1d34c37..6f206df 100644 --- a/crates/owlen-core/src/providers/ollama.rs +++ b/crates/owlen-core/src/providers/ollama.rs @@ -361,10 +361,8 @@ fn probe_default_local_daemon(timeout: Duration) -> bool { } for target in LOCAL_PROBE_TARGETS { - if let Ok(address) = target.parse::() { - if TcpStream::connect_timeout(&address, timeout).is_ok() { - return true; - } + if let Ok(address) = target.parse::() && TcpStream::connect_timeout(&address, timeout).is_ok() { + return true; } } false @@ -453,13 +451,11 @@ impl OllamaProvider { ProviderVariant::Cloud => OllamaMode::Cloud, }; - if matches!(variant, ProviderVariant::Cloud) { - if !api_key_present { - return Err(Error::Config( - "Ollama Cloud API key not configured. Set providers.ollama_cloud.api_key or export OLLAMA_API_KEY (legacy: OLLAMA_CLOUD_API_KEY / OWLEN_OLLAMA_CLOUD_API_KEY)." - .into(), - )); - } + if matches!(variant, ProviderVariant::Cloud) && !api_key_present { + return Err(Error::Config( + "Ollama Cloud API key not configured. Set providers.ollama_cloud.api_key or export OLLAMA_API_KEY (legacy: OLLAMA_CLOUD_API_KEY / OWLEN_OLLAMA_CLOUD_API_KEY)." + .into(), + )); } let base_candidate = match mode { @@ -642,10 +638,8 @@ impl OllamaProvider { return None; } - if let Some(ts) = entry.fetched_at { - if ts.elapsed() < self.model_cache_ttl { - return Some(entry.models.clone()); - } + if let Some(ts) = entry.fetched_at && ts.elapsed() < self.model_cache_ttl { + return Some(entry.models.clone()); } // Fallback to last good models even if stale; UI will mark as degraded @@ -903,10 +897,8 @@ impl OllamaProvider { } } - if combined.is_empty() { - if let Some(err) = errors.pop() { - return Err(err); - } + if combined.is_empty() && let Some(err) = errors.pop() { + return Err(err); } self.annotate_scope_status(&mut combined).await; @@ -1312,10 +1304,8 @@ impl OllamaProvider { provider_meta.insert("model".into(), Value::String(model)); provider_meta.insert("created_at".into(), Value::String(created_at)); - if let Some(ref final_block) = final_data { - if let Ok(value) = serde_json::to_value(final_block) { - provider_meta.insert("final_data".into(), value); - } + if let Some(ref final_block) = final_data && let Ok(value) = serde_json::to_value(final_block) { + provider_meta.insert("final_data".into(), value); } message @@ -1855,14 +1845,12 @@ fn model_supports_tools( capabilities: &[String], detail: Option<&OllamaModelInfo>, ) -> bool { - if let Some(info) = detail { - if info - .capabilities - .iter() - .any(|capability| capability_implies_tools(capability)) - { - return true; - } + if let Some(info) = detail && info + .capabilities + .iter() + .any(|capability| capability_implies_tools(capability)) + { + return true; } if capabilities @@ -2113,10 +2101,8 @@ fn normalize_base_url( return Err(format!("URL '{candidate}' cannot be used as a base URL")); } - if mode_hint == OllamaMode::Cloud && url.scheme() != "https" { - if std::env::var("OWLEN_ALLOW_INSECURE_CLOUD").is_err() { - return Err("Ollama Cloud requires https:// base URLs".to_string()); - } + if mode_hint == OllamaMode::Cloud && url.scheme() != "https" && std::env::var("OWLEN_ALLOW_INSECURE_CLOUD").is_err() { + return Err("Ollama Cloud requires https:// base URLs".to_string()); } let path = url.path().trim_end_matches('/'); @@ -2130,13 +2116,9 @@ fn normalize_base_url( } } - if mode_hint == OllamaMode::Cloud { - if let Some(host) = url.host_str() { - if host.eq_ignore_ascii_case("api.ollama.com") { - url.set_host(Some("ollama.com")) - .map_err(|err| format!("Failed to normalise Ollama Cloud host: {err}"))?; - } - } + if mode_hint == OllamaMode::Cloud && let Some(host) = url.host_str() && host.eq_ignore_ascii_case("api.ollama.com") { + url.set_host(Some("ollama.com")) + .map_err(|err| format!("Failed to normalise Ollama Cloud host: {err}"))?; } url.set_query(None); diff --git a/crates/owlen-core/src/session.rs b/crates/owlen-core/src/session.rs index 4e62909..26a7102 100644 --- a/crates/owlen-core/src/session.rs +++ b/crates/owlen-core/src/session.rs @@ -101,10 +101,8 @@ fn build_transcript(messages: &[Message]) -> String { Role::Tool => "Tool", }; let snippet = sanitize_snippet(&message.content); - if snippet.is_empty() { - if message.attachments.is_empty() { - continue; - } + if snippet.is_empty() && message.attachments.is_empty() { + continue; } transcript.push_str(&format!("{role}: {snippet}\n")); if !message.attachments.is_empty() { @@ -1072,15 +1070,13 @@ impl SessionController { } async fn persist_usage_serialized(path: PathBuf, serialized: String) { - if let Some(parent) = path.parent() { - if let Err(err) = fs::create_dir_all(parent).await { - warn!( - "Failed to create usage ledger directory {}: {}", - parent.display(), - err - ); - return; - } + if let Some(parent) = path.parent() && let Err(err) = fs::create_dir_all(parent).await { + warn!( + "Failed to create usage ledger directory {}: {}", + parent.display(), + err + ); + return; } if let Err(err) = fs::write(&path, serialized).await { @@ -2274,30 +2270,28 @@ impl SessionController { .pending_tool_requests .values() .any(|pending| pending.message_id == message_id) - { - if let Some((tool_name, data_types, endpoints)) = + && let Some((tool_name, data_types, endpoints)) = self.check_tools_consent_needed(&calls).into_iter().next() - { - let request_id = Uuid::new_v4(); - let pending = PendingToolRequest { - message_id, - tool_name: tool_name.clone(), - data_types: data_types.clone(), - endpoints: endpoints.clone(), - tool_calls: calls.clone(), - }; - self.pending_tool_requests.insert(request_id, pending); + { + let request_id = Uuid::new_v4(); + let pending = PendingToolRequest { + message_id, + tool_name: tool_name.clone(), + data_types: data_types.clone(), + endpoints: endpoints.clone(), + tool_calls: calls.clone(), + }; + self.pending_tool_requests.insert(request_id, pending); - if let Some(tx) = &self.event_tx { - let _ = tx.send(ControllerEvent::ToolRequested { - request_id, - message_id, - tool_name, - data_types, - endpoints, - tool_calls: calls.clone(), - }); - } + if let Some(tx) = &self.event_tx { + let _ = tx.send(ControllerEvent::ToolRequested { + request_id, + message_id, + tool_name, + data_types, + endpoints, + tool_calls: calls.clone(), + }); } } diff --git a/crates/owlen-core/src/storage.rs b/crates/owlen-core/src/storage.rs index c7aa80a..5b98be0 100644 --- a/crates/owlen-core/src/storage.rs +++ b/crates/owlen-core/src/storage.rs @@ -53,14 +53,12 @@ impl StorageManager { /// Create a storage manager using the provided database path pub async fn with_database_path(database_path: PathBuf) -> Result { - if let Some(parent) = database_path.parent() { - if !parent.exists() { - std::fs::create_dir_all(parent).map_err(|e| { - Error::Storage(format!( - "Failed to create database directory {parent:?}: {e}" - )) - })?; - } + if let Some(parent) = database_path.parent() && !parent.exists() { + std::fs::create_dir_all(parent).map_err(|e| { + Error::Storage(format!( + "Failed to create database directory {parent:?}: {e}" + )) + })?; } let options = SqliteConnectOptions::from_str(&format!( @@ -434,13 +432,11 @@ impl StorageManager { } } - if migrated > 0 { - if let Err(err) = archive_legacy_directory(&legacy_dir) { - println!( - "Warning: migrated sessions but failed to archive legacy directory: {}", - err - ); - } + if migrated > 0 && let Err(err) = archive_legacy_directory(&legacy_dir) { + println!( + "Warning: migrated sessions but failed to archive legacy directory: {}", + err + ); } println!("Migrated {} legacy sessions.", migrated); diff --git a/crates/owlen-core/tests/file_write.rs b/crates/owlen-core/tests/file_write.rs index e2bf5ee..674cbd8 100644 --- a/crates/owlen-core/tests/file_write.rs +++ b/crates/owlen-core/tests/file_write.rs @@ -60,7 +60,9 @@ async fn write_outside_root_is_rejected() { // The server returns a Network error with path traversal message let err_str = format!("{err}"); assert!( - err_str.contains("path traversal") || err_str.contains("Path traversal"), + err_str.contains("path traversal") + || err_str.contains("Path traversal") + || err_str.contains("escapes workspace boundary"), "Expected path traversal error, got: {}", err_str ); diff --git a/crates/owlen-core/tests/presets.rs b/crates/owlen-core/tests/presets.rs index 26c8c97..b24bb60 100644 --- a/crates/owlen-core/tests/presets.rs +++ b/crates/owlen-core/tests/presets.rs @@ -48,6 +48,7 @@ fn prune_preset_removes_extra_entries() { transport: "stdio".into(), env: Default::default(), oauth: None, + rpc_timeout_secs: None, }); let report = apply_preset(&mut config, PresetTier::Standard, true).expect("apply preset"); diff --git a/crates/owlen-core/tests/prompt_server.rs b/crates/owlen-core/tests/prompt_server.rs index dbc16b0..635bc64 100644 --- a/crates/owlen-core/tests/prompt_server.rs +++ b/crates/owlen-core/tests/prompt_server.rs @@ -45,9 +45,10 @@ async fn test_render_prompt_via_external_server() -> Result<()> { transport: "stdio".into(), env: std::collections::HashMap::new(), oauth: None, + rpc_timeout_secs: None, }; - let client = match RemoteMcpClient::new_with_config(&config) { + let client = match RemoteMcpClient::new_with_config(&config).await { Ok(client) => client, Err(err) => { eprintln!( diff --git a/crates/owlen-tui/src/app/mod.rs b/crates/owlen-tui/src/app/mod.rs index b09f29e..bd44643 100644 --- a/crates/owlen-tui/src/app/mod.rs +++ b/crates/owlen-tui/src/app/mod.rs @@ -272,10 +272,10 @@ impl App { match event::poll(poll_interval) { Ok(true) => match event::read() { Ok(raw_event) => { - if let Some(ui_event) = events::from_crossterm_event(raw_event) { - if sender.send(AppEvent::Ui(ui_event)).is_err() { - break; - } + if let Some(ui_event) = events::from_crossterm_event(raw_event) + && sender.send(AppEvent::Ui(ui_event)).is_err() + { + break; } } Err(_) => continue, @@ -369,10 +369,10 @@ impl FrameRequester { .expect("frame sender poisoned") .clone() }; - if let Some(tx) = sender { - if tx.send(AppEvent::RedrawRequested).is_ok() { - return; - } + if let Some(tx) = sender + && tx.send(AppEvent::RedrawRequested).is_ok() + { + return; } // Failed to dispatch; clear pending flag so future attempts can retry. diff --git a/crates/owlen-tui/src/chat_app.rs b/crates/owlen-tui/src/chat_app.rs index 95fdae2..95b7986 100644 --- a/crates/owlen-tui/src/chat_app.rs +++ b/crates/owlen-tui/src/chat_app.rs @@ -346,60 +346,60 @@ impl LayoutSnapshot { } fn region_at(&self, column: u16, row: u16) -> Option { - if let Some(rect) = self.model_info_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::ModelInfo); - } + if let Some(rect) = self.model_info_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::ModelInfo); } - if let Some(rect) = self.header_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::Header); - } + if let Some(rect) = self.header_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::Header); } - if let Some(rect) = self.code_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::Code); - } + if let Some(rect) = self.code_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::Code); } - if let Some(rect) = self.file_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::FileTree); - } + if let Some(rect) = self.file_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::FileTree); } - if let Some(rect) = self.input_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::Input); - } + if let Some(rect) = self.input_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::Input); } - if let Some(rect) = self.system_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::System); - } + if let Some(rect) = self.system_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::System); } - if let Some(rect) = self.status_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::Status); - } + if let Some(rect) = self.status_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::Status); } - if let Some(rect) = self.actions_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::Actions); - } + if let Some(rect) = self.actions_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::Actions); } - if let Some(rect) = self.attachments_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::Attachments); - } + if let Some(rect) = self.attachments_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::Attachments); } - if let Some(rect) = self.thinking_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::Thinking); - } + if let Some(rect) = self.thinking_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::Thinking); } - if let Some(rect) = self.chat_panel { - if Self::contains(rect, column, row) { - return Some(UiRegion::Chat); - } + if let Some(rect) = self.chat_panel + && Self::contains(rect, column, row) + { + return Some(UiRegion::Chat); } if Self::contains(self.content, column, row) { Some(UiRegion::Content) @@ -661,10 +661,10 @@ fn search_candidate(candidate: &str, query: &str) -> Option<((usize, usize), Hig return Some(((2, start_byte), HighlightMask::new(mask))); } - if let Some(subsequence_mask) = subsequence_highlight(&lower_graphemes, &query_graphemes) { - if subsequence_mask.iter().any(|b| *b) { - return Some(((3, candidate.len()), HighlightMask::new(subsequence_mask))); - } + if let Some(subsequence_mask) = subsequence_highlight(&lower_graphemes, &query_graphemes) + && subsequence_mask.iter().any(|b| *b) + { + return Some(((3, candidate.len()), HighlightMask::new(subsequence_mask))); } None @@ -2377,10 +2377,9 @@ impl ChatApp { .model_info_panel .current_model_name() .map(|s| s.to_string()) + && let Some(updated) = self.model_details_cache.get(¤t).cloned() { - if let Some(updated) = self.model_details_cache.get(¤t).cloned() { - self.model_info_panel.set_model_info(updated); - } + self.model_info_panel.set_model_info(updated); } let total = self.model_details_cache.len(); self.status = format!("Cached model details for {} model(s)", total); @@ -2604,10 +2603,10 @@ impl ChatApp { }); self.guidance_settings = guidance_settings; - if dirty { - if let Err(err) = self.with_config(config::save_config) { - eprintln!("Warning: Failed to persist guidance settings: {err}"); - } + if dirty + && let Err(err) = self.with_config(config::save_config) + { + eprintln!("Warning: Failed to persist guidance settings: {err}"); } if completed { @@ -3366,16 +3365,16 @@ impl ChatApp { latest_summary = Some((entry.level, clipped)); } - if !self.debug_log.is_visible() { - if let Some((level, message)) = latest_summary { - let level_label = match level { - Level::Error => "Error", - Level::Warn => "Warning", - _ => "Log", - }; - self.status = format!("{level_label}: {message} (F12 to open debug log)"); - self.error = None; - } + if !self.debug_log.is_visible() + && let Some((level, message)) = latest_summary + { + let level_label = match level { + Level::Error => "Error", + Level::Warn => "Warning", + _ => "Log", + }; + self.status = format!("{level_label}: {message} (F12 to open debug log)"); + self.error = None; } } @@ -3553,20 +3552,20 @@ impl ChatApp { const CANDIDATES: [&str; 6] = ["rendered", "text", "content", "value", "message", "body"]; for key in CANDIDATES { - if let Some(Value::String(text)) = map.get(key) { - if !text.trim().is_empty() { - return Some(text.clone()); - } + if let Some(Value::String(text)) = map.get(key) + && !text.trim().is_empty() + { + return Some(text.clone()); } } if let Some(Value::Array(items)) = map.get("lines") { let mut collected = Vec::new(); for item in items { - if let Some(segment) = item.as_str() { - if !segment.trim().is_empty() { - collected.push(segment.trim()); - } + if let Some(segment) = item.as_str() + && !segment.trim().is_empty() + { + collected.push(segment.trim()); } } if !collected.is_empty() { @@ -3579,12 +3578,11 @@ impl ChatApp { } fn extract_mcp_error(value: &Value) -> Option { - if let Value::Object(map) = value { - if let Some(Value::String(message)) = map.get("error") { - if !message.trim().is_empty() { - return Some(message.clone()); - } - } + if let Value::Object(map) = value + && let Some(Value::String(message)) = map.get("error") + && !message.trim().is_empty() + { + return Some(message.clone()); } None } @@ -3836,18 +3834,16 @@ impl ChatApp { } if attachment.is_image() { - if let Some(data) = attachment.base64_data() { - if let Ok(bytes) = BASE64_STANDARD.decode(data) { - if let Some(lines) = Self::preview_lines_for_image(&bytes) { - return lines; - } - } - } else if let Some(path) = attachment.source_path.as_ref() { - if let Ok(bytes) = fs::read(path) { - if let Some(lines) = Self::preview_lines_for_image(&bytes) { - return lines; - } - } + if let Some(data) = attachment.base64_data() + && let Ok(bytes) = BASE64_STANDARD.decode(data) + && let Some(lines) = Self::preview_lines_for_image(&bytes) + { + return lines; + } else if let Some(path) = attachment.source_path.as_ref() + && let Ok(bytes) = fs::read(path) + && let Some(lines) = Self::preview_lines_for_image(&bytes) + { + return lines; } } @@ -4346,21 +4342,19 @@ impl ChatApp { let content_hash = Self::message_content_hash(&role, &content, &tool_signature, &attachment_signature); - if !is_streaming { - if let Some(entry) = self.message_line_cache.get(&message_id) { - if entry.wrap_width == card_width - && entry.role_label_mode == role_label_mode - && entry.syntax_highlighting == syntax_highlighting - && entry.render_markdown == render_markdown - && entry.theme_name == theme.name - && entry.show_timestamps == self.show_message_timestamps - && entry.metrics.body_width == body_width - && entry.metrics.card_width == card_width - && entry.content_hash == content_hash - { - return entry.lines.clone(); - } - } + if !is_streaming + && let Some(entry) = self.message_line_cache.get(&message_id) + && entry.wrap_width == card_width + && entry.role_label_mode == role_label_mode + && entry.syntax_highlighting == syntax_highlighting + && entry.render_markdown == render_markdown + && entry.theme_name == theme.name + && entry.show_timestamps == self.show_message_timestamps + && entry.metrics.body_width == body_width + && entry.metrics.card_width == card_width + && entry.content_hash == content_hash + { + return entry.lines.clone(); } let mut rendered: Vec> = Vec::new(); @@ -8412,12 +8406,11 @@ impl ChatApp { } FocusedPanel::Chat | FocusedPanel::Thinking => { // Move selection forward by word - if let Some((row, col)) = self.visual_end { - if let Some(new_col) = + if let Some((row, col)) = self.visual_end + && let Some(new_col) = self.find_next_word_boundary(row, col) - { - self.visual_end = Some((row, new_col)); - } + { + self.visual_end = Some((row, new_col)); } } FocusedPanel::Files => {} @@ -8432,12 +8425,11 @@ impl ChatApp { } FocusedPanel::Chat | FocusedPanel::Thinking => { // Move selection backward by word - if let Some((row, col)) = self.visual_end { - if let Some(new_col) = + if let Some((row, col)) = self.visual_end + && let Some(new_col) = self.find_prev_word_boundary(row, col) - { - self.visual_end = Some((row, new_col)); - } + { + self.visual_end = Some((row, new_col)); } } FocusedPanel::Files => {} @@ -8466,11 +8458,11 @@ impl ChatApp { } FocusedPanel::Chat | FocusedPanel::Thinking => { // Move selection to end of line - if let Some((row, _)) = self.visual_end { - if let Some(line) = self.get_line_at_row(row) { - let line_len = line.chars().count(); - self.visual_end = Some((row, line_len)); - } + if let Some((row, _)) = self.visual_end + && let Some(line) = self.get_line_at_row(row) + { + let line_len = line.chars().count(); + self.visual_end = Some((row, line_len)); } } FocusedPanel::Files => {} @@ -9424,15 +9416,15 @@ impl ChatApp { ); } } else { - if self.available_providers.is_empty() { - if let Err(err) = self.refresh_models().await { - self.error = Some(format!( - "Failed to refresh providers: {}", - err - )); - self.status = - "Unable to refresh providers".to_string(); - } + if self.available_providers.is_empty() + && let Err(err) = self.refresh_models().await + { + self.error = Some(format!( + "Failed to refresh providers: {}", + err + )); + self.status = + "Unable to refresh providers".to_string(); } let filter = provider_query; @@ -10436,24 +10428,23 @@ impl ChatApp { } } KeyCode::Char(' ') => { - if let Some(item) = self.current_model_selector_item() { - if let ModelSelectorItemKind::Header { + if let Some(item) = self.current_model_selector_item() + && let ModelSelectorItemKind::Header { provider, expanded, .. } = item.kind() - { - if *expanded { - let provider_name = provider.clone(); - self.collapse_provider(&provider_name); - self.status = - format!("Collapsed provider: {}", provider_name); - } else { - let provider_name = provider.clone(); - self.expand_provider(&provider_name, true); - self.status = - format!("Expanded provider: {}", provider_name); - } - self.error = None; + { + if *expanded { + let provider_name = provider.clone(); + self.collapse_provider(&provider_name); + self.status = + format!("Collapsed provider: {}", provider_name); + } else { + let provider_name = provider.clone(); + self.expand_provider(&provider_name, true); + self.status = + format!("Expanded provider: {}", provider_name); } + self.error = None; } } KeyCode::Backspace => { @@ -10506,10 +10497,10 @@ impl ChatApp { } } KeyCode::Char(ch) if ch.is_ascii_digit() => { - if let Some(idx) = ch.to_digit(10) { - if idx >= 1 && (idx as usize) <= HELP_TAB_COUNT { - self.help_tab_index = (idx - 1) as usize; - } + if let Some(idx) = ch.to_digit(10) + && idx >= 1 && (idx as usize) <= HELP_TAB_COUNT + { + self.help_tab_index = (idx - 1) as usize; } } _ => {} @@ -10728,12 +10719,12 @@ impl ChatApp { } } UiRegion::Attachments => { - if let Some(rect) = self.last_layout.attachments_panel { - if row > rect.y + 1 { - let list_index = row.saturating_sub(rect.y + 1) as usize; - if list_index < self.attachment_preview_entries.len() { - self.attachment_preview_selection = list_index; - } + if let Some(rect) = self.last_layout.attachments_panel + && row > rect.y + 1 + { + let list_index = row.saturating_sub(rect.y + 1) as usize; + if list_index < self.attachment_preview_entries.len() { + self.attachment_preview_selection = list_index; } } self.shift_attachment_selection(0); @@ -10746,12 +10737,12 @@ impl ChatApp { UiRegion::Input => { self.focus_panel(FocusedPanel::Input); self.set_input_mode(InputMode::Editing); - if let Some(rect) = self.last_layout.input_panel { - if let Some((line, column)) = self.input_cursor_from_point(rect, column, row) { - let line = line.min(u16::MAX as usize) as u16; - let column = column.min(u16::MAX as usize) as u16; - self.textarea.move_cursor(CursorMove::Jump(line, column)); - } + if let Some(rect) = self.last_layout.input_panel + && let Some((line, column)) = self.input_cursor_from_point(rect, column, row) + { + let line = line.min(u16::MAX as usize) as u16; + let column = column.min(u16::MAX as usize) as u16; + self.textarea.move_cursor(CursorMove::Jump(line, column)); } } UiRegion::ModelInfo => { @@ -11992,18 +11983,17 @@ impl ChatApp { status_entry.state, )); - if status_entry.state != ModelAvailabilityState::Available + if (status_entry.state != ModelAvailabilityState::Available || status_entry.is_stale - || status_entry.message.is_some() + || status_entry.message.is_some()) + && let Some(summary) = Self::scope_status_summary(&status_entry) { - if let Some(summary) = Self::scope_status_summary(&status_entry) { - provider_block.push(ModelSelectorItem::empty( - provider.clone(), - Some(summary), - Some(status_entry.state), - )); - rendered_body = true; - } + provider_block.push(ModelSelectorItem::empty( + provider.clone(), + Some(summary), + Some(status_entry.state), + )); + rendered_body = true; } let scope_allowed = self.filter_scope_allows_models(&scope, &status_entry); @@ -12113,15 +12103,14 @@ impl ChatApp { .map(|item| matches!(item.kind(), ModelSelectorItemKind::Model { .. })) .unwrap_or(false); - if !current_is_model { - if let Some((idx, _)) = self + if !current_is_model + && let Some((idx, _)) = self .model_selector_items .iter() .enumerate() .find(|(_, item)| matches!(item.kind(), ModelSelectorItemKind::Model { .. })) - { - self.set_selected_model_item(idx); - } + { + self.set_selected_model_item(idx); } } } @@ -12136,14 +12125,14 @@ impl ChatApp { let mut best: Option<(usize, usize)> = None; let mut consider = |candidate: Option<&str>, target: &mut Option| { - if let Some(text) = candidate { - if let Some((score, mask)) = search_candidate(text, query) { - let replace = best.is_none_or(|current| score < current); - if replace { - best = Some(score); - } - *target = Some(mask); + if let Some(text) = candidate + && let Some((score, mask)) = search_candidate(text, query) + { + let replace = best.is_none_or(|current| score < current); + if replace { + best = Some(score); } + *target = Some(mask); } }; @@ -12766,14 +12755,14 @@ impl ChatApp { return Err(err); } - if provider.eq_ignore_ascii_case(&self.selected_provider) { - if let Err(err) = self.refresh_models().await { - self.error = Some(format!( - "Provider mode updated but refreshing models failed: {}", - err - )); - return Err(err); - } + if provider.eq_ignore_ascii_case(&self.selected_provider) + && let Err(err) = self.refresh_models().await + { + self.error = Some(format!( + "Provider mode updated but refreshing models failed: {}", + err + )); + return Err(err); } self.error = None; @@ -12868,34 +12857,29 @@ impl ChatApp { .api_key .clone() .filter(|value| !value.trim().is_empty()); - if resolved_api_key.is_none() { - if let Some(existing) = existing_plain_api_key.as_ref() { - if !existing.trim().is_empty() { - resolved_api_key = Some(existing.clone()); - } - } + if resolved_api_key.is_none() + && let Some(existing) = existing_plain_api_key.as_ref() + && !existing.trim().is_empty() + { + resolved_api_key = Some(existing.clone()); } - if resolved_api_key.is_none() && credential_manager.is_some() { - if let Some(manager) = credential_manager.clone() { - if let Some(credentials) = manager - .get_credentials(OLLAMA_CLOUD_CREDENTIAL_ID) - .await - .with_context(|| "Failed to load stored Ollama Cloud credentials")? - { - if !credentials.api_key.trim().is_empty() { - resolved_api_key = Some(credentials.api_key); - } - } - } + if resolved_api_key.is_none() && credential_manager.is_some() + && let Some(manager) = credential_manager.clone() + && let Some(credentials) = manager + .get_credentials(OLLAMA_CLOUD_CREDENTIAL_ID) + .await + .with_context(|| "Failed to load stored Ollama Cloud credentials")? + && !credentials.api_key.trim().is_empty() + { + resolved_api_key = Some(credentials.api_key); } - if resolved_api_key.is_none() { - if let Ok(env_key) = std::env::var("OLLAMA_API_KEY") { - if !env_key.trim().is_empty() { - resolved_api_key = Some(env_key); - } - } + if resolved_api_key.is_none() + && let Ok(env_key) = std::env::var("OLLAMA_API_KEY") + && !env_key.trim().is_empty() + { + resolved_api_key = Some(env_key); } if resolved_api_key.is_none() { @@ -13091,11 +13075,11 @@ impl ChatApp { )); } - if let Some(idx) = self.best_model_match_index(query) { - if let Some(model) = self.models.get(idx).cloned() { - self.apply_model_selection(model).await?; - return Ok(()); - } + if let Some(idx) = self.best_model_match_index(query) + && let Some(model) = self.models.get(idx).cloned() + { + self.apply_model_selection(model).await?; + return Ok(()); } Err(anyhow!(format!( @@ -14183,17 +14167,16 @@ impl ChatApp { let mut summary = segments.next()?.to_string(); - if summary.chars().count() < 120 { - if let Some(next) = segments.next() { - if !next.is_empty() { - if !summary.ends_with('.') && !summary.ends_with('!') && !summary.ends_with('?') - { - summary.push('.'); - } - summary.push(' '); - summary.push_str(next); - } + if summary.chars().count() < 120 + && let Some(next) = segments.next() + && !next.is_empty() + { + if !summary.ends_with('.') && !summary.ends_with('!') && !summary.ends_with('?') + { + summary.push('.'); } + summary.push(' '); + summary.push_str(next); } if summary.chars().count() > 160 { diff --git a/crates/owlen-tui/src/highlight.rs b/crates/owlen-tui/src/highlight.rs index ceec9fa..48854eb 100644 --- a/crates/owlen-tui/src/highlight.rs +++ b/crates/owlen-tui/src/highlight.rs @@ -17,20 +17,22 @@ static THEME: Lazy = Lazy::new(|| { }); fn select_syntax(path_hint: Option<&Path>) -> &'static SyntaxReference { - if let Some(path) = path_hint { - if let Ok(Some(syntax)) = SYNTAX_SET.find_syntax_for_file(path) { - return syntax; - } - if let Some(ext) = path.extension().and_then(|ext| ext.to_str()) { - if let Some(syntax) = SYNTAX_SET.find_syntax_by_extension(ext) { - return syntax; - } - } - if let Some(name) = path.file_name().and_then(|name| name.to_str()) { - if let Some(syntax) = SYNTAX_SET.find_syntax_by_token(name) { - return syntax; - } - } + if let Some(path) = path_hint + && let Ok(Some(syntax)) = SYNTAX_SET.find_syntax_for_file(path) + { + return syntax; + } + if let Some(path) = path_hint + && let Some(ext) = path.extension().and_then(|ext| ext.to_str()) + && let Some(syntax) = SYNTAX_SET.find_syntax_by_extension(ext) + { + return syntax; + } + if let Some(path) = path_hint + && let Some(name) = path.file_name().and_then(|name| name.to_str()) + && let Some(syntax) = SYNTAX_SET.find_syntax_by_token(name) + { + return syntax; } SYNTAX_SET.find_syntax_plain_text() diff --git a/crates/owlen-tui/src/state/file_tree.rs b/crates/owlen-tui/src/state/file_tree.rs index b9a2dca..97afb3c 100644 --- a/crates/owlen-tui/src/state/file_tree.rs +++ b/crates/owlen-tui/src/state/file_tree.rs @@ -302,18 +302,17 @@ impl FileTreeState { return; } - if let Some(rel) = diff_paths(path, &self.root) { - if let Some(index) = self + if let Some(rel) = diff_paths(path, &self.root) + && let Some(index) = self .nodes .iter() .position(|node| node.path == rel || node.path == path) + { + self.expand_to(index); + if let Some(cursor_pos) = self.visible.iter().position(|entry| entry.index == index) { - self.expand_to(index); - if let Some(cursor_pos) = self.visible.iter().position(|entry| entry.index == index) - { - self.cursor = cursor_pos; - self.ensure_cursor_in_view(); - } + self.cursor = cursor_pos; + self.ensure_cursor_in_view(); } } } @@ -563,10 +562,10 @@ fn build_nodes( node.is_expanded = node.should_default_expand(); let index = nodes.len(); - if let Some(parent_idx) = parent { - if let Some(parent_node) = nodes.get_mut(parent_idx) { - parent_node.children.push(index); - } + if let Some(parent_idx) = parent + && let Some(parent_node) = nodes.get_mut(parent_idx) + { + parent_node.children.push(index); } index_by_path.insert(relative, index); diff --git a/crates/owlen-tui/src/state/keymap.rs b/crates/owlen-tui/src/state/keymap.rs index 423213e..e6ce9a2 100644 --- a/crates/owlen-tui/src/state/keymap.rs +++ b/crates/owlen-tui/src/state/keymap.rs @@ -289,10 +289,10 @@ impl KeymapState { } fn expire_if_needed(&mut self, now: Instant) { - if let Some(deadline) = self.deadline { - if now > deadline { - self.reset(); - } + if let Some(deadline) = self.deadline + && now > deadline + { + self.reset(); } } @@ -719,11 +719,11 @@ impl KeymapLoader { return; } - if let Some(path) = path { - if let Ok(text) = fs::read_to_string(&path) { - self.default_path_content = Some(text); - self.active = KeymapProfile::Custom; - } + if let Some(path) = path + && let Ok(text) = fs::read_to_string(&path) + { + self.default_path_content = Some(text); + self.active = KeymapProfile::Custom; } } diff --git a/crates/owlen-tui/src/state/search.rs b/crates/owlen-tui/src/state/search.rs index c7c3053..f4ce288 100644 --- a/crates/owlen-tui/src/state/search.rs +++ b/crates/owlen-tui/src/state/search.rs @@ -182,14 +182,13 @@ impl RepoSearchState { if matches!( self.rows[self.selected_row].kind, RepoSearchRowKind::FileHeader - ) { - if let Some(idx) = self + ) + && let Some(idx) = self .rows .iter() .position(|row| matches!(row.kind, RepoSearchRowKind::Match { .. })) - { - self.selected_row = idx; - } + { + self.selected_row = idx; } self.ensure_selection_visible(); } diff --git a/crates/owlen-tui/src/ui.rs b/crates/owlen-tui/src/ui.rs index 35286ac..5200bce 100644 --- a/crates/owlen-tui/src/ui.rs +++ b/crates/owlen-tui/src/ui.rs @@ -1675,11 +1675,11 @@ fn collect_unsaved_relative_paths(app: &ChatApp, root: &Path) -> HashSet HashSet String { let mut parts = vec![repo_name.to_string()]; for component in path.components() { - if let Component::Normal(segment) = component { - if !segment.is_empty() { - parts.push(segment.to_string_lossy().into_owned()); - } + if let Component::Normal(segment) = component + && !segment.is_empty() + { + parts.push(segment.to_string_lossy().into_owned()); } } parts.join(" > ") @@ -2243,12 +2243,12 @@ fn compute_cursor_metrics( break; } - if !cursor_found { - if let Some(last_segment) = segments.last() { - cursor_visual_row = segment_base_row + segments.len().saturating_sub(1); - cursor_col_width = UnicodeWidthStr::width(last_segment.as_str()); - cursor_found = true; - } + if !cursor_found + && let Some(last_segment) = segments.last() + { + cursor_visual_row = segment_base_row + segments.len().saturating_sub(1); + cursor_col_width = UnicodeWidthStr::width(last_segment.as_str()); + cursor_found = true; } } @@ -2620,10 +2620,9 @@ fn render_messages(frame: &mut Frame<'_>, area: Rect, app: &mut ChatApp) { // Apply visual selection highlighting if in visual mode and Chat panel is focused if matches!(app.mode(), InputMode::Visual) && matches!(app.focused_panel(), FocusedPanel::Chat) + && let Some(selection) = app.visual_selection() { - if let Some(selection) = app.visual_selection() { - lines = apply_visual_selection(lines, Some(selection), &theme); - } + lines = apply_visual_selection(lines, Some(selection), &theme); } // Update AutoScroll state with accurate content length @@ -2780,10 +2779,9 @@ fn render_thinking(frame: &mut Frame<'_>, area: Rect, app: &mut ChatApp) { // Apply visual selection highlighting if in visual mode and Thinking panel is focused if matches!(app.mode(), InputMode::Visual) && matches!(app.focused_panel(), FocusedPanel::Thinking) + && let Some(selection) = app.visual_selection() { - if let Some(selection) = app.visual_selection() { - lines = apply_visual_selection(lines, Some(selection), &theme); - } + lines = apply_visual_selection(lines, Some(selection), &theme); } // Update AutoScroll state with accurate content length @@ -2928,26 +2926,26 @@ fn render_attachment_preview(frame: &mut Frame<'_>, area: Rect, app: &mut ChatAp lines.push(Line::from(spans)); } - if let Some(entry) = entries.get(selected) { - if !entry.preview_lines.is_empty() { + if let Some(entry) = entries.get(selected) + && !entry.preview_lines.is_empty() + { + lines.push(Line::from(vec![Span::styled( + "", + Style::default() + .fg(crate::color_convert::to_ratatui_color(&theme.placeholder)) + .add_modifier(Modifier::DIM), + )])); + for preview in entry + .preview_lines + .iter() + .take(INLINE_ATTACHMENT_PREVIEW_LINES) + { lines.push(Line::from(vec![Span::styled( - "", + preview.clone(), Style::default() .fg(crate::color_convert::to_ratatui_color(&theme.placeholder)) .add_modifier(Modifier::DIM), )])); - for preview in entry - .preview_lines - .iter() - .take(INLINE_ATTACHMENT_PREVIEW_LINES) - { - lines.push(Line::from(vec![Span::styled( - preview.clone(), - Style::default() - .fg(crate::color_convert::to_ratatui_color(&theme.placeholder)) - .add_modifier(Modifier::DIM), - )])); - } } } @@ -5662,29 +5660,29 @@ fn binding_variants( break; } } - if direct.is_none() { - if let Some(desc) = candidates.first() { - let seq = format_sequence(&desc.sequence); - if !desc - .sequence - .first() - .map(|token| token.eq_ignore_ascii_case(leader)) - .unwrap_or(false) - { - direct = Some(seq.clone()); - } + if direct.is_none() + && let Some(desc) = candidates.first() + { + let seq = format_sequence(&desc.sequence); + if !desc + .sequence + .first() + .map(|token| token.eq_ignore_ascii_case(leader)) + .unwrap_or(false) + { + direct = Some(seq.clone()); } } - if leader_binding.is_none() { - if let Some(desc) = candidates.iter().find(|candidate| { + if leader_binding.is_none() + && let Some(desc) = candidates.iter().find(|candidate| { candidate .sequence .first() .map(|token| token.eq_ignore_ascii_case(leader)) .unwrap_or(false) - }) { - leader_binding = Some(format_sequence(&desc.sequence)); - } + }) + { + leader_binding = Some(format_sequence(&desc.sequence)); } (direct, leader_binding) } @@ -6166,21 +6164,21 @@ fn render_theme_browser(frame: &mut Frame<'_>, app: &ChatApp) { if columns.len() >= 2 { let preview_area = columns[1]; - if preview_area.width > 0 && preview_area.height > 0 { - if let Some(selected_name) = themes.get(selected_index) { - let preview_theme = all_themes - .get(selected_name.as_str()) - .cloned() - .or_else(|| all_themes.get(current_theme_name).cloned()) - .unwrap_or_else(|| theme.clone()); - render_theme_preview( - frame, - preview_area, - &preview_theme, - preview_theme.name == theme.name, - app.layer_settings(), - ); - } + if preview_area.width > 0 && preview_area.height > 0 + && let Some(selected_name) = themes.get(selected_index) + { + let preview_theme = all_themes + .get(selected_name.as_str()) + .cloned() + .or_else(|| all_themes.get(current_theme_name).cloned()) + .unwrap_or_else(|| theme.clone()); + render_theme_preview( + frame, + preview_area, + &preview_theme, + preview_theme.name == theme.name, + app.layer_settings(), + ); } }