From f5f66d24b51d5d2169989e226dfcaa6eab5fa59b Mon Sep 17 00:00:00 2001 From: Mathieu Schopfer Date: Sun, 6 Oct 2019 21:58:17 +0200 Subject: [PATCH 1/2] Added possibility to complete whole transactions --- package.json | 5 +++++ pythonFiles/beancheck.py | 17 +++++++++++++++-- src/completer.ts | 40 +++++++++++++++++++++++++++++++++++++++- src/extension.ts | 8 +++++--- 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 87b8378..9e5cfb6 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,11 @@ "default": true, "description": "Controls whether the auto completion list should include payee and narration fields." }, + "beancount.completeTransaction": { + "type": "boolean", + "default": true, + "description": "Controls whether the auto completion list should include whole transactions." + }, "beancount.mainBeanFile": { "type": "string", "default": "", diff --git a/pythonFiles/beancheck.py b/pythonFiles/beancheck.py index c4ec800..2776a98 100644 --- a/pythonFiles/beancheck.py +++ b/pythonFiles/beancheck.py @@ -6,6 +6,7 @@ from beancount.core.data import Transaction, Open, Close from beancount.core.display_context import Align from beancount.core.realization import dump_balances, realize +from beancount.parser.printer import format_entry import io import json @@ -34,6 +35,7 @@ def get_flag_metadata(thing): entries, errors, options = loader.load_file(argv[1]) completePayeeNarration = "--payeeNarration" in argv +completeTransaction = "--transaction" in argv error_list = [{"file": e.source['filename'], "line": e.source['lineno'], "message": e.message} for e in errors] @@ -42,6 +44,7 @@ def get_flag_metadata(thing): commodities = set() payees = set() narrations = set() +transactions = {} tags = set() links = set() flagged_entries = [] @@ -51,16 +54,25 @@ def get_flag_metadata(thing): flagged_entries.append(get_flag_metadata(entry)) if isinstance(entry, Transaction): if completePayeeNarration: - payees.add(f'{entry.payee}') + payees.add(str(entry.payee)) if not entry.narration.startswith("(Padding inserted"): if completePayeeNarration: - narrations.add(f'{entry.narration}') + narrations.add(str(entry.narration)) tags.update(entry.tags) links.update(entry.links) for posting in entry.postings: commodities.add(posting.units.currency) if hasattr(posting, 'flag') and posting.flag == "!": flagged_entries.append(get_flag_metadata(posting)) + if completeTransaction: + # Add transaction to dict (replace if already exists, i.e. newest transaction wins) + if entry.payee: + transaction_key = ' '.join([entry.flag, entry.payee, entry.narration]) + else: + transaction_key = ' '.join([entry.flag, entry.narration]) + transaction_value = format_entry(entry) + transaction_value = transaction_value.lstrip(entry.date.__str__()).lstrip() + transactions[transaction_key] = transaction_value elif isinstance(entry, Open): accounts[entry.account] = { 'open': entry.date.__str__(), @@ -97,6 +109,7 @@ def get_flag_metadata(thing): output['commodities'] = list(commodities) output['payees'] = list(payees) output['narrations'] = list(narrations) +output['transactions'] = transactions output['tags'] = list(tags) output['links'] = list(links) diff --git a/src/completer.ts b/src/completer.ts index b1f4802..19c70d0 100644 --- a/src/completer.ts +++ b/src/completer.ts @@ -25,6 +25,7 @@ interface CompletionData { commodities: string[]; payees: string[]; narrations: string[]; + transactions: { [key: string]: string }; tags: string[]; links: string[]; } @@ -36,6 +37,7 @@ export class Completer payees: string[]; narrations: string[]; commodities: string[]; + transactions: { [key: string]: string }; tags: string[]; links: string[]; wordPattern: RegExp; @@ -47,6 +49,7 @@ export class Completer this.payees = []; this.narrations = []; this.commodities = []; + this.transactions = {}; this.tags = []; this.links = []; this.wordPattern = new RegExp('[A-Za-z:]+\\S+|"([^\\\\"]|\\\\")*"'); @@ -69,6 +72,7 @@ export class Completer this.commodities = data.commodities; this.payees = data.payees; this.narrations = data.narrations; + this.transactions = data.transactions; this.tags = data.tags; this.links = data.links; } @@ -193,6 +197,7 @@ export class Completer countOccurrences(textBefore, /\"/g) - countOccurrences(textBefore, /\\"/g); if (r != null && numQuotes % 2 === 1) { + const insertItemWithLetters = ( list: CompletionItem[], text: string, @@ -215,6 +220,7 @@ export class Completer list.push(new CompletionItem(text, kind)); } }; + const list: CompletionItem[] = []; if (numQuotes === 1) { this.payees.forEach((payee, i, a) => { @@ -271,9 +277,41 @@ export class Completer }); resolve(list); return; + } else if ( + vscode.workspace.getConfiguration('beancount')['completeTransaction'] && + textBefore.match(/([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))/) + ) { // Match a date at the string beginning + + const instertTransactionItem = (list: CompletionItem[], key: string, transactionText: string) => { + let findOne = false; + const completionItemKind = CompletionItemKind.Value; + + for (const inputMethod of this.inputMethods) { + const letters = inputMethod.getLetterRepresentation(key); + if (letters.length > 0) { + findOne = true; + const item = new CompletionItem( + letters + '(' + key + ')', + completionItemKind + ); + item.insertText = transactionText; + list.push(item); + } + } + if (!findOne) { + const item = new CompletionItem(key, completionItemKind); + item.insertText = transactionText; + list.push(item); + } + }; + + const list: CompletionItem[] = []; + for (const key in this.transactions) { + instertTransactionItem(list, key, this.transactions[key]); + } + resolve(list); } } - resolve([]); }); } } diff --git a/src/extension.ts b/src/extension.ts index 76b5251..83493d2 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -150,12 +150,14 @@ export class Extension { this.logger.appendLine('find no valid bean files.'); return; } + const extConfig = vscode.workspace.getConfiguration('beancount'); const pyArgs = [checkpy, mainBeanFile]; - if ( - vscode.workspace.getConfiguration('beancount')['completePayeeNarration'] - ) { + if (extConfig['completePayeeNarration']) { pyArgs.push('--payeeNarration'); } + if (extConfig['completeTransaction']) { + pyArgs.push('--transaction'); + } this.logger.appendLine( `running ${python3Path} ${pyArgs} to refresh data...` ); From c295886e77072c679a15ec70e41b8c8070a32263 Mon Sep 17 00:00:00 2001 From: Mathieu Schopfer Date: Wed, 16 Oct 2019 18:35:49 +0200 Subject: [PATCH 2/2] Added year and date and partial date (year+month) of last transaction to date autocompletion --- pythonFiles/beancheck.py | 4 ++++ src/completer.ts | 34 ++++++++++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/pythonFiles/beancheck.py b/pythonFiles/beancheck.py index 2776a98..8bf551a 100644 --- a/pythonFiles/beancheck.py +++ b/pythonFiles/beancheck.py @@ -1,5 +1,6 @@ ''' load beancount file and print errors ''' +from datetime import date from sys import argv from beancount import loader from beancount.core import flags @@ -45,6 +46,7 @@ def get_flag_metadata(thing): payees = set() narrations = set() transactions = {} +lastTransactionDate = date.fromtimestamp(0) tags = set() links = set() flagged_entries = [] @@ -53,6 +55,7 @@ def get_flag_metadata(thing): if hasattr(entry, 'flag') and entry.flag == "!": flagged_entries.append(get_flag_metadata(entry)) if isinstance(entry, Transaction): + lastTransactionDate = entry.date if entry.date > lastTransactionDate else lastTransactionDate if completePayeeNarration: payees.add(str(entry.payee)) if not entry.narration.startswith("(Padding inserted"): @@ -110,6 +113,7 @@ def get_flag_metadata(thing): output['payees'] = list(payees) output['narrations'] = list(narrations) output['transactions'] = transactions +output['lastTransactionDate'] = lastTransactionDate.isoformat() output['tags'] = list(tags) output['links'] = list(links) diff --git a/src/completer.ts b/src/completer.ts index 19c70d0..ba8708e 100644 --- a/src/completer.ts +++ b/src/completer.ts @@ -26,6 +26,7 @@ interface CompletionData { payees: string[]; narrations: string[]; transactions: { [key: string]: string }; + lastTransactionDate: string; tags: string[]; links: string[]; } @@ -38,6 +39,7 @@ export class Completer narrations: string[]; commodities: string[]; transactions: { [key: string]: string }; + lastTransactionDate: string; tags: string[]; links: string[]; wordPattern: RegExp; @@ -50,6 +52,7 @@ export class Completer this.narrations = []; this.commodities = []; this.transactions = {}; + this.lastTransactionDate = ''; this.tags = []; this.links = []; this.wordPattern = new RegExp('[A-Za-z:]+\\S+|"([^\\\\"]|\\\\")*"'); @@ -73,6 +76,7 @@ export class Completer this.payees = data.payees; this.narrations = data.narrations; this.transactions = data.transactions; + this.lastTransactionDate = data.lastTransactionDate; this.tags = data.tags; this.links = data.links; } @@ -180,13 +184,35 @@ export class Completer (today.getMonth() + 1).toString(); const date = (today.getDate() < 10 ? '0' : '') + today.getDate().toString(); - const dateString = year + '-' + month + '-' + date; + + const dateStringToday = year + '-' + month + '-' + date; const itemToday = new CompletionItem( - dateString, + dateStringToday, + CompletionItemKind.Event + ); + itemToday.documentation = 'Today'; + + const dateStringYear = year + '-'; + const itemYear = new CompletionItem( + dateStringYear, CompletionItemKind.Event ); - itemToday.documentation = 'today'; - resolve([itemToday]); + itemYear.documentation = 'This year'; + + const dateStringLastTransactionDate = this.lastTransactionDate; + const itemLastTransactionDate = new CompletionItem( + dateStringLastTransactionDate, + CompletionItemKind.Event + ); + itemLastTransactionDate.documentation = 'Last transaction'; + + const dateStringLastTransactionMonth = this.lastTransactionDate.slice(0, -2); + const itemLastTransactionMonth = new CompletionItem( + dateStringLastTransactionMonth, + CompletionItemKind.Event + ); + + resolve([itemToday, itemYear, itemLastTransactionDate, itemLastTransactionMonth]); return; } else if ( triggerCharacter === '"' &&