@@ -33,6 +33,8 @@ pub struct Menu<T: Item> {
3333
3434 scroll: usize,
3535 size: (u16, u16),
36+ viewport: (u16, u16),
37+ recalculate: bool,
3638}
3739
3840impl<T: Item> Menu<T> {
@@ -51,6 +53,8 @@ impl<T: Item> Menu<T> {
5153 callback_fn: Box::new(callback_fn),
5254 scroll: 0,
5355 size: (0, 0),
56+ viewport: (0, 0),
57+ recalculate: true,
5458 };
5559
5660 // TODO: scoring on empty input should just use a fastpath
@@ -83,6 +87,7 @@ impl<T: Item> Menu<T> {
8387 // reset cursor position
8488 self.cursor = None;
8589 self.scroll = 0;
90+ self.recalculate = true;
8691 }
8792
8893 pub fn move_up(&mut self) {
@@ -99,6 +104,41 @@ impl<T: Item> Menu<T> {
99104 self.adjust_scroll();
100105 }
101106
107+ fn recalculate_size(&mut self, viewport: (u16, u16)) {
108+ let n = self
109+ .options
110+ .first()
111+ .map(|option| option.row().cells.len())
112+ .unwrap_or_default();
113+ let max_lens = self.options.iter().fold(vec![0; n], |mut acc, option| {
114+ let row = option.row();
115+ // maintain max for each column
116+ for (acc, cell) in acc.iter_mut().zip(row.cells.iter()) {
117+ let width = cell.content.width();
118+ if width > *acc {
119+ *acc = width;
120+ }
121+ }
122+
123+ acc
124+ });
125+ let len = max_lens.iter().sum::<usize>() + n + 1; // +1: reserve some space for scrollbar
126+ let width = len.min(viewport.0 as usize);
127+
128+ self.widths = max_lens
129+ .into_iter()
130+ .map(|len| Constraint::Length(len as u16))
131+ .collect();
132+
133+ let height = self.matches.len().min(10).min(viewport.1 as usize);
134+
135+ self.size = (width as u16, height as u16);
136+
137+ // adjust scroll offsets if size changed
138+ self.adjust_scroll();
139+ self.recalculate = false;
140+ }
141+
102142 fn adjust_scroll(&mut self) {
103143 let win_height = self.size.1 as usize;
104144 if let Some(cursor) = self.cursor {
@@ -221,43 +261,13 @@ impl<T: Item + 'static> Component for Menu<T> {
221261 }
222262
223263 fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
224− let n = self
225− .options
226− .first()
227− .map(|option| option.row().cells.len())
228− .unwrap_or_default();
229− let max_lens = self.options.iter().fold(vec![0; n], |mut acc, option| {
230− let row = option.row();
231− // maintain max for each column
232− for (acc, cell) in acc.iter_mut().zip(row.cells.iter()) {
233− let width = cell.content.width();
234− if width > *acc {
235− *acc = width;
236− }
237− }
238−
239− acc
240− });
241− let len = max_lens.iter().sum::<usize>() + n + 1; // +1: reserve some space for scrollbar
242− let width = len.min(viewport.0 as usize);
243−
244− self.widths = max_lens
245− .into_iter()
246− .map(|len| Constraint::Length(len as u16))
247− .collect();
248−
249− let height = self.options.len().min(10).min(viewport.1 as usize);
250−
251− self.size = (width as u16, height as u16);
252−
253− // adjust scroll offsets if size changed
254− self.adjust_scroll();
264+ if viewport != self.viewport || self.recalculate {
265+ self.recalculate_size(viewport);
266+ }
255267
256268 Some(self.size)
257269 }
258270
259− // TODO: required size should re-trigger when we filter items so we can draw a smaller menu
260−
261271 fn render(&mut self, area: Rect, surface: &mut Surface, cx: &mut Context) {
262272 let theme = &cx.editor.theme;
263273 let style = theme