feat: forward const config

This commit is contained in:
Myriad-Dreamin 2024-03-07 16:12:49 +08:00
parent d49085338f
commit 0c7e67ed9a
12 changed files with 125 additions and 211 deletions

View file

@ -28,7 +28,6 @@ itertools = "0.12.0"
lazy_static = "1.4.0"
env_logger = "0.11.1"
log = "0.4.17"
percent-encoding = "2.3.0"
strum = { version = "0.25.0", features = ["derive"] }
async-trait = "0.1.73"
parking_lot = "0.12.1"

View file

@ -4,7 +4,6 @@ use crate::prelude::*;
pub struct CompletionRequest {
pub path: PathBuf,
pub position: LspPosition,
pub position_encoding: PositionEncoding,
pub explicit: bool,
}
@ -12,16 +11,16 @@ pub fn completion(
world: &TypstSystemWorld,
doc: Option<Arc<TypstDocument>>,
req: CompletionRequest,
position_encoding: PositionEncoding,
) -> Option<CompletionResponse> {
let source = get_suitable_source_in_workspace(world, &req.path).ok()?;
let typst_offset =
lsp_to_typst::position_to_offset(req.position, req.position_encoding, &source);
let typst_offset = lsp_to_typst::position_to_offset(req.position, position_encoding, &source);
let (typst_start_offset, completions) =
typst_ide::autocomplete(world, doc.as_deref(), &source, typst_offset, req.explicit)?;
let lsp_start_position =
typst_to_lsp::offset_to_position(typst_start_offset, req.position_encoding, &source);
typst_to_lsp::offset_to_position(typst_start_offset, position_encoding, &source);
let replace_range = LspRawRange::new(lsp_start_position, req.position);
Some(typst_to_lsp::completions(&completions, replace_range).into())
}

View file

@ -3,17 +3,17 @@ use crate::prelude::*;
#[derive(Debug, Clone)]
pub struct DocumentSymbolRequest {
pub path: PathBuf,
pub position_encoding: PositionEncoding,
}
pub fn document_symbol(
world: &TypstSystemWorld,
req: DocumentSymbolRequest,
position_encoding: PositionEncoding,
) -> Option<DocumentSymbolResponse> {
let source = get_suitable_source_in_workspace(world, &req.path).ok()?;
let uri = Url::from_file_path(req.path).unwrap();
let symbols = get_document_symbols(source, uri, req.position_encoding);
let symbols = get_document_symbols(source, uri, position_encoding);
symbols.map(DocumentSymbolResponse::Flat)
}

View file

