Skip to content

Commit

Permalink
Merge pull request #96 from ZoomRmc/cssdedup
Browse files Browse the repository at this point in the history
  • Loading branch information
h3rald committed Sep 20, 2023
2 parents df0b0b7 + 5c81843 commit 976e565
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 51 deletions.
57 changes: 31 additions & 26 deletions src/hastyscribe.nim
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import std/[
httpclient,
logging,
critbits,
sets,
]

from nimquery import querySelectorAll
Expand Down Expand Up @@ -261,6 +262,7 @@ proc parse_fields(hs: var HastyScribe, document: string): string {.gcsafe.} =
warn "Field '" & id & "' not defined."
result = result.replace(field, "")


proc load_styles(hs: var HastyScribe) =
type
StyleRuleMatches = array[0..1, string]
Expand Down Expand Up @@ -291,7 +293,7 @@ proc load_styles(hs: var HastyScribe) =
var matches: StyleRuleMatches
discard def.match(peg_notestyle_def, matches)
hs.noteStyles[matches[1].strip] = matches[0].strip
# Links -> already in `const.css_rules_links`
# Links -> already in `consts.css_rules_links`

# Snippet Definition:
# {{test -> My test snippet}}
Expand Down Expand Up @@ -362,33 +364,36 @@ proc create_optional_css*(hs: HastyScribe, document: string): string =
## used resources.
let html = document.parseHtml()
var rules: seq[string]
# Check icons
for icon in html.querySelectorAll("span[class^=fa-]"):
rules.add getTableValue(hs.iconStyles, icon.attr("class"), "Icon")
# Check badges
for badge in html.querySelectorAll("span[class^=badge-]"):
rules.add getTableValue(hs.badgeStyles, badge.attr("class"), "Badge")
# Check notes
for note in html.querySelectorAll("div.tip, div.warning, div.note, div.sidebar"):
rules.add getTableValue(hs.noteStyles, note.attr("class"), "Note")
# Check links
# Init with `document-top`: it's added to the document later with `utils.add_jump_to_top_links`
var linkHrefs: CritBitTree[void] = ["#document-top"].toCritBitTree()
for link in html.querySelectorAll("a[href]"): linkHrefs.incl(link.attr("href"))
block linkStyles:
var linkRulesSet: tuple[exts, doms, protos: CritBitTree[void]]

block icons_badges_notes:
var selSet: HashSet[string]
template fillFrom(rules: var seq[string]; t: Table[string, string]; selector, obj: string) =
for el in html.querySelectorAll(selector): selSet.incl(el.attr("class"))
for selV in selSet.items: rules.add(getTableValue(t, selV, obj))
selSet.init()
rules.fillFrom(hs.iconStyles, "span[class^=fa-]", "Icon") # Check icons
rules.fillFrom(hs.badgeStyles, "span[class^=badge-]", "Badge") # Check badges
rules.fillFrom(hs.noteStyles, "div.tip, div.warning, div.note, div.sidebar", "Note") # Check notes

block linkStyles: # Check links
# Init with `document-top`: it's added to the document later with `utils.add_jump_to_top_links`
var linkHrefs: CritBitTree[void] = ["#document-top"].toCritBitTree()
for link in html.querySelectorAll("a[href]"): linkHrefs.incl(link.attr("href"))
var linkRulesSets: array[CssSelPriority, CritBitTree[void]]
for href in linkHrefs.keys:
block search:
for (val, rule) in css_rules_links.extensions:
if href.endsWith(val): linkRulesSet.exts.incl(rule); break search
for (val, rule) in css_rules_links.domains:
if href.contains(val): linkRulesSet.doms.incl(rule); break search
for (val, rule) in css_rules_links.protocols:
if href.startsWith(val): linkRulesSet.protos.incl(rule); break search
# Adding to rules in reversed order of precedence
for rule in linkRulesSet.protos.keys: rules.add(rule)
for rule in linkRulesSet.doms.keys: rules.add(rule)
for rule in linkRulesSet.exts.keys: rules.add(rule)
for prio in countDown(csspProto, csspLowProto): # Traverse rules in order of decreasing priority
for (val, rule) in css_rules_links[prio]:
let match =
case prio:
of csspLowProto, csspProto: href.startsWith(val)
of csspDom: href.contains(val)
of csspExt: href.endsWith(val)
else: false
if match: linkRulesSets[prio].incl(rule); break search
# Adding rules in order of increasing priority
for prio in CssSelPriority:
for rule in linkRulesSets[prio]: rules.add(rule)

