-
-
Notifications
You must be signed in to change notification settings - Fork 31
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Feature Request: Code Lens (LSP 3.17)
Description
Implement textDocument/codeLens capability to show inline, actionable information above code elements without editing the document.
Use Cases
Account Statistics Above Account Opens
Display usage statistics and current balance:
💰 152 transactions | Balance: $5,234.56 USD | ▶ Show details ← Code lens
2020-01-01 open Assets:Bank:Checking
Transaction Validation Status
Show balance status for transactions:
✓ Balanced | 2 postings | ▶ View postings ← Code lens
2024-01-15 * "Grocery Store"
Expenses:Food:Groceries 45.23 USD
Assets:Bank:Checking -45.23 USD
Or for unbalanced:
⚠ Unbalanced: -0.01 USD | ▶ Fix automatically ← Code lens
2024-01-15 * "Restaurant"
Expenses:Food:Dining 52.34 USD
Assets:Bank:Checking -52.33 USD ; Missing 0.01 USD
Account Usage Summary
Show where account is used:
📊 Used in 45 transactions | Last: 2024-03-15 | ▶ Show all ← Code lens
2020-01-01 open Expenses:Food:Groceries
Commodity Price Information
Display latest price and changes:
💵 Latest: 1 AAPL = $150.25 USD (+2.3%) | ▶ Price history ← Code lens
2020-01-01 commodity AAPL
Balance Assertion Verification
Show actual vs expected:
✓ Verified: Actual $1,000.00 matches expected | As of 2024-01-15 ← Code lens
2024-01-15 balance Assets:Bank:Checking 1000.00 USD
Or when failing:
❌ Failed: Actual $995.50 ≠ Expected $1,000.00 | Diff: -$4.50 ← Code lens
2024-01-15 balance Assets:Bank:Checking 1000.00 USD
Implementation Details
File: crates/lsp/src/providers/code_lens.rs
Pattern:
pub fn code_lens(
snapshot: LspServerStateSnapshot,
params: CodeLensParams,
) -> Result<Option<Vec<CodeLens>>> {
let tree = snapshot.forest.get(¶ms.text_document.uri)?;
let mut lenses = vec![];
let mut cursor = tree.root_node().walk();
for child in tree.root_node().children(&mut cursor) {
match child.kind() {
"open" => {
let account = extract_account_name(&child);
let stats = calculate_account_stats(&account, &snapshot);
lenses.push(CodeLens {
range: node_to_range(&child),
command: Some(Command {
title: format!(
"💰 {} txns | {} | ▶ Show details",
stats.transaction_count,
stats.current_balance
),
command: "beancount.showAccountDetails".to_string(),
arguments: Some(vec![json!(account)]),
}),
data: None,
});
}
"txn" => {
let balance_status = check_transaction_balance(&child);
let icon = if balance_status.is_balanced { "✓" } else { "⚠" };
let action = if balance_status.is_balanced {
"View postings"
} else {
"Fix automatically"
};
lenses.push(CodeLens {
range: node_to_range(&child),
command: Some(Command {
title: format!(
"{} {} | {} postings | ▶ {}",
icon,
balance_status.message,
balance_status.posting_count,
action
),
command: if balance_status.is_balanced {
"beancount.viewPostings".to_string()
} else {
"beancount.fixTransaction".to_string()
},
arguments: Some(vec![json!(child.id())]),
}),
data: None,
});
}
"balance" => {
let assertion = extract_balance_assertion(&child);
let verification = verify_balance(&assertion, &snapshot);
lenses.push(CodeLens {
range: node_to_range(&child),
command: Some(Command {
title: format!(
"{} {} | As of {}",
if verification.matches { "✓ Verified" } else { "❌ Failed" },
verification.message,
assertion.date
),
command: "beancount.showBalanceDetails".to_string(),
arguments: Some(vec![json!(assertion)]),
}),
data: None,
});
}
"commodity" => {
let commodity = extract_commodity_name(&child);
if let Some(price_info) = get_latest_price(&commodity, &snapshot) {
lenses.push(CodeLens {
range: node_to_range(&child),
command: Some(Command {
title: format!(
"💵 Latest: {} | ▶ Price history",
price_info.display()
),
command: "beancount.showPriceHistory".to_string(),
arguments: Some(vec![json!(commodity)]),
}),
data: None,
});
}
}
_ => {}
}
}
Ok(Some(lenses))
}
struct AccountStats {
transaction_count: usize,
current_balance: String,
last_transaction_date: Option<NaiveDate>,
}
fn calculate_account_stats(account: &str, snapshot: &LspServerStateSnapshot) -> AccountStats {
// Query beancount data or bean-query for statistics
}Capability Registration
Update crates/lsp/src/capabilities.rs:
code_lens_provider: Some(CodeLensOptions {
resolve_provider: Some(false),
..Default::default()
}),Commands to Implement
The code lenses reference these commands (to be registered):
beancount.showAccountDetails- Open panel with account transactionsbeancount.viewPostings- Show posting details for transactionbeancount.fixTransaction- Auto-balance transaction (triggers code action)beancount.showBalanceDetails- Display balance verification detailsbeancount.showPriceHistory- Show commodity price chart/history
Priority
MEDIUM - Provides valuable at-a-glance information without cluttering the editor
Configuration Options (Future)
{
"beancountLanguageServer": {
"codeLens": {
"enableAccountStats": true,
"enableTransactionStatus": true,
"enableBalanceVerification": true,
"enableCommodityPrices": true,
"showIcons": true,
"refreshIntervalSeconds": 60
}
}
}Performance Considerations
- Lazy calculation: Only calculate stats for visible lenses
- Caching: Cache account statistics, refresh periodically
- Debouncing: Don't recalculate on every keystroke
- Incremental updates: Only update affected lenses when document changes
- Background computation: Calculate expensive stats (balance queries) asynchronously
User Experience
- Lenses should be clickable and provide immediate actions
- Keep text concise (one line)
- Use icons sparingly for visual scanning
- Consider user preference to disable certain lens types
- Allow hiding/showing code lenses via editor setting
Dependencies
- May need to call
bean-queryfor balance calculations - Requires commodity price database access
- Consider maintaining cache of account statistics
- May integrate with code action provider (Add Code Actions support (textDocument/codeAction) #749)
Enhancement Ideas
- Budget tracking: Show budget vs actual spending for expense accounts
- Account hierarchy: Show total including subaccounts
- Time-based stats: "This month: +$1,234.56" for income/expense accounts
- Tags/links summary: "Used in 23 transactions" for tags
- Net worth snapshot: Show total across all asset/liability accounts
References
- LSP 3.17 Code Lens specification
- VSCode Code Lens examples (GitLens is excellent reference)
- Related: Add Hover support (textDocument/hover) #746 (Hover), Add Code Actions support (textDocument/codeAction) #749 (Code Actions), Add Inlay Hints support (textDocument/inlayHint) - LSP 3.17 #750 (Inlay Hints)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request