murdoc 0.1.13 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,56 @@
1
+ module Murdoc
2
+ module Languages
3
+ # Base language module
4
+ #
5
+ # Any new language module should inherit from Base, redefine .extensions
6
+ # and .comment_symbols methods, if needed, and add itself to Languages.map
7
+ # map.
8
+ class Base
9
+ def self.applies_for?(filename)
10
+ if extensions.include?(File.extname(filename).sub(/^\./, ''))
11
+ true
12
+ else
13
+ false
14
+ end
15
+ end
16
+
17
+ def self.annotation_only?
18
+ false
19
+ end
20
+
21
+ def self.extensions
22
+ []
23
+ end
24
+
25
+ def self.comment_symbols
26
+ {
27
+ single_line: nil,
28
+ multiline: nil
29
+ }
30
+ end
31
+
32
+ def self.name
33
+ super.sub(/^(.*::)?([^:]+)$/, '\\2').
34
+ gsub(/([a-z])([A-Z])/, '\\1_\\2').
35
+ downcase.to_sym
36
+ end
37
+ end
38
+
39
+ def self.map
40
+ @map ||= {}
41
+ end
42
+
43
+ def self.list
44
+ map.values
45
+ end
46
+
47
+ def self.get(name)
48
+ map.fetch(name, Base)
49
+ end
50
+
51
+ def self.detect(filename)
52
+ name, lang = map.detect {|name, lang| lang.applies_for?(filename) }
53
+ name
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,23 @@
1
+ # Html language module
2
+ module Murdoc
3
+ module Languages
4
+ class Coffeescript < Base
5
+ def self.comment_symbols
6
+ {
7
+ single_line: '#',
8
+ multiline: {
9
+ :begin => "###",
10
+ :end => "###"
11
+ }
12
+ }
13
+ end
14
+
15
+ def self.extensions
16
+ ['coffee']
17
+ end
18
+ end
19
+
20
+ self.map[:coffee] = Coffeescript
21
+ self.map[:coffeescript] = Coffeescript
22
+ end
23
+ end
@@ -1,43 +1,22 @@
1
1
  # Html language module
2
2
  module Murdoc
3
3
  module Languages
4
- module Html
5
- module Annotator
6
- def self.included(base)
7
- base.extend ClassMethods
8
- end
9
-
10
- module ClassMethods
11
- protected
12
- def detect_source_type_from_filename(filename)
13
- if File.extname(filename) == ".html"
14
- :html
15
- else
16
- super if defined?(super)
17
- end
18
- end
19
- end
4
+ class Html < Base
5
+ def self.comment_symbols
6
+ {
7
+ single_line: nil,
8
+ multiline: {
9
+ :begin => "<!--",
10
+ :end => "-->"
11
+ }
12
+ }
20
13
  end
21
14
 
22
- module CommentSymbols
23
- protected
24
- def comment_symbols
25
- if source_type == "html"
26
- {:single_line => nil, :multiline => {:begin => "<!--", :end => "-->"}}
27
- else
28
- super if defined?(super)
29
- end
30
- end
15
+ def self.extensions
16
+ ['html']
31
17
  end
32
18
  end
33
- end
34
-
35
- class Annotator
36
- include Languages::Html::Annotator
37
- include Languages::Html::CommentSymbols
38
- end
39
19
 
40
- class Paragraph
41
- include Languages::Html::CommentSymbols
20
+ self.map[:html] = Html
42
21
  end
43
22
  end
@@ -1,43 +1,22 @@
1
- # Javascript language module
2
1
  module Murdoc
3
2
  module Languages
4
- module Javascript
5
- module Annotator
6
- def self.included(base)
7
- base.extend ClassMethods
8
- end
9
-
10
- module ClassMethods
11
- protected
12
- def detect_source_type_from_filename(filename)
13
- if File.extname(filename) == ".js"
14
- :javascript
15
- else
16
- super if defined?(super)
17
- end
18
- end
19
- end
3
+ class Javascript < Base
4
+ def self.comment_symbols
5
+ {
6
+ single_line: '//',
7
+ multiline: {
8
+ :begin => "/*",
9
+ :end => "*/"
10
+ }
11
+ }
20
12
  end
21
13
 
22
- module CommentSymbols
23
- protected
24
- def comment_symbols
25
- if source_type == "javascript"
26
- {:single_line => "//", :multiline => {:begin => "/*", :end => "*/"}}
27
- else
28
- super if defined?(super)
29
- end
30
- end
14
+ def self.extensions
15
+ ['js']
31
16
  end
32
17
  end
