/******************************************************************************* * Copyright (c) 2009 Andrey Loskutov. * 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 * Contributor: Andrey Loskutov - initial API and implementation *******************************************************************************/ /* This class is started as extension of Rahul Kuchal's whitespace plugin. * Rahul Kuchal - http://www.kuchhal.com/ */ package de.loskutov.anyedit.actions; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.ProjectScope; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.preferences.DefaultScope; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.osgi.service.prefs.BackingStoreException; import org.osgi.service.prefs.Preferences; import de.loskutov.anyedit.IAnyEditConstants; import de.loskutov.anyedit.jdt.JdtUtils; import de.loskutov.anyedit.ui.preferences.CombinedPreferences; import de.loskutov.anyedit.util.EclipseUtils; import de.loskutov.anyedit.util.LineReplaceResult; import de.loskutov.anyedit.util.TextReplaceResultSet; import de.loskutov.anyedit.util.TextUtil; public class Spaces extends AbstractTextAction { public Spaces() { super(); } @Override protected TextReplaceResultSet estimateActionRange(IDocument doc) { TextReplaceResultSet result = new TextReplaceResultSet(); if (doc == null) { return result; } int linesNumber = doc.getNumberOfLines(); result.setStartLine(0); result.setStopLine(linesNumber - 1); return result; } /** * Should be invoked always after estimateActionRange() to ensure that * operaton is possible * @param doc cannot be null * @param actionID * @param resultSet cannot be null */ @Override protected void doTextOperation(IDocument doc, String actionID, TextReplaceResultSet resultSet) throws BadLocationException { int maxNbr = resultSet.getStartLine() + resultSet.getNumberOfLines(); boolean removeTrailing; boolean convertEnabled; boolean tabsToSpaces; boolean addLineEnabled; boolean fixLineDelimiters; CombinedPreferences prefs = getCombinedPreferences(); boolean replaceAllTabs = isReplaceAllTabsEnabled(prefs); boolean replaceAllSpaces = isReplaceAllSpacesEnabled(prefs); boolean useModulo4Tabs = prefs.getBoolean(IAnyEditConstants.USE_MODULO_CALCULATION_FOR_TABS_REPLACE); boolean ignoreBlankLines = prefs.getBoolean(IAnyEditConstants.IGNORE_BLANK_LINES_WHEN_TRIMMING); boolean usedOnSave = isUsedOnSave(); if (usedOnSave) { removeTrailing = isSaveAndTrimEnabled(); tabsToSpaces = isDefaultTabToSpaces(); convertEnabled = isSaveAndConvertEnabled(); addLineEnabled = isSaveAndAddLineEnabled(prefs); fixLineDelimiters = isSaveAndFixLineDelimitersEnabled(prefs); } else { removeTrailing = isRemoveTrailingSpaceEnabled(prefs); tabsToSpaces = actionID.startsWith(ACTION_ID_CONVERT_TABS); convertEnabled = true; addLineEnabled = isAddLineEnabled(prefs); fixLineDelimiters = isFixLineDelimitersEnabled(prefs); } int tabWidth = getTabWidth(getFile(), prefs); String lineDelimiter = getLineDelimiter(); StringBuffer sb = new StringBuffer(); if (!tabsToSpaces && tabWidth == 0) { // TODO: prevent division by zero - probably on another place? tabWidth = 1; } String line; IRegion lineInfo; for (int i = resultSet.getStartLine(); i < maxNbr; i++) { lineInfo = doc.getLineInformation(i); // whole line text will be "replaced" int rangeToReplace = lineInfo.getLength(); line = doc.get(lineInfo.getOffset(), rangeToReplace); if (line == null) { resultSet.add(null); continue; } sb.append(line); boolean changed; if (convertEnabled) { if (tabsToSpaces) { changed = TextUtil.convertTabsToSpaces(sb, tabWidth, removeTrailing, ignoreBlankLines, replaceAllTabs, useModulo4Tabs); } else { changed = TextUtil.convertSpacesToTabs(sb, tabWidth, removeTrailing, ignoreBlankLines, replaceAllSpaces); } } else { if (!usedOnSave || removeTrailing) { changed = TextUtil.removeTrailingSpace(sb, ignoreBlankLines); } else { changed = false; } } // on the last NON empty line add new line character(s) if (addLineEnabled && i == maxNbr - 1 && sb.length() != 0) { sb.append(lineDelimiter); changed = true; } else if(fixLineDelimiters){ final String delimiter = doc.getLineDelimiter(i); if (delimiter != null && delimiter.length() > 0 && !delimiter.equals(lineDelimiter)) { rangeToReplace += delimiter.length(); sb.append(lineDelimiter); changed = true; } } if (changed) { LineReplaceResult result = new LineReplaceResult(); result.rangeToReplace = rangeToReplace; result.textToReplace = sb.toString(); resultSet.add(result); } else { resultSet.add(null); } // cleanup sb.setLength(0); } } protected String getLineDelimiter() { IFile file = getFile(); IProject project = null; if (file != null) { project = file.getProject(); } String value = getLineDelimiter(getPlatformPreferences(project)); if (value == null) { value = getLineDelimiter(getPlatformPreferences(null)); } if (value == null) { value = getLineDelimiter(Platform.getPreferencesService().getRootNode().node(DefaultScope.SCOPE)); } return value != null ? value : System.getProperty(Platform.PREF_LINE_SEPARATOR, "\n"); } private static Preferences getPlatformPreferences(IProject project) { IEclipsePreferences rootNode = Platform.getPreferencesService().getRootNode(); if (project != null) { return rootNode.node(ProjectScope.SCOPE).node(project.getName()); } return rootNode.node(InstanceScope.SCOPE); } private static String getLineDelimiter(Preferences node) { try { // be careful looking up for our node so not to create any nodes as side effect if (node.nodeExists(Platform.PI_RUNTIME)) { return node.node(Platform.PI_RUNTIME).get(Platform.PREF_LINE_SEPARATOR, null); } } catch (BackingStoreException e) { // ignore } return null; } public boolean isSaveAndTrimEnabled() { return getCombinedPreferences().getBoolean(IAnyEditConstants.SAVE_AND_TRIM_ENABLED); } private boolean isSaveAndAddLineEnabled(CombinedPreferences prefs) { return prefs.getBoolean(IAnyEditConstants.SAVE_AND_ADD_LINE); } private boolean isSaveAndFixLineDelimitersEnabled(CombinedPreferences prefs) { return prefs.getBoolean(IAnyEditConstants.SAVE_AND_FIX_LINE_DELIMITERS); } public boolean isSaveAndConvertEnabled() { return getCombinedPreferences().getBoolean(IAnyEditConstants.SAVE_AND_CONVERT_ENABLED); } private boolean isAddLineEnabled(CombinedPreferences prefs) { return prefs.getBoolean(IAnyEditConstants.ADD_NEW_LINE); } protected boolean isRemoveTrailingSpaceEnabled(CombinedPreferences prefs) { return prefs.getBoolean(IAnyEditConstants.REMOVE_TRAILING_SPACES); } protected boolean isFixLineDelimitersEnabled(CombinedPreferences prefs) { return prefs.getBoolean(IAnyEditConstants.FIX_LINE_DELIMITERS); } protected boolean isReplaceAllTabsEnabled(CombinedPreferences prefs) { return prefs.getBoolean(IAnyEditConstants.REPLACE_ALL_TABS_WITH_SPACES); } protected boolean isReplaceAllSpacesEnabled(CombinedPreferences prefs) { return prefs.getBoolean(IAnyEditConstants.REPLACE_ALL_SPACES_WITH_TABS); } public boolean isDefaultTabToSpaces() { String action = getCombinedPreferences().getString(IAnyEditConstants.CONVERT_ACTION_ON_SAVE); return IAnyEditConstants.ACTION_ID_CONVERT_TABS.equals(action); } public int getTabWidth(IFile file, CombinedPreferences prefs) { int tabWidth = -1; if (EclipseUtils.isJavaInput(file) && prefs.getBoolean(IAnyEditConstants.USE_JAVA_TAB_WIDTH_FOR_JAVA)) { tabWidth = JdtUtils.getTabWidth(file); } else { tabWidth = prefs.getInt(IAnyEditConstants.EDITOR_TAB_WIDTH); } if (tabWidth < 0) { tabWidth = IAnyEditConstants.DEFAULT_TAB_WIDTH; } return tabWidth; } }