From e9db550358e3c290cd29f65a42c417120d47acb2 Mon Sep 17 00:00:00 2001 From: Andrey Loskutov Date: Mon, 19 Jan 2026 15:29:45 +0100 Subject: [PATCH 1/2] WIP Fixes https://github.com/eclipse-platform/eclipse.platform.ui/issues/3685 --- .../META-INF/MANIFEST.MF | 8 +- .../filebuffers/SynchronizableDocument.java | 42 ++++---- bundles/org.eclipse.text/META-INF/MANIFEST.MF | 2 +- .../eclipse/jface/text/AbstractDocument.java | 101 ++++++++++-------- 4 files changed, 82 insertions(+), 71 deletions(-) diff --git a/bundles/org.eclipse.core.filebuffers/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.filebuffers/META-INF/MANIFEST.MF index 088168218658..89c1baed853f 100644 --- a/bundles/org.eclipse.core.filebuffers/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.core.filebuffers/META-INF/MANIFEST.MF @@ -10,9 +10,9 @@ Export-Package: org.eclipse.core.filebuffers.manipulation, org.eclipse.core.internal.filebuffers;x-internal:=true Require-Bundle: - org.eclipse.core.runtime;bundle-version="[3.29.0,4.0.0)", - org.eclipse.core.resources;bundle-version="[3.5.0,4.0.0)";resolution:=optional, - org.eclipse.text;bundle-version="[3.5.0,4.0.0)", - org.eclipse.core.filesystem;bundle-version="[1.2.0,2.0.0)" + org.eclipse.core.runtime;bundle-version="[3.34.0,4.0.0)", + org.eclipse.core.resources;bundle-version="[3.23.0,4.0.0)";resolution:=optional, + org.eclipse.text;bundle-version="[3.15.0,4.0.0)", + org.eclipse.core.filesystem;bundle-version="[1.11.0,2.0.0)" Bundle-RequiredExecutionEnvironment: JavaSE-17 Automatic-Module-Name: org.eclipse.core.filebuffers diff --git a/bundles/org.eclipse.core.filebuffers/src/org/eclipse/core/internal/filebuffers/SynchronizableDocument.java b/bundles/org.eclipse.core.filebuffers/src/org/eclipse/core/internal/filebuffers/SynchronizableDocument.java index 32cf3238f8af..34739634fa40 100644 --- a/bundles/org.eclipse.core.filebuffers/src/org/eclipse/core/internal/filebuffers/SynchronizableDocument.java +++ b/bundles/org.eclipse.core.filebuffers/src/org/eclipse/core/internal/filebuffers/SynchronizableDocument.java @@ -250,14 +250,7 @@ public long getModificationStamp() { @Override public void replace(int offset, int length, String text) throws BadLocationException { - Object lockObject= getLockObject(); - if (lockObject == null) { - super.replace(offset, length, text); - return; - } - synchronized (lockObject) { - super.replace(offset, length, text); - } + super.replace(offset, length, text); } @Override @@ -267,21 +260,19 @@ public void replace(int offset, int length, String text, long modificationStamp) super.replace(offset, length, text, modificationStamp); return; } - synchronized (lockObject) { - super.replace(offset, length, text, modificationStamp); + try { + stopListenerNotification(); + synchronized (lockObject) { + super.replace(offset, length, text, modificationStamp); + } + } finally { + resumeListenerNotification(); } } @Override public void set(String text) { - Object lockObject= getLockObject(); - if (lockObject == null) { - super.set(text); - return; - } - synchronized (lockObject) { - super.set(text); - } + super.set(text); } @Override @@ -291,11 +282,22 @@ public void set(String text, long modificationStamp) { super.set(text, modificationStamp); return; } - synchronized (lockObject) { - super.set(text, modificationStamp); + try { + stopListenerNotification(); + synchronized (lockObject) { + super.set(text, modificationStamp); + } + } finally { + resumeListenerNotification(); } } + @Override + protected void doFireDocumentChanged2(DocumentEvent event) { + // TODO this code could use a job to dispatch updates to listeners asynchronously + super.doFireDocumentChanged2(event); + } + @Override public void addPosition(String category, Position position) throws BadLocationException, BadPositionCategoryException { Object lockObject= getLockObject(); diff --git a/bundles/org.eclipse.text/META-INF/MANIFEST.MF b/bundles/org.eclipse.text/META-INF/MANIFEST.MF index 516af6ca8a04..d7dd60216cd9 100644 --- a/bundles/org.eclipse.text/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.text/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.text -Bundle-Version: 3.14.600.qualifier +Bundle-Version: 3.15.0.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: diff --git a/bundles/org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.java b/bundles/org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.java index 5da5c2d0862c..cbc01aeef1c0 100644 --- a/bundles/org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.java +++ b/bundles/org.eclipse.text/src/org/eclipse/jface/text/AbstractDocument.java @@ -23,6 +23,9 @@ import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import java.util.regex.PatternSyntaxException; import org.eclipse.core.runtime.Assert; @@ -115,17 +118,17 @@ static private class RegisteredReplace { * The list of post notification changes * @since 2.0 */ - private List fPostNotificationChanges; + private volatile List fPostNotificationChanges; /** * The reentrance count for post notification changes. * @since 2.0 */ - private int fReentranceCount= 0; + private volatile int fReentranceCount; /** * Indicates whether post notification change processing has been stopped. * @since 2.0 */ - private int fStoppedCount= 0; + private volatile int fStoppedCount; /** * Indicates whether the registration of post notification changes should be ignored. * @since 2.1 @@ -135,12 +138,12 @@ static private class RegisteredReplace { * Indicates whether the notification of listeners has been stopped. * @since 2.1 */ - private int fStoppedListenerNotification= 0; + private final AtomicInteger fStoppedListenerNotification; /** - * The document event to be sent after listener notification has been resumed. - * @since 2.1 + * The document event list to be sent after listener notification has been resumed. + * @since 3.15 */ - private DocumentEvent fDeferredDocumentEvent; + private final List fDeferredDocumentEvents; /** * The registered document partitioners. * @since 3.0 @@ -148,9 +151,9 @@ static private class RegisteredReplace { private Map fDocumentPartitioners; /** * The partitioning changed event. - * @since 3.0 + * @since 3.15.0 */ - private DocumentPartitioningChangedEvent fDocumentPartitioningChangedEvent; + private final AtomicReference fDocumentPartitioningChangedEvent; /** * The find/replace document adapter. * @since 3.0 @@ -175,7 +178,7 @@ static private class RegisteredReplace { * Keeps track of next modification stamp. * @since 3.1.1 */ - private long fNextModificationStamp= IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP; + private final AtomicLong fNextModificationStamp= new AtomicLong(IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP); /** * This document's default line delimiter. * @since 3.1 @@ -191,6 +194,9 @@ static private class RegisteredReplace { */ protected AbstractDocument() { fModificationStamp= getNextModificationStamp(); + fStoppedListenerNotification = new AtomicInteger(); + fDeferredDocumentEvents = new CopyOnWriteArrayList<>(); + fDocumentPartitioningChangedEvent = new AtomicReference<>(); } @@ -437,7 +443,7 @@ public boolean containsPositionCategory(String category) { * @see IDocument#computeIndexInCategory(String, int) * @deprecated As of 3.4, replaced by {@link #computeIndexInPositionList(List, int, boolean)} */ - @Deprecated + @Deprecated(forRemoval = true, since= "3.15.0") protected int computeIndexInPositionList(List positions, int offset) { return computeIndexInPositionList(positions, offset, true); } @@ -542,7 +548,7 @@ public int computeIndexInCategory(String category, int offset) throws BadLocatio * * @deprecated as of 2.0. Use fireDocumentPartitioningChanged(IRegion) instead. */ - @Deprecated + @Deprecated(forRemoval = true, since= "3.15.0") protected void fireDocumentPartitioningChanged() { if (fDocumentPartitioningListeners == null) { return; @@ -565,7 +571,7 @@ protected void fireDocumentPartitioningChanged() { * fireDocumentPartitioningChanged(DocumentPartitioningChangedEvent) * instead. */ - @Deprecated + @Deprecated(forRemoval = true, since= "3.15.0") protected void fireDocumentPartitioningChanged(IRegion region) { if (fDocumentPartitioningListeners == null) { return; @@ -670,7 +676,7 @@ protected void fireDocumentAboutToBeChanged(DocumentEvent event) { protected void updateDocumentStructures(DocumentEvent event) { if (fDocumentPartitioners != null) { - fDocumentPartitioningChangedEvent= new DocumentPartitioningChangedEvent(this); + DocumentPartitioningChangedEvent newValue= new DocumentPartitioningChangedEvent(this); for (Entry entry : fDocumentPartitioners.entrySet()) { String partitioning= entry.getKey(); @@ -685,14 +691,15 @@ protected void updateDocumentStructures(DocumentEvent event) { if (partitioner instanceof IDocumentPartitionerExtension extension) { IRegion r= extension.documentChanged2(event); if (r != null) { - fDocumentPartitioningChangedEvent.setPartitionChange(partitioning, r.getOffset(), r.getLength()); + newValue.setPartitionChange(partitioning, r.getOffset(), r.getLength()); } } else { if (partitioner.documentChanged(event)) { - fDocumentPartitioningChangedEvent.setPartitionChange(partitioning, 0, event.getDocument().getLength()); + newValue.setPartitionChange(partitioning, 0, event.getDocument().getLength()); } } } + fDocumentPartitioningChangedEvent.set(newValue); } if (!fPositions.isEmpty()) { @@ -709,8 +716,9 @@ protected void updateDocumentStructures(DocumentEvent event) { * @param event the event to be sent out. */ protected void doFireDocumentChanged(DocumentEvent event) { - boolean changed= fDocumentPartitioningChangedEvent != null && !fDocumentPartitioningChangedEvent.isEmpty(); - IRegion change= changed ? fDocumentPartitioningChangedEvent.getCoverage() : null; + DocumentPartitioningChangedEvent changedEvent= fDocumentPartitioningChangedEvent.get(); + boolean changed= changedEvent != null && !changedEvent.isEmpty(); + IRegion change= changed ? changedEvent.getCoverage() : null; doFireDocumentChanged(event, changed, change); } @@ -725,7 +733,7 @@ protected void doFireDocumentChanged(DocumentEvent event) { * @since 2.0 * @deprecated as of 3.0. Use doFireDocumentChanged2(DocumentEvent) instead; this method will be removed. */ - @Deprecated + @Deprecated(forRemoval = true, since= "3.15.0") protected void doFireDocumentChanged(DocumentEvent event, boolean firePartitionChange, IRegion partitionChange) { doFireDocumentChanged2(event); } @@ -743,8 +751,7 @@ protected void doFireDocumentChanged(DocumentEvent event, boolean firePartitionC */ protected void doFireDocumentChanged2(DocumentEvent event) { - DocumentPartitioningChangedEvent p= fDocumentPartitioningChangedEvent; - fDocumentPartitioningChangedEvent= null; + DocumentPartitioningChangedEvent p= fDocumentPartitioningChangedEvent.getAndSet(null); if (p != null && !p.isEmpty()) { fireDocumentPartitioningChanged(p); } @@ -786,10 +793,10 @@ protected void doFireDocumentChanged2(DocumentEvent event) { protected void fireDocumentChanged(DocumentEvent event) { updateDocumentStructures(event); - if (fStoppedListenerNotification == 0) { + if (fStoppedListenerNotification.get() == 0) { doFireDocumentChanged(event); } else { - fDeferredDocumentEvent= event; + fDeferredDocumentEvents.add(event); } } @@ -1099,13 +1106,13 @@ public void removePositionUpdater(IPositionUpdater updater) { } private long getNextModificationStamp() { - if (fNextModificationStamp == Long.MAX_VALUE || fNextModificationStamp == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { - fNextModificationStamp= 0; - } else { - fNextModificationStamp= fNextModificationStamp + 1; - } - - return fNextModificationStamp; + return fNextModificationStamp.getAndUpdate(current -> { + if (current == Long.MAX_VALUE || current == IDocumentExtension4.UNKNOWN_MODIFICATION_STAMP) { + return 0; + } else { + return current + 1; + } + }); } @Override @@ -1126,7 +1133,7 @@ public void replace(int pos, int length, String text, long modificationStamp) th getTracker().replace(pos, length, text); fModificationStamp= modificationStamp; - fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp); + fNextModificationStamp.getAndAccumulate(fModificationStamp, Math::max); e.fModificationStamp= fModificationStamp; fireDocumentChanged(e); @@ -1167,7 +1174,7 @@ public void set(String text, long modificationStamp) { getTracker().set(text); fModificationStamp= modificationStamp; - fNextModificationStamp= Math.max(fModificationStamp, fNextModificationStamp); + fNextModificationStamp.getAndAccumulate(fModificationStamp, Math::max); e.fModificationStamp= fModificationStamp; fireDocumentChanged(e); @@ -1192,7 +1199,7 @@ protected void updatePositions(DocumentEvent event) { * * @deprecated as of 3.0 search is provided by {@link FindReplaceDocumentAdapter} */ - @Deprecated + @Deprecated(forRemoval = true, since= "3.15.0") @Override public int search(int startPosition, String findString, boolean forwardSearch, boolean caseSensitive, boolean wholeWord) throws BadLocationException { try { @@ -1295,7 +1302,7 @@ public void resumePostNotificationProcessing() { * {@link IDocumentExtension4#startRewriteSession(DocumentRewriteSessionType)} * instead. */ - @Deprecated + @Deprecated(forRemoval = true, since= "3.15.0") @Override public void startSequentialRewrite(boolean normalized) { } @@ -1306,22 +1313,21 @@ public void startSequentialRewrite(boolean normalized) { * @since 2.0 * @deprecated As of 3.1, replaced by {@link IDocumentExtension4#stopRewriteSession(DocumentRewriteSession)} */ - @Deprecated + @Deprecated(forRemoval = true, since= "3.15.0") @Override public void stopSequentialRewrite() { } @Override public void resumeListenerNotification() { - -- fStoppedListenerNotification; - if (fStoppedListenerNotification == 0) { + if (fStoppedListenerNotification.decrementAndGet() == 0) { resumeDocumentListenerNotification(); } } @Override public void stopListenerNotification() { - ++ fStoppedListenerNotification; + fStoppedListenerNotification.incrementAndGet(); } /** @@ -1331,17 +1337,20 @@ public void stopListenerNotification() { * @since 2.1 */ private void resumeDocumentListenerNotification() { - if (fDeferredDocumentEvent != null) { - DocumentEvent event= fDeferredDocumentEvent; - fDeferredDocumentEvent= null; - doFireDocumentChanged(event); + while (!fDeferredDocumentEvents.isEmpty()) { + if (fStoppedListenerNotification.get() == 0) { + try { + DocumentEvent event= fDeferredDocumentEvents.remove(0); + doFireDocumentChanged(event); + } catch (IndexOutOfBoundsException ex) { + log(ex); + } + } else { + break; + } } } - /* - * @see org.eclipse.jface.text.IDocumentExtension3#computeZeroLengthPartitioning(java.lang.String, int, int) - * @since 3.0 - */ @Override public ITypedRegion[] computePartitioning(String partitioning, int offset, int length, boolean includeZeroLengthPartitions) throws BadLocationException, BadPartitioningException { if ((0 > offset) || (0 > length) || (offset + length > getLength())) { From 7a9d68138315aa9ed352a85d2d353dbb63a4853c Mon Sep 17 00:00:00 2001 From: Eclipse Platform Bot Date: Mon, 19 Jan 2026 14:45:58 +0000 Subject: [PATCH 2/2] Version bump(s) for 4.39 stream --- bundles/org.eclipse.core.filebuffers/META-INF/MANIFEST.MF | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bundles/org.eclipse.core.filebuffers/META-INF/MANIFEST.MF b/bundles/org.eclipse.core.filebuffers/META-INF/MANIFEST.MF index 89c1baed853f..0a739cf4f0e0 100644 --- a/bundles/org.eclipse.core.filebuffers/META-INF/MANIFEST.MF +++ b/bundles/org.eclipse.core.filebuffers/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %pluginName Bundle-SymbolicName: org.eclipse.core.filebuffers; singleton:=true -Bundle-Version: 3.8.500.qualifier +Bundle-Version: 3.8.600.qualifier Bundle-Vendor: %providerName Bundle-Localization: plugin Export-Package: