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    /// Returns markdown content to display in the provider's settings UI.
292    /// This can include setup instructions, links to documentation, etc.
293    fn llm_provider_settings_markdown(&self, _provider_id: &str) -> Option<String> {
294        None
295    }
296
297    /// Check if the provider is authenticated.
298    fn llm_provider_is_authenticated(&self, _provider_id: &str) -> bool {
299        false
300    }
301
302    /// Attempt to authenticate the provider.
303    fn llm_provider_authenticate(&mut self, _provider_id: &str) -> Result<(), String> {
304        Err("`llm_provider_authenticate` not implemented".to_string())
305    }
306
307    /// Reset credentials for the provider.
308    fn llm_provider_reset_credentials(&mut self, _provider_id: &str) -> Result<(), String> {
309        Err("`llm_provider_reset_credentials` not implemented".to_string())
310    }
311
312    /// Count tokens for a request.
313    fn llm_count_tokens(
314        &self,
315        _provider_id: &str,
316        _model_id: &str,
317        _request: &LlmCompletionRequest,
318    ) -> Result<u64, String> {
319        Err("`llm_count_tokens` not implemented".to_string())
320    }
321
322    /// Start streaming a completion from the model.
323    /// Returns a stream ID that can be used with `llm_stream_completion_next` and `llm_stream_completion_close`.
324    fn llm_stream_completion_start(
325        &mut self,
326        _provider_id: &str,
327        _model_id: &str,
328        _request: &LlmCompletionRequest,
329    ) -> Result<String, String> {
330        Err("`llm_stream_completion_start` not implemented".to_string())
331    }
332
333    /// Get the next event from a completion stream.
334    /// Returns `Ok(None)` when the stream is complete.
335    fn llm_stream_completion_next(
336        &mut self,
337        _stream_id: &str,
338    ) -> Result<Option<LlmCompletionEvent>, String> {
339        Err("`llm_stream_completion_next` not implemented".to_string())
340    }
341
342    /// Close a completion stream and release its resources.
343    fn llm_stream_completion_close(&mut self, _stream_id: &str) {
344        // Default implementation does nothing
345    }
346
347    /// Get cache configuration for a model (if prompt caching is supported).
348    fn llm_cache_configuration(
349        &self,
350        _provider_id: &str,
351        _model_id: &str,
352    ) -> Option<LlmCacheConfiguration> {
353        None
354    }
355}
356
357/// Registers the provided type as a Zed extension.
358///
359/// The type must implement the [`Extension`] trait.
360#[macro_export]
361macro_rules! register_extension {
362    ($extension_type:ty) => {
363        #[cfg(target_os = "wasi")]
364        mod wasi_ext {
365            unsafe extern "C" {
366                static mut errno: i32;
367                pub static mut __wasilibc_cwd: *mut std::ffi::c_char;
368            }
369
370            pub fn init_cwd() {
371                unsafe {
372                    // Ensure that our chdir function is linked, instead of the
373                    // one from wasi-libc in the chdir.o translation unit. Otherwise
374                    // we risk linking in `__wasilibc_find_relpath_alloc` which
375                    // is a weak symbol and is being used by
376                    // `__wasilibc_find_relpath`, which we do not want on
377                    // Windows.
378                    chdir(std::ptr::null());
379
380                    __wasilibc_cwd = std::ffi::CString::new(std::env::var("PWD").unwrap())
381                        .unwrap()
382                        .into_raw()
383                        .cast();
384                }
385            }
386
387            #[unsafe(no_mangle)]
388            pub unsafe extern "C" fn chdir(raw_path: *const std::ffi::c_char) -> i32 {
389                // Forbid extensions from changing CWD and so return an appropriate error code.
390                errno = 58; // NOTSUP
391                return -1;
392            }
393        }
394
395        #[unsafe(export_name = "init-extension")]
396        pub extern "C" fn __init_extension() {
397            #[cfg(target_os = "wasi")]
398            wasi_ext::init_cwd();
399
400            zed_extension_api::register_extension(|| {
401                Box::new(<$extension_type as zed_extension_api::Extension>::new())
402            });
403        }
404    };
405}
406
407#[doc(hidden)]
408pub fn register_extension(build_extension: fn() -> Box<dyn Extension>) {
409    unsafe { EXTENSION = Some((build_extension)()) }
410}
411
412fn extension() -> &'static mut dyn Extension {
413    #[expect(static_mut_refs)]
414    unsafe {
415        EXTENSION.as_deref_mut().unwrap()
416    }
417}
418
419static mut EXTENSION: Option<Box<dyn Extension>> = None;
420
421#[cfg(target_arch = "wasm32")]
422#[unsafe(link_section = "zed:api-version")]
423#[doc(hidden)]
424pub static ZED_API_VERSION: [u8; 6] = *include_bytes!(concat!(env!("OUT_DIR"), "/version_bytes"));
425
426mod wit {
427
428    wit_bindgen::generate!({
429        skip: ["init-extension"],
430        path: "./wit/since_v0.7.0",
431    });
432}
433
434wit::export!(Component);
435
436struct Component;
437
438impl wit::Guest for Component {
439    fn language_server_command(
440        language_server_id: String,
441        worktree: &wit::Worktree,
442    ) -> Result<wit::Command> {
443        let language_server_id = LanguageServerId(language_server_id);
444        extension().language_server_command(&language_server_id, worktree)
445    }
446
447    fn language_server_initialization_options(
448        language_server_id: String,
449        worktree: &Worktree,
450    ) -> Result<Option<String>, String> {
451        let language_server_id = LanguageServerId(language_server_id);
452        Ok(extension()
453            .language_server_initialization_options(&language_server_id, worktree)?
454            .and_then(|value| serde_json::to_string(&value).ok()))
455    }
456
457    fn language_server_workspace_configuration(
458        language_server_id: String,
459        worktree: &Worktree,
460    ) -> Result<Option<String>, String> {
461        let language_server_id = LanguageServerId(language_server_id);
462        Ok(extension()
463            .language_server_workspace_configuration(&language_server_id, worktree)?
464            .and_then(|value| serde_json::to_string(&value).ok()))
465    }
466
467    fn language_server_additional_initialization_options(
468        language_server_id: String,
469        target_language_server_id: String,
470        worktree: &Worktree,
471    ) -> Result<Option<String>, String> {
472        let language_server_id = LanguageServerId(language_server_id);
473        let target_language_server_id = LanguageServerId(target_language_server_id);
474        Ok(extension()
475            .language_server_additional_initialization_options(
476                &language_server_id,
477                &target_language_server_id,
478                worktree,
479            )?
480            .and_then(|value| serde_json::to_string(&value).ok()))
481    }
482
483    fn language_server_additional_workspace_configuration(
484        language_server_id: String,
485        target_language_server_id: String,
486        worktree: &Worktree,
487    ) -> Result<Option<String>, String> {
488        let language_server_id = LanguageServerId(language_server_id);
489        let target_language_server_id = LanguageServerId(target_language_server_id);
490        Ok(extension()
491            .language_server_additional_workspace_configuration(
492                &language_server_id,
493                &target_language_server_id,
494                worktree,
495            )?
496            .and_then(|value| serde_json::to_string(&value).ok()))
497    }
498
499    fn labels_for_completions(
500        language_server_id: String,
501        completions: Vec<Completion>,
502    ) -> Result<Vec<Option<CodeLabel>>, String> {
503        let language_server_id = LanguageServerId(language_server_id);
504        let mut labels = Vec::new();
505        for (ix, completion) in completions.into_iter().enumerate() {
506            let label = extension().label_for_completion(&language_server_id, completion);
507            if let Some(label) = label {
508                labels.resize(ix + 1, None);
509                *labels.last_mut().unwrap() = Some(label);
510            }
511        }
512        Ok(labels)
513    }
514
515    fn labels_for_symbols(
516        language_server_id: String,
517        symbols: Vec<Symbol>,
518    ) -> Result<Vec<Option<CodeLabel>>, String> {
519        let language_server_id = LanguageServerId(language_server_id);
520        let mut labels = Vec::new();
521        for (ix, symbol) in symbols.into_iter().enumerate() {
522            let label = extension().label_for_symbol(&language_server_id, symbol);
523            if let Some(label) = label {
524                labels.resize(ix + 1, None);
525                *labels.last_mut().unwrap() = Some(label);
526            }
527        }
528        Ok(labels)
529    }
530
531    fn complete_slash_command_argument(
532        command: SlashCommand,
533        args: Vec<String>,
534    ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
535        extension().complete_slash_command_argument(command, args)
536    }
537
538    fn run_slash_command(
539        command: SlashCommand,
540        args: Vec<String>,
541        worktree: Option<&Worktree>,
542    ) -> Result<SlashCommandOutput, String> {
543        extension().run_slash_command(command, args, worktree)
544    }
545
546    fn context_server_command(
547        context_server_id: String,
548        project: &Project,
549    ) -> Result<wit::Command> {
550        let context_server_id = ContextServerId(context_server_id);
551        extension().context_server_command(&context_server_id, project)
552    }
553
554    fn context_server_configuration(
555        context_server_id: String,
556        project: &Project,
557    ) -> Result<Option<ContextServerConfiguration>, String> {
558        let context_server_id = ContextServerId(context_server_id);
559        extension().context_server_configuration(&context_server_id, project)
560    }
561
562    fn suggest_docs_packages(provider: String) -> Result<Vec<String>, String> {
563        extension().suggest_docs_packages(provider)
564    }
565
566    fn index_docs(
567        provider: String,
568        package: String,
569        database: &KeyValueStore,
570    ) -> Result<(), String> {
571        extension().index_docs(provider, package, database)
572    }
573
574    fn get_dap_binary(
575        adapter_name: String,
576        config: DebugTaskDefinition,
577        user_installed_path: Option<String>,
578        worktree: &Worktree,
579    ) -> Result<wit::DebugAdapterBinary, String> {
580        extension().get_dap_binary(adapter_name, config, user_installed_path, worktree)
581    }
582
583    fn dap_request_kind(
584        adapter_name: String,
585        config: String,
586    ) -> Result<StartDebuggingRequestArgumentsRequest, String> {
587        extension().dap_request_kind(
588            adapter_name,
589            serde_json::from_str(&config).map_err(|e| format!("Failed to parse config: {e}"))?,
590        )
591    }
592    fn dap_config_to_scenario(config: DebugConfig) -> Result<DebugScenario, String> {
593        extension().dap_config_to_scenario(config)
594    }
595    fn dap_locator_create_scenario(
596        locator_name: String,
597        build_task: TaskTemplate,
598        resolved_label: String,
599        debug_adapter_name: String,
600    ) -> Option<DebugScenario> {
601        extension().dap_locator_create_scenario(
602            locator_name,
603            build_task,
604            resolved_label,
605            debug_adapter_name,
606        )
607    }
608    fn run_dap_locator(
609        locator_name: String,
610        build_task: TaskTemplate,
611    ) -> Result<DebugRequest, String> {
612        extension().run_dap_locator(locator_name, build_task)
613    }
614
615    // =========================================================================
616    // Language Model Provider Methods
617    // =========================================================================
618
619    fn llm_providers() -> Vec<LlmProviderInfo> {
620        extension().llm_providers()
621    }
622
623    fn llm_provider_models(provider_id: String) -> Result<Vec<LlmModelInfo>, String> {
624        extension().llm_provider_models(&provider_id)
625    }
626
627    fn llm_provider_settings_markdown(provider_id: String) -> Option<String> {
628        extension().llm_provider_settings_markdown(&provider_id)
629    }
630
631    fn llm_provider_is_authenticated(provider_id: String) -> bool {
632        extension().llm_provider_is_authenticated(&provider_id)
633    }
634
635    fn llm_provider_authenticate(provider_id: String) -> Result<(), String> {
636        extension().llm_provider_authenticate(&provider_id)
637    }
638
639    fn llm_provider_reset_credentials(provider_id: String) -> Result<(), String> {
640        extension().llm_provider_reset_credentials(&provider_id)
641    }
642
643    fn llm_count_tokens(
644        provider_id: String,
645        model_id: String,
646        request: LlmCompletionRequest,
647    ) -> Result<u64, String> {
648        extension().llm_count_tokens(&provider_id, &model_id, &request)
649    }
650
651    fn llm_stream_completion_start(
652        provider_id: String,
653        model_id: String,
654        request: LlmCompletionRequest,
655    ) -> Result<String, String> {
656        extension().llm_stream_completion_start(&provider_id, &model_id, &request)
657    }
658
659    fn llm_stream_completion_next(stream_id: String) -> Result<Option<LlmCompletionEvent>, String> {
660        extension().llm_stream_completion_next(&stream_id)
661    }
662
663    fn llm_stream_completion_close(stream_id: String) {
664        extension().llm_stream_completion_close(&stream_id)
665    }
666
667    fn llm_cache_configuration(
668        provider_id: String,
669        model_id: String,
670    ) -> Option<LlmCacheConfiguration> {
671        extension().llm_cache_configuration(&provider_id, &model_id)
672    }
673}
674
675/// The ID of a language server.
676#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
677pub struct LanguageServerId(String);
678
679impl AsRef<str> for LanguageServerId {
680    fn as_ref(&self) -> &str {
681        &self.0
682    }
683}
684
685impl fmt::Display for LanguageServerId {
686    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
687        write!(f, "{}", self.0)
688    }
689}
690
691/// The ID of a context server.
692#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
693pub struct ContextServerId(String);
694
695impl AsRef<str> for ContextServerId {
696    fn as_ref(&self) -> &str {
697        &self.0
698    }
699}
700
701impl fmt::Display for ContextServerId {
702    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
703        write!(f, "{}", self.0)
704    }
705}
706
707impl CodeLabelSpan {
708    /// Returns a [`CodeLabelSpan::CodeRange`].
709    pub fn code_range(range: impl Into<wit::Range>) -> Self {
710        Self::CodeRange(range.into())
711    }
712
713    /// Returns a [`CodeLabelSpan::Literal`].
714    pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
715        Self::Literal(CodeLabelSpanLiteral {
716            text: text.into(),
717            highlight_name,
718        })
719    }
720}
721
722impl From<std::ops::Range<u32>> for wit::Range {
723    fn from(value: std::ops::Range<u32>) -> Self {
724        Self {
725            start: value.start,
726            end: value.end,
727        }
728    }
729}
730
731impl From<std::ops::Range<usize>> for wit::Range {
732    fn from(value: std::ops::Range<usize>) -> Self {
733        Self {
734            start: value.start as u32,
735            end: value.end as u32,
736        }
737    }
738}