forked from Unity-Technologies/UnityCsReference
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathCSSSpec.cs
More file actions
120 lines (110 loc) · 4.37 KB
/
Copy pathCSSSpec.cs
File metadata and controls
120 lines (110 loc) · 4.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
// Unity C# reference source
// Copyright (c) Unity Technologies. For terms of use, see
// https://unity3d.com/legal/licenses/Unity_Reference_Only_License
using System;
using System.Text.RegularExpressions;
using UnityEngine.UIElements;
namespace UnityEditor.StyleSheets
{
static class CSSSpec
{
static readonly Regex rgx = new Regex(
@"(?<id>#[-]?\w[\w-]*)|(?<class>\.[\w-]+)|(?<pseudoclass>:[\w-]+(\((?<param>.+)\))?)|(?<type>[^\-]\w+)|(?<wildcard>\*)|\s+",
RegexOptions.IgnoreCase | RegexOptions.Compiled);
const int typeSelectorWeight = 1;
const int classSelectorWeight = 10;
const int idSelectorWeight = 100;
public static int GetSelectorSpecificity(string selector)
{
StyleSelectorPart[] parts;
int score = 0;
if (ParseSelector(selector, out parts))
{
score = GetSelectorSpecificity(parts);
}
return score;
}
// See https://www.w3.org/TR/selectors/#specificity
// A proper CSS library should provide us with this
public static int GetSelectorSpecificity(StyleSelectorPart[] parts)
{
// always add 1 otherwise we wouldn't be able to distinguish between default C# value for int (0)
// and an actual specificity (0 for *)
int score = 1;
for (int i = 0; i < parts.Length; i++)
{
switch (parts[i].type)
{
case StyleSelectorType.Type:
score += typeSelectorWeight;
break;
case StyleSelectorType.Class:
case StyleSelectorType.PseudoClass:
score += classSelectorWeight;
break;
case StyleSelectorType.RecursivePseudoClass:
throw new ArgumentException("Recursive pseudo classes are not supported");
case StyleSelectorType.ID:
score += idSelectorWeight;
break;
default:
break;
}
}
return score;
}
public static bool ParseSelector(string selector, out StyleSelectorPart[] parts)
{
var matches = rgx.Matches(selector);
int count = matches.Count;
if (count < 1)
{
parts = null;
return false;
}
parts = new StyleSelectorPart[count];
for (int i = 0; i < count; i++)
{
Match match = matches[i];
StyleSelectorType type = StyleSelectorType.Unknown;
string value = string.Empty;
if (!string.IsNullOrEmpty(match.Groups["wildcard"].Value))
{
value = "*";
type = StyleSelectorType.Wildcard;
}
else if (!string.IsNullOrEmpty(match.Groups["id"].Value))
{
value = match.Groups["id"].Value.Substring(1);
type = StyleSelectorType.ID;
}
else if (!string.IsNullOrEmpty(match.Groups["class"].Value))
{
value = match.Groups["class"].Value.Substring(1);
type = StyleSelectorType.Class;
}
else if (!string.IsNullOrEmpty(match.Groups["pseudoclass"].Value))
{
var pseudoClassParam = match.Groups["param"].Value;
if (!string.IsNullOrEmpty(pseudoClassParam))
{
value = pseudoClassParam;
type = StyleSelectorType.RecursivePseudoClass;
}
else
{
value = match.Groups["pseudoclass"].Value.Substring(1);
type = StyleSelectorType.PseudoClass;
}
}
else if (!string.IsNullOrEmpty(match.Groups["type"].Value))
{
value = match.Groups["type"].Value;
type = StyleSelectorType.Type;
}
parts[i] = new StyleSelectorPart() { type = type, value = value };
}
return true;
}
}
}