33
- end
34
-
35
- class Annotator
36
- include Languages::Javascript::Annotator
37
- include Languages::Javascript::CommentSymbols
38
- end
39
18
 
40
- class Paragraph
41
- include Languages::Javascript::CommentSymbols
19
+ self.map[:js] = Javascript
20
+ self.map[:javascript] = Javascript
42
21
  end
43
- end
22
+ end
@@ -0,0 +1,24 @@
1
+ # Html language module
2
+ module Murdoc
3
+ module Languages
4
+ class Markdown < Base
5
+ def self.comment_symbols
6
+ {
7
+ single_line: nil,
8
+ multiline: nil
9
+ }
10
+ end
11
+
12
+ def self.annotation_only?
13
+ true
14
+ end
15
+
16
+ def self.extensions
17
+ ['markdown', 'md']
18
+ end
19
+ end
20
+
21
+ self.map[:md] = Markdown
22
+ self.map[:markdown] = Markdown
23
+ end
24
+ end
@@ -1,43 +1,22 @@
1
1
  # Ruby language module
2
2
  module Murdoc
3
3
  module Languages
4
- module Ruby
5
- module Annotator
6
- def self.included(base)
7
- base.extend ClassMethods
8
- end
9
-
10
- module ClassMethods
11
- protected
12
- def detect_source_type_from_filename(filename)
13
- if File.extname(filename) == ".rb"
14
- :ruby
15
- else
16
- super if defined?(super)
17
- end
18
- end
19
- end
4
+ class Ruby < Base
5
+ def self.comment_symbols
6
+ {
7
+ single_line: '#',
8
+ multiline: {
9
+ :begin => "=begin",
10
+ :end => "=end"
11
+ }
12
+ }
20
13
  end
21
14
 
22
- module CommentSymbols
23
- protected
24
- def comment_symbols
25
- if source_type == "ruby"
26
- {:single_line => "#", :multiline => {:begin => "=begin", :end => "=end"}}
27
- else
28
- super if defined?(super)
29
- end
30
- end
15
+ def self.extensions
16
+ ['rb']
31
17
  end
32
18
  end
33
- end
34
-
35
- class Annotator
36
- include Languages::Ruby::Annotator
37
- include Languages::Ruby::CommentSymbols
38
- end
39
19
 
40
- class Paragraph
41
- include Languages::Ruby::CommentSymbols
20
+ self.map[:ruby] = Ruby
42
21
  end
43
- end
22
+ end
@@ -13,48 +13,12 @@ module Murdoc
13
13
  attr_accessor :annotation
14
14
  attr_accessor :source_type
15
15
  attr_accessor :starting_line
16
- attr_accessor :options
17
16
 
18
- def initialize(source, annotation, starting_line = 0, source_type = nil, options ={})
17
+ def initialize(source, annotation, starting_line = 0, source_type = nil)
19
18
  self.source = source
20
19
  self.annotation = annotation
21
20
  self.starting_line = starting_line
22
21
  self.source_type = source_type
23
- self.options = options
24
- end
25
-
26
- def source_type
27
- @source_type
28
- end
29
-
30
- def source_type=(source_type)
31
- @source_type = source_type.to_s
32
- end
33
-
34
-
35
- def formatted_annotation
36
- if defined?(Markdown)
37
- Markdown.new(annotation, :smart).to_html
38
- else
39
- Kramdown::Document.new(annotation, :input => :markdown).to_html
40
- end
41
- end
42
-
43
- def formatted_source
44
- @formatted_source ||= if pygments_installed? && options[:highlight_source]
45
- IO.popen("pygmentize -l #{source_type} -O encoding=UTF8 -f html -O nowrap", "w+") do |pipe|
46
- pipe.puts source
47
- pipe.close_write
48
- pipe.read
49
- end
50
- else
51
- CGI.escapeHTML(source)
52
- end
53
- end
54
-
55
- protected
56
- def pygments_installed?
57
- @@pygments_installed ||= ENV['PATH'].split(':').any? { |dir| File.exist?("#{dir}/pygmentize") }
58
22
  end
59
23
  end
60
24
  end
@@ -1,7 +1,7 @@
1
1
  require "haml"
2
2
 
3
3
  module Murdoc
4
- class Formatter
4
+ class Renderer
5
5
  attr_accessor :template
6
6
 
7
7
  def initialize(template_or_filename)
@@ -16,4 +16,4 @@ module Murdoc
16
16
  ::Haml::Engine.new(template).render(self, locals)
17
17
  end
18
18
  end
