text_tests.lua 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374
  1. -- major tests for text editing flows
  2. -- Arguably this should be called edit_tests.lua,
  3. -- but that would mess up the git blame at this point.
  4. function test_initial_state()
  5. App.screen.init{width=120, height=60}
  6. Editor_state = edit.initialize_test_state()
  7. Editor_state.lines = load_array{}
  8. Text.redraw_all(Editor_state)
  9. edit.draw(Editor_state)
  10. check_eq(#Editor_state.lines, 1, '#lines')
  11. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  12. check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
  13. check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
  14. check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
  15. end
  16. function test_move_left()
  17. App.screen.init{width=120, height=60}
  18. Editor_state = edit.initialize_test_state()
  19. Editor_state.lines = load_array{'a'}
  20. Text.redraw_all(Editor_state)
  21. Editor_state.cursor1 = {line=1, pos=2}
  22. edit.draw(Editor_state)
  23. edit.run_after_keychord(Editor_state, 'left', 'left')
  24. check_eq(Editor_state.cursor1.pos, 1, 'check')
  25. end
  26. function test_move_right()
  27. App.screen.init{width=120, height=60}
  28. Editor_state = edit.initialize_test_state()
  29. Editor_state.lines = load_array{'a'}
  30. Text.redraw_all(Editor_state)
  31. Editor_state.cursor1 = {line=1, pos=1}
  32. edit.draw(Editor_state)
  33. edit.run_after_keychord(Editor_state, 'right', 'right')
  34. check_eq(Editor_state.cursor1.pos, 2, 'check')
  35. end
  36. function test_move_left_to_previous_line()
  37. App.screen.init{width=120, height=60}
  38. Editor_state = edit.initialize_test_state()
  39. Editor_state.lines = load_array{'abc', 'def'}
  40. Text.redraw_all(Editor_state)
  41. Editor_state.cursor1 = {line=2, pos=1}
  42. edit.draw(Editor_state)
  43. edit.run_after_keychord(Editor_state, 'left', 'left')
  44. check_eq(Editor_state.cursor1.line, 1, 'line')
  45. check_eq(Editor_state.cursor1.pos, 4, 'pos') -- past end of line
  46. end
  47. function test_move_right_to_next_line()
  48. App.screen.init{width=120, height=60}
  49. Editor_state = edit.initialize_test_state()
  50. Editor_state.lines = load_array{'abc', 'def'}
  51. Text.redraw_all(Editor_state)
  52. Editor_state.cursor1 = {line=1, pos=4} -- past end of line
  53. edit.draw(Editor_state)
  54. edit.run_after_keychord(Editor_state, 'right', 'right')
  55. check_eq(Editor_state.cursor1.line, 2, 'line')
  56. check_eq(Editor_state.cursor1.pos, 1, 'pos')
  57. end
  58. function test_move_to_start_of_word()
  59. App.screen.init{width=120, height=60}
  60. Editor_state = edit.initialize_test_state()
  61. Editor_state.lines = load_array{'abc'}
  62. Text.redraw_all(Editor_state)
  63. Editor_state.cursor1 = {line=1, pos=3}
  64. edit.draw(Editor_state)
  65. edit.run_after_keychord(Editor_state, 'M-left', 'left')
  66. check_eq(Editor_state.cursor1.pos, 1, 'check')
  67. end
  68. function test_move_to_start_of_previous_word()
  69. App.screen.init{width=120, height=60}
  70. Editor_state = edit.initialize_test_state()
  71. Editor_state.lines = load_array{'abc def'}
  72. Text.redraw_all(Editor_state)
  73. Editor_state.cursor1 = {line=1, pos=4} -- at the space between words
  74. edit.draw(Editor_state)
  75. edit.run_after_keychord(Editor_state, 'M-left', 'left')
  76. check_eq(Editor_state.cursor1.pos, 1, 'check')
  77. end
  78. function test_skip_to_previous_word()
  79. App.screen.init{width=120, height=60}
  80. Editor_state = edit.initialize_test_state()
  81. Editor_state.lines = load_array{'abc def'}
  82. Text.redraw_all(Editor_state)
  83. Editor_state.cursor1 = {line=1, pos=5} -- at the start of second word
  84. edit.draw(Editor_state)
  85. edit.run_after_keychord(Editor_state, 'M-left', 'left')
  86. check_eq(Editor_state.cursor1.pos, 1, 'check')
  87. end
  88. function test_skip_past_tab_to_previous_word()
  89. App.screen.init{width=120, height=60}
  90. Editor_state = edit.initialize_test_state()
  91. Editor_state.lines = load_array{'abc def\tghi'}
  92. Text.redraw_all(Editor_state)
  93. Editor_state.cursor1 = {line=1, pos=10} -- within third word
  94. edit.draw(Editor_state)
  95. edit.run_after_keychord(Editor_state, 'M-left', 'left')
  96. check_eq(Editor_state.cursor1.pos, 9, 'check')
  97. end
  98. function test_skip_multiple_spaces_to_previous_word()
  99. App.screen.init{width=120, height=60}
  100. Editor_state = edit.initialize_test_state()
  101. Editor_state.lines = load_array{'abc def'}
  102. Text.redraw_all(Editor_state)
  103. Editor_state.cursor1 = {line=1, pos=6} -- at the start of second word
  104. edit.draw(Editor_state)
  105. edit.run_after_keychord(Editor_state, 'M-left', 'left')
  106. check_eq(Editor_state.cursor1.pos, 1, 'check')
  107. end
  108. function test_move_to_start_of_word_on_previous_line()
  109. App.screen.init{width=120, height=60}
  110. Editor_state = edit.initialize_test_state()
  111. Editor_state.lines = load_array{'abc def', 'ghi'}
  112. Text.redraw_all(Editor_state)
  113. Editor_state.cursor1 = {line=2, pos=1}
  114. edit.draw(Editor_state)
  115. edit.run_after_keychord(Editor_state, 'M-left', 'left')
  116. check_eq(Editor_state.cursor1.line, 1, 'line')
  117. check_eq(Editor_state.cursor1.pos, 5, 'pos')
  118. end
  119. function test_move_past_end_of_word()
  120. App.screen.init{width=120, height=60}
  121. Editor_state = edit.initialize_test_state()
  122. Editor_state.lines = load_array{'abc def'}
  123. Text.redraw_all(Editor_state)
  124. Editor_state.cursor1 = {line=1, pos=1}
  125. edit.draw(Editor_state)
  126. edit.run_after_keychord(Editor_state, 'M-right', 'right')
  127. check_eq(Editor_state.cursor1.pos, 4, 'check')
  128. end
  129. function test_skip_to_next_word()
  130. App.screen.init{width=120, height=60}
  131. Editor_state = edit.initialize_test_state()
  132. Editor_state.lines = load_array{'abc def'}
  133. Text.redraw_all(Editor_state)
  134. Editor_state.cursor1 = {line=1, pos=4} -- at the space between words
  135. edit.draw(Editor_state)
  136. edit.run_after_keychord(Editor_state, 'M-right', 'right')
  137. check_eq(Editor_state.cursor1.pos, 8, 'check')
  138. end
  139. function test_skip_past_tab_to_next_word()
  140. App.screen.init{width=120, height=60}
  141. Editor_state = edit.initialize_test_state()
  142. Editor_state.lines = load_array{'abc\tdef'}
  143. Text.redraw_all(Editor_state)
  144. Editor_state.cursor1 = {line=1, pos=1} -- at the space between words
  145. edit.draw(Editor_state)
  146. edit.run_after_keychord(Editor_state, 'M-right', 'right')
  147. check_eq(Editor_state.cursor1.pos, 4, 'check')
  148. end
  149. function test_skip_multiple_spaces_to_next_word()
  150. App.screen.init{width=120, height=60}
  151. Editor_state = edit.initialize_test_state()
  152. Editor_state.lines = load_array{'abc def'}
  153. Text.redraw_all(Editor_state)
  154. Editor_state.cursor1 = {line=1, pos=4} -- at the start of second word
  155. edit.draw(Editor_state)
  156. edit.run_after_keychord(Editor_state, 'M-right', 'right')
  157. check_eq(Editor_state.cursor1.pos, 9, 'check')
  158. end
  159. function test_move_past_end_of_word_on_next_line()
  160. App.screen.init{width=120, height=60}
  161. Editor_state = edit.initialize_test_state()
  162. Editor_state.lines = load_array{'abc def', 'ghi'}
  163. Text.redraw_all(Editor_state)
  164. Editor_state.cursor1 = {line=1, pos=8}
  165. edit.draw(Editor_state)
  166. edit.run_after_keychord(Editor_state, 'M-right', 'right')
  167. check_eq(Editor_state.cursor1.line, 2, 'line')
  168. check_eq(Editor_state.cursor1.pos, 4, 'pos')
  169. end
  170. function test_click_moves_cursor()
  171. App.screen.init{width=50, height=60}
  172. Editor_state = edit.initialize_test_state()
  173. Editor_state.lines = load_array{'abc', 'def', 'xyz'}
  174. Text.redraw_all(Editor_state)
  175. Editor_state.cursor1 = {line=1, pos=1}
  176. Editor_state.screen_top1 = {line=1, pos=1}
  177. Editor_state.selection1 = {}
  178. edit.draw(Editor_state) -- populate line_cache.startpos for each line
  179. edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  180. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  181. check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
  182. -- selection is empty to avoid perturbing future edits
  183. check_nil(Editor_state.selection1.line, 'selection:line')
  184. check_nil(Editor_state.selection1.pos, 'selection:pos')
  185. end
  186. function test_click_to_left_of_line()
  187. -- display a line with the cursor in the middle
  188. App.screen.init{width=50, height=80}
  189. Editor_state = edit.initialize_test_state()
  190. Editor_state.lines = load_array{'abc'}
  191. Text.redraw_all(Editor_state)
  192. Editor_state.cursor1 = {line=1, pos=3}
  193. Editor_state.screen_top1 = {line=1, pos=1}
  194. Editor_state.selection1 = {}
  195. -- click to the left of the line
  196. edit.draw(Editor_state)
  197. edit.run_after_mouse_click(Editor_state, Editor_state.left-4,Editor_state.top+5, 1)
  198. -- cursor moves to start of line
  199. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  200. check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
  201. check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
  202. end
  203. function test_click_takes_margins_into_account()
  204. -- display two lines with cursor on one of them
  205. App.screen.init{width=100, height=80}
  206. Editor_state = edit.initialize_test_state()
  207. Editor_state.left = 50 -- occupy only right side of screen
  208. Editor_state.lines = load_array{'abc', 'def'}
  209. Text.redraw_all(Editor_state)
  210. Editor_state.cursor1 = {line=2, pos=1}
  211. Editor_state.screen_top1 = {line=1, pos=1}
  212. Editor_state.selection1 = {}
  213. -- click on the other line
  214. edit.draw(Editor_state)
  215. edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  216. -- cursor moves
  217. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  218. check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
  219. check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
  220. end
  221. function test_click_on_empty_line()
  222. -- display two lines with the first one empty
  223. App.screen.init{width=50, height=80}
  224. Editor_state = edit.initialize_test_state()
  225. Editor_state.lines = load_array{'', 'def'}
  226. Text.redraw_all(Editor_state)
  227. Editor_state.cursor1 = {line=2, pos=1}
  228. Editor_state.screen_top1 = {line=1, pos=1}
  229. Editor_state.selection1 = {}
  230. -- click on the empty line
  231. edit.draw(Editor_state)
  232. edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  233. -- cursor moves
  234. check_eq(Editor_state.cursor1.line, 1, 'cursor')
  235. -- selection remains empty
  236. check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
  237. end
  238. function test_click_below_final_line_of_file()
  239. -- display one line
  240. App.screen.init{width=50, height=80}
  241. Editor_state = edit.initialize_test_state()
  242. Editor_state.lines = load_array{'abc'}
  243. Text.redraw_all(Editor_state)
  244. Editor_state.cursor1 = {line=1, pos=1}
  245. Editor_state.screen_top1 = {line=1, pos=1}
  246. Editor_state.selection1 = {}
  247. -- click below first line
  248. edit.draw(Editor_state)
  249. edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+50, 1)
  250. -- cursor goes to bottom
  251. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  252. check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
  253. -- selection remains empty
  254. check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
  255. end
  256. function test_draw_text()
  257. App.screen.init{width=120, height=60}
  258. Editor_state = edit.initialize_test_state()
  259. Editor_state.lines = load_array{'abc', 'def', 'ghi'}
  260. Text.redraw_all(Editor_state)
  261. Editor_state.cursor1 = {line=1, pos=1}
  262. Editor_state.screen_top1 = {line=1, pos=1}
  263. edit.draw(Editor_state)
  264. local y = Editor_state.top
  265. App.screen.check(y, 'abc', 'screen:1')
  266. y = y + Editor_state.line_height
  267. App.screen.check(y, 'def', 'screen:2')
  268. y = y + Editor_state.line_height
  269. App.screen.check(y, 'ghi', 'screen:3')
  270. end
  271. function test_draw_wrapping_text()
  272. App.screen.init{width=50, height=60}
  273. Editor_state = edit.initialize_test_state()
  274. Editor_state.lines = load_array{'abc', 'defgh', 'xyz'}
  275. Text.redraw_all(Editor_state)
  276. Editor_state.cursor1 = {line=1, pos=1}
  277. Editor_state.screen_top1 = {line=1, pos=1}
  278. edit.draw(Editor_state)
  279. local y = Editor_state.top
  280. App.screen.check(y, 'abc', 'screen:1')
  281. y = y + Editor_state.line_height
  282. App.screen.check(y, 'de', 'screen:2')
  283. y = y + Editor_state.line_height
  284. App.screen.check(y, 'fgh', 'screen:3')
  285. end
  286. function test_draw_word_wrapping_text()
  287. App.screen.init{width=60, height=60}
  288. Editor_state = edit.initialize_test_state()
  289. Editor_state.lines = load_array{'abc def ghi', 'jkl'}
  290. Text.redraw_all(Editor_state)
  291. Editor_state.cursor1 = {line=1, pos=1}
  292. Editor_state.screen_top1 = {line=1, pos=1}
  293. edit.draw(Editor_state)
  294. local y = Editor_state.top
  295. App.screen.check(y, 'abc ', 'screen:1')
  296. y = y + Editor_state.line_height
  297. App.screen.check(y, 'def ', 'screen:2')
  298. y = y + Editor_state.line_height
  299. App.screen.check(y, 'ghi', 'screen:3')
  300. end
  301. function test_click_on_wrapping_line()
  302. -- display two screen lines with cursor on one of them
  303. App.screen.init{width=50, height=80}
  304. Editor_state = edit.initialize_test_state()
  305. Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu'}
  306. Text.redraw_all(Editor_state)
  307. Editor_state.cursor1 = {line=1, pos=20}
  308. Editor_state.screen_top1 = {line=1, pos=1}
  309. -- click on the other line
  310. edit.draw(Editor_state)
  311. edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  312. -- cursor moves
  313. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  314. check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
  315. check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
  316. end
  317. function test_click_on_wrapping_line_takes_margins_into_account()
  318. -- display two screen lines with cursor on one of them
  319. App.screen.init{width=100, height=80}
  320. Editor_state = edit.initialize_test_state()
  321. Editor_state.left = 50 -- occupy only right side of screen
  322. Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu'}
  323. Text.redraw_all(Editor_state)
  324. Editor_state.cursor1 = {line=1, pos=20}
  325. Editor_state.screen_top1 = {line=1, pos=1}
  326. -- click on the other line
  327. edit.draw(Editor_state)
  328. edit.run_after_mouse_click(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  329. -- cursor moves
  330. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  331. check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
  332. check_nil(Editor_state.selection1.line, 'selection is empty to avoid perturbing future edits')
  333. end
  334. function test_draw_text_wrapping_within_word()
  335. -- arrange a screen line that needs to be split within a word
  336. App.screen.init{width=60, height=60}
  337. Editor_state = edit.initialize_test_state()
  338. Editor_state.lines = load_array{'abcd e fghijk', 'xyz'}
  339. Text.redraw_all(Editor_state)
  340. Editor_state.cursor1 = {line=1, pos=1}
  341. Editor_state.screen_top1 = {line=1, pos=1}
  342. edit.draw(Editor_state)
  343. local y = Editor_state.top
  344. App.screen.check(y, 'abcd ', 'screen:1')
  345. y = y + Editor_state.line_height
  346. App.screen.check(y, 'e fgh', 'screen:2')
  347. y = y + Editor_state.line_height
  348. App.screen.check(y, 'ijk', 'screen:3')
  349. end
  350. function test_draw_wrapping_text_containing_non_ascii()
  351. -- draw a long line containing non-ASCII
  352. App.screen.init{width=60, height=60}
  353. Editor_state = edit.initialize_test_state()
  354. Editor_state.lines = load_array{'madam I’m adam', 'xyz'} -- notice the non-ASCII apostrophe
  355. Text.redraw_all(Editor_state)
  356. Editor_state.cursor1 = {line=1, pos=1}
  357. Editor_state.screen_top1 = {line=1, pos=1}
  358. edit.draw(Editor_state)
  359. local y = Editor_state.top
  360. App.screen.check(y, 'mad', 'screen:1')
  361. y = y + Editor_state.line_height
  362. App.screen.check(y, 'am I', 'screen:2')
  363. y = y + Editor_state.line_height
  364. App.screen.check(y, '’m a', 'screen:3')
  365. end
  366. function test_click_past_end_of_screen_line()
  367. -- display a wrapping line
  368. App.screen.init{width=75, height=80}
  369. Editor_state = edit.initialize_test_state()
  370. -- 12345678901234
  371. Editor_state.lines = load_array{"madam I'm adam"}
  372. Text.redraw_all(Editor_state)
  373. Editor_state.cursor1 = {line=1, pos=1}
  374. Editor_state.screen_top1 = {line=1, pos=1}
  375. edit.draw(Editor_state)
  376. local y = Editor_state.top
  377. App.screen.check(y, 'madam ', 'baseline/screen:1')
  378. y = y + Editor_state.line_height
  379. App.screen.check(y, "I'm ad", 'baseline/screen:2')
  380. y = y + Editor_state.line_height
  381. -- click past end of second screen line
  382. edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
  383. -- cursor moves to end of screen line (one more than final character shown)
  384. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  385. check_eq(Editor_state.cursor1.pos, 13, 'cursor:pos')
  386. end
  387. function test_click_on_wrapping_line_rendered_from_partway_at_top_of_screen()
  388. -- display a wrapping line from its second screen line
  389. App.screen.init{width=75, height=80}
  390. Editor_state = edit.initialize_test_state()
  391. -- 12345678901234
  392. Editor_state.lines = load_array{"madam I'm adam"}
  393. Text.redraw_all(Editor_state)
  394. Editor_state.cursor1 = {line=1, pos=8}
  395. Editor_state.screen_top1 = {line=1, pos=7}
  396. edit.draw(Editor_state)
  397. local y = Editor_state.top
  398. App.screen.check(y, "I'm ad", 'baseline/screen:2')
  399. y = y + Editor_state.line_height
  400. -- click past end of second screen line
  401. edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
  402. -- cursor moves to end of screen line (one more than final character shown)
  403. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  404. check_eq(Editor_state.cursor1.pos, 13, 'cursor:pos')
  405. end
  406. function test_click_past_end_of_wrapping_line()
  407. -- display a wrapping line
  408. App.screen.init{width=75, height=80}
  409. Editor_state = edit.initialize_test_state()
  410. -- 12345678901234
  411. Editor_state.lines = load_array{"madam I'm adam"}
  412. Text.redraw_all(Editor_state)
  413. Editor_state.cursor1 = {line=1, pos=1}
  414. Editor_state.screen_top1 = {line=1, pos=1}
  415. edit.draw(Editor_state)
  416. local y = Editor_state.top
  417. App.screen.check(y, 'madam ', 'baseline/screen:1')
  418. y = y + Editor_state.line_height
  419. App.screen.check(y, "I'm ad", 'baseline/screen:2')
  420. y = y + Editor_state.line_height
  421. App.screen.check(y, 'am', 'baseline/screen:3')
  422. y = y + Editor_state.line_height
  423. -- click past the end of it
  424. edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
  425. -- cursor moves to end of line
  426. check_eq(Editor_state.cursor1.pos, 15, 'cursor') -- one more than the number of UTF-8 code-points
  427. end
  428. function test_click_past_end_of_wrapping_line_containing_non_ascii()
  429. -- display a wrapping line containing non-ASCII
  430. App.screen.init{width=75, height=80}
  431. Editor_state = edit.initialize_test_state()
  432. -- 12345678901234
  433. Editor_state.lines = load_array{'madam I’m adam'} -- notice the non-ASCII apostrophe
  434. Text.redraw_all(Editor_state)
  435. Editor_state.cursor1 = {line=1, pos=1}
  436. Editor_state.screen_top1 = {line=1, pos=1}
  437. edit.draw(Editor_state)
  438. local y = Editor_state.top
  439. App.screen.check(y, 'madam ', 'baseline/screen:1')
  440. y = y + Editor_state.line_height
  441. App.screen.check(y, 'I’m ad', 'baseline/screen:2')
  442. y = y + Editor_state.line_height
  443. App.screen.check(y, 'am', 'baseline/screen:3')
  444. y = y + Editor_state.line_height
  445. -- click past the end of it
  446. edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
  447. -- cursor moves to end of line
  448. check_eq(Editor_state.cursor1.pos, 15, 'cursor') -- one more than the number of UTF-8 code-points
  449. end
  450. function test_click_past_end_of_word_wrapping_line()
  451. -- display a long line wrapping at a word boundary on a screen of more realistic length
  452. App.screen.init{width=160, height=80}
  453. Editor_state = edit.initialize_test_state()
  454. -- 0 1 2
  455. -- 123456789012345678901
  456. Editor_state.lines = load_array{'the quick brown fox jumped over the lazy dog'}
  457. Text.redraw_all(Editor_state)
  458. Editor_state.cursor1 = {line=1, pos=1}
  459. Editor_state.screen_top1 = {line=1, pos=1}
  460. edit.draw(Editor_state)
  461. local y = Editor_state.top
  462. App.screen.check(y, 'the quick brown fox ', 'baseline/screen:1')
  463. y = y + Editor_state.line_height
  464. -- click past the end of the screen line
  465. edit.run_after_mouse_click(Editor_state, App.screen.width-2,y-2, 1)
  466. -- cursor moves to end of screen line (one more than final character shown)
  467. check_eq(Editor_state.cursor1.pos, 21, 'cursor')
  468. end
  469. function test_select_text()
  470. -- display a line of text
  471. App.screen.init{width=75, height=80}
  472. Editor_state = edit.initialize_test_state()
  473. Editor_state.lines = load_array{'abc def'}
  474. Text.redraw_all(Editor_state)
  475. Editor_state.cursor1 = {line=1, pos=1}
  476. Editor_state.screen_top1 = {line=1, pos=1}
  477. edit.draw(Editor_state)
  478. -- select a letter
  479. App.fake_key_press('lshift')
  480. edit.run_after_keychord(Editor_state, 'S-right', 'right')
  481. App.fake_key_release('lshift')
  482. edit.key_release(Editor_state, 'lshift')
  483. -- selection persists even after shift is released
  484. check_eq(Editor_state.selection1.line, 1, 'selection:line')
  485. check_eq(Editor_state.selection1.pos, 1, 'selection:pos')
  486. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  487. check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
  488. end
  489. function test_cursor_movement_without_shift_resets_selection()
  490. -- display a line of text with some part selected
  491. App.screen.init{width=75, height=80}
  492. Editor_state = edit.initialize_test_state()
  493. Editor_state.lines = load_array{'abc'}
  494. Text.redraw_all(Editor_state)
  495. Editor_state.cursor1 = {line=1, pos=1}
  496. Editor_state.selection1 = {line=1, pos=2}
  497. Editor_state.screen_top1 = {line=1, pos=1}
  498. edit.draw(Editor_state)
  499. -- press an arrow key without shift
  500. edit.run_after_keychord(Editor_state, 'right', 'right')
  501. -- no change to data, selection is reset
  502. check_nil(Editor_state.selection1.line, 'check')
  503. check_eq(Editor_state.lines[1].data, 'abc', 'data')
  504. end
  505. function test_copy_does_not_reset_selection()
  506. -- display a line of text with a selection
  507. App.screen.init{width=75, height=80}
  508. Editor_state = edit.initialize_test_state()
  509. Editor_state.lines = load_array{'abc'}
  510. Text.redraw_all(Editor_state)
  511. Editor_state.cursor1 = {line=1, pos=1}
  512. Editor_state.selection1 = {line=1, pos=2}
  513. Editor_state.screen_top1 = {line=1, pos=1}
  514. edit.draw(Editor_state)
  515. -- copy selection
  516. edit.run_after_keychord(Editor_state, 'C-c', 'c')
  517. check_eq(App.clipboard, 'a', 'clipboard')
  518. -- selection is reset since shift key is not pressed
  519. check(Editor_state.selection1.line, 'check')
  520. end
  521. function test_move_cursor_using_mouse()
  522. App.screen.init{width=50, height=60}
  523. Editor_state = edit.initialize_test_state()
  524. Editor_state.lines = load_array{'abc', 'def', 'xyz'}
  525. Text.redraw_all(Editor_state)
  526. Editor_state.cursor1 = {line=1, pos=1}
  527. Editor_state.screen_top1 = {line=1, pos=1}
  528. Editor_state.selection1 = {}
  529. edit.draw(Editor_state) -- populate line_cache.startpos for each line
  530. edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  531. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  532. check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
  533. check_nil(Editor_state.selection1.line, 'selection:line')
  534. check_nil(Editor_state.selection1.pos, 'selection:pos')
  535. end
  536. function test_select_text_using_mouse()
  537. App.screen.init{width=50, height=60}
  538. Editor_state = edit.initialize_test_state()
  539. Editor_state.lines = load_array{'abc', 'def', 'xyz'}
  540. Text.redraw_all(Editor_state)
  541. Editor_state.cursor1 = {line=1, pos=1}
  542. Editor_state.screen_top1 = {line=1, pos=1}
  543. Editor_state.selection1 = {}
  544. edit.draw(Editor_state) -- populate line_cache.startpos for each line
  545. -- press and hold on first location
  546. edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  547. -- drag and release somewhere else
  548. edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
  549. check_eq(Editor_state.selection1.line, 1, 'selection:line')
  550. check_eq(Editor_state.selection1.pos, 2, 'selection:pos')
  551. check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
  552. check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
  553. end
  554. function test_select_text_using_mouse_starting_above_text()
  555. App.screen.init{width=50, height=60}
  556. Editor_state = edit.initialize_test_state()
  557. Editor_state.lines = load_array{'abc', 'def', 'xyz'}
  558. Text.redraw_all(Editor_state)
  559. Editor_state.cursor1 = {line=1, pos=1}
  560. Editor_state.screen_top1 = {line=1, pos=1}
  561. Editor_state.selection1 = {}
  562. edit.draw(Editor_state) -- populate line_cache.startpos for each line
  563. -- press mouse above first line of text
  564. edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)
  565. check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')
  566. check_eq(Editor_state.selection1.line, 1, 'selection:line')
  567. check_eq(Editor_state.selection1.pos, 1, 'selection:pos')
  568. end
  569. function test_select_text_using_mouse_starting_above_text_wrapping_line()
  570. -- first screen line starts in the middle of a line
  571. App.screen.init{width=50, height=60}
  572. Editor_state = edit.initialize_test_state()
  573. Editor_state.lines = load_array{'abc', 'defgh', 'xyz'}
  574. Text.redraw_all(Editor_state)
  575. Editor_state.cursor1 = {line=2, pos=5}
  576. Editor_state.screen_top1 = {line=2, pos=3}
  577. -- press mouse above first line of text
  578. edit.draw(Editor_state)
  579. edit.run_after_mouse_press(Editor_state, Editor_state.left+8,5, 1)
  580. -- selection is at screen top
  581. check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')
  582. check_eq(Editor_state.selection1.line, 2, 'selection:line')
  583. check_eq(Editor_state.selection1.pos, 3, 'selection:pos')
  584. end
  585. function test_select_text_using_mouse_starting_below_text()
  586. -- I'd like to test what happens when a mouse click is below some page of
  587. -- text, potentially even in the middle of a line.
  588. -- However, it's brittle to set up a text line boundary just right.
  589. -- So I'm going to just check things below the bottom of the final line of
  590. -- text when it's in the middle of the screen.
  591. -- final screen line ends in the middle of screen
  592. App.screen.init{width=50, height=60}
  593. Editor_state = edit.initialize_test_state()
  594. Editor_state.lines = load_array{'abcde'}
  595. Text.redraw_all(Editor_state)
  596. Editor_state.cursor1 = {line=1, pos=1}
  597. Editor_state.screen_top1 = {line=1, pos=1}
  598. edit.draw(Editor_state)
  599. local y = Editor_state.top
  600. App.screen.check(y, 'ab', 'baseline:screen:1')
  601. y = y + Editor_state.line_height
  602. App.screen.check(y, 'cde', 'baseline:screen:2')
  603. -- press mouse above first line of text
  604. edit.run_after_mouse_press(Editor_state, 5,App.screen.height-5, 1)
  605. -- selection is past bottom-most text in screen
  606. check(Editor_state.selection1.line ~= nil, 'selection:line-not-nil')
  607. check_eq(Editor_state.selection1.line, 1, 'selection:line')
  608. check_eq(Editor_state.selection1.pos, 6, 'selection:pos')
  609. end
  610. function test_select_text_using_mouse_and_shift()
  611. App.screen.init{width=50, height=60}
  612. Editor_state = edit.initialize_test_state()
  613. Editor_state.lines = load_array{'abc', 'def', 'xyz'}
  614. Text.redraw_all(Editor_state)
  615. Editor_state.cursor1 = {line=1, pos=1}
  616. Editor_state.screen_top1 = {line=1, pos=1}
  617. Editor_state.selection1 = {}
  618. edit.draw(Editor_state) -- populate line_cache.startpos for each line
  619. -- click on first location
  620. edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  621. edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  622. -- hold down shift and click somewhere else
  623. App.fake_key_press('lshift')
  624. edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
  625. edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
  626. App.fake_key_release('lshift')
  627. check_eq(Editor_state.selection1.line, 1, 'selection:line')
  628. check_eq(Editor_state.selection1.pos, 2, 'selection:pos')
  629. check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
  630. check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
  631. end
  632. function test_select_text_repeatedly_using_mouse_and_shift()
  633. App.screen.init{width=50, height=60}
  634. Editor_state = edit.initialize_test_state()
  635. Editor_state.lines = load_array{'abc', 'def', 'xyz'}
  636. Text.redraw_all(Editor_state)
  637. Text.redraw_all(Editor_state)
  638. Editor_state.cursor1 = {line=1, pos=1}
  639. Editor_state.screen_top1 = {line=1, pos=1}
  640. Editor_state.selection1 = {}
  641. edit.draw(Editor_state) -- populate line_cache.startpos for each line
  642. -- click on first location
  643. edit.run_after_mouse_press(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  644. edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+5, 1)
  645. -- hold down shift and click on a second location
  646. App.fake_key_press('lshift')
  647. edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
  648. edit.run_after_mouse_release(Editor_state, Editor_state.left+20,Editor_state.top+Editor_state.line_height+5, 1)
  649. -- hold down shift and click at a third location
  650. App.fake_key_press('lshift')
  651. edit.run_after_mouse_press(Editor_state, Editor_state.left+20,Editor_state.top+5, 1)
  652. edit.run_after_mouse_release(Editor_state, Editor_state.left+8,Editor_state.top+Editor_state.line_height+5, 1)
  653. App.fake_key_release('lshift')
  654. -- selection is between first and third location. forget the second location, not the first.
  655. check_eq(Editor_state.selection1.line, 1, 'selection:line')
  656. check_eq(Editor_state.selection1.pos, 2, 'selection:pos')
  657. check_eq(Editor_state.cursor1.line, 2, 'cursor:line')
  658. check_eq(Editor_state.cursor1.pos, 2, 'cursor:pos')
  659. end
  660. function test_select_all_text()
  661. -- display a single line of text
  662. App.screen.init{width=75, height=80}
  663. Editor_state = edit.initialize_test_state()
  664. Editor_state.lines = load_array{'abc def'}
  665. Text.redraw_all(Editor_state)
  666. Editor_state.cursor1 = {line=1, pos=1}
  667. Editor_state.screen_top1 = {line=1, pos=1}
  668. edit.draw(Editor_state)
  669. -- select all
  670. App.fake_key_press('lctrl')
  671. edit.run_after_keychord(Editor_state, 'C-a', 'a')
  672. App.fake_key_release('lctrl')
  673. edit.key_release(Editor_state, 'lctrl')
  674. -- selection
  675. check_eq(Editor_state.selection1.line, 1, 'selection:line')
  676. check_eq(Editor_state.selection1.pos, 1, 'selection:pos')
  677. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  678. check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')
  679. end
  680. function test_pagedown()
  681. App.screen.init{width=120, height=45}
  682. Editor_state = edit.initialize_test_state()
  683. Editor_state.lines = load_array{'abc', 'def', 'ghi'}
  684. Text.redraw_all(Editor_state)
  685. Editor_state.cursor1 = {line=1, pos=1}
  686. Editor_state.screen_top1 = {line=1, pos=1}
  687. -- initially the first two lines are displayed
  688. edit.draw(Editor_state)
  689. local y = Editor_state.top
  690. App.screen.check(y, 'abc', 'baseline/screen:1')
  691. y = y + Editor_state.line_height
  692. App.screen.check(y, 'def', 'baseline/screen:2')
  693. -- after pagedown the bottom line becomes the top
  694. edit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')
  695. check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
  696. check_eq(Editor_state.cursor1.line, 2, 'cursor')
  697. y = Editor_state.top
  698. App.screen.check(y, 'def', 'screen:1')
  699. y = y + Editor_state.line_height
  700. App.screen.check(y, 'ghi', 'screen:2')
  701. end
  702. function test_pagedown_can_start_from_middle_of_long_wrapping_line()
  703. -- draw a few lines starting from a very long wrapping line
  704. App.screen.init{width=Editor_state.left+30, height=60}
  705. Editor_state = edit.initialize_test_state()
  706. Editor_state.lines = load_array{'abc def ghi jkl mno pqr stu vwx yza bcd efg hij', 'XYZ'}
  707. Text.redraw_all(Editor_state)
  708. Editor_state.cursor1 = {line=1, pos=2}
  709. Editor_state.screen_top1 = {line=1, pos=1}
  710. edit.draw(Editor_state)
  711. local y = Editor_state.top
  712. App.screen.check(y, 'abc ', 'baseline/screen:1')
  713. y = y + Editor_state.line_height
  714. App.screen.check(y, 'def ', 'baseline/screen:2')
  715. y = y + Editor_state.line_height
  716. App.screen.check(y, 'ghi ', 'baseline/screen:3')
  717. -- after pagedown we scroll down the very long wrapping line
  718. edit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')
  719. check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
  720. check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')
  721. y = Editor_state.top
  722. App.screen.check(y, 'ghi ', 'screen:1')
  723. y = y + Editor_state.line_height
  724. App.screen.check(y, 'jkl ', 'screen:2')
  725. y = y + Editor_state.line_height
  726. if Version == '12.0' then
  727. -- HACK: Maybe v12.0 uses a different font? Strange that it only causes
  728. -- issues in a couple of places.
  729. -- We'll need to rethink our tests if issues like this start to multiply.
  730. App.screen.check(y, 'mno ', 'screen:3')
  731. else
  732. App.screen.check(y, 'mn', 'screen:3')
  733. end
  734. end
  735. function test_pagedown_never_moves_up()
  736. -- draw the final screen line of a wrapping line
  737. App.screen.init{width=Editor_state.left+30, height=60}
  738. Editor_state = edit.initialize_test_state()
  739. Editor_state.lines = load_array{'abc def ghi'}
  740. Text.redraw_all(Editor_state)
  741. Editor_state.cursor1 = {line=1, pos=9}
  742. Editor_state.screen_top1 = {line=1, pos=9}
  743. edit.draw(Editor_state)
  744. -- pagedown makes no change
  745. edit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')
  746. check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
  747. check_eq(Editor_state.screen_top1.pos, 9, 'screen_top:pos')
  748. end
  749. function test_down_arrow_moves_cursor()
  750. App.screen.init{width=120, height=60}
  751. Editor_state = edit.initialize_test_state()
  752. Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
  753. Text.redraw_all(Editor_state)
  754. Editor_state.cursor1 = {line=1, pos=1}
  755. Editor_state.screen_top1 = {line=1, pos=1}
  756. -- initially the first three lines are displayed
  757. edit.draw(Editor_state)
  758. local y = Editor_state.top
  759. App.screen.check(y, 'abc', 'baseline/screen:1')
  760. y = y + Editor_state.line_height
  761. App.screen.check(y, 'def', 'baseline/screen:2')
  762. y = y + Editor_state.line_height
  763. App.screen.check(y, 'ghi', 'baseline/screen:3')
  764. -- after hitting the down arrow, the cursor moves down by 1 line
  765. edit.run_after_keychord(Editor_state, 'down', 'down')
  766. check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
  767. check_eq(Editor_state.cursor1.line, 2, 'cursor')
  768. -- the screen is unchanged
  769. y = Editor_state.top
  770. App.screen.check(y, 'abc', 'screen:1')
  771. y = y + Editor_state.line_height
  772. App.screen.check(y, 'def', 'screen:2')
  773. y = y + Editor_state.line_height
  774. App.screen.check(y, 'ghi', 'screen:3')
  775. end
  776. function test_down_arrow_scrolls_down_by_one_line()
  777. -- display the first three lines with the cursor on the bottom line
  778. App.screen.init{width=120, height=60}
  779. Editor_state = edit.initialize_test_state()
  780. Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
  781. Text.redraw_all(Editor_state)
  782. Editor_state.cursor1 = {line=3, pos=1}
  783. Editor_state.screen_top1 = {line=1, pos=1}
  784. edit.draw(Editor_state)
  785. local y = Editor_state.top
  786. App.screen.check(y, 'abc', 'baseline/screen:1')
  787. y = y + Editor_state.line_height
  788. App.screen.check(y, 'def', 'baseline/screen:2')
  789. y = y + Editor_state.line_height
  790. App.screen.check(y, 'ghi', 'baseline/screen:3')
  791. -- after hitting the down arrow the screen scrolls down by one line
  792. edit.run_after_keychord(Editor_state, 'down', 'down')
  793. check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
  794. check_eq(Editor_state.cursor1.line, 4, 'cursor')
  795. y = Editor_state.top
  796. App.screen.check(y, 'def', 'screen:1')
  797. y = y + Editor_state.line_height
  798. App.screen.check(y, 'ghi', 'screen:2')
  799. y = y + Editor_state.line_height
  800. App.screen.check(y, 'jkl', 'screen:3')
  801. end
  802. function test_down_arrow_scrolls_down_by_one_screen_line()
  803. -- display the first three lines with the cursor on the bottom line
  804. App.screen.init{width=Editor_state.left+30, height=60}
  805. Editor_state = edit.initialize_test_state()
  806. Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
  807. Text.redraw_all(Editor_state)
  808. Editor_state.cursor1 = {line=3, pos=1}
  809. Editor_state.screen_top1 = {line=1, pos=1}
  810. edit.draw(Editor_state)
  811. local y = Editor_state.top
  812. App.screen.check(y, 'abc', 'baseline/screen:1')
  813. y = y + Editor_state.line_height
  814. App.screen.check(y, 'def', 'baseline/screen:2')
  815. y = y + Editor_state.line_height
  816. App.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
  817. -- after hitting the down arrow the screen scrolls down by one line
  818. edit.run_after_keychord(Editor_state, 'down', 'down')
  819. check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
  820. check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
  821. check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
  822. y = Editor_state.top
  823. App.screen.check(y, 'def', 'screen:1')
  824. y = y + Editor_state.line_height
  825. App.screen.check(y, 'ghi ', 'screen:2')
  826. y = y + Editor_state.line_height
  827. App.screen.check(y, 'jkl', 'screen:3')
  828. end
  829. function test_down_arrow_scrolls_down_by_one_screen_line_after_splitting_within_word()
  830. -- display the first three lines with the cursor on the bottom line
  831. App.screen.init{width=Editor_state.left+30, height=60}
  832. Editor_state = edit.initialize_test_state()
  833. Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}
  834. Text.redraw_all(Editor_state)
  835. Editor_state.cursor1 = {line=3, pos=1}
  836. Editor_state.screen_top1 = {line=1, pos=1}
  837. edit.draw(Editor_state)
  838. local y = Editor_state.top
  839. App.screen.check(y, 'abc', 'baseline/screen:1')
  840. y = y + Editor_state.line_height
  841. App.screen.check(y, 'def', 'baseline/screen:2')
  842. y = y + Editor_state.line_height
  843. App.screen.check(y, 'ghij', 'baseline/screen:3')
  844. -- after hitting the down arrow the screen scrolls down by one line
  845. edit.run_after_keychord(Editor_state, 'down', 'down')
  846. check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
  847. check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
  848. check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
  849. y = Editor_state.top
  850. App.screen.check(y, 'def', 'screen:1')
  851. y = y + Editor_state.line_height
  852. App.screen.check(y, 'ghij', 'screen:2')
  853. y = y + Editor_state.line_height
  854. App.screen.check(y, 'kl', 'screen:3')
  855. end
  856. function test_pagedown_followed_by_down_arrow_does_not_scroll_screen_up()
  857. App.screen.init{width=Editor_state.left+30, height=60}
  858. Editor_state = edit.initialize_test_state()
  859. Editor_state.lines = load_array{'abc', 'def', 'ghijkl', 'mno'}
  860. Text.redraw_all(Editor_state)
  861. Editor_state.cursor1 = {line=3, pos=1}
  862. Editor_state.screen_top1 = {line=1, pos=1}
  863. edit.draw(Editor_state)
  864. local y = Editor_state.top
  865. App.screen.check(y, 'abc', 'baseline/screen:1')
  866. y = y + Editor_state.line_height
  867. App.screen.check(y, 'def', 'baseline/screen:2')
  868. y = y + Editor_state.line_height
  869. App.screen.check(y, 'ghij', 'baseline/screen:3')
  870. -- after hitting pagedown the screen scrolls down to start of a long line
  871. edit.run_after_keychord(Editor_state, 'pagedown', 'pagedown')
  872. check_eq(Editor_state.screen_top1.line, 3, 'baseline2/screen_top')
  873. check_eq(Editor_state.cursor1.line, 3, 'baseline2/cursor:line')
  874. check_eq(Editor_state.cursor1.pos, 1, 'baseline2/cursor:pos')
  875. -- after hitting down arrow the screen doesn't scroll down further, and certainly doesn't scroll up
  876. edit.run_after_keychord(Editor_state, 'down', 'down')
  877. check_eq(Editor_state.screen_top1.line, 3, 'screen_top')
  878. check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
  879. check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
  880. y = Editor_state.top
  881. App.screen.check(y, 'ghij', 'screen:1')
  882. y = y + Editor_state.line_height
  883. App.screen.check(y, 'kl', 'screen:2')
  884. y = y + Editor_state.line_height
  885. App.screen.check(y, 'mno', 'screen:3')
  886. end
  887. function test_up_arrow_moves_cursor()
  888. -- display the first 3 lines with the cursor on the bottom line
  889. App.screen.init{width=120, height=60}
  890. Editor_state = edit.initialize_test_state()
  891. Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
  892. Text.redraw_all(Editor_state)
  893. Editor_state.cursor1 = {line=3, pos=1}
  894. Editor_state.screen_top1 = {line=1, pos=1}
  895. edit.draw(Editor_state)
  896. local y = Editor_state.top
  897. App.screen.check(y, 'abc', 'baseline/screen:1')
  898. y = y + Editor_state.line_height
  899. App.screen.check(y, 'def', 'baseline/screen:2')
  900. y = y + Editor_state.line_height
  901. App.screen.check(y, 'ghi', 'baseline/screen:3')
  902. -- after hitting the up arrow the cursor moves up by 1 line
  903. edit.run_after_keychord(Editor_state, 'up', 'up')
  904. check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
  905. check_eq(Editor_state.cursor1.line, 2, 'cursor')
  906. -- the screen is unchanged
  907. y = Editor_state.top
  908. App.screen.check(y, 'abc', 'screen:1')
  909. y = y + Editor_state.line_height
  910. App.screen.check(y, 'def', 'screen:2')
  911. y = y + Editor_state.line_height
  912. App.screen.check(y, 'ghi', 'screen:3')
  913. end
  914. function test_up_arrow_scrolls_up_by_one_line()
  915. -- display the lines 2/3/4 with the cursor on line 2
  916. App.screen.init{width=120, height=60}
  917. Editor_state = edit.initialize_test_state()
  918. Editor_state.lines = load_array{'abc', 'def', 'ghi', 'jkl'}
  919. Text.redraw_all(Editor_state)
  920. Editor_state.cursor1 = {line=2, pos=1}
  921. Editor_state.screen_top1 = {line=2, pos=1}
  922. edit.draw(Editor_state)
  923. local y = Editor_state.top
  924. App.screen.check(y, 'def', 'baseline/screen:1')
  925. y = y + Editor_state.line_height
  926. App.screen.check(y, 'ghi', 'baseline/screen:2')
  927. y = y + Editor_state.line_height
  928. App.screen.check(y, 'jkl', 'baseline/screen:3')
  929. -- after hitting the up arrow the screen scrolls up by one line
  930. edit.run_after_keychord(Editor_state, 'up', 'up')
  931. check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
  932. check_eq(Editor_state.cursor1.line, 1, 'cursor')
  933. y = Editor_state.top
  934. App.screen.check(y, 'abc', 'screen:1')
  935. y = y + Editor_state.line_height
  936. App.screen.check(y, 'def', 'screen:2')
  937. y = y + Editor_state.line_height
  938. App.screen.check(y, 'ghi', 'screen:3')
  939. end
  940. function test_up_arrow_scrolls_up_by_one_screen_line()
  941. -- display lines starting from second screen line of a line
  942. App.screen.init{width=Editor_state.left+30, height=60}
  943. Editor_state = edit.initialize_test_state()
  944. Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
  945. Text.redraw_all(Editor_state)
  946. Editor_state.cursor1 = {line=3, pos=6}
  947. Editor_state.screen_top1 = {line=3, pos=5}
  948. edit.draw(Editor_state)
  949. local y = Editor_state.top
  950. App.screen.check(y, 'jkl', 'baseline/screen:1')
  951. y = y + Editor_state.line_height
  952. App.screen.check(y, 'mno', 'baseline/screen:2')
  953. -- after hitting the up arrow the screen scrolls up to first screen line
  954. edit.run_after_keychord(Editor_state, 'up', 'up')
  955. y = Editor_state.top
  956. App.screen.check(y, 'ghi ', 'screen:1')
  957. y = y + Editor_state.line_height
  958. App.screen.check(y, 'jkl', 'screen:2')
  959. y = y + Editor_state.line_height
  960. App.screen.check(y, 'mno', 'screen:3')
  961. check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
  962. check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
  963. check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
  964. check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
  965. end
  966. function test_up_arrow_scrolls_up_to_final_screen_line()
  967. -- display lines starting just after a long line
  968. App.screen.init{width=Editor_state.left+30, height=60}
  969. Editor_state = edit.initialize_test_state()
  970. Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}
  971. Text.redraw_all(Editor_state)
  972. Editor_state.cursor1 = {line=2, pos=1}
  973. Editor_state.screen_top1 = {line=2, pos=1}
  974. edit.draw(Editor_state)
  975. local y = Editor_state.top
  976. App.screen.check(y, 'ghi', 'baseline/screen:1')
  977. y = y + Editor_state.line_height
  978. App.screen.check(y, 'jkl', 'baseline/screen:2')
  979. y = y + Editor_state.line_height
  980. App.screen.check(y, 'mno', 'baseline/screen:3')
  981. -- after hitting the up arrow the screen scrolls up to final screen line of previous line
  982. edit.run_after_keychord(Editor_state, 'up', 'up')
  983. y = Editor_state.top
  984. App.screen.check(y, 'def', 'screen:1')
  985. y = y + Editor_state.line_height
  986. App.screen.check(y, 'ghi', 'screen:2')
  987. y = y + Editor_state.line_height
  988. App.screen.check(y, 'jkl', 'screen:3')
  989. check_eq(Editor_state.screen_top1.line, 1, 'screen_top:line')
  990. check_eq(Editor_state.screen_top1.pos, 5, 'screen_top:pos')
  991. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  992. check_eq(Editor_state.cursor1.pos, 5, 'cursor:pos')
  993. end
  994. function test_up_arrow_scrolls_up_to_empty_line()
  995. -- display a screenful of text with an empty line just above it outside the screen
  996. App.screen.init{width=120, height=60}
  997. Editor_state = edit.initialize_test_state()
  998. Editor_state.lines = load_array{'', 'abc', 'def', 'ghi', 'jkl'}
  999. Text.redraw_all(Editor_state)
  1000. Editor_state.cursor1 = {line=2, pos=1}
  1001. Editor_state.screen_top1 = {line=2, pos=1}
  1002. edit.draw(Editor_state)
  1003. local y = Editor_state.top
  1004. App.screen.check(y, 'abc', 'baseline/screen:1')
  1005. y = y + Editor_state.line_height
  1006. App.screen.check(y, 'def', 'baseline/screen:2')
  1007. y = y + Editor_state.line_height
  1008. App.screen.check(y, 'ghi', 'baseline/screen:3')
  1009. -- after hitting the up arrow the screen scrolls up by one line
  1010. edit.run_after_keychord(Editor_state, 'up', 'up')
  1011. check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
  1012. check_eq(Editor_state.cursor1.line, 1, 'cursor')
  1013. y = Editor_state.top
  1014. -- empty first line
  1015. y = y + Editor_state.line_height
  1016. App.screen.check(y, 'abc', 'screen:2')
  1017. y = y + Editor_state.line_height
  1018. App.screen.check(y, 'def', 'screen:3')
  1019. end
  1020. function test_pageup()
  1021. App.screen.init{width=120, height=45}
  1022. Editor_state = edit.initialize_test_state()
  1023. Editor_state.lines = load_array{'abc', 'def', 'ghi'}
  1024. Text.redraw_all(Editor_state)
  1025. Editor_state.cursor1 = {line=2, pos=1}
  1026. Editor_state.screen_top1 = {line=2, pos=1}
  1027. -- initially the last two lines are displayed
  1028. edit.draw(Editor_state)
  1029. local y = Editor_state.top
  1030. App.screen.check(y, 'def', 'baseline/screen:1')
  1031. y = y + Editor_state.line_height
  1032. App.screen.check(y, 'ghi', 'baseline/screen:2')
  1033. -- after pageup the cursor goes to first line
  1034. edit.run_after_keychord(Editor_state, 'pageup', 'pageup')
  1035. check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
  1036. check_eq(Editor_state.cursor1.line, 1, 'cursor')
  1037. y = Editor_state.top
  1038. App.screen.check(y, 'abc', 'screen:1')
  1039. y = y + Editor_state.line_height
  1040. App.screen.check(y, 'def', 'screen:2')
  1041. end
  1042. function test_pageup_scrolls_up_by_screen_line()
  1043. -- display the first three lines with the cursor on the bottom line
  1044. App.screen.init{width=Editor_state.left+30, height=60}
  1045. Editor_state = edit.initialize_test_state()
  1046. Editor_state.lines = load_array{'abc def', 'ghi', 'jkl', 'mno'}
  1047. Text.redraw_all(Editor_state)
  1048. Editor_state.cursor1 = {line=2, pos=1}
  1049. Editor_state.screen_top1 = {line=2, pos=1}
  1050. edit.draw(Editor_state)
  1051. local y = Editor_state.top
  1052. App.screen.check(y, 'ghi', 'baseline/screen:1')
  1053. y = y + Editor_state.line_height
  1054. App.screen.check(y, 'jkl', 'baseline/screen:2')
  1055. y = y + Editor_state.line_height
  1056. App.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
  1057. -- after hitting the page-up key the screen scrolls up to top
  1058. edit.run_after_keychord(Editor_state, 'pageup', 'pageup')
  1059. check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
  1060. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  1061. check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
  1062. y = Editor_state.top
  1063. App.screen.check(y, 'abc ', 'screen:1')
  1064. y = y + Editor_state.line_height
  1065. App.screen.check(y, 'def', 'screen:2')
  1066. y = y + Editor_state.line_height
  1067. App.screen.check(y, 'ghi', 'screen:3')
  1068. end
  1069. function test_pageup_scrolls_up_from_middle_screen_line()
  1070. -- display a few lines starting from the middle of a line (Editor_state.cursor1.pos > 1)
  1071. App.screen.init{width=Editor_state.left+30, height=60}
  1072. Editor_state = edit.initialize_test_state()
  1073. Editor_state.lines = load_array{'abc def', 'ghi jkl', 'mno'}
  1074. Text.redraw_all(Editor_state)
  1075. Editor_state.cursor1 = {line=2, pos=5}
  1076. Editor_state.screen_top1 = {line=2, pos=5}
  1077. edit.draw(Editor_state)
  1078. local y = Editor_state.top
  1079. App.screen.check(y, 'jkl', 'baseline/screen:2')
  1080. y = y + Editor_state.line_height
  1081. App.screen.check(y, 'mno', 'baseline/screen:3') -- line wrapping includes trailing whitespace
  1082. -- after hitting the page-up key the screen scrolls up to top
  1083. edit.run_after_keychord(Editor_state, 'pageup', 'pageup')
  1084. check_eq(Editor_state.screen_top1.line, 1, 'screen_top')
  1085. check_eq(Editor_state.cursor1.line, 1, 'cursor:line')
  1086. check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
  1087. y = Editor_state.top
  1088. App.screen.check(y, 'abc ', 'screen:1')
  1089. y = y + Editor_state.line_height
  1090. App.screen.check(y, 'def', 'screen:2')
  1091. y = y + Editor_state.line_height
  1092. App.screen.check(y, 'ghi ', 'screen:3')
  1093. end
  1094. function test_left_arrow_scrolls_up_in_wrapped_line()
  1095. -- display lines starting from second screen line of a line
  1096. App.screen.init{width=Editor_state.left+30, height=60}
  1097. Editor_state = edit.initialize_test_state()
  1098. Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
  1099. Text.redraw_all(Editor_state)
  1100. Editor_state.screen_top1 = {line=3, pos=5}
  1101. -- cursor is at top of screen
  1102. Editor_state.cursor1 = {line=3, pos=5}
  1103. edit.draw(Editor_state)
  1104. local y = Editor_state.top
  1105. App.screen.check(y, 'jkl', 'baseline/screen:1')
  1106. y = y + Editor_state.line_height
  1107. App.screen.check(y, 'mno', 'baseline/screen:2')
  1108. -- after hitting the left arrow the screen scrolls up to first screen line
  1109. edit.run_after_keychord(Editor_state, 'left', 'left')
  1110. y = Editor_state.top
  1111. App.screen.check(y, 'ghi ', 'screen:1')
  1112. y = y + Editor_state.line_height
  1113. App.screen.check(y, 'jkl', 'screen:2')
  1114. y = y + Editor_state.line_height
  1115. App.screen.check(y, 'mno', 'screen:3')
  1116. check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
  1117. check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
  1118. check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
  1119. check_eq(Editor_state.cursor1.pos, 4, 'cursor:pos')
  1120. end
  1121. function test_right_arrow_scrolls_down_in_wrapped_line()
  1122. -- display the first three lines with the cursor on the bottom line
  1123. App.screen.init{width=Editor_state.left+30, height=60}
  1124. Editor_state = edit.initialize_test_state()
  1125. Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
  1126. Text.redraw_all(Editor_state)
  1127. Editor_state.screen_top1 = {line=1, pos=1}
  1128. -- cursor is at bottom right of screen
  1129. Editor_state.cursor1 = {line=3, pos=5}
  1130. edit.draw(Editor_state)
  1131. local y = Editor_state.top
  1132. App.screen.check(y, 'abc', 'baseline/screen:1')
  1133. y = y + Editor_state.line_height
  1134. App.screen.check(y, 'def', 'baseline/screen:2')
  1135. y = y + Editor_state.line_height
  1136. App.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
  1137. -- after hitting the right arrow the screen scrolls down by one line
  1138. edit.run_after_keychord(Editor_state, 'right', 'right')
  1139. check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
  1140. check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
  1141. check_eq(Editor_state.cursor1.pos, 6, 'cursor:pos')
  1142. y = Editor_state.top
  1143. App.screen.check(y, 'def', 'screen:1')
  1144. y = y + Editor_state.line_height
  1145. App.screen.check(y, 'ghi ', 'screen:2')
  1146. y = y + Editor_state.line_height
  1147. App.screen.check(y, 'jkl', 'screen:3')
  1148. end
  1149. function test_home_scrolls_up_in_wrapped_line()
  1150. -- display lines starting from second screen line of a line
  1151. App.screen.init{width=Editor_state.left+30, height=60}
  1152. Editor_state = edit.initialize_test_state()
  1153. Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
  1154. Text.redraw_all(Editor_state)
  1155. Editor_state.screen_top1 = {line=3, pos=5}
  1156. -- cursor is at top of screen
  1157. Editor_state.cursor1 = {line=3, pos=5}
  1158. edit.draw(Editor_state)
  1159. local y = Editor_state.top
  1160. App.screen.check(y, 'jkl', 'baseline/screen:1')
  1161. y = y + Editor_state.line_height
  1162. App.screen.check(y, 'mno', 'baseline/screen:2')
  1163. -- after hitting home the screen scrolls up to first screen line
  1164. edit.run_after_keychord(Editor_state, 'home', 'home')
  1165. y = Editor_state.top
  1166. App.screen.check(y, 'ghi ', 'screen:1')
  1167. y = y + Editor_state.line_height
  1168. App.screen.check(y, 'jkl', 'screen:2')
  1169. y = y + Editor_state.line_height
  1170. App.screen.check(y, 'mno', 'screen:3')
  1171. check_eq(Editor_state.screen_top1.line, 3, 'screen_top:line')
  1172. check_eq(Editor_state.screen_top1.pos, 1, 'screen_top:pos')
  1173. check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
  1174. check_eq(Editor_state.cursor1.pos, 1, 'cursor:pos')
  1175. end
  1176. function test_end_scrolls_down_in_wrapped_line()
  1177. -- display the first three lines with the cursor on the bottom line
  1178. App.screen.init{width=Editor_state.left+30, height=60}
  1179. Editor_state = edit.initialize_test_state()
  1180. Editor_state.lines = load_array{'abc', 'def', 'ghi jkl', 'mno'}
  1181. Text.redraw_all(Editor_state)
  1182. Editor_state.screen_top1 = {line=1, pos=1}
  1183. -- cursor is at bottom right of screen
  1184. Editor_state.cursor1 = {line=3, pos=5}
  1185. edit.draw(Editor_state)
  1186. local y = Editor_state.top
  1187. App.screen.check(y, 'abc', 'baseline/screen:1')
  1188. y = y + Editor_state.line_height
  1189. App.screen.check(y, 'def', 'baseline/screen:2')
  1190. y = y + Editor_state.line_height
  1191. App.screen.check(y, 'ghi ', 'baseline/screen:3') -- line wrapping includes trailing whitespace
  1192. -- after hitting end the screen scrolls down by one line
  1193. edit.run_after_keychord(Editor_state, 'end', 'end')
  1194. check_eq(Editor_state.screen_top1.line, 2, 'screen_top')
  1195. check_eq(Editor_state.cursor1.line, 3, 'cursor:line')
  1196. check_eq(Editor_state.cursor1.pos, 8, 'cursor:pos')
  1197. y = Editor_state.top
  1198. App.screen.check(y, 'def', 'screen:1')
  1199. y = y + Editor_state.line_height
  1200. App.screen.check(y, 'ghi ', 'screen:2')
  1201. y = y + Editor_state.line_height
  1202. App.screen.check(y, 'jkl', 'screen:3')
  1203. end
  1204. function test_search()
  1205. App.screen.init{width=120, height=60}
  1206. Editor_state = edit.initialize_test_state()
  1207. Editor_state.lines = load_array{'abc', 'def', 'ghi', '’deg'} -- contains unicode quote in final line
  1208. Text.redraw_all(Editor_state)
  1209. Editor_state.cursor1 = {line=1, pos=1}
  1210. Editor_state.screen_top1 = {line=1, pos=1}
  1211. edit.draw(Editor_state)
  1212. -- search for a string
  1213. edit.run_after_keychord(Editor_state, 'C-f', 'f')
  1214. edit.run_after_text_input(Editor_state, 'd')
  1215. edit.run_after_keychord(Editor_state, 'return', 'return')
  1216. check_eq(Editor_state.cursor1.line, 2, '1/cursor:line')
  1217. check_eq(Editor_state.cursor1.pos, 1, '1/cursor:pos')
  1218. -- reset cursor
  1219. Editor_state.cursor1 = {line=1, pos=1}
  1220. Editor_state.screen_top1 = {line=1, pos=1}
  1221. -- search for second occurrence
  1222. edit.run_after_keychord(Editor_state, 'C-f', 'f')
  1223. edit.run_after_text_input(Editor_state, 'de')
  1224. edit.run_after_keychord(Editor_state, 'down', 'down')
  1225. edit.run_after_keychord(Editor_state, 'return', 'return')
  1226. check_eq(Editor_state.cursor1.line, 4, '2/cursor:line')
  1227. check_eq(Editor_state.cursor1.pos, 2, '2/cursor:pos')
  1228. end
  1229. function test_search_upwards()
  1230. App.screen.init{width=120, height=60}
  1231. Editor_state = edit.initialize_test_state()
  1232. Editor_state.lines = load_array{'’abc', 'abd'} -- contains unicode quote
  1233. Text.redraw_all(Editor_state)
  1234. Editor_state.cursor1 = {line=2, pos=1}
  1235. Editor_state.screen_top1 = {line=1, pos=1}
  1236. edit.draw(Editor_state)
  1237. -- search for a string
  1238. edit.run_after_keychord(Editor_state, 'C-f', 'f')
  1239. edit.run_after_text_input(Editor_state, 'a')
  1240. -- search for previous occurrence
  1241. edit.run_after_keychord(Editor_state, 'up', 'up')
  1242. check_eq(Editor_state.cursor1.line, 1, '2/cursor:line')
  1243. check_eq(Editor_state.cursor1.pos, 2, '2/cursor:pos')
  1244. end
  1245. function test_search_wrap()
  1246. App.screen.init{width=120, height=60}
  1247. Editor_state = edit.initialize_test_state()
  1248. Editor_state.lines = load_array{'’abc', 'def'} -- contains unicode quote in first line
  1249. Text.redraw_all(Editor_state)
  1250. Editor_state.cursor1 = {line=2, pos=1}
  1251. Editor_state.screen_top1 = {line=1, pos=1}
  1252. edit.draw(Editor_state)
  1253. -- search for a string
  1254. edit.run_after_keychord(Editor_state, 'C-f', 'f')
  1255. edit.run_after_text_input(Editor_state, 'a')
  1256. edit.run_after_keychord(Editor_state, 'return', 'return')
  1257. -- cursor wraps
  1258. check_eq(Editor_state.cursor1.line, 1, '1/cursor:line')
  1259. check_eq(Editor_state.cursor1.pos, 2, '1/cursor:pos')
  1260. end
  1261. function test_search_wrap_upwards()
  1262. App.screen.init{width=120, height=60}
  1263. Editor_state = edit.initialize_test_state()
  1264. Editor_state.lines = load_array{'abc ’abd'} -- contains unicode quote
  1265. Text.redraw_all(Editor_state)
  1266. Editor_state.cursor1 = {line=1, pos=1}
  1267. Editor_state.screen_top1 = {line=1, pos=1}
  1268. edit.draw(Editor_state)
  1269. -- search upwards for a string
  1270. edit.run_after_keychord(Editor_state, 'C-f', 'f')
  1271. edit.run_after_text_input(Editor_state, 'a')
  1272. edit.run_after_keychord(Editor_state, 'up', 'up')
  1273. -- cursor wraps
  1274. check_eq(Editor_state.cursor1.line, 1, '1/cursor:line')
  1275. check_eq(Editor_state.cursor1.pos, 6, '1/cursor:pos')
  1276. end
  1277. function test_search_downwards_from_end_of_line()
  1278. App.screen.init{width=120, height=60}
  1279. Editor_state = edit.initialize_test_state()
  1280. Editor_state.lines = load_array{'abc', 'def', 'ghi'}
  1281. Text.redraw_all(Editor_state)
  1282. Editor_state.cursor1 = {line=1, pos=4}
  1283. Editor_state.screen_top1 = {line=1, pos=1}
  1284. edit.draw(Editor_state)
  1285. -- search for empty string
  1286. edit.run_after_keychord(Editor_state, 'C-f', 'f')
  1287. edit.run_after_keychord(Editor_state, 'down', 'down')
  1288. -- no crash
  1289. end
  1290. function test_search_downwards_from_final_pos_of_line()
  1291. App.screen.init{width=120, height=60}
  1292. Editor_state = edit.initialize_test_state()
  1293. Editor_state.lines = load_array{'abc', 'def', 'ghi'}
  1294. Text.redraw_all(Editor_state)
  1295. Editor_state.cursor1 = {line=1, pos=3}
  1296. Editor_state.screen_top1 = {line=1, pos=1}
  1297. edit.draw(Editor_state)
  1298. -- search for empty string
  1299. edit.run_after_keychord(Editor_state, 'C-f', 'f')
  1300. edit.run_after_keychord(Editor_state, 'down', 'down')
  1301. -- no crash
  1302. end