optimization: replace all occurrence is dramatically faster
[fedora-idea.git] / platform-impl / src / com / intellij / openapi / editor / impl / DocumentImpl.java
blob549b96bdff8d380b59565f855db87f97e2a3cb24
1 package com.intellij.openapi.editor.impl;
3 import com.intellij.openapi.Disposable;
4 import com.intellij.openapi.application.Application;
5 import com.intellij.openapi.application.ApplicationManager;
6 import com.intellij.openapi.application.ex.ApplicationManagerEx;
7 import com.intellij.openapi.command.CommandProcessor;
8 import com.intellij.openapi.diagnostic.Logger;
9 import com.intellij.openapi.editor.*;
10 import com.intellij.openapi.editor.event.DocumentEvent;
11 import com.intellij.openapi.editor.event.DocumentListener;
12 import com.intellij.openapi.editor.ex.*;
13 import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter;
14 import com.intellij.openapi.editor.highlighter.EditorHighlighter;
15 import com.intellij.openapi.editor.impl.event.DocumentEventImpl;
16 import com.intellij.openapi.editor.markup.MarkupModel;
17 import com.intellij.openapi.project.Project;
18 import com.intellij.openapi.util.Disposer;
19 import com.intellij.openapi.util.Key;
20 import com.intellij.openapi.util.TextRange;
21 import com.intellij.openapi.util.UserDataHolderBase;
22 import com.intellij.openapi.util.text.StringUtil;
23 import com.intellij.util.ConcurrencyUtil;
24 import com.intellij.util.LocalTimeCounter;
25 import com.intellij.util.containers.ConcurrentHashMap;
26 import com.intellij.util.text.CharArrayUtil;
27 import org.jetbrains.annotations.NotNull;
28 import org.jetbrains.annotations.Nullable;
30 import java.beans.PropertyChangeListener;
31 import java.beans.PropertyChangeSupport;
32 import java.lang.ref.WeakReference;
33 import java.util.*;
34 import java.util.concurrent.ConcurrentMap;
36 public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
37 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.DocumentImpl");
39 private final List<DocumentListener> myDocumentListeners = new ArrayList<DocumentListener>();
40 private final WeakHashMap<RangeMarkerEx,String> myRangeMarkers = new WeakHashMap<RangeMarkerEx, String>();
41 private final List<RangeMarker> myGuardedBlocks = new ArrayList<RangeMarker>();
43 private final LineSet myLineSet = new LineSet();
44 private final CharArray myText = new CharArray(0) {
45 protected DocumentEvent beforeChangedUpdate(int offset, CharSequence oldString, CharSequence newString, boolean wholeTextReplaced) {
46 return DocumentImpl.this.beforeChangedUpdate(offset, oldString, newString, wholeTextReplaced);
49 protected void afterChangedUpdate(DocumentEvent event, long newModificationStamp) {
50 changedUpdate(event, newModificationStamp);
54 private boolean myIsReadOnly = false;
55 private boolean isStripTrailingSpacesEnabled = true;
56 private volatile long myModificationStamp;
57 private final ConcurrentMap<Project, MarkupModel> myProjectToMarkupModelMap = new ConcurrentHashMap<Project, MarkupModel>();
58 private final PropertyChangeSupport myPropertyChangeSupport = new PropertyChangeSupport(this);
60 private volatile MarkupModelEx myMarkupModel;
61 private DocumentListener[] myCachedDocumentListeners;
62 private final List<EditReadOnlyListener> myReadOnlyListeners = new ArrayList<EditReadOnlyListener>(1);
64 private static final Comparator<? super DocumentListener> ourListenersComparator = new Comparator<Object>() {
65 public int compare(Object o1, Object o2) {
66 return getPriority(o1) - getPriority(o2);
69 private int getPriority(Object o) {
70 if (o instanceof PrioritizedDocumentListener) return ((PrioritizedDocumentListener)o).getPriority();
71 return Integer.MAX_VALUE;
75 private int myCheckGuardedBlocks = 0;
76 private boolean myGuardsSuppressed = false;
77 private boolean myEventsHandling = false;
78 private boolean myAssertWriteAccess = true;
79 private static final Key<Boolean> DOING_BULK_UPDATE = Key.create("DoingBulkRefromat");
80 private static final Key<WeakReference<EditorHighlighter>> ourSomeEditorSyntaxHighlighter = Key.create("some editor highlighter");
81 private boolean myAcceptSlashR = false;
83 private DocumentImpl() {
84 setCyclicBufferSize(0);
85 setModificationStamp(LocalTimeCounter.currentTime());
88 public DocumentImpl(String text) {
89 this((CharSequence)text);
90 setModificationStamp(LocalTimeCounter.currentTime());
93 public boolean setAcceptSlashR(boolean accept) {
94 try {
95 return myAcceptSlashR;
97 finally {
98 myAcceptSlashR = accept;
102 public DocumentImpl(CharSequence chars) {
103 this();
104 assertValidSeparators(chars);
105 setChars(chars);
108 public void dontAssertWriteAccess() {
109 myAssertWriteAccess = false;
112 public char[] getRawChars() {
113 return myText.getChars();
116 public char[] getChars() {
117 return CharArrayUtil.fromSequence(getCharsSequence());
120 private void setChars(CharSequence chars) {
121 myText.replaceText(chars);
122 DocumentEvent event = new DocumentEventImpl(this, 0, null, null, -1, true);
123 myLineSet.documentCreated(event);
126 public MarkupModel getMarkupModel() {
127 return getMarkupModel(null);
130 public void setStripTrailingSpacesEnabled(boolean isEnabled) {
131 isStripTrailingSpacesEnabled = isEnabled;
134 public void stripTrailingSpaces(boolean inChangedLinesOnly) {
135 Editor[] editors = EditorFactory.getInstance().getEditors(this, null);
136 VisualPosition[] visualCarets = new VisualPosition[editors.length];
137 int[] caretLines = new int[editors.length];
138 for (int i = 0; i < editors.length; i++) {
139 visualCarets[i] = editors[i].getCaretModel().getVisualPosition();
140 caretLines[i] = editors[i].getCaretModel().getLogicalPosition().line;
143 if (!isStripTrailingSpacesEnabled) {
144 return;
147 boolean isTestMode = ApplicationManager.getApplication().isUnitTestMode();
149 lines:
150 for (int i = 0; i < myLineSet.getLineCount(); i++) {
151 if (!isTestMode) {
152 for (int caretLine : caretLines) {
153 if (caretLine == i) continue lines;
157 if (!inChangedLinesOnly || myLineSet.isModified(i)) {
158 int start = -1;
159 int lineEnd = myLineSet.getLineEnd(i) - myLineSet.getSeparatorLength(i);
160 int lineStart = myLineSet.getLineStart(i);
161 CharSequence text = myText.getCharArray();
162 for (int offset = lineEnd - 1; offset >= lineStart; offset--) {
163 char c = text.charAt(offset);
164 if (c != ' ' && c != '\t') {
165 break;
167 start = offset;
169 if (start != -1) {
170 deleteString(start, lineEnd);
175 for (int i = 0; i < editors.length; i++) {
176 editors[i].getCaretModel().moveToVisualPosition(visualCarets[i]);
180 public void setReadOnly(boolean isReadOnly) {
181 if (myIsReadOnly != isReadOnly) {
182 myIsReadOnly = isReadOnly;
183 myPropertyChangeSupport.firePropertyChange(PROP_WRITABLE, !isReadOnly, isReadOnly);
187 public boolean isWritable() {
188 return !myIsReadOnly;
191 public void removeRangeMarker(RangeMarkerEx rangeMarker) {
192 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
193 synchronized(myRangeMarkers) {
194 myRangeMarkers.remove(rangeMarker);
198 public void addRangeMarker(RangeMarkerEx rangeMarker) {
199 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
200 synchronized(myRangeMarkers) {
201 myRangeMarkers.put(rangeMarker, null);
205 public RangeMarker createGuardedBlock(int startOffset, int endOffset) {
206 LOG.assertTrue(startOffset <= endOffset, "Should be startOffset <= endOffset");
207 RangeMarker block = createRangeMarker(startOffset, endOffset, true);
208 myGuardedBlocks.add(block);
209 return block;
212 public void removeGuardedBlock(RangeMarker block) {
213 myGuardedBlocks.remove(block);
216 public List<RangeMarker> getGuardedBlocks() {
217 return myGuardedBlocks;
220 @SuppressWarnings({"ForLoopReplaceableByForEach"}) // Way too many garbage is produced otherwise in AbstractList.iterator()
221 public RangeMarker getOffsetGuard(int offset) {
222 for (int i = 0; i < myGuardedBlocks.size(); i++) {
223 RangeMarker block = myGuardedBlocks.get(i);
224 if (offsetInRange(offset, block.getStartOffset(), block.getEndOffset())) return block;
227 return null;
230 public RangeMarker getRangeGuard(int start, int end) {
231 for (RangeMarker block : myGuardedBlocks) {
232 if (rangeIntersect(start, end, block.getStartOffset(), block.getEndOffset())) return block;
235 return null;
238 public void startGuardedBlockChecking() {
239 myCheckGuardedBlocks++;
242 public void stopGuardedBlockChecking() {
243 LOG.assertTrue(myCheckGuardedBlocks > 0, "Unpaired start/stopGuardedBlockChecking");
244 myCheckGuardedBlocks--;
247 private static boolean offsetInRange(int offset, int start, int end) {
248 return start <= offset && offset < end;
251 private static boolean rangeIntersect(int s1, int e1, int s2, int e2) {
252 return s2 < s1 && s1 < e2 || s2 < e1 && e1 < e2
253 || s1 < s2 && s2 < e1 || s1 < e2 && e2 < e1
254 || s1 == s2 && e1 == e2;
257 public RangeMarker createRangeMarker(int startOffset, int endOffset) {
258 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
259 return new RangeMarkerImpl(this, startOffset, endOffset);
262 public RangeMarker createRangeMarker(int startOffset, int endOffset, boolean surviveOnExternalChange) {
263 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
264 if (!(0 <= startOffset && startOffset <= endOffset && endOffset <= getTextLength())) {
265 LOG.error("Incorrect offsets startOffset=" + startOffset + ", endOffset=" + endOffset + ", text length=" + getTextLength());
267 return surviveOnExternalChange
268 ? new PersistentRangeMarker(this, startOffset, endOffset)
269 : new RangeMarkerImpl(this, startOffset, endOffset);
272 public long getModificationStamp() {
273 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
274 return myModificationStamp;
277 public void setModificationStamp(long modificationStamp) {
278 myModificationStamp = modificationStamp;
281 public void replaceText(CharSequence chars, long newModificationStamp) {
282 replaceString(0, getTextLength(), chars, newModificationStamp, true); //TODO: optimization!!!
283 clearLineModificationFlags();
286 public int getListenersCount() {
287 return myDocumentListeners.size();
290 public void insertString(int offset, CharSequence s) {
291 if (offset < 0) throw new IndexOutOfBoundsException("Wrong offset: " + offset);
292 if (offset > getTextLength()) {
293 throw new IndexOutOfBoundsException("Wrong offset: " + offset +"; documentLength: "+getTextLength()+ "; " + s.subSequence(Math.max(0, getTextLength() - 20), getTextLength()));
295 assertWriteAccess();
296 assertValidSeparators(s);
298 if (!isWritable()) throw new ReadOnlyModificationException(this);
299 if (s.length() == 0) return;
301 RangeMarker marker = getRangeGuard(offset, offset);
302 if (marker != null) {
303 throwGuardedFragment(marker, offset, null, s.toString());
306 myText.insert(s, offset);
309 public void deleteString(int startOffset, int endOffset) {
310 assertBounds(startOffset, endOffset);
312 assertWriteAccess();
313 if (!isWritable()) throw new ReadOnlyModificationException(this);
314 if (startOffset == endOffset) return;
315 CharSequence sToDelete = myText.substring(startOffset, endOffset);
317 RangeMarker marker = getRangeGuard(startOffset, endOffset);
318 if (marker != null) {
319 throwGuardedFragment(marker, startOffset, sToDelete.toString(), null);
322 myText.remove(startOffset, endOffset,sToDelete);
325 public void replaceString(int startOffset, int endOffset, CharSequence s) {
326 replaceString(startOffset, endOffset, s, LocalTimeCounter.currentTime(), startOffset==0 && endOffset==getTextLength());
329 private void replaceString(int startOffset, int endOffset, CharSequence s, final long newModificationStamp, boolean wholeTextReplaced) {
330 assertBounds(startOffset, endOffset);
332 assertWriteAccess();
333 assertValidSeparators(s);
335 if (!isWritable()) {
336 throw new ReadOnlyModificationException(this);
338 final int newStringLength = s.length();
339 final CharSequence chars = getCharsSequence();
340 int newStartInString = 0;
341 int newEndInString = newStringLength;
342 while (newStartInString < newStringLength &&
343 startOffset < endOffset &&
344 s.charAt(newStartInString) == chars.charAt(startOffset)) {
345 startOffset++;
346 newStartInString++;
349 while(endOffset > startOffset &&
350 newEndInString > newStartInString &&
351 s.charAt(newEndInString - 1) == chars.charAt(endOffset - 1)){
352 newEndInString--;
353 endOffset--;
355 //if (newEndInString - newStartInString == 0 && startOffset == endOffset) {
356 //setModificationStamp(newModificationStamp);
357 //return;
360 s = s.subSequence(newStartInString, newEndInString);
361 CharSequence sToDelete = myText.substring(startOffset, endOffset);
362 RangeMarker guard = getRangeGuard(startOffset, endOffset);
363 if (guard != null) {
364 throwGuardedFragment(guard, startOffset, sToDelete.toString(), s.toString());
367 myText.replace(startOffset, endOffset, sToDelete, s,newModificationStamp, wholeTextReplaced);
370 private void assertBounds(final int startOffset, final int endOffset) {
371 if (startOffset < 0 || startOffset > getTextLength()) {
372 throw new IndexOutOfBoundsException("Wrong startOffset: " + startOffset+"; documentLength: "+getTextLength());
374 if (endOffset < 0 || endOffset > getTextLength()) {
375 throw new IndexOutOfBoundsException("Wrong endOffset: " + endOffset+"; documentLength: "+getTextLength());
377 if (endOffset < startOffset) {
378 throw new IllegalArgumentException("endOffset < startOffset: " + endOffset + " < " + startOffset+"; documentLength: "+getTextLength());
382 private void assertWriteAccess() {
383 if (myAssertWriteAccess) {
384 final Application application = ApplicationManager.getApplication();
385 if (application != null) {
386 application.assertWriteAccessAllowed();
391 private void assertValidSeparators(final CharSequence s) {
392 if (myAcceptSlashR) return;
393 StringUtil.assertValidSeparators(s);
396 private void throwGuardedFragment(RangeMarker guard, int offset, String oldString, String newString) {
397 if (myCheckGuardedBlocks > 0 && !myGuardsSuppressed) {
398 DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, false);
399 throw new ReadOnlyFragmentModificationException(event, guard);
403 public void suppressGuardedExceptions() {
404 myGuardsSuppressed = true;
407 public void unSuppressGuardedExceptions() {
408 myGuardsSuppressed = false;
411 public boolean isInEventsHandling() {
412 return myEventsHandling;
415 public void clearLineModificationFlags() {
416 myLineSet.clearModificationFlags();
419 private DocumentEvent beforeChangedUpdate(int offset, CharSequence oldString, CharSequence newString, boolean wholeTextReplaced) {
420 DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, wholeTextReplaced);
422 DocumentListener[] listeners = getCachedListeners();
423 for (int i = listeners.length - 1; i >= 0; i--) {
424 try {
425 listeners[i].beforeDocumentChange(event);
427 catch (Throwable e) {
428 LOG.error(e);
432 myEventsHandling = true;
433 return event;
436 private void changedUpdate(DocumentEvent event, long newModificationStamp) {
437 try{
438 if (LOG.isDebugEnabled()) LOG.debug(event.toString());
440 myLineSet.changedUpdate(event);
441 setModificationStamp(newModificationStamp);
443 updateRangeMarkers(event);
445 DocumentListener[] listeners = getCachedListeners();
446 for (DocumentListener listener : listeners) {
447 try {
448 listener.documentChanged(event);
450 catch (Throwable e) {
451 LOG.error(e);
455 finally{
456 myEventsHandling = false;
460 private void updateRangeMarkers(final DocumentEvent event) {
461 try {
462 synchronized(myRangeMarkers) {
463 for(Iterator<RangeMarkerEx> rangeMarkerIterator = myRangeMarkers.keySet().iterator(); rangeMarkerIterator.hasNext();) {
464 try {
465 final RangeMarkerEx rangeMarker = rangeMarkerIterator.next();
467 if (rangeMarker != null && rangeMarker.isValid()) {
468 if (event.getOffset() <= rangeMarker.getEndOffset()) {
469 rangeMarker.documentChanged(event);
470 if (!rangeMarker.isValid() && myGuardedBlocks.remove(rangeMarker)) {
471 LOG.error("Guarded blocks should stay valid");
475 else {
476 rangeMarkerIterator.remove();
479 catch (Exception e) {
480 LOG.error(e);
484 } catch(Exception e) {
485 LOG.error(e);
489 public String getText() {
490 assertReadAccessToDocumentsAllowed();
491 return myText.toString();
494 public int getTextLength() {
495 assertReadAccessToDocumentsAllowed();
496 return myText.length();
499 private static void assertReadAccessToDocumentsAllowed() {
501 final ApplicationEx application = ApplicationManagerEx.getApplicationEx();
502 if (application != null) {
503 application.assertReadAccessToDocumentsAllowed();
509 This method should be used very carefully - only to read the array, and to be sure, that nobody changes
510 text, while this array is processed.
511 Really it is used only to optimize paint in Editor.
512 [Valentin] 25.04.2001: More really, it is used in 61 places in 29 files across the project :-)))
515 CharSequence getCharsNoThreadCheck() {
516 return myText.getCharArray();
519 public CharSequence getCharsSequence() {
520 assertReadAccessToDocumentsAllowed();
521 return myText.getCharArray();
525 public void addDocumentListener(DocumentListener listener) {
526 myCachedDocumentListeners = null;
527 LOG.assertTrue(!myDocumentListeners.contains(listener), listener);
528 myDocumentListeners.add(listener);
531 public void addDocumentListener(final DocumentListener listener, Disposable parentDisposable) {
532 addDocumentListener(listener);
533 Disposer.register(parentDisposable, new Disposable() {
534 public void dispose() {
535 removeDocumentListener(listener);
540 public void removeDocumentListener(DocumentListener listener) {
541 myCachedDocumentListeners = null;
542 boolean success = myDocumentListeners.remove(listener);
543 LOG.assertTrue(success);
546 public int getLineNumber(int offset) {
547 assertReadAccessToDocumentsAllowed();
548 int lineIndex = myLineSet.findLineIndex(offset);
549 assert lineIndex >= 0;
550 return lineIndex;
553 public LineIterator createLineIterator() {
554 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
555 return myLineSet.createIterator();
558 public final int getLineStartOffset(int line) {
559 assertReadAccessToDocumentsAllowed();
560 if (line == 0) return 0; // otherwise it crashed for zero-length document
561 int lineStart = myLineSet.getLineStart(line);
562 assert lineStart >= 0;
563 return lineStart;
566 public final int getLineEndOffset(int line) {
567 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
568 if (getTextLength() == 0 && line == 0) return 0;
569 int result = myLineSet.getLineEnd(line) - getLineSeparatorLength(line);
570 assert result >= 0;
571 return result;
574 public final int getLineSeparatorLength(int line) {
575 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
576 int separatorLength = myLineSet.getSeparatorLength(line);
577 assert separatorLength >= 0;
578 return separatorLength;
581 public final int getLineCount() {
582 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
583 int lineCount = myLineSet.getLineCount();
584 assert lineCount >= 0;
585 return lineCount;
588 private DocumentListener[] getCachedListeners() {
589 if (myCachedDocumentListeners == null) {
590 Collections.sort(myDocumentListeners, ourListenersComparator);
591 myCachedDocumentListeners = myDocumentListeners.toArray(new DocumentListener[myDocumentListeners.size()]);
594 return myCachedDocumentListeners;
597 public void fireReadOnlyModificationAttempt() {
598 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
599 EditReadOnlyListener[] listeners = myReadOnlyListeners.toArray(
600 new EditReadOnlyListener[myReadOnlyListeners.size()]);
601 for (EditReadOnlyListener listener : listeners) {
602 listener.readOnlyModificationAttempt(this);
606 public void addEditReadOnlyListener(EditReadOnlyListener listener) {
607 myReadOnlyListeners.add(listener);
610 public void removeEditReadOnlyListener(EditReadOnlyListener listener) {
611 myReadOnlyListeners.remove(listener);
614 public void removeMarkupModel(Project project) {
615 MarkupModel model = myProjectToMarkupModelMap.remove(project);
616 if (model != null) {
617 ((MarkupModelEx)model).dispose();
621 @NotNull
622 public MarkupModel getMarkupModel(Project project) {
623 return getMarkupModel(project, true);
626 public void addPropertyChangeListener(PropertyChangeListener listener) {
627 myPropertyChangeSupport.addPropertyChangeListener(listener);
630 public void removePropertyChangeListener(PropertyChangeListener listener) {
631 myPropertyChangeSupport.removePropertyChangeListener(listener);
634 private final Object lock = new Object();
635 public MarkupModel getMarkupModel(@Nullable Project project, boolean create) {
636 if (project == null) {
637 MarkupModelEx markupModel = myMarkupModel;
638 if (create && markupModel == null) {
639 synchronized (lock) {
640 markupModel = myMarkupModel;
641 if (markupModel == null) {
642 myMarkupModel = markupModel = new MarkupModelImpl(this);
646 return markupModel;
649 final DocumentMarkupModelManager documentMarkupModelManager = project.isDisposed() ? null : DocumentMarkupModelManager.getInstance(project);
650 if (documentMarkupModelManager == null || documentMarkupModelManager.isDisposed()) {
651 return new EmptyMarkupModel(this);
654 MarkupModel model = myProjectToMarkupModelMap.get(project);
655 if (create && model == null) {
656 model = ConcurrencyUtil.cacheOrGet(myProjectToMarkupModelMap, project, new MarkupModelImpl(this));
657 documentMarkupModelManager.registerDocument(this);
660 return model;
663 public void setCyclicBufferSize(int bufferSize) {
664 myText.setBufferSize(bufferSize);
667 public void setText(final CharSequence text) {
668 Runnable runnable = new Runnable() {
669 public void run() {
670 replaceString(0, getTextLength(), text, LocalTimeCounter.currentTime(), true);
673 if (CommandProcessor.getInstance().isUndoTransparentActionInProgress()) {
674 runnable.run();
676 else {
677 CommandProcessor.getInstance().executeCommand(runnable, "file text set", this);
680 clearLineModificationFlags();
683 public RangeMarker createRangeMarker(final TextRange textRange) {
684 return createRangeMarker(textRange.getStartOffset(), textRange.getEndOffset());
687 public final boolean isInBulkUpdate() {
688 return getUserData(DOING_BULK_UPDATE) != null;
691 public final void setInBulkUpdate(boolean value) {
692 if (value) {
693 putUserData(DOING_BULK_UPDATE, Boolean.TRUE);
694 getPublisher().updateStarted(this);
696 else {
697 putUserData(DOING_BULK_UPDATE, null);
698 getPublisher().updateFinished(this);
702 @Nullable
703 public EditorHighlighter getEditorHighlighterForCachesBuilding() {
704 final WeakReference<EditorHighlighter> editorHighlighterWeakReference = getUserData(ourSomeEditorSyntaxHighlighter);
705 final EditorHighlighter someEditorHighlighter = editorHighlighterWeakReference != null ? editorHighlighterWeakReference.get():null;
707 if (someEditorHighlighter instanceof LexerEditorHighlighter) {
708 return someEditorHighlighter;
710 return null;
713 public void rememberEditorHighlighterForCachesOptimization(@NotNull final EditorHighlighter highlighter) {
714 putUserData(ourSomeEditorSyntaxHighlighter, new WeakReference<EditorHighlighter>(highlighter));
717 private static class DocumentBulkUpdateListenerHolder {
718 private static final DocumentBulkUpdateListener ourBulkChangePublisher =
719 ApplicationManager.getApplication().getMessageBus().syncPublisher(DocumentBulkUpdateListener.TOPIC);
722 private static DocumentBulkUpdateListener getPublisher() {
723 return DocumentBulkUpdateListenerHolder.ourBulkChangePublisher;