Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use safe delete in deletion quick fixes #1787

Merged
merged 7 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ intellij {
pluginName = "TeXiFy-IDEA"

// indices plugin doesn't work in tests
setPlugins("tanvd.grazi", "java", "com.firsttimeinforever.intellij.pdf.viewer.intellij-pdf-viewer:0.10.1") // , "com.jetbrains.hackathon.indices.viewer:1.12")
setPlugins("tanvd.grazi", "com.firsttimeinforever.intellij.pdf.viewer.intellij-pdf-viewer:0.10.2") // , "com.jetbrains.hackathon.indices.viewer:1.12")

// Use the since build number from plugin.xml
updateSinceUntilBuild = false
Expand All @@ -138,6 +138,7 @@ intellij {
// Docs: https://github.com/JetBrains/gradle-intellij-plugin#intellij-platform-properties
// All snapshot versions: https://www.jetbrains.com/intellij-repository/snapshots/
version = "2020.3.1"
// version = "PY-2020.3.3"
// version = "PY-203.5419.8-EAP-SNAPSHOT"
// type = "PY"

Expand Down
2 changes: 1 addition & 1 deletion gen/nl/hannahsten/texifyidea/psi/BibtexEntry.java

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions gen/nl/hannahsten/texifyidea/psi/BibtexId.java

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions gen/nl/hannahsten/texifyidea/psi/LatexParameterText.java

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions gen/nl/hannahsten/texifyidea/psi/impl/BibtexIdImpl.java

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion src/nl/hannahsten/texifyidea/LatexParserDefinition.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class LatexParserDefinition : ParserDefinition {
val FILE: IStubFileElementType<*> = object : IStubFileElementType<LatexFileStub>(
Language.findInstance(LatexLanguage::class.java)
) {
override fun getStubVersion(): Int = 20
override fun getStubVersion(): Int = 21
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/nl/hannahsten/texifyidea/grammar/Bibtex.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ type ::= TYPE_TOKEN
preamble ::= (quoted_string (CONCATENATE quoted_string)*) | NUMBER | IDENTIFIER

id ::= comment* IDENTIFIER comment* {
methods=[getNameIdentifier getName setName]
methods=[getNameIdentifier getName setName delete]
}

tag ::= comment* key comment* ASSIGNMENT comment* content comment* {
Expand Down
2 changes: 1 addition & 1 deletion src/nl/hannahsten/texifyidea/grammar/Latex.bnf
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ keyval_content ::= parameter_text | parameter_group
// This assumes that parameter text which is a reference, appears directly under param_content
// Commands is here instead of in required_param_content because it can be part of reference text for example to a file
parameter_text ::= (commands | NORMAL_TEXT_WORD | STAR | AMPERSAND | OPEN_ANGLE_BRACKET | CLOSE_ANGLE_BRACKET | QUOTATION_MARK | PIPE | EXCLAMATION_MARK)+ {
methods=[getReferences getReference getNameIdentifier getName setName]
methods=[getReferences getReference getNameIdentifier getName setName delete]
}

group ::= OPEN_BRACE content CLOSE_BRACE { pin=1 }
Expand Down
55 changes: 55 additions & 0 deletions src/nl/hannahsten/texifyidea/inspections/SafeDeleteFix.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
*
* Licensed 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 nl.hannahsten.texifyidea.inspections

import com.intellij.codeInsight.FileModificationService
import com.intellij.codeInspection.LocalQuickFixAndIntentionActionOnPsiElement
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.refactoring.safeDelete.SafeDeleteHandler

/**
* Source: com.intellij.codeInsight.daemon.impl.quickfix.SafeDeleteFix
*/
open class SafeDeleteFix(element: PsiElement) : LocalQuickFixAndIntentionActionOnPsiElement(element) {

override fun getText(): String {
val startElement = startElement
return "Safe delete " + startElement.text
}

override fun getFamilyName(): String {
return "Safe delete"
}

override fun invoke(
project: Project,
file: PsiFile,
editor: Editor?,
startElement: PsiElement,
endElement: PsiElement
) {
if (!FileModificationService.getInstance().prepareFileForWrite(file)) return
val elements = arrayOf(startElement)
SafeDeleteHandler.invoke(project, elements, true)
}

override fun startInWriteAction(): Boolean {
return false
}
}
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
package nl.hannahsten.texifyidea.inspections.bibtex

import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.PsiWhiteSpace
import com.intellij.psi.SmartPsiElementPointer
import com.intellij.psi.search.GlobalSearchScope
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.suggested.createSmartPointer
import nl.hannahsten.texifyidea.index.BibtexEntryIndex
import nl.hannahsten.texifyidea.inspections.SafeDeleteFix
import nl.hannahsten.texifyidea.inspections.InsightGroup
import nl.hannahsten.texifyidea.inspections.TexifyInspectionBase
import nl.hannahsten.texifyidea.psi.BibtexId
import nl.hannahsten.texifyidea.util.childrenOfType
import nl.hannahsten.texifyidea.util.previousSiblingOfType
import org.jetbrains.annotations.NotNull

class BibtexUnusedEntryInspection : TexifyInspectionBase() {

Expand All @@ -39,18 +36,8 @@ class BibtexUnusedEntryInspection : TexifyInspectionBase() {
}
.toList()

class RemoveBibtexEntryFix(private val id: SmartPsiElementPointer<BibtexId>) : LocalQuickFix {
class RemoveBibtexEntryFix(private val id: SmartPsiElementPointer<BibtexId>) : SafeDeleteFix(id.element as @NotNull PsiElement) {

override fun getFamilyName(): String = "Remove BibTeX entry ${id.element?.text}"

override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val text = id.element?.text ?: return

val searchScope = GlobalSearchScope.fileScope(descriptor.psiElement.containingFile)
BibtexEntryIndex.getEntryByName(text, project, searchScope).forEach {
it.previousSiblingOfType(PsiWhiteSpace::class)?.let { w -> w.parent.node.removeChild(w.node) }
it.parent.node.removeChild(it.node)
}
}
override fun getText(): String = "Safe delete ${id.element?.text}"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,20 @@ package nl.hannahsten.texifyidea.inspections.latex
import com.intellij.codeInspection.InspectionManager
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.SmartPsiElementPointer
import com.intellij.refactoring.suggested.createSmartPointer
import nl.hannahsten.texifyidea.inspections.InsightGroup
import nl.hannahsten.texifyidea.inspections.SafeDeleteFix
import nl.hannahsten.texifyidea.inspections.TexifyInspectionBase
import nl.hannahsten.texifyidea.lang.magic.MagicCommentScope
import nl.hannahsten.texifyidea.psi.LatexCommands
import nl.hannahsten.texifyidea.psi.LatexParameterText
import nl.hannahsten.texifyidea.util.*
import nl.hannahsten.texifyidea.util.files.commandsInFileSet
import nl.hannahsten.texifyidea.util.magic.CommandMagic
import nl.hannahsten.texifyidea.util.magic.EnvironmentMagic
import org.jetbrains.annotations.NotNull
import java.util.*

open class LatexFigureNotReferencedInspection : TexifyInspectionBase() {
Expand All @@ -22,7 +27,7 @@ open class LatexFigureNotReferencedInspection : TexifyInspectionBase() {

override val outerSuppressionScopes = EnumSet.of(MagicCommentScope.GROUP)!!

override fun getDisplayName(): String = "Figure Not Referenced"
override fun getDisplayName(): String = "Figure not referenced"

override fun inspectFile(file: PsiFile, manager: InspectionManager, isOntheFly: Boolean): MutableList<ProblemDescriptor> {
val figureLabels = getFigureLabels(file)
Expand All @@ -31,7 +36,7 @@ open class LatexFigureNotReferencedInspection : TexifyInspectionBase() {

val descriptors = descriptorList()
for (label in figureLabels.values) {
descriptors.add(createDescriptor(manager, label, isOntheFly))
descriptors.add(createDescriptor(manager, label, isOntheFly) ?: continue)
}

return descriptors
Expand All @@ -49,30 +54,38 @@ open class LatexFigureNotReferencedInspection : TexifyInspectionBase() {
}
}

private fun createDescriptor(manager: InspectionManager, label: LatexCommands, isOntheFly: Boolean): ProblemDescriptor =
manager.createProblemDescriptor(
label,
"Figure is not referenced",
isOntheFly,
emptyArray(),
ProblemHighlightType.GENERIC_ERROR_OR_WARNING
)
private fun createDescriptor(manager: InspectionManager, label: LatexCommands, isOntheFly: Boolean): ProblemDescriptor? =
label.firstChildOfType(LatexParameterText::class)?.let {
manager.createProblemDescriptor(
it,
"Figure is not referenced",
RemoveFigureFix(it.createSmartPointer()),
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
isOntheFly
)
}

/**
* Find all commands in the file that label a figure.
*/
private fun getFigureLabels(file: PsiFile): MutableMap<String?, LatexCommands> =
file.findLabelingCommandsInFileAsSequence()
.filter(this::isFigureLabel)
.filter(LatexCommands::isFigureLabel)
.associateBy(LatexCommands::labelName)
.toMutableMap()

private fun isFigureLabel(label: LatexCommands): Boolean =
label.inDirectEnvironment(EnvironmentMagic.figures)
class RemoveFigureFix(label: SmartPsiElementPointer<LatexParameterText>) : SafeDeleteFix(label.element as @NotNull PsiElement) {

override fun getText(): String {
return "Safe delete figure environment"
}
}
}

private val LatexCommands.labelName: String?
get() = requiredParameter(0)

private val LatexCommands.referencedLabelNames: List<String>
get() = requiredParameter(0)?.split(",") ?: emptyList()

fun dummy() = Unit
12 changes: 12 additions & 0 deletions src/nl/hannahsten/texifyidea/psi/BibtexIdUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ package nl.hannahsten.texifyidea.psi

import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFileFactory
import com.intellij.psi.search.GlobalSearchScope
import nl.hannahsten.texifyidea.BibtexLanguage
import nl.hannahsten.texifyidea.index.BibtexEntryIndex
import nl.hannahsten.texifyidea.util.firstParentOfType
import nl.hannahsten.texifyidea.util.remove

fun getNameIdentifier(element: BibtexId): PsiElement {
return element
Expand All @@ -21,4 +24,13 @@ fun setName(element: BibtexId, name: String): PsiElement {

fun getName(element: BibtexId): String {
return element.text
}

fun delete(element: BibtexId) {
val text = element.text ?: return

val searchScope = GlobalSearchScope.fileScope(element.containingFile)
BibtexEntryIndex.getEntryByName(text, element.project, searchScope).forEach {
it.remove()
}
}
4 changes: 4 additions & 0 deletions src/nl/hannahsten/texifyidea/psi/BibtexPsiImplUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ public static String getName(@NotNull BibtexId element) {
return BibtexIdUtilKt.getName(element);
}

public static void delete(@NotNull BibtexId element) {
BibtexIdUtilKt.delete(element);
}

/*
* BibtexTag
*/
Expand Down
6 changes: 2 additions & 4 deletions src/nl/hannahsten/texifyidea/psi/LatexCommandsImplUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,9 @@ import nl.hannahsten.texifyidea.lang.commands.RequiredFileArgument
import nl.hannahsten.texifyidea.reference.CommandDefinitionReference
import nl.hannahsten.texifyidea.reference.InputFileReference
import nl.hannahsten.texifyidea.reference.LatexLabelReference
import nl.hannahsten.texifyidea.util.getLabelReferenceCommands
import nl.hannahsten.texifyidea.util.*
import nl.hannahsten.texifyidea.util.magic.CommandMagic
import nl.hannahsten.texifyidea.util.magic.PatternMagic
import nl.hannahsten.texifyidea.util.requiredParameters
import nl.hannahsten.texifyidea.util.shrink
import java.util.*
import java.util.regex.Pattern

Expand Down Expand Up @@ -146,7 +144,7 @@ fun stripGroup(text: String): String {
}

/**
* Generates a map of parameter names and values (assuming they are in the form []name=]value) for all optional parameters, comma-separated and separate optional parameters are treated equally.
* Generates a map of parameter names and values (assuming they are in the form name=value) for all optional parameters, comma-separated and separate optional parameters are treated equally.
* If a value does not have a name, the value will be the key in the hashmap mapping to the empty string.
*/
// Explicitly use a LinkedHashMap to preserve iteration order
Expand Down
11 changes: 11 additions & 0 deletions src/nl/hannahsten/texifyidea/psi/LatexParameterTextUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,14 @@ val LatexParameterText.command: PsiElement?
get() {
return this.firstParentOfType(LatexCommands::class)?.firstChild
}

fun delete(element: LatexParameterText) {
val cmd = element.parentOfType(LatexCommands::class) ?: return
if (cmd.isFigureLabel()) {
// Look for the NoMathContent that is around the environment, because that is the PsiElement that has the
// whitespace and other normal text as siblings.
cmd.parentOfType(LatexEnvironment::class)
?.parentOfType(LatexNoMathContent::class)
?.remove()
}
}
4 changes: 4 additions & 0 deletions src/nl/hannahsten/texifyidea/psi/LatexPsiImplUtil.java
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,10 @@ public static String getName(@NotNull LatexParameterText element) {
return LatexParameterTextUtilKt.getName(element);
}


public static void delete(@NotNull LatexParameterText element) {
LatexParameterTextUtilKt.delete(element);
}
/*
* LatexParameter
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package nl.hannahsten.texifyidea.refactoring

import com.intellij.lang.refactoring.RefactoringSupportProvider
import com.intellij.psi.PsiElement
import nl.hannahsten.texifyidea.psi.BibtexId

/**
* This class is used to enable inline refactoring.
Expand All @@ -12,4 +13,8 @@ class BibtexRefactoringSupportProvider : RefactoringSupportProvider() {
// Inline refactoring is not enabled for BibtexId because it will not handle the comma separator correctly when the bibtex id is renamed directly (not from usages).
return false
}

override fun isSafeDeleteAvailable(element: PsiElement): Boolean {
return element is BibtexId
}
}
Loading