/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
*
* Oracle and Java are registered trademarks of Oracle and/or its affiliates.
* Other names may be trademarks of their respective owners.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common
* Development and Distribution License("CDDL") (collectively, the
* "License"). You may not use this file except in compliance with the
* License. You can obtain a copy of the License at
* http://www.netbeans.org/cddl-gplv2.html
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
* specific language governing permissions and limitations under the
* License. When distributing the software, include this License Header
* Notice in each file and include the License file at
* nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the
* License Header, with the fields enclosed by brackets [] replaced by
* your own identifying information:
* "Portions Copyrighted [year] [name of copyright owner]"
*
* Contributor(s):
*
* Portions Copyrighted 2007 Sun Microsystems, Inc.
*/
package org.netbeans.modules.ruby.rhtml;
import java.util.ArrayList;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import org.netbeans.api.lexer.Token;
import org.netbeans.api.lexer.TokenHierarchy;
import org.netbeans.api.lexer.TokenId;
import org.netbeans.api.lexer.TokenSequence;
import org.netbeans.editor.BaseDocument;
import org.netbeans.editor.Utilities;
import org.netbeans.modules.csl.spi.GsfUtilities;
import org.netbeans.modules.editor.indent.api.IndentUtils;
import org.netbeans.modules.editor.indent.spi.Context;
import org.netbeans.modules.editor.indent.spi.ExtraLock;
import org.netbeans.modules.editor.indent.spi.IndentTask;
import org.netbeans.modules.ruby.RubyFormatter;
import org.netbeans.modules.ruby.rhtml.lexer.api.RhtmlTokenId;
/**
* Indent task for RHTML.
* The important work is realy done by the HTML and Ruby indenters, but
* the HTML indenter no longer indents non-tag lines, so the RHTML indenter
* (which will be run first, given that it's the outermost language)
* goes and moves everything to column 0 first, such that the HTML indenter
* can adjust it afterwards.
*
* @author Tor Norbye
*/
public class RhtmlIndentTask implements IndentTask {
private Context context;
RhtmlIndentTask(Context context) {
this.context = context;
}
public void reindent() throws BadLocationException {
BaseDocument doc = (BaseDocument) context.document();
int start = context.startOffset();
int end = Math.min(context.endOffset(), doc.getLength());
//doc.putProperty(HTMLLexerFormatter.HTML_FORMATTER_ACTS_ON_TOP_LEVEL, Boolean.TRUE);
doc.putProperty("HTML_FORMATTER_ACTS_ON_TOP_LEVEL", Boolean.TRUE);
TokenHierarchy<Document> th = TokenHierarchy.get((Document)doc);
TokenSequence<?extends RhtmlTokenId> ts = th.tokenSequence(RhtmlTokenId.language());
if (ts == null) {
return;
}
int offset = Utilities.getRowStart(doc, end);
List<Integer> offsets = new ArrayList<Integer>();
boolean prevWasNonHtml = false;
while (offset >= start) {
int lineStart = Utilities.getRowFirstNonWhite(doc, offset);
if (lineStart != -1) {
prevWasNonHtml = false;
ts.move(lineStart);
if (ts.moveNext()) {
TokenId id = ts.token().id();
if (id != RhtmlTokenId.HTML) {
prevWasNonHtml = true;
offsets.add(offset);
}
}
} else if (prevWasNonHtml) {
// Include blank lines leading up to a non-html block since HTML
// will treat these as part of the block to be indented
offsets.add(offset);
}
if (offset > 0) {
// XXX >= ? What about empty first line?
offset--;
offset = Utilities.getRowStart(doc, offset);
} else {
break;
}
}
// Process offsets to be reformatted
if (offsets.size() > 0) {
for (Integer lineOffset : offsets) {
assert lineOffset == Utilities.getRowStart(doc, lineOffset);
context.modifyIndent(lineOffset, 0);
}
}
if (context.isIndent() && start > 0) { // inserting a newline
int rowEnd = Utilities.getRowLastNonWhite(doc, start-1);
if (rowEnd != -1) {
String s = doc.getText(start, end-start);
if (s.indexOf('\n') != -1) {
// We're not just splitting a line
return;
}
int delta = ts.move(rowEnd+1); // +1: getRowLastNonWhite returns START of last char
if (delta > 0) {
if (!ts.moveNext()) {
return;
}
} else {
if (!ts.movePrevious()) {
return;
}
}
Token<? extends RhtmlTokenId> token = ts.token();
if (token.id() == RhtmlTokenId.DELIMITER) {
int rowStart = Utilities.getRowFirstNonWhite(doc, rowEnd);
int balance = RubyFormatter.getTokenBalance(doc, rowStart, rowEnd+1, true, true);
int indent = GsfUtilities.getLineIndent(doc, start-1);
if (balance > 0) {
indent += IndentUtils.indentLevelSize(doc);
}
context.modifyIndent(Utilities.getRowStart(doc, start), indent);
}
}
}
}
public ExtraLock indentLock() {
return null;
}
}