-- major tests for text editing flows -- Arguably this should be called edit_tests.lua, -- but that would mess up the git blame at this point. function test_initial_state() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{} Text.redraw_all(Editor_state) edit.draw(Editor_state) check_eq(#Editor_state.lines, 1, '#lines') check_eq(Editor_state.cursor1.line, 1, 'cursor:line') check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos') check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line') check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos') end function test_click_to_create_drawing() App.screen.init{width=800, height=600} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{} Text.redraw_all(Editor_state) edit.draw(Editor_state) edit.run_after_mouse_click(Editor_state, 8,Editor_state.top+8, 1) -- cursor skips drawing to always remain on text check_eq(#Editor_state.lines, 2, '#lines') check_eq(Editor_state.cursor1.line, 2, 'cursor') end function test_backspace_to_delete_drawing() -- display a drawing followed by a line of text (you shouldn't ever have a drawing right at the end) App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'```lines', '```', ''} Text.redraw_all(Editor_state) -- cursor is on text as always (outside tests this will get initialized correctly) Editor_state.cursor1.line = 2 -- backspacing deletes the drawing edit.run_after_keychord(Editor_state, 'backspace', 'backspace') check_eq(#Editor_state.lines, 1, '#lines') check_eq(Editor_state.cursor1.line, 1, 'cursor') end function test_backspace_from_start_of_final_line() -- display final line of text with cursor at start of it App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc', 'def'} Editor_state.screen_top1 = {line=2, pos=1} Editor_state.cursor1 = {line=2, pos=1} Text.redraw_all(Editor_state) -- backspace scrolls up edit.run_after_keychord(Editor_state, 'backspace', 'backspace') check_eq(#Editor_state.lines, 1, '#lines') check_eq(Editor_state.cursor1.line, 1, 'cursor') check_eq(Editor_state.screen_top1.line, 1, 'screen_top') end function test_insert_first_character() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{} Text.redraw_all(Editor_state) edit.draw(Editor_state) edit.run_after_text_input(Editor_state, 'a') local y = Editor_state.top App.screen.check(y, 'a', 'screen:1') end function test_press_ctrl() -- press ctrl while the cursor is on text App.screen.init{width=50, height=80} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{''} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} edit.run_after_keychord(Editor_state, 'C-m', 'm') end function test_move_left() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'a'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=2} edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'left', 'left') check_eq(Editor_state.cursor1.pos, 1, 'check') end function test_move_right() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'a'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'right', 'right') check_eq(Editor_state.cursor1.pos, 2, 'check') end function test_move_left_to_previous_line() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc', 'def'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'left', 'left') check_eq(Editor_state.cursor1.line, 1, 'line') check_eq(Editor_state.cursor1.pos, 4, 'pos') -- past end of line end function test_move_right_to_next_line() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc', 'def'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=4} -- past end of line edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'right', 'right') check_eq(Editor_state.cursor1.line, 2, 'line') check_eq(Editor_state.cursor1.pos, 1, 'pos') end function test_move_to_start_of_word() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=3} edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-left', 'left') check_eq(Editor_state.cursor1.pos, 1, 'check') end function test_move_to_start_of_previous_word() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc def'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=4} -- at the space between words edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-left', 'left') check_eq(Editor_state.cursor1.pos, 1, 'check') end function test_skip_to_previous_word() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc def'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=5} -- at the start of second word edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-left', 'left') check_eq(Editor_state.cursor1.pos, 1, 'check') end function test_skip_past_tab_to_previous_word() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc def\tghi'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=10} -- within third word edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-left', 'left') check_eq(Editor_state.cursor1.pos, 9, 'check') end function test_skip_multiple_spaces_to_previous_word() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc def'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=6} -- at the start of second word edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-left', 'left') check_eq(Editor_state.cursor1.pos, 1, 'check') end function test_move_to_start_of_word_on_previous_line() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc def', 'ghi'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-left', 'left') check_eq(Editor_state.cursor1.line, 1, 'line') check_eq(Editor_state.cursor1.pos, 5, 'pos') end function test_move_past_end_of_word() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc def'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-right', 'right') check_eq(Editor_state.cursor1.pos, 4, 'check') end function test_skip_to_next_word() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc def'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=4} -- at the space between words edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-right', 'right') check_eq(Editor_state.cursor1.pos, 8, 'check') end function test_skip_past_tab_to_next_word() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc\tdef'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} -- at the space between words edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-right', 'right') check_eq(Editor_state.cursor1.pos, 4, 'check') end function test_skip_multiple_spaces_to_next_word() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc def'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=4} -- at the start of second word edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-right', 'right') check_eq(Editor_state.cursor1.pos, 9, 'check') end function test_move_past_end_of_word_on_next_line() App.screen.init{width=120, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc def', 'ghi'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=8} edit.draw(Editor_state) edit.run_after_keychord(Editor_state, 'M-right', 'right') check_eq(Editor_state.cursor1.line, 2, 'line') check_eq(Editor_state.cursor1.pos, 4, 'pos') end function test_click_moves_cursor() App.screen.init{width=50, height=60} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc', 'def', 'xyz'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} Editor_state.selection1 = {} edit.draw(Editor_state) -- populate line_cache.startpos for each line edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) check_eq(Editor_state.cursor1.line, 1, 'cursor:line') check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos') -- selection is empty to avoid perturbing future edits check_nil(Editor_state.selection1.line, 'selection:line') check_nil(Editor_state.selection1.pos, 'selection:pos') end function test_click_to_left_of_line() -- display a line with the cursor in the middle App.screen.init{width=50, height=80} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=3} Editor_state.screen_top1 = {line=1, pos=1} Editor_state.selection1 = {} -- click to the left of the line edit.draw(Editor_state) edit.run_after_mouse_click(Editor_state, Editor_state.left-4,Editor_state.top+5, 1) -- cursor moves to start of line check_eq(Editor_state.cursor1.line, 1, 'cursor:line') check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos') check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits') end function test_click_takes_margins_into_account() -- display two lines with cursor on one of them App.screen.init{width=100, height=80} Editor_state = edit.initialize_test_state() Editor_state.left = 50 -- occupy only right side of screen Editor_state.lines = load_array{'abc', 'def'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=1, pos=1} Editor_state.selection1 = {} -- click on the other line edit.draw(Editor_state) edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) -- cursor moves check_eq(Editor_state.cursor1.line, 1, 'cursor:line') check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos') check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits') end function test_click_on_empty_line() -- display two lines with the first one empty App.screen.init{width=50, height=80} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'', 'def'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=2, pos=1} Editor_state.screen_top1 = {line=1, pos=1} Editor_state.selection1 = {} -- click on the empty line edit.draw(Editor_state) edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1) -- cursor moves check_eq(Editor_state.cursor1.line, 1, 'cursor') -- selection remains empty check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits') end function test_click_below_all_lines() -- display one line App.screen.init{width=50, height=80} Editor_state = edit.initialize_test_state() Editor_state.lines = load_array{'abc'} Text.redraw_all(Editor_state) Editor_state.cursor1 = {line=1, pos=1} Editor_state.screen_top1 = {line=1, pos=1} Editor_state.selection1 = {} -- click below first line edit.draw(Editor_state) edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+50, 1) -- cursor doesn't move check_eq(Editor_state.cursor1.line, 1, 'cursor') -- selection remains empty check_nil(Editor_state.select
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Mu - 014literal_string.cc</title>
<meta name="Generator" content="Vim/8.0">
<meta name="plugin-version" content="vim7.4_v2">
<meta name="syntax" content="cpp">
<meta name="settings" content="number_lines,use_css,pre_wrap,no_foldcolumn,expand_tabs,line_ids,prevent_copy=">
<meta name="colorscheme" content="minimal">
<style type="text/css">
<!--
pre { white-space: pre-wrap; font-family: monospace; color: #aaaaaa; background-color: #080808; }
body { font-size: 12pt; font-family: monospace; color: #aaaaaa; background-color: #080808; }
a { color:#eeeeee; text-decoration: none; }
a:hover { text-decoration: underline; }
* { font-size: 12pt; font-size: 1em; }
.cSpecial { color: #008000; }
.LineNr { color: #444444; }
.Constant { color: #00a0a0; }
.muRecipe { color: #ff8700; }
.Delimiter { color: #800080; }
.Special { color: #c00000; }
.Identifier { color: #c0a020; }
.Normal { color: #aaaaaa; background-color: #080808; padding-bottom: 1px; }
.Comment { color: #9090ff; }
.Comment a { color:#0000ee; text-decoration:underline; }
.traceContains { color: #008000; }
-->
</style>

<script type='text/javascript'>
<!--

/* function to open any folds containing a jumped-to line before jumping to it */
function JumpToLine()
{
  var lineNum;
  lineNum = window.location.hash;
  lineNum = lineNum.substr(1); /* strip off '#' */

  if (lineNum.indexOf('L') == -1) {
    lineNum = 'L'+lineNum;
  }
  lineElem = document.getElementById(lineNum);
  /* Always jump to new location even if the line was hidden inside a fold, or
   * we corrected the raw number to a line ID.
   */
  if (lineElem) {
    lineElem.scrollIntoView(true);
  }
  return true;
}
if ('onhashchange' in window) {
  window.onhashchange = JumpToLine;
}

-->
</script>
</head>
<body onload='JumpToLine();'>
<pre id='vimCodeElement'>
<span id="L1" class="LineNr">  1 </span><span class="Comment">//: For convenience, some instructions will take literal arrays of characters</span>
<span id="L2" class="LineNr">  2 </span><span class="Comment">//: (text or strings).</span>
<span id="L3" class="LineNr">  3 </span><span class="Comment">//:</span>
<span id="L4" class="LineNr">  4 </span><span class="Comment">//: Instead of quotes, we'll use [] to delimit strings. That'll reduce the</span>
<span id="L5" class="LineNr">  5 </span><span class="Comment">//: need for escaping since we can support nested brackets. And we can also</span>
<span id="L6" class="LineNr">  6 </span><span class="Comment">//: imagine that 'recipe' might one day itself be defined in Mu, doing its own</span>
<span id="L7" class="LineNr">  7 </span><span class="Comment">//: parsing.</span>
<span id="L8" class="LineNr">  8 </span>
<span id="L9" class="LineNr">  9 </span><span class="Delimiter">:(scenarios load)</span>
<span id="L10" class="LineNr"> 10 </span><span class="Delimiter">:(scenario string_literal)</span>
<span id="L11" class="LineNr"> 11 </span><span class="muRecipe">def</span> <a href='000organization.cc.html#L113'>main</a> [
<span id="L12" class="LineNr"> 12 </span>  <span class="Constant">1</span>:<a href='043space.cc.html#L101'>address</a>:array:character<span class="Special"> &lt;- </span>copy [abc def]
<span id="L13" class="LineNr"> 13 </span>]
<span id="L14" class="LineNr"> 14 </span><span class="traceContains">+parse:   ingredient: {&quot;abc def&quot;: &quot;literal-string&quot;}</span>
<span id="L15" class="LineNr"> 15 </span>
<span id="L16" class="LineNr"> 16 </span><span class="Delimiter">:(scenario string_literal_with_colons)</span>
<span id="L17" class="LineNr"> 17 </span><span class="muRecipe">def</span> <a href='000organization.cc.html#L113'>main</a> [
<span id="L18" class="LineNr"> 18 </span>  <span class="Constant">1</span>:<a href='043space.cc.html#L101'>address</a>:array:character<span class="Special"> &lt;- </span>copy [abc:def/ghi]
<span id="L19" class="LineNr"> 19 </span>]
<span id="L20" class="LineNr"> 20 </span><span class="traceContains">+parse:   ingredient: {&quot;abc:def/ghi&quot;: &quot;literal-string&quot;}</span>
<span id="L21" class="LineNr"> 21 </span>
<span id="L22" class="LineNr"> 22 </span><span class="Delimiter">:(before &quot;End Mu Types Initialization&quot;)</span>
<span id="L23" class="LineNr"> 23 </span><a href='001help.cc.html#L221'>put</a><span class="Delimiter">(</span>Type_ordinal<span class="Delimiter">,</span> <span class="Constant">&quot;literal-string&quot;</span><span class="Delimiter">,</span> <span class="Constant">0</span><span class="Delimiter">);</span>
<span id="L24" class="LineNr"> 24 </span>
<span id="L25" class="LineNr"> 25 </span><span class="Delimiter">:(before &quot;End <a href='011load.cc.html#L169'>next_word</a> Special-cases&quot;)</span>
<span id="L26" class="LineNr"> 26 </span><span class="Normal">if</span> <span class="Delimiter">(</span>in<span class="Delimiter">.</span>peek<span class="Delimiter">()</span> == <span class="Constant">'['</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L27" class="LineNr"> 27 </span>  string result = <a href='014literal_string.cc.html#L33'>slurp_quoted</a><span class="Delimiter">(</span>in<span class="Delimiter">);</span>
<span id="L28" class="LineNr"> 28 </span>  <a href='011load.cc.html#L222'>skip_whitespace_and_comments_but_not_newline</a><span class="Delimiter">(</span>in<span class="Delimiter">);</span>
<span id="L29" class="LineNr"> 29 </span>  <span class="Identifier">return</span> result<span class="Delimiter">;</span>
<span id="L30" class="LineNr"> 30 </span><span class="Delimiter">}</span>
<span id="L31" class="LineNr"> 31 </span>
<span id="L32" class="LineNr"> 32 </span><span class="Delimiter">:(code)</span>
<span id="L33" class="LineNr"> 33 </span>string <a href='014literal_string.cc.html#L33'>slurp_quoted</a><span class="Delimiter">(</span>istream&amp; in<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L34" class="LineNr"> 34 </span>  ostringstream out<span class="Delimiter">;</span>
<span id="L35" class="LineNr"> 35 </span>  assert<span class="Delimiter">(</span><a href='001help.cc.html#L239'>has_data</a><span class="Delimiter">(</span>in<span class="Delimiter">));</span>  assert<span class="Delimiter">(</span>in<span class="Delimiter">.</span>peek<span class="Delimiter">()</span> == <span class="Constant">'['</span><span class="Delimiter">);</span>  out &lt;&lt; <span class="Normal">static_cast</span>&lt;<span class="Normal">char</span>&gt;<span class="Delimiter">(</span>in<span class="Delimiter">.</span>get<span class="Delimiter">());</span>  <span class="Comment">// slurp the '['</span>
<span id="L36" class="LineNr"> 36 </span>  <span class="Normal">if</span> <span class="Delimiter">(</span><a href='014literal_string.cc.html#L45'>is_code_string</a><span class="Delimiter">(</span>in<span class="Delimiter">,</span> out<span class="Delimiter">))</span>
<span id="L37" class="LineNr"> 37 </span>    <a href='014literal_string.cc.html#L82'>slurp_quoted_comment_aware</a><span class="Delimiter">(</span>in<span class="Delimiter">,</span> out<span class="Delimiter">);</span>
<span id="L38" class="LineNr"> 38 </span>  <span class="Normal">else</span>
<span id="L39" class="LineNr"> 39 </span>    <a href='014literal_string.cc.html#L62'>slurp_quoted_comment_oblivious</a><span class="Delimiter">(</span>in<span class="Delimiter">,</span> out<span class="Delimiter">);</span>
<span id="L40" class="LineNr"> 40 </span>  <span class="Identifier">return</span> out<span class="Delimiter">.</span>str<span class="Delimiter">();</span>
<span id="L41" class="LineNr"> 41 </span><span class="Delimiter">}</span>
<span id="L42" class="LineNr"> 42 </span>
<span id="L43" class="LineNr"> 43 </span><span class="Comment">// A string is a code string (ignores comments when scanning for matching</span>
<span id="L44" class="LineNr"> 44 </span><span class="Comment">// brackets) if it contains a newline at the start before any non-whitespace.</span>
<span id="L45" class="LineNr"> 45 </span><span class="Normal">bool</span> <a href='014literal_string.cc.html#L45'>is_code_string</a><span class="Delimiter">(</span>istream&amp; in<span class="Delimiter">,</span> ostream&amp; out<span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L46" class="LineNr"> 46 </span>  <span class="Normal">while</span> <span class="Delimiter">(</span><a href='001help.cc.html#L239'>has_data</a><span class="Delimiter">(</span>in<span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span id="L47" class="LineNr"> 47 </span>    <span class="Normal">char</span> c = in<span class="Delimiter">.</span>get<span class="Delimiter">();</span>
<span id="L48" class="LineNr"> 48 </span>    <span class="Normal">if</span> <span class="Delimiter">(</span>!isspace<span class="Delimiter">(</span>c<span class="Delimiter">))</span> <span class="Delimiter">{</span>
<span id="L49" class="LineNr"> 49 </span>      in<span class="Delimiter">.</span>putback<span class="Delimiter">(</span>c<span class="Delimiter">);</span>
<span id="L50" class="LineNr"> 50 </span>      <span class="Identifier">return</span><span class="Constant"> false</span><span class="Delimiter">;</span>
<span id="L51" class="LineNr"> 51 </span>    <span class="Delimiter">}</span>
<span id="L52" class="LineNr"> 52 </span>    out &lt;&lt; c<span class="Delimiter">;</span>
<span id="L53" class="LineNr"> 53 </span>    <span class="Normal">if</span> <span class="Delimiter">(</span>c == <span class="cSpecial">'\n'</span><span class="Delimiter">)</span> <span class="Delimiter">{</span>
<span id="L54" class="LineNr"> 54 </span>      <span class="Identifier">return</span><span class="Constant"> true</span><span class="Delimiter">;</span>
<span id="L55" class="LineNr"> 55 </span>    <span class="Delimiter">}</span>
<span id="L56" class="LineNr"> 56 </span>  <span class="Delimiter">}</span>
<span id="L57" class