@@ -2,9 +2,10 @@ use crate::{
22 error:: WasmError ,
33 hostcall:: handle_host_calls,
44 http_client:: GatewayHttpViaWSClient ,
5- types:: { SecurityWarningCallback , WasmWinRmConfig } ,
5+ types:: { SecurityWarningCallback , WasmCommandCompletion , WasmWinRmConfig } ,
66 JsSessionEvent , WasmPowerShellStream ,
77} ;
8+ use std:: convert:: TryFrom ;
89use futures:: StreamExt ;
910use ironposh_async:: RemoteAsyncPowershellClient ;
1011use ironposh_client_core:: { connector:: WinRmConfig , powershell:: PipelineHandle } ;
@@ -242,6 +243,73 @@ impl WasmPowerShellClient {
242243 Ok ( stream)
243244 }
244245
246+ #[ wasm_bindgen]
247+ pub async fn tab_complete (
248+ & mut self ,
249+ input_script : String ,
250+ cursor_column : u32 ,
251+ ) -> Result < WasmCommandCompletion , WasmError > {
252+ use ironposh_client_core:: connector:: active_session:: UserEvent ;
253+
254+ fn escape_ps_single_quoted ( input : & str ) -> String {
255+ input. replace ( '\'' , "''" )
256+ }
257+
258+ let escaped = escape_ps_single_quoted ( & input_script) ;
259+ let script = format ! (
260+ "TabExpansion2 -inputScript '{escaped}' -cursorColumn {cursor_column}"
261+ ) ;
262+
263+ info ! (
264+ cursor_column,
265+ input_len = input_script. len( ) ,
266+ "tab_complete: sending TabExpansion2"
267+ ) ;
268+
269+ let stream = self . client . send_script_raw ( script) . await ?;
270+ let mut stream = stream. boxed ( ) ;
271+
272+ let mut output: Option < ironposh_psrp:: PsValue > = None ;
273+ let mut error_message: Option < String > = None ;
274+
275+ while let Some ( ev) = stream. next ( ) . await {
276+ match ev {
277+ UserEvent :: PipelineOutput { output : out, .. } => {
278+ if output. is_none ( ) {
279+ output = Some ( out. data ) ;
280+ }
281+ }
282+ UserEvent :: ErrorRecord { error_record, .. } => {
283+ let concise = error_record. render_concise ( ) ;
284+ error_message = Some ( concise. clone ( ) ) ;
285+ warn ! ( error_message = %concise, "tab_complete: error record" ) ;
286+ }
287+ UserEvent :: PipelineFinished { .. } => break ,
288+ _ => { }
289+ }
290+ }
291+
292+ let Some ( ps_value) = output else {
293+ return Err ( WasmError :: Generic (
294+ error_message. unwrap_or_else ( || "TabExpansion2 returned no output" . into ( ) ) ,
295+ ) ) ;
296+ } ;
297+
298+ let completion = ironposh_psrp:: CommandCompletion :: try_from ( & ps_value)
299+ . map_err ( |e| WasmError :: Generic ( e. to_string ( ) ) ) ?;
300+
301+ info ! (
302+ ?completion,
303+ cursor_column,
304+ replacement_index = completion. replacement_index,
305+ replacement_length = completion. replacement_length,
306+ matches = completion. completion_matches. len( ) ,
307+ "tab_complete: parsed completion"
308+ ) ;
309+
310+ Ok ( WasmCommandCompletion :: from ( & completion) )
311+ }
312+
245313 // pub async fn next_host_call
246314
247315 #[ wasm_bindgen]
0 commit comments