Text Editor with a Cursor: Typing, Backspace, and Four-Way Cursor Movement
DatologyaiSoftware Engineer · Technical ScreenLast updated
Implement a text editor class that types characters at a cursor, deletes with backspace, and renders the text with a bar marking the cursor. Follow-ups add left/right and then up/down cursor movement, testing the choice of data structure for cheap edits, boundary handling, and line-aware navigation.
Implement a TextEditor class that holds one document and a cursor. The cursor sits between two characters, or at the start or end of the text, and every edit happens at the cursor. Start with three operations:
type_in(char): insert one character at the cursor. The cursor ends up just after the inserted character.backspace(): delete the character immediately before the cursor.__str__(): return the text with a|inserted at the cursor position.
e = TextEditor()
e.type_in("h")
e.type_in("i")
str(e) # "hi|"
e.backspace()
str(e) # "h|"
The interviewer then adds cursor movement in two follow-ups: first left and right, then up and down.
Constraints and Clarifications
- A new editor is empty, with the cursor at the start, so
str(TextEditor())is"|". type_inreceives exactly one character per call.- Strings in the examples are shown as Python literals, so
\nstands for a newline character.
Clarifying Questions
- What should
backspace()do when the cursor is at the start of the document? - Can the document itself contain
|, and if so, how should__str__keep the cursor position unambiguous? - What should a move do when it would leave the document, such as left at the start or right at the end?
- For vertical movement, what defines a line: a typed newline character, or a fixed display width with wrapping?
Part 1 — Typing, backspace and rendering
Implement type_in, backspace and __str__, and state the time complexity of each operation.
What This Part Should Cover
- A representation chosen for cheap edits at the cursor, and why it beats a plain string or a list with a cursor index
- Correct behavior of backspace at the start of the document
- Per-operation complexity, including the cost of building the string in
__str__
Part 2 — Moving the cursor left and right
Add move_cursor(direction), where direction is "left" or "right", moving the cursor by one character. Typing and backspace must keep working at the new cursor position.
# document "cat", cursor at the end: "cat|"
e.move_cursor("left") # "ca|t"
e.type_in("r") # "car|t"
e.backspace() # "ca|t"
What This Part Should Cover
- Constant-time left and right moves that keep later edits at the cursor correct
- Behavior at both ends of the document
- How typing and backspace interact with a cursor in the middle of the text
Part 3 — Moving the cursor up and down
Extend move_cursor to accept "up" and "down", moving the cursor to the previous or next line.
# document "abc\nxyz", cursor after "x": "abc\nx|yz"
e.move_cursor("up") # "a|bc\nxyz"
Clarifying Questions for this Part
- When the target line is shorter than the cursor's current column, where should the cursor land?
- Should a run of vertical moves remember the column the user started from, as most editors do, or should each move use the current column?
- What happens on
"up"from the first line or"down"from the last line?
What This Part Should Cover
- How lines are defined and how the cursor's column is computed
- The landing position when the target line is shorter, and behavior on the first and last lines
- The cost of a vertical move, and whether a line-oriented structure would be a better trade
Hints
Hint 1
Edits happen in one place: Every operation touches the text only at the cursor. Look for a representation in which the cost of an edit does not depend on how much text sits after the cursor.
Hint 2
One character changes sides: A one-step move changes which side of the cursor exactly one character is on. Check that your representation can do that without touching the rest of the text.
Hint 3
Column and line length: A vertical move needs two numbers: the cursor's column in its current line and the length of the line it moves to. Think about how you find each one in your representation, and whether storing something extra avoids rescanning.
Follow-up questions
- How would you add `undo()` and `redo()` for typing and backspace?
- If documents are many megabytes and `__str__` is called after every keystroke, what would you change?
- How do a gap buffer, a rope and a list of lines compare for this editor, and when would you pick each?
Solution walkthrough is for members
Get the full walkthrough with reference code and complexity analysis, plus Kitty Coach at 300 messages a day. Hints, the code runner and community answers stay free.