mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[rd debugger] IJPL-192277 Fix a race between highlighter creation and disposal (especially for light breakpoints)
GitOrigin-RevId: 1621249a9c253144f637997f2cb92f027eecee28
This commit is contained in:
committed by
intellij-monorepo-bot
parent
d1e92a2ce8
commit
9d980cb2c7
+98
-83
@@ -39,6 +39,8 @@ import org.jetbrains.annotations.ApiStatus
|
||||
import java.awt.Cursor
|
||||
import java.awt.dnd.DnDConstants
|
||||
import java.awt.dnd.DragSource
|
||||
import java.util.concurrent.locks.ReentrantLock
|
||||
import kotlin.concurrent.withLock
|
||||
|
||||
@ApiStatus.Internal
|
||||
class XBreakpointVisualRepresentation(
|
||||
@@ -48,7 +50,11 @@ class XBreakpointVisualRepresentation(
|
||||
) {
|
||||
private val myProject: Project = myBreakpoint.project
|
||||
|
||||
// Avoid races between highlighter creation and disposal
|
||||
private val lock = ReentrantLock()
|
||||
|
||||
var rangeMarker: RangeMarker? = null
|
||||
get() = lock.withLock { field }
|
||||
private set
|
||||
|
||||
val highlighter: RangeHighlighter?
|
||||
@@ -80,13 +86,12 @@ class XBreakpointVisualRepresentation(
|
||||
if (document == null) {
|
||||
// currently LazyRangeMarkerFactory creates document for non binary files
|
||||
if (file.fileType.isBinary()) {
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
{
|
||||
if (this.rangeMarker == null) {
|
||||
this.rangeMarker = LazyRangeMarkerFactory.getInstance(myProject).createRangeMarker(file, myBreakpoint.getLine(), 0, true)
|
||||
callOnUpdate.run()
|
||||
}
|
||||
}, myProject.getDisposed())
|
||||
invokeLaterWithLockAndChecks {
|
||||
if (this.rangeMarker == null) {
|
||||
this.rangeMarker = LazyRangeMarkerFactory.getInstance(myProject).createRangeMarker(file, myBreakpoint.getLine(), 0, true)
|
||||
callOnUpdate.run()
|
||||
}
|
||||
}
|
||||
return@nonBlocking
|
||||
}
|
||||
document = FileDocumentManager.getInstance().getDocument(file)
|
||||
@@ -103,92 +108,102 @@ class XBreakpointVisualRepresentation(
|
||||
val range = myBreakpoint.getHighlightRange()
|
||||
|
||||
val finalDocument: Document = document
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
{
|
||||
if (myBreakpoint.isDisposed()) return@invokeLater
|
||||
if (this.rangeMarker != null && this.rangeMarker !is RangeHighlighter) {
|
||||
removeHighlighter()
|
||||
assert(this.highlighter == null)
|
||||
invokeLaterWithLockAndChecks {
|
||||
if (this.rangeMarker != null && this.rangeMarker !is RangeHighlighter) {
|
||||
removeHighlighter()
|
||||
assert(this.highlighter == null)
|
||||
}
|
||||
|
||||
var attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(DebuggerColors.BREAKPOINT_ATTRIBUTES)
|
||||
|
||||
if (!myBreakpoint.isEnabled()) {
|
||||
attributes = attributes.clone()
|
||||
attributes.backgroundColor = null
|
||||
}
|
||||
|
||||
var highlighter = this.highlighter
|
||||
if (highlighter != null &&
|
||||
(!highlighter.isValid()
|
||||
|| range != null && highlighter.textRange != range
|
||||
//breakpoint range marker is out-of-sync with actual breakpoint text range
|
||||
|| !DocumentUtil.isValidOffset(highlighter.getStartOffset(), finalDocument)
|
||||
|| !Comparing.equal(highlighter.getTextAttributes(null), attributes)
|
||||
// it seems that this check is not needed - we always update line number from the highlighter
|
||||
// and highlighter is removed on line and file change anyway
|
||||
/*|| document.getLineNumber(highlighter.getStartOffset()) != getLine()*/
|
||||
)
|
||||
) {
|
||||
removeHighlighter()
|
||||
redrawInlineInlays()
|
||||
highlighter = null
|
||||
}
|
||||
|
||||
myBreakpoint.updateIcon()
|
||||
|
||||
if (highlighter == null) {
|
||||
val line = myBreakpoint.getLine()
|
||||
if (line >= finalDocument.getLineCount()) {
|
||||
callOnUpdate.run()
|
||||
return@invokeLaterWithLockAndChecks
|
||||
}
|
||||
|
||||
var attributes = EditorColorsManager.getInstance().getGlobalScheme().getAttributes(DebuggerColors.BREAKPOINT_ATTRIBUTES)
|
||||
|
||||
if (!myBreakpoint.isEnabled()) {
|
||||
attributes = attributes.clone()
|
||||
attributes.backgroundColor = null
|
||||
val markupModel = DocumentMarkupModel.forDocument(finalDocument, myProject, true) as MarkupModelEx
|
||||
if (range != null && !range.isEmpty) {
|
||||
val lineRange = DocumentUtil.getLineTextRange(finalDocument, line)
|
||||
if (range.intersectsStrict(lineRange)) {
|
||||
highlighter = markupModel.addRangeHighlighter(range.startOffset, range.endOffset,
|
||||
DebuggerColors.BREAKPOINT_HIGHLIGHTER_LAYER, attributes,
|
||||
HighlighterTargetArea.EXACT_RANGE)
|
||||
}
|
||||
}
|
||||
|
||||
var highlighter = this.highlighter
|
||||
if (highlighter != null &&
|
||||
(!highlighter.isValid()
|
||||
|| range != null && highlighter.textRange != range
|
||||
//breakpoint range marker is out-of-sync with actual breakpoint text range
|
||||
|| !DocumentUtil.isValidOffset(highlighter.getStartOffset(), finalDocument)
|
||||
|| !Comparing.equal(highlighter.getTextAttributes(null), attributes)
|
||||
// it seems that this check is not needed - we always update line number from the highlighter
|
||||
// and highlighter is removed on line and file change anyway
|
||||
/*|| document.getLineNumber(highlighter.getStartOffset()) != getLine()*/
|
||||
)
|
||||
) {
|
||||
removeHighlighter()
|
||||
redrawInlineInlays()
|
||||
highlighter = null
|
||||
}
|
||||
|
||||
myBreakpoint.updateIcon()
|
||||
|
||||
if (highlighter == null) {
|
||||
val line = myBreakpoint.getLine()
|
||||
if (line >= finalDocument.getLineCount()) {
|
||||
callOnUpdate.run()
|
||||
return@invokeLater
|
||||
}
|
||||
val markupModel = DocumentMarkupModel.forDocument(finalDocument, myProject, true) as MarkupModelEx
|
||||
if (range != null && !range.isEmpty) {
|
||||
val lineRange = DocumentUtil.getLineTextRange(finalDocument, line)
|
||||
if (range.intersectsStrict(lineRange)) {
|
||||
highlighter = markupModel.addRangeHighlighter(range.startOffset, range.endOffset,
|
||||
DebuggerColors.BREAKPOINT_HIGHLIGHTER_LAYER, attributes,
|
||||
HighlighterTargetArea.EXACT_RANGE)
|
||||
}
|
||||
}
|
||||
if (highlighter == null) {
|
||||
highlighter = markupModel.addPersistentLineHighlighter(line, DebuggerColors.BREAKPOINT_HIGHLIGHTER_LAYER, attributes)
|
||||
}
|
||||
if (highlighter == null) {
|
||||
callOnUpdate.run()
|
||||
return@invokeLater
|
||||
}
|
||||
|
||||
highlighter.setGutterIconRenderer(myBreakpoint.createGutterIconRenderer())
|
||||
highlighter.putUserData(DebuggerColors.BREAKPOINT_HIGHLIGHTER_KEY, true)
|
||||
highlighter.setEditorFilter(MarkupEditorFilter { editor -> isHighlighterAvailableIn(editor) })
|
||||
this.rangeMarker = highlighter
|
||||
|
||||
redrawInlineInlays()
|
||||
highlighter = markupModel.addPersistentLineHighlighter(line, DebuggerColors.BREAKPOINT_HIGHLIGHTER_LAYER, attributes)
|
||||
}
|
||||
else {
|
||||
val markupModel = DocumentMarkupModel.forDocument(finalDocument, myProject, false) as MarkupModelEx?
|
||||
if (markupModel != null) {
|
||||
// renderersChanged false - we don't change gutter size
|
||||
val filter = highlighter.getEditorFilter()
|
||||
highlighter.setEditorFilter(MarkupEditorFilter.EMPTY)
|
||||
highlighter.setEditorFilter(filter) // to fireChanged
|
||||
}
|
||||
if (highlighter == null) {
|
||||
callOnUpdate.run()
|
||||
return@invokeLaterWithLockAndChecks
|
||||
}
|
||||
callOnUpdate.run()
|
||||
}, myProject.getDisposed())
|
||||
|
||||
highlighter.setGutterIconRenderer(myBreakpoint.createGutterIconRenderer())
|
||||
highlighter.putUserData(DebuggerColors.BREAKPOINT_HIGHLIGHTER_KEY, true)
|
||||
highlighter.setEditorFilter(MarkupEditorFilter { editor -> isHighlighterAvailableIn(editor) })
|
||||
this.rangeMarker = highlighter
|
||||
|
||||
redrawInlineInlays()
|
||||
}
|
||||
else {
|
||||
val markupModel = DocumentMarkupModel.forDocument(finalDocument, myProject, false) as MarkupModelEx?
|
||||
if (markupModel != null) {
|
||||
// renderersChanged false - we don't change gutter size
|
||||
val filter = highlighter.getEditorFilter()
|
||||
highlighter.setEditorFilter(MarkupEditorFilter.EMPTY)
|
||||
highlighter.setEditorFilter(filter) // to fireChanged
|
||||
}
|
||||
}
|
||||
callOnUpdate.run()
|
||||
}
|
||||
}.executeSynchronously()
|
||||
}
|
||||
|
||||
private inline fun invokeLaterWithLockAndChecks(crossinline runnable: () -> Unit) {
|
||||
ApplicationManager.getApplication().invokeLater(
|
||||
{
|
||||
lock.withLock {
|
||||
if (myBreakpoint.isDisposed()) return@invokeLater
|
||||
runnable()
|
||||
}
|
||||
}, myProject.getDisposed())
|
||||
}
|
||||
|
||||
fun removeHighlighter() {
|
||||
try {
|
||||
rangeMarker?.dispose()
|
||||
lock.withLock {
|
||||
try {
|
||||
rangeMarker?.dispose()
|
||||
}
|
||||
catch (e: Exception) {
|
||||
LOG.error(e)
|
||||
}
|
||||
rangeMarker = null
|
||||
}
|
||||
catch (e: Exception) {
|
||||
LOG.error(e)
|
||||
}
|
||||
rangeMarker = null
|
||||
}
|
||||
|
||||
private fun redrawInlineInlays() {
|
||||
|
||||
Reference in New Issue
Block a user