--- /dev/null
+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<String> lines = page.getText();
+ List<KLineType> 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<lines.size(); end++) {
+ if (lineInfo.get(end) != pType) {
+ end--;
+ break;
+ }
+ }
+ // Get offset info
+ int sOff = doc.getLineOffset(start);
+ IRegion endLine = doc.getLineInformation(end); // exclude final line end
+ int eOff = endLine.getOffset()+endLine.getLength();
+ return new Region(sOff, eOff-sOff);
+ }
+
+ public void addHandlerListener(IHandlerListener handlerListener) {
+ // Ignore
+ }
+
+ public void dispose() {
+ // Ignore
+ }
+
+ public Object execute(ExecutionEvent event) throws ExecutionException {
+ run();
+ return null;
+ }
+
+ public void removeHandlerListener(IHandlerListener handlerListener) {
+ // Ignore
+ }
+
+}