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