@@ -381,104 +381,106 @@ impl Component for Completion {
381 self.popup.render(area, surface, cx);
381 self.popup.render(area, surface, cx);
383 // if we have a selection, render a markdown popup on top/below with info
383 // if we have a selection, render a markdown popup on top/below with info
384− if let Some(option) = self.popup.contents().selection() {
384+ let option = match self.popup.contents().selection() {
385+ Some(option) => option,
385− // need to render:
388+ // need to render:
386− // option.detail
389+ // option.detail
388− // option.documentation
391+ // option.documentation
390− let (view, doc) = current!(cx.editor);
393+ let (view, doc) = current!(cx.editor);
391− let language = doc.language_name().unwrap_or("");
394+ let language = doc.language_name().unwrap_or("");
392− let text = doc.text().slice(..);
395+ let text = doc.text().slice(..);
393− let cursor_pos = doc.selection(view.id).primary().cursor(text);
396+ let cursor_pos = doc.selection(view.id).primary().cursor(text);
394− let coords = view
397+ let coords = view
395− .screen_coords_at_pos(doc, text, cursor_pos)
398+ .screen_coords_at_pos(doc, text, cursor_pos)
396− .expect("cursor must be in view");
399+ .expect("cursor must be in view");
397− let cursor_pos = coords.row as u16;
400+ let cursor_pos = coords.row as u16;
399− let mut markdown_doc = match &option.documentation {
402+ let mut markdown_doc = match &option.documentation {
400− Some(lsp::Documentation::String(contents))
403+ Some(lsp::Documentation::String(contents))
401− | Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
404+ | Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
402− kind: lsp::MarkupKind::PlainText,
405+ kind: lsp::MarkupKind::PlainText,
403− value: contents,
406+ value: contents,
404− })) => {
407+ })) => {
405− // TODO: convert to wrapped text
408+ // TODO: convert to wrapped text
406− Markdown::new(
409+ Markdown::new(
407− format!(
410+ format!(
408− "```{}\n{}\n```\n{}",
411+ "```{}\n{}\n```\n{}",
409− language,
412+ language,
410− option.detail.as_deref().unwrap_or_default(),
413+ option.detail.as_deref().unwrap_or_default(),
411− contents
414+ contents
413− cx.editor.syn_loader.clone(),
416+ cx.editor.syn_loader.clone(),
416− Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
419+ Some(lsp::Documentation::MarkupContent(lsp::MarkupContent {
417− kind: lsp::MarkupKind::Markdown,
420+ kind: lsp::MarkupKind::Markdown,
418− value: contents,
421+ value: contents,
419− })) => {
422+ })) => {
420− // TODO: set language based on doc scope
423+ // TODO: set language based on doc scope
421− if let Some(detail) = &option.detail.as_deref() {
424+ if let Some(detail) = &option.detail.as_deref() {
423− format!("```{}\n{}\n```\n{}", language, detail, contents),
424− cx.editor.syn_loader.clone(),
427− Markdown::new(contents.to_string(), cx.editor.syn_loader.clone())
430− None if option.detail.is_some() => {
431− // TODO: copied from above
433− // TODO: set language based on doc scope
434 Markdown::new(
425 Markdown::new(
437− language,
426+ format!("```{}\n{}\n```\n{}", language, detail, contents),
438− option.detail.as_deref().unwrap_or_default(),
440 cx.editor.syn_loader.clone(),
427 cx.editor.syn_loader.clone(),
430+ Markdown::new(contents.to_string(), cx.editor.syn_loader.clone())
433+ None if option.detail.is_some() => {
434+ // TODO: copied from above
436+ // TODO: set language based on doc scope
441+ option.detail.as_deref().unwrap_or_default(),
443+ cx.editor.syn_loader.clone(),
443− None => return,
446+ None => return,
446− let (popup_x, popup_y) = self.popup.get_rel_position(area, cx);
449+ let (popup_x, popup_y) = self.popup.get_rel_position(area, cx);
447− let (popup_width, _popup_height) = self.popup.get_size();
450+ let (popup_width, _popup_height) = self.popup.get_size();
448− let mut width = area
451+ let mut width = area
450− .saturating_sub(popup_x)
453+ .saturating_sub(popup_x)
451− .saturating_sub(popup_width);
454+ .saturating_sub(popup_width);
452− let area = if width > 30 {
455+ let area = if width > 30 {
453− let mut height = area.height.saturating_sub(popup_y);
456+ let mut height = area.height.saturating_sub(popup_y);
454− let x = popup_x + popup_width;
457+ let x = popup_x + popup_width;
455− let y = popup_y;
458+ let y = popup_y;
457− if let Some((rel_width, rel_height)) = markdown_doc.required_size((width, height)) {
460+ if let Some((rel_width, rel_height)) = markdown_doc.required_size((width, height)) {
458− width = rel_width.min(width);
461+ width = rel_width.min(width);
459− height = rel_height.min(height);
462+ height = rel_height.min(height);
461− Rect::new(x, y, width, height)
464+ Rect::new(x, y, width, height)
466+ let half = area.height / 2;
467+ let height = 15.min(half);
468+ // we want to make sure the cursor is visible (not hidden behind the documentation)
469+ let y = if cursor_pos + area.y
470+ >= (cx.editor.tree.area().height - height - 2/* statusline + commandline */)
463− let half = area.height / 2;
464− let height = 15.min(half);
465− // we want to make sure the cursor is visible (not hidden behind the documentation)
466− let y = if cursor_pos + area.y
467− >= (cx.editor.tree.area().height - height - 2/* statusline + commandline */)
471− // -2 to subtract command line + statusline. a bit of a hack, because of splits.
474+ // -2 to subtract command line + statusline. a bit of a hack, because of splits.
472− area.height.saturating_sub(height).saturating_sub(2)
475+ area.height.saturating_sub(height).saturating_sub(2)
475− Rect::new(0, y, area.width, height)
478+ Rect::new(0, y, area.width, height)
478− // clear area
481+ // clear area
479− let background = cx.editor.theme.get("ui.popup");
482+ let background = cx.editor.theme.get("ui.popup");
480− surface.clear_with(area, background);
483+ surface.clear_with(area, background);
481− markdown_doc.render(area, surface, cx);
484+ markdown_doc.render(area, surface, cx);