Commit ef6376b7 authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Auto-detection of CSS variable types.

parent 45587546
Loading
Loading
Loading
Loading
+67 −12
Original line number Diff line number Diff line
@@ -28,24 +28,20 @@ module JsDuck
      @docs
    end

    # <code-block> := <mixin> | <nop>
    # <code-block> := <mixin-declaration> | <var-declaration> | <nop>
    def code_block
      if look("@", "mixin")
        mixin
        return mixin_declaration
      elsif look(:ident)
        name = css_ident
        if name =~ /\A\$/ && look(":")
          {:type => :css_var, :name => name}
        else
          {:type => :nop}
        end
      else
        {:type => :nop}
        var = var_declaration
        return var if var
      end

      return {:type => :nop}
    end

    # <mixin> := "@mixin" <css-ident>
    def mixin
    # <mixin-declaration> := "@mixin" <css-ident>
    def mixin_declaration
      match("@", "mixin")
      return {
        :type => :css_mixin,
@@ -53,6 +49,37 @@ module JsDuck
      }
    end

    # <var> := <css-ident> ":" <css-value>
    def var_declaration
      name = css_ident
      return nil unless name =~ /\A\$/ && look(":")

      match(":")
      val = css_value
      return {
        :type => :css_var,
        :name => name,
        :value => {
          :default => val,
          :type => value_type(val),
        }
      }
    end

    # <css-value> := ...anything up to... [ ";" | "}" ]
    def css_value
      val = []
      while !look(";") && !look("}") && !look("!", :default)
        tok = @lex.next(true)
        if tok[:type] == :string
          val << '"' + tok[:value] + '"'
        else
          val << tok[:value]
        end
      end
      val.join("");
    end

    # <css-ident> := <ident>  [ "-" <ident> ]*
    def css_ident
      chain = [match(:ident)]
@@ -62,6 +89,34 @@ module JsDuck
      return chain.join("-")
    end

    # Determines type of CSS value
    def value_type(val)
      case val
      when /\A([0-9]+(\.[0-9]*)?|\.[0-9]+)\Z/
        "number"
      when /\A([0-9]+(\.[0-9]*)?|\.[0-9]+)[a-z]+\Z/
        "measurement"
      when /\A([0-9]+(\.[0-9]*)?|\.[0-9]+)?%\Z/
        "percentage"
      when /\A(true|false)\Z/
        "boolean"
      when /\A".*"\Z/
        "string"
      when /\A#[0-9a-fA-F]{3}\Z/
        "color"
      when /\A#[0-9a-fA-F]{6}\Z/
        "color"
      when /\A(rgb|hsl)a?\(.*\)\Z/
        "color"
      when /\A(black|silver|gray|white|maroon|red|purple|fuchsia|green|lime|olive|yellow|navy|blue|teal|aqua|orange)\Z/
        "color"
      when /\Atransparent\Z/
        "color"
      else
        nil
      end
    end

    # Matches all arguments, returns the value of last match
    # When the whole sequence doesn't match, throws exception
    def match(*args)
+2 −0
Original line number Diff line number Diff line
@@ -287,6 +287,8 @@ module JsDuck
          elsif code[:right][:type] == :literal && code[:right][:class] != nil
            return code[:right][:class]
          end
        elsif code[:type] == :css_var && code[:value][:type] != nil
          return code[:value][:type]
        end
      end
      return "Object"
+84 −0
Original line number Diff line number Diff line
@@ -68,6 +68,90 @@ describe JsDuck::Aggregator do
    it "detects variable name" do
      @doc[:name].should == "$button-height"
    end
    it "detects variable type" do
      @doc[:type].should == "measurement"
    end
  end

  describe "$var-name: value followed by !default" do
    before do
      @doc = parse(<<-EOCSS)[0]
        /** */
        $button-height: 25px !default;
      EOCSS
    end

    it "detects variable" do
      @doc[:tagname].should == :css_var
    end
    it "detects variable type" do
      @doc[:type].should == "measurement"
    end
  end

  def detect_type(value)
    return parse(<<-EOCSS)[0][:type]
      /** */
      $foo: #{value};
    EOCSS
  end

  describe "auto-detection of CSS variable types" do
    it "detects integer" do
      detect_type("15").should == "number"
    end
    it "detects float" do
      detect_type("15.6").should == "number"
    end
    it "detects float begging with dot" do
      detect_type(".6").should == "number"
    end
    it "detects measurement" do
      detect_type("15em").should == "measurement"
    end
    it "detects percentage" do
      detect_type("99.9%").should == "percentage"
    end
    it "detects boolean true" do
      detect_type("true").should == "boolean"
    end
    it "detects boolean false" do
      detect_type("false").should == "boolean"
    end
    it "detects string" do
      detect_type('"Hello"').should == "string"
    end
    it "detects #000 color" do
      detect_type("#F0a").should == "color"
    end
    it "detects #000000 color" do
      detect_type("#FF00aa").should == "color"
    end
    it "detects rgb(...) color" do
      detect_type("rgb(255, 0, 0)").should == "color"
    end
    it "detects rgba(...) color" do
      detect_type("rgba(100%, 0%, 0%, 0.5)").should == "color"
    end
    it "detects hsl(...) color" do
      detect_type("hsl(255, 0, 0)").should == "color"
    end
    it "detects hsla(...) color" do
      detect_type("hsla(100%, 0%, 0%, 0.5)").should == "color"
    end

    # basic CSS color keywords
    "black silver gray white maroon red purple fuchsia green lime olive yellow navy blue teal aqua".split(/ /).each do |c|
      it "detects #{c} color keyword" do
        detect_type(c).should == "color"
      end
    end
    it "detects wide-supported orange color keyword" do
      detect_type("orange").should == "color"
    end
    it "detects transparent color keyword" do
      detect_type("transparent").should == "color"
    end
  end

  describe "CSS doc-comment followed by @mixin" do