encodings_ui.rs

  1//! A crate for handling file encodings in the text editor.
  2
  3use editor::Editor;
  4use gpui::{Entity, Subscription, WeakEntity};
  5use language::{Buffer, BufferEvent};
  6use ui::{
  7    App, Button, ButtonCommon, Context, IntoElement, LabelSize, Render, Tooltip, Window, div,
  8};
  9use ui::{Clickable, ParentElement};
 10use workspace::notifications::NotifyTaskExt;
 11use workspace::{ItemHandle, StatusItemView, Workspace};
 12use zed_actions::encodings_ui::OpenWithEncoding;
 13// use zed_actions::encodings_ui::Toggle;
 14
 15/// A status bar item that shows the current file encoding and allows changing it.
 16pub struct EncodingIndicator {
 17    pub buffer: Option<WeakEntity<Buffer>>,
 18    pub workspace: WeakEntity<Workspace>,
 19    observe_buffer: Option<Subscription>,
 20}
 21
 22pub mod selectors;
 23
 24impl Render for EncodingIndicator {
 25    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
 26        let Some(buffer) = self.buffer() else {
 27            return gpui::Empty.into_any_element();
 28        };
 29
 30        div()
 31            .child(
 32                Button::new("encoding", buffer.read(cx).encoding().name())
 33                    .label_size(LabelSize::Small)
 34                    .tooltip(Tooltip::text("Select Encoding"))
 35                    .on_click(cx.listener(move |this, _, window, cx| {
 36                        let Some(buffer) = this.buffer() else {
 37                            return;
 38                        };
 39                        this.workspace
 40                            .update(cx, move |workspace, cx| {
 41                                if buffer.read(cx).file().is_some() {
 42                                    selectors::save_or_reopen(buffer, workspace, window, cx)
 43                                } else {
 44                                    // todo!()
 45                                }
 46                            })
 47                            .ok();
 48                    })),
 49            )
 50            .into_any_element()
 51    }
 52}
 53
 54impl EncodingIndicator {
 55    pub fn new(workspace: WeakEntity<Workspace>) -> EncodingIndicator {
 56        EncodingIndicator {
 57            workspace,
 58            buffer: None,
 59            observe_buffer: None,
 60        }
 61    }
 62
 63    fn buffer(&self) -> Option<Entity<Buffer>> {
 64        self.buffer.as_ref().and_then(|b| b.upgrade())
 65    }
 66
 67    /// Update the encoding when the `encoding` field of the `Buffer` struct changes.
 68    pub fn on_buffer_event(
 69        &mut self,
 70        _: Entity<Buffer>,
 71        e: &BufferEvent,
 72        cx: &mut Context<EncodingIndicator>,
 73    ) {
 74        if matches!(e, BufferEvent::EncodingChanged) {
 75            cx.notify();
 76        }
 77    }
 78}
 79
 80impl StatusItemView for EncodingIndicator {
 81    fn set_active_pane_item(
 82        &mut self,
 83        active_pane_item: Option<&dyn ItemHandle>,
 84        _window: &mut Window,
 85        cx: &mut Context<Self>,
 86    ) {
 87        if let Some(editor) = active_pane_item.and_then(|item| item.act_as::<Editor>(cx))
 88            && let Some(buffer) = editor.read(cx).buffer().read(cx).as_singleton()
 89        {
 90            self.buffer = Some(buffer.downgrade());
 91            self.observe_buffer = Some(cx.subscribe(&buffer, Self::on_buffer_event));
 92        } else {
 93            self.buffer = None;
 94            self.observe_buffer = None;
 95        }
 96        cx.notify();
 97    }
 98}
 99
100pub fn init(cx: &mut App) {
101    cx.observe_new(|workspace: &mut Workspace, _, _| {
102        workspace.register_action(|workspace, action: &OpenWithEncoding, window, cx| {
103            selectors::open_with_encoding(action.0.clone(), workspace, window, cx)
104                .detach_and_notify_err(window, cx);
105        });
106    })
107    .detach();
108}