From 5723987b59323f33e43c8eb4f32f181a86aafa0b Mon Sep 17 00:00:00 2001 From: R Aadarsh Date: Sat, 23 Aug 2025 20:03:26 +0530 Subject: [PATCH] Implement the actual encoding selector. There are currently only two encodings in the selector used as placeholders, but more will be added in the future. As of now, the encoding picker is not actually triggered. --- Cargo.lock | 68 +++++- Cargo.toml | 16 -- crates/encodings/Cargo.toml | 2 +- crates/encodings/src/lib.rs | 196 +++--------------- crates/encodings/src/selectors.rs | 331 ++++++++++++++++++++++++++++++ crates/fs/Cargo.toml | 2 + crates/fs/src/encodings.rs | 21 ++ crates/language/Cargo.toml | 1 + crates/language/src/buffer.rs | 2 + crates/zed/src/zed.rs | 5 +- 10 files changed, 453 insertions(+), 191 deletions(-) create mode 100644 crates/encodings/src/selectors.rs create mode 100644 crates/fs/src/encodings.rs diff --git a/Cargo.lock b/Cargo.lock index e6f1aabf84..8326916c01 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5194,6 +5194,70 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" +[[package]] +name = "encoding" +version = "0.2.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec" +dependencies = [ + "encoding-index-japanese", + "encoding-index-korean", + "encoding-index-simpchinese", + "encoding-index-singlebyte", + "encoding-index-tradchinese", +] + +[[package]] +name = "encoding-index-japanese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-korean" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-simpchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87a7194909b9118fc707194baa434a4e3b0fb6a5a757c73c3adb07aa25031f7" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-singlebyte" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3351d5acffb224af9ca265f435b859c7c01537c0849754d3db3fdf2bfe2ae84a" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding-index-tradchinese" +version = "1.20141219.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd0e20d5688ce3cab59eb3ef3a2083a5c77bf496cb798dc6fcdb75f323890c18" +dependencies = [ + "encoding_index_tests", +] + +[[package]] +name = "encoding_index_tests" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a246d82be1c9d791c5dfde9a2bd045fc3cbba3fa2b11ad558f27d01712f00569" + [[package]] name = "encoding_rs" version = "0.8.35" @@ -5207,9 +5271,9 @@ dependencies = [ name = "encodings" version = "0.1.0" dependencies = [ + "editor", "fuzzy", "gpui", - "language", "picker", "ui", "util", @@ -6055,6 +6119,7 @@ dependencies = [ "async-trait", "cocoa 0.26.0", "collections", + "encoding", "fsevent", "futures 0.3.31", "git", @@ -9035,6 +9100,7 @@ dependencies = [ "ctor", "diffy", "ec4rs", + "encoding", "fs", "futures 0.3.31", "fuzzy", diff --git a/Cargo.toml b/Cargo.toml index e4d8638bc2..7ba19fdcd8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -238,7 +238,6 @@ activity_indicator = { path = "crates/activity_indicator" } agent_ui = { path = "crates/agent_ui" } agent_settings = { path = "crates/agent_settings" } agent_servers = { path = "crates/agent_servers" } -ai = { path = "crates/ai" } ai_onboarding = { path = "crates/ai_onboarding" } anthropic = { path = "crates/anthropic" } askpass = { path = "crates/askpass" } @@ -250,7 +249,6 @@ assistant_tool = { path = "crates/assistant_tool" } assistant_tools = { path = "crates/assistant_tools" } audio = { path = "crates/audio" } auto_update = { path = "crates/auto_update" } -auto_update_helper = { path = "crates/auto_update_helper" } auto_update_ui = { path = "crates/auto_update_ui" } aws_http_client = { path = "crates/aws_http_client" } bedrock = { path = "crates/bedrock" } @@ -264,7 +262,6 @@ clock = { path = "crates/clock" } cloud_api_client = { path = "crates/cloud_api_client" } cloud_api_types = { path = "crates/cloud_api_types" } cloud_llm_client = { path = "crates/cloud_llm_client" } -collab = { path = "crates/collab" } collab_ui = { path = "crates/collab_ui" } collections = { path = "crates/collections" } command_palette = { path = "crates/command_palette" } @@ -348,8 +345,6 @@ outline_panel = { path = "crates/outline_panel" } panel = { path = "crates/panel" } paths = { path = "crates/paths" } picker = { path = "crates/picker" } -plugin = { path = "crates/plugin" } -plugin_macros = { path = "crates/plugin_macros" } prettier = { path = "crates/prettier" } settings_profile_selector = { path = "crates/settings_profile_selector" } project = { path = "crates/project" } @@ -370,7 +365,6 @@ rope = { path = "crates/rope" } rpc = { path = "crates/rpc" } rules_library = { path = "crates/rules_library" } search = { path = "crates/search" } -semantic_index = { path = "crates/semantic_index" } semantic_version = { path = "crates/semantic_version" } session = { path = "crates/session" } settings = { path = "crates/settings" } @@ -381,7 +375,6 @@ snippets_ui = { path = "crates/snippets_ui" } sqlez = { path = "crates/sqlez" } sqlez_macros = { path = "crates/sqlez_macros" } story = { path = "crates/story" } -storybook = { path = "crates/storybook" } streaming_diff = { path = "crates/streaming_diff" } sum_tree = { path = "crates/sum_tree" } supermaven = { path = "crates/supermaven" } @@ -397,7 +390,6 @@ terminal_view = { path = "crates/terminal_view" } text = { path = "crates/text" } theme = { path = "crates/theme" } theme_extension = { path = "crates/theme_extension" } -theme_importer = { path = "crates/theme_importer" } theme_selector = { path = "crates/theme_selector" } time_format = { path = "crates/time_format" } title_bar = { path = "crates/title_bar" } @@ -469,7 +461,6 @@ ciborium = "0.2" circular-buffer = "1.0" clap = { version = "4.4", features = ["derive"] } cocoa = "0.26" -cocoa-foundation = "0.2.0" convert_case = "0.8.0" core-foundation = "0.10.0" core-foundation-sys = "0.8.6" @@ -545,7 +536,6 @@ pet = { git = "https://github.com/microsoft/python-environment-tools.git", rev = pet-conda = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" } pet-core = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" } pet-fs = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" } -pet-pixi = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" } pet-poetry = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" } pet-reporter = { git = "https://github.com/microsoft/python-environment-tools.git", rev = "845945b830297a50de0e24020b980a65e4820559" } portable-pty = "0.9.0" @@ -668,7 +658,6 @@ wasmtime = { version = "29", default-features = false, features = [ wasmtime-wasi = "29" which = "6.0.0" windows-core = "0.61" -wit-component = "0.221" workspace-hack = "0.1.0" yawc = "0.2.5" zstd = "0.11" @@ -742,11 +731,7 @@ codegen-units = 16 [profile.dev.package] taffy = { opt-level = 3 } cranelift-codegen = { opt-level = 3 } -cranelift-codegen-meta = { opt-level = 3 } -cranelift-codegen-shared = { opt-level = 3 } resvg = { opt-level = 3 } -rustybuzz = { opt-level = 3 } -ttf-parser = { opt-level = 3 } wasmtime-cranelift = { opt-level = 3 } wasmtime = { opt-level = 3 } # Build single-source-file crates with cg=1 as it helps make `cargo build` of a whole workspace a bit faster @@ -756,7 +741,6 @@ breadcrumbs = { codegen-units = 1 } collections = { codegen-units = 1 } command_palette = { codegen-units = 1 } command_palette_hooks = { codegen-units = 1 } -extension_cli = { codegen-units = 1 } feature_flags = { codegen-units = 1 } file_icons = { codegen-units = 1 } fsevent = { codegen-units = 1 } diff --git a/crates/encodings/Cargo.toml b/crates/encodings/Cargo.toml index d49dff06ec..2d75395810 100644 --- a/crates/encodings/Cargo.toml +++ b/crates/encodings/Cargo.toml @@ -9,9 +9,9 @@ ui.workspace = true workspace.workspace = true gpui.workspace = true picker.workspace = true -language.workspace = true util.workspace = true fuzzy.workspace = true +editor.workspace = true [lints] workspace = true diff --git a/crates/encodings/src/lib.rs b/crates/encodings/src/lib.rs index 23b7429526..bfecfceea3 100644 --- a/crates/encodings/src/lib.rs +++ b/crates/encodings/src/lib.rs @@ -1,194 +1,42 @@ -use std::sync::Weak; -use std::sync::atomic::AtomicBool; - -use fuzzy::{StringMatch, StringMatchCandidate}; -use gpui::{AppContext, ClickEvent, DismissEvent, Entity, EventEmitter, Focusable, WeakEntity}; -use language::Buffer; -use picker::{Picker, PickerDelegate}; -use ui::{ - Button, ButtonCommon, Context, Label, LabelSize, ListItem, Render, Styled, Tooltip, Window, - div, rems, v_flex, -}; +use editor::Editor; +use gpui::{ClickEvent, Entity, WeakEntity}; +use ui::{Button, ButtonCommon, Context, LabelSize, Render, Tooltip, Window, div}; use ui::{Clickable, ParentElement}; -use util::ResultExt; -use workspace::{ItemHandle, ModalView, StatusItemView, Workspace}; +use workspace::{ItemHandle, StatusItemView, Workspace}; + +use crate::selectors::save_or_reopen::{EncodingSaveOrReopenSelector, get_current_encoding}; pub enum Encoding { - Utf8(WeakEntity), + Utf8, + Iso8859_1, } impl Encoding { pub fn as_str(&self) -> &str { match &self { - Encoding::Utf8(_) => "UTF-8", + Encoding::Utf8 => "UTF-8", + Encoding::Iso8859_1 => "ISO 8859-1", } } } -impl EncodingSaveOrReopenSelector { - pub fn new(window: &mut Window, cx: &mut Context) -> Self { - let delegate = EncodingSaveOrReopenDelegate::new(cx.entity().downgrade()); - - let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx)); - - Self { picker } - } - - pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context) { - workspace.toggle_modal(window, cx, |window, cx| { - EncodingSaveOrReopenSelector::new(window, cx) - }); - } +pub struct EncodingIndicator { + pub encoding: Encoding, + pub workspace: WeakEntity, } -pub struct EncodingSaveOrReopenSelector { - picker: Entity>, -} +pub mod selectors; -impl Focusable for EncodingSaveOrReopenSelector { - fn focus_handle(&self, cx: &ui::App) -> gpui::FocusHandle { - self.picker.focus_handle(cx) - } -} - -impl Render for EncodingSaveOrReopenSelector { - fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl ui::IntoElement { - v_flex().w(rems(34.0)).child(self.picker.clone()) - } -} - -impl ModalView for EncodingSaveOrReopenSelector {} - -impl EventEmitter for EncodingSaveOrReopenSelector {} - -pub struct EncodingSaveOrReopenDelegate { - encoding_selector: WeakEntity, - current_selection: usize, - matches: Vec, - pub actions: Vec, -} - -impl EncodingSaveOrReopenDelegate { - pub fn new(selector: WeakEntity) -> Self { - Self { - encoding_selector: selector, - current_selection: 0, - matches: Vec::new(), - actions: vec![ - StringMatchCandidate::new(0, "Save with encoding"), - StringMatchCandidate::new(1, "Reopen with encoding"), - ], - } - } - - pub fn get_actions(&self) -> (&str, &str) { - (&self.actions[0].string, &self.actions[1].string) - } -} - -impl PickerDelegate for EncodingSaveOrReopenDelegate { - type ListItem = ListItem; - - fn match_count(&self) -> usize { - self.matches.len() - } - - fn selected_index(&self) -> usize { - self.current_selection - } - - fn set_selected_index( - &mut self, - ix: usize, - _window: &mut Window, - _cx: &mut Context>, - ) { - self.current_selection = ix; - } - - fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc { - "Select an action...".into() - } - - fn update_matches( - &mut self, - query: String, - window: &mut Window, - cx: &mut Context>, - ) -> gpui::Task<()> { - let executor = cx.background_executor().clone(); - let actions = self.actions.clone(); - - cx.spawn_in(window, async move |this, cx| { - let matches = if query.is_empty() { - actions - .into_iter() - .enumerate() - .map(|(index, value)| StringMatch { - candidate_id: index, - score: 0.0, - positions: vec![], - string: value.string, - }) - .collect::>() - } else { - fuzzy::match_strings( - &actions, - &query, - false, - false, - 2, - &AtomicBool::new(false), - executor, - ) - .await - }; - - this.update(cx, |picker, cx| { - let delegate = &mut picker.delegate; - delegate.current_selection = matches.len().saturating_sub(1); - delegate.matches = matches; - cx.notify(); - }) - .log_err(); - }) - } - - fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context>) {} - - fn dismissed(&mut self, _window: &mut Window, cx: &mut Context>) { - self.encoding_selector - .update(cx, |_, cx| cx.emit(DismissEvent)) - .log_err(); - } - - fn render_match( - &self, - ix: usize, - selected: bool, - window: &mut Window, - cx: &mut Context>, - ) -> Option { - Some(ListItem::new(ix).child(Label::new(&self.matches[ix].string))) - } -} - -fn get_current_encoding() -> &'static str { - "UTF-8" -} - -impl Render for Encoding { +impl Render for EncodingIndicator { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl ui::IntoElement { - let encoding_indicator = div(); + let status_element = div(); - encoding_indicator.child( + status_element.child( Button::new("encoding", get_current_encoding()) .label_size(LabelSize::Small) .tooltip(Tooltip::text("Select Encoding")) - .on_click(cx.listener(|encoding, _: &ClickEvent, window, cx| { - if let Some(workspace) = match encoding { - Encoding::Utf8(workspace) => workspace.upgrade(), - } { + .on_click(cx.listener(|indicator, _: &ClickEvent, window, cx| { + if let Some(workspace) = indicator.workspace.upgrade() { workspace.update(cx, |workspace, cx| { EncodingSaveOrReopenSelector::toggle(workspace, window, cx) }) @@ -199,7 +47,11 @@ impl Render for Encoding { } } -impl StatusItemView for Encoding { +impl EncodingIndicator { + pub fn get_current_encoding(&self, cx: &mut Context, editor: WeakEntity) {} +} + +impl StatusItemView for EncodingIndicator { fn set_active_pane_item( &mut self, _active_pane_item: Option<&dyn ItemHandle>, diff --git a/crates/encodings/src/selectors.rs b/crates/encodings/src/selectors.rs new file mode 100644 index 0000000000..368f85d584 --- /dev/null +++ b/crates/encodings/src/selectors.rs @@ -0,0 +1,331 @@ +pub mod save_or_reopen { + use gpui::Styled; + use gpui::{AppContext, ParentElement}; + use picker::Picker; + use picker::PickerDelegate; + use std::sync::atomic::AtomicBool; + use util::ResultExt; + + use fuzzy::{StringMatch, StringMatchCandidate}; + use gpui::{DismissEvent, Entity, EventEmitter, Focusable, WeakEntity}; + + use ui::{Context, Label, ListItem, Render, Window, rems, v_flex}; + use workspace::{ModalView, Workspace}; + + pub struct EncodingSaveOrReopenSelector { + picker: Entity>, + } + + impl EncodingSaveOrReopenSelector { + pub fn new(window: &mut Window, cx: &mut Context) -> Self { + let delegate = EncodingSaveOrReopenDelegate::new(cx.entity().downgrade()); + + let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx)); + + Self { picker } + } + + pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context) { + workspace.toggle_modal(window, cx, |window, cx| { + EncodingSaveOrReopenSelector::new(window, cx) + }); + } + } + + impl Focusable for EncodingSaveOrReopenSelector { + fn focus_handle(&self, cx: &ui::App) -> gpui::FocusHandle { + self.picker.focus_handle(cx) + } + } + + impl Render for EncodingSaveOrReopenSelector { + fn render( + &mut self, + _window: &mut Window, + _cx: &mut Context, + ) -> impl ui::IntoElement { + v_flex().w(rems(34.0)).child(self.picker.clone()) + } + } + + impl ModalView for EncodingSaveOrReopenSelector {} + + impl EventEmitter for EncodingSaveOrReopenSelector {} + + pub struct EncodingSaveOrReopenDelegate { + encoding_selector: WeakEntity, + current_selection: usize, + matches: Vec, + pub actions: Vec, + } + + impl EncodingSaveOrReopenDelegate { + pub fn new(selector: WeakEntity) -> Self { + Self { + encoding_selector: selector, + current_selection: 0, + matches: Vec::new(), + actions: vec![ + StringMatchCandidate::new(0, "Save with encoding"), + StringMatchCandidate::new(1, "Reopen with encoding"), + ], + } + } + + pub fn get_actions(&self) -> (&str, &str) { + (&self.actions[0].string, &self.actions[1].string) + } + } + + impl PickerDelegate for EncodingSaveOrReopenDelegate { + type ListItem = ListItem; + + fn match_count(&self) -> usize { + self.matches.len() + } + + fn selected_index(&self) -> usize { + self.current_selection + } + + fn set_selected_index( + &mut self, + ix: usize, + _window: &mut Window, + _cx: &mut Context>, + ) { + self.current_selection = ix; + } + + fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc { + "Select an action...".into() + } + + fn update_matches( + &mut self, + query: String, + window: &mut Window, + cx: &mut Context>, + ) -> gpui::Task<()> { + let executor = cx.background_executor().clone(); + let actions = self.actions.clone(); + + cx.spawn_in(window, async move |this, cx| { + let matches = if query.is_empty() { + actions + .into_iter() + .enumerate() + .map(|(index, value)| StringMatch { + candidate_id: index, + score: 0.0, + positions: vec![], + string: value.string, + }) + .collect::>() + } else { + fuzzy::match_strings( + &actions, + &query, + false, + false, + 2, + &AtomicBool::new(false), + executor, + ) + .await + }; + + this.update(cx, |picker, cx| { + let delegate = &mut picker.delegate; + delegate.current_selection = matches.len().saturating_sub(1); + delegate.matches = matches; + cx.notify(); + }) + .log_err(); + }) + } + + fn confirm( + &mut self, + secondary: bool, + window: &mut Window, + cx: &mut Context>, + ) { + } + + fn dismissed(&mut self, _window: &mut Window, cx: &mut Context>) { + self.encoding_selector + .update(cx, |_, cx| cx.emit(DismissEvent)) + .log_err(); + } + + fn render_match( + &self, + ix: usize, + selected: bool, + window: &mut Window, + cx: &mut Context>, + ) -> Option { + Some(ListItem::new(ix).child(Label::new(&self.matches[ix].string))) + } + } + + pub fn get_current_encoding() -> &'static str { + "UTF-8" + } +} + +pub mod encoding { + use std::sync::atomic::AtomicBool; + + use fuzzy::{StringMatch, StringMatchCandidate}; + use gpui::{ + AppContext, BackgroundExecutor, DismissEvent, Entity, EventEmitter, Focusable, WeakEntity, + }; + use picker::{Picker, PickerDelegate}; + use ui::{Context, Label, ListItem, ParentElement, Render, Styled, Window, rems, v_flex}; + use util::{ResultExt, TryFutureExt}; + use workspace::{ModalView, Workspace}; + + pub struct EncodingSelector { + pub picker: Entity>, + } + + pub struct EncodingSelectorDelegate { + current_selection: usize, + encodings: Vec, + matches: Vec, + selector: WeakEntity, + } + + impl EncodingSelectorDelegate { + pub fn new(selector: WeakEntity) -> EncodingSelectorDelegate { + EncodingSelectorDelegate { + current_selection: 0, + encodings: vec![ + StringMatchCandidate::new(0, "UTF-8"), + StringMatchCandidate::new(1, "ISO 8859-1"), + ], + matches: Vec::new(), + selector, + } + } + } + + impl PickerDelegate for EncodingSelectorDelegate { + type ListItem = ListItem; + + fn match_count(&self) -> usize { + self.matches.len() + } + + fn selected_index(&self) -> usize { + self.current_selection + } + + fn set_selected_index( + &mut self, + ix: usize, + window: &mut Window, + cx: &mut Context>, + ) { + self.current_selection = ix; + } + + fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc { + "Select an encoding...".into() + } + + fn update_matches( + &mut self, + query: String, + window: &mut Window, + cx: &mut Context>, + ) -> gpui::Task<()> { + let executor = cx.background_executor().clone(); + let encodings = self.encodings.clone(); + let current_selection = self.current_selection; + + cx.spawn_in(window, async move |picker, cx| { + let matches: Vec; + + if query.is_empty() { + matches = encodings + .into_iter() + .enumerate() + .map(|(index, value)| StringMatch { + candidate_id: index, + score: 0.0, + positions: Vec::new(), + string: value.string, + }) + .collect(); + } else { + matches = fuzzy::match_strings( + &encodings, + &query, + false, + false, + 0, + &AtomicBool::new(false), + executor, + ) + .await + } + }) + } + + fn confirm( + &mut self, + secondary: bool, + window: &mut Window, + cx: &mut Context>, + ) { + } + + fn dismissed(&mut self, window: &mut Window, cx: &mut Context>) { + self.selector + .update(cx, |_, cx| cx.emit(DismissEvent)) + .log_err(); + } + + fn render_match( + &self, + ix: usize, + selected: bool, + window: &mut Window, + cx: &mut Context>, + ) -> Option { + Some(ListItem::new(ix).child(Label::new(&self.matches[ix].string))) + } + } + + impl EncodingSelector { + pub fn new(window: &mut Window, cx: &mut Context) -> EncodingSelector { + let delegate = EncodingSelectorDelegate::new(cx.entity().downgrade()); + let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx)); + + EncodingSelector { picker: picker } + } + + pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context) { + workspace.toggle_modal(window, cx, |window, cx| EncodingSelector::new(window, cx)); + } + } + + impl EventEmitter for EncodingSelector {} + + impl Focusable for EncodingSelector { + fn focus_handle(&self, cx: &ui::App) -> gpui::FocusHandle { + cx.focus_handle() + } + } + + impl ModalView for EncodingSelector {} + + impl Render for EncodingSelector { + fn render(&mut self, _: &mut Window, _: &mut Context) -> impl ui::IntoElement { + v_flex().w(rems(34.0)).child(self.picker.clone()) + } + } +} diff --git a/crates/fs/Cargo.toml b/crates/fs/Cargo.toml index 1d4161134e..6476c67636 100644 --- a/crates/fs/Cargo.toml +++ b/crates/fs/Cargo.toml @@ -34,6 +34,8 @@ text.workspace = true time.workspace = true util.workspace = true workspace-hack.workspace = true +encoding = "0.2.33" + [target.'cfg(target_os = "macos")'.dependencies] fsevent.workspace = true diff --git a/crates/fs/src/encodings.rs b/crates/fs/src/encodings.rs new file mode 100644 index 0000000000..8fb38ff24f --- /dev/null +++ b/crates/fs/src/encodings.rs @@ -0,0 +1,21 @@ +use encoding::Encoding; + +pub enum CharacterEncoding { + Utf8, + Iso8859_1, + Cp865, +} + +pub fn to_utf8<'a>(input: Vec, encoding: &'a impl encoding::Encoding) -> String { + match encoding.decode(&input, encoding::DecoderTrap::Strict) { + Ok(v) => return v, + Err(_) => panic!(), + } +} + +pub fn to<'a>(input: String, target: &'a impl encoding::Encoding) -> Vec { + match target.encode(&input, encoding::EncoderTrap::Strict) { + Ok(v) => v, + Err(_) => panic!(), + } +} diff --git a/crates/language/Cargo.toml b/crates/language/Cargo.toml index 4ab56d6647..fafd1fdcb7 100644 --- a/crates/language/Cargo.toml +++ b/crates/language/Cargo.toml @@ -70,6 +70,7 @@ util.workspace = true watch.workspace = true workspace-hack.workspace = true diffy = "0.4.2" +encoding = "0.2.33" [dev-dependencies] collections = { workspace = true, features = ["test-support"] } diff --git a/crates/language/src/buffer.rs b/crates/language/src/buffer.rs index 4ddc2b3018..85c24ef0ac 100644 --- a/crates/language/src/buffer.rs +++ b/crates/language/src/buffer.rs @@ -127,6 +127,7 @@ pub struct Buffer { has_unsaved_edits: Cell<(clock::Global, bool)>, change_bits: Vec>>, _subscriptions: Vec, + encoding: &'static dyn encoding::Encoding, } #[derive(Copy, Clone, Debug, PartialEq, Eq)] @@ -958,6 +959,7 @@ impl Buffer { has_conflict: false, change_bits: Default::default(), _subscriptions: Vec::new(), + encoding: encoding::all::UTF_8, } } diff --git a/crates/zed/src/zed.rs b/crates/zed/src/zed.rs index e780d9d5a0..36189c1110 100644 --- a/crates/zed/src/zed.rs +++ b/crates/zed/src/zed.rs @@ -397,7 +397,10 @@ pub fn initialize_workspace( } }); - let encoding_indicator = cx.new(|_cx| encodings::Encoding::Utf8(workspace.weak_handle())); + let encoding_indicator = cx.new(|_cx| encodings::EncodingIndicator { + encoding: encodings::Encoding::Utf8, + workspace: workspace_handle.downgrade(), + }); let cursor_position = cx.new(|_| go_to_line::cursor_position::CursorPosition::new(workspace));