Command Examples:

Java
Java
C#
C#
PHP
PHP
Python
Python
C++
C++
TypeScript
TypeScript
▸ Command Quick Review

Writing Text Editor in TypeScript

To showcase the Command pattern, we'll be creating a basic implementation of a text editor. This includes the ability to add content(type it on the page), backspace to remove content, and the ability to undo and redo our actions.

We'll start out with a Document class that will contain the text data and actions to add and remove content:

The Document Class:

class TextDocument
{
   protected content: string;

   constructor()
   {
       this.content = "";
   }

   public printDocument()
   {
       console.log(this.content);
   }

   public addContent(content: string)
   {
       this.content += content;
   }

   public length(): number
   {
       return this.content.length;
   }

   public charAt(index: number): string
   {
       return this.content[index];
   }

   public removeContent(numChars: number)
   {
       if(numChars < 0) return;
       if(numChars > this.content.length - 1)
       {
           this.content = "";
           return;
       }

       this.content = this.content.substring(0, this.content.length - numChars);

   }
}

Next, we'll want to create an interface for our commands. We'll want to have the usual execute method, in addition to undo and redo methods:

IUndoableCommand

interface IUndoableCommand
{
   execute(): void;
   undo(): void;
   redo(): void;
}

AddContentCommand:

class AddContentCommand implements IUndoableCommand
{
   document: TextDocument;
   content: string;
   
   constructor(document: TextDocument, content: string)
   {
       this.document = document;
       this.content = content;
   }

   execute() {
       this.document.addContent(this.content);
   }
   undo() {
       this.document.removeContent(this.content.length);
   }
   redo() {
       this.execute();
   }
   
}

BackspaceCommand

class BackspaceCommand implements IUndoableCommand
{
   document: TextDocument;
   removedChar: string;
   
   constructor(document: TextDocument)
   {
       this.document = document;
       this.removedChar = null;
   }

   execute()
   {
       this.removedChar = this.document.charAt(this.document.length() - 1);
       this.document.removeContent(1);
   }
   undo() 
   {
       this.document.addContent(this.removedChar);
   }
   redo()
   {
       this.execute();
   }
   
}

Lastly, we'll want to create a Document writer that will hold onto a list of our actions in order to undo and redo inputs:

DocumentWriter

class DocumentWriter
{
   undoStack: Array<IUndoableCommand>;
   redoStack: Array<IUndoableCommand>;

   document: TextDocument;

   constructor(document: TextDocument)
   {
       this.document = document;
       this.undoStack = new Array<IUndoableCommand>();
       this.redoStack = new Array<IUndoableCommand>();
   }

   public write(content: string)
   {
       const command = new AddContentCommand(this.document, content);
       this.undoStack.push(command);
       this.redoStack = new Array<IUndoableCommand>();

       command.execute();
   }

   public backspace()
   {
       if(this.document.length() == 0) return;
       const command = new BackspaceCommand(this.document);
       this.undoStack.push(command);
       this.redoStack = new Array<IUndoableCommand>();

       command.execute();
   }

   public undo()
   {
       if(this.undoStack.length > 0)
       {
           const command = this.undoStack.pop();
           this.redoStack.push(command);
           command.undo();
           
       }
   }

   public redo()
   {
       if(this.redoStack.length > 0)
       {
           const command = this.redoStack.pop();
           this.undoStack.push(command);
           command.redo();
       }
   }
}

Demo:

class CommandSolution
{
   public execute()
   {
       let doc = new TextDocument();
       let writer = new DocumentWriter(doc);

       // Write to the document:
       writer.write("Hello world!");
       doc.printDocument(); // Hello world!
       writer.backspace();
       doc.printDocument(); // Hello world
       writer.undo();
       doc.printDocument(); // Hello world!
       writer.redo();
       doc.printDocument(); // Hello world

       writer.write(" Goodbye!"); 
       doc.printDocument(); // Hello world Goodbye!
       writer.undo();
       doc.printDocument(); // Hello world
       writer.undo();
       doc.printDocument(); // Hello world!


   }
}

const solution = new CommandSolution();
solution.execute();

Find any bugs in the code? let us know!