PageRenderTime 55ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/platform/lang-impl/src/com/intellij/codeInsight/folding/impl/DocumentFoldingInfo.java

https://github.com/joewalnes/idea-community
Java | 268 lines | 222 code | 31 blank | 15 comment | 72 complexity | b57f654307f140263422f9b92c11ef77 MD5 | raw file
  1. /*
  2. * Copyright 2000-2009 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.codeInsight.folding.impl;
  17. import com.intellij.lang.ASTNode;
  18. import com.intellij.lang.folding.FoldingBuilder;
  19. import com.intellij.lang.folding.FoldingDescriptor;
  20. import com.intellij.lang.folding.LanguageFolding;
  21. import com.intellij.openapi.application.ApplicationManager;
  22. import com.intellij.openapi.diagnostic.Logger;
  23. import com.intellij.openapi.editor.Document;
  24. import com.intellij.openapi.editor.Editor;
  25. import com.intellij.openapi.editor.FoldRegion;
  26. import com.intellij.openapi.editor.RangeMarker;
  27. import com.intellij.openapi.fileEditor.FileDocumentManager;
  28. import com.intellij.openapi.fileEditor.impl.text.CodeFoldingState;
  29. import com.intellij.openapi.project.Project;
  30. import com.intellij.openapi.util.JDOMExternalizable;
  31. import com.intellij.openapi.util.TextRange;
  32. import com.intellij.openapi.util.WriteExternalException;
  33. import com.intellij.openapi.vfs.VirtualFile;
  34. import com.intellij.psi.PsiDocumentManager;
  35. import com.intellij.psi.PsiElement;
  36. import com.intellij.psi.PsiFile;
  37. import com.intellij.psi.PsiManager;
  38. import com.intellij.util.text.StringTokenizer;
  39. import org.jdom.Element;
  40. import org.jetbrains.annotations.NonNls;
  41. import java.util.*;
  42. public class DocumentFoldingInfo implements JDOMExternalizable, CodeFoldingState {
  43. private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.folding.impl.DocumentFoldingInfo");
  44. private final Project myProject;
  45. private final VirtualFile myFile;
  46. private final ArrayList<Object> myPsiElementsOrRangeMarkers = new ArrayList<Object>();
  47. private final ArrayList<Boolean> myExpandedStates = new ArrayList<Boolean>();
  48. private final Map<RangeMarker, String> myPlaceholderTexts = new HashMap<RangeMarker,String>();
  49. private static final String DEFAULT_PLACEHOLDER = "...";
  50. @NonNls private static final String ELEMENT_TAG = "element";
  51. @NonNls private static final String SIGNATURE_ATT = "signature";
  52. @NonNls private static final String EXPANDED_ATT = "expanded";
  53. @NonNls private static final String MARKER_TAG = "marker";
  54. @NonNls private static final String DATE_ATT = "date";
  55. @NonNls private static final String PLACEHOLDER_ATT = "placeholder";
  56. public DocumentFoldingInfo(Project project, Document document) {
  57. myProject = project;
  58. myFile = FileDocumentManager.getInstance().getFile(document);
  59. }
  60. public void loadFromEditor(Editor editor) {
  61. LOG.assertTrue(!editor.isDisposed());
  62. clear();
  63. PsiDocumentManager.getInstance(myProject).commitDocument(editor.getDocument());
  64. EditorFoldingInfo info = EditorFoldingInfo.get(editor);
  65. FoldRegion[] foldRegions = editor.getFoldingModel().getAllFoldRegions();
  66. for (FoldRegion region : foldRegions) {
  67. PsiElement element = info.getPsiElement(region);
  68. boolean expanded = region.isExpanded();
  69. boolean collapseByDefault = element != null &&
  70. FoldingPolicy.isCollapseByDefault(element) &&
  71. !FoldingUtil.caretInsideRange(editor, new TextRange(region.getStartOffset(), region.getEndOffset()));
  72. if (collapseByDefault != !expanded || element == null) {
  73. if (element != null) {
  74. myPsiElementsOrRangeMarkers.add(element);
  75. }
  76. else if (region.isValid()) {
  77. myPsiElementsOrRangeMarkers.add(region);
  78. String placeholderText = region.getPlaceholderText();
  79. myPlaceholderTexts.put(region, placeholderText);
  80. }
  81. myExpandedStates.add(expanded ? Boolean.TRUE : Boolean.FALSE);
  82. }
  83. }
  84. }
  85. void setToEditor(Editor editor) {
  86. LOG.assertTrue(ApplicationManager.getApplication().isReadAccessAllowed());
  87. final PsiManager psiManager = PsiManager.getInstance(myProject);
  88. if (psiManager.isDisposed()) return;
  89. if (!myFile.isValid()) return;
  90. final PsiFile psiFile = psiManager.findFile(myFile);
  91. if (psiFile == null) return;
  92. Map<PsiElement, FoldingDescriptor> ranges = null;
  93. for(int i = 0; i < myPsiElementsOrRangeMarkers.size(); i++){
  94. Object o = myPsiElementsOrRangeMarkers.get(i);
  95. if (o instanceof PsiElement) {
  96. PsiElement element = (PsiElement)o;
  97. if (!element.isValid()) continue;
  98. if (ranges == null) ranges = buildRanges(editor, psiFile);
  99. FoldingDescriptor descriptor = ranges.get(element);
  100. if (descriptor == null) continue;
  101. TextRange range = descriptor.getRange();
  102. FoldRegion region = FoldingUtil.findFoldRegion(editor, range.getStartOffset(), range.getEndOffset());
  103. if (region != null) {
  104. boolean state = myExpandedStates.get(i).booleanValue();
  105. region.setExpanded(state);
  106. }
  107. }
  108. else if (o instanceof RangeMarker) {
  109. RangeMarker marker = (RangeMarker)o;
  110. if (!marker.isValid()) continue;
  111. FoldRegion region = FoldingUtil.findFoldRegion(editor, marker.getStartOffset(), marker.getEndOffset());
  112. if (region == null) {
  113. String placeHolderText = myPlaceholderTexts.get(marker);
  114. region = editor.getFoldingModel().addFoldRegion(marker.getStartOffset(), marker.getEndOffset(), placeHolderText);
  115. if (region == null) return;
  116. }
  117. boolean state = myExpandedStates.get(i).booleanValue();
  118. region.setExpanded(state);
  119. }
  120. else{
  121. LOG.error("o = " + o);
  122. }
  123. }
  124. }
  125. private static Map<PsiElement, FoldingDescriptor> buildRanges(final Editor editor, final PsiFile psiFile) {
  126. final FoldingBuilder foldingBuilder = LanguageFolding.INSTANCE.forLanguage(psiFile.getLanguage());
  127. final ASTNode node = psiFile.getNode();
  128. if (node == null) return Collections.emptyMap();
  129. final FoldingDescriptor[] descriptors = LanguageFolding.buildFoldingDescriptors(foldingBuilder, psiFile, editor.getDocument(), true);
  130. Map<PsiElement, FoldingDescriptor> ranges = new HashMap<PsiElement, FoldingDescriptor>();
  131. for (FoldingDescriptor descriptor : descriptors) {
  132. final ASTNode ast = descriptor.getElement();
  133. final PsiElement psi = ast.getPsi();
  134. if (psi != null) {
  135. ranges.put(psi, descriptor);
  136. }
  137. }
  138. return ranges;
  139. }
  140. public void clear() {
  141. myPsiElementsOrRangeMarkers.clear();
  142. myExpandedStates.clear();
  143. myPlaceholderTexts.clear();
  144. }
  145. public void writeExternal(Element element) throws WriteExternalException {
  146. PsiDocumentManager.getInstance(myProject).commitAllDocuments();
  147. if (myPsiElementsOrRangeMarkers.isEmpty()){
  148. throw new WriteExternalException();
  149. }
  150. String date = null;
  151. for(int i = 0; i < myPsiElementsOrRangeMarkers.size(); i++){
  152. Object o = myPsiElementsOrRangeMarkers.get(i);
  153. Boolean state = myExpandedStates.get(i);
  154. if (o instanceof PsiElement){
  155. PsiElement psiElement = (PsiElement)o;
  156. if (!psiElement.isValid()) continue;
  157. String signature = FoldingPolicy.getSignature(psiElement);
  158. if (signature == null) continue;
  159. PsiElement restoredElement = FoldingPolicy.restoreBySignature(psiElement.getContainingFile(), signature);
  160. if (!psiElement.equals(restoredElement)){
  161. LOG.error("element:" + psiElement +"; restoredElement:"+ restoredElement+"; signature: '" + signature + "'; file:" + psiElement.getContainingFile());
  162. }
  163. Element e = new Element(ELEMENT_TAG);
  164. e.setAttribute(SIGNATURE_ATT, signature);
  165. e.setAttribute(EXPANDED_ATT, state.toString());
  166. element.addContent(e);
  167. } else {
  168. RangeMarker marker = (RangeMarker) o;
  169. Element e = new Element(MARKER_TAG);
  170. if (date == null) {
  171. date = getTimeStamp();
  172. }
  173. if ("".equals(date)) continue;
  174. e.setAttribute(DATE_ATT, date);
  175. e.setAttribute(EXPANDED_ATT, state.toString());
  176. String signature = Integer.valueOf(marker.getStartOffset()) + ":" + Integer.valueOf(marker.getEndOffset());
  177. e.setAttribute(SIGNATURE_ATT, signature);
  178. String placeHolderText = myPlaceholderTexts.get(marker);
  179. e.setAttribute(PLACEHOLDER_ATT, placeHolderText);
  180. element.addContent(e);
  181. }
  182. }
  183. }
  184. public void readExternal(Element element) {
  185. myPsiElementsOrRangeMarkers.clear();
  186. myExpandedStates.clear();
  187. if (!myFile.isValid()) return;
  188. final Document document = FileDocumentManager.getInstance().getDocument(myFile);
  189. if (document == null) return;
  190. PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document);
  191. if (psiFile == null || !psiFile.getViewProvider().isPhysical()) return;
  192. String date = null;
  193. for (final Object o : element.getChildren()) {
  194. Element e = (Element)o;
  195. if (ELEMENT_TAG.equals(e.getName())) {
  196. String signature = e.getAttributeValue(SIGNATURE_ATT);
  197. if (signature == null) {
  198. continue;
  199. }
  200. PsiElement restoredElement = FoldingPolicy.restoreBySignature(psiFile, signature);
  201. if (restoredElement != null) {
  202. myPsiElementsOrRangeMarkers.add(restoredElement);
  203. myExpandedStates.add(Boolean.valueOf(e.getAttributeValue(EXPANDED_ATT)));
  204. }
  205. }
  206. else if (MARKER_TAG.equals(e.getName())) {
  207. if (date == null) {
  208. date = getTimeStamp();
  209. }
  210. if ("".equals(date)) continue;
  211. if (!date.equals(e.getAttributeValue(DATE_ATT)) || FileDocumentManager.getInstance().isDocumentUnsaved(document)) continue;
  212. StringTokenizer tokenizer = new StringTokenizer(e.getAttributeValue(SIGNATURE_ATT), ":");
  213. try {
  214. int start = Integer.valueOf(tokenizer.nextToken()).intValue();
  215. int end = Integer.valueOf(tokenizer.nextToken()).intValue();
  216. if (start < 0 || end >= document.getTextLength() || start > end) continue;
  217. RangeMarker marker = document.createRangeMarker(start, end);
  218. myPsiElementsOrRangeMarkers.add(marker);
  219. myExpandedStates.add(Boolean.valueOf(e.getAttributeValue(EXPANDED_ATT)));
  220. String placeHolderText = e.getAttributeValue(PLACEHOLDER_ATT);
  221. if (placeHolderText == null) placeHolderText = DEFAULT_PLACEHOLDER;
  222. myPlaceholderTexts.put(marker, placeHolderText);
  223. }
  224. catch (NoSuchElementException exc) {
  225. LOG.error(exc);
  226. }
  227. }
  228. else {
  229. throw new IllegalStateException("unknown tag: " + e.getName());
  230. }
  231. }
  232. }
  233. private String getTimeStamp() {
  234. if (!myFile.isValid()) return "";
  235. return Long.toString(myFile.getTimeStamp());
  236. }
  237. }