package flow.netbeans.markdown.typinghooks; import flow.netbeans.markdown.highlighter.MarkdownTokenId; import flow.netbeans.markdown.options.MarkdownGlobalOptions; import flow.netbeans.markdown.utils.MarkdownDocUtil; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import javax.swing.text.BadLocationException; import javax.swing.text.Document; import javax.swing.text.JTextComponent; import javax.swing.text.StyledDocument; import org.netbeans.api.lexer.Token; import org.netbeans.api.lexer.TokenSequence; import org.openide.text.NbDocument; import org.openide.util.Exceptions; /** * * @author junichi11 */ public class OrderedListReorderer { private final JTextComponent component; private final Document document; private final int caretOffset; public OrderedListReorderer(JTextComponent component, Document document, int caretOffset) { this.component = component; this.document = document; this.caretOffset = caretOffset; } /** * Reorder odered list. * * @param isInserted {@code true} if break line, {@code false} otherwise. */ public void reorder(final boolean isInserted) { reorder(isInserted, -1, 0); } /** * Reorder odered list. * * @param isInserted {@code true} if break line, {@code false} otherwise. * @param removeOffset offset for removing * @param removeLength length for removing */ public void reorder(final boolean isInserted, final int removeOffset, final int removeLength) { final List<Integer> keyList; final Map<Integer, Integer> orderedListMap; if (isReorderOrderedListNumber()) { orderedListMap = getOrderedListMap(isInserted); keyList = new LinkedList<Integer>(orderedListMap.keySet()); // sort Collections.sort(keyList); Collections.reverse(keyList); } else { orderedListMap = Collections.emptyMap(); keyList = Collections.emptyList(); } // reorder NbDocument.runAtomic((StyledDocument) document, new Runnable() { @Override public void run() { for (Integer offset : keyList) { try { Integer number = orderedListMap.get(offset); Integer oldNumber; if (isInserted) { oldNumber = number - 1; } else { oldNumber = number + 1; } int removeLength = oldNumber.toString().length(); document.remove(offset, removeLength); document.insertString(offset, number.toString(), null); } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); } } // remove if (isRemoveOrderedListNumber()) { if (removeOffset >= 0 && removeLength > 0) { try { document.remove(removeOffset, removeLength); } catch (BadLocationException ex) { Exceptions.printStackTrace(ex); } } } } }); } private Map<Integer, Integer> getOrderedListMap(boolean isInserted) { // get token sequence TokenSequence<MarkdownTokenId> ts = MarkdownDocUtil.getTokenSequence(document); if (ts == null) { return Collections.emptyMap(); } // getindent string String indentString = MarkdownDocUtil.getIndentString(document, caretOffset); // reorder ordered list number ts.move(caretOffset); final HashMap<Integer, Integer> orderedListMap = new HashMap<Integer, Integer>(); boolean isFirst = true; int nextNumber = 0; while (ts.moveNext()) { Token<MarkdownTokenId> token = ts.token(); MarkdownTokenId id = token.id(); if (id != MarkdownTokenId.ORDEREDLIST) { continue; } int offset = ts.offset(); String numberText = token.text().toString(); if (numberText.matches("^\n+$")) { // NOI18N break; } if (numberText.startsWith("\n")) { // NOI18N numberText = numberText.replace("\n", ""); // NOI18N offset++; // exists indent boolean isSameIndent = true; int indentStringLength = indentString.length(); int numberTextlength = numberText.length(); for (int i = 0; i < numberTextlength; i++) { char ch = numberText.charAt(i); if (ch == ' ' || ch == '\t') { // NOI18N offset++; if (i < indentStringLength && indentString.charAt(i) != ch) { isSameIndent = false; break; } continue; } break; } if (!isSameIndent) { break; } numberText = numberText.trim(); } int dotIndex = numberText.indexOf("."); // NOI18N if (dotIndex == -1) { continue; } numberText = numberText.substring(0, dotIndex); int currentNumber; try { currentNumber = Integer.parseInt(numberText); } catch (NumberFormatException e) { continue; } if (isInserted) { if (isFirst) { nextNumber = currentNumber; isFirst = false; continue; } if (currentNumber == nextNumber) { nextNumber++; orderedListMap.put(offset, nextNumber); continue; } } else { if (isFirst) { if (currentNumber == 1) { break; } nextNumber = currentNumber; isFirst = false; } if (nextNumber == currentNumber) { nextNumber = currentNumber + 1; orderedListMap.put(offset, --currentNumber); continue; } } // not consecutive number break; } return orderedListMap; } private boolean isReorderOrderedListNumber() { return MarkdownGlobalOptions.getInstance().isReorderOrderedListNumber(); } private boolean isRemoveOrderedListNumber() { return MarkdownGlobalOptions.getInstance().isRemoveOrderedListNumber(); } }