Class: JsDuck::Doc::Parser

Inherits:
Scanner
  • Object
show all
Defined in:
lib/jsduck/doc/parser.rb

Overview

Parses doc-comment into array of @tags

For each @tag it produces Hash like the following:

{
  :tagname => :cfg/:property/:type/:extends/...,
  :doc => "Some documentation for this tag",
  ...@tag specific stuff like :name, :type, and so on...
}

When doc-comment begins with comment, not preceded by @tag, then the comment will be placed into Hash with :tagname => :default.

Unrecognized @tags are left as is into documentation as if they were normal text.

@example, @img, @link and @video are parsed separately in JsDuck::DocFormatter.

Instance Attribute Summary

Attributes inherited from Scanner

#input

Instance Method Summary collapse

Methods inherited from Scanner

#hw, #ident, #ident_chain, #initialize, #look, #match, #skip_white, #standard_tag, #warn

Constructor Details

This class inherits a constructor from JsDuck::Doc::Scanner

Instance Method Details

#add_tag(tag) ⇒ Object

Appends new @tag to parsed tags list



64
65
66
67
68
69
70
71
# File 'lib/jsduck/doc/parser.rb', line 64

def add_tag(tag)
  @tags << tag

  if tag[:doc] == :multiline
    tag[:doc] = ""
    @multiline_tag = tag
  end
end

#indented_as_code?Boolean

Returns:

  • (Boolean)


129
130
131
# File 'lib/jsduck/doc/parser.rb', line 129

def indented_as_code?
  @multiline_tag[:doc] =~ /^ {4,}[^\n]*\z/
end

#parse(input, filename = "", linenr = 0) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
# File 'lib/jsduck/doc/parser.rb', line 30

def parse(input, filename="", linenr = 0)
  @position = {:filename => filename, :linenr => linenr}
  @tags = []
  @non_repeatable_tags = {}
  @input = StringScanner.new(Doc::Comment.purify(input))

  parse_loop

  strip_docs
  @tags
end

#parse_at_tagObject

Processes anything beginning with @-sign.

  • When @ is not followed by any word chars, do nothing.

  • When it’s one of the builtin tags, process it as such.

  • When it’s something else, print a warning.



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
# File 'lib/jsduck/doc/parser.rb', line 79

def parse_at_tag
  match(/@/)
  name = look(/\w+/)

  if !name
    # ignore
  elsif tag = TagRegistry.get_by_pattern(name)
    match(/\w+/)
    hw # Skip the whitespace right after the tag.

    tags = tag.parse_doc(self, @position)
    if tags.is_a?(Hash)
      add_tag(tags)
    elsif tags.is_a?(Array)
      tags.each {|t| add_tag(t) }
    end

    if !tag.repeatable
      if @non_repeatable_tags[name]
        warn(:tag_repeated, "@#{name} tag can occur only once per doc-comment")
      end
      @non_repeatable_tags[name] = true
    end

    skip_white
  else
    warn(:tag, "Unsupported tag: @#{name}", [name.to_sym])
    @multiline_tag[:doc] += "@"
  end
end

#parse_loopObject

The main loop of the DocParser



51
52
53
54
55
56
57
58
59
60
61
# File 'lib/jsduck/doc/parser.rb', line 51

def parse_loop
  add_tag({:tagname => :doc, :doc => :multiline})

  while !@input.eos? do
    if look(/@/)
      parse_at_tag
    elsif look(/[^@]/)
      skip_to_next_at_tag
    end
  end
end

#prev_char_is_whitespace?Boolean

Returns:

  • (Boolean)


125
126
127
# File 'lib/jsduck/doc/parser.rb', line 125

def prev_char_is_whitespace?
  @multiline_tag[:doc][-1,1] =~ /\s/
end

#skip_to_next_at_tagObject

Skips until the beginning of next @tag.

There must be space before the next @tag - this ensures that we don’t detect tags inside “[email protected]” or “@link”.

Also check that the @tag is not part of an indented code block - in which case we also ignore the tag.



117
118
119
120
121
122
123
# File 'lib/jsduck/doc/parser.rb', line 117

def skip_to_next_at_tag
  @multiline_tag[:doc] += match(/[^@]+/)

  while look(/@/) && (!prev_char_is_whitespace? || indented_as_code?)
    @multiline_tag[:doc] += match(/@+[^@]+/)
  end
end

#strip_docsObject

The parsing process can leave whitespace at the ends of doc-strings, here we get rid of it.



44
45
46
47
48
# File 'lib/jsduck/doc/parser.rb', line 44

def strip_docs
  @tags.each do |tag|
    tag[:doc].strip! if tag[:doc]
  end
end