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    /// Returns information about language model providers offered by this extension.
278    fn llm_providers(&self) -> Vec<LlmProviderInfo> {
279        Vec::new()
280    }
281
282    /// Returns the models available for a provider.
283    fn llm_provider_models(&self, _provider_id: &str) -> Result<Vec<LlmModelInfo>, String> {
284        Ok(Vec::new())
285    }
286
287    /// Returns markdown content to display in the provider's settings UI.
288    /// This can include setup instructions, links to documentation, etc.
289    fn llm_provider_settings_markdown(&self, _provider_id: &str) -> Option<String> {
290        None
291    }
292
293    /// Check if the provider is authenticated.
294    fn llm_provider_is_authenticated(&self, _provider_id: &str) -> bool {
295        false
296    }
297
298    /// Attempt to authenticate the provider.
299    fn llm_provider_authenticate(&mut self, _provider_id: &str) -> Result<(), String> {
300        Err("`llm_provider_authenticate` not implemented".to_string())
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_authenticate(provider_id: String) -> Result<(), String> {
628        extension().llm_provider_authenticate(&provider_id)
629    }
630
631    fn llm_provider_reset_credentials(provider_id: String) -> Result<(), String> {
632        extension().llm_provider_reset_credentials(&provider_id)
633    }
634
635    fn llm_count_tokens(
636        provider_id: String,
637        model_id: String,
638        request: LlmCompletionRequest,
639    ) -> Result<u64, String> {
640        extension().llm_count_tokens(&provider_id, &model_id, &request)
641    }
642
643    fn llm_stream_completion_start(
644        provider_id: String,
645        model_id: String,
646        request: LlmCompletionRequest,
647    ) -> Result<String, String> {
648        extension().llm_stream_completion_start(&provider_id, &model_id, &request)
649    }
650
651    fn llm_stream_completion_next(stream_id: String) -> Result<Option<LlmCompletionEvent>, String> {
652        extension().llm_stream_completion_next(&stream_id)
653    }
654
655    fn llm_stream_completion_close(stream_id: String) {
656        extension().llm_stream_completion_close(&stream_id)
657    }
658
659    fn llm_cache_configuration(
660        provider_id: String,
661        model_id: String,
662    ) -> Option<LlmCacheConfiguration> {
663        extension().llm_cache_configuration(&provider_id, &model_id)
664    }
665}
666
667/// The ID of a language server.
668#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
669pub struct LanguageServerId(String);
670
671impl AsRef<str> for LanguageServerId {
672    fn as_ref(&self) -> &str {
673        &self.0
674    }
675}
676
677impl fmt::Display for LanguageServerId {
678    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
679        write!(f, "{}", self.0)
680    }
681}
682
683/// The ID of a context server.
684#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
685pub struct ContextServerId(String);
686
687impl AsRef<str> for ContextServerId {
688    fn as_ref(&self) -> &str {
689        &self.0
690    }
691}
692
693impl fmt::Display for ContextServerId {
694    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
695        write!(f, "{}", self.0)
696    }
697}
698
699impl CodeLabelSpan {
700    /// Returns a [`CodeLabelSpan::CodeRange`].
701    pub fn code_range(range: impl Into<wit::Range>) -> Self {
702        Self::CodeRange(range.into())
703    }
704
705    /// Returns a [`CodeLabelSpan::Literal`].
706    pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
707        Self::Literal(CodeLabelSpanLiteral {
708            text: text.into(),
709            highlight_name,
710        })
711    }
712}
713
714impl From<std::ops::Range<u32>> for wit::Range {
715    fn from(value: std::ops::Range<u32>) -> Self {
716        Self {
717            start: value.start,
718            end: value.end,
719        }
720    }
721}
722
723impl From<std::ops::Range<usize>> for wit::Range {
724    fn from(value: std::ops::Range<usize>) -> Self {
725        Self {
726            start: value.start as u32,
727            end: value.end as u32,
728        }
729    }
730}