Skip to content

Commit

Permalink
helix-view: completion-item-kind support for path completion
Browse files Browse the repository at this point in the history
  • Loading branch information
nferhat committed Nov 29, 2024
1 parent 4303494 commit 8cb535a
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 104 deletions.
42 changes: 23 additions & 19 deletions helix-term/src/ui/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use crate::ui::{menu, Markdown, Menu, Popup, PromptEvent};
use helix_lsp::{lsp, util, OffsetEncoding};

pub struct CompletionData {
completion_item_kinds: Arc<HashMap<lsp::CompletionItemKind, CompletionItemKindStyle>>,
completion_item_kinds: Arc<HashMap<&'static str, CompletionItemKindStyle>>,
default_style: Style,
}

Expand Down Expand Up @@ -108,30 +108,34 @@ impl menu::Item for CompletionItem {
CompletionItem::Lsp(LspCompletionItem { item, .. }) => {
// If the user specified a custom kind text, use that. It will cause an allocation
// though it should not have much impact since its pretty short strings
if let Some(kind_style) = item
.kind
// NOTE: This table gets populated with default text as well.
.and_then(|kind| data.completion_item_kinds.get(&kind))
{
let kind_name = completion_item_kind_name(item.kind).unwrap_or_else(|| {
log::error!("Got invalid LSP completion item kind: {:?}", item.kind);
""
});

if let Some(kind_style) = data.completion_item_kinds.get(kind_name) {
let style = kind_style.style.unwrap_or(data.default_style);
if let Some(text) = kind_style.text.clone() {
menu::Cell::from(Span::styled(text, style))
if let Some(text) = kind_style.text.as_ref() {
menu::Cell::from(Span::styled(text.clone(), style))
} else {
let text = completion_item_kind_name(item.kind).unwrap_or_else(|| {
log::error!("Got invalid LSP completion item kind: {:?}", item.kind);
""
});
menu::Cell::from(Span::styled(text, style))
menu::Cell::from(Span::styled(kind_name, style))
}
} else {
let text = completion_item_kind_name(item.kind).unwrap_or_else(|| {
log::error!("Got invalid LSP completion item kind: {:?}", item.kind);
""
});
menu::Cell::from(Span::styled(text, data.default_style))
menu::Cell::from(Span::styled(kind_name, data.default_style))
}
}
CompletionItem::Other(core::CompletionItem { kind, .. }) => {
if let Some(kind_style) = data.completion_item_kinds.get(kind.as_ref()) {
let style = kind_style.style.unwrap_or(data.default_style);
if let Some(text) = kind_style.text.as_ref() {
menu::Cell::from(Span::styled(text.clone(), style))
} else {
menu::Cell::from(Span::styled(kind.as_ref(), style))
}
} else {
menu::Cell::from(Span::styled(kind.as_ref(), data.default_style))
}
}
CompletionItem::Other(core::CompletionItem { kind, .. }) => menu::Cell::from(&**kind),
};

menu::Row::new([label_cell, kind_cell])
Expand Down
124 changes: 39 additions & 85 deletions helix-view/src/editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,8 +306,7 @@ pub struct Config {
pub completion_replace: bool,
/// The completion item kind text to display in the completion menu. Leave kind empty to use
/// the kind's name.
#[serde(deserialize_with = "deserialize_completion_item_kinds")]
pub completion_item_kinds: HashMap<lsp::CompletionItemKind, String>,
pub completion_item_kinds: HashMap<String, String>,
/// Whether to display infoboxes. Defaults to true.
pub auto_info: bool,
pub file_picker: FilePickerConfig,
Expand Down Expand Up @@ -852,58 +851,6 @@ where
}
}

fn deserialize_completion_item_kinds<'de, D>(
deserializer: D,
) -> Result<HashMap<lsp::CompletionItemKind, String>, D::Error>
where
D: serde::Deserializer<'de>,
{
let raw = HashMap::<String, String>::deserialize(deserializer)?;
let mut ret = HashMap::with_capacity(raw.len());

for (kind, text) in raw {
// NOTE: Doing manual string check since CompletionItemKind::from_str uses
// PascalCase while the configuration is in kebab-case
let kind = match kind.as_str() {
"text" => lsp::CompletionItemKind::TEXT,
"method" => lsp::CompletionItemKind::METHOD,
"function" => lsp::CompletionItemKind::FUNCTION,
"constructor" => lsp::CompletionItemKind::CONSTRUCTOR,
"field" => lsp::CompletionItemKind::FIELD,
"variable" => lsp::CompletionItemKind::VARIABLE,
"class" => lsp::CompletionItemKind::CLASS,
"interface" => lsp::CompletionItemKind::INTERFACE,
"module" => lsp::CompletionItemKind::MODULE,
"property" => lsp::CompletionItemKind::PROPERTY,
"unit" => lsp::CompletionItemKind::UNIT,
"value" => lsp::CompletionItemKind::VALUE,
"enum" => lsp::CompletionItemKind::ENUM,
"keyword" => lsp::CompletionItemKind::KEYWORD,
"snippet" => lsp::CompletionItemKind::SNIPPET,
"color" => lsp::CompletionItemKind::COLOR,
"file" => lsp::CompletionItemKind::FILE,
"reference" => lsp::CompletionItemKind::REFERENCE,
"folder" => lsp::CompletionItemKind::FOLDER,
"enum-member" => lsp::CompletionItemKind::ENUM_MEMBER,
"constant" => lsp::CompletionItemKind::CONSTANT,
"struct" => lsp::CompletionItemKind::STRUCT,
"event" => lsp::CompletionItemKind::EVENT,
"operator" => lsp::CompletionItemKind::OPERATOR,
"type-parameter" => lsp::CompletionItemKind::TYPE_PARAMETER,
_ => {
return Err(<D::Error as serde::de::Error>::invalid_value(
serde::de::Unexpected::Str(kind.as_str()),
&"CompletionItemKind",
));
}
};

ret.insert(kind, text);
}

Ok(ret)
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(default)]
pub struct WhitespaceCharacters {
Expand Down Expand Up @@ -1131,7 +1078,7 @@ pub struct Editor {

pub config: Arc<dyn DynAccess<Config>>,
pub auto_pairs: Option<AutoPairs>,
pub completion_item_kind_styles: Arc<HashMap<lsp::CompletionItemKind, CompletionItemKindStyle>>,
pub completion_item_kind_styles: Arc<HashMap<&'static str, CompletionItemKindStyle>>,

pub idle_timer: Pin<Box<Sleep>>,
redraw_timer: Pin<Box<Sleep>>,
Expand Down Expand Up @@ -1322,6 +1269,10 @@ impl Editor {
pub fn refresh_config(&mut self) {
let config = self.config();
self.auto_pairs = (&config.auto_pairs).into();
self.completion_item_kind_styles = Arc::new(compute_completion_item_kind_styles(
&self.theme,
&self.config(),
));
self.reset_idle_timer();
self._refresh();
}
Expand Down Expand Up @@ -2292,39 +2243,42 @@ fn try_restore_indent(doc: &mut Document, view: &mut View) {
fn compute_completion_item_kind_styles(
theme: &Theme,
config: &DynGuard<Config>,
) -> HashMap<lsp::CompletionItemKind, CompletionItemKindStyle> {
) -> HashMap<&'static str, CompletionItemKindStyle> {
let mut ret = HashMap::new();
for (scope_name, kind) in [
("text", lsp::CompletionItemKind::TEXT),
("method", lsp::CompletionItemKind::METHOD),
("function", lsp::CompletionItemKind::FUNCTION),
("constructor", lsp::CompletionItemKind::CONSTRUCTOR),
("field", lsp::CompletionItemKind::FIELD),
("variable", lsp::CompletionItemKind::VARIABLE),
("class", lsp::CompletionItemKind::CLASS),
("interface", lsp::CompletionItemKind::INTERFACE),
("module", lsp::CompletionItemKind::MODULE),
("property", lsp::CompletionItemKind::PROPERTY),
("unit", lsp::CompletionItemKind::UNIT),
("value", lsp::CompletionItemKind::VALUE),
("enum", lsp::CompletionItemKind::ENUM),
("keyword", lsp::CompletionItemKind::KEYWORD),
("snippet", lsp::CompletionItemKind::SNIPPET),
("color", lsp::CompletionItemKind::COLOR),
("file", lsp::CompletionItemKind::FILE),
("reference", lsp::CompletionItemKind::REFERENCE),
("folder", lsp::CompletionItemKind::FOLDER),
("enum-member", lsp::CompletionItemKind::ENUM_MEMBER),
("constant", lsp::CompletionItemKind::CONSTANT),
("struct", lsp::CompletionItemKind::STRUCT),
("event", lsp::CompletionItemKind::EVENT),
("operator", lsp::CompletionItemKind::OPERATOR),
("type-parameter", lsp::CompletionItemKind::TYPE_PARAMETER),
// We populate with LSP kinds and additionally file+folder for path completion
for name in [
"text",
"method",
"function",
"constructor",
"field",
"variable",
"class",
"interface",
"module",
"property",
"unit",
"value",
"enum",
"keyword",
"snippet",
"color",
"file",
"reference",
"folder",
"enum-member",
"constant",
"struct",
"event",
"operator",
"type-parameter",
"file",
"folder",
] {
let style = theme.try_get(&dbg!(format!("ui.completion.kind.{scope_name}")));
let text = config.completion_item_kinds.get(&kind).cloned();
let style = theme.try_get(&dbg!(format!("ui.completion.kind.{name}")));
let text = config.completion_item_kinds.get(name).cloned();
if style.is_some() || text.is_some() {
ret.insert(kind, CompletionItemKindStyle { text, style });
ret.insert(name, CompletionItemKindStyle { text, style });
}
}

Expand Down

0 comments on commit 8cb535a

Please sign in to comment.