diff --git a/src/Highlighter.php b/src/Highlighter.php
index 843cf5b6..f5bdcbb0 100644
--- a/src/Highlighter.php
+++ b/src/Highlighter.php
@@ -16,6 +16,7 @@
use Tempest\Highlight\Languages\Dockerfile\DockerfileLanguage;
use Tempest\Highlight\Languages\DotEnv\DotEnvLanguage;
use Tempest\Highlight\Languages\Gdscript\GdscriptLanguage;
+use Tempest\Highlight\Languages\Go\GoLanguage;
use Tempest\Highlight\Languages\Graphql\GraphqlLanguage;
use Tempest\Highlight\Languages\Html\HtmlLanguage;
use Tempest\Highlight\Languages\Http\HttpLanguage;
@@ -77,6 +78,7 @@ public function __construct(
->addLanguage(new DocCommentLanguage())
->addLanguage(new DockerfileLanguage())
->addLanguage(new GdscriptLanguage())
+ ->addLanguage(new GoLanguage())
->addLanguage(new GraphqlLanguage())
->addLanguage(new HtmlLanguage())
->addLanguage(new HttpLanguage())
diff --git a/src/Languages/Go/GoLanguage.php b/src/Languages/Go/GoLanguage.php
new file mode 100644
index 00000000..5d9984e3
--- /dev/null
+++ b/src/Languages/Go/GoLanguage.php
@@ -0,0 +1,52 @@
+\/\/[^\r\n]*|\/\*[^*]*(?:\*(?!\/)[^*]*)*\*\/)/s';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::COMMENT;
+ }
+}
diff --git a/src/Languages/Go/Patterns/GoFunctionPattern.php b/src/Languages/Go/Patterns/GoFunctionPattern.php
new file mode 100644
index 00000000..b33bb6df
--- /dev/null
+++ b/src/Languages/Go/Patterns/GoFunctionPattern.php
@@ -0,0 +1,27 @@
+[A-Za-z_]\w*)(?=\s*(?:\[[^\[\]\r\n]*\]\s*)?\()';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::PROPERTY;
+ }
+}
diff --git a/src/Languages/Go/Patterns/GoKeywordPattern.php b/src/Languages/Go/Patterns/GoKeywordPattern.php
new file mode 100644
index 00000000..bfbd56a7
--- /dev/null
+++ b/src/Languages/Go/Patterns/GoKeywordPattern.php
@@ -0,0 +1,27 @@
+break|case|chan|const|continue|default|defer|else|fallthrough|for|func|go|goto|if|import|interface|map|package|range|return|select|struct|switch|type|var)\b';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::KEYWORD;
+ }
+}
diff --git a/src/Languages/Go/Patterns/GoLiteralPattern.php b/src/Languages/Go/Patterns/GoLiteralPattern.php
new file mode 100644
index 00000000..cff01268
--- /dev/null
+++ b/src/Languages/Go/Patterns/GoLiteralPattern.php
@@ -0,0 +1,27 @@
+false|iota|nil|true)\b';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::LITERAL;
+ }
+}
diff --git a/src/Languages/Go/Patterns/GoNumberPattern.php b/src/Languages/Go/Patterns/GoNumberPattern.php
new file mode 100644
index 00000000..38d65975
--- /dev/null
+++ b/src/Languages/Go/Patterns/GoNumberPattern.php
@@ -0,0 +1,27 @@
+(?:0[xX][\dA-Fa-f](?:_?[\dA-Fa-f])*(?:\.[\dA-Fa-f](?:_?[\dA-Fa-f])*)?(?:[pP][+-]?\d(?:_?\d)*)?|0[bB][01](?:_?[01])*|0[oO][0-7](?:_?[0-7])*|\d(?:_?\d)*(?:\.\d(?:_?\d)*)?(?:[eE][+-]?\d(?:_?\d)*)?)(?:i)?)(?![\w.])/';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::NUMBER;
+ }
+}
diff --git a/src/Languages/Go/Patterns/GoOperatorPattern.php b/src/Languages/Go/Patterns/GoOperatorPattern.php
new file mode 100644
index 00000000..386b65c7
--- /dev/null
+++ b/src/Languages/Go/Patterns/GoOperatorPattern.php
@@ -0,0 +1,27 @@
+<-|:=|\+\+|--|<<=?|>>=?|&\^=?|&&|\|\||==|!=|<=|>=|\.\.\.|[-+*\/%&|^!<>=]=?|[~.,:;{}()\[\]])/';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::OPERATOR;
+ }
+}
diff --git a/src/Languages/Go/Patterns/GoStringPattern.php b/src/Languages/Go/Patterns/GoStringPattern.php
new file mode 100644
index 00000000..6c664f77
--- /dev/null
+++ b/src/Languages/Go/Patterns/GoStringPattern.php
@@ -0,0 +1,28 @@
+`[^`]*`|"(?:\\\\.|[^"\\\\\r\n])*"|\'(?:\\\\.|[^\'\\\\\r\n])+\')/';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::VALUE;
+ }
+}
diff --git a/src/Languages/Go/Patterns/GoTypePattern.php b/src/Languages/Go/Patterns/GoTypePattern.php
new file mode 100644
index 00000000..22d051ed
--- /dev/null
+++ b/src/Languages/Go/Patterns/GoTypePattern.php
@@ -0,0 +1,27 @@
+(?<=\btype\s)[A-Za-z_]\w*|\b(?:any|bool|byte|comparable|complex64|complex128|error|float32|float64|int|int8|int16|int32|int64|rune|string|uint|uint8|uint16|uint32|uint64|uintptr)\b)';
+ }
+
+ public function getTokenType(): TokenTypeEnum
+ {
+ return TokenTypeEnum::TYPE;
+ }
+}
diff --git a/tests/Bench/Fixtures/go.txt b/tests/Bench/Fixtures/go.txt
new file mode 100644
index 00000000..7b2c5599
--- /dev/null
+++ b/tests/Bench/Fixtures/go.txt
@@ -0,0 +1,26 @@
+package main
+
+import (
+ "context"
+ "fmt"
+)
+
+type Result[T any] struct {
+ Value T
+ Err error
+}
+
+func collect[T comparable](ctx context.Context, input <-chan T) []Result[T] {
+ results := make([]Result[T], 0, 16)
+ for value := range input {
+ select {
+ case <-ctx.Done():
+ return append(results, Result[T]{Err: ctx.Err()})
+ default:
+ results = append(results, Result[T]{Value: value})
+ }
+ }
+
+ fmt.Printf("collected %d values\n", len(results))
+ return results
+}
diff --git a/tests/Bench/HighlighterBench.php b/tests/Bench/HighlighterBench.php
index 154b03c3..316c8635 100644
--- a/tests/Bench/HighlighterBench.php
+++ b/tests/Bench/HighlighterBench.php
@@ -26,6 +26,7 @@ final class HighlighterBench
'dockerfile' => 'dockerfile.txt',
'dotenv' => 'dotenv.txt',
'gdscript' => 'gdscript.txt',
+ 'go' => 'go.txt',
'graphql' => 'graphql.txt',
'html' => 'html.txt',
'http' => 'http.txt',
diff --git a/tests/Languages/Go/GoLanguageTest.php b/tests/Languages/Go/GoLanguageTest.php
new file mode 100644
index 00000000..f387bed8
--- /dev/null
+++ b/tests/Languages/Go/GoLanguageTest.php
@@ -0,0 +1,103 @@
+assertContains('go', $highlighter->getSupportedLanguageNames());
+ $this->assertContains('golang', $highlighter->getSupportedLanguageNames());
+
+ foreach (['go', 'golang'] as $name) {
+ $highlighter->parse('', $name);
+
+ $this->assertInstanceOf(GoLanguage::class, $highlighter->getCurrentLanguage());
+ }
+ }
+
+ #[Test]
+ #[DataProvider('provide_it_highlights_representative_syntax_cases')]
+ public function it_highlights_representative_syntax(string $source, string $expected): void
+ {
+ $highlighted = (new Highlighter())->parse($source, 'go');
+
+ $this->assertStringContainsString($expected, $highlighted);
+ }
+
+ public static function provide_it_highlights_representative_syntax_cases(): iterable
+ {
+ yield 'line comment' => ['// note', ''];
+ yield 'block comment' => ['/* note */', ''];
+ yield 'import' => ['import "fmt"', 'import'];
+ yield 'import path' => ['import "fmt"', '"fmt"'];
+ yield 'string' => ['"hello\\n"', '"hello\\n"'];
+ yield 'raw string' => ['`hello`', '`hello`'];
+ yield 'rune' => ["'λ'", '\'λ\''];
+ yield 'hexadecimal float' => ['0x1.fp2', '0x1.fp2'];
+ yield 'imaginary number' => ['2i', '2i'];
+ yield 'keyword' => ['defer', 'defer'];
+ yield 'built-in type' => ['string', 'string'];
+ yield 'constant' => ['nil', 'nil'];
+ yield 'built-in function' => ['append(values, value)', 'append'];
+ yield 'declared type' => ['type Result struct{}', 'Result'];
+ yield 'declared function' => ['func render() {}', 'render'];
+ yield 'operator' => ['value <- input', '<-'];
+ yield 'punctuation' => ['map[string]int{}', '{'];
+ }
+
+ #[Test]
+ public function it_escapes_html_and_strips_reserved_markers(): void
+ {
+ $source = "package main\r\n"
+ . "var raw = `<©> ❷img src=x onerror=alert(1)❸ ❶❹❿`\r"
+ . "var script = \"\"\r\n";
+
+ $highlighted = (new Highlighter())->parse($source, 'go');
+ $renderedSource = html_entity_decode(strip_tags($highlighted), ENT_QUOTES | ENT_HTML5);
+
+ $expectedSource = str_replace(["\r\n", "\r"], "\n", str_replace(['❶', '❷', '❸', '❹', '❿'], '', $source));
+
+ $this->assertSame($expectedSource, $renderedSource);
+ $this->assertStringNotContainsString('