Improve performance of command completion

This commit is contained in:
Patrick Förster 2020-04-17 10:06:51 +02:00
parent 48b2e7b6b4
commit 7eb1adcf92
3 changed files with 123 additions and 86 deletions

View file

@ -39,15 +39,15 @@ impl Into<serde_json::Value> for CompletionItemData {
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum LatexComponentId<'a> {
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum LatexComponentId {
User,
Component(Vec<&'a str>),
Component(&'static [String]),
}
impl<'a> LatexComponentId<'a> {
impl LatexComponentId {
pub fn kernel() -> Self {
LatexComponentId::Component(vec![])
LatexComponentId::Component(&[])
}
pub fn detail(&self) -> String {

View file

@ -1,9 +1,21 @@
use super::combinators;
use crate::factory::{self, LatexComponentId};
use crate::{
factory::{self, LatexComponentId},
quality::QualityEvaluator,
COMPLETION_LIMIT,
};
use futures_boxed::boxed;
use texlab_feature::{FeatureProvider, FeatureRequest};
use texlab_protocol::{CompletionItem, CompletionParams, TextEdit};
#[derive(Debug)]
struct CommandItem {
id: LatexComponentId,
name: &'static str,
image: &'static Option<String>,
glyph: &'static Option<String>,
}
#[derive(Debug, PartialEq, Eq, Clone, Copy, Default)]
pub struct LatexComponentCommandCompletionProvider;
@ -17,25 +29,40 @@ impl FeatureProvider for LatexComponentCommandCompletionProvider {
let table = req.current().content.as_latex().unwrap();
let cmd = table.as_command(cmd_node).unwrap();
let range = cmd.short_name_range();
let mut items = Vec::new();
req.view.components();
for comp in req.view.components() {
let file_names = comp.file_names.iter().map(AsRef::as_ref).collect();
let id = LatexComponentId::Component(file_names);
for cmd in &comp.commands {
let text_edit = TextEdit::new(range, (&cmd.name).into());
let item = factory::command(
req,
(&cmd.name).into(),
cmd.image.as_ref().map(AsRef::as_ref),
cmd.glyph.as_ref().map(AsRef::as_ref),
text_edit,
&id,
);
items.push(item);
}
}
let pos = req.params.text_document_position.position;
let eval = QualityEvaluator::parse(req.current(), pos);
let mut items: Vec<_> = req
.view
.components()
.into_iter()
.flat_map(|comp| {
comp.commands.iter().map(move |cmd| CommandItem {
id: LatexComponentId::Component(&comp.file_names),
name: &cmd.name,
image: &cmd.image,
glyph: &cmd.glyph,
})
})
.collect();
items.sort_by_key(|item| -eval.quality_of(item.name, &None));
items
.into_iter()
.take(COMPLETION_LIMIT)
.map(|item| {
let text_edit = TextEdit::new(range, item.name.into());
let new_item = factory::command(
req,
item.name.into(),
item.image.as_ref().map(AsRef::as_ref),
item.glyph.as_ref().map(AsRef::as_ref),
text_edit,
&item.id,
);
new_item
})
.collect()
})
.await
}
@ -52,8 +79,7 @@ impl FeatureProvider for LatexComponentEnvironmentCompletionProvider {
combinators::environment(req, |ctx| async move {
let mut items = Vec::new();
for comp in req.view.components() {
let file_names = comp.file_names.iter().map(AsRef::as_ref).collect();
let id = LatexComponentId::Component(file_names);
let id = LatexComponentId::Component(&comp.file_names);
for env in &comp.environments {
let text_edit = TextEdit::new(ctx.range, env.into());
let item = factory::environment(req, env.into(), text_edit, &id);

View file

@ -4,40 +4,64 @@ use texlab_feature::{Document, DocumentContent, FeatureProvider, FeatureRequest}
use texlab_protocol::{CompletionItem, CompletionParams, Position, RangeExt};
use texlab_syntax::{bibtex, latex, SyntaxNode};
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct OrderByQualityCompletionProvider<F>(pub F);
impl<F> FeatureProvider for OrderByQualityCompletionProvider<F>
where
F: FeatureProvider<Params = CompletionParams, Output = Vec<CompletionItem>> + Send + Sync,
{
type Params = CompletionParams;
type Output = Vec<CompletionItem>;
#[boxed]
async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output {
let query = Self::query(req.current(), req.params.text_document_position.position);
let mut items = self.0.execute(req).await;
items.sort_by_key(|item| -Self::get_quality(&query, &item));
items
}
#[derive(Debug)]
pub struct QualityEvaluator<'a> {
query: Option<Cow<'a, str>>,
}
impl<F> OrderByQualityCompletionProvider<F> {
fn query(doc: &Document, pos: Position) -> Option<Cow<str>> {
impl<'a> QualityEvaluator<'a> {
pub fn quality_of(&self, label: &str, preselect: &Option<bool>) -> i32 {
if *preselect == Some(true) {
return 8;
}
if let Some(query) = &self.query {
if label == query {
return 7;
}
if label.to_lowercase() == query.to_lowercase() {
return 6;
}
if label.starts_with(query.as_ref()) {
return 5;
}
if label.to_lowercase().starts_with(&query.to_lowercase()) {
return 4;
}
if label.contains(query.as_ref()) {
return 3;
}
if label.to_lowercase().contains(&query.to_lowercase()) {
return 2;
}
1
} else {
0
}
}
pub fn parse(doc: &'a Document, pos: Position) -> Self {
Self {
query: Self::parse_query(doc, pos),
}
}
fn parse_query(doc: &'a Document, pos: Position) -> Option<Cow<'a, str>> {
match &doc.content {
DocumentContent::Latex(table) => {
fn command_query(cmd: &latex::Command) -> Cow<str> {
cmd.name.text()[1..].to_owned().into()
}
if let Some(node) = table.find_command_by_short_name_range(pos) {
return Some(command_query(table.as_command(node).unwrap()));
return Some(Self::command_query(table.as_command(node).unwrap()));
}
match &table[table.find(pos).into_iter().last()?] {
latex::Node::Root(_) | latex::Node::Group(_) => Some("".into()),
latex::Node::Command(cmd) => Some(command_query(cmd)),
latex::Node::Command(cmd) => Some(Self::command_query(cmd)),
latex::Node::Text(text) => text
.words
.iter()
@ -79,40 +103,27 @@ impl<F> OrderByQualityCompletionProvider<F> {
}
}
fn get_quality(query: &Option<Cow<str>>, item: &CompletionItem) -> i32 {
if item.preselect == Some(true) {
return 8;
}
let label = &item.label;
if let Some(query) = query {
if label == query {
return 7;
}
if label.to_lowercase() == query.to_lowercase() {
return 6;
}
if label.starts_with(query.as_ref()) {
return 5;
}
if label.to_lowercase().starts_with(&query.to_lowercase()) {
return 4;
}
if label.contains(query.as_ref()) {
return 3;
}
if label.to_lowercase().contains(&query.to_lowercase()) {
return 2;
}
1
} else {
0
}
fn command_query(cmd: &'a latex::Command) -> Cow<'a, str> {
cmd.name.text()[1..].to_owned().into()
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct OrderByQualityCompletionProvider<F>(pub F);
impl<F> FeatureProvider for OrderByQualityCompletionProvider<F>
where
F: FeatureProvider<Params = CompletionParams, Output = Vec<CompletionItem>> + Send + Sync,
{
type Params = CompletionParams;
type Output = Vec<CompletionItem>;
#[boxed]
async fn execute<'a>(&'a self, req: &'a FeatureRequest<Self::Params>) -> Self::Output {
let pos = req.params.text_document_position.position;
let eval = QualityEvaluator::parse(req.current(), pos);
let mut items = self.0.execute(req).await;
items.sort_by_key(|item| -eval.quality_of(&item.label, &item.preselect));
items
}
}