@@ -106,11 +106,17 @@ peg::parser!(grammar selector_parser() for str {
106106 rule _( ) = whitespace( ) *
107107 rule s( ) = whitespace( ) +
108108
109+ rule escape( ) -> char = "\\ " c: [ _] { c }
110+
111+ rule ident_char( ) -> char
112+ = escape( )
113+ / c: [ 'a' ..='z' | 'A' ..='Z' | '0' ..='9' | '-' | '_' ] { c }
114+
109115 // Tag names, class names, and IDs
110- rule ident( ) -> String = s : $ ( [ 'a' ..= 'z' | 'A' ..= 'Z' | '0' ..= '9' | '-' | '_' ] + ) { s . to_string ( ) }
116+ rule ident( ) -> String = chars : ident_char ( ) + { chars . into_iter ( ) . collect ( ) }
111117
112118 // Attribute names
113- rule attr_name( ) -> String = s : $ ( [ 'a' ..= 'z' | 'A' ..= 'Z' | '0' ..= '9' | '-' | '_' | ':' ] + ) { s . to_string ( ) }
119+ rule attr_name( ) -> String = chars : ident_char ( ) + { chars . into_iter ( ) . collect ( ) }
114120
115121 // Attribute value
116122 rule unquoted( ) -> char = [ c if c != ']' && !c. is_ascii_whitespace( ) ]
@@ -298,7 +304,17 @@ impl AttributeSelector {
298304 // like `inkscape:label` which roxmltree may expose under a namespace.
299305 let value = node
300306 . attributes ( )
301- . find ( |a| a. name ( ) == self . name )
307+ . find ( |a| {
308+ if let Some ( ( prefix, local) ) = self . name . split_once ( ':' ) {
309+ if let Some ( ns_uri) = node. lookup_namespace_uri ( Some ( prefix) ) {
310+ a. namespace ( ) == Some ( ns_uri) && a. name ( ) == local
311+ } else {
312+ false
313+ }
314+ } else {
315+ a. name ( ) == self . name
316+ }
317+ } )
302318 . map ( |a| a. value ( ) ) ;
303319
304320 match ( & self . op , value) {
@@ -343,7 +359,9 @@ mod tests {
343359 ( "[id=foo]" , true ) ,
344360 ( "[id=\" foo bar\" ]" , true ) ,
345361 ( "[style*=\" display:none\" ]" , true ) ,
346- ( "[inkscape:label=\" Layer 1\" ]" , true ) ,
362+ ( "[inkscape\\ :label=\" Layer 1\" ]" , true ) ,
363+ ( "[inkscape:label=\" Layer 1\" ]" , false ) ,
364+ ( "g\\ #layer1" , true ) ,
347365 // combinators
348366 ( "g path" , true ) ,
349367 ( "g > path" , true ) ,
@@ -385,8 +403,8 @@ mod tests {
385403
386404 #[ test]
387405 fn test_match ( ) {
388- let svg = r#"<svg>
389- <g id="layer1">
406+ let svg = r#"<svg xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" >
407+ <g id="layer1" inkscape:label="Layer 1" >
390408 <path id="p-cut" class="cut"/>
391409 <path id="p-draft" class="draft"/>
392410 <g id="nested">
@@ -415,6 +433,8 @@ mod tests {
415433 // attribute selector
416434 ( "[style*=\" display:none\" ]" , "p-outer" , true ) ,
417435 ( "[style*=\" display:none\" ]" , "r-draft" , false ) ,
436+ ( "[inkscape\\ :label=\" Layer 1\" ]" , "layer1" , true ) ,
437+ ( "[inkscape\\ :label=\" Layer 1\" ]" , "p-cut" , false ) ,
418438 // descendant combinator — p-cut is inside layer1, p-outer is not
419439 ( "#layer1 path" , "p-cut" , true ) ,
420440 ( "#layer1 path" , "p-nested" , true ) ,
0 commit comments