Skip to content

Commit 3061291

Browse files
committed
Add brace matching and fix property-argument highlighting for complex scenarios
- Add new brace matching provider for Serilog templates with multi-line support. - Fix property-argument highlighting when LogError has Exception as first parameter - Exclude string literals from parenthesis counting in multi-line detection - Find template string by properties instead of assuming first string literal - Add test coverage for both features
1 parent ac8f65c commit 3061291

File tree

9 files changed

+1154
-381
lines changed

9 files changed

+1154
-381
lines changed

CLAUDE.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ The extension follows a modular architecture designed for performance and mainta
2323
2. **Providers** (`src/providers/`) ✅
2424
- `navigationProvider.ts` - Code actions for navigating from properties to arguments
2525
- `propertyArgumentHighlighter.ts` - Highlights template properties and their corresponding arguments
26+
- `braceMatchProvider.ts` - Highlights matching braces in Serilog contexts
2627

2728
3. **Decorations** (`src/decorations/`) ✅
2829
- `decorationManager.ts` - Manages text decorations for highlighting (replaces semantic tokens)
@@ -90,6 +91,7 @@ vsce publish
9091
- **Configuration**: Extensive user customization options
9192
- **Property-Argument Highlighting**: Cursor-based highlighting showing connection between properties and arguments
9293
- **Navigation Provider**: Code actions to jump from properties to their arguments
94+
- **Brace Matching**: Highlights matching braces in all Serilog contexts (templates, expressions, output templates)
9395

9496
### 🚫 Not Implemented (by design)
9597
- **Semantic Tokens Provider**: Replaced with decoration-based approach for better control

Example/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ Open `Program.cs` in Visual Studio with the Serilog Syntax extension installed t
9494
Colors automatically adapt for light themes with WCAG AA compliant contrast ratios - darker variants of the same color families for optimal readability on light backgrounds.
9595

9696
### Interactive Features
97+
- **Brace matching** when cursor is on `{` or `}` in any Serilog context
9798
- **Property-Argument highlighting** when cursor is on a property or argument
9899
- **Light bulb navigation** from properties to arguments
99100
- **Immediate highlighting** as you type (before closing quotes)

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,19 @@ A Visual Studio Code extension that provides syntax highlighting and enhanced su
4747
- **Navigate to argument** - jump from template properties to their corresponding arguments
4848
- Click the light bulb and select "Navigate to 'PropertyName' argument"
4949

50+
### 🔍 Brace Matching
51+
- **Highlight matching braces** when cursor is positioned on `{` or `}`
52+
- Works in all Serilog contexts: message templates, output templates, and Serilog.Expressions
53+
- **Multi-line support** - matches braces across line boundaries in verbatim and raw strings
54+
- Uses colored, bold braces for visibility without interfering with other highlighting
55+
5056
### 🎯 Property-Argument Highlighting
5157
- **Synchronous highlighting** of template properties and their corresponding arguments
5258
- Position cursor on a property like `{UserId}` to highlight both the property (including braces) and its argument
5359
- Position cursor on an argument to highlight it and its template property
5460
- Includes quotes when highlighting string arguments (e.g., `"userId"`)
5561
- Helps visualize the connection between template properties and their values
62+
- Uses background fill to distinguish from brace matching
5663

5764
## Installation
5865

src/extension.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { Debouncer } from './utils/debouncer';
99
import { ThemeManager } from './utils/themeManager';
1010
import { SerilogNavigationProvider, registerNavigationCommand } from './providers/navigationProvider';
1111
import { PropertyArgumentHighlighter } from './providers/propertyArgumentHighlighter';
12+
import { SerilogBraceMatchProvider } from './providers/braceMatchProvider';
1213

1314
export function activate(context: vscode.ExtensionContext) {
1415
// Create output channel for logging
@@ -59,6 +60,10 @@ export function activate(context: vscode.ExtensionContext) {
5960
const propertyArgumentHighlighter = new PropertyArgumentHighlighter();
6061
context.subscriptions.push(propertyArgumentHighlighter);
6162

63+
// Initialize brace match provider
64+
const braceMatchProvider = new SerilogBraceMatchProvider();
65+
context.subscriptions.push(braceMatchProvider);
66+
6267
function updateDecorations() {
6368
const config = vscode.workspace.getConfiguration('serilog');
6469
const enabled = config.get<boolean>('enabled', true);
@@ -379,13 +384,15 @@ export function activate(context: vscode.ExtensionContext) {
379384
// Listen for cursor position changes to update brace matching
380385
vscode.window.onDidChangeTextEditorSelection(event => {
381386
if (event.textEditor === vscode.window.activeTextEditor) {
387+
braceMatchProvider.updateBraceMatching(event.textEditor);
382388
}
383389
}, null, context.subscriptions);
384390

385391
// Listen for active editor changes
386392
vscode.window.onDidChangeActiveTextEditor(editor => {
387393
if (editor) {
388394
updateDecorations();
395+
braceMatchProvider.updateBraceMatching(editor);
389396
}
390397
}, null, context.subscriptions);
391398

src/parsers/templateParser.ts

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,44 @@ export interface TemplateProperty {
99

1010
export function parseTemplate(template: string): TemplateProperty[] {
1111
const properties: TemplateProperty[] = [];
12+
1213
// First remove escaped braces from consideration
1314
const cleanedTemplate = template.replace(/\{\{|\}\}/g, ' ');
14-
const regex = /\{([@$])?([A-Za-z_][A-Za-z0-9_]*|\d+)(,([+-]?\d+))?(:[^}]+)?\}/g;
15+
16+
const regex = /\{([@$#])?([A-Za-z_][A-Za-z0-9_]*|\d+)(,([+-]?\d+))?(:[^}]+)?\}/g;
1517
let match;
1618

1719
while ((match = regex.exec(cleanedTemplate)) !== null) {
20+
const prefix = match[1];
21+
const name = match[2];
22+
23+
// Skip expression directives (start with #)
24+
if (prefix === '#') {
25+
continue;
26+
}
27+
28+
// Skip expression built-ins (@t, @m, @l, @x, @i, @p)
29+
// BUT ONLY for property-argument highlighting purposes
30+
// Brace matching should still work on these!
31+
if (prefix === '@' && ['t', 'm', 'l', 'x', 'i', 'p'].includes(name)) {
32+
continue;
33+
}
34+
35+
// Skip expression variables ($x, $y, etc. - single letter)
36+
if (prefix === '$' && name.length === 1) {
37+
continue;
38+
}
39+
1840
const property: TemplateProperty = {
19-
name: match[2],
41+
name: name,
2042
startIndex: match.index,
2143
endIndex: match.index + match[0].length,
2244
type: 'standard'
2345
};
2446

25-
if (match[1] === '@') property.type = 'destructured';
26-
else if (match[1] === '$') property.type = 'stringified';
27-
else if (/^\d+$/.test(match[2])) property.type = 'positional';
47+
if (prefix === '@') property.type = 'destructured';
48+
else if (prefix === '$') property.type = 'stringified';
49+
else if (/^\d+$/.test(name)) property.type = 'positional';
2850

2951
if (match[4]) property.alignment = match[4];
3052
if (match[5]) property.formatSpecifier = match[5].substring(1);

0 commit comments

Comments
 (0)