@@ -17,6 +17,7 @@ pub struct FlaggedEntry {
1717#[ derive( Clone , Debug ) ]
1818pub struct BeancountData {
1919 accounts : Vec < String > ,
20+ payees : Vec < String > ,
2021 narration : Vec < String > ,
2122 pub flagged_entries : Vec < FlaggedEntry > ,
2223 tags : Vec < String > ,
@@ -27,6 +28,7 @@ pub struct BeancountData {
2728impl BeancountData {
2829 pub fn new ( tree : & tree_sitter:: Tree , content : & ropey:: Rope ) -> Self {
2930 let mut accounts = vec ! [ ] ;
31+ let mut payees = vec ! [ ] ;
3032 let mut narration = vec ! [ ] ;
3133 let mut flagged_entries = vec ! [ ] ;
3234
@@ -55,17 +57,41 @@ impl BeancountData {
5557 accounts. push ( account) ;
5658 }
5759
58- // Update narration with frequency tracking
59- tracing:: debug!( "beancount_data:: get narration nodes" ) ;
60+ // Update payees and narration with frequency tracking
61+ tracing:: debug!( "beancount_data:: get payee and narration nodes" ) ;
6062 let transactions = tree
6163 . root_node ( )
6264 . children ( & mut cursor)
6365 . filter ( |c| c. kind ( ) == "transaction" )
6466 . collect :: < Vec < _ > > ( ) ;
6567
68+ let mut payee_count: std:: collections:: HashMap < String , usize > =
69+ std:: collections:: HashMap :: new ( ) ;
6670 let mut narration_count: std:: collections:: HashMap < String , usize > =
6771 std:: collections:: HashMap :: new ( ) ;
6872 for transaction in transactions {
73+ // When there's a payee field (two strings), use it
74+ if let Some ( payee_node) = transaction. child_by_field_name ( "payee" ) {
75+ let payee_text = text_for_tree_sitter_node ( content, & payee_node)
76+ . trim ( )
77+ . to_string ( ) ;
78+ if !payee_text. is_empty ( ) {
79+ * payee_count. entry ( payee_text) . or_insert ( 0 ) += 1 ;
80+ }
81+ }
82+ // When there's only narration (one string), also add it to payees
83+ // since semantically it often represents the payee
84+ else if let Some ( narration_node) = transaction. child_by_field_name ( "narration" ) {
85+ let narration_text = text_for_tree_sitter_node ( content, & narration_node)
86+ . trim ( )
87+ . to_string ( ) ;
88+ if !narration_text. is_empty ( ) {
89+ // Add single-string transactions to payees for completion
90+ * payee_count. entry ( narration_text. clone ( ) ) . or_insert ( 0 ) += 1 ;
91+ }
92+ }
93+
94+ // Always collect narration for narration completions
6995 if let Some ( narration_node) = transaction. child_by_field_name ( "narration" ) {
7096 let narration_text = text_for_tree_sitter_node ( content, & narration_node)
7197 . trim ( )
@@ -76,6 +102,14 @@ impl BeancountData {
76102 }
77103 }
78104
105+ tracing:: debug!( "beancount_data:: update payees" ) ;
106+ payees. clear ( ) ;
107+
108+ // Sort by frequency (most used first), then alphabetically
109+ let mut payee_vec: Vec < ( String , usize ) > = payee_count. into_iter ( ) . collect ( ) ;
110+ payee_vec. sort_by ( |a, b| b. 1 . cmp ( & a. 1 ) . then_with ( || a. 0 . cmp ( & b. 0 ) ) ) ;
111+ payees = payee_vec. into_iter ( ) . map ( |( text, _) | text) . collect ( ) ;
112+
79113 tracing:: debug!( "beancount_data:: update narration" ) ;
80114 narration. clear ( ) ;
81115
@@ -235,6 +269,7 @@ impl BeancountData {
235269
236270 Self {
237271 accounts,
272+ payees,
238273 narration,
239274 flagged_entries,
240275 tags,
@@ -247,6 +282,10 @@ impl BeancountData {
247282 self . accounts . clone ( )
248283 }
249284
285+ pub fn get_payees ( & self ) -> Vec < String > {
286+ self . payees . clone ( )
287+ }
288+
250289 pub fn get_narration ( & self ) -> Vec < String > {
251290 self . narration . clone ( )
252291 }
0 commit comments