v0.5.1 657f658

selector.go RawHistoryFolder

1package css
2
3import "strings"
4
5// StateFlags are the supported CSS pseudo-class states.
6type StateFlags uint8
7
8const (
9	Hover StateFlags = 1 << iota
10	Active
11	Focus
12	FocusVisible
13	Disabled
14	Checked
15)
16
17// Element is the matching identity for one Compute call. Parentage comes from Computed.
18type Element struct {
19	Type, ID string
20	Classes  []string
21	State    StateFlags
22	Inline   *Declarations
23}
24type Specificity struct{ IDs, Classes, Types int }
25
26func (s Specificity) greater(t Specificity) bool {
27	if s.IDs != t.IDs {
28		return s.IDs > t.IDs
29	}
30	if s.Classes != t.Classes {
31		return s.Classes > t.Classes
32	}
33	return s.Types > t.Types
34}
35
36type compound struct {
37	typ, id          string
38	classes, pseudos []string
39	combinator       string
40	typID, idID      uint32
41	classIDs         []uint32
42	state            StateFlags
43}
44type Selector struct {
45	parts       []compound
46	Specificity Specificity
47}
48
49func parseSelectors(ts []Token) ([]Selector, bool) {
50	var result []Selector
51	start := 0
52	for i := 0; i <= len(ts); i++ {
53		if i == len(ts) || ts[i].Kind == "," {
54			s, ok := parseSelector(ts[start:i])
55			if !ok {
56				return nil, false
57			}
58			result = append(result, s)
59			start = i + 1
60		}
61	}
62	return result, len(result) > 0
63}
64func parseSelector(ts []Token) (Selector, bool) {
65	s := Selector{}
66	i := 0
67	pending := ""
68	for i < len(ts) {
69		space := false
70		for i < len(ts) && ts[i].Kind == "space" {
71			space = true
72			i++
73		}
74		if space && len(s.parts) > 0 && pending == "" {
75			pending = " "
76		}
77		if i >= len(ts) {
78			break
79		}
80		if ts[i].Kind == ">" {
81			if len(s.parts) == 0 || pending == ">" {
82				return s, false
83			}
84			pending = ">"
85			i++
86			continue
87		}
88		p := compound{combinator: pending}
89		pending = ""
90		seen := false
91		for i < len(ts) {
92			t := ts[i]
93			if t.Kind == "space" || t.Kind == ">" {
94				break
95			}
96			switch t.Kind {
97			case "hash":
98				if p.id != "" {
99					return s, false
100				}
101				p.id = t.Value
102				s.Specificity.IDs++
103				seen = true
104				i++
105			case "ident":
106				if seen || p.typ != "" {
107					return s, false
108				}
109				p.typ = strings.ToLower(t.Value)
110				s.Specificity.Types++
111				seen = true
112				i++
113			case ".", "#", ":":
114				if i+1 >= len(ts) || ts[i+1].Kind != "ident" {
115					return s, false
116				}
117				v := ts[i+1].Value
118				switch t.Kind {
119				case ".":
120					p.classes = append(p.classes, v)
121					s.Specificity.Classes++
122				case "#":
123					if p.id != "" {
124						return s, false
125					}
126					p.id = v
127					s.Specificity.IDs++
128				case ":":
129					switch strings.ToLower(v) {
130					case "hover", "active", "focus", "focus-visible", "disabled", "checked":
131						p.pseudos = append(p.pseudos, strings.ToLower(v))
132						s.Specificity.Classes++
133					default:
134						return s, false
135					}
136				}
137				seen = true
138				i += 2
139			default:
140				return s, false
141			}
142		}
143		if !seen || len(s.parts) > 0 && p.combinator == "" {
144			return s, false
145		}
146		s.parts = append(s.parts, p)
147	}
148	return s, len(s.parts) > 0 && pending != ">"
149}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 12

↑↓ moveenter openesc close