Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
230 lines
4.8 KiB
230 lines
4.8 KiB
package css |
|
|
|
import ( |
|
"fmt" |
|
"strings" |
|
) |
|
|
|
const ( |
|
indentSpace = 2 |
|
) |
|
|
|
// RuleKind represents a Rule kind |
|
type RuleKind int |
|
|
|
// Rule kinds |
|
const ( |
|
QualifiedRule RuleKind = iota |
|
AtRule |
|
) |
|
|
|
// At Rules than have Rules inside their block instead of Declarations |
|
var atRulesWithRulesBlock = []string{ |
|
"@document", "@font-feature-values", "@keyframes", "@media", "@supports", |
|
} |
|
|
|
// Rule represents a parsed CSS rule |
|
type Rule struct { |
|
Kind RuleKind |
|
|
|
// At Rule name (eg: "@media") |
|
Name string |
|
|
|
// Raw prelude |
|
Prelude string |
|
|
|
// Qualified Rule selectors parsed from prelude |
|
Selectors []string |
|
|
|
// Style properties |
|
Declarations []*Declaration |
|
|
|
// At Rule embedded rules |
|
Rules []*Rule |
|
|
|
// Current rule embedding level |
|
EmbedLevel int |
|
} |
|
|
|
// NewRule instanciates a new Rule |
|
func NewRule(kind RuleKind) *Rule { |
|
return &Rule{ |
|
Kind: kind, |
|
} |
|
} |
|
|
|
// Returns string representation of rule kind |
|
func (kind RuleKind) String() string { |
|
switch kind { |
|
case QualifiedRule: |
|
return "Qualified Rule" |
|
case AtRule: |
|
return "At Rule" |
|
default: |
|
return "WAT" |
|
} |
|
} |
|
|
|
// EmbedsRules returns true if this rule embeds another rules |
|
func (rule *Rule) EmbedsRules() bool { |
|
if rule.Kind == AtRule { |
|
for _, atRuleName := range atRulesWithRulesBlock { |
|
if rule.Name == atRuleName { |
|
return true |
|
} |
|
} |
|
} |
|
|
|
return false |
|
} |
|
|
|
// Equal returns true if both rules are equals |
|
func (rule *Rule) Equal(other *Rule) bool { |
|
if (rule.Kind != other.Kind) || |
|
(rule.Prelude != other.Prelude) || |
|
(rule.Name != other.Name) { |
|
return false |
|
} |
|
|
|
if (len(rule.Selectors) != len(other.Selectors)) || |
|
(len(rule.Declarations) != len(other.Declarations)) || |
|
(len(rule.Rules) != len(other.Rules)) { |
|
return false |
|
} |
|
|
|
for i, sel := range rule.Selectors { |
|
if sel != other.Selectors[i] { |
|
return false |
|
} |
|
} |
|
|
|
for i, decl := range rule.Declarations { |
|
if !decl.Equal(other.Declarations[i]) { |
|
return false |
|
} |
|
} |
|
|
|
for i, rule := range rule.Rules { |
|
if !rule.Equal(other.Rules[i]) { |
|
return false |
|
} |
|
} |
|
|
|
return true |
|
} |
|
|
|
// Diff returns a string representation of rules differences |
|
func (rule *Rule) Diff(other *Rule) []string { |
|
result := []string{} |
|
|
|
if rule.Kind != other.Kind { |
|
result = append(result, fmt.Sprintf("Kind: %s | %s", rule.Kind.String(), other.Kind.String())) |
|
} |
|
|
|
if rule.Prelude != other.Prelude { |
|
result = append(result, fmt.Sprintf("Prelude: \"%s\" | \"%s\"", rule.Prelude, other.Prelude)) |
|
} |
|
|
|
if rule.Name != other.Name { |
|
result = append(result, fmt.Sprintf("Name: \"%s\" | \"%s\"", rule.Name, other.Name)) |
|
} |
|
|
|
if len(rule.Selectors) != len(other.Selectors) { |
|
result = append(result, fmt.Sprintf("Selectors: %v | %v", strings.Join(rule.Selectors, ", "), strings.Join(other.Selectors, ", "))) |
|
} else { |
|
for i, sel := range rule.Selectors { |
|
if sel != other.Selectors[i] { |
|
result = append(result, fmt.Sprintf("Selector: \"%s\" | \"%s\"", sel, other.Selectors[i])) |
|
} |
|
} |
|
} |
|
|
|
if len(rule.Declarations) != len(other.Declarations) { |
|
result = append(result, fmt.Sprintf("Declarations Nb: %d | %d", len(rule.Declarations), len(other.Declarations))) |
|
} else { |
|
for i, decl := range rule.Declarations { |
|
if !decl.Equal(other.Declarations[i]) { |
|
result = append(result, fmt.Sprintf("Declaration: \"%s\" | \"%s\"", decl.String(), other.Declarations[i].String())) |
|
} |
|
} |
|
} |
|
|
|
if len(rule.Rules) != len(other.Rules) { |
|
result = append(result, fmt.Sprintf("Rules Nb: %d | %d", len(rule.Rules), len(other.Rules))) |
|
} else { |
|
|
|
for i, rule := range rule.Rules { |
|
if !rule.Equal(other.Rules[i]) { |
|
result = append(result, fmt.Sprintf("Rule: \"%s\" | \"%s\"", rule.String(), other.Rules[i].String())) |
|
} |
|
} |
|
} |
|
|
|
return result |
|
} |
|
|
|
// Returns the string representation of a rule |
|
func (rule *Rule) String() string { |
|
result := "" |
|
|
|
if rule.Kind == QualifiedRule { |
|
for i, sel := range rule.Selectors { |
|
if i != 0 { |
|
result += ", " |
|
} |
|
result += sel |
|
} |
|
} else { |
|
// AtRule |
|
result += fmt.Sprintf("%s", rule.Name) |
|
|
|
if rule.Prelude != "" { |
|
if result != "" { |
|
result += " " |
|
} |
|
result += fmt.Sprintf("%s", rule.Prelude) |
|
} |
|
} |
|
|
|
if (len(rule.Declarations) == 0) && (len(rule.Rules) == 0) { |
|
result += ";" |
|
} else { |
|
result += " {\n" |
|
|
|
if rule.EmbedsRules() { |
|
for _, subRule := range rule.Rules { |
|
result += fmt.Sprintf("%s%s\n", rule.indent(), subRule.String()) |
|
} |
|
} else { |
|
for _, decl := range rule.Declarations { |
|
result += fmt.Sprintf("%s%s\n", rule.indent(), decl.String()) |
|
} |
|
} |
|
|
|
result += fmt.Sprintf("%s}", rule.indentEndBlock()) |
|
} |
|
|
|
return result |
|
} |
|
|
|
// Returns identation spaces for declarations and rules |
|
func (rule *Rule) indent() string { |
|
result := "" |
|
|
|
for i := 0; i < ((rule.EmbedLevel + 1) * indentSpace); i++ { |
|
result += " " |
|
} |
|
|
|
return result |
|
} |
|
|
|
// Returns identation spaces for end of block character |
|
func (rule *Rule) indentEndBlock() string { |
|
result := "" |
|
|
|
for i := 0; i < (rule.EmbedLevel * indentSpace); i++ { |
|
result += " " |
|
} |
|
|
|
return result |
|
}
|
|
|