Skip to content

Commit 8816020

Browse files
GSF/CSL Languages: Centralize "Enable Occurrences Hightlight" and "Keep Marks" Handling
According to the documentation of OccurrencesFinder the occurrences scanning should be stateless. The current API holds a calculate and a getter method, but in the spirit of keeping it mostly stateless, more state should not be added. The OccurrencesFinder implementation returns a set of occurrenes corresponding to the currently highlighted area. These areas are described by integer arrays (2 elements, start and end index). Given the return types these areas are only valid until the next edit occurrs. On each input the highlights are recalculated. That works fine until "Keep Marks" is activated. "Keep Marks" keeps the marks, when the caret is moved of a highlightable variable. If "Keep Marks" is enabled the marks will be retained then until the caret is placed onto another highlightable element. The marks in this case must adapt to changes of the content of the element. Swing Document provides the "Position" class for this. That class is used by GsfSemanticLayer to hold the areas returned by the OccurrencesFinder implementation. So to support "Keep Marks" the CSL/GSF layer has to be aware of this, so that the GsfSemanticLayer can retain the highlights. In addition to this there is no need to scan for occurrences if the highlighting is disabled, so that also moves to the CSL/GSF layer. Closes: #8803
1 parent 3d84015 commit 8816020

File tree

6 files changed

+114
-77
lines changed

6 files changed

+114
-77
lines changed

ide/csl.api/apichanges.xml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@
2525
<apidef name="csl.api">Common Scripting Language API</apidef>
2626
</apidefs>
2727
<changes>
28+
<change id="OccurrencesFinder.KeepMarks">
29+
<api name="csl.api"/>
30+
<summary>Enable controlling highlighting of occurrences</summary>
31+
<version major="2" minor="88"/>
32+
<date day="17" month="9" year="2025"/>
33+
<author login="matthiasblaesing"/>
34+
<compatibility addition="yes" deletion="no" modification="no" binary="compatible" source="compatible" deprecation="no"/>
35+
<description>
36+
<p>
37+
Add two accessors <code>isMarkOccurrencesEnabled</code> and
38+
<code>isKeepMarks</code> to <code>OccurrencesFinder</code>. The
39+
intention is, that implementors of <code>OccurrencesFinder</code>
40+
can indicate, whether highlighting of occurrenes is enabled and
41+
if highlights should be retained, after the caret is moved off
42+
the highlighted item.
43+
</p>
44+
</description>
45+
<class package="org.netbeans.modules.csl.api" name="OccurrencesFinder"/>
46+
</change>
2847
<change id="StructureItem.InheritedItem">
2948
<api name="csl.api"/>
3049
<summary>Adding a new StructureItem.InheritedItem interface</summary>

ide/csl.api/src/org/netbeans/modules/csl/api/OccurrencesFinder.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,28 @@ public abstract class OccurrencesFinder<T extends Parser.Result> extends ParserR
4949
* {@link #run}.
5050
*/
5151
public abstract @NonNull Map<OffsetRange, ColoringAttributes> getOccurrences();
52+
53+
/**
54+
* Control occurrence highlight visibility.
55+
*
56+
* @return {@code false} to disable occurrence highlighting. Default value
57+
* is {@code true}.
58+
*
59+
* @since 2.88.0
60+
*/
61+
public boolean isMarkOccurrencesEnabled() {
62+
return true;
63+
}
64+
65+
/**
66+
* Control retaining highlights when carret is moved off a highlightable
67+
* element. If {@code true} highlights will be retained if carret is moved
68+
* off a highlightable element, if {@code false} the highlights will be
69+
* removed.
70+
*
71+
* @since 2.88.0
72+
*/
73+
public boolean isKeepMarks() {
74+
return true;
75+
}
5276
}

ide/csl.api/src/org/netbeans/modules/csl/editor/semantic/MarkOccurrencesHighlighter.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package org.netbeans.modules.csl.editor.semantic;
2020

2121
import java.awt.Color;
22+
import java.util.Collections;
2223
import java.util.List;
2324
import java.util.Map;
2425
import java.util.SortedSet;
@@ -107,11 +108,10 @@ public void run(ParserResult info, SchedulerEvent event) {
107108
return;
108109
}
109110

