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