mirror of
https://gitea.com/gitea/tea.git
synced 2024-11-25 20:11:36 +01:00
222d0501df
~~this is semi-blocked by https://github.com/charmbracelet/glamour/pull/96, but behaviour isn't really worse than the previous behaviour (most links work, some are still broken)~~ #### testcase for link resolver ``` tea pr 332 tea checkout 332 && make install && tea pr 332 ``` - [rel](./332) - [abs](/gitea/tea/pulls/332) - [full](https://gitea.com/gitea/tea/pulls/332) Co-authored-by: Norwin Roosen <git@nroo.de> Co-authored-by: 6543 <6543@obermui.de> Reviewed-on: https://gitea.com/gitea/tea/pulls/332 Reviewed-by: 6543 <6543@obermui.de> Reviewed-by: Andrew Thornton <art27@cantab.net> Co-authored-by: Norwin <noerw@noreply.gitea.io> Co-committed-by: Norwin <noerw@noreply.gitea.io>
231 lines
4.8 KiB
Go
231 lines
4.8 KiB
Go
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
|
|
}
|