kss 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/kss.rb +1 -4
- data/lib/kss/comment_parser.rb +164 -0
- data/lib/kss/parser.rb +11 -90
- data/lib/kss/version.rb +1 -1
- data/test/comment_parser_test.rb +73 -0
- data/test/fixtures/comments.txt +33 -0
- data/test/parser_test.rb +4 -11
- metadata +9 -35
data/lib/kss.rb
CHANGED
@@ -0,0 +1,164 @@
|
|
1
|
+
module Kss
|
2
|
+
# Public: Takes a file path of a text file and extracts comments from it.
|
3
|
+
# Currently accepts two formats:
|
4
|
+
#
|
5
|
+
# // Single line style.
|
6
|
+
# /* Multi-line style. */
|
7
|
+
class CommentParser
|
8
|
+
|
9
|
+
# Public: Is this a single-line comment? // This style
|
10
|
+
#
|
11
|
+
# line - A String of one line of text.
|
12
|
+
#
|
13
|
+
# Returns a boolean.
|
14
|
+
def self.single_line_comment?(line)
|
15
|
+
!!(line =~ /^\s*\/\//)
|
16
|
+
end
|
17
|
+
|
18
|
+
# Public: Is this the start of a multi-line comment? /* This style */
|
19
|
+
#
|
20
|
+
# line - A String of one line of text.
|
21
|
+
#
|
22
|
+
# Returns a boolean.
|
23
|
+
def self.start_multi_line_comment?(line)
|
24
|
+
!!(line =~ /^\s*\/\*/)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Public: Is this the end of a multi-line comment? /* This style */
|
28
|
+
#
|
29
|
+
# line - A String of one line of text.
|
30
|
+
#
|
31
|
+
# Returns a boolean.
|
32
|
+
def self.end_multi_line_comment?(line)
|
33
|
+
return false if self.single_line_comment?(line)
|
34
|
+
!!(line =~ /.*\*\//)
|
35
|
+
end
|
36
|
+
|
37
|
+
# Public: Removes comment identifiers for single-line comments.
|
38
|
+
#
|
39
|
+
# line - A String of one line of text.
|
40
|
+
#
|
41
|
+
# Returns a String.
|
42
|
+
def self.parse_single_line(line)
|
43
|
+
cleaned = line.to_s.sub(/\s*\/\//, '')
|
44
|
+
cleaned.rstrip
|
45
|
+
end
|
46
|
+
|
47
|
+
# Public: Remove comment identifiers for multi-line comments.
|
48
|
+
#
|
49
|
+
# line - A String of one line of text.
|
50
|
+
#
|
51
|
+
# Returns a String.
|
52
|
+
def self.parse_multi_line(line)
|
53
|
+
cleaned = line.to_s.sub(/\s*\/\*/, '')
|
54
|
+
cleaned = cleaned.sub(/\*\//, '')
|
55
|
+
cleaned.rstrip
|
56
|
+
end
|
57
|
+
|
58
|
+
# Public: Initializes a new comment parser object. Does not parse on
|
59
|
+
# initialization.
|
60
|
+
#
|
61
|
+
# file_path - The location of the file to parse as a String.
|
62
|
+
# options - Optional options hash.
|
63
|
+
# :preserve_whitespace - Preserve the whitespace before/after comment
|
64
|
+
# markers (default:false).
|
65
|
+
#
|
66
|
+
def initialize(file_path, options={})
|
67
|
+
@options = options
|
68
|
+
@options[:preserve_whitespace] = false if @options[:preserve_whitespace].nil?
|
69
|
+
@file_path = file_path
|
70
|
+
@blocks = []
|
71
|
+
@parsed = false
|
72
|
+
end
|
73
|
+
|
74
|
+
# Public: The different sections of parsed comment text. A section is
|
75
|
+
# either a multi-line comment block's content, or consecutive lines of
|
76
|
+
# single-line comments.
|
77
|
+
#
|
78
|
+
# Returns an Array of parsed comment Strings.
|
79
|
+
def blocks
|
80
|
+
@parsed ? @blocks : parse_blocks
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
# Parse the file for comment blocks and populate them into @blocks.
|
85
|
+
#
|
86
|
+
# Returns an Array of parsed comment Strings.
|
87
|
+
def parse_blocks
|
88
|
+
File.open @file_path do |file|
|
89
|
+
current_block = nil
|
90
|
+
inside_single_line_block = false
|
91
|
+
inside_multi_line_block = false
|
92
|
+
|
93
|
+
file.each_line do |line|
|
94
|
+
# Parse single-line style
|
95
|
+
if self.class.single_line_comment?(line)
|
96
|
+
parsed = self.class.parse_single_line line
|
97
|
+
if inside_single_line_block
|
98
|
+
current_block += "\n#{parsed}"
|
99
|
+
else
|
100
|
+
current_block = parsed.to_s
|
101
|
+
inside_single_line_block = true
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Parse multi-lines tyle
|
106
|
+
if self.class.start_multi_line_comment?(line) || inside_multi_line_block
|
107
|
+
parsed = self.class.parse_multi_line line
|
108
|
+
if inside_multi_line_block
|
109
|
+
current_block += "\n#{parsed}"
|
110
|
+
else
|
111
|
+
current_block = parsed
|
112
|
+
inside_multi_line_block = true
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# End a multi-line block if detected
|
117
|
+
inside_multi_line_block = false if self.class.end_multi_line_comment?(line)
|
118
|
+
|
119
|
+
# Store the current block if we're done
|
120
|
+
unless self.class.single_line_comment?(line) || inside_multi_line_block
|
121
|
+
@blocks << normalize(current_block) unless current_block.nil?
|
122
|
+
|
123
|
+
inside_single_line_block = false
|
124
|
+
current_block = nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
@parsed = true
|
130
|
+
@blocks
|
131
|
+
end
|
132
|
+
|
133
|
+
# Normalizes the comment block to ignore any consistent preceding
|
134
|
+
# whitespace. Consistent means the same amount of whitespace on every line
|
135
|
+
# of the comment block. Also strips any whitespace at the start and end of
|
136
|
+
# the whole block.
|
137
|
+
#
|
138
|
+
# Returns a String of normalized text.
|
139
|
+
def normalize(text_block)
|
140
|
+
return text_block if @options[:preserve_whitespace]
|
141
|
+
|
142
|
+
# Strip out any preceding [whitespace]* that occur on every line. Not
|
143
|
+
# the smartest, but I wonder if I care.
|
144
|
+
text_block = text_block.gsub(/^(\s*\*+)/, '')
|
145
|
+
|
146
|
+
# Strip consistent indenting by measuring first line's whitespace
|
147
|
+
indent_size = nil
|
148
|
+
unindented = text_block.split("\n").collect do |line|
|
149
|
+
preceding_whitespace = line.scan(/^\s*/)[0].to_s.size
|
150
|
+
indent_size = preceding_whitespace if indent_size.nil?
|
151
|
+
if line == ""
|
152
|
+
""
|
153
|
+
elsif indent_size <= preceding_whitespace && indent_size > 0
|
154
|
+
line.slice(indent_size, line.length - 1)
|
155
|
+
else
|
156
|
+
line
|
157
|
+
end
|
158
|
+
end.join("\n")
|
159
|
+
|
160
|
+
unindented.strip
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
end
|
data/lib/kss/parser.rb
CHANGED
@@ -2,7 +2,10 @@ module Kss
|
|
2
2
|
# Public: The main KSS parser. Takes a directory full of SASS / SCSS / CSS
|
3
3
|
# files and parses the KSS within them.
|
4
4
|
class Parser
|
5
|
-
|
5
|
+
|
6
|
+
# Public: Returns a hash of Sections.
|
7
|
+
attr_accessor :sections
|
8
|
+
|
6
9
|
# Public: Initializes a new parser based on a directory of files. Scans
|
7
10
|
# within the directory recursively for any comment blocks that look like
|
8
11
|
# KSS.
|
@@ -12,73 +15,17 @@ module Kss
|
|
12
15
|
@sections = {}
|
13
16
|
|
14
17
|
Dir["#{base_path}/**/*.*"].each do |filename|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
parse(File.read(filename)).instance_variable_get "@tree"
|
19
|
-
|
20
|
-
# inject less.js into a new V8 context
|
21
|
-
less = Less.instance_variable_get "@less"
|
22
|
-
cxt = V8::Context.new
|
23
|
-
cxt['less'] = less
|
24
|
-
|
25
|
-
# parse node tree for comments
|
26
|
-
parse_v8_node(cxt, tree, filename)
|
27
|
-
else
|
28
|
-
if filename.match(/\.css$/)
|
29
|
-
root_node = Sass::SCSS::Parser.new(File.read(filename), filename).parse
|
30
|
-
else
|
31
|
-
root_node = Sass::Engine.for_file(filename, {}).to_tree
|
32
|
-
end
|
33
|
-
parse_sass_node(root_node, filename)
|
18
|
+
parser = CommentParser.new(filename)
|
19
|
+
parser.blocks.each do |comment_block|
|
20
|
+
add_section comment_block, filename if self.class.kss_block?(comment_block)
|
34
21
|
end
|
35
22
|
end
|
36
23
|
end
|
37
24
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
# filename - The filename String this node is found at.
|
43
|
-
#
|
44
|
-
# Returns the Sass::Tree::Node given.
|
45
|
-
def parse_sass_node parent_node, filename
|
46
|
-
parent_node.children.each do |node|
|
47
|
-
unless node.is_a? Sass::Tree::CommentNode
|
48
|
-
parse_sass_node(node, filename) if node.has_children
|
49
|
-
next
|
50
|
-
end
|
51
|
-
comment_text = self.class.clean_comments node.to_scss
|
52
|
-
add_section node.to_scss, filename
|
53
|
-
end
|
54
|
-
parent_node
|
55
|
-
end
|
56
|
-
|
57
|
-
def parse_v8_node cxt, parent_node, filename
|
58
|
-
parent_node.rules.each do |node|
|
59
|
-
# inject the current to into JS context
|
60
|
-
cxt['node'] = node
|
61
|
-
|
62
|
-
unless cxt.eval "node instanceof less.tree.Comment"
|
63
|
-
parse_v8_node(cxt, node, filename) if cxt.eval 'node.rules != null'
|
64
|
-
|
65
|
-
next
|
66
|
-
end
|
67
|
-
|
68
|
-
add_section node.value, filename
|
69
|
-
end
|
70
|
-
|
71
|
-
parent_node
|
72
|
-
end
|
73
|
-
|
74
|
-
def add_section comment, filename
|
75
|
-
comment_text = self.class.clean_comments comment
|
76
|
-
|
77
|
-
if self.class.kss_block? comment_text
|
78
|
-
base_name = File.basename(filename)
|
79
|
-
section = Section.new(comment_text, base_name)
|
80
|
-
@sections[section.section] = section
|
81
|
-
end
|
25
|
+
def add_section comment_text, filename
|
26
|
+
base_name = File.basename(filename)
|
27
|
+
section = Section.new(comment_text, base_name)
|
28
|
+
@sections[section.section] = section
|
82
29
|
end
|
83
30
|
|
84
31
|
# Public: Takes a cleaned (no comment syntax like // or /* */) comment
|
@@ -92,32 +39,6 @@ module Kss
|
|
92
39
|
possible_reference =~ /Styleguide \d/
|
93
40
|
end
|
94
41
|
|
95
|
-
# Takes the raw comment text including comment syntax and strips all
|
96
|
-
# comment syntax and normalizes the indention and whitespace to generate
|
97
|
-
# a clean comment block.
|
98
|
-
#
|
99
|
-
# Returns a claned comment String.
|
100
|
-
def self.clean_comments(text)
|
101
|
-
text.strip!
|
102
|
-
|
103
|
-
# SASS generated multi-line comment syntax
|
104
|
-
text.gsub!(/(\/\* )?( \*\/)?/, '') # [/* + space] or [space + */]
|
105
|
-
text.gsub!(/\n\s\* ?/, "\n") # * in front of every line
|
106
|
-
|
107
|
-
# SASS generated single-line comment syntax
|
108
|
-
text.gsub!(/\/\/ /, '') # [// + space]
|
109
|
-
text.gsub!(/\/\/\n/, "\n") # [// + carriage return]
|
110
|
-
|
111
|
-
# Manual generated comment syntax
|
112
|
-
text.gsub!(/^\/\*/, '') # starting block
|
113
|
-
text.gsub!(/\*\/$/, '') # ending block
|
114
|
-
|
115
|
-
text.gsub!(/^[ \t]+/, '') # remove leading whitespace
|
116
|
-
|
117
|
-
text.strip!
|
118
|
-
text
|
119
|
-
end
|
120
|
-
|
121
42
|
# Public: Finds the Section for a given styleguide reference.
|
122
43
|
#
|
123
44
|
# Returns a Section for a reference, or a blank Section if none found.
|
data/lib/kss/version.rb
CHANGED
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'test/helper'
|
2
|
+
|
3
|
+
class CommentParser < Kss::Test
|
4
|
+
|
5
|
+
def setup
|
6
|
+
loc = 'test/fixtures/comments.txt'
|
7
|
+
@parsed_comments = Kss::CommentParser.new(loc).blocks
|
8
|
+
end
|
9
|
+
|
10
|
+
test "detects single-line comment syntax" do
|
11
|
+
assert Kss::CommentParser.single_line_comment?("// yuuuuup")
|
12
|
+
assert !Kss::CommentParser.single_line_comment?("nooooope")
|
13
|
+
end
|
14
|
+
|
15
|
+
test "detects start of multi--line comment syntax" do
|
16
|
+
assert Kss::CommentParser.start_multi_line_comment?("/* yuuuuup")
|
17
|
+
assert !Kss::CommentParser.start_multi_line_comment?("nooooope")
|
18
|
+
end
|
19
|
+
|
20
|
+
test "detects end of multi-line comment syntax" do
|
21
|
+
assert Kss::CommentParser.end_multi_line_comment?(" yuuuuup */")
|
22
|
+
assert !Kss::CommentParser.end_multi_line_comment?("nooooope")
|
23
|
+
end
|
24
|
+
|
25
|
+
test "parses the single-line comment syntax" do
|
26
|
+
assert_equal " yuuuuup", Kss::CommentParser.parse_single_line("// yuuuuup")
|
27
|
+
end
|
28
|
+
|
29
|
+
test "parses the multi-line comment syntax" do
|
30
|
+
assert_equal " yuuuup", Kss::CommentParser.parse_multi_line("/* yuuuup */")
|
31
|
+
end
|
32
|
+
|
33
|
+
test "finds single-line comment styles" do
|
34
|
+
expected = <<comment
|
35
|
+
This comment block has comment identifiers on every line.
|
36
|
+
|
37
|
+
Fun fact: this is Kyle's favorite comment syntax!
|
38
|
+
comment
|
39
|
+
assert @parsed_comments.include? expected.rstrip
|
40
|
+
end
|
41
|
+
|
42
|
+
test "finds block-style comment styles" do
|
43
|
+
expected = <<comment
|
44
|
+
This comment block is a block-style comment syntax.
|
45
|
+
|
46
|
+
There's only two identifier across multiple lines.
|
47
|
+
comment
|
48
|
+
assert @parsed_comments.include? expected.rstrip
|
49
|
+
|
50
|
+
|
51
|
+
expected = <<comment
|
52
|
+
This is another common multi-line comment style.
|
53
|
+
|
54
|
+
It has stars at the begining of every line.
|
55
|
+
comment
|
56
|
+
assert @parsed_comments.include? expected.rstrip
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
test "handles mixed styles" do
|
61
|
+
expected = "This comment has a /* comment */ identifier inside of it!"
|
62
|
+
assert @parsed_comments.include? expected
|
63
|
+
|
64
|
+
expected = "Look at my //cool// comment art!"
|
65
|
+
assert @parsed_comments.include? expected
|
66
|
+
end
|
67
|
+
|
68
|
+
test "handles indented comments" do
|
69
|
+
assert @parsed_comments.include? "Indented single-line comment."
|
70
|
+
assert @parsed_comments.include? "Indented block comment."
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
This file is used for generic comment parsing across CSS, SCSS, SASS & LESS.
|
2
|
+
|
3
|
+
There's single-line comment styles:
|
4
|
+
|
5
|
+
// This comment block has comment identifiers on every line.
|
6
|
+
//
|
7
|
+
// Fun fact: this is Kyle's favorite comment syntax!
|
8
|
+
|
9
|
+
|
10
|
+
There's block comment styles:
|
11
|
+
|
12
|
+
/* This comment block is a block-style comment syntax.
|
13
|
+
|
14
|
+
There's only two identifier across multiple lines. */
|
15
|
+
|
16
|
+
/* This is another common multi-line comment style.
|
17
|
+
*
|
18
|
+
* It has stars at the begining of every line.
|
19
|
+
*/
|
20
|
+
|
21
|
+
|
22
|
+
Some people do crazy things like mix comment styles:
|
23
|
+
|
24
|
+
// This comment has a /* comment */ identifier inside of it!
|
25
|
+
|
26
|
+
/* Look at my //cool// comment art! */
|
27
|
+
|
28
|
+
|
29
|
+
Indented comments:
|
30
|
+
|
31
|
+
// Indented single-line comment.
|
32
|
+
|
33
|
+
/* Indented block comment. */
|
data/test/parser_test.rb
CHANGED
@@ -85,17 +85,6 @@ comment
|
|
85
85
|
@css_parsed.section('2.1.1').description
|
86
86
|
end
|
87
87
|
|
88
|
-
test "cleans css comments" do
|
89
|
-
assert_equal @cleaned_css_comment,
|
90
|
-
Kss::Parser.clean_comments(@css_comment)
|
91
|
-
assert_equal @cleaned_css_comment,
|
92
|
-
Kss::Parser.clean_comments(@starred_css_comment)
|
93
|
-
assert_equal @cleaned_css_comment,
|
94
|
-
Kss::Parser.clean_comments(@slashed_css_comment)
|
95
|
-
assert_equal @cleaned_css_comment,
|
96
|
-
Kss::Parser.clean_comments(@indented_css_comment)
|
97
|
-
end
|
98
|
-
|
99
88
|
test "parses nested SCSS documents" do
|
100
89
|
assert_equal "Your standard form element.", @scss_parsed.section('3.0.0').description
|
101
90
|
assert_equal "Your standard text input box.", @scss_parsed.section('3.0.1').description
|
@@ -111,4 +100,8 @@ comment
|
|
111
100
|
assert_equal "Your standard text input box.", @sass_parsed.section('3.0.1').description
|
112
101
|
end
|
113
102
|
|
103
|
+
test "public sections returns hash of sections" do
|
104
|
+
assert_equal 2, @css_parsed.sections.count
|
105
|
+
end
|
106
|
+
|
114
107
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: kss
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 19
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 3
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.3.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Kyle Neath
|
@@ -15,39 +15,10 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-02-06 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
22
|
-
name: sass
|
23
|
-
prerelease: false
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 5
|
30
|
-
segments:
|
31
|
-
- 3
|
32
|
-
- 1
|
33
|
-
version: "3.1"
|
34
|
-
type: :runtime
|
35
|
-
version_requirements: *id001
|
36
|
-
- !ruby/object:Gem::Dependency
|
37
|
-
name: less
|
38
|
-
prerelease: false
|
39
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
-
none: false
|
41
|
-
requirements:
|
42
|
-
- - ">="
|
43
|
-
- !ruby/object:Gem::Version
|
44
|
-
hash: 3
|
45
|
-
segments:
|
46
|
-
- 2
|
47
|
-
- 0
|
48
|
-
version: "2.0"
|
49
|
-
type: :runtime
|
50
|
-
version_requirements: *id002
|
20
|
+
dependencies: []
|
21
|
+
|
51
22
|
description: " Inspired by TomDoc, KSS attempts to provide a methodology for writing\n maintainable, documented CSS within a team. Specifically, KSS is a CSS\n structure, documentation specification, and styleguide format.\n\n This is a ruby library for parsing KSS documented CSS and generating\n styleguides.\n"
|
52
23
|
email: kneath@gmail.com
|
53
24
|
executables: []
|
@@ -60,12 +31,15 @@ files:
|
|
60
31
|
- README.md
|
61
32
|
- Rakefile
|
62
33
|
- LICENSE
|
34
|
+
- lib/kss/comment_parser.rb
|
63
35
|
- lib/kss/modifier.rb
|
64
36
|
- lib/kss/parser.rb
|
65
37
|
- lib/kss/section.rb
|
66
38
|
- lib/kss/version.rb
|
67
39
|
- lib/kss.coffee
|
68
40
|
- lib/kss.rb
|
41
|
+
- test/comment_parser_test.rb
|
42
|
+
- test/fixtures/comments.txt
|
69
43
|
- test/fixtures/css/buttons.css
|
70
44
|
- test/fixtures/less/_label.less
|
71
45
|
- test/fixtures/less/buttons.less
|