11use clap:: Subcommand ;
2- use crate :: { cli :: chat :: tools :: todo :: TodoState , os :: Os } ;
3- use crossterm:: {
4- execute ,
5- style :: { self , Stylize } ,
2+ use crossterm :: execute ;
3+ use crossterm:: style :: {
4+ self ,
5+ Stylize ,
66} ;
7+ use dialoguer:: FuzzySelect ;
8+ use eyre:: Result ;
79
10+ use crate :: cli:: chat:: tools:: todo:: TodoState ;
811use crate :: cli:: chat:: {
912 ChatError ,
1013 ChatSession ,
1114 ChatState ,
1215} ;
13-
14- use eyre:: Result ;
15-
16- use dialoguer:: {
17- FuzzySelect
18- } ;
16+ use crate :: os:: Os ;
1917
2018#[ derive( Debug , PartialEq , Subcommand ) ]
2119pub enum TodoSubcommand {
20+ // Show all tracked to-do lists
2221 Show ,
22+
23+ // Clear completed to-do lists
2324 ClearFinished ,
24- Select ,
25+
26+ // Resume a selected to-do list
27+ Resume ,
28+
29+ // View a to-do list
30+ View ,
2531}
2632
2733/// Used for displaying completed and in-progress todo lists
@@ -35,13 +41,12 @@ pub struct TodoDisplayEntry {
3541impl std:: fmt:: Display for TodoDisplayEntry {
3642 fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
3743 if self . num_completed == self . num_tasks {
38- write ! ( f, "{} {}" ,
39- "✓" . green( ) . bold( ) ,
40- self . description. clone( ) ,
41- )
44+ write ! ( f, "{} {}" , "✓" . green( ) . bold( ) , self . description. clone( ) , )
4245 } else {
43- write ! ( f, "{} {} ({}/{})" ,
44- "✗" . red( ) . bold( ) ,
46+ write ! (
47+ f,
48+ "{} {} ({}/{})" ,
49+ "✗" . red( ) . bold( ) ,
4550 self . description. clone( ) ,
4651 self . num_completed,
4752 self . num_tasks
@@ -53,49 +58,32 @@ impl std::fmt::Display for TodoDisplayEntry {
5358impl TodoSubcommand {
5459 pub async fn execute ( self , os : & mut Os , session : & mut ChatSession ) -> Result < ChatState , ChatError > {
5560 match self {
56- Self :: Show => {
57- match self . get_descriptions_and_statuses ( os) {
58- Ok ( entries) => {
59- if entries. len ( ) == 0 {
60- execute ! (
61- session. stderr,
62- style:: Print ( "No to-do lists to show" ) ,
63- ) ?;
64- }
65- for e in entries {
66- execute ! (
67- session. stderr,
68- style:: Print ( e) ,
69- style:: Print ( "\n " ) ,
70- ) ?;
71- }
61+ Self :: Show => match Self :: get_descriptions_and_statuses ( os) {
62+ Ok ( entries) => {
63+ if entries. is_empty ( ) {
64+ execute ! ( session. stderr, style:: Print ( "No to-do lists to show" ) , ) ?;
7265 }
73- Err ( _) => {
74- execute ! (
75- session. stderr,
76- style:: Print ( "Could not show to-do lists" ) ,
77- ) ?;
66+ for e in entries {
67+ execute ! ( session. stderr, style:: Print ( e) , style:: Print ( "\n " ) , ) ?;
7868 }
79- }
69+ } ,
70+ Err ( _) => return Err ( ChatError :: Custom ( "Could not show to-do lists" . into ( ) ) ) ,
8071 } ,
8172 Self :: ClearFinished => {
82- ( ) ;
73+
8374 } ,
84- Self :: Select => {
85- match self . get_descriptions_and_statuses ( os) {
75+ Self :: Resume => {
76+ match Self :: get_descriptions_and_statuses ( os) {
8677 Ok ( entries) => {
87- if entries. len ( ) == 0 {
88- execute ! (
89- session. stderr,
90- style:: Print ( "No to-do lists to show" ) ,
91- ) ?;
78+ if entries. is_empty ( ) {
79+ execute ! ( session. stderr, style:: Print ( "No to-do lists to show" ) , ) ?;
9280 } else {
9381 let selection = FuzzySelect :: new ( )
94- . with_prompt ( "Select task:" )
82+ . with_prompt ( "Select task to resume :" )
9583 . items ( & entries)
9684 . report ( false )
9785 . interact_opt ( )
98- . unwrap_or ( None ) ;
86+ . unwrap_or ( None ) ;
9987
10088 if let Some ( index) = selection {
10189 if index < entries. len ( ) {
@@ -108,82 +96,116 @@ impl TodoSubcommand {
10896 }
10997 }
11098 }
111- }
112- // %%% FIX %%%
113- Err ( e) => println ! ( "{:?}" , e) ,
114- // %%% --- %%%
99+ } ,
100+ Err ( _) => return Err ( ChatError :: Custom ( "Could not show to-do lists" . into ( ) ) ) ,
115101 } ;
116102 } ,
117- } ;
118- Ok ( ChatState :: PromptUser { skip_printing_tools : true } )
103+ Self :: View => {
104+ match Self :: get_descriptions_and_statuses ( os) {
105+ Ok ( entries) => {
106+ if entries. is_empty ( ) {
107+ execute ! ( session. stderr, style:: Print ( "No to-do lists to view" ) ) ?;
108+ } else {
109+ let selection = FuzzySelect :: new ( )
110+ . with_prompt ( "Select task to view:" )
111+ . items ( & entries)
112+ . report ( false )
113+ . interact_opt ( )
114+ . unwrap_or ( None ) ;
115+
116+ if let Some ( index) = selection {
117+ if index < entries. len ( ) {
118+ let list = match TodoState :: load ( os, & entries[ index] . id ) {
119+ Ok ( list) => list,
120+ Err ( _) => {
121+ return Err ( ChatError :: Custom ( "Could not load requested to-do list" . into ( ) ) ) ;
122+ }
123+ } ;
124+ match list. display_list ( & mut session. stderr ) {
125+ Ok ( _) => { } ,
126+ Err ( _) => {
127+ return Err ( ChatError :: Custom ( "Could not display requested to-do list" . into ( ) ) ) ;
128+ }
129+ } ;
130+ execute ! ( session. stderr, style:: Print ( "\n " ) , ) ?;
131+ }
132+ }
133+ }
134+ } ,
135+ Err ( _) => return Err ( ChatError :: Custom ( "Could not show to-do lists" . into ( ) ) ) ,
136+ }
137+ }
138+ }
139+ Ok ( ChatState :: PromptUser {
140+ skip_printing_tools : true ,
141+ } )
119142 }
120143
121- /// Convert all to-do list state files to displayable entries
122- fn get_descriptions_and_statuses ( self , os : & Os ) -> Result < Vec < TodoDisplayEntry > > {
144+ /// Convert all to-do list state entries to displayable entries
145+ fn get_descriptions_and_statuses ( os : & Os ) -> Result < Vec < TodoDisplayEntry > > {
123146 let mut out = Vec :: new ( ) ;
124147 let entries = os. database . get_all_todos ( ) ?;
125148 for ( id, value) in entries. iter ( ) {
126149 let temp_struct = match value. as_str ( ) {
127- Some ( s) => { match serde_json:: from_str :: < TodoState > ( s) {
150+ Some ( s) => match serde_json:: from_str :: < TodoState > ( s) {
128151 Ok ( state) => state,
129152 Err ( _) => continue ,
130- } } ,
153+ } ,
131154 None => continue ,
132155 } ;
133156 // For some reason this doesn't work
134- // Has to do with the Value::String wrapping in os.database.all_entries() rather than Value::from_str()
135- // let temp_struct = match serde_json::from_value::<TodoState>(value.clone()) {
136- // Ok(state) => state,
157+ // Has to do with the Value::String wrapping in os.database.all_entries() rather than
158+ // Value::from_str()
159+ // let temp_struct = match
160+ // serde_json::from_value::<TodoState>(value.clone()) { Ok(state) => state,
137161 // Err(_) => continue,
138162 // };
139163
140164 out. push ( TodoDisplayEntry {
141165 num_completed : temp_struct. completed . iter ( ) . filter ( |b| * * b) . count ( ) ,
142166 num_tasks : temp_struct. completed . len ( ) ,
143167 description : prewrap ( & temp_struct. task_description ) ,
144- id : id. to_string ( ) ,
168+ id : id. clone ( ) ,
145169 } ) ;
146170 }
147171 Ok ( out)
148172 }
149-
150173}
151174
152-
153175const MAX_LINE_LENGTH : usize = 80 ;
154176
155177// FIX: Hacky workaround for cleanly wrapping lines
156178/// Insert newlines every n characters, not within a word and not at the end.
157- ///
179+ ///
158180/// Generated by Q
159181fn prewrap ( text : & str ) -> String {
160182 if text. is_empty ( ) || MAX_LINE_LENGTH == 0 {
161183 return text. to_string ( ) ;
162184 }
163-
185+
164186 let mut result = String :: new ( ) ;
165187 let mut current_line_length = 0 ;
166188 let words: Vec < & str > = text. split_whitespace ( ) . collect ( ) ;
167-
168- for ( _ , word) in words. iter ( ) . enumerate ( ) {
189+
190+ for word in words. iter ( ) {
169191 let word_length = word. len ( ) ;
170-
192+
171193 // If adding this word would exceed the line length and we're not at the start of a line
172194 if current_line_length > 0 && current_line_length + 1 + word_length > MAX_LINE_LENGTH {
173195 result. push ( '\n' ) ;
174196 result. push_str ( & " " . repeat ( "> " . len ( ) ) ) ;
175197 current_line_length = 0 ;
176198 }
177-
199+
178200 // Add space before word if not at start of line
179201 if current_line_length > 0 {
180202 result. push ( ' ' ) ;
181203 current_line_length += 1 ;
182204 }
183-
205+
184206 result. push_str ( word) ;
185207 current_line_length += word_length;
186208 }
187-
209+
188210 result
189- }
211+ }
0 commit comments