Skip to content

fix(ai): mirror inline-completion idle re-trigger from openplc-web - #926

Merged
thiagoralves merged 1 commit into
developmentfrom
fix/ai-completion-idle-retrigger
Jul 5, 2026
Merged

fix(ai): mirror inline-completion idle re-trigger from openplc-web#926
thiagoralves merged 1 commit into
developmentfrom
fix/ai-completion-idle-retrigger

Conversation

@thiagoralves

@thiagoralves thiagoralves commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Byte-identical mirror of the shared monaco/index.tsx change in openplc-web#589: after 2s idle with AI on and no ghost visible, re-trigger inline suggest so completions don't "give up" after a late/dropped result.

The debounce + token-guard halves of the fix are web-only (AI adapter), so this is purely the shared editor wiring. No version bump, no tag, no desktop release.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Improved AI inline completions in the editor by automatically rechecking for suggestions after a short idle period when you keep typing.
    • Inline suggestions now reappear more reliably when the editor is focused and there’s still content to work with.
  • Bug Fixes
    • Reduced cases where ghost text would fail to show again after content changes.

Byte-identical mirror of the shared monaco/index.tsx change in openplc-web
(fix/ai-completion-idle-retrigger): after 2s idle with AI on and no ghost
visible, re-trigger inline suggest so completions don't "give up" after a
late/dropped result. The debounce + token-guard halves of that fix live in
the web-only AI adapter, so no desktop behavior beyond this shared editor
wiring — no version bump, no tag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Adds a useEffect to the Monaco editor component that, while AI inline completions are active, restarts a 2-second idle timer on each model content change and re-triggers inline suggest if the editor has focus, the model is non-empty, and no ghost text is currently displayed. Cleans up timer and listener on unmount.

Changes

AI Inline Completion Idle Re-trigger

Layer / File(s) Summary
Idle re-trigger effect
src/frontend/components/_features/[workspace]/editor/monaco/index.tsx
New useEffect gated by inlineCompletionsActive listens for model content changes, resets a 2s idle timer, and conditionally calls editor.action.inlineSuggest.trigger when focused, non-empty, and no ghost text is present; cleans up timer and listener on teardown.

Estimated code review effort: 2 (Simple) | ~10 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Editor
  participant IdleTimer

  User->>Editor: types content
  Editor->>IdleTimer: reset 2s timer
  IdleTimer-->>Editor: timer elapses (idle)
  Editor->>Editor: check focus, model non-empty, no ghost text
  alt conditions met
    Editor->>Editor: trigger inlineSuggest
  else conditions not met
    Editor-->>Editor: skip trigger
  end
Loading

Possibly related PRs

Poem

