Detailed changes
@@ -5583,6 +5583,17 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "encoding_selector"
+version = "0.1.0"
+dependencies = [
+ "editor",
+ "encoding_rs",
+ "gpui",
+ "ui",
+ "workspace",
+]
+
[[package]]
name = "endi"
version = "1.1.0"
@@ -20710,6 +20721,7 @@ dependencies = [
"edit_prediction",
"edit_prediction_ui",
"editor",
+ "encoding_selector",
"env_logger 0.11.8",
"extension",
"extension_host",
@@ -59,6 +59,7 @@ members = [
"crates/edit_prediction_ui",
"crates/edit_prediction_context",
"crates/editor",
+ "crates/encoding_selector",
"crates/eval",
"crates/eval_utils",
"crates/explorer_command_injector",
@@ -292,6 +293,7 @@ deepseek = { path = "crates/deepseek" }
derive_refineable = { path = "crates/refineable/derive_refineable" }
diagnostics = { path = "crates/diagnostics" }
editor = { path = "crates/editor" }
+encoding_selector = { path = "crates/encoding_selector" }
eval_utils = { path = "crates/eval_utils" }
extension = { path = "crates/extension" }
extension_host = { path = "crates/extension_host" }
@@ -1472,6 +1472,8 @@
"cursor_position_button": true,
// Whether to show active line endings button in the status bar.
"line_endings_button": false,
+ // Control when to show the active encoding in the status bar.
+ "active_encoding_button": "non_utf8",
},
// Settings specific to the terminal
"terminal": {
@@ -0,0 +1,20 @@
+[package]
+name = "encoding_selector"
+version = "0.1.0"
+edition.workspace = true
+publish.workspace = true
+license = "GPL-3.0-or-later"
+
+[lints]
+workspace = true
+
+[lib]
+path = "src/encoding_selector.rs"
+doctest = false
+
+[dependencies]
+editor.workspace = true
+encoding_rs.workspace = true
+gpui.workspace = true
+ui.workspace = true
+workspace.workspace = true
@@ -0,0 +1 @@
+../../LICENSE-GPL
@@ -0,0 +1,91 @@
+use editor::Editor;
+use encoding_rs::{Encoding, UTF_8};
+use gpui::{
+ Context, Entity, IntoElement, ParentElement, Render, Styled, Subscription, Window, div,
+};
+use ui::{Button, ButtonCommon, Clickable, LabelSize, Tooltip};
+use workspace::{
+ StatusBarSettings, StatusItemView, Workspace,
+ item::{ItemHandle, Settings},
+};
+
+pub struct ActiveBufferEncoding {
+ active_encoding: Option<&'static Encoding>,
+ //workspace: WeakEntity<Workspace>,
+ _observe_active_editor: Option<Subscription>,
+ has_bom: bool,
+}
+
+impl ActiveBufferEncoding {
+ pub fn new(_workspace: &Workspace) -> Self {
+ Self {
+ active_encoding: None,
+ //workspace: workspace.weak_handle(),
+ _observe_active_editor: None,
+ has_bom: false,
+ }
+ }
+
+ fn update_encoding(&mut self, editor: Entity<Editor>, _: &mut Window, cx: &mut Context<Self>) {
+ self.active_encoding = None;
+
+ let editor = editor.read(cx);
+ if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
+ let buffer = buffer.read(cx);
+
+ self.active_encoding = Some(buffer.encoding());
+ self.has_bom = buffer.has_bom();
+ }
+
+ cx.notify();
+ }
+}
+
+impl Render for ActiveBufferEncoding {
+ fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
+ let Some(active_encoding) = self.active_encoding else {
+ return div().hidden();
+ };
+
+ let display_option = StatusBarSettings::get_global(cx).active_encoding_button;
+ let is_utf8 = active_encoding == UTF_8;
+ if !display_option.should_show(is_utf8, self.has_bom) {
+ return div().hidden();
+ }
+
+ let mut text = active_encoding.name().to_string();
+ if self.has_bom {
+ text.push_str(" (BOM)");
+ }
+
+ div().child(
+ Button::new("change-encoding", text)
+ .label_size(LabelSize::Small)
+ .on_click(|_, _, _cx| {
+ // No-op
+ })
+ .tooltip(Tooltip::text("Current Encoding")),
+ )
+ }
+}
+
+impl StatusItemView for ActiveBufferEncoding {
+ 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_encoding));
+ self.update_encoding(editor, window, cx);
+ } else {
+ self.active_encoding = None;
+ self.has_bom = false;
+ self._observe_active_editor = None;
+ }
+
+ cx.notify();
+ }
+}
@@ -0,0 +1,4 @@
+mod active_buffer_encoding;
+pub use active_buffer_encoding::ActiveBufferEncoding;
+
+pub fn init() {}
@@ -435,6 +435,44 @@ pub struct StatusBarSettingsContent {
///
/// Default: false
pub line_endings_button: Option<bool>,
+ /// Whether to show the active encoding button in the status bar.
+ ///
+ /// Default: non_utf8
+ pub active_encoding_button: Option<EncodingDisplayOptions>,
+}
+
+#[derive(
+ Copy,
+ Clone,
+ Debug,
+ Eq,
+ PartialEq,
+ Default,
+ Serialize,
+ Deserialize,
+ JsonSchema,
+ MergeFrom,
+ strum::VariantNames,
+ strum::VariantArray,
+)]
+#[serde(rename_all = "snake_case")]
+pub enum EncodingDisplayOptions {
+ Enabled,
+ Disabled,
+ #[default]
+ NonUtf8,
+}
+impl EncodingDisplayOptions {
+ pub fn should_show(&self, is_utf8: bool, has_bom: bool) -> bool {
+ match self {
+ Self::Disabled => false,
+ Self::Enabled => true,
+ Self::NonUtf8 => {
+ let is_standard_utf8 = is_utf8 && !has_bom;
+ !is_standard_utf8
+ }
+ }
+ }
}
#[derive(
@@ -655,6 +655,7 @@ impl VsCodeSettings {
active_language_button: None,
cursor_position_button: None,
line_endings_button: None,
+ active_encoding_button: None,
})
}
@@ -2835,6 +2835,28 @@ pub(crate) fn settings_data(cx: &App) -> Vec<SettingsPage> {
metadata: None,
files: USER,
}),
+ SettingsPageItem::SettingItem(SettingItem {
+ title: "Active Encoding Button",
+ description: "Control when to show the active encoding in the status bar.",
+ field: Box::new(SettingField {
+ json_path: Some("status_bar.active_encoding_button"),
+ pick: |settings_content| {
+ settings_content
+ .status_bar
+ .as_ref()?
+ .active_encoding_button
+ .as_ref()
+ },
+ write: |settings_content, value| {
+ settings_content
+ .status_bar
+ .get_or_insert_default()
+ .active_encoding_button = value;
+ },
+ }),
+ metadata: None,
+ files: USER,
+ }),
SettingsPageItem::SettingItem(SettingItem {
title: "Cursor Position Button",
description: "Show the cursor position button in the status bar.",
@@ -498,6 +498,7 @@ fn init_renderers(cx: &mut App) {
.add_basic_renderer::<settings::NotifyWhenAgentWaiting>(render_dropdown)
.add_basic_renderer::<settings::ImageFileSizeUnit>(render_dropdown)
.add_basic_renderer::<settings::StatusStyle>(render_dropdown)
+ .add_basic_renderer::<settings::EncodingDisplayOptions>(render_dropdown)
.add_basic_renderer::<settings::PaneSplitDirectionHorizontal>(render_dropdown)
.add_basic_renderer::<settings::PaneSplitDirectionVertical>(render_dropdown)
.add_basic_renderer::<settings::PaneSplitDirectionVertical>(render_dropdown)
@@ -4,8 +4,9 @@ use crate::DockPosition;
use collections::HashMap;
use serde::Deserialize;
pub use settings::{
- AutosaveSetting, BottomDockLayout, InactiveOpacity, PaneSplitDirectionHorizontal,
- PaneSplitDirectionVertical, RegisterSetting, RestoreOnStartupBehavior, Settings,
+ AutosaveSetting, BottomDockLayout, EncodingDisplayOptions, InactiveOpacity,
+ PaneSplitDirectionHorizontal, PaneSplitDirectionVertical, RegisterSetting,
+ RestoreOnStartupBehavior, Settings,
};
#[derive(RegisterSetting)]
@@ -130,6 +131,7 @@ pub struct StatusBarSettings {
pub active_language_button: bool,
pub cursor_position_button: bool,
pub line_endings_button: bool,
+ pub active_encoding_button: EncodingDisplayOptions,
}
impl Settings for StatusBarSettings {
@@ -140,6 +142,7 @@ impl Settings for StatusBarSettings {
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(),
+ active_encoding_button: status_bar.active_encoding_button.unwrap(),
}
}
}
@@ -92,6 +92,7 @@ debugger_tools.workspace = true
debugger_ui.workspace = true
diagnostics.workspace = true
editor.workspace = true
+encoding_selector.workspace = true
env_logger.workspace = true
extension.workspace = true
extension_host.workspace = true
@@ -433,6 +433,8 @@ pub fn initialize_workspace(
window,
cx,
);
+ let active_buffer_encoding =
+ cx.new(|_| encoding_selector::ActiveBufferEncoding::new(workspace));
let active_buffer_language =
cx.new(|_| language_selector::ActiveBufferLanguage::new(workspace));
let active_toolchain_language =
@@ -459,6 +461,7 @@ pub fn initialize_workspace(
status_bar.add_left_item(diagnostic_summary, window, cx);
status_bar.add_left_item(activity_indicator, window, cx);
status_bar.add_right_item(edit_prediction_ui, window, cx);
+ status_bar.add_right_item(active_buffer_encoding, 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);
@@ -1615,7 +1615,8 @@ Positive `integer` value between 1 and 32. Values outside of this range will be
"status_bar": {
"active_language_button": true,
"cursor_position_button": true,
- "line_endings_button": false
+ "line_endings_button": false,
+ "active_encoding_button": "non_utf8"
},
```
@@ -332,7 +332,11 @@ TBD: Centered layout related settings
// 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
+ "line_endings_button": false,
+ // Show/hide a button that displays the buffer's character encoding.
+ // If set to "non_utf8", the button is hidden only for UTF-8 without BOM.
+ // Defaults to "non_utf8".
+ "active_encoding_button": "non_utf8"
},
"global_lsp_settings": {
// Show/hide the LSP button in the status bar.