Highlight Multiple Languages

Published: (December 1, 2025 at 09:13 PM EST)
4 min read
Source: Dev.to

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.

Back to Blog

Related posts

Read more »