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        )
class Formatter:
 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.

Formatter( books: dict[tuple[typing.Annotated[int, Ge(ge=1), Le(le=999)], typing.Annotated[int, Ge(ge=1), Le(le=999)]], refspy.models.book.Book], book_aliases: dict[str, tuple[typing.Annotated[int, Ge(ge=1), Le(le=999)], typing.Annotated[int, Ge(ge=1), Le(le=999)]]])
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
books
book_aliases
def format( self, reference: refspy.models.reference.Reference, format: refspy.models.format.Format, if_invalid='[INVALID]') -> str:
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.

def name_format( self, language: refspy.models.language.Language) -> refspy.models.format.Format:
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        )
def book_format( self, language: refspy.models.language.Language) -> refspy.models.format.Format:
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        )
def number_format( self, language: refspy.models.language.Language) -> refspy.models.format.Format:
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        )
def abbrev_name_format( self, language: refspy.models.language.Language) -> refspy.models.format.Format:
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        )
def abbrev_book_format( self, language: refspy.models.language.Language) -> refspy.models.format.Format:
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        )
def abbrev_number_format( self, language: refspy.models.language.Language) -> refspy.models.format.Format:
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        )
def make_divider( self, _: refspy.models.range.Range, last: refspy.models.verse.Verse | None, format: refspy.models.format.Format) -> str:
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.

def make_book_range( self, _: refspy.models.range.Range, format: refspy.models.format.Format) -> str:
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

def make_inter_book_range( self, _: refspy.models.range.Range, format: refspy.models.format.Format) -> str:
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

def make_book( self, _: refspy.models.range.Range, format: refspy.models.format.Format) -> str:
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

def book_name_if_required( self, _: refspy.models.range.Range, last: refspy.models.verse.Verse | None, format: refspy.models.format.Format) -> str:
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

def make_chapter_range( self, _: refspy.models.range.Range, last: refspy.models.verse.Verse | None, format: refspy.models.format.Format) -> str:
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-2 or Matthew 1-2

def make_inter_chapter_range( self, _: refspy.models.range.Range, last: refspy.models.verse.Verse | None, format: refspy.models.format.Format) -> str:
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:2 or Matthew 1:1-2:2

def make_chapter( self, _: refspy.models.range.Range, last: refspy.models.verse.Verse | None, format: refspy.models.format.Format) -> str:
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:

1 or Matthew 1

def chapter_number_if_required( self, _: refspy.models.range.Range, last: refspy.models.verse.Verse | None, format: refspy.models.format.Format) -> str:
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:

def make_verse_range( self, _: refspy.models.range.Range, last: refspy.models.verse.Verse | None, format: refspy.models.format.Format) -> str:
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-3 or 1:2-3 or Matthew 1:2-3

Note:

We abbreviate the second number if possible. See abbreviate_range().

def make_verse( self, _: refspy.models.range.Range, last: refspy.models.verse.Verse | None, format: refspy.models.format.Format) -> str:
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:

2 or 1:2 or Matthew 1:2