/* * $Id$ * * Copyright (c) 2004-2005 by the TeXlapse Team. * 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 */ package net.sourceforge.texlipse.actions; import java.util.HashSet; import java.util.Set; import net.sourceforge.texlipse.TexlipsePlugin; import net.sourceforge.texlipse.editor.TexEditor; import net.sourceforge.texlipse.editor.TexEditorTools; import net.sourceforge.texlipse.properties.TexlipseProperties; import org.eclipse.jface.action.IAction; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.TextSelection; import org.eclipse.jface.viewers.ISelection; import org.eclipse.ui.IEditorActionDelegate; import org.eclipse.ui.IEditorPart; import org.eclipse.ui.texteditor.AbstractDecoratedTextEditorPreferenceConstants; import org.eclipse.ui.texteditor.ITextEditor; /** * This class handles the action based text wrapping. * * @author Antti Pirinen * @author Oskar Ojala */ public class TexHardLineWrapAction implements IEditorActionDelegate { private IEditorPart targetEditor; private int tabWidth = 2; private int lineLength = 80; private TexEditorTools tools; //private static TexSelections selection; private static Set environmentsToProcess = new HashSet(); static { environmentsToProcess.add("document"); } public TexHardLineWrapAction() { this.tools = new TexEditorTools(); } /** * From what editot the event will come. * @param action not used in this method, can also be </code>null</code> * @param targetEditor the editor that calls this class * @see org.eclipse.ui.IEditorActionDelegate#setActiveEditor(org.eclipse.jface.action.IAction, org.eclipse.ui.IEditorPart) */ public void setActiveEditor(IAction action, IEditorPart targetEditor) { this.targetEditor = targetEditor; } /** * When the user presses <code>Esc, q</code> or selects from menu bar * <code>Wrap Lines</code> this method is invoked. * @param action an action that invokes * @see org.eclipse.ui.IActionDelegate#run(org.eclipse.jface.action.IAction) */ public void run(IAction action) { this.lineLength = TexlipsePlugin.getDefault().getPreferenceStore().getInt(TexlipseProperties.WORDWRAP_LENGTH); this.tabWidth = TexlipsePlugin.getDefault().getPreferenceStore().getInt(AbstractDecoratedTextEditorPreferenceConstants.EDITOR_TAB_WIDTH); TexSelections selection = new TexSelections(getTexEditor()); try { doWrapB(selection); } catch(BadLocationException e) { TexlipsePlugin.log("TexCorrectIndentationAction.run", e); } } /* (non-Javadoc) * @see org.eclipse.ui.IActionDelegate#selectionChanged(org.eclipse.jface.action.IAction, org.eclipse.jface.viewers.ISelection) */ public void selectionChanged(IAction action, ISelection selection) { if (selection instanceof TextSelection) { action.setEnabled(true); return; } action.setEnabled( targetEditor instanceof ITextEditor); } /** * Returns the TexEditor. */ private TexEditor getTexEditor() { if (targetEditor instanceof TexEditor) { return (TexEditor) targetEditor; } else { throw new RuntimeException("Expecting text editor. Found:"+targetEditor.getClass().getName()); } } /** * This method does actual wrapping... * @throws BadLocationException */ private void doWrap(TexSelections selection) throws BadLocationException { boolean itemFound = false; IDocument document = selection.getDocument(); //selection.selectCompleteLines(); selection.selectParagraph(); String delimiter = document.getLineDelimiter(selection.getStartLineIndex()); //String[] lines = document.get(document.getLineOffset(selection.getStartLineIndex()), selection.getSelLength()).split(delimiter); String[] lines = selection.getCompleteSelection().split(delimiter); if (lines.length == 0) return; int index = 0; StringBuffer buff = new StringBuffer(); boolean fix = true; String selectedLine = ""; String correctIndentation = ""; while (index < lines.length) { if (tools.isLineCommandLine(lines[index]) || tools.isLineCommentLine(lines[index]) || lines[index].trim().length() == 0) { buff.append(lines[index]); if (lines[index].trim().length() == 0 || isList(lines[index])) { fix = true; } index++; if (index < lines.length) buff.append(delimiter); continue; } // a current line is NOT a comment, a command or an empty line -> continue // OO: fix empty lines and lists, but only on the next iteration? if (fix) { correctIndentation = tools.getIndentation(lines[index], tabWidth); fix = false; } StringBuffer temp = new StringBuffer(); boolean end = false; while (index < lines.length && !end) { if (!tools.isLineCommandLine(lines[index]) && !tools.isLineCommentLine(lines[index]) && lines[index].trim().length() > 0) { if (lines[index].trim().startsWith("\\item") && !itemFound) { end = true; itemFound = true; } else { temp.append(lines[index].trim() + " "); itemFound = false; //Respect \\ with a subsequent line break if (lines[index].trim().endsWith("\\\\")) { end = true; } index++; } } else { /* a current line is a command, a comment or en empty -> do not handle the line at this iteration. */ end = true; } } int wsLast = 0; selectedLine = temp.toString().trim(); while (selectedLine.length() > 0) { /* find the last white space before MAX */ wsLast = tools.getLastWSPosition(selectedLine, (lineLength - correctIndentation.length())) + 1; if (wsLast == 0) { /* there was no white space before MAX, try if there is one after */ wsLast = tools.getFirstWSPosition(selectedLine, (lineLength - correctIndentation.length())) + 1; } if (wsLast == 0 || wsLast > selectedLine.length() || selectedLine.length() < (lineLength - correctIndentation.length())){ //there was no white space character at the line wsLast = selectedLine.length(); } buff.append(correctIndentation); buff.append(selectedLine.substring(0,wsLast)); selectedLine = selectedLine.substring(wsLast); selectedLine = tools.trimBegin(selectedLine); if (index < lines.length || selectedLine.length() > 0) buff.append(delimiter); } } // document.replace(selection.getTextSelection().getOffset(), // selection.getSelLength(), // buff.toString()); document.replace(document.getLineOffset(selection.getStartLineIndex()), selection.getSelLength(), buff.toString()); } /** * Checks if the command word is \begin{itemize} or \begin{enumerate} * @param txt string to test * @return <code>true</code> if txt contains \begin{itemize} or * \begin{enumerate}, <code>false</code> otherwise */ private boolean isList(String txt){ boolean rv = false; int bi = -1; if ((bi = txt.indexOf("\\begin")) != -1) { String end = tools.getEndLine(txt.substring(bi), "\\begin"); String env = tools.getEnvironment(end); if (env.equals("itemize") || env.equals("enumerate")|| env.equals("description")) rv = true; } return rv; } // testing private void doWrapB(TexSelections selection) throws BadLocationException { selection.selectParagraph(); String delimiter = tools.getLineDelimiter(selection.getDocument()); IDocument document = selection.getDocument(); // FIXME complete selection just returns the current line //String[] lines = selection.getCompleteSelection().split(delimiter); String[] lines = document.get(document.getLineOffset(selection.getStartLineIndex()), selection.getSelLength()).split(delimiter); if (lines.length == 0) { return; } // FIXME doc.get String endNewlines = tools.getNewlinesAtEnd(document.get(document.getLineOffset(selection.getStartLineIndex()), selection.getSelLength()), delimiter); StringBuffer newText = new StringBuffer(); TextWrapper wrapper = new TextWrapper(tools, delimiter); boolean inEnvironment = false; String environment = ""; String indentation = ""; String newIndentation; for (int index = 0; index < lines.length; index++) { String trimmedLine = lines[index].trim(); if (tools.isLineCommandLine(trimmedLine) || inEnvironment) { // command lines or environments -> don't wrap them newText.append(wrapper.loadWrapped(indentation)); newText.append(lines[index]); newText.append(delimiter); // TODO this will not find a match in case begins and ends // are scattered on one line String[] command = tools.getEnvCommandArg(trimmedLine); if (!environmentsToProcess.contains(command[1])) { if ("begin".equals(command[0]) && !inEnvironment) { inEnvironment = true; environment = command[1]; } else if ("end".equals(command[0]) && inEnvironment && environment.equals(command[0])) { inEnvironment = false; environment = ""; } } } else if (trimmedLine.length() == 0){ // empty lines -> don't wrap them newText.append(wrapper.loadWrapped(indentation)); newText.append(lines[index]); newText.append(delimiter); } else { // normal paragraphs -> buffer and wrap if (tools.isLineCommentLine(trimmedLine)) { newIndentation = tools.getIndentationWithComment(lines[index]); trimmedLine = trimmedLine.substring(1).trim(); // FIXME remove all % signs } else { newIndentation = tools.getIndentation(lines[index], tabWidth); } if (!indentation.equals(newIndentation)) { newText.append(wrapper.loadWrapped(indentation)); } indentation = newIndentation; wrapper.storeUnwrapped(trimmedLine); if (trimmedLine.endsWith("\\\\") || trimmedLine.endsWith(".") || trimmedLine.endsWith(":")) { // On forced breaks, end of sentence or enumerations keep existing breaks newText.append(wrapper.loadWrapped(indentation)); } } } // empty the buffer newText.append(wrapper.loadWrapped(indentation)); // put old delims here newText.delete(newText.length() - delimiter.length(), newText.length()); newText.append(endNewlines); // selection.getDocument().replace(selection.getTextSelection().getOffset(), // selection.getSelLength(), // newText.toString()); document.replace(document.getLineOffset(selection.getStartLineIndex()), selection.getSelLength(), newText.toString()); } private class TextWrapper { private StringBuffer tempBuf = new StringBuffer(); private TexEditorTools tools; private String delimiter; TextWrapper(TexEditorTools tet, String delim) { this.tools = tet; this.delimiter = delim; } private void storeUnwrapped(String s) { tempBuf.append(s); tempBuf.append(" "); } private String loadWrapped(String indentation) { String wrapped = tools.wrapWordString(tempBuf.toString(), indentation, lineLength, delimiter); tempBuf = new StringBuffer(); return wrapped; } } }