package com.aptana.editor.php.internal.ui.editor.formatting;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.DocumentCommand;
import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.source.ISourceViewer;
import org2.eclipse.php.internal.core.documentModel.parser.regions.PHPRegionTypes;
import com.aptana.core.logging.IdeLog;
import com.aptana.editor.php.PHPEditorPlugin;
import com.aptana.editor.php.internal.core.IPHPConstants;
import com.aptana.editor.php.internal.ui.editor.PHPSourceViewerConfiguration;
/**
* A PHP auto-edit strategy for indenting alternative syntax and block-ending elements while typing.<br>
* The alternative syntax that we are dealing with includes: endif, endwhile, endfor, endforeach and endswitch <br>
* The block-ending syntax that we are dealing with includes: elseif and else
*
* @author Shalom Gibly <sgibly@aptana.com>
*/
public class BlockEndingSyntaxAutoEditStrategy extends AbstractPHPAutoEditStrategy
{
private Map<Character, List<String>> suffixToIncompleteWords;
private StringBuilder buffer;
private Map<String, Set<String>> sameIndentMatch;
/**
* Constructs a new BlockEndingSyntaxAutoEditStrategy
*
* @param contentType
* @param configuration
* @param sourceViewer
*/
public BlockEndingSyntaxAutoEditStrategy(String contentType, PHPSourceViewerConfiguration configuration,
ISourceViewer sourceViewer)
{
super(contentType, configuration, sourceViewer);
buffer = new StringBuilder();
suffixToIncompleteWords = new HashMap<Character, List<String>>();
suffixToIncompleteWords.put('f', Arrays.asList("endi", "elsei")); //$NON-NLS-1$ //$NON-NLS-2$
suffixToIncompleteWords.put('e', Arrays.asList("endwhil", "els")); //$NON-NLS-1$ //$NON-NLS-2$
suffixToIncompleteWords.put('r', Arrays.asList("endfo")); //$NON-NLS-1$
suffixToIncompleteWords.put('h', Arrays.asList("endforeac", "endswitc")); //$NON-NLS-1$ //$NON-NLS-2$
// create the maps for the indentation matching of a block ending word to it's initial block
sameIndentMatch = new HashMap<String, Set<String>>();
sameIndentMatch.put("endif", new HashSet<String>(Arrays.asList(PHPRegionTypes.PHP_IF, PHPRegionTypes.PHP_ELSE, PHPRegionTypes.PHP_ELSEIF))); //$NON-NLS-1$
sameIndentMatch.put("else", new HashSet<String>(Arrays.asList(PHPRegionTypes.PHP_IF, PHPRegionTypes.PHP_ELSEIF))); //$NON-NLS-1$
sameIndentMatch.put("elseif", new HashSet<String>(Arrays.asList(PHPRegionTypes.PHP_IF, PHPRegionTypes.PHP_ELSEIF))); //$NON-NLS-1$
sameIndentMatch.put("endwhile", new HashSet<String>(Arrays.asList(PHPRegionTypes.PHP_WHILE))); //$NON-NLS-1$
sameIndentMatch.put("endfor", new HashSet<String>(Arrays.asList(PHPRegionTypes.PHP_FOR))); //$NON-NLS-1$
sameIndentMatch.put("endforeach", new HashSet<String>(Arrays.asList(PHPRegionTypes.PHP_FOREACH))); //$NON-NLS-1$
sameIndentMatch.put("endswitch", new HashSet<String>(Arrays.asList(PHPRegionTypes.PHP_SWITCH))); //$NON-NLS-1$
}
/*
* (non-Javadoc)
* @see
* com.aptana.editor.php.internal.ui.editor.formatting.AbstractPHPAutoEditStrategy#isValidAutoInsertLocation(org
* .eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
*/
@Override
protected boolean isValidAutoInsertLocation(IDocument document, DocumentCommand command)
{
if (command.text == null || command.text.length() == 0)
{
return false;
}
return suffixToIncompleteWords.containsKey(command.text.charAt(command.text.length() - 1));
}
/*
* (non-Javadoc)
* @see
* com.aptana.editor.php.internal.ui.editor.formatting.AbstractPHPAutoEditStrategy#customizeDocumentCommand(org.
* eclipse.jface.text.IDocument, org.eclipse.jface.text.DocumentCommand)
*/
@Override
public void customizeDocumentCommand(IDocument document, DocumentCommand command)
{
// At this point, we only know that the command text ends with f, e, r or h. We need to make sure it's actually
// an alternative syntax or a block ending word.
try
{
char lastChar = command.text.charAt(command.text.length() - 1);
List<String> incompleteWords = suffixToIncompleteWords.get(lastChar);
String incompleteWord = null;
String completeWord = null;
for (String word : incompleteWords)
{
// For each word that ends with that char, check if the document contains it.
incompleteWord = word;
int length = incompleteWord.length();
if (command.offset >= length + 1)
{
if (document.get(command.offset - length, length).equals(incompleteWord))
{
// we found a possible match in the document
completeWord = incompleteWord + lastChar;
}
}
}
if (completeWord == null)
{
return;
}
String type = document.getContentType(command.offset);
if (!IPHPConstants.DEFAULT.equals(type))
{
return;
}
indentBlockEnding(completeWord, document, command);
}
catch (BadLocationException e)
{
IdeLog.logError(PHPEditorPlugin.getDefault(), "Error customizing a PHP block-ending command", e); //$NON-NLS-1$
}
}
@SuppressWarnings("unchecked")
private void indentBlockEnding(String completeWord, IDocument document, DocumentCommand command) throws BadLocationException
{
// We add one, as the letter is just being type
int startOffset = command.offset - completeWord.length() + 1;
int lineNumber = document.getLineOfOffset(command.offset);
IRegion lineInfo = document.getLineInformation(lineNumber);
int lineOffset = lineInfo.getOffset();
String startLine = document.get(lineOffset, startOffset - lineOffset);
// only if the word is the first non-whitespace words
if (startLine.trim().length() == 0)
{
buffer.setLength(0);
// match the indentation of the inserted word to a previous block start word
matchIndent(document, buffer, lineNumber, startOffset, sameIndentMatch.get(completeWord), Collections.EMPTY_SET);
String bufferString = buffer.toString();
if (!bufferString.equals(startLine))
{
// we remove some spaces before the text, and practically dedent.
command.length += (command.offset - lineOffset);
command.offset = lineOffset;
command.text = bufferString + completeWord;
}
}
}
}