lib.rs

  1use std::sync::Weak;
  2use std::sync::atomic::AtomicBool;
  3
  4use fuzzy::{StringMatch, StringMatchCandidate};
  5use gpui::{AppContext, ClickEvent, DismissEvent, Entity, EventEmitter, Focusable, WeakEntity};
  6use language::Buffer;
  7use picker::{Picker, PickerDelegate};
  8use ui::{
  9    Button, ButtonCommon, Context, Label, LabelSize, ListItem, Render, Styled, Tooltip, Window,
 10    div, rems, v_flex,
 11};
 12use ui::{Clickable, ParentElement};
 13use util::ResultExt;
 14use workspace::{ItemHandle, ModalView, StatusItemView, Workspace};
 15
 16pub enum Encoding {
 17    Utf8(WeakEntity<Workspace>),
 18}
 19
 20impl Encoding {
 21    pub fn as_str(&self) -> &str {
 22        match &self {
 23            Encoding::Utf8(_) => "UTF-8",
 24        }
 25    }
 26}
 27
 28impl EncodingSaveOrReopenSelector {
 29    pub fn new(window: &mut Window, cx: &mut Context<EncodingSaveOrReopenSelector>) -> Self {
 30        let delegate = EncodingSaveOrReopenDelegate::new(cx.entity().downgrade());
 31
 32        let picker = cx.new(|cx| Picker::uniform_list(delegate, window, cx));
 33
 34        Self { picker }
 35    }
 36
 37    pub fn toggle(workspace: &mut Workspace, window: &mut Window, cx: &mut Context<Workspace>) {
 38        workspace.toggle_modal(window, cx, |window, cx| {
 39            EncodingSaveOrReopenSelector::new(window, cx)
 40        });
 41    }
 42}
 43
 44pub struct EncodingSaveOrReopenSelector {
 45    picker: Entity<Picker<EncodingSaveOrReopenDelegate>>,
 46}
 47
 48impl Focusable for EncodingSaveOrReopenSelector {
 49    fn focus_handle(&self, cx: &ui::App) -> gpui::FocusHandle {
 50        self.picker.focus_handle(cx)
 51    }
 52}
 53
 54impl Render for EncodingSaveOrReopenSelector {
 55    fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl ui::IntoElement {
 56        v_flex().w(rems(34.0)).child(self.picker.clone())
 57    }
 58}
 59
 60impl ModalView for EncodingSaveOrReopenSelector {}
 61
 62impl EventEmitter<DismissEvent> for EncodingSaveOrReopenSelector {}
 63
 64pub struct EncodingSaveOrReopenDelegate {
 65    encoding_selector: WeakEntity<EncodingSaveOrReopenSelector>,
 66    current_selection: usize,
 67    matches: Vec<StringMatch>,
 68    pub actions: Vec<StringMatchCandidate>,
 69}
 70
 71impl EncodingSaveOrReopenDelegate {
 72    pub fn new(selector: WeakEntity<EncodingSaveOrReopenSelector>) -> Self {
 73        Self {
 74            encoding_selector: selector,
 75            current_selection: 0,
 76            matches: Vec::new(),
 77            actions: vec![
 78                StringMatchCandidate::new(0, "Save with encoding"),
 79                StringMatchCandidate::new(1, "Reopen with encoding"),
 80            ],
 81        }
 82    }
 83
 84    pub fn get_actions(&self) -> (&str, &str) {
 85        (&self.actions[0].string, &self.actions[1].string)
 86    }
 87}
 88
 89impl PickerDelegate for EncodingSaveOrReopenDelegate {
 90    type ListItem = ListItem;
 91
 92    fn match_count(&self) -> usize {
 93        self.matches.len()
 94    }
 95
 96    fn selected_index(&self) -> usize {
 97        self.current_selection
 98    }
 99
100    fn set_selected_index(
101        &mut self,
102        ix: usize,
103        _window: &mut Window,
104        _cx: &mut Context<Picker<Self>>,
105    ) {
106        self.current_selection = ix;
107    }
108
109    fn placeholder_text(&self, _window: &mut Window, _cx: &mut ui::App) -> std::sync::Arc<str> {
110        "Select an action...".into()
111    }
112
113    fn update_matches(
114        &mut self,
115        query: String,
116        window: &mut Window,
117        cx: &mut Context<Picker<Self>>,
118    ) -> gpui::Task<()> {
119        let executor = cx.background_executor().clone();
120        let actions = self.actions.clone();
121
122        cx.spawn_in(window, async move |this, cx| {
123            let matches = if query.is_empty() {
124                actions
125                    .into_iter()
126                    .enumerate()
127                    .map(|(index, value)| StringMatch {
128                        candidate_id: index,
129                        score: 0.0,
130                        positions: vec![],
131                        string: value.string,
132                    })
133                    .collect::<Vec<StringMatch>>()
134            } else {
135                fuzzy::match_strings(
136                    &actions,
137                    &query,
138                    false,
139                    false,
140                    2,
141                    &AtomicBool::new(false),
142                    executor,
143                )
144                .await
145            };
146
147            this.update(cx, |picker, cx| {
148                let delegate = &mut picker.delegate;
149                delegate.current_selection = matches.len().saturating_sub(1);
150                delegate.matches = matches;
151                cx.notify();
152            })
153            .log_err();
154        })
155    }
156
157    fn confirm(&mut self, secondary: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {}
158
159    fn dismissed(&mut self, _window: &mut Window, cx: &mut Context<Picker<Self>>) {
160        self.encoding_selector
161            .update(cx, |_, cx| cx.emit(DismissEvent))
162            .log_err();
163    }
164
165    fn render_match(
166        &self,
167        ix: usize,
168        selected: bool,
169        window: &mut Window,
170        cx: &mut Context<Picker<Self>>,
171    ) -> Option<Self::ListItem> {
172        Some(ListItem::new(ix).child(Label::new(&self.matches[ix].string)))
173    }
174}
175
176fn get_current_encoding() -> &'static str {
177    "UTF-8"
178}
179
180impl Render for Encoding {
181    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl ui::IntoElement {
182        let encoding_indicator = div();
183
184        encoding_indicator.child(
185            Button::new("encoding", get_current_encoding())
186                .label_size(LabelSize::Small)
187                .tooltip(Tooltip::text("Select Encoding"))
188                .on_click(cx.listener(|encoding, _: &ClickEvent, window, cx| {
189                    if let Some(workspace) = match encoding {
190                        Encoding::Utf8(workspace) => workspace.upgrade(),
191                    } {
192                        workspace.update(cx, |workspace, cx| {
193                            EncodingSaveOrReopenSelector::toggle(workspace, window, cx)
194                        })
195                    } else {
196                    }
197                })),
198        )
199    }
200}
201
202impl StatusItemView for Encoding {
203    fn set_active_pane_item(
204        &mut self,
205        _active_pane_item: Option<&dyn ItemHandle>,
206        _window: &mut Window,
207        _cx: &mut Context<Self>,
208    ) {
209    }
210}