PageRenderTime 43ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

/projects/netbeans-7.3/csl.api/src/org/netbeans/modules/csl/editor/fold/GsfFoldManager.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 850 lines | 594 code | 139 blank | 117 comment | 117 complexity | acd46bee40daf516a49f1ff137005da0 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.modules.csl.editor.fold;
  45. import java.io.PrintWriter;
  46. import java.io.StringWriter;
  47. import java.lang.ref.Reference;
  48. import java.lang.ref.WeakReference;
  49. import java.util.ArrayList;
  50. import java.util.Collection;
  51. import java.util.Collections;
  52. import java.util.HashMap;
  53. import java.util.Iterator;
  54. import java.util.List;
  55. import java.util.Map;
  56. import java.util.TreeMap;
  57. import java.util.TreeSet;
  58. import java.util.WeakHashMap;
  59. import java.util.concurrent.atomic.AtomicBoolean;
  60. import java.util.logging.Level;
  61. import java.util.logging.Logger;
  62. import java.util.prefs.Preferences;
  63. import javax.swing.SwingUtilities;
  64. import javax.swing.event.DocumentEvent;
  65. import javax.swing.text.BadLocationException;
  66. import javax.swing.text.Document;
  67. import javax.swing.text.Position;
  68. import org.netbeans.api.editor.fold.Fold;
  69. import org.netbeans.api.editor.fold.FoldType;
  70. import org.netbeans.api.editor.mimelookup.MimeLookup;
  71. import org.netbeans.modules.csl.api.OffsetRange;
  72. import org.netbeans.modules.csl.api.Severity;
  73. import org.netbeans.modules.csl.api.StructureScanner;
  74. import org.netbeans.api.lexer.Token;
  75. import org.netbeans.api.lexer.TokenHierarchy;
  76. import org.netbeans.api.lexer.TokenSequence;
  77. import org.netbeans.editor.BaseDocument;
  78. import org.netbeans.lib.editor.util.swing.DocumentUtilities;
  79. import org.netbeans.modules.parsing.api.ResultIterator;
  80. import org.netbeans.modules.parsing.spi.Scheduler;
  81. import org.netbeans.spi.editor.fold.FoldHierarchyTransaction;
  82. import org.netbeans.spi.editor.fold.FoldManager;
  83. import org.netbeans.spi.editor.fold.FoldOperation;
  84. import org.openide.filesystems.FileObject;
  85. import org.netbeans.modules.csl.core.Language;
  86. import org.netbeans.modules.csl.core.LanguageRegistry;
  87. import org.netbeans.modules.csl.api.DataLoadersBridge;
  88. import org.netbeans.modules.csl.spi.ParserResult;
  89. import org.netbeans.modules.parsing.api.Embedding;
  90. import org.netbeans.modules.parsing.api.ParserManager;
  91. import org.netbeans.modules.parsing.api.Source;
  92. import org.netbeans.modules.parsing.api.UserTask;
  93. import org.netbeans.modules.parsing.spi.*;
  94. import org.openide.text.NbDocument;
  95. /**
  96. * This file is originally from Retouche, the Java Support
  97. * infrastructure in NetBeans. I have modified the file as little
  98. * as possible to make merging Retouche fixes back as simple as
  99. * possible.
  100. *
  101. * Copied from both JavaFoldManager and JavaElementFoldManager
  102. *
  103. *
  104. * @author Jan Lahoda
  105. * @author Tor Norbye
  106. */
  107. public class GsfFoldManager implements FoldManager {
  108. private static final Logger LOG = Logger.getLogger(GsfFoldManager.class.getName());
  109. public static final FoldType CODE_BLOCK_FOLD_TYPE = new FoldType("code-block"); // NOI18N
  110. public static final FoldType INITIAL_COMMENT_FOLD_TYPE = new FoldType("initial-comment"); // NOI18N
  111. public static final FoldType IMPORTS_FOLD_TYPE = new FoldType("imports"); // NOI18N
  112. public static final FoldType JAVADOC_FOLD_TYPE = new FoldType("javadoc"); // NOI18N
  113. public static final FoldType TAG_FOLD_TYPE = new FoldType("tag"); // NOI18N
  114. public static final FoldType INNER_CLASS_FOLD_TYPE = new FoldType("inner-class"); // NOI18N
  115. private static final String IMPORTS_FOLD_DESCRIPTION = "..."; // NOI18N
  116. private static final String COMMENT_FOLD_DESCRIPTION = "..."; // NOI18N
  117. private static final String JAVADOC_FOLD_DESCRIPTION = "..."; // NOI18N
  118. private static final String CODE_BLOCK_FOLD_DESCRIPTION = "{...}"; // NOI18N
  119. private static final String TAG_FOLD_DESCRIPTION = "<.../>"; // NOI18N
  120. public static final FoldTemplate CODE_BLOCK_FOLD_TEMPLATE
  121. = new FoldTemplate(CODE_BLOCK_FOLD_TYPE, CODE_BLOCK_FOLD_DESCRIPTION, 1, 1);
  122. public static final FoldTemplate INITIAL_COMMENT_FOLD_TEMPLATE
  123. = new FoldTemplate(INITIAL_COMMENT_FOLD_TYPE, COMMENT_FOLD_DESCRIPTION, 2, 2);
  124. public static final FoldTemplate IMPORTS_FOLD_TEMPLATE
  125. = new FoldTemplate(IMPORTS_FOLD_TYPE, IMPORTS_FOLD_DESCRIPTION, 0, 0);
  126. public static final FoldTemplate JAVADOC_FOLD_TEMPLATE
  127. = new FoldTemplate(JAVADOC_FOLD_TYPE, JAVADOC_FOLD_DESCRIPTION, 3, 2);
  128. public static final FoldTemplate TAG_FOLD_TEMPLATE
  129. = new FoldTemplate(TAG_FOLD_TYPE, TAG_FOLD_DESCRIPTION, 0, 0);
  130. public static final FoldTemplate INNER_CLASS_FOLD_TEMPLATE
  131. = new FoldTemplate(INNER_CLASS_FOLD_TYPE, CODE_BLOCK_FOLD_DESCRIPTION, 0, 0);
  132. public static final String CODE_FOLDING_ENABLE = "code-folding-enable"; //NOI18N
  133. /** Collapse methods by default
  134. * NOTE: This must be kept in sync with string literal in editor/options
  135. */
  136. public static final String CODE_FOLDING_COLLAPSE_METHOD = "code-folding-collapse-method"; //NOI18N
  137. /**
  138. * Collapse inner classes by default
  139. * NOTE: This must be kept in sync with string literal in editor/options
  140. */
  141. public static final String CODE_FOLDING_COLLAPSE_INNERCLASS = "code-folding-collapse-innerclass"; //NOI18N
  142. /**
  143. * Collapse import section default
  144. * NOTE: This must be kept in sync with string literal in editor/options
  145. */
  146. public static final String CODE_FOLDING_COLLAPSE_IMPORT = "code-folding-collapse-import"; //NOI18N
  147. /**
  148. * Collapse javadoc comment by default
  149. * NOTE: This must be kept in sync with string literal in editor/options
  150. */
  151. public static final String CODE_FOLDING_COLLAPSE_JAVADOC = "code-folding-collapse-javadoc"; //NOI18N
  152. /**
  153. * Collapse initial comment by default
  154. * NOTE: This must be kept in sync with string literal in editor/options
  155. */
  156. public static final String CODE_FOLDING_COLLAPSE_INITIAL_COMMENT = "code-folding-collapse-initial-comment"; //NOI18N
  157. /**
  158. * Collapse tags by default
  159. * NOTE: This must be kept in sync with string literal in editor/options
  160. */
  161. public static final String CODE_FOLDING_COLLAPSE_TAGS = "code-folding-collapse-tags"; //NOI18N
  162. protected static final class FoldTemplate {
  163. private FoldType type;
  164. private String description;
  165. private int startGuardedLength;
  166. private int endGuardedLength;
  167. protected FoldTemplate(FoldType type, String description,
  168. int startGuardedLength, int endGuardedLength) {
  169. this.type = type;
  170. this.description = description;
  171. this.startGuardedLength = startGuardedLength;
  172. this.endGuardedLength = endGuardedLength;
  173. }
  174. public FoldType getType() {
  175. return type;
  176. }
  177. public String getDescription() {
  178. return description;
  179. }
  180. public int getStartGuardedLength() {
  181. return startGuardedLength;
  182. }
  183. public int getEndGuardedLength() {
  184. return endGuardedLength;
  185. }
  186. }
  187. private FoldOperation operation;
  188. private FileObject file;
  189. private JavaElementFoldTask task;
  190. // // Folding presets
  191. // private boolean foldImportsPreset;
  192. // private boolean foldInnerClassesPreset;
  193. // private boolean foldJavadocsPreset;
  194. // private boolean foldCodeBlocksPreset;
  195. // private boolean foldInitialCommentsPreset;
  196. private static volatile Preferences prefs;
  197. /** Creates a new instance of GsfFoldManager */
  198. public GsfFoldManager() {
  199. }
  200. public void init(FoldOperation operation) {
  201. this.operation = operation;
  202. String mimeType = DocumentUtilities.getMimeType(operation.getHierarchy().getComponent());
  203. if (prefs == null) {
  204. prefs = MimeLookup.getLookup(mimeType).lookup(Preferences.class);
  205. }
  206. }
  207. @Override
  208. public synchronized void initFolds(FoldHierarchyTransaction transaction) {
  209. Document doc = operation.getHierarchy().getComponent().getDocument();
  210. file = DataLoadersBridge.getDefault().getFileObject(doc);
  211. if (file != null) {
  212. currentFolds = new HashMap<FoldInfo, Fold>();
  213. task = JavaElementFoldTask.getTask(file);
  214. task.setGsfFoldManager(GsfFoldManager.this, file);
  215. }
  216. }
  217. @Override
  218. public void insertUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) {
  219. }
  220. @Override
  221. public void removeUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) {
  222. }
  223. @Override
  224. public void changedUpdate(DocumentEvent evt, FoldHierarchyTransaction transaction) {
  225. }
  226. @Override
  227. public void removeEmptyNotify(Fold emptyFold) {
  228. removeDamagedNotify(emptyFold);
  229. }
  230. @Override
  231. public void removeDamagedNotify(Fold damagedFold) {
  232. currentFolds.remove(operation.getExtraInfo(damagedFold));
  233. if (importsFold == damagedFold) {
  234. importsFold = null;//not sure if this is correct...
  235. }
  236. if (initialCommentFold == damagedFold) {
  237. initialCommentFold = null;//not sure if this is correct...
  238. }
  239. }
  240. public void expandNotify(Fold expandedFold) {
  241. }
  242. public synchronized void release() {
  243. if (task != null) {
  244. task.setGsfFoldManager(this, null);
  245. }
  246. task = null;
  247. file = null;
  248. currentFolds = null;
  249. importsFold = null;
  250. initialCommentFold = null;
  251. }
  252. // public void settingsChange(SettingsChangeEvent evt) {
  253. // // Get folding presets
  254. // foldInitialCommentsPreset = getSetting(GsfOptions.CODE_FOLDING_COLLAPSE_INITIAL_COMMENT);
  255. // foldImportsPreset = getSetting(GsfOptions.CODE_FOLDING_COLLAPSE_IMPORT);
  256. // foldCodeBlocksPreset = getSetting(GsfOptions.CODE_FOLDING_COLLAPSE_METHOD);
  257. // foldInnerClassesPreset = getSetting(GsfOptions.CODE_FOLDING_COLLAPSE_INNERCLASS);
  258. // foldJavadocsPreset = getSetting(GsfOptions.CODE_FOLDING_COLLAPSE_JAVADOC);
  259. // }
  260. private static boolean getSetting(String settingName) {
  261. return prefs.getBoolean(settingName, false);
  262. }
  263. static final class JavaElementFoldTask extends IndexingAwareParserResultTask<ParserResult> {
  264. private final AtomicBoolean cancelled = new AtomicBoolean(false);
  265. public JavaElementFoldTask() {
  266. super(TaskIndexingMode.ALLOWED_DURING_SCAN);
  267. }
  268. //XXX: this will hold JavaElementFoldTask as long as the FileObject exists:
  269. private static Map<FileObject, JavaElementFoldTask> file2Task = new WeakHashMap<FileObject, JavaElementFoldTask>();
  270. static JavaElementFoldTask getTask(FileObject file) {
  271. JavaElementFoldTask task = file2Task.get(file);
  272. if (task == null) {
  273. file2Task.put(file, task = new JavaElementFoldTask());
  274. }
  275. return task;
  276. }
  277. private Collection<Reference<GsfFoldManager>> managers = new ArrayList<Reference<GsfFoldManager>>(2);
  278. synchronized void setGsfFoldManager(GsfFoldManager manager, FileObject file) {
  279. if (file == null) {
  280. for (Iterator<Reference<GsfFoldManager>> it = managers.iterator(); it.hasNext(); ) {
  281. Reference<GsfFoldManager> ref = it.next();
  282. GsfFoldManager fm = ref.get();
  283. if (fm == null || fm == manager) {
  284. it.remove();
  285. break;
  286. }
  287. }
  288. } else {
  289. managers.add(new WeakReference<GsfFoldManager>(manager));
  290. GsfFoldScheduler.reschedule();
  291. }
  292. }
  293. private synchronized Object findLiveManagers() {
  294. GsfFoldManager oneMgr = null;
  295. List<GsfFoldManager> result = null;
  296. for (Iterator<Reference<GsfFoldManager>> it = managers.iterator(); it.hasNext(); ) {
  297. Reference<GsfFoldManager> ref = it.next();
  298. GsfFoldManager fm = ref.get();
  299. if (fm == null) {
  300. it.remove();
  301. continue;
  302. }
  303. if (result != null) {
  304. result.add(fm);
  305. } else if (oneMgr != null) {
  306. result = new ArrayList<GsfFoldManager>(2);
  307. result.add(oneMgr);
  308. result.add(fm);
  309. } else {
  310. oneMgr = fm;
  311. }
  312. }
  313. return result != null ? result : oneMgr;
  314. }
  315. public void run(final ParserResult info, SchedulerEvent event) {
  316. cancelled.set(false);
  317. final Object mgrs = findLiveManagers();
  318. if (mgrs == null) {
  319. return;
  320. }
  321. long startTime = System.currentTimeMillis();
  322. // Don't update folds, if there is an invalid result
  323. // It should be solved per lenguages, but then there has to be remembered
  324. // lates folds and transformed to the new possition.
  325. if (hasErrors(info)) {
  326. return;
  327. }
  328. final TreeSet<FoldInfo> folds = new TreeSet<FoldInfo>();
  329. final Document doc = info.getSnapshot().getSource().getDocument(false);
  330. if (doc == null) {
  331. return;
  332. }
  333. boolean success = gsfFoldScan(doc, info, folds);
  334. if (!success || cancelled.get()) {
  335. return;
  336. }
  337. if (mgrs instanceof GsfFoldManager) {
  338. SwingUtilities.invokeLater(((GsfFoldManager)mgrs).new CommitFolds(folds, doc, info.getSnapshot().getSource()));
  339. } else {
  340. SwingUtilities.invokeLater(new Runnable() {
  341. Collection<GsfFoldManager> jefms = (Collection<GsfFoldManager>)mgrs;
  342. public void run() {
  343. for (GsfFoldManager jefm : jefms) {
  344. jefm.new CommitFolds(folds, doc, info.getSnapshot().getSource()).run();
  345. }
  346. }});
  347. }
  348. long endTime = System.currentTimeMillis();
  349. Logger.getLogger("TIMER").log(Level.FINE, "Folds - 1", //NOI18N
  350. new Object[] {info.getSnapshot().getSource().getFileObject(), endTime - startTime});
  351. }
  352. /**
  353. * Ask the language plugin to scan for folds.
  354. *
  355. * @return true If folds were found, false if cancelled
  356. */
  357. private boolean gsfFoldScan(final Document doc, ParserResult info, final TreeSet<FoldInfo> folds) {
  358. final boolean [] success = new boolean [] { false };
  359. Source source = info.getSnapshot().getSource();
  360. try {
  361. ParserManager.parse(Collections.singleton(source), new UserTask() {
  362. public @Override void run(ResultIterator resultIterator) throws Exception {
  363. String mimeType = resultIterator.getSnapshot().getMimeType();
  364. Language language = LanguageRegistry.getInstance().getLanguageByMimeType(mimeType);
  365. if (language == null) {
  366. return;
  367. }
  368. StructureScanner scanner = language.getStructure();
  369. if (scanner == null) {
  370. return;
  371. }
  372. Parser.Result r = resultIterator.getParserResult();
  373. if (!(r instanceof ParserResult)) {
  374. return;
  375. }
  376. scan((ParserResult) r, folds, doc, scanner);
  377. if (cancelled.get()) {
  378. return;
  379. }
  380. for(Embedding e : resultIterator.getEmbeddings()) {
  381. run(resultIterator.getResultIterator(e));
  382. if (cancelled.get()) {
  383. return;
  384. }
  385. }
  386. success[0] = true;
  387. }
  388. });
  389. } catch (ParseException e) {
  390. LOG.log(Level.WARNING, null, e);
  391. }
  392. if (success[0]) {
  393. //check for initial fold:
  394. success[0] = checkInitialFold(doc, folds);
  395. }
  396. return success[0];
  397. }
  398. private boolean checkInitialFold(final Document doc, final TreeSet<FoldInfo> folds) {
  399. final boolean[] ret = new boolean[1];
  400. ret[0] = true;
  401. final TokenHierarchy<?> th = TokenHierarchy.get(doc);
  402. if (th == null) {
  403. return false;
  404. }
  405. doc.render(new Runnable() {
  406. @Override
  407. public void run() {
  408. try {
  409. TokenSequence<?> ts = th.tokenSequence();
  410. if (ts == null) {
  411. return;
  412. }
  413. while (ts.moveNext()) {
  414. Token<?> token = ts.token();
  415. String category = token.id().primaryCategory();
  416. if ("comment".equals(category)) { // NOI18N
  417. int startOffset = ts.offset();
  418. int endOffset = startOffset + token.length();
  419. boolean collapsed = getSetting(CODE_FOLDING_COLLAPSE_INITIAL_COMMENT); //foldInitialCommentsPreset;
  420. // Find end - could be a block of single-line statements
  421. while (ts.moveNext()) {
  422. token = ts.token();
  423. category = token.id().primaryCategory();
  424. if ("comment".equals(category)) { // NOI18N
  425. endOffset = ts.offset() + token.length();
  426. } else if (!"whitespace".equals(category)) { // NOI18N
  427. break;
  428. }
  429. }
  430. try {
  431. // Start the fold at the END of the line
  432. startOffset = org.netbeans.editor.Utilities.getRowEnd((BaseDocument) doc, startOffset);
  433. if (startOffset >= endOffset) {
  434. return;
  435. }
  436. } catch (BadLocationException ex) {
  437. LOG.log(Level.WARNING, null, ex);
  438. }
  439. folds.add(new FoldInfo(doc, startOffset, endOffset, INITIAL_COMMENT_FOLD_TEMPLATE, collapsed));
  440. return;
  441. }
  442. if (!"whitespace".equals(category)) { // NOI18N
  443. break;
  444. }
  445. }
  446. } catch (BadLocationException e) {
  447. ret[0] = false;
  448. }
  449. }
  450. });
  451. return ret[0];
  452. }
  453. private void scan(final ParserResult info,
  454. final TreeSet<FoldInfo> folds, final Document doc, final
  455. StructureScanner scanner) {
  456. doc.render(new Runnable() {
  457. @Override
  458. public void run() {
  459. addTree(folds, info, doc, scanner);
  460. }
  461. });
  462. }
  463. private void addFoldsOfType(
  464. StructureScanner scanner,
  465. String type, Map<String,List<OffsetRange>> folds,
  466. TreeSet<FoldInfo> result,
  467. Document doc,
  468. String collapsedOptionName,
  469. FoldTemplate template) {
  470. List<OffsetRange> ranges = folds.get(type); //NOI18N
  471. if (ranges != null) {
  472. boolean collapseByDefault = getSetting(collapsedOptionName);
  473. if (LOG.isLoggable(Level.FINEST)) {
  474. LOG.log(Level.FINEST, "Creating folds {0}, collapsed: {1}", new Object[] {
  475. type, collapseByDefault
  476. });
  477. }
  478. for (OffsetRange range : ranges) {
  479. try {
  480. if (LOG.isLoggable(Level.FINEST)) {
  481. LOG.log(Level.FINEST, "Fold: {0}", range);
  482. }
  483. addFold(range, result, doc, collapseByDefault, template);
  484. } catch (BadLocationException ble) {
  485. LOG.log(Level.WARNING, "StructureScanner " + scanner + " supplied invalid fold " + range, ble); //NOI18N
  486. }
  487. }
  488. } else {
  489. LOG.log(Level.FINEST, "No folds of type {0}", type);
  490. }
  491. }
  492. private void addTree(TreeSet<FoldInfo> result, ParserResult info, Document doc, StructureScanner scanner) {
  493. // #217322, disabled folding -> no folds will be created
  494. if (!getSetting(CODE_FOLDING_ENABLE)) {
  495. return;
  496. }
  497. Map<String,List<OffsetRange>> folds = scanner.folds(info);
  498. if (cancelled.get()) {
  499. return;
  500. }
  501. addFoldsOfType(scanner, "codeblocks", folds, result, doc,
  502. CODE_FOLDING_COLLAPSE_METHOD, CODE_BLOCK_FOLD_TEMPLATE);
  503. addFoldsOfType(scanner, "comments", folds, result, doc,
  504. CODE_FOLDING_COLLAPSE_JAVADOC, JAVADOC_FOLD_TEMPLATE);
  505. addFoldsOfType(scanner, "initial-comment", folds, result, doc,
  506. CODE_FOLDING_COLLAPSE_INITIAL_COMMENT, INITIAL_COMMENT_FOLD_TEMPLATE);
  507. addFoldsOfType(scanner, "imports", folds, result, doc,
  508. CODE_FOLDING_COLLAPSE_IMPORT, IMPORTS_FOLD_TEMPLATE);
  509. addFoldsOfType(scanner, "tags", folds, result, doc,
  510. CODE_FOLDING_COLLAPSE_TAGS, TAG_FOLD_TEMPLATE);
  511. addFoldsOfType(scanner, "othercodeblocks", folds, result, doc,
  512. CODE_FOLDING_COLLAPSE_TAGS, CODE_BLOCK_FOLD_TEMPLATE);
  513. addFoldsOfType(scanner, "inner-classes", folds, result, doc,
  514. CODE_FOLDING_COLLAPSE_INNERCLASS, INNER_CLASS_FOLD_TEMPLATE);
  515. }
  516. private void addFold(OffsetRange range, TreeSet<FoldInfo> folds, Document doc, boolean collapseByDefault, FoldTemplate template) throws BadLocationException {
  517. if (range != OffsetRange.NONE) {
  518. int start = range.getStart();
  519. int end = range.getEnd();
  520. if (start != (-1) && end != (-1) && end <= doc.getLength()) {
  521. folds.add(new FoldInfo(doc, start, end, template, collapseByDefault));
  522. }
  523. }
  524. }
  525. @Override
  526. public int getPriority() {
  527. return Integer.MAX_VALUE;
  528. }
  529. @Override
  530. public Class<? extends Scheduler> getSchedulerClass() {
  531. return GsfFoldScheduler.class;
  532. }
  533. @Override
  534. public void cancel() {
  535. cancelled.set(true);
  536. }
  537. }
  538. private class CommitFolds implements Runnable {
  539. private Document scannedDocument;
  540. private Source scanSource;
  541. private boolean insideRender;
  542. private TreeSet<FoldInfo> infos;
  543. private long startTime;
  544. public CommitFolds(TreeSet<FoldInfo> infos, Document scanedDocument, Source s) {
  545. this.infos = infos;
  546. this.scannedDocument = scanedDocument;
  547. this.scanSource = s;
  548. }
  549. /**
  550. * For singular folds, if they exist in the FoldManager already
  551. * ignores the default state, and takes it from the actual state of
  552. * existing fold.
  553. */
  554. private boolean mergeSpecialFoldState(FoldInfo fi) {
  555. if (fi.template == IMPORTS_FOLD_TEMPLATE) {
  556. if (importsFold != null) {
  557. return importsFold.isCollapsed();
  558. }
  559. } else if (fi.template == INITIAL_COMMENT_FOLD_TEMPLATE) {
  560. if (initialCommentFold != null) {
  561. return initialCommentFold.isCollapsed();
  562. }
  563. }
  564. return fi.collapseByDefault;
  565. }
  566. public void run() {
  567. Document document = operation.getHierarchy().getComponent().getDocument();
  568. if (!insideRender) {
  569. startTime = System.currentTimeMillis();
  570. insideRender = true;
  571. document.render(this);
  572. return;
  573. }
  574. operation.getHierarchy().lock();
  575. if (operation.getHierarchy().getComponent().getDocument() != this.scannedDocument) {
  576. Throwable t = (Throwable)scannedDocument.getProperty("Issue-222763-debug");
  577. StringWriter sw = new StringWriter();
  578. PrintWriter pw = new PrintWriter(sw);
  579. if (t != null) {
  580. pw.print("Scanned document: ");
  581. t.printStackTrace(pw);
  582. }
  583. t = (Throwable)operation.getHierarchy().getComponent().getDocument().getProperty("Issue-222763-debug");
  584. if (t != null) {
  585. pw.print("Manager document: ");
  586. t.printStackTrace(pw);
  587. }
  588. // hopefully will be removed before release, see issue #223800
  589. pw.flush();
  590. LOG.warning("Fold manager works with different document than scanner. FmDoc: " + operation.getHierarchy().getComponent().getDocument() +
  591. ", ScanDoc: " + scannedDocument + ", source: " + scanSource);
  592. LOG.warning("Creation stacks: " + sw.toString());
  593. // prevent folding, bad offsets, see issue #223800
  594. return;
  595. }
  596. try {
  597. FoldHierarchyTransaction tr = operation.openTransaction();
  598. try {
  599. if (currentFolds == null) {
  600. return;
  601. }
  602. Map<FoldInfo, Fold> added = new TreeMap<FoldInfo, Fold>();
  603. // TODO - use map duplication here instead?
  604. TreeSet<FoldInfo> removed = new TreeSet<FoldInfo>(currentFolds.keySet());
  605. int documentLength = document.getLength();
  606. for (FoldInfo i : infos) {
  607. if (removed.remove(i)) {
  608. continue ;
  609. }
  610. int start = i.start.getOffset();
  611. int end = i.end.getOffset();
  612. if (end > documentLength) {
  613. continue;
  614. }
  615. if (end > start && (end - start) > (i.template.getStartGuardedLength() + i.template.getEndGuardedLength())) {
  616. Fold f = operation.addToHierarchy(i.template.getType(),
  617. i.template.getDescription(),
  618. mergeSpecialFoldState(i),
  619. start,
  620. end,
  621. i.template.getStartGuardedLength(),
  622. i.template.getEndGuardedLength(),
  623. i,
  624. tr);
  625. added.put(i, f);
  626. if (i.template == IMPORTS_FOLD_TEMPLATE) {
  627. importsFold = f;
  628. }
  629. if (i.template == INITIAL_COMMENT_FOLD_TEMPLATE) {
  630. initialCommentFold = f;
  631. }
  632. }
  633. }
  634. for (FoldInfo i : removed) {
  635. Fold f = currentFolds.remove(i);
  636. operation.removeFromHierarchy(f, tr);
  637. if (importsFold == f ) {
  638. importsFold = null;
  639. }
  640. if (initialCommentFold == f) {
  641. initialCommentFold = f;
  642. }
  643. }
  644. currentFolds.putAll(added);
  645. } catch (BadLocationException e) {
  646. LOG.log(Level.WARNING, null, e);
  647. } finally {
  648. tr.commit();
  649. }
  650. } finally {
  651. operation.getHierarchy().unlock();
  652. }
  653. long endTime = System.currentTimeMillis();
  654. Logger.getLogger("TIMER").log(Level.FINE, "Folds - 2",
  655. new Object[] {file, endTime - startTime});
  656. }
  657. }
  658. private Map<FoldInfo, Fold> currentFolds;
  659. private Fold initialCommentFold;
  660. private Fold importsFold;
  661. protected static final class FoldInfo implements Comparable {
  662. private Position start;
  663. private Position end;
  664. private FoldTemplate template;
  665. private boolean collapseByDefault;
  666. public FoldInfo(Document doc, int start, int end, FoldTemplate template, boolean collapseByDefault) throws BadLocationException {
  667. this.start = doc.createPosition(start);
  668. // see issue #216378; while Fold.end Position is manually updated by FoldHierarchyTransactionImpl, the
  669. // FoldInfos are left alone, and end marker must stick with the content, so characters typed after it does
  670. // not extend the FoldInfo
  671. this.end = NbDocument.createPosition(doc, end, Position.Bias.Backward);
  672. this.template = template;
  673. this.collapseByDefault = collapseByDefault;
  674. }
  675. @Override
  676. public int hashCode() {
  677. return 1;
  678. }
  679. @Override
  680. public boolean equals(Object o) {
  681. if (!(o instanceof FoldInfo)) {
  682. return false;
  683. }
  684. return compareTo(o) == 0;
  685. }
  686. public int compareTo(Object o) {
  687. FoldInfo remote = (FoldInfo) o;
  688. if (start.getOffset() < remote.start.getOffset()) {
  689. return -1;
  690. }
  691. if (start.getOffset() > remote.start.getOffset()) {
  692. return 1;
  693. }
  694. if (end.getOffset() < remote.end.getOffset()) {
  695. return -1;
  696. }
  697. if (end.getOffset() > remote.end.getOffset()) {
  698. return 1;
  699. }
  700. return 0;
  701. }
  702. }
  703. private static boolean hasErrors(ParserResult r) {
  704. for(org.netbeans.modules.csl.api.Error e : r.getDiagnostics()) {
  705. if (e.getSeverity() == Severity.FATAL) {
  706. return true;
  707. }
  708. }
  709. return false;
  710. }
  711. }