2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
18 * Created by IntelliJ IDEA.
22 * To change template for new class use
23 * Code Style | Class Templates options (Tools | IDE Options).
25 package com
.intellij
.openapi
.editor
.impl
;
27 import com
.intellij
.openapi
.application
.ex
.ApplicationManagerEx
;
28 import com
.intellij
.openapi
.diagnostic
.Logger
;
29 import com
.intellij
.openapi
.editor
.FoldRegion
;
30 import com
.intellij
.openapi
.editor
.FoldingGroup
;
31 import com
.intellij
.openapi
.editor
.LogicalPosition
;
32 import com
.intellij
.openapi
.editor
.VisualPosition
;
33 import com
.intellij
.openapi
.editor
.colors
.EditorColors
;
34 import com
.intellij
.openapi
.editor
.event
.DocumentEvent
;
35 import com
.intellij
.openapi
.editor
.ex
.DocumentEx
;
36 import com
.intellij
.openapi
.editor
.ex
.FoldingModelEx
;
37 import com
.intellij
.openapi
.editor
.ex
.PrioritizedDocumentListener
;
38 import com
.intellij
.openapi
.editor
.markup
.TextAttributes
;
39 import com
.intellij
.util
.ArrayUtil
;
40 import com
.intellij
.util
.containers
.CollectionFactory
;
41 import com
.intellij
.util
.containers
.MultiMap
;
42 import org
.jetbrains
.annotations
.NotNull
;
46 import java
.util
.List
;
48 public class FoldingModelImpl
implements FoldingModelEx
, PrioritizedDocumentListener
{
49 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.editor.impl.EditorFoldingModelImpl");
50 private boolean myIsFoldingEnabled
;
51 private final EditorImpl myEditor
;
52 private final FoldRegionsTree myFoldTree
;
53 private TextAttributes myFoldTextAttributes
;
54 private boolean myIsBatchFoldingProcessing
;
55 private boolean myDoNotCollapseCaret
;
56 private boolean myFoldRegionsProcessed
;
58 private int mySavedCaretX
;
59 private int mySavedCaretY
;
60 private int mySavedCaretShift
;
61 private boolean myCaretPositionSaved
;
62 private final MultiMap
<FoldingGroup
, FoldRegion
> myGroups
= new MultiMap
<FoldingGroup
, FoldRegion
>();
63 private static final Comparator
<?
super FoldRegion
> OUR_COMPARATOR
= new Comparator
<FoldRegion
>() {
64 public int compare(final FoldRegion o1
, final FoldRegion o2
) {
65 return o1
.getStartOffset() - o2
.getStartOffset();
68 private static final Comparator
<FoldRegion
> BY_END_OFFSET
= new Comparator
<FoldRegion
>() {
69 public int compare(FoldRegion r1
, FoldRegion r2
) {
70 int end1
= r1
.getEndOffset();
71 int end2
= r2
.getEndOffset();
72 if (end1
< end2
) return -1;
73 if (end1
> end2
) return 1;
77 private static final Comparator
<?
super FoldRegion
> BY_END_OFFSET_REVERSE
= Collections
.reverseOrder(BY_END_OFFSET
);
79 public FoldingModelImpl(EditorImpl editor
) {
81 myIsFoldingEnabled
= true;
82 myIsBatchFoldingProcessing
= false;
83 myDoNotCollapseCaret
= false;
84 myFoldTree
= new FoldRegionsTree();
85 myFoldRegionsProcessed
= false;
90 public List
<FoldRegion
> getGroupedRegions(@NotNull FoldingGroup group
) {
91 return (List
<FoldRegion
>)myGroups
.get(group
);
95 public FoldRegion
getFirstRegion(@NotNull FoldingGroup group
) {
96 final List
<FoldRegion
> regions
= getGroupedRegions(group
);
97 FoldRegion main
= regions
.get(0);
98 for (int i
= 1; i
< regions
.size(); i
++) {
99 FoldRegion region
= regions
.get(i
);
100 if (main
.getStartOffset() > region
.getStartOffset()) {
107 public int getEndOffset(@NotNull FoldingGroup group
) {
108 final List
<FoldRegion
> regions
= getGroupedRegions(group
);
109 int endOffset
= regions
.get(0).getEndOffset();
110 for (int i
= 1; i
< regions
.size(); i
++) {
111 if (regions
.get(i
).isValid()) {
112 endOffset
= Math
.max(endOffset
, regions
.get(i
).getEndOffset());
118 public void refreshSettings() {
119 myFoldTextAttributes
= myEditor
.getColorsScheme().getAttributes(EditorColors
.FOLDED_TEXT_ATTRIBUTES
);
122 public boolean isFoldingEnabled() {
123 return myIsFoldingEnabled
;
126 public boolean isOffsetCollapsed(int offset
) {
128 return getCollapsedRegionAtOffset(offset
) != null;
131 private void assertIsDispatchThread() {
132 ApplicationManagerEx
.getApplicationEx().assertIsDispatchThread(myEditor
.getComponent());
134 private static void assertReadAccess() {
135 ApplicationManagerEx
.getApplicationEx().assertReadAccessAllowed();
138 public void setFoldingEnabled(boolean isEnabled
) {
139 assertIsDispatchThread();
140 myIsFoldingEnabled
= isEnabled
;
143 public FoldRegion
addFoldRegion(int startOffset
, int endOffset
, @NotNull String placeholderText
) {
144 final FoldRegionImpl region
= new FoldRegionImpl(myEditor
, startOffset
, endOffset
, placeholderText
, null);
145 return addFoldRegion(region
) ? region
: null;
148 public boolean addFoldRegion(@NotNull final FoldRegion region
) {
149 assertIsDispatchThread();
150 if (isFoldingEnabled()) {
151 if (!myIsBatchFoldingProcessing
) {
152 LOG
.error("Fold regions must be added or removed inside batchFoldProcessing() only.");
156 myFoldRegionsProcessed
= true;
157 if (myFoldTree
.addRegion(region
)) {
158 final FoldingGroup group
= region
.getGroup();
160 myGroups
.putValue(group
, region
);
169 public void runBatchFoldingOperation(@NotNull Runnable operation
) {
170 runBatchFoldingOperation(operation
, false);
173 private void runBatchFoldingOperation(final Runnable operation
, final boolean dontCollapseCaret
) {
174 assertIsDispatchThread();
175 boolean oldDontCollapseCaret
= myDoNotCollapseCaret
;
176 myDoNotCollapseCaret
|= dontCollapseCaret
;
177 boolean oldBatchFlag
= myIsBatchFoldingProcessing
;
179 mySavedCaretShift
= myEditor
.visibleLineNumberToYPosition(myEditor
.getCaretModel().getVisualPosition().line
) - myEditor
.getScrollingModel().getVerticalScrollOffset();
182 myIsBatchFoldingProcessing
= true;
183 myFoldTree
.myCachedLastIndex
= -1;
185 myFoldTree
.myCachedLastIndex
= -1;
188 if (myFoldRegionsProcessed
) {
189 notifyBatchFoldingProcessingDone();
190 myFoldRegionsProcessed
= false;
192 myIsBatchFoldingProcessing
= false;
194 myDoNotCollapseCaret
= oldDontCollapseCaret
;
197 public void runBatchFoldingOperationDoNotCollapseCaret(@NotNull final Runnable operation
) {
198 runBatchFoldingOperation(operation
, true);
201 public void flushCaretShift() {
202 mySavedCaretShift
= -1;
206 public FoldRegion
[] getAllFoldRegions() {
208 return myFoldTree
.fetchAllRegions();
211 public FoldRegion
getCollapsedRegionAtOffset(int offset
) {
212 return myFoldTree
.fetchOutermost(offset
);
215 int getLastTopLevelIndexBefore (int offset
) {
216 return myFoldTree
.getLastTopLevelIndexBefore(offset
);
219 public FoldRegion
getFoldingPlaceholderAt(Point p
) {
221 LogicalPosition pos
= myEditor
.xyToLogicalPosition(p
);
224 if (line
>= myEditor
.getDocument().getLineCount()) return null;
226 //leftmost folded block position
227 if (myEditor
.xyToVisualPosition(p
).equals(myEditor
.logicalToVisualPosition(pos
))) return null;
229 int offset
= myEditor
.logicalPositionToOffset(pos
);
231 return myFoldTree
.fetchOutermost(offset
);
234 public FoldRegion
[] getAllFoldRegionsIncludingInvalid() {
236 return myFoldTree
.fetchAllRegionsIncludingInvalid();
239 public void removeFoldRegion(@NotNull final FoldRegion region
) {
240 assertIsDispatchThread();
242 if (!myIsBatchFoldingProcessing
) {
243 LOG
.error("Fold regions must be added or removed inside batchFoldProcessing() only.");
246 region
.setExpanded(true);
247 final FoldingGroup group
= region
.getGroup();
249 myGroups
.removeValue(group
, region
);
251 myFoldTree
.removeRegion(region
);
252 myFoldRegionsProcessed
= true;
255 public void expandFoldRegion(FoldRegion region
) {
256 assertIsDispatchThread();
257 if (region
.isExpanded()) return;
259 if (!myIsBatchFoldingProcessing
) {
260 LOG
.error("Fold regions must be collapsed or expanded inside batchFoldProcessing() only.");
263 if (myCaretPositionSaved
) {
264 int savedOffset
= myEditor
.logicalPositionToOffset(new LogicalPosition(mySavedCaretY
, mySavedCaretX
));
266 FoldRegion
[] allCollapsed
= myFoldTree
.fetchCollapsedAt(savedOffset
);
267 if (allCollapsed
.length
== 1 && allCollapsed
[0] == region
) {
268 LogicalPosition pos
= new LogicalPosition(mySavedCaretY
, mySavedCaretX
);
269 myEditor
.getCaretModel().moveToLogicalPosition(pos
);
273 myFoldRegionsProcessed
= true;
274 ((FoldRegionImpl
) region
).setExpandedInternal(true);
277 public void collapseFoldRegion(FoldRegion region
) {
278 assertIsDispatchThread();
279 if (!region
.isExpanded()) return;
281 if (!myIsBatchFoldingProcessing
) {
282 LOG
.error("Fold regions must be collapsed or expanded inside batchFoldProcessing() only.");
285 LogicalPosition caretPosition
= myEditor
.getCaretModel().getLogicalPosition();
287 int caretOffset
= myEditor
.logicalPositionToOffset(caretPosition
);
289 if (myFoldTree
.contains(region
, caretOffset
)) {
290 if (myDoNotCollapseCaret
) return;
292 if (!myCaretPositionSaved
) {
293 mySavedCaretX
= caretPosition
.column
;
294 mySavedCaretY
= caretPosition
.line
;
295 myCaretPositionSaved
= true;
299 int selectionStart
= myEditor
.getSelectionModel().getSelectionStart();
300 int selectionEnd
= myEditor
.getSelectionModel().getSelectionEnd();
302 if (myFoldTree
.contains(region
, selectionStart
-1) || myFoldTree
.contains(region
, selectionEnd
)) myEditor
.getSelectionModel().removeSelection();
304 myFoldRegionsProcessed
= true;
305 ((FoldRegionImpl
) region
).setExpandedInternal(false);
308 private void notifyBatchFoldingProcessingDone() {
309 myFoldTree
.rebuild();
311 myEditor
.updateCaretCursor();
312 myEditor
.recalcSizeAndRepaint();
313 if (myEditor
.getGutterComponentEx().isFoldingOutlineShown()) {
314 myEditor
.getGutterComponentEx().repaint();
317 LogicalPosition caretPosition
= myEditor
.getCaretModel().getLogicalPosition();
318 int caretOffset
= myEditor
.logicalPositionToOffset(caretPosition
);
319 boolean hasBlockSelection
= myEditor
.getSelectionModel().hasBlockSelection();
320 int selectionStart
= myEditor
.getSelectionModel().getSelectionStart();
321 int selectionEnd
= myEditor
.getSelectionModel().getSelectionEnd();
326 FoldRegion collapsed
= myFoldTree
.fetchOutermost(caretOffset
);
327 if (myCaretPositionSaved
) {
328 int savedOffset
= myEditor
.logicalPositionToOffset(new LogicalPosition(mySavedCaretY
, mySavedCaretX
));
329 FoldRegion collapsedAtSaved
= myFoldTree
.fetchOutermost(savedOffset
);
330 column
= mySavedCaretX
;
331 line
= collapsedAtSaved
!= null ? collapsedAtSaved
.getDocument().getLineNumber(collapsedAtSaved
.getStartOffset()) : mySavedCaretY
;
334 if (collapsed
!= null && column
== -1) {
335 line
= collapsed
.getDocument().getLineNumber(collapsed
.getStartOffset());
336 column
= myEditor
.getCaretModel().getVisualPosition().column
;
339 boolean oldCaretPositionSaved
= myCaretPositionSaved
;
342 LogicalPosition log
= new LogicalPosition(line
, 0);
343 VisualPosition vis
= myEditor
.logicalToVisualPosition(log
);
344 VisualPosition pos
= new VisualPosition(vis
.line
, column
);
345 myEditor
.getCaretModel().moveToVisualPosition(pos
);
347 myEditor
.getCaretModel().moveToLogicalPosition(caretPosition
);
350 myCaretPositionSaved
= oldCaretPositionSaved
;
352 if (!hasBlockSelection
) {
353 myEditor
.getSelectionModel().setSelection(selectionStart
, selectionEnd
);
356 if (mySavedCaretShift
> 0) {
357 myEditor
.getScrollingModel().disableAnimation();
358 int scrollTo
= myEditor
.visibleLineNumberToYPosition(myEditor
.getCaretModel().getVisualPosition().line
) - mySavedCaretShift
;
359 myEditor
.getScrollingModel().scrollVertically(scrollTo
);
360 myEditor
.getScrollingModel().enableAnimation();
364 public void rebuild() {
365 myFoldTree
.rebuild();
368 private void updateCachedOffsets() {
369 myFoldTree
.updateCachedOffsets();
372 public int getFoldedLinesCountBefore(int offset
) {
373 return myFoldTree
.getFoldedLinesCountBefore(offset
);
376 public FoldRegion
[] fetchTopLevel() {
377 return myFoldTree
.fetchTopLevel();
380 public FoldRegion
fetchOutermost(int offset
) {
381 return myFoldTree
.fetchOutermost(offset
);
384 public FoldRegion
[] fetchCollapsedAt(int offset
) {
385 return myFoldTree
.fetchCollapsedAt(offset
);
388 public boolean intersectsRegion (int startOffset
, int endOffset
) {
389 return myFoldTree
.intersectsRegion(startOffset
, endOffset
);
392 public FoldRegion
[] fetchVisible() {
393 return myFoldTree
.fetchVisible();
396 public int getLastCollapsedRegionBefore(int offset
) {
397 return myFoldTree
.getLastTopLevelIndexBefore(offset
);
400 public TextAttributes
getPlaceholderAttributes() {
401 return myFoldTextAttributes
;
404 public void flushCaretPosition() {
405 myCaretPositionSaved
= false;
408 class FoldRegionsTree
{
409 private FoldRegion
[] myCachedVisible
;
410 private FoldRegion
[] myCachedTopLevelRegions
;
411 private int[] myCachedEndOffsets
;
412 private int[] myCachedStartOffsets
;
413 private int[] myCachedFoldedLines
;
414 private int myCachedLastIndex
= -1;
415 private ArrayList
<FoldRegion
> myRegions
= CollectionFactory
.arrayList(); //sorted in tree left-to-right topdown traversal order
417 private void clear() {
418 myCachedVisible
= null;
419 myCachedTopLevelRegions
= null;
420 myCachedEndOffsets
= null;
421 myCachedStartOffsets
= null;
422 myCachedFoldedLines
= null;
423 myRegions
= new ArrayList
<FoldRegion
>();
426 private boolean isFoldingEnabled() {
427 return FoldingModelImpl
.this.isFoldingEnabled() && myCachedVisible
!= null;
431 ArrayList
<FoldRegion
> topLevels
= new ArrayList
<FoldRegion
>(myRegions
.size() / 2);
432 ArrayList
<FoldRegion
> visible
= new ArrayList
<FoldRegion
>(myRegions
.size());
433 FoldRegion
[] regions
= myRegions
.toArray(new FoldRegion
[myRegions
.size()]);
434 FoldRegion currentToplevel
= null;
435 for (FoldRegion region
: regions
) {
436 if (region
.isValid()) {
438 if (!region
.isExpanded()) {
439 if (currentToplevel
== null || currentToplevel
.getEndOffset() < region
.getStartOffset()) {
440 currentToplevel
= region
;
441 topLevels
.add(region
);
447 myCachedTopLevelRegions
= topLevels
.isEmpty() ? FoldRegion
.EMPTY_ARRAY
: topLevels
.toArray(new FoldRegion
[topLevels
.size()]);
449 Arrays
.sort(myCachedTopLevelRegions
, BY_END_OFFSET
);
451 FoldRegion
[] visibleArrayed
= visible
.toArray(new FoldRegion
[visible
.size()]);
452 for (FoldRegion visibleRegion
: visibleArrayed
) {
453 for (FoldRegion topLevelRegion
: myCachedTopLevelRegions
) {
454 if (contains(topLevelRegion
, visibleRegion
)) {
455 visible
.remove(visibleRegion
);
461 myCachedVisible
= visible
.toArray(new FoldRegion
[visible
.size()]);
463 Arrays
.sort(myCachedVisible
, BY_END_OFFSET_REVERSE
);
465 updateCachedOffsets();
468 void updateCachedOffsets() {
469 if (FoldingModelImpl
.this.isFoldingEnabled()) {
470 if (myCachedVisible
== null) {
475 for (FoldRegion foldRegion
: myCachedVisible
) {
476 if (!foldRegion
.isValid()) {
482 int length
= myCachedTopLevelRegions
.length
;
483 if (myCachedEndOffsets
== null || myCachedEndOffsets
.length
!= length
) {
485 myCachedEndOffsets
= new int[length
];
486 myCachedStartOffsets
= new int[length
];
487 myCachedFoldedLines
= new int[length
];
490 myCachedEndOffsets
= ArrayUtil
.EMPTY_INT_ARRAY
;
491 myCachedStartOffsets
= ArrayUtil
.EMPTY_INT_ARRAY
;
492 myCachedFoldedLines
= ArrayUtil
.EMPTY_INT_ARRAY
;
497 for (int i
= 0; i
< length
; i
++) {
498 FoldRegion region
= myCachedTopLevelRegions
[i
];
499 myCachedStartOffsets
[i
] = region
.getStartOffset();
500 myCachedEndOffsets
[i
] = region
.getEndOffset() - 1;
501 sum
+= region
.getDocument().getLineNumber(region
.getEndOffset()) - region
.getDocument().getLineNumber(region
.getStartOffset());
502 myCachedFoldedLines
[i
] = sum
;
507 boolean addRegion(FoldRegion range
) {
508 // During batchProcessing elements are inserted in ascending order,
509 // binary search find acceptable insertion place first time
510 int fastIndex
= myCachedLastIndex
!= -1 && myIsBatchFoldingProcessing? myCachedLastIndex
+ 1:Collections
.binarySearch(myRegions
, range
, OUR_COMPARATOR
);
511 if (fastIndex
< 0) fastIndex
= -fastIndex
- 1;
513 for (int i
= fastIndex
- 1; i
>=0; --i
) {
514 final FoldRegion region
= myRegions
.get(i
);
515 if (region
.getEndOffset() < range
.getStartOffset()) break;
516 if (region
.isValid() && intersects(region
, range
)) {
521 for (int i
= fastIndex
; i
< myRegions
.size(); i
++) {
522 final FoldRegion region
= myRegions
.get(i
);
524 if (range
.getStartOffset() < region
.getStartOffset() ||
525 range
.getStartOffset() == region
.getStartOffset() && range
.getEndOffset() > region
.getEndOffset()) {
526 for (int j
= i
+ 1; j
< myRegions
.size(); j
++) {
527 final FoldRegion next
= myRegions
.get(j
);
528 if (next
.getEndOffset() >= range
.getEndOffset() && next
.isValid()) {
529 if (next
.getStartOffset() < range
.getStartOffset()) {
538 myRegions
.add(myCachedLastIndex
= i
, range
);
542 myRegions
.add(myCachedLastIndex
= myRegions
.size(),range
);
546 FoldRegion
fetchOutermost(int offset
) {
547 if (!isFoldingEnabled()) return null;
549 final int[] starts
= myCachedStartOffsets
;
550 final int[] ends
= myCachedEndOffsets
;
553 int end
= ends
.length
- 1;
555 while (start
<= end
) {
556 int i
= (start
+ end
) / 2;
557 if (offset
< starts
[i
]) {
559 } else if (offset
> ends
[i
]) {
563 return myCachedTopLevelRegions
[i
];
570 FoldRegion
[] fetchVisible() {
571 if (!isFoldingEnabled()) return FoldRegion
.EMPTY_ARRAY
;
572 return myCachedVisible
;
575 FoldRegion
[] fetchTopLevel() {
576 if (!isFoldingEnabled()) return null;
577 return myCachedTopLevelRegions
;
580 private boolean contains(FoldRegion outer
, FoldRegion inner
) {
581 return outer
.getStartOffset() < inner
.getStartOffset() && outer
.getEndOffset() > inner
.getStartOffset();
584 private boolean intersects(FoldRegion r1
, FoldRegion r2
) {
585 final int s1
= r1
.getStartOffset();
586 final int s2
= r2
.getStartOffset();
587 final int e1
= r1
.getEndOffset();
588 final int e2
= r2
.getEndOffset();
589 return s1
== s2
&& e1
== e2
|| s1
< s2
&& s2
< e1
&& e1
< e2
|| s2
< s1
&& s1
< e2
&& e2
< e1
;
592 private boolean contains(FoldRegion region
, int offset
) {
593 return region
.getStartOffset() < offset
&& region
.getEndOffset() > offset
;
596 public FoldRegion
[] fetchCollapsedAt(int offset
) {
597 if (!isFoldingEnabled()) return FoldRegion
.EMPTY_ARRAY
;
598 ArrayList
<FoldRegion
> allCollapsed
= new ArrayList
<FoldRegion
>();
599 for (FoldRegion region
: myRegions
) {
600 if (!region
.isExpanded() && contains(region
, offset
)) {
601 allCollapsed
.add(region
);
605 return allCollapsed
.toArray(new FoldRegion
[allCollapsed
.size()]);
608 boolean intersectsRegion(int startOffset
, int endOffset
) {
609 if (!FoldingModelImpl
.this.isFoldingEnabled()) return true;
610 for (FoldRegion region
: myRegions
) {
611 boolean contains1
= contains(region
, startOffset
);
612 boolean contains2
= contains(region
, endOffset
);
613 if (contains1
!= contains2
) {
620 FoldRegion
[] fetchAllRegions() {
621 if (!isFoldingEnabled()) return FoldRegion
.EMPTY_ARRAY
;
623 return myRegions
.toArray(new FoldRegion
[myRegions
.size()]);
626 void removeRegion(FoldRegion range
) {
627 myRegions
.remove(range
);
630 int getFoldedLinesCountBefore(int offset
) {
631 int idx
= getLastTopLevelIndexBefore(offset
);
632 if (idx
== -1) return 0;
633 return myCachedFoldedLines
[idx
];
636 public int getLastTopLevelIndexBefore(int offset
) {
637 if (!isFoldingEnabled()) return -1;
640 int end
= myCachedEndOffsets
.length
- 1;
642 while (start
<= end
) {
643 int i
= (start
+ end
) / 2;
644 if (offset
< myCachedEndOffsets
[i
]) {
646 } else if (offset
> myCachedEndOffsets
[i
]) {
656 // for (int i = 0; i < myCachedEndOffsets.length; i++) {
657 // if (!myCachedTopLevelRegions[i].isValid()) continue;
658 // int endOffset = myCachedEndOffsets[i];
659 // if (endOffset > offset) break;
666 public FoldRegion
[] fetchAllRegionsIncludingInvalid() {
667 if (!FoldingModelImpl
.this.isFoldingEnabled()) return FoldRegion
.EMPTY_ARRAY
;
669 return myRegions
.toArray(new FoldRegion
[myRegions
.size()]);
673 public void beforeDocumentChange(DocumentEvent event
) {
676 public void documentChanged(DocumentEvent event
) {
677 if (((DocumentEx
)event
.getDocument()).isInBulkUpdate()) {
680 updateCachedOffsets();
684 public int getPriority() {