Add `OpenTextEditor` event

finchie
Jan 2, 2026, 12:53 AM
M5RW5PN4VFYZOKHUFMVWR2XAUAXHUYROVZFTS3RIE7AAUGTVHAEQC

Dependencies

  • [2] WFWTKCJN Create initial Visual Studio Code extension
  • [3] 3YGYMEXV Create `event_loop` module
  • [4] NB2MF3MY Add `OpenWorkspaceFolder` event
  • [5] OUADGWKR Create fully-initialized `SourceControl` object in `event_loop`
  • [6] 2ZAM5V35 Move event handling into modules
  • [7] TWEUQ64D Move threadsafe function builder logic into function-specific modules
  • [8] QY4Z5ZXZ Refactor `event_loop/threadsafe_function` file into `event_loop::js_function` module
  • [9] IBVCQSSG Use `External` to wrap `initialize_source_control` arguments
  • [10] IDY5SNLO Update source control resource states in event loop
  • [11] 3RNQI5RX Refactor `provide_file_decoration` to return a `Promise` resolved by `event_loop`

Change contents

  • edit in editors/vscode/src/lib.rs at line 238
    [2.117772]
    [2.117772]
    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()?,
    });
    }
  • replacement in editors/vscode/src/event_loop/mod.rs at line 6
    [11.538][11.538:577]()
    use iri_string::types::UriAbsoluteStr;
    [11.538]
    [3.198]
    use iri_string::types::{UriAbsoluteStr, UriAbsoluteString};
  • replacement in editors/vscode/src/event_loop/mod.rs at line 24
    [5.2708][5.2708:2786]()
    open_editors: HashMap<Utf8PathBuf, vscode_sys::reference::TextEditorRef>,
    [5.2708]
    [10.18]
    open_editors: HashMap<Utf8PathBuf, Rc<vscode_sys::reference::TextEditorRef>>,
  • replacement in editors/vscode/src/event_loop/mod.rs at line 35
    [11.721][11.721:771]()
    fn find_repository<'uri>(
    &'uri self,
    [11.721]
    [11.771]
    fn find_repository_root<'uri>(
    &self,
  • replacement in editors/vscode/src/event_loop/mod.rs at line 38
    [11.806][11.806:860]()
    ) -> Option<(&'uri Utf8Path, &'uri Repository)> {
    [11.806]
    [11.860]
    ) -> Option<(&'uri Utf8Path, &'uri Utf8Path)> {
  • replacement in editors/vscode/src/event_loop/mod.rs at line 43
    [11.936][11.936:1129]()
    for ancestor in Utf8Path::new(uri.path_str()).ancestors() {
    if let Some(repository) = self.repositories.get(ancestor) {
    return Some((ancestor, repository));
    [11.936]
    [11.1129]
    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()));
  • edit in editors/vscode/src/event_loop/mod.rs at line 51
    [11.1167]
    [11.1167]
    }
    #[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(),
    )
    })
  • edit in editors/vscode/src/event_loop/mod.rs at line 115
    [10.589]
    [10.589]
    )
    .await
    }
    Event::OpenTextEditor { uri, text_editor } => {
    event::open_text_editor::handle(
    uri,
    text_editor,
    &mut extension_state,
    &js_functions,
  • edit in editors/vscode/src/event_loop/js_function/mod.rs at line 9
    [4.833]
    [9.56]
    pub mod get_text_editor_contents;
  • edit in editors/vscode/src/event_loop/js_function/mod.rs at line 14
    [8.510]
    [5.46]
    get_text_editor_contents: get_text_editor_contents::Prototype,
  • edit in editors/vscode/src/event_loop/js_function/mod.rs at line 23
    [4.1076]
    [7.0]
    get_text_editor_contents: get_text_editor_contents::build(env)?,
  • edit in editors/vscode/src/event_loop/js_function/mod.rs at line 28
    [4.1210]
    [4.1210]
    }
    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
  • file addition: get_text_editor_contents.rs (----------)
    [8.435]
    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)
    }
  • edit in editors/vscode/src/event_loop/event/request_file_decoration.rs at line 1
    [11.1917][11.1918:1940]()
    use camino::Utf8Path;
  • replacement in editors/vscode/src/event_loop/event/request_file_decoration.rs at line 58
    [11.4024][11.4024:4114]()
    let Some((repository_path, repository)) = extension_state.find_repository(uri) else {
    [11.4024]
    [11.4114]
    let Some((_repository_path, relative_path, repository)) = extension_state.get_repository(uri)
    else {
  • replacement in editors/vscode/src/event_loop/event/request_file_decoration.rs at line 64
    [11.4218][11.4218:4681]()
    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)
    [11.4218]
    [11.4681]
    repository.repository.get_path_state(relative_path)
  • file addition: open_text_editor.rs (----------)
    [6.332]
    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 repository
    tracing::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
    );
    }
    }
  • edit in editors/vscode/src/event_loop/event/mod.rs at line 3
    [11.4972]
    [10.5705]
    use crate::vscode_sys;
  • edit in editors/vscode/src/event_loop/event/mod.rs at line 6
    [10.5706]
    [6.3068]
    pub mod open_text_editor;
  • edit in editors/vscode/src/event_loop/event/mod.rs at line 15
    [11.5061]
    [11.5061]
    OpenTextEditor {
    uri: UriAbsoluteString,
    text_editor: vscode_sys::reference::TextEditorRef,
    },