summaryrefslogtreecommitdiff
path: root/support/texlab/src/completion/util.rs
blob: ec3c8eb40679dd9f4bcc3b31ee6e0a1f372a4777 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use crate::{
    feature::FeatureRequest,
    protocol::{
        CompletionItemKind, CompletionParams, Documentation, MarkupContent, MarkupKind, Position,
        RangeExt,
    },
    syntax::{bibtex, latex, SyntaxNode},
    workspace::DocumentContent,
};
use std::borrow::Cow;

pub fn current_word(req: &FeatureRequest<CompletionParams>) -> Option<Cow<str>> {
    let pos = req.params.text_document_position.position;
    match &req.current().content {
        DocumentContent::Latex(table) => {
            if let Some(node) = table.find_command_by_short_name_range(pos) {
                return Some(command_word(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_word(cmd)),
                latex::Node::Text(text) => text
                    .words
                    .iter()
                    .find(|word| word.range().contains(pos))
                    .map(|word| word.text().split('/').last().unwrap().to_owned().into()),
                latex::Node::Comma(_) => Some(",".into()),
                latex::Node::Math(math) => Some(math.token.text().to_owned().into()),
            }
        }
        DocumentContent::Bibtex(tree) => {
            fn type_query(ty: &bibtex::Token, pos: Position) -> Option<Cow<str>> {
                if ty.range().contains(pos) {
                    Some((&ty.text()[1..]).into())
                } else {
                    Some("".into())
                }
            }

            match &tree.graph[tree.find(pos).pop()?] {
                bibtex::Node::Root(_) => Some("".into()),
                bibtex::Node::Preamble(preamble) => type_query(&preamble.ty, pos),
                bibtex::Node::String(string) => type_query(&string.ty, pos),
                bibtex::Node::Entry(entry) => type_query(&entry.ty, pos),
                bibtex::Node::Comment(comment) => Some(comment.token.text().into()),
                bibtex::Node::Field(field) => {
                    if field.name.range().contains(pos) {
                        Some(field.name.text().into())
                    } else {
                        Some("".into())
                    }
                }
                bibtex::Node::Word(word) => Some(word.token.text().into()),
                bibtex::Node::Command(cmd) => Some((&cmd.token.text()[1..]).into()),
                bibtex::Node::QuotedContent(_)
                | bibtex::Node::BracedContent(_)
                | bibtex::Node::Concat(_) => Some("".into()),
            }
        }
    }
}

fn command_word(cmd: &latex::Command) -> Cow<str> {
    cmd.name.text()[1..].into()
}

pub fn component_detail(file_names: &[String]) -> String {
    if file_names.is_empty() {
        "built-in".to_owned()
    } else {
        file_names.join(", ")
    }
}

pub fn image_documentation(
    req: &FeatureRequest<CompletionParams>,
    name: &str,
    image: &str,
) -> Option<Documentation> {
    if supports_images(req) {
        Some(Documentation::MarkupContent(MarkupContent {
            kind: MarkupKind::Markdown,
            value: format!(
                "![{}](data:image/png;base64,{}|width=48,height=48)",
                name, image
            ),
        }))
    } else {
        None
    }
}

fn supports_images(req: &FeatureRequest<CompletionParams>) -> bool {
    req.client_capabilities
        .text_document
        .as_ref()
        .and_then(|cap| cap.completion.as_ref())
        .and_then(|cap| cap.completion_item.as_ref())
        .and_then(|cap| cap.documentation_format.as_ref())
        .map_or(true, |formats| formats.contains(&MarkupKind::Markdown))
}

pub fn adjust_kind(
    req: &FeatureRequest<CompletionParams>,
    kind: CompletionItemKind,
) -> CompletionItemKind {
    if let Some(value_set) = req
        .client_capabilities
        .text_document
        .as_ref()
        .and_then(|cap| cap.completion.as_ref())
        .and_then(|cap| cap.completion_item_kind.as_ref())
        .and_then(|cap| cap.value_set.as_ref())
    {
        if value_set.contains(&kind) {
            return kind;
        }
    }
    CompletionItemKind::Text
}