feat: add live debug inline values for ST/IL editor#639
Conversation
Display real-time debug variable values as green inline badges next to variable occurrences in the Structured Text and Instruction List editors, matching the existing debug visualization in FBD/LD graphical editors. - Strip ST comment regions (//, (* *), /* */) before scanning to avoid decorating variables inside comments - Match complex variable expressions (FB members like TON0.Q, array elements like my_array[3]) by reading keys from debugVariableValues - Sort expressions longest-first with overlap detection to prevent partial matches (e.g. TON0 inside TON0.Q) - Set editor to read-only during active debug sessions - Support function-block instance context for composite key resolution Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
WalkthroughAdds comment-aware parsing and inline debug-value decorations to the Monaco editor, wires editor readOnly to debugger visibility, resolves fbInstanceContext and debugVariableValues for matching, and adds CSS for debug-value badges. Changes
Sequence DiagramsequenceDiagram
participant Editor as Monaco Editor
participant Debugger as Debug Context
participant Parser as Line Parser
participant Matcher as Pattern Matcher
participant Renderer as Decoration Renderer
Editor->>Debugger: request fbInstanceContext & debugVariableValues
Debugger-->>Editor: return instance context & variable map
Editor->>Parser: for each line -> stripLineComments(preserve cols)
Parser-->>Editor: stripped line text
Editor->>Matcher: build regex patterns from debugVarKeySet
Matcher->>Matcher: match patterns (prioritize long names, avoid overlaps)
Matcher-->>Editor: matches with source ranges
Editor->>Renderer: create decorations (claimed ranges, value lookups)
Renderer-->>Editor: apply inline debug-value decorations
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderer/components/_features/`[workspace]/editor/monaco/index.tsx:
- Around line 71-105: The stripLineComments function incorrectly treats comment
markers inside string literals as comments; update stripLineComments to track a
stringLiteral state (e.g., false | "'" | '"' ) alongside BlockCommentState and
skip recognizing '//' , '(*', '*)', '/*', '*/' when inside a string, handling
escape/backslash so an escaped quote doesn't end the string; modify the while
loop logic in stripLineComments to toggle stringLiteral on unescaped quotes and
only enter or exit block/line comment logic when stringLiteral is false so
inline strings like 'http://...' are preserved.
In `@src/renderer/styles/globals.css`:
- Around line 198-200: The dark-mode inline debug badge (.dark
.oplc-monaco-wrapper .debug-inline-value) uses a too-light green; update its
background-color to a slightly darker, higher-contrast shade (for example
`#2e7d32` or a similar darker green) so 11px white text meets
accessibility/contrast and remains readable in dark mode.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (2)
src/renderer/components/_features/[workspace]/editor/monaco/index.tsxsrc/renderer/styles/globals.css
| function stripLineComments(line: string, state: BlockCommentState): { stripped: string; state: BlockCommentState } { | ||
| const chars = [...line] | ||
| let i = 0 | ||
| let s = state | ||
|
|
||
| while (i < chars.length) { | ||
| if (s) { | ||
| const endMarker = s === 'paren' ? ')' : '/' | ||
| if (chars[i] === '*' && chars[i + 1] === endMarker) { | ||
| chars[i] = ' ' | ||
| chars[i + 1] = ' ' | ||
| i += 2 | ||
| s = false | ||
| } else { | ||
| chars[i] = ' ' | ||
| i++ | ||
| } | ||
| } else { | ||
| if (chars[i] === '/' && chars[i + 1] === '/') { | ||
| for (let j = i; j < chars.length; j++) chars[j] = ' ' | ||
| break | ||
| } | ||
| if (chars[i] === '(' && chars[i + 1] === '*') { | ||
| chars[i] = ' ' | ||
| chars[i + 1] = ' ' | ||
| i += 2 | ||
| s = 'paren' | ||
| } else if (chars[i] === '/' && chars[i + 1] === '*') { | ||
| chars[i] = ' ' | ||
| chars[i + 1] = ' ' | ||
| i += 2 | ||
| s = 'slash' | ||
| } else { | ||
| i++ | ||
| } |
There was a problem hiding this comment.
Guard comment parsing against string literals.
Line 89, Line 93, and Line 98 currently treat comment markers as comments even when they appear inside ST/IL strings (e.g., 'http://...'), which can suppress valid inline decorations later on the same line.
💡 Suggested fix
function stripLineComments(line: string, state: BlockCommentState): { stripped: string; state: BlockCommentState } {
const chars = [...line]
let i = 0
let s = state
+ let inString = false
while (i < chars.length) {
if (s) {
const endMarker = s === 'paren' ? ')' : '/'
if (chars[i] === '*' && chars[i + 1] === endMarker) {
@@
} else {
+ if (chars[i] === "'") {
+ // ST escaped quote inside string: ''
+ if (inString && chars[i + 1] === "'") {
+ i += 2
+ continue
+ }
+ inString = !inString
+ i++
+ continue
+ }
+
+ if (inString) {
+ i++
+ continue
+ }
+
if (chars[i] === '/' && chars[i + 1] === '/') {
for (let j = i; j < chars.length; j++) chars[j] = ' '
break
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/components/_features/`[workspace]/editor/monaco/index.tsx around
lines 71 - 105, The stripLineComments function incorrectly treats comment
markers inside string literals as comments; update stripLineComments to track a
stringLiteral state (e.g., false | "'" | '"' ) alongside BlockCommentState and
skip recognizing '//' , '(*', '*)', '/*', '*/' when inside a string, handling
escape/backslash so an escaped quote doesn't end the string; modify the while
loop logic in stripLineComments to toggle stringLiteral on unescaped quotes and
only enter or exit block/line comment logic when stringLiteral is false so
inline strings like 'http://...' are preserved.
| .dark .oplc-monaco-wrapper .debug-inline-value { | ||
| background-color: #388e3c; | ||
| } |
There was a problem hiding this comment.
Increase dark-mode badge contrast for small text.
Line 199 uses a lighter green that can drop readability for 11px white text. A slightly darker shade would improve accessibility.
💡 Suggested tweak
.dark .oplc-monaco-wrapper .debug-inline-value {
- background-color: `#388e3c`;
+ background-color: `#2e7d32`;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| .dark .oplc-monaco-wrapper .debug-inline-value { | |
| background-color: #388e3c; | |
| } | |
| .dark .oplc-monaco-wrapper .debug-inline-value { | |
| background-color: `#2e7d32`; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/styles/globals.css` around lines 198 - 200, The dark-mode inline
debug badge (.dark .oplc-monaco-wrapper .debug-inline-value) uses a too-light
green; update its background-color to a slightly darker, higher-contrast shade
(for example `#2e7d32` or a similar darker green) so 11px white text meets
accessibility/contrast and remains readable in dark mode.
Split the single useEffect into a position-scanning useMemo (runs once when the watched variable set changes) and a value-stamping useEffect (runs on each 50ms poll with O(n) map lookups only). The editor is read-only during debug so positions are stable and don't need rescanning. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
src/renderer/components/_features/[workspace]/editor/monaco/index.tsx (1)
71-106:⚠️ Potential issue | 🟠 MajorString literals are still parsed as comments in
stripLineComments.This still misclassifies markers like
//,(*, or/*when they appear inside ST/IL string literals, which can hide valid debug decorations on that line.Proposed fix
type BlockCommentState = false | 'paren' | 'slash' function stripLineComments(line: string, state: BlockCommentState): { stripped: string; state: BlockCommentState } { const chars = [...line] let i = 0 let s = state + let inString: false | "'" | '"' = false while (i < chars.length) { if (s) { const endMarker = s === 'paren' ? ')' : '/' if (chars[i] === '*' && chars[i + 1] === endMarker) { @@ } else { chars[i] = ' ' i++ } } else { + const ch = chars[i] + if ((ch === "'" || ch === '"') && !inString) { + inString = ch + i++ + continue + } + if (inString) { + // ST escaped single quote: '' + if (inString === "'" && chars[i] === "'" && chars[i + 1] === "'") { + i += 2 + continue + } + if (chars[i] === inString) inString = false + i++ + continue + } + if (chars[i] === '/' && chars[i + 1] === '/') { for (let j = i; j < chars.length; j++) chars[j] = ' ' break }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/renderer/components/_features/`[workspace]/editor/monaco/index.tsx around lines 71 - 106, stripLineComments currently treats comment markers inside string literals as real comments; update stripLineComments to track string literal state (e.g., a inString flag or current quote char) alongside the BlockCommentState (s) and ignore sequences like //, (*, /* when inside a string, handling escaped quotes correctly so you don't exit the string on an escaped quote; modify the loop that scans chars (used in stripLineComments) to toggle the string state when encountering opening/closing quotes and to skip comment-detection logic while inString, preserving existing block-comment handling when not in a string.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/renderer/components/_features/`[workspace]/editor/monaco/index.tsx:
- Around line 347-354: The loop that finds matches for each expression stops
after the first match due to the break, causing only one badge per expression
per line; remove the break inside the while ((match =
pattern.exec(result.stripped)) !== null) loop so the code continues scanning for
additional non-overlapping matches (respecting the claimed array check) and
pushes multiple positions for the same expr on the same line (keep using
claimed, positions.push, startCol/endCol, and lineNumber as-is).
---
Duplicate comments:
In `@src/renderer/components/_features/`[workspace]/editor/monaco/index.tsx:
- Around line 71-106: stripLineComments currently treats comment markers inside
string literals as real comments; update stripLineComments to track string
literal state (e.g., a inString flag or current quote char) alongside the
BlockCommentState (s) and ignore sequences like //, (*, /* when inside a string,
handling escaped quotes correctly so you don't exit the string on an escaped
quote; modify the loop that scans chars (used in stripLineComments) to toggle
the string state when encountering opening/closing quotes and to skip
comment-detection logic while inString, preserving existing block-comment
handling when not in a string.
ℹ️ Review info
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Disabled knowledge base sources:
- Linear integration is disabled
You can enable these sources in your CodeRabbit configuration.
📒 Files selected for processing (1)
src/renderer/components/_features/[workspace]/editor/monaco/index.tsx
| while ((match = pattern.exec(result.stripped)) !== null) { | ||
| const startCol = match.index + 1 | ||
| const endCol = startCol + match[0].length | ||
| if (claimed.some(([s, e]) => startCol < e && endCol > s)) continue | ||
| claimed.push([startCol, endCol]) | ||
| positions.push({ expr, line: lineNumber, startCol, endCol }) | ||
| break // Only first occurrence per expression per line | ||
| } |
There was a problem hiding this comment.
Only the first same-expression occurrence per line gets a badge.
break at Line 353 stops scanning after one match per expression, so repeated uses on the same line are skipped.
Proposed fix
while ((match = pattern.exec(result.stripped)) !== null) {
const startCol = match.index + 1
const endCol = startCol + match[0].length
if (claimed.some(([s, e]) => startCol < e && endCol > s)) continue
claimed.push([startCol, endCol])
positions.push({ expr, line: lineNumber, startCol, endCol })
- break // Only first occurrence per expression per line
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/renderer/components/_features/`[workspace]/editor/monaco/index.tsx around
lines 347 - 354, The loop that finds matches for each expression stops after the
first match due to the break, causing only one badge per expression per line;
remove the break inside the while ((match = pattern.exec(result.stripped)) !==
null) loop so the code continues scanning for additional non-overlapping matches
(respecting the claimed array check) and pushes multiple positions for the same
expr on the same line (keep using claimed, positions.push, startCol/endCol, and
lineNumber as-is).
Summary
//,(* *),/* */) before scanning so variables inside comments are never decoratedTON0.Q, array elements likemy_array[3]) by reading keys directly from thedebugVariableValuesstore map, with longest-first matching and overlap detection to prevent partial matchesTest plan
TON0.Q,TON0.ET,my_array[3]etc. show their own badges at the correct positions//,(* *),/* */) do NOT get badgesnpm run lintandnpm run test— no regressions🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Improvements
Style