IDEA-51739
[fedora-idea.git] / platform / platform-impl / src / com / intellij / openapi / editor / impl / DocumentImpl.java
blobb48f3816df8e07d85f8917cecdae94415127a5dc
1 /*
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.
16 package com.intellij.openapi.editor.impl;
18 import com.intellij.openapi.Disposable;
19 import com.intellij.openapi.application.Application;
20 import com.intellij.openapi.application.ApplicationManager;
21 import com.intellij.openapi.application.ex.ApplicationManagerEx;
22 import com.intellij.openapi.command.CommandProcessor;
23 import com.intellij.openapi.diagnostic.Logger;
24 import com.intellij.openapi.editor.*;
25 import com.intellij.openapi.editor.actionSystem.DocCommandGroupId;
26 import com.intellij.openapi.editor.actionSystem.ReadonlyFragmentModificationHandler;
27 import com.intellij.openapi.editor.event.DocumentEvent;
28 import com.intellij.openapi.editor.event.DocumentListener;
29 import com.intellij.openapi.editor.ex.*;
30 import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter;
31 import com.intellij.openapi.editor.highlighter.EditorHighlighter;
32 import com.intellij.openapi.editor.impl.event.DocumentEventImpl;
33 import com.intellij.openapi.editor.markup.MarkupModel;
34 import com.intellij.openapi.project.Project;
35 import com.intellij.openapi.util.*;
36 import com.intellij.openapi.util.text.StringUtil;
37 import com.intellij.util.ArrayUtil;
38 import com.intellij.util.ConcurrencyUtil;
39 import com.intellij.util.LocalTimeCounter;
40 import com.intellij.util.containers.ConcurrentHashMap;
41 import com.intellij.util.text.CharArrayUtil;
42 import org.jetbrains.annotations.NotNull;
43 import org.jetbrains.annotations.Nullable;
45 import java.beans.PropertyChangeListener;
46 import java.beans.PropertyChangeSupport;
47 import java.lang.ref.WeakReference;
48 import java.util.*;
49 import java.util.concurrent.ConcurrentMap;
51 public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
52 private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.DocumentImpl");
54 private final List<DocumentListener> myDocumentListeners = new ArrayList<DocumentListener>();
55 private final WeakHashMap<RangeMarkerEx,String> myRangeMarkers = new WeakHashMap<RangeMarkerEx, String>();
56 private final List<RangeMarker> myGuardedBlocks = new ArrayList<RangeMarker>();
57 private ReadonlyFragmentModificationHandler myReadonlyFragmentModificationHandler;
59 private final LineSet myLineSet = new LineSet();
60 private final CharArray myText = new CharArray(0) {
61 protected DocumentEvent beforeChangedUpdate(int offset, CharSequence oldString, CharSequence newString, boolean wholeTextReplaced) {
62 return DocumentImpl.this.beforeChangedUpdate(offset, oldString, newString, wholeTextReplaced);
65 protected void afterChangedUpdate(DocumentEvent event, long newModificationStamp) {
66 changedUpdate(event, newModificationStamp);
70 private boolean myIsReadOnly = false;
71 private boolean isStripTrailingSpacesEnabled = true;
72 private volatile long myModificationStamp;
73 private final ConcurrentMap<Project, MarkupModel> myProjectToMarkupModelMap = new ConcurrentHashMap<Project, MarkupModel>();
74 private final PropertyChangeSupport myPropertyChangeSupport = new PropertyChangeSupport(this);
76 private volatile MarkupModelEx myMarkupModel;
77 private DocumentListener[] myCachedDocumentListeners;
78 private final List<EditReadOnlyListener> myReadOnlyListeners = new ArrayList<EditReadOnlyListener>(1);
80 private static final Comparator<? super DocumentListener> ourListenersComparator = new Comparator<Object>() {
81 public int compare(Object o1, Object o2) {
82 return getPriority(o1) - getPriority(o2);
85 private int getPriority(Object o) {
86 if (o instanceof PrioritizedDocumentListener) return ((PrioritizedDocumentListener)o).getPriority();
87 return Integer.MAX_VALUE;
91 private int myCheckGuardedBlocks = 0;
92 private boolean myGuardsSuppressed = false;
93 private boolean myEventsHandling = false;
94 private final boolean myAssertWriteAccess;
95 private boolean myDoingBulkUpdate = false;
96 private static final Key<WeakReference<EditorHighlighter>> ourSomeEditorSyntaxHighlighter = Key.create("some editor highlighter");
97 private boolean myAcceptSlashR = false;
99 private DocumentImpl() {
100 setCyclicBufferSize(0);
101 setModificationStamp(LocalTimeCounter.currentTime());
102 myAssertWriteAccess = true;
105 public DocumentImpl(String text) {
106 this((CharSequence)text);
109 public DocumentImpl(boolean forUseInNonAWTThread) {
110 setCyclicBufferSize(0);
111 setModificationStamp(LocalTimeCounter.currentTime());
112 myAssertWriteAccess = !forUseInNonAWTThread;
115 public boolean setAcceptSlashR(boolean accept) {
116 try {
117 return myAcceptSlashR;
119 finally {
120 myAcceptSlashR = accept;
124 public DocumentImpl(CharSequence chars) {
125 this();
126 assertValidSeparators(chars);
127 myText.setText(chars);
128 DocumentEvent event = new DocumentEventImpl(this, 0, null, null, -1, true);
129 myLineSet.documentCreated(event);
132 public char[] getRawChars() {
133 return myText.getChars();
136 @NotNull
137 public char[] getChars() {
138 return CharArrayUtil.fromSequence(getCharsSequence());
141 @NotNull
142 public MarkupModel getMarkupModel() {
143 return getMarkupModel(null);
146 public void setStripTrailingSpacesEnabled(boolean isEnabled) {
147 isStripTrailingSpacesEnabled = isEnabled;
150 public void stripTrailingSpaces(boolean inChangedLinesOnly) {
151 Editor[] editors = EditorFactory.getInstance().getEditors(this, null);
152 VisualPosition[] visualCarets = new VisualPosition[editors.length];
153 int[] caretLines = new int[editors.length];
154 for (int i = 0; i < editors.length; i++) {
155 visualCarets[i] = editors[i].getCaretModel().getVisualPosition();
156 caretLines[i] = editors[i].getCaretModel().getLogicalPosition().line;
159 if (!isStripTrailingSpacesEnabled) {
160 return;
163 boolean isTestMode = ApplicationManager.getApplication().isUnitTestMode();
165 lines:
166 for (int i = 0; i < myLineSet.getLineCount(); i++) {
167 if (!isTestMode) {
168 for (int caretLine : caretLines) {
169 if (caretLine == i) continue lines;
173 if (!inChangedLinesOnly || myLineSet.isModified(i)) {
174 int start = -1;
175 int lineEnd = myLineSet.getLineEnd(i) - myLineSet.getSeparatorLength(i);
176 int lineStart = myLineSet.getLineStart(i);
177 CharSequence text = myText.getCharArray();
178 for (int offset = lineEnd - 1; offset >= lineStart; offset--) {
179 char c = text.charAt(offset);
180 if (c != ' ' && c != '\t') {
181 break;
183 start = offset;
185 if (start != -1) {
186 deleteString(start, lineEnd);
191 if (!ShutDownTracker.isShutdownHookRunning()) {
192 for (int i = 0; i < editors.length; i++) {
193 editors[i].getCaretModel().moveToVisualPosition(visualCarets[i]);
198 public void setReadOnly(boolean isReadOnly) {
199 if (myIsReadOnly != isReadOnly) {
200 myIsReadOnly = isReadOnly;
201 myPropertyChangeSupport.firePropertyChange(PROP_WRITABLE, !isReadOnly, isReadOnly);
205 public ReadonlyFragmentModificationHandler getReadonlyFragmentModificationHandler() {
206 return myReadonlyFragmentModificationHandler;
209 public void setReadonlyFragmentModificationHandler(final ReadonlyFragmentModificationHandler readonlyFragmentModificationHandler) {
210 myReadonlyFragmentModificationHandler = readonlyFragmentModificationHandler;
213 public boolean isWritable() {
214 return !myIsReadOnly;
217 public void removeRangeMarker(@NotNull RangeMarkerEx rangeMarker) {
218 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
219 synchronized(myRangeMarkers) {
220 myRangeMarkers.remove(rangeMarker);
224 public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker) {
225 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
226 synchronized(myRangeMarkers) {
227 myRangeMarkers.put(rangeMarker, null);
231 @NotNull
232 public RangeMarker createGuardedBlock(int startOffset, int endOffset) {
233 LOG.assertTrue(startOffset <= endOffset, "Should be startOffset <= endOffset");
234 RangeMarker block = createRangeMarker(startOffset, endOffset, true);
235 myGuardedBlocks.add(block);
236 return block;
239 public void removeGuardedBlock(@NotNull RangeMarker block) {
240 myGuardedBlocks.remove(block);
243 @NotNull
244 public List<RangeMarker> getGuardedBlocks() {
245 return myGuardedBlocks;
248 @SuppressWarnings({"ForLoopReplaceableByForEach"}) // Way too many garbage is produced otherwise in AbstractList.iterator()
249 public RangeMarker getOffsetGuard(int offset) {
250 for (int i = 0; i < myGuardedBlocks.size(); i++) {
251 RangeMarker block = myGuardedBlocks.get(i);
252 if (offsetInRange(offset, block.getStartOffset(), block.getEndOffset())) return block;
255 return null;
258 public RangeMarker getRangeGuard(int start, int end) {
259 for (RangeMarker block : myGuardedBlocks) {
260 if (rangeIntersect(new int[]{start, block.getStartOffset()}, new int[]{end, block.getEndOffset()}, new boolean[]{true, block.isGreedyToLeft()}, new boolean[]{true, block.isGreedyToRight()})) {
261 return block;
265 return null;
268 public void startGuardedBlockChecking() {
269 myCheckGuardedBlocks++;
272 public void stopGuardedBlockChecking() {
273 LOG.assertTrue(myCheckGuardedBlocks > 0, "Unpaired start/stopGuardedBlockChecking");
274 myCheckGuardedBlocks--;
277 private static boolean offsetInRange(int offset, int start, int end) {
278 return start <= offset && offset < end;
281 private static boolean rangeIntersect(int[] start, int[] end, boolean[] leftInclusive, boolean[] rightInclusive) {
282 if (start[0] > start[1] || start[0] == start[1] && !leftInclusive[0]) {
283 ArrayUtil.swap(start, 0, 1);
284 ArrayUtil.swap(end, 0, 1);
285 ArrayUtil.swap(leftInclusive, 0, 1);
286 ArrayUtil.swap(rightInclusive, 0, 1);
288 if (end[0] < start[1]) return false;
289 if (end[0] > start[1]) return true;
291 return leftInclusive[1] && rightInclusive[0];
294 @NotNull
295 public RangeMarker createRangeMarker(int startOffset, int endOffset) {
296 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
297 return new RangeMarkerImpl(this, startOffset, endOffset);
300 @NotNull
301 public RangeMarker createRangeMarker(int startOffset, int endOffset, boolean surviveOnExternalChange) {
302 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
303 if (!(0 <= startOffset && startOffset <= endOffset && endOffset <= getTextLength())) {
304 LOG.error("Incorrect offsets startOffset=" + startOffset + ", endOffset=" + endOffset + ", text length=" + getTextLength());
306 return surviveOnExternalChange
307 ? new PersistentRangeMarker(this, startOffset, endOffset)
308 : new RangeMarkerImpl(this, startOffset, endOffset);
311 public long getModificationStamp() {
312 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
313 return myModificationStamp;
316 public void setModificationStamp(long modificationStamp) {
317 myModificationStamp = modificationStamp;
320 public void replaceText(@NotNull CharSequence chars, long newModificationStamp) {
321 replaceString(0, getTextLength(), chars, newModificationStamp, true); //TODO: optimization!!!
322 clearLineModificationFlags();
325 public int getListenersCount() {
326 return myDocumentListeners.size();
329 public void insertString(int offset, @NotNull CharSequence s) {
330 if (offset < 0) throw new IndexOutOfBoundsException("Wrong offset: " + offset);
331 if (offset > getTextLength()) {
332 throw new IndexOutOfBoundsException("Wrong offset: " + offset +"; documentLength: "+getTextLength()+ "; " + s.subSequence(Math.max(0, getTextLength() - 20), getTextLength()));
334 assertWriteAccess();
335 assertValidSeparators(s);
337 if (!isWritable()) throw new ReadOnlyModificationException(this);
338 if (s.length() == 0) return;
340 RangeMarker marker = getRangeGuard(offset, offset);
341 if (marker != null) {
342 throwGuardedFragment(marker, offset, null, s.toString());
345 myText.insert(s, offset);
348 public void deleteString(int startOffset, int endOffset) {
349 assertBounds(startOffset, endOffset);
351 assertWriteAccess();
352 if (!isWritable()) throw new ReadOnlyModificationException(this);
353 if (startOffset == endOffset) return;
354 CharSequence sToDelete = myText.substring(startOffset, endOffset);
356 RangeMarker marker = getRangeGuard(startOffset, endOffset);
357 if (marker != null) {
358 throwGuardedFragment(marker, startOffset, sToDelete.toString(), null);
361 myText.remove(startOffset, endOffset,sToDelete);
364 public void replaceString(int startOffset, int endOffset, @NotNull CharSequence s) {
365 replaceString(startOffset, endOffset, s, LocalTimeCounter.currentTime(), startOffset==0 && endOffset==getTextLength());
368 private void replaceString(int startOffset, int endOffset, CharSequence s, final long newModificationStamp, boolean wholeTextReplaced) {
369 assertBounds(startOffset, endOffset);
371 assertWriteAccess();
372 assertValidSeparators(s);
374 if (!isWritable()) {
375 throw new ReadOnlyModificationException(this);
377 final int newStringLength = s.length();
378 final CharSequence chars = getCharsSequence();
379 int newStartInString = 0;
380 int newEndInString = newStringLength;
381 while (newStartInString < newStringLength &&
382 startOffset < endOffset &&
383 s.charAt(newStartInString) == chars.charAt(startOffset)) {
384 startOffset++;
385 newStartInString++;
388 while(endOffset > startOffset &&
389 newEndInString > newStartInString &&
390 s.charAt(newEndInString - 1) == chars.charAt(endOffset - 1)){
391 newEndInString--;
392 endOffset--;
394 //if (newEndInString - newStartInString == 0 && startOffset == endOffset) {
395 //setModificationStamp(newModificationStamp);
396 //return;
399 s = s.subSequence(newStartInString, newEndInString);
400 CharSequence sToDelete = myText.substring(startOffset, endOffset);
401 RangeMarker guard = getRangeGuard(startOffset, endOffset);
402 if (guard != null) {
403 throwGuardedFragment(guard, startOffset, sToDelete.toString(), s.toString());
406 myText.replace(startOffset, endOffset, sToDelete, s,newModificationStamp, wholeTextReplaced);
409 private void assertBounds(final int startOffset, final int endOffset) {
410 if (startOffset < 0 || startOffset > getTextLength()) {
411 throw new IndexOutOfBoundsException("Wrong startOffset: " + startOffset+"; documentLength: "+getTextLength());
413 if (endOffset < 0 || endOffset > getTextLength()) {
414 throw new IndexOutOfBoundsException("Wrong endOffset: " + endOffset+"; documentLength: "+getTextLength());
416 if (endOffset < startOffset) {
417 throw new IllegalArgumentException("endOffset < startOffset: " + endOffset + " < " + startOffset+"; documentLength: "+getTextLength());
421 private void assertWriteAccess() {
422 if (myAssertWriteAccess) {
423 final Application application = ApplicationManager.getApplication();
424 if (application != null) {
425 application.assertWriteAccessAllowed();
430 private void assertValidSeparators(final CharSequence s) {
431 if (myAcceptSlashR) return;
432 StringUtil.assertValidSeparators(s);
435 private void throwGuardedFragment(RangeMarker guard, int offset, String oldString, String newString) {
436 if (myCheckGuardedBlocks > 0 && !myGuardsSuppressed) {
437 DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, false);
438 throw new ReadOnlyFragmentModificationException(event, guard);
442 public void suppressGuardedExceptions() {
443 myGuardsSuppressed = true;
446 public void unSuppressGuardedExceptions() {
447 myGuardsSuppressed = false;
450 public boolean isInEventsHandling() {
451 return myEventsHandling;
454 public void clearLineModificationFlags() {
455 myLineSet.clearModificationFlags();
458 private DocumentEvent beforeChangedUpdate(int offset, CharSequence oldString, CharSequence newString, boolean wholeTextReplaced) {
459 if (ShutDownTracker.isShutdownHookRunning()) {
460 return null; // suppress events in shutdown hook
462 DocumentEvent event = new DocumentEventImpl(this, offset, oldString, newString, myModificationStamp, wholeTextReplaced);
464 DocumentListener[] listeners = getCachedListeners();
465 for (int i = listeners.length - 1; i >= 0; i--) {
466 try {
467 listeners[i].beforeDocumentChange(event);
469 catch (Throwable e) {
470 LOG.error(e);
474 myEventsHandling = true;
475 return event;
478 private void changedUpdate(DocumentEvent event, long newModificationStamp) {
479 if (ShutDownTracker.isShutdownHookRunning()) {
480 return; // suppress events in shutdown hook
482 try{
483 if (LOG.isDebugEnabled()) LOG.debug(event.toString());
485 myLineSet.changedUpdate(event);
486 setModificationStamp(newModificationStamp);
488 updateRangeMarkers(event);
490 DocumentListener[] listeners = getCachedListeners();
491 for (DocumentListener listener : listeners) {
492 try {
493 listener.documentChanged(event);
495 catch (Throwable e) {
496 LOG.error(e);
500 finally{
501 myEventsHandling = false;
505 private void updateRangeMarkers(final DocumentEvent event) {
506 synchronized(myRangeMarkers) {
507 for(Iterator<RangeMarkerEx> rangeMarkerIterator = myRangeMarkers.keySet().iterator(); rangeMarkerIterator.hasNext();) {
508 try {
509 final RangeMarkerEx rangeMarker = rangeMarkerIterator.next();
511 if (rangeMarker != null && rangeMarker.isValid()) {
512 if (event.getOffset() <= rangeMarker.getEndOffset()) {
513 rangeMarker.documentChanged(event);
514 if (!rangeMarker.isValid()) {
515 rangeMarkerIterator.remove();
516 if (myGuardedBlocks.remove(rangeMarker)) {
517 LOG.error("Guarded blocks should stay valid: "+rangeMarker);
522 else {
523 rangeMarkerIterator.remove();
526 catch (Exception e) {
527 LOG.error(e);
533 public String getText() {
534 assertReadAccessToDocumentsAllowed();
535 return myText.toString();
538 public int getTextLength() {
539 assertReadAccessToDocumentsAllowed();
540 return myText.length();
543 private static void assertReadAccessToDocumentsAllowed() {
545 final ApplicationEx application = ApplicationManagerEx.getApplicationEx();
546 if (application != null) {
547 application.assertReadAccessToDocumentsAllowed();
553 This method should be used very carefully - only to read the array, and to be sure, that nobody changes
554 text, while this array is processed.
555 Really it is used only to optimize paint in Editor.
556 [Valentin] 25.04.2001: More really, it is used in 61 places in 29 files across the project :-)))
559 CharSequence getCharsNoThreadCheck() {
560 return myText.getCharArray();
563 @NotNull
564 public CharSequence getCharsSequence() {
565 assertReadAccessToDocumentsAllowed();
566 return myText.getCharArray();
570 public void addDocumentListener(@NotNull DocumentListener listener) {
571 myCachedDocumentListeners = null;
572 LOG.assertTrue(!myDocumentListeners.contains(listener), listener);
573 myDocumentListeners.add(listener);
576 public void addDocumentListener(@NotNull final DocumentListener listener, @NotNull Disposable parentDisposable) {
577 addDocumentListener(listener);
578 Disposer.register(parentDisposable, new Disposable() {
579 public void dispose() {
580 removeDocumentListener(listener);
585 public void removeDocumentListener(@NotNull DocumentListener listener) {
586 myCachedDocumentListeners = null;
587 boolean success = myDocumentListeners.remove(listener);
588 LOG.assertTrue(success);
591 public int getLineNumber(int offset) {
592 assertReadAccessToDocumentsAllowed();
593 int lineIndex = myLineSet.findLineIndex(offset);
594 assert lineIndex >= 0;
595 return lineIndex;
598 @NotNull
599 public LineIterator createLineIterator() {
600 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
601 return myLineSet.createIterator();
604 public final int getLineStartOffset(int line) {
605 assertReadAccessToDocumentsAllowed();
606 if (line == 0) return 0; // otherwise it crashed for zero-length document
607 int lineStart = myLineSet.getLineStart(line);
608 assert lineStart >= 0;
609 return lineStart;
612 public final int getLineEndOffset(int line) {
613 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
614 if (getTextLength() == 0 && line == 0) return 0;
615 int result = myLineSet.getLineEnd(line) - getLineSeparatorLength(line);
616 assert result >= 0;
617 return result;
620 public final int getLineSeparatorLength(int line) {
621 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
622 int separatorLength = myLineSet.getSeparatorLength(line);
623 assert separatorLength >= 0;
624 return separatorLength;
627 public final int getLineCount() {
628 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
629 int lineCount = myLineSet.getLineCount();
630 assert lineCount >= 0;
631 return lineCount;
634 private DocumentListener[] getCachedListeners() {
635 if (myCachedDocumentListeners == null) {
636 Collections.sort(myDocumentListeners, ourListenersComparator);
637 myCachedDocumentListeners = myDocumentListeners.toArray(new DocumentListener[myDocumentListeners.size()]);
640 return myCachedDocumentListeners;
643 public void fireReadOnlyModificationAttempt() {
644 ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
645 EditReadOnlyListener[] listeners = myReadOnlyListeners.toArray(
646 new EditReadOnlyListener[myReadOnlyListeners.size()]);
647 for (EditReadOnlyListener listener : listeners) {
648 listener.readOnlyModificationAttempt(this);
652 public void addEditReadOnlyListener(@NotNull EditReadOnlyListener listener) {
653 myReadOnlyListeners.add(listener);
656 public void removeEditReadOnlyListener(@NotNull EditReadOnlyListener listener) {
657 myReadOnlyListeners.remove(listener);
660 public void removeMarkupModel(Project project) {
661 MarkupModel model = myProjectToMarkupModelMap.remove(project);
662 if (model != null) {
663 ((MarkupModelEx)model).dispose();
667 @NotNull
668 public MarkupModel getMarkupModel(Project project) {
669 return getMarkupModel(project, true);
672 public void addPropertyChangeListener(@NotNull PropertyChangeListener listener) {
673 myPropertyChangeSupport.addPropertyChangeListener(listener);
676 public void removePropertyChangeListener(@NotNull PropertyChangeListener listener) {
677 myPropertyChangeSupport.removePropertyChangeListener(listener);
680 private final Object lock = new Object();
681 public MarkupModel getMarkupModel(@Nullable Project project, boolean create) {
682 if (project == null) {
683 MarkupModelEx markupModel = myMarkupModel;
684 if (create && markupModel == null) {
685 synchronized (lock) {
686 markupModel = myMarkupModel;
687 if (markupModel == null) {
688 myMarkupModel = markupModel = new MarkupModelImpl(this);
692 return markupModel;
695 final DocumentMarkupModelManager documentMarkupModelManager = project.isDisposed() ? null : DocumentMarkupModelManager.getInstance(project);
696 if (documentMarkupModelManager == null || documentMarkupModelManager.isDisposed()) {
697 return new EmptyMarkupModel(this);
700 MarkupModel model = myProjectToMarkupModelMap.get(project);
701 if (create && model == null) {
702 model = ConcurrencyUtil.cacheOrGet(myProjectToMarkupModelMap, project, new MarkupModelImpl(this));
703 documentMarkupModelManager.registerDocument(this);
706 return model;
709 public void setCyclicBufferSize(int bufferSize) {
710 myText.setBufferSize(bufferSize);
713 public void setText(@NotNull final CharSequence text) {
714 Runnable runnable = new Runnable() {
715 public void run() {
716 replaceString(0, getTextLength(), text, LocalTimeCounter.currentTime(), true);
719 if (CommandProcessor.getInstance().isUndoTransparentActionInProgress()) {
720 runnable.run();
722 else {
723 CommandProcessor.getInstance().executeCommand(null, runnable, "", DocCommandGroupId.noneGroupId(this));
726 clearLineModificationFlags();
729 @NotNull
730 public RangeMarker createRangeMarker(@NotNull final TextRange textRange) {
731 return createRangeMarker(textRange.getStartOffset(), textRange.getEndOffset());
734 public final boolean isInBulkUpdate() {
735 return myDoingBulkUpdate;
738 public final void setInBulkUpdate(boolean value) {
739 if (value) {
740 myDoingBulkUpdate = true;
741 getPublisher().updateStarted(this);
743 else {
744 myDoingBulkUpdate = false;
745 getPublisher().updateFinished(this);
749 @Nullable
750 public EditorHighlighter getEditorHighlighterForCachesBuilding() {
751 final WeakReference<EditorHighlighter> editorHighlighterWeakReference = getUserData(ourSomeEditorSyntaxHighlighter);
752 final EditorHighlighter someEditorHighlighter = editorHighlighterWeakReference != null ? editorHighlighterWeakReference.get():null;
754 if (someEditorHighlighter instanceof LexerEditorHighlighter) {
755 return someEditorHighlighter;
757 return null;
760 public void rememberEditorHighlighterForCachesOptimization(@NotNull final EditorHighlighter highlighter) {
761 putUserData(ourSomeEditorSyntaxHighlighter, new WeakReference<EditorHighlighter>(highlighter));
764 private static class DocumentBulkUpdateListenerHolder {
765 private static final DocumentBulkUpdateListener ourBulkChangePublisher =
766 ApplicationManager.getApplication().getMessageBus().syncPublisher(DocumentBulkUpdateListener.TOPIC);
769 private static DocumentBulkUpdateListener getPublisher() {
770 return DocumentBulkUpdateListenerHolder.ourBulkChangePublisher;