|
From: Christopher W. <caw...@us...> - 2006-02-10 18:27:53
|
Update of /cvsroot/rubyeclipse/org.rubypeople.rdt.core/src/org/rubypeople/rdt/core/formatter In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv2863/src/org/rubypeople/rdt/core/formatter Added Files: Indents.java CodeFormatter.java DefaultCodeFormatterConstants.java Log Message: Create a new abstract class CodeFormatter that all formatters should extends from. We're starting to move towards a JDT like formatter... --- NEW FILE: CodeFormatter.java --- /******************************************************************************* * Copyright (c) 2000, 2004 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.rubypeople.rdt.core.formatter; import org.eclipse.text.edits.TextEdit; /** * Specification for a generic source code formatter. * * @since 0.8.0 */ public abstract class CodeFormatter { /** * Unknown kind */ public static final int K_UNKNOWN = 0x00; /** * Kind used to format an expression */ public static final int K_EXPRESSION = 0x01; /** * Kind used to format a set of statements */ public static final int K_STATEMENTS = 0x02; /** * Kind used to format a set of class body declarations */ public static final int K_CLASS_BODY_DECLARATIONS = 0x04; /** * Kind used to format a ruby script */ public static final int K_RUBY_SCRIPT = 0x08; /** * Kind used to format a single-line comment * @since 0.8.0 */ public static final int K_SINGLE_LINE_COMMENT = 0x10; /** * Kind used to format a multi-line comment * @since 0.8.0 */ public static final int K_MULTI_LINE_COMMENT = 0x20; /** * Kind used to format a Javadoc comment * @since 0.8.0 */ public static final int K_RUBY_DOC = 0x40; /** * Format <code>source</code>, * and returns a text edit that correspond to the difference between the given string and the formatted string. * It returns null if the given string cannot be formatted. * * If the offset position is matching a whitespace, the result can include whitespaces. It would be up to the * caller to get rid of preceeding whitespaces. * * @param kind Use to specify the kind of the code snippet to format. It can be any of these: * K_EXPRESSION, K_STATEMENTS, K_CLASS_BODY_DECLARATIONS, K_RUBY_SCRIPT, K_UNKNOWN, * K_SINGLE_LINE_COMMENT, K_MULTI_LINE_COMMENT, K_RUBY_DOC * @param source the source to format * @param offset the given offset to start recording the edits (inclusive). * @param length the given length to stop recording the edits (exclusive). * @param indentationLevel the initial indentation level, used * to shift left/right the entire source fragment. An initial indentation * level of zero or below has no effect. * @param lineSeparator the line separator to use in formatted source, * if set to <code>null</code>, then the platform default one will be used. * @return the text edit * @throws IllegalArgumentException if offset is lower than 0, length is lower than 0 or * length is greater than source length. */ public abstract TextEdit format(int kind, String source, int offset, int length, int indentationLevel, String lineSeparator); } --- NEW FILE: Indents.java --- package org.rubypeople.rdt.core.formatter; import java.util.Map; import org.eclipse.jface.text.Assert; import org.rubypeople.rdt.core.RubyCore; public class Indents { private Indents() { } /** * Returns the tab width as configured in the given map. * @param options the map to get the formatter settings from. Use {@link org.eclipse.jdt.core.IJavaProject#getOptions(boolean)} to * get the most current project options. * @return the tab width */ public static int getTabWidth(Map options) { if (options == null) { throw new IllegalArgumentException(); } return getIntValue(options, DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, 4); } /** * Returns the tab width as configured in the given map. * @param options the map to get the formatter settings from. Use {@link org.eclipse.jdt.core.IJavaProject#getOptions(boolean)} to * get the most current project options. * @return the indent width */ public static int getIndentWidth(Map options) { if (options == null) { throw new IllegalArgumentException(); } int tabWidth=getTabWidth(options); boolean isMixedMode= DefaultCodeFormatterConstants.MIXED.equals(options.get(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR)); if (isMixedMode) { return getIntValue(options, DefaultCodeFormatterConstants.FORMATTER_INDENTATION_SIZE, tabWidth); } return tabWidth; } private static int getIntValue(Map options, String key, int def) { try { return Integer.parseInt((String) options.get(key)); } catch (NumberFormatException e) { return def; } } /** * Returns the indentation of the given line in indentation units. Odd spaces are * not counted. This method only analyzes the content of <code>line</code> up to the first * non-whitespace character. * * @param line the string to measure the indent of * @param tabWidth the width of one tab character in space equivalents * @param indentWidth the width of one indentation unit in space equivalents * @return the number of indentation units that line is indented by */ public static int measureIndentUnits(CharSequence line, int tabWidth, int indentWidth) { if (indentWidth <= 0 || tabWidth < 0 || line == null) { throw new IllegalArgumentException(); } int visualLength= measureIndentInSpaces(line, tabWidth); return visualLength / indentWidth; } /** * Creates a string that represents the given number of indentation units. * The returned string can contain tabs and/or spaces depending on the core * formatter preferences. * * @param indentationUnits the number of indentation units to generate * @param options the options to get the formatter settings from. Use {@link org.eclipse.jdt.core.IJavaProject#getOptions(boolean)} to * get the most current project options. * @return the indent string */ public static String createIndentString(int indentationUnits, Map options) { if (options == null || indentationUnits < 0) { throw new IllegalArgumentException(); } String tabChar= getStringValue(options, DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, RubyCore.TAB); final int tabs, spaces; if (RubyCore.SPACE.equals(tabChar)) { tabs= 0; spaces= indentationUnits * getIndentWidth(options); } else if (RubyCore.TAB.equals(tabChar)) { // indentWidth == tabWidth tabs= indentationUnits; spaces= 0; } else if (DefaultCodeFormatterConstants.MIXED.equals(tabChar)){ int tabWidth= getTabWidth(options); int spaceEquivalents= indentationUnits * getIndentWidth(options); if (tabWidth > 0) { tabs= spaceEquivalents / tabWidth; spaces= spaceEquivalents % tabWidth; } else { tabs= 0; spaces= spaceEquivalents; } } else { // new indent type not yet handled Assert.isTrue(false); return null; } StringBuffer buffer= new StringBuffer(tabs + spaces); for(int i= 0; i < tabs; i++) buffer.append('\t'); for(int i= 0; i < spaces; i++) buffer.append(' '); return buffer.toString(); } private static String getStringValue(Map options, String key, String def) { Object value= options.get(key); if (value instanceof String) return (String) value; return def; } /** * Returns the indentation of the given line in space equivalents. * Tab characters are counted using the given <code>tabWidth</code> and every other indent * character as one. This method analyzes the content of <code>line</code> up to the first * non-whitespace character. * * @param line the string to measure the indent of * @param tabWidth the width of one tab in space equivalents * @return the measured indent width in space equivalents */ public static int measureIndentInSpaces(CharSequence line, int tabWidth) { if (tabWidth < 0 || line == null) { throw new IllegalArgumentException(); } int length= 0; int max= line.length(); for (int i= 0; i < max; i++) { char ch= line.charAt(i); if (ch == '\t') { int reminder= length % tabWidth; length += tabWidth - reminder; } else if (isIndentChar(ch)) { length++; } else { return length; } } return length; } /** * Returns the leading indentation string of the given line. Note that the * returned string need not be equal to the leading whitespace as odd spaces * are not considered part of the indentation. * * @param line * the line to scan * @param tabWidth * the size of one tab in space equivalents * @param indentWidth * the width of one indentation unit in space equivalents * @return the indent part of <code>line</code>, but no odd spaces */ public static String extractIndentString(String line, int tabWidth, int indentWidth) { if (tabWidth < 0 || indentWidth <= 0 || line == null) { throw new IllegalArgumentException(); } int size = line.length(); int end = 0; int spaceEquivs = 0; int characters = 0; for (int i = 0; i < size; i++) { char c = line.charAt(i); if (c == '\t') { int remainder = spaceEquivs % tabWidth; spaceEquivs += tabWidth - remainder; characters++; } else if (isIndentChar(c)) { spaceEquivs++; characters++; } else { break; } if (spaceEquivs >= indentWidth) { end += characters; characters = 0; spaceEquivs = spaceEquivs % indentWidth; } } if (end == 0) return ""; //$NON-NLS-1$ else if (end == size) return line; else return line.substring(0, end); } /** * Tests if a character is an indent character. Indent character are all * whitespace characters except the line delimiter characters. * * @param ch * The character to test * @return Returns <code>true</code> if this the character is a indent * character */ public static boolean isIndentChar(char ch) { return Character.isWhitespace(ch) && !isLineDelimiterChar(ch); } /** * Tests if a character is a line delimiter character. * * @param ch * The character to test * @return Returns <code>true</code> if this the character is a line * delimiter character */ public static boolean isLineDelimiterChar(char ch) { return ch == '\n' || ch == '\r'; } } --- NEW FILE: DefaultCodeFormatterConstants.java --- package org.rubypeople.rdt.core.formatter; import java.util.Map; import org.rubypeople.rdt.core.RubyCore; import org.rubypeople.rdt.internal.formatter.DefaultCodeFormatterOptions; public class DefaultCodeFormatterConstants { /** * <pre> * FORMATTER / Possible value for the option FORMATTER_TAB_CHAR * </pre> * * @since 0.8.0 * @see RubyCore#TAB * @see RubyCore#SPACE * @see #FORMATTER_TAB_CHAR */ public static final String MIXED = "mixed"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Option to specify the equivalent number of spaces that represents one indentation * - option id: "org.rubypeople.rdt.core.formatter.indentation.size" * - possible values: "<n>", where n is zero or a positive integer * - default: "4" * </pre> * * <p> * This option is used only if the tab char is set to MIXED. * </p> * * @see #FORMATTER_TAB_CHAR * @since 0.8.0 */ public static final String FORMATTER_INDENTATION_SIZE = RubyCore.PLUGIN_ID + ".formatter.indentation.size"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Option to specify the tabulation size * - option id: "org.rubypeople.rdt.core.formatter.tabulation.char" * - possible values: { TAB, SPACE, MIXED } * - default: TAB * </pre> * * More values may be added in the future. * * @see RubyCore#TAB * @see RubyCore#SPACE * @see #MIXED * @since 0.8.0 */ public static final String FORMATTER_TAB_CHAR = RubyCore.PLUGIN_ID + ".formatter.tabulation.char"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Option to specify the equivalent number of spaces that represents one tabulation * - option id: "org.rubypeople.rdt.core.formatter.tabulation.size" * - possible values: "<n>", where n is zero or a positive integer * - default: "4" * </pre> * * @since 0.8.0 */ public static final String FORMATTER_TAB_SIZE = RubyCore.PLUGIN_ID + ".formatter.tabulation.size"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Value to set an option to true. * </pre> * * @since 0.8.0 */ public static final String FALSE = "false"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Value to set an option to false. * </pre> * * @since 0.8.0 */ public static final String TRUE = "true"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Option to specify the length of the page. Beyond this length, the formatter will try to split the code * - option id: "org.rubypeople.rdt.core.formatter.lineSplit" * - possible values: "<n>", where n is zero or a positive integer * - default: "80" * </pre> * * @since 0.8.0 */ public static final String FORMATTER_LINE_SPLIT = RubyCore.PLUGIN_ID + ".formatter.lineSplit"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Option to ident empty lines * - option id: "org.rubypeople.rdt.core.formatter.indent_empty_lines" * - possible values: { TRUE, FALSE } * - default: FALSE * </pre> * * @see #TRUE * @see #FALSE * @since 0.8.0 */ public static final String FORMATTER_INDENT_EMPTY_LINES = RubyCore.PLUGIN_ID + ".formatter.indent_empty_lines"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Option to use tabulations only for leading indentations * - option id: "org.rubypeople.rdt.core.formatter.use_tabs_only_for_leading_indentations" * - possible values: { TRUE, FALSE } * - default: FALSE * </pre> * * @see #TRUE * @see #FALSE * @since 0.8.0 */ public static final String FORMATTER_USE_TABS_ONLY_FOR_LEADING_INDENTATIONS = RubyCore.PLUGIN_ID + ".formatter.use_tabs_only_for_leading_indentations"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Option to control whether comments are formatted * - option id: "org.rubypeople.rdt.core.formatter.comment.format_comments" * - possible values: { TRUE, FALSE } * - default: TRUE * </pre> * * @see #TRUE * @see #FALSE * @since 0.8.0 */ public final static String FORMATTER_COMMENT_FORMAT = RubyCore.PLUGIN_ID + ".formatter.comment.format_comments"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Option to control whether the header comment of a Ruby source file is formatted * - option id: "org.rubypeople.rdt.core.formatter.comment.format_header" * - possible values: { TRUE, FALSE } * - default: FALSE * </pre> * * @see #TRUE * @see #FALSE * @since 0.8.0 */ public final static String FORMATTER_COMMENT_FORMAT_HEADER = RubyCore.PLUGIN_ID + ".formatter.comment.format_header"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Option to specify the line length for comments. * - option id: "org.rubypeople.rdt.core.formatter.comment.line_length" * - possible values: "<n>", where n is zero or a positive integer * - default: "80" * </pre> * * @since 0.8.0 */ public final static String FORMATTER_COMMENT_LINE_LENGTH = RubyCore.PLUGIN_ID + ".formatter.comment.line_length"; //$NON-NLS-1$ /** * <pre> * FORMATTER / Option to control whether blank lines are cleared inside comments * - option id: "org.rubypeople.rdt.core.formatter.comment.clear_blank_lines" * - possible values: { TRUE, FALSE } * - default: FALSE * </pre> * @see #TRUE * @see #FALSE * @since 0.8.0 */ public final static String FORMATTER_COMMENT_CLEAR_BLANK_LINES = RubyCore.PLUGIN_ID + ".formatter.comment.clear_blank_lines"; //$NON-NLS-1$ /** * Returns the default Eclipse formatter settings * * @return the Eclipse default settings * @since 0.8.0 */ public static Map getEclipseDefaultSettings() { return DefaultCodeFormatterOptions.getEclipseDefaultSettings().getMap(); } public static Map getRubyConventionsSettings() { return DefaultCodeFormatterOptions.getRubyConventionsSettings().getMap(); } } |