lib.rs

  1//! A crate for handling file encodings in the text editor.
  2
  3use editor::{Editor, EditorSettings};
  4use encoding_rs::Encoding;
  5use gpui::{ClickEvent, Entity, Subscription, WeakEntity};
  6use language::Buffer;
  7use settings::Settings;
  8use ui::{Button, ButtonCommon, Context, LabelSize, Render, Tooltip, Window, div};
  9use ui::{Clickable, ParentElement};
 10use workspace::{ItemHandle, StatusItemView, Workspace};
 11
 12use crate::selectors::save_or_reopen::EncodingSaveOrReopenSelector;
 13
 14/// A status bar item that shows the current file encoding and allows changing it.
 15pub struct EncodingIndicator {
 16    pub encoding: Option<&'static Encoding>,
 17    pub workspace: WeakEntity<Workspace>,
 18
 19    /// Subscription to observe changes in the active editor
 20    observe_editor: Option<Subscription>,
 21
 22    /// Subscription to observe changes in the `encoding` field of the `Buffer` struct
 23    observe_buffer_encoding: Option<Subscription>,
 24
 25    show: bool, // Whether to show the indicator or not, based on whether an editor is active
 26}
 27
 28pub mod selectors;
 29
 30impl Render for EncodingIndicator {
 31    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
 32        let status_element = div();
 33
 34        if (!EditorSettings::get_global(cx).status_bar.encoding_indicator) || !self.show {
 35            return status_element;
 36        }
 37
 38        status_element.child(
 39            Button::new(
 40                "encoding",
 41                encoding_name(self.encoding.unwrap_or(encoding_rs::UTF_8)),
 42            )
 43            .label_size(LabelSize::Small)
 44            .tooltip(Tooltip::text("Select Encoding"))
 45            .on_click(cx.listener(|indicator, _: &ClickEvent, window, cx| {
 46                if let Some(workspace) = indicator.workspace.upgrade() {
 47                    workspace.update(cx, |workspace, cx| {
 48                        EncodingSaveOrReopenSelector::toggle(workspace, window, cx)
 49                    })
 50                } else {
 51                }
 52            })),
 53        )
 54    }
 55}
 56
 57impl EncodingIndicator {
 58    pub fn new(
 59        encoding: Option<&'static Encoding>,
 60        workspace: WeakEntity<Workspace>,
 61        observe_editor: Option<Subscription>,
 62        observe_buffer_encoding: Option<Subscription>,
 63    ) -> EncodingIndicator {
 64        EncodingIndicator {
 65            encoding,
 66            workspace,
 67            observe_editor,
 68            show: true,
 69            observe_buffer_encoding,
 70        }
 71    }
 72
 73    /// Update the encoding when the active editor is switched.
 74    pub fn update_when_editor_is_switched(
 75        &mut self,
 76        editor: Entity<Editor>,
 77        _: &mut Window,
 78        cx: &mut Context<EncodingIndicator>,
 79    ) {
 80        let editor = editor.read(cx);
 81        if let Some((_, buffer, _)) = editor.active_excerpt(cx) {
 82            let encoding = buffer.read(cx).encoding.clone();
 83            self.encoding = Some(&*encoding.lock().unwrap());
 84        }
 85
 86        cx.notify();
 87    }
 88
 89    /// Update the encoding when the `encoding` field of the `Buffer` struct changes.
 90    pub fn update_when_buffer_encoding_changes(
 91        &mut self,
 92        buffer: Entity<Buffer>,
 93        _: &mut Window,
 94        cx: &mut Context<EncodingIndicator>,
 95    ) {
 96        let encoding = buffer.read(cx).encoding.clone();
 97        self.encoding = Some(&*encoding.lock().unwrap());
 98        cx.notify();
 99    }
100}
101
102impl StatusItemView for EncodingIndicator {
103    fn set_active_pane_item(
104        &mut self,
105        active_pane_item: Option<&dyn ItemHandle>,
106        window: &mut Window,
107        cx: &mut Context<Self>,
108    ) {
109        match active_pane_item.and_then(|item| item.downcast::<Editor>()) {
110            Some(editor) => {
111                self.observe_editor =
112                    Some(cx.observe_in(&editor, window, Self::update_when_editor_is_switched));
113                if let Some((_, buffer, _)) = &editor.read(cx).active_excerpt(cx) {
114                    self.observe_buffer_encoding = Some(cx.observe_in(
115                        buffer,
116                        window,
117                        Self::update_when_buffer_encoding_changes,
118                    ));
119                }
120                self.update_when_editor_is_switched(editor, window, cx);
121                self.show = true;
122            }
123            None => {
124                self.encoding = None;
125                self.observe_editor = None;
126                self.show = false;
127            }
128        }
129    }
130}
131
132/// Get a human-readable name for the given encoding.
133pub fn encoding_name(encoding: &'static Encoding) -> String {
134    let name = encoding.name();
135
136    match name {
137        "UTF-8" => "UTF-8",
138        "UTF-16LE" => "UTF-16 LE",
139        "UTF-16BE" => "UTF-16 BE",
140        "windows-1252" => "Windows-1252",
141        "windows-1251" => "Windows-1251",
142        "windows-1250" => "Windows-1250",
143        "ISO-8859-2" => "ISO 8859-2",
144        "ISO-8859-3" => "ISO 8859-3",
145        "ISO-8859-4" => "ISO 8859-4",
146        "ISO-8859-5" => "ISO 8859-5",
147        "ISO-8859-6" => "ISO 8859-6",
148        "ISO-8859-7" => "ISO 8859-7",
149        "ISO-8859-8" => "ISO 8859-8",
150        "ISO-8859-13" => "ISO 8859-13",
151        "ISO-8859-15" => "ISO 8859-15",
152        "KOI8-R" => "KOI8-R",
153        "KOI8-U" => "KOI8-U",
154        "macintosh" => "MacRoman",
155        "x-mac-cyrillic" => "Mac Cyrillic",
156        "windows-874" => "Windows-874",
157        "windows-1253" => "Windows-1253",
158        "windows-1254" => "Windows-1254",
159        "windows-1255" => "Windows-1255",
160        "windows-1256" => "Windows-1256",
161        "windows-1257" => "Windows-1257",
162        "windows-1258" => "Windows-1258",
163        "EUC-KR" => "Windows-949",
164        "EUC-JP" => "EUC-JP",
165        "ISO-2022-JP" => "ISO 2022-JP",
166        "GBK" => "GBK",
167        "gb18030" => "GB18030",
168        "Big5" => "Big5",
169        _ => name,
170    }
171    .to_string()
172}
173
174/// Get an encoding from its index in the predefined list.
175/// If the index is out of range, UTF-8 is returned as a default.
176pub fn encoding_from_index(index: usize) -> &'static Encoding {
177    match index {
178        0 => encoding_rs::UTF_8,
179        1 => encoding_rs::UTF_16LE,
180        2 => encoding_rs::UTF_16BE,
181        3 => encoding_rs::WINDOWS_1252,
182        4 => encoding_rs::WINDOWS_1251,
183        5 => encoding_rs::WINDOWS_1250,
184        6 => encoding_rs::ISO_8859_2,
185        7 => encoding_rs::ISO_8859_3,
186        8 => encoding_rs::ISO_8859_4,
187        9 => encoding_rs::ISO_8859_5,
188        10 => encoding_rs::ISO_8859_6,
189        11 => encoding_rs::ISO_8859_7,
190        12 => encoding_rs::ISO_8859_8,
191        13 => encoding_rs::ISO_8859_13,
192        14 => encoding_rs::ISO_8859_15,
193        15 => encoding_rs::KOI8_R,
194        16 => encoding_rs::KOI8_U,
195        17 => encoding_rs::MACINTOSH,
196        18 => encoding_rs::X_MAC_CYRILLIC,
197        19 => encoding_rs::WINDOWS_874,
198        20 => encoding_rs::WINDOWS_1253,
199        21 => encoding_rs::WINDOWS_1254,
200        22 => encoding_rs::WINDOWS_1255,
201        23 => encoding_rs::WINDOWS_1256,
202        24 => encoding_rs::WINDOWS_1257,
203        25 => encoding_rs::WINDOWS_1258,
204        26 => encoding_rs::EUC_KR,
205        27 => encoding_rs::EUC_JP,
206        28 => encoding_rs::ISO_2022_JP,
207        29 => encoding_rs::GBK,
208        30 => encoding_rs::GB18030,
209        31 => encoding_rs::BIG5,
210        _ => encoding_rs::UTF_8,
211    }
212}
213
214/// Get an encoding from its name.
215pub fn encoding_from_name(name: &str) -> &'static Encoding {
216    match name {
217        "UTF-8" => encoding_rs::UTF_8,
218        "UTF-16 LE" => encoding_rs::UTF_16LE,
219        "UTF-16 BE" => encoding_rs::UTF_16BE,
220        "Windows-1252" => encoding_rs::WINDOWS_1252,
221        "Windows-1251" => encoding_rs::WINDOWS_1251,
222        "Windows-1250" => encoding_rs::WINDOWS_1250,
223        "ISO 8859-2" => encoding_rs::ISO_8859_2,
224        "ISO 8859-3" => encoding_rs::ISO_8859_3,
225        "ISO 8859-4" => encoding_rs::ISO_8859_4,
226        "ISO 8859-5" => encoding_rs::ISO_8859_5,
227        "ISO 8859-6" => encoding_rs::ISO_8859_6,
228        "ISO 8859-7" => encoding_rs::ISO_8859_7,
229        "ISO 8859-8" => encoding_rs::ISO_8859_8,
230        "ISO 8859-13" => encoding_rs::ISO_8859_13,
231        "ISO 8859-15" => encoding_rs::ISO_8859_15,
232        "KOI8-R" => encoding_rs::KOI8_R,
233        "KOI8-U" => encoding_rs::KOI8_U,
234        "MacRoman" => encoding_rs::MACINTOSH,
235        "Mac Cyrillic" => encoding_rs::X_MAC_CYRILLIC,
236        "Windows-874" => encoding_rs::WINDOWS_874,
237        "Windows-1253" => encoding_rs::WINDOWS_1253,
238        "Windows-1254" => encoding_rs::WINDOWS_1254,
239        "Windows-1255" => encoding_rs::WINDOWS_1255,
240        "Windows-1256" => encoding_rs::WINDOWS_1256,
241        "Windows-1257" => encoding_rs::WINDOWS_1257,
242        "Windows-1258" => encoding_rs::WINDOWS_1258,
243        "Windows-949" => encoding_rs::EUC_KR,
244        "EUC-JP" => encoding_rs::EUC_JP,
245        "ISO 2022-JP" => encoding_rs::ISO_2022_JP,
246        "GBK" => encoding_rs::GBK,
247        "GB18030" => encoding_rs::GB18030,
248        "Big5" => encoding_rs::BIG5,
249        "HZ-GB-2312" => encoding_rs::UTF_8, // encoding_rs doesn't support HZ, fallback to UTF-8
250        _ => encoding_rs::UTF_8,            // Default to UTF-8 for unknown names
251    }
252}