Repository: alecthomas/chroma
Pull request #815 (2023-08-18 12:39:16)

Add C lexer
This PR adds C lexer as well its analyzer as implemented in Pygments.

https://github.com/pygments/pygments/blob/master/pygments/lexers/c_cpp.py#L313



Patch:
diff --git a/lexers/c.go b/lexers/c.go
new file mode 100644
index 0000000..4ed2dd5
--- /dev/null
+++ b/lexers/c.go
@@ -0,0 +1,37 @@
+package lexers
+
+import (
+	"regexp"
+
+	. "github.com/alecthomas/chroma/v2" // nolint
+)
+
+var (
+	cAnalyserIncludeRe = regexp.MustCompile(`(?m)^\s*#include [<"]`)
+	cAnalyserIfdefRe   = regexp.MustCompile(`(?m)^\s*#ifn?def `)
+)
+
+// C lexer.
+var C = Register(MustNewXMLLexer(
+	embedded,
+	"embedded/c.xml",
+).SetConfig(
+	&Config{
+		Name:      "C",
+		Aliases:   []string{"c"},
+		Filenames: []string{"*.c", "*.h", "*.idc", "*.x[bp]m"},
+		MimeTypes: []string{"text/x-chdr", "text/x-csrc", "image/x-xbitmap", "image/x-xpixmap"},
+		EnsureNL:  true,
+		Priority:  0.1,
+	},
+).SetAnalyser(func(text string) float32 {
+	if cAnalyserIncludeRe.MatchString(text) {
+		return 0.1
+	}
+
+	if cAnalyserIfdefRe.MatchString(text) {
+		return 0.1
+	}
+
+	return 0
+}))


Test patch:
diff --git a/lexers/c_test.go b/lexers/c_test.go
new file mode 100644
index 0000000..4ef5b5d
--- /dev/null
+++ b/lexers/c_test.go
@@ -0,0 +1,44 @@
+package lexers_test
+
+import (
+	"io/ioutil"
+	"testing"
+
+	"github.com/alecthomas/chroma/v2"
+	"github.com/alecthomas/chroma/v2/lexers"
+
+	"github.com/alecthomas/assert/v2"
+)
+
+func TestC_AnalyseText(t *testing.T) {
+	tests := map[string]struct {
+		Filepath string
+		Expected float32
+	}{
+		"include": {
+			Filepath: "testdata/c_include.c",
+			Expected: 0.1,
+		},
+		"ifdef": {
+			Filepath: "testdata/c_ifdef.c",
+			Expected: 0.1,
+		},
+		"ifndef": {
+			Filepath: "testdata/c_ifndef.c",
+			Expected: 0.1,
+		},
+	}
+
+	for name, test := range tests {
+		test := test
+		t.Run(name, func(t *testing.T) {
+			data, err := ioutil.ReadFile(test.Filepath)
+			assert.NoError(t, err)
+
+			analyser, ok := lexers.C.(chroma.Analyser)
+			assert.True(t, ok)
+
+			assert.Equal(t, test.Expected, analyser.AnalyseText(string(data)))
+		})
+	}
+}
diff --git a/lexers/cpp_test.go b/lexers/cpp_test.go
index 50ee8c8..ade2743 100644
--- a/lexers/cpp_test.go
+++ b/lexers/cpp_test.go
@@ -1,14 +1,44 @@
 package lexers_test
 
 import (
+	"os"
 	"testing"
 
-	assert "github.com/alecthomas/assert/v2"
-
 	"github.com/alecthomas/chroma/v2"
 	"github.com/alecthomas/chroma/v2/lexers"
+
+	"github.com/alecthomas/assert/v2"
 )
 
+func TestCpp_AnalyseText(t *testing.T) {
+	tests := map[string]struct {
+		Filepath string
+		Expected float32
+	}{
+		"include": {
+			Filepath: "testdata/cpp_include.cpp",
+			Expected: 0.2,
+		},
+		"namespace": {
+			Filepath: "testdata/cpp_namespace.cpp",
+			Expected: 0.4,
+		},
+	}
+
+	for name, test := range tests {
+		test := test
+		t.Run(name, func(t *testing.T) {
+			data, err := os.ReadFile(test.Filepath)
+			assert.NoError(t, err)
+
+			analyser, ok := lexers.CPP.(chroma.Analyser)
+			assert.True(t, ok)
+
+			assert.Equal(t, test.Expected, analyser.AnalyseText(string(data)))
+		})
+	}
+}
+
 func TestIssue290(t *testing.T) {
 	input := `// 64-bit floats have 53 digits of precision, including the whole-number-part.
 double a =     0011111110111001100110011001100110011001100110011001100110011010; // imperfect representation of 0.1
diff --git a/lexers/testdata/c_ifdef.c b/lexers/testdata/c_ifdef.c
new file mode 100644
index 0000000..9f27815
--- /dev/null
+++ b/lexers/testdata/c_ifdef.c
@@ -0,0 +1,2 @@
+
+#ifdef DEBUG
diff --git a/lexers/testdata/c_ifndef.c b/lexers/testdata/c_ifndef.c
new file mode 100644
index 0000000..9affdff
--- /dev/null
+++ b/lexers/testdata/c_ifndef.c
@@ -0,0 +1,2 @@
+
+#ifndef DEBUG
diff --git a/lexers/testdata/c_include.c b/lexers/testdata/c_include.c
new file mode 100644
index 0000000..3b63d08
--- /dev/null
+++ b/lexers/testdata/c_include.c
@@ -0,0 +1,2 @@
+
+#include <stdio.h>
diff --git a/lexers/testdata/cpp_include.cpp b/lexers/testdata/cpp_include.cpp
new file mode 100644
index 0000000..604782e
--- /dev/null
+++ b/lexers/testdata/cpp_include.cpp
@@ -0,0 +1,1 @@
+#include <iostream>
diff --git a/lexers/testdata/cpp_namespace.cpp b/lexers/testdata/cpp_namespace.cpp
new file mode 100644
index 0000000..253f3bf
--- /dev/null
+++ b/lexers/testdata/cpp_namespace.cpp
@@ -0,0 +1,1 @@
+using namespace std;