use emmylua_code_analysis::Emmyrc;
use emmylua_parser::{LuaAstNode, LuaTokenKind};
use lsp_types::{CompletionItem, Range};
use rowan::{TextRange, TokenAtOffset};
use crate::handlers::completion::completion_builder::CompletionBuilder;
pub fn add_completion(builder: &mut CompletionBuilder) -> Option<()> {
if builder.is_cancelled() {
return None;
}
let emmyrc = builder.semantic_model.get_emmyrc();
let trigger_kind = builder.trigger_token.kind();
if !is_postfix_trigger(trigger_kind.into(), emmyrc) {
return None;
}
let trigger_pos = u32::from(builder.trigger_token.text_range().start());
let left_pos = if trigger_pos > 0 {
trigger_pos - 1
} else {
return None;
};
let left_token = match builder
.semantic_model
.get_root()
.syntax()
.token_at_offset(left_pos.into())
{
TokenAtOffset::Single(token) => token,
TokenAtOffset::Between(left, right) => {
if left.kind() == LuaTokenKind::TkName.into() {
left
} else {
right
}
},
TokenAtOffset::None => return None,
};
let left_token_text = left_token.text().to_string();
let replace_range = TextRange::new(
left_token.text_range().start(),
builder.trigger_token.text_range().end(),
);
let repalce_lsp_range = builder
.semantic_model
.get_document()
.to_lsp_range(replace_range)?;
add_postfix_completion(
builder,
repalce_lsp_range,
"if",
format!("if {} then\n\t$0\nend", left_token_text),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"ifn",
format!("if not {} then\n\t$0\nend", left_token_text),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"while",
format!("while {} do\n\t$0\nend", left_token_text),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"forp",
format!(
"for ${{1:k}}, ${{2:v}} in pairs({}) do\n\t$0\nend",
left_token_text
),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"forip",
format!(
"for ${{1:i}}, ${{2:v}} in ipairs({}) do\n\t$0\nend",
left_token_text
),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"fori",
format!("for ${{1:i}} = 1, {} do\n\t$0\nend", left_token_text),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"function",
format!("function {}(${{1:...}})\n\t$0\nend", left_token_text),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"insert",
format!("table.insert({}, ${{1:value}})", left_token_text),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"remove",
format!("table.remove({}, ${{1:index}})", left_token_text),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"++",
format!("{0} = {0} + 1", left_token_text),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"--",
format!("{0} = {0} - 1", left_token_text),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"+n",
format!("{0} = {0} + $1", left_token_text),
);
add_postfix_completion(
builder,
repalce_lsp_range,
"-n",
format!("{0} = {0} - $1", left_token_text),
);
Some(())
}
fn is_postfix_trigger(trigger_kind: LuaTokenKind, emmyrc: &Emmyrc) -> bool {
let trigger_string = &emmyrc.completion.postfix;
if trigger_string.is_empty() {
return false;
}
let first_char = trigger_string.chars().next().unwrap();
return match first_char {
'.' => trigger_kind == LuaTokenKind::TkDot,
'@' => trigger_kind == LuaTokenKind::TkAt,
':' => trigger_kind == LuaTokenKind::TkColon,
_ => false,
};
}
fn add_postfix_completion(
builder: &mut CompletionBuilder,
replace_range: Range,
label: &str,
text: String,
) -> Option<()> {
let item = CompletionItem {
label: label.to_string(),
insert_text: Some(text),
additional_text_edits: Some(vec![lsp_types::TextEdit {
range: replace_range,
new_text: "".to_string(),
}]),
insert_text_format: Some(lsp_types::InsertTextFormat::SNIPPET),
..Default::default()
};
builder.add_completion_item(item);
Some(())
}