Repository: helix-editor/helix
File: helix-term/src/ui/popup.rs (Rust)

Code before:
            }
        }
        // for some events, we want to process them but send ignore, specifically all input except
        // tab/enter/ctrl-k or whatever will confirm the selection/ ctrl-n/ctrl-p for scroll.
    }

    fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
        let max_width = 120.min(viewport.0);
        let max_height = 26.min(viewport.1.saturating_sub(2)); // add some spacing in the viewport

        let inner = Rect::new(0, 0, max_width, max_height).inner(&self.margin);

        let (width, height) = self
            .contents
            .required_size((inner.width, inner.height))
            .expect("Component needs required_size implemented in order to be embedded in a popup");

        let size = (
            (width + self.margin.width()).min(max_width),
            (height + self.margin.height()).min(max_height),
        );

        Some(size)
    }

    fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut Context) {
        let area = self.area(viewport, cx.editor);
        self.area = area;

        let child_size = self
            .contents
            .required_size((area.width, area.width))
            .expect("Component needs required_size implemented in order to be embedded in a popup");

        let max_offset = child_size.1.saturating_sub(area.height) as usize;
        let half_page_size = (area.height / 2) as usize;
        let scroll = max_offset.min(self.scroll_half_pages * half_page_size);
        if half_page_size > 0 {
            self.scroll_half_pages = scroll / half_page_size;
        }
        cx.scroll = Some(scroll);

        // clear area
        let background = cx.editor.theme.get("ui.popup");
        surface.clear_with(area, background);

        let render_borders = cx.editor.popup_border();

        let inner = if self
            .contents
            .type_name()

Review comment (bug):
If you look at the definition of required size (in the component trait) it says you should not be clamping the size to the size of the viewport. I would like to follow the documented definition of the function. The reason the old code was buggy and confusing is because people did adhox things that kind of worked but didn't follow the actual api. 

Code after:

                if self.auto_close {
                    if let EventResult::Ignored(None) = contents_event_result {
                        return EventResult::Ignored(Some(close_fn));
                    }
                }

                contents_event_result
            }
        }
        // for some events, we want to process them but send ignore, specifically all input except
        // tab/enter/ctrl-k or whatever will confirm the selection/ ctrl-n/ctrl-p for scroll.
    }

    fn required_size(&mut self, _viewport: (u16, u16)) -> Option<(u16, u16)> {
        const MAX_WIDTH: u16 = 120;
        const MAX_HEIGHT: u16 = 26;

        let inner = Rect::new(0, 0, MAX_WIDTH, MAX_HEIGHT).inner(&self.margin);

        let (width, height) = self
            .contents
            .required_size((inner.width, inner.height))
            .expect("Component needs required_size implemented in order to be embedded in a popup");

        let size = (
            (width + self.margin.width()).min(MAX_WIDTH),
            (height + self.margin.height()).min(MAX_HEIGHT),
        );

        Some(size)
    }

    fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut Context) {
        let child_size = self
            .contents
            .required_size((viewport.width, viewport.height))
            .expect("Component needs required_size implemented in order to be embedded in a popup");

        let area = self.area_internal(viewport, cx.editor, child_size);
        self.area = area;

        let max_offset = child_size.1.saturating_sub(area.height) as usize;
        let half_page_size = (area.height / 2) as usize;
        let scroll = max_offset.min(self.scroll_half_pages * half_page_size);
        if half_page_size > 0 {
            self.scroll_half_pages = scroll / half_page_size;
        }
        cx.scroll = Some(scroll);

        // clear area