mirror of
https://github.com/latex-lsp/texlab.git
synced 2025-12-23 09:19:21 +00:00
Refactor configuration management module
This commit is contained in:
parent
09e6050f37
commit
36ccaf8ab3
9 changed files with 495 additions and 31 deletions
|
|
@ -35,7 +35,7 @@ serde_json = "1.0"
|
|||
serde_repr = "0.1"
|
||||
stderrlog = "0.4"
|
||||
tempfile = "3.1"
|
||||
tokio = { version = "0.2", features = ["fs", "macros", "process", "time"] }
|
||||
tokio = { version = "0.2", features = ["fs", "io-std", "macros", "process", "time"] }
|
||||
tokio-util = { version = "0.2", features = ["codec"] }
|
||||
url = "2.1"
|
||||
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ async fn criterion_benchmark(criterion: &mut Criterion) {
|
|||
uri: &uri,
|
||||
resolver: &resolver,
|
||||
options: &options,
|
||||
cwd: &cwd,
|
||||
current_dir: &cwd,
|
||||
};
|
||||
|
||||
criterion.bench_function("symbols.tex", |b| b.iter(|| latex::open(params)));
|
||||
|
|
|
|||
103
src/config.rs
Normal file
103
src/config.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
use crate::protocol::*;
|
||||
use futures::lock::Mutex;
|
||||
use log::{error, warn};
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ConfigManager<C> {
|
||||
client: Arc<C>,
|
||||
client_capabilities: Arc<ClientCapabilities>,
|
||||
options: Mutex<Options>,
|
||||
}
|
||||
|
||||
impl<C: LspClient + Send + Sync + 'static> ConfigManager<C> {
|
||||
pub fn new(client: Arc<C>, client_capabilities: Arc<ClientCapabilities>) -> Self {
|
||||
Self {
|
||||
client,
|
||||
client_capabilities,
|
||||
options: Mutex::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(&self) -> Options {
|
||||
self.options.lock().await.clone()
|
||||
}
|
||||
|
||||
pub async fn register(&self) {
|
||||
if !self.client_capabilities.has_pull_configuration_support()
|
||||
&& self.client_capabilities.has_push_configuration_support()
|
||||
{
|
||||
let registration = Registration {
|
||||
id: "pull-config".into(),
|
||||
method: "workspace/didChangeConfiguration".into(),
|
||||
register_options: None,
|
||||
};
|
||||
let params = RegistrationParams {
|
||||
registrations: vec![registration],
|
||||
};
|
||||
|
||||
if let Err(why) = self.client.register_capability(params).await {
|
||||
error!(
|
||||
"Failed to register \"workspace/didChangeConfiguration\": {}",
|
||||
why.message
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn push(&self, options: serde_json::Value) {
|
||||
match serde_json::from_value(options) {
|
||||
Ok(options) => {
|
||||
*self.options.lock().await = options;
|
||||
}
|
||||
Err(why) => {
|
||||
error!("Invalid configuration: {}", why);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn pull(&self) -> bool {
|
||||
if self.client_capabilities.has_pull_configuration_support() {
|
||||
let latex = self.pull_section("latex").await;
|
||||
let bibtex = self.pull_section("bibtex").await;
|
||||
|
||||
let new_options = Options {
|
||||
latex: Some(latex),
|
||||
bibtex: Some(bibtex),
|
||||
};
|
||||
let mut old_options = self.options.lock().await;
|
||||
let has_changed = *old_options != new_options;
|
||||
*old_options = new_options;
|
||||
has_changed
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
async fn pull_section<T: DeserializeOwned + Default>(&self, section: &str) -> T {
|
||||
let params = ConfigurationParams {
|
||||
items: vec![ConfigurationItem {
|
||||
section: Some(section.into()),
|
||||
scope_uri: None,
|
||||
}],
|
||||
};
|
||||
|
||||
match self.client.configuration(params).await {
|
||||
Ok(json) => match serde_json::from_value::<Vec<T>>(json) {
|
||||
Ok(config) => config.into_iter().next().unwrap(),
|
||||
Err(_) => {
|
||||
warn!("Invalid configuration: {}", section);
|
||||
T::default()
|
||||
}
|
||||
},
|
||||
Err(why) => {
|
||||
error!(
|
||||
"Retrieving configuration for {} failed: {}",
|
||||
section, why.message
|
||||
);
|
||||
T::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
pub mod components;
|
||||
pub mod config;
|
||||
pub mod jsonrpc;
|
||||
pub mod protocol;
|
||||
pub mod server;
|
||||
pub mod syntax;
|
||||
pub mod tex;
|
||||
pub mod workspace;
|
||||
|
|
|
|||
41
src/main.rs
41
src/main.rs
|
|
@ -1,12 +1,17 @@
|
|||
use clap::{app_from_crate, crate_authors, crate_description, crate_name, crate_version, Arg};
|
||||
use std::error;
|
||||
use futures::{channel::mpsc, prelude::*};
|
||||
use std::{env, error, sync::Arc};
|
||||
use stderrlog::{ColorChoice, Timestamp};
|
||||
use texlab::tex::Distribution;
|
||||
use texlab::{
|
||||
jsonrpc::MessageHandler,
|
||||
protocol::{LatexLspClient, LspCodec},
|
||||
server::LatexLspServer,
|
||||
tex::Distribution,
|
||||
};
|
||||
use tokio_util::codec::{FramedRead, FramedWrite};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn error::Error>> {
|
||||
let _ = Distribution::detect().await;
|
||||
|
||||
let matches = app_from_crate!()
|
||||
.author("")
|
||||
.arg(
|
||||
|
|
@ -32,5 +37,33 @@ async fn main() -> Result<(), Box<dyn error::Error>> {
|
|||
.init()
|
||||
.expect("failed to initialize logger");
|
||||
|
||||
let mut stdin = FramedRead::new(tokio::io::stdin(), LspCodec);
|
||||
let (stdout_tx, mut stdout_rx) = mpsc::channel(0);
|
||||
|
||||
let distro = Distribution::detect().await;
|
||||
let client = Arc::new(LatexLspClient::new(stdout_tx.clone()));
|
||||
let server = Arc::new(LatexLspServer::new(
|
||||
Arc::new(distro),
|
||||
Arc::clone(&client),
|
||||
env::current_dir().expect("failed to get working directory"),
|
||||
));
|
||||
let mut handler = MessageHandler {
|
||||
server,
|
||||
client,
|
||||
output: stdout_tx,
|
||||
};
|
||||
|
||||
tokio::spawn(async move {
|
||||
let mut stdout = FramedWrite::new(tokio::io::stdout(), LspCodec);
|
||||
loop {
|
||||
let message = stdout_rx.next().await.unwrap();
|
||||
stdout.send(message).await.unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
while let Some(json) = stdin.next().await {
|
||||
handler.handle(&json.unwrap()).await;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
301
src/server.rs
Normal file
301
src/server.rs
Normal file
|
|
@ -0,0 +1,301 @@
|
|||
use crate::{
|
||||
components::COMPONENT_DATABASE,
|
||||
config::ConfigManager,
|
||||
jsonrpc::{server::Result, Middleware},
|
||||
protocol::*,
|
||||
tex::Distribution,
|
||||
workspace::Workspace,
|
||||
};
|
||||
use futures::lock::Mutex;
|
||||
use futures_boxed::boxed;
|
||||
use jsonrpc_derive::{jsonrpc_method, jsonrpc_server};
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use std::{mem, path::PathBuf, sync::Arc};
|
||||
|
||||
pub struct LatexLspServer<C> {
|
||||
distro: Arc<Box<dyn Distribution + Send + Sync>>,
|
||||
client: Arc<C>,
|
||||
client_capabilities: OnceCell<Arc<ClientCapabilities>>,
|
||||
config_manager: OnceCell<ConfigManager<C>>,
|
||||
action_manager: ActionManager,
|
||||
workspace: Workspace,
|
||||
}
|
||||
|
||||
#[jsonrpc_server]
|
||||
impl<C: LspClient + Send + Sync + 'static> LatexLspServer<C> {
|
||||
pub fn new(
|
||||
distro: Arc<Box<dyn Distribution + Send + Sync>>,
|
||||
client: Arc<C>,
|
||||
current_dir: PathBuf,
|
||||
) -> Self {
|
||||
let workspace = Workspace::new(Arc::clone(&distro), current_dir);
|
||||
Self {
|
||||
distro,
|
||||
client,
|
||||
client_capabilities: OnceCell::new(),
|
||||
config_manager: OnceCell::new(),
|
||||
action_manager: ActionManager::default(),
|
||||
workspace,
|
||||
}
|
||||
}
|
||||
|
||||
fn client_capabilities(&self) -> Arc<ClientCapabilities> {
|
||||
Arc::clone(
|
||||
self.client_capabilities
|
||||
.get()
|
||||
.expect("initialize has not been called"),
|
||||
)
|
||||
}
|
||||
|
||||
fn config_manager(&self) -> &ConfigManager<C> {
|
||||
self.config_manager
|
||||
.get()
|
||||
.expect("initialize has not been called")
|
||||
}
|
||||
|
||||
#[jsonrpc_method("initialize", kind = "request")]
|
||||
pub async fn initialize(&self, params: InitializeParams) -> Result<InitializeResult> {
|
||||
self.client_capabilities
|
||||
.set(Arc::new(params.capabilities))
|
||||
.expect("initialize was called two times");
|
||||
|
||||
let _ = self.config_manager.set(ConfigManager::new(
|
||||
Arc::clone(&self.client),
|
||||
self.client_capabilities(),
|
||||
));
|
||||
|
||||
let capabilities = ServerCapabilities {
|
||||
text_document_sync: Some(TextDocumentSyncCapability::Options(
|
||||
TextDocumentSyncOptions {
|
||||
open_close: Some(true),
|
||||
change: Some(TextDocumentSyncKind::Full),
|
||||
will_save: None,
|
||||
will_save_wait_until: None,
|
||||
save: Some(SaveOptions {
|
||||
include_text: Some(false),
|
||||
}),
|
||||
},
|
||||
)),
|
||||
hover_provider: Some(true),
|
||||
completion_provider: Some(CompletionOptions {
|
||||
resolve_provider: Some(true),
|
||||
trigger_characters: Some(vec![
|
||||
"\\".into(),
|
||||
"{".into(),
|
||||
"}".into(),
|
||||
"@".into(),
|
||||
"/".into(),
|
||||
" ".into(),
|
||||
]),
|
||||
}),
|
||||
signature_help_provider: None,
|
||||
definition_provider: Some(true),
|
||||
type_definition_provider: None,
|
||||
implementation_provider: None,
|
||||
references_provider: Some(true),
|
||||
document_highlight_provider: Some(true),
|
||||
document_symbol_provider: Some(true),
|
||||
workspace_symbol_provider: Some(true),
|
||||
code_action_provider: None,
|
||||
code_lens_provider: None,
|
||||
document_formatting_provider: Some(true),
|
||||
document_range_formatting_provider: None,
|
||||
document_on_type_formatting_provider: None,
|
||||
rename_provider: Some(RenameProviderCapability::Options(RenameOptions {
|
||||
prepare_provider: Some(true),
|
||||
})),
|
||||
document_link_provider: Some(DocumentLinkOptions {
|
||||
resolve_provider: Some(false),
|
||||
}),
|
||||
color_provider: None,
|
||||
folding_range_provider: Some(FoldingRangeProviderCapability::Simple(true)),
|
||||
execute_command_provider: None,
|
||||
workspace: None,
|
||||
selection_range_provider: None,
|
||||
};
|
||||
|
||||
Lazy::force(&COMPONENT_DATABASE);
|
||||
Ok(InitializeResult { capabilities })
|
||||
}
|
||||
|
||||
#[jsonrpc_method("initialized", kind = "notification")]
|
||||
pub async fn initialized(&self, _params: InitializedParams) {
|
||||
self.action_manager.push(Action::PullConfiguration).await;
|
||||
self.action_manager.push(Action::RegisterCapabilities).await;
|
||||
}
|
||||
|
||||
#[jsonrpc_method("shutdown", kind = "request")]
|
||||
pub async fn shutdown(&self, _params: ()) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[jsonrpc_method("exit", kind = "notification")]
|
||||
pub async fn exit(&self, _params: ()) {}
|
||||
|
||||
#[jsonrpc_method("$/cancelRequest", kind = "notification")]
|
||||
pub async fn cancel_request(&self, _params: CancelParams) {}
|
||||
|
||||
#[jsonrpc_method("textDocument/didOpen", kind = "notification")]
|
||||
pub async fn did_open(&self, params: DidOpenTextDocumentParams) {}
|
||||
|
||||
#[jsonrpc_method("textDocument/didChange", kind = "notification")]
|
||||
pub async fn did_change(&self, params: DidChangeTextDocumentParams) {}
|
||||
|
||||
#[jsonrpc_method("textDocument/didSave", kind = "notification")]
|
||||
pub async fn did_save(&self, params: DidSaveTextDocumentParams) {}
|
||||
|
||||
#[jsonrpc_method("textDocument/didClose", kind = "notification")]
|
||||
pub async fn did_close(&self, _params: DidCloseTextDocumentParams) {}
|
||||
|
||||
#[jsonrpc_method("workspace/didChangeConfiguration", kind = "notification")]
|
||||
pub async fn did_change_configuration(&self, params: DidChangeConfigurationParams) {
|
||||
self.config_manager().push(params.settings).await;
|
||||
}
|
||||
|
||||
#[jsonrpc_method("window/workDoneProgress/cancel", kind = "notification")]
|
||||
pub async fn work_done_progress_cancel(&self, params: WorkDoneProgressCancelParams) {}
|
||||
|
||||
#[jsonrpc_method("textDocument/completion", kind = "request")]
|
||||
pub async fn completion(&self, params: CompletionParams) -> Result<CompletionList> {
|
||||
Ok(CompletionList {
|
||||
is_incomplete: true,
|
||||
items: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
#[jsonrpc_method("completionItem/resolve", kind = "request")]
|
||||
pub async fn completion_resolve(&self, mut item: CompletionItem) -> Result<CompletionItem> {
|
||||
Ok(item)
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/hover", kind = "request")]
|
||||
pub async fn hover(&self, params: TextDocumentPositionParams) -> Result<Option<Hover>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/definition", kind = "request")]
|
||||
pub async fn definition(
|
||||
&self,
|
||||
params: TextDocumentPositionParams,
|
||||
) -> Result<DefinitionResponse> {
|
||||
Ok(DefinitionResponse::Locations(Vec::new()))
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/references", kind = "request")]
|
||||
pub async fn references(&self, params: ReferenceParams) -> Result<Vec<Location>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/documentHighlight", kind = "request")]
|
||||
pub async fn document_highlight(
|
||||
&self,
|
||||
params: TextDocumentPositionParams,
|
||||
) -> Result<Vec<DocumentHighlight>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[jsonrpc_method("workspace/symbol", kind = "request")]
|
||||
pub async fn workspace_symbol(
|
||||
&self,
|
||||
params: WorkspaceSymbolParams,
|
||||
) -> Result<Vec<SymbolInformation>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/documentSymbol", kind = "request")]
|
||||
pub async fn document_symbol(
|
||||
&self,
|
||||
params: DocumentSymbolParams,
|
||||
) -> Result<DocumentSymbolResponse> {
|
||||
Ok(DocumentSymbolResponse::Flat(Vec::new()))
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/documentLink", kind = "request")]
|
||||
pub async fn document_link(&self, params: DocumentLinkParams) -> Result<Vec<DocumentLink>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/formatting", kind = "request")]
|
||||
pub async fn formatting(&self, params: DocumentFormattingParams) -> Result<Vec<TextEdit>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/prepareRename", kind = "request")]
|
||||
pub async fn prepare_rename(
|
||||
&self,
|
||||
params: TextDocumentPositionParams,
|
||||
) -> Result<Option<Range>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/rename", kind = "request")]
|
||||
pub async fn rename(&self, params: RenameParams) -> Result<Option<WorkspaceEdit>> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/foldingRange", kind = "request")]
|
||||
pub async fn folding_range(&self, params: FoldingRangeParams) -> Result<Vec<FoldingRange>> {
|
||||
Ok(Vec::new())
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/build", kind = "request")]
|
||||
pub async fn build(&self, params: BuildParams) -> Result<BuildResult> {
|
||||
Ok(BuildResult {
|
||||
status: BuildStatus::Failure,
|
||||
})
|
||||
}
|
||||
|
||||
#[jsonrpc_method("textDocument/forwardSearch", kind = "request")]
|
||||
pub async fn forward_search(
|
||||
&self,
|
||||
params: TextDocumentPositionParams,
|
||||
) -> Result<ForwardSearchResult> {
|
||||
Ok(ForwardSearchResult {
|
||||
status: ForwardSearchStatus::Failure,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: LspClient + Send + Sync + 'static> Middleware for LatexLspServer<C> {
|
||||
#[boxed]
|
||||
async fn before_message(&self) {}
|
||||
|
||||
#[boxed]
|
||||
async fn after_message(&self) {
|
||||
for action in self.action_manager.take().await {
|
||||
match action {
|
||||
Action::PullConfiguration => {
|
||||
let config_manager = self.config_manager();
|
||||
if config_manager.pull().await {
|
||||
let options = config_manager.get().await;
|
||||
self.workspace.reparse(&options).await;
|
||||
}
|
||||
}
|
||||
Action::RegisterCapabilities => self.config_manager().register().await,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
enum Action {
|
||||
PullConfiguration,
|
||||
RegisterCapabilities,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ActionManager {
|
||||
actions: Mutex<Vec<Action>>,
|
||||
}
|
||||
|
||||
impl ActionManager {
|
||||
pub async fn push(&self, action: Action) {
|
||||
let mut actions = self.actions.lock().await;
|
||||
actions.push(action);
|
||||
}
|
||||
|
||||
pub async fn take(&self) -> Vec<Action> {
|
||||
let mut actions = self.actions.lock().await;
|
||||
mem::replace(&mut *actions, Vec::new())
|
||||
}
|
||||
}
|
||||
|
|
@ -14,7 +14,7 @@ pub struct SymbolTableParams<'a> {
|
|||
pub uri: &'a Uri,
|
||||
pub resolver: &'a Resolver,
|
||||
pub options: &'a Options,
|
||||
pub cwd: &'a Path,
|
||||
pub current_dir: &'a Path,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -46,7 +46,7 @@ impl SymbolTable {
|
|||
uri,
|
||||
resolver,
|
||||
options,
|
||||
cwd,
|
||||
current_dir,
|
||||
} = params;
|
||||
|
||||
let commands: Vec<_> = tree.commands().collect();
|
||||
|
|
@ -56,7 +56,7 @@ impl SymbolTable {
|
|||
uri,
|
||||
resolver,
|
||||
options,
|
||||
cwd,
|
||||
current_dir,
|
||||
};
|
||||
|
||||
let mut environments = None;
|
||||
|
|
@ -134,7 +134,7 @@ pub struct SymbolContext<'a> {
|
|||
uri: &'a Uri,
|
||||
resolver: &'a Resolver,
|
||||
options: &'a Options,
|
||||
cwd: &'a Path,
|
||||
current_dir: &'a Path,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
|
||||
|
|
@ -298,7 +298,7 @@ impl Include {
|
|||
.and_then(|opts| opts.root_directory.as_ref())
|
||||
{
|
||||
let file_name = ctx.uri.path_segments()?.last()?;
|
||||
let path = ctx.cwd.join(root_directory).join(file_name);
|
||||
let path = ctx.current_dir.join(root_directory).join(file_name);
|
||||
Uri::from_file_path(path).ok()
|
||||
} else {
|
||||
Some(ctx.uri.clone())
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ pub struct OpenParams<'a> {
|
|||
pub uri: &'a Uri,
|
||||
pub resolver: &'a Resolver,
|
||||
pub options: &'a Options,
|
||||
pub cwd: &'a Path,
|
||||
pub current_dir: &'a Path,
|
||||
}
|
||||
|
||||
pub fn open(params: OpenParams) -> SymbolTable {
|
||||
|
|
@ -27,7 +27,7 @@ pub fn open(params: OpenParams) -> SymbolTable {
|
|||
uri,
|
||||
resolver,
|
||||
options,
|
||||
cwd,
|
||||
current_dir,
|
||||
} = params;
|
||||
|
||||
let lexer = Lexer::new(text);
|
||||
|
|
@ -39,7 +39,7 @@ pub fn open(params: OpenParams) -> SymbolTable {
|
|||
uri,
|
||||
resolver,
|
||||
options,
|
||||
cwd,
|
||||
current_dir,
|
||||
};
|
||||
SymbolTable::analyze(params)
|
||||
}
|
||||
|
|
@ -62,7 +62,7 @@ mod tests {
|
|||
uri: &Uri::parse("http://www.foo.com/bar.tex").unwrap(),
|
||||
resolver: &Resolver::default(),
|
||||
options: &Options::default(),
|
||||
cwd: &env::current_dir().unwrap(),
|
||||
current_dir: &env::current_dir().unwrap(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
@ -222,7 +222,7 @@ mod tests {
|
|||
uri: &Uri::parse("http://www.foo.com/bar.tex").unwrap(),
|
||||
resolver: &Resolver::default(),
|
||||
options: &Options::default(),
|
||||
cwd: &env::current_dir().unwrap(),
|
||||
current_dir: &env::current_dir().unwrap(),
|
||||
});
|
||||
|
||||
let actual_names: Vec<_> = table
|
||||
|
|
@ -364,7 +364,7 @@ mod tests {
|
|||
uri: &Uri::parse("http://www.foo.com/dir1/dir2/foo.tex").unwrap(),
|
||||
resolver: &resolver,
|
||||
options: &Options::default(),
|
||||
cwd: &env::current_dir().unwrap(),
|
||||
current_dir: &env::current_dir().unwrap(),
|
||||
});
|
||||
|
||||
assert_eq!(table.includes.len(), 1);
|
||||
|
|
@ -463,7 +463,7 @@ mod tests {
|
|||
uri: &Uri::parse("http://www.foo.com/bar.tex").unwrap(),
|
||||
resolver: &Resolver::default(),
|
||||
options: &Options::default(),
|
||||
cwd: &env::current_dir().unwrap(),
|
||||
current_dir: &env::current_dir().unwrap(),
|
||||
});
|
||||
assert_eq!(
|
||||
table.components,
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use crate::{
|
|||
tex::{Distribution, Language, Resolver},
|
||||
};
|
||||
use futures::lock::Mutex;
|
||||
use log::{error, warn};
|
||||
use log::{debug, error, warn};
|
||||
use petgraph::{graph::Graph, visit::Dfs};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
|
|
@ -28,7 +28,7 @@ pub struct DocumentParams<'a> {
|
|||
language: Language,
|
||||
resolver: &'a Resolver,
|
||||
options: &'a Options,
|
||||
cwd: &'a Path,
|
||||
current_dir: &'a Path,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
|
|
@ -57,7 +57,7 @@ impl Document {
|
|||
language,
|
||||
resolver,
|
||||
options,
|
||||
cwd,
|
||||
current_dir,
|
||||
} = params;
|
||||
|
||||
let content = match language {
|
||||
|
|
@ -67,7 +67,7 @@ impl Document {
|
|||
text: &text,
|
||||
resolver,
|
||||
options,
|
||||
cwd,
|
||||
current_dir,
|
||||
});
|
||||
DocumentContent::Latex(Box::new(table))
|
||||
}
|
||||
|
|
@ -269,15 +269,15 @@ impl error::Error for WorkspaceLoadError {
|
|||
|
||||
pub struct Workspace {
|
||||
distro: Arc<Box<dyn Distribution + Send + Sync>>,
|
||||
cwd: PathBuf,
|
||||
current_dir: PathBuf,
|
||||
snapshot: Mutex<Arc<Snapshot>>,
|
||||
}
|
||||
|
||||
impl Workspace {
|
||||
pub fn new(distro: Arc<Box<dyn Distribution + Send + Sync>>, cwd: PathBuf) -> Self {
|
||||
pub fn new(distro: Arc<Box<dyn Distribution + Send + Sync>>, current_dir: PathBuf) -> Self {
|
||||
Self {
|
||||
distro,
|
||||
cwd,
|
||||
current_dir,
|
||||
snapshot: Mutex::default(),
|
||||
}
|
||||
}
|
||||
|
|
@ -299,6 +299,7 @@ impl Workspace {
|
|||
}
|
||||
};
|
||||
|
||||
debug!("Adding document: {}", document.uri);
|
||||
let mut snapshot = self.snapshot.lock().await;
|
||||
*snapshot = self
|
||||
.add_or_update(
|
||||
|
|
@ -340,6 +341,7 @@ impl Workspace {
|
|||
}
|
||||
};
|
||||
|
||||
debug!("Loading document: {}", uri);
|
||||
let mut snapshot = self.snapshot.lock().await;
|
||||
*snapshot = self
|
||||
.add_or_update(&snapshot, uri, text, language, options)
|
||||
|
|
@ -363,11 +365,34 @@ impl Workspace {
|
|||
DocumentContent::Bibtex(_) => Language::Bibtex,
|
||||
};
|
||||
|
||||
debug!("Updating document: {}", uri);
|
||||
*snapshot = self
|
||||
.add_or_update(&snapshot, uri, text, language, options)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub async fn reparse(&self, options: &Options) {
|
||||
let snapshot = self.get().await;
|
||||
for doc in &snapshot.0 {
|
||||
let language = match doc.content {
|
||||
DocumentContent::Latex(_) => Language::Latex,
|
||||
DocumentContent::Bibtex(_) => Language::Bibtex,
|
||||
};
|
||||
|
||||
let mut snapshot = self.snapshot.lock().await;
|
||||
debug!("Reparsing document: {}", doc.uri);
|
||||
*snapshot = self
|
||||
.add_or_update(
|
||||
&snapshot,
|
||||
doc.uri.clone(),
|
||||
doc.text.clone(),
|
||||
language,
|
||||
options,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn add_or_update(
|
||||
&self,
|
||||
snapshot: &Snapshot,
|
||||
|
|
@ -383,7 +408,7 @@ impl Workspace {
|
|||
language,
|
||||
resolver: &resolver,
|
||||
options,
|
||||
cwd: &self.cwd,
|
||||
current_dir: &self.current_dir,
|
||||
});
|
||||
|
||||
let mut documents: Vec<Arc<Document>> = snapshot
|
||||
|
|
@ -412,7 +437,7 @@ mod tests {
|
|||
language,
|
||||
resolver: &Resolver::default(),
|
||||
options: &Options::default(),
|
||||
cwd: &env::current_dir().unwrap(),
|
||||
current_dir: &env::current_dir().unwrap(),
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
@ -586,7 +611,7 @@ mod tests {
|
|||
language: Language::Latex,
|
||||
resolver: &Resolver::default(),
|
||||
options: &options,
|
||||
cwd: &cwd,
|
||||
current_dir: &cwd,
|
||||
})),
|
||||
Arc::new(Document::open(DocumentParams {
|
||||
uri: uri2.clone(),
|
||||
|
|
@ -594,7 +619,7 @@ mod tests {
|
|||
language: Language::Latex,
|
||||
resolver: &Resolver::default(),
|
||||
options: &options,
|
||||
cwd: &cwd,
|
||||
current_dir: &cwd,
|
||||
})),
|
||||
];
|
||||
let actual_uris: Vec<_> = snapshot
|
||||
|
|
@ -627,7 +652,7 @@ mod tests {
|
|||
language: Language::Latex,
|
||||
resolver: &Resolver::default(),
|
||||
options: &options,
|
||||
cwd: &cwd,
|
||||
current_dir: &cwd,
|
||||
})),
|
||||
Arc::new(Document::open(DocumentParams {
|
||||
uri: uri2.clone(),
|
||||
|
|
@ -635,7 +660,7 @@ mod tests {
|
|||
language: Language::Latex,
|
||||
resolver: &Resolver::default(),
|
||||
options: &options,
|
||||
cwd: &cwd,
|
||||
current_dir: &cwd,
|
||||
})),
|
||||
];
|
||||
let actual_uris: Vec<_> = snapshot
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue