Highlight Multiple Languages
Source: Dev.to
The changes explained in this post can be found in the Kilo-go GitHub repository, in the multilingual branch.
Refactor: move the syntax struct to its own package
The EditorSyntax struct is extracted into its own package so that the editor can use the methods defined there.
syntax/syntax.go
package syntax
import "github.com/alcb1310/kilo-go/utils"
var GO_HL_EXTENSIONS = []string{".go"}
var GO_HL_KEYWORDS = []string{
"package", "import", "func", "type", "var", "const", "if", "else",
"switch", "case", "default", "for", "range", "goto", "continue",
"select", "return", "break",
}
var GO_HL_TYPES = []string{
"bool", "byte", "error", "float32", "float64", "int", "int16", "int32",
"int64", "int8", "rune", "string", "uint", "uint16", "uint32", "uint64", "uint8",
}
var GO_SYNTAX = EditorSyntax{
Filetype: "go",
Filematch: GO_HL_EXTENSIONS,
Flags: utils.HL_HIGHLIGHT_NUMBER | utils.HL_HIGHLIGHT_STRING,
SingleLineComment: "//",
MultiLineCommentStart: "/*",
MultiLineCommentEnd: "*/",
Keywords: GO_HL_KEYWORDS,
Types: GO_HL_TYPES,
}
type EditorSyntax struct {
Filetype string
Filematch []string
Flags uint
SingleLineComment string
MultiLineCommentStart string
MultiLineCommentEnd string
Keywords []string
Types []string
}
// HLDB holds all supported syntaxes.
var HLDB = []EditorSyntax{
GO_SYNTAX,
// C_SYNTAX will be added later.
}
Updated references in the editor
editor/editor.go
type EditorConfig struct {
// …
syntax *syntax.EditorSyntax
}
editor/output.go
func (e *EditorConfig) editorDrawStatusBar(abuf *ab.AppendBuffer) {
// …
if e.syntax != nil {
filetype = "[" + e.syntax.Filetype + "]"
}
// …
}
editor/syntax.go
func (e *EditorConfig) editorUpdateSyntax(row *EditorRow) {
// …
scs := e.syntax.SingleLineComment
mcs := e.syntax.MultiLineCommentStart
mce := e.syntax.MultiLineCommentEnd
keywords := e.syntax.Keywords
types := e.syntax.Types
// …
if e.syntax.Flags&utils.HL_HIGHLIGHT_STRING == 2 {
// handle strings
}
if e.syntax.Flags&utils.HL_HIGHLIGHT_NUMBER == 1 {
// handle numbers
}
// …
}
Adding C/C++ support
Support for C/C++ is added by defining a new EditorSyntax value.
syntax/go.go (unchanged)
package syntax
import "github.com/alcb1310/kilo-go/utils"
var GO_HL_EXTENSIONS = []string{".go"}
var GO_HL_KEYWORDS = []string{
"package", "import", "func", "type", "var", "const", "if", "else",
"switch", "case", "default", "for", "range", "goto", "continue",
"select", "return", "break",
}
var GO_HL_TYPES = []string{
"bool", "byte", "error", "float32", "float64", "int", "int16", "int32",
"int64", "int8", "rune", "string", "uint", "uint16", "uint32", "uint64", "uint8",
}
var GO_SYNTAX = EditorSyntax{
Filetype: "go",
Filematch: GO_HL_EXTENSIONS,
Flags: utils.HL_HIGHLIGHT_NUMBER | utils.HL_HIGHLIGHT_STRING,
SingleLineComment: "//",
MultiLineCommentStart: "/*",
MultiLineCommentEnd: "*/",
Keywords: GO_HL_KEYWORDS,
Types: GO_HL_TYPES,
}
syntax/c.go
package syntax
import "github.com/alcb1310/kilo-go/utils"
var C_HL_EXTENSIONS = []string{".c", ".h", ".cpp"}
var C_SYNTAX = EditorSyntax{
Filetype: "c",
Filematch: C_HL_EXTENSIONS,
Flags: utils.HL_HIGHLIGHT_NUMBER | utils.HL_HIGHLIGHT_STRING,
SingleLineComment: "//",
MultiLineCommentStart: "/*",
MultiLineCommentEnd: "*/",
Keywords: []string{
"switch", "if", "while", "for", "break", "continue", "return", "else",
"struct", "union", "typedef", "static", "enum", "class", "case",
"#include", "#define", "#ifndef", "#ifdef", "#endif", "#else",
},
Types: []string{
"int", "long", "double", "float", "char", "unsigned", "signed", "void",
},
}
Update HLDB to include C syntax
var HLDB = []EditorSyntax{
GO_SYNTAX,
C_SYNTAX,
}
Bug fix in syntax selection
The syntax‑selection routine now iterates over all file‑match patterns for each language.
func (e *EditorConfig) editorSelectSyntaxHighlight() {
// …
for _, s := range syntax.HLDB {
for j := range s.Filematch {
isExt := s.Filematch[j][0] == '.'
if (isExt && ext == s.Filematch[j]) ||
(!isExt && strings.Contains(ext, s.Filematch[j])) {
e.syntax = &s
for _, row := range e.rows {
e.editorUpdateSyntax(&row)
}
return
}
}
}
}
Multilingual support without changing the editor
Instead of recompiling the binary for each new language, syntax definitions can be placed in the user’s configuration directory (${XDG_CONFIG}/kilo/highlight/) as TOML files. The editor loads them at startup.
${XDG_CONFIG}/kilo/highlight/go.toml
number = true
string = true
filetype = "GO"
extensions = [".go"]
keywords = [
"package", "import", "func", "type", "var", "const", "if", "else",
"switch", "case", "default", "for", "range", "goto", "continue",
"select", "return", "break",
]
types = [
"bool", "byte", "error", "float32", "float64", "int", "int16", "int32",
"nil", "int64", "int8", "rune", "string", "uint", "uint16", "uint32",
"uint64", "uint8",
]
slc = "//"
mlcs = "/*"
mlce = "*/"
${XDG_CONFIG}/kilo/highlight/c.toml
number = true
string = true
filetype = "C/CPP"
extensions = [".c", ".h", ".cpp"]
keywords = [
"switch", "if", "while", "for", "break", "continue", "return", "else",
"struct", "union", "typedef", "static", "enum", "class", "case",
"#include", "#define", "#ifndef", "#ifdef", "#endif", "#else"
]
types = ["int", "long", "double", "float", "char", "unsigned", "signed", "void", "NULL"]
slc = "//"
mlcs = "/*"
mlce = "*/"
Loading syntax definitions
func init() {
// …
syntax.LoadSyntax() // reads all *.toml files from ${XDG_CONFIG}/kilo/highlight/
}
With this approach, adding a new language only requires dropping a correctly‑formatted TOML file into the highlight directory—no changes to the editor’s source code are needed.