initial indentation support, format action into editor context menu
authorMartin Fousek <marfous@netbeans.org>
Sun, 27 Mar 2011 16:09:57 +0200
changeset 17339eb8bbca948b9
parent 17309 60f4cc6038b3
child 17340 07429a4ce259
initial indentation support, format action into editor context menu
php.smarty/manifest.mf
php.smarty/nbproject/project.xml
php.smarty/src/org/netbeans/modules/php/smarty/editor/indent/TplIndentTask.java
php.smarty/src/org/netbeans/modules/php/smarty/editor/indent/TplIndentTaskFactory.java
php.smarty/src/org/netbeans/modules/php/smarty/editor/indent/TplIndenter.java
php.smarty/src/org/netbeans/modules/php/smarty/resources/layer.xml
     1.1 --- a/php.smarty/manifest.mf	Thu Mar 03 00:03:37 2011 +0100
     1.2 +++ b/php.smarty/manifest.mf	Sun Mar 27 16:09:57 2011 +0200
     1.3 @@ -2,5 +2,5 @@
     1.4  OpenIDE-Module: org.netbeans.modules.php.smarty
     1.5  OpenIDE-Module-Layer: org/netbeans/modules/php/smarty/resources/layer.xml
     1.6  OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/php/smarty/resources/Bundle.properties
     1.7 -OpenIDE-Module-Specification-Version: 1.39
     1.8 +OpenIDE-Module-Specification-Version: 1.50
     1.9  
     2.1 --- a/php.smarty/nbproject/project.xml	Thu Mar 03 00:03:37 2011 +0100
     2.2 +++ b/php.smarty/nbproject/project.xml	Sun Mar 27 16:09:57 2011 +0200
     2.3 @@ -15,6 +15,15 @@
     2.4                      </run-dependency>
     2.5                  </dependency>
     2.6                  <dependency>
     2.7 +                    <code-name-base>org.netbeans.modules.css.editor</code-name-base>
     2.8 +                    <build-prerequisite/>
     2.9 +                    <compile-dependency/>
    2.10 +                    <run-dependency>
    2.11 +                        <release-version>1</release-version>
    2.12 +                        <implementation-version/>
    2.13 +                    </run-dependency>
    2.14 +                </dependency>
    2.15 +                <dependency>
    2.16                      <code-name-base>org.netbeans.modules.editor</code-name-base>
    2.17                      <build-prerequisite/>
    2.18                      <compile-dependency/>
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/php.smarty/src/org/netbeans/modules/php/smarty/editor/indent/TplIndentTask.java	Sun Mar 27 16:09:57 2011 +0200
     3.3 @@ -0,0 +1,65 @@
     3.4 +/*
     3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3.6 + *
     3.7 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
     3.8 + *
     3.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    3.10 + * Other names may be trademarks of their respective owners.
    3.11 + *
    3.12 + * The contents of this file are subject to the terms of either the GNU
    3.13 + * General Public License Version 2 only ("GPL") or the Common
    3.14 + * Development and Distribution License("CDDL") (collectively, the
    3.15 + * "License"). You may not use this file except in compliance with the
    3.16 + * License. You can obtain a copy of the License at
    3.17 + * http://www.netbeans.org/cddl-gplv2.html
    3.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    3.19 + * specific language governing permissions and limitations under the
    3.20 + * License.  When distributing the software, include this License Header
    3.21 + * Notice in each file and include the License file at
    3.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    3.23 + * particular file as subject to the "Classpath" exception as provided
    3.24 + * by Oracle in the GPL Version 2 section of the License file that
    3.25 + * accompanied this code. If applicable, add the following below the
    3.26 + * License Header, with the fields enclosed by brackets [] replaced by
    3.27 + * your own identifying information:
    3.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    3.29 + *
    3.30 + * Contributor(s):
    3.31 + *
    3.32 + * Portions Copyrighted 2007 Sun Microsystems, Inc.
    3.33 + */
    3.34 +package org.netbeans.modules.php.smarty.editor.indent;
    3.35 +
    3.36 +import javax.swing.text.BadLocationException;
    3.37 +import org.netbeans.modules.editor.indent.spi.Context;
    3.38 +import org.netbeans.modules.editor.indent.spi.ExtraLock;
    3.39 +import org.netbeans.modules.editor.indent.spi.IndentTask;
    3.40 +import org.openide.util.Lookup;
    3.41 +import org.openide.util.lookup.Lookups;
    3.42 +
    3.43 +public class TplIndentTask implements IndentTask, Lookup.Provider {
    3.44 +
    3.45 +    private TplIndenter indenter;
    3.46 +    private Lookup lookup;
    3.47 +    
    3.48 +    TplIndentTask(Context context) {
    3.49 +        indenter = new TplIndenter(context);
    3.50 +        lookup = Lookups.singleton(indenter.createFormattingContext());
    3.51 +    }
    3.52 +
    3.53 +    @Override
    3.54 +    public void reindent() throws BadLocationException {
    3.55 +        indenter.reindent();
    3.56 +    }
    3.57 +    
    3.58 +    @Override
    3.59 +    public ExtraLock indentLock() {
    3.60 +        return null;
    3.61 +    }
    3.62 +
    3.63 +    @Override
    3.64 +    public Lookup getLookup() {
    3.65 +        return lookup;
    3.66 +    }
    3.67 +
    3.68 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/php.smarty/src/org/netbeans/modules/php/smarty/editor/indent/TplIndentTaskFactory.java	Sun Mar 27 16:09:57 2011 +0200
     4.3 @@ -0,0 +1,44 @@
     4.4 +
     4.5 +/*
     4.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     4.7 + *
     4.8 + * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
     4.9 + *
    4.10 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    4.11 + * Other names may be trademarks of their respective owners.
    4.12 + *
    4.13 + * The contents of this file are subject to the terms of either the GNU
    4.14 + * General Public License Version 2 only ("GPL") or the Common
    4.15 + * Development and Distribution License("CDDL") (collectively, the
    4.16 + * "License"). You may not use this file except in compliance with the
    4.17 + * License. You can obtain a copy of the License at
    4.18 + * http://www.netbeans.org/cddl-gplv2.html
    4.19 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    4.20 + * specific language governing permissions and limitations under the
    4.21 + * License.  When distributing the software, include this License Header
    4.22 + * Notice in each file and include the License file at
    4.23 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    4.24 + * particular file as subject to the "Classpath" exception as provided
    4.25 + * by Oracle in the GPL Version 2 section of the License file that
    4.26 + * accompanied this code. If applicable, add the following below the
    4.27 + * License Header, with the fields enclosed by brackets [] replaced by
    4.28 + * your own identifying information:
    4.29 + * "Portions Copyrighted [year] [name of copyright owner]"
    4.30 + *
    4.31 + * Contributor(s):
    4.32 + *
    4.33 + * Portions Copyrighted 2007 Sun Microsystems, Inc.
    4.34 + */
    4.35 +package org.netbeans.modules.php.smarty.editor.indent;
    4.36 +
    4.37 +import org.netbeans.modules.editor.indent.spi.Context;
    4.38 +import org.netbeans.modules.editor.indent.spi.IndentTask;
    4.39 +
    4.40 +public class TplIndentTaskFactory implements IndentTask.Factory {
    4.41 +
    4.42 +    @Override
    4.43 +    public IndentTask createTask(Context context) {
    4.44 +        return new TplIndentTask(context);
    4.45 +    }
    4.46 +    
    4.47 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/php.smarty/src/org/netbeans/modules/php/smarty/editor/indent/TplIndenter.java	Sun Mar 27 16:09:57 2011 +0200
     5.3 @@ -0,0 +1,379 @@
     5.4 +/*
     5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     5.6 + *
     5.7 + * Copyright 2010 Oracle and/or its affiliates. All rights reserved.
     5.8 + *
     5.9 + * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
    5.10 + * Other names may be trademarks of their respective owners.
    5.11 + *
    5.12 + * The contents of this file are subject to the terms of either the GNU
    5.13 + * General Public License Version 2 only ("GPL") or the Common
    5.14 + * Development and Distribution License("CDDL") (collectively, the
    5.15 + * "License"). You may not use this file except in compliance with the
    5.16 + * License. You can obtain a copy of the License at
    5.17 + * http://www.netbeans.org/cddl-gplv2.html
    5.18 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    5.19 + * specific language governing permissions and limitations under the
    5.20 + * License.  When distributing the software, include this License Header
    5.21 + * Notice in each file and include the License file at
    5.22 + * nbbuild/licenses/CDDL-GPL-2-CP.  Oracle designates this
    5.23 + * particular file as subject to the "Classpath" exception as provided
    5.24 + * by Oracle in the GPL Version 2 section of the License file that
    5.25 + * accompanied this code. If applicable, add the following below the
    5.26 + * License Header, with the fields enclosed by brackets [] replaced by
    5.27 + * your own identifying information:
    5.28 + * "Portions Copyrighted [year] [name of copyright owner]"
    5.29 + *
    5.30 + * If you wish your version of this file to be governed by only the CDDL
    5.31 + * or only the GPL Version 2, indicate your decision by adding
    5.32 + * "[Contributor] elects to include this software in this distribution
    5.33 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
    5.34 + * single choice of license, a recipient has the option to distribute
    5.35 + * your version of this file under either the CDDL, the GPL Version 2 or
    5.36 + * to extend the choice of license to its licensees as provided above.
    5.37 + * However, if you add GPL Version 2 code and therefore, elected the GPL
    5.38 + * Version 2 license, then the option applies only if the new code is
    5.39 + * made subject to such option by the copyright holder.
    5.40 + *
    5.41 + * Contributor(s):
    5.42 + *
    5.43 + * Portions Copyrighted 2008 Sun Microsystems, Inc.
    5.44 + */
    5.45 +package org.netbeans.modules.php.smarty.editor.indent;
    5.46 +
    5.47 +import java.util.ArrayList;
    5.48 +import java.util.Arrays;
    5.49 +import java.util.HashMap;
    5.50 +import java.util.List;
    5.51 +import java.util.Stack;
    5.52 +import javax.swing.text.BadLocationException;
    5.53 +import org.netbeans.api.lexer.Token;
    5.54 +import org.netbeans.api.lexer.TokenHierarchy;
    5.55 +import org.netbeans.api.lexer.TokenId;
    5.56 +import org.netbeans.api.lexer.TokenSequence;
    5.57 +import org.netbeans.editor.Utilities;
    5.58 +import org.netbeans.modules.css.formatting.api.support.AbstractIndenter;
    5.59 +import org.netbeans.modules.css.formatting.api.support.IndenterContextData;
    5.60 +import org.netbeans.modules.css.formatting.api.support.IndentCommand;
    5.61 +import org.netbeans.modules.css.formatting.api.embedding.JoinedTokenSequence;
    5.62 +import org.netbeans.modules.css.formatting.api.LexUtilities;
    5.63 +import org.netbeans.modules.editor.indent.spi.Context;
    5.64 +import org.netbeans.modules.php.smarty.editor.lexer.TplTokenId;
    5.65 +import org.netbeans.modules.php.smarty.editor.lexer.TplTopTokenId;
    5.66 +
    5.67 +/**
    5.68 + * @author Martin Fousek
    5.69 + **/
    5.70 +public class TplIndenter extends AbstractIndenter<TplTopTokenId> {
    5.71 +
    5.72 +    private Stack<TplStackItem> stack = null;
    5.73 +    private int preservedLineIndentation = -1;
    5.74 +    private static final List<String> bodyCommands = new ArrayList<String>(Arrays.asList("capture", "foreach", "foreachelse", "if", "elseif", "else", //NOI18N
    5.75 +            "literal", "php", "section", "sectionelse", "strip"));      //NOI18N
    5.76 +    private static final List<String> elseCommands = new ArrayList<String>(Arrays.asList("foreachelse", "elseif", "else", "sectionelse"));  //NOI18N
    5.77 +    private static final HashMap<String, ArrayList<String>> relatedCommands = new HashMap<String, ArrayList<String>>() {
    5.78 +
    5.79 +        {
    5.80 +            put("if", new ArrayList(Arrays.asList("else", "elseif")));  //NOI18N
    5.81 +            put("foreach", new ArrayList(Arrays.asList("foreachelsef")));  //NOI18N
    5.82 +            put("section", new ArrayList(Arrays.asList("sectionelse")));  //NOI18N
    5.83 +        }
    5.84 +    };
    5.85 +
    5.86 +    public TplIndenter(Context context) {
    5.87 +        super(TplTopTokenId.language(), context);
    5.88 +    }
    5.89 +
    5.90 +    private Stack<TplStackItem> getStack() {
    5.91 +        return stack;
    5.92 +    }
    5.93 +
    5.94 +    private String getFunctionalTplTokenId(Token<TplTopTokenId> token) {
    5.95 +        TokenHierarchy<CharSequence> th = TokenHierarchy.create(token.text(), TplTokenId.language());
    5.96 +        TokenSequence<TplTokenId> sequence = th.tokenSequence(TplTokenId.language());
    5.97 +        while (sequence.moveNext()) {
    5.98 +            if (sequence.token().id() == TplTokenId.WHITESPACE) {
    5.99 +                continue;
   5.100 +            } else {
   5.101 +                return sequence.token().toString();
   5.102 +            }
   5.103 +        }
   5.104 +        return "";
   5.105 +    }
   5.106 +
   5.107 +    @Override
   5.108 +    protected boolean isWhiteSpaceToken(Token<TplTopTokenId> token) {
   5.109 +        return getFunctionalTplTokenId(token).equals("");
   5.110 +    }
   5.111 +
   5.112 +    private boolean isCommentToken(Token<TplTopTokenId> token) {
   5.113 +        return token.id() == TplTopTokenId.T_COMMENT;
   5.114 +    }
   5.115 +
   5.116 +    @Override
   5.117 +    protected void reset() {
   5.118 +        stack = new Stack<TplStackItem>();
   5.119 +    }
   5.120 +
   5.121 +    @Override
   5.122 +    protected int getFormatStableStart(JoinedTokenSequence<TplTopTokenId> ts, int startOffset, int endOffset,
   5.123 +            AbstractIndenter.OffsetRanges rangesToIgnore) {
   5.124 +        // start from the end offset to properly calculate braces balance and
   5.125 +        // find correfct formatting start (consider case of a rule defined
   5.126 +        // within a media rule):
   5.127 +        ts.move(endOffset, false);
   5.128 +
   5.129 +        if (!ts.moveNext() && !ts.movePrevious()) {
   5.130 +            return LexUtilities.getTokenSequenceStartOffset(ts);
   5.131 +        }
   5.132 +
   5.133 +        int balance = 0;
   5.134 +        // Look backwards to find a suitable context - beginning of a rule
   5.135 +        do {
   5.136 +            Token<TplTopTokenId> token = ts.token();
   5.137 +            TokenId id = token.id();
   5.138 +
   5.139 +            if (id == TplTopTokenId.T_SMARTY && ts.offset() < startOffset && balance == 0) {
   5.140 +                int index = ts.index();
   5.141 +                ts.moveNext();
   5.142 +                Token tk = LexUtilities.findNext(ts, Arrays.asList(TplTopTokenId.T_SMARTY));
   5.143 +                ts.moveIndex(index);
   5.144 +                ts.moveNext();
   5.145 +                if (tk != null && tk.id() == TplTopTokenId.T_SMARTY_OPEN_DELIMITER) {
   5.146 +                    if (ts.movePrevious()) {
   5.147 +                        tk = LexUtilities.findPrevious(ts, Arrays.asList(TplTopTokenId.T_SMARTY));
   5.148 +                        if (tk != null) {
   5.149 +                            ts.moveNext();
   5.150 +                            tk = LexUtilities.findNext(ts, Arrays.asList(TplTopTokenId.T_SMARTY));
   5.151 +                        }
   5.152 +                    }
   5.153 +                    return ts.offset();
   5.154 +                }
   5.155 +            } else if (id == TplTopTokenId.T_SMARTY_CLOSE_DELIMITER) {
   5.156 +                balance++;
   5.157 +            } else if (id == TplTopTokenId.T_SMARTY_OPEN_DELIMITER) {
   5.158 +                balance--;
   5.159 +            } else if (id == TplTopTokenId.T_SMARTY && ts.offset() < startOffset && balance == 0) {
   5.160 +                return ts.offset();
   5.161 +            }
   5.162 +        } while (ts.movePrevious());
   5.163 +
   5.164 +        return LexUtilities.getTokenSequenceStartOffset(ts);
   5.165 +    }
   5.166 +
   5.167 +    private void getIndentFromState(List<IndentCommand> iis, boolean updateState, int lineStartOffset) {
   5.168 +        Stack<TplStackItem> blockStack = getStack();
   5.169 +
   5.170 +        int lastUnprocessedItem = blockStack.size();
   5.171 +        for (int i = blockStack.size() - 1; i >= 0; i--) {
   5.172 +            if (!blockStack.get(i).processed) {
   5.173 +                lastUnprocessedItem = i;
   5.174 +            } else {
   5.175 +                break;
   5.176 +            }
   5.177 +        }
   5.178 +        for (int i = lastUnprocessedItem; i < blockStack.size(); i++) {
   5.179 +            TplStackItem item = blockStack.get(i);
   5.180 +            assert !item.processed : item;
   5.181 +            if (item.state == StackItemState.IN_BODY) {
   5.182 +                IndentCommand ii = new IndentCommand(IndentCommand.Type.INDENT, lineStartOffset);
   5.183 +                if (item.indent != -1) {
   5.184 +                    ii.setFixedIndentSize(item.indent);
   5.185 +                }
   5.186 +                iis.add(ii);
   5.187 +                if (updateState) {
   5.188 +                    item.processed = Boolean.TRUE;
   5.189 +                }
   5.190 +            } else if (item.state == StackItemState.BODY_FINISHED) {
   5.191 +                IndentCommand ii = new IndentCommand(IndentCommand.Type.RETURN, lineStartOffset);
   5.192 +                iis.add(ii);
   5.193 +                if (updateState) {
   5.194 +                    item.processed = Boolean.TRUE;
   5.195 +                    blockStack.remove(i);
   5.196 +                    i--;
   5.197 +                }
   5.198 +            }
   5.199 +        }
   5.200 +    }
   5.201 +
   5.202 +    @Override
   5.203 +    protected List<IndentCommand> getLineIndent(IndenterContextData<TplTopTokenId> context,
   5.204 +            List<IndentCommand> preliminaryNextLineIndent) throws BadLocationException {
   5.205 +        Stack<TplStackItem> blockStack = getStack();
   5.206 +        List<IndentCommand> iis = new ArrayList<IndentCommand>();
   5.207 +        getIndentFromState(iis, true, context.getLineStartOffset());
   5.208 +
   5.209 +        JoinedTokenSequence<TplTopTokenId> ts = context.getJoinedTokenSequences();
   5.210 +        ts.move(context.getLineStartOffset());
   5.211 +
   5.212 +        boolean isSmartyBodyCommand = false;
   5.213 +        boolean isSmartyElseCommand = false;
   5.214 +        boolean isActionReturnUsed = false;
   5.215 +        String lastTplCommand = "";
   5.216 +        // iterate over tokens on the line and push to stack any changes
   5.217 +        while (!context.isBlankLine() && ts.moveNext()
   5.218 +                && ((ts.isCurrentTokenSequenceVirtual() && ts.offset() < context.getLineEndOffset())
   5.219 +                || ts.offset() <= context.getLineEndOffset())) {
   5.220 +            Token<TplTopTokenId> token = ts.token();
   5.221 +            if (token == null) {
   5.222 +                continue;
   5.223 +            } else if (ts.embedded() != null) {
   5.224 +                if (token.id() == TplTopTokenId.T_SMARTY && context.isIndentThisLine()) {
   5.225 +                    String tplToken = getFunctionalTplTokenId(token);
   5.226 +                    isSmartyBodyCommand = isBodyCommand(tplToken, context);
   5.227 +                    if (isSmartyBodyCommand) {
   5.228 +                        lastTplCommand = tplToken;
   5.229 +                        isSmartyElseCommand = isElseCommand(tplToken);
   5.230 +                    }
   5.231 +                } else {
   5.232 +                    isSmartyBodyCommand = false;
   5.233 +                    isSmartyElseCommand = false;
   5.234 +                }
   5.235 +                continue;
   5.236 +            }
   5.237 +
   5.238 +            if (token.id() == TplTopTokenId.T_SMARTY_OPEN_DELIMITER) {
   5.239 +                TplStackItem state = new TplStackItem(StackItemState.IN_RULE);
   5.240 +                blockStack.push(state);
   5.241 +            } else if (token.id() == TplTopTokenId.T_SMARTY_CLOSE_DELIMITER) {
   5.242 +                if (isInState(blockStack, StackItemState.IN_RULE)) {
   5.243 +                    // check that IN_RULE is the last state
   5.244 +                    TplStackItem item = blockStack.pop();
   5.245 +                    assert item.state == StackItemState.IN_RULE;
   5.246 +                    if (isSmartyBodyCommand) {
   5.247 +                        if (!blockStack.isEmpty() && isInRelatedCommand(lastTplCommand, blockStack.peek().getCommand())) {
   5.248 +                            if (isSmartyElseCommand) {
   5.249 +                                String command = blockStack.pop().command;
   5.250 +                                blockStack.push(new TplStackItem(StackItemState.IN_BODY, command));
   5.251 +                            } else {
   5.252 +                                blockStack.pop();
   5.253 +                            }
   5.254 +                            iis.add(new IndentCommand(IndentCommand.Type.RETURN, preservedLineIndentation));
   5.255 +                        } else {
   5.256 +                            blockStack.push(new TplStackItem(StackItemState.IN_BODY, lastTplCommand));
   5.257 +                        }
   5.258 +                    }
   5.259 +                }
   5.260 +            } else if (isCommentToken(token)) {
   5.261 +                int start = context.getLineStartOffset();
   5.262 +                if (start < ts.offset()) {
   5.263 +                    start = ts.offset();
   5.264 +                }
   5.265 +                int commentEndOffset = ts.offset() + ts.token().text().toString().trim().length() - 1;
   5.266 +                int end = context.getLineEndOffset();
   5.267 +                if (end > commentEndOffset) {
   5.268 +                    end = commentEndOffset;
   5.269 +                }
   5.270 +                if (start > end) {
   5.271 +                    // do nothing
   5.272 +                } else if (start == ts.offset()) {
   5.273 +                    if (end < commentEndOffset) {
   5.274 +                        // if comment ends on next line put formatter to IN_COMMENT state
   5.275 +                        int lineStart = Utilities.getRowStart(getDocument(), ts.offset());
   5.276 +                        preservedLineIndentation = start - lineStart;
   5.277 +                    }
   5.278 +                } else if (end == commentEndOffset) {
   5.279 +                    String text = getDocument().getText(start, end - start + 1).trim();
   5.280 +                    if (!text.startsWith("*/")) {
   5.281 +                        // if line does not start with '*/' then treat it as unformattable
   5.282 +                        IndentCommand ic = new IndentCommand(IndentCommand.Type.PRESERVE_INDENTATION, context.getLineStartOffset());
   5.283 +                        ic.setFixedIndentSize(preservedLineIndentation);
   5.284 +                        iis.add(ic);
   5.285 +                    }
   5.286 +                    preservedLineIndentation = -1;
   5.287 +                } else {
   5.288 +                    IndentCommand ic = new IndentCommand(IndentCommand.Type.PRESERVE_INDENTATION, context.getLineStartOffset());
   5.289 +                    ic.setFixedIndentSize(preservedLineIndentation);
   5.290 +                    iis.add(ic);
   5.291 +                }
   5.292 +            }
   5.293 +        }
   5.294 +        if (context.isBlankLine() && iis.isEmpty()) {
   5.295 +            IndentCommand ic = new IndentCommand(IndentCommand.Type.PRESERVE_INDENTATION, context.getLineStartOffset());
   5.296 +            ic.setFixedIndentSize(preservedLineIndentation);
   5.297 +            iis.add(ic);
   5.298 +        }
   5.299 +
   5.300 +        if (iis.isEmpty()) {
   5.301 +            iis.add(new IndentCommand(IndentCommand.Type.NO_CHANGE, context.getLineStartOffset()));
   5.302 +        }
   5.303 +
   5.304 +        if (context.getNextLineStartOffset() != -1) {
   5.305 +            getIndentFromState(preliminaryNextLineIndent, false, context.getNextLineStartOffset());
   5.306 +            if (isActionReturnUsed) {
   5.307 +            }
   5.308 +            if (preliminaryNextLineIndent.isEmpty()) {
   5.309 +                preliminaryNextLineIndent.add(new IndentCommand(IndentCommand.Type.NO_CHANGE, context.getNextLineStartOffset()));
   5.310 +            }
   5.311 +        }
   5.312 +
   5.313 +        return iis;
   5.314 +    }
   5.315 +
   5.316 +    private boolean isInState(Stack<TplStackItem> stack, StackItemState state) {
   5.317 +        for (TplStackItem item : stack) {
   5.318 +            if (item.state == state) {
   5.319 +                return true;
   5.320 +            }
   5.321 +        }
   5.322 +        return false;
   5.323 +    }
   5.324 +
   5.325 +    private boolean isBodyCommand(String tplToken, IndenterContextData<TplTopTokenId> context) {
   5.326 +        String tokenText = tplToken.toLowerCase();
   5.327 +        return bodyCommands.contains(tokenText) || bodyCommands.contains(tokenText.substring(1));
   5.328 +    }
   5.329 +
   5.330 +    private boolean isElseCommand(String tplToken) {
   5.331 +        String tokenText = tplToken.toLowerCase();
   5.332 +        return elseCommands.contains(tokenText) || elseCommands.contains(tokenText.substring(1));
   5.333 +    }
   5.334 +
   5.335 +    private boolean isInRelatedCommand(String actualString, String comparingString) {
   5.336 +        if (comparingString != null && actualString != null) {
   5.337 +            return actualString.substring(1).equals(comparingString)
   5.338 +                    || (relatedCommands.get(comparingString) != null
   5.339 +                    && relatedCommands.get(comparingString).contains(actualString));
   5.340 +        } else {
   5.341 +            return false;
   5.342 +        }
   5.343 +    }
   5.344 +
   5.345 +    private static enum StackItemState {
   5.346 +
   5.347 +        IN_BODY,
   5.348 +        IN_RULE,
   5.349 +        IN_VALUE,
   5.350 +        RULE_FINISHED,
   5.351 +        BODY_FINISHED;
   5.352 +    }
   5.353 +
   5.354 +    private static class TplStackItem {
   5.355 +
   5.356 +        private StackItemState state;
   5.357 +        private Boolean processed = false;
   5.358 +        private String command;
   5.359 +        private int indent;
   5.360 +
   5.361 +        private TplStackItem(StackItemState state, String command) {
   5.362 +            assert state != StackItemState.IN_BODY || (state == StackItemState.IN_BODY && !command.isEmpty());
   5.363 +            this.command = command;
   5.364 +            this.state = state;
   5.365 +            this.indent = -1;
   5.366 +        }
   5.367 +
   5.368 +        private TplStackItem(StackItemState state) {
   5.369 +            this.state = state;
   5.370 +            this.indent = -1;
   5.371 +        }
   5.372 +
   5.373 +        public String getCommand() {
   5.374 +            return command;
   5.375 +        }
   5.376 +
   5.377 +        @Override
   5.378 +        public String toString() {
   5.379 +            return "TplStackItem[state=" + state + ",indent=" + indent + ",processed=" + processed + ",command=" + command + "]";
   5.380 +        }
   5.381 +    }
   5.382 +}
     6.1 --- a/php.smarty/src/org/netbeans/modules/php/smarty/resources/layer.xml	Thu Mar 03 00:03:37 2011 +0100
     6.2 +++ b/php.smarty/src/org/netbeans/modules/php/smarty/resources/layer.xml	Sun Mar 27 16:09:57 2011 +0200
     6.3 @@ -55,6 +55,10 @@
     6.4                      <attr name="instanceCreate" methodvalue="org.netbeans.modules.csl.core.GsfParserFactory.create"/>
     6.5                  </file>
     6.6  
     6.7 +                <file name="org-netbeans-modules-php-smarty-editor-indent-TplIndentTaskFactory.instance">
     6.8 +                    <attr name="instanceOf" stringvalue="org.netbeans.modules.editor.indent.spi.IndentTask$Factory"/>
     6.9 +                </file>
    6.10 +
    6.11                  <folder name="SideBar">
    6.12                      <file name="org-netbeans-modules-csl-editor-GsfCodeFoldingSideBarFactory.instance">
    6.13                          <attr name="position" intvalue="1200"/>
    6.14 @@ -99,6 +103,10 @@
    6.15                          <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>
    6.16                      </file>
    6.17  
    6.18 +                    <file name="format">
    6.19 +                        <attr intvalue="750" name="position"/>
    6.20 +                    </file>
    6.21 +
    6.22                      <file name="SeparatorAfterFormat.instance">
    6.23                          <attr intvalue="780" name="position"/>
    6.24                          <attr name="instanceClass" stringvalue="javax.swing.JSeparator"/>