mirror of
https://github.com/latex-lsp/texlab.git
synced 2025-12-23 09:19:21 +00:00
Provide completion for LaTeX kernel environments
This commit is contained in:
parent
69c3f2335a
commit
574f448942
6 changed files with 259 additions and 1 deletions
|
|
@ -1,6 +1,7 @@
|
|||
use crate::feature::FeatureRequest;
|
||||
use crate::range;
|
||||
use crate::syntax::latex::analysis::command::LatexCommandAnalyzer;
|
||||
use crate::syntax::latex::analysis::environment::ENVIRONMENT_COMMANDS;
|
||||
use crate::syntax::latex::analysis::finder::{LatexFinder, LatexNode};
|
||||
use crate::syntax::latex::ast::*;
|
||||
use crate::syntax::text::SyntaxNode;
|
||||
use crate::workspace::SyntaxTree;
|
||||
|
|
@ -26,6 +27,78 @@ impl LatexCombinators {
|
|||
}
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
pub async fn argument<'a, E, F>(
|
||||
request: &'a FeatureRequest<CompletionParams>,
|
||||
command_names: &'a [&'a str],
|
||||
argument_index: usize,
|
||||
execute: E,
|
||||
) -> Vec<CompletionItem>
|
||||
where
|
||||
E: Fn(&LatexCommand) -> F,
|
||||
F: std::future::Future<Output = Vec<CompletionItem>>,
|
||||
{
|
||||
let find_command = |nodes: &[LatexNode<'a>], node_index: usize| {
|
||||
if let LatexNode::Group(group) = nodes[node_index] {
|
||||
if let LatexNode::Command(command) = nodes[node_index + 1] {
|
||||
if command_names.contains(&command.name.text())
|
||||
&& command.args.len() > argument_index
|
||||
&& command.args[argument_index] == *group
|
||||
{
|
||||
return Some(command);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
let find_non_empty_command = |nodes: &[LatexNode<'a>]| {
|
||||
if nodes.len() >= 3 {
|
||||
if let LatexNode::Text(_) = nodes[0] {
|
||||
return find_command(nodes, 1);
|
||||
}
|
||||
}
|
||||
None
|
||||
};
|
||||
|
||||
let find_empty_command = |nodes: &[LatexNode<'a>]| {
|
||||
if nodes.len() >= 2 {
|
||||
find_command(nodes, 0)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
|
||||
if let SyntaxTree::Latex(tree) = &request.document.tree {
|
||||
let mut finder = LatexFinder::new(request.params.position);
|
||||
finder.visit_root(&tree.root);
|
||||
let mut nodes = finder.results;
|
||||
nodes.reverse();
|
||||
|
||||
let command = find_non_empty_command(&nodes).or_else(|| find_empty_command(&nodes));
|
||||
|
||||
if let Some(command) = command {
|
||||
if range::contains_exclusive(
|
||||
command.args[argument_index].range(),
|
||||
request.params.position,
|
||||
) {
|
||||
return await!(execute(command));
|
||||
}
|
||||
}
|
||||
}
|
||||
Vec::new()
|
||||
}
|
||||
|
||||
pub async fn environment<E, F>(
|
||||
request: &FeatureRequest<CompletionParams>,
|
||||
execute: E,
|
||||
) -> Vec<CompletionItem>
|
||||
where
|
||||
E: Fn(&LatexCommand) -> F,
|
||||
F: std::future::Future<Output = Vec<CompletionItem>>,
|
||||
{
|
||||
await!(Self::argument(&request, &ENVIRONMENT_COMMANDS, 0, execute))
|
||||
}
|
||||
}
|
||||
|
||||
struct LatexCommandFinder<'a> {
|
||||
|
|
|
|||
89
src/completion/latex/kernel_environment.rs
Normal file
89
src/completion/latex/kernel_environment.rs
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
use crate::completion::factory;
|
||||
use crate::completion::factory::LatexComponentId;
|
||||
use crate::completion::latex::combinators::LatexCombinators;
|
||||
use crate::completion::latex::kernel_primitives::KERNEL_ENVIRONMENTS;
|
||||
use crate::feature::FeatureRequest;
|
||||
use lsp_types::{CompletionItem, CompletionParams};
|
||||
|
||||
pub struct LatexKernelEnvironmentCompletionProvider;
|
||||
|
||||
impl LatexKernelEnvironmentCompletionProvider {
|
||||
pub async fn execute(request: &FeatureRequest<CompletionParams>) -> Vec<CompletionItem> {
|
||||
await!(LatexCombinators::environment(&request, async move |_| {
|
||||
KERNEL_ENVIRONMENTS
|
||||
.iter()
|
||||
.map(|name| {
|
||||
factory::create_environment((*name).to_owned(), LatexComponentId::Kernel)
|
||||
})
|
||||
.collect()
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::feature::FeatureTester;
|
||||
use crate::workspace::WorkspaceBuilder;
|
||||
use futures::executor;
|
||||
|
||||
#[test]
|
||||
fn test_inside_of_empty_begin() {
|
||||
let mut builder = WorkspaceBuilder::new();
|
||||
let uri = builder.document("foo.tex", "\\begin{}");
|
||||
let request = FeatureTester::new(builder.workspace, uri, 0, 7, "").into();
|
||||
|
||||
let items = executor::block_on(LatexKernelEnvironmentCompletionProvider::execute(&request));
|
||||
assert_eq!(true, items.iter().any(|item| item.label == "document"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inside_of_nonempty_end() {
|
||||
let mut builder = WorkspaceBuilder::new();
|
||||
let uri = builder.document("foo.tex", "\\end{foo}");
|
||||
let request = FeatureTester::new(builder.workspace, uri, 0, 6, "").into();
|
||||
|
||||
let items = executor::block_on(LatexKernelEnvironmentCompletionProvider::execute(&request));
|
||||
assert_eq!(true, items.iter().any(|item| item.label == "document"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_outside_of_empty_begin() {
|
||||
let mut builder = WorkspaceBuilder::new();
|
||||
let uri = builder.document("foo.tex", "\\begin{}");
|
||||
let request = FeatureTester::new(builder.workspace, uri, 0, 6, "").into();
|
||||
|
||||
let items = executor::block_on(LatexKernelEnvironmentCompletionProvider::execute(&request));
|
||||
assert_eq!(items, Vec::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_outside_of_empty_end() {
|
||||
let mut builder = WorkspaceBuilder::new();
|
||||
let uri = builder.document("foo.tex", "\\end{}");
|
||||
let request = FeatureTester::new(builder.workspace, uri, 0, 6, "").into();
|
||||
|
||||
let items = executor::block_on(LatexKernelEnvironmentCompletionProvider::execute(&request));
|
||||
assert_eq!(items, Vec::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inside_of_other_command() {
|
||||
let mut builder = WorkspaceBuilder::new();
|
||||
let uri = builder.document("foo.tex", "\\foo{bar}");
|
||||
let request = FeatureTester::new(builder.workspace, uri, 0, 6, "").into();
|
||||
|
||||
let items = executor::block_on(LatexKernelEnvironmentCompletionProvider::execute(&request));
|
||||
assert_eq!(items, Vec::new());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_inside_second_argument() {
|
||||
let mut builder = WorkspaceBuilder::new();
|
||||
let uri = builder.document("foo.tex", "\\begin{foo}{bar}");
|
||||
let request = FeatureTester::new(builder.workspace, uri, 0, 14, "").into();
|
||||
|
||||
let items = executor::block_on(LatexKernelEnvironmentCompletionProvider::execute(&request));
|
||||
assert_eq!(items, Vec::new());
|
||||
}
|
||||
}
|
||||
|
|
@ -1928,3 +1928,43 @@ pub const KERNEL_COMMANDS: &'static [&'static str] = &[
|
|||
"}",
|
||||
"~",
|
||||
];
|
||||
|
||||
pub const KERNEL_ENVIRONMENTS: &'static [&'static str] = &[
|
||||
"abstract",
|
||||
"array",
|
||||
"center",
|
||||
"csname",
|
||||
"description",
|
||||
"displaymath",
|
||||
"document",
|
||||
"enumerate",
|
||||
"eqnarray",
|
||||
"eqnarray*",
|
||||
"equation",
|
||||
"equation*",
|
||||
"figure",
|
||||
"filecontents",
|
||||
"flushleft",
|
||||
"flushright",
|
||||
"input",
|
||||
"itemize",
|
||||
"line",
|
||||
"list",
|
||||
"lrbox",
|
||||
"math",
|
||||
"minipage",
|
||||
"picture",
|
||||
"quotation",
|
||||
"quote",
|
||||
"sloppypar",
|
||||
"tabbing",
|
||||
"table",
|
||||
"tabular",
|
||||
"tabular*",
|
||||
"thebibliography",
|
||||
"titlepage",
|
||||
"trivlist",
|
||||
"verbatim",
|
||||
"verbatim*",
|
||||
"verse",
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
mod combinators;
|
||||
mod kernel_command;
|
||||
mod kernel_environment;
|
||||
mod kernel_primitives;
|
||||
|
|
|
|||
54
src/syntax/latex/analysis/finder.rs
Normal file
54
src/syntax/latex/analysis/finder.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
use crate::range;
|
||||
use crate::syntax::latex::ast::*;
|
||||
use crate::syntax::text::SyntaxNode;
|
||||
use lsp_types::Position;
|
||||
|
||||
pub enum LatexNode<'a> {
|
||||
Root(&'a LatexRoot),
|
||||
Group(&'a LatexGroup),
|
||||
Command(&'a LatexCommand),
|
||||
Text(&'a LatexText),
|
||||
}
|
||||
|
||||
pub struct LatexFinder<'a> {
|
||||
pub position: Position,
|
||||
pub results: Vec<LatexNode<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> LatexFinder<'a> {
|
||||
pub fn new(position: Position) -> Self {
|
||||
LatexFinder {
|
||||
position,
|
||||
results: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> LatexVisitor<'a> for LatexFinder<'a> {
|
||||
fn visit_root(&mut self, root: &'a LatexRoot) {
|
||||
if range::contains(root.range(), self.position) {
|
||||
self.results.push(LatexNode::Root(root));
|
||||
LatexWalker::walk_root(self, root);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_group(&mut self, group: &'a LatexGroup) {
|
||||
if range::contains(group.range(), self.position) {
|
||||
self.results.push(LatexNode::Group(group));
|
||||
LatexWalker::walk_group(self, group);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_command(&mut self, command: &'a LatexCommand) {
|
||||
if range::contains(command.range(), self.position) {
|
||||
self.results.push(LatexNode::Command(command));
|
||||
LatexWalker::walk_command(self, command);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_text(&mut self, text: &'a LatexText) {
|
||||
if range::contains(text.range(), self.position) {
|
||||
self.results.push(LatexNode::Text(text));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ pub mod citation;
|
|||
pub mod command;
|
||||
pub mod environment;
|
||||
pub mod equation;
|
||||
pub mod finder;
|
||||
pub mod include;
|
||||
pub mod label;
|
||||
pub mod section;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue