1
+ // Copyright 2012-2018 The Rust Project Developers. See the COPYRIGHT
2
+ // file at the top-level directory of this distribution and at
3
+ // http://rust-lang.org/COPYRIGHT.
4
+ //
5
+ // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6
+ // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7
+ // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8
+ // option. This file may not be copied, modified, or distributed
9
+ // except according to those terms.
10
+
11
+ use std:: collections:: HashSet ;
12
+ use std:: fs:: File ;
13
+ use std:: hash:: { Hash , Hasher } ;
14
+ use std:: io:: Read ;
15
+ use std:: path:: Path ;
16
+
17
+ macro_rules! try_false {
18
+ ( $e: expr) => ( {
19
+ match $e {
20
+ Ok ( c) => c,
21
+ Err ( e) => {
22
+ eprintln!( "rustdoc: got an error: {}" , e) ;
23
+ return false ;
24
+ }
25
+ }
26
+ } )
27
+ }
28
+
29
+ #[ derive( Debug , Clone , Eq ) ]
30
+ pub struct CssPath {
31
+ pub name : String ,
32
+ pub children : HashSet < CssPath > ,
33
+ }
34
+
35
+ // This PartialEq implementation IS NOT COMMUTATIVE!!!
36
+ //
37
+ // The order is very important: the second object must have all first's rules.
38
+ // However, the first doesn't require to have all second's rules.
39
+ impl PartialEq for CssPath {
40
+ fn eq ( & self , other : & CssPath ) -> bool {
41
+ if self . name != other. name {
42
+ false
43
+ } else {
44
+ for child in & self . children {
45
+ if !other. children . iter ( ) . any ( |c| child == c) {
46
+ return false ;
47
+ }
48
+ }
49
+ true
50
+ }
51
+ }
52
+ }
53
+
54
+ impl Hash for CssPath {
55
+ fn hash < H : Hasher > ( & self , state : & mut H ) {
56
+ self . name . hash ( state) ;
57
+ for x in & self . children {
58
+ x. hash ( state) ;
59
+ }
60
+ }
61
+ }
62
+
63
+ impl CssPath {
64
+ fn new ( name : String ) -> CssPath {
65
+ CssPath {
66
+ name,
67
+ children : HashSet :: new ( ) ,
68
+ }
69
+ }
70
+ }
71
+
72
+ /// All variants contain the position they occur.
73
+ #[ derive( Debug , Clone , Copy ) ]
74
+ enum Events {
75
+ StartLineComment ( usize ) ,
76
+ StartComment ( usize ) ,
77
+ EndComment ( usize ) ,
78
+ InBlock ( usize ) ,
79
+ OutBlock ( usize ) ,
80
+ }
81
+
82
+ impl Events {
83
+ fn get_pos ( & self ) -> usize {
84
+ match * self {
85
+ Events :: StartLineComment ( p) |
86
+ Events :: StartComment ( p) |
87
+ Events :: EndComment ( p) |
88
+ Events :: InBlock ( p) |
89
+ Events :: OutBlock ( p) => p,
90
+ }
91
+ }
92
+
93
+ fn is_comment ( & self ) -> bool {
94
+ match * self {
95
+ Events :: StartLineComment ( _) |
96
+ Events :: StartComment ( _) |
97
+ Events :: EndComment ( _) => true ,
98
+ _ => false ,
99
+ }
100
+ }
101
+ }
102
+
103
+ fn previous_is_line_comment ( events : & [ Events ] ) -> bool {
104
+ if let Some ( & Events :: StartLineComment ( _) ) = events. last ( ) {
105
+ true
106
+ } else {
107
+ false
108
+ }
109
+ }
110
+
111
+ fn is_line_comment ( pos : usize , v : & [ u8 ] , events : & [ Events ] ) -> bool {
112
+ if let Some ( & Events :: StartComment ( _) ) = events. last ( ) {
113
+ return false ;
114
+ }
115
+ pos + 1 < v. len ( ) && v[ pos + 1 ] == b'/'
116
+ }
117
+
118
+ fn load_css_events ( v : & [ u8 ] ) -> Vec < Events > {
119
+ let mut pos = 0 ;
120
+ let mut events = Vec :: with_capacity ( 100 ) ;
121
+
122
+ while pos < v. len ( ) - 1 {
123
+ match v[ pos] {
124
+ b'/' if pos + 1 < v. len ( ) && v[ pos + 1 ] == b'*' => {
125
+ events. push ( Events :: StartComment ( pos) ) ;
126
+ pos += 1 ;
127
+ }
128
+ b'/' if is_line_comment ( pos, v, & events) => {
129
+ events. push ( Events :: StartLineComment ( pos) ) ;
130
+ pos += 1 ;
131
+ }
132
+ b'\n' if previous_is_line_comment ( & events) => {
133
+ events. push ( Events :: EndComment ( pos) ) ;
134
+ }
135
+ b'*' if pos + 1 < v. len ( ) && v[ pos + 1 ] == b'/' => {
136
+ events. push ( Events :: EndComment ( pos + 2 ) ) ;
137
+ pos += 1 ;
138
+ }
139
+ b'{' if !previous_is_line_comment ( & events) => {
140
+ if let Some ( & Events :: StartComment ( _) ) = events. last ( ) {
141
+ pos += 1 ;
142
+ continue
143
+ }
144
+ events. push ( Events :: InBlock ( pos + 1 ) ) ;
145
+ }
146
+ b'}' if !previous_is_line_comment ( & events) => {
147
+ if let Some ( & Events :: StartComment ( _) ) = events. last ( ) {
148
+ pos += 1 ;
149
+ continue
150
+ }
151
+ events. push ( Events :: OutBlock ( pos + 1 ) ) ;
152
+ }
153
+ _ => { }
154
+ }
155
+ pos += 1 ;
156
+ }
157
+ events
158
+ }
159
+
160
+ fn get_useful_next ( events : & [ Events ] , pos : & mut usize ) -> Option < Events > {
161
+ while * pos < events. len ( ) {
162
+ if !events[ * pos] . is_comment ( ) {
163
+ return Some ( events[ * pos] ) ;
164
+ }
165
+ * pos += 1 ;
166
+ }
167
+ None
168
+ }
169
+
170
+ fn inner ( v : & [ u8 ] , events : & [ Events ] , pos : & mut usize ) -> HashSet < CssPath > {
171
+ let mut pathes = Vec :: with_capacity ( 50 ) ;
172
+
173
+ while * pos < events. len ( ) {
174
+ if let Some ( Events :: OutBlock ( _) ) = get_useful_next ( events, pos) {
175
+ println ! ( "00 => {:?}" , events[ * pos] ) ;
176
+ * pos += 1 ;
177
+ break
178
+ }
179
+ println ! ( "a => {:?}" , events[ * pos] ) ;
180
+ if let Some ( Events :: InBlock ( start_pos) ) = get_useful_next ( events, pos) {
181
+ println ! ( "aa => {:?}" , events[ * pos] ) ;
182
+ pathes. push ( CssPath :: new ( :: std:: str:: from_utf8 ( if * pos > 0 {
183
+ & v[ events[ * pos - 1 ] . get_pos ( ) ..start_pos - 1 ]
184
+ } else {
185
+ & v[ ..start_pos]
186
+ } ) . unwrap_or ( "" ) . trim ( ) . to_owned ( ) ) ) ;
187
+ * pos += 1 ;
188
+ }
189
+ println ! ( "b => {:?}" , events[ * pos] ) ;
190
+ while let Some ( Events :: InBlock ( _) ) = get_useful_next ( events, pos) {
191
+ println ! ( "bb => {:?}" , events[ * pos] ) ;
192
+ if let Some ( ref mut path) = pathes. last_mut ( ) {
193
+ for entry in inner ( v, events, pos) . iter ( ) {
194
+ path. children . insert ( entry. clone ( ) ) ;
195
+ }
196
+ }
197
+ }
198
+ if * pos < events. len ( ) {
199
+ println ! ( "c => {:?}" , events[ * pos] ) ;
200
+ }
201
+ if let Some ( Events :: OutBlock ( _) ) = get_useful_next ( events, pos) {
202
+ * pos += 1 ;
203
+ }
204
+ }
205
+ pathes. iter ( ) . cloned ( ) . collect ( )
206
+ }
207
+
208
+ pub fn load_css_pathes ( v : & [ u8 ] ) -> CssPath {
209
+ let events = load_css_events ( v) ;
210
+ let mut pos = 0 ;
211
+
212
+ println ! ( "\n ======> {:?}" , events) ;
213
+ let mut parent = CssPath :: new ( "parent" . to_owned ( ) ) ;
214
+ parent. children = inner ( v, & events, & mut pos) ;
215
+ parent
216
+ }
217
+
218
+ pub fn test_theme_against < P : AsRef < Path > > ( f : & P , against : & CssPath ) -> bool {
219
+ let mut file = try_false ! ( File :: open( f) ) ;
220
+ let mut data = Vec :: with_capacity ( 1000 ) ;
221
+
222
+ try_false ! ( file. read_to_end( & mut data) ) ;
223
+ let pathes = load_css_pathes ( & data) ;
224
+ println ! ( "========= {:?}" , pathes) ;
225
+ println ! ( "========= {:?}" , against) ;
226
+ pathes == * against
227
+ }
228
+
229
+ #[ test]
230
+ fn test_comments_in_rules ( ) {
231
+ let text = r#"
232
+ rule a {}
233
+
234
+ rule b, c
235
+ // a line comment
236
+ {}
237
+
238
+ rule d
239
+ // another line comment
240
+ e {}
241
+
242
+ rule f/* a multine
243
+
244
+ comment*/{}
245
+
246
+ rule g/* another multine
247
+
248
+ comment*/h
249
+
250
+ i {}
251
+
252
+ rule j/*commeeeeent
253
+
254
+ you like things like "{}" in there? :)
255
+ */
256
+ end {}
257
+ "# ;
258
+ }
0 commit comments