1- use std:: { collections :: BTreeSet , sync:: Arc } ;
1+ use std:: sync:: Arc ;
22
33use promkit_widgets:: {
44 core:: { crossterm:: event:: Event , grapheme:: StyledGraphemes , Widget } ,
55 listbox:: { self , Listbox } ,
66} ;
77use tokio:: {
8- sync:: { mpsc, Mutex , RwLock } ,
9- task:: { self , JoinHandle } ,
8+ sync:: { mpsc, RwLock } ,
9+ task:: JoinHandle ,
1010} ;
1111
1212use crate :: {
1313 config:: CompletionKeybinds ,
1414 context:: { Index , SharedContext } ,
1515 guide:: { GuideAction , GuideMessage } ,
16- json ,
16+ jq_completion ,
1717 query_editor:: QueryEditorAction ,
1818} ;
1919
20- /// Progress information for loading suggestions
21- #[ derive( Clone , Default ) ]
22- pub struct SuggestionLoadProgress {
23- pub is_complete : bool ,
24- pub loaded_path_count : usize ,
25- }
26-
27- /// Store for suggestions with thread-safe access
28- struct SuggestionStore {
29- /// Set of all paths extracted from JSON input
30- paths : BTreeSet < String > ,
31- progress : SuggestionLoadProgress ,
32- }
33-
34- #[ derive( Clone ) ]
35- pub struct SharedSuggestionStore ( Arc < Mutex < SuggestionStore > > ) ;
36-
37- impl SharedSuggestionStore {
38- /// Collect suggestions that start with the given prefix
39- pub async fn collect_matches ( & self , prefix : & str ) -> ( Vec < String > , SuggestionLoadProgress ) {
40- let store = self . 0 . lock ( ) . await ;
41- let items = store
42- . paths
43- . iter ( )
44- . filter ( |p| p. starts_with ( prefix) )
45- . cloned ( )
46- . collect :: < Vec < _ > > ( ) ;
47- ( items, store. progress . clone ( ) )
48- }
49- }
50-
51- /// Spawn a background loader and return shared suggestion store with task handle.
52- pub fn spawn_initialize (
53- input : & ' static str ,
54- max_streams : Option < usize > ,
55- chunk_size : usize ,
56- ) -> ( SharedSuggestionStore , JoinHandle < ( ) > ) {
57- let shared = SharedSuggestionStore ( Arc :: new ( Mutex :: new ( SuggestionStore {
58- paths : BTreeSet :: new ( ) ,
59- progress : SuggestionLoadProgress :: default ( ) ,
60- } ) ) ) ;
61-
62- let shared_for_loading = shared. clone ( ) ;
63- let loader_task = task:: spawn ( async move {
64- // Load paths in a streaming manner and update the shared store incrementally
65- let iter = match json:: get_all_paths ( input, max_streams) . await {
66- Ok ( iter) => iter,
67- Err ( _) => {
68- let mut store = shared_for_loading. 0 . lock ( ) . await ;
69- store. progress . is_complete = true ;
70- return ;
71- }
72- } ;
73-
74- // Process paths in chunks to avoid holding the lock for too long
75- let mut batch = Vec :: with_capacity ( chunk_size) ;
76- for path in iter {
77- batch. push ( path) ;
78-
79- if batch. len ( ) >= chunk_size {
80- let loaded = batch. len ( ) ;
81- let mut store = shared_for_loading. 0 . lock ( ) . await ;
82- for item in batch. drain ( ..) {
83- store. paths . insert ( item) ;
84- }
85- store. progress . loaded_path_count += loaded;
86- }
87- }
88-
89- // Insert any remaining paths after the loop
90- let remaining = batch. len ( ) ;
91- let mut store = shared_for_loading. 0 . lock ( ) . await ;
92- for item in batch {
93- store. paths . insert ( item) ;
94- }
95-
96- // Mark loading as complete and update progress
97- store. progress . loaded_path_count += remaining;
98- store. progress . is_complete = true ;
99- } ) ;
100-
101- ( shared, loader_task)
102- }
103-
10420/// Navigator for managing the state of suggestions
10521/// and interactions in the completion view.
10622pub struct CompletionNavigator {
107- shared_suggestions : SharedSuggestionStore ,
10823 state : listbox:: State ,
10924 /// Number of suggestions to load in each chunk
11025 /// when the user scrolls near the end of the list.
@@ -115,12 +30,10 @@ pub struct CompletionNavigator {
11530
11631impl CompletionNavigator {
11732 pub fn new (
118- shared_suggestions : SharedSuggestionStore ,
11933 state : listbox:: State ,
12034 search_result_chunk_size : usize ,
12135 ) -> Self {
12236 Self {
123- shared_suggestions,
12437 state,
12538 search_result_chunk_size,
12639 remaining_items : Default :: default ( ) ,
@@ -198,8 +111,13 @@ impl CompletionNavigator {
198111 None
199112 }
200113
201- async fn enter ( & mut self , prefix : & str ) -> ( Option < String > , SuggestionLoadProgress ) {
202- let ( items, progress) = self . shared_suggestions . collect_matches ( prefix) . await ;
114+ async fn enter (
115+ & mut self ,
116+ completion_engine : & jq_completion:: CompletionEngine ,
117+ query : & str ,
118+ cursor_char : usize ,
119+ ) -> ( Option < String > , jq_completion:: LoadProgress ) {
120+ let ( items, progress) = completion_engine. suggest_strings ( query, cursor_char) . await ;
203121 let head_item = self . initialize_session_items ( items) ;
204122 ( head_item, progress)
205123 }
@@ -230,8 +148,8 @@ impl CompletionNavigator {
230148}
231149
232150pub enum CompletionAction {
233- /// Triggered when the user enters the completion view with a current query as prefix .
234- Enter { prefix : String } ,
151+ /// Triggered when the user enters completion with current query and cursor position .
152+ Enter { query : String , cursor_char : usize } ,
235153 /// Triggered when the user leaves the completion view.
236154 Leave ,
237155 /// Triggered on user input events within the completion view, such as navigation keys.
@@ -242,6 +160,7 @@ pub enum CompletionAction {
242160pub fn start_completion_task (
243161 mut action_rx : mpsc:: Receiver < CompletionAction > ,
244162 shared_ctx : SharedContext ,
163+ completion_engine : jq_completion:: CompletionEngine ,
245164 shared_completion : Arc < RwLock < CompletionNavigator > > ,
246165 shared_renderer : promkit_widgets:: core:: render:: SharedRenderer < Index > ,
247166 query_editor_action_tx : mpsc:: Sender < QueryEditorAction > ,
@@ -256,8 +175,10 @@ pub fn start_completion_task(
256175 let completion_view = {
257176 let mut completion = shared_completion. write( ) . await ;
258177 match action {
259- CompletionAction :: Enter { prefix } => {
260- let ( head_item, load_progress) = completion. enter( & prefix) . await ;
178+ CompletionAction :: Enter { query, cursor_char } => {
179+ let ( head_item, load_progress) = completion
180+ . enter( & completion_engine, & query, cursor_char)
181+ . await ;
261182 match head_item {
262183 Some ( head) => {
263184 let message = if load_progress. is_complete {
@@ -272,7 +193,7 @@ pub fn start_completion_task(
272193 }
273194 None => {
274195 guide_action_tx
275- . send( GuideAction :: Show ( GuideMessage :: NoSuggestionFound ( prefix ) ) )
196+ . send( GuideAction :: Show ( GuideMessage :: NoSuggestionFound ( query ) ) )
276197 . await ?;
277198 shared_ctx. set_active_index( Index :: QueryEditor ) . await ;
278199 completion. clear_session_state( ) ;
0 commit comments