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