@ -4,22 +4,21 @@ use crate::prelude::*;
pub struct HoverRequest {
pub path: PathBuf,
pub position: LspPosition,
pub position_encoding: PositionEncoding,
}
pub fn hover(
world: &TypstSystemWorld,
doc: Option<Arc<TypstDocument>>,
req: HoverRequest,
position_encoding: PositionEncoding,
) -> Option<Hover> {
let source = get_suitable_source_in_workspace(world, &req.path).ok()?;
let typst_offset =
lsp_to_typst::position_to_offset(req.position, req.position_encoding, &source);
let typst_offset = lsp_to_typst::position_to_offset(req.position, position_encoding, &source);
let typst_tooltip = typst_ide::tooltip(world, doc.as_deref(), &source, typst_offset)?;
let ast_node = LinkedNode::new(source.root()).leaf_at(typst_offset)?;
let range = typst_to_lsp::range(ast_node.range(), &source, req.position_encoding);
let range = typst_to_lsp::range(ast_node.range(), &source, position_encoding);
Some(Hover {
contents: typst_to_lsp::tooltip(&typst_tooltip),

View file

@ -4,22 +4,21 @@ use crate::prelude::*;
pub struct SelectionRangeRequest {
pub path: PathBuf,
pub positions: Vec<LspPosition>,
pub position_encoding: PositionEncoding,
}
pub fn selection_range(
world: &TypstSystemWorld,
req: SelectionRangeRequest,
position_encoding: PositionEncoding,
) -> Option<Vec<SelectionRange>> {
let source = get_suitable_source_in_workspace(world, &req.path).ok()?;
let mut ranges = Vec::new();
for position in req.positions {
let typst_offset =
lsp_to_typst::position_to_offset(position, req.position_encoding, &source);
let typst_offset = lsp_to_typst::position_to_offset(position, position_encoding, &source);
let tree = LinkedNode::new(source.root());
let leaf = tree.leaf_at(typst_offset)?;
ranges.push(range_for_node(&source, req.position_encoding, &leaf));
ranges.push(range_for_node(&source, position_encoding, &leaf));
}
Some(ranges)

View file

@ -15,7 +15,7 @@ use self::modifier_set::ModifierSet;
use self::token_encode::encode_tokens;
use self::typst_tokens::{Modifier, TokenType};
pub use self::delta::CacheInner as TokenCacheInner;
use self::delta::CacheInner as TokenCacheInner;
mod delta;
mod modifier_set;

View file

@ -4,18 +4,18 @@ use crate::{prelude::*, SemanticTokenCache};
pub struct SemanticTokensDeltaRequest {
pub path: PathBuf,
pub previous_result_id: String,
pub position_encoding: PositionEncoding,
}
pub fn semantic_tokens_delta(
cache: &SemanticTokenCache,
source: Source,
req: SemanticTokensDeltaRequest,
position_encoding: PositionEncoding,
) -> Option<SemanticTokensFullDeltaResult> {
let (tokens, result_id) = cache.try_semantic_tokens_delta_from_result_id(
&source,
&req.previous_result_id,
req.position_encoding,
position_encoding,
);
match tokens {

View file

@ -3,15 +3,15 @@ use crate::{prelude::*, SemanticTokenCache};
#[derive(Debug, Clone)]
pub struct SemanticTokensFullRequest {
pub path: PathBuf,
pub position_encoding: PositionEncoding,
}
pub fn semantic_tokens_full(
cache: &SemanticTokenCache,
source: Source,
req: SemanticTokensFullRequest,
_req: SemanticTokensFullRequest,
position_encoding: PositionEncoding,
) -> Option<SemanticTokensResult> {
let (tokens, result_id) = cache.get_semantic_tokens_full(&source, req.position_encoding);
let (tokens, result_id) = cache.get_semantic_tokens_full(&source, position_encoding);
Some(
SemanticTokens {

View file

@ -4,16 +4,12 @@ use crate::prelude::*;
pub struct SignatureHelpRequest {
pub path: PathBuf,
pub position: LspPosition,
pub position_encoding: PositionEncoding,
}
pub fn signature_help(
world: &TypstSystemWorld,
SignatureHelpRequest {
path,
position,
position_encoding,
}: SignatureHelpRequest,
SignatureHelpRequest { path, position }: SignatureHelpRequest,
position_encoding: PositionEncoding,
) -> Option<SignatureHelp> {
let source = get_suitable_source_in_workspace(world, &path).ok()?;
let typst_offset = lsp_to_typst::position_to_offset(position, position_encoding, &source);

View file

@ -6,15 +6,12 @@ use crate::prelude::*;
#[derive(Debug, Clone)]
pub struct SymbolRequest {
pub pattern: Option<String>,
pub position_encoding: PositionEncoding,
}
pub fn symbol(
world: &TypstSystemWorld,
SymbolRequest {
pattern,
position_encoding,
}: SymbolRequest,
SymbolRequest { pattern }: SymbolRequest,
position_encoding: PositionEncoding,
) -> Option<Vec<SymbolInformation>> {
// todo: expose source

View file

@ -31,12 +31,12 @@ use typst_ts_compiler::vfs::notify::{FileChangeSet, MemoryEvent};
use typst_ts_compiler::{Time, TypstSystemWorld};
use typst_ts_core::{
config::CompileOpts, debug_loc::SourceSpanOffset, error::prelude::*, typst::prelude::EcoVec,
Bytes, DynExporter, Error, ImmutPath, TypstDocument,
Bytes, Error, ImmutPath,
};
use crate::actor::render::PdfExportActor;
use crate::actor::render::RenderActorRequest;
use crate::LspHost;
use crate::{actor::render::PdfExportActor, ConstConfig};
type CompileService<H> = CompileActor<Reporter<CompileExporter<CompileDriver>, H>>;
type CompileClient<H> = TsCompileClient<CompileService<H>>;
@ -44,18 +44,33 @@ type CompileClient<H> = TsCompileClient<CompileService<H>>;
type DiagnosticsSender = mpsc::UnboundedSender<(String, DiagnosticsMap)>;
type DiagnosticsMap = HashMap<Url, Vec<LspDiagnostic>>;
type Client = TypstClient<CompileHandler>;
// type Client = TypstClient<CompileHandler>;
pub fn create_cluster(host: LspHost, roots: Vec<PathBuf>, opts: CompileOpts) -> CompileCluster {
pub struct CompileCluster {
position_encoding: PositionEncoding,
memory_changes: RwLock<HashMap<Arc<Path>, MemoryFileMeta>>,
primary: CompileNode<CompileHandler>,
pub tokens_cache: SemanticTokenCache,
actor: Option<CompileClusterActor>,
}
pub fn create_cluster(
host: LspHost,
cfg: &ConstConfig,
roots: Vec<PathBuf>,
opts: CompileOpts,
) -> CompileCluster {
let (diag_tx, diag_rx) = mpsc::unbounded_channel();
let primary = create_server(
"primary".to_owned(),
cfg,
create_compiler(roots.clone(), opts.clone()),
diag_tx,
);
CompileCluster {
position_encoding: cfg.position_encoding,
memory_changes: RwLock::new(HashMap::new()),
primary,
tokens_cache: Default::default(),
@ -75,32 +90,48 @@ fn create_compiler(roots: Vec<PathBuf>, opts: CompileOpts) -> CompileDriver {
fn create_server(
diag_group: String,
cfg: &ConstConfig,
compiler_driver: CompileDriver,
diag_tx: DiagnosticsSender,
) -> CompileNode {
) -> CompileNode<CompileHandler> {
let (doc_sender, doc_recv) = watch::channel(None);
let (render_tx, render_rx) = broadcast::channel(1024);
let exporter: DynExporter<TypstDocument> = Box::new(move |_w: &dyn World, doc| {
let _ = doc_sender.send(Some(doc)); // it is ok to ignore the error here
tokio::spawn(PdfExportActor::new(doc_recv, render_rx).run());
let root = compiler_driver.inner.world.root.as_ref().to_owned();
let handler: CompileHandler = compiler_driver.handler.clone();
let driver = CompileExporter::new(compiler_driver).with_exporter(Box::new(
move |_w: &dyn World, doc| {
let _ = doc_sender.send(Some(doc));
// todo: is it right that ignore zero broadcast receiver?
let _ = render_tx.send(RenderActorRequest::Render);
Ok(())
});
tokio::spawn(PdfExportActor::new(doc_recv, render_rx).run());
let handler: CompileHandler = compiler_driver.handler.clone();
let compile_server = CompileServer::new(
},
));
let driver = Reporter {
diag_group,
compiler_driver,
handler.clone(),
position_encoding: cfg.position_encoding,
diag_tx,
exporter,
);
inner: driver,
cb: handler.clone(),
};
let driver = CompileActor::new(driver, root).with_watch(true);
CompileNode::new(handler, compile_server.spawn().unwrap())
let (server, client) = driver.split();
tokio::spawn(server.spawn());
CompileNode::new(cfg.position_encoding, handler, client)
}
impl CompileCluster {
pub fn split(mut self) -> (Self, CompileClusterActor) {
let actor = self.actor.take().expect("actor is poisoned");
(self, actor)
}
}
pub struct CompileClusterActor {
@ -111,20 +142,6 @@ pub struct CompileClusterActor {
affect_map: HashMap<String, Vec<Url>>,
}
pub struct CompileCluster {
memory_changes: RwLock<HashMap<Arc<Path>, MemoryFileMeta>>,
primary: CompileNode,
pub tokens_cache: SemanticTokenCache,
actor: Option<CompileClusterActor>,
}
impl CompileCluster {
pub fn split(mut self) -> (Self, CompileClusterActor) {
let actor = self.actor.take().expect("actor is poisoned");
(self, actor)
}
}
impl CompileClusterActor {
pub async fn run(mut self) {
loop {
@ -205,16 +222,13 @@ impl CompileCluster {
},
);
let mut primary = self.primary.inner.lock().await;
let content: Bytes = content.as_bytes().into();
primary.change_entry(path.clone()).await?;
self.primary.change_entry(path.clone()).await?;
// todo: is it safe to believe that the path is normalized?
let files = FileChangeSet::new_inserts(vec![(path, FileResult::Ok((now, content)).into())]);
primary
.inner()
.add_memory_changes(MemoryEvent::Update(files));
let iw = self.primary.inner.lock().await;
iw.add_memory_changes(MemoryEvent::Update(files));
Ok(())
}
@ -224,14 +238,11 @@ impl CompileCluster {
self.memory_changes.write().await.remove(&path);
let mut primary = self.primary.inner.lock().await;
// todo: is it safe to believe that the path is normalized?
let files = FileChangeSet::new_removes(vec![path]);
// todo: change focus
primary
.inner()
.add_memory_changes(MemoryEvent::Update(files));
let iw = self.primary.inner.lock().await;
iw.add_memory_changes(MemoryEvent::Update(files));
Ok(())
}
@ -271,13 +282,10 @@ impl CompileCluster {
drop(memory_changes);
let mut primary = self.primary.inner.lock().await;
primary.change_entry(path.clone()).await?;
self.primary.change_entry(path.clone()).await?;
let files = FileChangeSet::new_inserts(vec![(path.clone(), snapshot)]);
primary
.inner()
.add_memory_changes(MemoryEvent::Update(files));
let iw = self.primary.inner.lock().await;
iw.add_memory_changes(MemoryEvent::Update(files));
Ok(())
}
@ -317,14 +325,16 @@ pub enum CompilerQueryResponse {
macro_rules! query_state {
($self:ident, $method:ident, $query:expr, $req:expr) => {{
let doc = $self.handler.result.lock().unwrap().clone().ok();
let res = $self.steal_world(|w| $query(w, doc, $req)).await;
let enc = $self.position_encoding;
let res = $self.steal_world(move |w| $query(w, doc, $req, enc)).await;
res.map(CompilerQueryResponse::$method)
}};
}
macro_rules! query_world {
($self:ident, $method:ident, $query:expr, $req:expr) => {{
let res = $self.steal_world(|w| $query(w, $req)).await;
let enc = $self.position_encoding;
let res = $self.steal_world(move |w| $query(w, $req, enc)).await;
res.map(CompilerQueryResponse::$method)
}};
}
@ -334,7 +344,10 @@ macro_rules! query_tokens_cache {
let path: ImmutPath = $req.path.clone().into();
let vfs = $self.memory_changes.read().await;
let snapshot = vfs.get(&path).ok_or_else(|| anyhow!("file missing"))?;
let res = $query(&$self.tokens_cache, snapshot.content.clone(), $req);
let source = snapshot.content.clone();
let enc = $self.position_encoding;
let res = $query(&$self.tokens_cache, source, $req, enc);
Ok(CompilerQueryResponse::$method(res))
}};
}
@ -441,50 +454,9 @@ impl CompileDriver {
}
}
pub struct CompileServer<H: CompilationHandle> {
inner: CompileService<H>,
client: TypstClient<H>,
}
impl<H: CompilationHandle> CompileServer<H> {
pub fn new(
diag_group: String,
compiler_driver: CompileDriver,
cb: H,
diag_tx: DiagnosticsSender,
exporter: DynExporter<TypstDocument>,
) -> Self {
let root = compiler_driver.inner.world.root.clone();
let driver = CompileExporter::new(compiler_driver).with_exporter(exporter);
let driver = Reporter {
diag_group,
diag_tx,
inner: driver,
cb,
};
let inner = CompileActor::new(driver, root.as_ref().to_owned()).with_watch(true);
Self {
inner,
client: TypstClient {
entry: Arc::new(SyncMutex::new(None)),
inner: once_cell::sync::OnceCell::new(),
},
}
}
pub fn spawn(self) -> Result<TypstClient<H>, Error> {
let (server, client) = self.inner.split();
tokio::spawn(server.spawn());
self.client.inner.set(client).ok().unwrap();
Ok(self.client)
}
}
pub struct Reporter<C, H> {
diag_group: String,
position_encoding: PositionEncoding,
diag_tx: DiagnosticsSender,
inner: C,
cb: H,
@ -539,7 +511,7 @@ impl<C: Compiler<World = TypstSystemWorld>, H> Reporter<C, H> {
let diagnostics = tinymist_query::convert_diagnostics(
self.inner.world(),
diagnostics.as_ref(),
PositionEncoding::Utf16,
self.position_encoding,
);
let err = self.diag_tx.send((self.diag_group.clone(), diagnostics));
@ -549,29 +521,31 @@ impl<C: Compiler<World = TypstSystemWorld>, H> Reporter<C, H> {
}
}
pub struct TypstClient<H: CompilationHandle> {
pub struct CompileNode<H: CompilationHandle> {
position_encoding: PositionEncoding,
handler: CompileHandler,
entry: Arc<SyncMutex<Option<ImmutPath>>>,
inner: once_cell::sync::OnceCell<CompileClient<H>>,
inner: Mutex<CompileClient<H>>,
}
// todo: remove unsafe impl send
unsafe impl<H: CompilationHandle> Send for TypstClient<H> {}
unsafe impl<H: CompilationHandle> Sync for TypstClient<H> {}
unsafe impl<H: CompilationHandle> Send for CompileNode<H> {}
unsafe impl<H: CompilationHandle> Sync for CompileNode<H> {}
impl<H: CompilationHandle> TypstClient<H> {
impl<H: CompilationHandle> CompileNode<H> {
fn inner(&mut self) -> &mut CompileClient<H> {
self.inner.get_mut().unwrap()
self.inner.get_mut()
}
/// Steal the compiler thread and run the given function.
pub async fn steal_async<Ret: Send + 'static>(
&mut self,
&self,
f: impl FnOnce(&mut CompileService<H>, tokio::runtime::Handle) -> Ret + Send + 'static,
) -> ZResult<Ret> {
self.inner().steal_async(f).await
self.inner.lock().await.steal_async(f).await
}
async fn change_entry(&mut self, path: ImmutPath) -> Result<(), Error> {
async fn change_entry(&self, path: ImmutPath) -> Result<(), Error> {
if !path.is_absolute() {
return Err(error_once!("entry file must be absolute", path: path.display()));
}
@ -602,7 +576,7 @@ impl<H: CompilationHandle> TypstClient<H> {
}
}
impl<H: CompilationHandle> SourceFileServer for TypstClient<H> {
impl<H: CompilationHandle> SourceFileServer for CompileNode<H> {
async fn resolve_source_span(
&mut self,
loc: Location,
@ -648,7 +622,7 @@ impl<H: CompilationHandle> SourceFileServer for TypstClient<H> {
}
}
impl<H: CompilationHandle> EditorServer for TypstClient<H> {
impl<H: CompilationHandle> EditorServer for CompileNode<H> {
async fn update_memory_files(
&mut self,
files: MemoryFiles,
@ -685,18 +659,19 @@ impl<H: CompilationHandle> EditorServer for TypstClient<H> {
}
}
impl<H: CompilationHandle> CompileHost for TypstClient<H> {}
impl<H: CompilationHandle> CompileHost for CompileNode<H> {}
pub struct CompileNode {
impl<H: CompilationHandle> CompileNode<H> {
fn new(
position_encoding: PositionEncoding,
handler: CompileHandler,
inner: Arc<Mutex<Client>>,
}
impl CompileNode {
fn new(handler: CompileHandler, unwrap: TypstClient<CompileHandler>) -> Self {
inner: CompileClient<H>,
) -> Self {
Self {
position_encoding,
handler,
inner: Arc::new(Mutex::new(unwrap)),
entry: Arc::new(SyncMutex::new(None)),
inner: Mutex::new(inner),
}
}

View file

@ -252,11 +252,16 @@ impl LanguageServer for TypstServer {
async fn initialize(&self, params: InitializeParams) -> jsonrpc::Result<InitializeResult> {
// self.tracing_init();
self.const_config
.set(ConstConfig::from(&params))
.expect("const config should not yet be initialized");
let cluster = {
let root_paths = params.root_paths();
let primary_root = root_paths.first().cloned().unwrap_or_default();
actor::typst::create_cluster(
self.client.clone(),
self.const_config.get().unwrap(),
root_paths,
CompileOpts {
root_dir: primary_root,
@ -275,10 +280,6 @@ impl LanguageServer for TypstServer {
.map_err(|_| ())
.expect("the cluster is already initialized");
self.const_config
.set(ConstConfig::from(&params))
.expect("const config should not yet be initialized");
tokio::spawn(cluster_bg.run());
if let Some(init) = &params.initialization_options {
@ -498,17 +499,8 @@ impl LanguageServer for TypstServer {
let uri = &params.text_document_position_params.text_document.uri;
let path = uri.to_file_path().unwrap();
let position = params.text_document_position_params.position;
let position_encoding = self.const_config().position_encoding;
run_query!(
self,
Hover,
HoverRequest {
path,
position,
position_encoding,
}
)
run_query!(self, Hover, HoverRequest { path, position })
}
async fn completion(
@ -522,7 +514,6 @@ impl LanguageServer for TypstServer {
.context
.map(|context| context.trigger_kind == CompletionTriggerKind::INVOKED)
.unwrap_or(false);
let position_encoding = self.const_config().position_encoding;
run_query!(
self,
@ -530,7 +521,6 @@ impl LanguageServer for TypstServer {
CompletionRequest {
path,
position,
position_encoding,
explicit,
}
)
@ -543,17 +533,8 @@ impl LanguageServer for TypstServer {
let uri = params.text_document_position_params.text_document.uri;
let path = uri.to_file_path().unwrap();
let position = params.text_document_position_params.position;
let position_encoding = self.const_config().position_encoding;
run_query!(
self,
SignatureHelp,
SignatureHelpRequest {
path,
position,
position_encoding,
}
)
run_query!(self, SignatureHelp, SignatureHelpRequest { path, position })
}
async fn document_symbol(
@ -562,16 +543,8 @@ impl LanguageServer for TypstServer {
) -> jsonrpc::Result<Option<DocumentSymbolResponse>> {
let uri = params.text_document.uri;
let path = uri.to_file_path().unwrap();
let position_encoding = self.const_config().position_encoding;
run_query!(
self,
DocumentSymbol,
DocumentSymbolRequest {
path,
position_encoding
}
)
run_query!(self, DocumentSymbol, DocumentSymbolRequest { path })
}
async fn symbol(
@ -579,16 +552,8 @@ impl LanguageServer for TypstServer {
params: WorkspaceSymbolParams,
) -> jsonrpc::Result<Option<Vec<SymbolInformation>>> {
let pattern = (!params.query.is_empty()).then_some(params.query);
let position_encoding = self.const_config().position_encoding;
run_query!(
self,
Symbol,
SymbolRequest {
pattern,
position_encoding
}
)
run_query!(self, Symbol, SymbolRequest { pattern })
}
async fn selection_range(
@ -598,16 +563,11 @@ impl LanguageServer for TypstServer {
let uri = params.text_document.uri;
let path = uri.to_file_path().unwrap();
let positions = params.positions;
let position_encoding = self.const_config().position_encoding;
run_query!(
self,
SelectionRange,
SelectionRangeRequest {
path,
positions,
position_encoding
}
SelectionRangeRequest { path, positions }
)
}
@ -617,16 +577,8 @@ impl LanguageServer for TypstServer {
) -> jsonrpc::Result<Option<SemanticTokensResult>> {
let uri = params.text_document.uri;
let path = uri.to_file_path().unwrap();
let position_encoding = self.const_config().position_encoding;
run_query!(
self,
SemanticTokensFull,
SemanticTokensFullRequest {
path,
position_encoding
}
)
run_query!(self, SemanticTokensFull, SemanticTokensFullRequest { path })
}
async fn semantic_tokens_full_delta(
@ -636,7 +588,6 @@ impl LanguageServer for TypstServer {
let uri = params.text_document.uri;
let path = uri.to_file_path().unwrap();
let previous_result_id = params.previous_result_id;
let position_encoding = self.const_config().position_encoding;
run_query!(
self,
@ -644,7 +595,6 @@ impl LanguageServer for TypstServer {
SemanticTokensDeltaRequest {
path,
previous_result_id,
position_encoding
}
)
}