use std::sync::MappedMutexGuard;
use std::sync::{Mutex, MutexGuard, OnceLock};
use napi::JsValue;
use napi::bindgen_prelude;
use napi::bindgen_prelude::{
FromNapiValue, JsObjectValue, ToNapiValue, TypeName, ValidateNapiValue,
};
use napi_sys::{napi_env, napi_value};
use macros::{
class_wrapper, class_wrapper_empty, empty_function_caller, field_getter, field_setter,
function_caller, function_setter, interface_builder, object_event_handler, static_class_method,
};
pub mod log;
mod macros;
pub mod reference;
pub mod scm;
pub mod window;
pub mod workspace;
static CONTEXT: Mutex<OnceLock<VscodeContext>> = Mutex::new(OnceLock::new());
struct VscodeContext {
vscode: bindgen_prelude::ObjectRef,
extension_context: bindgen_prelude::ObjectRef,
}
impl VscodeContext {
fn inner() -> Result<MappedMutexGuard<'static, Self>, napi::Error> {
let mutex_guard = CONTEXT.lock().map_err(|_error| {
napi::Error::from_reason("vscode_sys::Context mutex has been poisoned")
})?;
MutexGuard::filter_map(mutex_guard, |guarded_value| match guarded_value.get_mut() {
Some(vscode_context) => Some(vscode_context),
None => None,
})
.map_err(|_empty_guard| {
napi::Error::new(napi::Status::GenericFailure, "`vscode` object is not set")
})
}
fn extension_context<'env>(
env: &'env napi::Env,
) -> Result<bindgen_prelude::Object<'env>, napi::Error> {
let context = Self::inner()?;
context.extension_context.get_value(env)
}
fn vscode<'env>(env: &'env napi::Env) -> Result<bindgen_prelude::Object<'env>, napi::Error> {
let context = Self::inner()?;
context.vscode.get_value(env)
}
}
pub fn activate(
vscode_object: &bindgen_prelude::Object,
extension_context: &bindgen_prelude::Object,
) -> Result<(), napi::Error> {
let mutex_guard = CONTEXT.lock().map_err(|_error| {
napi::Error::from_reason(
"vscode_sys::Context mutex was poisoned before it could be initialized",
)
})?;
let context = VscodeContext {
vscode: vscode_object.create_ref()?,
extension_context: extension_context.create_ref()?,
};
mutex_guard.set(context).map_err(|_error| {
napi::Error::from_reason(
"`activate()` has already been called, but must be called exactly once",
)
})?;
Ok(())
}
class_wrapper_empty!(EventEmitter);
field_getter! {
EventEmitter {
"event": get_event -> bindgen_prelude::Unknown<'env>;
}
}
function_caller! {
EventEmitter {
"fire":
fire(
data: bindgen_prelude::Object,
) -> ();
}
}
class_wrapper!(FileDecoration(badge: &str, tooltip: &str, color: &ThemeColor));
class_wrapper!(MarkdownString(value: &str, support_theme_icons: bool));
field_setter! {
MarkdownString {
"supportHtml": set_support_html(bool);
}
}
class_wrapper!(Position(line: u32, character: u32));
field_getter! {
Position {
"line": get_line -> u32;
}
}
class_wrapper!(Range(start: &Position<'env>, end: &Position<'env>));
class_wrapper!(Selection(anchor: &Position<'env>, active: &Position<'env>));
field_getter! {
Selection {
"start": get_start -> Position<'env>;
"end": get_end -> Position<'env>;
}
}
class_wrapper!(ThemeColor(id: &str));
class_wrapper!(Uri(scheme: &str, authority: &str, path: &str, query: &str, fragment: &str));
field_getter! {
Uri {
"fsPath": get_fs_path -> String;
"scheme": get_scheme -> String;
}
}
empty_function_caller! {
Uri {
"toString":
to_string() -> String;
}
}
function_caller! {
Uri {
"with":
with(
change: UriWithChange,
) -> Uri<'env>;
}
}
static_class_method! {
Uri {
"file":
file(
path: &str,
) -> Uri<'env>;
}
}
interface_builder! {
DecorationOptions {
("range": Range, range: &Range),
}
("hoverMessage", hover_message: &MarkdownString),
("renderOptions", render_options: DecorationInstanceRenderOptions),
}
interface_builder! {
DecorationInstanceRenderOptions {}
("after", after: ThemableDecorationAttachmentRenderOptions),
}
interface_builder! {
DecorationRenderOptions {}
("after", after: ThemableDecorationAttachmentRenderOptions),
}
interface_builder! {
FileDecorationProvider {
F: set_provide_file_decoration =
provide_file_decoration(
uri: Uri,
cancellation_token: bindgen_prelude::Object
) -> FileDecoration<'function_context>;
}
}
field_setter! {
FileDecorationProvider {
"onDidChangeFileDecorations": set_on_did_change_file_decorations(bindgen_prelude::Unknown);
}
}
function_setter! {
FileDecorationProvider {
"provideFileDecoration" {
set_provide_file_decoration(
uri: Uri,
cancellation_token: bindgen_prelude::Object
) -> FileDecoration<'function_context>;
bindgen_prelude::ObjectRef = |returned_result: Option<FileDecoration>| returned_result.map(|object| object.inner.create_ref()).transpose();
}
}
}
interface_builder! {
FileSystemWatcher {}
}
object_event_handler! {
FileSystemWatcher {
"onDidChange": on_did_change(Uri);
"onDidCreate": on_did_create(Uri);
"onDidDelete": on_did_delete(Uri);
}
}
interface_builder! {
QuickDiffProvider {}
}
function_setter! {
QuickDiffProvider {
"provideOriginalResource" {
set_provide_original_resource(
uri: Uri<'function_context>,
cancellation_token: bindgen_prelude::Object<'function_context>
) -> Uri<'function_context>;
bindgen_prelude::ObjectRef = |returned_result: Option<Uri>| returned_result.map(|object| object.inner.create_ref()).transpose();
}
}
}
interface_builder! {
SourceControl {}
}
field_setter! {
SourceControl {
"quickDiffProvider": set_quick_diff_provider(QuickDiffProvider);
}
}
function_caller! {
SourceControl {
"createResourceGroup":
create_resource_group(
id: &str,
label: &str,
) -> SourceControlResourceGroup<'env>;
}
}
interface_builder! {
SourceControlResourceGroup {}
}
field_setter! {
SourceControlResourceGroup {
"resourceStates": set_resource_states(Vec<SourceControlResourceState>);
}
}
interface_builder! {
SourceControlResourceState {
("resourceUri": Uri, resource_uri: &Uri),
}
}
interface_builder! {
TextDocument {}
}
field_getter! {
TextDocument {
"isDirty": get_is_dirty -> bool;
"uri": get_uri -> Uri<'env>;
}
}
function_caller! {
TextDocument {
"getText":
get_text(
range: Option<Range>,
) -> String;
}
}
interface_builder! {
TextDocumentChangeEvent {}
}
field_getter! {
TextDocumentChangeEvent {
"document": get_document -> TextDocument<'env>;
"contentChanges": get_content_changes -> Vec<TextDocumentContentChangeEvent<'env>>;
}
}
interface_builder! {
TextDocumentContentChangeEvent {}
}
field_getter! {
TextDocumentContentChangeEvent {
"rangeOffset": get_range_offset -> u32;
"rangeLength": get_range_length -> u32;
"text": get_text -> String;
}
}
interface_builder! {
TextDocumentContentProvider {
F: set_provide_text_document_content =
provide_text_document_content(
uri: Uri<'function_context>,
cancellation_token: bindgen_prelude::Object<'function_context>
) -> String;
}
}
function_setter! {
TextDocumentContentProvider {
"provideTextDocumentContent" {
set_provide_text_document_content(
uri: Uri<'function_context>,
cancellation_token: bindgen_prelude::Object<'function_context>
) -> String;
String = |returned_result: Option<String>| Ok(returned_result);
}
}
}
interface_builder! {
TextEditor {}
}
field_getter! {
TextEditor {
"document": get_document -> TextDocument<'env>;
"selections": get_selections -> Vec<Selection<'env>>;
}
}
function_caller! {
TextEditor {
"setDecorations":
set_decorations(
decoration_type: TextEditorDecorationType,
options: Vec<DecorationOptions>,
) -> ();
}
}
interface_builder! {
TextEditorDecorationType {}
}
interface_builder! {
TextEditorSelectionChangeEvent {}
}
field_getter! {
TextEditorSelectionChangeEvent {
"textEditor": get_text_editor -> TextEditor<'env>;
"selections": get_selections -> Vec<Selection<'env>>;
}
}
interface_builder! {
ThemableDecorationAttachmentRenderOptions {}
("color", color: &ThemeColor),
("contentText", content_text: &str),
("margin", margin: &str),
}
interface_builder! {
UriWithChange {}
("scheme", scheme: &str),
("authority", authority: &str),
("path", path: &str),
("query", query: &str),
("fragment", fragment: &str),
}
interface_builder! {
WorkspaceFolder {}
}
field_getter! {
WorkspaceFolder {
"uri": get_uri -> Uri<'env>;
}
}
interface_builder! {
WorkspaceFoldersChangeEvent {}
}
field_getter! {
WorkspaceFoldersChangeEvent {
"added": get_added -> Vec<WorkspaceFolder<'env>>;
"removed": get_removed -> Vec<WorkspaceFolder<'env>>;
}
}