DEV Community

Andres Court
Andres Court

Posted on

Create a Text Editor in Go - Status Bar

You can access the code in this chapter in the Kilo-Go Github repository, in the statusbar branch.

Currently your file structure should look something like this:

Scrolling with Page Up and Page Down

Now that we have scrolling, let's make the Page Up and Page Down scroll an entire page

File: editor/input.go

func (e *EditorConfig) editorProcessKeypress() {
    ...
    switch b {
    ...
    case utils.PAGE_DOWN:
        e.cy = min(e.rowoffset+e.screenrows+1, e.numrows)
        times := e.screenrows
        for range times {
            e.editorMoveCursor(utils.ARROW_DOWN)
        }
    case utils.PAGE_UP:
        e.cy = e.rowoffset
        times := e.screenrows
        for range times {
            e.editorMoveCursor(utils.ARROW_UP)
        }
    ...
}
Enter fullscreen mode Exit fullscreen mode

Move to the end of the line with End

Now let's have the End key to move the cursor to the end of the current line, not the end of the screen.

File: editor/input.go

func (e *EditorConfig) editorProcessKeypress() {
    ...
    switch b {
    ...
    case utils.END_KEY:
        if e.cy < e.numrows {
            e.cx = len(e.rows[e.cy].chars)
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Make space for the status bar

The last thing we will add before editing text, is a status bar. This will display useful information such as the file name, the cursor position, and other things that we will find useful as we continue working on the editor. However, first thing is we need to have some space in the screen to display the status bar.

File: editor/editor.go

func NewEditor(f func()) *EditorConfig {
    rows, cols, err := utils.GetWindowSize()
    ...
    return &EditorConfig{
        ...
        screenrows:  rows - 1,
        ...
    }
}
Enter fullscreen mode Exit fullscreen mode

File: editor/output.go

func (e *EditorConfig) editorDrawRows(abuf *ab.AppendBuffer) {
    for y := range e.screenrows {
        filerow := y + e.rowoffset
        ...
        fmt.Fprintf(abuf, "%c[K", utils.ESC) // We moved this line from the top to the bottom of the for loop
        fmt.Fprintf(abuf, "\r\n")
    }
}
Enter fullscreen mode Exit fullscreen mode

Display the status bar

To display the status bar, lets make it have inverted color as the rest of the screen. The m command causes the text printed after it to be printed with various possible attributes including bold 1, underscore 4, blink 5, and inverted colors 7. For example, you could specify all of these attributes using the command <Esc>[1;4;5;7m. An argument of 0 clears all attributes, and is the default argument, so we use <Esc>[m to go back to normal text formatting.

File: editor/output.go

func (e *EditorConfig) editorRefreshScreen() {
    ...
    e.editorDrawRows(abuf)
    e.editorDrawStatusBar(abuf)
    ...
}

func (e *EditorConfig) editorDrawStatusBar(abuf *ab.AppendBuffer) {
    fmt.Fprintf(abuf, "%c[7m", utils.ESC)

    for range e.screencols {
        fmt.Fprintf(abuf, " ")
    }

    fmt.Fprintf(abuf, "%c[m", utils.ESC)
}
Enter fullscreen mode Exit fullscreen mode

Display the file name

So lets begin displaying some useful information to the status line, the first thing we will show is the name of the file we are currently editing, and if we haven't opened any file we will display [No name]

File: editor/editor.go

type EditorConfig struct {
    ...
    filename    string
    ...
}

func NewEditor(f func()) *EditorConfig {
    ...
    return &EditorConfig{
        ...
        filename:    "",
        ...
    }
}
Enter fullscreen mode Exit fullscreen mode

File: editor/file.go

func (e *EditorConfig) editorOpen(filename string) {
    ...
    e.filename = filename
    ...
}
Enter fullscreen mode Exit fullscreen mode

File: editor/output.go

func (e *EditorConfig) editorDrawStatusBar(abuf *ab.AppendBuffer) {
    status := e.filename
    if status == "" {
        status = "[No Name]"
    }

    fmt.Fprintf(abuf, "%c[7m", utils.ESC)

    fmt.Fprintf(abuf, " %s", status)
    for range e.screencols - (len(status) + 1) {
        fmt.Fprintf(abuf, " ")
    }

    fmt.Fprintf(abuf, "%c[m", utils.ESC)
}
Enter fullscreen mode Exit fullscreen mode

Display the cursor position

Let's display at the right side of the status bar the cursor's position information

File: editor/output.go

func (e *EditorConfig) editorDrawStatusBar(abuf *ab.AppendBuffer) {
    ...
    width := e.screencols - len(status) - 1

    rstatus := fmt.Sprintf("column: %d row: %d/%d ", e.rx+1, e.cy+1, e.numrows)
    ...
    for k := range width {
        if k+len(rstatus) == width {
            fmt.Fprintf(abuf, "%s", rstatus)
            break
        }
        fmt.Fprintf(abuf, " ")
    }
    ...
}
Enter fullscreen mode Exit fullscreen mode

Status message

We will now add a place where we can give user some information below the status bar

File: editor/editor.go

type EditorConfig struct {
    ...
    statusMessage string
    ...
}

func NewEditor(f func()) *EditorConfig {
    rows, cols, err := utils.GetWindowSize()
    if err != nil {
        utils.SafeExit(f, err)
    }

    return &EditorConfig{
        ...
        screenrows:    rows - 2,
        ...
        statusMessage: "",
        ...
    }
}

func (e *EditorConfig) EditorLoop() {
    ...
    e.editorSetStatusMessage("HELP: Ctrl-Q = quit")
    for {
        ...
    }
}
Enter fullscreen mode Exit fullscreen mode

File: editor/output.go

func (e *EditorConfig) editorRefreshScreen() {
    ...
    e.editorDrawRows(abuf)
    e.editorDrawStatusBar(abuf)
    e.editorDrawMessageBar(abuf)
    ...
}

func (e *EditorConfig) editorDrawStatusBar(abuf *ab.AppendBuffer) {
    ...
    fmt.Fprintf(abuf, "%c[m", utils.ESC)
    fmt.Fprintf(abuf, "\r\n")
}

func (e *EditorConfig) editorSetStatusMessage(message string) {
    e.statusMessage = message
}

func (e *EditorConfig) editorDrawMessageBar(abuf *ab.AppendBuffer) {
    fmt.Fprintf(abuf, "%c[K", utils.ESC)
    fmt.Fprintf(abuf, " %s", e.statusMessage)
}
Enter fullscreen mode Exit fullscreen mode

What we've achieved

After finishing all of the steps until now, the editor, should look something like this:

Top comments (0)