110-
List<OffsetRange> bag = processImpl(info, doc, caretPosition);
111-
if (cancel.isCancelled()) {
112-
//the occurrences finder haven't found anything, just ignore the result
113-
//and keep the previous occurrences
114-
return ;
111+
List<OffsetRange> bag = Collections.emptyList();
112+
113+
if (snapshotOffset >= 0) {
114+
bag = processImpl(info, doc, caretPosition);
115115
}
116116

117117
GsfSemanticLayer layer = GsfSemanticLayer.getLayer(MarkOccurrencesHighlighter.class, doc);
@@ -124,9 +124,22 @@ public void run(ParserResult info, SchedulerEvent event) {
124124
} catch (BadLocationException ex) {}
125125
}
126126
}
127-
layer.setColorings(seqs);
128127

129-
OccurrencesMarkProvider.get(doc).setOccurrences(OccurrencesMarkProvider.createMarks(doc, bag, ES_COLOR, NbBundle.getMessage(MarkOccurrencesHighlighter.class, "LBL_ES_TOOLTIP")));
128+
boolean updateHighlights = false;
129+
if(seqs.isEmpty()) {
130+
OccurrencesFinder finder = language.getOccurrencesFinder();
131+
if (finder == null || !finder.isMarkOccurrencesEnabled() || !finder.isKeepMarks()) {
132+
updateHighlights = true;
133+
}
134+
} else {
135+
updateHighlights = true;
136+
}
137+
138+
if(updateHighlights) {
139+
// Update both editor highlighs and sidebar marks
140+
layer.setColorings(seqs);
141+
OccurrencesMarkProvider.get(doc).setOccurrences(OccurrencesMarkProvider.createMarks(doc, bag, ES_COLOR, NbBundle.getMessage(MarkOccurrencesHighlighter.class, "LBL_ES_TOOLTIP")));
142+
}
130143
} finally {
131144
SpiSupportAccessor.getInstance().removeCancelSupport(cancel);
132145
}
@@ -135,7 +148,7 @@ public void run(ParserResult info, SchedulerEvent event) {
135148
@NonNull
136149
List<OffsetRange> processImpl(ParserResult info, Document doc, int caretPosition) {
137150
OccurrencesFinder finder = language.getOccurrencesFinder();
138-
if(finder == null) {
151+
if (finder == null || !finder.isMarkOccurrencesEnabled()) {
139152
return List.of();
140153
}
141154

php/php.editor/src/org/netbeans/modules/php/editor/csl/OccurrencesFinderImpl.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import java.util.Map;
2727
import java.util.Set;
2828
import java.util.TreeSet;
29-
import java.util.prefs.Preferences;
3029
import org.netbeans.api.lexer.Token;
3130
import org.netbeans.api.lexer.TokenHierarchy;
3231
import org.netbeans.api.lexer.TokenSequence;
@@ -81,9 +80,8 @@ public void run(PHPParseResult result, SchedulerEvent event) {
8180
return;
8281
}
8382

84-
Preferences node = MarkOccurencesSettings.getCurrentNode();
8583
Map<OffsetRange, ColoringAttributes> localRange2Attribs = new HashMap<>();
86-
if (node.getBoolean(MarkOccurencesSettings.ON_OFF, true)) {
84+
if (isMarkOccurrencesEnabled()) {
8785
for (OffsetRange r : compute(result, caretPosition)) {
8886
localRange2Attribs.put(r, ColoringAttributes.MARK_OCCURRENCES);
8987
}
@@ -94,14 +92,22 @@ public void run(PHPParseResult result, SchedulerEvent event) {
9492
return;
9593
}
9694

97-
if (!localRange2Attribs.isEmpty()) {
98-
// store the new occurrences if any were found
99-
range2Attribs = Collections.unmodifiableMap(localRange2Attribs);
100-
} else if (!node.getBoolean(MarkOccurencesSettings.KEEP_MARKS, true)) {
101-
// clear occurrences if "Keep Marks" is not selected. If "Keep Marks"
102-
// is enabled, the old ranges must be retained.
103-
range2Attribs = Collections.emptyMap();
104-
}
95+
// store the new occurrences if any were found
96+
range2Attribs = Collections.unmodifiableMap(localRange2Attribs);
97+
}
98+
99+
@Override
100+
public boolean isKeepMarks() {
101+
return MarkOccurencesSettings
102+
.getCurrentNode()
103+
.getBoolean(MarkOccurencesSettings.KEEP_MARKS, true);
104+
}
105+
106+
@Override
107+
public boolean isMarkOccurrencesEnabled() {
108+
return MarkOccurencesSettings
109+
.getCurrentNode()
110+
.getBoolean(MarkOccurencesSettings.ON_OFF, true);
105111
}
106112

107113
private Collection<OffsetRange> compute(final PHPParseResult parseResult, final int offset) {

php/php.editor/src/org/netbeans/modules/php/editor/options/MarkOccurencesPanel.form

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -50,38 +50,10 @@
5050
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
5151
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
5252
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
53+
<AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,75,0,0,2,18"/>
5354
</AuxValues>
5455

55-
<Layout>
56-
<DimensionLayout dim="0">
57-
<Group type="103" groupAlignment="0" attributes="0">
58-
<Group type="102" attributes="0">
59-
<Group type="103" groupAlignment="0" attributes="0">
60-
<Group type="102" attributes="0">
61-
<EmptySpace max="-2" attributes="0"/>
62-
<Component id="onOffCheckBox" min="-2" max="-2" attributes="0"/>
63-
</Group>
64-
<Group type="102" alignment="0" attributes="0">
65-
<EmptySpace min="-2" pref="37" max="-2" attributes="0"/>
66-
<Component id="keepMarksCheckBox" min="-2" max="-2" attributes="0"/>
67-
</Group>
68-
</Group>
69-
<EmptySpace pref="226" max="32767" attributes="0"/>
70-
</Group>
71-
</Group>
72-
</DimensionLayout>
73-
<DimensionLayout dim="1">
74-
<Group type="103" groupAlignment="0" attributes="0">
75-
<Group type="102" alignment="0" attributes="0">
76-
<EmptySpace max="-2" attributes="0"/>
77-
<Component id="onOffCheckBox" min="-2" max="-2" attributes="0"/>
78-
<EmptySpace type="unrelated" max="-2" attributes="0"/>
79-
<Component id="keepMarksCheckBox" min="-2" max="-2" attributes="0"/>
80-
<EmptySpace pref="237" max="32767" attributes="0"/>
81-
</Group>
82-
</Group>
83-
</DimensionLayout>
84-
</Layout>
56+
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
8557
<SubComponents>
8658
<Component class="javax.swing.JCheckBox" name="onOffCheckBox">
8759
<Properties>
@@ -102,6 +74,11 @@
10274
<ResourceString bundle="org/netbeans/modules/php/editor/options/Bundle.properties" key="ACSD_OnOff_CB" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
10375
</Property>
10476
</AccessibilityProperties>
77+
<Constraints>
78+
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
79+
<GridBagConstraints gridX="0" gridY="0" gridWidth="0" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="12" insetsRight="0" anchor="18" weightX="1.0" weightY="0.0"/>
80+
</Constraint>
81+
</Constraints>
10582
</Component>
10683
<Component class="javax.swing.JCheckBox" name="keepMarksCheckBox">
10784
<Properties>
@@ -110,6 +87,11 @@
11087
<ResourceString bundle="org/netbeans/modules/php/editor/options/Bundle.properties" key="MarkOccurencesPanel.keepMarksCheckBox.text" replaceFormat="org.openide.util.NbBundle.getMessage({sourceFileName}.class, &quot;{key}&quot;)"/>
11188
</Property>
11289
</Properties>
90+
<Constraints>
91+
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
92+
<GridBagConstraints gridX="0" gridY="1" gridWidth="0" gridHeight="0" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="20" insetsBottom="8" insetsRight="0" anchor="18" weightX="0.0" weightY="1.0"/>
93+
</Constraint>
94+
</Constraints>
11395
</Component>
11496
</SubComponents>
11597
</Form>

php/php.editor/src/org/netbeans/modules/php/editor/options/MarkOccurencesPanel.java

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ public boolean changed() {
9797
*/
9898
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
9999
private void initComponents() {
100+
java.awt.GridBagConstraints gridBagConstraints;
100101

101102
onOffCheckBox = new javax.swing.JCheckBox();
102103
keepMarksCheckBox = new javax.swing.JCheckBox();
@@ -124,40 +125,32 @@ public java.awt.Component getComponentBefore(java.awt.Container focusCycleRoot,
124125

125126
}}
126127
);
128+
setLayout(new java.awt.GridBagLayout());
127129

128130
org.openide.awt.Mnemonics.setLocalizedText(onOffCheckBox, org.openide.util.NbBundle.getMessage(MarkOccurencesPanel.class, "CTL_OnOff_CheckBox")); // NOI18N
129131
onOffCheckBox.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
130-
131-
keepMarksCheckBox.setMnemonic('s');
132-
org.openide.awt.Mnemonics.setLocalizedText(keepMarksCheckBox, org.openide.util.NbBundle.getMessage(MarkOccurencesPanel.class, "MarkOccurencesPanel.keepMarksCheckBox.text")); // NOI18N
133-
134-
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
135-
this.setLayout(layout);
136-
layout.setHorizontalGroup(
137-
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
138-
.addGroup(layout.createSequentialGroup()
139-
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
140-
.addGroup(layout.createSequentialGroup()
141-
.addContainerGap()
142-
.addComponent(onOffCheckBox))
143-
.addGroup(layout.createSequentialGroup()
144-
.addGap(37, 37, 37)
145-
.addComponent(keepMarksCheckBox)))
146-
.addContainerGap(226, Short.MAX_VALUE))
147-
);
148-
layout.setVerticalGroup(
149-
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
150-
.addGroup(layout.createSequentialGroup()
151-
.addContainerGap()
152-
.addComponent(onOffCheckBox)
153-
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
154-
.addComponent(keepMarksCheckBox)
155-
.addContainerGap(237, Short.MAX_VALUE))
156-
);
157-
132+
gridBagConstraints = new java.awt.GridBagConstraints();
133+
gridBagConstraints.gridx = 0;
134+
gridBagConstraints.gridy = 0;
135+
gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
136+
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
137+
gridBagConstraints.weightx = 1.0;
138+
gridBagConstraints.insets = new java.awt.Insets(0, 0, 12, 0);
139+
add(onOffCheckBox, gridBagConstraints);
158140
onOffCheckBox.getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(MarkOccurencesPanel.class, "MarkOccurrencesPanel.onOffCheckBox.AccessibleContext.accessibleName")); // NOI18N
159141
onOffCheckBox.getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(MarkOccurencesPanel.class, "ACSD_OnOff_CB")); // NOI18N
160142

143+
org.openide.awt.Mnemonics.setLocalizedText(keepMarksCheckBox, org.openide.util.NbBundle.getMessage(MarkOccurencesPanel.class, "MarkOccurencesPanel.keepMarksCheckBox.text")); // NOI18N
144+
gridBagConstraints = new java.awt.GridBagConstraints();
145+
gridBagConstraints.gridx = 0;
146+
gridBagConstraints.gridy = 1;
147+
gridBagConstraints.gridwidth = java.awt.GridBagConstraints.REMAINDER;
148+
gridBagConstraints.gridheight = java.awt.GridBagConstraints.REMAINDER;
149+
gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTHWEST;
150+
gridBagConstraints.weighty = 1.0;
151+
gridBagConstraints.insets = new java.awt.Insets(0, 20, 8, 0);
152+
add(keepMarksCheckBox, gridBagConstraints);
153+
161154
getAccessibleContext().setAccessibleName(org.openide.util.NbBundle.getMessage(MarkOccurencesPanel.class, "MarkOccurrencesPanel.AccessibleContext.accessibleName")); // NOI18N
162155
getAccessibleContext().setAccessibleDescription(org.openide.util.NbBundle.getMessage(MarkOccurencesPanel.class, "MarkOccurrencesPanel.AccessibleContext.accessibleDescription")); // NOI18N
163156
}// </editor-fold>//GEN-END:initComponents

0 commit comments

Comments
 (0)