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
;
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
) {
117 return myAcceptSlashR
;
120 myAcceptSlashR
= accept
;
124 public DocumentImpl(CharSequence chars
) {
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();
137 public char[] getChars() {
138 return CharArrayUtil
.fromSequence(getCharsSequence());
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
) {
163 boolean isTestMode
= ApplicationManager
.getApplication().isUnitTestMode();
166 for (int i
= 0; i
< myLineSet
.getLineCount(); i
++) {
168 for (int caretLine
: caretLines
) {
169 if (caretLine
== i
) continue lines
;
173 if (!inChangedLinesOnly
|| myLineSet
.isModified(i
)) {
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') {
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);
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
);
239 public void removeGuardedBlock(@NotNull RangeMarker block
) {
240 myGuardedBlocks
.remove(block
);
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
;
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()})) {
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];
295 public RangeMarker
createRangeMarker(int startOffset
, int endOffset
) {
296 ApplicationManagerEx
.getApplicationEx().assertReadAccessToDocumentsAllowed();
297 return new RangeMarkerImpl(this, startOffset
, endOffset
);
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()));
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
);
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
);
372 assertValidSeparators(s
);
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
)) {
388 while(endOffset
> startOffset
&&
389 newEndInString
> newStartInString
&&
390 s
.charAt(newEndInString
- 1) == chars
.charAt(endOffset
- 1)){
394 //if (newEndInString - newStartInString == 0 && startOffset == endOffset) {
395 //setModificationStamp(newModificationStamp);
399 s
= s
.subSequence(newStartInString
, newEndInString
);
400 CharSequence sToDelete
= myText
.substring(startOffset
, endOffset
);
401 RangeMarker guard
= getRangeGuard(startOffset
, endOffset
);
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
--) {
467 listeners
[i
].beforeDocumentChange(event
);
469 catch (Throwable e
) {
474 myEventsHandling
= true;
478 private void changedUpdate(DocumentEvent event
, long newModificationStamp
) {
479 if (ShutDownTracker
.isShutdownHookRunning()) {
480 return; // suppress events in shutdown hook
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
) {
493 listener
.documentChanged(event
);
495 catch (Throwable e
) {
501 myEventsHandling
= false;
505 private void updateRangeMarkers(final DocumentEvent event
) {
506 synchronized(myRangeMarkers
) {
507 for(Iterator
<RangeMarkerEx
> rangeMarkerIterator
= myRangeMarkers
.keySet().iterator(); rangeMarkerIterator
.hasNext();) {
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
);
523 rangeMarkerIterator
.remove();
526 catch (Exception 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();
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;
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;
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
);
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;
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
);
663 ((MarkupModelEx
)model
).dispose();
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);
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);
709 public void setCyclicBufferSize(int bufferSize
) {
710 myText
.setBufferSize(bufferSize
);
713 public void setText(@NotNull final CharSequence text
) {
714 Runnable runnable
= new Runnable() {
716 replaceString(0, getTextLength(), text
, LocalTimeCounter
.currentTime(), true);
719 if (CommandProcessor
.getInstance().isUndoTransparentActionInProgress()) {
723 CommandProcessor
.getInstance().executeCommand(null, runnable
, "", DocCommandGroupId
.noneGroupId(this));
726 clearLineModificationFlags();
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
) {
740 myDoingBulkUpdate
= true;
741 getPublisher().updateStarted(this);
744 myDoingBulkUpdate
= false;
745 getPublisher().updateFinished(this);
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
;
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
;