summaryrefslogtreecommitdiff
path: root/support/texlab/src/completion/latex/include.rs
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/completion/latex/include.rs')
-rw-r--r--support/texlab/src/completion/latex/include.rs129
1 files changed, 129 insertions, 0 deletions
diff --git a/support/texlab/src/completion/latex/include.rs b/support/texlab/src/completion/latex/include.rs
new file mode 100644
index 0000000000..2f4090b3f3
--- /dev/null
+++ b/support/texlab/src/completion/latex/include.rs
@@ -0,0 +1,129 @@
+use super::combinators::{self, Parameter};
+use crate::completion::factory;
+use crate::range::RangeExt;
+use crate::syntax::*;
+use crate::workspace::*;
+use futures_boxed::boxed;
+use lsp_types::{CompletionItem, CompletionParams, Range, TextEdit};
+use std::path::{Path, PathBuf};
+use walkdir::WalkDir;
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy)]
+pub struct LatexIncludeCompletionProvider;
+
+impl FeatureProvider for LatexIncludeCompletionProvider {
+ type Params = CompletionParams;
+ type Output = Vec<CompletionItem>;
+
+ #[boxed]
+ async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output {
+ let parameters = LANGUAGE_DATA
+ .include_commands
+ .iter()
+ .map(|cmd| Parameter::new(&cmd.name, cmd.index));
+
+ combinators::argument_word(request, parameters, |command, index| {
+ async move {
+ if !request.document().is_file() {
+ return Vec::new();
+ }
+
+ let position = request.params.text_document_position.position;
+ let mut items = Vec::new();
+ let path_word = command.extract_word(index);
+ let name_range = match path_word {
+ Some(path_word) => Range::new_simple(
+ path_word.start().line,
+ path_word.end().character
+ - path_word.text().split('/').last().unwrap().chars().count() as u64,
+ path_word.end().line,
+ path_word.end().character,
+ ),
+ None => Range::new(position, position),
+ };
+ let directory = current_directory(&request, &command);
+
+ for entry in WalkDir::new(directory)
+ .min_depth(1)
+ .max_depth(1)
+ .follow_links(false)
+ .into_iter()
+ .filter_map(std::result::Result::ok)
+ {
+ if entry.file_type().is_file() && is_included(&command, &entry.path()) {
+ let mut path = entry.into_path();
+ let include_extension = LANGUAGE_DATA
+ .include_commands
+ .iter()
+ .find(|cmd| command.name.text() == cmd.name)
+ .unwrap()
+ .include_extension;
+
+ if !include_extension {
+ remove_extension(&mut path);
+ }
+ let text_edit = make_text_edit(name_range, &path);
+ items.push(factory::file(request, &path, text_edit));
+ } else if entry.file_type().is_dir() {
+ let path = entry.into_path();
+ let text_edit = make_text_edit(name_range, &path);
+ items.push(factory::folder(request, &path, text_edit));
+ }
+ }
+ items
+ }
+ })
+ .await
+ }
+}
+
+fn current_directory(
+ request: &FeatureRequest<CompletionParams>,
+ command: &LatexCommand,
+) -> PathBuf {
+ let mut path = request.document().uri.to_file_path().unwrap();
+ path = PathBuf::from(path.to_string_lossy().into_owned().replace('\\', "/"));
+
+ path.pop();
+ if let Some(include) = command.extract_word(0) {
+ path.push(include.text());
+ if !include.text().ends_with('/') {
+ path.pop();
+ }
+ }
+ path
+}
+
+fn is_included(command: &LatexCommand, file: &Path) -> bool {
+ if let Some(allowed_extensions) = LANGUAGE_DATA
+ .include_commands
+ .iter()
+ .find(|cmd| command.name.text() == cmd.name)
+ .unwrap()
+ .kind
+ .extensions()
+ {
+ file.extension()
+ .map(|extension| extension.to_string_lossy().to_lowercase())
+ .map(|extension| allowed_extensions.contains(&extension.as_str()))
+ .unwrap_or(false)
+ } else {
+ true
+ }
+}
+
+fn remove_extension(path: &mut PathBuf) {
+ let stem = path
+ .file_stem()
+ .map(|stem| stem.to_string_lossy().into_owned());
+
+ if let Some(stem) = stem {
+ path.pop();
+ path.push(PathBuf::from(stem));
+ }
+}
+
+fn make_text_edit(range: Range, path: &Path) -> TextEdit {
+ let text = path.file_name().unwrap().to_string_lossy().into_owned();
+ TextEdit::new(range, text)
+}