Update to chapter 7 step 161 in kilo tutorial
Some checks failed
timw4mail/gilo/pipeline/head There was a failure building this commit

This commit is contained in:
Timothy Warren 2023-10-03 17:02:22 -04:00
parent 1b9c1744aa
commit cc99f08747
12 changed files with 1569 additions and 57 deletions

1468
_code/kilo.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -19,7 +19,7 @@ type statusMsg struct {
created time.Time created time.Time
} }
type editor struct { type Editor struct {
screen *terminal.Screen screen *terminal.Screen
cursor *gilo.Point cursor *gilo.Point
offset *gilo.Point offset *gilo.Point
@ -30,7 +30,7 @@ type editor struct {
renderX int renderX int
} }
func NewEditor() *editor { func NewEditor() *Editor {
// Subtract rows for status bar and message bar/prompt // Subtract rows for status bar and message bar/prompt
screen := terminal.Size() screen := terminal.Size()
screen.Rows -= 2 screen.Rows -= 2
@ -40,7 +40,7 @@ func NewEditor() *editor {
time.Now(), time.Now(),
} }
return &editor{ return &Editor{
screen, screen,
gilo.DefaultPoint(), gilo.DefaultPoint(),
gilo.DefaultPoint(), gilo.DefaultPoint(),
@ -52,24 +52,24 @@ func NewEditor() *editor {
} }
} }
func (e *editor) Open(filename string) { func (e *Editor) Open(filename string) {
e.doc.Open(filename) e.doc.Open(filename)
} }
func (e *editor) SetStatusMessage(template string, a ...interface{}) { func (e *Editor) SetStatusMessage(template string, a ...interface{}) {
e.status = &statusMsg{ e.status = &statusMsg{
fmt.Sprintf(template, a...), fmt.Sprintf(template, a...),
time.Now(), time.Now(),
} }
} }
func (e *editor) ProcessKeypress() bool { func (e *Editor) ProcessKeypress() bool {
ch, _ := terminal.ReadKey() ch, _ := terminal.ReadKey()
return e.processKeypressChar(ch) return e.processKeypressChar(ch)
} }
func (e *editor) save() { func (e *Editor) save() {
if e.doc.Filename == "" { if e.doc.Filename == "" {
e.doc.Filename = e.prompt("Save as: %s (ESC to cancel)", nil) e.doc.Filename = e.prompt("Save as: %s (ESC to cancel)", nil)
if e.doc.Filename == "" { if e.doc.Filename == "" {
@ -86,7 +86,7 @@ func (e *editor) save() {
} }
} }
func (e *editor) prompt(prompt string, callback func(string, string)) string { func (e *Editor) prompt(prompt string, callback func(string, string)) string {
buf := gilo.NewBuffer() buf := gilo.NewBuffer()
// Show the prompt message // Show the prompt message
@ -136,7 +136,7 @@ func (e *editor) prompt(prompt string, callback func(string, string)) string {
} }
} }
func (e *editor) insertChar(ch rune) { func (e *Editor) insertChar(ch rune) {
if e.cursor.Y == e.doc.RowCount() { if e.cursor.Y == e.doc.RowCount() {
e.doc.AppendRow("") e.doc.AppendRow("")
} }
@ -145,7 +145,7 @@ func (e *editor) insertChar(ch rune) {
e.cursor.X += 1 e.cursor.X += 1
} }
func (e *editor) delChar() { func (e *Editor) delChar() {
if e.cursor.Y == e.doc.RowCount() { if e.cursor.Y == e.doc.RowCount() {
return return
} }

View File

@ -4,12 +4,14 @@ import (
"bufio" "bufio"
"log" "log"
"os" "os"
"timshome.page/gilo/editor/highlight"
"timshome.page/gilo/internal/gilo" "timshome.page/gilo/internal/gilo"
) )
type Document struct { type Document struct {
dirty bool dirty bool
Filename string Filename string
Syntax *highlight.Syntax
rows []*Row rows []*Row
} }
@ -19,6 +21,7 @@ func NewDocument() *Document {
return &Document{ return &Document{
false, false,
"", "",
nil,
rows, rows,
} }
} }
@ -80,7 +83,7 @@ func (d *Document) GetRow(at int) *Row {
} }
func (d *Document) AppendRow(s string) { func (d *Document) AppendRow(s string) {
newRow := newRow(s) newRow := newRow(d, s)
newRow.update() newRow.update()
d.rows = append(d.rows, newRow) d.rows = append(d.rows, newRow)
@ -100,7 +103,7 @@ func (d *Document) InsertRow(at int, s string) {
// Splice it back together // Splice it back together
newRows = append(newRows, start...) newRows = append(newRows, start...)
newRows = append(newRows, newRow(s)) newRows = append(newRows, newRow(d, s))
newRows = append(newRows, end...) newRows = append(newRows, end...)
d.rows = newRows d.rows = newRows

View File

@ -9,12 +9,13 @@ import (
) )
type Row struct { type Row struct {
parent *Document
chars []rune chars []rune
render []rune render []rune
Hl []int Hl []int
} }
func newRow(s string) *Row { func newRow(parent *Document, s string) *Row {
var chars []rune var chars []rune
var render []rune var render []rune
@ -23,13 +24,14 @@ func newRow(s string) *Row {
render = append(render, ch) render = append(render, ch)
} }
return &Row{chars, render, []int{}} return &Row{parent, chars, render, []int{}}
} }
func (r *Row) Size() int { func (r *Row) Size() int {
return len(r.chars) return len(r.chars)
} }
// RenderSize is a convenient equivalent of row->rsize in kilo
func (r *Row) RenderSize() int { func (r *Row) RenderSize() int {
return len(r.render) return len(r.render)
} }
@ -106,11 +108,21 @@ func (r *Row) update() {
r.updateSyntax() r.updateSyntax()
} }
// updateSyntax is the equivalent of editorUpdateSyntax in kilo
func (r *Row) updateSyntax() { func (r *Row) updateSyntax() {
i := 0 i := 0
s := r.parent.Syntax
prevSep := true prevSep := true
r.Hl = make([]int, r.RenderSize()) r.Hl = make([]int, r.RenderSize())
for x := range r.Hl {
r.Hl[x] = highlight.Normal
}
// Don't bother updating the syntax if there isn't any
if s == nil {
return
}
for i < r.RenderSize() { for i < r.RenderSize() {
ch := r.render[i] ch := r.render[i]
@ -120,14 +132,14 @@ func (r *Row) updateSyntax() {
prevHl = r.Hl[i-1] prevHl = r.Hl[i-1]
} }
if (unicode.IsDigit(ch) && (prevSep || prevHl == highlight.Number)) || if s.Flags&highlight.HighlightNumbers == 1 {
(ch == '.' && prevHl == highlight.Number) { if (unicode.IsDigit(ch) && (prevSep || prevHl == highlight.Number)) ||
r.Hl[i] = highlight.Number (ch == '.' && prevHl == highlight.Number) {
i += 1 r.Hl[i] = highlight.Number
prevSep = false i += 1
continue prevSep = false
} else { continue
r.Hl[i] = highlight.Normal }
} }
prevSep = key.IsSeparator(ch) prevSep = key.IsSeparator(ch)
@ -139,6 +151,7 @@ func (r *Row) toString() string {
return string(r.chars) return string(r.chars)
} }
// CursorXToRenderX is the equivalent of editorRowCxToRx in kilo
func (r *Row) CursorXToRenderX(cursorX int) (renderX int) { func (r *Row) CursorXToRenderX(cursorX int) (renderX int) {
renderX = 0 renderX = 0
@ -153,6 +166,7 @@ func (r *Row) CursorXToRenderX(cursorX int) (renderX int) {
return renderX return renderX
} }
// RenderXtoCursorX is the equivalent of editorRowRxToCx in kilo
func (r *Row) RenderXtoCursorX(renderX int) (cursorX int) { func (r *Row) RenderXtoCursorX(renderX int) (cursorX int) {
currentRenderX := 0 currentRenderX := 0
cursorX = 0 cursorX = 0

View File

@ -14,7 +14,7 @@ import (
// !Editor Methods // !Editor Methods
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
func (e *editor) RefreshScreen() { func (e *Editor) RefreshScreen() {
e.scroll() e.scroll()
ab := gilo.NewBuffer() ab := gilo.NewBuffer()
@ -33,7 +33,7 @@ func (e *editor) RefreshScreen() {
terminal.Write(ab.ToString()) terminal.Write(ab.ToString())
} }
func (e *editor) scroll() { func (e *Editor) scroll() {
e.renderX = 0 e.renderX = 0
if e.cursor.Y < e.doc.RowCount() { if e.cursor.Y < e.doc.RowCount() {
@ -57,7 +57,7 @@ func (e *editor) scroll() {
} }
} }
func (e *editor) drawRows(ab *gilo.Buffer) { func (e *Editor) drawRows(ab *gilo.Buffer) {
for y := 0; y < e.screen.Rows; y++ { for y := 0; y < e.screen.Rows; y++ {
fileRow := y + e.offset.Y fileRow := y + e.offset.Y
@ -81,7 +81,7 @@ func (e *editor) drawRows(ab *gilo.Buffer) {
} }
} }
func (e *editor) drawFileRow(fileRow int, ab *gilo.Buffer) { func (e *Editor) drawFileRow(fileRow int, ab *gilo.Buffer) {
currentColor := terminal.DefaultFGColor currentColor := terminal.DefaultFGColor
row := e.doc.GetRow(fileRow) row := e.doc.GetRow(fileRow)
@ -107,7 +107,7 @@ func (e *editor) drawFileRow(fileRow int, ab *gilo.Buffer) {
ab.Append(terminal.DefaultFGColor) ab.Append(terminal.DefaultFGColor)
} }
func (e *editor) drawPlaceholderRow(y int, ab *gilo.Buffer) { func (e *Editor) drawPlaceholderRow(y int, ab *gilo.Buffer) {
if e.doc.RowCount() == 0 && y == e.screen.Rows/3 { if e.doc.RowCount() == 0 && y == e.screen.Rows/3 {
welcome := fmt.Sprintf("Gilo editor -- version %s", gilo.Version) welcome := fmt.Sprintf("Gilo editor -- version %s", gilo.Version)
if len(welcome) > e.screen.Cols { if len(welcome) > e.screen.Cols {
@ -131,7 +131,7 @@ func (e *editor) drawPlaceholderRow(y int, ab *gilo.Buffer) {
} }
} }
func (e *editor) drawStatusBar(ab *gilo.Buffer) { func (e *Editor) drawStatusBar(ab *gilo.Buffer) {
cols := e.screen.Cols cols := e.screen.Cols
ab.Append(terminal.InvertColor) ab.Append(terminal.InvertColor)
@ -156,7 +156,11 @@ func (e *editor) drawStatusBar(ab *gilo.Buffer) {
return return
} }
rightStatus := fmt.Sprintf("%d/%d", e.cursor.Y+1, e.doc.RowCount()) syntaxName := "no filetype"
if e.doc.Syntax != nil {
syntaxName = e.doc.Syntax.FileType
}
rightStatus := fmt.Sprintf("%s | %d/%d", syntaxName, e.cursor.Y+1, e.doc.RowCount())
rlength := len(rightStatus) rlength := len(rightStatus)
statusLength := length + rlength statusLength := length + rlength
@ -182,7 +186,7 @@ func (e *editor) drawStatusBar(ab *gilo.Buffer) {
ab.Append(terminal.ResetColor) ab.Append(terminal.ResetColor)
} }
func (e *editor) drawMessageBar(ab *gilo.Buffer) { func (e *Editor) drawMessageBar(ab *gilo.Buffer) {
ab.Append("\r\n") ab.Append("\r\n")
ab.Append(terminal.ClearLine) ab.Append(terminal.ClearLine)

View File

@ -8,3 +8,15 @@ const (
Number Number
Match Match
) )
const HighlightNumbers = (1 << 0)
var HLDB = []*Syntax{{
"c",
[]string{".c", ".h", ".cpp"},
HighlightNumbers,
}, {
"go",
[]string{".go"},
HighlightNumbers,
}}

View File

@ -0,0 +1,7 @@
package highlight
type Syntax struct {
FileType string
FileMatch []string
Flags int
}

View File

@ -26,7 +26,7 @@ const (
/** /**
* Determine what to do with an individual character of input * Determine what to do with an individual character of input
*/ */
func (e *editor) processKeypressChar(ch rune) bool { func (e *Editor) processKeypressChar(ch rune) bool {
switch ch { switch ch {
case key.Ctrl('q'): case key.Ctrl('q'):
if e.doc.IsDirty() && e.quitTimes > 0 { if e.doc.IsDirty() && e.quitTimes > 0 {
@ -85,7 +85,7 @@ func (e *editor) processKeypressChar(ch rune) bool {
/** /**
* Determine what do do with a parsed ANSI escape sequence * Determine what do do with a parsed ANSI escape sequence
*/ */
func (e *editor) processKeypressStr(key string) { func (e *Editor) processKeypressStr(key string) {
switch key { switch key {
case keyUp, case keyUp,
keyDown, keyDown,
@ -104,7 +104,7 @@ func (e *editor) processKeypressStr(key string) {
} }
} }
func (e *editor) moveCursor(key string) { func (e *Editor) moveCursor(key string) {
var row *document.Row var row *document.Row
if e.cursor.Y >= e.doc.RowCount() { if e.cursor.Y >= e.doc.RowCount() {
row = nil row = nil

View File

@ -27,7 +27,7 @@ func TestMoveCursor(t *testing.T) {
e := NewEditor() e := NewEditor()
if test.withFile { if test.withFile {
e.Open("editor.go") e.Open("Editor.go")
} }
for _, k := range test.keys { for _, k := range test.keys {

View File

@ -7,26 +7,26 @@ import (
) )
type search struct { type search struct {
cursor *gilo.Point cursor *gilo.Point
offset *gilo.Point offset *gilo.Point
hlLine int savedhlLine int
hl []int savedhl []int
direction int direction int
lastMatch int lastMatch int
} }
func newSearch() *search { func newSearch() *search {
return &search{ return &search{
cursor: gilo.DefaultPoint(), cursor: gilo.DefaultPoint(),
offset: gilo.DefaultPoint(), offset: gilo.DefaultPoint(),
hlLine: -1, savedhlLine: -1,
hl: []int{}, savedhl: []int{},
lastMatch: -1, lastMatch: -1,
direction: 1, direction: 1,
} }
} }
func (e *editor) find() { func (e *Editor) find() {
e.search.cursor.X = e.cursor.X e.search.cursor.X = e.cursor.X
e.search.cursor.Y = e.cursor.Y e.search.cursor.Y = e.cursor.Y
e.search.offset.X = e.offset.X e.search.offset.X = e.offset.X
@ -44,14 +44,15 @@ func (e *editor) find() {
} }
} }
func (e *editor) findCallback(query string, ch string) { func (e *Editor) findCallback(query string, ch string) {
if e.search.hlLine != -1 && e.search.hl != nil { // If we highlighted a previous match, restore the original highlighting
staleRow := e.doc.GetRow(e.search.hlLine) if e.search.savedhlLine != -1 && e.search.savedhl != nil {
for i, val := range e.search.hl { staleRow := e.doc.GetRow(e.search.savedhlLine)
for i, val := range e.search.savedhl {
staleRow.Hl[i] = val staleRow.Hl[i] = val
} }
e.search.hl = nil e.search.savedhl = nil
e.search.hlLine = -1 e.search.savedhlLine = -1
} }
if ch == string(key.Enter) || ch == string(key.Esc) { if ch == string(key.Enter) || ch == string(key.Esc) {
@ -97,12 +98,14 @@ func (e *editor) findCallback(query string, ch string) {
e.cursor.X = row.RenderXtoCursorX(matchIndex) e.cursor.X = row.RenderXtoCursorX(matchIndex)
e.offset.Y = e.doc.RowCount() e.offset.Y = e.doc.RowCount()
// Update highlighting of search result // Save the current highlighting of where the search result is
e.search.hlLine = current e.search.savedhlLine = current
e.search.hl = make([]int, row.RenderSize()) e.search.savedhl = make([]int, row.RenderSize())
for i, val := range row.Hl { for i, val := range row.Hl {
e.search.hl[i] = val e.search.savedhl[i] = val
} }
// Update highlighting of search result
for x := matchIndex; x < matchIndex+len(query); x++ { for x := matchIndex; x < matchIndex+len(query); x++ {
row.Hl[x] = highlight.Match row.Hl[x] = highlight.Match
} }

View File

@ -6,6 +6,7 @@ type Point struct {
X int X int
Y int Y int
} }
// DefaultPoint creates a Point at 0,0 // DefaultPoint creates a Point at 0,0
func DefaultPoint() *Point { func DefaultPoint() *Point {
return &Point{0, 0} return &Point{0, 0}