@@ -14,6 +14,7 @@ defmodule IO.ANSI.Docs do
1414 * `:doc_code` - code blocks (cyan)
1515 * `:doc_headings` - h1, h2, h3, h4, h5, h6 headings (yellow)
1616 * `:doc_metadata` - documentation metadata keys (yellow)
17+ * `:doc_quote` - leading quote character `> ` (light black)
1718 * `:doc_inline_code` - inline code (cyan)
1819 * `:doc_table_heading` - the style for table headings
1920 * `:doc_title` - top level heading (reverse, yellow)
@@ -31,6 +32,7 @@ defmodule IO.ANSI.Docs do
3132 doc_code: [ :cyan ] ,
3233 doc_headings: [ :yellow ] ,
3334 doc_metadata: [ :yellow ] ,
35+ doc_quote: [ :light_black ] ,
3436 doc_inline_code: [ :cyan ] ,
3537 doc_table_heading: [ :reverse ] ,
3638 doc_title: [ :reverse , :yellow ] ,
@@ -73,7 +75,7 @@ defmodule IO.ANSI.Docs do
7375 { key , value } , _printed when is_binary ( value ) and key in @ metadata_filter ->
7476 label = metadata_label ( key , options )
7577 indent = String . duplicate ( " " , length_without_escape ( label , 0 ) + 1 )
76- write_with_wrap ( [ label | String . split ( value , @ spaces ) ] , options [ :width ] , indent , true )
78+ write_with_wrap ( [ label | String . split ( value , @ spaces ) ] , options [ :width ] , indent , true , "" )
7779
7880 { key , value } , _printed when is_boolean ( value ) and key in @ metadata_filter ->
7981 IO . puts ( [ metadata_label ( key , options ) , ' ' , to_string ( value ) ] )
@@ -139,6 +141,11 @@ defmodule IO.ANSI.Docs do
139141 write_heading ( heading , rest , text , indent , options )
140142 end
141143
144+ defp process ( [ ">" <> line | rest ] , text , indent , options ) do
145+ write_text ( text , indent , options )
146+ process_quote ( rest , [ line ] , indent , options )
147+ end
148+
142149 defp process ( [ "" | rest ] , text , indent , options ) do
143150 write_text ( text , indent , options )
144151 process ( rest , [ ] , indent , options )
@@ -183,6 +190,47 @@ defmodule IO.ANSI.Docs do
183190 process ( rest , [ ] , "" , options )
184191 end
185192
193+ ## Quotes
194+
195+ defp process_quote ( [ ] , lines , indent , options ) do
196+ write_quote ( lines , indent , options , false )
197+ end
198+
199+ defp process_quote ( [ ">" , ">" <> line | rest ] , lines , indent , options ) do
200+ write_quote ( lines , indent , options , true )
201+ write_empty_quote_line ( options )
202+ process_quote ( rest , [ line ] , indent , options )
203+ end
204+
205+ defp process_quote ( [ ">" <> line | rest ] , lines , indent , options ) do
206+ process_quote ( rest , [ line | lines ] , indent , options )
207+ end
208+
209+ defp process_quote ( rest , lines , indent , options ) do
210+ write_quote ( lines , indent , options , false )
211+ process ( rest , [ ] , indent , options )
212+ end
213+
214+ defp write_quote ( lines , indent , options , no_wrap ) do
215+ lines
216+ |> Enum . map ( & String . trim / 1 )
217+ |> Enum . reverse ( )
218+ |> write_lines (
219+ indent ,
220+ options ,
221+ no_wrap ,
222+ quote_prefix ( options )
223+ )
224+ end
225+
226+ defp quote_prefix ( options ) , do: "#{ color ( :doc_quote , options ) } > #{ IO.ANSI . reset ( ) } "
227+
228+ defp write_empty_quote_line ( options ) do
229+ options
230+ |> quote_prefix ( )
231+ |> IO . puts ( )
232+ end
233+
186234 ## Lists
187235
188236 defp process_rest ( stripped , rest , count , text , indent , options ) do
@@ -267,16 +315,25 @@ defmodule IO.ANSI.Docs do
267315 end
268316
269317 defp write_text ( lines , indent , options , no_wrap ) do
318+ write_lines ( lines , indent , options , no_wrap , "" )
319+ end
320+
321+ defp write_lines ( lines , indent , options , no_wrap , prefix ) do
270322 lines
271323 |> Enum . join ( " " )
272- |> handle_links
273- |> handle_inline ( options )
324+ |> format_text ( options )
274325 |> String . split ( @ spaces )
275- |> write_with_wrap ( options [ :width ] - byte_size ( indent ) , indent , no_wrap )
326+ |> write_with_wrap ( options [ :width ] - byte_size ( indent ) , indent , no_wrap , prefix )
276327
277328 unless no_wrap , do: newline_after_block ( )
278329 end
279330
331+ defp format_text ( text , options ) do
332+ text
333+ |> handle_links ( )
334+ |> handle_inline ( options )
335+ end
336+
280337 ## Code blocks
281338
282339 defp process_code ( [ ] , code , indent , options ) do
@@ -470,14 +527,27 @@ defmodule IO.ANSI.Docs do
470527 IO . puts ( [ color ( style , options ) , string , IO.ANSI . reset ( ) ] )
471528 end
472529
473- defp write_with_wrap ( [ ] , _available , _indent , _first ) do
530+ defp write_with_wrap ( [ ] , _available , _indent , _first , _prefix ) do
474531 :ok
475532 end
476533
477- defp write_with_wrap ( words , available , indent , first ) do
478- { words , rest } = take_words ( words , available , [ ] )
479- IO . puts ( if ( first , do: "" , else: indent ) <> Enum . join ( words , " " ) )
480- write_with_wrap ( rest , available , indent , false )
534+ defp write_with_wrap ( words , available , indent , first , prefix ) do
535+ words
536+ |> wrap_text ( available , indent , first , prefix , [ ] )
537+ |> Enum . join ( "\n " )
538+ |> IO . puts ( )
539+ end
540+
541+ defp wrap_text ( [ ] , _available , _indent , _first , _prefix , wrapped_lines ) do
542+ Enum . reverse ( wrapped_lines )
543+ end
544+
545+ defp wrap_text ( words , available , indent , first , prefix , wrapped_lines ) do
546+ prefix_length = length_without_escape ( prefix , 0 )
547+ { words , rest } = take_words ( words , available - prefix_length , [ ] )
548+ line = [ if ( first , do: "" , else: indent ) , prefix , Enum . join ( words , " " ) ]
549+
550+ wrap_text ( rest , available , indent , false , prefix , [ line | wrapped_lines ] )
481551 end
482552
483553 defp take_words ( [ word | words ] , available , acc ) do
0 commit comments