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}