[m-rev.] for review: extend documentation of string.split_into_lines/1

Julien Fischer jfischer at opturion.com
Sat Sep 26 16:05:56 AEST 2026


For review by anyone.
----------------------------

Extend documentation of string.split_into_lines/1.

library/string.m:
    As above. In particular, mention that carriage returns do
    not participate in line breaking as far as split_int_lines/1
    is concerned.

tests/hard_coded/string_split_1.{m,exp}:
    Test the examples added to the documentation.

    Test strings containing other characters that are treated
    line breaks in Unicode, or might be mistaken as such.

Julien.

diff --git a/library/string.m b/library/string.m
index 35b54d282..988332ab5 100644
--- a/library/string.m
+++ b/library/string.m
@@ -1094,11 +1094,29 @@
     %
 :- func split_at_string(string, string) = list(string).

-    % split_into_lines(String) breaks String into a sequence of lines,
-    % with each line consisting of a possibly empty sequence of non-newline
-    % characters, followed either by a newline character, or by the end
-    % of the string. The string returned for a line will not contain
-    % the newline character.
+    % split_into_lines(String) breaks String into a list of lines,
+    % returning the text of each line without its terminating newline
+    % character, if it has one. A line is
+    %
+    % - a possibly empty sequence of non-newline characters terminated by
+    %   a newline character, or
+    % - a non-empty sequence of non-newline characters terminated by the
+    %   end of String.
+    %
+    % Only the newline character terminates a line; carriage returns and
+    % the other characters that Unicode treats as line breaks are ordinary
+    % text, and will appear in the returned strings.
+    %
+    % The empty string contains no lines, and a final newline character does
+    % not add an empty line at the end of the list.
+    %
+    % For example,
+    %
+    % split_into_lines("")       = []
+    % split_into_lines("a")      = ["a"]
+    % split_into_lines("a\n")    = ["a"]
+    % split_into_lines("\n")     = [""]
+    % split_into_lines("a\n\nb") = ["a", "", "b"]
     %
 :- func split_into_lines(string) = list(string).

diff --git a/tests/hard_coded/string_split_1.exp
b/tests/hard_coded/string_split_1.exp
index d791e97ec..d485e949f 100644
--- a/tests/hard_coded/string_split_1.exp
+++ b/tests/hard_coded/string_split_1.exp
@@ -16,3 +16,20 @@ col1<tab>col2:val2<tab>col3<tab>
 "line2"
 "line3"
 "line4nonl"
+
+split_into_lines("") = []
+split_into_lines("a") = ["a"]
+split_into_lines("a\n") = ["a"]
+split_into_lines("\n") = [""]
+split_into_lines("a\n\nb") = ["a", "", "b"]
+split_into_lines(" ") = [" "]
+split_into_lines("\r") = ["\r"]
+split_into_lines("\r\n") = ["\r"]
+split_into_lines("a\r") = ["a\r"]
+split_into_lines("a\r\n") = ["a\r"]
+split_into_lines("\a\b\e\f\t\v\n") = ["\a\b\e\f\t\v"]
+split_into_lines("a\034\b") = ["a\034\b"]
+split_into_lines("a\035\b") = ["a\035\b"]
+split_into_lines("a\036\b") = ["a\036\b"]
+split_into_lines("a\205\b") = ["a\205\b"]
+
diff --git a/tests/hard_coded/string_split_1.m
b/tests/hard_coded/string_split_1.m
index bccae5d3d..27fd0bdec 100644
--- a/tests/hard_coded/string_split_1.m
+++ b/tests/hard_coded/string_split_1.m
@@ -54,4 +54,49 @@ main(!IO) :-
     io.nl(!IO),
     list.foldl(io.write_line,
         split_into_lines("line1\nline2\nline3\nline4nonl"), !IO),
+    io.nl(!IO),
+
+    list.foldl(do_test_split_into_lines, split_into_line_tests, !IO),
+    io.nl(!IO),
+
     true.
+
+:- pred do_test_split_into_lines(string::in, io::di, io::uo) is det.
+
+do_test_split_into_lines(Str, !IO) :-
+    io.format("split_into_lines(%s) = ", [s(string(Str))], !IO),
+    Lines = split_into_lines(Str),
+    io.write_line(Lines, !IO).
+
+:- func split_into_line_tests = list(string).
+
+split_into_line_tests = [
+    % Examples from the documentation comment for split_into_lines/1.
+    "",
+    "a",
+    "a\n",
+    "\n",
+    "a\n\nb",
+
+    " ",
+
+    % Examples containing carriage returns.
+    "\r",
+    "\r\n",
+    "a\r",
+    "a\r\n",
+
+    % Mercury backslash escapes
+    "\a\b\e\f\t\v\n",
+
+    % Other code points that are treated as line breaks
+    % in various contexts, but not by split_into_lines/1.
+    "a\u001Cb", % FILE SEPARATOR
+    "a\u001Db", % GROUP SEPARATOR
+    "a\u001Eb", % RECORD SEPARATOR
+    "a\u0085b"  % NEXT LINE
+% XXX we should test these as well but string/1 does not currently
+% escape them.
+%    "a\u2028b", % LINE SEPARATOR
+%    "a\u2029b"  % PARAGRAPH SEPARATOR
+].


More information about the reviews mailing list