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.
This commit is contained in:
parent
fae2f8ff1c
commit
5723987b59
10 changed files with 453 additions and 191 deletions
68
Cargo.lock
generated
68
Cargo.lock
generated
|
@ -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",
|
||||
|
|
16
Cargo.toml
16
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 }
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Workspace>),
|
||||
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<EncodingSaveOrReopenSelector>) -> 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>) {
|
||||
workspace.toggle_modal(window, cx, |window, cx| {
|
||||
EncodingSaveOrReopenSelector::new(window, cx)
|
||||
});
|
||||
}
|
||||
pub struct EncodingIndicator {
|
||||
pub encoding: Encoding,
|
||||
pub workspace: WeakEntity<Workspace>,
|
||||
}
|
||||
|
||||
pub struct EncodingSaveOrReopenSelector {
|
||||
picker: Entity<Picker<EncodingSaveOrReopenDelegate>>,
|
||||
}
|
||||
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<Self>) -> impl ui::IntoElement {
|
||||
v_flex().w(rems(34.0)).child(self.picker.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl ModalView for EncodingSaveOrReopenSelector {}
|
||||
|
||||
impl EventEmitter<DismissEvent> for EncodingSaveOrReopenSelector {}
|
||||
|
||||
pub struct EncodingSaveOrReopenDelegate {
|
||||
encoding_selector: WeakEntity<EncodingSaveOrReopenSelector>,
|
||||
current_selection: usize,
|
||||
matches: Vec<StringMatch>,
|
||||
pub actions: Vec<StringMatchCandidate>,
|
||||
}
|
||||
|
||||
impl EncodingSaveOrReopenDelegate {
|
||||
pub fn new(selector: WeakEntity<EncodingSaveOrReopenSelector>) -> 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<Picker<Self>>,
|
||||
) {
|
||||
self.current_selection = ix;
|
||||
}
|
||||
|
||||
fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc<str> {
|
||||
"Select an action...".into()
|
||||
}
|
||||
|
||||
fn update_matches(
|
||||
&mut self,
|
||||
query: String,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> 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::<Vec<StringMatch>>()
|
||||
} 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<Picker<Self>>) {}
|
||||
|
||||
fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||
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<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
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<Self>) -> 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<Self>, editor: WeakEntity<Editor>) {}
|
||||
}
|
||||
|
||||
impl StatusItemView for EncodingIndicator {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
_active_pane_item: Option<&dyn ItemHandle>,
|
||||
|
|
331
crates/encodings/src/selectors.rs
Normal file
331
crates/encodings/src/selectors.rs
Normal file
|
@ -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<Picker<EncodingSaveOrReopenDelegate>>,
|
||||
}
|
||||
|
||||
impl EncodingSaveOrReopenSelector {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<EncodingSaveOrReopenSelector>) -> 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>) {
|
||||
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<Self>,
|
||||
) -> impl ui::IntoElement {
|
||||
v_flex().w(rems(34.0)).child(self.picker.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl ModalView for EncodingSaveOrReopenSelector {}
|
||||
|
||||
impl EventEmitter<DismissEvent> for EncodingSaveOrReopenSelector {}
|
||||
|
||||
pub struct EncodingSaveOrReopenDelegate {
|
||||
encoding_selector: WeakEntity<EncodingSaveOrReopenSelector>,
|
||||
current_selection: usize,
|
||||
matches: Vec<StringMatch>,
|
||||
pub actions: Vec<StringMatchCandidate>,
|
||||
}
|
||||
|
||||
impl EncodingSaveOrReopenDelegate {
|
||||
pub fn new(selector: WeakEntity<EncodingSaveOrReopenSelector>) -> 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<Picker<Self>>,
|
||||
) {
|
||||
self.current_selection = ix;
|
||||
}
|
||||
|
||||
fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc<str> {
|
||||
"Select an action...".into()
|
||||
}
|
||||
|
||||
fn update_matches(
|
||||
&mut self,
|
||||
query: String,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> 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::<Vec<StringMatch>>()
|
||||
} 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<Picker<Self>>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||
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<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
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<Picker<EncodingSelectorDelegate>>,
|
||||
}
|
||||
|
||||
pub struct EncodingSelectorDelegate {
|
||||
current_selection: usize,
|
||||
encodings: Vec<StringMatchCandidate>,
|
||||
matches: Vec<StringMatch>,
|
||||
selector: WeakEntity<EncodingSelector>,
|
||||
}
|
||||
|
||||
impl EncodingSelectorDelegate {
|
||||
pub fn new(selector: WeakEntity<EncodingSelector>) -> 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<Picker<Self>>,
|
||||
) {
|
||||
self.current_selection = ix;
|
||||
}
|
||||
|
||||
fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc<str> {
|
||||
"Select an encoding...".into()
|
||||
}
|
||||
|
||||
fn update_matches(
|
||||
&mut self,
|
||||
query: String,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> 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<StringMatch>;
|
||||
|
||||
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<Picker<Self>>,
|
||||
) {
|
||||
}
|
||||
|
||||
fn dismissed(&mut self, window: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||
self.selector
|
||||
.update(cx, |_, cx| cx.emit(DismissEvent))
|
||||
.log_err();
|
||||
}
|
||||
|
||||
fn render_match(
|
||||
&self,
|
||||
ix: usize,
|
||||
selected: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
Some(ListItem::new(ix).child(Label::new(&self.matches[ix].string)))
|
||||
}
|
||||
}
|
||||
|
||||
impl EncodingSelector {
|
||||
pub fn new(window: &mut Window, cx: &mut Context<EncodingSelector>) -> 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>) {
|
||||
workspace.toggle_modal(window, cx, |window, cx| EncodingSelector::new(window, cx));
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> 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<Self>) -> impl ui::IntoElement {
|
||||
v_flex().w(rems(34.0)).child(self.picker.clone())
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
|
21
crates/fs/src/encodings.rs
Normal file
21
crates/fs/src/encodings.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use encoding::Encoding;
|
||||
|
||||
pub enum CharacterEncoding {
|
||||
Utf8,
|
||||
Iso8859_1,
|
||||
Cp865,
|
||||
}
|
||||
|
||||
pub fn to_utf8<'a>(input: Vec<u8>, 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<u8> {
|
||||
match target.encode(&input, encoding::EncoderTrap::Strict) {
|
||||
Ok(v) => v,
|
||||
Err(_) => panic!(),
|
||||
}
|
||||
}
|
|
@ -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"] }
|
||||
|
|
|
@ -127,6 +127,7 @@ pub struct Buffer {
|
|||
has_unsaved_edits: Cell<(clock::Global, bool)>,
|
||||
change_bits: Vec<rc::Weak<Cell<bool>>>,
|
||||
_subscriptions: Vec<gpui::Subscription>,
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue