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