Sets up the groundwork for more Providers to use a request-based model that sends a JsDeferred to the event loop. This should hopefully be one of the last major changes before fully migrating to the event loop model.
3RNQI5RXZMXF5N2WKYS3O3G2GTD4TTWDNQ3EBVGUVVKNNOTKQTJAC WFWTKCJNC4VSURSQWE5FXJGTEJJOCDMQ6LN2O6EW2DNOVT2ZUMRQC LQJG2LGQMNJ35Y5H6XWCU3JWVIMOS5S7UQQQPIO5TCAXMLU3ORUQC 3YGYMEXVHOHRQVPC53MVPSLI2ZJM77MBPWNPDDCL56DNCPUC6XTAC LSZTPUQTQ3FA3YMG3OT5RX3NDQSXAY2IXFQ3MHHN2H3QD2WAM5DAC NB2MF3MYAJ25KNZP3GMHX42LUSBYOX6FTNLIBK3CQ7CMANFZFEGQC MGJ23FHFUNT4RSJP3AW5ZALGEDTNZPR4PLHLPOCFNEBIX2WHV2JAC OUADGWKR6A7G3UHCLSC7AWLJIMS45Y56NPVTQ7FENJPYNTAMXXMAC 2ZAM5V35CAQD5MOFPZEGU4ERQ2FM5CL5MB2DOJDW2ZQ74IQFBISQC SLTXBK5GTNH3PUZNA5VG7IDICV3NZPWSCTJSE4YPOQEDMWO67PIQC QY4Z5ZXZ7G6DZFCTRPX7D6TIDZ5DHSDTLOWFDANIPUHOEKQRAIXAC IDY5SNLOFZ663OCJ2L5NZMFNJXZSGL5O22QQXR5BIV3SJ2VUQIMQC ) -> Result<Option<vscode_sys::FileDecoration<'env>>, napi::Error> {let program_state = ExtensionState::get()?;let Some((repository_path, open_repository)) =program_state.repositories.get_open_repository(env, &uri)?else {return Ok(None);};let absolute_file_path = Utf8PathBuf::from(uri.get_fs_path()?);let relative_file_path =absolute_file_path.strip_prefix(&repository_path).map_err(|error| {napi::Error::from_reason(format!("Failed to strip prefix {repository_path} from {absolute_file_path}: {error}"))})?;let Some(path_state) = open_repository.repository.get_path_state(relative_file_path)else {return Ok(None);};// TODO: l10n_embedlet (badge, tooltip, color_id) = match path_state {PathState::Untracked => ("U", "Untracked", "pijul.decorations.path.untracked"),PathState::Tracked(modified_state) => match modified_state {TrackedState::Added => ("A", "Added", "pijul.decorations.path.added"),TrackedState::Removed => ("RM", "Removed", "pijul.decorations.path.removed"),TrackedState::Modified => ("M", "Modified", "pijul.decorations.path.modified"),TrackedState::Moved => ("MV", "Moved", "pijul.decorations.path.moved"),TrackedState::ModifiedAndMoved => ("M,MV","Modified, Moved","pijul.decorations.path.modifiedAndMoved",),},};let color = vscode_sys::ThemeColor::new(env, color_id)?;
) -> Result<bindgen_prelude::Object<'env>, napi::Error> {let (deferred_promise, promise_object) = env.create_deferred()?;let parsed_uri = UriAbsoluteString::try_from(uri.to_string()?).map_err(|error| napi::Error::from_reason(format!("Failed to parse URI: {error}")))?;event_loop::send(Event::RequestFileDecoration {uri: parsed_uri,deferred_promise,});
}struct ExtensionState {repositories: HashMap<Utf8PathBuf, Repository>,}impl ExtensionState {#[tracing::instrument(skip(self))]fn find_repository<'uri>(&'uri self,uri: &'uri UriAbsoluteStr,) -> Option<(&'uri Utf8Path, &'uri Repository)> {if uri.scheme_str() != "file" {return None;}for ancestor in Utf8Path::new(uri.path_str()).ancestors() {if let Some(repository) = self.repositories.get(ancestor) {return Some((ancestor, repository));}}None}
use camino::Utf8Path;use iri_string::types::{UriAbsoluteStr, UriAbsoluteString};use pijul_extension::path_state::{PathState, TrackedState};use crate::event_loop::ExtensionState;use crate::vscode_sys;pub type DeferredPromise = napi::JsDeferred<Option<vscode_sys::reference::FileDecorationRef>,Box<dyn FnOnce(napi::Env,)-> Result<Option<vscode_sys::reference::FileDecorationRef>, napi::Error>,>,>;#[tracing::instrument(skip(deferred_promise, extension_state))]pub async fn handle(uri: UriAbsoluteString,deferred_promise: DeferredPromise,extension_state: &ExtensionState,) {let optional_path_state = get_decoration(&uri, extension_state);deferred_promise.resolve(Box::new(move |env| {let Some(path_state) = optional_path_state else {return Ok(None);};// TODO: l10n_embedlet (badge, tooltip, color_id) = match path_state {PathState::Untracked => ("U", "Untracked", "pijul.decorations.path.untracked"),PathState::Tracked(modified_state) => match modified_state {TrackedState::Added => ("A", "Added", "pijul.decorations.path.added"),TrackedState::Removed => ("RM", "Removed", "pijul.decorations.path.removed"),TrackedState::Modified => ("M", "Modified", "pijul.decorations.path.modified"),TrackedState::Moved => ("MV", "Moved", "pijul.decorations.path.moved"),TrackedState::ModifiedAndMoved => ("M,MV","Modified, Moved","pijul.decorations.path.modifiedAndMoved",),},};let color = vscode_sys::ThemeColor::new(&env, color_id)?;let path_decoration = vscode_sys::FileDecoration::new(&env, badge, tooltip, &color)?;Ok(Some(path_decoration.create_ref()?))}));}fn get_decoration(uri: &UriAbsoluteStr, extension_state: &ExtensionState) -> Option<PathState> {if uri.scheme_str() != "file" {return None;}let Some((repository_path, repository)) = extension_state.find_repository(uri) else {tracing::info!(message = "No matching repository for path", ?uri);return None;};let decoration_path = match Utf8Path::new(uri.path_str()).strip_prefix(repository_path) {Ok(decoration_path) => decoration_path,Err(error) => {tracing::error!(message = "Failed to strip repository prefix from path",?repository_path,?uri,?error);return None;}};repository.repository.get_path_state(decoration_path)}
OpenWorkspaceFolder { uri: String },UpdateResourceStates { repository_path: Utf8PathBuf },
OpenWorkspaceFolder {uri: String,},RequestFileDecoration {uri: UriAbsoluteString,deferred_promise: request_file_decoration::DeferredPromise,},UpdateResourceStates {repository_path: Utf8PathBuf,},