diff --git a/lib/utils.py b/lib/utils.py
index 80eced1..429c130 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -1,7 +1,9 @@
"""Utility Functions."""
-from typing import NoReturn
+from typing import NoReturn, Self
-from PyQt6.QtCore import QCoreApplication
+from PyQt6.QtCore import QCoreApplication, QDir, QStandardPaths, Qt
+from PyQt6.QtGui import QColor, QFont, QFontDatabase
+from PyQt6.QtNetwork import QNetworkAccessManager, QNetworkDiskCache
from PyQt6.QtSql import QSqlQuery
translate = QCoreApplication.translate
@@ -19,3 +21,72 @@ def query_error(query: QSqlQuery) -> NoReturn:
)
)
raise Exception(translate("MainWindow", "SQL Error"))
+
+class Resources:
+ _instance = None
+ nam = QNetworkAccessManager()
+ headerFont: QFont
+ labelFont: QFont
+ boldFont: QFont
+ textFont: QFont
+ italicFont: QFont
+ capsFont: QFont
+ smallCapsFont: QFont
+ phonicFont: QFont
+
+ baseColor: QColor
+ linkColor: QColor
+ subduedColor: QColor
+
+ def __new__(cls: type[Self]) -> Self:
+ if cls._instance:
+ return cls._instance
+ cls._instance = super(Resources, cls).__new__(cls)
+ return cls._instance
+
+ def __init__(self) -> None:
+ super(Resources, self).__init__()
+ #
+ # Fonts
+ #
+ self.headerFont = QFontDatabase.font("OpenDyslexic", None, 10)
+ self.headerFont.setPixelSize(48)
+ self.labelFont = QFont(self.headerFont)
+ self.labelFont.setPixelSize(30)
+ self.boldFont = QFont(self.headerFont)
+ self.boldFont.setPixelSize(20)
+ self.textFont = QFont(self.boldFont)
+ self.italicFont = QFont(self.boldFont)
+ self.capsFont = QFont(self.boldFont)
+ self.smallCapsFont = QFont(self.boldFont)
+
+ self.headerFont.setWeight(QFont.Weight.Bold)
+ self.boldFont.setBold(True)
+ self.italicFont.setItalic(True)
+ self.capsFont.setCapitalization(QFont.Capitalization.AllUppercase)
+ self.smallCapsFont.setCapitalization(QFont.Capitalization.SmallCaps)
+
+ self.phonicFont = QFontDatabase.font("Gentium", None, 10)
+ self.phonicFont.setPixelSize(20)
+
+ #
+ # colors
+ #
+ self.baseColor = QColor(Qt.GlobalColor.white)
+ self.linkColor = QColor("#4a7d95")
+ self.subduedColor = QColor(Qt.GlobalColor.gray)
+
+ #
+ # Setup the Network Manager
+ #
+ cacheDir = QDir(
+ QStandardPaths.writableLocation(
+ QStandardPaths.StandardLocation.GenericCacheLocation
+ )
+ )
+ cacheDir.mkdir("Troglodite")
+ cacheDir = QDir(cacheDir.path() + QDir.separator() + "Troglodite")
+ netCache = QNetworkDiskCache()
+ netCache.setCacheDirectory(cacheDir.path())
+ self.nam.setCache(netCache)
+ return
diff --git a/lib/words.py b/lib/words.py
index f57df96..123f6af 100644
--- a/lib/words.py
+++ b/lib/words.py
@@ -2,22 +2,20 @@ import importlib
import pkgutil
import json
import re
-from typing import Any, Dict, cast
+from typing import Any, TypedDict, cast
from PyQt6.QtCore import (
+ QUrl,
Qt,
pyqtSlot,
)
from PyQt6.QtGui import (
QColor,
- QFont,
- QFontDatabase,
)
-from PyQt6.QtNetwork import QNetworkAccessManager
from PyQt6.QtSql import QSqlQuery
from PyQt6.QtWidgets import QScrollArea
-from lib import query_error
+from lib.utils import query_error, Resources
from lib.sounds import SoundOff
from lib.definition import Definition, Line, Fragment
@@ -32,20 +30,22 @@ discovered_plugins = {
API = "https://api.dictionaryapi.dev/api/v2/entries/en/{word}"
-
+class WordType(TypedDict):
+ word: str
+ source: str
+ definition: str
+
class Word:
"""All processing of a dictionary word."""
- _words: dict[str, Any] = {}
- _resources: Dict[str, Any] = {}
- _nam = QNetworkAccessManager()
+ _words: dict[str, WordType] = {}
+
def __init__(self, word: str) -> None:
- Word.set_resources()
#
# Have we already retrieved this word?
#
try:
- self.current = json.loads(Word._words[word])
+ self.current = Word._words[word]
return
except KeyError:
pass
@@ -82,50 +82,6 @@ class Word:
query_error(query)
return
- @classmethod
- def set_resources(cls) -> None:
- if len(cls._resources.keys()) > 0:
- return
- #
- # Colors we used
- #
- headerFont = QFontDatabase.font("OpenDyslexic", None, 10)
- headerFont.setPixelSize(48)
- labelFont = QFont(headerFont)
- labelFont.setPixelSize(30)
- boldFont = QFont(headerFont)
- boldFont.setPixelSize(20)
- textFont = QFont(boldFont)
- italicFont = QFont(boldFont)
- capsFont = QFont(boldFont)
- smallCapsFont = QFont(boldFont)
-
- headerFont.setWeight(QFont.Weight.Bold)
- boldFont.setBold(True)
- italicFont.setItalic(True)
- capsFont.setCapitalization(QFont.Capitalization.AllUppercase)
- smallCapsFont.setCapitalization(QFont.Capitalization.SmallCaps)
-
- phonicFont = QFontDatabase.font("Gentium", None, 10)
- phonicFont.setPixelSize(20)
-
- cls._resources = {
- "colors": {
- "base": QColor(Qt.GlobalColor.white),
- "link": QColor("#4a7d95"),
- "subdued": QColor(Qt.GlobalColor.gray),
- },
- "fonts": {
- "header": headerFont,
- "label": labelFont,
- "phonic": phonicFont,
- "bold": boldFont,
- "italic": italicFont,
- "text": textFont,
- "caps": capsFont,
- "smallCaps": smallCapsFont,
- },
- }
@pyqtSlot()
def playSound(self) -> None:
url = discovered_plugins[self.current['source']].getFirstSound(self.current['definition'])
@@ -145,23 +101,15 @@ class Word:
raise Exception(f"Unknown source: {src}")
def get_def(self) -> list[Line]:
- if len(self._lines) > 0:
- return self._lines
src = self.current['source']
try:
- return discovered_plugins[src].getDef(self.current)
+ lines = discovered_plugins[src].getDef(self.current["definition"])
+ return lines
except KeyError:
raise Exception(f"Unknown source: {self.current['source']}")
- def mw_def(self) -> list[Line]:
- lines: list[Line] = []
- # print(json.dumps(self.current,indent=2))
- for entry in self.current["definition"]:
- lines += self.mw_def_entry(entry)
- self._lines = lines
- return lines
-
def mw_seq(self, seq: list[Any]) -> list[Line]:
+ r=Resources()
lines: list[Line] = []
outer = " "
inner = " "
@@ -189,15 +137,15 @@ class Word:
line = Line()
frag = Fragment(
f"{outer} {inner} ",
- self._resources["fonts"]["bold"],
- color=self._resources["colors"]["base"],
+ r.boldFont,
+ color=r.baseColor
)
outer = " "
line.addFragment(frag)
frag = Fragment(
text,
- self._resources["fonts"]["italic"],
- color=self._resources["colors"]["base"],
+ r.italicFont,
+ color=r.baseColor
)
frag.setLeft(30)
line.addFragment(frag)
@@ -209,16 +157,16 @@ class Word:
line = Line()
frag = Fragment(
f"{outer} {inner} ",
- self._resources["fonts"]["bold"],
- color=self._resources["colors"]["base"],
+ r.boldFont,
+ color=r.baseColor
)
outer = " "
frag.setLeft(10)
line.addFragment(frag)
frag = Fragment(
dt[1],
- self._resources["fonts"]["text"],
- color=self._resources["colors"]["base"],
+ r.textFont,
+ color=r.baseColor
)
frag.setLeft(30)
line.addFragment(frag)
@@ -228,14 +176,14 @@ class Word:
line = Line()
frag = Fragment(
f" ",
- self._resources["fonts"]["bold"],
+ r.boldFont
)
frag.setLeft(45)
line.addFragment(frag)
line.addFragment(
Fragment(
vis["t"],
- self._resources["fonts"]["text"],
+ r.textFont,
color=QColor("#aaa"),
)
)
@@ -250,8 +198,8 @@ class Word:
line = Line()
frag = Fragment(
"\u27F6 " + seg[1],
- self._resources["fonts"]["text"],
- color=self._resources["colors"]["base"],
+ r.textFont,
+ color=r.baseColor
)
frag.setLeft(30)
line.addFragment(frag)
@@ -265,19 +213,17 @@ class Word:
return lines
def mw_def_entry(self, entry: dict[str, Any]) -> list[Line]:
+ r = Resources()
#
# Easy reference to colors
#
- base = self._resources["colors"]["base"]
- blue = self._resources["colors"]["blue"]
-
lines: list[Line] = []
line = Line()
hw = re.sub(r"\*", "", entry["hwi"]["hw"])
- frag = Fragment(hw, self._resources["fonts"]["header"], color=base)
+ frag = Fragment(hw, r.headerFont, color=r.baseColor)
line.addFragment(frag)
frag = Fragment(
- " " + entry["fl"], self._resources["fonts"]["label"], color=blue
+ " " + entry["fl"], r.labelFont, color=r.linkColor
)
line.addFragment(frag)
lines.append(line)
@@ -288,8 +234,8 @@ class Word:
for vrs in entry["vrs"]:
frag = Fragment(
space + vrs["va"],
- self._resources["fonts"]["label"],
- color=base,
+ r.labelFont,
+ color=r.baseColor
)
space = " "
line.addFragment(frag)
@@ -298,16 +244,16 @@ class Word:
line = Line()
frag = Fragment(
entry["hwi"]["hw"] + " ",
- self._resources["fonts"]["phonic"],
- color=base,
+ r.phonicFont,
+ color=r.baseColor,
)
line.addFragment(frag)
for prs in entry["hwi"]["prs"]:
- audio = self.mw_sound_url(prs)
+ audio = None
if audio is None:
audio = ""
frag = Fragment(
- prs["mw"], self._resources["fonts"]["phonic"], color=blue
+ prs["mw"], r.phonicFont, color=r.linkColor
)
frag.setAudio(audio)
line.addFragment(frag)
@@ -318,7 +264,7 @@ class Word:
for ins in entry["ins"]:
try:
frag = Fragment(
- ins["il"], self._resources["fonts"]["text"], color=base
+ ins["il"], r.textFont, color=r.baseColor
)
line.addFragment(frag)
space = " "
@@ -326,8 +272,8 @@ class Word:
pass
frag = Fragment(
space + ins["if"],
- self._resources["fonts"]["bold"],
- color=base,
+ r.boldFont,
+ color=r.baseColor
)
line.addFragment(frag)
space = "; "
@@ -336,8 +282,8 @@ class Word:
line = Line()
frag = Fragment(
"; ".join(entry["lbs"]),
- self._resources["fonts"]["bold"],
- color=base,
+ r.boldFont,
+ color=r.baseColor
)
line.addFragment(frag)
lines.append(line)
@@ -345,13 +291,13 @@ class Word:
for k, v in value.items():
if k == "sseq": # has multiple 'senses'
for seq in v:
- r = self.mw_seq(seq)
- lines += r
+ rr = self.mw_seq(seq)
+ lines += rr
elif k == "vd":
line = Line()
line.addFragment(
Fragment(
- v, self._resources["fonts"]["italic"], color=blue
+ v, r.italicFont, color=r.linkColor
)
)
lines.append(line)
@@ -361,7 +307,7 @@ class Word:
#
# Create the header, base word and its label
#
- word = self.current["hwi"]["hw"]
+ word = self.current['definition']["hwi"]["hw"]
label = self.current["fl"]
html = f'
{word} {label}
\n'
@@ -383,7 +329,7 @@ class Word:
if "prs" in self.current["hwi"].keys():
tmp = []
for prs in self.current["hwi"]["prs"]:
- url = self.mw_sound_url(prs)
+ url = QUrl()
how = prs["mw"]
if url:
tmp.append(f'\\{how}\\')
diff --git a/plugins/merriam-webster.py b/plugins/merriam-webster.py
index 323fab9..a71c296 100644
--- a/plugins/merriam-webster.py
+++ b/plugins/merriam-webster.py
@@ -1,12 +1,12 @@
+from PyQt6.QtGui import QColor
from trycast import trycast
import json
import re
-from typing import Any, Literal, NamedTuple, NotRequired, TypedDict, cast
+from typing import Any, NamedTuple, NotRequired, TypedDict
from PyQt6.QtCore import QEventLoop, QUrl, Qt
-from PyQt6.QtGui import QColor, QFont
from PyQt6.QtNetwork import QNetworkRequest
-from lib.words import Word
+from lib.utils import Resources
from lib.definition import Line, Fragment
registration = {
@@ -27,10 +27,6 @@ class VerbalIllustration(TypedDict):
t: str
aq: str
-class VerbalIllustrationTuple(NamedTuple):
- type_: str # 'vis'
- data: list[VerbalIllustration]
-
class Sound(TypedDict):
audio: str
ref: str
@@ -38,12 +34,10 @@ class Sound(TypedDict):
class Pronunciation(TypedDict):
mw: str
- l: str
- l2: str
- pun: str
- sound: Sound
-
-
+ l: NotRequired[str]
+ l2: NotRequired[str]
+ pun: NotRequired[str]
+ sound: NotRequired[Sound]
class Meta(TypedDict):
id: str
@@ -56,12 +50,12 @@ class Meta(TypedDict):
class HeadWordInfo(TypedDict):
hw: str
- prs: list[Pronunciation]
+ prs: NotRequired[list[Pronunciation]]
class HeadWord(TypedDict):
hw: str
- prs: list[Pronunciation]
- psl: str
+ prs: NotRequired[list[Pronunciation]]
+ psl: NotRequired[str]
class Variant(TypedDict):
va: str
@@ -109,34 +103,26 @@ class RunInWrap(TypedDict):
text: str
vrs: list[Variant]
-class Sense:
- dt: list[str] # not full
- et: list[str] # not full
- ins: list[Inflection]
- lbs: list[str]
- prs: list[Pronunciation]
- sdsense: DividedSense
- sgram: str
- sls: list[str]
- sn: str
- vrs: list[Variant]
-
-class SenseSequence(TypedDict):
- sense: Sense
- sen: Sense
+class Sense(TypedDict):
+ dt: list[list] # not full
+ et: NotRequired[list[str]]
+ ins: NotRequired[list[Inflection]]
+ lbs: NotRequired[list[str]]
+ prs: NotRequired[list[Pronunciation]]
+ sdsense: NotRequired[DividedSense]
+ sgram: NotRequired[str]
+ sls: NotRequired[list[str]]
+ sn: NotRequired[str]
+ vrs: NotRequired[list[Variant]]
class Definition(TypedDict):
- sseq: list[SenseSequence]
- vd: str
+ sseq: list[list[list[Any]]]
+ vd: NotRequired[str]
+
+class Pair(TypedDict):
+ objType: str
+ obj: list[Sense]|Sense|str|list[VerbalIllustration]|list[Any]
-class EntryX(TypedDict):
- meta: Meta
- hom: NotRequired[str]
- hwi: HeadWordInfo
- ahws: NotRequired[list[HeadWord]]
- vrs: NotRequired[list[Variant]]
- fl: str
- def_: list[Definition]
Entry = TypedDict(
'Entry',
{
@@ -149,13 +135,29 @@ Entry = TypedDict(
'def': list[Definition],
}
)
+class WordType(TypedDict):
+ word: str
+ source: str
+ definition: dict[str, Any]
-def fetch(word:str) -> dict[str, Any]:
+def make_pairs(src: list[Any]) -> list[Pair]:
+ result:list[Pair] = []
+ iters = [iter(src)]*2
+ for entry in zip(*iters):
+ pair = { 'objType': entry[0],
+ 'obj': entry[1],
+ }
+ pair = trycast(Pair, pair)
+ assert pair is not None
+ result.append(pair)
+ return result
+
+def fetch(word:str) -> WordType:
request = QNetworkRequest()
url = QUrl(API.format(word=word, key=key))
request.setUrl(url)
request.setTransferTimeout(3000)
- reply = Word._nam.get(request)
+ reply = Resources.nam.get(request)
assert reply is not None
loop = QEventLoop()
reply.finished.connect(loop.quit)
@@ -195,16 +197,16 @@ def getFirstSound(definition: list[Entry]) -> QUrl:
return url
return QUrl()
-def do_prs(prs: list[Pronunciation]) -> list[Fragment]:
+def do_prs(hwi: HeadWordInfo) -> list[Fragment]:
+ r = Resources()
frags: list[Fragment] = []
- font = trycast(QFont, Word._resources['fonts']['label'])
- assert font is not None
- linkColor = trycast(QColor, Word._resources['colors']['link'])
- assert linkColor is not None
- subduedColor = trycast(QColor, Word._resources['colors']['subdued'])
- assert subduedColor is not None
+ font = r.labelFont
+ linkColor = r.linkColor
+ subduedColor = r.subduedColor
- for pr in prs:
+ if 'prs' not in hwi:
+ return []
+ for pr in hwi['prs']:
if 'pun' in pr:
pun = pr['pun']
else:
@@ -216,6 +218,7 @@ def do_prs(prs: list[Pronunciation]) -> list[Fragment]:
frag = Fragment(pr['mw'], font, color=subduedColor)
if 'sound' in pr:
frag.setAudio(soundUrl(pr['sound']))
+ frag.setColor(linkColor)
frags.append(frag)
if 'l2' in pr:
frags.append(
@@ -223,38 +226,141 @@ def do_prs(prs: list[Pronunciation]) -> list[Fragment]:
)
return frags
+def do_sense(sense: Sense|None) -> tuple[list[Fragment], list[Line]]:
+ if sense is None:
+ return ([],[])
+ lines: list[Line] = []
+ frags: list[Fragment] = []
+ r = Resources()
+ if 'sn' in sense:
+ sn = sense['sn']
+ else:
+ sn = ''
+ print(f'{sn}\n\n',json.dumps(sense['dt'], indent=2))
+ iters = [iter(sense['dt'])]*2
+ for pair in zip(*iters):
+ pair = trycast(tuple[str, Any], pair)
+ assert pair is not None
+ print(pair[0])
+ if pair[0] == 'text':
+ line = Line()
+ line.addFragment(
+ Fragment(pair[1], r.textFont, color=r.baseColor)
+ )
+ lines.append(line)
+ return (frags, lines)
+
+def do_pseq(outer: int,
+ inner: int,
+ pseq: list[list[Pair]]| None ) -> tuple[list[Fragment], list[Line]]:
+ assert pseq is not None
+ lines: list[Line] = []
+ frags: list[Fragment] = []
+ for entry in pseq:
+ pairs = make_pairs(entry)
+ for pair in pairs:
+ if pair['objType'] == 'bs':
+ (newFrags, newLines) = do_sense(trycast(Sense, pair['obj']))
+ frags += newFrags
+ lines += newLines
+ elif pair['objType'] == 'sense':
+ (newFrags, newLines) = do_sense(trycast(Sense, pair['obj']))
+ frags += newFrags
+ lines += newLines
+ else:
+ raise Exception(f"Unknown object type {pair['objType']}")
+ return (frags, lines)
+
+def do_sseq(sseq:list[list[list[Pair]]]) -> list[Line]:
+ lines: list[Line] = []
+ r = Resources()
+ for outer, item_o in enumerate(sseq):
+ line = Line()
+ line.addFragment(
+ Fragment(str(outer+1), r.boldFont, color=r.baseColor)
+ )
+ for inner, item_i in enumerate(item_o):
+ line.addFragment(
+ Fragment(chr(ord('a')+inner), r.boldFont, color=r.baseColor)
+ )
+ pairs = make_pairs(item_i)
+ for pair in pairs:
+ objType = pair['objType']
+ if objType == 'sense':
+ sense = trycast(Sense, pair['obj'])
+ (frags, newlines) = do_sense(sense)
+ for frag in frags:
+ line.addFragment(frag)
+ lines.append(line)
+ lines += newlines
+ elif objType == 'sen':
+ raise Exception(f"sen unimplimented")
+ elif objType == 'pseq':
+ pseq = trycast(list[list[Pair]], pair['obj'])
+ (frags, newlines) = do_pseq(inner, outer, trycast(list[list[Pair]], pair['obj']))
+ for frag in frags:
+ line.addFragment(frag)
+ lines.append(line)
+ lines += newlines
+ elif objType == 'bs':
+ raise Exception(f"bs unimplimented")
+ else:
+ raise Exception(f"Unknown object[{objType}] for \n{json.dumps(pair['obj'],indent=2)}")
+ return lines
+
+def do_def(entry: Definition) -> list[Line]:
+ r = Resources()
+ lines: list[Line] = []
+ assert trycast(Definition, entry) is not None
+ if 'vd' in entry:
+ line = Line()
+ line.addFragment(
+ Fragment(entry['vd'], r.italicFont, color = r.linkColor)
+ )
+ lines.append(line)
+ #
+ # sseg is required
+ #
+ sseq = entry['sseq']
+ lines += do_sseq(sseq)
+ return lines
+
def getDef(definition: list[Entry]) -> list[Line]:
- lines = []
+ r = Resources()
+ lines:list[Line] = []
#
# Pull the fonts for ease of use
#
- headerFont = trycast(QFont, Word._resources['fonts']['header'])
- assert headerFont is not None
- textFont = trycast(QFont, Word._resources['fonts']['text'])
- assert textFont is not None
- labelFont = trycast(QFont, Word._resources['fonts']['label'])
- assert labelFont is not None
+ headerFont = r.headerFont
+ textFont = r.textFont
+ labelFont = r.labelFont
#
# Pull the colors for ease of use
#
- baseColor = trycast(QColor, Word._resources['colors']['base'])
- assert baseColor is not None
- linkColor = trycast(QColor, Word._resources['colors']['link'])
- assert linkColor is not None
- subduedColor = trycast(QColor, Word._resources['colors']['subdued'])
- assert subduedColor is not None
+ baseColor = r.baseColor
+ linkColor = r.linkColor
+ subduedColor = r.subduedColor
#
# No need to figure it out each time it is used
#
entries = 0
- id = definition[0]['meta']['id']
- id = ':'.split(id)[0].lower()
+ id = definition[0]['meta']['id'].lower().split(':')[0]
+ uses: dict[str,int] = {}
for entry in definition:
- if entry['meta']['id'].lower() == id:
+ testId = entry['meta']['id'].lower().split(':')[0]
+ if testId == id:
entries += 1
+ try:
+ uses[entry['fl']] = uses.get(entry['fl'], 0) + 1
+ except KeyError:
+ pass
+ used: dict[str, int] = {}
+ for k in uses.keys():
+ used[k] = 0
for count, entry in enumerate(definition):
- if entry['meta']['id'].lower() != id:
+ testId = entry['meta']['id'].lower().split(':')[0]
+ if testId != id:
continue
#
# Create the First line from the hwi, [ahws] and fl
@@ -270,13 +376,16 @@ def getDef(definition: list[Entry]) -> list[Line]:
for ahw in ahws:
hw = re.sub(r'\*', '', ahw['hw'])
line.addFragment(Fragment(', ' + hw, headerFont, color=baseColor))
- if 'hom' in entry:
-
+ if entries > 1:
+ frag = Fragment(f" {count + 1} of {entries} ", textFont, color= subduedColor)
+ frag.setBackground(QColor(Qt.GlobalColor.gray))
+ line.addFragment(frag)
if 'fl' in entry:
- frag = Fragment(f"{count} of {entries} ", textFont, color=
- frag.setBackground(QColor(Qt.GlobalColor.gray))
- line.addFragment(frag)
- line.addFragment(Fragment(entry['fl'], labelFont, color=baseColor))
+ text = entry['fl']
+ used[text] += 1
+ if uses[text] > 1:
+ text += f' ({used[text]})'
+ line.addFragment(Fragment(text, labelFont, color=baseColor))
lines.append(line)
#
@@ -284,11 +393,15 @@ def getDef(definition: list[Entry]) -> list[Line]:
# While 'prs' is optional, the headword is not. This gets us what we want.
#
line = Line()
- hw = re.sub(r'\*', '\u00b7', hwi['hw'])
- line.addFragment(Fragment(hw + ' ', textFont, color=subduedColor))
- for frag in do_prs(hwi['prs']):
+ if hwi['hw'].find('*') >= 0:
+ hw = re.sub(r'\*', '\u00b7', hwi['hw'])
+ line.addFragment(Fragment(hw + ' ', textFont, color=subduedColor))
+ for frag in do_prs(hwi):
line.addFragment(frag)
-
- #
- # Try for
- return [Line()]
+ if len(line.getLine()) > 0:
+ lines.append(line)
+ defines = trycast(list[Definition], entry['def'])
+ assert defines is not None
+ for define in defines:
+ lines += do_def(define)
+ return lines