1 package lexers
2
3 import (
4 "strings"
5
6 . "github.com/alecthomas/chroma/v2"
7 )
8
9
10 var HTTP = Register(httpBodyContentTypeLexer(MustNewLexer(
11 &Config{
12 Name: "HTTP",
13 Aliases: []string{"http"},
14 Filenames: []string{},
15 MimeTypes: []string{},
16 NotMultiline: true,
17 DotAll: true,
18 },
19 httpRules,
20 )))
21
22 func httpRules() Rules {
23 return Rules{
24 "root": {
25 {`(GET|POST|PUT|DELETE|HEAD|OPTIONS|TRACE|PATCH|CONNECT)( +)([^ ]+)( +)(HTTP)(/)([123](?:\.[01])?)(\r?\n|\Z)`, ByGroups(NameFunction, Text, NameNamespace, Text, KeywordReserved, Operator, LiteralNumber, Text), Push("headers")},
26 {`(HTTP)(/)([123](?:\.[01])?)( +)(\d{3})( *)([^\r\n]*)(\r?\n|\Z)`, ByGroups(KeywordReserved, Operator, LiteralNumber, Text, LiteralNumber, Text, NameException, Text), Push("headers")},
27 },
28 "headers": {
29 {`([^\s:]+)( *)(:)( *)([^\r\n]+)(\r?\n|\Z)`, EmitterFunc(httpHeaderBlock), nil},
30 {`([\t ]+)([^\r\n]+)(\r?\n|\Z)`, EmitterFunc(httpContinuousHeaderBlock), nil},
31 {`\r?\n`, Text, Push("content")},
32 },
33 "content": {
34 {`.+`, EmitterFunc(httpContentBlock), nil},
35 },
36 }
37 }
38
39 func httpContentBlock(groups []string, state *LexerState) Iterator {
40 tokens := []Token{
41 {Generic, groups[0]},
42 }
43 return Literator(tokens...)
44 }
45
46 func httpHeaderBlock(groups []string, state *LexerState) Iterator {
47 tokens := []Token{
48 {Name, groups[1]},
49 {Text, groups[2]},
50 {Operator, groups[3]},
51 {Text, groups[4]},
52 {Literal, groups[5]},
53 {Text, groups[6]},
54 }
55 return Literator(tokens...)
56 }
57
58 func httpContinuousHeaderBlock(groups []string, state *LexerState) Iterator {
59 tokens := []Token{
60 {Text, groups[1]},
61 {Literal, groups[2]},
62 {Text, groups[3]},
63 }
64 return Literator(tokens...)
65 }
66
67 func httpBodyContentTypeLexer(lexer Lexer) Lexer { return &httpBodyContentTyper{lexer} }
68
69 type httpBodyContentTyper struct{ Lexer }
70
71 func (d *httpBodyContentTyper) Tokenise(options *TokeniseOptions, text string) (Iterator, error) {
72 var contentType string
73 var isContentType bool
74 var subIterator Iterator
75
76 it, err := d.Lexer.Tokenise(options, text)
77 if err != nil {
78 return nil, err
79 }
80
81 return func() Token {
82 token := it()
83
84 if token == EOF {
85 if subIterator != nil {
86 return subIterator()
87 }
88 return EOF
89 }
90
91 switch {
92 case token.Type == Name && strings.ToLower(token.Value) == "content-type":
93 {
94 isContentType = true
95 }
96 case token.Type == Literal && isContentType:
97 {
98 isContentType = false
99 contentType = strings.TrimSpace(token.Value)
100 pos := strings.Index(contentType, ";")
101 if pos > 0 {
102 contentType = strings.TrimSpace(contentType[:pos])
103 }
104 }
105 case token.Type == Generic && contentType != "":
106 {
107 lexer := MatchMimeType(contentType)
108
109
110
111 if lexer == nil && strings.Contains(contentType, "+") {
112 slashPos := strings.Index(contentType, "/")
113 plusPos := strings.LastIndex(contentType, "+")
114 contentType = contentType[:slashPos+1] + contentType[plusPos+1:]
115 lexer = MatchMimeType(contentType)
116 }
117
118 if lexer == nil {
119 token.Type = Text
120 } else {
121 subIterator, err = lexer.Tokenise(nil, token.Value)
122 if err != nil {
123 panic(err)
124 }
125 return EOF
126 }
127 }
128 }
129 return token
130 }, nil
131 }
132
View as plain text