/*
* Copyright (c) 2015-2015 Vladimir Schneider <vladimir.schneider@gmail.com>
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* This file is based on the IntelliJ SimplePlugin tutorial
*
*/
package com.vladsch.idea.multimarkdown.editor;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.project.Project;
import org.pegdown.LinkRenderer;
import org.pegdown.ToHtmlSerializer;
import org.pegdown.VerbatimSerializer;
import org.pegdown.ast.*;
import org.pegdown.plugins.ToHtmlSerializerPlugin;
import java.util.List;
import java.util.Map;
public class MultiMarkdownToHtmlSerializer extends ToHtmlSerializer {
final public static int NO_WIKI_LINKS = 1;
protected final Project project;
protected final Document document;
protected int flags = 0;
public int getFlags() {
return flags;
}
public void setFlags(int flags) {
this.flags = flags;
}
public boolean isSet(int flags) {
return (this.flags & flags) != 0;
}
public void setFlag(int flags) {
this.flags |= flags;
}
public void clearFlag(int flags) {
this.flags &= ~flags;
}
public MultiMarkdownToHtmlSerializer(Project project, Document document, LinkRenderer linkRenderer) {
super(linkRenderer);
this.project = project;
this.document = document;
}
public MultiMarkdownToHtmlSerializer(LinkRenderer linkRenderer) {
super(linkRenderer);
this.project = null;
this.document = null;
}
public MultiMarkdownToHtmlSerializer(LinkRenderer linkRenderer, List<ToHtmlSerializerPlugin> plugins) {
super(linkRenderer, plugins);
this.project = null;
this.document = null;
}
public MultiMarkdownToHtmlSerializer(LinkRenderer linkRenderer, Map<String, VerbatimSerializer> verbatimSerializers) {
super(linkRenderer, verbatimSerializers);
project = null;
document = null;
}
public MultiMarkdownToHtmlSerializer(LinkRenderer linkRenderer, Map<String, VerbatimSerializer> verbatimSerializers, List<ToHtmlSerializerPlugin> plugins) {
super(linkRenderer, verbatimSerializers, plugins);
project = null;
document = null;
}
public void visit(HeaderNode node) {
printBreakBeforeTag(node, "h" + node.getLevel());
}
@Override
public void visit(AnchorLinkNode node) {
printAnchorLink(linkRenderer.render(node));
}
public void visit(ListItemNode node) {
if (node instanceof TaskListNode) {
// vsch: #185 handle GitHub style task list items, these are a bit messy because the <input> checkbox needs to be
// included inside the optional <p></p> first grand-child of the list item, first child is always RootNode
// because the list item text is recursively parsed.
Node firstChild = node.getChildren().size() == 0 ? null : (node.getChildren().get(0).getChildren().size() == 0 ? null : node.getChildren().get(0).getChildren().get(0));
if (firstChild != null) {
boolean firstIsPara = firstChild instanceof ParaNode;
int indent = node.getChildren().size() > 1 ? 2 : 0;
boolean startWasNewLine = printer.endsWithNewLine();
printer.println().print("<li class=\"task-list-item\">").indent(indent);
if (firstIsPara) {
printer.println().print("<p>");
printer.print("<span class=\"taskitem\">" + (((TaskListNode) node).isDone() ? "X" : "O") + "</span>");
visitChildren((SuperNode) firstChild);
// render the other children, the p tag is taken care of here
visitChildrenSkipFirst(node);
printer.print("</p>");
} else {
printer.print("<span class=\"taskitem\">" + (((TaskListNode) node).isDone() ? "X" : "O") + "</span>");
visitChildren(node);
}
printer.indent(-indent).printchkln(indent != 0).print("</li>")
.printchkln(startWasNewLine);
}
} else {
printConditionallyIndentedTag(node, "li");
}
}
@Override
protected void visitChildren(SuperNode node) {
visitChildrenSkipFirst(node, 0);
}
@Override
protected void visitChildrenSkipFirst(SuperNode node) {
visitChildrenSkipFirst(node, 1);
}
public void visit(WikiLinkNode node) {
if (isSet(NO_WIKI_LINKS)) {
printer.printEncoded("[[" + node.getText() + "]]");
} else {
printLink(node, linkRenderer.render(node));
}
}
protected void visitChildrenSkipFirst(SuperNode node, int skipFirst) {
// here we combine multiple segments of TextNode and SpecialText into a single TextNode
int startIndex = 0, endIndex = 0;
String combinedText = null;
Node lastTextNode = null;
for (Node child : node.getChildren()) {
if (skipFirst > 0) {
skipFirst--;
continue;
}
boolean processed = false;
// TODO: we don't really need to do this here, it is needed for the parser but not for HTML Serialization
if (child.getClass() == TextNode.class || (child.getClass() == SpecialTextNode.class && child.getEndIndex() - child.getStartIndex() <= 1)) {
if (combinedText != null) {
// combine range and text, if possible
if (endIndex == child.getStartIndex()) {
// combine
endIndex = child.getEndIndex();
combinedText += ((TextNode) child).getText();
lastTextNode = null;
processed = true;
} else {
// insert collected up to now
if (lastTextNode != null) {
lastTextNode.accept(this);
lastTextNode = null;
} else {
TextNode newNode = new TextNode(combinedText);
newNode.setStartIndex(startIndex);
newNode.setEndIndex(endIndex);
newNode.accept(this);
}
combinedText = null;
}
}
if (combinedText == null) {
startIndex = child.getStartIndex();
endIndex = child.getEndIndex();
combinedText = ((TextNode) child).getText();
lastTextNode = child;
processed = true;
}
}
if (!processed) {
if (combinedText != null) {
// process accumulated to date
if (lastTextNode != null) {
lastTextNode.accept(this);
} else {
TextNode newNode = new TextNode(combinedText);
newNode.setStartIndex(startIndex);
newNode.setEndIndex(endIndex);
newNode.accept(this);
}
combinedText = null;
lastTextNode = null;
}
child.accept(this);
}
}
if (combinedText != null) {
// process the last combined
if (lastTextNode != null) {
lastTextNode.accept(this);
} else {
TextNode newNode = new TextNode(combinedText);
newNode.setStartIndex(startIndex);
newNode.setEndIndex(endIndex);
newNode.accept(this);
}
}
}
protected void printAnchorLink(LinkRenderer.Rendering rendering) {
printer.print('<').print('a');
printAttribute("href", rendering.href);
for (LinkRenderer.Attribute attr : rendering.attributes) {
printAttribute(attr.name, attr.value);
}
printer.print('>').print("<span class=\"octicon octicon-link\"></span>").print(rendering.text).print("</a>");
}
}