Add line endings indicator in status bar (#39609)
Closes #5294 This PR adds a line ending indicator to the status bar, hidden by default as discussed in https://github.com/zed-industries/zed/issues/5294. ### Changes - 8b063a22d8700bed9c93989b9e0f6a064b2e86cf add the indicator and `status_bar.line_endings_button` setting. - ~~9926237b709dd4e25ce58d558fd385d63b405f3b changes `status_bar.line_endings_button` from a boolean to an enum:~~ <details> <summary> show details </summary> - `always` Always show line endings indicator. - `non_native` Indicate when line endings do not match the current platform. - `lf_only` Indicate when using unix-style (LF) line endings only. - `crlf_only` Indicate when using windows-style (CRLF) line endings only. - `never` Do not show line endings indicator. I know this many options might be overdoing it, but I was torn between the pleasant default of `non_native` and the simplicity of `lf_only` / `crlf_only`. My thinking was if one is developing on a project which exclusively uses one line-ending style or the other, it would be nice to be able to configure no-indicator-in-the-happy-case behavior regardless of the platform zed is running on. But I'm not really familiar with any projects that use exclusively CRLF line endings in practice. Is this a scenario worth supporting or just something I dreamed up? </details> - 01174191e4cf337069e7a31b0f0432ae94c52515 rename the action context for `line ending: Toggle` -> `line ending selector: Toggle`. When running the action in the command palette with the old name I felt surprised to be greeted with an additional menu, with the new name it feels more predictable (plus now it matches `language_selector::Toggle`!) ### Future work Hidden status bar items still get padding, creating inconsistent spacing (and it kind of stands out where I placed the line-endings button): <img alt="the gap after the indicator is larger than for other buttons" src="https://github.com/user-attachments/assets/24a346d4-3ff6-4f7f-bd87-64d453c2441a" /> I started a new follow-up PR to address that: https://github.com/zed-industries/zed/pull/39992 Release Notes: - Added line ending indicator to the status bar (disabled by default; enabled by setting `status_bar.line_endings_button` to `true`)
This commit is contained in:
@@ -1350,7 +1350,9 @@
|
||||
// Whether to show the active language button in the status bar.
|
||||
"active_language_button": true,
|
||||
// Whether to show the cursor position button in the status bar.
|
||||
"cursor_position_button": true
|
||||
"cursor_position_button": true,
|
||||
// Whether to show active line endings button in the status bar.
|
||||
"line_endings_button": false
|
||||
},
|
||||
// Settings specific to the terminal
|
||||
"terminal": {
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
use editor::Editor;
|
||||
use gpui::{Entity, Subscription, WeakEntity};
|
||||
use language::LineEnding;
|
||||
use ui::{Tooltip, prelude::*};
|
||||
use workspace::{StatusBarSettings, StatusItemView, item::ItemHandle, item::Settings};
|
||||
|
||||
use crate::{LineEndingSelector, Toggle};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LineEndingIndicator {
|
||||
line_ending: Option<LineEnding>,
|
||||
active_editor: Option<WeakEntity<Editor>>,
|
||||
_observe_active_editor: Option<Subscription>,
|
||||
}
|
||||
|
||||
impl LineEndingIndicator {
|
||||
fn update(&mut self, editor: Entity<Editor>, _: &mut Window, cx: &mut Context<Self>) {
|
||||
self.line_ending = None;
|
||||
self.active_editor = None;
|
||||
|
||||
if let Some((_, buffer, _)) = editor.read(cx).active_excerpt(cx) {
|
||||
let line_ending = buffer.read(cx).line_ending();
|
||||
self.line_ending = Some(line_ending);
|
||||
self.active_editor = Some(editor.downgrade());
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for LineEndingIndicator {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
if !StatusBarSettings::get_global(cx).line_endings_button {
|
||||
return div();
|
||||
}
|
||||
|
||||
div().when_some(self.line_ending.as_ref(), |el, line_ending| {
|
||||
el.child(
|
||||
Button::new("change-line-ending", line_ending.label())
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
if let Some(editor) = this.active_editor.as_ref() {
|
||||
LineEndingSelector::toggle(editor, window, cx);
|
||||
}
|
||||
}))
|
||||
.tooltip(|window, cx| {
|
||||
Tooltip::for_action("Select Line Ending", &Toggle, window, cx)
|
||||
}),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusItemView for LineEndingIndicator {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
active_pane_item: Option<&dyn ItemHandle>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
|
||||
self._observe_active_editor = Some(cx.observe_in(&editor, window, Self::update));
|
||||
self.update(editor, window, cx);
|
||||
} else {
|
||||
self.line_ending = None;
|
||||
self._observe_active_editor = None;
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
mod line_ending_indicator;
|
||||
|
||||
use editor::Editor;
|
||||
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity, actions};
|
||||
use language::{Buffer, LineEnding};
|
||||
pub use line_ending_indicator::LineEndingIndicator;
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use project::Project;
|
||||
use std::sync::Arc;
|
||||
@@ -9,7 +12,7 @@ use util::ResultExt;
|
||||
use workspace::ModalView;
|
||||
|
||||
actions!(
|
||||
line_ending,
|
||||
line_ending_selector,
|
||||
[
|
||||
/// Toggles the line ending selector modal.
|
||||
Toggle
|
||||
@@ -172,10 +175,7 @@ impl PickerDelegate for LineEndingSelectorDelegate {
|
||||
_: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let line_ending = self.matches.get(ix)?;
|
||||
let label = match line_ending {
|
||||
LineEnding::Unix => "LF",
|
||||
LineEnding::Windows => "CRLF",
|
||||
};
|
||||
let label = line_ending.label();
|
||||
|
||||
let mut list_item = ListItem::new(ix)
|
||||
.inset(true)
|
||||
|
||||
@@ -380,6 +380,10 @@ pub struct StatusBarSettingsContent {
|
||||
///
|
||||
/// Default: true
|
||||
pub cursor_position_button: Option<bool>,
|
||||
/// Whether to show active line endings button in the status bar.
|
||||
///
|
||||
/// Default: false
|
||||
pub line_endings_button: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(
|
||||
|
||||
@@ -3267,6 +3267,13 @@ impl LineEnding {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn label(&self) -> &'static str {
|
||||
match self {
|
||||
LineEnding::Unix => "LF",
|
||||
LineEnding::Windows => "CRLF",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detect(text: &str) -> Self {
|
||||
let mut max_ix = cmp::min(text.len(), 1000);
|
||||
while !text.is_char_boundary(max_ix) {
|
||||
|
||||
@@ -126,6 +126,7 @@ pub struct StatusBarSettings {
|
||||
pub show: bool,
|
||||
pub active_language_button: bool,
|
||||
pub cursor_position_button: bool,
|
||||
pub line_endings_button: bool,
|
||||
}
|
||||
|
||||
impl Settings for StatusBarSettings {
|
||||
@@ -135,6 +136,7 @@ impl Settings for StatusBarSettings {
|
||||
show: status_bar.show.unwrap(),
|
||||
active_language_button: status_bar.active_language_button.unwrap(),
|
||||
cursor_position_button: status_bar.cursor_position_button.unwrap(),
|
||||
line_endings_button: status_bar.line_endings_button.unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -417,6 +417,8 @@ pub fn initialize_workspace(
|
||||
|
||||
let cursor_position =
|
||||
cx.new(|_| go_to_line::cursor_position::CursorPosition::new(workspace));
|
||||
let line_ending_indicator =
|
||||
cx.new(|_| line_ending_selector::LineEndingIndicator::default());
|
||||
workspace.status_bar().update(cx, |status_bar, cx| {
|
||||
status_bar.add_left_item(search_button, window, cx);
|
||||
status_bar.add_left_item(lsp_button, window, cx);
|
||||
@@ -425,6 +427,7 @@ pub fn initialize_workspace(
|
||||
status_bar.add_right_item(edit_prediction_button, window, cx);
|
||||
status_bar.add_right_item(active_buffer_language, window, cx);
|
||||
status_bar.add_right_item(active_toolchain_language, window, cx);
|
||||
status_bar.add_right_item(line_ending_indicator, window, cx);
|
||||
status_bar.add_right_item(vim_mode_indicator, window, cx);
|
||||
status_bar.add_right_item(cursor_position, window, cx);
|
||||
status_bar.add_right_item(image_info, window, cx);
|
||||
@@ -4669,7 +4672,7 @@ mod tests {
|
||||
"keymap_editor",
|
||||
"keystroke_input",
|
||||
"language_selector",
|
||||
"line_ending",
|
||||
"line_ending_selector",
|
||||
"lsp_tool",
|
||||
"markdown",
|
||||
"menu",
|
||||
|
||||
@@ -1498,7 +1498,8 @@ Positive `integer` value between 1 and 32. Values outside of this range will be
|
||||
```json [settings]
|
||||
"status_bar": {
|
||||
"active_language_button": true,
|
||||
"cursor_position_button": true
|
||||
"cursor_position_button": true,
|
||||
"line_endings_button": false
|
||||
},
|
||||
```
|
||||
|
||||
|
||||
@@ -319,6 +319,10 @@ TBD: Centered layout related settings
|
||||
// Clicking the button brings up an input for jumping to a line and column.
|
||||
// Defaults to true.
|
||||
"cursor_position_button": true,
|
||||
// Show/hide a button that displays the buffer's line-ending mode.
|
||||
// Clicking the button brings up the line-ending selector.
|
||||
// Defaults to false.
|
||||
"line_endings_button": false
|
||||
},
|
||||
"global_lsp_settings": {
|
||||
// Show/hide the LSP button in the status bar.
|
||||
|
||||
Reference in New Issue
Block a user