From 43641c8c9cd721978b8e28b549750dea548f4b98 Mon Sep 17 00:00:00 2001 From: Owen Voke Date: Mon, 29 Jun 2026 10:01:51 +0100 Subject: [PATCH] feat: add support for HTTP language --- src/Highlighter.php | 2 + src/Languages/Http/HttpLanguage.php | 51 ++++++++++++ .../Http/Patterns/HttpAttributePattern.php | 27 +++++++ .../Http/Patterns/HttpCommentPattern.php | 28 +++++++ .../Http/Patterns/HttpMethodPattern.php | 28 +++++++ .../Http/Patterns/HttpStatusPattern.php | 30 +++++++ .../Http/Patterns/HttpUrlPattern.php | 27 +++++++ .../Http/Patterns/HttpVersionPattern.php | 28 +++++++ tests/Bench/Fixtures/http.txt | 12 +++ tests/Bench/HighlighterBench.php | 1 + tests/Languages/Http/HttpLanguageTest.php | 80 +++++++++++++++++++ tests/targets/http.md | 11 +++ 12 files changed, 325 insertions(+) create mode 100644 src/Languages/Http/HttpLanguage.php create mode 100644 src/Languages/Http/Patterns/HttpAttributePattern.php create mode 100644 src/Languages/Http/Patterns/HttpCommentPattern.php create mode 100644 src/Languages/Http/Patterns/HttpMethodPattern.php create mode 100644 src/Languages/Http/Patterns/HttpStatusPattern.php create mode 100644 src/Languages/Http/Patterns/HttpUrlPattern.php create mode 100644 src/Languages/Http/Patterns/HttpVersionPattern.php create mode 100644 tests/Bench/Fixtures/http.txt create mode 100644 tests/Languages/Http/HttpLanguageTest.php create mode 100644 tests/targets/http.md diff --git a/src/Highlighter.php b/src/Highlighter.php index 1961d6b..6c2236c 100644 --- a/src/Highlighter.php +++ b/src/Highlighter.php @@ -18,6 +18,7 @@ use Tempest\Highlight\Languages\Gdscript\GdscriptLanguage; use Tempest\Highlight\Languages\Graphql\GraphqlLanguage; use Tempest\Highlight\Languages\Html\HtmlLanguage; +use Tempest\Highlight\Languages\Http\HttpLanguage; use Tempest\Highlight\Languages\Ini\IniLanguage; use Tempest\Highlight\Languages\JavaScript\JavaScriptLanguage; use Tempest\Highlight\Languages\Json\JsonLanguage; @@ -73,6 +74,7 @@ public function __construct( ->addLanguage(new GdscriptLanguage()) ->addLanguage(new GraphqlLanguage()) ->addLanguage(new HtmlLanguage()) + ->addLanguage(new HttpLanguage()) ->addLanguage(new JavaScriptLanguage()) ->addLanguage(new JsonLanguage()) ->addLanguage(new MarkdownLanguage()) diff --git a/src/Languages/Http/HttpLanguage.php b/src/Languages/Http/HttpLanguage.php new file mode 100644 index 0000000..bb806bf --- /dev/null +++ b/src/Languages/Http/HttpLanguage.php @@ -0,0 +1,51 @@ +(?m)^[A-Za-z][A-Za-z0-9-]*(?=\:))'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::ATTRIBUTE; + } +} diff --git a/src/Languages/Http/Patterns/HttpCommentPattern.php b/src/Languages/Http/Patterns/HttpCommentPattern.php new file mode 100644 index 0000000..f283ce5 --- /dev/null +++ b/src/Languages/Http/Patterns/HttpCommentPattern.php @@ -0,0 +1,28 @@ +(?m)^(\/\/|\#).*)/'; + } + + public function getTokenType(): TokenType + { + return TokenTypeEnum::COMMENT; + } +} diff --git a/src/Languages/Http/Patterns/HttpMethodPattern.php b/src/Languages/Http/Patterns/HttpMethodPattern.php new file mode 100644 index 0000000..cbc3ddd --- /dev/null +++ b/src/Languages/Http/Patterns/HttpMethodPattern.php @@ -0,0 +1,28 @@ +(?m)^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS|CONNECT|TRACE)\b)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::KEYWORD; + } +} diff --git a/src/Languages/Http/Patterns/HttpStatusPattern.php b/src/Languages/Http/Patterns/HttpStatusPattern.php new file mode 100644 index 0000000..7e6ccf1 --- /dev/null +++ b/src/Languages/Http/Patterns/HttpStatusPattern.php @@ -0,0 +1,30 @@ +\d{3})/'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::NUMBER; + } +} diff --git a/src/Languages/Http/Patterns/HttpUrlPattern.php b/src/Languages/Http/Patterns/HttpUrlPattern.php new file mode 100644 index 0000000..db87aa0 --- /dev/null +++ b/src/Languages/Http/Patterns/HttpUrlPattern.php @@ -0,0 +1,27 @@ +\/[^\s]*|\w+:\/\/[^\s]*)(?=\sHTTP)'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::VALUE; + } +} diff --git a/src/Languages/Http/Patterns/HttpVersionPattern.php b/src/Languages/Http/Patterns/HttpVersionPattern.php new file mode 100644 index 0000000..edbf016 --- /dev/null +++ b/src/Languages/Http/Patterns/HttpVersionPattern.php @@ -0,0 +1,28 @@ +HTTP\/([32]|1\.[01]))'; + } + + public function getTokenType(): TokenTypeEnum + { + return TokenTypeEnum::PROPERTY; + } +} diff --git a/tests/Bench/Fixtures/http.txt b/tests/Bench/Fixtures/http.txt new file mode 100644 index 0000000..6b6445d --- /dev/null +++ b/tests/Bench/Fixtures/http.txt @@ -0,0 +1,12 @@ +### Get all users +GET https://api.example.com/users +Accept: application/json + +### Create a new user +POST https://api.example.com/users +Content-Type: application/json + +{ + "name": "Jane Doe", + "email": "jane@example.com" +} diff --git a/tests/Bench/HighlighterBench.php b/tests/Bench/HighlighterBench.php index 30c17bd..f44f669 100644 --- a/tests/Bench/HighlighterBench.php +++ b/tests/Bench/HighlighterBench.php @@ -28,6 +28,7 @@ final class HighlighterBench 'gdscript' => 'gdscript.txt', 'graphql' => 'graphql.txt', 'html' => 'html.txt', + 'http' => 'http.txt', 'ini' => 'ini.txt', 'javascript' => 'javascript.txt', 'json' => 'json.txt', diff --git a/tests/Languages/Http/HttpLanguageTest.php b/tests/Languages/Http/HttpLanguageTest.php new file mode 100644 index 0000000..400d1c2 --- /dev/null +++ b/tests/Languages/Http/HttpLanguageTest.php @@ -0,0 +1,80 @@ +addLanguage(new HttpLanguage()); + + $this->assertSame( + trim($expected), + trim($highlighter->parse($content, 'http')) + ); + } + + public static function provide_highlighting_cases(): iterable + { + return [ + // Standard Request Line + [ + 'GET /index.html HTTP/1.1', + 'GET /index.html HTTP/1.1', + ], + // Request with absolute URL + [ + 'POST https://api.tempest.php/v1/data HTTP/2', + 'POST https://api.tempest.php/v1/data HTTP/2', + ], + // Response Line + [ + 'HTTP/1.1 200 OK', + 'HTTP/1.1 200 OK', + ], + // Headers + [ + 'Host: localhost +Content-Type: application/json +X-Custom-Header: value', + 'Host: localhost +Content-Type: application/json +X-Custom-Header: value', + ], + // Full Request Example + [ + "DELETE /user/1 HTTP/1.1\nHost: api.com\nAccept: */*", + "DELETE /user/1 HTTP/1.1\nHost: api.com\nAccept: */*", + ], + // Error Response + [ + 'HTTP/2 404 Not Found', + 'HTTP/2 404 Not Found', + ], + // HTTP/3 response (highlight.js http3 fixture) + [ + 'HTTP/3 200', + 'HTTP/3 200', + ], + // Three-digit header values must NOT be highlighted as a status code + [ + "HTTP/2 301\ncontent-length: 220", + "HTTP/2 301\ncontent-length: 220", + ], + // Comments / request separators + [ + '### Get users', + '### Get users', + ], + ]; + } +} diff --git a/tests/targets/http.md b/tests/targets/http.md new file mode 100644 index 0000000..1c7067f --- /dev/null +++ b/tests/targets/http.md @@ -0,0 +1,11 @@ +```http +### Get users +POST https://api.example.com/users HTTP/1.1 +Content-Type: application/json + +HTTP/1.1 202 Accepted +{ + "name": "Jane Doe", + "email": "jane@example.com" +} +```