Autopopulate comment_chars for known languages
Adding comment characters for bash, c, c++, coffee script, java, javascript, lua, python, ruby, and scheme. Paving the way for block-comment parsing later on... Closes issue #20.
This commit is contained in:
parent
0b392c1094
commit
020e8050bc
88
lib/rocco.rb
88
lib/rocco.rb
@ -73,14 +73,16 @@ end
|
|||||||
# when rendering the final, highlighted file via Mustache. _Defaults
|
# when rendering the final, highlighted file via Mustache. _Defaults
|
||||||
# to `nil` (that is, Mustache will use `./lib/rocco/layout.mustache`)_.
|
# to `nil` (that is, Mustache will use `./lib/rocco/layout.mustache`)_.
|
||||||
#
|
#
|
||||||
# When `block` is given, it must read the contents of the file using whatever
|
|
||||||
# means necessary and return it as a string. With no `block`, the file is read
|
|
||||||
# to retrieve data.
|
|
||||||
class Rocco
|
class Rocco
|
||||||
VERSION = '0.5'
|
VERSION = '0.5'
|
||||||
|
|
||||||
def initialize(filename, sources=[], options={}, &block)
|
def initialize(filename, sources=[], options={}, &block)
|
||||||
@file = filename
|
@file = filename
|
||||||
|
@sources = sources
|
||||||
|
|
||||||
|
# When `block` is given, it must read the contents of the file using
|
||||||
|
# whatever means necessary and return it as a string. With no `block`,
|
||||||
|
# the file is read to retrieve data.
|
||||||
@data =
|
@data =
|
||||||
if block_given?
|
if block_given?
|
||||||
yield
|
yield
|
||||||
@ -93,31 +95,95 @@ class Rocco
|
|||||||
:template_file => nil
|
:template_file => nil
|
||||||
}
|
}
|
||||||
@options = defaults.merge(options)
|
@options = defaults.merge(options)
|
||||||
@sources = sources
|
|
||||||
@comment_pattern = Regexp.new("^\\s*#{@options[:comment_chars]}\s?")
|
|
||||||
@template_file = @options[:template_file]
|
@template_file = @options[:template_file]
|
||||||
|
|
||||||
|
# If we detect a language
|
||||||
|
if detect_language() != "text"
|
||||||
|
# then assign the detected language to `:language`
|
||||||
@options[:language] = detect_language()
|
@options[:language] = detect_language()
|
||||||
|
# and look for some comment characters
|
||||||
|
@options[:comment_chars] = generate_comment_chars()
|
||||||
|
# If we didn't detect a language, but the user provided one, use it
|
||||||
|
# to look around for comment characters to override the default.
|
||||||
|
elsif @options[:language] != defaults[:language]
|
||||||
|
@options[:comment_chars] = generate_comment_chars()
|
||||||
|
end
|
||||||
|
@comment_pattern = Regexp.new("^\\s*#{@options[:comment_chars]}\s?")
|
||||||
|
|
||||||
@sections = highlight(split(parse(@data)))
|
@sections = highlight(split(parse(@data)))
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns `true` if `pygmentize` is available locally, `false` otherwise.
|
# Returns `true` if `pygmentize` is available locally, `false` otherwise.
|
||||||
def pygmentize?
|
def pygmentize?
|
||||||
# Memoize the result, we'll call this a few times
|
# Memoize the result, we'll call this a few times
|
||||||
@pygmentize ||= ENV['PATH'].split(':').any? { |dir| executable?("#{dir}/pygmentize") }
|
@_pygmentize ||= ENV['PATH'].split(':').any? { |dir| executable?("#{dir}/pygmentize") }
|
||||||
end
|
end
|
||||||
|
|
||||||
# If `pygmentize` is available, we can use it to autodetect a file's
|
# If `pygmentize` is available, we can use it to autodetect a file's
|
||||||
# language based on its filename. Filenames without extensions, or with
|
# language based on its filename. Filenames without extensions, or with
|
||||||
# extensions that `pygmentize` doesn't understand will return `text`. In
|
# extensions that `pygmentize` doesn't understand will return `text`.
|
||||||
# that case, we'll fallback to the user-provided `:language` option.
|
# We'll also return `text` if `pygmentize` isn't available.
|
||||||
|
#
|
||||||
|
# We'll memoize the result, as we'll call this a few times.
|
||||||
def detect_language
|
def detect_language
|
||||||
default = @options[:language]
|
@_language ||= begin
|
||||||
if pygmentize?
|
if pygmentize?
|
||||||
lang = %x[pygmentize -N #{@file}].strip!
|
lang = %x[pygmentize -N #{@file}].strip!
|
||||||
( !lang || lang == "text" ) ? default : lang
|
|
||||||
else
|
else
|
||||||
default
|
"text"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Given a file's language, we should be able to autopopulate the
|
||||||
|
# `comment_chars` variables for single-line comments. If we don't
|
||||||
|
# have comment characters on record for a given language, we'll
|
||||||
|
# use the user-provided `:comment_char` option (which defaults to
|
||||||
|
# `#`).
|
||||||
|
#
|
||||||
|
# Comment characters are listed as:
|
||||||
|
#
|
||||||
|
# { :single => "//", :multi_start => "/**", :multi_middle => "*", :multi_end => "*/" }
|
||||||
|
#
|
||||||
|
# `:single` denotes the leading character of a single-line comment.
|
||||||
|
# `:multi_start` denotes the string that should appear alone on a
|
||||||
|
# line of code to begin a block of documentation. `:multi_middle`
|
||||||
|
# denotes the leading character of block comment content, and
|
||||||
|
# `:multi_end` is the string that ought appear alone on a line to
|
||||||
|
# close a block of documentation. That is:
|
||||||
|
#
|
||||||
|
# /** [:multi][:start]
|
||||||
|
# * [:multi][:middle]
|
||||||
|
# * [:multi][:middle]
|
||||||
|
# * [:multi][:middle]
|
||||||
|
# */ [:multi][:end]
|
||||||
|
#
|
||||||
|
# If a language only has one type of comment, the missing type
|
||||||
|
# should be assigned `nil`.
|
||||||
|
#
|
||||||
|
# At the moment, we're only returning `:single`. Consider this
|
||||||
|
# groundwork for block comment parsing.
|
||||||
|
def generate_comment_chars
|
||||||
|
@_commentchar ||= begin
|
||||||
|
language = @options[:language]
|
||||||
|
comment_styles = {
|
||||||
|
"bash" => { :single => "#", :multi => nil },
|
||||||
|
"c" => { :single => "//", :multi => { :start => "/**", :middle => "*", :end => "*/" } },
|
||||||
|
"coffee-script" => { :single => "#", :multi => { :start => "###", :middle => nil, :end => "###" } },
|
||||||
|
"cpp" => { :single => "//", :multi => { :start => "/**", :middle => "*", :end => "*/" } },
|
||||||
|
"java" => { :single => "//", :multi => { :start => "/**", :middle => "*", :end => "*/" } },
|
||||||
|
"js" => { :single => "//", :multi => { :start => "/**", :middle => "*", :end => "*/" } },
|
||||||
|
"lua" => { :single => "--", :multi => nil },
|
||||||
|
"python" => { :single => "#", :multi => { :start => '"""', :middle => nil, :end => '"""' } },
|
||||||
|
"ruby" => { :single => "#", :multi => nil },
|
||||||
|
"scheme" => { :single => ";;", :multi => nil },
|
||||||
|
}
|
||||||
|
|
||||||
|
if comment_styles[language]
|
||||||
|
comment_styles[language][:single]
|
||||||
|
else
|
||||||
|
@options[:comment_chars]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -106,6 +106,29 @@ class RoccoLanguageDetection < Test::Unit::TestCase
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class RoccoAutomaticCommentChars < Test::Unit::TestCase
|
||||||
|
def test_basic_detection
|
||||||
|
r = Rocco.new( 'filename.js' ) { "" }
|
||||||
|
assert_equal "//", r.options[:comment_chars]
|
||||||
|
end
|
||||||
|
def test_fallback_language
|
||||||
|
r = Rocco.new( 'filename.an_extension_with_no_meaning_whatsoever', '', { :language => "js" } ) { "" }
|
||||||
|
assert_equal "//", r.options[:comment_chars]
|
||||||
|
end
|
||||||
|
def test_fallback_default
|
||||||
|
r = Rocco.new( 'filename.an_extension_with_no_meaning_whatsoever' ) { "" }
|
||||||
|
assert_equal "#", r.options[:comment_chars], "`:comment_chars` should be `#` when falling back to defaults."
|
||||||
|
end
|
||||||
|
def test_fallback_user
|
||||||
|
r = Rocco.new( 'filename.an_extension_with_no_meaning_whatsoever', '', { :comment_chars => "user" } ) { "" }
|
||||||
|
assert_equal "user", r.options[:comment_chars], "`:comment_chars` should be the user's default when falling back to user-provided settings."
|
||||||
|
end
|
||||||
|
def test_fallback_user_with_unknown_language
|
||||||
|
r = Rocco.new( 'filename.an_extension_with_no_meaning_whatsoever', '', { :language => "not-a-language", :comment_chars => "user" } ) { "" }
|
||||||
|
assert_equal "user", r.options[:comment_chars], "`:comment_chars` should be the user's default when falling back to user-provided settings."
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
class RoccoIssueTests < Test::Unit::TestCase
|
class RoccoIssueTests < Test::Unit::TestCase
|
||||||
def test_issue07_incorrect_parsing_in_c_mode
|
def test_issue07_incorrect_parsing_in_c_mode
|
||||||
# Precursor to issue #13 below, Rocco incorrectly parsed C/C++
|
# Precursor to issue #13 below, Rocco incorrectly parsed C/C++
|
||||||
|
Loading…
Reference in New Issue
Block a user