]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/winterwell.markdown/src/winterwell/markdown/editors/FormatAction.java
migrated to svn revision 33108
[simantics/platform.git] / bundles / winterwell.markdown / src / winterwell / markdown / editors / FormatAction.java
1 package winterwell.markdown.editors;
2
3 import java.util.List;
4
5 import org.eclipse.core.commands.ExecutionEvent;
6 import org.eclipse.core.commands.ExecutionException;
7 import org.eclipse.core.commands.IHandler;
8 import org.eclipse.core.commands.IHandlerListener;
9 import org.eclipse.jface.action.Action;
10 import org.eclipse.jface.text.BadLocationException;
11 import org.eclipse.jface.text.IDocument;
12 import org.eclipse.jface.text.IRegion;
13 import org.eclipse.jface.text.ITextSelection;
14 import org.eclipse.jface.text.Region;
15 import org.eclipse.jface.text.source.ISourceViewer;
16
17 import winterwell.markdown.pagemodel.MarkdownFormatter;
18 import winterwell.markdown.pagemodel.MarkdownPage;
19 import winterwell.markdown.pagemodel.MarkdownPage.KLineType;
20 import winterwell.utils.containers.IntRange;
21
22 /**
23  * TODO An action for formatting text (via hard wrapping, i.e. inserting returns).
24  * 
25  *
26  * @author daniel
27  */
28 public class FormatAction extends Action implements IHandler {
29
30         public FormatAction() {
31                 super("&Format paragraph");
32                 setActionDefinitionId("winterwell.markdown.formatParagraphCommand");
33                 setToolTipText("Format the paragraph under the caret by inserting/removing line-breaks");
34         }
35         
36         @Override
37         public void run() {
38                 try {
39                         MarkdownEditor ed = (MarkdownEditor) ActionBarContributor.getActiveEditor();
40                         if (ed == null) return; // The active editor is not a markdown editor.
41                         int cols = ed.getPrintColumns();
42                         // Do we have a selection?
43                         ITextSelection s = (ITextSelection) ed.getSelectionProvider().getSelection();
44                         if (s != null && s.getLength() > 0) {
45                                 formatSelectedRegion(ed, s, cols);
46                                 return;
47                         }
48                         // Where is the caret?
49                         ISourceViewer viewer = ed.getViewer();
50                         int caretOffset = viewer.getTextWidget().getCaretOffset();
51                         int lineNum = ed.getDocument().getLineOfOffset(caretOffset);
52                         // Get a paragraph region
53                         MarkdownPage page = ed.getMarkdownPage();
54                         IRegion pRegion = getParagraph(page, lineNum, ed.getDocument());
55                         if (pRegion==null) {
56                                 // Not in a paragraph - so give up
57                                  // TODO tell the user why we've given up
58                                 return;
59                         }
60                         String paragraph = ed.getDocument().get(pRegion.getOffset(), pRegion.getLength());
61                         // Format
62                         String formatted = MarkdownFormatter.format(paragraph, cols);
63                         if (formatted.equals(paragraph)) return; // No change!
64                         // Replace the unformatted region with the new formatted one
65                         ed.getDocument().replace(pRegion.getOffset(), pRegion.getLength(), formatted);
66                         // Done
67                 } catch (Exception ex) {
68                         System.out.println(ex);
69                 }
70         }
71
72         private void formatSelectedRegion(MarkdownEditor ed, ITextSelection s, int cols) 
73         throws BadLocationException {
74                 int start = s.getStartLine();
75                 int end = s.getEndLine();
76                 IDocument doc = ed.getDocument();
77                 int soff = doc.getLineOffset(start);
78                 int eoff = lineEndOffset(end, doc);             
79                 IntRange editedRegion = new IntRange(soff, eoff);               
80                 MarkdownPage page = ed.getMarkdownPage();
81                 StringBuilder sb = new StringBuilder(s.getLength());
82                 for(int i=start; i<=end; i++) {
83                         IRegion para = getParagraph(page, i, ed.getDocument());
84                         if (para==null) {
85                                 sb.append(page.getText().get(i));
86                                 continue;
87                         }
88                         String paragraph = ed.getDocument().get(para.getOffset(), para.getLength());
89 //                      int lines = StrUtils.splitLines(paragraph).length;
90                         String formatted = MarkdownFormatter.format(paragraph, cols);
91                         // append formatted and move forward
92                         sb.append(formatted);
93                         CharSequence le = lineEnd(i, doc);
94                         sb.append(le);
95                         int pEnd = doc.getLineOfOffset(para.getOffset()+para.getLength());
96                         i = pEnd;
97                         // Adjust edited region?
98                         IntRange pr = new IntRange(para.getOffset(), 
99                                         para.getOffset()+para.getLength()+le.length());                 
100                         editedRegion = new IntRange(Math.min(pr.low, editedRegion.low), 
101                                                                                 Math.max(pr.high, editedRegion.high));                  
102                 }               
103                 // Replace the unformatted region with the new formatted one
104                 String old = doc.get(editedRegion.low, editedRegion.size());
105                 String newText = sb.toString();
106                 if (old.equals(newText)) return;
107                 ed.getDocument().replace(editedRegion.low, editedRegion.size(), newText);               
108         }
109
110         private CharSequence lineEnd(int line, IDocument doc) throws BadLocationException {
111                 int eoff = doc.getLineOffset(line) + doc.getLineInformation(line).getLength();          
112                 char c = doc.getChar(eoff);
113                 if (c=='\r' && doc.getLength() > eoff+1 
114                                 && doc.getChar(eoff+1) =='\n') return "\r\n";
115                 return ""+c;
116         }
117
118         private int lineEndOffset(int end, IDocument doc) 
119         throws BadLocationException {
120                 int eoff = doc.getLineOffset(end) + doc.getLineInformation(end).getLength();
121                 // Include line end
122                 char c = doc.getChar(eoff);
123                 if (c=='\r' && doc.getLength() > eoff+1 
124                                 && doc.getChar(eoff+1) =='\n') eoff += 2;
125                 else eoff += 1;
126                 return eoff;
127         }
128
129         /**
130          * 
131          * @param page
132          * @param lineNum
133          * @param doc
134          * @return region of paragraph containing this line, or null
135          * @throws BadLocationException
136          */
137         private IRegion getParagraph(MarkdownPage page, int lineNum, IDocument doc) 
138         throws BadLocationException {
139                 // Get doc info
140                 List<String> lines = page.getText();
141                 List<KLineType> lineInfo = page.getLineTypes();
142                 // Check we are in a paragraph or list
143                 KLineType pType = lineInfo.get(lineNum);
144                 switch(pType) {
145                 case NORMAL: break;
146                 default: // Not in a paragraph, so we cannot format.  
147                         return null;
148                 }
149                 // Work out the paragraph
150                 // Beginning
151                 int start;
152                 for(start=lineNum; start>-1; start--) {
153                         if (lineInfo.get(start) != pType) {
154                                 start++;
155                                 break;
156                         }
157                 }
158                 // End
159                 int end;
160                 for(end=lineNum; end<lines.size(); end++) {
161                         if (lineInfo.get(end) != pType) {
162                                 end--;
163                                 break;
164                         }
165                 }
166                 // Get offset info
167                 int sOff = doc.getLineOffset(start);
168                 IRegion endLine = doc.getLineInformation(end);  // exclude final line end
169                 int eOff = endLine.getOffset()+endLine.getLength();
170                 return new Region(sOff, eOff-sOff);
171         }
172
173         public void addHandlerListener(IHandlerListener handlerListener) {
174                 // Ignore
175         }
176
177         public void dispose() {
178                 // Ignore               
179         }
180
181         public Object execute(ExecutionEvent event) throws ExecutionException {
182                 run();
183                 return null;
184         }
185
186         public void removeHandlerListener(IHandlerListener handlerListener) {
187                 // Ignore               
188         }
189         
190 }