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}