/*
* 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.psi.impl;
import com.intellij.lang.ASTNode;
import com.intellij.navigation.ItemPresentation;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.tree.IElementType;
import com.vladsch.idea.multimarkdown.MultiMarkdownIcons;
import com.vladsch.idea.multimarkdown.psi.*;
import com.vladsch.idea.multimarkdown.util.FileRef;
import com.vladsch.idea.multimarkdown.util.LinkRef;
import com.vladsch.idea.multimarkdown.util.PathInfo;
import com.vladsch.idea.multimarkdown.util.WikiLinkRef;
import org.apache.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import static com.vladsch.idea.multimarkdown.psi.MultiMarkdownNamedElement.*;
import static com.vladsch.idea.multimarkdown.psi.MultiMarkdownTypes.*;
public class MultiMarkdownPsiImplUtil {
private static final Logger logger = Logger.getLogger(MultiMarkdownPsiImplUtil.class);
final protected static int EXTENSION_STRIP = 1;
final protected static int EXTENSION_KEEP_OLD = 2;
final protected static int EXTENSION_USE_NEW_IF_OLD_HAS = 3;
final protected static int EXTENSION_USE_NEW = 4;
final public static LinkRefElementTypes WIKI_LINK_ELEMENT = new LinkRefElementTypes(WIKI_LINK, WIKI_LINK_REF, WIKI_LINK_TEXT, WIKI_LINK_REF_ANCHOR, EXTENSION_STRIP);
public static class LinkRefElementTypes {
@NotNull public final IElementType parentType;
@NotNull public final IElementType linkRefType;
@NotNull public final IElementType textType;
@Nullable public final IElementType anchorType;
@Nullable public final IElementType titleType;
public final int extensionFlags;
public LinkRefElementTypes(@NotNull IElementType parentType, @NotNull IElementType linkRefType, @NotNull IElementType textType, @Nullable IElementType anchorType, int extensionFlags) {
this(parentType, linkRefType, textType, anchorType, null, extensionFlags);
}
public LinkRefElementTypes(@NotNull IElementType parentType, @NotNull IElementType linkRefType, @NotNull IElementType textType, @Nullable IElementType anchorType, @Nullable IElementType titleType, int extensionFlags) {
this.parentType = parentType;
this.linkRefType = linkRefType;
this.textType = textType;
this.anchorType = anchorType;
this.titleType = titleType;
this.extensionFlags = extensionFlags;
}
}
public static LinkRefElementTypes getNamedElementTypes(@Nullable PsiElement element) {
if (element instanceof MultiMarkdownWikiLink
|| element instanceof MultiMarkdownWikiLinkRef
|| element instanceof MultiMarkdownWikiLinkText
|| element instanceof MultiMarkdownWikiLinkAnchor
) return WIKI_LINK_ELEMENT;
return null;
}
@NotNull
public static String getElementText(IElementType parentType, @Nullable PsiElement element, @Nullable IElementType elementType, @Nullable String prefix, @Nullable String suffix) {
PsiElement parent = element == null || elementType == null ? null : (element.getNode().getElementType() == parentType ? element : element.getParent());
ASTNode astNode = element == null || elementType == null ? null : parent.getNode().findChildByType(elementType);
if (astNode == null) return "";
if (suffix != null && prefix != null) return prefix + astNode.getText() + suffix;
if (prefix != null) return prefix + astNode.getText();
if (suffix != null) return astNode.getText() + suffix;
return astNode.getText();
}
@Nullable
public static LinkRef getLinkRef(@Nullable PsiElement element) {
LinkRefElementTypes elementTypes = getNamedElementTypes(element);
if (elementTypes != null) {
return getLinkRef(elementTypes, element);
}
return null;
}
@Nullable
public static LinkRef getLinkRef(@NotNull LinkRefElementTypes elementTypes, @NotNull PsiElement element) {
if (!element.isValid()) return null;
if (elementTypes.parentType == WIKI_LINK) {
return LinkRef.parseWikiLinkRef(new FileRef(element), getLinkRefTextWithAnchor(element), null);
} else if (elementTypes.parentType == IMAGE || elementTypes.parentType == REFERENCE_IMAGE) {
return LinkRef.parseImageLinkRef(new FileRef(element), getLinkRefTextWithAnchor(element), null);
} else {
return LinkRef.parseLinkRef(new FileRef(element), getLinkRefTextWithAnchor(element), null);
}
}
@NotNull
public static String getLinkRefText(@Nullable PsiElement element) {
LinkRefElementTypes elementTypes = getNamedElementTypes(element);
return elementTypes == null ? "" : getElementText(elementTypes.parentType, element, elementTypes.linkRefType, null, null);
}
@NotNull
public static String getLinkRefTextWithAnchor(@Nullable PsiElement element) {
LinkRefElementTypes elementTypes = getNamedElementTypes(element);
return elementTypes == null ? "" : getElementText(elementTypes.parentType, element, elementTypes.linkRefType, null, null)
+ getElementText(elementTypes.parentType, element, elementTypes.anchorType, "#", null);
}
@NotNull
public static String getLinkAnchor(@Nullable PsiElement element) {
LinkRefElementTypes elementTypes = getNamedElementTypes(element);
return elementTypes == null ? "" : getElementText(elementTypes.parentType, element, elementTypes.anchorType, null, null);
}
@NotNull
public static String getLinkText(@Nullable PsiElement element) {
LinkRefElementTypes elementTypes = getNamedElementTypes(element);
return elementTypes == null ? "" : getElementText(elementTypes.parentType, element, elementTypes.textType, null, null);
}
@NotNull
public static String getLinkTitle(@Nullable PsiElement element) {
LinkRefElementTypes elementTypes = getNamedElementTypes(element);
return elementTypes == null ? "" : getElementText(elementTypes.parentType, element, elementTypes.titleType, null, null);
}
@NotNull
public static MultiMarkdownNamedElement setName(@NotNull MultiMarkdownNamedElement element, @NotNull String newName, int renameFlags) {
if (!element.isValid()) return element;
LinkRefElementTypes elementTypes = getNamedElementTypes(element);
if (elementTypes == null) return element;
ASTNode pageRefNode = element.getNode();
if (pageRefNode == null) return element;
LinkRef newNameInfo = LinkRef.parseLinkRef(new FileRef(element.getContainingFile().getVirtualFile().getPath()), newName, null);
PsiElement parent = element.getParent();
String linkRef = getElementText(elementTypes.parentType, parent, elementTypes.linkRefType, null, null);
String title = null;
String text = null;
String anchor = newNameInfo.getAnchor();
IElementType elementType = element.getNode().getElementType();
if (elementType == elementTypes.linkRefType) {
if (elementTypes.extensionFlags != 0 && (renameFlags & RENAME_ELEMENT_HANDLES_EXT) != 0) {
PathInfo linkRefInfo = new PathInfo(linkRef);
switch (elementTypes.extensionFlags) {
case EXTENSION_KEEP_OLD:
linkRef = newNameInfo.getFilePathNoExt() + linkRefInfo.getExtWithDot();
break;
case EXTENSION_STRIP:
linkRef = newNameInfo.getFilePathNoExt();
break;
case EXTENSION_USE_NEW_IF_OLD_HAS:
linkRef = linkRefInfo.getHasExt() ? newNameInfo.getFilePath() : newNameInfo.getFilePathNoExt();
break;
case EXTENSION_USE_NEW:
default:
linkRef = newNameInfo.getFilePath();
break;
}
} else {
linkRef = newName;
}
if ((renameFlags & RENAME_KEEP_PATH) != 0 && element.getText().contains("/")) {
// keep the old path
String path = new PathInfo(element.getText()).getPath();
String name = new PathInfo(newName).getFileName();
linkRef = path + name;
}
if ((renameFlags & RENAME_KEEP_NAME) != 0) {
// keep the old name
String path = new PathInfo(newName).getPath();
String name = new PathInfo(element.getText()).getFileName();
linkRef = path + name;
}
// preserve anchor
if ((renameFlags & RENAME_KEEP_ANCHOR) != 0 && elementTypes.anchorType != null) {
anchor = getElementText(elementTypes.parentType, parent, elementTypes.anchorType, "#", null);
}
// preserve text
if ((renameFlags & RENAME_KEEP_TEXT) != 0) {
text = getElementText(elementTypes.parentType, parent, elementTypes.textType, null, null);
}
// preserve title
if ((renameFlags & RENAME_KEEP_TITLE) != 0) {
title = getElementText(elementTypes.parentType, parent, elementTypes.titleType, null, null);
}
} else if (elementType == elementTypes.anchorType) {
linkRef += newName.isEmpty() ? newName : "#" + newName;
} else if (elementType == elementTypes.textType) {
text = newName;
} else if (elementType == elementTypes.titleType) {
title = newName;
} else {
// no such beast
logger.info("MultiMarkdownPsiImplUtil.setName called for unhandled element " + element);
return element;
}
PsiElement newLink = null;
if (elementTypes.parentType == WIKI_LINK) {
newLink = MultiMarkdownElementFactory.createWikiLink(element.getProject(), linkRef, text);
}
if (newLink != null) {
if (elementType == elementTypes.linkRefType) {
//element.getParent().replace(newLink);
ASTNode parentAST = parent.getNode();
ASTNode firstChildNode = parentAST.getFirstChildNode();
parentAST.addChildren(newLink.getFirstChild().getNode(), null, firstChildNode);
parentAST.removeRange(firstChildNode, null);
MultiMarkdownNamedElement newElement = (MultiMarkdownNamedElement) findChildByType(parent, elementType);
if (newElement != null) return newElement;
} else {
MultiMarkdownNamedElement newElement = (MultiMarkdownNamedElement) findChildByType(newLink, elementType);
if (newElement != null) {
element.replace(newElement);
return newElement;
}
}
}
return element;
}
@NotNull
public static MultiMarkdownWikiLink deleteWikiLinkTitle(MultiMarkdownWikiLink element) {
if (!element.isValid()) return element;
ASTNode pageTitleNode = element.getNode().findChildByType(WIKI_LINK_TEXT);
ASTNode pageRefNode = element.getNode().findChildByType(WIKI_LINK_REF);
if (pageRefNode != null && pageTitleNode != null) {
MultiMarkdownWikiLink wikiLink = MultiMarkdownElementFactory.createWikiLink(element.getProject(), pageRefNode.getText());
element.replace(wikiLink);
}
return element;
}
@NotNull
public static MultiMarkdownWikiLink deleteWikiLinkRef(MultiMarkdownWikiLink element) {
if (!element.isValid()) return element;
ASTNode pageTitleNode = element.getNode().findChildByType(WIKI_LINK_TEXT);
ASTNode pageRefNode = element.getNode().findChildByType(WIKI_LINK_REF);
if (pageRefNode != null && pageTitleNode != null) {
MultiMarkdownWikiLink wikiLink = MultiMarkdownElementFactory.createWikiLink(element.getProject(), pageTitleNode.getText());
element.replace(wikiLink);
}
return element;
}
@NotNull
public static MultiMarkdownWikiLink swapWikiLinkRefTitle(MultiMarkdownWikiLink element) {
if (!element.isValid()) return element;
ASTNode pageTitleNode = element.getNode().findChildByType(WIKI_LINK_TEXT);
ASTNode pageRefNode = element.getNode().findChildByType(WIKI_LINK_REF);
if (pageRefNode != null && pageTitleNode != null) {
String anchorText = getElementText(WIKI_LINK, element, WIKI_LINK_REF_ANCHOR, "#", null);
MultiMarkdownWikiLink wikiLink = MultiMarkdownElementFactory.createWikiLink(element.getProject(), pageTitleNode.getText(), pageRefNode.getText() + anchorText);
element.replace(wikiLink);
}
return element;
}
@Nullable
public static PsiElement findChildByType(@NotNull PsiElement parentElement, @NotNull IElementType childType) {
if (!parentElement.isValid()) return null;
for (PsiElement child : parentElement.getChildren()) {
if (child.getNode().getElementType() == childType) {
return child;
}
}
return null;
}
@Nullable
public static Document getElementDocument(@NotNull PsiElement element) {
return FileDocumentManager.getInstance().getDocument(element.getContainingFile().getVirtualFile());
}
public static ItemPresentation getPresentation(final MultiMarkdownNamedElement element) {
return new ItemPresentation() {
@Nullable
@Override
public String getPresentableText() {
return element.getDisplayName();
}
@Nullable
@Override
public String getLocationString() {
PsiFile containingFile = element.getContainingFile();
return containingFile == null ? null : containingFile.getName();
}
@Nullable
@Override
public Icon getIcon(boolean unused) {
return MultiMarkdownIcons.FILE;
}
};
}
}