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