Revert "debugger: Remove feature flag"

Conrad Irwin created

This reverts commit 82dfa82ba7c9866153bcd739ee680e6adf4f955f.

Change summary

crates/debugger_ui/src/debugger_ui.rs     | 277 ++++++++++++------------
crates/editor/src/editor.rs               |  79 +++---
crates/editor/src/element.rs              |  95 ++++---
crates/feature_flags/src/feature_flags.rs |   5 
crates/zed/src/zed.rs                     |  19 +
5 files changed, 260 insertions(+), 215 deletions(-)

Detailed changes

crates/debugger_ui/src/debugger_ui.rs 🔗

@@ -3,6 +3,7 @@ use std::any::TypeId;
 use dap::debugger_settings::DebuggerSettings;
 use debugger_panel::{DebugPanel, ToggleFocus};
 use editor::Editor;
+use feature_flags::{DebuggerFeatureFlag, FeatureFlagViewExt};
 use gpui::{App, DispatchPhase, EntityInputHandler, actions};
 use new_process_modal::{NewProcessModal, NewProcessMode};
 use project::debugger::{self, breakpoint_store::SourceBreakpoint, session::ThreadStatus};
@@ -61,163 +62,173 @@ pub fn init(cx: &mut App) {
     DebuggerSettings::register(cx);
     workspace::FollowableViewRegistry::register::<DebugSession>(cx);
 
-    cx.observe_new(|workspace: &mut Workspace, _, _| {
-        workspace
-            .register_action(spawn_task_or_modal)
-            .register_action(|workspace, _: &ToggleFocus, window, cx| {
-                workspace.toggle_panel_focus::<DebugPanel>(window, cx);
-            })
-            .register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
-                NewProcessModal::show(workspace, window, NewProcessMode::Debug, None, cx);
-            })
-            .register_action(
-                |workspace: &mut Workspace, _: &RerunLastSession, window, cx| {
+    cx.observe_new(|_: &mut Workspace, window, cx| {
+        let Some(window) = window else {
+            return;
+        };
+
+        cx.when_flag_enabled::<DebuggerFeatureFlag>(window, |workspace, _, _| {
+            workspace
+                .register_action(spawn_task_or_modal)
+                .register_action(|workspace, _: &ToggleFocus, window, cx| {
+                    workspace.toggle_panel_focus::<DebugPanel>(window, cx);
+                })
+                .register_action(|workspace: &mut Workspace, _: &Start, window, cx| {
+                    NewProcessModal::show(workspace, window, NewProcessMode::Debug, None, cx);
+                })
+                .register_action(
+                    |workspace: &mut Workspace, _: &RerunLastSession, window, cx| {
+                        let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
+                            return;
+                        };
+
+                        debug_panel.update(cx, |debug_panel, cx| {
+                            debug_panel.rerun_last_session(workspace, window, cx);
+                        })
+                    },
+                )
+                .register_action(
+                    |workspace: &mut Workspace, _: &ShutdownDebugAdapters, _window, cx| {
+                        workspace.project().update(cx, |project, cx| {
+                            project.dap_store().update(cx, |store, cx| {
+                                store.shutdown_sessions(cx).detach();
+                            })
+                        })
+                    },
+                )
+                .register_action_renderer(|div, workspace, _, cx| {
                     let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
-                        return;
+                        return div;
+                    };
+                    let Some(active_item) = debug_panel
+                        .read(cx)
+                        .active_session()
+                        .map(|session| session.read(cx).running_state().clone())
+                    else {
+                        return div;
                     };
+                    let running_state = active_item.read(cx);
+                    if running_state.session().read(cx).is_terminated() {
+                        return div;
+                    }
 
-                    debug_panel.update(cx, |debug_panel, cx| {
-                        debug_panel.rerun_last_session(workspace, window, cx);
-                    })
-                },
-            )
-            .register_action(
-                |workspace: &mut Workspace, _: &ShutdownDebugAdapters, _window, cx| {
-                    workspace.project().update(cx, |project, cx| {
-                        project.dap_store().update(cx, |store, cx| {
-                            store.shutdown_sessions(cx).detach();
+                    let caps = running_state.capabilities(cx);
+                    let supports_step_back = caps.supports_step_back.unwrap_or_default();
+                    let supports_detach = running_state.session().read(cx).is_attached();
+                    let status = running_state.thread_status(cx);
+
+                    let active_item = active_item.downgrade();
+                    div.when(status == Some(ThreadStatus::Running), |div| {
+                        let active_item = active_item.clone();
+                        div.on_action(move |_: &Pause, _, cx| {
+                            active_item
+                                .update(cx, |item, cx| item.pause_thread(cx))
+                                .ok();
                         })
                     })
-                },
-            )
-            .register_action_renderer(|div, workspace, _, cx| {
-                let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
-                    return div;
-                };
-                let Some(active_item) = debug_panel
-                    .read(cx)
-                    .active_session()
-                    .map(|session| session.read(cx).running_state().clone())
-                else {
-                    return div;
-                };
-                let running_state = active_item.read(cx);
-                if running_state.session().read(cx).is_terminated() {
-                    return div;
-                }
+                    .when(status == Some(ThreadStatus::Stopped), |div| {
+                        div.on_action({
+                            let active_item = active_item.clone();
+                            move |_: &StepInto, _, cx| {
+                                active_item.update(cx, |item, cx| item.step_in(cx)).ok();
+                            }
+                        })
+                        .on_action({
+                            let active_item = active_item.clone();
+                            move |_: &StepOver, _, cx| {
+                                active_item.update(cx, |item, cx| item.step_over(cx)).ok();
+                            }
+                        })
+                        .on_action({
+                            let active_item = active_item.clone();
+                            move |_: &StepOut, _, cx| {
+                                active_item.update(cx, |item, cx| item.step_out(cx)).ok();
+                            }
+                        })
+                        .when(supports_step_back, |div| {
+                            let active_item = active_item.clone();
+                            div.on_action(move |_: &StepBack, _, cx| {
+                                active_item.update(cx, |item, cx| item.step_back(cx)).ok();
+                            })
+                        })
+                        .on_action({
+                            let active_item = active_item.clone();
+                            move |_: &Continue, _, cx| {
+                                active_item
+                                    .update(cx, |item, cx| item.continue_thread(cx))
+                                    .ok();
+                            }
+                        })
+                        .on_action(cx.listener(
+                            |workspace, _: &ShowStackTrace, window, cx| {
+                                let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
+                                    return;
+                                };
 
-                let caps = running_state.capabilities(cx);
-                let supports_step_back = caps.supports_step_back.unwrap_or_default();
-                let supports_detach = running_state.session().read(cx).is_attached();
-                let status = running_state.thread_status(cx);
+                                if let Some(existing) = workspace.item_of_type::<StackTraceView>(cx)
+                                {
+                                    let is_active = workspace
+                                        .active_item(cx)
+                                        .is_some_and(|item| item.item_id() == existing.item_id());
+                                    workspace
+                                        .activate_item(&existing, true, !is_active, window, cx);
+                                } else {
+                                    let Some(active_session) =
+                                        debug_panel.read(cx).active_session()
+                                    else {
+                                        return;
+                                    };
+
+                                    let project = workspace.project();
+
+                                    let stack_trace_view =
+                                        active_session.update(cx, |session, cx| {
+                                            session.stack_trace_view(project, window, cx).clone()
+                                        });
 
-                let active_item = active_item.downgrade();
-                div.when(status == Some(ThreadStatus::Running), |div| {
-                    let active_item = active_item.clone();
-                    div.on_action(move |_: &Pause, _, cx| {
-                        active_item
-                            .update(cx, |item, cx| item.pause_thread(cx))
-                            .ok();
+                                    workspace.add_item_to_active_pane(
+                                        Box::new(stack_trace_view),
+                                        None,
+                                        true,
+                                        window,
+                                        cx,
+                                    );
+                                }
+                            },
+                        ))
                     })
-                })
-                .when(status == Some(ThreadStatus::Stopped), |div| {
-                    div.on_action({
+                    .when(supports_detach, |div| {
                         let active_item = active_item.clone();
-                        move |_: &StepInto, _, cx| {
-                            active_item.update(cx, |item, cx| item.step_in(cx)).ok();
-                        }
+                        div.on_action(move |_: &Detach, _, cx| {
+                            active_item
+                                .update(cx, |item, cx| item.detach_client(cx))
+                                .ok();
+                        })
                     })
                     .on_action({
                         let active_item = active_item.clone();
-                        move |_: &StepOver, _, cx| {
-                            active_item.update(cx, |item, cx| item.step_over(cx)).ok();
+                        move |_: &Restart, _, cx| {
+                            active_item
+                                .update(cx, |item, cx| item.restart_session(cx))
+                                .ok();
                         }
                     })
                     .on_action({
                         let active_item = active_item.clone();
-                        move |_: &StepOut, _, cx| {
-                            active_item.update(cx, |item, cx| item.step_out(cx)).ok();
+                        move |_: &Stop, _, cx| {
+                            active_item.update(cx, |item, cx| item.stop_thread(cx)).ok();
                         }
                     })
-                    .when(supports_step_back, |div| {
-                        let active_item = active_item.clone();
-                        div.on_action(move |_: &StepBack, _, cx| {
-                            active_item.update(cx, |item, cx| item.step_back(cx)).ok();
-                        })
-                    })
                     .on_action({
                         let active_item = active_item.clone();
-                        move |_: &Continue, _, cx| {
+                        move |_: &ToggleIgnoreBreakpoints, _, cx| {
                             active_item
-                                .update(cx, |item, cx| item.continue_thread(cx))
+                                .update(cx, |item, cx| item.toggle_ignore_breakpoints(cx))
                                 .ok();
                         }
                     })
-                    .on_action(cx.listener(
-                        |workspace, _: &ShowStackTrace, window, cx| {
-                            let Some(debug_panel) = workspace.panel::<DebugPanel>(cx) else {
-                                return;
-                            };
-
-                            if let Some(existing) = workspace.item_of_type::<StackTraceView>(cx) {
-                                let is_active = workspace
-                                    .active_item(cx)
-                                    .is_some_and(|item| item.item_id() == existing.item_id());
-                                workspace.activate_item(&existing, true, !is_active, window, cx);
-                            } else {
-                                let Some(active_session) = debug_panel.read(cx).active_session()
-                                else {
-                                    return;
-                                };
-
-                                let project = workspace.project();
-
-                                let stack_trace_view = active_session.update(cx, |session, cx| {
-                                    session.stack_trace_view(project, window, cx).clone()
-                                });
-
-                                workspace.add_item_to_active_pane(
-                                    Box::new(stack_trace_view),
-                                    None,
-                                    true,
-                                    window,
-                                    cx,
-                                );
-                            }
-                        },
-                    ))
-                })
-                .when(supports_detach, |div| {
-                    let active_item = active_item.clone();
-                    div.on_action(move |_: &Detach, _, cx| {
-                        active_item
-                            .update(cx, |item, cx| item.detach_client(cx))
-                            .ok();
-                    })
-                })
-                .on_action({
-                    let active_item = active_item.clone();
-                    move |_: &Restart, _, cx| {
-                        active_item
-                            .update(cx, |item, cx| item.restart_session(cx))
-                            .ok();
-                    }
-                })
-                .on_action({
-                    let active_item = active_item.clone();
-                    move |_: &Stop, _, cx| {
-                        active_item.update(cx, |item, cx| item.stop_thread(cx)).ok();
-                    }
-                })
-                .on_action({
-                    let active_item = active_item.clone();
-                    move |_: &ToggleIgnoreBreakpoints, _, cx| {
-                        active_item
-                            .update(cx, |item, cx| item.toggle_ignore_breakpoints(cx))
-                            .ok();
-                    }
-                })
-            });
+                });
+        })
     })
     .detach();
 

crates/editor/src/editor.rs 🔗

@@ -73,7 +73,7 @@ use element::{AcceptEditPredictionBinding, LineWithInvisibles, PositionMap, layo
 pub use element::{
     CursorLayout, EditorElement, HighlightedRange, HighlightedRangeLine, PointForPosition,
 };
-use feature_flags::FeatureFlagAppExt;
+use feature_flags::{DebuggerFeatureFlag, FeatureFlagAppExt};
 use futures::{
     FutureExt, StreamExt as _,
     future::{self, Shared, join},
@@ -5969,41 +5969,45 @@ impl Editor {
         buffer: &Entity<Buffer>,
         cx: &mut App,
     ) -> Task<Vec<task::DebugScenario>> {
-        maybe!({
-            let project = self.project.as_ref()?;
-            let dap_store = project.read(cx).dap_store();
-            let mut scenarios = vec![];
-            let resolved_tasks = resolved_tasks.as_ref()?;
-            let buffer = buffer.read(cx);
-            let language = buffer.language()?;
-            let file = buffer.file();
-            let debug_adapter = language_settings(language.name().into(), file, cx)
-                .debuggers
-                .first()
-                .map(SharedString::from)
-                .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
-
-            dap_store.update(cx, |dap_store, cx| {
-                for (_, task) in &resolved_tasks.templates {
-                    let maybe_scenario = dap_store.debug_scenario_for_build_task(
-                        task.original_task().clone(),
-                        debug_adapter.clone().into(),
-                        task.display_label().to_owned().into(),
-                        cx,
-                    );
-                    scenarios.push(maybe_scenario);
-                }
-            });
-            Some(cx.background_spawn(async move {
-                let scenarios = futures::future::join_all(scenarios)
-                    .await
-                    .into_iter()
-                    .flatten()
-                    .collect::<Vec<_>>();
-                scenarios
-            }))
-        })
-        .unwrap_or_else(|| Task::ready(vec![]))
+        if cx.has_flag::<DebuggerFeatureFlag>() {
+            maybe!({
+                let project = self.project.as_ref()?;
+                let dap_store = project.read(cx).dap_store();
+                let mut scenarios = vec![];
+                let resolved_tasks = resolved_tasks.as_ref()?;
+                let buffer = buffer.read(cx);
+                let language = buffer.language()?;
+                let file = buffer.file();
+                let debug_adapter = language_settings(language.name().into(), file, cx)
+                    .debuggers
+                    .first()
+                    .map(SharedString::from)
+                    .or_else(|| language.config().debuggers.first().map(SharedString::from))?;
+
+                dap_store.update(cx, |dap_store, cx| {
+                    for (_, task) in &resolved_tasks.templates {
+                        let maybe_scenario = dap_store.debug_scenario_for_build_task(
+                            task.original_task().clone(),
+                            debug_adapter.clone().into(),
+                            task.display_label().to_owned().into(),
+                            cx,
+                        );
+                        scenarios.push(maybe_scenario);
+                    }
+                });
+                Some(cx.background_spawn(async move {
+                    let scenarios = futures::future::join_all(scenarios)
+                        .await
+                        .into_iter()
+                        .flatten()
+                        .collect::<Vec<_>>();
+                    scenarios
+                }))
+            })
+            .unwrap_or_else(|| Task::ready(vec![]))
+        } else {
+            Task::ready(vec![])
+        }
     }
 
     fn code_actions(
@@ -10151,6 +10155,9 @@ impl Editor {
         window: &mut Window,
         cx: &mut Context<Self>,
     ) {
+        if !cx.has_flag::<DebuggerFeatureFlag>() {
+            return;
+        }
         let source = self
             .buffer
             .read(cx)

crates/editor/src/element.rs 🔗

@@ -32,7 +32,7 @@ use crate::{
 };
 use buffer_diff::{DiffHunkStatus, DiffHunkStatusKind};
 use collections::{BTreeMap, HashMap};
-use feature_flags::FeatureFlagAppExt;
+use feature_flags::{DebuggerFeatureFlag, FeatureFlagAppExt};
 use file_icons::FileIcons;
 use git::{
     Oid,
@@ -567,10 +567,12 @@ impl EditorElement {
         register_action(editor, window, Editor::insert_uuid_v4);
         register_action(editor, window, Editor::insert_uuid_v7);
         register_action(editor, window, Editor::open_selections_in_multibuffer);
-        register_action(editor, window, Editor::toggle_breakpoint);
-        register_action(editor, window, Editor::edit_log_breakpoint);
-        register_action(editor, window, Editor::enable_breakpoint);
-        register_action(editor, window, Editor::disable_breakpoint);
+        if cx.has_flag::<DebuggerFeatureFlag>() {
+            register_action(editor, window, Editor::toggle_breakpoint);
+            register_action(editor, window, Editor::edit_log_breakpoint);
+            register_action(editor, window, Editor::enable_breakpoint);
+            register_action(editor, window, Editor::disable_breakpoint);
+        }
     }
 
     fn register_key_listeners(&self, window: &mut Window, _: &mut App, layout: &EditorLayout) {
@@ -8171,9 +8173,11 @@ impl Element for EditorElement {
                     let mut breakpoint_rows = self.editor.update(cx, |editor, cx| {
                         editor.active_breakpoints(start_row..end_row, window, cx)
                     });
-                    for (display_row, (_, bp, state)) in &breakpoint_rows {
-                        if bp.is_enabled() && state.is_none_or(|s| s.verified) {
-                            active_rows.entry(*display_row).or_default().breakpoint = true;
+                    if cx.has_flag::<DebuggerFeatureFlag>() {
+                        for (display_row, (_, bp, state)) in &breakpoint_rows {
+                            if bp.is_enabled() && state.is_none_or(|s| s.verified) {
+                                active_rows.entry(*display_row).or_default().breakpoint = true;
+                            }
                         }
                     }
 
@@ -8194,27 +8198,30 @@ impl Element for EditorElement {
                     // We add the gutter breakpoint indicator to breakpoint_rows after painting
                     // line numbers so we don't paint a line number debug accent color if a user
                     // has their mouse over that line when a breakpoint isn't there
-                    self.editor.update(cx, |editor, _| {
-                        if let Some(phantom_breakpoint) = &mut editor
-                            .gutter_breakpoint_indicator
-                            .0
-                            .filter(|phantom_breakpoint| phantom_breakpoint.is_active)
-                        {
-                            // Is there a non-phantom breakpoint on this line?
-                            phantom_breakpoint.collides_with_existing_breakpoint = true;
-                            breakpoint_rows
-                                .entry(phantom_breakpoint.display_row)
-                                .or_insert_with(|| {
-                                    let position = snapshot.display_point_to_anchor(
-                                        DisplayPoint::new(phantom_breakpoint.display_row, 0),
-                                        Bias::Right,
-                                    );
-                                    let breakpoint = Breakpoint::new_standard();
-                                    phantom_breakpoint.collides_with_existing_breakpoint = false;
-                                    (position, breakpoint, None)
-                                });
-                        }
-                    });
+                    if cx.has_flag::<DebuggerFeatureFlag>() {
+                        self.editor.update(cx, |editor, _| {
+                            if let Some(phantom_breakpoint) = &mut editor
+                                .gutter_breakpoint_indicator
+                                .0
+                                .filter(|phantom_breakpoint| phantom_breakpoint.is_active)
+                            {
+                                // Is there a non-phantom breakpoint on this line?
+                                phantom_breakpoint.collides_with_existing_breakpoint = true;
+                                breakpoint_rows
+                                    .entry(phantom_breakpoint.display_row)
+                                    .or_insert_with(|| {
+                                        let position = snapshot.display_point_to_anchor(
+                                            DisplayPoint::new(phantom_breakpoint.display_row, 0),
+                                            Bias::Right,
+                                        );
+                                        let breakpoint = Breakpoint::new_standard();
+                                        phantom_breakpoint.collides_with_existing_breakpoint =
+                                            false;
+                                        (position, breakpoint, None)
+                                    });
+                            }
+                        })
+                    }
 
                     let mut expand_toggles =
                         window.with_element_namespace("expand_toggles", |window| {
@@ -8683,19 +8690,23 @@ impl Element for EditorElement {
                     let show_breakpoints = snapshot
                         .show_breakpoints
                         .unwrap_or(gutter_settings.breakpoints);
-                    let breakpoints = self.layout_breakpoints(
-                        line_height,
-                        start_row..end_row,
-                        scroll_pixel_position,
-                        &gutter_dimensions,
-                        &gutter_hitbox,
-                        &display_hunks,
-                        &snapshot,
-                        breakpoint_rows,
-                        &row_infos,
-                        window,
-                        cx,
-                    );
+                    let breakpoints = if cx.has_flag::<DebuggerFeatureFlag>() && show_breakpoints {
+                        self.layout_breakpoints(
+                            line_height,
+                            start_row..end_row,
+                            scroll_pixel_position,
+                            &gutter_dimensions,
+                            &gutter_hitbox,
+                            &display_hunks,
+                            &snapshot,
+                            breakpoint_rows,
+                            &row_infos,
+                            window,
+                            cx,
+                        )
+                    } else {
+                        vec![]
+                    };
 
                     self.layout_signature_help(
                         &hitbox,

crates/feature_flags/src/feature_flags.rs 🔗

@@ -77,6 +77,11 @@ impl FeatureFlag for NotebookFeatureFlag {
     const NAME: &'static str = "notebooks";
 }
 
+pub struct DebuggerFeatureFlag {}
+impl FeatureFlag for DebuggerFeatureFlag {
+    const NAME: &'static str = "debugger";
+}
+
 pub struct ThreadAutoCaptureFeatureFlag {}
 impl FeatureFlag for ThreadAutoCaptureFeatureFlag {
     const NAME: &'static str = "thread-auto-capture";

crates/zed/src/zed.rs 🔗

@@ -20,6 +20,7 @@ use collections::VecDeque;
 use debugger_ui::debugger_panel::DebugPanel;
 use editor::ProposedChangesEditorToolbar;
 use editor::{Editor, MultiBuffer, scroll::Autoscroll};
+use feature_flags::{DebuggerFeatureFlag, FeatureFlagViewExt};
 use futures::future::Either;
 use futures::{StreamExt, channel::mpsc, select_biased};
 use git_ui::git_panel::GitPanel;
@@ -480,7 +481,6 @@ fn initialize_panels(
             workspace_handle.clone(),
             cx.clone(),
         );
-        let debug_panel = DebugPanel::load(workspace_handle.clone(), cx);
 
         let (
             project_panel,
@@ -490,7 +490,6 @@ fn initialize_panels(
             channels_panel,
             chat_panel,
             notification_panel,
-            debug_panel,
         ) = futures::try_join!(
             project_panel,
             outline_panel,
@@ -499,7 +498,6 @@ fn initialize_panels(
             channels_panel,
             chat_panel,
             notification_panel,
-            debug_panel,
         )?;
 
         workspace_handle.update_in(cx, |workspace, window, cx| {
@@ -510,7 +508,20 @@ fn initialize_panels(
             workspace.add_panel(channels_panel, window, cx);
             workspace.add_panel(chat_panel, window, cx);
             workspace.add_panel(notification_panel, window, cx);
-            workspace.add_panel(debug_panel, window, cx);
+            cx.when_flag_enabled::<DebuggerFeatureFlag>(window, |_, window, cx| {
+                cx.spawn_in(
+                    window,
+                    async move |workspace: gpui::WeakEntity<Workspace>,
+                                cx: &mut AsyncWindowContext| {
+                        let debug_panel = DebugPanel::load(workspace.clone(), cx).await?;
+                        workspace.update_in(cx, |workspace, window, cx| {
+                            workspace.add_panel(debug_panel, window, cx);
+                        })?;
+                        anyhow::Ok(())
+                    },
+                )
+                .detach()
+            });
         })?;
 
         let is_assistant2_enabled = !cfg!(test);