[Parameter Name Hints] per-language blacklist, each language implementation can now provide it's own default list

This commit is contained in:
Yaroslav Lepenkin
2016-10-18 17:12:36 +03:00
parent 0d4a9a65b8
commit 83fea03184
9 changed files with 222 additions and 176 deletions
@@ -15,6 +15,8 @@
*/
package com.intellij.codeInsight.hints
import com.intellij.lang.Language
import com.intellij.lang.java.JavaLanguage
import com.intellij.psi.PsiCallExpression
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
@@ -46,5 +48,33 @@ class JavaInlayParameterHintsProvider : InlayParameterHintsProvider {
return MethodInfo(fullMethodName, paramNames)
}
override val language: Language = JavaLanguage.INSTANCE
override val defaultBlackList = setOf(
"(begin*, end*)",
"(start*, end*)",
"(first*, last*)",
"(first*, second*)",
"(from*, to*)",
"(min*, max*)",
"(key, value)",
"(format, arg*)",
"(message)",
"(message, error)",
"*.set(*,*)",
"*.print(*)",
"*.println(*)",
"*.get(*)",
"*.append(*)",
"*.charAt(*)",
"*.indexOf(*)",
"*.contains(*)",
"*.startsWith(*)",
"*.endsWith(*)",
"java.lang.Math.*",
"*.equals(*)",
"*.equal(*)"
)
}
@@ -657,6 +657,8 @@ class Test {
void test() {
String name = getTestName(true);
System.out.println("");
"xxx".equals(name);
Math.min(10, 20);
}
String getTestName(boolean lowerCase) {}
@@ -664,6 +666,8 @@ class Test {
""")
onLineStartingWith("String name").assertInlays("lowerCase->true")
onLineStartingWith("\"xxx\"").assertNoInlays()
onLineStartingWith("Math").assertNoInlays()
}
private fun getInlays(): List<Inlay> {
@@ -15,10 +15,10 @@
*/
package com.intellij.codeInsight.hints
import com.intellij.lang.Language
import com.intellij.lang.LanguageExtension
import com.intellij.psi.PsiElement
object InlayParameterHintsExtension: LanguageExtension<InlayParameterHintsProvider>("com.intellij.parameterHintsProvider")
interface InlayParameterHintsProvider {
@@ -31,4 +31,9 @@ interface InlayParameterHintsProvider {
return null
}
}
val language: Language
val defaultBlackList: Set<String>
}
@@ -75,7 +75,7 @@ public class ParameterHintsPassFactory extends AbstractProjectComponent implemen
List<Matcher> matchers = ParameterNameHintsSettings
.getInstance()
.getIgnorePatternSet()
.getIgnorePatternSet(provider)
.stream()
.map((item) -> MatcherConstructor.INSTANCE.createMatcher(item))
.collect(Collectors.toList());
@@ -48,7 +48,9 @@ class ShowParameterHintsSettings : AnAction() {
override fun actionPerformed(e: AnActionEvent) {
val project = CommonDataKeys.PROJECT.getData(e.dataContext) ?: return
val dialog = ParameterNameHintsConfigurable(project)
val file = CommonDataKeys.PSI_FILE.getData(e.dataContext) ?: return
val hintExtension = InlayParameterHintsExtension.forLanguage(file.language) ?: return
val dialog = ParameterNameHintsConfigurable(project, hintExtension)
dialog.show()
}
}
@@ -144,7 +146,7 @@ private fun addMethodAtCaretToBlackList(editor: Editor, file: PsiFile) {
val info = hintsProvider.getMethodInfo(method) ?: return
val pattern = info.fullyQualifiedName + '(' + info.paramNames.joinToString(",") + ')'
ParameterNameHintsSettings.getInstance().addIgnorePattern(pattern)
ParameterNameHintsSettings.getInstance().addIgnorePattern(file.language, pattern)
refreshAllOpenEditors()
}
@@ -15,6 +15,7 @@
*/
package com.intellij.codeInsight.hints.settings;
import com.intellij.codeInsight.hints.InlayParameterHintsProvider;
import com.intellij.codeInsight.hints.filtering.MatcherConstructor;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.editor.event.DocumentAdapter;
@@ -37,10 +38,12 @@ import java.util.stream.Collectors;
public class ParameterNameHintsConfigurable extends DialogWrapper {
private final Project myProject;
public ParameterNameHintsConfigurable(@NotNull Project project) {
private final InlayParameterHintsProvider myHintsProvider;
public ParameterNameHintsConfigurable(@NotNull Project project, @NotNull InlayParameterHintsProvider provider) {
super(project);
myProject = project;
myHintsProvider = provider;
setTitle("Configure Parameter Name Hints Blacklist");
init();
}
@@ -67,7 +70,7 @@ public class ParameterNameHintsConfigurable extends DialogWrapper {
.filter((e) -> !e.trim().isEmpty())
.collect(Collectors.toSet());
ParameterNameHintsSettings.getInstance().setIgnorePatternSet(updatedBlackList);
ParameterNameHintsSettings.getInstance().setIgnorePatternSet(myHintsProvider, updatedBlackList);
}
@Nullable
@@ -83,7 +86,7 @@ public class ParameterNameHintsConfigurable extends DialogWrapper {
EditorTextFieldProvider service = ServiceManager.getService(myProject, EditorTextFieldProvider.class);
myEditorTextField = service.getEditorField(PlainTextLanguage.INSTANCE, myProject, ContainerUtil.emptyIterable());
Set<String> blacklist = ParameterNameHintsSettings.getInstance().getIgnorePatternSet();
Set<String> blacklist = ParameterNameHintsSettings.getInstance().getIgnorePatternSet(myHintsProvider);
String text = StringUtil.join(blacklist, "\n");
myEditorTextField.setText(text);
@@ -1,125 +0,0 @@
/*
* Copyright 2000-2016 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 com.intellij.codeInsight.hints.settings;
import com.intellij.openapi.components.PersistentStateComponent;
import com.intellij.openapi.components.ServiceManager;
import com.intellij.openapi.components.State;
import com.intellij.openapi.components.Storage;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.List;
import java.util.Set;
@State(name = "ParameterNameHintsSettings", storages = @Storage("parameter.name.hints.xml"))
public class ParameterNameHintsSettings implements PersistentStateComponent<ParameterNameHintsSettings.State> {
private static final int VERSION = 1;
private static final Set<String> DEFAULT = ContainerUtil.newHashSet(
"(begin*, end*)",
"(start*, end*)",
"(first*, last*)",
"(first*, second*)",
"(from*, to*)",
"(min*, max*)",
"(key, value)",
"(format, arg*)",
"(message)",
"(message, error)",
"*.set(*,*)",
"*.print(*)",
"*.println(*)",
"*.get(*)",
"*.append(*)",
"*.charAt(*)",
"*.indexOf(*)",
"*.contains(*)",
"*.startsWith(*)",
"*.endsWith(*)",
"java.lang.Math.*"
);
private ParameterNameHintsSettings.State state = new State();
private Set<String> defaultIgnoreSet = DEFAULT;
@TestOnly
protected void setDefaultSet(@NotNull Set<String> ignoreSet) {
defaultIgnoreSet = ignoreSet;
}
public static ParameterNameHintsSettings getInstance() {
return ServiceManager.getService(ParameterNameHintsSettings.class);
}
@Nullable
@Override
public ParameterNameHintsSettings.State getState() {
return state;
}
@Override
public void loadState(ParameterNameHintsSettings.State state) {
this.state = state;
}
public void addIgnorePattern(@NotNull String pattern) {
state.diff.add('+' + pattern);
}
public int getVersion() {
return state.version;
}
public Set<String> getIgnorePatternSet() {
Set<String> ignoreSet = ContainerUtil.newHashSet(defaultIgnoreSet);
state.diff.forEach((item) -> {
if (item.startsWith("+")) {
ignoreSet.add(item.substring(1));
}
else if (item.startsWith("-")) {
ignoreSet.remove(item.substring(1));
}
});
return ignoreSet;
}
public void setVersion(int newVersion) {
state.version = newVersion;
}
public void setIgnorePatternSet(@NotNull Set<String> updatedBlackList) {
Set<String> addedItems = ContainerUtil.newHashSet(updatedBlackList);
defaultIgnoreSet.forEach((pattern) -> addedItems.remove(pattern));
Set<String> removedItems = ContainerUtil.newHashSet(defaultIgnoreSet);
updatedBlackList.forEach((pattern) -> removedItems.remove(pattern));
List<String> diff = ContainerUtil.newArrayList();
addedItems.forEach((item) -> diff.add('+' + item));
removedItems.forEach((item) -> diff.add('-' + item));
state.diff = diff;
}
public static class State {
public int version = VERSION;
public List<String> diff = ContainerUtil.newArrayList();
}
}
@@ -0,0 +1,99 @@
package com.intellij.codeInsight.hints.settings
import com.intellij.codeInsight.hints.InlayParameterHintsProvider
import com.intellij.lang.Language
import com.intellij.openapi.components.PersistentStateComponent
import com.intellij.openapi.components.State
import com.intellij.openapi.components.Storage
import com.intellij.openapi.components.service
import org.jdom.Element
private object XmlTagHelper {
val BLACKLIST = "blacklist"
val LANGUAGE = "language"
val ADDED = "added"
val REMOVED = "removed"
val PATTERN = "pattern"
}
@State(name = "ParameterNameHintsSettings", storages = arrayOf(Storage("parameter.hints.xml")))
class ParameterNameHintsSettings : PersistentStateComponent<Element> {
private var myState: Element = Element("settings")
override fun getState(): Element = myState
override fun loadState(state: Element) {
myState = state
}
private fun Language.toXmlName() = displayName.split(' ')[0]
private fun getStoredLanguageBlackList(language: Language): Element {
val allLists = myState.getOrCreateChild(XmlTagHelper.BLACKLIST)
val languageTagName = language.toXmlName()
return allLists.getOrCreateChild(languageTagName)
}
fun addIgnorePattern(language: Language, pattern: String) {
val langBlackList = getStoredLanguageBlackList(language)
val addedPattern = pattern.toPatternElement(XmlTagHelper.ADDED)
langBlackList.addContent(addedPattern)
}
fun getIgnorePatternSet(hintsProvider: InlayParameterHintsProvider): Set<String> {
val forLanguage = getStoredLanguageBlackList(hintsProvider.language).children
val added = forLanguage.filter { it.name == XmlTagHelper.ADDED }.mapNotNull { it.getAttribute(XmlTagHelper.PATTERN)?.value }
val removed = forLanguage.filter { it.name == XmlTagHelper.REMOVED }.mapNotNull { it.getAttribute(XmlTagHelper.PATTERN)?.value }
val updated = hintsProvider.defaultBlackList.toMutableSet()
updated.removeAll(removed)
updated.addAll(added)
return updated
}
fun setIgnorePatternSet(provider: InlayParameterHintsProvider, updatedBlackList: Set<String>) {
val defaultBlackList = provider.defaultBlackList
val removed = defaultBlackList.toMutableSet()
removed.removeAll(updatedBlackList)
val added = updatedBlackList.toMutableSet()
added.removeAll(defaultBlackList)
updateState(provider.language, added, removed)
}
private fun updateState(language: Language, added: MutableSet<String>, removed: MutableSet<String>) {
val languageBlackList = getStoredLanguageBlackList(language)
removed.map { it.toPatternElement(XmlTagHelper.REMOVED) }
.forEach { languageBlackList.addContent(it) }
added.map { it.toPatternElement(XmlTagHelper.ADDED) }
.forEach { languageBlackList.addContent(it) }
}
companion object {
@JvmStatic
fun getInstance() = service<ParameterNameHintsSettings>()
}
}
private fun Element.getOrCreateChild(name: String): Element {
var child = getChild(name)
if (child == null) {
child = Element(name)
addContent(child)
}
return child
}
private fun String.toPatternElement(status: String): Element {
val element = Element(status)
element.setAttribute(XmlTagHelper.PATTERN, this)
return element
}
@@ -15,58 +15,86 @@
*/
package com.intellij.codeInsight.hints.settings
import com.intellij.codeInsight.hints.InlayParameterHintsProvider
import com.intellij.lang.Language
import com.intellij.openapi.fileTypes.PlainTextLanguage
import junit.framework.TestCase
class MockInlayProvider(override val defaultBlackList: Set<String>): InlayParameterHintsProvider {
override val language: Language = PlainTextLanguage.INSTANCE
}
class ParameterNameSettingsTest : TestCase() {
fun `test deleted value is saved to state`() {
val settings = ParameterNameHintsSettings()
val ignoreSet = settings.ignorePatternSet
assert(ignoreSet.size > 0)
val first = ignoreSet.first()
ignoreSet.remove(first)
settings.ignorePatternSet = ignoreSet
val diff = settings.state!!.diff
assert(diff.size == 1)
assert(diff[0].startsWith("-"))
assert(diff[0].endsWith(first))
lateinit var settings: ParameterNameHintsSettings
lateinit var inlayProvider: InlayParameterHintsProvider
override fun setUp() {
settings = ParameterNameHintsSettings()
inlayProvider = MockInlayProvider(setOf())
}
fun defaultSettingsUpdated(vararg newDefault: String) {
inlayProvider = MockInlayProvider(setOf(*newDefault))
}
fun `test saved value is saved to state`() {
val settings = ParameterNameHintsSettings()
val newPattern = "java.util.*(*)"
settings.addIgnorePattern(newPattern)
val diff = settings.state!!.diff
assert(diff.size == 1)
assert(diff[0].startsWith("+"))
assert(diff[0].endsWith(newPattern))
fun addIgnorePattern(newPattern: String) {
settings.addIgnorePattern(inlayProvider.language, newPattern)
}
fun `test on defaults change ensure removed default items remains removed`() {
val settings = ParameterNameHintsSettings()
settings.setDefaultSet(setOf("aaa", "bbb", "ccc"))
settings.ignorePatternSet = setOf("aaa", "ccc", "zzz")
settings.addIgnorePattern("xxx")
val savedState = settings.state
val newSettings = ParameterNameHintsSettings()
newSettings.setDefaultSet(setOf("aaa", "bbb", "ccc", "qqq", "xxx"))
newSettings.loadState(savedState)
fun setIgnorePattern(vararg newPatternSet: String) {
settings.setIgnorePatternSet(inlayProvider, setOf(*newPatternSet))
}
fun getIgnoreSet(): Set<String> = settings.getIgnorePatternSet(inlayProvider)
val newIgnoreSet = newSettings.ignorePatternSet
assert(newIgnoreSet.containsAll(setOf("aaa", "ccc", "qqq", "zzz", "xxx")))
assert(newIgnoreSet.size == 5)
newSettings.ignorePatternSet = newSettings.ignorePatternSet
fun `test ignore pattern is added`() {
defaultSettingsUpdated("xxx")
val diff = newSettings.state!!.diff
assert(diff.containsAll(setOf("-bbb", "+zzz")))
assert(diff.size == 2)
var ignoreSet = getIgnoreSet()
assert(ignoreSet.size == 1)
addIgnorePattern("aaa")
ignoreSet = getIgnoreSet()
assert(ignoreSet.size == 2)
assert(ignoreSet.contains("aaa"))
assert(ignoreSet.contains("xxx"))
}
fun `test removed pattern is removed when defaults are updated`() {
defaultSettingsUpdated("aaa", "bbb")
var ignoreSet = getIgnoreSet()
assert(ignoreSet.size == 2)
setIgnorePattern("aaa")
assert(getIgnoreSet().size == 1)
defaultSettingsUpdated("aaa", "bbb", "ccc")
ignoreSet = getIgnoreSet()
assert(ignoreSet.size == 2)
assert(ignoreSet.contains("aaa"))
assert(ignoreSet.contains("ccc"))
}
fun `test added items remain added on defaults update`() {
defaultSettingsUpdated("aaa")
var ignoreSet = getIgnoreSet()
assert(ignoreSet.size == 1)
addIgnorePattern("xxx")
ignoreSet = getIgnoreSet()
assert(ignoreSet.size == 2)
defaultSettingsUpdated("aaa", "bbb")
ignoreSet = getIgnoreSet()
assert(ignoreSet.size == 3)
assert(ignoreSet.contains("aaa"))
assert(ignoreSet.contains("bbb"))
assert(ignoreSet.contains("xxx"))
}
}