since_v0_8_0.rs

   1use crate::wasm_host::wit::since_v0_6_0::{
   2    dap::{
   3        BuildTaskDefinition, BuildTaskDefinitionTemplatePayload, StartDebuggingRequestArguments,
   4        TcpArguments, TcpArgumentsTemplate,
   5    },
   6    slash_command::SlashCommandOutputSection,
   7};
   8use crate::wasm_host::wit::{CompletionKind, CompletionLabelDetails, InsertTextFormat, SymbolKind};
   9use crate::wasm_host::{WasmState, wit::ToWasmtimeResult};
  10use ::http_client::{AsyncBody, HttpRequestExt};
  11use ::settings::{Settings, WorktreeId};
  12use anyhow::{Context as _, Result, bail};
  13use async_compression::futures::bufread::GzipDecoder;
  14use async_tar::Archive;
  15use async_trait::async_trait;
  16use extension::{
  17    ExtensionLanguageServerProxy, KeyValueStoreDelegate, ProjectDelegate, WorktreeDelegate,
  18};
  19use futures::{AsyncReadExt, lock::Mutex};
  20use futures::{FutureExt as _, io::BufReader};
  21use gpui::{BackgroundExecutor, SharedString};
  22use language::{BinaryStatus, LanguageName, language_settings::AllLanguageSettings};
  23use project::project_settings::ProjectSettings;
  24use semver::Version;
  25use std::{
  26    env,
  27    net::Ipv4Addr,
  28    path::{Path, PathBuf},
  29    str::FromStr,
  30    sync::{Arc, OnceLock},
  31};
  32use task::{SpawnInTerminal, ZedDebugConfig};
  33use url::Url;
  34use util::{
  35    archive::extract_zip, fs::make_file_executable, maybe, paths::PathStyle, rel_path::RelPath,
  36};
  37use wasmtime::component::{Linker, Resource};
  38
  39pub const MIN_VERSION: Version = Version::new(0, 8, 0);
  40pub const MAX_VERSION: Version = Version::new(0, 8, 0);
  41
  42wasmtime::component::bindgen!({
  43    imports: {
  44        default: async | trappable,
  45    },
  46    exports: {
  47        default: async,
  48    },
  49    path: "../extension_api/wit/since_v0.8.0",
  50    with: {
  51         "worktree": ExtensionWorktree,
  52         "project": ExtensionProject,
  53         "key-value-store": ExtensionKeyValueStore,
  54         "zed:extension/http-client/http-response-stream": ExtensionHttpResponseStream
  55    },
  56});
  57
  58pub use self::zed::extension::*;
  59
  60mod settings {
  61    #![allow(dead_code)]
  62    include!(concat!(env!("OUT_DIR"), "/since_v0.8.0/settings.rs"));
  63}
  64
  65pub type ExtensionWorktree = Arc<dyn WorktreeDelegate>;
  66pub type ExtensionProject = Arc<dyn ProjectDelegate>;
  67pub type ExtensionKeyValueStore = Arc<dyn KeyValueStoreDelegate>;
  68pub type ExtensionHttpResponseStream = Arc<Mutex<::http_client::Response<AsyncBody>>>;
  69
  70pub fn linker(executor: &BackgroundExecutor) -> &'static Linker<WasmState> {
  71    static LINKER: OnceLock<Linker<WasmState>> = OnceLock::new();
  72    LINKER.get_or_init(|| {
  73        super::new_linker(executor, |linker| {
  74            Extension::add_to_linker::<_, WasmState>(linker, |s| s)
  75        })
  76    })
  77}
  78
  79impl From<Range> for std::ops::Range<usize> {
  80    fn from(range: Range) -> Self {
  81        let start = range.start as usize;
  82        let end = range.end as usize;
  83        start..end
  84    }
  85}
  86
  87impl From<Command> for extension::Command {
  88    fn from(value: Command) -> Self {
  89        Self {
  90            command: value.command.into(),
  91            args: value.args,
  92            env: value.env,
  93        }
  94    }
  95}
  96
  97impl From<StartDebuggingRequestArgumentsRequest>
  98    for extension::StartDebuggingRequestArgumentsRequest
  99{
 100    fn from(value: StartDebuggingRequestArgumentsRequest) -> Self {
 101        match value {
 102            StartDebuggingRequestArgumentsRequest::Launch => Self::Launch,
 103            StartDebuggingRequestArgumentsRequest::Attach => Self::Attach,
 104        }
 105    }
 106}
 107impl TryFrom<StartDebuggingRequestArguments> for extension::StartDebuggingRequestArguments {
 108    type Error = anyhow::Error;
 109
 110    fn try_from(value: StartDebuggingRequestArguments) -> Result<Self, Self::Error> {
 111        Ok(Self {
 112            configuration: serde_json::from_str(&value.configuration)?,
 113            request: value.request.into(),
 114        })
 115    }
 116}
 117impl From<TcpArguments> for extension::TcpArguments {
 118    fn from(value: TcpArguments) -> Self {
 119        Self {
 120            host: value.host.into(),
 121            port: value.port,
 122            timeout: value.timeout,
 123        }
 124    }
 125}
 126
 127impl From<extension::TcpArgumentsTemplate> for TcpArgumentsTemplate {
 128    fn from(value: extension::TcpArgumentsTemplate) -> Self {
 129        Self {
 130            host: value.host.map(Ipv4Addr::to_bits),
 131            port: value.port,
 132            timeout: value.timeout,
 133        }
 134    }
 135}
 136
 137impl From<TcpArgumentsTemplate> for extension::TcpArgumentsTemplate {
 138    fn from(value: TcpArgumentsTemplate) -> Self {
 139        Self {
 140            host: value.host.map(Ipv4Addr::from_bits),
 141            port: value.port,
 142            timeout: value.timeout,
 143        }
 144    }
 145}
 146
 147impl TryFrom<extension::DebugTaskDefinition> for DebugTaskDefinition {
 148    type Error = anyhow::Error;
 149    fn try_from(value: extension::DebugTaskDefinition) -> Result<Self, Self::Error> {
 150        Ok(Self {
 151            label: value.label.to_string(),
 152            adapter: value.adapter.to_string(),
 153            config: value.config.to_string(),
 154            tcp_connection: value.tcp_connection.map(Into::into),
 155        })
 156    }
 157}
 158
 159impl From<task::DebugRequest> for DebugRequest {
 160    fn from(value: task::DebugRequest) -> Self {
 161        match value {
 162            task::DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()),
 163            task::DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()),
 164        }
 165    }
 166}
 167
 168impl From<DebugRequest> for task::DebugRequest {
 169    fn from(value: DebugRequest) -> Self {
 170        match value {
 171            DebugRequest::Launch(launch_request) => Self::Launch(launch_request.into()),
 172            DebugRequest::Attach(attach_request) => Self::Attach(attach_request.into()),
 173        }
 174    }
 175}
 176
 177impl From<task::LaunchRequest> for LaunchRequest {
 178    fn from(value: task::LaunchRequest) -> Self {
 179        Self {
 180            program: value.program,
 181            cwd: value.cwd.map(|p| p.to_string_lossy().into_owned()),
 182            args: value.args,
 183            envs: value.env.into_iter().collect(),
 184        }
 185    }
 186}
 187
 188impl From<task::AttachRequest> for AttachRequest {
 189    fn from(value: task::AttachRequest) -> Self {
 190        Self {
 191            process_id: value.process_id,
 192        }
 193    }
 194}
 195
 196impl From<LaunchRequest> for task::LaunchRequest {
 197    fn from(value: LaunchRequest) -> Self {
 198        Self {
 199            program: value.program,
 200            cwd: value.cwd.map(|p| p.into()),
 201            args: value.args,
 202            env: value.envs.into_iter().collect(),
 203        }
 204    }
 205}
 206impl From<AttachRequest> for task::AttachRequest {
 207    fn from(value: AttachRequest) -> Self {
 208        Self {
 209            process_id: value.process_id,
 210        }
 211    }
 212}
 213
 214impl From<ZedDebugConfig> for DebugConfig {
 215    fn from(value: ZedDebugConfig) -> Self {
 216        Self {
 217            label: value.label.into(),
 218            adapter: value.adapter.into(),
 219            request: value.request.into(),
 220            stop_on_entry: value.stop_on_entry,
 221        }
 222    }
 223}
 224impl TryFrom<DebugAdapterBinary> for extension::DebugAdapterBinary {
 225    type Error = anyhow::Error;
 226    fn try_from(value: DebugAdapterBinary) -> Result<Self, Self::Error> {
 227        Ok(Self {
 228            command: value.command,
 229            arguments: value.arguments,
 230            envs: value.envs.into_iter().collect(),
 231            cwd: value.cwd.map(|s| s.into()),
 232            connection: value.connection.map(Into::into),
 233            request_args: value.request_args.try_into()?,
 234        })
 235    }
 236}
 237
 238impl From<BuildTaskDefinition> for extension::BuildTaskDefinition {
 239    fn from(value: BuildTaskDefinition) -> Self {
 240        match value {
 241            BuildTaskDefinition::ByName(name) => Self::ByName(name.into()),
 242            BuildTaskDefinition::Template(build_task_template) => Self::Template {
 243                task_template: build_task_template.template.into(),
 244                locator_name: build_task_template.locator_name.map(SharedString::from),
 245            },
 246        }
 247    }
 248}
 249
 250impl From<extension::BuildTaskDefinition> for BuildTaskDefinition {
 251    fn from(value: extension::BuildTaskDefinition) -> Self {
 252        match value {
 253            extension::BuildTaskDefinition::ByName(name) => Self::ByName(name.into()),
 254            extension::BuildTaskDefinition::Template {
 255                task_template,
 256                locator_name,
 257            } => Self::Template(BuildTaskDefinitionTemplatePayload {
 258                template: task_template.into(),
 259                locator_name: locator_name.map(String::from),
 260            }),
 261        }
 262    }
 263}
 264impl From<BuildTaskTemplate> for extension::BuildTaskTemplate {
 265    fn from(value: BuildTaskTemplate) -> Self {
 266        Self {
 267            label: value.label,
 268            command: value.command,
 269            args: value.args,
 270            env: value.env.into_iter().collect(),
 271            cwd: value.cwd,
 272            ..Default::default()
 273        }
 274    }
 275}
 276impl From<extension::BuildTaskTemplate> for BuildTaskTemplate {
 277    fn from(value: extension::BuildTaskTemplate) -> Self {
 278        Self {
 279            label: value.label,
 280            command: value.command,
 281            args: value.args,
 282            env: value.env.into_iter().collect(),
 283            cwd: value.cwd,
 284        }
 285    }
 286}
 287
 288impl TryFrom<DebugScenario> for extension::DebugScenario {
 289    type Error = anyhow::Error;
 290
 291    fn try_from(value: DebugScenario) -> std::result::Result<Self, Self::Error> {
 292        Ok(Self {
 293            adapter: value.adapter.into(),
 294            label: value.label.into(),
 295            build: value.build.map(Into::into),
 296            config: serde_json::Value::from_str(&value.config)?,
 297            tcp_connection: value.tcp_connection.map(Into::into),
 298        })
 299    }
 300}
 301
 302impl From<extension::DebugScenario> for DebugScenario {
 303    fn from(value: extension::DebugScenario) -> Self {
 304        Self {
 305            adapter: value.adapter.into(),
 306            label: value.label.into(),
 307            build: value.build.map(Into::into),
 308            config: value.config.to_string(),
 309            tcp_connection: value.tcp_connection.map(Into::into),
 310        }
 311    }
 312}
 313
 314impl TryFrom<SpawnInTerminal> for ResolvedTask {
 315    type Error = anyhow::Error;
 316
 317    fn try_from(value: SpawnInTerminal) -> Result<Self, Self::Error> {
 318        Ok(Self {
 319            label: value.label,
 320            command: value.command.context("missing command")?,
 321            args: value.args,
 322            env: value.env.into_iter().collect(),
 323            cwd: value.cwd.map(|s| {
 324                let s = s.to_string_lossy();
 325                if cfg!(target_os = "windows") {
 326                    s.replace('\\', "/")
 327                } else {
 328                    s.into_owned()
 329                }
 330            }),
 331        })
 332    }
 333}
 334
 335impl From<CodeLabel> for extension::CodeLabel {
 336    fn from(value: CodeLabel) -> Self {
 337        Self {
 338            code: value.code,
 339            spans: value.spans.into_iter().map(Into::into).collect(),
 340            filter_range: value.filter_range.into(),
 341        }
 342    }
 343}
 344
 345impl From<CodeLabelSpan> for extension::CodeLabelSpan {
 346    fn from(value: CodeLabelSpan) -> Self {
 347        match value {
 348            CodeLabelSpan::CodeRange(range) => Self::CodeRange(range.into()),
 349            CodeLabelSpan::Literal(literal) => Self::Literal(literal.into()),
 350        }
 351    }
 352}
 353
 354impl From<CodeLabelSpanLiteral> for extension::CodeLabelSpanLiteral {
 355    fn from(value: CodeLabelSpanLiteral) -> Self {
 356        Self {
 357            text: value.text,
 358            highlight_name: value.highlight_name,
 359        }
 360    }
 361}
 362
 363impl From<extension::Completion> for Completion {
 364    fn from(value: extension::Completion) -> Self {
 365        Self {
 366            label: value.label,
 367            label_details: value.label_details.map(Into::into),
 368            detail: value.detail,
 369            kind: value.kind.map(Into::into),
 370            insert_text_format: value.insert_text_format.map(Into::into),
 371        }
 372    }
 373}
 374
 375impl From<extension::CompletionLabelDetails> for CompletionLabelDetails {
 376    fn from(value: extension::CompletionLabelDetails) -> Self {
 377        Self {
 378            detail: value.detail,
 379            description: value.description,
 380        }
 381    }
 382}
 383
 384impl From<extension::CompletionKind> for CompletionKind {
 385    fn from(value: extension::CompletionKind) -> Self {
 386        match value {
 387            extension::CompletionKind::Text => Self::Text,
 388            extension::CompletionKind::Method => Self::Method,
 389            extension::CompletionKind::Function => Self::Function,
 390            extension::CompletionKind::Constructor => Self::Constructor,
 391            extension::CompletionKind::Field => Self::Field,
 392            extension::CompletionKind::Variable => Self::Variable,
 393            extension::CompletionKind::Class => Self::Class,
 394            extension::CompletionKind::Interface => Self::Interface,
 395            extension::CompletionKind::Module => Self::Module,
 396            extension::CompletionKind::Property => Self::Property,
 397            extension::CompletionKind::Unit => Self::Unit,
 398            extension::CompletionKind::Value => Self::Value,
 399            extension::CompletionKind::Enum => Self::Enum,
 400            extension::CompletionKind::Keyword => Self::Keyword,
 401            extension::CompletionKind::Snippet => Self::Snippet,
 402            extension::CompletionKind::Color => Self::Color,
 403            extension::CompletionKind::File => Self::File,
 404            extension::CompletionKind::Reference => Self::Reference,
 405            extension::CompletionKind::Folder => Self::Folder,
 406            extension::CompletionKind::EnumMember => Self::EnumMember,
 407            extension::CompletionKind::Constant => Self::Constant,
 408            extension::CompletionKind::Struct => Self::Struct,
 409            extension::CompletionKind::Event => Self::Event,
 410            extension::CompletionKind::Operator => Self::Operator,
 411            extension::CompletionKind::TypeParameter => Self::TypeParameter,
 412            extension::CompletionKind::Other(value) => Self::Other(value),
 413        }
 414    }
 415}
 416
 417impl From<extension::InsertTextFormat> for InsertTextFormat {
 418    fn from(value: extension::InsertTextFormat) -> Self {
 419        match value {
 420            extension::InsertTextFormat::PlainText => Self::PlainText,
 421            extension::InsertTextFormat::Snippet => Self::Snippet,
 422            extension::InsertTextFormat::Other(value) => Self::Other(value),
 423        }
 424    }
 425}
 426
 427impl From<extension::Symbol> for Symbol {
 428    fn from(value: extension::Symbol) -> Self {
 429        Self {
 430            kind: value.kind.into(),
 431            name: value.name,
 432            container_name: value.container_name,
 433        }
 434    }
 435}
 436
 437impl From<extension::SymbolKind> for SymbolKind {
 438    fn from(value: extension::SymbolKind) -> Self {
 439        match value {
 440            extension::SymbolKind::File => Self::File,
 441            extension::SymbolKind::Module => Self::Module,
 442            extension::SymbolKind::Namespace => Self::Namespace,
 443            extension::SymbolKind::Package => Self::Package,
 444            extension::SymbolKind::Class => Self::Class,
 445            extension::SymbolKind::Method => Self::Method,
 446            extension::SymbolKind::Property => Self::Property,
 447            extension::SymbolKind::Field => Self::Field,
 448            extension::SymbolKind::Constructor => Self::Constructor,
 449            extension::SymbolKind::Enum => Self::Enum,
 450            extension::SymbolKind::Interface => Self::Interface,
 451            extension::SymbolKind::Function => Self::Function,
 452            extension::SymbolKind::Variable => Self::Variable,
 453            extension::SymbolKind::Constant => Self::Constant,
 454            extension::SymbolKind::String => Self::String,
 455            extension::SymbolKind::Number => Self::Number,
 456            extension::SymbolKind::Boolean => Self::Boolean,
 457            extension::SymbolKind::Array => Self::Array,
 458            extension::SymbolKind::Object => Self::Object,
 459            extension::SymbolKind::Key => Self::Key,
 460            extension::SymbolKind::Null => Self::Null,
 461            extension::SymbolKind::EnumMember => Self::EnumMember,
 462            extension::SymbolKind::Struct => Self::Struct,
 463            extension::SymbolKind::Event => Self::Event,
 464            extension::SymbolKind::Operator => Self::Operator,
 465            extension::SymbolKind::TypeParameter => Self::TypeParameter,
 466            extension::SymbolKind::Other(value) => Self::Other(value),
 467        }
 468    }
 469}
 470
 471impl From<extension::SlashCommand> for SlashCommand {
 472    fn from(value: extension::SlashCommand) -> Self {
 473        Self {
 474            name: value.name,
 475            description: value.description,
 476            tooltip_text: value.tooltip_text,
 477            requires_argument: value.requires_argument,
 478        }
 479    }
 480}
 481
 482impl From<SlashCommandOutput> for extension::SlashCommandOutput {
 483    fn from(value: SlashCommandOutput) -> Self {
 484        Self {
 485            text: value.text,
 486            sections: value.sections.into_iter().map(Into::into).collect(),
 487        }
 488    }
 489}
 490
 491impl From<SlashCommandOutputSection> for extension::SlashCommandOutputSection {
 492    fn from(value: SlashCommandOutputSection) -> Self {
 493        Self {
 494            range: value.range.start as usize..value.range.end as usize,
 495            label: value.label,
 496        }
 497    }
 498}
 499
 500impl From<SlashCommandArgumentCompletion> for extension::SlashCommandArgumentCompletion {
 501    fn from(value: SlashCommandArgumentCompletion) -> Self {
 502        Self {
 503            label: value.label,
 504            new_text: value.new_text,
 505            run_command: value.run_command,
 506        }
 507    }
 508}
 509
 510impl TryFrom<ContextServerConfiguration> for extension::ContextServerConfiguration {
 511    type Error = anyhow::Error;
 512
 513    fn try_from(value: ContextServerConfiguration) -> Result<Self, Self::Error> {
 514        let settings_schema: serde_json::Value = serde_json::from_str(&value.settings_schema)
 515            .context("Failed to parse settings_schema")?;
 516
 517        Ok(Self {
 518            installation_instructions: value.installation_instructions,
 519            default_settings: value.default_settings,
 520            settings_schema,
 521        })
 522    }
 523}
 524
 525impl HostKeyValueStore for WasmState {
 526    async fn insert(
 527        &mut self,
 528        kv_store: Resource<ExtensionKeyValueStore>,
 529        key: String,
 530        value: String,
 531    ) -> wasmtime::Result<Result<(), String>> {
 532        let kv_store = self.table.get(&kv_store)?;
 533        kv_store.insert(key, value).await.to_wasmtime_result()
 534    }
 535
 536    async fn drop(&mut self, _worktree: Resource<ExtensionKeyValueStore>) -> Result<()> {
 537        // We only ever hand out borrows of key-value stores.
 538        Ok(())
 539    }
 540}
 541
 542impl HostProject for WasmState {
 543    async fn worktree_ids(
 544        &mut self,
 545        project: Resource<ExtensionProject>,
 546    ) -> wasmtime::Result<Vec<u64>> {
 547        let project = self.table.get(&project)?;
 548        Ok(project.worktree_ids())
 549    }
 550
 551    async fn drop(&mut self, _project: Resource<Project>) -> Result<()> {
 552        // We only ever hand out borrows of projects.
 553        Ok(())
 554    }
 555}
 556
 557impl HostWorktree for WasmState {
 558    async fn id(&mut self, delegate: Resource<Arc<dyn WorktreeDelegate>>) -> wasmtime::Result<u64> {
 559        let delegate = self.table.get(&delegate)?;
 560        Ok(delegate.id())
 561    }
 562
 563    async fn root_path(
 564        &mut self,
 565        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 566    ) -> wasmtime::Result<String> {
 567        let delegate = self.table.get(&delegate)?;
 568        Ok(delegate.root_path())
 569    }
 570
 571    async fn read_text_file(
 572        &mut self,
 573        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 574        path: String,
 575    ) -> wasmtime::Result<Result<String, String>> {
 576        let delegate = self.table.get(&delegate)?;
 577        Ok(delegate
 578            .read_text_file(&RelPath::new(Path::new(&path), PathStyle::Posix)?)
 579            .await
 580            .map_err(|error| error.to_string()))
 581    }
 582
 583    async fn shell_env(
 584        &mut self,
 585        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 586    ) -> wasmtime::Result<EnvVars> {
 587        let delegate = self.table.get(&delegate)?;
 588        Ok(delegate.shell_env().await.into_iter().collect())
 589    }
 590
 591    async fn which(
 592        &mut self,
 593        delegate: Resource<Arc<dyn WorktreeDelegate>>,
 594        binary_name: String,
 595    ) -> wasmtime::Result<Option<String>> {
 596        let delegate = self.table.get(&delegate)?;
 597        Ok(delegate.which(binary_name).await)
 598    }
 599
 600    async fn drop(&mut self, _worktree: Resource<Worktree>) -> Result<()> {
 601        // We only ever hand out borrows of worktrees.
 602        Ok(())
 603    }
 604}
 605
 606impl common::Host for WasmState {}
 607
 608impl http_client::Host for WasmState {
 609    async fn fetch(
 610        &mut self,
 611        request: http_client::HttpRequest,
 612    ) -> wasmtime::Result<Result<http_client::HttpResponse, String>> {
 613        maybe!(async {
 614            let url = &request.url;
 615            let request = convert_request(&request)?;
 616            let mut response = self.host.http_client.send(request).await?;
 617
 618            if response.status().is_client_error() || response.status().is_server_error() {
 619                bail!("failed to fetch '{url}': status code {}", response.status())
 620            }
 621            convert_response(&mut response).await
 622        })
 623        .await
 624        .to_wasmtime_result()
 625    }
 626
 627    async fn fetch_stream(
 628        &mut self,
 629        request: http_client::HttpRequest,
 630    ) -> wasmtime::Result<Result<Resource<ExtensionHttpResponseStream>, String>> {
 631        let request = convert_request(&request)?;
 632        let response = self.host.http_client.send(request);
 633        maybe!(async {
 634            let response = response.await?;
 635            let stream = Arc::new(Mutex::new(response));
 636            let resource = self.table.push(stream)?;
 637            Ok(resource)
 638        })
 639        .await
 640        .to_wasmtime_result()
 641    }
 642}
 643
 644impl http_client::HostHttpResponseStream for WasmState {
 645    async fn next_chunk(
 646        &mut self,
 647        resource: Resource<ExtensionHttpResponseStream>,
 648    ) -> wasmtime::Result<Result<Option<Vec<u8>>, String>> {
 649        let stream = self.table.get(&resource)?.clone();
 650        maybe!(async move {
 651            let mut response = stream.lock().await;
 652            let mut buffer = vec![0; 8192]; // 8KB buffer
 653            let bytes_read = response.body_mut().read(&mut buffer).await?;
 654            if bytes_read == 0 {
 655                Ok(None)
 656            } else {
 657                buffer.truncate(bytes_read);
 658                Ok(Some(buffer))
 659            }
 660        })
 661        .await
 662        .to_wasmtime_result()
 663    }
 664
 665    async fn drop(&mut self, _resource: Resource<ExtensionHttpResponseStream>) -> Result<()> {
 666        Ok(())
 667    }
 668}
 669
 670impl From<http_client::HttpMethod> for ::http_client::Method {
 671    fn from(value: http_client::HttpMethod) -> Self {
 672        match value {
 673            http_client::HttpMethod::Get => Self::GET,
 674            http_client::HttpMethod::Post => Self::POST,
 675            http_client::HttpMethod::Put => Self::PUT,
 676            http_client::HttpMethod::Delete => Self::DELETE,
 677            http_client::HttpMethod::Head => Self::HEAD,
 678            http_client::HttpMethod::Options => Self::OPTIONS,
 679            http_client::HttpMethod::Patch => Self::PATCH,
 680        }
 681    }
 682}
 683
 684fn convert_request(
 685    extension_request: &http_client::HttpRequest,
 686) -> anyhow::Result<::http_client::Request<AsyncBody>> {
 687    let mut request = ::http_client::Request::builder()
 688        .method(::http_client::Method::from(extension_request.method))
 689        .uri(&extension_request.url)
 690        .follow_redirects(match extension_request.redirect_policy {
 691            http_client::RedirectPolicy::NoFollow => ::http_client::RedirectPolicy::NoFollow,
 692            http_client::RedirectPolicy::FollowLimit(limit) => {
 693                ::http_client::RedirectPolicy::FollowLimit(limit)
 694            }
 695            http_client::RedirectPolicy::FollowAll => ::http_client::RedirectPolicy::FollowAll,
 696        });
 697    for (key, value) in &extension_request.headers {
 698        request = request.header(key, value);
 699    }
 700    let body = extension_request
 701        .body
 702        .clone()
 703        .map(AsyncBody::from)
 704        .unwrap_or_default();
 705    request.body(body).map_err(anyhow::Error::from)
 706}
 707
 708async fn convert_response(
 709    response: &mut ::http_client::Response<AsyncBody>,
 710) -> anyhow::Result<http_client::HttpResponse> {
 711    let mut extension_response = http_client::HttpResponse {
 712        body: Vec::new(),
 713        headers: Vec::new(),
 714    };
 715
 716    for (key, value) in response.headers() {
 717        extension_response
 718            .headers
 719            .push((key.to_string(), value.to_str().unwrap_or("").to_string()));
 720    }
 721
 722    response
 723        .body_mut()
 724        .read_to_end(&mut extension_response.body)
 725        .await?;
 726
 727    Ok(extension_response)
 728}
 729
 730impl nodejs::Host for WasmState {
 731    async fn node_binary_path(&mut self) -> wasmtime::Result<Result<String, String>> {
 732        self.host
 733            .node_runtime
 734            .binary_path()
 735            .await
 736            .map(|path| path.to_string_lossy().into_owned())
 737            .to_wasmtime_result()
 738    }
 739
 740    async fn npm_package_latest_version(
 741        &mut self,
 742        package_name: String,
 743    ) -> wasmtime::Result<Result<String, String>> {
 744        self.host
 745            .node_runtime
 746            .npm_package_latest_version(&package_name)
 747            .await
 748            .map(|v| v.to_string())
 749            .to_wasmtime_result()
 750    }
 751
 752    async fn npm_package_installed_version(
 753        &mut self,
 754        package_name: String,
 755    ) -> wasmtime::Result<Result<Option<String>, String>> {
 756        self.host
 757            .node_runtime
 758            .npm_package_installed_version(&self.work_dir(), &package_name)
 759            .await
 760            .map(|option| option.map(|version| version.to_string()))
 761            .to_wasmtime_result()
 762    }
 763
 764    async fn npm_install_package(
 765        &mut self,
 766        package_name: String,
 767        version: String,
 768    ) -> wasmtime::Result<Result<(), String>> {
 769        self.capability_granter
 770            .grant_npm_install_package(&package_name)?;
 771
 772        self.host
 773            .node_runtime
 774            .npm_install_packages(&self.work_dir(), &[(&package_name, &version)])
 775            .await
 776            .to_wasmtime_result()
 777    }
 778}
 779
 780#[async_trait]
 781impl lsp::Host for WasmState {}
 782
 783impl From<::http_client::github::GithubRelease> for github::GithubRelease {
 784    fn from(value: ::http_client::github::GithubRelease) -> Self {
 785        Self {
 786            version: value.tag_name,
 787            assets: value.assets.into_iter().map(Into::into).collect(),
 788        }
 789    }
 790}
 791
 792impl From<::http_client::github::GithubReleaseAsset> for github::GithubReleaseAsset {
 793    fn from(value: ::http_client::github::GithubReleaseAsset) -> Self {
 794        Self {
 795            name: value.name,
 796            download_url: value.browser_download_url,
 797            digest: value.digest,
 798        }
 799    }
 800}
 801
 802impl github::Host for WasmState {
 803    async fn latest_github_release(
 804        &mut self,
 805        repo: String,
 806        options: github::GithubReleaseOptions,
 807    ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
 808        maybe!(async {
 809            let release = ::http_client::github::latest_github_release(
 810                &repo,
 811                options.require_assets,
 812                options.pre_release,
 813                self.host.http_client.clone(),
 814            )
 815            .await?;
 816            Ok(release.into())
 817        })
 818        .await
 819        .to_wasmtime_result()
 820    }
 821
 822    async fn github_release_by_tag_name(
 823        &mut self,
 824        repo: String,
 825        tag: String,
 826    ) -> wasmtime::Result<Result<github::GithubRelease, String>> {
 827        maybe!(async {
 828            let release = ::http_client::github::get_release_by_tag_name(
 829                &repo,
 830                &tag,
 831                self.host.http_client.clone(),
 832            )
 833            .await?;
 834            Ok(release.into())
 835        })
 836        .await
 837        .to_wasmtime_result()
 838    }
 839}
 840
 841impl platform::Host for WasmState {
 842    async fn current_platform(&mut self) -> Result<(platform::Os, platform::Architecture)> {
 843        Ok((
 844            match env::consts::OS {
 845                "macos" => platform::Os::Mac,
 846                "linux" => platform::Os::Linux,
 847                "windows" => platform::Os::Windows,
 848                _ => panic!("unsupported os"),
 849            },
 850            match env::consts::ARCH {
 851                "aarch64" => platform::Architecture::Aarch64,
 852                "x86" => platform::Architecture::X86,
 853                "x86_64" => platform::Architecture::X8664,
 854                _ => panic!("unsupported architecture"),
 855            },
 856        ))
 857    }
 858}
 859
 860impl From<std::process::Output> for process::Output {
 861    fn from(output: std::process::Output) -> Self {
 862        Self {
 863            status: output.status.code(),
 864            stdout: output.stdout,
 865            stderr: output.stderr,
 866        }
 867    }
 868}
 869
 870impl process::Host for WasmState {
 871    async fn run_command(
 872        &mut self,
 873        command: process::Command,
 874    ) -> wasmtime::Result<Result<process::Output, String>> {
 875        maybe!(async {
 876            self.capability_granter
 877                .grant_exec(&command.command, &command.args)?;
 878
 879            let output = util::command::new_command(command.command.as_str())
 880                .args(&command.args)
 881                .envs(command.env)
 882                .output()
 883                .await?;
 884
 885            Ok(output.into())
 886        })
 887        .await
 888        .to_wasmtime_result()
 889    }
 890}
 891
 892#[async_trait]
 893impl slash_command::Host for WasmState {}
 894
 895#[async_trait]
 896impl context_server::Host for WasmState {}
 897
 898impl dap::Host for WasmState {
 899    async fn resolve_tcp_template(
 900        &mut self,
 901        template: TcpArgumentsTemplate,
 902    ) -> wasmtime::Result<Result<TcpArguments, String>> {
 903        maybe!(async {
 904            let (host, port, timeout) =
 905                ::dap::configure_tcp_connection(task::TcpArgumentsTemplate {
 906                    port: template.port,
 907                    host: template.host.map(Ipv4Addr::from_bits),
 908                    timeout: template.timeout,
 909                })
 910                .await?;
 911            Ok(TcpArguments {
 912                port,
 913                host: host.to_bits(),
 914                timeout,
 915            })
 916        })
 917        .await
 918        .to_wasmtime_result()
 919    }
 920}
 921
 922impl ExtensionImports for WasmState {
 923    async fn get_settings(
 924        &mut self,
 925        location: Option<self::SettingsLocation>,
 926        category: String,
 927        key: Option<String>,
 928    ) -> wasmtime::Result<Result<String, String>> {
 929        self.on_main_thread(|cx| {
 930            async move {
 931                let path = location.as_ref().and_then(|location| {
 932                    RelPath::new(Path::new(&location.path), PathStyle::Posix).ok()
 933                });
 934                let location = path
 935                    .as_ref()
 936                    .zip(location.as_ref())
 937                    .map(|(path, location)| ::settings::SettingsLocation {
 938                        worktree_id: WorktreeId::from_proto(location.worktree_id),
 939                        path,
 940                    });
 941
 942                cx.update(|cx| match category.as_str() {
 943                    "language" => {
 944                        let key = key.map(|k| LanguageName::new(&k));
 945                        let settings = AllLanguageSettings::get(location, cx).language(
 946                            location,
 947                            key.as_ref(),
 948                            cx,
 949                        );
 950                        Ok(serde_json::to_string(&settings::LanguageSettings {
 951                            tab_size: settings.tab_size,
 952                            preferred_line_length: settings.preferred_line_length,
 953                        })?)
 954                    }
 955                    "lsp" => {
 956                        let settings = key
 957                            .and_then(|key| {
 958                                ProjectSettings::get(location, cx)
 959                                    .lsp
 960                                    .get(&::lsp::LanguageServerName::from_proto(key))
 961                            })
 962                            .cloned()
 963                            .unwrap_or_default();
 964                        Ok(serde_json::to_string(&settings::LspSettings {
 965                            binary: settings.binary.map(|binary| settings::CommandSettings {
 966                                path: binary.path,
 967                                arguments: binary.arguments,
 968                                env: binary.env.map(|env| env.into_iter().collect()),
 969                            }),
 970                            settings: settings.settings,
 971                            initialization_options: settings.initialization_options,
 972                        })?)
 973                    }
 974                    "context_servers" => {
 975                        let settings = key
 976                            .and_then(|key| {
 977                                ProjectSettings::get(location, cx)
 978                                    .context_servers
 979                                    .get(key.as_str())
 980                            })
 981                            .cloned()
 982                            .unwrap_or_else(|| {
 983                                project::project_settings::ContextServerSettings::default_extension(
 984                                )
 985                            });
 986
 987                        match settings {
 988                            project::project_settings::ContextServerSettings::Stdio {
 989                                enabled: _,
 990                                command,
 991                                ..
 992                            } => Ok(serde_json::to_string(&settings::ContextServerSettings {
 993                                command: Some(settings::CommandSettings {
 994                                    path: command.path.to_str().map(|path| path.to_string()),
 995                                    arguments: Some(command.args),
 996                                    env: command.env.map(|env| env.into_iter().collect()),
 997                                }),
 998                                settings: None,
 999                            })?),
1000                            project::project_settings::ContextServerSettings::Extension {
1001                                enabled: _,
1002                                settings,
1003                                ..
1004                            } => Ok(serde_json::to_string(&settings::ContextServerSettings {
1005                                command: None,
1006                                settings: Some(settings),
1007                            })?),
1008                            project::project_settings::ContextServerSettings::Http { .. } => {
1009                                bail!("remote context server settings not supported in 0.6.0")
1010                            }
1011                        }
1012                    }
1013                    _ => {
1014                        bail!("Unknown settings category: {}", category);
1015                    }
1016                })
1017            }
1018            .boxed_local()
1019        })
1020        .await
1021        .to_wasmtime_result()
1022    }
1023
1024    async fn set_language_server_installation_status(
1025        &mut self,
1026        server_name: String,
1027        status: LanguageServerInstallationStatus,
1028    ) -> wasmtime::Result<()> {
1029        let status = match status {
1030            LanguageServerInstallationStatus::CheckingForUpdate => BinaryStatus::CheckingForUpdate,
1031            LanguageServerInstallationStatus::Downloading => BinaryStatus::Downloading,
1032            LanguageServerInstallationStatus::None => BinaryStatus::None,
1033            LanguageServerInstallationStatus::Failed(error) => BinaryStatus::Failed { error },
1034        };
1035
1036        self.host
1037            .proxy
1038            .update_language_server_status(::lsp::LanguageServerName(server_name.into()), status);
1039
1040        Ok(())
1041    }
1042
1043    async fn download_file(
1044        &mut self,
1045        url: String,
1046        path: String,
1047        file_type: DownloadedFileType,
1048    ) -> wasmtime::Result<Result<(), String>> {
1049        maybe!(async {
1050            let parsed_url = Url::parse(&url)?;
1051            self.capability_granter.grant_download_file(&parsed_url)?;
1052
1053            let path = PathBuf::from(path);
1054            let extension_work_dir = self.host.work_dir.join(self.manifest.id.as_ref());
1055
1056            self.host.fs.create_dir(&extension_work_dir).await?;
1057
1058            let destination_path = self
1059                .host
1060                .writeable_path_from_extension(&self.manifest.id, &path)
1061                .await?;
1062
1063            let mut response = self
1064                .host
1065                .http_client
1066                .get(&url, Default::default(), true)
1067                .await
1068                .context("downloading release")?;
1069
1070            anyhow::ensure!(
1071                response.status().is_success(),
1072                "download failed with status {}",
1073                response.status()
1074            );
1075            let body = BufReader::new(response.body_mut());
1076
1077            match file_type {
1078                DownloadedFileType::Uncompressed => {
1079                    futures::pin_mut!(body);
1080                    self.host
1081                        .fs
1082                        .create_file_with(&destination_path, body)
1083                        .await?;
1084                }
1085                DownloadedFileType::Gzip => {
1086                    let body = GzipDecoder::new(body);
1087                    futures::pin_mut!(body);
1088                    self.host
1089                        .fs
1090                        .create_file_with(&destination_path, body)
1091                        .await?;
1092                }
1093                DownloadedFileType::GzipTar => {
1094                    let body = GzipDecoder::new(body);
1095                    futures::pin_mut!(body);
1096                    self.host
1097                        .fs
1098                        .extract_tar_file(&destination_path, Archive::new(body))
1099                        .await?;
1100                }
1101                DownloadedFileType::Zip => {
1102                    futures::pin_mut!(body);
1103                    extract_zip(&destination_path, body)
1104                        .await
1105                        .with_context(|| format!("unzipping {path:?} archive"))?;
1106                }
1107            }
1108
1109            Ok(())
1110        })
1111        .await
1112        .to_wasmtime_result()
1113    }
1114
1115    async fn make_file_executable(&mut self, path: String) -> wasmtime::Result<Result<(), String>> {
1116        let path = self
1117            .host
1118            .writeable_path_from_extension(&self.manifest.id, Path::new(&path))
1119            .await?;
1120
1121        make_file_executable(&path)
1122            .await
1123            .with_context(|| format!("setting permissions for path {path:?}"))
1124            .to_wasmtime_result()
1125    }
1126}