refspy.formatter
Format Reference objects using Format objects.
1"""Format Reference objects using Format objects.""" 2 3from refspy.models.language import Language 4from refspy.languages.english import ENGLISH 5 6from refspy.models.book import Book 7from refspy.models.format import Format 8from refspy.models.range import Range 9from refspy.models.reference import Reference 10from refspy.models.verse import Verse 11 12from refspy.types.number import Number 13 14from refspy.constants import EM_DASH, SPACE 15from refspy.utils import string_together 16 17 18class Formatter: 19 """ 20 Format an arbitrary reference, using supplied formatting options. 21 22 References contain lists of `refspy.range.Range` objects. These can span 23 arbitrary verses across multiple libraries, and can be arranged in any 24 order. The formatter formats the individual ranges correctly and also 25 indicates any changes between books and chapters. 26 """ 27 28 def __init__( 29 self, 30 books: dict[tuple[Number, Number], Book], 31 book_aliases: dict[str, tuple[Number, Number]], 32 ) -> None: 33 self.books = books 34 self.book_aliases = book_aliases 35 36 def format( 37 self, 38 reference: Reference, 39 format: Format, 40 if_invalid="[INVALID]", 41 ) -> str: 42 """ 43 Compare each range with the one before and decide whether we need to add 44 the book name or chapter number to each range. This will work for 45 sorted and unsorted references, but sorted references will be more compact. 46 """ 47 out = "" 48 last_verse = None 49 for next_range in reference.ranges: 50 if len(out) > 0: 51 out += self.make_divider(next_range, last_verse, format) 52 if next_range.is_book_range(): 53 out += self.make_book_range(next_range, format) 54 elif next_range.is_inter_book_range(): 55 out += self.make_inter_book_range(next_range, format) 56 elif next_range.is_book(): 57 out += self.make_book(next_range, format) 58 elif next_range.is_chapter_range(): 59 out += self.make_chapter_range(next_range, last_verse, format) 60 elif next_range.is_inter_chapter_range(): 61 out += self.make_inter_chapter_range(next_range, last_verse, format) 62 elif next_range.is_chapter(): 63 out += self.make_chapter(next_range, last_verse, format) 64 elif next_range.is_verse_range(): 65 out += self.make_verse_range(next_range, last_verse, format) 66 elif next_range.is_verse(): 67 out += self.make_verse(next_range, last_verse, format) 68 else: 69 out += if_invalid 70 last_verse = next_range.start 71 return out 72 73 def link_format(self) -> Format: 74 """ 75 Follow English format conventions because they are more widely 76 used by linkable resources. 77 """ 78 return Format( 79 colon=ENGLISH.syntax.format_colon, 80 comma=ENGLISH.syntax.format_comma, 81 dash=ENGLISH.syntax.format_dash, 82 semicolon=ENGLISH.syntax.format_semicolon, 83 book_only=False, 84 number_only=False, 85 property="abbrev", 86 ) 87 88 def name_format(self, language: Language) -> Format: 89 return Format( 90 colon=language.syntax.format_colon, 91 comma=language.syntax.format_comma, 92 dash=language.syntax.format_dash, 93 semicolon=language.syntax.format_semicolon, 94 book_only=False, 95 number_only=False, 96 property="name", 97 ) 98 99 def book_format(self, language: Language) -> Format: 100 return Format( 101 colon=language.syntax.format_colon, 102 comma=language.syntax.format_comma, 103 dash=language.syntax.format_dash, 104 semicolon=language.syntax.format_semicolon, 105 book_only=True, 106 number_only=False, 107 property="name", 108 ) 109 110 def number_format(self, language: Language) -> Format: 111 return Format( 112 colon=language.syntax.format_colon, 113 comma=language.syntax.format_comma, 114 dash=language.syntax.format_dash, 115 semicolon=language.syntax.format_semicolon, 116 book_only=False, 117 number_only=True, 118 property=None, 119 ) 120 121 def abbrev_name_format(self, language: Language) -> Format: 122 return Format( 123 colon=language.syntax.format_colon, 124 comma=language.syntax.format_comma, 125 dash=language.syntax.format_dash, 126 semicolon=language.syntax.format_semicolon, 127 book_only=False, 128 number_only=False, 129 property="abbrev", 130 ) 131 132 def abbrev_book_format(self, language: Language) -> Format: 133 return Format( 134 colon=language.syntax.format_colon, 135 comma=language.syntax.format_comma, 136 dash=language.syntax.format_dash, 137 semicolon=language.syntax.format_semicolon, 138 book_only=True, 139 number_only=False, 140 property="abbrev", 141 ) 142 143 def abbrev_number_format(self, language: Language) -> Format: 144 return Format( 145 colon=language.syntax.format_colon, 146 comma=language.syntax.format_comma, 147 dash=language.syntax.format_dash, 148 semicolon=language.syntax.format_semicolon, 149 book_only=False, 150 number_only=True, 151 property="abbrev", 152 ) 153 154 def make_divider(self, _: Range, last: Verse | None, format: Format) -> str: 155 """Provide a separator between ranges. 156 157 This will be a semicolon or comma. 158 """ 159 if last is None: 160 return "" 161 if any( 162 [ 163 _.start.library != last.library, 164 _.start.book != last.book, 165 _.start.chapter != last.chapter, 166 ] 167 ): 168 return format.semicolon 169 else: 170 return format.comma 171 172 def make_book_range(self, _: Range, format: Format) -> str: 173 """Format a range between two whole books. 174 175 See `refspy.range.Range.is_book_range` . 176 177 Example: 178 `Matthew-John` 179 """ 180 start_book = self.books[_.start.library, _.start.book] 181 start_book_name = getattr(start_book, format.property or "", "") 182 end_book = self.books[_.end.library, _.end.book] 183 end_book_name = getattr(end_book, format.property or "", "") 184 return string_together(start_book_name, EM_DASH, end_book_name) 185 186 def make_inter_book_range(self, _: Range, format: Format) -> str: 187 """Format a range that spans multiple books (or libraries). 188 189 See `refspy.range.Range.is_inter_book_range` . 190 191 Example: 192 `Matt 15:15-John 15:15` 193 """ 194 start_book = self.books[_.start.library, _.start.book] 195 start_book_name = getattr(start_book, format.property or "", "") 196 if _.start.verse == 1 and _.end.verse == 999: 197 start_number = string_together(_.start.chapter) 198 else: 199 start_number = string_together(_.start.chapter, format.colon, _.start.verse) 200 end_book = self.books[_.end.library, _.end.book] 201 end_book_name = getattr(end_book, format.property or "", "") 202 if _.start.verse == 1 and _.end.verse == 999: 203 end_number = string_together(_.end.chapter) 204 else: 205 end_number = string_together(_.end.chapter, format.colon, _.end.verse) 206 207 return SPACE.join( 208 [start_book_name, start_number, format.dash, end_book_name, end_number] 209 ) 210 211 def make_book(self, _: Range, format: Format) -> str: 212 """Format a range that spans multiple books (or libraries). 213 214 See `refspy.range.Range.is_book` . 215 216 Example: 217 `Matthew` 218 """ 219 start_book = self.books[_.start.library, _.start.book] 220 start_book_name = getattr(start_book, format.property or "", "") 221 return start_book_name 222 223 def book_name_if_required( 224 self, _: Range, last: Verse | None, format: Format 225 ) -> str: 226 """Format the book name (and a space) if the next range is in a 227 different book. 228 229 Example: 230 `Matthew ` 231 """ 232 if format.number_only: 233 return "" 234 book_name = "" 235 if last is None: 236 book_name = self.make_book(_, format) 237 elif _.start.library != last.library or _.start.book != last.book: 238 book_name = self.make_book(_, format) 239 if book_name != "": 240 if format.book_only: 241 return book_name 242 else: 243 return book_name + SPACE 244 return "" 245 246 def make_chapter_range(self, _: Range, last: Verse | None, format: Format) -> str: 247 """Format a range that spans whole chapters. 248 249 Add the book name, if required. 250 251 See `refspy.range.Range.is_chapter_range` . 252 253 Example: 254 `1-2` or `Matthew 1-2` 255 """ 256 return string_together( 257 self.book_name_if_required(_, last, format), 258 _.start.chapter, 259 format.dash, 260 _.end.chapter, 261 ) 262 263 def make_inter_chapter_range( 264 self, _: Range, last: Verse | None, format: Format 265 ) -> str: 266 """Format a range that spans different chapters. 267 268 Add the book name, if required. 269 270 See `refspy.range.Range.is_inter_chapter_range` . 271 272 Example: 273 `1:1-2:2` or `Matthew 1:1-2:2` 274 """ 275 return string_together( 276 self.book_name_if_required(_, last, format), 277 _.start.chapter, 278 format.colon, 279 _.start.verse, 280 format.dash, 281 _.end.chapter, 282 format.colon, 283 _.end.verse, 284 ) 285 286 def make_chapter(self, _: Range, last: Verse | None, format: Format) -> str: 287 """Format a range that spans different chapters. 288 289 Add the book name, if required. 290 291 See `refspy.range.Range.is_chapter`. 292 293 Example: 294 `1` or `Matthew 1` 295 """ 296 return string_together( 297 self.book_name_if_required(_, last, format), _.start.chapter 298 ) 299 300 def chapter_number_if_required( 301 self, _: Range, last: Verse | None, format: Format 302 ) -> str: 303 """Format the chapter number and colon if the next range is in a 304 different book. 305 306 Example: 307 `1:` 308 """ 309 if format.book_only: 310 return "" 311 book = self.books[_.start.library, _.start.book] 312 if last is None: 313 if book.chapters > 1: 314 return string_together(_.start.chapter, format.colon) 315 elif ( 316 _.start.library != last.library 317 or _.start.book != last.book 318 or _.start.chapter != last.chapter 319 ): 320 if book.chapters > 1: 321 return string_together(_.start.chapter, format.colon) 322 return "" 323 324 def make_verse_range(self, _: Range, last: Verse | None, format: Format) -> str: 325 """Format a range of different verses within a single chapter. 326 327 Add the chapter number and book name, if required. 328 329 See `refspy.range.Range.is_verse_range` . 330 331 Example: 332 `2-3` or `1:2-3` or `Matthew 1:2-3` 333 334 Note: 335 We abbreviate the second number if possible. See `abbreviate_range()`. 336 """ 337 if format.book_only: 338 return "" 339 return string_together( 340 self.book_name_if_required(_, last, format), 341 self.chapter_number_if_required(_, last, format), 342 "" if format.book_only else _.start.verse, 343 "" if format.book_only else format.dash, 344 "" if format.book_only else _.end.verse, 345 ) 346 347 def make_verse(self, _: Range, last: Verse | None, format: Format) -> str: 348 """Format a single verse number. 349 350 Add the chapter number and book name, if required. 351 352 See `refspy.range.Range.is_verse` . 353 354 Example: 355 `2` or `1:2` or `Matthew 1:2` 356 """ 357 return string_together( 358 self.book_name_if_required(_, last, format), 359 self.chapter_number_if_required(_, last, format), 360 "" if format.book_only else _.start.verse, 361 )
19class Formatter: 20 """ 21 Format an arbitrary reference, using supplied formatting options. 22 23 References contain lists of `refspy.range.Range` objects. These can span 24 arbitrary verses across multiple libraries, and can be arranged in any 25 order. The formatter formats the individual ranges correctly and also 26 indicates any changes between books and chapters. 27 """ 28 29 def __init__( 30 self, 31 books: dict[tuple[Number, Number], Book], 32 book_aliases: dict[str, tuple[Number, Number]], 33 ) -> None: 34 self.books = books 35 self.book_aliases = book_aliases 36 37 def format( 38 self, 39 reference: Reference, 40 format: Format, 41 if_invalid="[INVALID]", 42 ) -> str: 43 """ 44 Compare each range with the one before and decide whether we need to add 45 the book name or chapter number to each range. This will work for 46 sorted and unsorted references, but sorted references will be more compact. 47 """ 48 out = "" 49 last_verse = None 50 for next_range in reference.ranges: 51 if len(out) > 0: 52 out += self.make_divider(next_range, last_verse, format) 53 if next_range.is_book_range(): 54 out += self.make_book_range(next_range, format) 55 elif next_range.is_inter_book_range(): 56 out += self.make_inter_book_range(next_range, format) 57 elif next_range.is_book(): 58 out += self.make_book(next_range, format) 59 elif next_range.is_chapter_range(): 60 out += self.make_chapter_range(next_range, last_verse, format) 61 elif next_range.is_inter_chapter_range(): 62 out += self.make_inter_chapter_range(next_range, last_verse, format) 63 elif next_range.is_chapter(): 64 out += self.make_chapter(next_range, last_verse, format) 65 elif next_range.is_verse_range(): 66 out += self.make_verse_range(next_range, last_verse, format) 67 elif next_range.is_verse(): 68 out += self.make_verse(next_range, last_verse, format) 69 else: 70 out += if_invalid 71 last_verse = next_range.start 72 return out 73 74 def link_format(self) -> Format: 75 """ 76 Follow English format conventions because they are more widely 77 used by linkable resources. 78 """ 79 return Format( 80 colon=ENGLISH.syntax.format_colon, 81 comma=ENGLISH.syntax.format_comma, 82 dash=ENGLISH.syntax.format_dash, 83 semicolon=ENGLISH.syntax.format_semicolon, 84 book_only=False, 85 number_only=False, 86 property="abbrev", 87 ) 88 89 def name_format(self, language: Language) -> Format: 90 return Format( 91 colon=language.syntax.format_colon, 92 comma=language.syntax.format_comma, 93 dash=language.syntax.format_dash, 94 semicolon=language.syntax.format_semicolon, 95 book_only=False, 96 number_only=False, 97 property="name", 98 ) 99 100 def book_format(self, language: Language) -> Format: 101 return Format( 102 colon=language.syntax.format_colon, 103 comma=language.syntax.format_comma, 104 dash=language.syntax.format_dash, 105 semicolon=language.syntax.format_semicolon, 106 book_only=True, 107 number_only=False, 108 property="name", 109 ) 110 111 def number_format(self, language: Language) -> Format: 112 return Format( 113 colon=language.syntax.format_colon, 114 comma=language.syntax.format_comma, 115 dash=language.syntax.format_dash, 116 semicolon=language.syntax.format_semicolon, 117 book_only=False, 118 number_only=True, 119 property=None, 120 ) 121 122 def abbrev_name_format(self, language: Language) -> Format: 123 return Format( 124 colon=language.syntax.format_colon, 125 comma=language.syntax.format_comma, 126 dash=language.syntax.format_dash, 127 semicolon=language.syntax.format_semicolon, 128 book_only=False, 129 number_only=False, 130 property="abbrev", 131 ) 132 133 def abbrev_book_format(self, language: Language) -> Format: 134 return Format( 135 colon=language.syntax.format_colon, 136 comma=language.syntax.format_comma, 137 dash=language.syntax.format_dash, 138 semicolon=language.syntax.format_semicolon, 139 book_only=True, 140 number_only=False, 141 property="abbrev", 142 ) 143 144 def abbrev_number_format(self, language: Language) -> Format: 145 return Format( 146 colon=language.syntax.format_colon, 147 comma=language.syntax.format_comma, 148 dash=language.syntax.format_dash, 149 semicolon=language.syntax.format_semicolon, 150 book_only=False, 151 number_only=True, 152 property="abbrev", 153 ) 154 155 def make_divider(self, _: Range, last: Verse | None, format: Format) -> str: 156 """Provide a separator between ranges. 157 158 This will be a semicolon or comma. 159 """ 160 if last is None: 161 return "" 162 if any( 163 [ 164 _.start.library != last.library, 165 _.start.book != last.book, 166 _.start.chapter != last.chapter, 167 ] 168 ): 169 return format.semicolon 170 else: 171 return format.comma 172 173 def make_book_range(self, _: Range, format: Format) -> str: 174 """Format a range between two whole books. 175 176 See `refspy.range.Range.is_book_range` . 177 178 Example: 179 `Matthew-John` 180 """ 181 start_book = self.books[_.start.library, _.start.book] 182 start_book_name = getattr(start_book, format.property or "", "") 183 end_book = self.books[_.end.library, _.end.book] 184 end_book_name = getattr(end_book, format.property or "", "") 185 return string_together(start_book_name, EM_DASH, end_book_name) 186 187 def make_inter_book_range(self, _: Range, format: Format) -> str: 188 """Format a range that spans multiple books (or libraries). 189 190 See `refspy.range.Range.is_inter_book_range` . 191 192 Example: 193 `Matt 15:15-John 15:15` 194 """ 195 start_book = self.books[_.start.library, _.start.book] 196 start_book_name = getattr(start_book, format.property or "", "") 197 if _.start.verse == 1 and _.end.verse == 999: 198 start_number = string_together(_.start.chapter) 199 else: 200 start_number = string_together(_.start.chapter, format.colon, _.start.verse) 201 end_book = self.books[_.end.library, _.end.book] 202 end_book_name = getattr(end_book, format.property or "", "") 203 if _.start.verse == 1 and _.end.verse == 999: 204 end_number = string_together(_.end.chapter) 205 else: 206 end_number = string_together(_.end.chapter, format.colon, _.end.verse) 207 208 return SPACE.join( 209 [start_book_name, start_number, format.dash, end_book_name, end_number] 210 ) 211 212 def make_book(self, _: Range, format: Format) -> str: 213 """Format a range that spans multiple books (or libraries). 214 215 See `refspy.range.Range.is_book` . 216 217 Example: 218 `Matthew` 219 """ 220 start_book = self.books[_.start.library, _.start.book] 221 start_book_name = getattr(start_book, format.property or "", "") 222 return start_book_name 223 224 def book_name_if_required( 225 self, _: Range, last: Verse | None, format: Format 226 ) -> str: 227 """Format the book name (and a space) if the next range is in a 228 different book. 229 230 Example: 231 `Matthew ` 232 """ 233 if format.number_only: 234 return "" 235 book_name = "" 236 if last is None: 237 book_name = self.make_book(_, format) 238 elif _.start.library != last.library or _.start.book != last.book: 239 book_name = self.make_book(_, format) 240 if book_name != "": 241 if format.book_only: 242 return book_name 243 else: 244 return book_name + SPACE 245 return "" 246 247 def make_chapter_range(self, _: Range, last: Verse | None, format: Format) -> str: 248 """Format a range that spans whole chapters. 249 250 Add the book name, if required. 251 252 See `refspy.range.Range.is_chapter_range` . 253 254 Example: 255 `1-2` or `Matthew 1-2` 256 """ 257 return string_together( 258 self.book_name_if_required(_, last, format), 259 _.start.chapter, 260 format.dash, 261 _.end.chapter, 262 ) 263 264 def make_inter_chapter_range( 265 self, _: Range, last: Verse | None, format: Format 266 ) -> str: 267 """Format a range that spans different chapters. 268 269 Add the book name, if required. 270 271 See `refspy.range.Range.is_inter_chapter_range` . 272 273 Example: 274 `1:1-2:2` or `Matthew 1:1-2:2` 275 """ 276 return string_together( 277 self.book_name_if_required(_, last, format), 278 _.start.chapter, 279 format.colon, 280 _.start.verse, 281 format.dash, 282 _.end.chapter, 283 format.colon, 284 _.end.verse, 285 ) 286 287 def make_chapter(self, _: Range, last: Verse | None, format: Format) -> str: 288 """Format a range that spans different chapters. 289 290 Add the book name, if required. 291 292 See `refspy.range.Range.is_chapter`. 293 294 Example: 295 `1` or `Matthew 1` 296 """ 297 return string_together( 298 self.book_name_if_required(_, last, format), _.start.chapter 299 ) 300 301 def chapter_number_if_required( 302 self, _: Range, last: Verse | None, format: Format 303 ) -> str: 304 """Format the chapter number and colon if the next range is in a 305 different book. 306 307 Example: 308 `1:` 309 """ 310 if format.book_only: 311 return "" 312 book = self.books[_.start.library, _.start.book] 313 if last is None: 314 if book.chapters > 1: 315 return string_together(_.start.chapter, format.colon) 316 elif ( 317 _.start.library != last.library 318 or _.start.book != last.book 319 or _.start.chapter != last.chapter 320 ): 321 if book.chapters > 1: 322 return string_together(_.start.chapter, format.colon) 323 return "" 324 325 def make_verse_range(self, _: Range, last: Verse | None, format: Format) -> str: 326 """Format a range of different verses within a single chapter. 327 328 Add the chapter number and book name, if required. 329 330 See `refspy.range.Range.is_verse_range` . 331 332 Example: 333 `2-3` or `1:2-3` or `Matthew 1:2-3` 334 335 Note: 336 We abbreviate the second number if possible. See `abbreviate_range()`. 337 """ 338 if format.book_only: 339 return "" 340 return string_together( 341 self.book_name_if_required(_, last, format), 342 self.chapter_number_if_required(_, last, format), 343 "" if format.book_only else _.start.verse, 344 "" if format.book_only else format.dash, 345 "" if format.book_only else _.end.verse, 346 ) 347 348 def make_verse(self, _: Range, last: Verse | None, format: Format) -> str: 349 """Format a single verse number. 350 351 Add the chapter number and book name, if required. 352 353 See `refspy.range.Range.is_verse` . 354 355 Example: 356 `2` or `1:2` or `Matthew 1:2` 357 """ 358 return string_together( 359 self.book_name_if_required(_, last, format), 360 self.chapter_number_if_required(_, last, format), 361 "" if format.book_only else _.start.verse, 362 )
Format an arbitrary reference, using supplied formatting options.
References contain lists of refspy.range.Range objects. These can span
arbitrary verses across multiple libraries, and can be arranged in any
order. The formatter formats the individual ranges correctly and also
indicates any changes between books and chapters.
37 def format( 38 self, 39 reference: Reference, 40 format: Format, 41 if_invalid="[INVALID]", 42 ) -> str: 43 """ 44 Compare each range with the one before and decide whether we need to add 45 the book name or chapter number to each range. This will work for 46 sorted and unsorted references, but sorted references will be more compact. 47 """ 48 out = "" 49 last_verse = None 50 for next_range in reference.ranges: 51 if len(out) > 0: 52 out += self.make_divider(next_range, last_verse, format) 53 if next_range.is_book_range(): 54 out += self.make_book_range(next_range, format) 55 elif next_range.is_inter_book_range(): 56 out += self.make_inter_book_range(next_range, format) 57 elif next_range.is_book(): 58 out += self.make_book(next_range, format) 59 elif next_range.is_chapter_range(): 60 out += self.make_chapter_range(next_range, last_verse, format) 61 elif next_range.is_inter_chapter_range(): 62 out += self.make_inter_chapter_range(next_range, last_verse, format) 63 elif next_range.is_chapter(): 64 out += self.make_chapter(next_range, last_verse, format) 65 elif next_range.is_verse_range(): 66 out += self.make_verse_range(next_range, last_verse, format) 67 elif next_range.is_verse(): 68 out += self.make_verse(next_range, last_verse, format) 69 else: 70 out += if_invalid 71 last_verse = next_range.start 72 return out
Compare each range with the one before and decide whether we need to add the book name or chapter number to each range. This will work for sorted and unsorted references, but sorted references will be more compact.
74 def link_format(self) -> Format: 75 """ 76 Follow English format conventions because they are more widely 77 used by linkable resources. 78 """ 79 return Format( 80 colon=ENGLISH.syntax.format_colon, 81 comma=ENGLISH.syntax.format_comma, 82 dash=ENGLISH.syntax.format_dash, 83 semicolon=ENGLISH.syntax.format_semicolon, 84 book_only=False, 85 number_only=False, 86 property="abbrev", 87 )
Follow English format conventions because they are more widely used by linkable resources.
89 def name_format(self, language: Language) -> Format: 90 return Format( 91 colon=language.syntax.format_colon, 92 comma=language.syntax.format_comma, 93 dash=language.syntax.format_dash, 94 semicolon=language.syntax.format_semicolon, 95 book_only=False, 96 number_only=False, 97 property="name", 98 )
100 def book_format(self, language: Language) -> Format: 101 return Format( 102 colon=language.syntax.format_colon, 103 comma=language.syntax.format_comma, 104 dash=language.syntax.format_dash, 105 semicolon=language.syntax.format_semicolon, 106 book_only=True, 107 number_only=False, 108 property="name", 109 )
111 def number_format(self, language: Language) -> Format: 112 return Format( 113 colon=language.syntax.format_colon, 114 comma=language.syntax.format_comma, 115 dash=language.syntax.format_dash, 116 semicolon=language.syntax.format_semicolon, 117 book_only=False, 118 number_only=True, 119 property=None, 120 )
122 def abbrev_name_format(self, language: Language) -> Format: 123 return Format( 124 colon=language.syntax.format_colon, 125 comma=language.syntax.format_comma, 126 dash=language.syntax.format_dash, 127 semicolon=language.syntax.format_semicolon, 128 book_only=False, 129 number_only=False, 130 property="abbrev", 131 )
133 def abbrev_book_format(self, language: Language) -> Format: 134 return Format( 135 colon=language.syntax.format_colon, 136 comma=language.syntax.format_comma, 137 dash=language.syntax.format_dash, 138 semicolon=language.syntax.format_semicolon, 139 book_only=True, 140 number_only=False, 141 property="abbrev", 142 )
144 def abbrev_number_format(self, language: Language) -> Format: 145 return Format( 146 colon=language.syntax.format_colon, 147 comma=language.syntax.format_comma, 148 dash=language.syntax.format_dash, 149 semicolon=language.syntax.format_semicolon, 150 book_only=False, 151 number_only=True, 152 property="abbrev", 153 )
155 def make_divider(self, _: Range, last: Verse | None, format: Format) -> str: 156 """Provide a separator between ranges. 157 158 This will be a semicolon or comma. 159 """ 160 if last is None: 161 return "" 162 if any( 163 [ 164 _.start.library != last.library, 165 _.start.book != last.book, 166 _.start.chapter != last.chapter, 167 ] 168 ): 169 return format.semicolon 170 else: 171 return format.comma
Provide a separator between ranges.
This will be a semicolon or comma.
173 def make_book_range(self, _: Range, format: Format) -> str: 174 """Format a range between two whole books. 175 176 See `refspy.range.Range.is_book_range` . 177 178 Example: 179 `Matthew-John` 180 """ 181 start_book = self.books[_.start.library, _.start.book] 182 start_book_name = getattr(start_book, format.property or "", "") 183 end_book = self.books[_.end.library, _.end.book] 184 end_book_name = getattr(end_book, format.property or "", "") 185 return string_together(start_book_name, EM_DASH, end_book_name)
Format a range between two whole books.
See refspy.range.Range.is_book_range .
Example:
Matthew-John
187 def make_inter_book_range(self, _: Range, format: Format) -> str: 188 """Format a range that spans multiple books (or libraries). 189 190 See `refspy.range.Range.is_inter_book_range` . 191 192 Example: 193 `Matt 15:15-John 15:15` 194 """ 195 start_book = self.books[_.start.library, _.start.book] 196 start_book_name = getattr(start_book, format.property or "", "") 197 if _.start.verse == 1 and _.end.verse == 999: 198 start_number = string_together(_.start.chapter) 199 else: 200 start_number = string_together(_.start.chapter, format.colon, _.start.verse) 201 end_book = self.books[_.end.library, _.end.book] 202 end_book_name = getattr(end_book, format.property or "", "") 203 if _.start.verse == 1 and _.end.verse == 999: 204 end_number = string_together(_.end.chapter) 205 else: 206 end_number = string_together(_.end.chapter, format.colon, _.end.verse) 207 208 return SPACE.join( 209 [start_book_name, start_number, format.dash, end_book_name, end_number] 210 )
Format a range that spans multiple books (or libraries).
See refspy.range.Range.is_inter_book_range .
Example:
Matt 15:15-John 15:15
212 def make_book(self, _: Range, format: Format) -> str: 213 """Format a range that spans multiple books (or libraries). 214 215 See `refspy.range.Range.is_book` . 216 217 Example: 218 `Matthew` 219 """ 220 start_book = self.books[_.start.library, _.start.book] 221 start_book_name = getattr(start_book, format.property or "", "") 222 return start_book_name
Format a range that spans multiple books (or libraries).
See refspy.range.Range.is_book .
Example:
Matthew
224 def book_name_if_required( 225 self, _: Range, last: Verse | None, format: Format 226 ) -> str: 227 """Format the book name (and a space) if the next range is in a 228 different book. 229 230 Example: 231 `Matthew ` 232 """ 233 if format.number_only: 234 return "" 235 book_name = "" 236 if last is None: 237 book_name = self.make_book(_, format) 238 elif _.start.library != last.library or _.start.book != last.book: 239 book_name = self.make_book(_, format) 240 if book_name != "": 241 if format.book_only: 242 return book_name 243 else: 244 return book_name + SPACE 245 return ""
Format the book name (and a space) if the next range is in a different book.
Example:
Matthew
247 def make_chapter_range(self, _: Range, last: Verse | None, format: Format) -> str: 248 """Format a range that spans whole chapters. 249 250 Add the book name, if required. 251 252 See `refspy.range.Range.is_chapter_range` . 253 254 Example: 255 `1-2` or `Matthew 1-2` 256 """ 257 return string_together( 258 self.book_name_if_required(_, last, format), 259 _.start.chapter, 260 format.dash, 261 _.end.chapter, 262 )
Format a range that spans whole chapters.
Add the book name, if required.
See refspy.range.Range.is_chapter_range .
Example:
1-2orMatthew 1-2
264 def make_inter_chapter_range( 265 self, _: Range, last: Verse | None, format: Format 266 ) -> str: 267 """Format a range that spans different chapters. 268 269 Add the book name, if required. 270 271 See `refspy.range.Range.is_inter_chapter_range` . 272 273 Example: 274 `1:1-2:2` or `Matthew 1:1-2:2` 275 """ 276 return string_together( 277 self.book_name_if_required(_, last, format), 278 _.start.chapter, 279 format.colon, 280 _.start.verse, 281 format.dash, 282 _.end.chapter, 283 format.colon, 284 _.end.verse, 285 )
Format a range that spans different chapters.
Add the book name, if required.
See refspy.range.Range.is_inter_chapter_range .
Example:
1:1-2:2orMatthew 1:1-2:2
287 def make_chapter(self, _: Range, last: Verse | None, format: Format) -> str: 288 """Format a range that spans different chapters. 289 290 Add the book name, if required. 291 292 See `refspy.range.Range.is_chapter`. 293 294 Example: 295 `1` or `Matthew 1` 296 """ 297 return string_together( 298 self.book_name_if_required(_, last, format), _.start.chapter 299 )
Format a range that spans different chapters.
Add the book name, if required.
See refspy.range.Range.is_chapter.
Example:
1orMatthew 1
301 def chapter_number_if_required( 302 self, _: Range, last: Verse | None, format: Format 303 ) -> str: 304 """Format the chapter number and colon if the next range is in a 305 different book. 306 307 Example: 308 `1:` 309 """ 310 if format.book_only: 311 return "" 312 book = self.books[_.start.library, _.start.book] 313 if last is None: 314 if book.chapters > 1: 315 return string_together(_.start.chapter, format.colon) 316 elif ( 317 _.start.library != last.library 318 or _.start.book != last.book 319 or _.start.chapter != last.chapter 320 ): 321 if book.chapters > 1: 322 return string_together(_.start.chapter, format.colon) 323 return ""
Format the chapter number and colon if the next range is in a different book.
Example:
1:
325 def make_verse_range(self, _: Range, last: Verse | None, format: Format) -> str: 326 """Format a range of different verses within a single chapter. 327 328 Add the chapter number and book name, if required. 329 330 See `refspy.range.Range.is_verse_range` . 331 332 Example: 333 `2-3` or `1:2-3` or `Matthew 1:2-3` 334 335 Note: 336 We abbreviate the second number if possible. See `abbreviate_range()`. 337 """ 338 if format.book_only: 339 return "" 340 return string_together( 341 self.book_name_if_required(_, last, format), 342 self.chapter_number_if_required(_, last, format), 343 "" if format.book_only else _.start.verse, 344 "" if format.book_only else format.dash, 345 "" if format.book_only else _.end.verse, 346 )
Format a range of different verses within a single chapter.
Add the chapter number and book name, if required.
See refspy.range.Range.is_verse_range .
Example:
2-3or1:2-3orMatthew 1:2-3
Note:
We abbreviate the second number if possible. See
abbreviate_range().
348 def make_verse(self, _: Range, last: Verse | None, format: Format) -> str: 349 """Format a single verse number. 350 351 Add the chapter number and book name, if required. 352 353 See `refspy.range.Range.is_verse` . 354 355 Example: 356 `2` or `1:2` or `Matthew 1:2` 357 """ 358 return string_together( 359 self.book_name_if_required(_, last, format), 360 self.chapter_number_if_required(_, last, format), 361 "" if format.book_only else _.start.verse, 362 )
Format a single verse number.
Add the chapter number and book name, if required.
See refspy.range.Range.is_verse .
Example:
2or1:2orMatthew 1:2