/** * Aptana Studio * Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the GNU Public License (GPL) v3 (with exceptions). * Please see the license.html included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.aptana.editor.haml.internal; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Stack; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Position; import org.eclipse.jface.text.source.projection.ProjectionAnnotation; import com.aptana.editor.common.AbstractThemeableEditor; import com.aptana.editor.common.text.reconciler.IFoldingComputer; import com.aptana.editor.common.text.reconciler.Messages; import com.aptana.parsing.ast.IParseRootNode; public class HAMLFoldingComputer implements IFoldingComputer { private IDocument fDocument; private AbstractThemeableEditor fEditor; public HAMLFoldingComputer(AbstractThemeableEditor editor, IDocument document) { this.fDocument = document; this.fEditor = editor; } /* * (non-Javadoc) * @see com.aptana.editor.common.text.reconciler.IFoldingComputer#emitFoldingRegions(org.eclipse.core.runtime. * IProgressMonitor) */ public Map<ProjectionAnnotation, Position> emitFoldingRegions(boolean initialReconcile, IProgressMonitor monitor, IParseRootNode ast) throws BadLocationException { int lineCount = fDocument.getNumberOfLines(); SubMonitor subMonitor = SubMonitor.convert(monitor, Messages.CommonReconcilingStrategy_FoldingTaskName, lineCount); if (lineCount <= 1) // Quick hack fix for minified files. We need at least two lines to have folding! { return Collections.emptyMap(); } // using shift operator to do a faster "divide by 4" Map<ProjectionAnnotation, Position> newPositions = new HashMap<ProjectionAnnotation, Position>(lineCount >> 2); Stack<Integer> indentLevels = new Stack<Integer>(); indentLevels.push(0); Map<Integer, Integer> starts = new HashMap<Integer, Integer>(3); for (int currentLine = 0; currentLine < lineCount; currentLine++) { // Check for cancellation if (subMonitor.isCanceled()) { return newPositions; } IRegion lineRegion = fDocument.getLineInformation(currentLine); int offset = lineRegion.getOffset(); String line = fDocument.get(offset, lineRegion.getLength()); if (line.trim().length() == 0) { // ignore blank lines? continue; } // Every new indent level is a possible folding start int indent = findIndent(line); if (!indentLevels.isEmpty()) { int peekedIndent = indentLevels.peek(); if (indent > peekedIndent) { // indent increased, might be a new folding start, add it indentLevels.push(indent); starts.put(indent, offset); } else if (indent == peekedIndent) { // same indent level, update folding offset for this indent level (multiple lines at same level) starts.put(indent, offset); } else if (indent < peekedIndent) { // indent level decreased, close all levels greater than current indent... while (!indentLevels.isEmpty() && indent <= indentLevels.peek()) { int toPop = indentLevels.pop(); if (!starts.containsKey(toPop)) { continue; } int startingOffset = starts.remove(toPop); IRegion startLine = fDocument.getLineInformationOfOffset(startingOffset); IRegion endLine = fDocument.getLineInformation(currentLine - 1); if (startLine.getOffset() == endLine.getOffset()) { continue; } int end = endLine.getOffset() + endLine.getLength() + 1; int posLength = end - startingOffset; if (posLength > 0) { Position position = new Position(startingOffset, posLength); newPositions.put(new ProjectionAnnotation(), position); } } starts.put(indent, offset); } } subMonitor.worked(1); } // Close all open starts at the end of the document! int documentEnd = fDocument.getLength(); IRegion endLine = fDocument.getLineInformationOfOffset(documentEnd); for (Integer startingOffset : starts.values()) { IRegion startLine = fDocument.getLineInformationOfOffset(startingOffset); if (startLine.getOffset() == endLine.getOffset()) { continue; } int posLength = documentEnd - startingOffset; if (posLength > 0) { Position position = new Position(startingOffset, posLength); newPositions.put(new ProjectionAnnotation(), position); } } subMonitor.done(); return newPositions; } private int findIndent(String text) { int indent = 0; while (indent < text.length()) { char c = text.charAt(indent); if (c == '\t') { indent += getTabSize(); continue; } if (!Character.isWhitespace(c)) break; indent++; } return indent; } protected int getTabSize() { return fEditor.getTabSize(); } }