Add git-ai blame visualization to JetBrains plugin#741
Add git-ai blame visualization to JetBrains plugin#741Krishnachaitanyakc wants to merge 3 commits intogit-ai-project:mainfrom
Conversation
b503fd1 to
e11fc9a
Compare
Implements AI blame annotations in the IntelliJ plugin, matching the VS Code extension's functionality. Calls `git-ai blame --json --contents -` to get per-line AI authorship data and renders it via: - Gutter color stripes (4px colored icons per AI-authored line) - Inline after-text inlays showing model name (e.g. "Sonnet 3.5 via Cursor") - Status bar widget showing AI/human indicator for current line - Toggle action (Cmd+Shift+G) cycling Off/Line/All modes New files: BlameModels, BlameService, BlameEditorManager, BlameStatusBarWidgetFactory, BlameToggleAction, BlameStartupActivity, ModelNameParser (ported from VS Code extension).
e11fc9a to
f9ea024
Compare
- BlameService: start stdout/stderr readers before stdin write and make stdin write async to prevent pipe buffer deadlock on files >64KB - BlameEditorManager: use getEditors(file) instead of selectedTextEditor in fileOpened callback to resolve correct editor in split-editor scenarios - Add consolidated pre-commit checks section to CLAUDE.md covering process I/O patterns, IntelliJ API misuses, Rust formatting, and lint
BlameEditorManager.dispose() was calling Disposer.dispose(state) and clearDecorations() directly without invokeLater, causing EDT threading violations when the service is disposed from a background thread during project closing. Now matches the pattern already used in detachFromEditor().
| if (editorStates.containsKey(editor)) return | ||
|
|
||
| val state = EditorBlameState(editor) | ||
| editorStates[editor] = state |
There was a problem hiding this comment.
🔴 TOCTOU race in attachToEditor causes leaked state and duplicate editor decorations
attachToEditor uses a non-atomic check-then-act pattern on ConcurrentHashMap at lines 133-136: it calls containsKey(editor) then separately calls editorStates[editor] = state. Since initialize() runs on a background coroutine (via ProjectActivity.execute at BlameStartupActivity.kt:10) and the fileOpened callback fires on EDT (BlameEditorManager.kt:112), two threads can simultaneously pass the containsKey guard for the same editor. The second put overwrites the first state, but the first state's caret and document listeners (registered at lines 139-154 with state as the Disposable parent) remain active on the editor. This orphaned state is never disposed, so its listeners keep firing independently — calling updateLineMode and scheduleBlameRefresh with a state that isn't tracked in the map — producing duplicate gutter icons and inline annotations.
| if (editorStates.containsKey(editor)) return | |
| val state = EditorBlameState(editor) | |
| editorStates[editor] = state | |
| val state = EditorBlameState(editor) | |
| if (editorStates.putIfAbsent(editor, state) != null) return |
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Implements AI blame annotations in the JetBrains IntelliJ plugin, matching the VS Code extension's functionality. Resolves #660.
git-ai blame --json --contents -CLI to get per-line AI authorship dataSonnet 3.5 via Cursor)Cmd+Shift+G/Ctrl+Shift+Gcycles through Off → Line → All modes