/* * Copyright (c) 2011-2014 Julien Nicoulaud <julien.nicoulaud@gmail.com> * Copyright (c) 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. */ package com.vladsch.idea.multimarkdown.annotator; import com.intellij.codeInsight.intention.impl.BaseIntentionAction; import com.intellij.lang.annotation.AnnotationHolder; import com.intellij.lang.annotation.Annotator; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.PsiReference; import com.vladsch.idea.multimarkdown.MultiMarkdownBundle; import com.vladsch.idea.multimarkdown.MultiMarkdownPlugin; import com.vladsch.idea.multimarkdown.psi.*; import com.vladsch.idea.multimarkdown.psi.impl.MultiMarkdownPsiImplUtil; import com.vladsch.idea.multimarkdown.psi.impl.MultiMarkdownReferenceWikiLinkRef; import com.vladsch.idea.multimarkdown.settings.MultiMarkdownGlobalSettings; import com.vladsch.idea.multimarkdown.util.*; import org.jetbrains.annotations.NotNull; import java.util.List; import static com.vladsch.idea.multimarkdown.annotator.AnnotationState.*; import static com.vladsch.idea.multimarkdown.psi.MultiMarkdownNamedElement.*; import static com.vladsch.idea.multimarkdown.util.GitHubLinkInspector.*; //public class MultiMarkdownAnnotator extends ExternalAnnotator<String, Set<MultiMarkdownAnnotator.HighlightableToken>> { public class MultiMarkdownAnnotator implements Annotator { private static final Logger LOGGER = Logger.getInstance(MultiMarkdownAnnotator.class); @SuppressWarnings("ConstantIfStatement,StatementWithEmptyBody") @Override public void annotate(@NotNull final PsiElement element, @NotNull AnnotationHolder holder) { //noinspection StatementWithEmptyBody AnnotationState state = new AnnotationState(holder); //noinspection ConstantConditions, StatementWithEmptyBody if (false) { } else if (element instanceof MultiMarkdownWikiLink) { } else if (element instanceof MultiMarkdownWikiLinkText) { } else if (element instanceof MultiMarkdownWikiLinkRef) { // TODO: implement inspections and move these annotation to info type inspections checkWikiLinkSwapRefTitle((MultiMarkdownWikiLink) element.getParent(), state); annotateLinkRef((MultiMarkdownLinkRefElement) element, state); } } public void annotateLinkRef(@NotNull MultiMarkdownLinkRefElement element, @NotNull AnnotationState state) { MultiMarkdownPsiImplUtil.LinkRefElementTypes elementTypes = MultiMarkdownPsiImplUtil.getNamedElementTypes(element); if (elementTypes == null || !(element.getParent() instanceof MultiMarkdownLinkElement) || (elementTypes != MultiMarkdownPsiImplUtil.WIKI_LINK_ELEMENT)) return; LinkRef linkRefInfo = MultiMarkdownPsiImplUtil.getLinkRef(elementTypes, element); if (linkRefInfo == null || linkRefInfo.getFilePathWithAnchor().isEmpty() || linkRefInfo instanceof WikiLinkRef && linkRefInfo.getFilePathWithAnchor().trim().isEmpty()) return; GitHubLinkResolver resolver = new GitHubLinkResolver(element); Project project = element.getProject(); GitHubVcsRoot gitHubVcsRoot = resolver.getProjectResolver().getVcsRoot(linkRefInfo.getContainingFile()); if (linkRefInfo.isExternal() && (gitHubVcsRoot == null || !linkRefInfo.getFilePath().toLowerCase().startsWith(gitHubVcsRoot.getBaseUrl().toLowerCase()))) { // not in the same repo, or no repo, we don't handle this, yet return; } //noinspection StatementWithEmptyBody final List<PathInfo> looseTargetRefs = resolver.multiResolve(linkRefInfo, LinkResolver.ANY | LinkResolver.LOOSE_MATCH, null); final List<PathInfo> targetRefs = resolver.multiResolve(linkRefInfo, LinkResolver.ANY, looseTargetRefs); PathInfo resolvedTargetInfo = targetRefs.size() > 0 ? targetRefs.get(0) : null; PathInfo targetInfo = resolvedTargetInfo != null ? resolvedTargetInfo : looseTargetRefs.size() > 0 ? looseTargetRefs.get(0) : null; PsiElement parentElement = element.getParent(); PsiElement textElement = MultiMarkdownPsiImplUtil.findChildByType(parentElement, elementTypes.textType); if (targetInfo == null) { // file creation quick fix handled later state.warningsOnly = false; state.unresolved = true; //if (reason.isA(ID_WIKI_LINK_NOT_IN_WIKI)) { if (linkRefInfo instanceof WikiLinkRef && !linkRefInfo.getContainingFile().isWikiPage()) { state.needTargetList = false; state.createAnnotation(Severity.ERROR, element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.github-only-on-wiki-page")); } } else if (targetInfo instanceof LinkRef && resolvedTargetInfo != null) { } else { assert targetInfo instanceof FileRef; ProjectFileRef targetRef = targetInfo.projectFileRef(project); assert targetRef != null; PsiFile psiFile = targetRef.getPsiFile(); String extensionList = StringUtilKt.splice(linkRefInfo.getLinkExtensions(), ", "); if (resolvedTargetInfo == null) { // file creation quick fix handled later state.warningsOnly = false; state.unresolved = true; } List<InspectionResult> inspectionResults = resolver.inspect(linkRefInfo, targetRef, null); for (InspectionResult reason : inspectionResults) { if (reason.getHandled()) continue; String fixedFilePath = reason.getFixedFilePath(); String fixedLink = reason.getFixedLink(); if (reason.isA(ID_CASE_MISMATCH)) { // it is a weak warning for wiki targets and error for non-wiki targets if (!state.alreadyOfferedIds(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink) || !state.alreadyOfferedId(TYPE_RENAME_FILE_QUICK_FIX, targetRef.getFilePath(), fixedLink)) { state.needTargetList = false; if (targetRef.isWikiPage()) { state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.case-mismatch")); } else { state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.link.case-mismatch")); } if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink, ChangeLinkRefQuickFix.MATCH_CASE_TO_FILE, REASON_FILE_MOVED)); } if (psiFile != null && fixedFilePath != null && state.addingAlreadyOffered(TYPE_RENAME_FILE_QUICK_FIX, targetRef.getFilePath(), fixedFilePath)) { state.annotator.registerFix(new RenameFileQuickFix(psiFile, null, fixedFilePath)); } } } else if (reason.isA(ID_LINK_NEEDS_EXT)) { state.needTargetList = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.link.missing-extension")); if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink)); } } else if (reason.isA(ID_LINK_HAS_BAD_EXT)) { state.needTargetList = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.link.bad-extension")); if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink)); } } else if (reason.isA(ID_LINK_TARGET_NEEDS_EXT)) { state.needTargetList = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.link.target-missing-extension", extensionList)); if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink)); } } else if (reason.isA(ID_LINK_TARGET_HAS_BAD_EXT)) { state.needTargetList = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.link.target-bad-extension", extensionList)); if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink)); } } else if (reason.isA(ID_LINK_TARGETS_WIKI_HAS_EXT)) { state.needTargetList = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.link.target-wikipage-with-extension")); if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink, ChangeLinkRefQuickFix.REMOVE_EXT, RENAME_KEEP_ANCHOR | RENAME_KEEP_TEXT | RENAME_KEEP_TITLE)); } } else if (reason.isA(ID_WIKI_LINK_NOT_IN_WIKI)) { state.needTargetList = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.github-only-on-wiki-page")); } else if (reason.isA(ID_LINK_TARGETS_WIKI_HAS_BAD_EXT)) { state.needTargetList = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.link.target-wikipage-bad-extension")); if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink)); } } else if (reason.isA(ID_TARGET_NOT_WIKI_PAGE_EXT)) { state.needTargetList = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.target-not-wiki-extension", extensionList)); if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink)); } if (psiFile != null && fixedFilePath != null && state.addingAlreadyOffered(TYPE_RENAME_FILE_QUICK_FIX, fixedFilePath, fixedFilePath)) { state.annotator.registerFix(new RenameFileQuickFix(psiFile, null, fixedFilePath)); } } else if (reason.isA(ID_NOT_UNDER_WIKI_HOME) || reason.isA(ID_NOT_UNDER_SOURCE_WIKI_HOME)) { // can offer to move the file, just add the logic state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.unreachable-page-reference-not-in-wiki-home")); InspectionResult.handled(inspectionResults, ID_NOT_UNDER_WIKI_HOME, ID_NOT_UNDER_SOURCE_WIKI_HOME); } else if (reason.isA(ID_WIKI_LINK_HAS_SLASH) || reason.isA(ID_WIKI_LINK_HAS_SUBDIR)) { state.canCreateFile = false; // can offer to move the file, just add the logic state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.link.linkref-has-slash")); InspectionResult.handled(inspectionResults, ID_WIKI_LINK_HAS_SLASH, ID_WIKI_LINK_HAS_SUBDIR); if (fixedLink != null && reason.isA(ID_WIKI_LINK_HAS_SLASH)) { if (state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink, ChangeLinkRefQuickFix.REMOVE_SLASHES, RENAME_KEEP_ANCHOR)); } } if (fixedLink != null && reason.isA(ID_WIKI_LINK_HAS_SUBDIR)) { if (state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, "remove-subdir" + fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink, ChangeLinkRefQuickFix.REMOVE_SUBDIR, RENAME_KEEP_ANCHOR)); } } } else if (reason.isA(ID_NOT_UNDER_SAME_REPO)) { // can offer to move the file, just add the logic state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.link.unreachable-link-reference-not-in-same-repo")); } else if (reason.isA(ID_TARGET_NOT_UNDER_VCS)) { // can offer to move the file, just add the logic state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.not-vcs-target")); } else if (reason.isA(ID_TARGET_HAS_SPACES)) { state.needTargetList = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.file-spaces")); if (fixedFilePath != null && linkRefInfo.canRenameFileTo(fixedFilePath)) { if (psiFile != null && state.addingAlreadyOffered(TYPE_RENAME_FILE_QUICK_FIX, fixedFilePath, fixedFilePath)) { state.annotator.registerFix(new RenameFileQuickFix(psiFile, null, fixedFilePath)); } } } else if (reason.isA(ID_IMAGE_TARGET_NOT_IN_RAW)) { state.needTargetList = false; state.canCreateFile = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.imagelink.link-to-raw")); if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink, ChangeLinkRefQuickFix.CHANGE_TO_RAW)); } } else if (reason.isA(ID_WIKI_LINK_HAS_DASHES)) { state.needTargetList = false; state.canCreateFile = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.link-dashes")); if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink, ChangeLinkRefQuickFix.REMOVE_DASHES)); } } else if (textElement != null && reason.isA(ID_WIKI_LINK_HAS_REDUNDANT_TEXT)) { state.needTargetList = false; state.canCreateFile = false; state.createAnnotation(reason.getSeverity(), textElement.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.redundant-page-title")); if (state.addingAlreadyOffered(TYPE_DELETE_WIKI_PAGE_TITLE_QUICK_FIX)) { assert parentElement instanceof MultiMarkdownWikiLink; state.annotator.registerFix(new DeleteWikiPageTitleQuickFix((MultiMarkdownWikiLink) parentElement)); } } else if (textElement != null && reason.isA(ID_WIKI_LINK_HAS_ADDRESS_TEXT_SWAPPED)) { if (!state.alreadyOfferedTypes(TYPE_SWAP_WIKI_PAGE_REF_TITLE_QUICK_FIX, TYPE_DELETE_WIKI_PAGE_REF_QUICK_FIX)) { assert parentElement instanceof MultiMarkdownWikiLink; state.createAnnotation(reason.getSeverity(), parentElement.getTextRange(), MultiMarkdownGlobalSettings.getInstance().githubWikiLinks.getValue() ? MultiMarkdownBundle.message("annotation.wikilink.ref-title-github") : MultiMarkdownBundle.message("annotation.wikilink.ref-title-swapped")); if (state.addingAlreadyOffered(TYPE_SWAP_WIKI_PAGE_REF_TITLE_QUICK_FIX)) state.annotator.registerFix(new SwapWikiPageRefTitleQuickFix((MultiMarkdownWikiLink) parentElement)); if (state.addingAlreadyOffered(TYPE_DELETE_WIKI_PAGE_REF_QUICK_FIX)) state.annotator.registerFix(new DeleteWikiPageRefQuickFix((MultiMarkdownWikiLink) parentElement)); } } else if (textElement != null && reason.isA(ID_WIKI_LINK_TEXT_MATCHES_ANOTHER_TARGET)) { state.needTargetList = false; state.canCreateFile = false; if (state.alreadyOfferedTypes(TYPE_DELETE_WIKI_PAGE_TITLE_QUICK_FIX, TYPE_DELETE_WIKI_PAGE_REF_QUICK_FIX, TYPE_SWAP_WIKI_PAGE_REF_TITLE_QUICK_FIX)) { assert parentElement instanceof MultiMarkdownWikiLink; state.createAnnotation(reason.getSeverity(), textElement.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.swap-ref-title")); if (state.addingAlreadyOffered(TYPE_DELETE_WIKI_PAGE_TITLE_QUICK_FIX)) state.annotator.registerFix(new DeleteWikiPageTitleQuickFix((MultiMarkdownWikiLink) parentElement)); if (state.addingAlreadyOffered(TYPE_DELETE_WIKI_PAGE_REF_QUICK_FIX)) state.annotator.registerFix(new DeleteWikiPageRefQuickFix((MultiMarkdownWikiLink) parentElement)); if (state.addingAlreadyOffered(TYPE_SWAP_WIKI_PAGE_REF_TITLE_QUICK_FIX)) state.annotator.registerFix(new SwapWikiPageRefTitleQuickFix((MultiMarkdownWikiLink) parentElement)); } } else if (textElement != null && reason.isA(ID_WIKI_LINK_TEXT_MATCHES_SELF_REF)) { state.needTargetList = false; state.canCreateFile = false; if (state.addingAlreadyOffered(TYPE_SWAP_WIKI_PAGE_REF_TITLE_QUICK_FIX)) { assert parentElement instanceof MultiMarkdownWikiLink; state.createInfoAnnotation(textElement.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.swap-ref-title")); state.annotator.registerFix(new SwapWikiPageRefTitleQuickFix((MultiMarkdownWikiLink) parentElement)); } } else if (reason.isA(ID_WIKI_LINK_HAS_ONLY_ANCHOR)) { if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.createAnnotation(reason.getSeverity(), parentElement.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.has-only-anchor")); state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink, ChangeLinkRefQuickFix.ADD_PAGE_REF, RENAME_KEEP_TEXT)); } assert parentElement instanceof MultiMarkdownWikiLink; } else if (reason.isA(ID_TARGET_NAME_HAS_ANCHOR) || reason.isA(ID_TARGET_PATH_HAS_ANCHOR)) { state.needTargetList = false; state.createAnnotation(reason.getSeverity(), element.getTextRange(), MultiMarkdownBundle.message("annotation.link.file-anchor")); InspectionResult.handled(inspectionResults, ID_TARGET_NAME_HAS_ANCHOR, ID_TARGET_PATH_HAS_ANCHOR); if (reason.isA(ID_TARGET_NAME_HAS_ANCHOR)) { if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink, ChangeLinkRefQuickFix.URL_ENCODE_ANCHOR, RENAME_KEEP_TEXT | RENAME_KEEP_RENAMED_TEXT | RENAME_KEEP_TITLE)); } if (fixedFilePath != null && targetRef.canRenameFileTo(fixedFilePath)) { if (state.addingAlreadyOffered(TYPE_RENAME_FILE_AND_RE_TARGET_QUICK_FIX, targetRef.getFilePath(), fixedFilePath)) { state.annotator.registerFix(new RenameFileAndReTargetQuickFix(psiFile, fixedFilePath, element, RENAME_KEEP_PATH | RENAME_KEEP_TEXT | RENAME_KEEP_RENAMED_TEXT | RENAME_KEEP_TITLE)); } } } if (reason.isA(ID_TARGET_PATH_HAS_ANCHOR)) { annotateTargetPathHasAnchor(element, state); if (fixedLink != null && state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, fixedLink)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, fixedLink, ChangeLinkRefQuickFix.URL_ENCODE_ANCHOR, RENAME_KEEP_TEXT | RENAME_KEEP_RENAMED_TEXT | RENAME_KEEP_TITLE)); } } } } // if this is a valid wikiLink see if more than one match if (state.warningsOnly) { if (targetRefs.size() > 1 && targetRef.isWikiPage()) { state.canCreateFile = false; state.createWarningAnnotation(element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.multiple-targets-match")); //state.annotator.setHighlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL); for (PathInfo otherMatchInfo : targetRefs.subList(1, targetRefs.size())) { if (otherMatchInfo instanceof FileRef) { String linkRef = ((FileRef) otherMatchInfo).getFilePathFromWikiDir(); String newName = linkRef.replace('/', '-'); if (!linkRef.contains("/")) { // it is in wiki home, prefix with home- newName = "home-" + newName; } if (!linkRef.equals(newName) && !otherMatchInfo.getFileName().equals(newName) && otherMatchInfo.canRenameFileTo(newName)) { ProjectFileRef projectFileRef = otherMatchInfo.projectFileRef(project); PsiFile psiTargetFile = projectFileRef == null ? null : projectFileRef.getPsiFile(); if (psiTargetFile != null && state.addingAlreadyOffered(TYPE_RENAME_FILE_QUICK_FIX, otherMatchInfo.getFilePath(), newName)) { state.annotator.registerFix(new RenameFileQuickFix(psiTargetFile, linkRef, newName, RenameFileQuickFix.RENAME_CONFLICTING_TARGET)); } } } } } } } if (!state.warningsOnly || state.unresolved) { registerCreateFileFix(element.getFileName(), element, state); // get all loose ones if (state.needTargetList) { //LinkRef emptyLinkRef = element instanceof WikiLinkRef ? new WikiLinkRef(containingFile, "", null, null) : (linkRefInfo instanceof ImageLinkRef ? new ImageLinkRef(containingFile, "", null, null) : (new LinkRef(containingFile, "", null, null))); //List<PathInfo> availableTargetRefs = resolver.multiResolve(emptyLinkRef, LinkResolver.PREFER_LOCAL | LinkResolver.ACCEPT_URI | LinkResolver.LOOSE_MATCH, null); List<PathInfo> availableTargetRefs = looseTargetRefs; int hadItems = state.getAlreadyOfferedSize(TYPE_CHANGE_LINK_REF_QUICK_FIX); for (PathInfo targetRef : availableTargetRefs) { String linkRefText = resolver.linkAddress(linkRefInfo, targetRef, null, null, null); if (state.addingAlreadyOffered(TYPE_CHANGE_LINK_REF_QUICK_FIX, linkRefText)) { state.annotator.registerFix(new ChangeLinkRefQuickFix(element, linkRefText, 0, RENAME_KEEP_TITLE | RENAME_KEEP_ANCHOR)); // TODO: make max quick fix wikilink targets a config item if (state.getAlreadyOfferedSize(TYPE_CHANGE_LINK_REF_QUICK_FIX) >= hadItems + 15) break; } } } } } protected void checkWikiLinkSwapRefTitle(@NotNull MultiMarkdownWikiLink element, @NotNull AnnotationState state) { // see if need to swap link ref and link text MultiMarkdownPsiImplUtil.LinkRefElementTypes elementTypes = MultiMarkdownPsiImplUtil.getNamedElementTypes(element); if (elementTypes == null || elementTypes != MultiMarkdownPsiImplUtil.WIKI_LINK_ELEMENT) return; PsiNamedElement textElement = (PsiNamedElement) MultiMarkdownPsiImplUtil.findChildByType(element, elementTypes.textType); MultiMarkdownLinkRefElement linkRefElement = (MultiMarkdownLinkRefElement) MultiMarkdownPsiImplUtil.findChildByType(element, elementTypes.linkRefType); PsiReference reference = linkRefElement != null ? linkRefElement.getReference() : null; if (reference != null) { String wikiPageTextName = textElement != null ? textElement.getName() : null; if (wikiPageTextName != null) { // see if the link text resolves to a page if (wikiPageTextName.equals(linkRefElement.getNameWithAnchor())) { // can get rid off the text if (state.addingAlreadyOffered(TYPE_DELETE_WIKI_PAGE_TITLE_QUICK_FIX)) { state.createWeakWarningAnnotation(textElement.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.redundant-page-title")); state.annotator.registerFix(new DeleteWikiPageTitleQuickFix(element)); } } else { Project project = element.getProject(); ProjectFileRef containingFile = new ProjectFileRef(element.getContainingFile()); GitHubLinkResolver resolver = new GitHubLinkResolver(element.getContainingFile()); LinkRef linkRefInfo = LinkRef.parseWikiLinkRef(containingFile, wikiPageTextName, null); List<PathInfo> targetRefs = resolver.multiResolve(linkRefInfo, LinkResolver.ANY, null); PathInfo targetInfo = targetRefs.size() > 0 ? targetRefs.get(0) : null; if (targetRefs.size() > 0 && targetInfo != null) { // have a resolve target if (((MultiMarkdownReferenceWikiLinkRef) reference).isResolveRefMissing()) { if (!state.alreadyOfferedTypes(TYPE_SWAP_WIKI_PAGE_REF_TITLE_QUICK_FIX, TYPE_DELETE_WIKI_PAGE_REF_QUICK_FIX)) { state.createErrorAnnotation(element.getTextRange(), MultiMarkdownGlobalSettings.getInstance().githubWikiLinks.getValue() ? MultiMarkdownBundle.message("annotation.wikilink.ref-title-github") : MultiMarkdownBundle.message("annotation.wikilink.ref-title-swapped")); if (state.addingAlreadyOffered(TYPE_SWAP_WIKI_PAGE_REF_TITLE_QUICK_FIX)) state.annotator.registerFix(new SwapWikiPageRefTitleQuickFix(element)); if (state.addingAlreadyOffered(TYPE_DELETE_WIKI_PAGE_REF_QUICK_FIX)) state.annotator.registerFix(new DeleteWikiPageRefQuickFix(element)); } } else if (WikiLinkRef.fileAsLink(targetInfo.getFileNameNoExt()).equals(wikiPageTextName)) { if (state.alreadyOfferedTypes(TYPE_DELETE_WIKI_PAGE_TITLE_QUICK_FIX, TYPE_DELETE_WIKI_PAGE_REF_QUICK_FIX, TYPE_SWAP_WIKI_PAGE_REF_TITLE_QUICK_FIX)) { state.createInfoAnnotation(textElement.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.swap-ref-title")); if (state.addingAlreadyOffered(TYPE_DELETE_WIKI_PAGE_TITLE_QUICK_FIX)) state.annotator.registerFix(new DeleteWikiPageTitleQuickFix(element)); if (state.addingAlreadyOffered(TYPE_DELETE_WIKI_PAGE_REF_QUICK_FIX)) state.annotator.registerFix(new DeleteWikiPageRefQuickFix(element)); if (state.addingAlreadyOffered(TYPE_SWAP_WIKI_PAGE_REF_TITLE_QUICK_FIX)) state.annotator.registerFix(new SwapWikiPageRefTitleQuickFix(element)); } } // TODO: when we can validate existence of anchors add it to the condition below } else if (wikiPageTextName.startsWith("#")) { if (state.addingAlreadyOffered(TYPE_SWAP_WIKI_PAGE_REF_TITLE_QUICK_FIX)) { state.createInfoAnnotation(textElement.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.swap-ref-title")); state.annotator.registerFix(new SwapWikiPageRefTitleQuickFix(element)); } } } } } } protected void annotateChangeLinkType(@NotNull PsiElement element, @NotNull AnnotationState state, Severity type, @NotNull BaseIntentionAction quickFix, @NotNull String messageKey) { if ((type == null || type != Severity.INFO)) { if (type != null) state.createAnnotation(type, element.getTextRange(), MultiMarkdownBundle.message(messageKey)); state.annotator.registerFix(quickFix); } } protected void registerCreateFileFix(@NotNull String fileName, @NotNull MultiMarkdownNamedElement element, @NotNull AnnotationState state) { if (state.canCreateFile && !fileName.isEmpty()) { state.createErrorAnnotation(element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.unresolved-link-reference")); PathInfo thisFile = new FileRef(element.getContainingFile()); PathInfo newFile = PathInfo.appendParts(thisFile.getPath(), fileName); if (newFile.canCreateFile()) { state.annotator.registerFix(new CreateFileQuickFix(newFile.getFilePath(), fileName)); } } } protected void annotateTargetPathHasAnchor(MultiMarkdownNamedElement element, AnnotationState state) { state.needTargetList = false; state.canCreateFile = false; state.createErrorAnnotation(element.getTextRange(), MultiMarkdownBundle.message("annotation.wikilink.path-anchor")); // TODO: create quick fix to remove anchors from all directories in the path //if (canRenameFile(referenceLink.getVirtualFile(), reasons.targetNameHasAnchorFixed())) { // state.annotator.registerFix(new RenameFileQuickFix(referenceLink.getVirtualFile(), reasons.targetNameHasAnchorFixed())); //} } }