[Pydev-cvs] org.python.pydev/src/org/python/pydev/editor/autoedit AbstractIndentPrefs.java,NONE,1.1
Brought to you by:
fabioz
From: Fabio Z. <fa...@us...> - 2005-06-18 21:27:46
|
Update of /cvsroot/pydev/org.python.pydev/src/org/python/pydev/editor/autoedit In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv17477/src/org/python/pydev/editor/autoedit Added Files: AbstractIndentPrefs.java DefaultIndentPrefs.java IIndentPrefs.java PyAutoIndentStrategy.java Log Message: --- NEW FILE: PyAutoIndentStrategy.java --- /* * Created on Dec 10, 2003 * Author: atotic * License: Common Public License 1.0 */ package org.python.pydev.editor.autoedit; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DefaultAutoIndentStrategy; import org.eclipse.jface.text.DocumentCommand; import org.eclipse.jface.text.IDocument; import org.python.pydev.editor.actions.PyAction; import org.python.pydev.editor.actions.PySelection; /** * Implements indenting behavior. * * <p> * Tabs vs. spaces indentation * <p> * Borrows heavily from {@link org.eclipse.jface.text.DefaultAutoIndentStrategy}, and the pyeclipse (PythonAutoIndentStrategy). */ public class PyAutoIndentStrategy extends DefaultAutoIndentStrategy { private IIndentPrefs prefs; public void setIndentPrefs(IIndentPrefs prefs) { this.prefs = prefs; } public IIndentPrefs getIndentPrefs() { if (this.prefs == null) { this.prefs = new DefaultIndentPrefs(); //create the default if this is still not done. } return this.prefs; } public static String createSpaceString(int width) { StringBuffer b = new StringBuffer(width); while (width-- > 0) b.append(" "); return b.toString(); } /** * Set indentation automatically after newline. * * @param document * @param length * @param text * @param offset * @return String * @throws BadLocationException */ private String autoIndentNewline(IDocument document, int length, String text, int offset) throws BadLocationException { if (length == 0 && text != null && AbstractIndentPrefs.endsWithNewline(document, text)) { if (offset > 0) { char lastChar = document.getChar(offset - 1); if (lastChar == ':') { String initial = text; text = initial + prefs.getIndentationString(); } else if (lastChar == ',') { text = indentAfterCommaNewline(document, text, offset); } } } return text; } /** * Create the indentation string after comma and a newline. * * @param document * @param text * @param offset * @return Indentation String * @throws BadLocationException */ private String indentAfterCommaNewline(IDocument document, String text, int offset) throws BadLocationException { int smartIndent = totalIndentAmountAfterCommaNewline(document, offset); if (smartIndent > 0) { String initial = text; // Discard everything but the newline from initial, since we'll // build the smart indent from scratch anyway. int initialLength = initial.length(); for (int i = 0; i < initialLength; i++) { char theChar = initial.charAt(i); // This covers all cases I know of, but if there is any platform // with weird newline then this would need to be smarter. if (theChar != '\r' && theChar != '\n') { if (i > 0) { initial = initial.substring(0, i); smartIndent -= --i; } break; } } // Create the actual indentation string String indentationString = prefs.getIndentationString(); int indentationSteps = smartIndent / prefs.getTabWidth(); int spaceSteps = smartIndent % prefs.getTabWidth(); StringBuffer b = new StringBuffer(smartIndent); while (indentationSteps-- > 0) b.append(indentationString); while (spaceSteps-- > 0) b.append(" "); return initial + b.toString(); } return text; } /** * Return smart indent amount for new line. This should be done for multiline structures like function parameters, tuples, lists and dictionaries. * * Example: * * a=foo(1, # * * We would return the indentation needed to place the caret at the # position. * * @param document The document * @param offset The document offset of the last character on the previous line * @return indent, or -1 if smart indent could not be determined (fall back to default) */ private int totalIndentAmountAfterCommaNewline(IDocument document, int offset) throws BadLocationException { int lineStart = document.getLineInformationOfOffset(offset).getOffset(); String line = document.get(lineStart, offset - lineStart); int lineLength = line.length(); for (int i = lineLength - 1; i > 0; i--) { char theChar = line.charAt(i); // This covers all cases I know of, but if there is any platform // with weird newline then this would need to be smarter. if (theChar == '\r' || theChar == '\n') break; if (theChar == '(' || theChar == '[' || theChar == '{') { //ok, it's not just returning the line now, we have to check for tabs and make each //tab count for the tabWidth. int smartIndent = lineLength - (lineLength - i) + 1; String string = line.substring(0, smartIndent - 1); for (int j = 0; j < string.length(); j++) { char c = string.charAt(j); if (c == '\t') { smartIndent += prefs.getTabWidth() - 1; } } return smartIndent; } } return -1; } /** * * @see org.eclipse.jface.text.IAutoEditStrategy#customizeDocumentCommand(IDocument, DocumentCommand) */ public void customizeDocumentCommand(IDocument document, DocumentCommand command) { // super idents newlines the same amount as the previous line super.customizeDocumentCommand(document, command); try { command.text = autoIndentNewline(document, command.length, command.text, command.offset); prefs.convertToStd(document, command); if(command.text.equals("(")){ PySelection ps = new PySelection(document, command.offset); String line = ps.getLine(); if(shouldClose(ps)){ boolean hasClass = line.indexOf("class ") != -1; boolean hasClassMethodDef = line.indexOf(" def ") != -1; boolean hasMethodDef = line.indexOf("def ") != -1; boolean hasNoDoublePoint = line.indexOf(":") == -1; command.shiftsCaret = false; if(hasNoDoublePoint && (hasClass || hasClassMethodDef || hasMethodDef)){ if(hasClass){ command.text = "(object):"; command.caretOffset = command.offset + 7; }else if (hasClassMethodDef){ command.text = "(self):"; command.caretOffset = command.offset + 5; }else if (hasMethodDef){ command.text = "():"; command.caretOffset = command.offset + 1; }else{ throw new RuntimeException("Something went wrong"); } }else{ command.text = "()"; command.caretOffset = command.offset + 1; } } } } catch (BadLocationException e) { e.printStackTrace(); } } /** * @param ps * @return * @throws BadLocationException */ private boolean shouldClose(PySelection ps) throws BadLocationException { String line = ps.getLine(); String lineContentsFromCursor = ps.getLineContentsFromCursor(); for (int i = 0; i < lineContentsFromCursor.length(); i++) { if(!Character.isWhitespace(lineContentsFromCursor.charAt(i))){ return false; } } int i = PyAction.countChars('(', line); int j = PyAction.countChars(')', line); if(j > i){ return false; } return true; } } --- NEW FILE: AbstractIndentPrefs.java --- /* * Created on May 5, 2005 * * @author Fabio Zadrozny */ package org.python.pydev.editor.autoedit; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentCommand; import org.eclipse.jface.text.IDocument; /** * @author Fabio Zadrozny */ public abstract class AbstractIndentPrefs implements IIndentPrefs{ private boolean forceTabs = false; public boolean getForceTabs() { return forceTabs; } public void setForceTabs(boolean forceTabs) { this.forceTabs = forceTabs; } /** * Naive implementation. Always redoes the indentation string based in the * spaces and tabs settings. * * @see org.python.pydev.editor.autoedit.IIndentPrefs#getIndentationString() */ public String getIndentationString() { if (getUseSpaces() && !getForceTabs()) return PyAutoIndentStrategy.createSpaceString(getTabWidth()); else return "\t"; } public void convertToStd(IDocument document, DocumentCommand command){ try { if (getUseSpaces()) { command.text = convertTabsToSpaces(document, command.length, command.text, command.offset, getIndentationString()); } else { command.text = convertSpacesToTabs(document, command.length, command.text, command.offset, getIndentationString()); } } catch (Exception e) { throw new RuntimeException(e); } } //------------------------------------------------------------- UTILS /** * Replaces tabs if needed by ident string or just a space depending of the * tab location * */ private String convertTabsToSpaces( IDocument document, int length, String text, int offset, String indentString) throws BadLocationException { // only interresting if it contains a tab (also if it is a tab only) if (text.indexOf("\t") != -1) { // get some text infos int lineStart = document.getLineInformationOfOffset(offset).getOffset(); String line = document.get(lineStart, offset - lineStart); if (text.equals("\t")) { //only a single tab? deleteWhitespaceAfter(document, offset); text = indentString; } else { // contains a char (pasted text) byte[] byteLine = text.getBytes(); StringBuffer newText = new StringBuffer(); for (int count = 0; count < byteLine.length; count++) { if (byteLine[count] == '\t') newText.append(indentString); // if it is not a tab add the char else newText.append((char) byteLine[count]); } text = newText.toString(); } } return text; } /** * Converts spaces to strings. Useful when pasting */ private String convertSpacesToTabs( IDocument document, int length, String text, int offset, String indentString) throws BadLocationException { String spaceStr = PyAutoIndentStrategy.createSpaceString(getTabWidth()); while(text.startsWith(spaceStr)){ text = text.replaceAll(spaceStr, "\t"); } return text; } /** * When hitting TAB, delete the whitespace after the cursor in the line */ private void deleteWhitespaceAfter(IDocument document, int offset) throws BadLocationException { if (offset < document.getLength() && !endsWithNewline(document, document.get(offset, 1))) { int lineLength = document.getLineInformationOfOffset(offset).getLength(); int lineStart = document.getLineInformationOfOffset(offset).getOffset(); String textAfter = document.get(offset, (lineStart + lineLength) - offset); if (textAfter.length() > 0 && isWhitespace(textAfter)) { document.replace(offset, textAfter.length(), ""); } } } private boolean isWhitespace(String s) { for (int i = s.length() - 1; i > -1 ; i--) if (!Character.isWhitespace(s.charAt(i))) return false; return true; } /** * True if text ends with a newline delimiter */ public static boolean endsWithNewline(IDocument document, String text) { String[] newlines = document.getLegalLineDelimiters(); boolean ends = false; for (int i = 0; i < newlines.length; i++) { String delimiter = newlines[i]; if (text.indexOf(delimiter) != -1) ends = true; } return ends; } } --- NEW FILE: IIndentPrefs.java --- /* * Created on May 5, 2005 * * @author Fabio Zadrozny */ package org.python.pydev.editor.autoedit; import org.eclipse.jface.text.DocumentCommand; import org.eclipse.jface.text.IDocument; /** * @author Fabio Zadrozny */ public interface IIndentPrefs { /** * @return True if we should substitute tabs for spaces. */ public boolean getUseSpaces(); /** * Sets the forceTabs preference for auto-indentation. * * <p> * This is the preference that overrides "use spaces" preference when file * contains tabs (like mine do). * <p> * If the first indented line starts with a tab, then tabs override spaces. * * @return True If tabs should be used even if it says we should use spaces. */ public boolean getForceTabs(); public void setForceTabs(boolean forceTabs); /** * @return the width a tab should have. */ public int getTabWidth(); /** * @return the indentation string based on the current settings. */ public String getIndentationString(); /** * Given the current settings, convert the current string to tabs or spaces. */ public void convertToStd(IDocument document, DocumentCommand command); /** * @return whether we should auto-close parentesis */ public boolean getAutoParentesis(); } --- NEW FILE: DefaultIndentPrefs.java --- /* * Created on May 5, 2005 * * @author Fabio Zadrozny */ package org.python.pydev.editor.autoedit; import org.python.pydev.plugin.PydevPrefs; public class DefaultIndentPrefs extends AbstractIndentPrefs { /** * Cache for indentation string */ private String indentString = null; private boolean useSpaces = PydevPrefs.getPreferences().getBoolean(PydevPrefs.SUBSTITUTE_TABS); private int tabWidth = PydevPrefs.getPreferences().getInt(PydevPrefs.TAB_WIDTH); public boolean getUseSpaces() { if(useSpaces != PydevPrefs.getPreferences().getBoolean(PydevPrefs.SUBSTITUTE_TABS)){ useSpaces = PydevPrefs.getPreferences().getBoolean(PydevPrefs.SUBSTITUTE_TABS); regenerateIndetString(); } return useSpaces; } public int getTabWidth() { if(tabWidth != PydevPrefs.getPreferences().getInt(PydevPrefs.TAB_WIDTH)){ tabWidth = PydevPrefs.getPreferences().getInt(PydevPrefs.TAB_WIDTH); regenerateIndetString(); } return tabWidth; } private void regenerateIndetString(){ indentString = super.getIndentationString(); } /** * This class also puts the indentation string in a cache and redoes it * if the preferences are changed. * * @return the indentation string. */ public String getIndentationString() { if (indentString == null){ regenerateIndetString(); } return indentString; } /** * @see org.python.pydev.editor.autoedit.IIndentPrefs#getAutoParentesis() */ public boolean getAutoParentesis() { return PydevPrefs.getPreferences().getBoolean(PydevPrefs.AUTO_PAR); } } |