From ca33cb142f94d4f227fb46fea4e5ad13aa37b179 Mon Sep 17 00:00:00 2001 From: Marty Oehme <marty.oehme@gmail.com> Date: Wed, 29 Jan 2025 11:52:17 +0100 Subject: [PATCH] Begin deduplicating code with a lib Starting to move the separated efforts of functionality between CV and resume into a library from which to import. Will take additional simplification still. --- cv.typ | 163 +++-------------------------------------------------- lib.typ | 139 +++++++++++++++++++++++++++++++++++++++++++++ resume.typ | 141 +-------------------------------------------- 3 files changed, 150 insertions(+), 293 deletions(-) create mode 100644 lib.typ diff --git a/cv.typ b/cv.typ index 518cd71..a17b353 100644 --- a/cv.typ +++ b/cv.typ @@ -1,149 +1,4 @@ -#show heading: set text(font: "New Computer Modern") -#show link: underline - -// smartypants and latex compatibility -#show "--": [#sym.dash.en] -#show "---": [#sym.dash.em] -#show "\&": [#sym.amp] - -// Choose the compiled language through cli by doing -// -// $ typst compile --input lang=de cv.typ -// -#let lang = { - if "lang" in sys.inputs and sys.inputs.lang == "de" { - "de" - } else { - "en" - } -} -#let sel_word_lang(de: "", en: "") = { - if lang == "de" { - de - } else { - en - } -} - -#let _columns_3(left_body, center_body, right_body) = { - block[ - #box(width: 1fr)[ - #align(left)[#left_body] - ] - #box(width: 1fr)[ - #align(center)[#center_body] - ] - #box(width: 1fr)[ - #align(right)[#right_body] - ] - ] -} - -#let header(about, columns: (1.5fr, 1fr, 1fr)) = { - [= #about.fullname] - let contact_fields = ( - for c in about.contact { - if "link" in c { - ([#c.icon ~ #link(c.link)[#c.text]],) - } else { - ([#c.icon ~ #c.text],) - } - } - ) - grid( - columns: columns, - gutter: 5pt, - ..contact_fields - ) -} - -#let subdued(body) = { - block(inset: 5%, width: 85%, text(fill: luma(150), body)) -} - -#let entry(item: ()) = { - if "title" in item { - [*#item.title.at(lang)*] - } - if "place" in item { - if "title" in item { - [, ] - } - [_#item.place.at(lang)_] - } - [#h(1fr)] - if "date" in item { - [ _#item.date.at(lang)_ \ ] - } - if "bullets" in item { - for bullet in item.bullets { - [- #bullet.at(lang)] - } - } - if "publication" in item { - subdued[#item.publication.at(lang) \ ] - } - if "abstract" in item { - subdued[#item.abstract.at(lang) \ ] - } -} - -#let horizon_line() = { - v(-3pt) - line(length: 100%) - v(-5pt) -} - -#let section_header(title) = { - [== #title] - horizon_line() -}; - -#let section(title: "Section", entries: (), body) = { - section_header(title) - if body == none or body == [] { - for e in entries { - entry(item: e) - } - } else { - body - } -}; - -// Slightly re-styled entry with PLACE first and TITLE second -#let education_entry(item: ()) = { - assert( - "place" in item and "title" in item and "date" in item, - message: "Education items require place, program and date.", - ) - [*#item.place.at(lang)*, #item.title.at(lang) #h(1fr)] - [ _#item.date.at(lang)_ \ ] -} - -// Restyled entry with PLACE not emphasized like usual, and no date but an abstract -#let thesis_entry(item: ()) = { - assert("title" in item and "place" in item, message: "Thesis items require type and title.") - [*#item.title.at(lang)* #item.place.at(lang) #h(1fr)] - [#par(item.abstract.at(lang))] -} - -// skill-specific entry, changing its style for sidebar -#let skill_item(item: (), is_sidebar: false) = { - let side_list(body) = if is_sidebar { list(body) } else { par(body) } - for skill in item { - side_list({ - [*#skill.name.at(lang)*] - if is_sidebar [\ ] else [ (] - for (i, v) in skill.items.enumerate() { - [#v.at(lang)] - if i < skill.items.len() - 1 { - [, ] - } - } - if not is_sidebar [)] - }) - } -} +#import "lib.typ": * #let cv(contents, use_sidebar: false) = { set text(lang: lang) @@ -188,23 +43,23 @@ } if "experience" in contents { - let title = sel_word_lang(en: "Professional Experience", de: "Berufserfahrung") + let title = (en: "Professional Experience", de: "Berufserfahrung").at(lang) section(title: title, entries: contents.experience)[] } if "education" in contents { - let title = sel_word_lang(en: "Education", de: "Ausbildung") + let title = (en: "Education", de: "Ausbildung").at(lang) section(title: title, entries: contents.thesis + contents.education)[] } if not use_sidebar { if "volunteering" in contents { - let title = sel_word_lang(en: "Volunteer Work", de: "Ehrenamt") + let title = (en: "Volunteer Work", de: "Ehrenamt").at(lang) section(title: title, entries: contents.volunteering)[] } if "skills" in contents { - let title = sel_word_lang(en: "Qualifications", de: "Qualifikationen") + let title = (en: "Qualifications", de: "Qualifikationen").at(lang) section( title: title, { @@ -214,7 +69,7 @@ } if "languages" in contents { - let title = sel_word_lang(en: "Languages", de: "Sprachen") + let title = (en: "Languages", de: "Sprachen").at(lang) section( title: title, { @@ -227,7 +82,7 @@ let sidebar = { if "volunteering" in contents { - let title = sel_word_lang(en: "Volunteer Work", de: "Ehrenamt") + let title = (en: "Volunteer Work", de: "Ehrenamt").at(lang) [== #title] for e in contents.volunteering { [ @@ -238,14 +93,14 @@ } if "languages" in contents { - let title = sel_word_lang(en: "Languages", de: "Sprachen") + let title = (en: "Languages", de: "Sprachen").at(lang) [== #title] skill_item(item: contents.languages, is_sidebar: true) [\ ] } if "skills" in contents { - let title = sel_word_lang(en: "Qualifications", de: "Kenntnisse") + let title = (en: "Qualifications", de: "Kenntnisse").at(lang) [== #title] skill_item(item: contents.skills, is_sidebar: true) } diff --git a/lib.typ b/lib.typ new file mode 100644 index 0000000..51862d7 --- /dev/null +++ b/lib.typ @@ -0,0 +1,139 @@ +#show heading: set text(font: "New Computer Modern") +#show link: underline + +// smartypants and latex compatibility +#show "--": [#sym.dash.en] +#show "---": [#sym.dash.em] +#show "\&": [#sym.amp] + +// Choose the compiled language through cli by doing +// +// $ typst compile --input lang=de cv.typ +// +#let lang = { + if "lang" in sys.inputs and sys.inputs.lang == "de" { + "de" + } else { + "en" + } +} + +#let _columns_3(left_body, center_body, right_body) = { + block[ + #box(width: 1fr)[ + #align(left)[#left_body] + ] + #box(width: 1fr)[ + #align(center)[#center_body] + ] + #box(width: 1fr)[ + #align(right)[#right_body] + ] + ] +} + +#let header(about, columns: (1.5fr, 1fr, 1fr)) = { + [= #about.fullname] + let contact_fields = ( + for c in about.contact { + if "link" in c { + ([#c.icon ~ #link(c.link)[#c.text]],) + } else { + ([#c.icon ~ #c.text],) + } + } + ) + grid( + columns: columns, + gutter: 5pt, + ..contact_fields + ) +} + +#let subdued(body) = { + block(inset: 5%, width: 85%, text(fill: luma(150), body)) +} + +#let entry(item: ()) = { + if "title" in item { + [*#item.title.at(lang)*] + } + if "place" in item { + if "title" in item { + [, ] + } + [_#item.place.at(lang)_] + } + [#h(1fr)] + if "date" in item { + [ _#item.date.at(lang)_ \ ] + } + if "bullets" in item { + for bullet in item.bullets { + [- #bullet.at(lang)] + } + } + if "publication" in item { + subdued[#item.publication.at(lang) \ ] + } + if "abstract" in item { + subdued[#item.abstract.at(lang) \ ] + } +} + +#let horizon_line() = { + v(-3pt) + line(length: 100%) + v(-5pt) +} + +#let section_header(title) = { + [== #title] + horizon_line() +}; + +#let section(title: "Section", entries: (), body) = { + section_header(title) + if body == none or body == [] { + for e in entries { + entry(item: e) + } + } else { + body + } +}; + +// Slightly re-styled entry with PLACE first and TITLE second +#let education_entry(item: ()) = { + assert( + "place" in item and "title" in item and "date" in item, + message: "Education items require place, program and date.", + ) + [*#item.place.at(lang)*, #item.title.at(lang) #h(1fr)] + [ _#item.date.at(lang)_ \ ] +} + +// Restyled entry with PLACE not emphasized like usual, and no date but an abstract +#let thesis_entry(item: ()) = { + assert("title" in item and "place" in item, message: "Thesis items require type and title.") + [*#item.title.at(lang)* #item.place.at(lang) #h(1fr)] + [#par(item.abstract.at(lang))] +} + +// skill-specific entry, changing its style for sidebar +#let skill_item(item: (), is_sidebar: false) = { + let side_list(body) = if is_sidebar { list(body) } else { par(body) } + for skill in item { + side_list({ + [*#skill.name.at(lang)*] + if is_sidebar [\ ] else [ (] + for (i, v) in skill.items.enumerate() { + [#v.at(lang)] + if i < skill.items.len() - 1 { + [, ] + } + } + if not is_sidebar [)] + }) + } +} diff --git a/resume.typ b/resume.typ index 2d10d7d..489d044 100644 --- a/resume.typ +++ b/resume.typ @@ -1,59 +1,6 @@ -#show heading: set text(font: "New Computer Modern") -#show link: underline - -// smartypants and latex compatibility -#show "--": [#sym.dash.en] -#show "---": [#sym.dash.em] -#show "\&": [#sym.amp] - -// Choose the compiled language through cli by doing -// -// $ typst compile --input lang=de cv.typ -// -#let lang = { - if "lang" in sys.inputs and sys.inputs.lang == "de" { - "de" - } else { - "en" - } -} - -#let _columns_3(left_body, center_body, right_body) = { - block[ - #box(width: 1fr)[ - #align(left)[#left_body] - ] - #box(width: 1fr)[ - #align(center)[#center_body] - ] - #box(width: 1fr)[ - #align(right)[#right_body] - ] - ] -} - -#let header(about, columns: (1.5fr, 1fr, 1fr)) = { - [= #about.fullname] - let contact_fields = ( - for c in about.contact { - if "link" in c { - ([#c.icon ~ #link(c.link)[#c.text]],) - } else { - ([#c.icon ~ #c.text],) - } - } - ) - grid( - columns: columns, - gutter: 5pt, - ..contact_fields - ) -} - -#let subdued(body) = { - block(inset: 5%, width: 85%, text(fill: luma(150), body)) -} +#import "lib.typ": * +// TODO: make it _return_ the data, not display it on its own #let by_client(experience: ()) = { let by_client = (:) @@ -86,90 +33,6 @@ } } -#let entry(item: ()) = { - if "title" in item { - [*#item.title.at(lang)*] - } - if "place" in item { - if "title" in item { - [, ] - } - [_#item.place.at(lang)_] - } - [#h(1fr)] - if "date" in item { - [ _#item.date.at(lang)_ \ ] - } - if "bullets" in item { - for bullet in item.bullets { - [- #bullet.at(lang)] - } - } - if "publication" in item { - subdued[#item.publication.at(lang) \ ] - } - if "abstract" in item { - subdued[#item.abstract.at(lang) \ ] - } -} - -#let horizon_line() = { - v(-3pt) - line(length: 100%) - v(-5pt) -} - -#let section_header(title) = { - [== #title] - horizon_line() -}; - -#let section(title: "Section", entries: (), body) = { - section_header(title) - if body == none or body == [] { - for e in entries { - entry(item: e) - } - } else { - body - } -}; - -// Slightly re-styled entry with PLACE first and TITLE second -#let education_entry(item: ()) = { - assert( - "place" in item and "title" in item and "date" in item, - message: "Education items require place, program and date.", - ) - [*#item.place.at(lang)*, #item.title.at(lang) #h(1fr)] - [ _#item.date.at(lang)_ \ ] -} - -// Restyled entry with PLACE not emphasized like usual, and no date but an abstract -#let thesis_entry(item: ()) = { - assert("title" in item and "place" in item, message: "Thesis items require type and title.") - [*#item.title.at(lang)* #item.place.at(lang) #h(1fr)] - [#par(item.abstract.at(lang))] -} - -// skill-specific entry, changing its style for sidebar -#let skill_item(item: (), is_sidebar: false) = { - let side_list(body) = if is_sidebar { list(body) } else { par(body) } - for skill in item { - side_list({ - [*#skill.name.at(lang)*] - if is_sidebar [\ ] else [ (] - for (i, v) in skill.items.enumerate() { - [#v.at(lang)] - if i < skill.items.len() - 1 { - [, ] - } - } - if not is_sidebar [)] - }) - } -} - #let resume(contents, main: ("experience", "education"), sidebar: ("volunteering", "languages", "skills")) = { set text(lang: lang)