active_buffer_encoding.rs

  1use crate::{EncodingSelector, Toggle};
  2
  3use editor::Editor;
  4use encoding_rs::{Encoding, UTF_8};
  5use gpui::{
  6    Context, Entity, IntoElement, ParentElement, Render, Styled, Subscription, WeakEntity, Window,
  7    div,
  8};
  9use project::Project;
 10use ui::{Button, ButtonCommon, Clickable, LabelSize, Tooltip};
 11use workspace::{
 12    StatusBarSettings, StatusItemView, Workspace,
 13    item::{ItemHandle, Settings},
 14};
 15
 16pub struct ActiveBufferEncoding {
 17    active_encoding: Option<&'static Encoding>,
 18    workspace: WeakEntity<Workspace>,
 19    project: Entity<Project>,
 20    _observe_active_editor: Option<Subscription>,
 21    has_bom: bool,
 22    is_dirty: bool,
 23    is_shared: bool,
 24    is_via_remote_server: bool,
 25}
 26
 27impl ActiveBufferEncoding {
 28    pub fn new(workspace: &Workspace) -> Self {
 29        Self {
 30            active_encoding: None,
 31            workspace: workspace.weak_handle(),
 32            project: workspace.project().clone(),
 33            _observe_active_editor: None,
 34            has_bom: false,
 35            is_dirty: false,
 36            is_shared: false,
 37            is_via_remote_server: false,
 38        }
 39    }
 40
 41    fn update_encoding(&mut self, editor: Entity<Editor>, _: &mut Window, cx: &mut Context<Self>) {
 42        self.active_encoding = None;
 43        self.has_bom = false;
 44        self.is_dirty = false;
 45
 46        let project = self.project.read(cx);
 47        self.is_shared = project.is_shared();
 48        self.is_via_remote_server = project.is_via_remote_server();
 49
 50        if let Some((_, buffer, _)) = editor.read(cx).active_excerpt(cx) {
 51            let buffer = buffer.read(cx);
 52            self.active_encoding = Some(buffer.encoding());
 53            self.has_bom = buffer.has_bom();
 54            self.is_dirty = buffer.is_dirty();
 55        }
 56
 57        cx.notify();
 58    }
 59}
 60
 61impl Render for ActiveBufferEncoding {
 62    fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
 63        let Some(active_encoding) = self.active_encoding else {
 64            return div().hidden();
 65        };
 66
 67        let display_option = StatusBarSettings::get_global(cx).active_encoding_button;
 68        let is_utf8 = active_encoding == UTF_8;
 69        if !display_option.should_show(is_utf8, self.has_bom) {
 70            return div().hidden();
 71        }
 72
 73        let mut text = active_encoding.name().to_string();
 74        if self.has_bom {
 75            text.push_str(" (BOM)");
 76        }
 77
 78        let (disabled, tooltip_text) = if self.is_dirty {
 79            (true, "Save file to change encoding")
 80        } else if self.is_shared {
 81            (true, "Cannot change encoding during collaboration")
 82        } else if self.is_via_remote_server {
 83            (true, "Cannot change encoding of remote server file")
 84        } else {
 85            (false, "Reopen with Encoding")
 86        };
 87
 88        div().child(
 89            Button::new("change-encoding", text)
 90                .label_size(LabelSize::Small)
 91                .on_click(cx.listener(move |this, _, window, cx| {
 92                    if disabled {
 93                        return;
 94                    }
 95                    if let Some(workspace) = this.workspace.upgrade() {
 96                        workspace.update(cx, |workspace, cx| {
 97                            EncodingSelector::toggle(workspace, window, cx)
 98                        });
 99                    }
100                }))
101                .tooltip(move |_window, cx| {
102                    if disabled {
103                        Tooltip::text(tooltip_text)(_window, cx)
104                    } else {
105                        Tooltip::for_action(tooltip_text, &Toggle, cx)
106                    }
107                }),
108        )
109    }
110}
111
112impl StatusItemView for ActiveBufferEncoding {
113    fn set_active_pane_item(
114        &mut self,
115        active_pane_item: Option<&dyn ItemHandle>,
116        window: &mut Window,
117        cx: &mut Context<Self>,
118    ) {
119        if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
120            self._observe_active_editor =
121                Some(cx.observe_in(&editor, window, Self::update_encoding));
122            self.update_encoding(editor, window, cx);
123        } else {
124            self.active_encoding = None;
125            self.has_bom = false;
126            self.is_dirty = false;
127            self.is_shared = false;
128            self.is_via_remote_server = false;
129            self._observe_active_editor = None;
130        }
131
132        cx.notify();
133    }
134}