file_finder: Remove common segments of long paths in search results (#25049)

Cole Miller and Max created

This PR makes progress on #7711 by identifying any common prefix of the
paths in the file finder's search results, and replacing the "interior"
of that prefix---every path segment but the first and last---with `...`,
when a heuristic indicates that the longest path would otherwise
overflow the modal.

The elision is not applied to any segment that contains a match for the
search query.

There may be more work to do on #7711 in the case of long result paths
that do not share a significant common prefix.

Release Notes:

- Improved display of long paths in the file finder modal

Co-authored-by: Max <max@zed.dev>

Change summary

Cargo.toml                                                    |   1 
crates/assistant/src/inline_assistant.rs                      |   3 
crates/assistant/src/terminal_inline_assistant.rs             |   1 
crates/assistant2/src/context_strip.rs                        |   1 
crates/assistant2/src/inline_assistant.rs                     |   3 
crates/assistant2/src/inline_prompt_editor.rs                 |   2 
crates/assistant_context_editor/src/context.rs                |   2 
crates/assistant_context_editor/src/context_editor.rs         |   2 
crates/assistant_context_editor/src/slash_command.rs          |   1 
crates/assistant_slash_command/src/assistant_slash_command.rs |   2 
crates/buffer_diff/src/buffer_diff.rs                         |   1 
crates/collab/src/db/queries/messages.rs                      |   1 
crates/collab/src/db/queries/users.rs                         |   1 
crates/collab/src/llm/db/queries/usages.rs                    |   2 
crates/collab/src/llm/token.rs                                |   1 
crates/collab/src/rpc.rs                                      |   2 
crates/collab/src/tests/randomized_test_helpers.rs            |   1 
crates/collab_ui/src/collab_panel.rs                          |   1 
crates/editor/src/display_map.rs                              |   1 
crates/editor/src/display_map/block_map.rs                    |   1 
crates/editor/src/editor.rs                                   |   1 
crates/editor/src/element.rs                                  |  30 
crates/editor/src/scroll.rs                                   |   2 
crates/evals/src/eval.rs                                      |   1 
crates/extension_api/src/extension_api.rs                     |   2 
crates/extension_host/src/extension_host.rs                   |   1 
crates/file_finder/src/file_finder.rs                         | 247 +++-
crates/file_finder/src/file_finder_tests.rs                   |  13 
crates/fuzzy/src/matcher.rs                                   |   1 
crates/gpui/src/platform/linux/wayland/window.rs              |   1 
crates/gpui/src/platform/linux/x11/window.rs                  |   2 
crates/gpui/src/text_system/line.rs                           |   1 
crates/language/src/syntax_map.rs                             |   1 
crates/lsp/src/lsp.rs                                         |   1 
crates/multi_buffer/src/multi_buffer.rs                       |   1 
crates/outline_panel/src/outline_panel.rs                     |   5 
crates/project/src/lsp_store.rs                               |   6 
crates/project/src/project.rs                                 |   1 
crates/remote/src/ssh_session.rs                              |   1 
crates/rich_text/src/rich_text.rs                             |   1 
crates/semantic_index/src/worktree_index.rs                   |   1 
crates/terminal/src/terminal.rs                               |   1 
crates/terminal_view/src/terminal_element.rs                  |   1 
crates/title_bar/src/collab.rs                                |   1 
crates/toolchain_selector/src/toolchain_selector.rs           |   2 
crates/workspace/src/pane.rs                                  |   1 
crates/workspace/src/pane_group.rs                            |   5 
crates/workspace/src/workspace.rs                             |   1 
crates/zeta/src/zeta.rs                                       |   2 
49 files changed, 207 insertions(+), 157 deletions(-)

Detailed changes

Cargo.toml 🔗

@@ -701,6 +701,7 @@ codegen-units = 16
 [workspace.lints.clippy]
 dbg_macro = "deny"
 todo = "deny"
+too_many_arguments = "allow"
 
 # Motivation: We use `vec![a..b]` a lot when dealing with ranges in text, so
 # warning on this rule produces a lot of noise.

crates/assistant/src/inline_assistant.rs 🔗

@@ -387,7 +387,6 @@ impl InlineAssistant {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub fn suggest_assist(
         &mut self,
         editor: &Entity<Editor>,
@@ -1674,7 +1673,6 @@ impl Focusable for PromptEditor {
 impl PromptEditor {
     const MAX_LINES: u8 = 8;
 
-    #[allow(clippy::too_many_arguments)]
     fn new(
         id: InlineAssistId,
         gutter_dimensions: Arc<Mutex<GutterDimensions>>,
@@ -2333,7 +2331,6 @@ struct InlineAssist {
 }
 
 impl InlineAssist {
-    #[allow(clippy::too_many_arguments)]
     fn new(
         assist_id: InlineAssistId,
         group_id: InlineAssistGroupId,

crates/assistant2/src/context_strip.rs 🔗

@@ -36,7 +36,6 @@ pub struct ContextStrip {
 }
 
 impl ContextStrip {
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         context_store: Entity<ContextStore>,
         workspace: WeakEntity<Workspace>,

crates/assistant2/src/inline_assistant.rs 🔗

@@ -476,7 +476,6 @@ impl InlineAssistant {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub fn suggest_assist(
         &mut self,
         editor: &Entity<Editor>,
@@ -1433,7 +1432,6 @@ struct InlineAssistScrollLock {
 }
 
 impl EditorInlineAssists {
-    #[allow(clippy::too_many_arguments)]
     fn new(editor: &Entity<Editor>, window: &mut Window, cx: &mut App) -> Self {
         let (highlight_updates_tx, mut highlight_updates_rx) = async_watch::channel(());
         Self {
@@ -1545,7 +1543,6 @@ pub struct InlineAssist {
 }
 
 impl InlineAssist {
-    #[allow(clippy::too_many_arguments)]
     fn new(
         assist_id: InlineAssistId,
         group_id: InlineAssistGroupId,

crates/assistant2/src/inline_prompt_editor.rs 🔗

@@ -823,7 +823,6 @@ impl InlineAssistId {
 }
 
 impl PromptEditor<BufferCodegen> {
-    #[allow(clippy::too_many_arguments)]
     pub fn new_buffer(
         id: InlineAssistId,
         gutter_dimensions: Arc<Mutex<GutterDimensions>>,
@@ -984,7 +983,6 @@ impl TerminalInlineAssistId {
 }
 
 impl PromptEditor<TerminalCodegen> {
-    #[allow(clippy::too_many_arguments)]
     pub fn new_terminal(
         id: TerminalInlineAssistId,
         prompt_history: VecDeque<String>,

crates/assistant_context_editor/src/context.rs 🔗

@@ -650,7 +650,6 @@ impl AssistantContext {
         )
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         id: ContextId,
         replica_id: ReplicaId,
@@ -771,7 +770,6 @@ impl AssistantContext {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub fn deserialize(
         saved_context: SavedContext,
         path: PathBuf,

crates/assistant_context_editor/src/context_editor.rs 🔗

@@ -517,7 +517,6 @@ impl ContextEditor {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub fn run_command(
         &mut self,
         command_range: Range<language::Anchor>,
@@ -2058,7 +2057,6 @@ impl ContextEditor {
             .unwrap_or_else(|| Cow::Borrowed(DEFAULT_TAB_TITLE))
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn render_patch_block(
         &mut self,
         range: Range<text::Anchor>,

crates/buffer_diff/src/buffer_diff.rs 🔗

@@ -721,7 +721,6 @@ impl BufferDiff {
         Some(start..end)
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub async fn update_diff(
         this: Entity<BufferDiff>,
         buffer: text::BufferSnapshot,

crates/collab/src/db/queries/messages.rs 🔗

@@ -229,7 +229,6 @@ impl Database {
     }
 
     /// Creates a new channel message.
-    #[allow(clippy::too_many_arguments)]
     pub async fn create_channel_message(
         &self,
         channel_id: ChannelId,

crates/collab/src/db/queries/users.rs 🔗

@@ -122,7 +122,6 @@ impl Database {
         .await
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub async fn get_or_create_user_by_github_account_tx(
         &self,
         github_login: &str,

crates/collab/src/llm/db/queries/usages.rs 🔗

@@ -289,7 +289,6 @@ impl LlmDatabase {
         .await
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub async fn record_usage(
         &self,
         user_id: UserId,
@@ -554,7 +553,6 @@ impl LlmDatabase {
         .await
     }
 
-    #[allow(clippy::too_many_arguments)]
     async fn update_usage_for_measure(
         &self,
         user_id: UserId,

crates/collab/src/llm/token.rs 🔗

@@ -33,7 +33,6 @@ pub struct LlmTokenClaims {
 const LLM_TOKEN_LIFETIME: Duration = Duration::from_secs(60 * 60);
 
 impl LlmTokenClaims {
-    #[allow(clippy::too_many_arguments)]
     pub fn create(
         user: &user::Model,
         is_staff: bool,

crates/collab/src/rpc.rs 🔗

@@ -691,7 +691,6 @@ impl Server {
         })
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub fn handle_connection(
         self: &Arc<Self>,
         connection: Connection,
@@ -1075,7 +1074,6 @@ pub fn routes(server: Arc<Server>) -> Router<(), Body> {
         .layer(Extension(server))
 }
 
-#[allow(clippy::too_many_arguments)]
 pub async fn handle_websocket_request(
     TypedHeader(ProtocolVersion(protocol_version)): TypedHeader<ProtocolVersion>,
     app_version_header: Option<TypedHeader<AppVersionHeader>>,

crates/editor/src/display_map.rs 🔗

@@ -113,7 +113,6 @@ pub struct DisplayMap {
 }
 
 impl DisplayMap {
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         buffer: Entity<MultiBuffer>,
         font: Font,

crates/editor/src/display_map/block_map.rs 🔗

@@ -723,7 +723,6 @@ impl BlockMap {
         self.show_excerpt_controls
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn header_and_footer_blocks<'a, R, T>(
         show_excerpt_controls: bool,
         excerpt_footer_height: u32,

crates/editor/src/editor.rs 🔗

@@ -5883,7 +5883,6 @@ impl Editor {
         editor_bg_color.blend(accent_color.opacity(0.1))
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn render_edit_prediction_cursor_popover(
         &self,
         min_width: Pixels,

crates/editor/src/element.rs 🔗

@@ -930,7 +930,6 @@ impl EditorElement {
         cx.notify()
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_selections(
         &self,
         start_anchor: Anchor,
@@ -1102,7 +1101,6 @@ impl EditorElement {
         cursors
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_visible_cursors(
         &self,
         snapshot: &EditorSnapshot,
@@ -1452,7 +1450,6 @@ impl EditorElement {
         axis_pair(horizontal_scrollbar, vertical_scrollbar)
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn prepaint_crease_toggles(
         &self,
         crease_toggles: &mut [Option<AnyElement>],
@@ -1487,7 +1484,6 @@ impl EditorElement {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn prepaint_crease_trailers(
         &self,
         trailers: Vec<Option<AnyElement>>,
@@ -1609,7 +1605,6 @@ impl EditorElement {
         display_hunks
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_inline_blame(
         &self,
         display_row: DisplayRow,
@@ -1696,7 +1691,6 @@ impl EditorElement {
         Some(element)
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_blame_entries(
         &self,
         buffer_rows: &[RowInfo],
@@ -1765,7 +1759,6 @@ impl EditorElement {
         Some(shaped_lines)
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_indent_guides(
         &self,
         content_origin: gpui::Point<Pixels>,
@@ -1883,7 +1876,6 @@ impl EditorElement {
         (offset_y, length)
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_run_indicators(
         &self,
         line_height: Pixels,
@@ -1976,7 +1968,6 @@ impl EditorElement {
         })
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_code_actions_indicator(
         &self,
         line_height: Pixels,
@@ -2075,7 +2066,6 @@ impl EditorElement {
         relative_rows
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_line_numbers(
         &self,
         gutter_hitbox: Option<&Hitbox>,
@@ -2288,7 +2278,6 @@ impl EditorElement {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn prepaint_lines(
         &self,
         start_row: DisplayRow,
@@ -2315,7 +2304,6 @@ impl EditorElement {
         line_elements
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn render_block(
         &self,
         block: &Block,
@@ -2777,7 +2765,6 @@ impl EditorElement {
         }))
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn render_blocks(
         &self,
         rows: Range<DisplayRow>,
@@ -2962,7 +2949,6 @@ impl EditorElement {
 
     /// Returns true if any of the blocks changed size since the previous frame. This will trigger
     /// a restart of rendering for the editor based on the new sizes.
-    #[allow(clippy::too_many_arguments)]
     fn layout_blocks(
         &self,
         blocks: &mut Vec<BlockLayout>,
@@ -3006,7 +2992,6 @@ impl EditorElement {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_sticky_buffer_header(
         &self,
         StickyHeaderExcerpt {
@@ -3081,7 +3066,6 @@ impl EditorElement {
         header
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_cursor_popovers(
         &self,
         line_height: Pixels,
@@ -3270,7 +3254,6 @@ impl EditorElement {
         );
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_gutter_menu(
         &self,
         line_height: Pixels,
@@ -3323,7 +3306,6 @@ impl EditorElement {
         );
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_popovers_above_or_below_line(
         &self,
         target_position: gpui::Point<Pixels>,
@@ -3428,7 +3410,6 @@ impl EditorElement {
         Some((laid_out_popovers, y_flipped))
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_context_menu_aside(
         &self,
         y_flipped: bool,
@@ -3548,7 +3529,6 @@ impl EditorElement {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_edit_prediction_popover(
         &self,
         text_bounds: &Bounds<Pixels>,
@@ -4005,7 +3985,6 @@ impl EditorElement {
         Some(element)
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_hover_popovers(
         &self,
         snapshot: &EditorSnapshot,
@@ -4122,7 +4101,6 @@ impl EditorElement {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_diff_hunk_controls(
         &self,
         row_range: Range<DisplayRow>,
@@ -4203,7 +4181,6 @@ impl EditorElement {
         controls
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn layout_signature_help(
         &self,
         hitbox: &Hitbox,
@@ -5471,7 +5448,6 @@ impl EditorElement {
         });
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn paint_highlighted_range(
         &self,
         range: Range<DisplayPoint>,
@@ -5886,7 +5862,6 @@ impl AcceptEditPredictionBinding {
     }
 }
 
-#[allow(clippy::too_many_arguments)]
 fn prepaint_gutter_button(
     button: IconButton,
     row: DisplayRow,
@@ -6126,7 +6101,6 @@ impl fmt::Debug for LineFragment {
 }
 
 impl LineWithInvisibles {
-    #[allow(clippy::too_many_arguments)]
     fn from_chunks<'a>(
         chunks: impl Iterator<Item = HighlightedChunk<'a>>,
         editor_style: &EditorStyle,
@@ -6331,7 +6305,6 @@ impl LineWithInvisibles {
         layouts
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn prepaint(
         &mut self,
         line_height: Pixels,
@@ -6366,7 +6339,6 @@ impl LineWithInvisibles {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn draw(
         &self,
         layout: &EditorLayout,
@@ -6410,7 +6382,6 @@ impl LineWithInvisibles {
         );
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn draw_invisibles(
         &self,
         selection_ranges: &[Range<DisplayPoint>],
@@ -7718,7 +7689,6 @@ struct ScrollbarRangeData {
 }
 
 impl ScrollbarRangeData {
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         scrollbar_bounds: Bounds<Pixels>,
         letter_size: Size<Pixels>,

crates/editor/src/scroll.rs 🔗

@@ -224,7 +224,6 @@ impl ScrollManager {
         self.anchor.scroll_position(snapshot)
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn set_scroll_position(
         &mut self,
         scroll_position: gpui::Point<f32>,
@@ -299,7 +298,6 @@ impl ScrollManager {
         );
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn set_anchor(
         &mut self,
         anchor: ScrollAnchor,

crates/evals/src/eval.rs 🔗

@@ -399,7 +399,6 @@ async fn run_evaluation(
     }
 }
 
-#[allow(clippy::too_many_arguments)]
 async fn run_eval_project(
     evaluation_project: EvaluationProject,
     user_store: &Entity<UserStore>,

crates/extension_api/src/extension_api.rs 🔗

@@ -191,7 +191,7 @@ static mut EXTENSION: Option<Box<dyn Extension>> = None;
 pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "/version_bytes"));
 
 mod wit {
-    #![allow(clippy::too_many_arguments, clippy::missing_safety_doc)]
+    #![allow(clippy::missing_safety_doc)]
 
     wit_bindgen::generate!({
         skip: ["init-extension"],

crates/extension_host/src/extension_host.rs 🔗

@@ -218,7 +218,6 @@ impl ExtensionStore {
         cx.global::<GlobalExtensionStore>().0.clone()
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         extensions_dir: PathBuf,
         build_dir: Option<PathBuf>,

crates/file_finder/src/file_finder.rs 🔗

@@ -25,6 +25,7 @@ use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
 use settings::Settings;
 use std::{
     cmp,
+    ops::Range,
     path::{Path, PathBuf},
     sync::{
         atomic::{self, AtomicBool},
@@ -381,6 +382,7 @@ impl PartialOrd for ProjectPanelOrdMatch {
 struct Matches {
     separate_history: bool,
     matches: Vec<Match>,
+    elided_byte_range: Option<Range<usize>>,
 }
 
 #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
@@ -392,6 +394,35 @@ enum Match {
     Search(ProjectPanelOrdMatch),
 }
 
+struct MatchLabels {
+    path: String,
+    path_positions: Vec<usize>,
+    file_name: String,
+    file_name_positions: Vec<usize>,
+}
+
+#[derive(Clone, Copy, Debug)]
+struct EmWidths {
+    normal: Pixels,
+    small: Pixels,
+}
+
+fn em_widths(window: &mut Window, cx: &mut App) -> EmWidths {
+    let style = window.text_style();
+    let font_id = window.text_system().resolve_font(&style.font());
+    let font_size = TextSize::Default.rems(cx).to_pixels(window.rem_size());
+    let normal = cx
+        .text_system()
+        .em_width(font_id, font_size)
+        .unwrap_or(px(16.));
+    let font_size = TextSize::Small.rems(cx).to_pixels(window.rem_size());
+    let small = cx
+        .text_system()
+        .em_width(font_id, font_size)
+        .unwrap_or(px(10.));
+    EmWidths { normal, small }
+}
+
 impl Match {
     fn path(&self) -> &Arc<Path> {
         match self {
@@ -406,6 +437,43 @@ impl Match {
             Match::Search(panel_match) => Some(&panel_match),
         }
     }
+
+    fn common_prefix<'a>(&self, other: &'a Path) -> &'a Path {
+        let mut prefix = other;
+        let path_positions = match self {
+            Match::History {
+                panel_match: Some(mat),
+                ..
+            }
+            | Match::Search(mat) => mat.0.positions.as_slice(),
+            Match::History {
+                panel_match: None, ..
+            } => &[],
+        };
+        let first_path_position = *path_positions.iter().min().unwrap_or(&0);
+        while self.path().strip_prefix(prefix).is_err()
+            || prefix.to_string_lossy().len() > first_path_position
+        {
+            let Some(parent) = prefix.parent() else {
+                break;
+            };
+            prefix = parent;
+        }
+        prefix
+    }
+
+    fn approx_width(&self, em_widths: EmWidths) -> Pixels {
+        let file_name = self.path().file_name().map_or_else(
+            || self.path().to_string_lossy(),
+            |file_name| file_name.to_string_lossy(),
+        );
+        let parent = self
+            .path()
+            .parent()
+            .map_or_else(|| "".into(), |parent| parent.to_string_lossy());
+        px(file_name.chars().count() as f32) * em_widths.normal
+            + px(parent.chars().count() as f32) * em_widths.small
+    }
 }
 
 impl Matches {
@@ -451,7 +519,11 @@ impl Matches {
         query: Option<&FileSearchQuery>,
         new_search_matches: impl Iterator<Item = ProjectPanelOrdMatch>,
         extend_old_matches: bool,
+        em_widths: EmWidths,
+        max_width: Pixels,
     ) {
+        self.elided_byte_range = None;
+
         let Some(query) = query else {
             // assuming that if there's no query, then there's no search matches.
             self.matches.clear();
@@ -507,6 +579,31 @@ impl Matches {
                 }
             }
         }
+
+        let Some((first, rest)) = self.matches.split_first() else {
+            return;
+        };
+        let mut prefix = first.path().as_ref();
+        let mut widest_match = first.approx_width(em_widths);
+        for mat in rest {
+            widest_match = widest_match.max(mat.approx_width(em_widths));
+            prefix = mat.common_prefix(prefix);
+        }
+
+        if widest_match > max_width {
+            let components = prefix.components().collect::<Vec<_>>();
+            let prefix = prefix.to_string_lossy();
+            if components.len() > 3 {
+                let after_first = components[1..].iter().collect::<PathBuf>();
+                let after_first = after_first.to_string_lossy();
+                let start = prefix.len() - after_first.len();
+                let after_first_before_last = components[1..components.len() - 2]
+                    .iter()
+                    .collect::<PathBuf>();
+                let after_first_before_last = after_first_before_last.to_string_lossy();
+                self.elided_byte_range = Some(start..start + after_first_before_last.len());
+            }
+        }
     }
 
     /// If a < b, then a is a worse match, aligning with the `ProjectPanelOrdMatch` ordering.
@@ -535,6 +632,25 @@ impl Matches {
     }
 }
 
+impl MatchLabels {
+    fn check(&self) {
+        let file_name = &self.file_name;
+        for i in &self.file_name_positions {
+            assert!(
+                self.file_name.is_char_boundary(*i),
+                "{i} is not a valid char boundary in file name {file_name:?}"
+            );
+        }
+        let path = &self.path;
+        for i in &self.path_positions {
+            assert!(
+                self.path.is_char_boundary(*i),
+                "{i} is not a valid char boundary in path {path:?}"
+            );
+        }
+    }
+}
+
 fn matching_history_items<'a>(
     history_items: impl IntoIterator<Item = &'a FoundPath>,
     currently_opened: Option<&'a FoundPath>,
@@ -644,7 +760,6 @@ impl FileSearchQuery {
 }
 
 impl FileFinderDelegate {
-    #[allow(clippy::too_many_arguments)]
     fn new(
         file_finder: WeakEntity<FileFinder>,
         workspace: WeakEntity<Workspace>,
@@ -745,10 +860,10 @@ impl FileFinderDelegate {
             .map(ProjectPanelOrdMatch);
             let did_cancel = cancel_flag.load(atomic::Ordering::Relaxed);
             picker
-                .update(&mut cx, |picker, cx| {
+                .update_in(&mut cx, |picker, window, cx| {
                     picker
                         .delegate
-                        .set_search_matches(search_id, did_cancel, query, matches, cx)
+                        .set_search_matches(search_id, did_cancel, query, matches, window, cx)
                 })
                 .log_err();
         })
@@ -760,9 +875,13 @@ impl FileFinderDelegate {
         did_cancel: bool,
         query: FileSearchQuery,
         matches: impl IntoIterator<Item = ProjectPanelOrdMatch>,
-
+        window: &mut Window,
         cx: &mut Context<Picker<Self>>,
     ) {
+        let em_widths = em_widths(window, cx);
+        let file_finder_settings = FileFinderSettings::get_global(cx);
+        let max_width = FileFinder::modal_max_width(file_finder_settings.modal_max_width, window);
+
         if search_id >= self.latest_search_id {
             self.latest_search_id = search_id;
             let query_changed = Some(query.path_query())
@@ -784,6 +903,8 @@ impl FileFinderDelegate {
                 Some(&query),
                 matches.into_iter(),
                 extend_old_matches,
+                em_widths,
+                max_width,
             );
 
             self.selected_index = selected_match.map_or_else(
@@ -802,13 +923,8 @@ impl FileFinderDelegate {
         }
     }
 
-    fn labels_for_match(
-        &self,
-        path_match: &Match,
-        cx: &App,
-        ix: usize,
-    ) -> (String, Vec<usize>, String, Vec<usize>) {
-        let (file_name, file_name_positions, full_path, full_path_positions) = match &path_match {
+    fn labels_for_match(&self, path_match: &Match, cx: &App, ix: usize) -> MatchLabels {
+        let mut labels = match &path_match {
             Match::History {
                 path: entry_path,
                 panel_match,
@@ -823,18 +939,18 @@ impl FileFinderDelegate {
 
                 if !has_worktree {
                     if let Some(absolute_path) = &entry_path.absolute {
-                        return (
-                            absolute_path
+                        return MatchLabels {
+                            file_name: absolute_path
                                 .file_name()
                                 .map_or_else(
                                     || project_relative_path.to_string_lossy(),
                                     |file_name| file_name.to_string_lossy(),
                                 )
                                 .to_string(),
-                            Vec::new(),
-                            absolute_path.to_string_lossy().to_string(),
-                            Vec::new(),
-                        );
+                            file_name_positions: Vec::new(),
+                            path: absolute_path.to_string_lossy().to_string(),
+                            path_positions: Vec::new(),
+                        };
                     }
                 }
 
@@ -865,44 +981,39 @@ impl FileFinderDelegate {
             Match::Search(path_match) => self.labels_for_path_match(&path_match.0),
         };
 
-        if file_name_positions.is_empty() {
+        if labels.file_name_positions.is_empty() {
             if let Some(user_home_path) = std::env::var("HOME").ok() {
                 let user_home_path = user_home_path.trim();
                 if !user_home_path.is_empty() {
-                    if (&full_path).starts_with(user_home_path) {
-                        return (
-                            file_name,
-                            file_name_positions,
-                            full_path.replace(user_home_path, "~"),
-                            full_path_positions,
-                        );
+                    if labels.path.starts_with(user_home_path) {
+                        labels.path.replace_range(0..user_home_path.len(), "~");
+                        labels.path_positions.retain_mut(|position| {
+                            if *position >= user_home_path.len() {
+                                *position -= user_home_path.len();
+                                *position += 1;
+                                true
+                            } else {
+                                false
+                            }
+                        })
                     }
                 }
             }
         }
 
-        (
-            file_name,
-            file_name_positions,
-            full_path,
-            full_path_positions,
-        )
+        labels.check();
+        labels
     }
 
-    fn labels_for_path_match(
-        &self,
-        path_match: &PathMatch,
-    ) -> (String, Vec<usize>, String, Vec<usize>) {
-        let path = &path_match.path;
-        let path_string = path.to_string_lossy();
-        let full_path = [path_match.path_prefix.as_ref(), path_string.as_ref()].join("");
+    fn labels_for_path_match(&self, path_match: &PathMatch) -> MatchLabels {
         let mut path_positions = path_match.positions.clone();
 
-        let file_name = path.file_name().map_or_else(
+        let file_name = path_match.path.file_name().map_or_else(
             || path_match.path_prefix.to_string(),
             |file_name| file_name.to_string_lossy().to_string(),
         );
-        let file_name_start = path_match.path_prefix.len() + path_string.len() - file_name.len();
+        let mut path = path_match.path.to_string_lossy().to_string();
+        let file_name_start = path_match.path_prefix.len() + path.len() - file_name.len();
         let file_name_positions = path_positions
             .iter()
             .filter_map(|pos| {
@@ -914,10 +1025,31 @@ impl FileFinderDelegate {
             })
             .collect();
 
-        let full_path = full_path.trim_end_matches(&file_name).to_string();
-        path_positions.retain(|idx| *idx < full_path.len());
+        path.drain(file_name_start.saturating_sub(path_match.path_prefix.len())..);
+        path_positions.retain_mut(|idx| {
+            if *idx < path.len() {
+                if let Some(elided_range) = &self.matches.elided_byte_range {
+                    if *idx >= elided_range.end {
+                        *idx += '…'.len_utf8();
+                        *idx -= elided_range.len();
+                    }
+                }
+                true
+            } else {
+                false
+            }
+        });
 
-        (file_name, file_name_positions, full_path, path_positions)
+        if let Some(elided_range) = &self.matches.elided_byte_range {
+            path.replace_range(elided_range.clone(), "…");
+        }
+
+        MatchLabels {
+            file_name,
+            file_name_positions,
+            path,
+            path_positions,
+        }
     }
 
     fn lookup_absolute_path(
@@ -969,10 +1101,17 @@ impl FileFinderDelegate {
             }
 
             picker
-                .update_in(&mut cx, |picker, _, cx| {
+                .update_in(&mut cx, |picker, window, cx| {
                     let picker_delegate = &mut picker.delegate;
                     let search_id = util::post_inc(&mut picker_delegate.search_count);
-                    picker_delegate.set_search_matches(search_id, false, query, path_matches, cx);
+                    picker_delegate.set_search_matches(
+                        search_id,
+                        false,
+                        query,
+                        path_matches,
+                        window,
+                        cx,
+                    );
 
                     anyhow::Ok(())
                 })
@@ -1049,6 +1188,10 @@ impl PickerDelegate for FileFinderDelegate {
         window: &mut Window,
         cx: &mut Context<Picker<Self>>,
     ) -> Task<()> {
+        let em_widths = em_widths(window, cx);
+        let file_finder_settings = FileFinderSettings::get_global(cx);
+        let max_width = FileFinder::modal_max_width(file_finder_settings.modal_max_width, window);
+
         let raw_query = raw_query.replace(' ', "");
         let raw_query = raw_query.trim();
         if raw_query.is_empty() {
@@ -1076,6 +1219,8 @@ impl PickerDelegate for FileFinderDelegate {
                     None,
                     None.into_iter(),
                     false,
+                    em_widths,
+                    max_width,
                 );
 
                 self.first_update = false;
@@ -1269,11 +1414,10 @@ impl PickerDelegate for FileFinderDelegate {
                 .size(IconSize::Small.rems())
                 .into_any_element(),
         };
-        let (file_name, file_name_positions, full_path, full_path_positions) =
-            self.labels_for_match(path_match, cx, ix);
+        let labels = self.labels_for_match(path_match, cx, ix);
 
         let file_icon = if settings.file_icons {
-            FileIcons::get_icon(Path::new(&file_name), cx)
+            FileIcons::get_icon(Path::new(&labels.file_name), cx)
                 .map(Icon::from_path)
                 .map(|icon| icon.color(Color::Muted))
         } else {
@@ -1291,9 +1435,12 @@ impl PickerDelegate for FileFinderDelegate {
                     h_flex()
                         .gap_2()
                         .py_px()
-                        .child(HighlightedLabel::new(file_name, file_name_positions))
+                        .child(HighlightedLabel::new(
+                            labels.file_name,
+                            labels.file_name_positions,
+                        ))
                         .child(
-                            HighlightedLabel::new(full_path, full_path_positions)
+                            HighlightedLabel::new(labels.path, labels.path_positions)
                                 .size(LabelSize::Small)
                                 .color(Color::Muted),
                         ),

crates/file_finder/src/file_finder_tests.rs 🔗

@@ -384,6 +384,7 @@ async fn test_matching_cancellation(cx: &mut TestAppContext) {
                 ProjectPanelOrdMatch(matches[1].clone()),
                 ProjectPanelOrdMatch(matches[3].clone()),
             ],
+            window,
             cx,
         );
 
@@ -398,6 +399,7 @@ async fn test_matching_cancellation(cx: &mut TestAppContext) {
                 ProjectPanelOrdMatch(matches[2].clone()),
                 ProjectPanelOrdMatch(matches[3].clone()),
             ],
+            window,
             cx,
         );
 
@@ -492,12 +494,11 @@ async fn test_single_file_worktrees(cx: &mut TestAppContext) {
         let matches = collect_search_matches(picker).search_matches_only();
         assert_eq!(matches.len(), 1);
 
-        let (file_name, file_name_positions, full_path, full_path_positions) =
-            delegate.labels_for_path_match(&matches[0]);
-        assert_eq!(file_name, "the-file");
-        assert_eq!(file_name_positions, &[0, 1, 4]);
-        assert_eq!(full_path, "");
-        assert_eq!(full_path_positions, &[0; 0]);
+        let labels = delegate.labels_for_path_match(&matches[0]);
+        assert_eq!(labels.file_name, "the-file");
+        assert_eq!(labels.file_name_positions, &[0, 1, 4]);
+        assert_eq!(labels.path, "");
+        assert_eq!(labels.path_positions, &[0; 0]);
     });
 
     // Since the worktree root is a file, searching for its name followed by a slash does

crates/fuzzy/src/matcher.rs 🔗

@@ -164,7 +164,6 @@ impl<'a> Matcher<'a> {
         score
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn recursive_score_match(
         &mut self,
         path: &[char],

crates/gpui/src/platform/linux/x11/window.rs 🔗

@@ -353,7 +353,6 @@ where
 }
 
 impl X11WindowState {
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         handle: AnyWindowHandle,
         client: X11ClientStatePtr,
@@ -712,7 +711,6 @@ enum WmHintPropertyState {
 }
 
 impl X11Window {
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         handle: AnyWindowHandle,
         client: X11ClientStatePtr,

crates/language/src/syntax_map.rs 🔗

@@ -1250,7 +1250,6 @@ fn parse_text(
     })
 }
 
-#[allow(clippy::too_many_arguments)]
 fn get_injections(
     config: &InjectionConfig,
     text: &BufferSnapshot,

crates/lsp/src/lsp.rs 🔗

@@ -393,7 +393,6 @@ impl LanguageServer {
         Ok(server)
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn new_internal<Stdin, Stdout, Stderr, F>(
         server_id: LanguageServerId,
         server_name: LanguageServerName,

crates/multi_buffer/src/multi_buffer.rs 🔗

@@ -2906,7 +2906,6 @@ impl MultiBuffer {
         snapshot.check_invariants();
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn recompute_diff_transforms_for_edit(
         &self,
         edit: &Edit<TypedOffset<Excerpt>>,

crates/outline_panel/src/outline_panel.rs 🔗

@@ -2361,7 +2361,6 @@ impl OutlinePanel {
         )
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn render_search_match(
         &mut self,
         multi_buffer_snapshot: Option<&MultiBufferSnapshot>,
@@ -2453,7 +2452,6 @@ impl OutlinePanel {
         ))
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn entry_element(
         &self,
         rendered_entry: PanelEntry,
@@ -3839,7 +3837,6 @@ impl OutlinePanel {
         })
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn push_entry(
         &self,
         state: &mut GenerationState,
@@ -4057,7 +4054,6 @@ impl OutlinePanel {
         update_cached_entries
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn add_excerpt_entries(
         &self,
         state: &mut GenerationState,
@@ -4116,7 +4112,6 @@ impl OutlinePanel {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn add_search_entries(
         &mut self,
         state: &mut GenerationState,

crates/project/src/lsp_store.rs 🔗

@@ -1201,7 +1201,6 @@ impl LocalLspStore {
         Ok(project_transaction)
     }
 
-    #[allow(clippy::too_many_arguments)]
     async fn execute_formatters(
         lsp_store: WeakEntity<LspStore>,
         formatters: &[Formatter],
@@ -1451,7 +1450,6 @@ impl LocalLspStore {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     async fn format_via_lsp(
         this: &WeakEntity<LspStore>,
         buffer: &Entity<Buffer>,
@@ -2960,7 +2958,6 @@ impl LspStore {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub fn new_local(
         buffer_store: Entity<BufferStore>,
         worktree_store: Entity<WorktreeStore>,
@@ -3054,7 +3051,6 @@ impl LspStore {
         })
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub(super) fn new_remote(
         buffer_store: Entity<BufferStore>,
         worktree_store: Entity<WorktreeStore>,
@@ -4485,7 +4481,6 @@ impl LspStore {
         Ok(())
     }
 
-    #[allow(clippy::too_many_arguments)]
     async fn resolve_completion_remote(
         project_id: u64,
         server_id: LanguageServerId,
@@ -7546,7 +7541,6 @@ impl LspStore {
         Ok(())
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn insert_newly_running_language_server(
         &mut self,
         adapter: Arc<CachedLspAdapter>,

crates/project/src/project.rs 🔗

@@ -971,7 +971,6 @@ impl Project {
         .await
     }
 
-    #[allow(clippy::too_many_arguments)]
     async fn from_join_project_response(
         response: TypedEnvelope<proto::JoinProjectResponse>,
         subscriptions: [EntitySubscription; 5],

crates/remote/src/ssh_session.rs 🔗

@@ -1280,7 +1280,6 @@ impl From<SshRemoteClient> for AnyProtoClient {
 
 #[async_trait(?Send)]
 trait RemoteConnection: Send + Sync {
-    #[allow(clippy::too_many_arguments)]
     fn start_proxy(
         &self,
         unique_identifier: String,

crates/rich_text/src/rich_text.rs 🔗

@@ -175,7 +175,6 @@ impl RichText {
     }
 }
 
-#[allow(clippy::too_many_arguments)]
 pub fn render_markdown_mut(
     block: &str,
     mut mentions: &[Mention],

crates/terminal/src/terminal.rs 🔗

@@ -321,7 +321,6 @@ pub struct TerminalBuilder {
 }
 
 impl TerminalBuilder {
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         working_directory: Option<PathBuf>,
         python_venv_directory: Option<PathBuf>,

crates/terminal_view/src/terminal_element.rs 🔗

@@ -171,7 +171,6 @@ impl InteractiveElement for TerminalElement {
 impl StatefulInteractiveElement for TerminalElement {}
 
 impl TerminalElement {
-    #[allow(clippy::too_many_arguments)]
     pub fn new(
         terminal: Entity<Terminal>,
         terminal_view: Entity<TerminalView>,

crates/toolchain_selector/src/toolchain_selector.rs 🔗

@@ -85,7 +85,6 @@ impl ToolchainSelector {
         Some(())
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn new(
         workspace: WeakEntity<Workspace>,
         project: Entity<Project>,
@@ -143,7 +142,6 @@ pub struct ToolchainSelectorDelegate {
 }
 
 impl ToolchainSelectorDelegate {
-    #[allow(clippy::too_many_arguments)]
     fn new(
         active_toolchain: Option<Toolchain>,
         toolchain_selector: WeakEntity<ToolchainSelector>,

crates/workspace/src/pane.rs 🔗

@@ -843,7 +843,6 @@ impl Pane {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub(crate) fn open_item(
         &mut self,
         project_entry_id: Option<ProjectEntryId>,

crates/workspace/src/pane_group.rs 🔗

@@ -122,7 +122,6 @@ impl PaneGroup {
         };
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub fn render(
         &self,
         project: &Entity<Project>,
@@ -228,7 +227,6 @@ impl Member {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     pub fn render(
         &self,
         project: &Entity<Project>,
@@ -678,7 +676,6 @@ impl PaneAxis {
         None
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn render(
         &self,
         project: &Entity<Project>,
@@ -882,7 +879,6 @@ mod element {
             self
         }
 
-        #[allow(clippy::too_many_arguments)]
         fn compute_resize(
             flexes: &Arc<Mutex<Vec<f32>>>,
             e: &MouseMoveEvent,
@@ -972,7 +968,6 @@ mod element {
             window.refresh();
         }
 
-        #[allow(clippy::too_many_arguments)]
         fn layout_handle(
             axis: Axis,
             pane_bounds: Bounds<Pixels>,

crates/zeta/src/zeta.rs 🔗

@@ -355,7 +355,6 @@ impl Zeta {
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn request_completion_impl<F, R>(
         &mut self,
         workspace: Option<Entity<Workspace>>,
@@ -794,7 +793,6 @@ and then another
         }
     }
 
-    #[allow(clippy::too_many_arguments)]
     fn process_completion_response(
         prediction_response: PredictEditsResponse,
         buffer: Entity<Buffer>,