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