lsp: Pass invalidation of files on to live-preview

This commit is contained in:
Tobias Hunger 2024-10-08 15:49:00 +02:00 committed by Tobias Hunger
parent e62d9e0695
commit 3d3bf9f01b
7 changed files with 65 additions and 14 deletions

View file

@ -847,6 +847,18 @@ impl TypeLoader {
myself
}
pub fn drop_document(&mut self, path: &Path) -> Result<(), std::io::Error> {
self.all_documents.docs.remove(path);
if self.all_documents.currently_loading.contains_key(path) {
Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("{path:?} is still loading"),
))
} else {
Ok(())
}
}
/// Imports of files that don't have the .slint extension are returned.
pub async fn load_dependencies_recursively<'a>(
&'a mut self,
@ -1016,6 +1028,17 @@ impl TypeLoader {
}
}
/// Read a file, taking the `CompilerConfiguration`s `open_import_fallback`
/// into account when necessary.
async fn read_file(&self, path: &Path) -> Result<String, std::io::Error> {
if let Some(fallback) = self.compiler_config.open_import_fallback.clone() {
let result = fallback(path.to_string_lossy().into()).await;
result.unwrap_or_else(|| std::fs::read_to_string(path))
} else {
std::fs::read_to_string(path)
}
}
async fn ensure_document_loaded<'a: 'b, 'b>(
state: &'a RefCell<BorrowedTypeLoader<'a>>,
file_to_import: &'b str,
@ -1097,14 +1120,8 @@ impl TypeLoader {
core::str::from_utf8(builtin)
.expect("internal error: embedded file is not UTF-8 source code"),
))
} else if let Some(fallback) = {
let fallback = state.borrow().tl.compiler_config.open_import_fallback.clone();
fallback
} {
let result = fallback(path_canon.to_string_lossy().into()).await;
result.unwrap_or_else(|| std::fs::read_to_string(&path_canon))
} else {
std::fs::read_to_string(&path_canon)
state.borrow().tl.read_file(&path_canon).await
};
let ok = match source_code_result {

View file

@ -481,6 +481,7 @@ pub struct PreviewComponent {
#[allow(unused)]
#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
pub enum LspToPreviewMessage {
InvalidateContents { url: lsp_types::Url },
SetContents { url: VersionedUrl, contents: String },
SetConfiguration { config: PreviewConfig },
ShowPreview(PreviewComponent),

View file

@ -281,6 +281,11 @@ impl DocumentCache {
Ok(())
}
pub fn drop_document(&mut self, url: &Url) -> Result<()> {
let path = uri_to_file(url).ok_or("Failed to convert path")?;
Ok(self.type_loader.drop_document(&path)?)
}
pub fn compiler_configuration(&self) -> CompilerConfiguration {
CompilerConfiguration {
include_paths: self.type_loader.compiler_config.include_paths.clone(),

View file

@ -614,7 +614,7 @@ pub async fn open_document(
pub async fn close_document(ctx: &Rc<Context>, url: lsp_types::Url) -> common::Result<()> {
ctx.open_urls.borrow_mut().remove(&url);
Ok(())
invalidate_document(ctx, url).await
}
pub async fn reload_document(
@ -638,9 +638,18 @@ pub async fn reload_document(
Ok(())
}
pub async fn trigger_file_watcher(ctx: &Context, url: &lsp_types::Url) -> common::Result<()> {
if !ctx.open_urls.borrow().contains(url) {
// do nothing for now...
pub async fn invalidate_document(ctx: &Rc<Context>, url: lsp_types::Url) -> common::Result<()> {
// The preview cares about resources and slint files, so forward everything
ctx.server_notifier.send_message_to_preview(common::LspToPreviewMessage::InvalidateContents {
url: url.clone(),
});
ctx.document_cache.borrow_mut().drop_document(&url)
}
pub async fn trigger_file_watcher(ctx: &Rc<Context>, url: lsp_types::Url) -> common::Result<()> {
if !ctx.open_urls.borrow().contains(&url) {
invalidate_document(ctx, url).await?;
}
Ok(())
}

View file

@ -460,7 +460,7 @@ async fn handle_notification(req: lsp_server::Notification, ctx: &Rc<Context>) -
DidChangeWatchedFiles::METHOD => {
let params: DidChangeWatchedFilesParams = serde_json::from_value(req.params)?;
for fe in params.changes {
trigger_file_watcher(ctx, &fe.uri).await?;
trigger_file_watcher(ctx, fe.uri).await?;
}
Ok(())
}

View file

@ -17,6 +17,7 @@ use i_slint_core::model::VecModel;
use lsp_types::Url;
use slint::PlatformError;
use slint_interpreter::{ComponentDefinition, ComponentHandle, ComponentInstance};
use std::borrow::BorrowMut;
use std::cell::RefCell;
use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf};
@ -124,6 +125,23 @@ pub fn poll_once<F: std::future::Future>(future: F) -> Option<F::Output> {
}
}
fn invalidate_contents(url: &lsp_types::Url) {
let component = {
let mut cache = CONTENT_CACHE.get_or_init(Default::default).lock().unwrap();
cache.source_code.remove(url);
(cache.dependency.contains(url) && cache.ui_is_visible)
.then_some(cache.current_component())
.flatten()
};
// No need to bother with the document_cache: It follows the preview state at all times!
if let Some(component) = component {
load_preview(component, LoadBehavior::Reload);
}
}
fn set_contents(url: &common::VersionedUrl, content: String) {
let mut cache = CONTENT_CACHE.get_or_init(Default::default).lock().unwrap();
let old = cache.source_code.insert(url.url().clone(), (*url.version(), content.clone()));
@ -960,7 +978,7 @@ fn finish_parsing(preview_url: &Url, ok: bool) {
let mut preview_state = preview_state.borrow_mut();
preview_state.known_components = components;
preview_state.document_cache.borrow_mut().replace(Rc::new(document_cache));
preview_state.document_cache.borrow_mut().replace(Some(Rc::new(document_cache)));
if let Some(ui) = &preview_state.ui {
ui::ui_set_uses_widgets(ui, uses_widgets);
@ -1667,6 +1685,7 @@ fn update_preview_area(
pub fn lsp_to_preview_message(message: crate::common::LspToPreviewMessage) {
use crate::common::LspToPreviewMessage as M;
match message {
M::InvalidateContents { url } => invalidate_contents(&url),
M::SetContents { url, contents } => {
set_contents(&url, contents);
}

View file

@ -338,7 +338,7 @@ impl SlintServer {
wasm_bindgen_futures::future_to_promise(async move {
let _lock = ReentryGuard::lock(guard).await;
let url: lsp_types::Url = serde_wasm_bindgen::from_value(url)?;
language::trigger_file_watcher(&ctx, &url)
language::trigger_file_watcher(&ctx, url)
.await
.map_err(|e| JsError::new(&e.to_string()))?;
Ok(JsValue::UNDEFINED)