extension_api.rs

  1//! The Zed Rust Extension API allows you write extensions for [Zed](https://zed.dev/) in Rust.
  2
  3pub mod http_client;
  4pub mod process;
  5pub mod settings;
  6
  7use core::fmt;
  8
  9use wit::*;
 10
 11pub use serde_json;
 12
 13// WIT re-exports.
 14//
 15// We explicitly enumerate the symbols we want to re-export, as there are some
 16// that we may want to shadow to provide a cleaner Rust API.
 17pub use wit::{
 18    CodeLabel, CodeLabelSpan, CodeLabelSpanLiteral, Command, DownloadedFileType, EnvVars,
 19    KeyValueStore, LanguageServerInstallationStatus, Project, Range, Worktree, download_file,
 20    make_file_executable,
 21    zed::extension::context_server::ContextServerConfiguration,
 22    zed::extension::dap::{
 23        AttachRequest, BuildTaskDefinition, BuildTaskDefinitionTemplatePayload, BuildTaskTemplate,
 24        DebugAdapterBinary, DebugConfig, DebugRequest, DebugScenario, DebugTaskDefinition,
 25        LaunchRequest, StartDebuggingRequestArguments, StartDebuggingRequestArgumentsRequest,
 26        TaskTemplate, TcpArguments, TcpArgumentsTemplate, resolve_tcp_template,
 27    },
 28    zed::extension::github::{
 29        GithubRelease, GithubReleaseAsset, GithubReleaseOptions, github_release_by_tag_name,
 30        latest_github_release,
 31    },
 32    zed::extension::llm_provider::{
 33        CacheConfiguration as LlmCacheConfiguration, CompletionEvent as LlmCompletionEvent,
 34        CompletionRequest as LlmCompletionRequest, DeviceFlowPromptInfo as LlmDeviceFlowPromptInfo,
 35        ImageData as LlmImageData, MessageContent as LlmMessageContent,
 36        MessageRole as LlmMessageRole, ModelCapabilities as LlmModelCapabilities,
 37        ModelInfo as LlmModelInfo, OauthWebAuthConfig as LlmOauthWebAuthConfig,
 38        OauthWebAuthResult as LlmOauthWebAuthResult, ProviderInfo as LlmProviderInfo,
 39        RequestMessage as LlmRequestMessage, StopReason as LlmStopReason,
 40        ThinkingContent as LlmThinkingContent, TokenUsage as LlmTokenUsage,
 41        ToolChoice as LlmToolChoice, ToolDefinition as LlmToolDefinition,
 42        ToolInputFormat as LlmToolInputFormat, ToolResult as LlmToolResult,
 43        ToolResultContent as LlmToolResultContent, ToolUse as LlmToolUse,
 44        ToolUseJsonParseError as LlmToolUseJsonParseError,
 45        delete_credential as llm_delete_credential, get_credential as llm_get_credential,
 46        get_env_var as llm_get_env_var, oauth_open_browser as llm_oauth_open_browser,
 47        oauth_send_http_request as llm_oauth_send_http_request,
 48        oauth_start_web_auth as llm_oauth_start_web_auth, store_credential as llm_store_credential,
 49    },
 50    zed::extension::nodejs::{
 51        node_binary_path, npm_install_package, npm_package_installed_version,
 52        npm_package_latest_version,
 53    },
 54    zed::extension::platform::{Architecture, Os, current_platform},
 55    zed::extension::slash_command::{
 56        SlashCommand, SlashCommandArgumentCompletion, SlashCommandOutput, SlashCommandOutputSection,
 57    },
 58};
 59
 60// Undocumented WIT re-exports.
 61//
 62// These are symbols that need to be public for the purposes of implementing
 63// the extension host, but aren't relevant to extension authors.
 64#[doc(hidden)]
 65pub use wit::Guest;
 66
 67/// Constructs for interacting with language servers over the
 68/// Language Server Protocol (LSP).
 69pub mod lsp {
 70    pub use crate::wit::zed::extension::lsp::{
 71        Completion, CompletionKind, InsertTextFormat, Symbol, SymbolKind,
 72    };
 73}
 74
 75/// A result returned from a Zed extension.
 76pub type Result<T, E = String> = core::result::Result<T, E>;
 77
 78/// Updates the installation status for the given language server.
 79pub fn set_language_server_installation_status(
 80    language_server_id: &LanguageServerId,
 81    status: &LanguageServerInstallationStatus,
 82) {
 83    wit::set_language_server_installation_status(&language_server_id.0, status)
 84}
 85
 86/// A Zed extension.
 87pub trait Extension: Send + Sync {
 88    /// Returns a new instance of the extension.
 89    fn new() -> Self
 90    where
 91        Self: Sized;
 92
 93    /// Returns the command used to start the language server for the specified
 94    /// language.
 95    fn language_server_command(
 96        &mut self,
 97        _language_server_id: &LanguageServerId,
 98        _worktree: &Worktree,
 99    ) -> Result<Command> {
100        Err("`language_server_command` not implemented".to_string())
101    }
102
103    /// Returns the initialization options to pass to the specified language server.
104    fn language_server_initialization_options(
105        &mut self,
106        _language_server_id: &LanguageServerId,
107        _worktree: &Worktree,
108    ) -> Result<Option<serde_json::Value>> {
109        Ok(None)
110    }
111
112    /// Returns the workspace configuration options to pass to the language server.
113    fn language_server_workspace_configuration(
114        &mut self,
115        _language_server_id: &LanguageServerId,
116        _worktree: &Worktree,
117    ) -> Result<Option<serde_json::Value>> {
118        Ok(None)
119    }
120
121    /// Returns the initialization options to pass to the other language server.
122    fn language_server_additional_initialization_options(
123        &mut self,
124        _language_server_id: &LanguageServerId,
125        _target_language_server_id: &LanguageServerId,
126        _worktree: &Worktree,
127    ) -> Result<Option<serde_json::Value>> {
128        Ok(None)
129    }
130
131    /// Returns the workspace configuration options to pass to the other language server.
132    fn language_server_additional_workspace_configuration(
133        &mut self,
134        _language_server_id: &LanguageServerId,
135        _target_language_server_id: &LanguageServerId,
136        _worktree: &Worktree,
137    ) -> Result<Option<serde_json::Value>> {
138        Ok(None)
139    }
140
141    /// Returns the label for the given completion.
142    fn label_for_completion(
143        &self,
144        _language_server_id: &LanguageServerId,
145        _completion: Completion,
146    ) -> Option<CodeLabel> {
147        None
148    }
149
150    /// Returns the label for the given symbol.
151    fn label_for_symbol(
152        &self,
153        _language_server_id: &LanguageServerId,
154        _symbol: Symbol,
155    ) -> Option<CodeLabel> {
156        None
157    }
158
159    /// Returns the completions that should be shown when completing the provided slash command with the given query.
160    fn complete_slash_command_argument(
161        &self,
162        _command: SlashCommand,
163        _args: Vec<String>,
164    ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
165        Ok(Vec::new())
166    }
167
168    /// Returns the output from running the provided slash command.
169    fn run_slash_command(
170        &self,
171        _command: SlashCommand,
172        _args: Vec<String>,
173        _worktree: Option<&Worktree>,
174    ) -> Result<SlashCommandOutput, String> {
175        Err("`run_slash_command` not implemented".to_string())
176    }
177
178    /// Returns the command used to start a context server.
179    fn context_server_command(
180        &mut self,
181        _context_server_id: &ContextServerId,
182        _project: &Project,
183    ) -> Result<Command> {
184        Err("`context_server_command` not implemented".to_string())
185    }
186
187    /// Returns the configuration options for the specified context server.
188    fn context_server_configuration(
189        &mut self,
190        _context_server_id: &ContextServerId,
191        _project: &Project,
192    ) -> Result<Option<ContextServerConfiguration>> {
193        Ok(None)
194    }
195
196    /// Returns a list of package names as suggestions to be included in the
197    /// search results of the `/docs` slash command.
198    ///
199    /// This can be used to provide completions for known packages (e.g., from the
200    /// local project or a registry) before a package has been indexed.
201    fn suggest_docs_packages(&self, _provider: String) -> Result<Vec<String>, String> {
202        Ok(Vec::new())
203    }
204
205    /// Indexes the docs for the specified package.
206    fn index_docs(
207        &self,
208        _provider: String,
209        _package: String,
210        _database: &KeyValueStore,
211    ) -> Result<(), String> {
212        Err("`index_docs` not implemented".to_string())
213    }
214
215    /// Returns the debug adapter binary for the specified adapter name and configuration.
216    fn get_dap_binary(
217        &mut self,
218        _adapter_name: String,
219        _config: DebugTaskDefinition,
220        _user_provided_debug_adapter_path: Option<String>,
221        _worktree: &Worktree,
222    ) -> Result<DebugAdapterBinary, String> {
223        Err("`get_dap_binary` not implemented".to_string())
224    }
225
226    /// Determines whether the specified adapter configuration should *launch* a new debuggee process
227    /// or *attach* to an existing one. This function should not perform any further validation (outside of determining the kind of a request).
228    /// This function should return an error when the kind cannot be determined (rather than fall back to a known default).
229    fn dap_request_kind(
230        &mut self,
231        _adapter_name: String,
232        _config: serde_json::Value,
233    ) -> Result<StartDebuggingRequestArgumentsRequest, String> {
234        Err("`dap_request_kind` not implemented".to_string())
235    }
236    /// Converts a high-level definition of a debug scenario (originating in a new session UI) to a "low-level" configuration suitable for a particular adapter.
237    ///
238    /// In layman's terms: given a program, list of arguments, current working directory and environment variables,
239    /// create a configuration that can be used to start a debug session.
240    fn dap_config_to_scenario(&mut self, _config: DebugConfig) -> Result<DebugScenario, String> {
241        Err("`dap_config_to_scenario` not implemented".to_string())
242    }
243
244    /// Locators are entities that convert a Zed task into a debug scenario.
245    ///
246    /// They can be provided even by extensions that don't provide a debug adapter.
247    /// For all tasks applicable to a given buffer, Zed will query all locators to find one that can turn the task into a debug scenario.
248    /// A converted debug scenario can include a build task (it shouldn't contain any configuration in such case); a build task result will later
249    /// be resolved with [`Extension::run_dap_locator`].
250    ///
251    /// To work through a real-world example, take a `cargo run` task and a hypothetical `cargo` locator:
252    /// 1. We may need to modify the task; in this case, it is problematic that `cargo run` spawns a binary. We should turn `cargo run` into a debug scenario with
253    ///    `cargo build` task. This is the decision we make at `dap_locator_create_scenario` scope.
254    /// 2. Then, after the build task finishes, we will run `run_dap_locator` of the locator that produced the build task to find the program to be debugged. This function
255    ///    should give us a debugger-agnostic configuration for launching a debug target (that we end up resolving with [`Extension::dap_config_to_scenario`]). It's almost as if the user
256    ///    found the artifact path by themselves.
257    ///
258    /// Note that you're not obliged to use build tasks with locators. Specifically, it is sufficient to provide a debug configuration directly in the return value of
259    /// `dap_locator_create_scenario` if you're able to do that. Make sure to not fill out `build` field in that case, as that will prevent Zed from running second phase of resolution in such case.
260    /// This might be of particular relevance to interpreted languages.
261    fn dap_locator_create_scenario(
262        &mut self,
263        _locator_name: String,
264        _build_task: TaskTemplate,
265        _resolved_label: String,
266        _debug_adapter_name: String,
267    ) -> Option<DebugScenario> {
268        None
269    }
270
271    /// Runs the second phase of locator resolution.
272    /// See [`Extension::dap_locator_create_scenario`] for a hefty comment on locators.
273    fn run_dap_locator(
274        &mut self,
275        _locator_name: String,
276        _build_task: TaskTemplate,
277    ) -> Result<DebugRequest, String> {
278        Err("`run_dap_locator` not implemented".to_string())
279    }
280
281    /// Returns information about language model providers offered by this extension.
282    fn llm_providers(&self) -> Vec<LlmProviderInfo> {
283        Vec::new()
284    }
285
286    /// Returns the models available for a provider.
287    fn llm_provider_models(&self, _provider_id: &str) -> Result<Vec<LlmModelInfo>, String> {
288        Ok(Vec::new())
289    }
290
291    /// Returns markdown content to display in the provider's settings UI.
292    /// This can include setup instructions, links to documentation, etc.
293    fn llm_provider_settings_markdown(&self, _provider_id: &str) -> Option<String> {
294        None
295    }
296
297    /// Check if the provider is authenticated.
298    fn llm_provider_is_authenticated(&self, _provider_id: &str) -> bool {
299        false
300    }
301
302    /// Start an OAuth device flow sign-in.
303    /// This is called when the user explicitly clicks "Sign in with GitHub" or similar.
304    /// Returns information needed to display the device flow prompt modal to the user.
305    fn llm_provider_start_device_flow_sign_in(
306        &mut self,
307        _provider_id: &str,
308    ) -> Result<LlmDeviceFlowPromptInfo, String> {
309        Err("`llm_provider_start_device_flow_sign_in` not implemented".to_string())
310    }
311
312    /// Poll for device flow sign-in completion.
313    /// This is called after llm_provider_start_device_flow_sign_in returns the user code.
314    /// The extension should poll the OAuth provider until the user authorizes or the flow times out.
315    fn llm_provider_poll_device_flow_sign_in(&mut self, _provider_id: &str) -> Result<(), String> {
316        Err("`llm_provider_poll_device_flow_sign_in` not implemented".to_string())
317    }
318
319    /// Reset credentials for the provider.
320    fn llm_provider_reset_credentials(&mut self, _provider_id: &str) -> Result<(), String> {
321        Err("`llm_provider_reset_credentials` not implemented".to_string())
322    }
323
324    /// Count tokens for a request.
325    fn llm_count_tokens(
326        &self,
327        _provider_id: &str,
328        _model_id: &str,
329        _request: &LlmCompletionRequest,
330    ) -> Result<u64, String> {
331        Err("`llm_count_tokens` not implemented".to_string())
332    }
333
334    /// Start streaming a completion from the model.
335    /// Returns a stream ID that can be used with `llm_stream_completion_next` and `llm_stream_completion_close`.
336    fn llm_stream_completion_start(
337        &mut self,
338        _provider_id: &str,
339        _model_id: &str,
340        _request: &LlmCompletionRequest,
341    ) -> Result<String, String> {
342        Err("`llm_stream_completion_start` not implemented".to_string())
343    }
344
345    /// Get the next event from a completion stream.
346    /// Returns `Ok(None)` when the stream is complete.
347    fn llm_stream_completion_next(
348        &mut self,
349        _stream_id: &str,
350    ) -> Result<Option<LlmCompletionEvent>, String> {
351        Err("`llm_stream_completion_next` not implemented".to_string())
352    }
353
354    /// Close a completion stream and release its resources.
355    fn llm_stream_completion_close(&mut self, _stream_id: &str) {
356        // Default implementation does nothing
357    }
358
359    /// Get cache configuration for a model (if prompt caching is supported).
360    fn llm_cache_configuration(
361        &self,
362        _provider_id: &str,
363        _model_id: &str,
364    ) -> Option<LlmCacheConfiguration> {
365        None
366    }
367}
368
369/// Registers the provided type as a Zed extension.
370///
371/// The type must implement the [`Extension`] trait.
372#[macro_export]
373macro_rules! register_extension {
374    ($extension_type:ty) => {
375        #[cfg(target_os = "wasi")]
376        mod wasi_ext {
377            unsafe extern "C" {
378                static mut errno: i32;
379                pub static mut __wasilibc_cwd: *mut std::ffi::c_char;
380            }
381
382            pub fn init_cwd() {
383                unsafe {
384                    // Ensure that our chdir function is linked, instead of the
385                    // one from wasi-libc in the chdir.o translation unit. Otherwise
386                    // we risk linking in `__wasilibc_find_relpath_alloc` which
387                    // is a weak symbol and is being used by
388                    // `__wasilibc_find_relpath`, which we do not want on
389                    // Windows.
390                    chdir(std::ptr::null());
391
392                    __wasilibc_cwd = std::ffi::CString::new(std::env::var("PWD").unwrap())
393                        .unwrap()
394                        .into_raw()
395                        .cast();
396                }
397            }
398
399            #[unsafe(no_mangle)]
400            pub unsafe extern "C" fn chdir(raw_path: *const std::ffi::c_char) -> i32 {
401                // Forbid extensions from changing CWD and so return an appropriate error code.
402                errno = 58; // NOTSUP
403                return -1;
404            }
405        }
406
407        #[unsafe(export_name = "init-extension")]
408        pub extern "C" fn __init_extension() {
409            #[cfg(target_os = "wasi")]
410            wasi_ext::init_cwd();
411
412            zed_extension_api::register_extension(|| {
413                Box::new(<$extension_type as zed_extension_api::Extension>::new())
414            });
415        }
416    };
417}
418
419#[doc(hidden)]
420pub fn register_extension(build_extension: fn() -> Box<dyn Extension>) {
421    unsafe { EXTENSION = Some((build_extension)()) }
422}
423
424fn extension() -> &'static mut dyn Extension {
425    #[expect(static_mut_refs)]
426    unsafe {
427        EXTENSION.as_deref_mut().unwrap()
428    }
429}
430
431static mut EXTENSION: Option<Box<dyn Extension>> = None;
432
433#[cfg(target_arch = "wasm32")]
434#[unsafe(link_section = "zed:api-version")]
435#[doc(hidden)]
436pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "/version_bytes"));
437
438mod wit {
439
440    wit_bindgen::generate!({
441        skip: ["init-extension"],
442        path: "./wit/since_v0.8.0",
443    });
444}
445
446wit::export!(Component);
447
448struct Component;
449
450impl wit::Guest for Component {
451    fn language_server_command(
452        language_server_id: String,
453        worktree: &wit::Worktree,
454    ) -> Result<wit::Command> {
455        let language_server_id = LanguageServerId(language_server_id);
456        extension().language_server_command(&language_server_id, worktree)
457    }
458
459    fn language_server_initialization_options(
460        language_server_id: String,
461        worktree: &Worktree,
462    ) -> Result<Option<String>, String> {
463        let language_server_id = LanguageServerId(language_server_id);
464        Ok(extension()
465            .language_server_initialization_options(&language_server_id, worktree)?
466            .and_then(|value| serde_json::to_string(&value).ok()))
467    }
468
469    fn language_server_workspace_configuration(
470        language_server_id: String,
471        worktree: &Worktree,
472    ) -> Result<Option<String>, String> {
473        let language_server_id = LanguageServerId(language_server_id);
474        Ok(extension()
475            .language_server_workspace_configuration(&language_server_id, worktree)?
476            .and_then(|value| serde_json::to_string(&value).ok()))
477    }
478
479    fn language_server_additional_initialization_options(
480        language_server_id: String,
481        target_language_server_id: String,
482        worktree: &Worktree,
483    ) -> Result<Option<String>, String> {
484        let language_server_id = LanguageServerId(language_server_id);
485        let target_language_server_id = LanguageServerId(target_language_server_id);
486        Ok(extension()
487            .language_server_additional_initialization_options(
488                &language_server_id,
489                &target_language_server_id,
490                worktree,
491            )?
492            .and_then(|value| serde_json::to_string(&value).ok()))
493    }
494
495    fn language_server_additional_workspace_configuration(
496        language_server_id: String,
497        target_language_server_id: String,
498        worktree: &Worktree,
499    ) -> Result<Option<String>, String> {
500        let language_server_id = LanguageServerId(language_server_id);
501        let target_language_server_id = LanguageServerId(target_language_server_id);
502        Ok(extension()
503            .language_server_additional_workspace_configuration(
504                &language_server_id,
505                &target_language_server_id,
506                worktree,
507            )?
508            .and_then(|value| serde_json::to_string(&value).ok()))
509    }
510
511    fn labels_for_completions(
512        language_server_id: String,
513        completions: Vec<Completion>,
514    ) -> Result<Vec<Option<CodeLabel>>, String> {
515        let language_server_id = LanguageServerId(language_server_id);
516        let mut labels = Vec::new();
517        for (ix, completion) in completions.into_iter().enumerate() {
518            let label = extension().label_for_completion(&language_server_id, completion);
519            if let Some(label) = label {
520                labels.resize(ix + 1, None);
521                *labels.last_mut().unwrap() = Some(label);
522            }
523        }
524        Ok(labels)
525    }
526
527    fn labels_for_symbols(
528        language_server_id: String,
529        symbols: Vec<Symbol>,
530    ) -> Result<Vec<Option<CodeLabel>>, String> {
531        let language_server_id = LanguageServerId(language_server_id);
532        let mut labels = Vec::new();
533        for (ix, symbol) in symbols.into_iter().enumerate() {
534            let label = extension().label_for_symbol(&language_server_id, symbol);
535            if let Some(label) = label {
536                labels.resize(ix + 1, None);
537                *labels.last_mut().unwrap() = Some(label);
538            }
539        }
540        Ok(labels)
541    }
542
543    fn complete_slash_command_argument(
544        command: SlashCommand,
545        args: Vec<String>,
546    ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
547        extension().complete_slash_command_argument(command, args)
548    }
549
550    fn run_slash_command(
551        command: SlashCommand,
552        args: Vec<String>,
553        worktree: Option<&Worktree>,
554    ) -> Result<SlashCommandOutput, String> {
555        extension().run_slash_command(command, args, worktree)
556    }
557
558    fn context_server_command(
559        context_server_id: String,
560        project: &Project,
561    ) -> Result<wit::Command> {
562        let context_server_id = ContextServerId(context_server_id);
563        extension().context_server_command(&context_server_id, project)
564    }
565
566    fn context_server_configuration(
567        context_server_id: String,
568        project: &Project,
569    ) -> Result<Option<ContextServerConfiguration>, String> {
570        let context_server_id = ContextServerId(context_server_id);
571        extension().context_server_configuration(&context_server_id, project)
572    }
573
574    fn suggest_docs_packages(provider: String) -> Result<Vec<String>, String> {
575        extension().suggest_docs_packages(provider)
576    }
577
578    fn index_docs(
579        provider: String,
580        package: String,
581        database: &KeyValueStore,
582    ) -> Result<(), String> {
583        extension().index_docs(provider, package, database)
584    }
585
586    fn get_dap_binary(
587        adapter_name: String,
588        config: DebugTaskDefinition,
589        user_installed_path: Option<String>,
590        worktree: &Worktree,
591    ) -> Result<wit::DebugAdapterBinary, String> {
592        extension().get_dap_binary(adapter_name, config, user_installed_path, worktree)
593    }
594
595    fn dap_request_kind(
596        adapter_name: String,
597        config: String,
598    ) -> Result<StartDebuggingRequestArgumentsRequest, String> {
599        extension().dap_request_kind(
600            adapter_name,
601            serde_json::from_str(&config).map_err(|e| format!("Failed to parse config: {e}"))?,
602        )
603    }
604    fn dap_config_to_scenario(config: DebugConfig) -> Result<DebugScenario, String> {
605        extension().dap_config_to_scenario(config)
606    }
607    fn dap_locator_create_scenario(
608        locator_name: String,
609        build_task: TaskTemplate,
610        resolved_label: String,
611        debug_adapter_name: String,
612    ) -> Option<DebugScenario> {
613        extension().dap_locator_create_scenario(
614            locator_name,
615            build_task,
616            resolved_label,
617            debug_adapter_name,
618        )
619    }
620    fn run_dap_locator(
621        locator_name: String,
622        build_task: TaskTemplate,
623    ) -> Result<DebugRequest, String> {
624        extension().run_dap_locator(locator_name, build_task)
625    }
626
627    fn llm_providers() -> Vec<LlmProviderInfo> {
628        extension().llm_providers()
629    }
630
631    fn llm_provider_models(provider_id: String) -> Result<Vec<LlmModelInfo>, String> {
632        extension().llm_provider_models(&provider_id)
633    }
634
635    fn llm_provider_settings_markdown(provider_id: String) -> Option<String> {
636        extension().llm_provider_settings_markdown(&provider_id)
637    }
638
639    fn llm_provider_is_authenticated(provider_id: String) -> bool {
640        extension().llm_provider_is_authenticated(&provider_id)
641    }
642
643    fn llm_provider_start_device_flow_sign_in(
644        provider_id: String,
645    ) -> Result<LlmDeviceFlowPromptInfo, String> {
646        extension().llm_provider_start_device_flow_sign_in(&provider_id)
647    }
648
649    fn llm_provider_poll_device_flow_sign_in(provider_id: String) -> Result<(), String> {
650        extension().llm_provider_poll_device_flow_sign_in(&provider_id)
651    }
652
653    fn llm_provider_reset_credentials(provider_id: String) -> Result<(), String> {
654        extension().llm_provider_reset_credentials(&provider_id)
655    }
656
657    fn llm_count_tokens(
658        provider_id: String,
659        model_id: String,
660        request: LlmCompletionRequest,
661    ) -> Result<u64, String> {
662        extension().llm_count_tokens(&provider_id, &model_id, &request)
663    }
664
665    fn llm_stream_completion_start(
666        provider_id: String,
667        model_id: String,
668        request: LlmCompletionRequest,
669    ) -> Result<String, String> {
670        extension().llm_stream_completion_start(&provider_id, &model_id, &request)
671    }
672
673    fn llm_stream_completion_next(stream_id: String) -> Result<Option<LlmCompletionEvent>, String> {
674        extension().llm_stream_completion_next(&stream_id)
675    }
676
677    fn llm_stream_completion_close(stream_id: String) {
678        extension().llm_stream_completion_close(&stream_id)
679    }
680
681    fn llm_cache_configuration(
682        provider_id: String,
683        model_id: String,
684    ) -> Option<LlmCacheConfiguration> {
685        extension().llm_cache_configuration(&provider_id, &model_id)
686    }
687}
688
689/// The ID of a language server.
690#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
691pub struct LanguageServerId(String);
692
693impl AsRef<str> for LanguageServerId {
694    fn as_ref(&self) -> &str {
695        &self.0
696    }
697}
698
699impl fmt::Display for LanguageServerId {
700    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
701        write!(f, "{}", self.0)
702    }
703}
704
705/// The ID of a context server.
706#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
707pub struct ContextServerId(String);
708
709impl AsRef<str> for ContextServerId {
710    fn as_ref(&self) -> &str {
711        &self.0
712    }
713}
714
715impl fmt::Display for ContextServerId {
716    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
717        write!(f, "{}", self.0)
718    }
719}
720
721impl CodeLabelSpan {
722    /// Returns a [`CodeLabelSpan::CodeRange`].
723    pub fn code_range(range: impl Into<wit::Range>) -> Self {
724        Self::CodeRange(range.into())
725    }
726
727    /// Returns a [`CodeLabelSpan::Literal`].
728    pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
729        Self::Literal(CodeLabelSpanLiteral {
730            text: text.into(),
731            highlight_name,
732        })
733    }
734}
735
736impl From<std::ops::Range<u32>> for wit::Range {
737    fn from(value: std::ops::Range<u32>) -> Self {
738        Self {
739            start: value.start,
740            end: value.end,
741        }
742    }
743}
744
745impl From<std::ops::Range<usize>> for wit::Range {
746    fn from(value: std::ops::Range<usize>) -> Self {
747        Self {
748            start: value.start as u32,
749            end: value.end as u32,
750        }
751    }
752}