fresh/app/
menu_context.rs1use super::Editor;
9use crate::view::ui::context_keys;
10
11impl Editor {
12 pub fn menu_context(&self) -> crate::view::ui::MenuContext {
18 self.menu_state.context.clone()
19 }
20
21 pub fn expanded_menu_definitions(&self) -> Vec<fresh_core::menu::Menu> {
25 use crate::config::{MenuConfig, MenuExt};
26
27 let mut menus = MenuConfig::translated_menus();
28 let themes_dir = self.menu_state.themes_dir.clone();
29 for menu in &mut menus {
30 menu.expand_dynamic_items(&themes_dir);
31 }
32 menus
33 }
34
35 pub fn all_menus_expanded(&self) -> Vec<fresh_core::menu::Menu> {
42 use crate::config::MenuExt;
43
44 let themes_dir = self.menu_state.themes_dir.clone();
45 let mut all: Vec<fresh_core::menu::Menu> = match self.expanded_menus_cache.get() {
48 Some(cached) => cached.menus.clone(),
49 None => {
50 let mut m = self.menus.clone();
51 for menu in &mut m.menus {
52 menu.expand_dynamic_items(&themes_dir);
53 }
54 m.menus
55 }
56 };
57 for plugin_menu in &self.menu_state.plugin_menus {
58 let mut menu = plugin_menu.clone();
59 menu.expand_dynamic_items(&themes_dir);
60 all.push(menu);
61 }
62 all
63 }
64
65 pub fn update_menu_context(&mut self) {
68 let line_numbers = self.active_window().is_line_numbers_visible();
70 let line_wrap = self.active_window().is_line_wrap_enabled();
71 let page_view = self.active_window().is_page_view();
72 let file_explorer_visible = self.file_explorer_visible();
73 let file_explorer_focused = self.active_window().is_file_explorer_focused();
74 let mouse_capture = self.active_window_mut().mouse_enabled;
75 let mouse_hover = self.config.editor.mouse_hover_enabled;
76 let inlay_hints = self.config.editor.enable_inlay_hints;
77 let has_buffer = !self
81 .active_window()
82 .buffer_metadata
83 .get(&self.active_buffer())
84 .map(|m| m.synthetic_placeholder)
85 .unwrap_or(false);
86 let has_selection = has_buffer && self.has_active_selection();
87 let can_copy = has_selection
88 || file_explorer_focused
89 || self
90 .file_explorer()
91 .as_ref()
92 .map(|fe| fe.get_selected().is_some())
93 .unwrap_or(false);
94 let can_paste = if file_explorer_focused {
98 self.active_window().file_explorer_clipboard.is_some()
99 } else {
100 has_buffer && self.active_window().file_explorer_clipboard.is_none()
101 };
102 let menu_bar = self.active_window_mut().menu_bar_visible;
103 let vertical_scrollbar = self.config.editor.show_vertical_scrollbar;
104 let horizontal_scrollbar = self.config.editor.show_horizontal_scrollbar;
105
106 let show_hidden = self.active_window().is_file_explorer_showing_hidden();
108 let show_gitignored = self.active_window().is_file_explorer_showing_gitignored();
109
110 let lsp_available = self.active_window().is_lsp_available();
112 let formatter_available = self.active_window().is_formatter_available();
113
114 let session_mode = self.session_mode;
116
117 let scroll_sync = self.active_window().same_buffer_scroll_sync;
119 let has_same_buffer_splits = self.active_window().has_same_buffer_splits();
120
121 let active_keymap: &str = &self.config.active_keybinding_map;
123
124 self.menu_state
126 .context
127 .set(context_keys::HAS_BUFFER, has_buffer)
128 .set(context_keys::KEYMAP_DEFAULT, active_keymap == "default")
129 .set(context_keys::KEYMAP_EMACS, active_keymap == "emacs")
130 .set(context_keys::KEYMAP_VSCODE, active_keymap == "vscode")
131 .set(context_keys::KEYMAP_MACOS_GUI, active_keymap == "macos-gui")
132 .set(context_keys::LINE_NUMBERS, line_numbers)
133 .set(context_keys::LINE_WRAP, line_wrap)
134 .set(context_keys::PAGE_VIEW, page_view)
135 .set(context_keys::COMPOSE_MODE, page_view)
137 .set(context_keys::FILE_EXPLORER, file_explorer_visible)
138 .set(context_keys::FILE_EXPLORER_FOCUSED, file_explorer_focused)
139 .set(context_keys::MOUSE_CAPTURE, mouse_capture)
140 .set(context_keys::MOUSE_HOVER, mouse_hover)
141 .set(context_keys::INLAY_HINTS, inlay_hints)
142 .set(context_keys::LSP_AVAILABLE, lsp_available)
143 .set(context_keys::FILE_EXPLORER_SHOW_HIDDEN, show_hidden)
144 .set(context_keys::FILE_EXPLORER_SHOW_GITIGNORED, show_gitignored)
145 .set(context_keys::HAS_SELECTION, has_selection)
146 .set(context_keys::CAN_COPY, can_copy)
147 .set(context_keys::CAN_PASTE, can_paste)
148 .set(context_keys::MENU_BAR, menu_bar)
149 .set(context_keys::FORMATTER_AVAILABLE, formatter_available)
150 .set(context_keys::SESSION_MODE, session_mode)
151 .set(context_keys::VERTICAL_SCROLLBAR, vertical_scrollbar)
152 .set(context_keys::HORIZONTAL_SCROLLBAR, horizontal_scrollbar)
153 .set(context_keys::SCROLL_SYNC, scroll_sync)
154 .set(context_keys::HAS_SAME_BUFFER_SPLITS, has_same_buffer_splits);
155 }
156}
157
158impl crate::app::window::Window {
159 pub(crate) fn is_line_numbers_visible(&self) -> bool {
161 let (mgr, vs) = self
162 .buffers
163 .splits()
164 .expect("active window must have a populated split layout");
165 vs.get(&mgr.active_split())
166 .map(|vs| vs.show_line_numbers)
167 .unwrap_or(true)
168 }
169
170 pub(crate) fn is_line_wrap_enabled(&self) -> bool {
172 let (mgr, vs) = self
173 .buffers
174 .splits()
175 .expect("active window must have a populated split layout");
176 vs.get(&mgr.active_split())
177 .map(|vs| vs.viewport.line_wrap_enabled)
178 .unwrap_or(false)
179 }
180
181 pub(crate) fn is_page_view(&self) -> bool {
183 let (mgr, vs) = self
184 .buffers
185 .splits()
186 .expect("active window must have a populated split layout");
187 vs.get(&mgr.active_split())
188 .map(|vs| vs.view_mode == crate::state::ViewMode::PageView)
189 .unwrap_or(false)
190 }
191
192 pub(crate) fn is_file_explorer_focused(&self) -> bool {
194 self.key_context == crate::input::keybindings::KeyContext::FileExplorer
195 }
196
197 pub(crate) fn is_file_explorer_showing_hidden(&self) -> bool {
199 self.file_explorer
200 .as_ref()
201 .map(|fe| fe.ignore_patterns().show_hidden())
202 .unwrap_or(false)
203 }
204
205 pub(crate) fn is_file_explorer_showing_gitignored(&self) -> bool {
207 self.file_explorer
208 .as_ref()
209 .map(|fe| fe.ignore_patterns().show_gitignored())
210 .unwrap_or(false)
211 }
212
213 pub(crate) fn is_lsp_available(&self) -> bool {
215 let buffer_id = self.active_buffer();
216
217 if let Some(metadata) = self.buffer_metadata.get(&buffer_id) {
219 if !metadata.lsp_enabled {
220 return false;
221 }
222 } else {
223 return false;
224 }
225
226 self.buffers
228 .get(&buffer_id)
229 .map(|state| self.lsp.is_server_ready(&state.language))
230 .unwrap_or(false)
231 }
232
233 pub(crate) fn has_same_buffer_splits(&self) -> bool {
235 let (mgr, vs) = self
236 .buffers
237 .splits()
238 .expect("active window must have a populated split layout");
239 let active_split = mgr.active_split();
240 let active_buf_id = mgr.buffer_for_split(active_split);
241 if let Some(buf_id) = active_buf_id {
242 vs.keys()
243 .any(|&s| s != active_split && mgr.buffer_for_split(s) == Some(buf_id))
244 } else {
245 false
246 }
247 }
248
249 pub(crate) fn is_formatter_available(&self) -> bool {
251 let buffer_id = self.active_buffer();
252 self.buffers
253 .get(&buffer_id)
254 .and_then(|state| {
255 self.config()
256 .languages
257 .get(&state.language)
258 .and_then(|lc| lc.formatter.as_ref())
259 .map(|_| true)
260 })
261 .unwrap_or(false)
262 }
263}