11use crate :: {
22 input,
3- tasks:: { self , TaskRef , TaskState } ,
3+ tasks:: { self , Task , TaskRef , TaskState } ,
44 view:: { self , bold} ,
5+ warnings,
56} ;
67use std:: convert:: TryFrom ;
78use tui:: {
@@ -10,8 +11,12 @@ use tui::{
1011 text:: { self , Span , Spans } ,
1112 widgets:: { Cell , Paragraph , Row , Table , TableState } ,
1213} ;
13- #[ derive( Clone , Debug , Default ) ]
14+
15+ #[ derive( Debug ) ]
1416pub ( crate ) struct List {
17+ /// A list of linters (implementing the [`warnings::Warn`] trait) used to generate
18+ /// warnings.
19+ linters : Vec < Box < dyn warnings:: Warn < Task > > > ,
1520 sorted_tasks : Vec < TaskRef > ,
1621 sort_by : tasks:: SortBy ,
1722 table_state : TableState ,
@@ -76,27 +81,6 @@ impl List {
7681 area : layout:: Rect ,
7782 state : & mut tasks:: State ,
7883 ) {
79- let chunks = layout:: Layout :: default ( )
80- . direction ( layout:: Direction :: Vertical )
81- . margin ( 0 )
82- . constraints (
83- [
84- layout:: Constraint :: Length ( 1 ) ,
85- layout:: Constraint :: Min ( area. height - 1 ) ,
86- ]
87- . as_ref ( ) ,
88- )
89- . split ( area) ;
90- let controls_area = chunks[ 0 ] ;
91- let tasks_area = chunks[ 1 ] ;
92-
93- let now = if let Some ( now) = state. last_updated_at ( ) {
94- now
95- } else {
96- // If we have never gotten an update yet, skip...
97- return ;
98- } ;
99-
10084 const STATE_LEN : u16 = List :: HEADER [ 1 ] . len ( ) as u16 ;
10185 const DUR_LEN : usize = 10 ;
10286 // This data is only updated every second, so it doesn't make a ton of
@@ -105,6 +89,13 @@ impl List {
10589 const DUR_PRECISION : usize = 4 ;
10690 const POLLS_LEN : usize = 5 ;
10791
92+ let now = if let Some ( now) = state. last_updated_at ( ) {
93+ now
94+ } else {
95+ // If we have never gotten an update yet, skip...
96+ return ;
97+ } ;
98+
10899 self . sorted_tasks . extend ( state. take_new_tasks ( ) ) ;
109100 self . sort_by . sort ( now, & mut self . sorted_tasks ) ;
110101
@@ -123,17 +114,36 @@ impl List {
123114 let mut target_width = view:: Width :: new ( Self :: HEADER [ 7 ] . len ( ) as u16 ) ;
124115 let mut num_idle = 0 ;
125116 let mut num_running = 0 ;
117+ let mut warnings = Vec :: new ( ) ;
126118 let rows = {
127119 let id_width = & mut id_width;
128120 let target_width = & mut target_width;
129121 let name_width = & mut name_width;
130122 let num_running = & mut num_running;
131123 let num_idle = & mut num_idle;
124+ let warnings = & mut warnings;
125+
126+ let linters = & self . linters ;
132127 self . sorted_tasks . iter ( ) . filter_map ( move |task| {
133128 let task = task. upgrade ( ) ?;
134129 let task = task. borrow ( ) ;
135130 let state = task. state ( ) ;
136-
131+ warnings. extend ( linters. iter ( ) . filter_map ( |warning| {
132+ let warning = warning. check ( & * task) ?;
133+ let task = if let Some ( name) = task. name ( ) {
134+ Span :: from ( format ! ( "Task '{}' (ID {}) " , name, task. id( ) ) )
135+ } else {
136+ Span :: from ( format ! ( "Task ID {} " , task. id( ) ) )
137+ } ;
138+ Some ( Spans :: from ( vec ! [
139+ Span :: styled(
140+ styles. if_utf8( "\u{26A0} " , "/!\\ " ) ,
141+ styles. fg( Color :: LightYellow ) ,
142+ ) ,
143+ task,
144+ Span :: from( warning) ,
145+ ] ) )
146+ } ) ) ;
137147 // Count task states
138148 match state {
139149 TaskState :: Running => * num_running += 1 ,
@@ -215,6 +225,33 @@ impl List {
215225 + POLLS_LEN as u16
216226 + target_width.chars();
217227 */
228+ let layout = layout:: Layout :: default ( )
229+ . direction ( layout:: Direction :: Vertical )
230+ . margin ( 0 ) ;
231+ let ( controls_area, tasks_area, warnings_area) = if warnings. is_empty ( ) {
232+ let chunks = layout
233+ . constraints (
234+ [
235+ layout:: Constraint :: Length ( 1 ) ,
236+ layout:: Constraint :: Min ( area. height - 1 ) ,
237+ ]
238+ . as_ref ( ) ,
239+ )
240+ . split ( area) ;
241+ ( chunks[ 0 ] , chunks[ 1 ] , None )
242+ } else {
243+ let chunks = layout
244+ . constraints (
245+ [
246+ layout:: Constraint :: Length ( 1 ) ,
247+ layout:: Constraint :: Length ( warnings. len ( ) as u16 + 2 ) ,
248+ layout:: Constraint :: Min ( area. height - 1 ) ,
249+ ]
250+ . as_ref ( ) ,
251+ )
252+ . split ( area) ;
253+ ( chunks[ 0 ] , chunks[ 2 ] , Some ( chunks[ 1 ] ) )
254+ } ;
218255
219256 // Fill all remaining characters in the frame with the task's fields.
220257 //
@@ -260,6 +297,14 @@ impl List {
260297
261298 frame. render_widget ( Paragraph :: new ( controls) , controls_area) ;
262299
300+ if let Some ( area) = warnings_area {
301+ let block = styles. border_block ( ) . title ( Spans :: from ( vec ! [
302+ bold( "Warnings" ) ,
303+ Span :: from( format!( " ({})" , warnings. len( ) ) ) ,
304+ ] ) ) ;
305+ frame. render_widget ( Paragraph :: new ( warnings) . block ( block) , area) ;
306+ }
307+
263308 self . sorted_tasks . retain ( |t| t. upgrade ( ) . is_some ( ) ) ;
264309 }
265310
@@ -317,3 +362,16 @@ impl List {
317362 . unwrap_or_default ( )
318363 }
319364}
365+
366+ impl Default for List {
367+ fn default ( ) -> Self {
368+ Self {
369+ linters : vec ! [ Box :: new( warnings:: SelfWakePercent :: default ( ) ) ] ,
370+ sorted_tasks : Vec :: new ( ) ,
371+ sort_by : tasks:: SortBy :: default ( ) ,
372+ table_state : TableState :: default ( ) ,
373+ selected_column : 0 ,
374+ sort_descending : false ,
375+ }
376+ }
377+ }
0 commit comments