Skip to content

Commit bbe63e9

Browse files
author
José Valim
committed
Merge pull request #1975 from thmzlt/ansi_docs
Move IEx.ANSIDocs to IO.ANSI.Docs and decouple it from IEx
2 parents 6f0f0d5 + 9395bb1 commit bbe63e9

File tree

5 files changed

+155
-115
lines changed

5 files changed

+155
-115
lines changed
Lines changed: 132 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,75 +1,110 @@
1-
defmodule IEx.ANSIDocs do
1+
defmodule IO.ANSI.Docs do
22
@moduledoc false
33

44
@bullets [?*, ?-, ?+]
55

6+
@default_colors [ enabled: true,
7+
doc_code: "cyan,bright",
8+
doc_inline_code: "cyan",
9+
doc_headings: "yellow,bright",
10+
doc_title: "reverse,yellow,bright",
11+
doc_bold: "bright",
12+
doc_underline: "underline" ]
13+
14+
@shared_print_doc """
15+
In addition to the priting string, takes a truth value for whether to use ANSI
16+
escape codes, and a keyword list for the printing color settings. Supported
17+
keys for the color settings are:
18+
19+
* `:enabled` - toggles coloring on and off (true)
20+
* `:doc_code` - code blocks (cyan, bright)
21+
* `:doc_inline_code` - inline code (cyan)
22+
* `:doc_headings` - h1 and h2 headings (yellow, bright)
23+
* `:doc_title` - top level heading (reverse, yellow, bright)
24+
* `:doc_bold` - bold text (bright)
25+
* `:doc_underline` - underlined text (underline)
26+
27+
Values for the color settings are strings with comma-separated attributes.
28+
Supported attributes are:
29+
30+
* Colors: `black red green yellow blue magenta cyan white`
31+
* Intensity: `normal bright`
32+
* Decoration: `underline reverse`
33+
"""
34+
635
@doc """
7-
Prints the head of the documentation (i.e. the function signature)
36+
Prints the head of the documentation (i.e. the function signature).
37+
38+
#{@shared_print_doc}
839
"""
9-
def print_heading(string, use_ansi // IO.ANSI.terminal?) do
40+
def print_heading(string, use_ansi // IO.ANSI.terminal?, colors // @default_colors) do
1041
if use_ansi do
11-
write_doc_heading(string)
42+
write_doc_heading(string, colors)
1243
else
1344
IO.puts "* #{string}\n"
1445
end
15-
IEx.dont_display_result
46+
dont_display_result
1647
end
1748

18-
defp write_doc_heading(heading) do
49+
defp write_doc_heading(heading, colors) do
1950
IO.puts IO.ANSI.reset
2051
width = column_width()
2152
padding = div(width + String.length(heading), 2)
2253
heading = heading |> String.rjust(padding) |> String.ljust(width)
23-
write(:doc_title, heading)
54+
write(:doc_title, heading, colors)
2455
end
2556

2657
@doc """
2758
Prints the documentation body.
59+
60+
#{@shared_print_doc}
2861
"""
29-
def print(doc, use_ansi // IO.ANSI.terminal?) do
62+
def print(doc, use_ansi // IO.ANSI.terminal?, colors // @default_colors) do
3063
if use_ansi do
3164
doc
3265
|> String.split(["\r\n","\n"], trim: false)
3366
|> Enum.map(&String.rstrip/1)
34-
|> process("")
67+
|> process("", colors)
3568
else
3669
IO.puts doc
3770
end
38-
IEx.dont_display_result
71+
dont_display_result
3972
end
4073

41-
defp process([], _indent), do: nil
74+
defp dont_display_result, do: :"do not show this result in output"
4275

43-
defp process(["# " <> heading | rest], _indent) do
44-
write_h1(String.strip(heading))
45-
process(rest, "")
76+
defp process([], _indent, _colors), do: nil
77+
78+
defp process(["# " <> heading | rest], _indent, colors) do
79+
write_h1(String.strip(heading), colors)
80+
process(rest, "", colors)
4681
end
4782

48-
defp process(["## " <> heading | rest], _indent) do
49-
write_h2(String.strip(heading))
50-
process(rest, "")
83+
defp process(["## " <> heading | rest], _indent, colors) do
84+
write_h2(String.strip(heading), colors)
85+
process(rest, "", colors)
5186
end
5287

53-
defp process(["### " <> heading | rest], indent) do
54-
write_h3(String.strip(heading), indent)
55-
process(rest, indent)
88+
defp process(["### " <> heading | rest], indent, colors) do
89+
write_h3(String.strip(heading), indent, colors)
90+
process(rest, indent, colors)
5691
end
5792

58-
defp process(["" | rest], indent) do
59-
process(rest, indent)
93+
defp process(["" | rest], indent, colors) do
94+
process(rest, indent, colors)
6095
end
6196

62-
defp process([" " <> line | rest], indent) do
63-
process_code(rest, [line], indent)
97+
defp process([" " <> line | rest], indent, colors) do
98+
process_code(rest, [line], indent, colors)
6499
end
65100

66-
defp process([line | rest], indent) do
101+
defp process([line | rest], indent, colors) do
67102
{ stripped, count } = strip_spaces(line, 0)
68103
case stripped do
69104
<<bullet, ?\s, item :: binary >> when bullet in @bullets ->
70-
process_list(item, rest, count, indent)
105+
process_list(item, rest, count, indent, colors)
71106
_ ->
72-
process_text(rest, [line], indent, false)
107+
process_text(rest, [line], indent, false, colors)
73108
end
74109
end
75110

@@ -83,27 +118,27 @@ defmodule IEx.ANSIDocs do
83118

84119
## Headings
85120

86-
defp write_h1(heading) do
87-
write_h2(String.upcase(heading))
121+
defp write_h1(heading, colors) do
122+
write_h2(String.upcase(heading), colors)
88123
end
89124

90-
defp write_h2(heading) do
91-
write(:doc_headings, heading)
125+
defp write_h2(heading, colors) do
126+
write(:doc_headings, heading, colors)
92127
end
93128

94-
defp write_h3(heading, indent) do
129+
defp write_h3(heading, indent, colors) do
95130
IO.write(indent)
96-
write(:doc_headings, heading)
131+
write(:doc_headings, heading, colors)
97132
end
98133

99134
## Lists
100135

101-
defp process_list(line, rest, count, indent) do
136+
defp process_list(line, rest, count, indent, colors) do
102137
IO.write indent <> "• "
103138
{ contents, rest, done } = process_list_next(rest, count, false, [])
104-
process_text(contents, [line], indent <> " ", true)
139+
process_text(contents, [line], indent <> " ", true, colors)
105140
if done, do: IO.puts(IO.ANSI.reset)
106-
process(rest, indent)
141+
process(rest, indent, colors)
107142
end
108143

109144
# Process the thing after a list item entry. It can be either:
@@ -141,35 +176,35 @@ defmodule IEx.ANSIDocs do
141176

142177
## Text (paragraphs / lists)
143178

144-
defp process_text(doc=["" | _], para, indent, from_list) do
145-
write_text(Enum.reverse(para), indent, from_list)
146-
process(doc, indent)
179+
defp process_text(doc=["" | _], para, indent, from_list, colors) do
180+
write_text(Enum.reverse(para), indent, from_list, colors)
181+
process(doc, indent, colors)
147182
end
148183

149-
defp process_text([], para, indent, from_list) do
150-
write_text(Enum.reverse(para), indent, from_list)
184+
defp process_text([], para, indent, from_list, colors) do
185+
write_text(Enum.reverse(para), indent, from_list, colors)
151186
end
152187

153-
defp process_text([line | rest], para, indent, true) do
188+
defp process_text([line | rest], para, indent, true, colors) do
154189
{ stripped, count } = strip_spaces(line, 0)
155190
case stripped do
156191
<<bullet, ?\s, item :: binary>> when bullet in @bullets ->
157-
write_text(Enum.reverse(para), indent, true)
158-
process_list(item, rest, count, indent)
192+
write_text(Enum.reverse(para), indent, true, colors)
193+
process_list(item, rest, count, indent, colors)
159194
_ ->
160-
process_text(rest, [line | para], indent, true)
195+
process_text(rest, [line | para], indent, true, colors)
161196
end
162197
end
163198

164-
defp process_text([line | rest], para, indent, from_list) do
165-
process_text(rest, [line | para], indent, from_list)
199+
defp process_text([line | rest], para, indent, from_list, colors) do
200+
process_text(rest, [line | para], indent, from_list, colors)
166201
end
167202

168-
defp write_text(lines, indent, from_list) do
203+
defp write_text(lines, indent, from_list, colors) do
169204
lines
170205
|> Enum.join(" ")
171206
|> handle_links
172-
|> handle_inline(nil, [], [])
207+
|> handle_inline(nil, [], [], colors)
173208
|> String.split(%r{\s})
174209
|> write_with_wrap(column_width() - size(indent), indent, from_list)
175210

@@ -178,32 +213,32 @@ defmodule IEx.ANSIDocs do
178213

179214
## Code blocks
180215

181-
defp process_code([], code, indent) do
182-
write_code(code, indent)
216+
defp process_code([], code, indent, colors) do
217+
write_code(code, indent, colors)
183218
end
184219

185220
# Blank line between code blocks
186-
defp process_code([ "", " " <> line | rest ], code, indent) do
187-
process_code(rest, [line, "" | code], indent)
221+
defp process_code([ "", " " <> line | rest ], code, indent, colors) do
222+
process_code(rest, [line, "" | code], indent, colors)
188223
end
189224

190-
defp process_code([ " " <> line | rest ], code, indent) do
191-
process_code(rest, [line|code], indent)
225+
defp process_code([ " " <> line | rest ], code, indent, colors) do
226+
process_code(rest, [line|code], indent, colors)
192227
end
193228

194-
defp process_code(rest, code, indent) do
195-
write_code(code, indent)
196-
process(rest, indent)
229+
defp process_code(rest, code, indent, colors) do
230+
write_code(code, indent, colors)
231+
process(rest, indent, colors)
197232
end
198233

199-
defp write_code(code, indent) do
200-
write(:doc_code, "#{indent}#{Enum.join(Enum.reverse(code), "\n#{indent}┃ ")}")
234+
defp write_code(code, indent, colors) do
235+
write(:doc_code, "#{indent}#{Enum.join(Enum.reverse(code), "\n#{indent}┃ ")}", colors)
201236
end
202237

203238
## Helpers
204239

205-
defp write(style, string) do
206-
IO.puts IEx.color(style, string)
240+
defp write(style, string, colors) do
241+
IO.puts color(style, colors) <> string <> IO.ANSI.reset
207242
IO.puts IO.ANSI.reset
208243
end
209244

@@ -280,81 +315,77 @@ defmodule IEx.ANSIDocs do
280315
@spaced [?_, ?*]
281316

282317
# Clauses for handling spaces
283-
defp handle_inline(<<?*, ?*, ?\s, rest :: binary>>, nil, buffer, acc) do
284-
handle_inline(rest, nil, [?\s, ?*, ?*|buffer], acc)
318+
defp handle_inline(<<?*, ?*, ?\s, rest :: binary>>, nil, buffer, acc, colors) do
319+
handle_inline(rest, nil, [?\s, ?*, ?*|buffer], acc, colors)
285320
end
286321

287-
defp handle_inline(<<mark, ?\s, rest :: binary>>, nil, buffer, acc) when mark in @spaced do
288-
handle_inline(rest, nil, [?\s, mark|buffer], acc)
322+
defp handle_inline(<<mark, ?\s, rest :: binary>>, nil, buffer, acc, colors) when mark in @spaced do
323+
handle_inline(rest, nil, [?\s, mark|buffer], acc, colors)
289324
end
290325

291-
defp handle_inline(<<?\s, ?*, ?*, rest :: binary>>, limit, buffer, acc) do
292-
handle_inline(rest, limit, [?*, ?*, ?\s|buffer], acc)
326+
defp handle_inline(<<?\s, ?*, ?*, rest :: binary>>, limit, buffer, acc, colors) do
327+
handle_inline(rest, limit, [?*, ?*, ?\s|buffer], acc, colors)
293328
end
294329

295-
defp handle_inline(<<?\s, mark, rest :: binary>>, limit, buffer, acc) when mark in @spaced do
296-
handle_inline(rest, limit, [mark, ?\s|buffer], acc)
330+
defp handle_inline(<<?\s, mark, rest :: binary>>, limit, buffer, acc, colors) when mark in @spaced do
331+
handle_inline(rest, limit, [mark, ?\s|buffer], acc, colors)
297332
end
298333

299334
# Clauses for handling escape
300-
defp handle_inline(<<?\\, ?\\, rest :: binary>>, limit, buffer, acc) do
301-
handle_inline(rest, limit, [?\\|buffer], acc)
335+
defp handle_inline(<<?\\, ?\\, rest :: binary>>, limit, buffer, acc, colors) do
336+
handle_inline(rest, limit, [?\\|buffer], acc, colors)
302337
end
303338

304-
defp handle_inline(<<?\\, ?*, ?*, rest :: binary>>, limit, buffer, acc) do
305-
handle_inline(rest, limit, [?*, ?*|buffer], acc)
339+
defp handle_inline(<<?\\, ?*, ?*, rest :: binary>>, limit, buffer, acc, colors) do
340+
handle_inline(rest, limit, [?*, ?*|buffer], acc, colors)
306341
end
307342

308343
# A escape is not valid inside `
309-
defp handle_inline(<<?\\, mark, rest :: binary>>, limit, buffer, acc)
344+
defp handle_inline(<<?\\, mark, rest :: binary>>, limit, buffer, acc, colors)
310345
when mark in [?_, ?*, ?`] and not(mark == limit and mark == ?`) do
311-
handle_inline(rest, limit, [mark|buffer], acc)
346+
handle_inline(rest, limit, [mark|buffer], acc, colors)
312347
end
313348

314349
# Inline start
315-
defp handle_inline(<<?*, ?*, rest :: binary>>, nil, buffer, acc) when rest != "" do
316-
handle_inline(rest, ?d, ["**"], [Enum.reverse(buffer)|acc])
350+
defp handle_inline(<<?*, ?*, rest :: binary>>, nil, buffer, acc, colors) when rest != "" do
351+
handle_inline(rest, ?d, ["**"], [Enum.reverse(buffer)|acc], colors)
317352
end
318353

319-
defp handle_inline(<<mark, rest :: binary>>, nil, buffer, acc) when rest != "" and mark in @single do
320-
handle_inline(rest, mark, [<<mark>>], [Enum.reverse(buffer)|acc])
354+
defp handle_inline(<<mark, rest :: binary>>, nil, buffer, acc, colors) when rest != "" and mark in @single do
355+
handle_inline(rest, mark, [<<mark>>], [Enum.reverse(buffer)|acc], colors)
321356
end
322357

323358
# Inline end
324-
defp handle_inline(<<?*, ?*, rest :: binary>>, ?d, buffer, acc) do
325-
handle_inline(rest, nil, [], [inline_buffer(buffer)|acc])
359+
defp handle_inline(<<?*, ?*, rest :: binary>>, ?d, buffer, acc, colors) do
360+
handle_inline(rest, nil, [], [inline_buffer(buffer, colors)|acc], colors)
326361
end
327362

328-
defp handle_inline(<<mark, rest :: binary>>, mark, buffer, acc) when mark in @single do
329-
handle_inline(rest, nil, [], [inline_buffer(buffer)|acc])
363+
defp handle_inline(<<mark, rest :: binary>>, mark, buffer, acc, colors) when mark in @single do
364+
handle_inline(rest, nil, [], [inline_buffer(buffer, colors)|acc], colors)
330365
end
331366

332-
defp handle_inline(<<char, rest :: binary>>, mark, buffer, acc) do
333-
handle_inline(rest, mark, [char|buffer], acc)
367+
defp handle_inline(<<char, rest :: binary>>, mark, buffer, acc, colors) do
368+
handle_inline(rest, mark, [char|buffer], acc, colors)
334369
end
335370

336-
defp handle_inline(<<>>, _mark, buffer, acc) do
371+
defp handle_inline(<<>>, _mark, buffer, acc, _colors) do
337372
iolist_to_binary Enum.reverse([Enum.reverse(buffer)|acc])
338373
end
339374

340-
defp inline_buffer(buffer) do
375+
defp inline_buffer(buffer, colors) do
341376
[h|t] = Enum.reverse([IO.ANSI.reset|buffer])
342-
[color_for(h)|t]
377+
[color_for(h, colors)|t]
343378
end
344379

345-
defp color_for("`"), do: color(:doc_inline_code)
346-
defp color_for("_"), do: color(:doc_underline)
347-
defp color_for("*"), do: color(:doc_bold)
348-
defp color_for("**"), do: color(:doc_bold)
349-
350-
defp color(color_name) do
351-
colors = IEx.Options.get(:colors)
380+
defp color_for("`", colors), do: color(:doc_inline_code, colors)
381+
defp color_for("_", colors), do: color(:doc_underline, colors)
382+
defp color_for("*", colors), do: color(:doc_bold, colors)
383+
defp color_for("**", colors), do: color(:doc_bold, colors)
352384

353-
if colors[:enabled] do
354-
IO.ANSI.escape_fragment("%{#{colors[color_name]}}", true)
355-
else
356-
""
357-
end
385+
defp color(style, colors) do
386+
color = colors[style]
387+
enabled = colors[:enabled]
388+
IO.ANSI.escape_fragment("%{#{color}}", enabled)
358389
end
359390

360391
defp column_width() do

0 commit comments

Comments
 (0)