Skip to content

Instantly share code, notes, and snippets.

@gorkaio
Created June 1, 2020 05:37
Show Gist options
  • Save gorkaio/b2739066f6d7e9fbc9aa08e8c3bd8588 to your computer and use it in GitHub Desktop.
Save gorkaio/b2739066f6d7e9fbc9aa08e8c3bd8588 to your computer and use it in GitHub Desktop.
-module(text).
-export([load/1,format/2,format/3,format_file/2,format_file/3]).
-export([split_words_test/0,format_line_test/0,remove_line_test/0,format_test/0,line_test/0,join_test/0]).
-spec load(string()) -> string() | {error, atom()}.
load(FileName) ->
case file:read_file(FileName) of
{ok, Data} -> Data;
_ -> {error, unable_to_read_file}
end.
% Format a file
-spec format_file(string(), integer()) -> [string()].
format_file(FileName, Width) -> format_file(FileName, Width, left).
-spec format_file(string(), integer(), atom()) -> [string()].
format_file(FileName, Width, Type) ->
Data = load(FileName),
format(Data, Width, Type).
% Formats a text distributing it in lines of max width
-spec format(string(), integer()) -> [string()].
format(Ws, Width) -> format(Ws, Width, left).
-spec format(string(), integer(), atom()) -> [string()].
format(Text, Width, Type) ->
Words = split_words(Text),
format_internal(Words, Width, Type).
-spec format_internal([string()], integer(), atom()) -> [string()].
format_internal([],_,_) -> [];
format_internal(Words, Width, Type) ->
Line = format_line(Words, Width),
NewText = remove_line(Words, Line),
[line(Line, Width, Type)|format_internal(NewText, Width, Type)].
format_test() ->
[] = format("", 80),
["text"] = format("text", 80),
["text me"] = format("text me", 80),
["text me,", "babe."] = format("text me, babe.", 8),
["text me,", " babe."] = format("text me, babe.", 8, right),
["a long word like", "pneumonoultramicroscopicsilicovolcanoconiosis", "works"] = format("a long word like pneumonoultramicroscopicsilicovolcanoconiosis works", 16),
passed.
% Build a single line from a list of words
-spec line([string()], integer(), atom()) -> string().
line([], _, _) -> "";
line([""|Ws], Width, Type) -> line(Ws, Width, Type);
line(Ws, _Width, left) ->
join(Ws);
line(Ws, Width, right) ->
Line = join(Ws),
FormattedLine = case length(Line) > Width of
true -> Line;
false -> lists:duplicate(Width - length(Line), 32) ++ Line
end,
re:replace(FormattedLine, "^\\s+$", "").
line_test() ->
"" = line([], 80, left),
"" = line(["", ""], 80, left),
"" = line([" ", ""], 80, left),
"hello" = line(["hello "], 80, left),
"hello, world!" = line(["hello,", "world!"], 80, left),
"" = line([], 80, right),
"" = line(["", ""], 80, right),
" hello" = line(["hello"], 8, right),
" hello, world!" = line(["hello,", "world!"], 15, right),
passed.
% Joins a string
join(Ws) ->
string:trim(lists:flatten(lists:join(" ", Ws))).
join_test() ->
"" = join([]),
"" = join(["", ""]),
"" = join([" ", " "]),
"hello" = join(["hello"]),
"hello world" = join(["hello", "world"]),
passed.
% Composes a string of max width from given list of words
-spec format_line([string()], integer()) -> [string()].
format_line(Ws, Width) -> lists:reverse(format_line(Ws, Width, [])).
-spec format_line([string()], integer(), [string()]) -> [string()].
format_line([], _, Ac) -> Ac;
format_line([W|_], Width, []) when length(W) > Width ->
[W];
format_line([W|_], Width, Ac) when length(W) > Width ->
Ac;
format_line([W|Ws], Width, Ac) ->
format_line(Ws, Width - length(W) - 1, [W|Ac]).
format_line_test() ->
[] = format_line([], 20),
["hello"] = format_line(["hello"], 8),
["hello,"] = format_line(["hello,", "beauty.", "How", "are", "you?"], 4),
["hello,"] = format_line(["hello,", "beauty.", "How", "are", "you?"], 8),
["hello,", "beauty."] = format_line(["hello,", "beauty.", "How", "are", "you?"], 15),
["text","me,"] = format_line(["text", "me,", "babe."], 8),
["pneumonoultramicroscopicsilicovolcanoconiosis"] = format_line(["pneumonoultramicroscopicsilicovolcanoconiosis"], 15),
["hello"] = format_line(["hello", "pneumonoultramicroscopicsilicovolcanoconiosis"], 15),
["pneumonoultramicroscopicsilicovolcanoconiosis"] = format_line(["pneumonoultramicroscopicsilicovolcanoconiosis", "line"], 15),
passed.
% Removes words from first list from second one in order
-spec remove_line([string()], [string()]) -> [string()].
remove_line([], _) -> [];
remove_line(Text, []) -> Text;
remove_line([W|Ws],  [L|Ls]) ->
case W == L of
true -> remove_line(Ws, Ls);
false -> [W|Ws]
end.
remove_line_test() ->
[] = remove_line([], []),
["test", "me"] = remove_line(["test", "me"], []),
[] = remove_line(["test", "me"], ["test", "me"]),
["me"] = remove_line(["test", "me"], ["test"]),
passed.
% Splits a string in words
-spec split_words(string()) -> [string()].
split_words("") -> [];
split_words(S) ->
S2 = re:replace(string:trim(S), "\\s+", " ", [global]),
re:split(S2, "[[:space:]]", [{return,list}]).
split_words_test() ->
[] = split_words(""),
["hello", "again"] = split_words("hello again"),
["hello", "again"] = split_words(" hello\n\nagain "),
passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment