diff options
author | Norbert Preining <norbert@preining.info> | 2023-05-21 03:01:40 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2023-05-21 03:01:40 +0000 |
commit | acbf029eb7f81c05b715461ab1ce2ab7e4f6ce09 (patch) | |
tree | 1b2e5899af7b651ddc4cb4ff54c08b182cd344e7 /support/texlab/crates | |
parent | 9cdcfcf8d9333b1d9b34b61ddc21910bbcc04491 (diff) |
CTAN sync 202305210301
Diffstat (limited to 'support/texlab/crates')
14 files changed, 215 insertions, 82 deletions
diff --git a/support/texlab/crates/base-db/src/document.rs b/support/texlab/crates/base-db/src/document.rs index cc1a0d44e5..8b6e07fb0f 100644 --- a/support/texlab/crates/base-db/src/document.rs +++ b/support/texlab/crates/base-db/src/document.rs @@ -1,14 +1,13 @@ use std::path::PathBuf; use distro::Language; -use rowan::TextSize; use syntax::{bibtex, latex, BuildError}; use url::Url; use crate::{ diagnostics::{self, Diagnostic}, semantics, - util::LineIndex, + util::{LineCol, LineIndex}, Config, }; @@ -26,7 +25,7 @@ pub struct Document { pub text: String, pub line_index: LineIndex, pub owner: Owner, - pub cursor: TextSize, + pub cursor: LineCol, pub language: Language, pub data: DocumentData, pub diagnostics: Vec<Diagnostic>, @@ -38,7 +37,7 @@ impl Document { text: String, language: Language, owner: Owner, - cursor: TextSize, + cursor: LineCol, config: &Config, ) -> Self { let dir = uri.join(".").unwrap(); diff --git a/support/texlab/crates/base-db/src/semantics/tex.rs b/support/texlab/crates/base-db/src/semantics/tex.rs index 6e238737b0..46c805ba8a 100644 --- a/support/texlab/crates/base-db/src/semantics/tex.rs +++ b/support/texlab/crates/base-db/src/semantics/tex.rs @@ -92,10 +92,12 @@ impl Semantics { } let Some(path) = import.file().and_then(|path| path.key()) else { return }; + let text = format!("{base_dir}{}", path.to_string()); + let range = latex::small_range(&path); self.links.push(Link { kind: LinkKind::Tex, - path: Span::from(&path), + path: Span { text, range }, base_dir: Some(base_dir), }); } diff --git a/support/texlab/crates/base-db/src/workspace.rs b/support/texlab/crates/base-db/src/workspace.rs index 1872c33df1..581d93cfc3 100644 --- a/support/texlab/crates/base-db/src/workspace.rs +++ b/support/texlab/crates/base-db/src/workspace.rs @@ -5,11 +5,12 @@ use std::{ use distro::{Distro, Language}; use itertools::Itertools; -use rowan::{TextRange, TextSize}; +use rowan::TextRange; use rustc_hash::FxHashSet; +use text_size::TextLen; use url::Url; -use crate::{graph, Config, Document, DocumentData, Owner}; +use crate::{graph, util::LineCol, Config, Document, DocumentData, Owner}; #[derive(Debug, Default)] pub struct Workspace { @@ -51,7 +52,7 @@ impl Workspace { text: String, language: Language, owner: Owner, - cursor: TextSize, + cursor: LineCol, ) { log::debug!("Opening document {uri}..."); self.documents.remove(&uri); @@ -74,19 +75,26 @@ impl Workspace { Cow::Owned(text) => text, }; - Ok(self.open(uri, text, language, owner, TextSize::default())) + Ok(self.open(uri, text, language, owner, LineCol { line: 0, col: 0 })) } pub fn edit(&mut self, uri: &Url, delete: TextRange, insert: &str) -> Option<()> { let document = self.lookup(uri)?; let mut text = document.text.clone(); + let cursor = if delete.len() == text.text_len() { + let line = document.cursor.line.min(text.lines().count() as u32); + LineCol { line, col: 0 } + } else { + document.line_index.line_col(delete.start()) + }; + text.replace_range(std::ops::Range::<usize>::from(delete), insert); self.open( document.uri.clone(), text, document.language, Owner::Client, - delete.start(), + cursor, ); Some(()) @@ -184,9 +192,10 @@ impl Workspace { self.folders = folders; } - pub fn set_cursor(&mut self, uri: &Url, cursor: TextSize) -> Option<()> { + pub fn set_cursor(&mut self, uri: &Url, cursor: LineCol) -> Option<()> { let mut document = self.lookup(uri)?.clone(); document.cursor = cursor; + self.documents.remove(&document); self.documents.insert(document); Some(()) } diff --git a/support/texlab/crates/commands/Cargo.toml b/support/texlab/crates/commands/Cargo.toml index ea7358364f..68d034675e 100644 --- a/support/texlab/crates/commands/Cargo.toml +++ b/support/texlab/crates/commands/Cargo.toml @@ -12,6 +12,7 @@ base-db = { path = "../base-db" } bstr = "1.4.0" crossbeam-channel = "0.5.8" itertools = "0.10.5" +libc = "0.2.144" log = "0.4.17" rowan = "0.15.11" rustc-hash = "1.1.0" diff --git a/support/texlab/crates/commands/src/build.rs b/support/texlab/crates/commands/src/build.rs index 3d93601e39..47e156ef72 100644 --- a/support/texlab/crates/commands/src/build.rs +++ b/support/texlab/crates/commands/src/build.rs @@ -1,7 +1,7 @@ use std::{ io::{BufReader, Read}, path::{Path, PathBuf}, - process::{ExitStatus, Stdio}, + process::{Child, Stdio}, thread::{self, JoinHandle}, }; @@ -64,7 +64,7 @@ impl BuildCommand { }) } - pub fn run(self, sender: Sender<String>) -> Result<ExitStatus, BuildError> { + pub fn spawn(self, sender: Sender<String>) -> Result<Child, BuildError> { log::debug!( "Spawning compiler {} {:#?} in directory {}", self.program, @@ -72,19 +72,56 @@ impl BuildCommand { self.working_dir.display() ); - let mut process = std::process::Command::new(&self.program) - .args(self.args) + let mut process = self.spawn_internal()?; + track_output(process.stderr.take().unwrap(), sender.clone()); + track_output(process.stdout.take().unwrap(), sender); + Ok(process) + } + + #[cfg(windows)] + fn spawn_internal(&self) -> Result<Child, BuildError> { + std::process::Command::new(&self.program) + .args(self.args.clone()) .stdin(Stdio::null()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .current_dir(&self.working_dir) - .spawn()?; + .spawn() + .map_err(Into::into) + } - track_output(process.stderr.take().unwrap(), sender.clone()); - track_output(process.stdout.take().unwrap(), sender); + #[cfg(unix)] + fn spawn_internal(&self) -> Result<Child, BuildError> { + use std::os::unix::process::CommandExt; + std::process::Command::new(&self.program) + .args(self.args.clone()) + .stdin(Stdio::null()) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .current_dir(&self.working_dir) + .process_group(0) + .spawn() + .map_err(Into::into) + } + + #[cfg(windows)] + pub fn cancel(pid: u32) -> std::io::Result<bool> { + Ok(std::process::Command::new("taskkill") + .arg("/PID") + .arg(pid.to_string()) + .arg("/F") + .arg("/T") + .status()? + .success()) + } + + #[cfg(not(windows))] + pub fn cancel(pid: u32) -> Result<bool> { + unsafe { + libc::killpg(pid as libc::pid_t, libc::SIGTERM); + } - let status = process.wait(); - Ok(status?) + Ok(true) } } diff --git a/support/texlab/crates/commands/src/fwd_search.rs b/support/texlab/crates/commands/src/fwd_search.rs index c458f33cf1..fc0a130f86 100644 --- a/support/texlab/crates/commands/src/fwd_search.rs +++ b/support/texlab/crates/commands/src/fwd_search.rs @@ -32,6 +32,7 @@ pub enum ForwardSearchError { LaunchViewer(#[from] std::io::Error), } +#[derive(Debug)] pub struct ForwardSearch { program: String, args: Vec<String>, @@ -79,7 +80,7 @@ impl ForwardSearch { let tex_path = tex_path.to_string_lossy().into_owned(); let pdf_path = pdf_path.to_string_lossy().into_owned(); - let line = line.unwrap_or_else(|| child.line_index.line_col(child.cursor).line); + let line = line.unwrap_or_else(|| child.cursor.line); let line = (line + 1).to_string(); let program = config.program.clone(); diff --git a/support/texlab/crates/symbols/src/workspace/sort.rs b/support/texlab/crates/symbols/src/workspace/sort.rs index bc30728da9..0e24c25ac9 100644 --- a/support/texlab/crates/symbols/src/workspace/sort.rs +++ b/support/texlab/crates/symbols/src/workspace/sort.rs @@ -38,9 +38,8 @@ impl<'a> From<&'a Workspace> for ProjectOrdering<'a> { #[cfg(test)] mod tests { - use base_db::Owner; + use base_db::{util::LineCol, Owner}; use distro::Language; - use rowan::TextSize; use super::{ProjectOrdering, Url, Workspace}; @@ -57,7 +56,7 @@ mod tests { String::new(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); workspace.open( @@ -65,7 +64,7 @@ mod tests { String::new(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); workspace.open( @@ -73,7 +72,7 @@ mod tests { r#"\documentclass{article}\include{b}\include{a}"#.to_string(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); let ordering = ProjectOrdering::from(&workspace); @@ -95,7 +94,7 @@ mod tests { String::new(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); workspace.open( @@ -103,7 +102,7 @@ mod tests { r#"\include{a}"#.to_string(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); workspace.open( @@ -111,7 +110,7 @@ mod tests { r#"\begin{documnent}\include{b}\end{document}"#.to_string(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); let ordering = ProjectOrdering::from(&workspace); @@ -132,7 +131,7 @@ mod tests { r#"\begin{document}\include{b}\end{document}"#.to_string(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); workspace.open( @@ -140,7 +139,7 @@ mod tests { r#"\include{a}"#.to_string(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); workspace.open( @@ -148,7 +147,7 @@ mod tests { r#"\include{a}"#.to_string(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); let ordering = ProjectOrdering::from(&workspace); @@ -169,7 +168,7 @@ mod tests { r#"\begin{document}\include{b}\end{document}"#.to_string(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); workspace.open( @@ -177,7 +176,7 @@ mod tests { String::new(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); workspace.open( @@ -185,7 +184,7 @@ mod tests { String::new(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); workspace.open( @@ -193,7 +192,7 @@ mod tests { r#"\begin{document}\include{c}\end{document}"#.to_string(), Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); let ordering = ProjectOrdering::from(&workspace); diff --git a/support/texlab/crates/test-utils/src/fixture.rs b/support/texlab/crates/test-utils/src/fixture.rs index 2935b4f519..1a5071b2db 100644 --- a/support/texlab/crates/test-utils/src/fixture.rs +++ b/support/texlab/crates/test-utils/src/fixture.rs @@ -1,6 +1,6 @@ use std::path::PathBuf; -use base_db::{Owner, Workspace}; +use base_db::{util::LineCol, Owner, Workspace}; use rowan::{TextRange, TextSize}; use url::Url; @@ -35,7 +35,7 @@ impl Fixture { document.text.clone(), language, Owner::Client, - TextSize::from(0), + LineCol { line: 0, col: 0 }, ); } diff --git a/support/texlab/crates/texlab/Cargo.toml b/support/texlab/crates/texlab/Cargo.toml index dc1eb5b913..821ecb9813 100644 --- a/support/texlab/crates/texlab/Cargo.toml +++ b/support/texlab/crates/texlab/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "texlab" description = "LaTeX Language Server" -version = "5.5.1" +version = "5.6.0" license.workspace = true readme = "README.md" authors.workspace = true diff --git a/support/texlab/crates/texlab/benches/bench_main.rs b/support/texlab/crates/texlab/benches/bench_main.rs index 8e69e1e5a4..47b302e313 100644 --- a/support/texlab/crates/texlab/benches/bench_main.rs +++ b/support/texlab/crates/texlab/benches/bench_main.rs @@ -1,9 +1,8 @@ -use base_db::{Owner, Workspace}; +use base_db::{util::LineCol, Owner, Workspace}; use criterion::{black_box, criterion_group, criterion_main, Criterion}; use distro::Language; use lsp_types::{ClientCapabilities, Position, Url}; use parser::{parse_latex, SyntaxConfig}; -use rowan::TextSize; const CODE: &str = include_str!("../../../texlab.tex"); @@ -22,7 +21,7 @@ fn criterion_benchmark(c: &mut Criterion) { text, Language::Tex, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); let client_capabilities = ClientCapabilities::default(); diff --git a/support/texlab/crates/texlab/src/main.rs b/support/texlab/crates/texlab/src/main.rs index ed807f8799..d588a568bc 100644 --- a/support/texlab/crates/texlab/src/main.rs +++ b/support/texlab/crates/texlab/src/main.rs @@ -50,9 +50,6 @@ fn setup_logger(opts: Opts) { let logger = fern::Dispatch::new() .format(|out, message, record| out.finish(format_args!("{} - {}", record.level(), message))) .level(verbosity_level) - .filter(|metadata| { - metadata.target().contains("texlab") || metadata.target().contains("lsp_server") - }) .chain(io::stderr()); let logger = match opts.log_file { diff --git a/support/texlab/crates/texlab/src/server.rs b/support/texlab/crates/texlab/src/server.rs index 6b118655bc..0370f00192 100644 --- a/support/texlab/crates/texlab/src/server.rs +++ b/support/texlab/crates/texlab/src/server.rs @@ -10,15 +10,14 @@ use std::{ }; use anyhow::Result; -use base_db::{Config, Owner, Workspace}; +use base_db::{util::LineCol, Config, Owner, Workspace}; use commands::{BuildCommand, CleanCommand, CleanTarget, ForwardSearch}; use crossbeam_channel::{Receiver, Sender}; use distro::{Distro, Language}; -use itertools::{FoldWhile, Itertools}; use lsp_server::{Connection, ErrorCode, Message, RequestId}; use lsp_types::{notification::*, request::*, *}; use parking_lot::{Mutex, RwLock}; -use rowan::{ast::AstNode, TextSize}; +use rowan::ast::AstNode; use rustc_hash::{FxHashMap, FxHashSet}; use serde::{de::DeserializeOwned, Serialize}; use syntax::bibtex; @@ -67,6 +66,7 @@ pub struct Server { chktex_diagnostics: FxHashMap<Url, Vec<Diagnostic>>, watcher: FileWatcher, pool: ThreadPool, + pending_builds: Arc<Mutex<FxHashSet<u32>>>, } impl Server { @@ -86,6 +86,7 @@ impl Server { chktex_diagnostics: Default::default(), watcher, pool: threadpool::Builder::new().build(), + pending_builds: Default::default(), } } @@ -360,7 +361,7 @@ impl Server { params.text_document.text, language, Owner::Client, - TextSize::default(), + LineCol { line: 0, col: 0 }, ); self.update_workspace(); @@ -387,28 +388,18 @@ impl Server { workspace.edit(&uri, range, &change.text); } None => { + let new_line = document.cursor.line.min(change.text.lines().count() as u32); let language = document.language; - let line_col = document.line_index.line_col(document.cursor); - - let (_, new_cursor) = change - .text - .lines() - .fold_while((0, 0), |(number, index), line| { - if number == line_col.line { - FoldWhile::Done((number, index)) - } else { - itertools::FoldWhile::Continue((number + 1, index + line.len())) - } - }) - .into_inner(); - drop(document); workspace.open( uri.clone(), change.text, language, Owner::Client, - TextSize::from(new_cursor as u32), + LineCol { + line: new_line as u32, + col: 0, + }, ); } }; @@ -509,6 +500,9 @@ impl Server { let position = params.text_document_position.position; let client_capabilities = Arc::clone(&self.client_capabilities); let client_info = self.client_info.clone(); + + self.update_cursor(&uri, position); + self.run_query(id, move |db| { completion::complete( db, @@ -521,6 +515,16 @@ impl Server { Ok(()) } + fn update_cursor(&self, uri: &Url, position: Position) { + self.workspace.write().set_cursor( + uri, + LineCol { + line: position.line, + col: 0, + }, + ); + } + fn completion_resolve(&self, id: RequestId, mut item: CompletionItem) -> Result<()> { self.run_query(id, move |workspace| { match item @@ -582,17 +586,9 @@ impl Server { let mut uri = params.text_document_position_params.text_document.uri; normalize_uri(&mut uri); - let workspace = self.workspace.read(); - if let Some(document) = workspace.lookup(&uri) { - let position = document - .line_index - .offset_lsp(params.text_document_position_params.position); - - drop(workspace); - self.workspace.write().set_cursor(&uri, position); - } - let position = params.text_document_position_params.position; + self.update_cursor(&uri, position); + self.run_query(id, move |db| hover::find(db, &uri, position)); Ok(()) } @@ -672,6 +668,16 @@ impl Server { self.client .send_response(lsp_server::Response::new_ok(id, dot))?; } + "texlab.cancelBuild" => { + let pending_builds = Arc::clone(&self.pending_builds); + self.run_fallible(id, move || { + for pid in pending_builds.lock().drain() { + let _ = BuildCommand::cancel(pid); + } + + Ok(()) + }); + } _ => { self.client .send_error( @@ -728,6 +734,8 @@ impl Server { let command = BuildCommand::new(&workspace, &uri); let internal = self.internal_tx.clone(); let progress = self.client_capabilities.has_work_done_progress_support(); + let pending_builds = Arc::clone(&self.pending_builds); + self.pool.execute(move || { let guard = LOCK.lock(); @@ -738,14 +746,29 @@ impl Server { None }; - let status = match command.and_then(|command| command.run(sender)) { - Ok(status) if status.success() => BuildStatus::SUCCESS, - Ok(_) => BuildStatus::ERROR, - Err(why) => { + let status = command + .and_then(|command| { + let mut process = command.spawn(sender)?; + let pid = process.id(); + pending_builds.lock().insert(pid); + let result = process.wait(); + + let status = if pending_builds.lock().remove(&pid) { + if result?.success() { + BuildStatus::SUCCESS + } else { + BuildStatus::ERROR + } + } else { + BuildStatus::CANCELLED + }; + + Ok(status) + }) + .unwrap_or_else(|why| { log::error!("Failed to compile document \"{uri}\": {why}"); BuildStatus::FAILURE - } - }; + }); drop(progress_reporter); drop(guard); @@ -755,7 +778,7 @@ impl Server { let _ = client.send_response(lsp_server::Response::new_ok(id, result)); } - if fwd_search_after { + if fwd_search_after && status != BuildStatus::CANCELLED { let _ = internal.send(InternalMessage::ForwardSearch(uri, params.position)); } }); diff --git a/support/texlab/crates/texlab/tests/lsp/text_document/completion.rs b/support/texlab/crates/texlab/tests/lsp/text_document/completion.rs index 78df56c583..e76f4360bd 100644 --- a/support/texlab/crates/texlab/tests/lsp/text_document/completion.rs +++ b/support/texlab/crates/texlab/tests/lsp/text_document/completion.rs @@ -786,7 +786,7 @@ fn test_project_resolution_import() { r#" %! main.tex \documentclass{article} -\import{sub}{sub/sub.tex} +\import{sub}{sub.tex} \lipsu | ^^^^^ @@ -864,3 +864,51 @@ fn issue_883() { % Comment"# )) } + +#[test] +fn issue_885() { + assert_json_snapshot!(complete( + r#" +%! main.tex +\documentclass{book} +\usepackage{import} +\begin{document} +\subincludefrom{part 1}{main} +\include{part 2/main} + +\ref{sec} + | + ^^^ +\end{document} + +%! part 1/main.tex +\part{1} +\label{part 1} +\subimport{chapter 1}{main} + +%! part 1/chapter 1/main.tex +\chapter{1} +\label{chapter 1} +\subimport{./}{section 1} +%\subimport{}{section 1} + +%! part 1/chapter 1/section 1.tex +\section{1} +\label{section 1} + +%! part 2/main.tex +\part{2} +\label{part 2} +\input{part 2/chapter 2/main} + +%! part 2/chapter 2/main.tex +\chapter{2} +\label{chapter 2} +\input{part 2/chapter 2/section 2} + +%! part 2/chapter 2/section 2.tex +\section{2} +\label{section 2} +"# + )); +} diff --git a/support/texlab/crates/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__issue_885.snap b/support/texlab/crates/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__issue_885.snap new file mode 100644 index 0000000000..2f286288b9 --- /dev/null +++ b/support/texlab/crates/texlab/tests/lsp/text_document/snapshots/lsp__text_document__completion__issue_885.snap @@ -0,0 +1,18 @@ +--- +source: crates/texlab/tests/lsp/text_document/completion.rs +expression: "complete(r#\"\n%! main.tex\n\\documentclass{book}\n\\usepackage{import}\n\\begin{document}\n\\subincludefrom{part 1}{main}\n\\include{part 2/main}\n\n\\ref{sec}\n |\n ^^^\n\\end{document}\n\n%! part 1/main.tex\n\\part{1}\n\\label{part 1}\n\\subimport{chapter 1}{main}\n\n%! part 1/chapter 1/main.tex\n\\chapter{1}\n\\label{chapter 1}\n\\subimport{./}{section 1}\n%\\subimport{}{section 1}\n\n%! part 1/chapter 1/section 1.tex\n\\section{1}\n\\label{section 1}\n\n%! part 2/main.tex\n\\part{2}\n\\label{part 2}\n\\input{part 2/chapter 2/main}\n\n%! part 2/chapter 2/main.tex\n\\chapter{2}\n\\label{chapter 2}\n\\input{part 2/chapter 2/section 2}\n\n%! part 2/chapter 2/section 2.tex\n\\section{2}\n\\label{section 2}\n\"#)" +--- +[ + { + "label": "section 1", + "detail": "Section (1)", + "preselect": false, + "filterText": "section 1 Section (1)" + }, + { + "label": "section 2", + "detail": "Section (2)", + "preselect": false, + "filterText": "section 2 Section (2)" + } +] |