kss 0.2.0 → 0.3.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.
- 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
|