A timer ticks, two seconds still,
No ghost text lurking on the sill,
Focus held, the model's fed,
Suggestions wake from idle bed. 🐇
Hop along, dear cursor, write!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the change, but it does not follow the required template sections or include the references and DOD checklist. Rewrite the PR description using the repo template, including References, a bullet list of changes, and the full DOD checklist.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the mirrored inline-completion idle re-trigger change.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/ai-completion-idle-retrigger

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/frontend/components/_features/`[workspace]/editor/monaco/index.tsx:
- Around line 1335-1360: Programmatic model updates are incorrectly re-arming
the idle inline-suggest retrigger in the Monaco editor. Update the `useEffect`
that registers `editor.onDidChangeModelContent(scheduleIdleRetrigger)` so it
ignores changes caused by internal writes, using the existing
`isSyncingModelRef` guard (or equivalent) before scheduling the timer. Make sure
the guard covers programmatic `executeEdits`/`setValue` paths used by the diff
undo, AI tool updates, and file reload sync, while still allowing real user
typing to trigger `editor.action.inlineSuggest.trigger`.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 653be223-0d9c-41a5-9bd5-aeb59e4ee556

📥 Commits

Reviewing files that changed from the base of the PR and between f38b450 and 523fe2c.

📒 Files selected for processing (1)
  • src/frontend/components/_features/[workspace]/editor/monaco/index.tsx

Comment on lines +1335 to +1360
useEffect(() => {
if (!inlineCompletionsActive) return
const editor = editorRef.current
if (!editor) return

const IDLE_MS = 2000
let idleTimer: ReturnType<typeof setTimeout> | undefined

const scheduleIdleRetrigger = () => {
if (idleTimer) clearTimeout(idleTimer)
idleTimer = setTimeout(() => {
if (!editor.hasTextFocus()) return
const model = editor.getModel()
if (!model || model.getValueLength() === 0) return
// Skip if a ghost is already showing (avoid a redundant request).
const dom = editor.getDomNode()
if (dom?.querySelector('.ghost-text-decoration, .ghost-text, [class*="ghost-text"]')) return
editor.trigger('openplc-ai-idle', 'editor.action.inlineSuggest.trigger', {})
}, IDLE_MS)
}

const changeDisposable = editor.onDidChangeModelContent(scheduleIdleRetrigger)
return () => {
if (idleTimer) clearTimeout(idleTimer)
changeDisposable.dispose()
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Programmatic model edits also re-arm the idle re-trigger.

onDidChangeModelContent fires for programmatic writes too (model.setValue, executeEdits), not just user keystrokes. This file performs several such writes — diff-review hunk undo (executeEdits('ai-diff-undo-hunk', ...)), AI chat/tool updates (executeEdits('ai-tool-update', ...)), and external file-reload sync — all guarded elsewhere by isSyncingModelRef to suppress handleWriteInPou's store-sync side effects, but this new listener isn't gated on that flag. If the editor still has focus when one of those programmatic writes lands, this effect will schedule (and 2s later fire) an inline-suggest re-trigger that wasn't caused by user typing.

🐛 Proposed fix
-    const changeDisposable = editor.onDidChangeModelContent(scheduleIdleRetrigger)
+    const changeDisposable = editor.onDidChangeModelContent(() => {
+      if (isSyncingModelRef.current) return
+      scheduleIdleRetrigger()
+    })
📝 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.

Suggested change
useEffect(() => {
if (!inlineCompletionsActive) return
const editor = editorRef.current
if (!editor) return
const IDLE_MS = 2000
let idleTimer: ReturnType<typeof setTimeout> | undefined
const scheduleIdleRetrigger = () => {
if (idleTimer) clearTimeout(idleTimer)
idleTimer = setTimeout(() => {
if (!editor.hasTextFocus()) return
const model = editor.getModel()
if (!model || model.getValueLength() === 0) return
// Skip if a ghost is already showing (avoid a redundant request).
const dom = editor.getDomNode()
if (dom?.querySelector('.ghost-text-decoration, .ghost-text, [class*="ghost-text"]')) return
editor.trigger('openplc-ai-idle', 'editor.action.inlineSuggest.trigger', {})
}, IDLE_MS)
}
const changeDisposable = editor.onDidChangeModelContent(scheduleIdleRetrigger)
return () => {
if (idleTimer) clearTimeout(idleTimer)
changeDisposable.dispose()
}
useEffect(() => {
if (!inlineCompletionsActive) return
const editor = editorRef.current
if (!editor) return
const IDLE_MS = 2000
let idleTimer: ReturnType<typeof setTimeout> | undefined
const scheduleIdleRetrigger = () => {
if (idleTimer) clearTimeout(idleTimer)
idleTimer = setTimeout(() => {
if (!editor.hasTextFocus()) return
const model = editor.getModel()
if (!model || model.getValueLength() === 0) return
// Skip if a ghost is already showing (avoid a redundant request).
const dom = editor.getDomNode()
if (dom?.querySelector('.ghost-text-decoration, .ghost-text, [class*="ghost-text"]')) return
editor.trigger('openplc-ai-idle', 'editor.action.inlineSuggest.trigger', {})
}, IDLE_MS)
}
const changeDisposable = editor.onDidChangeModelContent(() => {
if (isSyncingModelRef.current) return
scheduleIdleRetrigger()
})
return () => {
if (idleTimer) clearTimeout(idleTimer)
changeDisposable.dispose()
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/frontend/components/_features/`[workspace]/editor/monaco/index.tsx around
lines 1335 - 1360, Programmatic model updates are incorrectly re-arming the idle
inline-suggest retrigger in the Monaco editor. Update the `useEffect` that
registers `editor.onDidChangeModelContent(scheduleIdleRetrigger)` so it ignores
changes caused by internal writes, using the existing `isSyncingModelRef` guard
(or equivalent) before scheduling the timer. Make sure the guard covers
programmatic `executeEdits`/`setValue` paths used by the diff undo, AI tool
updates, and file reload sync, while still allowing real user typing to trigger
`editor.action.inlineSuggest.trigger`.

@thiagoralves
thiagoralves merged commit 91bfbd3 into development Jul 5, 2026
14 checks passed
@thiagoralves
thiagoralves deleted the fix/ai-completion-idle-retrigger branch July 5, 2026 00:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant