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}