mirror of
https://github.com/Myriad-Dreamin/tinymist.git
synced 2025-08-04 02:08:17 +00:00
refactor: rearrange preview entry for future improvement (#426)
* dev: refactor preview entry for future improvement * dev: move watcher ahead * dev: move host ahead
This commit is contained in:
parent
8413c66c51
commit
1d011155cf
7 changed files with 257 additions and 222 deletions
|
@ -380,10 +380,10 @@ where
|
|||
self
|
||||
}
|
||||
|
||||
pub fn with_command(
|
||||
pub fn with_command<T: Serialize + 'static>(
|
||||
mut self,
|
||||
cmd: &'static str,
|
||||
handler: fn(&mut Args::S, Vec<JsonValue>) -> AnySchedulableResponse,
|
||||
handler: fn(&mut Args::S, Vec<JsonValue>) -> SchedulableResponse<T>,
|
||||
) -> Self {
|
||||
self.exec_cmds.insert(
|
||||
cmd,
|
||||
|
|
|
@ -51,7 +51,10 @@ impl PreviewActor {
|
|||
};
|
||||
|
||||
// Unregister preview early
|
||||
tab.compile_handler.unregister_preview(&task_id);
|
||||
let unregistered = tab.compile_handler.unregister_preview(&task_id);
|
||||
if !unregistered {
|
||||
log::warn!("PreviewTask({task_id}): failed to unregister preview");
|
||||
}
|
||||
|
||||
let client = self.client.clone();
|
||||
self.client.handle.spawn(async move {
|
||||
|
|
|
@ -226,24 +226,27 @@ impl CompileHandler {
|
|||
Ok(f(&mut analysis))
|
||||
}
|
||||
|
||||
// todo: multiple preview support
|
||||
#[cfg(feature = "preview")]
|
||||
pub fn register_preview(&self, handle: Arc<typst_preview::CompileWatcher>) {
|
||||
// todo: conflict detection
|
||||
*self.inner.write() = Some(handle);
|
||||
#[must_use]
|
||||
pub fn register_preview(&self, handle: &Arc<typst_preview::CompileWatcher>) -> bool {
|
||||
let mut p = self.inner.write();
|
||||
if p.as_ref().is_some() {
|
||||
return false;
|
||||
}
|
||||
*p = Some(handle.clone());
|
||||
true
|
||||
}
|
||||
|
||||
#[cfg(feature = "preview")]
|
||||
pub fn unregister_preview(&self, task_id: &str) {
|
||||
#[must_use]
|
||||
pub fn unregister_preview(&self, task_id: &str) -> bool {
|
||||
let mut p = self.inner.write();
|
||||
if p.as_ref().is_some_and(|p| p.task_id() == task_id) {
|
||||
*p = None;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// todo: multiple preview support
|
||||
#[cfg(feature = "preview")]
|
||||
pub fn registered_preview(&self) -> bool {
|
||||
self.inner.read().is_some()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -93,7 +93,10 @@ impl LanguageState {
|
|||
|
||||
/// Start a preview instance.
|
||||
#[cfg(feature = "preview")]
|
||||
pub fn start_preview(&mut self, mut args: Vec<JsonValue>) -> AnySchedulableResponse {
|
||||
pub fn start_preview(
|
||||
&mut self,
|
||||
mut args: Vec<JsonValue>,
|
||||
) -> SchedulableResponse<crate::tool::preview::StartPreviewResponse> {
|
||||
use std::path::Path;
|
||||
|
||||
use crate::tool::preview::PreviewCliArgs;
|
||||
|
@ -104,7 +107,7 @@ impl LanguageState {
|
|||
let cli_args = ["preview"]
|
||||
.into_iter()
|
||||
.chain(cli_args.iter().map(|e| e.as_str()));
|
||||
let cli_args =
|
||||
let mut cli_args =
|
||||
PreviewCliArgs::try_parse_from(cli_args).map_err(|e| invalid_params(e.to_string()))?;
|
||||
|
||||
// todo: preview specific arguments are not used
|
||||
|
@ -121,17 +124,21 @@ impl LanguageState {
|
|||
return Err(invalid_params("entry file must be absolute path"));
|
||||
};
|
||||
|
||||
// todo: race condition
|
||||
let handle = self.primary().handle.clone();
|
||||
if handle.registered_preview() {
|
||||
// Disble control plane host
|
||||
cli_args.preview.control_plane_host = String::default();
|
||||
|
||||
let primary = self.primary().handle.clone();
|
||||
|
||||
let previewer = typst_preview::PreviewBuilder::new(cli_args.preview.clone());
|
||||
|
||||
if !primary.register_preview(previewer.compile_watcher()) {
|
||||
return Err(internal_error("preview is already running"));
|
||||
}
|
||||
|
||||
// todo: recover pin status reliably
|
||||
self.pin_entry(Some(entry))
|
||||
.map_err(|e| internal_error(format!("could not pin file: {e}")))?;
|
||||
|
||||
self.preview.start(cli_args, handle)
|
||||
self.preview.start(cli_args, previewer, primary)
|
||||
}
|
||||
|
||||
/// Kill a preview instance.
|
||||
|
|
|
@ -18,9 +18,9 @@ use typst::syntax::{LinkedNode, Source, Span, SyntaxKind, VirtualPath};
|
|||
use typst::World;
|
||||
pub use typst_preview::CompileStatus;
|
||||
use typst_preview::{
|
||||
preview, CompileHost, ControlPlaneMessage, ControlPlaneResponse, DocToSrcJumpInfo,
|
||||
EditorServer, Location, LspControlPlaneRx, LspControlPlaneTx, MemoryFiles, MemoryFilesShort,
|
||||
PreviewArgs, PreviewMode, Previewer, SourceFileServer,
|
||||
CompileHost, ControlPlaneMessage, ControlPlaneResponse, DocToSrcJumpInfo, EditorServer,
|
||||
Location, LspControlPlaneRx, LspControlPlaneTx, MemoryFiles, MemoryFilesShort, PreviewArgs,
|
||||
PreviewBuilder, PreviewMode, Previewer, SourceFileServer,
|
||||
};
|
||||
use typst_ts_compiler::vfs::notify::{FileChangeSet, MemoryEvent};
|
||||
use typst_ts_compiler::EntryReader;
|
||||
|
@ -224,9 +224,10 @@ impl PreviewState {
|
|||
}
|
||||
}
|
||||
|
||||
/// Response for starting a preview.
|
||||
#[derive(Debug, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct StartPreviewResponse {
|
||||
pub struct StartPreviewResponse {
|
||||
static_server_port: Option<u16>,
|
||||
static_server_addr: Option<String>,
|
||||
data_plane_port: Option<u16>,
|
||||
|
@ -236,15 +237,13 @@ impl PreviewState {
|
|||
/// Start a preview on a given compiler.
|
||||
pub fn start(
|
||||
&self,
|
||||
mut args: PreviewCliArgs,
|
||||
args: PreviewCliArgs,
|
||||
mut previewer: PreviewBuilder,
|
||||
compile_handler: Arc<CompileHandler>,
|
||||
) -> AnySchedulableResponse {
|
||||
) -> SchedulableResponse<StartPreviewResponse> {
|
||||
let task_id = args.preview.task_id.clone();
|
||||
log::info!("PreviewTask({task_id}): arguments: {args:#?}");
|
||||
|
||||
// Disble control plane host
|
||||
args.preview.control_plane_host = String::default();
|
||||
|
||||
let (lsp_tx, lsp_rx) = LspControlPlaneTx::new();
|
||||
let LspControlPlaneRx {
|
||||
resp_rx,
|
||||
|
@ -253,12 +252,8 @@ impl PreviewState {
|
|||
} = lsp_rx;
|
||||
|
||||
// Create a previewer
|
||||
let previewer = preview(
|
||||
args.preview,
|
||||
compile_handler.clone(),
|
||||
Some(lsp_tx),
|
||||
TYPST_PREVIEW_HTML,
|
||||
);
|
||||
previewer = previewer.with_lsp_connection(Some(lsp_tx));
|
||||
let previewer = previewer.start(compile_handler.clone(), TYPST_PREVIEW_HTML);
|
||||
|
||||
// Forward preview responses to lsp client
|
||||
let tid = task_id.clone();
|
||||
|
@ -301,7 +296,6 @@ impl PreviewState {
|
|||
let preview_tx = self.preview_tx.clone();
|
||||
just_future(async move {
|
||||
let previewer = previewer.await;
|
||||
compile_handler.register_preview(previewer.compile_watcher().clone());
|
||||
|
||||
// Put a fence to ensure the previewer can receive the first compilation. z
|
||||
// The fence must be put after the previewer is initialized.
|
||||
|
@ -327,7 +321,7 @@ impl PreviewState {
|
|||
}));
|
||||
sent.map_err(|_| internal_error("failed to register preview tab"))?;
|
||||
|
||||
Ok(serde_json::to_value(resp).unwrap())
|
||||
Ok(resp)
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -496,9 +490,10 @@ pub async fn preview_main(args: PreviewCliArgs) -> anyhow::Result<()> {
|
|||
(service, handle)
|
||||
};
|
||||
|
||||
let previewer = preview(args.preview, handle.clone(), None, TYPST_PREVIEW_HTML).await;
|
||||
|
||||
handle.register_preview(previewer.compile_watcher().clone());
|
||||
let previewer = PreviewBuilder::new(args.preview);
|
||||
let registered = handle.register_preview(previewer.compile_watcher());
|
||||
assert!(registered, "failed to register preview");
|
||||
let previewer = previewer.start(handle.clone(), TYPST_PREVIEW_HTML).await;
|
||||
tokio::spawn(service.spawn());
|
||||
|
||||
let (static_server_addr, _tx, static_server_handle) =
|
||||
|
|
|
@ -35,30 +35,7 @@ pub struct TypstActor<T> {
|
|||
renderer_sender: broadcast::Sender<RenderActorRequest>,
|
||||
}
|
||||
|
||||
type MpScChannel<T> = (mpsc::UnboundedSender<T>, mpsc::UnboundedReceiver<T>);
|
||||
type BroadcastChannel<T> = (broadcast::Sender<T>, broadcast::Receiver<T>);
|
||||
|
||||
pub struct Channels {
|
||||
pub typst_mailbox: MpScChannel<TypstActorRequest>,
|
||||
pub renderer_mailbox: BroadcastChannel<RenderActorRequest>,
|
||||
pub editor_conn: MpScChannel<EditorActorRequest>,
|
||||
pub webview_conn: BroadcastChannel<WebviewActorRequest>,
|
||||
}
|
||||
|
||||
impl<T> TypstActor<T> {
|
||||
pub fn set_up_channels() -> Channels {
|
||||
let typst_mailbox = mpsc::unbounded_channel();
|
||||
let renderer_mailbox = broadcast::channel(1024);
|
||||
let editor_conn = mpsc::unbounded_channel();
|
||||
let webview_conn = broadcast::channel(32);
|
||||
Channels {
|
||||
typst_mailbox,
|
||||
renderer_mailbox,
|
||||
editor_conn,
|
||||
webview_conn,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
client: Arc<T>,
|
||||
mailbox: mpsc::UnboundedReceiver<TypstActorRequest>,
|
||||
|
|
|
@ -6,7 +6,9 @@ mod outline;
|
|||
pub use actor::editor::{
|
||||
CompileStatus, ControlPlaneMessage, ControlPlaneResponse, LspControlPlaneRx, LspControlPlaneTx,
|
||||
};
|
||||
use actor::webview::WebviewActorRequest;
|
||||
pub use args::*;
|
||||
use once_cell::sync::OnceCell;
|
||||
pub use outline::Outline;
|
||||
|
||||
use std::pin::Pin;
|
||||
|
@ -29,93 +31,7 @@ use typst_ts_core::{ImmutStr, TypstDocument as Document};
|
|||
use crate::actor::editor::EditorActorRequest;
|
||||
use crate::actor::render::RenderActorRequest;
|
||||
use actor::editor::{EditorActor, EditorConnection};
|
||||
use actor::typst::TypstActor;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DocToSrcJumpInfo {
|
||||
pub filepath: String,
|
||||
pub start: Option<(usize, usize)>, // row, column
|
||||
pub end: Option<(usize, usize)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct ChangeCursorPositionRequest {
|
||||
filepath: PathBuf,
|
||||
line: usize,
|
||||
/// fixme: character is 0-based, UTF-16 code unit.
|
||||
/// We treat it as UTF-8 now.
|
||||
character: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SrcToDocJumpRequest {
|
||||
filepath: PathBuf,
|
||||
line: usize,
|
||||
/// fixme: character is 0-based, UTF-16 code unit.
|
||||
/// We treat it as UTF-8 now.
|
||||
character: usize,
|
||||
}
|
||||
|
||||
impl SrcToDocJumpRequest {
|
||||
pub fn to_byte_offset(&self, src: &typst::syntax::Source) -> Option<usize> {
|
||||
src.line_column_to_byte(self.line, self.character)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct MemoryFiles {
|
||||
pub files: HashMap<PathBuf, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct MemoryFilesShort {
|
||||
pub files: Vec<PathBuf>,
|
||||
// mtime: Option<u64>,
|
||||
}
|
||||
|
||||
pub struct CompileWatcher {
|
||||
task_id: String,
|
||||
refresh_style: RefreshStyle,
|
||||
doc_sender: watch::Sender<Option<Arc<Document>>>,
|
||||
editor_tx: mpsc::UnboundedSender<EditorActorRequest>,
|
||||
render_tx: broadcast::Sender<RenderActorRequest>,
|
||||
}
|
||||
|
||||
impl CompileWatcher {
|
||||
pub fn task_id(&self) -> &str {
|
||||
&self.task_id
|
||||
}
|
||||
|
||||
pub fn status(&self, status: CompileStatus) {
|
||||
let _ = self
|
||||
.editor_tx
|
||||
.send(EditorActorRequest::CompileStatus(status));
|
||||
}
|
||||
|
||||
pub fn notify_compile(&self, res: Result<Arc<Document>, CompileStatus>, is_on_saved: bool) {
|
||||
if self.refresh_style == RefreshStyle::OnSave && !is_on_saved {
|
||||
return;
|
||||
}
|
||||
|
||||
match res {
|
||||
Ok(doc) => {
|
||||
// it is ok to ignore the error here
|
||||
let _ = self.doc_sender.send(Some(doc));
|
||||
|
||||
// todo: is it right that ignore zero broadcast receiver?
|
||||
let _ = self.render_tx.send(RenderActorRequest::RenderIncremental);
|
||||
let _ = self.editor_tx.send(EditorActorRequest::CompileStatus(
|
||||
CompileStatus::CompileSuccess,
|
||||
));
|
||||
}
|
||||
Err(status) => {
|
||||
let _ = self
|
||||
.editor_tx
|
||||
.send(EditorActorRequest::CompileStatus(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
use actor::typst::{TypstActor, TypstActorRequest};
|
||||
|
||||
type StopFuture = Pin<Box<dyn Future<Output = ()> + Send + Sync>>;
|
||||
|
||||
|
@ -125,7 +41,6 @@ pub struct Previewer {
|
|||
data_plane_handle: tokio::task::JoinHandle<()>,
|
||||
control_plane_handle: tokio::task::JoinHandle<()>,
|
||||
data_plane_port: u16,
|
||||
compile_watcher: Arc<CompileWatcher>,
|
||||
}
|
||||
|
||||
impl Previewer {
|
||||
|
@ -138,10 +53,6 @@ impl Previewer {
|
|||
self.data_plane_port
|
||||
}
|
||||
|
||||
pub fn compile_watcher(&self) -> &Arc<CompileWatcher> {
|
||||
&self.compile_watcher
|
||||
}
|
||||
|
||||
/// Join the previewer actors.
|
||||
pub async fn join(self) {
|
||||
let _ = tokio::join!(self.data_plane_handle, self.control_plane_handle);
|
||||
|
@ -154,86 +65,38 @@ impl Previewer {
|
|||
}
|
||||
}
|
||||
|
||||
pub type SourceLocation = typst_ts_core::debug_loc::SourceLocation;
|
||||
|
||||
pub enum Location {
|
||||
Src(SourceLocation),
|
||||
}
|
||||
|
||||
pub trait SourceFileServer {
|
||||
fn resolve_source_span(
|
||||
&self,
|
||||
_by: Location,
|
||||
) -> impl Future<Output = Result<Option<SourceSpanOffset>, Error>> + Send {
|
||||
async { Ok(None) }
|
||||
}
|
||||
|
||||
fn resolve_document_position(
|
||||
&self,
|
||||
_by: Location,
|
||||
) -> impl Future<Output = Result<Option<Position>, Error>> + Send {
|
||||
async { Ok(None) }
|
||||
}
|
||||
|
||||
fn resolve_source_location(
|
||||
&self,
|
||||
_s: Span,
|
||||
_offset: Option<usize>,
|
||||
) -> impl Future<Output = Result<Option<DocToSrcJumpInfo>, Error>> + Send {
|
||||
async { Ok(None) }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EditorServer {
|
||||
fn update_memory_files(
|
||||
&self,
|
||||
_files: MemoryFiles,
|
||||
_reset_shadow: bool,
|
||||
) -> impl Future<Output = Result<(), Error>> + Send {
|
||||
async { Ok(()) }
|
||||
}
|
||||
|
||||
fn remove_shadow_files(
|
||||
&self,
|
||||
_files: MemoryFilesShort,
|
||||
) -> impl Future<Output = Result<(), Error>> + Send {
|
||||
async { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CompileHost: SourceFileServer + EditorServer {}
|
||||
|
||||
pub async fn preview<T: CompileHost + Send + Sync + 'static>(
|
||||
arguments: PreviewArgs,
|
||||
client: Arc<T>,
|
||||
lsp_connection: Option<LspControlPlaneTx>,
|
||||
html: &str,
|
||||
) -> Previewer {
|
||||
let enable_partial_rendering = arguments.enable_partial_rendering;
|
||||
let invert_colors = arguments.invert_colors;
|
||||
let idle_timeout = Duration::from_secs(5);
|
||||
PreviewBuilder::new(arguments).start(client, html).await
|
||||
}
|
||||
|
||||
// Creates the world that serves sources, fonts and files.
|
||||
let actor::typst::Channels {
|
||||
async fn preview_<T: CompileHost + Send + Sync + 'static>(
|
||||
builder: PreviewBuilder,
|
||||
client: Arc<T>,
|
||||
html: &str,
|
||||
) -> Previewer {
|
||||
let PreviewBuilder {
|
||||
arguments,
|
||||
lsp_connection,
|
||||
typst_mailbox,
|
||||
renderer_mailbox,
|
||||
editor_conn,
|
||||
webview_conn: (webview_tx, _),
|
||||
} = TypstActor::<()>::set_up_channels();
|
||||
doc_sender,
|
||||
..
|
||||
} = builder;
|
||||
let enable_partial_rendering = arguments.enable_partial_rendering;
|
||||
let invert_colors = arguments.invert_colors;
|
||||
let idle_timeout = Duration::from_secs(5);
|
||||
|
||||
// Shared resource
|
||||
let span_interner = SpanInterner::new();
|
||||
|
||||
// Set callback
|
||||
let doc_watcher = watch::channel::<Option<Arc<Document>>>(None);
|
||||
let compile_watcher = CompileWatcher {
|
||||
task_id: arguments.task_id,
|
||||
refresh_style: arguments.refresh_style,
|
||||
doc_sender: doc_watcher.0,
|
||||
editor_tx: editor_conn.0.clone(),
|
||||
render_tx: renderer_mailbox.0.clone(),
|
||||
};
|
||||
|
||||
// Spawns the typst actor
|
||||
let typst_actor = TypstActor::new(
|
||||
client,
|
||||
|
@ -309,7 +172,7 @@ pub async fn preview<T: CompileHost + Send + Sync + 'static>(
|
|||
});
|
||||
let render_actor = actor::render::RenderActor::new(
|
||||
renderer_tx.subscribe(),
|
||||
doc_watcher.1.clone(),
|
||||
doc_sender.subscribe(),
|
||||
typst_tx,
|
||||
svg.0,
|
||||
webview_tx,
|
||||
|
@ -317,7 +180,7 @@ pub async fn preview<T: CompileHost + Send + Sync + 'static>(
|
|||
tokio::spawn(render_actor.run());
|
||||
let outline_render_actor = actor::render::OutlineRenderActor::new(
|
||||
renderer_tx.subscribe(),
|
||||
doc_watcher.1.clone(),
|
||||
doc_sender.subscribe(),
|
||||
editor_tx.clone(),
|
||||
span_interner,
|
||||
);
|
||||
|
@ -415,7 +278,194 @@ pub async fn preview<T: CompileHost + Send + Sync + 'static>(
|
|||
control_plane_handle,
|
||||
data_plane_port,
|
||||
stop: Some(Box::new(stop)),
|
||||
compile_watcher: Arc::new(compile_watcher),
|
||||
}
|
||||
}
|
||||
|
||||
type MpScChannel<T> = (mpsc::UnboundedSender<T>, mpsc::UnboundedReceiver<T>);
|
||||
type BroadcastChannel<T> = (broadcast::Sender<T>, broadcast::Receiver<T>);
|
||||
|
||||
pub struct PreviewBuilder {
|
||||
arguments: PreviewArgs,
|
||||
lsp_connection: Option<LspControlPlaneTx>,
|
||||
|
||||
typst_mailbox: MpScChannel<TypstActorRequest>,
|
||||
renderer_mailbox: BroadcastChannel<RenderActorRequest>,
|
||||
editor_conn: MpScChannel<EditorActorRequest>,
|
||||
webview_conn: BroadcastChannel<WebviewActorRequest>,
|
||||
doc_sender: watch::Sender<Option<Arc<Document>>>,
|
||||
|
||||
compile_watcher: OnceCell<Arc<CompileWatcher>>,
|
||||
}
|
||||
|
||||
impl PreviewBuilder {
|
||||
pub fn new(arguments: PreviewArgs) -> Self {
|
||||
Self {
|
||||
arguments,
|
||||
lsp_connection: None,
|
||||
typst_mailbox: mpsc::unbounded_channel(),
|
||||
renderer_mailbox: broadcast::channel(1024),
|
||||
editor_conn: mpsc::unbounded_channel(),
|
||||
webview_conn: broadcast::channel(32),
|
||||
doc_sender: watch::channel(None).0,
|
||||
compile_watcher: OnceCell::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_lsp_connection(mut self, lsp_connection: Option<LspControlPlaneTx>) -> Self {
|
||||
self.lsp_connection = lsp_connection;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn compile_watcher(&self) -> &Arc<CompileWatcher> {
|
||||
self.compile_watcher.get_or_init(|| {
|
||||
Arc::new(CompileWatcher {
|
||||
task_id: self.arguments.task_id.clone(),
|
||||
refresh_style: self.arguments.refresh_style,
|
||||
doc_sender: self.doc_sender.clone(),
|
||||
editor_tx: self.editor_conn.0.clone(),
|
||||
render_tx: self.renderer_mailbox.0.clone(),
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn start<T>(self, client: Arc<T>, html: &str) -> Previewer
|
||||
where
|
||||
T: CompileHost + Send + Sync + 'static,
|
||||
{
|
||||
preview_(self, client, html).await
|
||||
}
|
||||
}
|
||||
|
||||
pub type SourceLocation = typst_ts_core::debug_loc::SourceLocation;
|
||||
|
||||
pub enum Location {
|
||||
Src(SourceLocation),
|
||||
}
|
||||
|
||||
pub trait SourceFileServer {
|
||||
fn resolve_source_span(
|
||||
&self,
|
||||
_by: Location,
|
||||
) -> impl Future<Output = Result<Option<SourceSpanOffset>, Error>> + Send {
|
||||
async { Ok(None) }
|
||||
}
|
||||
|
||||
fn resolve_document_position(
|
||||
&self,
|
||||
_by: Location,
|
||||
) -> impl Future<Output = Result<Option<Position>, Error>> + Send {
|
||||
async { Ok(None) }
|
||||
}
|
||||
|
||||
fn resolve_source_location(
|
||||
&self,
|
||||
_s: Span,
|
||||
_offset: Option<usize>,
|
||||
) -> impl Future<Output = Result<Option<DocToSrcJumpInfo>, Error>> + Send {
|
||||
async { Ok(None) }
|
||||
}
|
||||
}
|
||||
|
||||
pub trait EditorServer {
|
||||
fn update_memory_files(
|
||||
&self,
|
||||
_files: MemoryFiles,
|
||||
_reset_shadow: bool,
|
||||
) -> impl Future<Output = Result<(), Error>> + Send {
|
||||
async { Ok(()) }
|
||||
}
|
||||
|
||||
fn remove_shadow_files(
|
||||
&self,
|
||||
_files: MemoryFilesShort,
|
||||
) -> impl Future<Output = Result<(), Error>> + Send {
|
||||
async { Ok(()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct DocToSrcJumpInfo {
|
||||
pub filepath: String,
|
||||
pub start: Option<(usize, usize)>, // row, column
|
||||
pub end: Option<(usize, usize)>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct ChangeCursorPositionRequest {
|
||||
filepath: PathBuf,
|
||||
line: usize,
|
||||
/// fixme: character is 0-based, UTF-16 code unit.
|
||||
/// We treat it as UTF-8 now.
|
||||
character: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct SrcToDocJumpRequest {
|
||||
filepath: PathBuf,
|
||||
line: usize,
|
||||
/// fixme: character is 0-based, UTF-16 code unit.
|
||||
/// We treat it as UTF-8 now.
|
||||
character: usize,
|
||||
}
|
||||
|
||||
impl SrcToDocJumpRequest {
|
||||
pub fn to_byte_offset(&self, src: &typst::syntax::Source) -> Option<usize> {
|
||||
src.line_column_to_byte(self.line, self.character)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct MemoryFiles {
|
||||
pub files: HashMap<PathBuf, String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct MemoryFilesShort {
|
||||
pub files: Vec<PathBuf>,
|
||||
// mtime: Option<u64>,
|
||||
}
|
||||
|
||||
pub struct CompileWatcher {
|
||||
task_id: String,
|
||||
refresh_style: RefreshStyle,
|
||||
doc_sender: watch::Sender<Option<Arc<Document>>>,
|
||||
editor_tx: mpsc::UnboundedSender<EditorActorRequest>,
|
||||
render_tx: broadcast::Sender<RenderActorRequest>,
|
||||
}
|
||||
|
||||
impl CompileWatcher {
|
||||
pub fn task_id(&self) -> &str {
|
||||
&self.task_id
|
||||
}
|
||||
|
||||
pub fn status(&self, status: CompileStatus) {
|
||||
let _ = self
|
||||
.editor_tx
|
||||
.send(EditorActorRequest::CompileStatus(status));
|
||||
}
|
||||
|
||||
pub fn notify_compile(&self, res: Result<Arc<Document>, CompileStatus>, is_on_saved: bool) {
|
||||
if self.refresh_style == RefreshStyle::OnSave && !is_on_saved {
|
||||
return;
|
||||
}
|
||||
|
||||
match res {
|
||||
Ok(doc) => {
|
||||
// it is ok to ignore the error here
|
||||
let _ = self.doc_sender.send(Some(doc));
|
||||
|
||||
// todo: is it right that ignore zero broadcast receiver?
|
||||
let _ = self.render_tx.send(RenderActorRequest::RenderIncremental);
|
||||
let _ = self.editor_tx.send(EditorActorRequest::CompileStatus(
|
||||
CompileStatus::CompileSuccess,
|
||||
));
|
||||
}
|
||||
Err(status) => {
|
||||
let _ = self
|
||||
.editor_tx
|
||||
.send(EditorActorRequest::CompileStatus(status));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue