editor: Add minimap (#26893)

## Overview

This PR adds the minimap feature to the Zed editor, closely following
the [design from Visual Studio
Code](https://code.visualstudio.com/docs/getstarted/userinterface#_minimap).
When configured, a second instance of the editor will appear to the left
of the scrollbar. This instance is not interactive and it has a slimmed
down set of annotations, but it is otherwise just a zoomed-out version
of the main editor instance. A thumb shows the line boundaries of the
main viewport, as well as the progress through the document. Clicking on
a section of code in the minimap will jump the editor to that code.
Dragging the thumb will act like the scrollbar, moving sequentially
through the document.

![screenshot of Zed with three editors open and the minimap enabled,
showing the
slider](https://github.com/user-attachments/assets/4178d23a-a5ea-4e38-b871-06dd2a8f9560)

## New settings

This adds a `minimap` section to the editor settings with the following
keys:

### `show`

When to show the minimap in the editor.
This setting can take three values:
1. Show the minimap if the editor's scrollbar is visible: `"auto"`
2. Always show the minimap: `"always"`
3. Never show the minimap: `"never"` (default)

### `thumb`

When to show the minimap thumb.
This setting can take two values:
1. Show the minimap thumb if the mouse is over the minimap: `"hover"`
2. Always show the minimap thumb: `"always"` (default)

### `width`

The width of the minimap in pixels.

Default: `100`

### `font_size`

The font size of the minimap in pixels.

Default: `2`

## Providing feedback

In order to keep the PR focused on development updates, please use the
discussion thread for feature suggestions and usability feedback: #26894


## Features left to add

- [x] fix scrolling performance
- [x] user settings for enable/disable, width, text size, etc.
- [x] show overview of visible lines in minimap
- [x] clicking on minimap should navigate to the corresponding section
of code
- ~[ ] more prominent highlighting in the minimap editor~
- ~[ ] override scrollbar auto setting to always when minimap is set to
always show~

Release Notes:

- Added minimap for high-level overview and quick navigation of editor
contents.

---------

Co-authored-by: MrSubidubi <dev@bahn.sh>
Co-authored-by: Kirill Bulatov <kirill@zed.dev>
This commit is contained in:
Evan Simkowitz 2025-05-07 13:11:09 -07:00 committed by GitHub
parent 902931fdfc
commit 607a9445fc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 1083 additions and 216 deletions

View file

@ -11,9 +11,9 @@ use crate::{CycleNextInlineAssist, CyclePreviousInlineAssist};
use crate::{RemoveAllContext, ToggleContextPicker};
use client::ErrorExt;
use collections::VecDeque;
use editor::display_map::EditorMargins;
use editor::{
ContextMenuOptions, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle,
GutterDimensions, MultiBuffer,
ContextMenuOptions, Editor, EditorElement, EditorEvent, EditorMode, EditorStyle, MultiBuffer,
actions::{MoveDown, MoveUp},
};
use feature_flags::{FeatureFlagAppExt as _, ZedProFeatureFlag};
@ -61,11 +61,13 @@ impl<T: 'static> Render for PromptEditor<T> {
let ui_font_size = ThemeSettings::get_global(cx).ui_font_size(cx);
let mut buttons = Vec::new();
let left_gutter_width = match &self.mode {
const RIGHT_PADDING: Pixels = px(9.);
let (left_gutter_width, right_padding) = match &self.mode {
PromptEditorMode::Buffer {
id: _,
codegen,
gutter_dimensions,
editor_margins,
} => {
let codegen = codegen.read(cx);
@ -73,13 +75,17 @@ impl<T: 'static> Render for PromptEditor<T> {
buttons.push(self.render_cycle_controls(&codegen, cx));
}
let gutter_dimensions = gutter_dimensions.lock();
let editor_margins = editor_margins.lock();
let gutter = editor_margins.gutter;
gutter_dimensions.full_width() + (gutter_dimensions.margin / 2.0)
let left_gutter_width = gutter.full_width() + (gutter.margin / 2.0);
let right_padding = editor_margins.right + RIGHT_PADDING;
(left_gutter_width, right_padding)
}
PromptEditorMode::Terminal { .. } => {
// Give the equivalent of the same left-padding that we're using on the right
Pixels::from(40.0)
(Pixels::from(40.0), Pixels::from(24.))
}
};
@ -100,7 +106,7 @@ impl<T: 'static> Render for PromptEditor<T> {
.size_full()
.pt_0p5()
.pb(bottom_padding)
.pr_6()
.pr(right_padding)
.child(
h_flex()
.items_start()
@ -806,7 +812,7 @@ pub enum PromptEditorMode {
Buffer {
id: InlineAssistId,
codegen: Entity<BufferCodegen>,
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
editor_margins: Arc<Mutex<EditorMargins>>,
},
Terminal {
id: TerminalInlineAssistId,
@ -838,7 +844,7 @@ impl InlineAssistId {
impl PromptEditor<BufferCodegen> {
pub fn new_buffer(
id: InlineAssistId,
gutter_dimensions: Arc<Mutex<GutterDimensions>>,
editor_margins: Arc<Mutex<EditorMargins>>,
prompt_history: VecDeque<String>,
prompt_buffer: Entity<MultiBuffer>,
codegen: Entity<BufferCodegen>,
@ -855,7 +861,7 @@ impl PromptEditor<BufferCodegen> {
let mode = PromptEditorMode::Buffer {
id,
codegen,
gutter_dimensions,
editor_margins,
};
let prompt_editor = cx.new(|cx| {
@ -995,11 +1001,9 @@ impl PromptEditor<BufferCodegen> {
}
}
pub fn gutter_dimensions(&self) -> &Arc<Mutex<GutterDimensions>> {
pub fn editor_margins(&self) -> &Arc<Mutex<EditorMargins>> {
match &self.mode {
PromptEditorMode::Buffer {
gutter_dimensions, ..
} => gutter_dimensions,
PromptEditorMode::Buffer { editor_margins, .. } => editor_margins,
PromptEditorMode::Terminal { .. } => unreachable!(),
}
}