1
- use anyhow:: { anyhow, bail , Context , Result } ;
1
+ use anyhow:: { anyhow, Context , Result } ;
2
2
use async_compression:: futures:: bufread:: GzipDecoder ;
3
3
use async_trait:: async_trait;
4
4
use collections:: HashMap ;
5
5
use futures:: { io:: BufReader , StreamExt } ;
6
6
use gpui:: { AppContext , AsyncAppContext } ;
7
+ use http_client:: github:: AssetKind ;
7
8
use http_client:: github:: { latest_github_release, GitHubLspBinaryVersion } ;
8
9
pub use language:: * ;
9
10
use lsp:: { LanguageServerBinary , LanguageServerName } ;
10
11
use regex:: Regex ;
11
- use smol:: fs:: { self , File } ;
12
+ use smol:: fs:: { self } ;
12
13
use std:: {
13
14
any:: Any ,
14
15
borrow:: Cow ,
15
- env:: consts,
16
16
path:: { Path , PathBuf } ,
17
17
sync:: Arc ,
18
18
sync:: LazyLock ,
@@ -24,8 +24,41 @@ use crate::language_settings::language_settings;
24
24
25
25
pub struct RustLspAdapter ;
26
26
27
+ #[ cfg( target_os = "macos" ) ]
28
+ impl RustLspAdapter {
29
+ const GITHUB_ASSET_KIND : AssetKind = AssetKind :: TarGz ;
30
+ const ARCH_SERVER_NAME : & str = "apple-darwin" ;
31
+ }
32
+
33
+ #[ cfg( target_os = "linux" ) ]
34
+ impl RustLspAdapter {
35
+ const GITHUB_ASSET_KIND : AssetKind = AssetKind :: TarGz ;
36
+ const ARCH_SERVER_NAME : & str = "unknown-linux-gnu" ;
37
+ }
38
+
39
+ #[ cfg( target_os = "windows" ) ]
40
+ impl RustLspAdapter {
41
+ const GITHUB_ASSET_KIND : AssetKind = AssetKind :: Zip ;
42
+ const ARCH_SERVER_NAME : & str = "pc-windows-msvc" ;
43
+ }
44
+
27
45
impl RustLspAdapter {
28
46
const SERVER_NAME : LanguageServerName = LanguageServerName :: new_static ( "rust-analyzer" ) ;
47
+
48
+ fn build_asset_name ( ) -> String {
49
+ let extension = match Self :: GITHUB_ASSET_KIND {
50
+ AssetKind :: TarGz => "gz" , // Nb: rust-analyzer releases use .gz not .tar.gz
51
+ AssetKind :: Zip => "zip" ,
52
+ } ;
53
+
54
+ format ! (
55
+ "{}-{}-{}.{}" ,
56
+ Self :: SERVER_NAME ,
57
+ std:: env:: consts:: ARCH ,
58
+ Self :: ARCH_SERVER_NAME ,
59
+ extension
60
+ )
61
+ }
29
62
}
30
63
31
64
#[ async_trait( ?Send ) ]
@@ -79,13 +112,8 @@ impl LspAdapter for RustLspAdapter {
79
112
delegate. http_client ( ) ,
80
113
)
81
114
. await ?;
82
- let os = match consts:: OS {
83
- "macos" => "apple-darwin" ,
84
- "linux" => "unknown-linux-gnu" ,
85
- "windows" => "pc-windows-msvc" ,
86
- other => bail ! ( "Running on unsupported os: {other}" ) ,
87
- } ;
88
- let asset_name = format ! ( "rust-analyzer-{}-{os}.gz" , consts:: ARCH ) ;
115
+ let asset_name = Self :: build_asset_name ( ) ;
116
+
89
117
let asset = release
90
118
. assets
91
119
. iter ( )
@@ -105,31 +133,47 @@ impl LspAdapter for RustLspAdapter {
105
133
) -> Result < LanguageServerBinary > {
106
134
let version = version. downcast :: < GitHubLspBinaryVersion > ( ) . unwrap ( ) ;
107
135
let destination_path = container_dir. join ( format ! ( "rust-analyzer-{}" , version. name) ) ;
136
+ let server_path = match Self :: GITHUB_ASSET_KIND {
137
+ AssetKind :: TarGz => destination_path. clone ( ) , // Tar extracts in place.
138
+ AssetKind :: Zip => destination_path. clone ( ) . join ( "rust-analyzer.exe" ) , // zip contains a .exe
139
+ } ;
140
+
141
+ if fs:: metadata ( & server_path) . await . is_err ( ) {
142
+ remove_matching ( & container_dir, |entry| entry != destination_path) . await ;
108
143
109
- if fs:: metadata ( & destination_path) . await . is_err ( ) {
110
144
let mut response = delegate
111
145
. http_client ( )
112
146
. get ( & version. url , Default :: default ( ) , true )
113
147
. await
114
148
. map_err ( |err| anyhow ! ( "error downloading release: {}" , err) ) ?;
115
- let decompressed_bytes = GzipDecoder :: new ( BufReader :: new ( response. body_mut ( ) ) ) ;
116
- let mut file = File :: create ( & destination_path) . await ?;
117
- futures:: io:: copy ( decompressed_bytes, & mut file) . await ?;
149
+ match Self :: GITHUB_ASSET_KIND {
150
+ AssetKind :: TarGz => {
151
+ let decompressed_bytes = GzipDecoder :: new ( BufReader :: new ( response. body_mut ( ) ) ) ;
152
+ let archive = async_tar:: Archive :: new ( decompressed_bytes) ;
153
+ archive. unpack ( & destination_path) . await ?;
154
+ }
155
+ AssetKind :: Zip => {
156
+ node_runtime:: extract_zip (
157
+ & destination_path,
158
+ BufReader :: new ( response. body_mut ( ) ) ,
159
+ )
160
+ . await ?;
161
+ }
162
+ } ;
163
+
118
164
// todo("windows")
119
165
#[ cfg( not( windows) ) ]
120
166
{
121
167
fs:: set_permissions (
122
- & destination_path ,
168
+ & server_path ,
123
169
<fs:: Permissions as fs:: unix:: PermissionsExt >:: from_mode ( 0o755 ) ,
124
170
)
125
171
. await ?;
126
172
}
127
-
128
- remove_matching ( & container_dir, |entry| entry != destination_path) . await ;
129
173
}
130
174
131
175
Ok ( LanguageServerBinary {
132
- path : destination_path ,
176
+ path : server_path ,
133
177
env : None ,
134
178
arguments : Default :: default ( ) ,
135
179
} )
0 commit comments