Loading lib/jsduck/doc_type.rb 0 → 100644 +111 −0 Original line number Diff line number Diff line module JsDuck # Detects the type of documentation object: class, method, cfg, etc class DocType # Given parsed documentation and code, returns the tagname for # documentation item. # # @param docs parsing result from DocParser # @param ast :code from Result of EsprimaParser # @returns One of: :class, :method, :event, :cfg, :property, :css_var # def detect(docs, ast) doc_map = build_doc_map(docs) ast = ast || {} if doc_map[:class] :class elsif doc_map[:event] :event elsif doc_map[:method] :method elsif doc_map[:property] || doc_map[:type] :property elsif doc_map[:css_var] :css_var elsif doc_map[:cfg] && doc_map[:cfg].length == 1 # When just one @cfg, avoid treating it as @class :cfg else exp = expression?(ast) ? ast["expression"] : nil if exp && call?(exp) && ext_define?(exp["callee"]) :class elsif exp && assignment?(exp) && class_name?(exp["left"]) :class elsif function?(ast) && class_name?(ast["id"]) :class elsif ast[:type] == :css_mixin :css_mixin elsif doc_map[:cfg] :cfg elsif function?(ast) :method elsif exp && assignment?(exp) && function?(exp["right"]) :method elsif doc_map[:return] || doc_map[:param] :method else :property end end end private def expression?(ast) ast["type"] == "ExpressionStatement" end def call?(ast) ast["type"] == "CallExpression" end def assignment?(ast) ast["type"] == "AssignmentExpression" end def ext_define?(callee) ["Ext.define", "Ext.ClassManager.create"].include?(to_s(callee)) end def function?(ast) ast["type"] == "FunctionDeclaration" || ast["type"] == "FunctionExpression" end # Class name begins with upcase char def class_name?(ast) return to_s(ast).split(/\./).last =~ /\A[A-Z]/ end def to_s(ast) case ast["type"] when "MemberExpression" if ast["computed"] to_s(ast["object"]) + "[" + to_s(ast["property"]) + "]" else to_s(ast["object"]) + "." + to_s(ast["property"]) end when "Identifier" ast["name"] when "Literal" ast["value"] end end # Build map of at-tags for quick lookup def build_doc_map(docs) map = {} docs.each do |tag| if map[tag[:tagname]] map[tag[:tagname]] << tag else map[tag[:tagname]] = [tag] end end map end end end spec/doc_type_spec.rb 0 → 100644 +109 −0 Original line number Diff line number Diff line require "jsduck/doc_type" require "jsduck/esprima_parser" require "jsduck/css_parser" require "jsduck/doc_parser" describe JsDuck::DocType do def detect(string, type = :js) if type == :css node = JsDuck::CssParser.new(string).parse[0] else node = JsDuck::EsprimaParser.new(string).parse[0] end doc_parser = JsDuck::DocParser.new node[:comment] = doc_parser.parse(node[:comment]) return JsDuck::DocType.new.detect(node[:comment], node[:code]) end describe "detects as class" do it "@class tag" do detect("/** @class */").should == :class end it "function beginning with uppercase letter" do detect("/** */ function MyClass() {}").should == :class end it "function assignment to uppercase name" do detect("/** */ MyClass = function() {}").should == :class end it "Ext.extend()" do detect("/** */ MyClass = Ext.extend(Your.Class, { });").should == :class end it "Ext.define()" do detect(<<-EOS).should == :class /** */ Ext.define('MyClass', { }); EOS end it "Ext.ClassManager.create()" do detect(<<-EOS).should == :class /** */ Ext.ClassManager.create('MyClass', { }); EOS end end describe "detects as method" do it "@method tag" do detect("/** @method */").should == :method end it "function beginning with underscore" do detect("/** */ function _Foo() {}").should == :method end it "lowercase function name" do detect("/** */ function foo() {}").should == :method end it "assignment of function" do detect("/** */ foo = function() {}").should == :method end end describe "detects as event" do it "@event tag" do detect("/** @event */").should == :event end end describe "detects as config" do it "@cfg tag" do detect("/** @cfg */").should == :cfg end end describe "detects as property" do it "@property tag" do detect("/** @property */").should == :property end it "@type tag" do detect("/** @type Foo */").should == :property end it "empty doc-comment with no code" do detect("/** */").should == :property end end describe "detects as css variable" do it "@var tag" do detect("/** @var */").should == :css_var end end describe "detects as css mixin" do it "@mixin in code" do detect("/** */ @mixin foo-bar {}", :css).should == :css_mixin end end end Loading
lib/jsduck/doc_type.rb 0 → 100644 +111 −0 Original line number Diff line number Diff line module JsDuck # Detects the type of documentation object: class, method, cfg, etc class DocType # Given parsed documentation and code, returns the tagname for # documentation item. # # @param docs parsing result from DocParser # @param ast :code from Result of EsprimaParser # @returns One of: :class, :method, :event, :cfg, :property, :css_var # def detect(docs, ast) doc_map = build_doc_map(docs) ast = ast || {} if doc_map[:class] :class elsif doc_map[:event] :event elsif doc_map[:method] :method elsif doc_map[:property] || doc_map[:type] :property elsif doc_map[:css_var] :css_var elsif doc_map[:cfg] && doc_map[:cfg].length == 1 # When just one @cfg, avoid treating it as @class :cfg else exp = expression?(ast) ? ast["expression"] : nil if exp && call?(exp) && ext_define?(exp["callee"]) :class elsif exp && assignment?(exp) && class_name?(exp["left"]) :class elsif function?(ast) && class_name?(ast["id"]) :class elsif ast[:type] == :css_mixin :css_mixin elsif doc_map[:cfg] :cfg elsif function?(ast) :method elsif exp && assignment?(exp) && function?(exp["right"]) :method elsif doc_map[:return] || doc_map[:param] :method else :property end end end private def expression?(ast) ast["type"] == "ExpressionStatement" end def call?(ast) ast["type"] == "CallExpression" end def assignment?(ast) ast["type"] == "AssignmentExpression" end def ext_define?(callee) ["Ext.define", "Ext.ClassManager.create"].include?(to_s(callee)) end def function?(ast) ast["type"] == "FunctionDeclaration" || ast["type"] == "FunctionExpression" end # Class name begins with upcase char def class_name?(ast) return to_s(ast).split(/\./).last =~ /\A[A-Z]/ end def to_s(ast) case ast["type"] when "MemberExpression" if ast["computed"] to_s(ast["object"]) + "[" + to_s(ast["property"]) + "]" else to_s(ast["object"]) + "." + to_s(ast["property"]) end when "Identifier" ast["name"] when "Literal" ast["value"] end end # Build map of at-tags for quick lookup def build_doc_map(docs) map = {} docs.each do |tag| if map[tag[:tagname]] map[tag[:tagname]] << tag else map[tag[:tagname]] = [tag] end end map end end end
spec/doc_type_spec.rb 0 → 100644 +109 −0 Original line number Diff line number Diff line require "jsduck/doc_type" require "jsduck/esprima_parser" require "jsduck/css_parser" require "jsduck/doc_parser" describe JsDuck::DocType do def detect(string, type = :js) if type == :css node = JsDuck::CssParser.new(string).parse[0] else node = JsDuck::EsprimaParser.new(string).parse[0] end doc_parser = JsDuck::DocParser.new node[:comment] = doc_parser.parse(node[:comment]) return JsDuck::DocType.new.detect(node[:comment], node[:code]) end describe "detects as class" do it "@class tag" do detect("/** @class */").should == :class end it "function beginning with uppercase letter" do detect("/** */ function MyClass() {}").should == :class end it "function assignment to uppercase name" do detect("/** */ MyClass = function() {}").should == :class end it "Ext.extend()" do detect("/** */ MyClass = Ext.extend(Your.Class, { });").should == :class end it "Ext.define()" do detect(<<-EOS).should == :class /** */ Ext.define('MyClass', { }); EOS end it "Ext.ClassManager.create()" do detect(<<-EOS).should == :class /** */ Ext.ClassManager.create('MyClass', { }); EOS end end describe "detects as method" do it "@method tag" do detect("/** @method */").should == :method end it "function beginning with underscore" do detect("/** */ function _Foo() {}").should == :method end it "lowercase function name" do detect("/** */ function foo() {}").should == :method end it "assignment of function" do detect("/** */ foo = function() {}").should == :method end end describe "detects as event" do it "@event tag" do detect("/** @event */").should == :event end end describe "detects as config" do it "@cfg tag" do detect("/** @cfg */").should == :cfg end end describe "detects as property" do it "@property tag" do detect("/** @property */").should == :property end it "@type tag" do detect("/** @type Foo */").should == :property end it "empty doc-comment with no code" do detect("/** */").should == :property end end describe "detects as css variable" do it "@var tag" do detect("/** @var */").should == :css_var end end describe "detects as css mixin" do it "@mixin in code" do detect("/** */ @mixin foo-bar {}", :css).should == :css_mixin end end end