1use crate::console_log;
2use futures::FutureExt;
3use js_sys::Promise;
4use nu_parser::{flatten_block, parse};
5use nu_protocol::engine::StateWorkingSet;
6use wasm_bindgen::prelude::*;
7use wasm_bindgen_futures::future_to_promise;
8
9use super::*;
10
11pub mod context;
12pub mod helpers;
13pub mod suggestions;
14pub mod types;
15pub mod variables;
16
17pub use context::determine_context;
18pub use suggestions::generate_suggestions;
19pub use types::{CompletionContext, Suggestion};
20
21#[wasm_bindgen]
22pub fn completion(input: String, js_cursor_pos: usize) -> Promise {
23 future_to_promise(completion_impl(input, js_cursor_pos).map(|s| Ok(JsValue::from_str(&s))))
24}
25
26pub async fn completion_impl(input: String, js_cursor_pos: usize) -> String {
27 let engine_guard = read_engine_state().await;
28 let stack_guard = crate::read_stack().await;
29 let root = get_pwd();
30
31 // Map UTF-16 cursor position (from JS) to Byte index (for Rust)
32 let byte_pos = input
33 .char_indices()
34 .map(|(i, _)| i)
35 .nth(js_cursor_pos)
36 .unwrap_or(input.len());
37
38 let (working_set, shapes, global_offset) = {
39 let mut working_set = StateWorkingSet::new(&engine_guard);
40 let global_offset = working_set.next_span_start();
41 let block = parse(&mut working_set, None, input.as_bytes(), false);
42 let shapes = flatten_block(&working_set, &block);
43 (working_set, shapes, global_offset)
44 };
45
46 // Initial state logging
47 console_log!(
48 "[completion] Input: {input:?}, JS cursor: {js_cursor_pos}, byte cursor: {byte_pos}"
49 );
50 console_log!(
51 "[completion] Found {count} shapes, global_offset: {global_offset}",
52 count = shapes.len()
53 );
54 for (idx, (span, shape)) in shapes.iter().enumerate() {
55 let (local_start, local_end) = (
56 span.start.saturating_sub(global_offset),
57 span.end.saturating_sub(global_offset),
58 );
59 console_log!(
60 "[completion] Shape {idx}: {shape:?} at [{start}, {end}] (local: [{local_start}, {local_end}])",
61 start = span.start,
62 end = span.end
63 );
64 }
65
66 // Determine completion context
67 let context = determine_context(
68 &input,
69 &shapes,
70 &working_set,
71 &engine_guard,
72 byte_pos,
73 global_offset,
74 );
75
76 // Convert Vec to HashSet
77 use std::collections::HashSet;
78 let context_set: HashSet<CompletionContext> = context.into_iter().collect();
79
80 // Generate suggestions based on context
81 let suggestions = generate_suggestions(
82 &input,
83 context_set,
84 &working_set,
85 &engine_guard,
86 &stack_guard,
87 &root,
88 byte_pos,
89 );
90
91 drop(working_set);
92 drop(engine_guard);
93
94 let suggestions = serde_json::to_string(&suggestions).unwrap_or_else(|_| "[]".to_string());
95 console_log!("{suggestions}");
96 suggestions
97}