summaryrefslogtreecommitdiff
path: root/support/texlab/src/workspace
diff options
context:
space:
mode:
Diffstat (limited to 'support/texlab/src/workspace')
-rw-r--r--support/texlab/src/workspace/feature.rs246
-rw-r--r--support/texlab/src/workspace/mod.rs417
-rw-r--r--support/texlab/src/workspace/outline.rs438
-rw-r--r--support/texlab/src/workspace/uri.rs73
4 files changed, 0 insertions, 1174 deletions
diff --git a/support/texlab/src/workspace/feature.rs b/support/texlab/src/workspace/feature.rs
deleted file mode 100644
index 815f5e1724..0000000000
--- a/support/texlab/src/workspace/feature.rs
+++ /dev/null
@@ -1,246 +0,0 @@
-use super::{Document, DocumentView, Workspace, WorkspaceBuilder};
-use futures_boxed::boxed;
-use lsp_types::*;
-use std::sync::Arc;
-
-pub struct FeatureRequest<P> {
- pub params: P,
- pub view: DocumentView,
- pub client_capabilities: Arc<ClientCapabilities>,
- pub distribution: Arc<Box<dyn tex::Distribution>>,
-}
-
-impl<P> FeatureRequest<P> {
- pub fn workspace(&self) -> &Workspace {
- &self.view.workspace
- }
-
- pub fn document(&self) -> &Document {
- &self.view.document
- }
-
- pub fn related_documents(&self) -> &[Arc<Document>] {
- &self.view.related_documents
- }
-}
-
-pub trait FeatureProvider {
- type Params;
- type Output;
-
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<Self::Params>) -> Self::Output;
-}
-
-type ListProvider<P, O> = Box<dyn FeatureProvider<Params = P, Output = Vec<O>> + Send + Sync>;
-
-#[derive(Default)]
-pub struct ConcatProvider<P, O> {
- providers: Vec<ListProvider<P, O>>,
-}
-
-impl<P, O> ConcatProvider<P, O> {
- pub fn new(providers: Vec<ListProvider<P, O>>) -> Self {
- Self { providers }
- }
-}
-
-impl<P, O> FeatureProvider for ConcatProvider<P, O>
-where
- P: Send + Sync,
- O: Send + Sync,
-{
- type Params = P;
- type Output = Vec<O>;
-
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<P>) -> Vec<O> {
- let mut items = Vec::new();
- for provider in &self.providers {
- items.append(&mut provider.execute(request).await);
- }
- items
- }
-}
-
-type OptionProvider<P, O> = Box<dyn FeatureProvider<Params = P, Output = Option<O>> + Send + Sync>;
-
-#[derive(Default)]
-pub struct ChoiceProvider<P, O> {
- providers: Vec<OptionProvider<P, O>>,
-}
-
-impl<P, O> ChoiceProvider<P, O> {
- pub fn new(providers: Vec<OptionProvider<P, O>>) -> Self {
- Self { providers }
- }
-}
-
-impl<P, O> FeatureProvider for ChoiceProvider<P, O>
-where
- P: Send + Sync,
- O: Send + Sync,
-{
- type Params = P;
- type Output = Option<O>;
-
- #[boxed]
- async fn execute<'a>(&'a self, request: &'a FeatureRequest<P>) -> Option<O> {
- for provider in &self.providers {
- let item = provider.execute(request).await;
- if item.is_some() {
- return item;
- }
- }
- None
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct FeatureSpecFile {
- name: &'static str,
- text: &'static str,
-}
-
-pub struct FeatureSpec {
- pub files: Vec<FeatureSpecFile>,
- pub main_file: &'static str,
- pub position: Position,
- pub new_name: &'static str,
- pub include_declaration: bool,
- pub client_capabilities: ClientCapabilities,
- pub distribution: Box<dyn tex::Distribution>,
-}
-
-impl Default for FeatureSpec {
- fn default() -> Self {
- Self {
- files: Vec::new(),
- main_file: "",
- position: Position::new(0, 0),
- new_name: "",
- include_declaration: false,
- client_capabilities: ClientCapabilities::default(),
- distribution: Box::new(tex::Unknown::default()),
- }
- }
-}
-
-impl FeatureSpec {
- pub fn file(name: &'static str, text: &'static str) -> FeatureSpecFile {
- FeatureSpecFile { name, text }
- }
-
- pub fn uri(name: &str) -> Url {
- let path = std::env::temp_dir().join(name);
- Url::from_file_path(path).unwrap()
- }
-
- fn identifier(&self) -> TextDocumentIdentifier {
- let uri = Self::uri(self.main_file);
- TextDocumentIdentifier::new(uri)
- }
-
- fn view(&self) -> DocumentView {
- let mut builder = WorkspaceBuilder::new();
- for file in &self.files {
- builder.document(file.name, file.text);
- }
- let workspace = builder.workspace;
- let main_uri = Self::uri(self.main_file);
- let main_document = workspace.find(&main_uri.into()).unwrap();
- DocumentView::new(Arc::new(workspace), main_document)
- }
-
- fn request<T>(self, params: T) -> FeatureRequest<T> {
- FeatureRequest {
- params,
- view: self.view(),
- client_capabilities: Arc::new(self.client_capabilities),
- distribution: Arc::new(self.distribution),
- }
- }
-}
-
-impl Into<FeatureRequest<TextDocumentPositionParams>> for FeatureSpec {
- fn into(self) -> FeatureRequest<TextDocumentPositionParams> {
- let params = TextDocumentPositionParams::new(self.identifier(), self.position);
- self.request(params)
- }
-}
-
-impl Into<FeatureRequest<CompletionParams>> for FeatureSpec {
- fn into(self) -> FeatureRequest<CompletionParams> {
- let params = CompletionParams {
- text_document_position: TextDocumentPositionParams::new(
- self.identifier(),
- self.position,
- ),
- context: None,
- };
- self.request(params)
- }
-}
-
-impl Into<FeatureRequest<FoldingRangeParams>> for FeatureSpec {
- fn into(self) -> FeatureRequest<FoldingRangeParams> {
- let params = FoldingRangeParams {
- text_document: self.identifier(),
- };
- self.request(params)
- }
-}
-
-impl Into<FeatureRequest<DocumentLinkParams>> for FeatureSpec {
- fn into(self) -> FeatureRequest<DocumentLinkParams> {
- let params = DocumentLinkParams {
- text_document: self.identifier(),
- };
- self.request(params)
- }
-}
-
-impl Into<FeatureRequest<ReferenceParams>> for FeatureSpec {
- fn into(self) -> FeatureRequest<ReferenceParams> {
- let params = ReferenceParams {
- text_document_position: TextDocumentPositionParams::new(
- self.identifier(),
- self.position,
- ),
- context: ReferenceContext {
- include_declaration: self.include_declaration,
- },
- };
- self.request(params)
- }
-}
-
-impl Into<FeatureRequest<RenameParams>> for FeatureSpec {
- fn into(self) -> FeatureRequest<RenameParams> {
- let params = RenameParams {
- text_document_position: TextDocumentPositionParams::new(
- self.identifier(),
- self.position,
- ),
- new_name: self.new_name.to_owned(),
- };
- self.request(params)
- }
-}
-
-impl Into<FeatureRequest<DocumentSymbolParams>> for FeatureSpec {
- fn into(self) -> FeatureRequest<DocumentSymbolParams> {
- let params = DocumentSymbolParams {
- text_document: self.identifier(),
- };
- self.request(params)
- }
-}
-
-pub fn test_feature<F, P, O, S>(provider: F, spec: S) -> O
-where
- F: FeatureProvider<Params = P, Output = O>,
- S: Into<FeatureRequest<P>>,
-{
- futures::executor::block_on(provider.execute(&spec.into()))
-}
diff --git a/support/texlab/src/workspace/mod.rs b/support/texlab/src/workspace/mod.rs
deleted file mode 100644
index 5ad2c21bf2..0000000000
--- a/support/texlab/src/workspace/mod.rs
+++ /dev/null
@@ -1,417 +0,0 @@
-mod feature;
-mod outline;
-mod uri;
-
-pub use self::feature::*;
-pub use self::outline::*;
-pub use self::uri::*;
-use crate::completion::DATABASE;
-use crate::syntax::*;
-use log::*;
-use lsp_types::*;
-use std::ffi::OsStr;
-use std::fs;
-use std::path::{Path, PathBuf};
-use std::sync::{Arc, Mutex};
-use std::time::SystemTime;
-use tex::Language;
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct Document {
- pub uri: Uri,
- pub text: String,
- pub tree: SyntaxTree,
- pub modified: SystemTime,
-}
-
-impl Document {
- pub fn new(uri: Uri, text: String, tree: SyntaxTree) -> Self {
- Self {
- uri,
- text,
- tree,
- modified: SystemTime::now(),
- }
- }
-
- pub fn parse(uri: Uri, text: String, language: Language) -> Self {
- let tree = SyntaxTree::parse(&uri, &text, language);
- Self::new(uri, text, tree)
- }
-
- pub fn is_file(&self) -> bool {
- self.uri.scheme() == "file"
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Default)]
-pub struct Workspace {
- pub documents: Vec<Arc<Document>>,
-}
-
-impl Workspace {
- pub fn new() -> Self {
- Workspace {
- documents: Vec::new(),
- }
- }
-
- pub fn find(&self, uri: &Uri) -> Option<Arc<Document>> {
- self.documents
- .iter()
- .find(|document| &document.uri == uri)
- .map(|document| Arc::clone(&document))
- }
-
- pub fn related_documents(&self, uri: &Uri) -> Vec<Arc<Document>> {
- let edges = self.build_dependency_graph();
- let mut results = Vec::new();
- if let Some(start) = self.find(uri) {
- let mut visited: Vec<Arc<Document>> = Vec::new();
- let mut stack = vec![start];
- while let Some(current) = stack.pop() {
- if visited.contains(&current) {
- continue;
- }
- visited.push(Arc::clone(&current));
-
- results.push(Arc::clone(&current));
- for edge in &edges {
- if edge.0 == current {
- stack.push(Arc::clone(&edge.1));
- }
- }
- }
- }
- results
- }
-
- fn build_dependency_graph(&self) -> Vec<(Arc<Document>, Arc<Document>)> {
- let mut edges: Vec<(Arc<Document>, Arc<Document>)> = Vec::new();
- for parent in self.documents.iter().filter(|document| document.is_file()) {
- if let SyntaxTree::Latex(tree) = &parent.tree {
- for include in &tree.includes {
- for targets in &include.all_targets {
- for target in targets {
- if let Some(ref child) = self.find(target) {
- edges.push((Arc::clone(&parent), Arc::clone(&child)));
- edges.push((Arc::clone(&child), Arc::clone(&parent)));
- }
- }
- }
- }
-
- let tex_path = parent.uri.to_file_path().unwrap();
- let aux_path = tex_path.with_extension("aux");
- if let Some(child) = self.find(&Uri::from_file_path(aux_path).unwrap()) {
- edges.push((Arc::clone(&parent), Arc::clone(&child)));
- edges.push((Arc::clone(&child), Arc::clone(&parent)));
- }
- }
- }
- edges
- }
-
- pub fn find_parent(&self, uri: &Uri) -> Option<Arc<Document>> {
- for document in self.related_documents(uri) {
- if let SyntaxTree::Latex(tree) = &document.tree {
- if tree.env.is_standalone {
- return Some(document);
- }
- }
- }
- None
- }
-
- pub fn unresolved_includes(&self) -> Vec<PathBuf> {
- let mut includes = Vec::new();
- for document in &self.documents {
- if let SyntaxTree::Latex(tree) = &document.tree {
- for include in &tree.includes {
- match include.kind {
- LatexIncludeKind::Bibliography | LatexIncludeKind::Latex => (),
- LatexIncludeKind::Everything
- | LatexIncludeKind::Image
- | LatexIncludeKind::Pdf
- | LatexIncludeKind::Svg => continue,
- LatexIncludeKind::Package | LatexIncludeKind::Class => {
- if include
- .paths()
- .iter()
- .all(|name| DATABASE.contains(name.text()))
- {
- continue;
- }
- }
- }
-
- for targets in &include.all_targets {
- if targets.iter().any(|target| self.find(target).is_some()) {
- continue;
- }
-
- for target in targets {
- let path = target.to_file_path().unwrap();
- if path.exists() {
- includes.push(path);
- }
- }
- }
- }
-
- let aux_path = document.uri.to_file_path().unwrap().with_extension("aux");
- if self
- .find(&Uri::from_file_path(&aux_path).unwrap())
- .is_none()
- {
- includes.push(aux_path);
- }
- }
- }
- includes
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct DocumentView {
- pub workspace: Arc<Workspace>,
- pub document: Arc<Document>,
- pub related_documents: Vec<Arc<Document>>,
-}
-
-impl DocumentView {
- pub fn new(workspace: Arc<Workspace>, document: Arc<Document>) -> Self {
- let related_documents = workspace.related_documents(&document.uri);
- Self {
- workspace,
- document,
- related_documents,
- }
- }
-}
-
-#[derive(Debug)]
-pub enum LoadError {
- UnknownLanguage,
- InvalidPath,
- IO(std::io::Error),
-}
-
-#[derive(Debug, Default)]
-pub struct WorkspaceManager {
- workspace: Mutex<Arc<Workspace>>,
-}
-
-impl WorkspaceManager {
- pub fn get(&self) -> Arc<Workspace> {
- let workspace = self.workspace.lock().unwrap();
- Arc::clone(&workspace)
- }
-
- pub fn add(&self, document: TextDocumentItem) {
- let language = match Language::by_language_id(&document.language_id) {
- Some(language) => language,
- None => {
- error!("Invalid language id: {}", &document.language_id);
- return;
- }
- };
-
- let mut workspace = self.workspace.lock().unwrap();
- *workspace = Self::add_or_update(&workspace, document.uri.into(), document.text, language);
- }
-
- pub fn load(&self, path: &Path) -> Result<(), LoadError> {
- let language = match path
- .extension()
- .and_then(OsStr::to_str)
- .and_then(Language::by_extension)
- {
- Some(language) => language,
- None => {
- warn!("Could not determine language: {}", path.to_string_lossy());
- return Err(LoadError::UnknownLanguage);
- }
- };
-
- let uri = match Uri::from_file_path(path) {
- Ok(uri) => uri,
- Err(_) => {
- error!("Invalid path: {}", path.to_string_lossy());
- return Err(LoadError::InvalidPath);
- }
- };
-
- let text = match fs::read_to_string(path) {
- Ok(text) => text,
- Err(why) => {
- warn!("Could not open file: {}", path.to_string_lossy());
- return Err(LoadError::IO(why));
- }
- };
-
- let mut workspace = self.workspace.lock().unwrap();
- *workspace = Self::add_or_update(&workspace, uri, text, language);
- Ok(())
- }
-
- pub fn update(&self, uri: Uri, text: String) {
- let mut workspace = self.workspace.lock().unwrap();
-
- let old_document = match workspace.documents.iter().find(|x| x.uri == uri) {
- Some(document) => document,
- None => {
- warn!("Document not found: {}", uri);
- return;
- }
- };
-
- let language = match old_document.tree {
- SyntaxTree::Latex(_) => Language::Latex,
- SyntaxTree::Bibtex(_) => Language::Bibtex,
- };
-
- *workspace = Self::add_or_update(&workspace, uri, text, language);
- }
-
- fn add_or_update(
- workspace: &Workspace,
- uri: Uri,
- text: String,
- language: Language,
- ) -> Arc<Workspace> {
- let document = Document::parse(uri, text, language);
- let mut documents: Vec<Arc<Document>> = workspace
- .documents
- .iter()
- .filter(|x| x.uri != document.uri)
- .cloned()
- .collect();
-
- documents.push(Arc::new(document));
- Arc::new(Workspace { documents })
- }
-}
-
-#[derive(Debug, Default)]
-pub struct WorkspaceBuilder {
- pub workspace: Workspace,
-}
-
-impl WorkspaceBuilder {
- pub fn new() -> Self {
- Self::default()
- }
-
- pub fn document(&mut self, name: &str, text: &str) -> Uri {
- let path = std::env::temp_dir().join(name);
- let language = Language::by_extension(path.extension().unwrap().to_str().unwrap()).unwrap();
- let uri = Uri::from_file_path(path).unwrap();
- let document = Document::parse(uri.clone(), text.to_owned(), language);
- self.workspace.documents.push(Arc::new(document));
- uri
- }
-}
-
-#[cfg(test)]
-mod tests {
- use super::*;
-
- fn verify_documents(expected: Vec<Uri>, actual: Vec<Arc<Document>>) {
- assert_eq!(expected.len(), actual.len());
- for i in 0..expected.len() {
- assert_eq!(expected[i], actual[i].uri);
- }
- }
-
- #[test]
- fn test_related_documents_append_extensions() {
- let mut builder = WorkspaceBuilder::new();
- let uri1 = builder.document("foo.tex", "\\include{bar/baz}");
- let uri2 = builder.document("bar/baz.tex", "");
- let documents = builder.workspace.related_documents(&uri1);
- verify_documents(vec![uri1, uri2], documents);
- }
-
- #[test]
- fn test_related_documents_relative_path() {
- let mut builder = WorkspaceBuilder::new();
- let uri1 = builder.document("foo.tex", "");
- let uri2 = builder.document("bar/baz.tex", "\\input{../foo.tex}");
- let documents = builder.workspace.related_documents(&uri1);
- verify_documents(vec![uri1, uri2], documents);
- }
-
- #[test]
- fn test_related_documents_invalid_includes() {
- let mut builder = WorkspaceBuilder::new();
- let uri = builder.document("foo.tex", "\\include{<foo>?|bar|:}\n\\include{}");
- let documents = builder.workspace.related_documents(&uri);
- verify_documents(vec![uri], documents);
- }
-
- #[test]
- fn test_related_documents_bibliographies() {
- let mut builder = WorkspaceBuilder::new();
- let uri1 = builder.document("foo.tex", "\\addbibresource{bar.bib}");
- let uri2 = builder.document("bar.bib", "");
- let documents = builder.workspace.related_documents(&uri2);
- verify_documents(vec![uri2, uri1], documents);
- }
-
- #[test]
- fn test_related_documents_unresolvable_include() {
- let mut builder = WorkspaceBuilder::new();
- let uri = builder.document("foo.tex", "\\include{bar.tex}");
- builder.document("baz.tex", "");
- let documents = builder.workspace.related_documents(&uri);
- verify_documents(vec![uri], documents);
- }
-
- #[test]
- fn test_related_documents_include_cycles() {
- let mut builder = WorkspaceBuilder::new();
- let uri1 = builder.document("foo.tex", "\\input{bar.tex}");
- let uri2 = builder.document("bar.tex", "\\input{foo.tex}");
- let documents = builder.workspace.related_documents(&uri1);
- verify_documents(vec![uri1, uri2], documents);
- }
-
- #[test]
- fn test_related_documents_same_parent() {
- let mut builder = WorkspaceBuilder::new();
- let uri1 = builder.document("test.tex", "\\include{test1}\\include{test2}");
- let uri2 = builder.document("test1.tex", "\\label{foo}");
- let uri3 = builder.document("test2.tex", "\\ref{foo}");
- let documents = builder.workspace.related_documents(&uri3);
- verify_documents(vec![uri3, uri1, uri2], documents);
- }
-
- #[test]
- fn test_related_documents_aux_file() {
- let mut builder = WorkspaceBuilder::new();
- let uri1 = builder.document("foo.tex", "\\include{bar}");
- let uri2 = builder.document("bar.tex", "");
- let uri3 = builder.document("foo.aux", "");
- let documents = builder.workspace.related_documents(&uri2);
- verify_documents(vec![uri2, uri1, uri3], documents);
- }
-
- #[test]
- fn test_find_parent() {
- let mut builder = WorkspaceBuilder::new();
- let uri1 = builder.document("foo.tex", "");
- let uri2 = builder.document("bar.tex", "\\begin{document}\\include{foo}\\end{document}");
- let document = builder.workspace.find_parent(&uri1).unwrap();
- assert_eq!(uri2, document.uri);
- }
-
- #[test]
- fn test_find_parent_no_parent() {
- let mut builder = WorkspaceBuilder::new();
- let uri = builder.document("foo.tex", "");
- builder.document("bar.tex", "\\begin{document}\\end{document}");
- let document = builder.workspace.find_parent(&uri);
- assert_eq!(None, document);
- }
-}
diff --git a/support/texlab/src/workspace/outline.rs b/support/texlab/src/workspace/outline.rs
deleted file mode 100644
index 818eed1a53..0000000000
--- a/support/texlab/src/workspace/outline.rs
+++ /dev/null
@@ -1,438 +0,0 @@
-use super::{Document, DocumentView};
-use crate::range::RangeExt;
-use crate::syntax::*;
-use crate::workspace::Uri;
-use lsp_types::*;
-use std::collections::HashSet;
-
-#[derive(Debug, PartialEq, Eq, Clone, Default)]
-pub struct Outline<'a> {
- sections: Vec<OutlineSection<'a>>,
-}
-
-impl<'a> Outline<'a> {
- fn new(sections: Vec<OutlineSection<'a>>) -> Self {
- Self { sections }
- }
-
- pub fn find(&self, uri: &Uri, position: Position) -> Option<&'a LatexSection> {
- self.sections
- .iter()
- .filter(|sec| sec.document.uri == *uri)
- .rev()
- .find(|sec| sec.item.end() <= position)
- .map(|sec| sec.item)
- }
-}
-
-impl<'a> From<&'a DocumentView> for Outline<'a> {
- fn from(view: &'a DocumentView) -> Self {
- let mut finder = OutlineSectionFinder::default();
- let document = if let Some(parent) = view.workspace.find_parent(&view.document.uri) {
- view.related_documents
- .iter()
- .find(|doc| doc.uri == parent.uri)
- .unwrap()
- } else {
- &view.document
- };
- finder.analyze(view, &document);
- Outline::new(finder.sections)
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-struct OutlineSection<'a> {
- pub document: &'a Document,
- pub item: &'a LatexSection,
-}
-
-impl<'a> OutlineSection<'a> {
- fn new(document: &'a Document, item: &'a LatexSection) -> Self {
- Self { document, item }
- }
-}
-
-#[derive(Debug, Default)]
-struct OutlineSectionFinder<'a> {
- visited: HashSet<&'a Uri>,
- sections: Vec<OutlineSection<'a>>,
-}
-
-impl<'a> OutlineSectionFinder<'a> {
- fn analyze(&mut self, view: &'a DocumentView, document: &'a Document) {
- if !self.visited.insert(&document.uri) {
- return;
- }
-
- if let SyntaxTree::Latex(tree) = &document.tree {
- let mut items = Vec::new();
- for section in &tree.structure.sections {
- items.push(OutlineItem::Section(section));
- }
- for include in &tree.includes {
- items.push(OutlineItem::Include(include));
- }
- items.sort_by_key(SyntaxNode::start);
-
- for item in items {
- match item {
- OutlineItem::Section(item) => {
- let section = OutlineSection::new(document, item);
- self.sections.push(section);
- }
- OutlineItem::Include(item) => {
- for document in &view.related_documents {
- for targets in &item.all_targets {
- if targets.contains(&document.uri) {
- self.analyze(view, document);
- break;
- }
- }
- }
- }
- }
- }
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-enum OutlineItem<'a> {
- Section(&'a LatexSection),
- Include(&'a LatexInclude),
-}
-
-impl<'a> SyntaxNode for OutlineItem<'a> {
- fn range(&self) -> Range {
- match self {
- OutlineItem::Section(section) => section.range(),
- OutlineItem::Include(include) => include.range(),
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-pub enum OutlineCaptionKind {
- Figure,
- Table,
- Listing,
- Algorithm,
-}
-
-impl OutlineCaptionKind {
- pub fn as_str(self) -> &'static str {
- match self {
- Self::Figure => "Figure",
- Self::Table => "Table",
- Self::Listing => "Listing",
- Self::Algorithm => "Algorithm",
- }
- }
-
- pub fn parse(environment_name: &str) -> Option<Self> {
- match environment_name {
- "figure" | "subfigure" => Some(Self::Figure),
- "table" | "subtable" => Some(Self::Table),
- "listing" | "lstlisting" => Some(Self::Listing),
- "algorithm" => Some(Self::Algorithm),
- _ => None,
- }
- }
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub enum OutlineContextItem {
- Section {
- prefix: &'static str,
- text: String,
- },
- Caption {
- kind: Option<OutlineCaptionKind>,
- text: String,
- },
- Theorem {
- kind: String,
- description: Option<String>,
- },
- Equation,
- Item,
-}
-
-#[derive(Debug, PartialEq, Eq, Clone)]
-pub struct OutlineContext {
- pub range: Range,
- pub number: Option<String>,
- pub item: OutlineContextItem,
-}
-
-impl OutlineContext {
- pub fn reference(&self) -> String {
- match (&self.number, &self.item) {
- (Some(number), OutlineContextItem::Section { prefix, text }) => {
- format!("{} {} ({})", prefix, number, text)
- }
- (Some(number), OutlineContextItem::Caption { kind: None, text }) => {
- format!("{} {}", number, text)
- }
- (
- Some(number),
- OutlineContextItem::Caption {
- kind: Some(kind),
- text,
- },
- ) => format!("{} {}: {}", kind.as_str(), number, text),
- (
- Some(number),
- OutlineContextItem::Theorem {
- kind,
- description: None,
- },
- ) => format!("{} {}", kind, number),
- (
- Some(number),
- OutlineContextItem::Theorem {
- kind,
- description: Some(description),
- },
- ) => format!("{} {} ({})", kind, number, description),
- (Some(number), OutlineContextItem::Equation) => format!("Equation ({})", number),
- (Some(number), OutlineContextItem::Item) => format!("Item {}", number),
- (None, OutlineContextItem::Section { prefix, text }) => {
- format!("{} ({})", prefix, text)
- }
- (None, OutlineContextItem::Caption { kind: None, text }) => text.clone(),
- (
- None,
- OutlineContextItem::Caption {
- kind: Some(kind),
- text,
- },
- ) => format!("{}: {}", kind.as_str(), text),
- (
- None,
- OutlineContextItem::Theorem {
- kind,
- description: None,
- },
- ) => kind.to_owned(),
- (
- None,
- OutlineContextItem::Theorem {
- kind,
- description: Some(description),
- },
- ) => format!("{} ({})", kind, description),
- (None, OutlineContextItem::Equation) => "Equation".to_owned(),
- (None, OutlineContextItem::Item) => "Item".to_owned(),
- }
- }
-
- pub fn detail(&self) -> Option<String> {
- match &self.item {
- OutlineContextItem::Section { .. }
- | OutlineContextItem::Theorem { .. }
- | OutlineContextItem::Equation
- | OutlineContextItem::Item => Some(self.reference()),
- OutlineContextItem::Caption {
- kind: Some(kind), ..
- } => Some(match &self.number {
- Some(number) => format!("{} {}", kind.as_str(), number),
- None => kind.as_str().to_owned(),
- }),
- OutlineContextItem::Caption { .. } => None,
- }
- }
-
- pub fn documentation(&self) -> MarkupContent {
- MarkupContent {
- kind: MarkupKind::PlainText,
- value: self.reference(),
- }
- }
-
- pub fn parse(view: &DocumentView, label: &LatexLabel, outline: &Outline) -> Option<Self> {
- if let SyntaxTree::Latex(tree) = &view.document.tree {
- Self::find_caption(view, label, tree)
- .or_else(|| Self::find_theorem(view, label, tree))
- .or_else(|| Self::find_equation(view, label, tree))
- .or_else(|| Self::find_item(view, label, tree))
- .or_else(|| Self::find_section(view, label, outline))
- } else {
- None
- }
- }
-
- fn find_caption(
- view: &DocumentView,
- label: &LatexLabel,
- tree: &LatexSyntaxTree,
- ) -> Option<Self> {
- let caption_env = tree
- .env
- .environments
- .iter()
- .filter(|env| env.left.name().map(LatexToken::text) != Some("document"))
- .find(|env| env.range().contains(label.start()))?;
-
- let caption = tree
- .structure
- .captions
- .iter()
- .find(|cap| tree.is_direct_child(caption_env, cap.start()))?;
-
- let caption_content = &caption.command.args[caption.index];
- let caption_text = extract_group(caption_content);
- let caption_kind = caption_env
- .left
- .name()
- .map(LatexToken::text)
- .and_then(OutlineCaptionKind::parse);
-
- Some(Self {
- range: caption_env.range(),
- number: Self::find_number(view, label),
- item: OutlineContextItem::Caption {
- kind: caption_kind,
- text: caption_text,
- },
- })
- }
-
- fn find_theorem(
- view: &DocumentView,
- label: &LatexLabel,
- tree: &LatexSyntaxTree,
- ) -> Option<Self> {
- let env = tree
- .env
- .environments
- .iter()
- .find(|env| env.range().contains(label.start()))?;
-
- let env_name = env.left.name().map(LatexToken::text)?;
-
- for document in &view.related_documents {
- if let SyntaxTree::Latex(tree) = &document.tree {
- for definition in &tree.math.theorem_definitions {
- if env_name == definition.name().text() {
- let kind = definition
- .command
- .args
- .get(definition.index + 1)
- .map(|content| extract_group(&content))
- .unwrap_or_else(|| titlelize(env_name));
-
- let description = env
- .left
- .command
- .options
- .get(0)
- .map(|content| extract_group(&content));
-
- return Some(Self {
- range: env.range(),
- number: Self::find_number(view, label),
- item: OutlineContextItem::Theorem { kind, description },
- });
- }
- }
- }
- }
- None
- }
-
- fn find_equation(
- view: &DocumentView,
- label: &LatexLabel,
- tree: &LatexSyntaxTree,
- ) -> Option<Self> {
- tree.env
- .environments
- .iter()
- .filter(|env| env.left.is_math())
- .map(|env| env.range())
- .find(|range| range.contains(label.start()))
- .map(|range| Self {
- range,
- number: Self::find_number(view, label),
- item: OutlineContextItem::Equation,
- })
- }
-
- fn find_item(view: &DocumentView, label: &LatexLabel, tree: &LatexSyntaxTree) -> Option<Self> {
- struct LatexItemNode<'a> {
- item: &'a LatexItem,
- range: Range,
- }
-
- let enumeration = tree
- .env
- .environments
- .iter()
- .find(|env| env.left.is_enum() && env.range().contains(label.start()))?;
-
- let mut item_nodes: Vec<_> = tree
- .structure
- .items
- .iter()
- .filter(|item| tree.is_enumeration_item(enumeration, item))
- .map(|item| LatexItemNode {
- item,
- range: Range::default(),
- })
- .collect();
-
- for i in 0..item_nodes.len() {
- let start = item_nodes[i].item.start();
- let end = item_nodes
- .get(i + 1)
- .map(|node| node.item.start())
- .unwrap_or_else(|| enumeration.right.start());
- item_nodes[i].range = Range::new(start, end);
- }
-
- let node = item_nodes
- .iter()
- .find(|node| node.range.contains(label.start()))?;
-
- let number = node.item.name().or_else(|| Self::find_number(view, label));
-
- Some(Self {
- range: enumeration.range(),
- number,
- item: OutlineContextItem::Item,
- })
- }
-
- fn find_section(view: &DocumentView, label: &LatexLabel, outline: &Outline) -> Option<Self> {
- let section = outline.find(&view.document.uri, label.start())?;
- let content = &section.command.args[section.index];
- Some(Self {
- range: section.range(),
- number: Self::find_number(view, label),
- item: OutlineContextItem::Section {
- prefix: section.prefix,
- text: extract_group(content),
- },
- })
- }
-
- pub fn find_number(view: &DocumentView, label: &LatexLabel) -> Option<String> {
- let label_names = label.names();
- if label_names.len() != 1 {
- return None;
- }
-
- for document in &view.related_documents {
- if let SyntaxTree::Latex(tree) = &document.tree {
- for numbering in &tree.structure.label_numberings {
- if numbering.name().text() == label_names[0].text() {
- return Some(numbering.number.clone());
- }
- }
- }
- }
- None
- }
-}
diff --git a/support/texlab/src/workspace/uri.rs b/support/texlab/src/workspace/uri.rs
deleted file mode 100644
index 7fc24d238f..0000000000
--- a/support/texlab/src/workspace/uri.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-use lsp_types::*;
-use serde::{Deserialize, Serialize};
-use std::fmt;
-use std::hash::{Hash, Hasher};
-use std::ops::Deref;
-use std::path::Path;
-
-#[derive(Debug, Eq, Clone, Serialize, Deserialize)]
-pub struct Uri(Url);
-
-impl Uri {
- pub fn from_file_path<P: AsRef<Path>>(path: P) -> Result<Self, ()> {
- Url::from_file_path(path).map(|url| url.into())
- }
-}
-
-impl PartialEq for Uri {
- fn eq(&self, other: &Self) -> bool {
- if cfg!(windows) {
- self.as_str().to_lowercase() == other.as_str().to_lowercase()
- } else {
- self.as_str() == other.as_str()
- }
- }
-}
-
-impl Hash for Uri {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.as_str().to_lowercase().hash(state);
- }
-}
-
-impl Deref for Uri {
- type Target = Url;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
-}
-
-impl From<Url> for Uri {
- fn from(url: Url) -> Self {
- Uri(url)
- }
-}
-
-impl Into<Url> for Uri {
- fn into(self) -> Url {
- self.0
- }
-}
-
-impl fmt::Display for Uri {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
- }
-}
-
-pub trait AsUri {
- fn as_uri(&self) -> Uri;
-}
-
-impl AsUri for TextDocumentIdentifier {
- fn as_uri(&self) -> Uri {
- self.uri.clone().into()
- }
-}
-
-impl AsUri for TextDocumentPositionParams {
- fn as_uri(&self) -> Uri {
- self.text_document.as_uri()
- }
-}