Skip to content

Commit 8b063a2

Browse files
committed
Add line ending indicator
Add a line ending indicator to the status bar, disabled by default but can be enabled with `status_bar.line_endings_button` as discussed in https://redirect.github.com/zed-industries/zed/issues/5294
1 parent 42365df commit 8b063a2

File tree

11 files changed

+101
-6
lines changed

11 files changed

+101
-6
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

assets/settings/default.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1342,7 +1342,9 @@
13421342
// Whether to show the active language button in the status bar.
13431343
"active_language_button": true,
13441344
// Whether to show the cursor position button in the status bar.
1345-
"cursor_position_button": true
1345+
"cursor_position_button": true,
1346+
// Whether to show active line endings button in the status bar.
1347+
"line_endings_button": false
13461348
},
13471349
// Settings specific to the terminal
13481350
"terminal": {

crates/line_ending_selector/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,6 @@ picker.workspace = true
2020
project.workspace = true
2121
ui.workspace = true
2222
util.workspace = true
23+
settings.workspace = true
2324
workspace.workspace = true
2425
workspace-hack.workspace = true
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
use editor::Editor;
2+
use gpui::{Entity, Subscription, WeakEntity};
3+
use language::LineEnding;
4+
use ui::{Tooltip, prelude::*};
5+
use workspace::{StatusBarSettings, StatusItemView, item::ItemHandle, item::Settings};
6+
7+
use crate::{LineEndingSelector, Toggle};
8+
9+
#[derive(Default)]
10+
pub struct LineEndingIndicator {
11+
line_ending: Option<LineEnding>,
12+
active_editor: Option<WeakEntity<Editor>>,
13+
_observe_active_editor: Option<Subscription>,
14+
}
15+
16+
impl LineEndingIndicator {
17+
fn update(&mut self, editor: Entity<Editor>, _: &mut Window, cx: &mut Context<Self>) {
18+
self.line_ending = None;
19+
self.active_editor = None;
20+
21+
if let Some((_, buffer, _)) = editor.read(cx).active_excerpt(cx) {
22+
let line_ending = buffer.read(cx).line_ending();
23+
self.line_ending = Some(line_ending);
24+
self.active_editor = Some(editor.downgrade());
25+
}
26+
27+
cx.notify();
28+
}
29+
}
30+
31+
impl Render for LineEndingIndicator {
32+
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
33+
if !StatusBarSettings::get_global(cx).line_endings_button {
34+
return div();
35+
}
36+
37+
div().when_some(self.line_ending.as_ref(), |el, line_ending| {
38+
el.child(
39+
Button::new("change-line-ending", line_ending.label())
40+
.label_size(LabelSize::Small)
41+
.on_click(cx.listener(|this, _, window, cx| {
42+
if let Some(editor) = this.active_editor.as_ref() {
43+
LineEndingSelector::toggle(editor, window, cx);
44+
}
45+
}))
46+
.tooltip(|window, cx| {
47+
Tooltip::for_action("Select Line Ending", &Toggle, window, cx)
48+
}),
49+
)
50+
})
51+
}
52+
}
53+
54+
impl StatusItemView for LineEndingIndicator {
55+
fn set_active_pane_item(
56+
&mut self,
57+
active_pane_item: Option<&dyn ItemHandle>,
58+
window: &mut Window,
59+
cx: &mut Context<Self>,
60+
) {
61+
if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
62+
self._observe_active_editor = Some(cx.observe_in(&editor, window, Self::update));
63+
self.update(editor, window, cx);
64+
} else {
65+
self.line_ending = None;
66+
self._observe_active_editor = None;
67+
}
68+
cx.notify();
69+
}
70+
}

crates/line_ending_selector/src/line_ending_selector.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1+
mod line_ending_indicator;
2+
13
use editor::Editor;
24
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity, actions};
35
use language::{Buffer, LineEnding};
6+
pub use line_ending_indicator::LineEndingIndicator;
47
use picker::{Picker, PickerDelegate};
58
use project::Project;
69
use std::sync::Arc;
@@ -172,10 +175,7 @@ impl PickerDelegate for LineEndingSelectorDelegate {
172175
_: &mut Context<Picker<Self>>,
173176
) -> Option<Self::ListItem> {
174177
let line_ending = self.matches.get(ix)?;
175-
let label = match line_ending {
176-
LineEnding::Unix => "LF",
177-
LineEnding::Windows => "CRLF",
178-
};
178+
let label = line_ending.label();
179179

180180
let mut list_item = ListItem::new(ix)
181181
.inset(true)

crates/settings/src/settings_content/workspace.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,10 @@ pub struct StatusBarSettingsContent {
377377
///
378378
/// Default: true
379379
pub cursor_position_button: Option<bool>,
380+
/// Whether to show active line endings button in the status bar.
381+
///
382+
/// Default: false
383+
pub line_endings_button: Option<bool>,
380384
}
381385

382386
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema, MergeFrom)]

crates/text/src/text.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3261,6 +3261,13 @@ impl LineEnding {
32613261
}
32623262
}
32633263

3264+
pub fn label(&self) -> &'static str {
3265+
match self {
3266+
LineEnding::Unix => "LF",
3267+
LineEnding::Windows => "CRLF",
3268+
}
3269+
}
3270+
32643271
pub fn detect(text: &str) -> Self {
32653272
let mut max_ix = cmp::min(text.len(), 1000);
32663273
while !text.is_char_boundary(max_ix) {

crates/workspace/src/workspace_settings.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ pub struct StatusBarSettings {
227227
pub show: bool,
228228
pub active_language_button: bool,
229229
pub cursor_position_button: bool,
230+
pub line_endings_button: bool,
230231
}
231232

232233
impl Settings for StatusBarSettings {
@@ -236,6 +237,7 @@ impl Settings for StatusBarSettings {
236237
show: status_bar.show.unwrap(),
237238
active_language_button: status_bar.active_language_button.unwrap(),
238239
cursor_position_button: status_bar.cursor_position_button.unwrap(),
240+
line_endings_button: status_bar.line_endings_button.unwrap(),
239241
}
240242
}
241243

crates/zed/src/zed.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,8 @@ pub fn initialize_workspace(
419419

420420
let cursor_position =
421421
cx.new(|_| go_to_line::cursor_position::CursorPosition::new(workspace));
422+
let line_ending_indicator =
423+
cx.new(|_| line_ending_selector::LineEndingIndicator::default());
422424
workspace.status_bar().update(cx, |status_bar, cx| {
423425
status_bar.add_left_item(search_button, window, cx);
424426
status_bar.add_left_item(lsp_button, window, cx);
@@ -427,6 +429,7 @@ pub fn initialize_workspace(
427429
status_bar.add_right_item(edit_prediction_button, window, cx);
428430
status_bar.add_right_item(active_buffer_language, window, cx);
429431
status_bar.add_right_item(active_toolchain_language, window, cx);
432+
status_bar.add_right_item(line_ending_indicator, window, cx);
430433
status_bar.add_right_item(vim_mode_indicator, window, cx);
431434
status_bar.add_right_item(cursor_position, window, cx);
432435
status_bar.add_right_item(image_info, window, cx);

docs/src/configuring-zed.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1498,7 +1498,8 @@ Positive `integer` value between 1 and 32. Values outside of this range will be
14981498
```json
14991499
"status_bar": {
15001500
"active_language_button": true,
1501-
"cursor_position_button": true
1501+
"cursor_position_button": true,
1502+
"line_endings_button": false
15021503
},
15031504
```
15041505

0 commit comments

Comments
 (0)