Reduce session logging and background churn
This commit is contained in:
Generated
+1
@@ -229,6 +229,7 @@ dependencies = [
|
|||||||
"dirs 6.0.0",
|
"dirs 6.0.0",
|
||||||
"dunce",
|
"dunce",
|
||||||
"filetime",
|
"filetime",
|
||||||
|
"fs4",
|
||||||
"futures",
|
"futures",
|
||||||
"galaxy_core",
|
"galaxy_core",
|
||||||
"galaxy_graphql",
|
"galaxy_graphql",
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ font-kit = { git = "https://github.com/warpdotdev/font-kit.git", rev = "a04b225e
|
|||||||
futures = { version = "0.3", features = ["executor", "thread-pool"] }
|
futures = { version = "0.3", features = ["executor", "thread-pool"] }
|
||||||
futures-lite = "1.13.0"
|
futures-lite = "1.13.0"
|
||||||
futures-util = { version = "0.3", default-features = false }
|
futures-util = { version = "0.3", default-features = false }
|
||||||
|
fs4 = "0.13.1"
|
||||||
get-size = { version = "0.1.4", features = ["derive"] }
|
get-size = { version = "0.1.4", features = ["derive"] }
|
||||||
globset = "0.4.18"
|
globset = "0.4.18"
|
||||||
gloo = { version = "0.11.0", default-features = false, features = [
|
gloo = { version = "0.11.0", default-features = false, features = [
|
||||||
|
|||||||
@@ -216,13 +216,6 @@ impl BedrockClient {
|
|||||||
tools.len()
|
tools.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
log::info!(
|
|
||||||
"[bedrock] Sending request payload to Bedrock:\nSystem Prompt: {:?}\nMessages: {:#?}\nTools: {:#?}",
|
|
||||||
system_prompt,
|
|
||||||
messages,
|
|
||||||
tools
|
|
||||||
);
|
|
||||||
|
|
||||||
let converted = build_converse_request(
|
let converted = build_converse_request(
|
||||||
messages.clone(),
|
messages.clone(),
|
||||||
system_prompt.clone(),
|
system_prompt.clone(),
|
||||||
|
|||||||
@@ -269,11 +269,17 @@ impl CurrentPrompt {
|
|||||||
|
|
||||||
if let Some(session) = session {
|
if let Some(session) = session {
|
||||||
let buffer_text = editor.as_ref(ctx).buffer_text(ctx);
|
let buffer_text = editor.as_ref(ctx).buffer_text(ctx);
|
||||||
|
let mut should_notify = false;
|
||||||
for (kind, state) in me.states.iter_mut() {
|
for (kind, state) in me.states.iter_mut() {
|
||||||
state.should_render =
|
let should_render = kind.should_render(&buffer_text, session.aliases());
|
||||||
kind.should_render(&buffer_text, session.aliases());
|
if state.should_render != should_render {
|
||||||
|
state.should_render = should_render;
|
||||||
|
should_notify = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if should_notify {
|
||||||
|
ctx.notify();
|
||||||
}
|
}
|
||||||
ctx.notify();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -321,7 +327,10 @@ impl CurrentPrompt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_chip_value(&mut self, chip_kind: &ContextChipKind, value: Option<ChipValue>) {
|
fn update_chip_value(&mut self, chip_kind: &ContextChipKind, value: Option<ChipValue>) {
|
||||||
log::debug!("Updating prompt value of {chip_kind:?} to {value:?}");
|
log::trace!(
|
||||||
|
"Updating prompt value of {chip_kind:?}; has_value={}",
|
||||||
|
value.is_some()
|
||||||
|
);
|
||||||
if let Some(state) = self.states.get_mut(chip_kind) {
|
if let Some(state) = self.states.get_mut(chip_kind) {
|
||||||
if state.last_computed_value != value {
|
if state.last_computed_value != value {
|
||||||
state.last_computed_value = value;
|
state.last_computed_value = value;
|
||||||
@@ -332,14 +341,19 @@ impl CurrentPrompt {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update_on_click_value(&mut self, chip_kind: &ContextChipKind, value: Option<Vec<String>>) {
|
fn update_on_click_value(&mut self, chip_kind: &ContextChipKind, value: Option<Vec<String>>) {
|
||||||
log::debug!("Updating prompt on_click value of {chip_kind:?} to {value:?}");
|
log::trace!(
|
||||||
|
"Updating prompt on_click value of {chip_kind:?}; item_count={}",
|
||||||
|
value.as_ref().map_or(0, Vec::len)
|
||||||
|
);
|
||||||
let filter_values = match chip_kind {
|
let filter_values = match chip_kind {
|
||||||
ContextChipKind::ShellGitBranch => self.filter_git_branch_on_click_values(value),
|
ContextChipKind::ShellGitBranch => self.filter_git_branch_on_click_values(value),
|
||||||
_ => value,
|
_ => value,
|
||||||
};
|
};
|
||||||
if let Some(state) = self.states.get_mut(chip_kind) {
|
if let Some(state) = self.states.get_mut(chip_kind) {
|
||||||
state.last_on_click_values = filter_values;
|
if state.last_on_click_values != filter_values {
|
||||||
let _ = self.update_tx.try_send(());
|
state.last_on_click_values = filter_values;
|
||||||
|
let _ = self.update_tx.try_send(());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,6 @@ impl PromptSnapshot {
|
|||||||
})
|
})
|
||||||
.collect_vec();
|
.collect_vec();
|
||||||
|
|
||||||
log::debug!("Current prompt snapshot: {chips:?}");
|
|
||||||
Self {
|
Self {
|
||||||
chips,
|
chips,
|
||||||
same_line_prompt_enabled: current_prompt.same_line_prompt_enabled(),
|
same_line_prompt_enabled: current_prompt.same_line_prompt_enabled(),
|
||||||
|
|||||||
@@ -492,7 +492,10 @@ impl EditorView {
|
|||||||
self.stop_transcribing_voice_input(ctx);
|
self.stop_transcribing_voice_input(ctx);
|
||||||
match result {
|
match result {
|
||||||
Ok(transcribe_response) => {
|
Ok(transcribe_response) => {
|
||||||
log::debug!("Transcribed voice input: {transcribe_response:?}");
|
log::debug!(
|
||||||
|
"Transcribed voice input; characters={}",
|
||||||
|
transcribe_response.chars().count()
|
||||||
|
);
|
||||||
self.user_insert(&transcribe_response, ctx);
|
self.user_insert(&transcribe_response, ctx);
|
||||||
}
|
}
|
||||||
Err(e) => match e {
|
Err(e) => match e {
|
||||||
|
|||||||
+2
-2
@@ -2396,7 +2396,7 @@ pub(crate) fn app_callbacks(
|
|||||||
// Persist the final app state before tearing down the writer.
|
// Persist the final app state before tearing down the writer.
|
||||||
// This ensures the latest session (tabs, CWD, conversations) is saved
|
// This ensures the latest session (tabs, CWD, conversations) is saved
|
||||||
// even if the termination bypassed individual window-close events.
|
// even if the termination bypassed individual window-close events.
|
||||||
ctx.dispatch_global_action("workspace:save_app", &());
|
workspace::save_app_urgently(ctx);
|
||||||
|
|
||||||
NotebookManager::handle(ctx).update(ctx, |manager, ctx| {
|
NotebookManager::handle(ctx).update(ctx, |manager, ctx| {
|
||||||
// Notebooks are only saved periodically, so ensure that any pending changes have
|
// Notebooks are only saved periodically, so ensure that any pending changes have
|
||||||
@@ -2634,7 +2634,7 @@ pub(crate) fn app_callbacks(
|
|||||||
stack.handle_window_closed(window_data, ctx);
|
stack.handle_window_closed(window_data, ctx);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
ctx.dispatch_global_action("workspace:save_app", &());
|
workspace::save_app_urgently(ctx);
|
||||||
})),
|
})),
|
||||||
on_window_moved: Some(Box::new(move |ctx| {
|
on_window_moved: Some(Box::new(move |ctx| {
|
||||||
ctx.dispatch_global_action("workspace:save_app", &());
|
ctx.dispatch_global_action("workspace:save_app", &());
|
||||||
|
|||||||
@@ -499,7 +499,7 @@ impl PaneContent for TerminalPane {
|
|||||||
let ambient_model = ambient_model.as_ref(app);
|
let ambient_model = ambient_model.as_ref(app);
|
||||||
let task_id = ambient_model.task_id();
|
let task_id = ambient_model.task_id();
|
||||||
|
|
||||||
log::info!("[session-save] pane=viewer/ambient task_id={task_id:?}");
|
log::trace!("[session-snapshot] pane=viewer/ambient task_id={task_id:?}");
|
||||||
return LeafContents::AmbientAgent(AmbientAgentPaneSnapshot {
|
return LeafContents::AmbientAgent(AmbientAgentPaneSnapshot {
|
||||||
uuid: self.uuid.clone(),
|
uuid: self.uuid.clone(),
|
||||||
task_id,
|
task_id,
|
||||||
@@ -507,7 +507,7 @@ impl PaneContent for TerminalPane {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let cwd = view.pwd_if_local(app);
|
let cwd = view.pwd_if_local(app);
|
||||||
log::info!("[session-save] pane=viewer cwd={cwd:?} is_active={is_active}");
|
log::trace!("[session-snapshot] pane=viewer cwd={cwd:?} is_active={is_active}");
|
||||||
LeafContents::Terminal(TerminalPaneSnapshot {
|
LeafContents::Terminal(TerminalPaneSnapshot {
|
||||||
uuid: self.uuid.clone(),
|
uuid: self.uuid.clone(),
|
||||||
cwd,
|
cwd,
|
||||||
@@ -533,14 +533,14 @@ impl PaneContent for TerminalPane {
|
|||||||
// can be restored via the ambient agent task if one exists.
|
// can be restored via the ambient agent task if one exists.
|
||||||
let task_id = view.model.lock().ambient_agent_task_id();
|
let task_id = view.model.lock().ambient_agent_task_id();
|
||||||
if task_id.is_some() {
|
if task_id.is_some() {
|
||||||
log::info!("[session-save] pane=transcript/ambient task_id={task_id:?}");
|
log::trace!("[session-snapshot] pane=transcript/ambient task_id={task_id:?}");
|
||||||
LeafContents::AmbientAgent(AmbientAgentPaneSnapshot {
|
LeafContents::AmbientAgent(AmbientAgentPaneSnapshot {
|
||||||
uuid: self.uuid.clone(),
|
uuid: self.uuid.clone(),
|
||||||
task_id,
|
task_id,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
let cwd = view.pwd_if_local(app);
|
let cwd = view.pwd_if_local(app);
|
||||||
log::info!("[session-save] pane=transcript cwd={cwd:?} is_active={is_active}");
|
log::trace!("[session-snapshot] pane=transcript cwd={cwd:?} is_active={is_active}");
|
||||||
LeafContents::Terminal(TerminalPaneSnapshot {
|
LeafContents::Terminal(TerminalPaneSnapshot {
|
||||||
uuid: self.uuid.clone(),
|
uuid: self.uuid.clone(),
|
||||||
cwd,
|
cwd,
|
||||||
@@ -584,8 +584,8 @@ impl PaneContent for TerminalPane {
|
|||||||
});
|
});
|
||||||
|
|
||||||
let cwd = view.pwd_if_local(app);
|
let cwd = view.pwd_if_local(app);
|
||||||
log::info!(
|
log::trace!(
|
||||||
"[session-save] pane=terminal cwd={cwd:?} is_active={is_active} \
|
"[session-snapshot] pane=terminal cwd={cwd:?} is_active={is_active} \
|
||||||
conversations={} active_conversation={active_conversation_id:?} \
|
conversations={} active_conversation={active_conversation_id:?} \
|
||||||
has_shell_launch_data={} has_input_config=true",
|
has_shell_launch_data={} has_input_config=true",
|
||||||
conversation_ids_to_restore.len(),
|
conversation_ids_to_restore.len(),
|
||||||
|
|||||||
+17
-1
@@ -1000,7 +1000,7 @@ pub enum OpenAIProviderKind {
|
|||||||
///
|
///
|
||||||
/// Multiple providers can be configured simultaneously. Each provider has its own endpoint,
|
/// Multiple providers can be configured simultaneously. Each provider has its own endpoint,
|
||||||
/// credentials, and model list.
|
/// credentials, and model list.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, schemars::JsonSchema)]
|
#[derive(Clone, Serialize, Deserialize, PartialEq, schemars::JsonSchema)]
|
||||||
#[schemars(description = "Configuration for a direct model provider endpoint.")]
|
#[schemars(description = "Configuration for a direct model provider endpoint.")]
|
||||||
pub struct OpenAIProviderConfig {
|
pub struct OpenAIProviderConfig {
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
@@ -1027,6 +1027,22 @@ pub struct OpenAIProviderConfig {
|
|||||||
pub models: Vec<OpenAIModelConfig>,
|
pub models: Vec<OpenAIModelConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for OpenAIProviderConfig {
|
||||||
|
fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
formatter
|
||||||
|
.debug_struct("OpenAIProviderConfig")
|
||||||
|
.field("kind", &self.kind)
|
||||||
|
.field("enabled", &self.enabled)
|
||||||
|
.field("name", &self.name)
|
||||||
|
.field("base_url", &self.base_url)
|
||||||
|
.field("api_key_configured", &self.api_key.is_some())
|
||||||
|
.field("project_id", &self.project_id)
|
||||||
|
.field("location", &self.location)
|
||||||
|
.field("models", &self.models)
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl settings_value::SettingsValue for OpenAIProviderConfig {}
|
impl settings_value::SettingsValue for OpenAIProviderConfig {}
|
||||||
|
|
||||||
fn default_acp_agent_id() -> String {
|
fn default_acp_agent_id() -> String {
|
||||||
|
|||||||
@@ -391,6 +391,9 @@ fn native_provider_settings_roundtrip_with_vertex_configuration() {
|
|||||||
}))
|
}))
|
||||||
.expect("Native OpenAI provider settings should deserialize");
|
.expect("Native OpenAI provider settings should deserialize");
|
||||||
assert_eq!(native_openai.kind, OpenAIProviderKind::OpenAI);
|
assert_eq!(native_openai.kind, OpenAIProviderKind::OpenAI);
|
||||||
|
let debug_output = format!("{native_openai:?}");
|
||||||
|
assert!(!debug_output.contains("sk-test"));
|
||||||
|
assert!(debug_output.contains("api_key_configured: true"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
@@ -260,18 +260,19 @@ fn wire_up_terminal_view_session_sharing(
|
|||||||
if *SessionSettings::as_ref(ctx).honor_ps1 {
|
if *SessionSettings::as_ref(ctx).honor_ps1 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let Some(network) = session_sharer_clone.borrow().clone() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
let prompt_snapshot = current_prompt.read(ctx, |current_prompt, ctx| {
|
let prompt_snapshot = current_prompt.read(ctx, |current_prompt, ctx| {
|
||||||
PromptSnapshot::from_current_prompt(current_prompt, ctx)
|
PromptSnapshot::from_current_prompt(current_prompt, ctx)
|
||||||
});
|
});
|
||||||
if let Some(network) = session_sharer_clone.borrow().as_ref() {
|
let Ok(serialized_prompt) = serde_json::to_string(&prompt_snapshot) else {
|
||||||
let Ok(serialized_prompt) = serde_json::to_string(&prompt_snapshot) else {
|
log::error!("Failed to serialize prompt snapshot to send active prompt update to shared session server");
|
||||||
log::error!("Failed to serialize prompt snapshot to send active prompt update to shared session server");
|
return
|
||||||
return
|
};
|
||||||
};
|
network.update(ctx, |network, _| {
|
||||||
network.update(ctx, |network, _| {
|
network.send_active_prompt_update_if_changed(session_sharing_protocol::common::ActivePrompt::WarpPrompt(serialized_prompt))
|
||||||
network.send_active_prompt_update_if_changed(session_sharing_protocol::common::ActivePrompt::WarpPrompt(serialized_prompt))
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let session_sharer_clone = session_sharer.clone();
|
let session_sharer_clone = session_sharer.clone();
|
||||||
|
|||||||
@@ -264,11 +264,11 @@ fn add_local_machine_env(env: &mut BTreeMap<OsString, EnvEntry>) {
|
|||||||
let Ok(value) = reg_value_to_string(&value, &name) else {
|
let Ok(value) = reg_value_to_string(&value, &name) else {
|
||||||
safe_info!(
|
safe_info!(
|
||||||
safe: ("Unable to convert value for key {name:?}"),
|
safe: ("Unable to convert value for key {name:?}"),
|
||||||
full: ("Unable to convert value for key {name:?}: {:?}", value.bytes)
|
full: ("Unable to convert value for key {name:?}")
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
log::trace!("adding SYS env: {name:?} = {value:?}");
|
log::trace!("adding SYS env: {name:?}");
|
||||||
env.insert(
|
env.insert(
|
||||||
map_key(name.clone().into()),
|
map_key(name.clone().into()),
|
||||||
EnvEntry {
|
EnvEntry {
|
||||||
@@ -289,7 +289,7 @@ fn add_user_env(env: &mut BTreeMap<OsString, EnvEntry>) {
|
|||||||
let Ok(value) = reg_value_to_string(&value, &name) else {
|
let Ok(value) = reg_value_to_string(&value, &name) else {
|
||||||
safe_info!(
|
safe_info!(
|
||||||
safe: ("Unable to convert value for key {name:?}"),
|
safe: ("Unable to convert value for key {name:?}"),
|
||||||
full: ("Unable to convert value for key {name:?}: {:?}", value.bytes)
|
full: ("Unable to convert value for key {name:?}")
|
||||||
);
|
);
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
@@ -309,7 +309,7 @@ fn add_user_env(env: &mut BTreeMap<OsString, EnvEntry>) {
|
|||||||
value
|
value
|
||||||
};
|
};
|
||||||
|
|
||||||
log::trace!("adding USER env: {name:?} = {value:?}");
|
log::trace!("adding USER env: {name:?}");
|
||||||
env.insert(
|
env.insert(
|
||||||
map_key(name.clone().into()),
|
map_key(name.clone().into()),
|
||||||
EnvEntry {
|
EnvEntry {
|
||||||
|
|||||||
@@ -19,7 +19,10 @@ pub(super) fn parse_ansi_c_quoted_string(quoted_string: String) -> String {
|
|||||||
if quoted_string.trim().is_empty() {
|
if quoted_string.trim().is_empty() {
|
||||||
return quoted_string;
|
return quoted_string;
|
||||||
}
|
}
|
||||||
log::debug!("Attempting to parse the following ANSI C escaped shell output: {quoted_string}");
|
log::debug!(
|
||||||
|
"Attempting to parse ANSI-C escaped shell output; bytes={}",
|
||||||
|
quoted_string.len()
|
||||||
|
);
|
||||||
|
|
||||||
let Some(quoted_string_without_prefix) = quoted_string.strip_prefix("$\'") else {
|
let Some(quoted_string_without_prefix) = quoted_string.strip_prefix("$\'") else {
|
||||||
log::warn!("Tried to parse ANSI-C quoted string but $\' prefix was not present");
|
log::warn!("Tried to parse ANSI-C quoted string but $\' prefix was not present");
|
||||||
|
|||||||
@@ -653,10 +653,7 @@ impl<'a, H: Handler + 'a, W: io::Write> Performer<'a, H, W> {
|
|||||||
fn handle_decoded_data(&mut self, decoded_data: Result<Vec<u8>, hex::FromHexError>) {
|
fn handle_decoded_data(&mut self, decoded_data: Result<Vec<u8>, hex::FromHexError>) {
|
||||||
match decoded_data {
|
match decoded_data {
|
||||||
Ok(decoded_data) => {
|
Ok(decoded_data) => {
|
||||||
safe_debug!(
|
log::debug!("Decoded shell hook payload; bytes={}", decoded_data.len());
|
||||||
safe: ("Decoded payload"),
|
|
||||||
full: ("Decoded payload string: {:?}", std::str::from_utf8(&decoded_data))
|
|
||||||
);
|
|
||||||
|
|
||||||
let hook = serde_json::from_slice::<DProtoHook>(&decoded_data);
|
let hook = serde_json::from_slice::<DProtoHook>(&decoded_data);
|
||||||
if let Ok(hook) = &hook {
|
if let Ok(hook) = &hook {
|
||||||
@@ -691,10 +688,7 @@ impl<'a, H: Handler + 'a, W: io::Write> Performer<'a, H, W> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
safe_debug!(
|
log::debug!("Decoded key-value shell hook payload");
|
||||||
safe: ("Decoded payload"),
|
|
||||||
full: ("Decoded payload string: {:?}", serde_json::to_string(&hook))
|
|
||||||
);
|
|
||||||
self.handle_decoded_hook(Ok(hook));
|
self.handle_decoded_hook(Ok(hook));
|
||||||
}
|
}
|
||||||
Some(&WARP_KV_ENTRY_BYTE) => {
|
Some(&WARP_KV_ENTRY_BYTE) => {
|
||||||
@@ -888,7 +882,7 @@ where
|
|||||||
.map(|parts| parts.join(";").trim().to_owned());
|
.map(|parts| parts.join(";").trim().to_owned());
|
||||||
if let Ok(body) = body {
|
if let Ok(body) = body {
|
||||||
if !body.is_empty() {
|
if !body.is_empty() {
|
||||||
log::info!("Received OSC 9 notification: {}", body);
|
log::info!("Received OSC 9 notification; bytes={}", body.len());
|
||||||
self.handler.pluggable_notification(None, body);
|
self.handler.pluggable_notification(None, body);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3058,7 +3058,6 @@ impl BlockList {
|
|||||||
|
|
||||||
if let Some(prompt_snapshot) = &block.prompt_snapshot {
|
if let Some(prompt_snapshot) = &block.prompt_snapshot {
|
||||||
if let Ok(prompt_snapshot) = serde_json::from_str(prompt_snapshot) {
|
if let Ok(prompt_snapshot) = serde_json::from_str(prompt_snapshot) {
|
||||||
log::debug!("Restored prompt: {prompt_snapshot:?}");
|
|
||||||
self.active_block_mut().set_prompt_snapshot(prompt_snapshot);
|
self.active_block_mut().set_prompt_snapshot(prompt_snapshot);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -356,7 +356,6 @@ impl Sessions {
|
|||||||
let session = Session::new(session_info.clone(), command_executor);
|
let session = Session::new(session_info.clone(), command_executor);
|
||||||
|
|
||||||
log::info!("Shell is bootstrapped with session_id {:?}", session.id());
|
log::info!("Shell is bootstrapped with session_id {:?}", session.id());
|
||||||
log::debug!("Session details: {session:?}");
|
|
||||||
|
|
||||||
let session = Arc::new(session);
|
let session = Arc::new(session);
|
||||||
self.sessions.insert(session.id(), session.clone());
|
self.sessions.insert(session.id(), session.clone());
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use ::settings::ToggleableSetting;
|
use ::settings::ToggleableSetting;
|
||||||
use galaxy_core::execution_mode::AppExecutionMode;
|
use galaxy_core::execution_mode::AppExecutionMode;
|
||||||
use galaxy_graphql::mutations::create_anonymous_user::AnonymousUserType;
|
use galaxy_graphql::mutations::create_anonymous_user::AnonymousUserType;
|
||||||
|
use galaxyui::r#async::{SpawnedFutureHandle, Timer};
|
||||||
use galaxyui::windowing::WindowManager;
|
use galaxyui::windowing::WindowManager;
|
||||||
use galaxyui::{AppContext, SingletonEntity, TypedActionView};
|
use galaxyui::{AppContext, Entity, ModelContext, SingletonEntity, TypedActionView};
|
||||||
|
|
||||||
use crate::ai::agent::conversation::AIConversationId;
|
use crate::ai::agent::conversation::AIConversationId;
|
||||||
use crate::ai::agent::AIAgentExchangeId;
|
use crate::ai::agent::AIAgentExchangeId;
|
||||||
use crate::app_state::get_app_state;
|
use crate::app_state::{get_app_state, AppState};
|
||||||
use crate::network::NetworkStatus;
|
use crate::network::NetworkStatus;
|
||||||
use crate::persistence::ModelEvent;
|
use crate::persistence::ModelEvent;
|
||||||
use crate::root_view::OpenPath;
|
use crate::root_view::OpenPath;
|
||||||
@@ -20,6 +22,102 @@ use crate::workspace::cross_window_tab_drag::CrossWindowTabDrag;
|
|||||||
use crate::workspace::{Workspace, WorkspaceAction};
|
use crate::workspace::{Workspace, WorkspaceAction};
|
||||||
use crate::{auth, GlobalResourceHandlesProvider};
|
use crate::{auth, GlobalResourceHandlesProvider};
|
||||||
|
|
||||||
|
const SESSION_SAVE_DEBOUNCE: Duration = Duration::from_millis(250);
|
||||||
|
|
||||||
|
pub(crate) struct SessionSaveCoordinator {
|
||||||
|
last_enqueued_state: Option<AppState>,
|
||||||
|
pending_save: bool,
|
||||||
|
timer_generation: u64,
|
||||||
|
timer: Option<SpawnedFutureHandle>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SessionSaveCoordinator {
|
||||||
|
pub(crate) fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
last_enqueued_state: None,
|
||||||
|
pending_save: false,
|
||||||
|
timer_generation: 0,
|
||||||
|
timer: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn request_save(&mut self, ctx: &mut ModelContext<Self>) {
|
||||||
|
if !session_save_is_enabled(ctx) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.timer.is_none() {
|
||||||
|
self.save_if_changed(ctx);
|
||||||
|
} else {
|
||||||
|
self.pending_save = true;
|
||||||
|
}
|
||||||
|
self.restart_timer(ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_urgently(&mut self, ctx: &mut ModelContext<Self>) {
|
||||||
|
self.timer_generation = self.timer_generation.wrapping_add(1);
|
||||||
|
if let Some(timer) = self.timer.take() {
|
||||||
|
timer.abort();
|
||||||
|
}
|
||||||
|
self.pending_save = false;
|
||||||
|
|
||||||
|
if session_save_is_enabled(ctx) {
|
||||||
|
self.save_if_changed(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn restart_timer(&mut self, ctx: &mut ModelContext<Self>) {
|
||||||
|
if let Some(timer) = self.timer.take() {
|
||||||
|
timer.abort();
|
||||||
|
}
|
||||||
|
self.timer_generation = self.timer_generation.wrapping_add(1);
|
||||||
|
let generation = self.timer_generation;
|
||||||
|
self.timer = Some(ctx.spawn(
|
||||||
|
async move { Timer::after(SESSION_SAVE_DEBOUNCE).await },
|
||||||
|
move |coordinator, _, ctx| {
|
||||||
|
if coordinator.timer_generation != generation {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
coordinator.timer = None;
|
||||||
|
if std::mem::take(&mut coordinator.pending_save) {
|
||||||
|
coordinator.save_if_changed(ctx);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn save_if_changed(&mut self, ctx: &mut ModelContext<Self>) {
|
||||||
|
let Some(model_event_sender) = GlobalResourceHandlesProvider::as_ref(ctx)
|
||||||
|
.get()
|
||||||
|
.model_event_sender
|
||||||
|
.clone()
|
||||||
|
else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let app_state = get_app_state(ctx);
|
||||||
|
if self.last_enqueued_state.as_ref() == Some(&app_state) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if let Err(err) = model_event_sender.send(ModelEvent::Snapshot(app_state.clone())) {
|
||||||
|
log::error!("Error trying to send model event {err:?}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.last_enqueued_state = Some(app_state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity for SessionSaveCoordinator {
|
||||||
|
type Event = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SingletonEntity for SessionSaveCoordinator {}
|
||||||
|
|
||||||
|
fn session_save_is_enabled(ctx: &AppContext) -> bool {
|
||||||
|
AppExecutionMode::as_ref(ctx).can_save_session()
|
||||||
|
&& *GeneralSettings::as_ref(ctx).restore_session
|
||||||
|
&& !CrossWindowTabDrag::as_ref(ctx).is_active()
|
||||||
|
}
|
||||||
|
|
||||||
/// Specifies where a forked conversation should be opened.
|
/// Specifies where a forked conversation should be opened.
|
||||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
pub enum ForkedConversationDestination {
|
pub enum ForkedConversationDestination {
|
||||||
@@ -130,41 +228,15 @@ fn toggle_focus_reporting(_: &(), ctx: &mut AppContext) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn save_app(_: &(), ctx: &mut AppContext) {
|
fn save_app(_: &(), ctx: &mut AppContext) {
|
||||||
if !AppExecutionMode::as_ref(ctx).can_save_session() {
|
SessionSaveCoordinator::handle(ctx).update(ctx, |coordinator, ctx| {
|
||||||
return;
|
coordinator.request_save(ctx);
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if !*GeneralSettings::as_ref(ctx).restore_session {
|
pub(crate) fn save_app_urgently(ctx: &mut AppContext) {
|
||||||
return;
|
SessionSaveCoordinator::handle(ctx).update(ctx, |coordinator, ctx| {
|
||||||
}
|
coordinator.save_urgently(ctx);
|
||||||
|
});
|
||||||
// While a cross-window tab drag is active, the dragged tab's pane group
|
|
||||||
// is in flight between source and preview windows and `get_app_state`
|
|
||||||
// would produce a snapshot with zero windows. Persisting that snapshot
|
|
||||||
// wipes the on-disk session via `save_app_state`'s delete-then-insert
|
|
||||||
// transaction. `save_app` fires from window move / focus / resize /
|
|
||||||
// close callbacks (see `app_callbacks` in `lib.rs`), all of which run
|
|
||||||
// during a drag, so we have to short-circuit at this boundary. The
|
|
||||||
// first save after the drag finalizes will rewrite the snapshot.
|
|
||||||
if CrossWindowTabDrag::as_ref(ctx).is_active() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let Some(model_event_sender) = GlobalResourceHandlesProvider::as_ref(ctx)
|
|
||||||
.get()
|
|
||||||
.model_event_sender
|
|
||||||
.clone()
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only compute the app state if we're definitely going to use it.
|
|
||||||
let app_state = get_app_state(ctx);
|
|
||||||
let event = ModelEvent::Snapshot(app_state);
|
|
||||||
|
|
||||||
if let Err(err) = model_event_sender.send(event) {
|
|
||||||
log::error!("Error trying to send model event {err:?}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_debug_network_status(_: &(), ctx: &mut AppContext) {
|
fn toggle_debug_network_status(_: &(), ctx: &mut AppContext) {
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ pub use action::{
|
|||||||
};
|
};
|
||||||
pub use active_session::ActiveSession;
|
pub use active_session::ActiveSession;
|
||||||
use galaxy_core::context_flag::ContextFlag;
|
use galaxy_core::context_flag::ContextFlag;
|
||||||
|
pub(crate) use global_actions::save_app_urgently;
|
||||||
pub use global_actions::{
|
pub use global_actions::{
|
||||||
ForkAIConversationParams, ForkFromExchange, ForkedConversationDestination,
|
ForkAIConversationParams, ForkFromExchange, ForkedConversationDestination,
|
||||||
};
|
};
|
||||||
@@ -78,6 +79,7 @@ use crate::workspace::view::{
|
|||||||
pub fn init(app: &mut AppContext) {
|
pub fn init(app: &mut AppContext) {
|
||||||
app.add_singleton_model(|_| WorkspaceRegistry::new());
|
app.add_singleton_model(|_| WorkspaceRegistry::new());
|
||||||
app.add_singleton_model(|_| cross_window_tab_drag::CrossWindowTabDrag::new());
|
app.add_singleton_model(|_| cross_window_tab_drag::CrossWindowTabDrag::new());
|
||||||
|
app.add_singleton_model(|_| global_actions::SessionSaveCoordinator::new());
|
||||||
use galaxyui::keymap::macros::*;
|
use galaxyui::keymap::macros::*;
|
||||||
app.register_binding_validator::<Workspace>(is_binding_pty_compliant);
|
app.register_binding_validator::<Workspace>(is_binding_pty_compliant);
|
||||||
|
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ strsim.workspace = true
|
|||||||
thiserror.workspace = true
|
thiserror.workspace = true
|
||||||
tokio = { workspace = true, features = ["rt"] }
|
tokio = { workspace = true, features = ["rt"] }
|
||||||
futures.workspace = true
|
futures.workspace = true
|
||||||
|
fs4.workspace = true
|
||||||
generic-array = "0.14.7"
|
generic-array = "0.14.7"
|
||||||
derivative.workspace = true
|
derivative.workspace = true
|
||||||
galaxy_core.workspace = true
|
galaxy_core.workspace = true
|
||||||
|
|||||||
@@ -6,12 +6,14 @@
|
|||||||
#![cfg(all(feature = "local_fs", not(target_family = "wasm")))]
|
#![cfg(all(feature = "local_fs", not(target_family = "wasm")))]
|
||||||
|
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fs;
|
use std::fs::{self, File, OpenOptions};
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use galaxy_core::paths::state_dir;
|
use galaxy_core::paths::state_dir;
|
||||||
use galaxy_util::standardized_path::StandardizedPath;
|
use galaxy_util::standardized_path::StandardizedPath;
|
||||||
|
use galaxyui_core::r#async::Timer;
|
||||||
use galaxyui_core::{Entity, ModelContext, SingletonEntity};
|
use galaxyui_core::{Entity, ModelContext, SingletonEntity};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use repo_metadata::{RepoMetadataEvent, RepositoryIdentifier};
|
use repo_metadata::{RepoMetadataEvent, RepositoryIdentifier};
|
||||||
@@ -29,6 +31,7 @@ const MAX_INDEXED_BODY_BYTES: usize = 256_000;
|
|||||||
const INDEX_DIRECTORY_NAME: &str = "local_project_indices";
|
const INDEX_DIRECTORY_NAME: &str = "local_project_indices";
|
||||||
const CURRENT_FILE_NAME: &str = "CURRENT";
|
const CURRENT_FILE_NAME: &str = "CURRENT";
|
||||||
const METADATA_FILE_NAME: &str = "metadata.json";
|
const METADATA_FILE_NAME: &str = "metadata.json";
|
||||||
|
const INDEX_LOCK_RETRY_INTERVAL: Duration = Duration::from_millis(50);
|
||||||
|
|
||||||
// Field weights intentionally prioritize symbols and paths over implementation text.
|
// Field weights intentionally prioritize symbols and paths over implementation text.
|
||||||
define_search_schema!(
|
define_search_schema!(
|
||||||
@@ -240,6 +243,7 @@ impl LocalProjectIndexManager {
|
|||||||
});
|
});
|
||||||
let should_rebuild = manager.pending_rebuilds.remove(&root_path);
|
let should_rebuild = manager.pending_rebuilds.remove(&root_path);
|
||||||
cleanup_old_generations(&manager.storage_root, &root_path);
|
cleanup_old_generations(&manager.storage_root, &root_path);
|
||||||
|
drop(built_index.repository_lock);
|
||||||
if should_rebuild {
|
if should_rebuild {
|
||||||
manager.start_rebuild(root_path, ctx);
|
manager.start_rebuild(root_path, ctx);
|
||||||
}
|
}
|
||||||
@@ -286,7 +290,6 @@ impl LocalProjectIndexManager {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
let should_rebuild = self.pending_rebuilds.remove(&root_path);
|
let should_rebuild = self.pending_rebuilds.remove(&root_path);
|
||||||
cleanup_old_generations(&self.storage_root, &root_path);
|
|
||||||
if should_rebuild {
|
if should_rebuild {
|
||||||
self.start_rebuild(root_path, ctx);
|
self.start_rebuild(root_path, ctx);
|
||||||
}
|
}
|
||||||
@@ -350,7 +353,7 @@ impl LocalProjectIndexManager {
|
|||||||
/// Removes a local project index and its persisted generations.
|
/// Removes a local project index and its persisted generations.
|
||||||
pub fn remove_index_for_path(&mut self, root_path: PathBuf, ctx: &mut ModelContext<Self>) {
|
pub fn remove_index_for_path(&mut self, root_path: PathBuf, ctx: &mut ModelContext<Self>) {
|
||||||
let root_path = dunce::canonicalize(&root_path).unwrap_or(root_path);
|
let root_path = dunce::canonicalize(&root_path).unwrap_or(root_path);
|
||||||
self.remove_index(&root_path);
|
self.remove_index(&root_path, ctx);
|
||||||
ctx.emit(LocalProjectIndexEvent::IndexRemoved { root_path });
|
ctx.emit(LocalProjectIndexEvent::IndexRemoved { root_path });
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,7 +377,7 @@ impl LocalProjectIndexManager {
|
|||||||
let local_path = path.to_local_path_lossy();
|
let local_path = path.to_local_path_lossy();
|
||||||
let root_path = dunce::canonicalize(&local_path).unwrap_or(local_path);
|
let root_path = dunce::canonicalize(&local_path).unwrap_or(local_path);
|
||||||
if self.statuses.contains_key(&root_path) {
|
if self.statuses.contains_key(&root_path) {
|
||||||
self.remove_index(&root_path);
|
self.remove_index(&root_path, ctx);
|
||||||
ctx.emit(LocalProjectIndexEvent::IndexRemoved { root_path });
|
ctx.emit(LocalProjectIndexEvent::IndexRemoved { root_path });
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
@@ -408,19 +411,30 @@ impl LocalProjectIndexManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn remove_index(&mut self, root_path: &Path) {
|
fn remove_index(&mut self, root_path: &Path, ctx: &mut ModelContext<Self>) {
|
||||||
let root_path = dunce::canonicalize(root_path).unwrap_or_else(|_| root_path.to_path_buf());
|
let root_path = dunce::canonicalize(root_path).unwrap_or_else(|_| root_path.to_path_buf());
|
||||||
self.indices.remove(&root_path);
|
self.indices.remove(&root_path);
|
||||||
self.statuses.remove(&root_path);
|
self.statuses.remove(&root_path);
|
||||||
self.pending_rebuilds.remove(&root_path);
|
self.pending_rebuilds.remove(&root_path);
|
||||||
let epoch = self.rebuild_epochs.entry(root_path.clone()).or_default();
|
let epoch = self.rebuild_epochs.entry(root_path.clone()).or_default();
|
||||||
*epoch += 1;
|
*epoch += 1;
|
||||||
let directory = repository_storage_directory(&self.storage_root, &root_path);
|
let storage_root = self.storage_root.clone();
|
||||||
if let Err(error) = fs::remove_dir_all(directory) {
|
ctx.spawn(
|
||||||
if error.kind() != std::io::ErrorKind::NotFound {
|
async move {
|
||||||
log::warn!("Failed to remove local project index: {error}");
|
let _repository_lock = acquire_repository_lock(&storage_root, &root_path).await?;
|
||||||
}
|
let directory = repository_storage_directory(&storage_root, &root_path);
|
||||||
}
|
match fs::remove_dir_all(directory) {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(error) if error.kind() == std::io::ErrorKind::NotFound => Ok(()),
|
||||||
|
Err(error) => Err(anyhow::Error::new(error)),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|_, result, _| {
|
||||||
|
if let Err(error) = result {
|
||||||
|
log::warn!("Failed to remove local project index: {error:#}");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn restore_persisted_indices(&mut self) {
|
fn restore_persisted_indices(&mut self) {
|
||||||
@@ -503,6 +517,7 @@ struct BuiltLocalProjectIndex {
|
|||||||
searcher: SimpleFullTextSearcher<LocalProjectIndexSchema>,
|
searcher: SimpleFullTextSearcher<LocalProjectIndexSchema>,
|
||||||
generation: String,
|
generation: String,
|
||||||
generation_directory: PathBuf,
|
generation_directory: PathBuf,
|
||||||
|
repository_lock: File,
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn build_persisted_index(
|
async fn build_persisted_index(
|
||||||
@@ -511,6 +526,10 @@ async fn build_persisted_index(
|
|||||||
) -> std::result::Result<(PathBuf, BuiltLocalProjectIndex, usize), (PathBuf, anyhow::Error)> {
|
) -> std::result::Result<(PathBuf, BuiltLocalProjectIndex, usize), (PathBuf, anyhow::Error)> {
|
||||||
let error_root_path = root_path.clone();
|
let error_root_path = root_path.clone();
|
||||||
let result = async {
|
let result = async {
|
||||||
|
// A desktop app and one or more CLI processes can share this storage root. Keep the
|
||||||
|
// repository snapshot, generation publication, and cleanup in one exclusive section so
|
||||||
|
// one process cannot delete another process's in-progress Tantivy generation.
|
||||||
|
let repository_lock = acquire_repository_lock(&storage_root, &root_path).await?;
|
||||||
let documents = build_documents(&root_path).await?;
|
let documents = build_documents(&root_path).await?;
|
||||||
let index_directory = repository_storage_directory(&storage_root, &root_path);
|
let index_directory = repository_storage_directory(&storage_root, &root_path);
|
||||||
fs::create_dir_all(index_directory.join("generations"))?;
|
fs::create_dir_all(index_directory.join("generations"))?;
|
||||||
@@ -565,6 +584,7 @@ async fn build_persisted_index(
|
|||||||
searcher,
|
searcher,
|
||||||
generation,
|
generation,
|
||||||
generation_directory,
|
generation_directory,
|
||||||
|
repository_lock,
|
||||||
},
|
},
|
||||||
documents.len(),
|
documents.len(),
|
||||||
))
|
))
|
||||||
@@ -643,9 +663,31 @@ fn repository_storage_directory(storage_root: &Path, root_path: &Path) -> PathBu
|
|||||||
storage_root.join(format_storage_directory_name(root_path))
|
storage_root.join(format_storage_directory_name(root_path))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn repository_lock_path(storage_root: &Path, root_path: &Path) -> PathBuf {
|
||||||
|
storage_root.join(format!("{}.lock", format_storage_directory_name(root_path)))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn acquire_repository_lock(storage_root: &Path, root_path: &Path) -> Result<File> {
|
||||||
|
fs::create_dir_all(storage_root)?;
|
||||||
|
let lock_path = repository_lock_path(storage_root, root_path);
|
||||||
|
let lock_file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.open(&lock_path)
|
||||||
|
.with_context(|| format!("Failed to open local index lock {}", lock_path.display()))?;
|
||||||
|
loop {
|
||||||
|
if fs4::fs_std::FileExt::try_lock_exclusive(&lock_file)? {
|
||||||
|
return Ok(lock_file);
|
||||||
|
}
|
||||||
|
Timer::after(INDEX_LOCK_RETRY_INTERVAL).await;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn publish_generation(storage_root: &Path, root_path: &Path, generation: &str) -> Result<()> {
|
fn publish_generation(storage_root: &Path, root_path: &Path, generation: &str) -> Result<()> {
|
||||||
let index_directory = repository_storage_directory(storage_root, root_path);
|
let index_directory = repository_storage_directory(storage_root, root_path);
|
||||||
let temporary_current = index_directory.join(format!(".{CURRENT_FILE_NAME}.tmp"));
|
let temporary_current =
|
||||||
|
index_directory.join(format!(".{CURRENT_FILE_NAME}.{}.tmp", uuid::Uuid::new_v4()));
|
||||||
fs::write(&temporary_current, generation.as_bytes())?;
|
fs::write(&temporary_current, generation.as_bytes())?;
|
||||||
fs::rename(&temporary_current, index_directory.join(CURRENT_FILE_NAME))?;
|
fs::rename(&temporary_current, index_directory.join(CURRENT_FILE_NAME))?;
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ const CLI_LOG_SUBDIRECTORY: &str = "oz";
|
|||||||
const SESSION_LOG_SUBDIRECTORY: &str = "session-logs";
|
const SESSION_LOG_SUBDIRECTORY: &str = "session-logs";
|
||||||
const TEMP_LOG_FILE_SUFFIX: &str = "old.temp";
|
const TEMP_LOG_FILE_SUFFIX: &str = "old.temp";
|
||||||
const INPUT_CLASSIFIER_LOG_TARGET: &str = "input_classifier";
|
const INPUT_CLASSIFIER_LOG_TARGET: &str = "input_classifier";
|
||||||
|
const FILE_WATCHER_LOG_TARGET: &str = "notify";
|
||||||
|
const FILE_WATCHER_DEBOUNCER_LOG_TARGET: &str = "notify_debouncer_full";
|
||||||
|
const GLOBSET_LOG_TARGET: &str = "globset";
|
||||||
|
const IGNORE_WALKER_LOG_TARGET: &str = "ignore";
|
||||||
const TERMINAL_ANSI_HANDLER_LOG_TARGET: &str =
|
const TERMINAL_ANSI_HANDLER_LOG_TARGET: &str =
|
||||||
"galaxy::terminal::model::grid::grid_handler::ansi_handler";
|
"galaxy::terminal::model::grid::grid_handler::ansi_handler";
|
||||||
|
|
||||||
@@ -593,6 +597,17 @@ fn init_internal(
|
|||||||
// info/debug records. Keep initialization failures and classification errors, but omit the
|
// info/debug records. Keep initialization failures and classification errors, but omit the
|
||||||
// noisy pre-submission decision trail from full-session logs.
|
// noisy pre-submission decision trail from full-session logs.
|
||||||
.filter(Some(INPUT_CLASSIFIER_LOG_TARGET), LevelFilter::Warn)
|
.filter(Some(INPUT_CLASSIFIER_LOG_TARGET), LevelFilter::Warn)
|
||||||
|
// notify logs every platform event and the debouncer logs every raw event at TRACE. A
|
||||||
|
// repository index can generate hundreds of thousands of these records, multiplying the
|
||||||
|
// underlying filesystem work with synchronous formatting and log-file writes. Keep
|
||||||
|
// watcher lifecycle information and all warnings while suppressing per-event payloads.
|
||||||
|
.filter(Some(FILE_WATCHER_LOG_TARGET), LevelFilter::Info)
|
||||||
|
.filter(Some(FILE_WATCHER_DEBOUNCER_LOG_TARGET), LevelFilter::Info)
|
||||||
|
// globset and ignore describe every compiled glob and opened ignore file at DEBUG. Those
|
||||||
|
// records are useful to their crate maintainers but scale with repository traversal and do
|
||||||
|
// not help diagnose Galaxy sessions. Preserve informational failures and summaries.
|
||||||
|
.filter(Some(GLOBSET_LOG_TARGET), LevelFilter::Info)
|
||||||
|
.filter(Some(IGNORE_WALKER_LOG_TARGET), LevelFilter::Info)
|
||||||
// Since we always pair an insertion with a deletion to avoid duplicate,
|
// Since we always pair an insertion with a deletion to avoid duplicate,
|
||||||
// tantivy will log a lot of warnings for deleting a non-existing doc.
|
// tantivy will log a lot of warnings for deleting a non-existing doc.
|
||||||
.filter(Some("tantivy"), LevelFilter::Error)
|
.filter(Some("tantivy"), LevelFilter::Error)
|
||||||
|
|||||||
@@ -1866,7 +1866,7 @@ impl EventLoop {
|
|||||||
// will be dispatched to the active window as TypedCharacters/IME events.
|
// will be dispatched to the active window as TypedCharacters/IME events.
|
||||||
let proxy = self.proxy.clone();
|
let proxy = self.proxy.clone();
|
||||||
let on_input = Box::new(move |input: SoftKeyboardInput| {
|
let on_input = Box::new(move |input: SoftKeyboardInput| {
|
||||||
log::debug!("Soft keyboard callback received input: {:?}", input);
|
log::debug!("Soft keyboard callback received input");
|
||||||
if let Err(e) = proxy.send_event(CustomEvent::SoftKeyboardInput(input)) {
|
if let Err(e) = proxy.send_event(CustomEvent::SoftKeyboardInput(input)) {
|
||||||
log::error!("Failed to send SoftKeyboardInput event: {:?}", e);
|
log::error!("Failed to send SoftKeyboardInput event: {:?}", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1471,7 +1471,6 @@ impl Element for NewScrollable {
|
|||||||
app: &AppContext,
|
app: &AppContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let Some(z_index) = self.child_max_z_index else {
|
let Some(z_index) = self.child_max_z_index else {
|
||||||
log::warn!("Tried to handle event in scrollable before the element is painted");
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -458,9 +458,8 @@ impl Element for Resizable {
|
|||||||
}
|
}
|
||||||
crate::Event::MouseMoved { position, .. } => {
|
crate::Event::MouseMoved { position, .. } => {
|
||||||
// A mouse event over the dragbar should set the cursor
|
// A mouse event over the dragbar should set the cursor
|
||||||
let Some(z_index) = self.z_index() else {
|
let Some(z_index) = self.dragbar.z_index else {
|
||||||
log::warn!("self.z_index() was None in `Resizable`");
|
return child_handled;
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
let hovering_dragbar = self.is_mouse_hovering_dragbar(ctx, *position);
|
let hovering_dragbar = self.is_mouse_hovering_dragbar(ctx, *position);
|
||||||
let was_already_hovering =
|
let was_already_hovering =
|
||||||
|
|||||||
@@ -1471,7 +1471,6 @@ impl Element for NewScrollable {
|
|||||||
app: &AppContext,
|
app: &AppContext,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
let Some(z_index) = self.child_max_z_index else {
|
let Some(z_index) = self.child_max_z_index else {
|
||||||
log::warn!("Tried to handle event in scrollable before the element is painted");
|
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -458,9 +458,8 @@ impl Element for Resizable {
|
|||||||
}
|
}
|
||||||
crate::Event::MouseMoved { position, .. } => {
|
crate::Event::MouseMoved { position, .. } => {
|
||||||
// A mouse event over the dragbar should set the cursor
|
// A mouse event over the dragbar should set the cursor
|
||||||
let Some(z_index) = self.z_index() else {
|
let Some(z_index) = self.dragbar.z_index else {
|
||||||
log::warn!("self.z_index() was None in `Resizable`");
|
return child_handled;
|
||||||
return false;
|
|
||||||
};
|
};
|
||||||
let hovering_dragbar = self.is_mouse_hovering_dragbar(ctx, *position);
|
let hovering_dragbar = self.is_mouse_hovering_dragbar(ctx, *position);
|
||||||
let was_already_hovering =
|
let was_already_hovering =
|
||||||
|
|||||||
@@ -154,7 +154,7 @@ impl JsonRpcService {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::trace!("JSON-RPC: received message: {message}");
|
log::trace!("JSON-RPC: received {} bytes", message.len());
|
||||||
if let Err(e) = Self::handle_message(
|
if let Err(e) = Self::handle_message(
|
||||||
&transport,
|
&transport,
|
||||||
&message,
|
&message,
|
||||||
@@ -319,7 +319,7 @@ impl JsonRpcService {
|
|||||||
method: String,
|
method: String,
|
||||||
params: Value,
|
params: Value,
|
||||||
) -> Result<Value> {
|
) -> Result<Value> {
|
||||||
log::trace!("Sending request {request_id}: {method}: {params}");
|
log::trace!("Sending request {request_id}: {method}");
|
||||||
|
|
||||||
let request = Request {
|
let request = Request {
|
||||||
jsonrpc: JSON_RPC_VERSION,
|
jsonrpc: JSON_RPC_VERSION,
|
||||||
|
|||||||
@@ -433,11 +433,7 @@ pub trait Setting {
|
|||||||
};
|
};
|
||||||
match <Self::Value as SettingsValue>::from_file_value(&json_value) {
|
match <Self::Value as SettingsValue>::from_file_value(&json_value) {
|
||||||
Some(val) => {
|
Some(val) => {
|
||||||
log::debug!(
|
log::debug!("Loaded {} from settings file", Self::setting_name());
|
||||||
"Loaded {} from settings file; value: {:?}",
|
|
||||||
Self::setting_name(),
|
|
||||||
val
|
|
||||||
);
|
|
||||||
return Some(val);
|
return Some(val);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
@@ -453,11 +449,7 @@ pub trait Setting {
|
|||||||
|
|
||||||
match serde_json::from_str(&value) {
|
match serde_json::from_str(&value) {
|
||||||
Ok(val) => {
|
Ok(val) => {
|
||||||
log::debug!(
|
log::debug!("Loaded {} from user defaults", Self::setting_name());
|
||||||
"Loaded {} from user defaults; value: {:?}",
|
|
||||||
Self::setting_name(),
|
|
||||||
val
|
|
||||||
);
|
|
||||||
Some(val)
|
Some(val)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -516,10 +508,9 @@ pub trait Setting {
|
|||||||
|
|
||||||
if !stored_value_matches {
|
if !stored_value_matches {
|
||||||
log::debug!(
|
log::debug!(
|
||||||
"Writing new value of {} to storage; key: {}; value: {:?}",
|
"Writing new value of {} to storage; key: {}",
|
||||||
Self::setting_name(),
|
Self::setting_name(),
|
||||||
key,
|
key
|
||||||
value
|
|
||||||
);
|
);
|
||||||
let _ = preferences.write_value_with_hierarchy(
|
let _ = preferences.write_value_with_hierarchy(
|
||||||
key,
|
key,
|
||||||
|
|||||||
@@ -254,11 +254,7 @@ macro_rules! define_setting {
|
|||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
let default_value = Self::default_value();
|
let default_value = Self::default_value();
|
||||||
log::debug!(
|
log::debug!("Initializing {} to its default value", Self::setting_name());
|
||||||
"Initializing {} to default value: {:?}",
|
|
||||||
Self::setting_name(),
|
|
||||||
default_value
|
|
||||||
);
|
|
||||||
Self {
|
Self {
|
||||||
inner: default_value,
|
inner: default_value,
|
||||||
is_explicitly_set: false,
|
is_explicitly_set: false,
|
||||||
|
|||||||
Reference in New Issue
Block a user