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