rules.join("\n").style_tag()

Expand Down
46 changes: 21 additions & 25 deletions src/hastyscribepkg/consts.nim
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import std/[pegs]
from std/strutils import strip

type
Rule = tuple[selValue: string, definition: string]
CssSelPriority* = enum csspIgnore, csspLowProto, csspDom, csspExt, csspProto
Rule = tuple[selValue: string, definition: string] ## CSS selector rule
Rules = seq[Rule]

proc parseLinkRules(css: string): tuple[extensions, domains, protocols: Rules] =
const CssSelLowPriorityProtos = ["http", "https"]

proc parseLinkRules(css: string): array[CssSelPriority, Rules] =
## Parses a CSS in format adgering to `hastystyles.links.css`:
## Each line is a link styling with a single selector of
## Each line is a link styling with a single selector of
## `^=` / `*=` / `$=` and a `:before`
# TODO: - Support multiple selectors for a styling:
# either in form of "a[href$='.zip']:before, a[href$='.gz']:before"
Expand All @@ -19,14 +22,8 @@ proc parseLinkRules(css: string): tuple[extensions, domains, protocols: Rules] =
op <- ['^*$'] '='
val <- [a-z0-9-.#]+
"""
type
CssSelectorKind = enum
selUnknown = "", selEnds = "$=", selContains = "*=", selStarts = "^="
RuleAttrs = object
kind: CssSelectorKind = selUnknown
selValue: string = ""
var extensions, domains, protocols: Rules
var attrs = default(RuleAttrs)
var attr: tuple[kind: CssSelPriority; selValue: string]
var linkRules: array[CssSelPriority, Rules]
let parse = peg_linkstyle_def.eventParser:
pkNonTerminal:
leave:
Expand All @@ -35,25 +32,24 @@ proc parseLinkRules(css: string): tuple[extensions, domains, protocols: Rules] =
case p.nt.name
of "op":
# debugEcho " op:", s.substr(start, start+1)
attrs.kind = case s[start]:
of '$': selEnds
of '*': selContains
of '^': selStarts
else: selUnknown
of "val": attrs.selValue = s.substr(start, start+length-1)
attr.kind = case s[start]:
of '$': csspExt # endsWiths
of '*': csspDom # contains
of '^': csspProto # startsWith
else: csspIgnore
of "val":
attr.selValue = s.substr(start, start+length-1)
if attr.kind == csspProto and attr.selValue in CssSelLowPriorityProtos:
attr.kind = csspLowProto
of "definition":
let definition = s.substr(start, start+length-1).strip()
if attrs.kind == selUnknown or attrs.selValue == "" or definition == "":
if attr.kind == csspIgnore or attr.selValue == "" or definition == "":
echo "Error parsing `stylesheet_links`!"; quit(1)
case attrs.kind:
of selEnds: extensions.add((attrs.selValue, definition))
of selContains: domains.add((attrs.selValue, definition))
of selStarts: protocols.add((attrs.selValue, definition))
else: doAssert(false) # already checked
attrs = default(RuleAttrs)
linkRules[attr.kind].add((attr.selValue, definition))
attr = (csspLowProto, "")
else: discard # parsed the file
discard parse(css)
(extensions: extensions, domains: domains, protocols: protocols)
linkRules


const
Expand Down

0 comments on commit 976e565

Please sign in to comment.