Skip to content

Commit e08d95d

Browse files
vogellaclaude
andcommitted
Fix TargetPlatformPresentationReconciler to not scan past damage end
The reconciler always scanned the full document (offset 0 to document.getLength()) on every keystroke to ensure multi-line XML comment rules are honoured. While scanning from offset 0 is required for correctness, scanning *past* the damage end is wasted work because the returned TextPresentation is bounded by the damage region and silently discards any style ranges beyond it. Change the upper bound of TextUtilities.computePartitioning() from document.getLength() to damage.getOffset() + damage.getLength(). This avoids scanning content whose results would be thrown away, while preserving the full-context scan from offset 0 that multi-line rules require. Add TargetPlatformPresentationReconcilerTest to verify correctness for the common cases: simple documents, partial damage regions, multi-line comments before the damage, and damage inside a multi-line comment. Fixes: #2283 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 3389587 commit e08d95d

File tree

4 files changed

+135
-5
lines changed

4 files changed

+135
-5
lines changed

ui/org.eclipse.pde.genericeditor.extension.tests/src/org/eclipse/pde/genericeditor/extension/tests/AllTargetEditorTests.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@
2020
@RunWith(Suite.class)
2121
@SuiteClasses({ AttributeNameCompletionTests.class, AttributeValueCompletionTests.class, TagNameCompletionTests.class,
2222
TagValueCompletionTests.class, Bug527084CompletionWithCommentsTest.class,
23-
Bug528706CompletionWithMultilineTagsTest.class, UpdateUnitVersionsCommandTests.class, Bug531602FormattingTests.class })
23+
Bug528706CompletionWithMultilineTagsTest.class, UpdateUnitVersionsCommandTests.class, Bug531602FormattingTests.class,
24+
TargetPlatformPresentationReconcilerTest.class })
2425
public class AllTargetEditorTests {
2526

2627
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
/*******************************************************************************
2+
* Copyright (c) 2026 vogella GmbH and others
3+
*
4+
* This program and the accompanying materials
5+
* are made available under the terms of the Eclipse Public License 2.0
6+
* which accompanies this distribution, and is available at
7+
* https://www.eclipse.org/legal/epl-2.0/
8+
*
9+
* SPDX-License-Identifier: EPL-2.0
10+
*
11+
* Contributors:
12+
* Lars Vogel - initial implementation
13+
*******************************************************************************/
14+
package org.eclipse.pde.genericeditor.extension.tests;
15+
16+
import static org.junit.Assert.assertNotNull;
17+
18+
import org.eclipse.jface.text.Document;
19+
import org.eclipse.jface.text.IDocument;
20+
import org.eclipse.jface.text.IRegion;
21+
import org.eclipse.jface.text.Region;
22+
import org.eclipse.jface.text.TextPresentation;
23+
import org.eclipse.jface.text.presentation.IPresentationRepairer;
24+
import org.eclipse.pde.internal.genericeditor.target.extension.reconciler.presentation.TargetPlatformPresentationReconciler;
25+
import org.junit.Test;
26+
27+
/**
28+
* Tests for {@link TargetPlatformPresentationReconciler}.
29+
*
30+
* <p>The reconciler overrides {@code createPresentation} to scan from offset 0
31+
* so that multi-line rules (XML comments) are correctly recognised even when
32+
* the damaged region is deep inside the document. These tests verify that the
33+
* presentation is computed correctly for several representative document shapes
34+
* and damage positions.
35+
*/
36+
public class TargetPlatformPresentationReconcilerTest {
37+
38+
/** Subclass that exposes the protected {@code createPresentation} for testing. */
39+
private static class TestableReconciler extends TargetPlatformPresentationReconciler {
40+
TextPresentation createPresentation(IDocument document, IRegion damage) {
41+
IPresentationRepairer repairer = getRepairer(IDocument.DEFAULT_CONTENT_TYPE);
42+
if (repairer != null) {
43+
repairer.setDocument(document);
44+
}
45+
return createPresentation(damage, document);
46+
}
47+
}
48+
49+
@Test
50+
public void testCreatePresentationReturnsNonNullForSimpleDocument() {
51+
TestableReconciler reconciler = new TestableReconciler();
52+
String content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" //$NON-NLS-1$
53+
+ "<target name=\"test\">\n" //$NON-NLS-1$
54+
+ " <locations/>\n" //$NON-NLS-1$
55+
+ "</target>\n"; //$NON-NLS-1$
56+
IDocument document = new Document(content);
57+
IRegion damage = new Region(0, content.length());
58+
59+
TextPresentation presentation = reconciler.createPresentation(document, damage);
60+
61+
assertNotNull("Expected a non-null TextPresentation for a simple document", presentation); //$NON-NLS-1$
62+
}
63+
64+
@Test
65+
public void testCreatePresentationWithDamageSubsetOfDocument() {
66+
TestableReconciler reconciler = new TestableReconciler();
67+
String content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" //$NON-NLS-1$
68+
+ "<target name=\"test\">\n" //$NON-NLS-1$
69+
+ " <locations>\n" //$NON-NLS-1$
70+
+ " </locations>\n" //$NON-NLS-1$
71+
+ "</target>\n"; //$NON-NLS-1$
72+
IDocument document = new Document(content);
73+
// Damage is only the last few characters, not the full document.
74+
int damageOffset = content.indexOf("</target>"); //$NON-NLS-1$
75+
IRegion damage = new Region(damageOffset, content.length() - damageOffset);
76+
77+
TextPresentation presentation = reconciler.createPresentation(document, damage);
78+
79+
assertNotNull("Expected a non-null TextPresentation when damage covers only end of document", presentation); //$NON-NLS-1$
80+
}
81+
82+
@Test
83+
public void testCreatePresentationWithMultilineCommentBeforeDamage() {
84+
TestableReconciler reconciler = new TestableReconciler();
85+
// Multi-line comment appears before the damage region. The reconciler must
86+
// scan from offset 0 so it can correctly determine the scanner state
87+
// (comment vs normal) at the start of the damage.
88+
String content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" //$NON-NLS-1$
89+
+ "<target name=\"test\">\n" //$NON-NLS-1$
90+
+ "<!--\n" //$NON-NLS-1$
91+
+ " multi-line comment\n" //$NON-NLS-1$
92+
+ "-->\n" //$NON-NLS-1$
93+
+ " <locations/>\n" //$NON-NLS-1$
94+
+ "</target>\n"; //$NON-NLS-1$
95+
IDocument document = new Document(content);
96+
// Damage is after the comment closes.
97+
int damageOffset = content.indexOf("<locations/>"); //$NON-NLS-1$
98+
IRegion damage = new Region(damageOffset, content.length() - damageOffset);
99+
100+
TextPresentation presentation = reconciler.createPresentation(document, damage);
101+
102+
assertNotNull("Expected a non-null TextPresentation when a multi-line comment precedes the damage", //$NON-NLS-1$
103+
presentation);
104+
}
105+
106+
@Test
107+
public void testCreatePresentationWithDamageInsideMultilineComment() {
108+
TestableReconciler reconciler = new TestableReconciler();
109+
String content = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" //$NON-NLS-1$
110+
+ "<target name=\"test\">\n" //$NON-NLS-1$
111+
+ "<!--\n" //$NON-NLS-1$
112+
+ " line one\n" //$NON-NLS-1$
113+
+ " line two\n" //$NON-NLS-1$
114+
+ "-->\n" //$NON-NLS-1$
115+
+ "</target>\n"; //$NON-NLS-1$
116+
IDocument document = new Document(content);
117+
// Damage is inside the comment body.
118+
int damageOffset = content.indexOf(" line two"); //$NON-NLS-1$
119+
IRegion damage = new Region(damageOffset, " line two\n".length()); //$NON-NLS-1$
120+
121+
TextPresentation presentation = reconciler.createPresentation(document, damage);
122+
123+
assertNotNull("Expected a non-null TextPresentation when damage is inside a multi-line comment", presentation); //$NON-NLS-1$
124+
}
125+
}

ui/org.eclipse.pde.genericeditor.extension/META-INF/MANIFEST.MF

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,6 @@ Export-Package: org.eclipse.pde.internal.genericeditor.target.extension.autocomp
2828
org.eclipse.pde.internal.genericeditor.target.extension.model.xml;x-internal:=true,
2929
org.eclipse.pde.internal.genericeditor.target.extension.p2;x-internal:=true,
3030
org.eclipse.pde.internal.genericeditor.target.extension.reconciler.folding;x-internal:=true,
31-
org.eclipse.pde.internal.genericeditor.target.extension.reconciler.presentation;x-internal:=true,
31+
org.eclipse.pde.internal.genericeditor.target.extension.reconciler.presentation;x-friends:="org.eclipse.pde.genericeditor.extension.tests",
3232
org.eclipse.pde.internal.genericeditor.target.extension.validator;x-internal:=true
3333
Automatic-Module-Name: org.eclipse.pde.genericeditor.extension

ui/org.eclipse.pde.genericeditor.extension/src/org/eclipse/pde/internal/genericeditor/target/extension/reconciler/presentation/TargetPlatformPresentationReconciler.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -109,17 +109,21 @@ private void setDamageRepairerScanner() {
109109
}
110110

111111
/**
112-
* Performs the repair on the full document to ensure MultiLineRules are
113-
* enforced
112+
* Performs the repair from the start of the document up to and including the
113+
* damaged region to ensure MultiLineRules (e.g. XML comments) are correctly
114+
* recognised. Scanning stops at the damage end rather than the document end
115+
* because the returned {@link TextPresentation} is bounded by {@code damage}
116+
* anyway, so any style ranges beyond that point would be discarded.
114117
*/
115118
@Override
116119
protected TextPresentation createPresentation(IRegion damage, IDocument document) {
117120
TextPresentation presentation = new TextPresentation(damage, 1000);
118121
IPresentationRepairer repairer = this.getRepairer(IDocument.DEFAULT_CONTENT_TYPE);
119122
if (repairer != null) {
120123
try {
124+
int scanEnd = damage.getOffset() + damage.getLength();
121125
ITypedRegion[] regions = TextUtilities.computePartitioning(document, getDocumentPartitioning(), 0,
122-
document.getLength(), false);
126+
scanEnd, false);
123127
if (regions.length > 0) {
124128
repairer.createPresentation(presentation, regions[0]);
125129
return presentation;

0 commit comments

Comments
 (0)