CS 1410-20 Homework 2

Due: Friday, September 10th, 2010 9:40am

This assignment builds on the text editor of HW 1. You’ll want to copy over your implementation of insert-at, or use this one:

  ;; insert-at : string num string -> string
  ;;  Inserts new into orig at position n
  (define (insert-at orig n new)
    (string-append (substring orig 0 n)
                   new
                   (substring orig n)))

Part 1 – Caret Position

A more realistic text editor lets the user position the caret for inserting text that is typed (instead of always inserting into the middle). In that case, the state of the text editor consists of two parts: the content string and the position of the insertion caret.

  ;; An editor1 is
  ;;  (make-editor1 string num)
  ;;      where the num is always between 0 and
  ;;      the length of the string
  (define-struct editor1 (content caret))
(make-editor1 "hello" 3)

Implement the function insert1, which takes a editor1 and a string and returns an editor1 with the given string inserted at the current caret place. The caret place should be advanced as many characters as the inserted string.

For example, (insert1 (make-editor1 "hello" 3) "q") should produce (make-editor1 "helqlo" 4).

(make-editor1 "hello" 3)
 → "q" → 
(make-editor1 "helqlo" 4)

Part 2 – Optional: Editor Image

This part is optional.

Write an editor1-image function that takes an editor1 and produces an image that shows the editor text with a vertical line at the caret place (which is between characters in the content). This function is similar to editor-image from HW 1, except that the caret is not always in the middle of the content.

You can now add (require 2htdp/universe) at the beginning of your program and then add

  ;; main : editor1 -> editor1
  (define (main starting-editor1)
    (big-bang starting-editor1
              (on-key insert)
              (to-draw editor1-image)))

In the interactions window (that is, not in the main program that you hand in, please), you can run (main (make-editor1 "" 0)) to start your text editor. At this point, however, typing plain characters will insert them at the end.

Part 3 – Caret Movement

With your current text editor, if you hit the left-arrow key, then the text “left” is inserted into your editor. That’s because the function provided with on-key in big-bang receives the string "left" when you hit the left-arrow key. In general, keys that correspond to text that should be inserted are represented as single-character strings, while most other special keys are represented by multi-character strings (such as "left" for the left-arrow key or "f2" for the F2 key).

Define a function insert-or-move1 that takes an editor1 and a string. It should behave like insert1 if the string contains a single character. If the string is "left", it should decrease the editor’s caret place by one but go no lower than 0. If the string is "right", it should increase the editor’s caret place by one, but go no higher than the length of the content string. All other multi-character strings should have no effect on the content of the editor.

For example, (insert-or-move1 (make-editor1 "hello" 3) "q") should produce (make-editor1 "helqlo" 4), while (insert-or-move1 (make-editor1 "hello" 3) "left") hould produce (make-editor1 "hello" 2).

(make-editor1 "hello" 3)
 → "left" → 
(make-editor1 "hello" 2)

To avoid going lower than 0, you may find it useful to use the max function with 0 and anther expression. To avoid going higher than the length of the content string, you may find it useful to use the min function.

You can change the main function to use insert-or-move1 instead of insert1, and now you can move the caret in the text editor.

Part 4 – The Backspace/Delete Key

The delete key is unusual, in that is is represented by a single-character string where the character cannot be written in the usual way. This special string is written as "\b". The backslash and “b” together create a single character within the string.

Define a function change-or-move1 that is like insert-or-move1, except that if the string is "\b", then the character just before the caret in the editor is removed, and the caret place is decreased by one. If the caret is at place 0, no content is removed from the editor.

For example, (change-or-move1 (make-editor1 "hello" 3) "\b") should produce (make-editor1 "helo" 2).

(make-editor1 "hello" 3)
 → "\b" → 
(make-editor1 "helo" 2)

You should reuse insert-or-move1, rather than copying its implementation into change-or-move1. Also, you may find it useful to implement a helper function remove-chars, which could take a string, a starting position within the string, and a number of characters to remove from the string.

Part 5 – Caret or Selection Range

A yet-more realistic text editor allows the caret to be expanded into a selection range. Typically, the selection can be extended by holding down the shift key in combination with the left- or right-arrow key. To keep things simple, we will use the up-arrow key instead of shift-left-arrow, and we’ll use the down-arrow key instead of shift-right-arrow.

To represent an editor with a caret that is expanded to a selection, we use the following data definitions:

  ;; An editor2 is
  ;;  (make-editor2 string num num)
  ;;      where the first num is always between 0 and
  ;;      the one less length of the string, and the second
  ;;      num is greater than the first and less or equal
  ;;      to the length of the string
  (define-struct editor2 (content left right))
  
  ;; An editor is either
  ;;  - an editor1
  ;;  - an editor2

Using these data defintions, (make-editor1 "nihao" 5) represents a caret at position 5 in the editor (i.e., after the “o” in “nihao”), while (make-editor2 "nihao" 2 4) represents a selection that covers the third and fourth characters in the editor (i.e., the “ha” part of “nihao”).

(make-editor2 "nihao" 2 4)

Implement the function move-left, which takes an editor and returns a editor after the left-arrow key is pressed. Specifically:

Of course, you should implement move-left by implementing move-left1 and move-left2, since editor is defined in terms of editor1 and editor. For now, do not try to reuse change-or-move1 or insert-or-move1.

For example, (move-left (make-editor1 "hello" 2)) should produce (make-editor1 "hello" 1), while (move-left (make-editor2 "hello" 2 4)) should produce (make-editor1 "hello" 2).

Part 6 – Moving Carets and Selections

Implement the function handle-key, which takes an editor and a key string and returns an editor after the key is handled according to the following rules:

This function is complicated, but you should be able to break it down into smaller problems. Of course, you will need to implement handle-key by implementing handle-key1 and handle-key2. For handle-key1, you should reuse change-or-move1, which works for any key except "up" or "down".

Some examples:

  (check-expect (handle-key (make-editor1 "hello" 5) "left")
                (make-editor1 "hello" 4))
  (check-expect (handle-key (make-editor1 "hello" 5) "right")
                (make-editor1 "hello" 5))
  (check-expect (handle-key (make-editor1 "hello" 5) "up")
                (make-editor2 "hello" 4 5))
  (check-expect (handle-key (make-editor1 "hello" 5) "down")
                (make-editor1 "hello" 5))
  (check-expect (handle-key (make-editor2 "hello" 3 5) "right")
                (make-editor1 "hello" 5))
  (check-expect (handle-key (make-editor2 "hello" 3 5) "down")
                (make-editor2 "hello" 3 5))
  (check-expect (handle-key (make-editor2 "hello" 3 4) "f2")
                (make-editor2 "hello" 3 4))
  (check-expect (handle-key (make-editor1 "hello" 3) "q")
                (make-editor1 "helqlo" 4))
  (check-expect (handle-key (make-editor2 "hello" 1 3) "\b")
                (make-editor1 "hlo" 1))

Part 7 – Optional: General Editor Image

This part is optional.

Write an editor2-image function that takes an editor2 and produces an image that shows the editor text with the selected range as white-on-black. Recall that you learned how to put an image on a color background in HW 0.

Write editor-image, which uses editor1-image and editor2-image to create an image for any editor.

Finally, change main to

  ;; main : editor -> editor
  (define (main starting-editor)
    (big-bang starting-editor
              (on-key handle-key)
              (to-draw editor-image)))

and you can run your fairly realistic single-line text editor with (main (make-editor1 "" 0)).


Last update: Wednesday, October 20th, 2010
mflatt@cs.utah.edu