39. Design a Text Editor with cursor operations and Basic Editing

Design a Text Editor with cursor operations and Basic Editing
Design an in-memory Notepad text editor that stores text as lines and maintains a cursor. Support cursor movement, character insertion, deletion, and reading the current line.

Cursor & Document Model

  • Document is a sequence of lines (no embedded '\n' inside a line).
  • Cursor is identified by (row, col), both 0-based.
  • Initially: one empty line [""], i.e. cursor is at (0, 0).
  • When cursor moves to previous or next line in move letf, right, up or down or delete operation then it tries to move to same column it currently is.
    But if next line has fewer characters then cursor simply moves to next right column after last character of next line.

Methods

Class: NotepadEditor

  • NotepadEditor(int linesPerPage) — create editor with given page size (lines per page).
  • addString(String text)
    • Characters are inserted at cursor's row, col, shifting existing characters right.
    • 'text' contains only A-Z, a-z, space ' ', hyphen '-' characters.
    • Cursor is placed just after the last inserted character.
    • If cursor is beyond last character, then space between cursor's row, column and last existing character will be filled with hyphen '-' character.
      e.g. current row = "abcde", and cursor is at index 8 in same row,
      addString("pqr") -> new string : abcde---pqr i.e. columns 5, 6 and 7 were filled with '-'
  • deleteChar()
    • Deletes the character at column - 1 relative to the cursor, if it exists.
    • If col > 0:
      • Remove the character at col - 1 from the current line.
      • Cursor moves to col - 1.
    • If col == 0 and row > 0:
      • Current row is removed and cursor moves to the end of row-1.
    • If cursor is at (0, 0), do nothing.
  • void moveLeft() — move cursor one position left.
  • void moveRight() — move cursor one position right.
  • moveLeft() / moveRight() move within a line;
    if cursor is at 0th column, then moveLeft() will do nothing.
    If cursor is after rightmost character then moveRight() will move cursor 1 column right.
    Cursor will never move to next line.
  • void moveUp() — move cursor one line up, trying to keep preferred column.
  • void moveDown() — move cursor one line down, trying to keep preferred column. If there is no row below then an empty row is created below and cursor is placed at column 0.
  • moveUp() / moveDown() move to previous/next line while trying to keep the cursor's current column, or at end if next line is shorter.
  • void pageUp() — move cursor up by up to linesPerPage lines.
    If there are not enough lines above, then cursor will remain at its existing place.
  • void pageDown() — move cursor down by up to linesPerPage lines.
    if there are not enough rows below then empty rows will be created and cursor would be placed at begining of row.
  • pageUp() / pageDown() are like calling moveUp() / moveDown() up to linesPerPage times.
  • readLeft()
    • Returns characters from 0 till cursor's column-1 from the line where the cursor currently is.
    • If the logical line is shorter than cursor's column, then return text from 0 till last character in line.

Example

NotepadEditor ed = new NotepadEditor(3);
Initial: one empty line [""], cursor at (0,0).

Example 1: basic insert, delete and readLeft.
ed.addString("abc");
Lines:   ["abc"]
Cursor:  (0,3)   logically after "c"

String line0 = ed.readLeft();     // "abc"

ed.moveLeft();                    // Cursor: (0,2)   between 'b' and 'c'
ed.deleteChar();                  // Deletes 'b'; line becomes "ac"
                                  // Cursor: (0,1)   between 'a' and 'c'

String line1 = ed.readLeft();     // "a"
                                  // readLeft() returns characters from index 0
                                  // up to (cursorCol - 1).

------------------------------------------------------------
Example 2 (conceptual): hyphen-fill when cursor is beyond last character.

Assume current row text is "abcde" and cursor is at column index 8
(three positions beyond the last character at index 4).

Calling:

  ed.addString("pqr");

will first fill positions 5, 6 and 7 with '-' and then insert "pqr":
  Resulting line: "abcde---pqr"
Cursor will be placed just after 'r' at column 11.

------------------------------------------------------------
Example 3: moving between lines with different lengths and using readLeft.

Suppose the document currently has lines:
  row 0: "hi"
  row 1: "world"

Cursor is at (1,5), i.e. just after "world".

ed.moveUp();          // move to previous line while trying to keep same column.
                      // row 0 has length 2, so cursor moves to column just after
                      // the last character of row 0.
                      // Cursor: (0,2)   just after "hi"

String sTop = ed.readLeft();      // "hi"
                                  // even though preferred column was 5,
                                  // readLeft returns from index 0 to last char.

ed.moveDown();        // move to next line, trying to keep column 2.
                      // row 1 ("world") has length 5, so column 2 is valid.
                      // Cursor: (1,2)   between 'o' and 'r'

String sMid = ed.readLeft();      // "wo"
                                  // characters from index 0 to cursorCol - 1.

------------------------------------------------------------
Example 4: moveDown and pageDown creating new lines.

Start again with:
  row 0: "hello"
  row 1: "world"

Cursor is at the end of row 1:
  Cursor: (1,5)   just after "world"

1) moveDown() when there is no row below:
   ed.moveDown();

   Since there is no row 2, moveDown() creates a new empty line at row 2
   and places the cursor at column 0:
     row 0: "hello"
     row 1: "world"
     row 2: ""
   Cursor: (2,0)

2) pageDown() when there are not enough rows below.

Assume linesPerPage == 3 and cursor is now at row 0, col 0:
  Cursor: (0,0)

Calling:
  ed.pageDown();

- pageDown() tries to move down by up to 3 lines.
- There are only 3 existing lines (0, 1, 2), so to move a full 3 lines,
  it must create extra rows as needed.
- It creates any missing rows below and moves the cursor to the beginning
  of the target row.

After pageDown():
  New rows may be created so that the cursor lands exactly
  3 lines below its original row, at column 0.

For example, starting from:
  row 0: "hello"
  row 1: "world"
  row 2: ""

Cursor: (0,0)

After ed.pageDown():
  rows become (conceptually):
    row 0: "hello"
    row 1: "world"
    row 2: ""
    row 3: ""        // newly created to satisfy 3-line jump

  Cursor: (3,0)      // beginning of the newly created row

------------------------------------------------------------
Example 5: pageUp with insufficient lines above is a no-op.

Continuing from the previous state:
  row 0: "hello"
  row 1: "world"
  row 2: ""
  row 3: ""
Cursor: (1,0)

If we call:
  ed.pageUp();

- pageUp() tries to move up by up to linesPerPage (= 3) lines.
- There are not enough lines above row 1 to complete a 3-line jump.
  (It would need to go to row -2.)

Therefore, according to the rules:
  - If there are not enough lines above, the cursor remains where it is.

After ed.pageUp():
  Cursor is still at (1,0); the document is unchanged.




Please use Laptop/Desktop or any other large screen to add/edit code.