1b570cf2f3f1695bcd7bda76442ab7c12954b7b0
[idea/community.git] / platform / lang-impl / src / com / intellij / codeInsight / completion / ActionTracker.java
1 // Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
2 package com.intellij.codeInsight.completion;
3
4 import com.intellij.injected.editor.EditorWindow;
5 import com.intellij.openapi.Disposable;
6 import com.intellij.openapi.actionSystem.AnAction;
7 import com.intellij.openapi.actionSystem.AnActionEvent;
8 import com.intellij.openapi.actionSystem.DataContext;
9 import com.intellij.openapi.actionSystem.ex.AnActionListener;
10 import com.intellij.openapi.command.CommandEvent;
11 import com.intellij.openapi.command.CommandListener;
12 import com.intellij.openapi.command.CommandProcessor;
13 import com.intellij.openapi.editor.Caret;
14 import com.intellij.openapi.editor.Editor;
15 import com.intellij.openapi.project.DumbService;
16 import com.intellij.openapi.project.Project;
17 import com.intellij.util.containers.ContainerUtil;
18 import com.intellij.util.messages.MessageBusConnection;
19 import org.jetbrains.annotations.NotNull;
20
21 import java.util.List;
22 import java.util.Objects;
23
24 /**
25  * @author peter
26  */
27 class ActionTracker {
28   private final @NotNull MessageBusConnection myConnection;
29   private @NotNull List<Integer> myCaretOffsets;
30   private long myStartDocStamp;
31   private boolean myActionsHappened;
32   private final Editor myEditor;
33   private final Project myProject;
34   private final boolean myIsDumb;
35
36   ActionTracker(Editor editor, Disposable parentDisposable) {
37     myEditor = editor;
38     myProject = Objects.requireNonNull(editor.getProject());
39     myIsDumb = DumbService.getInstance(myProject).isDumb();
40
41     myConnection = myProject.getMessageBus().connect(parentDisposable);
42     myConnection.subscribe(AnActionListener.TOPIC, new AnActionListener() {
43       @Override
44       public void beforeActionPerformed(@NotNull AnAction action, @NotNull DataContext dataContext, @NotNull AnActionEvent event) {
45         myActionsHappened = true;
46       }
47     });
48     myStartDocStamp = docStamp();
49     myCaretOffsets = caretOffsets();
50   }
51
52   private List<Integer> caretOffsets() {
53     return ContainerUtil.map(myEditor.getCaretModel().getAllCarets(), Caret::getOffset);
54   }
55
56   private long docStamp() {
57     return myEditor.getDocument().getModificationStamp();
58   }
59
60   void ignoreCurrentDocumentChange() {
61     if (CommandProcessor.getInstance().getCurrentCommand() == null) {
62       return;
63     }
64
65     myConnection.subscribe(CommandListener.TOPIC, new CommandListener() {
66       boolean insideCommand = true;
67       @Override
68       public void commandFinished(@NotNull CommandEvent event) {
69         if (insideCommand) {
70           insideCommand = false;
71           myStartDocStamp = docStamp();
72           myCaretOffsets = caretOffsets();
73         }
74       }
75     });
76   }
77
78   boolean hasAnythingHappened() {
79     return myActionsHappened || myIsDumb != DumbService.getInstance(myProject).isDumb() ||
80            myEditor.isDisposed() ||
81            (myEditor instanceof EditorWindow && !((EditorWindow)myEditor).isValid()) ||
82            myStartDocStamp != docStamp() ||
83            !myCaretOffsets.equals(caretOffsets());
84   }
85 }