From b1a9fb60ab6806036839d4681212b3a471e8a242 Mon Sep 17 00:00:00 2001 From: Matthias Langer Date: Sun, 28 Dec 2014 21:17:50 +0100 Subject: [PATCH] Highlight by-name parameters at their creation Minor refactoring to avoid code duplication Fix #1002340 --- .../src/org/scalaide/TestsSuite.scala | 4 +- .../AbstractSymbolClassifierTest.scala | 2 +- .../classifier/CallByNameParameterTest.scala | 2 +- .../classifier/RegionParser.scala | 38 +++- .../classifier/RegionParserTest.scala | 56 ++++-- ...llByNameParamAtCreationPresenterTest.scala | 175 ++++++++++++++++++ .../full/elcl16/create_call_by_name_param.png | Bin 0 -> 525 bytes org.scala-ide.sdt.core/plugin.xml | 33 ++++ .../CallByNameParamAtCreationAnnotation.scala | 13 ++ ...ramAtCreationHighlightingParticipant.scala | 8 + .../CallByNameParamAtCreationPresenter.scala | 96 ++++++++++ .../BasicFieldEditorPreferencePage.scala | 20 ++ ...allByNameParamCreationPreferencePage.scala | 44 +++++ .../preferences/ImplicitsPreferencePage.scala | 29 +-- .../internal/preferences/PrefPageUtils.scala | 23 +++ 15 files changed, 504 insertions(+), 39 deletions(-) create mode 100644 org.scala-ide.sdt.core.tests/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationPresenterTest.scala create mode 100644 org.scala-ide.sdt.core/icons/full/elcl16/create_call_by_name_param.png create mode 100644 org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationAnnotation.scala create mode 100644 org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationHighlightingParticipant.scala create mode 100644 org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationPresenter.scala create mode 100644 org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/BasicFieldEditorPreferencePage.scala create mode 100644 org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/CallByNameParamCreationPreferencePage.scala create mode 100644 org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/PrefPageUtils.scala diff --git a/org.scala-ide.sdt.core.tests/src/org/scalaide/TestsSuite.scala b/org.scala-ide.sdt.core.tests/src/org/scalaide/TestsSuite.scala index a42a6acf6e..3c6bed4468 100644 --- a/org.scala-ide.sdt.core.tests/src/org/scalaide/TestsSuite.scala +++ b/org.scala-ide.sdt.core.tests/src/org/scalaide/TestsSuite.scala @@ -50,6 +50,7 @@ import org.scalaide.util.eclipse.RegionUtilsTest import org.scalaide.core.pc.DeclarationPrinterTest import org.scalaide.core.compiler.NamePrinterTest import org.scalaide.core.ui.ScalaTemplateContextTest +import org.scalaide.ui.internal.editor.decorators.params.byname.CallByNameParamAtCreationPresenterTest @RunWith(classOf[Suite]) @Suite.SuiteClasses( @@ -103,6 +104,7 @@ import org.scalaide.core.ui.ScalaTemplateContextTest classOf[RegionUtilsTest], classOf[DeclarationPrinterTest], classOf[NamePrinterTest], - classOf[ScalaTemplateContextTest] + classOf[ScalaTemplateContextTest], + classOf[CallByNameParamAtCreationPresenterTest] )) class TestsSuite diff --git a/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/AbstractSymbolClassifierTest.scala b/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/AbstractSymbolClassifierTest.scala index c18c9a2a85..23eec9873e 100644 --- a/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/AbstractSymbolClassifierTest.scala +++ b/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/AbstractSymbolClassifierTest.scala @@ -33,7 +33,7 @@ class AbstractSymbolClassifierTest { } protected def checkSymbolInfoClassification(source: String, locationTemplate: String, regionTagToSymbolInfo: Map[String, SymbolInfo], delimiter: Char = '$') { - val expectedRegionToSymbolNameMap: Map[IRegion, String] = RegionParser.getRegions(locationTemplate, delimiter) + val expectedRegionToSymbolNameMap: Map[IRegion, String] = RegionParser.delimitedRegions(locationTemplate, delimiter) val expectedRegionsAndSymbols: List[(IRegion, SymbolInfo)] = expectedRegionToSymbolNameMap.mapValues(regionTagToSymbolInfo).toList sortBy regionOffset val actualRegionsAndSymbols: List[(IRegion, SymbolInfo)] = diff --git a/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/CallByNameParameterTest.scala b/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/CallByNameParameterTest.scala index c0dadb93ae..1d5a718d96 100644 --- a/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/CallByNameParameterTest.scala +++ b/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/CallByNameParameterTest.scala @@ -73,4 +73,4 @@ class CallByNameParameterTest extends AbstractSymbolClassifierTest { } """, Map("BNP" -> CallByNameParameter, "LV" -> LocalVal, "P" -> Param)) } -} \ No newline at end of file +} diff --git a/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/RegionParser.scala b/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/RegionParser.scala index 5e12985529..0d6bf91c7f 100644 --- a/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/RegionParser.scala +++ b/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/RegionParser.scala @@ -2,9 +2,43 @@ package org.scalaide.core.semantichighlighting.classifier import org.eclipse.jface.text.IRegion import org.eclipse.jface.text.Region +import scala.annotation.tailrec object RegionParser { + /** + * This class represents a substring with an optional prefix and suffix. + */ + case class EmbeddedSubstr(str: String, prefix: String = "", suffix: String = "") { + private[RegionParser] val searchString = prefix + str + suffix + } + + object EmbeddedSubstr { + implicit def wrapAsEmbeddedSubstring(str: String) = EmbeddedSubstr(str) + } + + + /** + * Extracts the regions marked by the given substrings. + */ + def substrRegions(test: String, substrs: EmbeddedSubstr*): Map[IRegion, EmbeddedSubstr] = { + @tailrec + def regions(substr: EmbeddedSubstr, fromIndex: Int = 0, acc: Seq[IRegion] = Seq()): Seq[IRegion] = { + val index = test.indexOf(substr.searchString, fromIndex) + if (index < 0) { + acc + } else { + val newAcc = acc :+ new Region(index + substr.prefix.length, substr.str.length) + regions(substr, index + substr.searchString.length, newAcc) + } + + } + + substrs.foldLeft(Map[IRegion, EmbeddedSubstr]()) { (acc, substr) => + acc ++ regions(substr).map(_ -> substr) + } + } + /** * Search for regions delimited with a sign. In the default case the * delimited sign is a '$'. @@ -26,7 +60,7 @@ object RegionParser { * are handled as there were no escape sign. This means that the String `$a\$b$` * is treated as `$a$b$`. */ - def getRegions(text: String, delimiter: Char = '$'): Map[IRegion, String] = { + def delimitedRegions(text: String, delimiter: Char = '$'): Map[IRegion, String] = { val sb = new StringBuilder var curPos = 0 var offset = 0 @@ -57,4 +91,4 @@ object RegionParser { require(sb.isEmpty, "odd number of '"+delimiter+"' signs in text") regions } -} \ No newline at end of file +} diff --git a/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/RegionParserTest.scala b/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/RegionParserTest.scala index a56afd8386..a7504462c9 100644 --- a/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/RegionParserTest.scala +++ b/org.scala-ide.sdt.core.tests/src/org/scalaide/core/semantichighlighting/classifier/RegionParserTest.scala @@ -4,23 +4,53 @@ import org.eclipse.jface.text.IRegion import org.eclipse.jface.text.Region import org.junit.Test import org.junit.Assert._ - -private case class GetRegionsTestCase(input: String, expected: Map[IRegion, String]) +import org.scalaide.core.semantichighlighting.classifier.RegionParser.EmbeddedSubstr +import org.scalaide.core.semantichighlighting.classifier.RegionParser.EmbeddedSubstr.wrapAsEmbeddedSubstring class RegionParserTest { + @Test + def testDelimitedRegions() { + case class TestCase(input: String, expected: Map[IRegion, String]) - private val getRegionsTestCases = Seq( - GetRegionsTestCase("", Map()), - GetRegionsTestCase("""$abc$ def $ghi$""", Map(new Region(0, 5) -> "abc", new Region(10, 5) -> "ghi")), - GetRegionsTestCase("""$a\$bc$ de\$f $ghi$""", Map(new Region(0, 6) -> "a$bc", new Region(12, 5) -> "ghi")), - GetRegionsTestCase("""\""", Map())) - + val testCases = Seq( + TestCase("", Map()), + TestCase("""$abc$ def $ghi$""", Map(new Region(0, 5) -> "abc", new Region(10, 5) -> "ghi")), + TestCase("""$a\$bc$ de\$f $ghi$""", Map(new Region(0, 6) -> "a$bc", new Region(12, 5) -> "ghi")), + TestCase("""\""", Map())) - @Test - def getRegions() { - for(testCase <- getRegionsTestCases) { - val actual = RegionParser.getRegions(testCase.input) + for(testCase <- testCases) { + val actual = RegionParser.delimitedRegions(testCase.input) assertEquals(testCase.expected, actual) } } -} \ No newline at end of file + + @Test + def testSubstrRegions() { + case class TestCase(expected: Map[IRegion, EmbeddedSubstr], input: String, substrs: EmbeddedSubstr*) + def toRegion(offset: Int, len: Int) = new Region(offset, len) + + val xStrY = EmbeddedSubstr("str", "x", "y") + val xxxStr = EmbeddedSubstr("str", "xxx") + val strYyy = EmbeddedSubstr("str", "", "yyy") + + val testCases = Seq( + TestCase(Map(), ""), + TestCase(Map(), "asdf", "xyz"), + TestCase(Map(toRegion(0, 1) -> "a"), "a", "a"), + TestCase(Map(toRegion(0, 1) -> "a", toRegion(1, 1) -> "a"), "aa", "a"), + TestCase(Map( + toRegion(1, 3) -> xStrY, + toRegion(11, 3) -> xxxStr, + toRegion(16, 3) -> strYyy, + toRegion(5, 1) -> "5", + toRegion(15, 1) -> "5"), + "xstry56xxxxstr45stryyy", + xStrY, xxxStr, strYyy, "5")) + + for (testCase <- testCases) { + val actual = RegionParser.substrRegions(testCase.input, testCase.substrs :_*) + assertEquals(testCase.expected, actual) + } + + } +} diff --git a/org.scala-ide.sdt.core.tests/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationPresenterTest.scala b/org.scala-ide.sdt.core.tests/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationPresenterTest.scala new file mode 100644 index 0000000000..8132b239d1 --- /dev/null +++ b/org.scala-ide.sdt.core.tests/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationPresenterTest.scala @@ -0,0 +1,175 @@ +package org.scalaide.ui.internal.editor.decorators.params.byname + +import scala.annotation.migration + +import org.eclipse.jface.text.Region +import org.junit.Assert.assertEquals +import org.junit.Assert._ +import org.junit.Test +import org.scalaide.CompilerSupportTests +import org.scalaide.core.semantichighlighting.classifier.RegionParser +import org.scalaide.core.semantichighlighting.classifier.RegionParser.EmbeddedSubstr +import org.scalaide.core.semantichighlighting.classifier.RegionParser.EmbeddedSubstr.wrapAsEmbeddedSubstring + +import CallByNameParamAtCreationPresenterTest.mkScalaCompilationUnit + +object CallByNameParamAtCreationPresenterTest extends CompilerSupportTests + +class CallByNameParamAtCreationPresenterTest { + import CallByNameParamAtCreationPresenterTest._ + + import RegionParser.EmbeddedSubstr + import RegionParser.EmbeddedSubstr.wrapAsEmbeddedSubstring + import CallByNameParamAtCreationPresenter.Cfg + + @Test + def testWithStringArgument() { + testWithSingleLineCfg(""" + object O { + def method(s: => String) = Unit + method("Hanelore Hostasch") + }""", + "\"Hanelore Hostasch\"") + } + + @Test + def testWithValArgument() { + testWithSingleLineCfg(""" + object O { + val wert = 43 + def method(i: => Int) = Unit + method(wert) + }""", + EmbeddedSubstr("wert", "(", ")")) + } + + @Test + def testWithMultipleMethods() { + testWithSingleLineCfg(""" + object O { + val wert = 43 + def method1(i: => Int) = Unit + def method2(i: Int) = Unit + method1(wert) + method2(wert) + }""", + EmbeddedSubstr("wert", "1(", ")")) + } + + @Test + def testWithMathExpression() { + testWithSingleLineCfg(""" + object O { + def method(i: => Int) = Unit + method(1 + 2 + 3 + 4) + }""", + "1 + 2 + 3 + 4") + } + + @Test + def testWithStringExpression() { + testWithSingleLineCfg(""" + object O { + def method(s: => String) = Unit + method("" + "asdf".substring(1)) + }""", + """"" + "asdf".substring(1)""") + } + + @Test + def testWithRecursion() { + testWithSingleLineCfg(""" + object O { + def method(s: => String) = s + method(method(method("recursive"))) + }""", + "\"recursive\"", """method("recursive")""", """method(method("recursive"))""") + } + + @Test + def testWithMultipleArgs() { + testWithSingleLineCfg(""" + object O { + def method(s1: => String, s2: String) = s1 + s2 + method("hallo", "welt") + }""", + "\"hallo\"") + } + + @Test + def testWithMultipleArgLists() { + testWithSingleLineCfg(""" + object O { + def method(s1: => String)(s2: String)(i1: Int, i2: => Int, i3: Int) = Unit + method("hallo")("welt")(1, 2, 3) + }""", + "\"hallo\"", EmbeddedSubstr("2", " ", ",")) + } + + @Test + def testWithCompilationErrorAlreadyDefined() { + testWithSingleLineCfg(""" + object O { + def alreadyDefined(s: => String) = Unit + def alreadyDefined(s: => String) = Unit + alreadyDefined("") + }""") + } + + @Test + def testWithCompilationErrorInArg() { + testWithSingleLineCfg(""" + object O { + def method(s: => String) = Unit + alreadyDefined("" "ups") + }""") + } + + @Test + def testWithMuliLineCfgWithMultipleLines() { + testWithMultiLineCfg(""" + object O { + def method(s: => String) = Unit + method("a" + + "b + c" + + "d") + }""", """"a" + + "b + c" + + "d"""") + } + + @Test + def testWithSingleLineCfgWithMultipleLines() { + testWithSingleLineCfg(""" + object O { + def method(s: => String) = Unit + method("a" + + "b + c" + + "d") + }""", """"a" +""") + } + + private def testWith(source: String, cfg: Cfg, paramCreations: EmbeddedSubstr*) { + val cu = mkScalaCompilationUnit(source) + cu.withSourceFile { (sourceFile, compiler) => + val res = CallByNameParamAtCreationPresenter.annotations(compiler, cu, sourceFile, cfg) + val regions = res.values.map(pos => new Region(pos.offset, pos.length)).toSet + val expectedRegions = RegionParser.substrRegions(source, paramCreations: _*).keySet + assertEquals(expectedRegions, regions) + + assertEquals(Set(), res.keys.filterNot(_.isInstanceOf[CallByNameParamAtCreationAnnotation])) + val annotationMsgs = res.keySet.map(_.getText) + for (substr <- paramCreations) { + assertTrue(annotationMsgs.toString(), annotationMsgs.exists(_.contains(substr.str))) + } + } + } + + private def testWithSingleLineCfg(source: String, paramCreations: EmbeddedSubstr*) { + testWith(source, Cfg(firstLineOnly = true), paramCreations: _*) + } + + private def testWithMultiLineCfg(source: String, paramCreations: EmbeddedSubstr*) { + testWith(source, Cfg(firstLineOnly = false), paramCreations: _*) + } +} diff --git a/org.scala-ide.sdt.core/icons/full/elcl16/create_call_by_name_param.png b/org.scala-ide.sdt.core/icons/full/elcl16/create_call_by_name_param.png new file mode 100644 index 0000000000000000000000000000000000000000..3289d9d6c0ba4e1f80836f576879422d4c9a6012 GIT binary patch literal 525 zcmV+o0`mQdP)Opy^MEHzr+1IzEr7CP z4o8o(O1}AT4R@EepEMyP5ZvpXvSPTpdf9c&- zJHVxPR~R0wnyCU-Q;DJ!oFH6pbUoS*&>LNkBAgVQtfmshh7Q2x0ZhSUJHVZG<81(B zrpHdrYzO(_rCecNl4_bHE#(UH$Pb&@PG)-S^j2%Wh2_0M66uAZ&wbzavjD%JO}!L0 zf!T-5Oya2C-vR(w&V72~amDr2_&LDa+?PRzf0#*4o;7>#1^ml&d`y1 + + + + + + + + @@ -1873,5 +1903,8 @@ + + diff --git a/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationAnnotation.scala b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationAnnotation.scala new file mode 100644 index 0000000000..7ebe57d3fc --- /dev/null +++ b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationAnnotation.scala @@ -0,0 +1,13 @@ +package org.scalaide.ui.internal.editor.decorators.params.byname + +import org.eclipse.jface.text.source.Annotation +import org.scalaide.ui.editor.ScalaEditorAnnotation + +object CallByNameParamAtCreationAnnotation { + val ID = "scala.tools.eclipse.semantichighlighting.callByNameParam.creationAnnotation" +} + +class CallByNameParamAtCreationAnnotation(text: String) + extends Annotation(CallByNameParamAtCreationAnnotation.ID, false, text) with ScalaEditorAnnotation { + +} diff --git a/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationHighlightingParticipant.scala b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationHighlightingParticipant.scala new file mode 100644 index 0000000000..c38dc052db --- /dev/null +++ b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationHighlightingParticipant.scala @@ -0,0 +1,8 @@ +package org.scalaide.ui.internal.editor.decorators.params.byname + +import org.scalaide.core.extensions.SemanticHighlightingParticipant + +class CallByNameParamAtCreationHighlightingParticipant extends + SemanticHighlightingParticipant(viewer => new CallByNameParamAtCreationPresenter(viewer)) { + +} diff --git a/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationPresenter.scala b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationPresenter.scala new file mode 100644 index 0000000000..67128cd5ac --- /dev/null +++ b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/editor/decorators/params/byname/CallByNameParamAtCreationPresenter.scala @@ -0,0 +1,96 @@ +package org.scalaide.ui.internal.editor.decorators.params.byname + +import org.scalaide.core.extensions.SemanticHighlightingParticipant +import org.scalaide.core.internal.jdt.model.ScalaCompilationUnit +import scala.reflect.internal.util.SourceFile +import org.scalaide.core.compiler.IScalaPresentationCompiler +import org.eclipse.jface.text.Position +import org.eclipse.jface.text.source.Annotation +import org.scalaide.logging.HasLogger +import org.eclipse.jface.text.source.ISourceViewer +import org.scalaide.ui.internal.editor.decorators.BaseSemanticAction +import org.scalaide.core.internal.compiler.ScalaPresentationCompiler +import org.scalaide.ui.internal.preferences.CallByNameParamCreationPreferencePage +import org.scalaide.core.IScalaPlugin + +class CallByNameParamAtCreationPresenter(sourceViewer: ISourceViewer) extends + BaseSemanticAction(sourceViewer, CallByNameParamAtCreationAnnotation.ID, Some("callByNameParamCreation")) { + + import CallByNameParamAtCreationPresenter._ + + protected override def findAll(compiler: ScalaPresentationCompiler, scu: ScalaCompilationUnit, sourceFile: SourceFile): Map[Annotation, Position] = + CallByNameParamAtCreationPresenter.annotations(compiler, scu, sourceFile, prefStoreCfg) +} + +object CallByNameParamAtCreationPresenter extends HasLogger { + case class Cfg(firstLineOnly: Boolean) + + private def prefStoreCfg: Cfg = { + val prefStore = IScalaPlugin().getPreferenceStore + Cfg(prefStore.getBoolean(CallByNameParamCreationPreferencePage.P_FIRST_LINE_ONLY)) + } + + def annotations(compiler: IScalaPresentationCompiler, scu: ScalaCompilationUnit, sourceFile: SourceFile, cfg: Cfg): Map[Annotation, Position] = { + def annotations(tree: compiler.Tree) = { + + object traverser extends compiler.Traverser { + var result: Map[Annotation, Position] = Map() + + override def traverse(tree: compiler.Tree) { + result = tree match { + case compiler.Apply(fun, args) if (fun.tpe != null) => result ++ processArgs(fun.tpe, args) + case _ => result + } + + super.traverse(tree) + } + + def processArgs(funTpe: compiler.Type, args: List[compiler.Tree]): Map[Annotation, Position] = { + if (funTpe.params.size != args.size /* <- possible for faulty code */) { + Map() + } else { + val byNameArgs = funTpe.params.zip(args).withFilter { case (param, _) => + param.isByNameParam + }.map(_._2) + + (for (arg <- byNameArgs) yield { + val txt = toText(arg) + (toAnnotation(arg, txt), toPositioin(arg, txt)) + }).toMap + } + } + + def toText(arg: compiler.Tree): String = { + sourceFile.content.view(arg.pos.start, arg.pos.end).mkString("") + } + + def toAnnotation(arg: compiler.Tree, txt: String): Annotation = { + new CallByNameParamAtCreationAnnotation(s"Call-by-name parameter creation: () => $txt") + } + + def toPositioin(arg: compiler.Tree, txt: String): Position = { + val start = arg.pos.start + val length = { + if (cfg.firstLineOnly) { + val eol = txt.indexOf('\n') + if (eol > -1) eol else txt.length + } else { + txt.length + } + } + new Position(start, length) + } + } + + traverser.traverse(tree) + traverser.result + } + + compiler.askLoadedTyped(sourceFile, false).get match { + case Left(tree) => annotations(tree) + case Right(th) => + logger.error("Error while searching for call-by-name parameter creations.", th) + Map() + } + } +} diff --git a/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/BasicFieldEditorPreferencePage.scala b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/BasicFieldEditorPreferencePage.scala new file mode 100644 index 0000000000..013e2173ff --- /dev/null +++ b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/BasicFieldEditorPreferencePage.scala @@ -0,0 +1,20 @@ +package org.scalaide.ui.internal.preferences + +import org.eclipse.jface.preference.FieldEditorPreferencePage +import org.eclipse.ui.IWorkbenchPreferencePage +import org.eclipse.ui.IWorkbench +import org.scalaide.core.IScalaPlugin +import org.eclipse.jface.preference.BooleanFieldEditor + +abstract class BasicFieldEditorPreferencePage(description: String) extends FieldEditorPreferencePage with IWorkbenchPreferencePage { + setPreferenceStore(IScalaPlugin().getPreferenceStore) + setDescription(description) + + override def init(workbench: IWorkbench) = Unit + + protected def addBooleanFieldEditors(editors: (String, String)*) { + for ((name, label) <- editors) { + addField(new BooleanFieldEditor(name, label, getFieldEditorParent)) + } + } +} diff --git a/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/CallByNameParamCreationPreferencePage.scala b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/CallByNameParamCreationPreferencePage.scala new file mode 100644 index 0000000000..99bf8de032 --- /dev/null +++ b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/CallByNameParamCreationPreferencePage.scala @@ -0,0 +1,44 @@ +package org.scalaide.ui.internal.preferences + +import org.eclipse.swt.widgets.Composite +import org.eclipse.swt.widgets.Control +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer +import org.scalaide.core.IScalaPlugin + +class CallByNameParamCreationPreferencePage extends BasicFieldEditorPreferencePage("Configure highlighting for the creation of call-by-name parameters") { + import CallByNameParamCreationPreferencePage._ + + override def createContents(parent: Composite): Control = { + val control = super.createContents(parent).asInstanceOf[Composite] + PrefPageUtils.mkLinkToAnnotationsPref(parent)(a => s"More options for highlighting for call-by-name parameters on the $a preference page.") + control + } + + override def createFieldEditors() { + addBooleanFieldEditors( + P_ACTIVE -> "Enabled", + P_BOLD -> "Bold", + P_ITALIC -> "Italic", + P_FIRST_LINE_ONLY -> "Only highlight the first line") + } +} + +object CallByNameParamCreationPreferencePage { + val BASE = "scala.tools.eclipse.ui.preferences.callByNameParamCreation" + val P_ACTIVE = BASE + ".enabled" + val P_BOLD = BASE + ".text.bold" + val P_ITALIC = BASE + ".text.italic" + val P_FIRST_LINE_ONLY = BASE + ".firstline.only" +} + +class CallByNameParamCreationPreferenceInitializer extends AbstractPreferenceInitializer { + import CallByNameParamCreationPreferencePage._ + + override def initializeDefaultPreferences() { + val store = IScalaPlugin().getPreferenceStore + store.setDefault(P_ACTIVE, true) + store.setDefault(P_BOLD, false) + store.setDefault(P_ITALIC, false) + store.setDefault(P_FIRST_LINE_ONLY, true) + } +} diff --git a/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/ImplicitsPreferencePage.scala b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/ImplicitsPreferencePage.scala index b2c9d814b5..d1c09fdd92 100644 --- a/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/ImplicitsPreferencePage.scala +++ b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/ImplicitsPreferencePage.scala @@ -14,36 +14,23 @@ import org.eclipse.swt.SWT import org.eclipse.swt.events.SelectionEvent import org.eclipse.ui.dialogs.PreferencesUtil -class ImplicitsPreferencePage extends FieldEditorPreferencePage with IWorkbenchPreferencePage { +class ImplicitsPreferencePage extends BasicFieldEditorPreferencePage("Set the highlighting for implicit conversions and implicit parameters") { import ImplicitsPreferencePage._ - import org.scalaide.util.eclipse.SWTUtils.fnToSelectionAdapter - - setPreferenceStore(IScalaPlugin().getPreferenceStore) - setDescription(""" -Set the highlighting for implicit conversions and implicit parameters. - """) override def createContents(parent: Composite): Control = { val control = super.createContents(parent).asInstanceOf[Composite] - val link = new Link(control, SWT.NONE) - link.setText("""More options for highlighting for implicit conversions on the Text Editors/Annotations preference page.""") - link.addSelectionListener { e: SelectionEvent => - PreferencesUtil.createPreferenceDialogOn(parent.getShell, e.text, null, null) - } - + PrefPageUtils.mkLinkToAnnotationsPref(parent)(a => s"More options for highlighting for implicit conversions on the $a preference page.") control } override def createFieldEditors() { - addField(new BooleanFieldEditor(P_ACTIVE, "Enabled", getFieldEditorParent)) - addField(new BooleanFieldEditor(P_BOLD, "Bold", getFieldEditorParent)) - addField(new BooleanFieldEditor(P_ITALIC, "Italic", getFieldEditorParent)) - addField(new BooleanFieldEditor(P_CONVERSIONS_ONLY, "Only highlight implicit conversions", getFieldEditorParent)) - addField(new BooleanFieldEditor(P_FIRST_LINE_ONLY, "Only highlight the first line in an implicit conversion", getFieldEditorParent)) + addBooleanFieldEditors( + P_ACTIVE -> "Enabled", + P_BOLD -> "Bold", + P_ITALIC -> "Italic", + P_CONVERSIONS_ONLY -> "Only highlight implicit conversions", + P_FIRST_LINE_ONLY -> "Only highlight the first line in an implicit conversion") } - - def init(workbench: IWorkbench) {} - } object ImplicitsPreferencePage { diff --git a/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/PrefPageUtils.scala b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/PrefPageUtils.scala new file mode 100644 index 0000000000..ae1dbffb03 --- /dev/null +++ b/org.scala-ide.sdt.core/src/org/scalaide/ui/internal/preferences/PrefPageUtils.scala @@ -0,0 +1,23 @@ +package org.scalaide.ui.internal.preferences + +import org.scalaide.util.eclipse.SWTUtils.fnToSelectionAdapter +import org.eclipse.swt.widgets.Link +import org.eclipse.swt.widgets.Composite +import org.eclipse.swt.SWT +import org.eclipse.ui.dialogs.PreferencesUtil +import org.eclipse.swt.events.SelectionEvent + +private object PrefPageUtils { + def mkLink(parent: Composite, anchor: String, style: Int = SWT.None)(anchorToLinkText: String => String) = { + val link = new Link(parent, style) + link.setText(anchorToLinkText(anchor)) + link.addSelectionListener { e: SelectionEvent => + PreferencesUtil.createPreferenceDialogOn(parent.getShell, e.text, null, null) + } + link + } + + def mkLinkToAnnotationsPref(parent: Composite, style: Int = SWT.None)(anchorToLinkText: String => String) = { + mkLink(parent, """Text Editors/Annotations""", style)(anchorToLinkText) + } +}