package winterwell.markdown.editors; import java.util.List; import org.eclipse.core.commands.ExecutionEvent; import org.eclipse.core.commands.ExecutionException; import org.eclipse.core.commands.IHandler; import org.eclipse.core.commands.IHandlerListener; import org.eclipse.jface.action.Action; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.IDocument; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.ITextSelection; import org.eclipse.jface.text.Region; import org.eclipse.jface.text.source.ISourceViewer; import winterwell.markdown.pagemodel.MarkdownFormatter; import winterwell.markdown.pagemodel.MarkdownPage; import winterwell.markdown.pagemodel.MarkdownPage.KLineType; import winterwell.utils.containers.IntRange; /** * TODO An action for formatting text (via hard wrapping, i.e. inserting returns). * * * @author daniel */ public class FormatAction extends Action implements IHandler { public FormatAction() { super("&Format paragraph"); setActionDefinitionId("winterwell.markdown.formatParagraphCommand"); setToolTipText("Format the paragraph under the caret by inserting/removing line-breaks"); } @Override public void run() { try { MarkdownEditor ed = (MarkdownEditor) ActionBarContributor.getActiveEditor(); if (ed == null) return; // The active editor is not a markdown editor. int cols = ed.getPrintColumns(); // Do we have a selection? ITextSelection s = (ITextSelection) ed.getSelectionProvider().getSelection(); if (s != null && s.getLength() > 0) { formatSelectedRegion(ed, s, cols); return; } // Where is the caret? ISourceViewer viewer = ed.getViewer(); int caretOffset = viewer.getTextWidget().getCaretOffset(); int lineNum = ed.getDocument().getLineOfOffset(caretOffset); // Get a paragraph region MarkdownPage page = ed.getMarkdownPage(); IRegion pRegion = getParagraph(page, lineNum, ed.getDocument()); if (pRegion==null) { // Not in a paragraph - so give up // TODO tell the user why we've given up return; } String paragraph = ed.getDocument().get(pRegion.getOffset(), pRegion.getLength()); // Format String formatted = MarkdownFormatter.format(paragraph, cols); if (formatted.equals(paragraph)) return; // No change! // Replace the unformatted region with the new formatted one ed.getDocument().replace(pRegion.getOffset(), pRegion.getLength(), formatted); // Done } catch (Exception ex) { System.out.println(ex); } } private void formatSelectedRegion(MarkdownEditor ed, ITextSelection s, int cols) throws BadLocationException { int start = s.getStartLine(); int end = s.getEndLine(); IDocument doc = ed.getDocument(); int soff = doc.getLineOffset(start); int eoff = lineEndOffset(end, doc); IntRange editedRegion = new IntRange(soff, eoff); MarkdownPage page = ed.getMarkdownPage(); StringBuilder sb = new StringBuilder(s.getLength()); for(int i=start; i<=end; i++) { IRegion para = getParagraph(page, i, ed.getDocument()); if (para==null) { sb.append(page.getText().get(i)); continue; } String paragraph = ed.getDocument().get(para.getOffset(), para.getLength()); // int lines = StrUtils.splitLines(paragraph).length; String formatted = MarkdownFormatter.format(paragraph, cols); // append formatted and move forward sb.append(formatted); CharSequence le = lineEnd(i, doc); sb.append(le); int pEnd = doc.getLineOfOffset(para.getOffset()+para.getLength()); i = pEnd; // Adjust edited region? IntRange pr = new IntRange(para.getOffset(), para.getOffset()+para.getLength()+le.length()); editedRegion = new IntRange(Math.min(pr.low, editedRegion.low), Math.max(pr.high, editedRegion.high)); } // Replace the unformatted region with the new formatted one String old = doc.get(editedRegion.low, editedRegion.size()); String newText = sb.toString(); if (old.equals(newText)) return; ed.getDocument().replace(editedRegion.low, editedRegion.size(), newText); } private CharSequence lineEnd(int line, IDocument doc) throws BadLocationException { int eoff = doc.getLineOffset(line) + doc.getLineInformation(line).getLength(); char c = doc.getChar(eoff); if (c=='\r' && doc.getLength() > eoff+1 && doc.getChar(eoff+1) =='\n') return "\r\n"; return ""+c; } private int lineEndOffset(int end, IDocument doc) throws BadLocationException { int eoff = doc.getLineOffset(end) + doc.getLineInformation(end).getLength(); // Include line end char c = doc.getChar(eoff); if (c=='\r' && doc.getLength() > eoff+1 && doc.getChar(eoff+1) =='\n') eoff += 2; else eoff += 1; return eoff; } /** * * @param page * @param lineNum * @param doc * @return region of paragraph containing this line, or null * @throws BadLocationException */ private IRegion getParagraph(MarkdownPage page, int lineNum, IDocument doc) throws BadLocationException { // Get doc info List lines = page.getText(); List lineInfo = page.getLineTypes(); // Check we are in a paragraph or list KLineType pType = lineInfo.get(lineNum); switch(pType) { case NORMAL: break; default: // Not in a paragraph, so we cannot format. return null; } // Work out the paragraph // Beginning int start; for(start=lineNum; start>-1; start--) { if (lineInfo.get(start) != pType) { start++; break; } } // End int end; for(end=lineNum; end