/*
* 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.fileEditor.FileDocumentManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.vladsch.idea.multimarkdown.util.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.pegdown.LinkRenderer;
import org.pegdown.ast.*;
import static org.pegdown.FastEncoder.obfuscate;
public class MultiMarkdownLinkRenderer extends LinkRenderer {
final public static int GITHUB_WIKI_LINK_FORMAT = 1;
final public static int VALIDATE_LINKS = 2;
@NotNull final protected String missingTargetClass;
final protected int options;
final protected GitHubLinkResolver resolver;
final protected String localOnlyTargetClass;
public MultiMarkdownLinkRenderer() {
this(0);
}
public MultiMarkdownLinkRenderer(int options) {
this(null, null, null, null, options);
}
enum LinkType {
Wiki, Image, Link
}
public MultiMarkdownLinkRenderer(@Nullable Project project, @Nullable Document document, @Nullable String missingTargetClass, @Nullable String localOnlyTargetClass, int options) {
super();
this.missingTargetClass = missingTargetClass == null ? "absent" : missingTargetClass;
this.localOnlyTargetClass = localOnlyTargetClass == null ? "local-only" : localOnlyTargetClass;
if ((options & VALIDATE_LINKS) != 0) {
VirtualFile file = document == null ? null : FileDocumentManager.getInstance().getFile(document);
this.resolver = file == null || project == null ? null : new GitHubLinkResolver(file, project);
if (this.resolver == null) options &= ~VALIDATE_LINKS;
} else {
this.resolver = null;
}
this.options = options;
}
@Nullable
public String getLinkTarget(@NotNull String url, LinkType linkType, @NotNull boolean[] localOnly) {
// return null if does not resolved, but only if validating links
if ((options & VALIDATE_LINKS) != 0 && (linkType == LinkType.Wiki)) {
assert resolver != null;
LinkRef targetRef = LinkRef.parseWikiLinkRef(resolver.getContainingFile(), url, null);
PathInfo resolvedTarget = resolver.resolve(targetRef, LinkResolver.ONLY_REMOTE | LinkResolver.ONLY_URI, null);
if (resolvedTarget != null) {
assert resolvedTarget.isURI() && resolvedTarget instanceof LinkRef && (!resolvedTarget.isLocal() || ((LinkRef) resolvedTarget).isResolved()) : "Expected URI only target, got " + resolvedTarget;
FileRef fileRef = resolvedTarget.isLocal() ? ((LinkRef) resolvedTarget).getTargetRef() : null;
localOnly[0] = fileRef instanceof ProjectFileRef && !((ProjectFileRef) fileRef).isUnderVcs();
return ((LinkRef) resolvedTarget).getFilePathWithAnchor();
}
return null;
}
return url;
}
public Rendering renderLink(LinkType linkType, String url, String title, String text) {
boolean[] localOnly = new boolean[]{false};
String href = getLinkTarget(url, linkType, localOnly);
Rendering rendering = new Rendering(href == null ? url : href, text);
if (href == null) rendering.withAttribute("class", missingTargetClass);
else if (localOnly[0]) {
rendering.withAttribute("class", localOnlyTargetClass);
}
return rendering;
}
@Override
public Rendering render(AnchorLinkNode node) {
return super.render(node);
}
@Override
public Rendering render(AutoLinkNode node) {
return super.render(node);
}
@Override
public Rendering render(ExpLinkNode node, String text) {
return renderLink(LinkType.Link, node.getUrl(), node.getTitle(), text);
}
@Override
public Rendering render(ExpImageNode node, String text) {
return renderLink(LinkType.Image, node.getUrl(), node.getTitle(), text);
}
@Override
public Rendering render(RefLinkNode node, String url, String title, String text) {
return renderLink(LinkType.Link, url, title, text);
}
@Override
public Rendering render(RefImageNode node, String url, String title, String alt) {
return renderLink(LinkType.Image, url, title, alt);
}
@Override
public Rendering render(MailLinkNode node) {
String obfuscated = obfuscate(node.getText());
return (new Rendering(obfuscate("mailto:") + obfuscated, obfuscated)).withAttribute("class", obfuscate("mail-link"));
}
@Override
public Rendering render(WikiLinkNode node) {
int pos;
String text = node.getText();
String url = text;
if ((options & GITHUB_WIKI_LINK_FORMAT) != 0) {
// vsch: #202 handle WikiLinks alternative format à la GitHub [[text|page]]
if ((pos = text.indexOf("|")) >= 0) {
url = text.substring(pos + 1);
text = text.substring(0, pos);
}
} else {
// vsch: #182 handle WikiLinks alternative format [[page|text]]
if ((pos = text.indexOf("|")) >= 0) {
url = text.substring(0, pos);
text = text.substring(pos + 1);
}
}
// vsch: #200 WikiLinks can have anchor # refs, these are now handled by link resolution engine
return renderLink(LinkType.Wiki, url, "", text);
}
}