extension_api.rs

  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    wit_bindgen::generate!({
442        skip: ["init-extension"],
443        path: "./wit/since_v0.8.0",
444    });
445}
446
447wit::export!(Component);
448
449struct Component;
450
451impl wit::Guest for Component {
452    fn language_server_command(
453        language_server_id: String,
454        worktree: &wit::Worktree,
455    ) -> Result<wit::Command> {
456        let language_server_id = LanguageServerId(language_server_id);
457        extension().language_server_command(&language_server_id, worktree)
458    }
459
460    fn language_server_initialization_options(
461        language_server_id: String,
462        worktree: &Worktree,
463    ) -> Result<Option<String>, String> {
464        let language_server_id = LanguageServerId(language_server_id);
465        Ok(extension()
466            .language_server_initialization_options(&language_server_id, worktree)?
467            .and_then(|value| serde_json::to_string(&value).ok()))
468    }
469
470    fn language_server_workspace_configuration(
471        language_server_id: String,
472        worktree: &Worktree,
473    ) -> Result<Option<String>, String> {
474        let language_server_id = LanguageServerId(language_server_id);
475        Ok(extension()
476            .language_server_workspace_configuration(&language_server_id, worktree)?
477            .and_then(|value| serde_json::to_string(&value).ok()))
478    }
479
480    fn language_server_additional_initialization_options(
481        language_server_id: String,
482        target_language_server_id: String,
483        worktree: &Worktree,
484    ) -> Result<Option<String>, String> {
485        let language_server_id = LanguageServerId(language_server_id);
486        let target_language_server_id = LanguageServerId(target_language_server_id);
487        Ok(extension()
488            .language_server_additional_initialization_options(
489                &language_server_id,
490                &target_language_server_id,
491                worktree,
492            )?
493            .and_then(|value| serde_json::to_string(&value).ok()))
494    }
495
496    fn language_server_additional_workspace_configuration(
497        language_server_id: String,
498        target_language_server_id: String,
499        worktree: &Worktree,
500    ) -> Result<Option<String>, String> {
501        let language_server_id = LanguageServerId(language_server_id);
502        let target_language_server_id = LanguageServerId(target_language_server_id);
503        Ok(extension()
504            .language_server_additional_workspace_configuration(
505                &language_server_id,
506                &target_language_server_id,
507                worktree,
508            )?
509            .and_then(|value| serde_json::to_string(&value).ok()))
510    }
511
512    fn labels_for_completions(
513        language_server_id: String,
514        completions: Vec<Completion>,
515    ) -> Result<Vec<Option<CodeLabel>>, String> {
516        let language_server_id = LanguageServerId(language_server_id);
517        let mut labels = Vec::new();
518        for (ix, completion) in completions.into_iter().enumerate() {
519            let label = extension().label_for_completion(&language_server_id, completion);
520            if let Some(label) = label {
521                labels.resize(ix + 1, None);
522                *labels.last_mut().unwrap() = Some(label);
523            }
524        }
525        Ok(labels)
526    }
527
528    fn labels_for_symbols(
529        language_server_id: String,
530        symbols: Vec<Symbol>,
531    ) -> Result<Vec<Option<CodeLabel>>, String> {
532        let language_server_id = LanguageServerId(language_server_id);
533        let mut labels = Vec::new();
534        for (ix, symbol) in symbols.into_iter().enumerate() {
535            let label = extension().label_for_symbol(&language_server_id, symbol);
536            if let Some(label) = label {
537                labels.resize(ix + 1, None);
538                *labels.last_mut().unwrap() = Some(label);
539            }
540        }
541        Ok(labels)
542    }
543
544    fn complete_slash_command_argument(
545        command: SlashCommand,
546        args: Vec<String>,
547    ) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
548        extension().complete_slash_command_argument(command, args)
549    }
550
551    fn run_slash_command(
552        command: SlashCommand,
553        args: Vec<String>,
554        worktree: Option<&Worktree>,
555    ) -> Result<SlashCommandOutput, String> {
556        extension().run_slash_command(command, args, worktree)
557    }
558
559    fn context_server_command(
560        context_server_id: String,
561        project: &Project,
562    ) -> Result<wit::Command> {
563        let context_server_id = ContextServerId(context_server_id);
564        extension().context_server_command(&context_server_id, project)
565    }
566
567    fn context_server_configuration(
568        context_server_id: String,
569        project: &Project,
570    ) -> Result<Option<ContextServerConfiguration>, String> {
571        let context_server_id = ContextServerId(context_server_id);
572        extension().context_server_configuration(&context_server_id, project)
573    }
574
575    fn suggest_docs_packages(provider: String) -> Result<Vec<String>, String> {
576        extension().suggest_docs_packages(provider)
577    }
578
579    fn index_docs(
580        provider: String,
581        package: String,
582        database: &KeyValueStore,
583    ) -> Result<(), String> {
584        extension().index_docs(provider, package, database)
585    }
586
587    fn get_dap_binary(
588        adapter_name: String,
589        config: DebugTaskDefinition,
590        user_installed_path: Option<String>,
591        worktree: &Worktree,
592    ) -> Result<wit::DebugAdapterBinary, String> {
593        extension().get_dap_binary(adapter_name, config, user_installed_path, worktree)
594    }
595
596    fn dap_request_kind(
597        adapter_name: String,
598        config: String,
599    ) -> Result<StartDebuggingRequestArgumentsRequest, String> {
600        extension().dap_request_kind(
601            adapter_name,
602            serde_json::from_str(&config).map_err(|e| format!("Failed to parse config: {e}"))?,
603        )
604    }
605    fn dap_config_to_scenario(config: DebugConfig) -> Result<DebugScenario, String> {
606        extension().dap_config_to_scenario(config)
607    }
608    fn dap_locator_create_scenario(
609        locator_name: String,
610        build_task: TaskTemplate,
611        resolved_label: String,
612        debug_adapter_name: String,
613    ) -> Option<DebugScenario> {
614        extension().dap_locator_create_scenario(
615            locator_name,
616            build_task,
617            resolved_label,
618            debug_adapter_name,
619        )
620    }
621    fn run_dap_locator(
622        locator_name: String,
623        build_task: TaskTemplate,
624    ) -> Result<DebugRequest, String> {
625        extension().run_dap_locator(locator_name, build_task)
626    }
627
628    fn llm_providers() -> Vec<LlmProviderInfo> {
629        extension().llm_providers()
630    }
631
632    fn llm_provider_models(provider_id: String) -> Result<Vec<LlmModelInfo>, String> {
633        extension().llm_provider_models(&provider_id)
634    }
635
636    fn llm_provider_settings_markdown(provider_id: String) -> Option<String> {
637        extension().llm_provider_settings_markdown(&provider_id)
638    }
639
640    fn llm_provider_is_authenticated(provider_id: String) -> bool {
641        extension().llm_provider_is_authenticated(&provider_id)
642    }
643
644    fn llm_provider_start_device_flow_sign_in(
645        provider_id: String,
646    ) -> Result<LlmDeviceFlowPromptInfo, String> {
647        extension().llm_provider_start_device_flow_sign_in(&provider_id)
648    }
649
650    fn llm_provider_poll_device_flow_sign_in(provider_id: String) -> Result<(), String> {
651        extension().llm_provider_poll_device_flow_sign_in(&provider_id)
652    }
653
654    fn llm_provider_reset_credentials(provider_id: String) -> Result<(), String> {
655        extension().llm_provider_reset_credentials(&provider_id)
656    }
657
658    fn llm_count_tokens(
659        provider_id: String,
660        model_id: String,
661        request: LlmCompletionRequest,
662    ) -> Result<u64, String> {
663        extension().llm_count_tokens(&provider_id, &model_id, &request)
664    }
665
666    fn llm_stream_completion_start(
667        provider_id: String,
668        model_id: String,
669        request: LlmCompletionRequest,
670    ) -> Result<String, String> {
671        extension().llm_stream_completion_start(&provider_id, &model_id, &request)
672    }
673
674    fn llm_stream_completion_next(stream_id: String) -> Result<Option<LlmCompletionEvent>, String> {
675        extension().llm_stream_completion_next(&stream_id)
676    }
677
678    fn llm_stream_completion_close(stream_id: String) {
679        extension().llm_stream_completion_close(&stream_id)
680    }
681
682    fn llm_cache_configuration(
683        provider_id: String,
684        model_id: String,
685    ) -> Option<LlmCacheConfiguration> {
686        extension().llm_cache_configuration(&provider_id, &model_id)
687    }
688}
689
690/// The ID of a language server.
691#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
692pub struct LanguageServerId(String);
693
694impl LanguageServerId {
695    pub fn new(value: String) -> Self {
696        Self(value)
697    }
698}
699
700impl AsRef<str> for LanguageServerId {
701    fn as_ref(&self) -> &str {
702        &self.0
703    }
704}
705
706impl fmt::Display for LanguageServerId {
707    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
708        write!(f, "{}", self.0)
709    }
710}
711
712/// The ID of a context server.
713#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone)]
714pub struct ContextServerId(String);
715
716impl ContextServerId {
717    pub fn new(value: String) -> Self {
718        Self(value)
719    }
720}
721
722impl AsRef<str> for ContextServerId {
723    fn as_ref(&self) -> &str {
724        &self.0
725    }
726}
727
728impl fmt::Display for ContextServerId {
729    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
730        write!(f, "{}", self.0)
731    }
732}
733
734impl CodeLabelSpan {
735    /// Returns a [`CodeLabelSpan::CodeRange`].
736    pub fn code_range(range: impl Into<wit::Range>) -> Self {
737        Self::CodeRange(range.into())
738    }
739
740    /// Returns a [`CodeLabelSpan::Literal`].
741    pub fn literal(text: impl Into<String>, highlight_name: Option<String>) -> Self {
742        Self::Literal(CodeLabelSpanLiteral {
743            text: text.into(),
744            highlight_name,
745        })
746    }
747}
748
749impl From<std::ops::Range<u32>> for wit::Range {
750    fn from(value: std::ops::Range<u32>) -> Self {
751        Self {
752            start: value.start,
753            end: value.end,
754        }
755    }
756}
757
758impl From<std::ops::Range<usize>> for wit::Range {
759    fn from(value: std::ops::Range<usize>) -> Self {
760        Self {
761            start: value.start as u32,
762            end: value.end as u32,
763        }
764    }
765}