diff: add annotate action to diff viewers
[idea/community.git] / platform / diff-impl / src / com / intellij / diff / actions / DocumentFragmentContent.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package com.intellij.diff.actions;
17
18 import com.intellij.diff.contents.DocumentContent;
19 import com.intellij.openapi.command.undo.UndoManager;
20 import com.intellij.openapi.editor.Document;
21 import com.intellij.openapi.editor.EditorFactory;
22 import com.intellij.openapi.editor.RangeMarker;
23 import com.intellij.openapi.editor.event.DocumentEvent;
24 import com.intellij.openapi.fileEditor.OpenFileDescriptor;
25 import com.intellij.openapi.fileTypes.FileType;
26 import com.intellij.openapi.project.Project;
27 import com.intellij.openapi.util.TextRange;
28 import com.intellij.openapi.vfs.VirtualFile;
29 import com.intellij.util.LineSeparator;
30 import org.jetbrains.annotations.NotNull;
31 import org.jetbrains.annotations.Nullable;
32
33 import java.nio.charset.Charset;
34
35 /**
36  * Represents sub text of other content. Original content should provide not null document.
37  */
38 public class DocumentFragmentContent implements DocumentContent {
39   // TODO: reuse DocumentWindow ?
40
41   @NotNull private final DocumentContent myOriginal;
42
43   @NotNull private final MyDocumentsSynchronizer mySynchonizer;
44
45   private int myAssignments = 0;
46
47   public DocumentFragmentContent(@NotNull Project project, @NotNull DocumentContent original, @NotNull TextRange range) {
48     myOriginal = original;
49
50     RangeMarker rangeMarker = myOriginal.getDocument().createRangeMarker(range.getStartOffset(), range.getEndOffset(), true);
51     rangeMarker.setGreedyToLeft(true);
52     rangeMarker.setGreedyToRight(true);
53
54     mySynchonizer = new MyDocumentsSynchronizer(project, rangeMarker);
55   }
56
57   @NotNull
58   @Override
59   public Document getDocument() {
60     return mySynchonizer.getDocument2();
61   }
62
63   @Nullable
64   @Override
65   public VirtualFile getHighlightFile() {
66     return myOriginal.getHighlightFile();
67   }
68
69   @Nullable
70   @Override
71   public OpenFileDescriptor getOpenFileDescriptor(int offset) {
72     return myOriginal.getOpenFileDescriptor(offset + mySynchonizer.getStartOffset());
73   }
74
75   @Nullable
76   @Override
77   public LineSeparator getLineSeparator() {
78     return null;
79   }
80
81   @Nullable
82   @Override
83   public Charset getCharset() {
84     return null;
85   }
86
87   @Nullable
88   @Override
89   public FileType getContentType() {
90     return myOriginal.getContentType();
91   }
92
93   @Nullable
94   @Override
95   public OpenFileDescriptor getOpenFileDescriptor() {
96     return getOpenFileDescriptor(0);
97   }
98
99   @Override
100   public void onAssigned(boolean isAssigned) {
101     if (isAssigned) {
102       if (myAssignments == 0) mySynchonizer.startListen();
103       myAssignments++;
104     }
105     else {
106       myAssignments--;
107       if (myAssignments == 0) mySynchonizer.stopListen();
108     }
109     assert myAssignments >= 0;
110   }
111
112   private static class MyDocumentsSynchronizer extends DocumentsSynchronizer {
113     @NotNull private final RangeMarker myRangeMarker;
114
115     public MyDocumentsSynchronizer(@NotNull Project project, @NotNull RangeMarker originalRange) {
116       super(project, getOriginal(originalRange), getCopy(originalRange));
117       myRangeMarker = originalRange;
118     }
119
120     public int getStartOffset() {
121       return myRangeMarker.getStartOffset();
122     }
123
124     public int getEndOffset() {
125       return myRangeMarker.getEndOffset();
126     }
127
128     @Override
129     protected void onDocumentChanged1(@NotNull DocumentEvent event) {
130       if (!myRangeMarker.isValid()) {
131         myDocument2.setReadOnly(false);
132         replaceString(myDocument2, 0, myDocument2.getTextLength(), "Invalid selection range");
133         myDocument2.setReadOnly(true);
134         return;
135       }
136       CharSequence newText = myDocument1.getCharsSequence().subSequence(myRangeMarker.getStartOffset(), myRangeMarker.getEndOffset());
137       replaceString(myDocument2, 0, myDocument2.getTextLength(), newText);
138     }
139
140     @Override
141     protected void onDocumentChanged2(@NotNull DocumentEvent event) {
142       if (!myRangeMarker.isValid()) {
143         return;
144       }
145       if (!myDocument1.isWritable()) return;
146
147       CharSequence newText = event.getNewFragment();
148       int originalOffset = event.getOffset() + myRangeMarker.getStartOffset();
149       int originalEnd = originalOffset + event.getOldLength();
150       replaceString(myDocument1, originalOffset, originalEnd, newText);
151     }
152
153     @Override
154     public void startListen() {
155       if (myRangeMarker.isValid()) {
156         myDocument2.setReadOnly(false);
157         CharSequence nexText = myDocument1.getCharsSequence().subSequence(myRangeMarker.getStartOffset(), myRangeMarker.getEndOffset());
158         replaceString(myDocument2, 0, myDocument2.getTextLength(), nexText);
159         myDocument2.setReadOnly(!myDocument1.isWritable());
160       }
161       else {
162         myDocument2.setReadOnly(false);
163         replaceString(myDocument2, 0, myDocument2.getTextLength(), "Invalid selection range");
164         myDocument2.setReadOnly(true);
165       }
166       super.startListen();
167     }
168   }
169
170   @NotNull
171   protected static Document getOriginal(@NotNull RangeMarker rangeMarker) {
172     return rangeMarker.getDocument();
173   }
174
175   @NotNull
176   protected static Document getCopy(@NotNull RangeMarker rangeMarker) {
177     final Document originalDocument = rangeMarker.getDocument();
178
179     Document result = EditorFactory.getInstance().createDocument("");
180     result.putUserData(UndoManager.ORIGINAL_DOCUMENT, originalDocument);
181     return result;
182   }
183 }