Add a status indicator to indicate the current file's encoding. When clicked a modal view opens that lets user choose to either reopen or save a file with a particular encoding. The actual implementations are incomplete
This commit is contained in:
parent
b1b60bb7fe
commit
fae2f8ff1c
7 changed files with 249 additions and 1 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -5203,6 +5203,19 @@ dependencies = [
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "encodings"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"fuzzy",
|
||||||
|
"gpui",
|
||||||
|
"language",
|
||||||
|
"picker",
|
||||||
|
"ui",
|
||||||
|
"util",
|
||||||
|
"workspace",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "endi"
|
name = "endi"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -20437,6 +20450,7 @@ dependencies = [
|
||||||
"diagnostics",
|
"diagnostics",
|
||||||
"edit_prediction_button",
|
"edit_prediction_button",
|
||||||
"editor",
|
"editor",
|
||||||
|
"encodings",
|
||||||
"env_logger 0.11.8",
|
"env_logger 0.11.8",
|
||||||
"extension",
|
"extension",
|
||||||
"extension_host",
|
"extension_host",
|
||||||
|
|
|
@ -55,6 +55,7 @@ members = [
|
||||||
"crates/diagnostics",
|
"crates/diagnostics",
|
||||||
"crates/docs_preprocessor",
|
"crates/docs_preprocessor",
|
||||||
"crates/editor",
|
"crates/editor",
|
||||||
|
"crates/encodings",
|
||||||
"crates/eval",
|
"crates/eval",
|
||||||
"crates/explorer_command_injector",
|
"crates/explorer_command_injector",
|
||||||
"crates/extension",
|
"crates/extension",
|
||||||
|
@ -214,7 +215,7 @@ members = [
|
||||||
#
|
#
|
||||||
|
|
||||||
"tooling/workspace-hack",
|
"tooling/workspace-hack",
|
||||||
"tooling/xtask",
|
"tooling/xtask", "crates/encodings",
|
||||||
]
|
]
|
||||||
default-members = ["crates/zed"]
|
default-members = ["crates/zed"]
|
||||||
|
|
||||||
|
@ -309,6 +310,7 @@ icons = { path = "crates/icons" }
|
||||||
image_viewer = { path = "crates/image_viewer" }
|
image_viewer = { path = "crates/image_viewer" }
|
||||||
edit_prediction = { path = "crates/edit_prediction" }
|
edit_prediction = { path = "crates/edit_prediction" }
|
||||||
edit_prediction_button = { path = "crates/edit_prediction_button" }
|
edit_prediction_button = { path = "crates/edit_prediction_button" }
|
||||||
|
encodings = {path = "crates/encodings"}
|
||||||
inspector_ui = { path = "crates/inspector_ui" }
|
inspector_ui = { path = "crates/inspector_ui" }
|
||||||
install_cli = { path = "crates/install_cli" }
|
install_cli = { path = "crates/install_cli" }
|
||||||
jj = { path = "crates/jj" }
|
jj = { path = "crates/jj" }
|
||||||
|
|
17
crates/encodings/Cargo.toml
Normal file
17
crates/encodings/Cargo.toml
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
[package]
|
||||||
|
name = "encodings"
|
||||||
|
version = "0.1.0"
|
||||||
|
publish.workspace = true
|
||||||
|
edition.workspace = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ui.workspace = true
|
||||||
|
workspace.workspace = true
|
||||||
|
gpui.workspace = true
|
||||||
|
picker.workspace = true
|
||||||
|
language.workspace = true
|
||||||
|
util.workspace = true
|
||||||
|
fuzzy.workspace = true
|
||||||
|
|
||||||
|
[lints]
|
||||||
|
workspace = true
|
210
crates/encodings/src/lib.rs
Normal file
210
crates/encodings/src/lib.rs
Normal file
|
@ -0,0 +1,210 @@
|
||||||
|
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 ui::{Clickable, ParentElement};
|
||||||
|
use util::ResultExt;
|
||||||
|
use workspace::{ItemHandle, ModalView, StatusItemView, Workspace};
|
||||||
|
|
||||||
|
pub enum Encoding {
|
||||||
|
Utf8(WeakEntity<Workspace>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encoding {
|
||||||
|
pub fn as_str(&self) -> &str {
|
||||||
|
match &self {
|
||||||
|
Encoding::Utf8(_) => "UTF-8",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 EncodingSaveOrReopenSelector {
|
||||||
|
picker: Entity<Picker<EncodingSaveOrReopenDelegate>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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 {
|
||||||
|
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
|
||||||
|
let encoding_indicator = div();
|
||||||
|
|
||||||
|
encoding_indicator.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(),
|
||||||
|
} {
|
||||||
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
EncodingSaveOrReopenSelector::toggle(workspace, window, cx)
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
})),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl StatusItemView for Encoding {
|
||||||
|
fn set_active_pane_item(
|
||||||
|
&mut self,
|
||||||
|
_active_pane_item: Option<&dyn ItemHandle>,
|
||||||
|
_window: &mut Window,
|
||||||
|
_cx: &mut Context<Self>,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -118,6 +118,7 @@ use crate::persistence::{
|
||||||
model::{DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup},
|
model::{DockData, DockStructure, SerializedItem, SerializedPane, SerializedPaneGroup},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
pub const SERIALIZATION_THROTTLE_TIME: Duration = Duration::from_millis(200);
|
pub const SERIALIZATION_THROTTLE_TIME: Duration = Duration::from_millis(200);
|
||||||
|
|
||||||
static ZED_WINDOW_SIZE: LazyLock<Option<Size<Pixels>>> = LazyLock::new(|| {
|
static ZED_WINDOW_SIZE: LazyLock<Option<Size<Pixels>>> = LazyLock::new(|| {
|
||||||
|
|
|
@ -55,6 +55,7 @@ debugger_tools.workspace = true
|
||||||
debugger_ui.workspace = true
|
debugger_ui.workspace = true
|
||||||
diagnostics.workspace = true
|
diagnostics.workspace = true
|
||||||
editor.workspace = true
|
editor.workspace = true
|
||||||
|
encodings.workspace = true
|
||||||
env_logger.workspace = true
|
env_logger.workspace = true
|
||||||
extension.workspace = true
|
extension.workspace = true
|
||||||
extension_host.workspace = true
|
extension_host.workspace = true
|
||||||
|
|
|
@ -397,6 +397,8 @@ pub fn initialize_workspace(
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let encoding_indicator = cx.new(|_cx| encodings::Encoding::Utf8(workspace.weak_handle()));
|
||||||
|
|
||||||
let cursor_position =
|
let cursor_position =
|
||||||
cx.new(|_| go_to_line::cursor_position::CursorPosition::new(workspace));
|
cx.new(|_| go_to_line::cursor_position::CursorPosition::new(workspace));
|
||||||
workspace.status_bar().update(cx, |status_bar, cx| {
|
workspace.status_bar().update(cx, |status_bar, cx| {
|
||||||
|
@ -409,6 +411,7 @@ pub fn initialize_workspace(
|
||||||
status_bar.add_right_item(active_toolchain_language, window, cx);
|
status_bar.add_right_item(active_toolchain_language, window, cx);
|
||||||
status_bar.add_right_item(vim_mode_indicator, window, cx);
|
status_bar.add_right_item(vim_mode_indicator, window, cx);
|
||||||
status_bar.add_right_item(cursor_position, window, cx);
|
status_bar.add_right_item(cursor_position, window, cx);
|
||||||
|
status_bar.add_right_item(encoding_indicator, window, cx);
|
||||||
status_bar.add_right_item(image_info, window, cx);
|
status_bar.add_right_item(image_info, window, cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue