summaryrefslogtreecommitdiff
path: root/src/compiler/scala/tools/nsc/settings/AbsSettings.scala
blob: 75e2c5ce11761aeed26a3b7e87d529a0550581c1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
/* NSC -- new Scala compiler
 * Copyright 2005-2010 LAMP/EPFL
 * @author  Paul Phillips
 */

package scala.tools.nsc
package settings

import io.AbstractFile

/** A Settings abstraction boiled out of the original highly mutable Settings
 *  class with the intention of creating an ImmutableSettings which can be used
 *  interchangeably.   Except of course without the mutants.
 */

trait AbsSettings {
  type Setting <: AbsSetting      // Fix to the concrete Setting type
  type ResultOfTryToSet           // List[String] in mutable, (Settings, List[String]) in immutable
  def errorFn: String => Unit
  protected def allSettings: collection.Set[Setting]

  // settings minus internal usage settings
  def visibleSettings = allSettings filterNot (_.isInternalOnly)

  // only settings which differ from default
  def userSetSettings = visibleSettings filterNot (_.isDefault)

  // an argument list which (should) be usable to recreate the Settings
  def recreateArgs = userSetSettings.toList flatMap (_.unparse)

  // checks both name and any available abbreviations
  def lookupSetting(cmd: String): Option[Setting] = allSettings find (_ respondsTo cmd)

  // two AbsSettings objects are equal if their visible settings are equal.
  override def hashCode() = visibleSettings.hashCode
  override def equals(that: Any) = that match {
    case s: AbsSettings => this.visibleSettings == s.visibleSettings
    case _              => false
  }
  override def toString() = "Settings {\n%s}\n" format (userSetSettings map ("  " + _ + "\n") mkString)
  def toConciseString = userSetSettings.mkString("(", " ", ")")

  def checkDependencies =
    visibleSettings filterNot (_.isDefault) forall (setting => setting.dependencies forall {
      case (dep, value) =>
        (Option(dep.value) exists (_.toString == value)) || {
          errorFn("incomplete option %s (requires %s)".format(setting.name, dep.name))
          false
        }
    })

  implicit lazy val SettingOrdering: Ordering[Setting] = Ordering.ordered

  trait AbsSettingValue {
    type T <: Any
    def value: T
    def isDefault: Boolean
  }

  trait AbsSetting extends Ordered[Setting] with AbsSettingValue {
    def name: String
    def helpDescription: String
    def unparse: List[String]     // A list of Strings which can recreate this setting.

    /* For tools which need to populate lists of available choices */
    def choices : List[String] = Nil

    /** In mutable Settings, these return the same object with a var set.
     *  In immutable, of course they will return a new object, which means
     *  we can't use "this.type", at least not in a non-casty manner, which
     *  is unfortunate because we lose type information without it.
     */
    def withAbbreviation(name: String): Setting
    def withHelpSyntax(help: String): Setting

    def helpSyntax: String = name
    def abbreviations: List[String] = Nil
    def dependencies: List[(Setting, String)] = Nil
    def respondsTo(label: String) = (name == label) || (abbreviations contains label)

    /** If the setting should not appear in help output, etc. */
    def isInternalOnly = false

    /** Issue error and return */
    def errorAndValue[T](msg: String, x: T): T = { errorFn(msg) ; x }

    /** After correct Setting has been selected, tryToSet is called with the
     *  remainder of the command line.  It consumes any applicable arguments and
     *  returns the unconsumed ones.
     */
    protected[nsc] def tryToSet(args: List[String]): Option[ResultOfTryToSet]

    /** Commands which can take lists of arguments in form -Xfoo:bar,baz override
     *  this method and accept them as a list.  It returns List[String] for
     *  consistency with tryToSet, and should return its incoming arguments
     *  unmodified on failure, and Nil on success.
     */
    protected[nsc] def tryToSetColon(args: List[String]): Option[ResultOfTryToSet] =
      errorAndValue("'%s' does not accept multiple arguments" format name, None)

    /** Commands which take properties in form -Dfoo=bar or -Dfoo
     */
    protected[nsc] def tryToSetProperty(args: List[String]): Option[ResultOfTryToSet] =
      errorAndValue("'%s' does not accept property style arguments" format name, None)

    /** Attempt to set from a properties file style property value.
     */
    def tryToSetFromPropertyValue(s: String): Unit = tryToSet(s :: Nil)

    /** These categorizations are so the help output shows -X and -P among
     *  the standard options and -Y among the advanced options.
     */
    def isAdvanced  = name match { case "-Y" => true ; case "-X" => false ; case _  => name startsWith "-X" }
    def isPrivate   = name match { case "-Y" => false ; case _  => name startsWith "-Y" }
    def isStandard  = !isAdvanced && !isPrivate

    def compare(that: Setting): Int = name compare that.name

    /** Equality tries to sidestep all the drama and define it simply and
     *  in one place: two AbsSetting objects are equal if their names and
     *  values compare equal.
     */
    override def equals(that: Any) = that match {
      case x: AbsSettings#AbsSetting  => (name == x.name) && (value == x.value)
      case _                          => false
    }
    override def hashCode() = (name, value).hashCode
    override def toString() = "%s = %s".format(name, value)
  }

  trait InternalSetting extends AbsSetting {
    override def isInternalOnly = true
  }
}