Includes a new threadsafe function to get the text contents from a TextEditorRef. This probably has potential for a TOCTOU bug, but I'm not sure how to avoid this without unnecessary calls to Document::get_text() every time the visible editor changes.
M5RW5PN4VFYZOKHUFMVWR2XAUAXHUYROVZFTS3RIE7AAUGTVHAEQC WFWTKCJNC4VSURSQWE5FXJGTEJJOCDMQ6LN2O6EW2DNOVT2ZUMRQC 3YGYMEXVHOHRQVPC53MVPSLI2ZJM77MBPWNPDDCL56DNCPUC6XTAC NB2MF3MYAJ25KNZP3GMHX42LUSBYOX6FTNLIBK3CQ7CMANFZFEGQC OUADGWKR6A7G3UHCLSC7AWLJIMS45Y56NPVTQ7FENJPYNTAMXXMAC 2ZAM5V35CAQD5MOFPZEGU4ERQ2FM5CL5MB2DOJDW2ZQ74IQFBISQC TWEUQ64DU5ZXAEG67GQSS7GDEZ2O2JR5352WRVIIUK5J7ZOMO5QAC QY4Z5ZXZ7G6DZFCTRPX7D6TIDZ5DHSDTLOWFDANIPUHOEKQRAIXAC IBVCQSSGPKLQTD4T4TAJZRC4LY3G6FP4QWFEBHN5KEFI44ZGGAXQC IDY5SNLOFZ663OCJ2L5NZMFNJXZSGL5O22QQXR5BIV3SJ2VUQIMQC 3RNQI5RXZMXF5N2WKYS3O3G2GTD4TTWDNQ3EBVGUVVKNNOTKQTJAC for text_editor in &visible_text_editors {let uri = UriAbsoluteString::try_from(text_editor.get_document()?.get_uri()?.to_string()?).map_err(|error| napi::Error::from_reason(format!("Failed to parse URI: {error}")))?;event_loop::send(Event::OpenTextEditor {uri,text_editor: text_editor.create_ref()?,});}
for ancestor in Utf8Path::new(uri.path_str()).ancestors() {if let Some(repository) = self.repositories.get(ancestor) {return Some((ancestor, repository));
let uri_path = Utf8Path::new(uri.path_str());for ancestor in uri_path.ancestors() {if self.repositories.contains_key(ancestor) {return Some((ancestor, uri_path.strip_prefix(ancestor).unwrap()));
}#[tracing::instrument(skip(self))]fn get_repository<'uri>(&self,uri: &'uri UriAbsoluteStr,) -> Option<(&'uri Utf8Path, &'uri Utf8Path, &Repository)> {self.find_repository_root(uri).map(|(repository_path, relative_path)| {(repository_path,relative_path,self.repositories.get(repository_path).unwrap(),)})}#[tracing::instrument(skip(self))]fn get_repository_mut<'uri>(&mut self,uri: &'uri UriAbsoluteStr,) -> Option<(&'uri Utf8Path, &'uri Utf8Path, &mut Repository)> {self.find_repository_root(uri).map(|(repository_path, relative_path)| {(repository_path,relative_path,self.repositories.get_mut(repository_path).unwrap(),)})
}pub async fn get_text_editor_contents(&self,text_editor_reference: &Rc<vscode_sys::reference::TextEditorRef>,) -> Result<String, napi::Error> {let arguments = get_text_editor_contents::Arguments {text_editor_reference: Rc::clone(text_editor_reference),};self.get_text_editor_contents.call_async(External::new(arguments)).await
use std::rc::Rc;use napi::bindgen_prelude::{External, FunctionCallContext};use napi::threadsafe_function::ThreadsafeFunction;use crate::vscode_sys;pub struct Arguments {pub text_editor_reference: Rc<vscode_sys::reference::TextEditorRef>,}pub type Prototype = ThreadsafeFunction<External<Arguments>,String,External<Arguments>,napi::Status,false,false,0,>;pub fn build(env: &napi::Env) -> Result<Prototype, napi::Error> {env.create_function_from_closure("get_text_editor_contents", callback)?.build_threadsafe_function().build()}fn callback(function_call_context: FunctionCallContext) -> Result<String, napi::Error> {let (external,): (&External<Arguments>,) = function_call_context.args()?;let Arguments {text_editor_reference,} = &**external;let text_editor = text_editor_reference.get_inner(function_call_context.env)?;let document = text_editor.get_document()?;document.get_text(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)
repository.repository.get_path_state(relative_path)
use std::rc::Rc;use iri_string::types::UriAbsoluteString;use crate::event_loop::ExtensionState;use crate::event_loop::js_function::Functions;use crate::vscode_sys;#[tracing::instrument(skip(text_editor_reference, extension_state, js_functions))]pub async fn handle(uri: UriAbsoluteString,text_editor_reference: vscode_sys::reference::TextEditorRef,extension_state: &mut ExtensionState,js_functions: &Functions,) {let Some((repository_path, relative_path, repository)) =extension_state.get_repository_mut(&uri)else {// TODO: keep track of unassigned text editors in case they later become part of a repositorytracing::info!(message = "Ignoring text editor");return;};if repository.open_editors.contains_key(relative_path) {tracing::debug!(message = "Ignoring existing text editor",?repository_path,?relative_path);} else {let text_editor = Rc::new(text_editor_reference);let editor_contents = match js_functions.get_text_editor_contents(&text_editor).await {Ok(contents) => contents,Err(error) => {tracing::error!(message = "Failed to get text editor contents", ?error);return;}};if let Err(error) = repository.repository.create_open_file(relative_path.to_path_buf(), editor_contents){tracing::error!(message = "Failed to create open file", ?error);};repository.open_editors.insert(relative_path.to_path_buf(), text_editor);tracing::info!(message = "Opened new text editor",?repository_path,?relative_path);}}