19
- end
19
+ end
@@ -0,0 +1,138 @@
1
+ require 'strscan'
2
+
3
+ module Murdoc
4
+ class Scanner
5
+ attr_reader :language
6
+
7
+ def initialize(language)
8
+ @language = language
9
+ end
10
+
11
+ def call(source, do_not_count_comment_lines = false)
12
+ paragraphs = []
13
+ ss = StringScanner.new(source)
14
+ line = i = src_line = 0
15
+
16
+ loop do
17
+ comment_lines = []
18
+ code_lines = []
19
+
20
+ # Multi line comments
21
+ if has_mlc?
22
+ while (ss.scan(mlcb_regex))
23
+ comment = ''
24
+
25
+ while (!ss.eos? && !ss.match?(/.*#{mlce_regex}/))
26
+ i += 1
27
+ comment << ss.scan(/.*?$/)
28
+ comment << ss.getch.to_s
29
+ end
30
+
31
+ if (fragment = ss.scan(/.*#{mlce_regex}/))
32
+ comment << fragment.sub(mlce_regex, '')
33
+ end
34
+
35
+ ss.scan(/[ \t]*\n/) # skip trailing whitespace and a newline
36
+ comment_lines << remove_common_space_prefix(comment)
37
+ end
38
+ end
39
+
40
+ # Single line comments
41
+ if has_slc?
42
+ while (ss.scan(slc_regex))
43
+ comment = ''
44
+ comment << ss.scan(/.*?$/)
45
+ comment << ss.getch.to_s
46
+ comment_lines << comment
47
+ i += 1
48
+ end
49
+ end
50
+
51
+
52
+ # Code
53
+ empty_leading_lines_count = skip_empty_lines(ss)
54
+ i += empty_leading_lines_count
55
+ src_line += empty_leading_lines_count
56
+
57
+ line = do_not_count_comment_lines ? src_line : i
58
+ while (!comment_start?(ss) && !ss.eos?)
59
+ code = ss.scan(/^.*?$/)
60
+ code << ss.getch.to_s
61
+ code_lines << code
62
+ i += 1
63
+ src_line += 1
64
+ end
65
+
66
+ code = post_process_code(code_lines.join(''))
67
+ comments = post_process_comments(comment_lines.join(''))
68
+
69
+ paragraphs << Paragraph.new(code,
70
+ comments,
71
+ line,
72
+ language.name)
73
+
74
+ break if ss.eos?
75
+ end
76
+
77
+ paragraphs
78
+ end
79
+
80
+ protected
81
+
82
+ def post_process_code(code)
83
+ code.rstrip
84
+ end
85
+
86
+ def post_process_comments(comments)
87
+ comments.strip.gsub(/^\s(\S)/, '\\1')
88
+ end
89
+
90
+ def comment_start?(ss)
91
+ (has_slc? && ss.match?(slc_regex)) ||
92
+ (has_mlc? && ss.match?(mlcb_regex))
93
+ end
94
+
95
+ def skip_empty_lines(ss)
96
+ i = 0
97
+ while (ss.scan(/\s*?$/) && !ss.eos?)
98
+ i += 1
99
+ ss.getch
100
+ end
101
+ i
102
+ end
103
+
104
+ def remove_common_space_prefix(str)
105
+ lines = str.split("\n")
106
+ # delete empty leading and trailing lines
107
+ lines.delete_at(0) while lines[0] && lines[0].empty?
108
+ lines.delete_at(-1) while lines[-1] && lines[-1].empty?
109
+
110
+ prefix_lengths = lines.map {|l| l.match(/^( *)/)[1].length }.reject(&:zero?)
111
+ prefix = ' ' * (prefix_lengths.min || 0)
112
+ lines.map {|line| line.sub(/^#{prefix}/, '') }.join("\n")
113
+ end
114
+
115
+ def has_slc?
116
+ !!language.comment_symbols[:single_line]
117
+ end
118
+
119
+ def slc_regex
120
+ return @slc_regex unless @slc_regex.nil?
121
+ @slc_regex = has_slc? && /^[ \t]*#{Regexp.escape(language.comment_symbols[:single_line])}/
122
+ end
123
+
124
+ def has_mlc?
125
+ language.comment_symbols[:multiline] &&
126
+ language.comment_symbols[:multiline][:begin] &&
127
+ language.comment_symbols[:multiline][:end]
128
+ end
129
+
130
+ def mlcb_regex
131
+ has_mlc? && /^[ \t]*#{Regexp.escape(language.comment_symbols[:multiline][:begin])}/
132
+ end
133
+
134
+ def mlce_regex
135
+ has_mlc? && /#{Regexp.escape(language.comment_symbols[:multiline][:end])}/
136
+ end
137
+ end
138
+ end