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