aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--helix-term/src/ui/completion.rs45
-rw-r--r--helix-term/src/ui/markdown.rs28
-rw-r--r--helix-term/src/ui/popup.rs50
3 files changed, 84 insertions, 39 deletions
diff --git a/helix-term/src/ui/completion.rs b/helix-term/src/ui/completion.rs
index 90657764..6c9e3a80 100644
--- a/helix-term/src/ui/completion.rs
+++ b/helix-term/src/ui/completion.rs
@@ -262,8 +262,7 @@ impl Component for Completion {
.cursor(doc.text().slice(..));
let cursor_pos = (helix_core::coords_at_pos(doc.text().slice(..), cursor_pos).row
- view.offset.row) as u16;
-
- let mut doc = match &option.documentation {
+ let mut markdown_doc = match &option.documentation {
Some(lsp::Documentation::String(contents))
| Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
kind: lsp::MarkupKind::PlainText,
@@ -311,24 +310,42 @@ impl Component for Completion {
None => return,
};
- let half = area.height / 2;
- let height = 15.min(half);
- // we want to make sure the cursor is visible (not hidden behind the documentation)
- let y = if cursor_pos + area.y
- >= (cx.editor.tree.area().height - height - 2/* statusline + commandline */)
- {
- 0
+ let (popup_x, popup_y) = self.popup.get_rel_position(area, cx);
+ let (popup_width, _popup_height) = self.popup.get_size();
+ let mut width = area
+ .width
+ .saturating_sub(popup_x)
+ .saturating_sub(popup_width);
+ let area = if width > 30 {
+ let mut height = area.height.saturating_sub(popup_y);
+ let x = popup_x + popup_width;
+ let y = popup_y;
+
+ if let Some((rel_width, rel_height)) = markdown_doc.required_size((width, height)) {
+ width = rel_width;
+ height = rel_height;
+ }
+ Rect::new(x, y, width, height)
} else {
- // -2 to subtract command line + statusline. a bit of a hack, because of splits.
- area.height.saturating_sub(height).saturating_sub(2)
- };
+ let half = area.height / 2;
+ let height = 15.min(half);
+ // we want to make sure the cursor is visible (not hidden behind the documentation)
+ let y = if cursor_pos + area.y
+ >= (cx.editor.tree.area().height - height - 2/* statusline + commandline */)
+ {
+ 0
+ } else {
+ // -2 to subtract command line + statusline. a bit of a hack, because of splits.
+ area.height.saturating_sub(height).saturating_sub(2)
+ };
- let area = Rect::new(0, y, area.width, height);
+ Rect::new(0, y, area.width, height)
+ };
// clear area
let background = cx.editor.theme.get("ui.popup");
surface.clear_with(area, background);
- doc.render(area, surface, cx);
+ markdown_doc.render(area, surface, cx);
}
}
}
diff --git a/helix-term/src/ui/markdown.rs b/helix-term/src/ui/markdown.rs
index 28542cdc..87b35a2d 100644
--- a/helix-term/src/ui/markdown.rs
+++ b/helix-term/src/ui/markdown.rs
@@ -215,10 +215,30 @@ impl Component for Markdown {
}
fn required_size(&mut self, viewport: (u16, u16)) -> Option<(u16, u16)> {
- let contents = parse(&self.contents, None, &self.config_loader);
let padding = 2;
- let width = std::cmp::min(contents.width() as u16 + padding, viewport.0);
- let height = std::cmp::min(contents.height() as u16 + padding, viewport.1);
- Some((width, height))
+ if padding >= viewport.1 || padding >= viewport.0 {
+ return None;
+ }
+ let contents = parse(&self.contents, None, &self.config_loader);
+ let max_text_width = (viewport.0 - padding).min(120);
+ let mut text_width = 0;
+ let mut height = padding;
+ for content in contents {
+ height += 1;
+ let content_width = content.width() as u16;
+ if content_width > max_text_width {
+ text_width = max_text_width;
+ height += content_width / max_text_width;
+ } else if content_width > text_width {
+ text_width = content_width;
+ }
+
+ if height >= viewport.1 {
+ height = viewport.1;
+ break;
+ }
+ }
+
+ Some((text_width + padding, height))
}
}
diff --git a/helix-term/src/ui/popup.rs b/helix-term/src/ui/popup.rs
index e126c845..846cefb8 100644
--- a/helix-term/src/ui/popup.rs
+++ b/helix-term/src/ui/popup.rs
@@ -31,6 +31,33 @@ impl<T: Component> Popup<T> {
self.position = pos;
}
+ pub fn get_rel_position(&mut self, viewport: Rect, cx: &Context) -> (u16, u16) {
+ let position = self
+ .position
+ .get_or_insert_with(|| cx.editor.cursor().0.unwrap_or_default());
+
+ let (width, height) = self.size;
+
+ // -- make sure frame doesn't stick out of bounds
+ let mut rel_x = position.col as u16;
+ let rel_y = position.row as u16;
+ if viewport.width <= rel_x + width {
+ rel_x = rel_x.saturating_sub((rel_x + width).saturating_sub(viewport.width));
+ };
+
+ // TODO: be able to specify orientation preference. We want above for most popups, below
+ // for menus/autocomplete.
+ if height <= rel_y {
+ (rel_x, rel_y.saturating_sub(height)) // position above point
+ } else {
+ (rel_x, rel_y + 1) // position below point
+ }
+ }
+
+ pub fn get_size(&self) -> (u16, u16) {
+ (self.size.0, self.size.1)
+ }
+
pub fn scroll(&mut self, offset: usize, direction: bool) {
if direction {
self.scroll += offset;
@@ -108,29 +135,10 @@ impl<T: Component> Component for Popup<T> {
fn render(&mut self, viewport: Rect, surface: &mut Surface, cx: &mut Context) {
cx.scroll = Some(self.scroll);
- let position = self
- .position
- .get_or_insert_with(|| cx.editor.cursor().0.unwrap_or_default());
-
- let (width, height) = self.size;
-
- // -- make sure frame doesn't stick out of bounds
- let mut rel_x = position.col as u16;
- let mut rel_y = position.row as u16;
- if viewport.width <= rel_x + width {
- rel_x = rel_x.saturating_sub((rel_x + width).saturating_sub(viewport.width));
- };
-
- // TODO: be able to specify orientation preference. We want above for most popups, below
- // for menus/autocomplete.
- if height <= rel_y {
- rel_y = rel_y.saturating_sub(height) // position above point
- } else {
- rel_y += 1 // position below point
- }
+ let (rel_x, rel_y) = self.get_rel_position(viewport, cx);
// clip to viewport
- let area = viewport.intersection(Rect::new(rel_x, rel_y, width, height));
+ let area = viewport.intersection(Rect::new(rel_x, rel_y, self.size.0, self.size.1));
// clear area
let background = cx.editor.theme.get("ui.popup");