diff options
author | bptato <nincsnevem662@gmail.com> | 2021-08-04 17:54:27 +0200 |
---|---|---|
committer | bptato <nincsnevem662@gmail.com> | 2021-08-04 17:54:27 +0200 |
commit | caad7b577162a73524277a943050493c489bfb59 (patch) | |
tree | a149be40ceebc4303d94c797d2a62ef62b1a42a0 /src | |
parent | 34b023515599bc746c10c597467ecb07f53c49fe (diff) | |
download | chawan-caad7b577162a73524277a943050493c489bfb59.tar.gz |
More css stuff
Diffstat (limited to 'src')
-rw-r--r-- | src/css/cssparser.nim | 16 | ||||
-rw-r--r-- | src/css/selector.nim | 61 | ||||
-rw-r--r-- | src/css/style.nim | 193 | ||||
-rw-r--r-- | src/html/dom.nim | 128 | ||||
-rw-r--r-- | src/html/htmlparser.nim | 68 | ||||
-rw-r--r-- | src/io/buffer.nim (renamed from src/buffer.nim) | 18 | ||||
-rw-r--r-- | src/io/display.nim | 4 | ||||
-rw-r--r-- | src/io/twtio.nim | 11 | ||||
-rw-r--r-- | src/main.nim | 13 | ||||
-rw-r--r-- | src/types/enums.nim | 41 |
10 files changed, 325 insertions, 228 deletions
diff --git a/src/css/cssparser.nim b/src/css/cssparser.nim index 5ecb470a..5e76bcc0 100644 --- a/src/css/cssparser.nim +++ b/src/css/cssparser.nim @@ -6,6 +6,8 @@ import unicode import streams import math import options +import sequtils +import sugar import ../io/twtio @@ -464,6 +466,8 @@ func curr(state: CSSParseState): CSSParsedItem = func has(state: CSSParseState): bool = return state.at < state.tokens.len +proc consumeComponentValue(state: var CSSParseState): CSSComponentValue + proc consumeSimpleBlock(state: var CSSParseState): CSSSimpleBlock = state.reconsume() let t = CSSToken(state.consume()) @@ -483,11 +487,10 @@ proc consumeSimpleBlock(state: var CSSParseState): CSSSimpleBlock = if t == CSS_LBRACE_TOKEN or t == CSS_LBRACKET_TOKEN or t == CSS_LPAREN_TOKEN: result.value.add(state.consumeSimpleBlock()) else: - result.value.add(CSSComponentValue(t)) + state.reconsume() + result.value.add(state.consumeComponentValue()) return result -proc consumeComponentValue*(state: var CSSParseState): CSSComponentValue - proc consumeFunction(state: var CSSParseState): CSSFunction = let t = (CSSToken)state.consume() result = CSSFunction(name: t.value) @@ -691,6 +694,13 @@ proc parseCSSDeclaration*(inputStream: Stream): CSSDeclaration = proc parseListOfDeclarations(state: var CSSParseState): seq[CSSParsedItem] = return state.consumeListOfDeclarations() +proc parseCSSListOfDeclarations*(cvals: seq[CSSComponentValue]): seq[CSSParsedItem] = + var state = CSSParseState() + state.tokens = collect(newSeq): + for cval in cvals: + CSSParsedItem(cval) + return state.consumeListOfDeclarations() + proc parseCSSListOfDeclarations*(inputStream: Stream): seq[CSSParsedItem] = var state = CSSParseState() state.tokens = tokenizeCSS(inputStream) diff --git a/src/css/selector.nim b/src/css/selector.nim index 1ca417dd..a474bd8f 100644 --- a/src/css/selector.nim +++ b/src/css/selector.nim @@ -3,7 +3,7 @@ import unicode import ../types/enums import ../types/tagtypes -import cssparser +import ./cssparser type SelectorType* = enum @@ -52,10 +52,65 @@ proc setLen*(sellist: SelectorList, i: int) = sellist.sels.setLen(i) proc `[]`*(sellist: SelectorList, i: int): Selector = sellist.sels[i] proc len*(sellist: SelectorList): int = sellist.sels.len +func getSpecificity(sel: Selector): int = + case sel.t + of ID_SELECTOR: + result += 1000000 + of CLASS_SELECTOR, ATTR_SELECTOR, PSEUDO_SELECTOR: + result += 1000 + of TYPE_SELECTOR, PSELEM_SELECTOR: + result += 1 + of FUNC_SELECTOR: + case sel.name + of "is": + var best = 0 + for child in sel.selectors.sels: + let s = getSpecificity(child) + if s > best: + best = s + result += best + of "not": + for child in sel.selectors.sels: + result += getSpecificity(child) + else: discard + of UNIVERSAL_SELECTOR: + discard + +func getSpecificity*(sels: SelectorList): int = + for sel in sels.sels: + result += getSpecificity(sel) + +func optimizeSelectorList*(selectors: SelectorList): SelectorList = + new(result) + #pass 1: check for invalid sequences + var i = 1 + while i < selectors.len: + let sel = selectors[i] + if sel.t == TYPE_SELECTOR or sel.t == UNIVERSAL_SELECTOR: + return SelectorList() + inc i + + #pass 2: move selectors in combination + if selectors.len > 1: + var i = 0 + var slow = SelectorList() + if selectors[0].t == UNIVERSAL_SELECTOR: + inc i + + while i < selectors.len: + if selectors[i].t in {ATTR_SELECTOR, PSEUDO_SELECTOR, PSELEM_SELECTOR}: + slow.add(selectors[i]) + else: + result.add(selectors[i]) + inc i + + result.add(slow) + else: + result.add(selectors[0]) + proc parseSelectorToken(state: var SelectorParser, csstoken: CSSToken) = case csstoken.tokenType of CSS_IDENT_TOKEN: - var sel: Selector case state.query of QUERY_CLASS: state.selectors[^1].add(Selector(t: CLASS_SELECTOR, class: $csstoken.value)) @@ -120,7 +175,7 @@ proc parseSelectorSimpleBlock(state: var SelectorParser, cssblock: CSSSimpleBloc proc parseSelectorFunction(state: var SelectorParser, cssfunction: CSSFunction) = case $cssfunction.name - of "not": + of "not", "is": if state.query != QUERY_PSEUDO: return state.query = QUERY_TYPE diff --git a/src/css/style.nim b/src/css/style.nim index 56e6b00b..d98c1f6c 100644 --- a/src/css/style.nim +++ b/src/css/style.nim @@ -1,4 +1,3 @@ -import streams import unicode import terminal import tables @@ -9,7 +8,7 @@ import ../utils/twtstr import ../types/enums -import cssparser +import ./cssparser type CSSLength* = object @@ -36,13 +35,14 @@ type centered*: bool display*: DisplayType bold*: bool - italic*: bool + fontStyle*: CSSFontStyle underscore*: bool islink*: bool selected*: bool indent*: int color*: CSSColor position*: CSSPosition + content*: seq[Rune] CSSCanvas* = object rootBox*: CSSBox @@ -63,13 +63,32 @@ type paddingEdge*: CSSRect borderEdge*: CSSRect marginEdge*: CSSRect - color*: CSSColor props*: CSS2Properties content*: seq[Rune] dispcontent*: string children*: seq[CSSBox] CSSColor* = tuple[r: uint8, g: uint8, b: uint8, a: uint8] + + CSSComputedValue* = object of RootObj + case t: CSSRuleType + of RULE_ALL: discard + of RULE_COLOR: + color: CSSColor + of RULE_MARGIN, RULE_MARGIN_TOP, RULE_MARGIN_LEFT, RULE_MARGIN_RIGHT, + RULE_MARGIN_BOTTOM: + length: CSSLength + of RULE_FONT_STYLE: + fontStyle: CSSFontStyle + of RULE_DISPLAY: + display: DisplayType + of RULE_CONTENT: + content: seq[Rune] + + CSSSpecifiedValue* = object of CSSComputedValue + hasGlobalValue: bool + globalValue: CSSGlobalValueType + func `+`(a: CSSRect, b: CSSRect): CSSRect = result.x1 = a.x1 + b.x1 @@ -82,7 +101,7 @@ proc `+=`(a: var CSSRect, b: CSSRect) = func cells(l: CSSLength): int = case l.unit - of EM_UNIT: + of UNIT_EM: return int(l.num) else: #TODO @@ -112,22 +131,22 @@ const defaultColor = (0xffu8, 0xffu8, 0xffu8, 0x00u8) func cssLength(val: float64, unit: string): CSSLength = case unit - of "%": return CSSLength(num: val, unit: PERC_UNIT) - of "cm": return CSSLength(num: val, unit: CM_UNIT) - of "mm": return CSSLength(num: val, unit: MM_UNIT) - of "in": return CSSLength(num: val, unit: IN_UNIT) - of "px": return CSSLength(num: val, unit: PX_UNIT) - of "pt": return CSSLength(num: val, unit: PT_UNIT) - of "pc": return CSSLength(num: val, unit: PC_UNIT) - of "em": return CSSLength(num: val, unit: EM_UNIT) - of "ex": return CSSLength(num: val, unit: EX_UNIT) - of "ch": return CSSLength(num: val, unit: CH_UNIT) - of "rem": return CSSLength(num: val, unit: REM_UNIT) - of "vw": return CSSLength(num: val, unit: VW_UNIT) - of "vh": return CSSLength(num: val, unit: VH_UNIT) - of "vmin": return CSSLength(num: val, unit: VMIN_UNIT) - of "vmax": return CSSLength(num: val, unit: VMAX_UNIT) - else: return CSSLength(num: 0, unit: EM_UNIT) + of "%": return CSSLength(num: val, unit: UNIT_PERC) + of "cm": return CSSLength(num: val, unit: UNIT_CM) + of "mm": return CSSLength(num: val, unit: UNIT_MM) + of "in": return CSSLength(num: val, unit: UNIT_IN) + of "px": return CSSLength(num: val, unit: UNIT_PX) + of "pt": return CSSLength(num: val, unit: UNIT_PT) + of "pc": return CSSLength(num: val, unit: UNIT_PC) + of "em": return CSSLength(num: val, unit: UNIT_EM) + of "ex": return CSSLength(num: val, unit: UNIT_EX) + of "ch": return CSSLength(num: val, unit: UNIT_CH) + of "rem": return CSSLength(num: val, unit: UNIT_REM) + of "vw": return CSSLength(num: val, unit: UNIT_VW) + of "vh": return CSSLength(num: val, unit: UNIT_VH) + of "vmin": return CSSLength(num: val, unit: UNIT_VMIN) + of "vmax": return CSSLength(num: val, unit: UNIT_VMAX) + else: return CSSLength(num: 0, unit: UNIT_EM) func cssColor*(d: CSSDeclaration): CSSColor = if d.value.len > 0: @@ -165,7 +184,7 @@ func cssColor*(d: CSSDeclaration): CSSColor = else: eprint "else", tok.tokenType return defaultColor - elif d of CSSFunction: + elif d.value[0] of CSSFunction: let f = CSSFunction(d.value[0]) eprint "func", f.name #todo calc etc (cssnumber function or something) @@ -209,11 +228,11 @@ func cssLength(d: CSSDeclaration): CSSLength = return cssLength(tok.nvalue, $tok.unit) of CSS_IDENT_TOKEN: if $tok.value == "auto": - return CSSLength(num: 0, unit: EM_UNIT, auto: true) + return CSSLength(auto: true) else: - return CSSLength(num: 0, unit: EM_UNIT) + return CSSLength(num: 0, unit: UNIT_EM) - return CSSLength(num: 0, unit: EM_UNIT) + return CSSLength(num: 0, unit: UNIT_EM) func hasColor*(style: CSS2Properties): bool = return style.color.r != 0 or style.color.b != 0 or style.color.g != 0 or style.color.a != 0 @@ -228,35 +247,101 @@ func termColor*(style: CSS2Properties): ForegroundColor = else: return fgWhite -proc applyProperties*(box: CSSBox, s: string) = - let decls = parseCSSListOfDeclarations(newStringStream(s)) - if box.props == nil: - box.props = CSS2Properties() - let props = box.props - - for item in decls: - if item of CSSDeclaration: - let d = CSSDeclaration(item) - case $d.name - of "color": - props.color = cssColor(d) - eprint props.color #TODO - of "margin": - let l = cssLength(d) - props.margintop = l - props.marginbottom = l - props.marginleft = l - props.marginright = l - of "margin-top": - props.margintop = cssLength(d) - of "margin-left": - props.marginleft = cssLength(d) - of "margin-right": - props.marginright = cssLength(d) - of "margin-bottom": - props.marginbottom = cssLength(d) - else: - printc(d) #TODO +func isToken(d: CSSDeclaration): bool = d.value.len > 0 and d.value[0] of CSSToken +func getToken(d: CSSDeclaration): CSSToken = (CSSToken)d.value[0] + +func cssString(d: CSSDeclaration): seq[Rune] = + if isToken(d): + let tok = getToken(d) + case tok.tokenType + of CSS_IDENT_TOKEN, CSS_STRING_TOKEN: + return tok.value + else: return + +func cssDisplay(d: CSSDeclaration): DisplayType = + if isToken(d): + let tok = getToken(d) + if tok.tokenType == CSS_IDENT_TOKEN: + case $tok.value + of "block": return DISPLAY_BLOCK + of "inline": return DISPLAY_INLINE + of "inline-block": return DISPLAY_INLINE_BLOCK + of "list-item": return DISPLAY_LIST_ITEM + of "table-column": return DISPLAY_TABLE_COLUMN + of "none": return DISPLAY_NONE + else: return DISPLAY_INLINE + return DISPLAY_INLINE + +func cssFontStyle(d: CSSDeclaration): CSSFontStyle = + if isToken(d): + let tok = getToken(d) + if tok.tokenType == CSS_IDENT_TOKEN: + case $tok.value + of "normal": return FONTSTYLE_NORMAL + of "italic": return FONTSTYLE_ITALIC + of "oblique": return FONTSTYLE_OBLIQUE + else: return FONTSTYLE_NORMAL + return FONTSTYLE_NORMAL + +func getSpecifiedValue*(d: CSSDeclaration): CSSSpecifiedValue = + case $d.name + of "color": + return CSSSpecifiedValue(t: RULE_COLOR, color: cssColor(d)) + of "margin": + return CSSSpecifiedValue(t: RULE_MARGIN, length: cssLength(d)) + of "margin-top": + return CSSSpecifiedValue(t: RULE_MARGIN_TOP, length: cssLength(d)) + of "margin-left": + return CSSSpecifiedValue(t: RULE_MARGIN_LEFT, length: cssLength(d)) + of "margin-bottom": + return CSSSpecifiedValue(t: RULE_MARGIN_BOTTOM, length: cssLength(d)) + of "margin-right": + return CSSSpecifiedValue(t: RULE_MARGIN_RIGHT, length: cssLength(d)) + of "font-style": + return CSSSpecifiedValue(t: RULE_FONT_STYLE, fontStyle: cssFontStyle(d)) + of "display": + return CSSSpecifiedValue(t: RULE_DISPLAY, display: cssDisplay(d)) + of "content": + return CSSSpecifiedValue(t: RULE_CONTENT, content: cssString(d)) + +func getComputedValue*(rule: CSSSpecifiedValue): CSSComputedValue = + let inherit = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) + let initial = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) + let unset = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) + let revert = rule.hasGlobalValue and (rule.globalValue == VALUE_INHERIT) + #case rule.t + #of RULE_COLOR: + # return CSSComputedValue(t: rule.t, + +func getComputedValue*(d: CSSDeclaration): CSSComputedValue = + return getComputedValue(getSpecifiedValue(d)) + +proc applyProperty*(props: CSS2Properties, d: CSSDeclaration) = + case $d.name + of "color": + props.color = cssColor(d) + of "margin": + let l = cssLength(d) + props.margintop = l + props.marginbottom = l + props.marginleft = l + props.marginright = l + of "margin-top": + props.margintop = cssLength(d) + of "margin-left": + props.marginleft = cssLength(d) + of "margin-right": + props.marginright = cssLength(d) + of "margin-bottom": + props.marginbottom = cssLength(d) + of "font-style": + props.fontStyle = cssFontStyle(d) + of "display": + props.display = cssDisplay(d) + of "content": + props.content = cssString(d) + else: + printc(d) #TODO func getLength(s: seq[Rune], start: int, wlimit: int): tuple[wrap: bool, len: int, width: int] = var len = 0 diff --git a/src/html/dom.nim b/src/html/dom.nim index 74ebe5ea..1a393134 100644 --- a/src/html/dom.nim +++ b/src/html/dom.nim @@ -6,13 +6,13 @@ import tables import streams import sequtils import sugar +import algorithm import ../css/style import ../css/cssparser import ../css/selector import ../types/enums -import ../types/tagtypes import ../utils/twtstr @@ -46,6 +46,7 @@ type width*: int height*: int hidden*: bool + box*: CSSBox Attr* = ref AttrObj AttrObj = object of NodeObj @@ -89,7 +90,8 @@ type id*: string classList*: seq[string] attributes*: Table[string, Attr] - box*: CSSBox + style*: CSS2Properties + cssvalues*: seq[CSSComputedValue] HTMLElement* = ref HTMLElementObj HTMLElementObj = object of ElementObj @@ -159,8 +161,8 @@ func nodeAttr*(node: Node): HtmlElement = func getStyle*(node: Node): CSS2Properties = case node.nodeType - of TEXT_NODE: return node.parentElement.box.props - of ELEMENT_NODE: return Element(node).box.props + of TEXT_NODE: return node.parentElement.style + of ELEMENT_NODE: return Element(node).style else: assert(false) func displayed*(node: Node): bool = @@ -260,11 +262,11 @@ proc getRawText*(htmlNode: Node): string = #eprint "char data", chardata.data if htmlNode.parentElement != nil and htmlNode.parentElement.tagType != TAG_PRE: result = chardata.data.remove("\n") - if unicode.strip(result).runeLen() > 0: - if htmlNode.getStyle().display != DISPLAY_INLINE: - result = unicode.strip(result) - else: - result = "" + #if unicode.strip(result).runeLen() > 0: + # if htmlNode.getStyle().display != DISPLAY_INLINE: + # result = unicode.strip(result) + #else: + # result = "" else: result = unicode.strip(chardata.data) if htmlNode.parentElement != nil and htmlNode.parentElement.tagType == TAG_OPTION: @@ -290,7 +292,7 @@ func getFmtText*(node: Node): seq[string] = if style.bold: result = result.ansiStyle(styleBright).ansiReset() - if style.italic: + if style.fontStyle == FONTSTYLE_ITALIC or style.fontStyle == FONTSTYLE_OBLIQUE: result = result.ansiStyle(styleItalic).ansiReset() if style.underscore: result = result.ansiStyle(styleUnderscore).ansiReset() @@ -307,10 +309,6 @@ func newComment*(): Comment = new(result) result.nodeType = COMMENT_NODE -func newBox*(element: HTMLElement): CSSBox = - new(result) - result.props = CSS2Properties() - func newHtmlElement*(tagType: TagType): HTMLElement = case tagType of TAG_INPUT: @@ -332,7 +330,7 @@ func newHtmlElement*(tagType: TagType): HTMLElement = result.nodeType = ELEMENT_NODE result.tagType = tagType - result.box = result.newBox() + result.style = CSS2Properties() func newDocument*(): Document = new(result) @@ -414,7 +412,6 @@ func selectElems(document: Document, sel: Selector): seq[Element] = return document.class_elements[sel.class] of UNIVERSAL_SELECTOR: return document.all_elements - #TODO: following selectors are rather inefficient of ATTR_SELECTOR: return document.all_elements.filter((elem) => attrSelectorMatches(elem, sel)) of PSEUDO_SELECTOR: @@ -422,38 +419,13 @@ func selectElems(document: Document, sel: Selector): seq[Element] = of PSELEM_SELECTOR: return document.all_elements.filter((elem) => pseudoElemSelectorMatches(elem, sel)) of FUNC_SELECTOR: - if sel.name == "not": + case sel.name + of "not": return document.all_elements.filter((elem) => not selectorsMatch(elem, sel.selectors)) + of "is", "where": + return document.all_elements.filter((elem) => selectorsMatch(elem, sel.selectors)) return newSeq[Element]() -func optimizeSelectorList(selectors: SelectorList): SelectorList = - new(result) - #pass 1: check for invalid sequences - var i = 1 - while i < selectors.len: - let sel = selectors[i] - if sel.t == TYPE_SELECTOR or sel.t == UNIVERSAL_SELECTOR: - return SelectorList() - inc i - - #pass 2: move selectors in combination - if selectors.len > 1: - var i = 0 - var slow = SelectorList() - if selectors[0].t == UNIVERSAL_SELECTOR: - inc i - - while i < selectors.len: - if selectors[i].t in {ATTR_SELECTOR, PSEUDO_SELECTOR, PSELEM_SELECTOR}: - slow.add(selectors[i]) - else: - result.add(selectors[i]) - inc i - - result.add(slow) - else: - result.add(selectors[0]) - func selectElems(document: Document, selectors: SelectorList): seq[Element] = assert(selectors.len > 0) let sellist = optimizeSelectorList(selectors) @@ -462,8 +434,12 @@ func selectElems(document: Document, selectors: SelectorList): seq[Element] = while i < sellist.len: if sellist[i].t == FUNC_SELECTOR: - if sellist[i].name == "not": + case sellist[i].name + of "not": result = result.filter((elem) => not selectorsMatch(elem, sellist[i].selectors)) + of "is", "where": + result = result.filter((elem) => selectorsMatch(elem, sellist[i].selectors)) + else: discard else: result = result.filter((elem) => selectorMatches(elem, sellist[i])) inc i @@ -476,17 +452,65 @@ proc querySelector*(document: Document, q: string): seq[Element] = for sel in selectors: result.add(document.selectElems(sel)) -proc applyRule(elem: Element, rule: CSSRule) = - let selectors = parseSelectors(rule.prelude) - for sel in selectors: - if elem.selectorsMatch(sel): - eprint "match!" +func calcRules(elem: Element, rules: CSSStylesheet): seq[CSSSimpleBlock] = + var tosort: seq[tuple[s:int,b:CSSSimpleBlock]] + for rule in rules.value: + let selectors = parseSelectors(rule.prelude) #TODO perf: compute this once + for sel in selectors: + if elem.selectorsMatch(sel): + let spec = getSpecificity(sel) + tosort.add((spec,rule.oblock)) -proc applyRules(document: Document, rules: CSSStylesheet) = + tosort.sort((x, y) => cmp(x.s,y.s)) + return tosort.map((x) => x.b) + +proc applyRules*(document: Document, rules: CSSStylesheet): seq[tuple[e:Element,d:CSSDeclaration]] = + var stack: seq[Element] + + stack.add(document.firstElementChild) + while stack.len > 0: + let elem = stack.pop() + for oblock in calcRules(elem, rules): + let decls = parseCSSListOfDeclarations(oblock.value) + for item in decls: + if item of CSSDeclaration: + if ((CSSDeclaration)item).important: + result.add((elem, (CSSDeclaration)item)) + else: + elem.style.applyProperty((CSSDeclaration)item) + + for child in elem.children: + stack.add(child) + +proc addBoxes*(elem: Element) = + var b = false + for child in elem.childNodes: + if child.nodeType == ELEMENT_NODE: + if ((Element)child).style.display == DISPLAY_BLOCK: + b = true + break + for child in elem.childNodes: + if child.nodeType == ELEMENT_NODE: + if b: + child.box = CSSBox(display: DISPLAY_BLOCK) + else: + child.box = CSSBox() + +proc generateBoxModel(document: Document) = var stack: seq[Element] stack.add(document.firstElementChild) + document.firstElementChild.box = CSSBox() while stack.len > 0: let elem = stack.pop() + elem.addBoxes() for child in elem.children: stack.add(child) + +proc applyDefaultStylesheet*(document: Document) = + let important = document.applyRules(stylesheet) + for rule in important: + rule.e.style.applyProperty(rule.d) + rule.e.cssvalues.add(getComputedValue(rule.d)) + + document.generateBoxModel() diff --git a/src/html/htmlparser.nim b/src/html/htmlparser.nim index f43bcf40..3cfc1d8d 100644 --- a/src/html/htmlparser.nim +++ b/src/html/htmlparser.nim @@ -31,71 +31,6 @@ type parentNode: Node textNode: Text -#func newHtmlElement(tagType: TagType, parentNode: Node): HtmlElement = -# case tagType -# of TAG_INPUT: result = new(HtmlInputElement) -# of TAG_A: result = new(HtmlAnchorElement) -# of TAG_SELECT: result = new(HtmlSelectElement) -# of TAG_OPTION: result = new(HtmlOptionElement) -# else: result = new(HtmlElement) -# -# result.nodeType = ELEMENT_NODE -# result.tagType = tagType -# result.parentNode = parentNode -# if parentNode.isElemNode(): -# result.parentElement = HtmlElement(parentNode) -# -# if tagType in DisplayInlineTags: -# result.display = DISPLAY_INLINE -# elif tagType in DisplayBlockTags: -# result.display = DISPLAY_BLOCK -# elif tagType in DisplayInlineBlockTags: -# result.display = DISPLAY_INLINE_BLOCK -# elif tagType == TAG_LI: -# result.display = DISPLAY_LIST_ITEM -# else: -# result.display = DISPLAY_NONE -# -# case tagType -# of TAG_CENTER: -# result.centered = true -# of TAG_B: -# result.bold = true -# of TAG_I: -# result.italic = true -# of TAG_U: -# result.underscore = true -# of TAG_HEAD: -# result.hidden = true -# of TAG_STYLE: -# result.hidden = true -# of TAG_SCRIPT: -# result.hidden = true -# of TAG_OPTION: -# result.hidden = true #TODO -# of TAG_PRE, TAG_TD, TAG_TH: -# result.margin = 1 -# of TAG_UL, TAG_OL: -# result.indent = 2 -# result.margin = 1 -# of TAG_H1, TAG_H2, TAG_H3, TAG_H4, TAG_H5, TAG_H6: -# result.bold = true -# result.margin = 1 -# of TAG_A: -# result.islink = true -# of TAG_INPUT: -# HtmlInputElement(result).size = 20 -# else: discard -# -# if parentNode.isElemNode(): -# let parent = HtmlElement(parentNode) -# result.centered = result.centered or parent.centered -# result.bold = result.bold or parent.bold -# result.italic = result.italic or parent.italic -# result.underscore = result.underscore or parent.underscore -# result.hidden = result.hidden or parent.hidden -# result.islink = result.islink or parent.islink - func inputSize*(str: string): int = if str.len == 0: return 20 @@ -104,7 +39,7 @@ func inputSize*(str: string): int = return 20 return str.parseInt() -#w3m's getescapecmd and parse_tag, transpiled to nim. +#w3m's getescapecmd and parse_tag, transpiled to nim and heavily modified. #(C) Copyright 1994-2002 by Akinori Ito #(C) Copyright 2002-2011 by Akinori Ito, Hironori Sakamoto, Fumitoshi Ukai # @@ -153,7 +88,6 @@ proc getescapecmd(buf: string, at: var int): string = elif not isAlphaAscii(buf[i]): return "" - #TODO this could be way more efficient (and radixnode needs better interface) when defined(small): var n = entityMap var s = "" diff --git a/src/buffer.nim b/src/io/buffer.nim index dc171137..ce28e84e 100644 --- a/src/buffer.nim +++ b/src/io/buffer.nim @@ -4,20 +4,19 @@ import tables import strutils import unicode -import types/enums +import ../types/enums -import utils/termattrs -import utils/twtstr +import ../utils/termattrs +import ../utils/twtstr -import html/dom +import ../html/dom -import io/twtio +import ./twtio type Buffer* = ref BufferObj BufferObj = object - fmttext*: seq[string] - rawtext*: seq[string] + text*: seq[Rune] title*: string hovertext*: string width*: int @@ -37,11 +36,16 @@ type attrs*: TermAttributes document*: Document + #TODO remove these + fmttext*: seq[string] + rawtext*: seq[string] + proc newBuffer*(attrs: TermAttributes): Buffer = return Buffer(width: attrs.termWidth, height: attrs.termHeight, attrs: attrs) +#TODO go through these and remove ones that don't make sense in the new model func lastLine*(buffer: Buffer): int = assert(buffer.fmttext.len == buffer.rawtext.len) return buffer.fmttext.len - 1 diff --git a/src/io/display.nim b/src/io/display.nim index e3ce6bac..1c72d959 100644 --- a/src/io/display.nim +++ b/src/io/display.nim @@ -11,10 +11,10 @@ import ../utils/twtstr import ../html/dom -import ../buffer import ../config -import twtio +import ./buffer +import ./twtio proc clearStatusMsg*(at: int) = setCursorPos(0, at) diff --git a/src/io/twtio.nim b/src/io/twtio.nim index 34cf4ce6..4e2789b0 100644 --- a/src/io/twtio.nim +++ b/src/io/twtio.nim @@ -3,6 +3,7 @@ import tables import unicode import strutils import sequtils +import sugar import ../utils/twtstr import ../utils/radixtree @@ -21,9 +22,6 @@ template printesc*(s: string) = else: stdout.write($r) -template printspc(i: int) = - print(' '.repeat(i)) - template eprint*(s: varargs[string, `$`]) = {.cast(noSideEffect).}: var a = false for x in s: @@ -134,11 +132,12 @@ proc zeroShiftRedraw(state: var LineState) = proc insertCharseq(state: var LineState, cs: var seq[Rune]) = let escNext = state.escNext - cs.keepIf(func(r: Rune): bool = escNext or not r.isControlChar()) + cs.keepIf((r) => escNext or not r.isControlChar()) state.escNext = false if cs.len == 0: return - elif state.cursor >= state.news.len and state.news.width(state.shift, state.cursor) + cs.width() < state.displen: + + if state.cursor >= state.news.len and state.news.width(state.shift, state.cursor) + cs.width() < state.displen: state.news &= cs state.cursor += cs.len printesc($cs) @@ -185,7 +184,7 @@ proc readLine*(current: var string, minlen: int, maxlen: int): bool = state.minlen = minlen state.maxlen = maxlen state.displen = state.maxlen - 1 - #ugh + #cache strings for i in 0..(maxlen - minlen): state.spaces.add(' '.repeat(i)) printesc(current) diff --git a/src/main.nim b/src/main.nim index 539527f1..92d6b3b9 100644 --- a/src/main.nim +++ b/src/main.nim @@ -3,8 +3,6 @@ import uri import os import streams -import css/style - import utils/termattrs import html/dom @@ -12,8 +10,8 @@ import html/htmlparser import io/display import io/twtio +import io/buffer -import buffer import config let clientInstance = newHttpClient() @@ -39,6 +37,9 @@ proc getPageUri(uri: Uri): Stream = var buffers: seq[Buffer] + +const defaultcss = staticRead"../res/default.css" + proc main*() = if paramCount() != 1: eprint "Invalid parameters. Usage:\ntwt <url>" @@ -50,10 +51,7 @@ proc main*() = let uri = parseUri(paramStr(1)) buffers.add(buffer) buffer.document = parseHtml(getPageUri(uri)) - let s = buffer.document.querySelector(":not(:first-child)") - eprint s.len - for q in s: - eprint q + buffer.document.applyDefaultStylesheet() buffer.setLocation(uri) buffer.renderHtml() var lastUri = uri @@ -71,4 +69,3 @@ proc main*() = buffer.renderHtml() lastUri = newUri main() -#parseCSS(newFileStream("default.css", fmRead)) diff --git a/src/types/enums.nim b/src/types/enums.nim index 21b27ab6..f88f665b 100644 --- a/src/types/enums.nim +++ b/src/types/enums.nim @@ -28,9 +28,8 @@ type INPUT_URL, INPUT_WEEK WhitespaceType* = enum - WHITESPACE_UNKNOWN, WHITESPACE_NORMAL, WHITESPACE_NOWRAP, WHITESPACE_PRE, - WHITESPACE_PRE_LINE, WHITESPACE_PRE_WRAP, WHITESPACE_INITIAL, - WHITESPACE_INHERIT + WHITESPACE_NORMAL, WHITESPACE_NOWRAP, WHITESPACE_PRE, WHITESPACE_PRE_LINE, + WHITESPACE_PRE_WRAP TagType* = enum TAG_UNKNOWN, TAG_HTML, TAG_BASE, TAG_HEAD, TAG_LINK, TAG_META, TAG_STYLE, @@ -62,34 +61,24 @@ type CSS_RBRACE_TOKEN CSSUnit* = enum - CM_UNIT, MM_UNIT, IN_UNIT, PX_UNIT, PT_UNIT, PC_UNIT, - EM_UNIT, EX_UNIT, CH_UNIT, REM_UNIT, VW_UNIT, VH_UNIT, VMIN_UNIT, VMAX_UNIT, - PERC_UNIT + UNIT_CM, UNIT_MM, UNIT_IN, UNIT_PX, UNIT_PT, UNIT_PC, + UNIT_EM, UNIT_EX, UNIT_CH, UNIT_REM, UNIT_VW, UNIT_VH, UNIT_VMIN, UNIT_VMAX, + UNIT_PERC CSSPosition* = enum - STATIC_POSITION, RELATIVE_POSITION, ABSOLUTE_POSITION, FIXED_POSITION, - INHERIT_POSITION + POSITION_STATIC, POSITION_RELATIVE, POSITION_ABSOLUTE, POSITION_FIXED, + POSITION_INHERIT -const DisplayInlineTags* = { - TAG_A, TAG_ABBR, TAG_B, TAG_BDO, TAG_BR, TAG_BUTTON, TAG_CITE, TAG_CODE, - TAG_DEL, TAG_DFN, TAG_EM, TAG_FONT, TAG_I, TAG_IMG, TAG_INS, TAG_INPUT, - TAG_IFRAME, TAG_KBD, TAG_LABEL, TAG_MAP, TAG_OBJECT, TAG_Q, TAG_SAMP, - TAG_SCRIPT, TAG_SELECT, TAG_SMALL, TAG_SPAN, TAG_STRONG, TAG_SUB, TAG_SUP, - TAG_TEXTAREA, TAG_TT, TAG_VAR, TAG_FONT, TAG_IFRAME, TAG_U, TAG_S, TAG_STRIKE, - TAG_FRAME, TAG_IMG, TAG_INPUT -} + CSSFontStyle* = enum + FONTSTYLE_NORMAL, FONTSTYLE_ITALIC, FONTSTYLE_OBLIQUE -const DisplayNoneTags* = { - TAG_AREA, TAG_BASE, TAG_SOURCE, TAG_TRACK, TAG_LINK, TAG_META, TAG_PARAM, TAG_WBR -} + CSSRuleType* = enum + RULE_ALL, RULE_COLOR, RULE_MARGIN, RULE_MARGIN_TOP, RULE_MARGIN_LEFT, + RULE_MARGIN_RIGHT, RULE_MARGIN_BOTTOM, RULE_FONT_STYLE, RULE_DISPLAY, + RULE_CONTENT -const DisplayInlineBlockTags* = { - TAG_IMG -} - -const DisplayTableColumnTags* = { - TAG_COL -} + CSSGlobalValueType* = enum + VALUE_INITIAL, VALUE_INHERIT, VALUE_REVERT, VALUE_UNSET const SelfClosingTagTypes* = { TAG_LI, TAG_P |