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.
23 package com
.intellij
.openapi
.vcs
.impl
;
25 import com
.intellij
.openapi
.application
.ApplicationManager
;
26 import com
.intellij
.openapi
.components
.ProjectComponent
;
27 import com
.intellij
.openapi
.diagnostic
.Logger
;
28 import com
.intellij
.openapi
.editor
.Document
;
29 import com
.intellij
.openapi
.editor
.Editor
;
30 import com
.intellij
.openapi
.editor
.EditorFactory
;
31 import com
.intellij
.openapi
.editor
.colors
.EditorColorsListener
;
32 import com
.intellij
.openapi
.editor
.colors
.EditorColorsManager
;
33 import com
.intellij
.openapi
.editor
.colors
.EditorColorsScheme
;
34 import com
.intellij
.openapi
.editor
.event
.EditorFactoryAdapter
;
35 import com
.intellij
.openapi
.editor
.event
.EditorFactoryEvent
;
36 import com
.intellij
.openapi
.editor
.event
.EditorFactoryListener
;
37 import com
.intellij
.openapi
.editor
.ex
.DocumentBulkUpdateListener
;
38 import com
.intellij
.openapi
.fileEditor
.FileDocumentManager
;
39 import com
.intellij
.openapi
.fileEditor
.FileEditorManager
;
40 import com
.intellij
.openapi
.project
.Project
;
41 import com
.intellij
.openapi
.util
.Disposer
;
42 import com
.intellij
.openapi
.vcs
.AbstractVcs
;
43 import com
.intellij
.openapi
.vcs
.FileStatus
;
44 import com
.intellij
.openapi
.vcs
.FileStatusListener
;
45 import com
.intellij
.openapi
.vcs
.FileStatusManager
;
46 import com
.intellij
.openapi
.vcs
.ex
.LineStatusTracker
;
47 import com
.intellij
.openapi
.vfs
.VirtualFile
;
48 import com
.intellij
.openapi
.vfs
.VirtualFileAdapter
;
49 import com
.intellij
.openapi
.vfs
.VirtualFileEvent
;
50 import com
.intellij
.openapi
.vfs
.VirtualFileManager
;
51 import com
.intellij
.openapi
.Disposable
;
52 import com
.intellij
.testFramework
.LightVirtualFile
;
53 import com
.intellij
.util
.Alarm
;
54 import com
.intellij
.util
.containers
.HashMap
;
55 import org
.jetbrains
.annotations
.NonNls
;
56 import org
.jetbrains
.annotations
.NotNull
;
58 import java
.util
.Arrays
;
59 import java
.util
.Collection
;
61 public class LineStatusTrackerManager
implements ProjectComponent
{
62 private static final Logger LOG
= Logger
.getInstance("#com.intellij.openapi.vcs.impl.LineStatusTrackerManager");
64 public static LineStatusTrackerManager
getInstance(Project project
) {
65 return project
.getComponent(LineStatusTrackerManager
.class);
68 private final Project myProject
;
70 private HashMap
<Document
, LineStatusTracker
> myLineStatusTrackers
=
71 new HashMap
<Document
, LineStatusTracker
>();
73 private final HashMap
<Document
, Alarm
> myLineStatusUpdateAlarms
=
74 new HashMap
<Document
, Alarm
>();
76 private final Object TRACKERS_LOCK
= new Object();
77 private boolean myIsDisposed
= false;
78 @NonNls protected static final String IGNORE_CHANGEMARKERS_KEY
= "idea.ignore.changemarkers";
79 private final ProjectLevelVcsManagerImpl myVcsManager
;
80 private final VcsFileStatusProvider myStatusProvider
;
82 public LineStatusTrackerManager(final Project project
, final ProjectLevelVcsManagerImpl vcsManager
, final VcsFileStatusProvider statusProvider
) {
84 myVcsManager
= vcsManager
;
85 myStatusProvider
= statusProvider
;
87 project
.getMessageBus().connect().subscribe(DocumentBulkUpdateListener
.TOPIC
, new DocumentBulkUpdateListener
.Adapter() {
88 public void updateStarted(final Document doc
) {
89 final LineStatusTracker tracker
= getLineStatusTracker(doc
);
90 if (tracker
!= null) tracker
.startBulkUpdate();
93 public void updateFinished(final Document doc
) {
94 final LineStatusTracker tracker
= getLineStatusTracker(doc
);
95 if (tracker
!= null) tracker
.finishBulkUpdate();
100 public void projectOpened() {
102 final MyFileStatusListener fileStatusListener
= new MyFileStatusListener();
103 final EditorFactoryListener editorFactoryListener
= new MyEditorFactoryListener();
104 final MyVirtualFileListener virtualFileListener
= new MyVirtualFileListener();
105 final EditorColorsListener editorColorsListener
= new EditorColorsListener() {
106 public void globalSchemeChange(EditorColorsScheme scheme
) {
107 resetTrackersForOpenFiles();
111 myLineStatusTrackers
= new HashMap
<Document
, LineStatusTracker
>();
112 final FileStatusManager fsManager
= FileStatusManager
.getInstance(myProject
);
113 fsManager
.addFileStatusListener(fileStatusListener
, myProject
);
115 final EditorFactory editorFactory
= EditorFactory
.getInstance();
116 editorFactory
.addEditorFactoryListener(editorFactoryListener
);
118 final VirtualFileManager virtualFileManager
= VirtualFileManager
.getInstance();
119 virtualFileManager
.addVirtualFileListener(virtualFileListener
,myProject
);
121 final EditorColorsManager editorColorsManager
= EditorColorsManager
.getInstance();
122 editorColorsManager
.addEditorColorsListener(editorColorsListener
);
124 Disposer
.register(myProject
, new Disposable() {
125 public void dispose() {
127 fsManager
.removeFileStatusListener(fileStatusListener
);
128 editorFactory
.removeEditorFactoryListener(editorFactoryListener
);
129 virtualFileManager
.removeVirtualFileListener(virtualFileListener
);
130 editorColorsManager
.removeEditorColorsListener(editorColorsListener
);
135 public void projectClosed() {
146 public String
getComponentName() {
147 return "LineStatusTrackerManager";
150 public void initComponent() {
151 //To change body of implemented methods use File | Settings | File Templates.
154 public void disposeComponent() {
155 //To change body of implemented methods use File | Settings | File Templates.
158 private void dispose() {
159 final Collection
<LineStatusTracker
> trackers
= myLineStatusTrackers
.values();
160 final LineStatusTracker
[] lineStatusTrackers
= trackers
.toArray(new LineStatusTracker
[trackers
.size()]);
161 for (LineStatusTracker tracker
: lineStatusTrackers
) {
162 releaseTracker(tracker
.getDocument());
165 myLineStatusTrackers
= null;
168 public LineStatusTracker
getLineStatusTracker(Document document
) {
170 if (myLineStatusTrackers
== null) return null;
171 return myLineStatusTrackers
.get(document
);
175 public LineStatusTracker
setUpToDateContent(final Document document
, final String lastUpToDateContent
, final VirtualFile vf
) {
177 LineStatusTracker result
= myLineStatusTrackers
.get(document
);
178 if (result
== null) {
179 result
= LineStatusTracker
.createOn(document
, lastUpToDateContent
, myProject
, vf
);
180 myLineStatusTrackers
.put(document
, result
);
185 private LineStatusTracker
createTrackerForDocument(Document document
, VirtualFile vf
) {
186 LOG
.assertTrue(!myLineStatusTrackers
.containsKey(document
));
187 LineStatusTracker result
= LineStatusTracker
.createOn(document
, myProject
, vf
);
188 myLineStatusTrackers
.put(document
, result
);
192 private void resetTracker(final VirtualFile virtualFile
) {
193 if (System
.getProperty(IGNORE_CHANGEMARKERS_KEY
) != null) return;
195 final Document document
= FileDocumentManager
.getInstance().getCachedDocument(virtualFile
);
196 if (document
== null) {
197 if (LOG
.isDebugEnabled()) {
198 LOG
.debug("Skipping resetTracker() because no cached document for " + virtualFile
.getPath());
203 if (LOG
.isDebugEnabled()) {
204 LOG
.debug("resetting tracker for file " + virtualFile
.getPath());
206 synchronized (TRACKERS_LOCK
) {
207 final LineStatusTracker tracker
= myLineStatusTrackers
.get(document
);
208 if (tracker
!= null) {
209 resetTracker(tracker
);
212 if (Arrays
.asList(FileEditorManager
.getInstance(myProject
).getOpenFiles()).contains(virtualFile
)) {
213 installTracker(virtualFile
, document
);
219 private boolean releaseTracker(Document document
) {
220 synchronized (TRACKERS_LOCK
) {
221 releaseUpdateAlarms(document
);
222 if (myLineStatusTrackers
== null) return false;
223 if (!myLineStatusTrackers
.containsKey(document
)) return false;
224 LineStatusTracker tracker
= myLineStatusTrackers
.remove(document
);
230 private void releaseUpdateAlarms(final Document document
) {
231 if (myLineStatusUpdateAlarms
.containsKey(document
)) {
232 final Alarm alarm
= myLineStatusUpdateAlarms
.get(document
);
234 alarm
.cancelAllRequests();
236 myLineStatusUpdateAlarms
.remove(document
);
240 public void resetTracker(final LineStatusTracker tracker
) {
242 if (tracker
!= null) {
243 ApplicationManager
.getApplication().invokeLater(new Runnable() {
245 if (myIsDisposed
) return;
246 if (releaseTracker(tracker
.getDocument())) {
247 installTracker(tracker
.getVirtualFile(), tracker
.getDocument());
254 private void installTracker(final VirtualFile virtualFile
, final Document document
) {
255 if (virtualFile
== null || virtualFile
instanceof LightVirtualFile
) return;
256 ApplicationManager
.getApplication().assertIsDispatchThread();
258 if (myProject
.isDisposed()) return;
259 final FileStatusManager statusManager
= FileStatusManager
.getInstance(myProject
);
260 if (statusManager
== null) return;
261 final FileStatus status
= statusManager
.getStatus(virtualFile
);
263 synchronized (TRACKERS_LOCK
) {
264 if (myLineStatusTrackers
.containsKey(document
)) return;
266 if (status
== FileStatus
.NOT_CHANGED
||
267 status
== FileStatus
.ADDED
||
268 status
== FileStatus
.UNKNOWN
||
269 status
== FileStatus
.IGNORED
) {
270 if (LOG
.isDebugEnabled()) {
271 LOG
.debug("installTracker() for file " + virtualFile
.getPath() + " failed: status=" + status
);
276 AbstractVcs activeVcs
= myVcsManager
.getVcsFor(virtualFile
);
278 if (activeVcs
== null) {
279 if (LOG
.isDebugEnabled()) {
280 LOG
.debug("installTracker() for file " + virtualFile
.getPath() + " failed: no active VCS");
285 if (!virtualFile
.isInLocalFileSystem()) return;
287 if (System
.getProperty(IGNORE_CHANGEMARKERS_KEY
) != null) return;
291 if (myLineStatusUpdateAlarms
.containsKey(document
)) {
292 alarm
= myLineStatusUpdateAlarms
.get(document
);
293 alarm
.cancelAllRequests();
296 alarm
= new Alarm(Alarm
.ThreadToUse
.SHARED_THREAD
);
297 myLineStatusUpdateAlarms
.put(document
, alarm
);
300 final LineStatusTracker tracker
= createTrackerForDocument(document
, virtualFile
);
302 alarm
.addRequest(new Runnable() {
305 alarm
.cancelAllRequests();
306 if (!virtualFile
.isValid()) {
307 if (LOG
.isDebugEnabled()) {
308 LOG
.debug("installTracker() for file " + virtualFile
.getPath() + " failed: virtual file not valid");
312 final String lastUpToDateContent
= myStatusProvider
.getBaseVersionContent(virtualFile
);
313 if (lastUpToDateContent
== null) {
314 if (LOG
.isDebugEnabled()) {
315 LOG
.debug("installTracker() for file " + virtualFile
.getPath() + " failed: no up to date content");
319 ApplicationManager
.getApplication().invokeLater(new Runnable() {
321 if (!myProject
.isDisposed()) {
322 ApplicationManager
.getApplication().runWriteAction(new Runnable() {
324 if (LOG
.isDebugEnabled()) {
325 LOG
.debug("initializing tracker for file " + virtualFile
.getPath());
327 synchronized (TRACKERS_LOCK
) {
328 tracker
.initialize(lastUpToDateContent
);
337 // todo guard alarms!!!
338 myLineStatusUpdateAlarms
.remove(document
);
346 private void resetTrackersForOpenFiles() {
347 final VirtualFile
[] openFiles
= FileEditorManager
.getInstance(myProject
).getOpenFiles();
348 synchronized (TRACKERS_LOCK
) {
349 for(VirtualFile openFile
: openFiles
) {
350 resetTracker(openFile
);
355 private class MyFileStatusListener
implements FileStatusListener
{
356 public void fileStatusesChanged() {
357 if (myProject
.isDisposed()) return;
358 LOG
.debug("LineStatusTrackerManager: fileStatusesChanged");
360 resetTrackersForOpenFiles();
363 public void fileStatusChanged(@NotNull VirtualFile virtualFile
) {
365 resetTracker(virtualFile
);
369 private class MyEditorFactoryListener
extends EditorFactoryAdapter
{
370 public void editorCreated(EditorFactoryEvent event
) {
372 Editor editor
= event
.getEditor();
373 if (editor
.getProject() != null && editor
.getProject() != myProject
) return;
374 Document document
= editor
.getDocument();
375 VirtualFile virtualFile
= FileDocumentManager
.getInstance().getFile(document
);
376 installTracker(virtualFile
, document
);
379 public void editorReleased(EditorFactoryEvent event
) {
381 final Editor editor
= event
.getEditor();
382 if (editor
.getProject() != null && editor
.getProject() != myProject
) return;
383 final Document doc
= editor
.getDocument();
384 final Editor
[] editors
= event
.getFactory().getEditors(doc
, myProject
);
385 if (editors
.length
== 0) {
391 private class MyVirtualFileListener
extends VirtualFileAdapter
{
392 public void beforeContentsChange(VirtualFileEvent event
) {
394 if (event
.isFromRefresh()) {
395 resetTracker(event
.getFile());
400 private void trackAwtThread() {
401 if (! ApplicationManager
.getApplication().isDispatchThread()) {
402 LOG
.info("NOT dispatch thread: " + Thread
.currentThread().getName(), new Throwable());