feat(sshd_config): Add match completions; Bugfixes

This commit is contained in:
Myzel394 2024-09-15 15:33:54 +02:00
parent 6911511b51
commit 9b1a5af2d0
No known key found for this signature in database
GPG Key ID: DEC4AAB876F73185
22 changed files with 825 additions and 356 deletions

View File

@ -1,7 +1,7 @@
grammar Config;
lineStatement
: (entry | (WHITESPACE? leadingComment) | WHITESPACE?) EOF
: (entry | (leadingComment) | WHITESPACE?) EOF
;
entry
@ -17,7 +17,7 @@ key
;
value
: (STRING WHITESPACE)? STRING WHITESPACE?
: (STRING WHITESPACE)* STRING? WHITESPACE?
;
leadingComment

View File

@ -95,8 +95,15 @@ func (s *sshParserListener) ExitEntry(ctx *parser.EntryContext) {
if s.sshContext.isKeyAMatchBlock {
// Add new match block
match := match_parser.NewMatch()
errors := match.Parse(s.sshContext.currentOption.OptionValue.Value, location.Start.Line)
var match *match_parser.Match
if s.sshContext.currentOption.OptionValue != nil {
matchParser := match_parser.NewMatch()
errors := matchParser.Parse(
s.sshContext.currentOption.OptionValue.Value,
location.Start.Line,
s.sshContext.currentOption.OptionValue.Start.Character,
)
if len(errors) > 0 {
for _, err := range errors {
@ -106,6 +113,10 @@ func (s *sshParserListener) ExitEntry(ctx *parser.EntryContext) {
})
}
} else {
match = matchParser
}
}
matchBlock := &SSHMatchBlock{
LocationRange: location,
MatchEntry: s.sshContext.currentOption,
@ -118,7 +129,6 @@ func (s *sshParserListener) ExitEntry(ctx *parser.EntryContext) {
)
s.sshContext.currentMatchBlock = matchBlock
}
s.sshContext.isKeyAMatchBlock = false
} else if s.sshContext.currentMatchBlock != nil {

View File

@ -22,4 +22,4 @@ leadingComment
atn:
[4, 1, 4, 64, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 1, 0, 1, 0, 3, 0, 15, 8, 0, 1, 0, 1, 0, 3, 0, 19, 8, 0, 3, 0, 21, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 26, 8, 1, 1, 1, 3, 1, 29, 8, 1, 1, 1, 3, 1, 32, 8, 1, 1, 1, 3, 1, 35, 8, 1, 1, 1, 3, 1, 38, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 3, 4, 46, 8, 4, 1, 4, 1, 4, 3, 4, 50, 8, 4, 1, 5, 1, 5, 3, 5, 54, 8, 5, 1, 5, 1, 5, 3, 5, 58, 8, 5, 4, 5, 60, 8, 5, 11, 5, 12, 5, 61, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10, 0, 0, 71, 0, 20, 1, 0, 0, 0, 2, 25, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 41, 1, 0, 0, 0, 8, 45, 1, 0, 0, 0, 10, 51, 1, 0, 0, 0, 12, 21, 3, 2, 1, 0, 13, 15, 5, 2, 0, 0, 14, 13, 1, 0, 0, 0, 14, 15, 1, 0, 0, 0, 15, 16, 1, 0, 0, 0, 16, 21, 3, 10, 5, 0, 17, 19, 5, 2, 0, 0, 18, 17, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 21, 1, 0, 0, 0, 20, 12, 1, 0, 0, 0, 20, 14, 1, 0, 0, 0, 20, 18, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 23, 5, 0, 0, 1, 23, 1, 1, 0, 0, 0, 24, 26, 5, 2, 0, 0, 25, 24, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 28, 1, 0, 0, 0, 27, 29, 3, 6, 3, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, 0, 0, 0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 4, 2, 0, 31, 30, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 34, 1, 0, 0, 0, 33, 35, 3, 8, 4, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 37, 1, 0, 0, 0, 36, 38, 3, 10, 5, 0, 37, 36, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 3, 1, 0, 0, 0, 39, 40, 5, 2, 0, 0, 40, 5, 1, 0, 0, 0, 41, 42, 5, 3, 0, 0, 42, 7, 1, 0, 0, 0, 43, 44, 5, 3, 0, 0, 44, 46, 5, 2, 0, 0, 45, 43, 1, 0, 0, 0, 45, 46, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 49, 5, 3, 0, 0, 48, 50, 5, 2, 0, 0, 49, 48, 1, 0, 0, 0, 49, 50, 1, 0, 0, 0, 50, 9, 1, 0, 0, 0, 51, 53, 5, 1, 0, 0, 52, 54, 5, 2, 0, 0, 53, 52, 1, 0, 0, 0, 53, 54, 1, 0, 0, 0, 54, 59, 1, 0, 0, 0, 55, 57, 5, 3, 0, 0, 56, 58, 5, 2, 0, 0, 57, 56, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 60, 1, 0, 0, 0, 59, 55, 1, 0, 0, 0, 60, 61, 1, 0, 0, 0, 61, 59, 1, 0, 0, 0, 61, 62, 1, 0, 0, 0, 62, 11, 1, 0, 0, 0, 13, 14, 18, 20, 25, 28, 31, 34, 37, 45, 49, 53, 57, 61]
[4, 1, 4, 66, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 1, 0, 1, 0, 1, 0, 3, 0, 16, 8, 0, 3, 0, 18, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 23, 8, 1, 1, 1, 3, 1, 26, 8, 1, 1, 1, 3, 1, 29, 8, 1, 1, 1, 3, 1, 32, 8, 1, 1, 1, 3, 1, 35, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 5, 4, 43, 8, 4, 10, 4, 12, 4, 46, 9, 4, 1, 4, 3, 4, 49, 8, 4, 1, 4, 3, 4, 52, 8, 4, 1, 5, 1, 5, 3, 5, 56, 8, 5, 1, 5, 1, 5, 3, 5, 60, 8, 5, 4, 5, 62, 8, 5, 11, 5, 12, 5, 63, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10, 0, 0, 73, 0, 17, 1, 0, 0, 0, 2, 22, 1, 0, 0, 0, 4, 36, 1, 0, 0, 0, 6, 38, 1, 0, 0, 0, 8, 44, 1, 0, 0, 0, 10, 53, 1, 0, 0, 0, 12, 18, 3, 2, 1, 0, 13, 18, 3, 10, 5, 0, 14, 16, 5, 2, 0, 0, 15, 14, 1, 0, 0, 0, 15, 16, 1, 0, 0, 0, 16, 18, 1, 0, 0, 0, 17, 12, 1, 0, 0, 0, 17, 13, 1, 0, 0, 0, 17, 15, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 20, 5, 0, 0, 1, 20, 1, 1, 0, 0, 0, 21, 23, 5, 2, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 25, 1, 0, 0, 0, 24, 26, 3, 6, 3, 0, 25, 24, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 28, 1, 0, 0, 0, 27, 29, 3, 4, 2, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, 0, 0, 0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 8, 4, 0, 31, 30, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 34, 1, 0, 0, 0, 33, 35, 3, 10, 5, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 3, 1, 0, 0, 0, 36, 37, 5, 2, 0, 0, 37, 5, 1, 0, 0, 0, 38, 39, 5, 3, 0, 0, 39, 7, 1, 0, 0, 0, 40, 41, 5, 3, 0, 0, 41, 43, 5, 2, 0, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 48, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 49, 5, 3, 0, 0, 48, 47, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 51, 1, 0, 0, 0, 50, 52, 5, 2, 0, 0, 51, 50, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 9, 1, 0, 0, 0, 53, 55, 5, 1, 0, 0, 54, 56, 5, 2, 0, 0, 55, 54, 1, 0, 0, 0, 55, 56, 1, 0, 0, 0, 56, 61, 1, 0, 0, 0, 57, 59, 5, 3, 0, 0, 58, 60, 5, 2, 0, 0, 59, 58, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 62, 1, 0, 0, 0, 61, 57, 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 61, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0, 64, 11, 1, 0, 0, 0, 13, 15, 17, 22, 25, 28, 31, 34, 44, 48, 51, 55, 59, 63]

View File

@ -43,34 +43,35 @@ func configParserInit() {
}
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
4, 1, 4, 64, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4,
2, 5, 7, 5, 1, 0, 1, 0, 3, 0, 15, 8, 0, 1, 0, 1, 0, 3, 0, 19, 8, 0, 3,
0, 21, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 26, 8, 1, 1, 1, 3, 1, 29, 8, 1, 1,
1, 3, 1, 32, 8, 1, 1, 1, 3, 1, 35, 8, 1, 1, 1, 3, 1, 38, 8, 1, 1, 2, 1,
2, 1, 3, 1, 3, 1, 4, 1, 4, 3, 4, 46, 8, 4, 1, 4, 1, 4, 3, 4, 50, 8, 4,
1, 5, 1, 5, 3, 5, 54, 8, 5, 1, 5, 1, 5, 3, 5, 58, 8, 5, 4, 5, 60, 8, 5,
11, 5, 12, 5, 61, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10, 0, 0, 71, 0, 20, 1,
0, 0, 0, 2, 25, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 41, 1, 0, 0, 0, 8, 45,
1, 0, 0, 0, 10, 51, 1, 0, 0, 0, 12, 21, 3, 2, 1, 0, 13, 15, 5, 2, 0, 0,
14, 13, 1, 0, 0, 0, 14, 15, 1, 0, 0, 0, 15, 16, 1, 0, 0, 0, 16, 21, 3,
10, 5, 0, 17, 19, 5, 2, 0, 0, 18, 17, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19,
21, 1, 0, 0, 0, 20, 12, 1, 0, 0, 0, 20, 14, 1, 0, 0, 0, 20, 18, 1, 0, 0,
0, 21, 22, 1, 0, 0, 0, 22, 23, 5, 0, 0, 1, 23, 1, 1, 0, 0, 0, 24, 26, 5,
2, 0, 0, 25, 24, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 28, 1, 0, 0, 0, 27,
29, 3, 6, 3, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, 0, 0, 0, 29, 31, 1, 0, 0,
0, 30, 32, 3, 4, 2, 0, 31, 30, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 34,
1, 0, 0, 0, 33, 35, 3, 8, 4, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0,
35, 37, 1, 0, 0, 0, 36, 38, 3, 10, 5, 0, 37, 36, 1, 0, 0, 0, 37, 38, 1,
0, 0, 0, 38, 3, 1, 0, 0, 0, 39, 40, 5, 2, 0, 0, 40, 5, 1, 0, 0, 0, 41,
42, 5, 3, 0, 0, 42, 7, 1, 0, 0, 0, 43, 44, 5, 3, 0, 0, 44, 46, 5, 2, 0,
0, 45, 43, 1, 0, 0, 0, 45, 46, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 49,
5, 3, 0, 0, 48, 50, 5, 2, 0, 0, 49, 48, 1, 0, 0, 0, 49, 50, 1, 0, 0, 0,
50, 9, 1, 0, 0, 0, 51, 53, 5, 1, 0, 0, 52, 54, 5, 2, 0, 0, 53, 52, 1, 0,
0, 0, 53, 54, 1, 0, 0, 0, 54, 59, 1, 0, 0, 0, 55, 57, 5, 3, 0, 0, 56, 58,
5, 2, 0, 0, 57, 56, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 60, 1, 0, 0, 0,
59, 55, 1, 0, 0, 0, 60, 61, 1, 0, 0, 0, 61, 59, 1, 0, 0, 0, 61, 62, 1,
0, 0, 0, 62, 11, 1, 0, 0, 0, 13, 14, 18, 20, 25, 28, 31, 34, 37, 45, 49,
53, 57, 61,
4, 1, 4, 66, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4,
2, 5, 7, 5, 1, 0, 1, 0, 1, 0, 3, 0, 16, 8, 0, 3, 0, 18, 8, 0, 1, 0, 1,
0, 1, 1, 3, 1, 23, 8, 1, 1, 1, 3, 1, 26, 8, 1, 1, 1, 3, 1, 29, 8, 1, 1,
1, 3, 1, 32, 8, 1, 1, 1, 3, 1, 35, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4,
1, 4, 5, 4, 43, 8, 4, 10, 4, 12, 4, 46, 9, 4, 1, 4, 3, 4, 49, 8, 4, 1,
4, 3, 4, 52, 8, 4, 1, 5, 1, 5, 3, 5, 56, 8, 5, 1, 5, 1, 5, 3, 5, 60, 8,
5, 4, 5, 62, 8, 5, 11, 5, 12, 5, 63, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10,
0, 0, 73, 0, 17, 1, 0, 0, 0, 2, 22, 1, 0, 0, 0, 4, 36, 1, 0, 0, 0, 6, 38,
1, 0, 0, 0, 8, 44, 1, 0, 0, 0, 10, 53, 1, 0, 0, 0, 12, 18, 3, 2, 1, 0,
13, 18, 3, 10, 5, 0, 14, 16, 5, 2, 0, 0, 15, 14, 1, 0, 0, 0, 15, 16, 1,
0, 0, 0, 16, 18, 1, 0, 0, 0, 17, 12, 1, 0, 0, 0, 17, 13, 1, 0, 0, 0, 17,
15, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 20, 5, 0, 0, 1, 20, 1, 1, 0, 0,
0, 21, 23, 5, 2, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 25,
1, 0, 0, 0, 24, 26, 3, 6, 3, 0, 25, 24, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0,
26, 28, 1, 0, 0, 0, 27, 29, 3, 4, 2, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1,
0, 0, 0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 8, 4, 0, 31, 30, 1, 0, 0, 0, 31,
32, 1, 0, 0, 0, 32, 34, 1, 0, 0, 0, 33, 35, 3, 10, 5, 0, 34, 33, 1, 0,
0, 0, 34, 35, 1, 0, 0, 0, 35, 3, 1, 0, 0, 0, 36, 37, 5, 2, 0, 0, 37, 5,
1, 0, 0, 0, 38, 39, 5, 3, 0, 0, 39, 7, 1, 0, 0, 0, 40, 41, 5, 3, 0, 0,
41, 43, 5, 2, 0, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1,
0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 48, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47,
49, 5, 3, 0, 0, 48, 47, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 51, 1, 0, 0,
0, 50, 52, 5, 2, 0, 0, 51, 50, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 9, 1,
0, 0, 0, 53, 55, 5, 1, 0, 0, 54, 56, 5, 2, 0, 0, 55, 54, 1, 0, 0, 0, 55,
56, 1, 0, 0, 0, 56, 61, 1, 0, 0, 0, 57, 59, 5, 3, 0, 0, 58, 60, 5, 2, 0,
0, 59, 58, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 62, 1, 0, 0, 0, 61, 57,
1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 61, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0,
64, 11, 1, 0, 0, 0, 13, 15, 17, 22, 25, 28, 31, 34, 44, 48, 51, 55, 59,
63,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
@ -240,13 +241,13 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) {
var _la int
p.EnterOuterAlt(localctx, 1)
p.SetState(20)
p.SetState(17)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 2, p.GetParserRuleContext()) {
switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 1, p.GetParserRuleContext()) {
case 1:
{
p.SetState(12)
@ -254,31 +255,13 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) {
}
case 2:
p.SetState(14)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == ConfigParserWHITESPACE {
{
p.SetState(13)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
{
p.SetState(16)
p.LeadingComment()
}
case 3:
p.SetState(18)
p.SetState(15)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -287,7 +270,7 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) {
if _la == ConfigParserWHITESPACE {
{
p.SetState(17)
p.SetState(14)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
@ -301,7 +284,7 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) {
goto errorExit
}
{
p.SetState(22)
p.SetState(19)
p.Match(ConfigParserEOF)
if p.HasError() {
// Recognition error - abort rule
@ -466,17 +449,29 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) {
var _la int
p.EnterOuterAlt(localctx, 1)
p.SetState(22)
p.GetErrorHandler().Sync(p)
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 2, p.GetParserRuleContext()) == 1 {
{
p.SetState(21)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
} else if p.HasError() { // JIM
goto errorExit
}
p.SetState(25)
p.GetErrorHandler().Sync(p)
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 {
{
p.SetState(24)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
p.Key()
}
} else if p.HasError() { // JIM
@ -488,7 +483,7 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) {
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 4, p.GetParserRuleContext()) == 1 {
{
p.SetState(27)
p.Key()
p.Separator()
}
} else if p.HasError() { // JIM
@ -496,17 +491,15 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) {
}
p.SetState(31)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == ConfigParserWHITESPACE {
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext()) == 1 {
{
p.SetState(30)
p.Separator()
p.Value()
}
} else if p.HasError() { // JIM
goto errorExit
}
p.SetState(34)
p.GetErrorHandler().Sync(p)
@ -515,23 +508,9 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) {
}
_la = p.GetTokenStream().LA(1)
if _la == ConfigParserSTRING {
{
p.SetState(33)
p.Value()
}
}
p.SetState(37)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == ConfigParserHASH {
{
p.SetState(36)
p.SetState(33)
p.LeadingComment()
}
@ -625,7 +604,7 @@ func (p *ConfigParser) Separator() (localctx ISeparatorContext) {
p.EnterRule(localctx, 4, ConfigParserRULE_separator)
p.EnterOuterAlt(localctx, 1)
{
p.SetState(39)
p.SetState(36)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
@ -721,7 +700,7 @@ func (p *ConfigParser) Key() (localctx IKeyContext) {
p.EnterRule(localctx, 6, ConfigParserRULE_key)
p.EnterOuterAlt(localctx, 1)
{
p.SetState(41)
p.SetState(38)
p.Match(ConfigParserSTRING)
if p.HasError() {
// Recognition error - abort rule
@ -832,13 +811,22 @@ func (p *ConfigParser) Value() (localctx IValueContext) {
p.EnterRule(localctx, 8, ConfigParserRULE_value)
var _la int
p.EnterOuterAlt(localctx, 1)
p.SetState(45)
p.GetErrorHandler().Sync(p)
var _alt int
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 8, p.GetParserRuleContext()) == 1 {
p.EnterOuterAlt(localctx, 1)
p.SetState(44)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 7, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
for _alt != 2 && _alt != antlr.ATNInvalidAltNumber {
if _alt == 1 {
{
p.SetState(43)
p.SetState(40)
p.Match(ConfigParserSTRING)
if p.HasError() {
// Recognition error - abort rule
@ -846,7 +834,7 @@ func (p *ConfigParser) Value() (localctx IValueContext) {
}
}
{
p.SetState(44)
p.SetState(41)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
@ -854,9 +842,25 @@ func (p *ConfigParser) Value() (localctx IValueContext) {
}
}
} else if p.HasError() { // JIM
}
p.SetState(46)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 7, p.GetParserRuleContext())
if p.HasError() {
goto errorExit
}
}
p.SetState(48)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == ConfigParserSTRING {
{
p.SetState(47)
p.Match(ConfigParserSTRING)
@ -865,7 +869,9 @@ func (p *ConfigParser) Value() (localctx IValueContext) {
goto errorExit
}
}
p.SetState(49)
}
p.SetState(51)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -874,7 +880,7 @@ func (p *ConfigParser) Value() (localctx IValueContext) {
if _la == ConfigParserWHITESPACE {
{
p.SetState(48)
p.SetState(50)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
@ -994,14 +1000,14 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
p.EnterOuterAlt(localctx, 1)
{
p.SetState(51)
p.SetState(53)
p.Match(ConfigParserHASH)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(53)
p.SetState(55)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -1010,7 +1016,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
if _la == ConfigParserWHITESPACE {
{
p.SetState(52)
p.SetState(54)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
@ -1019,7 +1025,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
}
}
p.SetState(59)
p.SetState(61)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -1028,14 +1034,14 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
for ok := true; ok; ok = _la == ConfigParserSTRING {
{
p.SetState(55)
p.SetState(57)
p.Match(ConfigParserSTRING)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(57)
p.SetState(59)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -1044,7 +1050,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
if _la == ConfigParserWHITESPACE {
{
p.SetState(56)
p.SetState(58)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
@ -1054,7 +1060,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
}
p.SetState(61)
p.SetState(63)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit

View File

@ -92,7 +92,7 @@ Match Address 192.168.0.1
t.Errorf("Expected second entry to be 'Match Address 192.168.0.1', but got: %v", secondEntry.MatchEntry.Value)
}
if !(secondEntry.MatchValue.Entries[0].Criteria == "Address" && secondEntry.MatchValue.Entries[0].Values[0].Value == "192.168.0.1") {
if !(secondEntry.MatchValue.Entries[0].Criteria.Type == "Address" && secondEntry.MatchValue.Entries[0].Values.Values[0].Value == "192.168.0.1" && secondEntry.MatchEntry.OptionValue.Start.Character == 6) {
t.Errorf("Expected second entry to be 'Match Address 192.168.0.1', but got: %v", secondEntry.MatchValue)
}
@ -107,6 +107,65 @@ Match Address 192.168.0.1
}
}
func TestMultipleEntriesInMatchBlock(
t *testing.T,
) {
input := utils.Dedent(`
Match User lena User root
`)
p := NewSSHConfig()
errors := p.Parse(input)
if len(errors) != 0 {
t.Fatalf("Expected no errors, got %v", errors)
}
_, matchBlock := p.FindOption(uint32(0))
if !(matchBlock.MatchEntry.Value == "Match User lena User root") {
t.Errorf("Expected match block to be 'Match User lena User root', but got: %v", matchBlock.MatchEntry.Value)
}
if !(len(matchBlock.MatchValue.Entries) == 2) {
t.Errorf("Expected 2 entries in match block, but got: %v", matchBlock.MatchValue.Entries)
}
if !(matchBlock.MatchValue.Entries[0].Criteria.Type == "User" && matchBlock.MatchValue.Entries[0].Values.Values[0].Value == "lena") {
t.Errorf("Expected first entry to be 'User lena', but got: %v", matchBlock.MatchValue.Entries[0])
}
if !(matchBlock.MatchValue.Entries[1].Criteria.Type == "User" && matchBlock.MatchValue.Entries[1].Values.Values[0].Value == "root") {
t.Errorf("Expected second entry to be 'User root', but got: %v", matchBlock.MatchValue.Entries[1])
}
}
func TestIncompleteMatchBlock(
t *testing.T,
) {
input := "Match User lena User "
p := NewSSHConfig()
errors := p.Parse(input)
if !(len(errors) == 0) {
t.Errorf("Expected 0 error, got %v", errors)
}
_, matchBlock := p.FindOption(uint32(0))
if !(matchBlock.MatchEntry.Value == "Match User lena User ") {
t.Errorf("Expected match block to be 'Match User lena User ', but got: %v", matchBlock.MatchEntry.Value)
}
if !(matchBlock.MatchValue.Entries[0].Criteria.Type == "User" && matchBlock.MatchValue.Entries[0].Values.Values[0].Value == "lena") {
t.Errorf("Expected first entry to be 'User lena', but got: %v", matchBlock.MatchValue.Entries[0])
}
if !(matchBlock.MatchValue.Entries[1].Value == "User " && matchBlock.MatchValue.Entries[1].Criteria.Type == "User" && matchBlock.MatchValue.Entries[1].Values == nil) {
t.Errorf("Expected second entry to be 'User ', but got: %v", matchBlock.MatchValue.Entries[1])
}
}
func TestMultipleMatchBlocks(
t *testing.T,
) {
@ -168,12 +227,12 @@ Match Address 192.168.0.2
}
emptyOption, matchBlock := p.FindOption(uint32(5))
if !(emptyOption == nil && matchBlock.MatchEntry.Value == "Match User lena" && matchBlock.MatchValue.Entries[0].Values[0].Value == "lena") {
if !(emptyOption == nil && matchBlock.MatchEntry.Value == "Match User lena" && matchBlock.MatchValue.Entries[0].Values.Values[0].Value == "lena") {
t.Errorf("Expected empty option and match block to be 'Match User lena', but got: %v, %v", emptyOption, matchBlock)
}
matchOption, matchBlock := p.FindOption(uint32(2))
if !(matchOption.Value == "Match User lena" && matchBlock.MatchEntry.Value == "Match User lena" && matchBlock.MatchValue.Entries[0].Values[0].Value == "lena") {
if !(matchOption.Value == "Match User lena" && matchBlock.MatchEntry.Value == "Match User lena" && matchBlock.MatchValue.Entries[0].Values.Values[0].Value == "lena" && matchBlock.MatchEntry.OptionValue.Start.Character == 6) {
t.Errorf("Expected match option to be 'Match User lena', but got: %v, %v", matchOption, matchBlock)
}
}
@ -416,7 +475,7 @@ Match Address 172.22.100.0/24,172.22.5.0/24,127.0.0.1
t.Errorf("Expected fourth entry to be 'Match User anoncvs', but got: %v", fourthEntry.MatchEntry.Value)
}
if !(fourthEntry.MatchValue.Entries[0].Criteria == "User" && fourthEntry.MatchValue.Entries[0].Values[0].Value == "anoncvs") {
if !(fourthEntry.MatchValue.Entries[0].Criteria.Type == "User" && fourthEntry.MatchValue.Entries[0].Values.Values[0].Value == "anoncvs") {
t.Errorf("Expected fourth entry to be 'Match User anoncvs', but got: %v", fourthEntry.MatchValue)
}
@ -440,7 +499,7 @@ Match Address 172.22.100.0/24,172.22.5.0/24,127.0.0.1
t.Errorf("Expected sixth entry to be 'Match Address 172.22.100.0/24,172.22.5.0/24,127.0.0.1', but got: %v", sixthEntry.MatchEntry.Value)
}
if !(sixthEntry.MatchValue.Entries[0].Criteria == "Address" && len(sixthEntry.MatchValue.Entries[0].Values) == 3) {
if !(sixthEntry.MatchValue.Entries[0].Criteria.Type == "Address" && len(sixthEntry.MatchValue.Entries[0].Values.Values) == 3) {
t.Errorf("Expected sixth entry to contain 3 values, but got: %v", sixthEntry.MatchValue)
}

View File

@ -42,14 +42,6 @@ type SSHOption struct {
OptionValue *SSHValue
}
func (o SSHOption) GetType() SSHEntryType {
return SSHEntryTypeOption
}
func (o SSHOption) GetOption() SSHOption {
return o
}
type SSHMatchBlock struct {
common.LocationRange
MatchEntry *SSHOption
@ -59,66 +51,9 @@ type SSHMatchBlock struct {
Options *treemap.Map
}
func (m SSHMatchBlock) GetType() SSHEntryType {
return SSHEntryTypeMatchBlock
}
func (m SSHMatchBlock) GetOption() SSHOption {
return *m.MatchEntry
}
type SSHConfig struct {
// [uint32]SSHOption -> line number -> *SSHEntry
Options *treemap.Map
// [uint32]{} -> line number -> {}
CommentLines map[uint32]struct{}
}
func (c SSHConfig) FindMatchBlock(line uint32) *SSHMatchBlock {
for currentLine := line; currentLine > 0; currentLine-- {
rawEntry, found := c.Options.Get(currentLine)
if !found {
continue
}
switch entry := rawEntry.(type) {
case *SSHMatchBlock:
return entry
}
}
return nil
}
func (c SSHConfig) FindOption(line uint32) (*SSHOption, *SSHMatchBlock) {
matchBlock := c.FindMatchBlock(line)
if matchBlock != nil {
if line == matchBlock.MatchEntry.Start.Line {
return matchBlock.MatchEntry, matchBlock
}
rawEntry, found := matchBlock.Options.Get(line)
if found {
return rawEntry.(*SSHOption), matchBlock
} else {
return nil, matchBlock
}
}
rawEntry, found := c.Options.Get(line)
if found {
switch rawEntry.(type) {
case *SSHMatchBlock:
return rawEntry.(*SSHMatchBlock).MatchEntry, rawEntry.(*SSHMatchBlock)
case *SSHOption:
return rawEntry.(*SSHOption), nil
}
}
return nil, nil
}

View File

@ -0,0 +1,66 @@
package ast
func (o SSHOption) GetType() SSHEntryType {
return SSHEntryTypeOption
}
func (o SSHOption) GetOption() SSHOption {
return o
}
func (m SSHMatchBlock) GetType() SSHEntryType {
return SSHEntryTypeMatchBlock
}
func (m SSHMatchBlock) GetOption() SSHOption {
return *m.MatchEntry
}
func (c SSHConfig) FindMatchBlock(line uint32) *SSHMatchBlock {
for currentLine := line; currentLine > 0; currentLine-- {
rawEntry, found := c.Options.Get(currentLine)
if !found {
continue
}
switch entry := rawEntry.(type) {
case *SSHMatchBlock:
return entry
}
}
return nil
}
func (c SSHConfig) FindOption(line uint32) (*SSHOption, *SSHMatchBlock) {
matchBlock := c.FindMatchBlock(line)
if matchBlock != nil {
if line == matchBlock.MatchEntry.Start.Line {
return matchBlock.MatchEntry, matchBlock
}
rawEntry, found := matchBlock.Options.Get(line)
if found {
return rawEntry.(*SSHOption), matchBlock
} else {
return nil, matchBlock
}
}
rawEntry, found := c.Options.Get(line)
if found {
switch rawEntry.(type) {
case *SSHMatchBlock:
return rawEntry.(*SSHMatchBlock).MatchEntry, rawEntry.(*SSHMatchBlock)
case *SSHOption:
return rawEntry.(*SSHOption), nil
}
}
return nil, nil
}

View File

@ -563,27 +563,7 @@ The arguments to Match are one or more criteria-pattern pairs or the single toke
The match patterns may consist of single entries or comma-separated lists and may use the wildcard and negation operators described in the PATTERNS section of ssh_config(5).
The patterns in an Address criteria may additionally contain addresses to match in CIDR address/masklen format, such as 192.0.2.0/24 or 2001:db8::/32. Note that the mask length provided must be consistent with the address - it is an error to specify a mask length that is too long for the address or one with bits set in this host portion of the address. For example, 192.0.2.0/33 and 192.0.2.0/8, respectively.
Only a subset of keywords may be used on the lines following a Match keyword. Available keywords are AcceptEnv, AllowAgentForwarding, AllowGroups, AllowStreamLocalForwarding, AllowTcpForwarding, AllowUsers, AuthenticationMethods, AuthorizedKeysCommand, AuthorizedKeysCommandUser, AuthorizedKeysFile, AuthorizedPrincipalsCommand, AuthorizedPrincipalsCommandUser, AuthorizedPrincipalsFile, Banner, CASignatureAlgorithms, ChannelTimeout, ChrootDirectory, ClientAliveCountMax, ClientAliveInterval, DenyGroups, DenyUsers, DisableForwarding, ExposeAuthInfo, ForceCommand, GatewayPorts, GSSAPIAuthentication, HostbasedAcceptedAlgorithms, HostbasedAuthentication, HostbasedUsesNameFromPacketOnly, IgnoreRhosts, Include, IPQoS, KbdInteractiveAuthentication, KerberosAuthentication, LogLevel, MaxAuthTries, MaxSessions, PasswordAuthentication, PermitEmptyPasswords, PermitListen, PermitOpen, PermitRootLogin, PermitTTY, PermitTunnel, PermitUserRC, PubkeyAcceptedAlgorithms, PubkeyAuthentication, PubkeyAuthOptions, RekeyLimit, RevokedKeys, RDomain, SetEnv, StreamLocalBindMask, StreamLocalBindUnlink, TrustedUserCAKeys, UnusedConnectionTimeout, X11DisplayOffset, X11Forwarding and X11UseLocalhost.`,
Value: docvalues.OrValue{
Values: []docvalues.Value{
docvalues.SingleEnumValue("All"),
docvalues.ArrayValue{
Separator: ",",
DuplicatesExtractor: &docvalues.SimpleDuplicatesExtractor,
SubValue: docvalues.KeyEnumAssignmentValue{
Separator: " ",
Values: map[docvalues.EnumString]docvalues.Value{
docvalues.CreateEnumString("User"): docvalues.UserValue("", false),
docvalues.CreateEnumString("Group"): docvalues.GroupValue("", false),
docvalues.CreateEnumString("Host"): docvalues.StringValue{},
docvalues.CreateEnumString("LocalAddress"): docvalues.StringValue{},
docvalues.CreateEnumString("LocalPort"): docvalues.NumberValue{Min: &ZERO, Max: &MAX_PORT},
docvalues.CreateEnumString("RDomain"): docvalues.StringValue{},
docvalues.CreateEnumString("Address"): docvalues.StringValue{},
},
},
},
},
},
Value: docvalues.StringValue{},
},
"MaxAuthTries": {
Documentation: `Specifies the maximum number of authentication attempts permitted per connection. Once the number of failures reaches half this value, additional failures are logged. The default is 6.`,

View File

@ -1,25 +1,23 @@
grammar Match;
root
: matchEntry? (WHITESPACE matchEntry)* EOF
: matchEntry? (WHITESPACE matchEntry?)* EOF
;
matchEntry
: criteria WHITESPACE? values?
: criteria separator? values?
;
separator
: WHITESPACE
;
criteria
: USER
| GROUP
| HOST
| LOCALADDRESS
| LOCALPORT
| RDOMAIN
| ADDRESS
: (USER | GROUP | HOST| LOCALADDRESS | LOCALPORT | RDOMAIN | ADDRESS)
;
values
: value? (COMMA value?)*
: value (COMMA value?)*
;
value

View File

@ -11,16 +11,19 @@ import (
func createMatchListenerContext(
line uint32,
startCharacter uint32,
) *matchListenerContext {
return &matchListenerContext{
currentEntry: nil,
line: line,
startCharacter: startCharacter,
}
}
type matchListenerContext struct {
currentEntry *MatchEntry
line uint32
startCharacter uint32
}
func createListener(
@ -42,13 +45,12 @@ type matchParserListener struct {
}
func (s *matchParserListener) EnterMatchEntry(ctx *parser.MatchEntryContext) {
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext).ShiftHorizontal(s.matchContext.startCharacter)
location.ChangeBothLines(s.matchContext.line)
entry := &MatchEntry{
LocationRange: location,
Value: ctx.GetText(),
Values: make([]*MatchValue, 0),
}
s.match.Entries = append(s.match.Entries, entry)
@ -70,7 +72,7 @@ var availableCriteria = map[string]MatchCriteriaType{
}
func (s *matchParserListener) EnterCriteria(ctx *parser.CriteriaContext) {
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext).ShiftHorizontal(s.matchContext.startCharacter)
location.ChangeBothLines(s.matchContext.line)
criteria, found := availableCriteria[ctx.GetText()]
@ -83,11 +85,33 @@ func (s *matchParserListener) EnterCriteria(ctx *parser.CriteriaContext) {
return
}
s.matchContext.currentEntry.Criteria = criteria
s.matchContext.currentEntry.Criteria = MatchCriteria{
LocationRange: location,
Type: criteria,
}
}
func (s *matchParserListener) EnterSeparator(ctx *parser.SeparatorContext) {
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext).ShiftHorizontal(s.matchContext.startCharacter)
location.ChangeBothLines(s.matchContext.line)
s.matchContext.currentEntry.Separator = &MatchSeparator{
LocationRange: location,
}
}
func (s *matchParserListener) EnterValues(ctx *parser.ValuesContext) {
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext).ShiftHorizontal(s.matchContext.startCharacter)
location.ChangeBothLines(s.matchContext.line)
s.matchContext.currentEntry.Values = &MatchValues{
LocationRange: location,
Values: make([]*MatchValue, 0),
}
}
func (s *matchParserListener) EnterValue(ctx *parser.ValueContext) {
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext)
location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext).ShiftHorizontal(s.matchContext.startCharacter)
location.ChangeBothLines(s.matchContext.line)
value := &MatchValue{
@ -95,5 +119,5 @@ func (s *matchParserListener) EnterValue(ctx *parser.ValueContext) {
Value: ctx.GetText(),
}
s.matchContext.currentEntry.Values = append(s.matchContext.currentEntry.Values, value)
s.matchContext.currentEntry.Values.Values = append(s.matchContext.currentEntry.Values.Values, value)
}

View File

@ -26,12 +26,23 @@ type MatchCriteria struct {
Type MatchCriteriaType
}
type MatchSeparator struct {
common.LocationRange
}
type MatchValues struct {
common.LocationRange
Values []*MatchValue
}
type MatchEntry struct {
common.LocationRange
Value string
Criteria MatchCriteriaType
Values []*MatchValue
Criteria MatchCriteria
Separator *MatchSeparator
Values *MatchValues
}
type MatchValue struct {

View File

@ -0,0 +1,67 @@
package match_parser
import "slices"
func (m Match) GetEntryByCursor(cursor uint32) *MatchEntry {
index, found := slices.BinarySearchFunc(
m.Entries,
cursor,
func(entry *MatchEntry, cursor uint32) int {
if cursor < entry.Start.Character {
return 1
}
if cursor > entry.End.Character {
return -1
}
return 0
},
)
if !found {
return nil
}
entry := m.Entries[index]
return entry
}
func (c MatchCriteria) IsCursorBetween(cursor uint32) bool {
return cursor >= c.Start.Character && cursor <= c.End.Character
}
func (e MatchEntry) GetValueByCursor(cursor uint32) *MatchValue {
if e.Values == nil {
return nil
}
index, found := slices.BinarySearchFunc(
e.Values.Values,
cursor,
func(value *MatchValue, cursor uint32) int {
if cursor < value.Start.Character {
return 1
}
if cursor > value.End.Character {
return -1
}
return 0
},
)
if !found {
return nil
}
value := e.Values.Values[index]
return value
}
func (v MatchValues) IsCursorBetween(cursor uint32) bool {
return cursor >= v.Start.Character && cursor <= v.End.Character
}

View File

@ -20,8 +20,9 @@ func (m *Match) Clear() {
func (m *Match) Parse(
input string,
line uint32,
startCharacter uint32,
) []common.LSPError {
context := createMatchListenerContext(line)
context := createMatchListenerContext(line, startCharacter)
stream := antlr.NewInputStream(input)

View File

@ -27,10 +27,11 @@ WHITESPACE
rule names:
root
matchEntry
separator
criteria
values
value
atn:
[4, 1, 10, 46, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 1, 0, 3, 0, 12, 8, 0, 1, 0, 1, 0, 5, 0, 16, 8, 0, 10, 0, 12, 0, 19, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 25, 8, 1, 1, 1, 3, 1, 28, 8, 1, 1, 2, 1, 2, 1, 3, 3, 3, 33, 8, 3, 1, 3, 1, 3, 3, 3, 37, 8, 3, 5, 3, 39, 8, 3, 10, 3, 12, 3, 42, 9, 3, 1, 4, 1, 4, 1, 4, 0, 0, 5, 0, 2, 4, 6, 8, 0, 1, 1, 0, 1, 7, 47, 0, 11, 1, 0, 0, 0, 2, 22, 1, 0, 0, 0, 4, 29, 1, 0, 0, 0, 6, 32, 1, 0, 0, 0, 8, 43, 1, 0, 0, 0, 10, 12, 3, 2, 1, 0, 11, 10, 1, 0, 0, 0, 11, 12, 1, 0, 0, 0, 12, 17, 1, 0, 0, 0, 13, 14, 5, 10, 0, 0, 14, 16, 3, 2, 1, 0, 15, 13, 1, 0, 0, 0, 16, 19, 1, 0, 0, 0, 17, 15, 1, 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 20, 1, 0, 0, 0, 19, 17, 1, 0, 0, 0, 20, 21, 5, 0, 0, 1, 21, 1, 1, 0, 0, 0, 22, 24, 3, 4, 2, 0, 23, 25, 5, 10, 0, 0, 24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 27, 1, 0, 0, 0, 26, 28, 3, 6, 3, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 3, 1, 0, 0, 0, 29, 30, 7, 0, 0, 0, 30, 5, 1, 0, 0, 0, 31, 33, 3, 8, 4, 0, 32, 31, 1, 0, 0, 0, 32, 33, 1, 0, 0, 0, 33, 40, 1, 0, 0, 0, 34, 36, 5, 8, 0, 0, 35, 37, 3, 8, 4, 0, 36, 35, 1, 0, 0, 0, 36, 37, 1, 0, 0, 0, 37, 39, 1, 0, 0, 0, 38, 34, 1, 0, 0, 0, 39, 42, 1, 0, 0, 0, 40, 38, 1, 0, 0, 0, 40, 41, 1, 0, 0, 0, 41, 7, 1, 0, 0, 0, 42, 40, 1, 0, 0, 0, 43, 44, 5, 9, 0, 0, 44, 9, 1, 0, 0, 0, 7, 11, 17, 24, 27, 32, 36, 40]
[4, 1, 10, 50, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 1, 0, 3, 0, 14, 8, 0, 1, 0, 1, 0, 3, 0, 18, 8, 0, 5, 0, 20, 8, 0, 10, 0, 12, 0, 23, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 29, 8, 1, 1, 1, 3, 1, 32, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 3, 4, 41, 8, 4, 5, 4, 43, 8, 4, 10, 4, 12, 4, 46, 9, 4, 1, 5, 1, 5, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10, 0, 1, 1, 0, 1, 7, 50, 0, 13, 1, 0, 0, 0, 2, 26, 1, 0, 0, 0, 4, 33, 1, 0, 0, 0, 6, 35, 1, 0, 0, 0, 8, 37, 1, 0, 0, 0, 10, 47, 1, 0, 0, 0, 12, 14, 3, 2, 1, 0, 13, 12, 1, 0, 0, 0, 13, 14, 1, 0, 0, 0, 14, 21, 1, 0, 0, 0, 15, 17, 5, 10, 0, 0, 16, 18, 3, 2, 1, 0, 17, 16, 1, 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 20, 1, 0, 0, 0, 19, 15, 1, 0, 0, 0, 20, 23, 1, 0, 0, 0, 21, 19, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 24, 1, 0, 0, 0, 23, 21, 1, 0, 0, 0, 24, 25, 5, 0, 0, 1, 25, 1, 1, 0, 0, 0, 26, 28, 3, 6, 3, 0, 27, 29, 3, 4, 2, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, 0, 0, 0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 8, 4, 0, 31, 30, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 3, 1, 0, 0, 0, 33, 34, 5, 10, 0, 0, 34, 5, 1, 0, 0, 0, 35, 36, 7, 0, 0, 0, 36, 7, 1, 0, 0, 0, 37, 44, 3, 10, 5, 0, 38, 40, 5, 8, 0, 0, 39, 41, 3, 10, 5, 0, 40, 39, 1, 0, 0, 0, 40, 41, 1, 0, 0, 0, 41, 43, 1, 0, 0, 0, 42, 38, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 9, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 48, 5, 9, 0, 0, 48, 11, 1, 0, 0, 0, 7, 13, 17, 21, 28, 31, 40, 44]

View File

@ -33,6 +33,12 @@ func (s *BaseMatchListener) EnterMatchEntry(ctx *MatchEntryContext) {}
// ExitMatchEntry is called when production matchEntry is exited.
func (s *BaseMatchListener) ExitMatchEntry(ctx *MatchEntryContext) {}
// EnterSeparator is called when production separator is entered.
func (s *BaseMatchListener) EnterSeparator(ctx *SeparatorContext) {}
// ExitSeparator is called when production separator is exited.
func (s *BaseMatchListener) ExitSeparator(ctx *SeparatorContext) {}
// EnterCriteria is called when production criteria is entered.
func (s *BaseMatchListener) EnterCriteria(ctx *CriteriaContext) {}

View File

@ -14,6 +14,9 @@ type MatchListener interface {
// EnterMatchEntry is called when entering the matchEntry production.
EnterMatchEntry(c *MatchEntryContext)
// EnterSeparator is called when entering the separator production.
EnterSeparator(c *SeparatorContext)
// EnterCriteria is called when entering the criteria production.
EnterCriteria(c *CriteriaContext)
@ -29,6 +32,9 @@ type MatchListener interface {
// ExitMatchEntry is called when exiting the matchEntry production.
ExitMatchEntry(c *MatchEntryContext)
// ExitSeparator is called when exiting the separator production.
ExitSeparator(c *SeparatorContext)
// ExitCriteria is called when exiting the criteria production.
ExitCriteria(c *CriteriaContext)

View File

@ -40,29 +40,30 @@ func matchParserInit() {
"ADDRESS", "COMMA", "STRING", "WHITESPACE",
}
staticData.RuleNames = []string{
"root", "matchEntry", "criteria", "values", "value",
"root", "matchEntry", "separator", "criteria", "values", "value",
}
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
4, 1, 10, 46, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7,
4, 1, 0, 3, 0, 12, 8, 0, 1, 0, 1, 0, 5, 0, 16, 8, 0, 10, 0, 12, 0, 19,
9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 25, 8, 1, 1, 1, 3, 1, 28, 8, 1, 1,
2, 1, 2, 1, 3, 3, 3, 33, 8, 3, 1, 3, 1, 3, 3, 3, 37, 8, 3, 5, 3, 39, 8,
3, 10, 3, 12, 3, 42, 9, 3, 1, 4, 1, 4, 1, 4, 0, 0, 5, 0, 2, 4, 6, 8, 0,
1, 1, 0, 1, 7, 47, 0, 11, 1, 0, 0, 0, 2, 22, 1, 0, 0, 0, 4, 29, 1, 0, 0,
0, 6, 32, 1, 0, 0, 0, 8, 43, 1, 0, 0, 0, 10, 12, 3, 2, 1, 0, 11, 10, 1,
0, 0, 0, 11, 12, 1, 0, 0, 0, 12, 17, 1, 0, 0, 0, 13, 14, 5, 10, 0, 0, 14,
16, 3, 2, 1, 0, 15, 13, 1, 0, 0, 0, 16, 19, 1, 0, 0, 0, 17, 15, 1, 0, 0,
0, 17, 18, 1, 0, 0, 0, 18, 20, 1, 0, 0, 0, 19, 17, 1, 0, 0, 0, 20, 21,
5, 0, 0, 1, 21, 1, 1, 0, 0, 0, 22, 24, 3, 4, 2, 0, 23, 25, 5, 10, 0, 0,
24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 27, 1, 0, 0, 0, 26, 28, 3,
6, 3, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 3, 1, 0, 0, 0, 29,
30, 7, 0, 0, 0, 30, 5, 1, 0, 0, 0, 31, 33, 3, 8, 4, 0, 32, 31, 1, 0, 0,
0, 32, 33, 1, 0, 0, 0, 33, 40, 1, 0, 0, 0, 34, 36, 5, 8, 0, 0, 35, 37,
3, 8, 4, 0, 36, 35, 1, 0, 0, 0, 36, 37, 1, 0, 0, 0, 37, 39, 1, 0, 0, 0,
38, 34, 1, 0, 0, 0, 39, 42, 1, 0, 0, 0, 40, 38, 1, 0, 0, 0, 40, 41, 1,
0, 0, 0, 41, 7, 1, 0, 0, 0, 42, 40, 1, 0, 0, 0, 43, 44, 5, 9, 0, 0, 44,
9, 1, 0, 0, 0, 7, 11, 17, 24, 27, 32, 36, 40,
4, 1, 10, 50, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7,
4, 2, 5, 7, 5, 1, 0, 3, 0, 14, 8, 0, 1, 0, 1, 0, 3, 0, 18, 8, 0, 5, 0,
20, 8, 0, 10, 0, 12, 0, 23, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 29, 8,
1, 1, 1, 3, 1, 32, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 3, 4,
41, 8, 4, 5, 4, 43, 8, 4, 10, 4, 12, 4, 46, 9, 4, 1, 5, 1, 5, 1, 5, 0,
0, 6, 0, 2, 4, 6, 8, 10, 0, 1, 1, 0, 1, 7, 50, 0, 13, 1, 0, 0, 0, 2, 26,
1, 0, 0, 0, 4, 33, 1, 0, 0, 0, 6, 35, 1, 0, 0, 0, 8, 37, 1, 0, 0, 0, 10,
47, 1, 0, 0, 0, 12, 14, 3, 2, 1, 0, 13, 12, 1, 0, 0, 0, 13, 14, 1, 0, 0,
0, 14, 21, 1, 0, 0, 0, 15, 17, 5, 10, 0, 0, 16, 18, 3, 2, 1, 0, 17, 16,
1, 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 20, 1, 0, 0, 0, 19, 15, 1, 0, 0, 0,
20, 23, 1, 0, 0, 0, 21, 19, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 24, 1,
0, 0, 0, 23, 21, 1, 0, 0, 0, 24, 25, 5, 0, 0, 1, 25, 1, 1, 0, 0, 0, 26,
28, 3, 6, 3, 0, 27, 29, 3, 4, 2, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, 0, 0,
0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 8, 4, 0, 31, 30, 1, 0, 0, 0, 31, 32,
1, 0, 0, 0, 32, 3, 1, 0, 0, 0, 33, 34, 5, 10, 0, 0, 34, 5, 1, 0, 0, 0,
35, 36, 7, 0, 0, 0, 36, 7, 1, 0, 0, 0, 37, 44, 3, 10, 5, 0, 38, 40, 5,
8, 0, 0, 39, 41, 3, 10, 5, 0, 40, 39, 1, 0, 0, 0, 40, 41, 1, 0, 0, 0, 41,
43, 1, 0, 0, 0, 42, 38, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0,
0, 44, 45, 1, 0, 0, 0, 45, 9, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47, 48, 5,
9, 0, 0, 48, 11, 1, 0, 0, 0, 7, 13, 17, 21, 28, 31, 40, 44,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
@ -117,9 +118,10 @@ const (
const (
MatchParserRULE_root = 0
MatchParserRULE_matchEntry = 1
MatchParserRULE_criteria = 2
MatchParserRULE_values = 3
MatchParserRULE_value = 4
MatchParserRULE_separator = 2
MatchParserRULE_criteria = 3
MatchParserRULE_values = 4
MatchParserRULE_value = 5
)
// IRootContext is an interface to support dynamic dispatch.
@ -251,7 +253,7 @@ func (p *MatchParser) Root() (localctx IRootContext) {
var _la int
p.EnterOuterAlt(localctx, 1)
p.SetState(11)
p.SetState(13)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -260,12 +262,12 @@ func (p *MatchParser) Root() (localctx IRootContext) {
if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&254) != 0 {
{
p.SetState(10)
p.SetState(12)
p.MatchEntry()
}
}
p.SetState(17)
p.SetState(21)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -274,19 +276,29 @@ func (p *MatchParser) Root() (localctx IRootContext) {
for _la == MatchParserWHITESPACE {
{
p.SetState(13)
p.SetState(15)
p.Match(MatchParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(17)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&254) != 0 {
{
p.SetState(14)
p.SetState(16)
p.MatchEntry()
}
p.SetState(19)
}
p.SetState(23)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -294,7 +306,7 @@ func (p *MatchParser) Root() (localctx IRootContext) {
_la = p.GetTokenStream().LA(1)
}
{
p.SetState(20)
p.SetState(24)
p.Match(MatchParserEOF)
if p.HasError() {
// Recognition error - abort rule
@ -324,7 +336,7 @@ type IMatchEntryContext interface {
// Getter signatures
Criteria() ICriteriaContext
WHITESPACE() antlr.TerminalNode
Separator() ISeparatorContext
Values() IValuesContext
// IsMatchEntryContext differentiates from other interfaces.
@ -379,8 +391,20 @@ func (s *MatchEntryContext) Criteria() ICriteriaContext {
return t.(ICriteriaContext)
}
func (s *MatchEntryContext) WHITESPACE() antlr.TerminalNode {
return s.GetToken(MatchParserWHITESPACE, 0)
func (s *MatchEntryContext) Separator() ISeparatorContext {
var t antlr.RuleContext
for _, ctx := range s.GetChildren() {
if _, ok := ctx.(ISeparatorContext); ok {
t = ctx.(antlr.RuleContext)
break
}
}
if t == nil {
return nil
}
return t.(ISeparatorContext)
}
func (s *MatchEntryContext) Values() IValuesContext {
@ -422,17 +446,129 @@ func (s *MatchEntryContext) ExitRule(listener antlr.ParseTreeListener) {
func (p *MatchParser) MatchEntry() (localctx IMatchEntryContext) {
localctx = NewMatchEntryContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 2, MatchParserRULE_matchEntry)
var _la int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(22)
p.SetState(26)
p.Criteria()
}
p.SetState(24)
p.SetState(28)
p.GetErrorHandler().Sync(p)
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 2, p.GetParserRuleContext()) == 1 {
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 {
{
p.SetState(23)
p.SetState(27)
p.Separator()
}
} else if p.HasError() { // JIM
goto errorExit
}
p.SetState(31)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == MatchParserSTRING {
{
p.SetState(30)
p.Values()
}
}
errorExit:
if p.HasError() {
v := p.GetError()
localctx.SetException(v)
p.GetErrorHandler().ReportError(p, v)
p.GetErrorHandler().Recover(p, v)
p.SetError(nil)
}
p.ExitRule()
return localctx
goto errorExit // Trick to prevent compiler error if the label is not used
}
// ISeparatorContext is an interface to support dynamic dispatch.
type ISeparatorContext interface {
antlr.ParserRuleContext
// GetParser returns the parser.
GetParser() antlr.Parser
// Getter signatures
WHITESPACE() antlr.TerminalNode
// IsSeparatorContext differentiates from other interfaces.
IsSeparatorContext()
}
type SeparatorContext struct {
antlr.BaseParserRuleContext
parser antlr.Parser
}
func NewEmptySeparatorContext() *SeparatorContext {
var p = new(SeparatorContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = MatchParserRULE_separator
return p
}
func InitEmptySeparatorContext(p *SeparatorContext) {
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1)
p.RuleIndex = MatchParserRULE_separator
}
func (*SeparatorContext) IsSeparatorContext() {}
func NewSeparatorContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *SeparatorContext {
var p = new(SeparatorContext)
antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState)
p.parser = parser
p.RuleIndex = MatchParserRULE_separator
return p
}
func (s *SeparatorContext) GetParser() antlr.Parser { return s.parser }
func (s *SeparatorContext) WHITESPACE() antlr.TerminalNode {
return s.GetToken(MatchParserWHITESPACE, 0)
}
func (s *SeparatorContext) GetRuleContext() antlr.RuleContext {
return s
}
func (s *SeparatorContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
return antlr.TreesStringTree(s, ruleNames, recog)
}
func (s *SeparatorContext) EnterRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(MatchListener); ok {
listenerT.EnterSeparator(s)
}
}
func (s *SeparatorContext) ExitRule(listener antlr.ParseTreeListener) {
if listenerT, ok := listener.(MatchListener); ok {
listenerT.ExitSeparator(s)
}
}
func (p *MatchParser) Separator() (localctx ISeparatorContext) {
localctx = NewSeparatorContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 4, MatchParserRULE_separator)
p.EnterOuterAlt(localctx, 1)
{
p.SetState(33)
p.Match(MatchParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
@ -440,22 +576,6 @@ func (p *MatchParser) MatchEntry() (localctx IMatchEntryContext) {
}
}
} else if p.HasError() { // JIM
goto errorExit
}
p.SetState(27)
p.GetErrorHandler().Sync(p)
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 {
{
p.SetState(26)
p.Values()
}
} else if p.HasError() { // JIM
goto errorExit
}
errorExit:
if p.HasError() {
v := p.GetError()
@ -571,12 +691,12 @@ func (s *CriteriaContext) ExitRule(listener antlr.ParseTreeListener) {
func (p *MatchParser) Criteria() (localctx ICriteriaContext) {
localctx = NewCriteriaContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 4, MatchParserRULE_criteria)
p.EnterRule(localctx, 6, MatchParserRULE_criteria)
var _la int
p.EnterOuterAlt(localctx, 1)
{
p.SetState(29)
p.SetState(35)
_la = p.GetTokenStream().LA(1)
if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&254) != 0) {
@ -720,25 +840,15 @@ func (s *ValuesContext) ExitRule(listener antlr.ParseTreeListener) {
func (p *MatchParser) Values() (localctx IValuesContext) {
localctx = NewValuesContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 6, MatchParserRULE_values)
p.EnterRule(localctx, 8, MatchParserRULE_values)
var _la int
p.EnterOuterAlt(localctx, 1)
p.SetState(32)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if _la == MatchParserSTRING {
{
p.SetState(31)
p.SetState(37)
p.Value()
}
}
p.SetState(40)
p.SetState(44)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -747,14 +857,14 @@ func (p *MatchParser) Values() (localctx IValuesContext) {
for _la == MatchParserCOMMA {
{
p.SetState(34)
p.SetState(38)
p.Match(MatchParserCOMMA)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(36)
p.SetState(40)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -763,13 +873,13 @@ func (p *MatchParser) Values() (localctx IValuesContext) {
if _la == MatchParserSTRING {
{
p.SetState(35)
p.SetState(39)
p.Value()
}
}
p.SetState(42)
p.SetState(46)
p.GetErrorHandler().Sync(p)
if p.HasError() {
goto errorExit
@ -862,10 +972,10 @@ func (s *ValueContext) ExitRule(listener antlr.ParseTreeListener) {
func (p *MatchParser) Value() (localctx IValueContext) {
localctx = NewValueContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 8, MatchParserRULE_value)
p.EnterRule(localctx, 10, MatchParserRULE_value)
p.EnterOuterAlt(localctx, 1)
{
p.SetState(43)
p.SetState(47)
p.Match(MatchParserSTRING)
if p.HasError() {
// Recognition error - abort rule

View File

@ -7,10 +7,11 @@ import (
func TestComplexExample(
t *testing.T,
) {
offset := uint32(5)
input := "User root,admin,alice Address *,!192.168.0.1"
match := NewMatch()
errors := match.Parse(input, 32)
errors := match.Parse(input, 32, offset)
if len(errors) > 0 {
t.Fatalf("Expected no errors, but got %v", errors)
@ -20,32 +21,32 @@ func TestComplexExample(
t.Fatalf("Expected 2 entries, but got %v", len(match.Entries))
}
if !(match.Entries[0].Criteria == MatchCriteriaTypeUser) {
if !(match.Entries[0].Criteria.Type == MatchCriteriaTypeUser) {
t.Fatalf("Expected User, but got %v", match.Entries[0])
}
if !(match.Entries[0].Values[0].Value == "root") {
t.Fatalf("Expected root, but got %v", match.Entries[0].Values[0])
if !(match.Entries[0].Values.Values[0].Value == "root" && match.Entries[0].Values.Values[0].Start.Character == 5+offset && match.Entries[0].Values.Values[0].End.Character == 8+offset && match.Entries[0].Start.Character == 0+offset && match.Entries[0].End.Character == 20+offset) {
t.Errorf("Expected root, but got %v", match.Entries[0].Values.Values[0])
}
if !(match.Entries[0].Values[1].Value == "admin") {
t.Fatalf("Expected admin, but got %v", match.Entries[0].Values[1])
if !(match.Entries[0].Values.Values[1].Value == "admin" && match.Entries[0].Values.Values[1].Start.Character == 10+offset && match.Entries[0].Values.Values[1].End.Character == 14+offset) {
t.Errorf("Expected admin, but got %v", match.Entries[0].Values.Values[1])
}
if !(match.Entries[0].Values[2].Value == "alice") {
t.Fatalf("Expected alice, but got %v", match.Entries[0].Values[2])
if !(match.Entries[0].Values.Values[2].Value == "alice" && match.Entries[0].Values.Values[2].Start.Character == 16+offset && match.Entries[0].Values.Values[2].End.Character == 20+offset) {
t.Errorf("Expected alice, but got %v", match.Entries[0].Values.Values[2])
}
if !(match.Entries[1].Criteria == MatchCriteriaTypeAddress) {
t.Fatalf("Expected Address, but got %v", match.Entries[1])
if !(match.Entries[1].Criteria.Type == MatchCriteriaTypeAddress) {
t.Errorf("Expected Address, but got %v", match.Entries[1])
}
if !(match.Entries[1].Values[0].Value == "*") {
t.Fatalf("Expected *, but got %v", match.Entries[1].Values[0])
if !(match.Entries[1].Values.Values[0].Value == "*" && match.Entries[1].Values.Values[0].Start.Character == 30+offset && match.Entries[1].Values.Values[0].End.Character == 30+offset) {
t.Errorf("Expected *, but got %v", match.Entries[1].Values.Values[0])
}
if !(match.Entries[1].Values[1].Value == "!192.168.0.1") {
t.Fatalf("Expected !192.168.0.1, but got %v", match.Entries[1].Values[1])
if !(match.Entries[1].Values.Values[1].Value == "!192.168.0.1" && match.Entries[1].Values.Values[1].Start.Character == 32+offset && match.Entries[1].Values.Values[1].End.Character == 43+offset) {
t.Errorf("Expected !192.168.0.1, but got %v", match.Entries[1].Values.Values[1])
}
}
@ -55,7 +56,7 @@ func TestSecondComplexExample(
input := "Address 172.22.100.0/24,172.22.5.0/24,127.0.0.1"
match := NewMatch()
errors := match.Parse(input, 0)
errors := match.Parse(input, 0, 20)
if len(errors) > 0 {
t.Fatalf("Expected no errors, but got %v", errors)
@ -65,15 +66,69 @@ func TestSecondComplexExample(
t.Fatalf("Expected 1 entries, but got %v", len(match.Entries))
}
if !(match.Entries[0].Criteria == MatchCriteriaTypeAddress) {
if !(match.Entries[0].Criteria.Type == MatchCriteriaTypeAddress) {
t.Fatalf("Expected Address, but got %v", match.Entries[0])
}
if !(len(match.Entries[0].Values) == 3) {
t.Fatalf("Expected 3 values, but got %v", len(match.Entries[0].Values))
if !(len(match.Entries[0].Values.Values) == 3) {
t.Fatalf("Expected 3 values, but got %v", len(match.Entries[0].Values.Values))
}
if !(match.Entries[0].Values[0].Value == "172.22.100.0/24") {
t.Fatalf("Expected 172.22.100.0/24, but got %v", match.Entries[0].Values[0])
if !(match.Entries[0].Values.Values[0].Value == "172.22.100.0/24") {
t.Fatalf("Expected 172.22.100.0/24, but got %v", match.Entries[0].Values.Values[0])
}
}
func TestIncompleteBetweenEntriesExample(
t *testing.T,
) {
input := "User root,admin,alice "
match := NewMatch()
errors := match.Parse(input, 0, 0)
if len(errors) > 0 {
t.Fatalf("Expected no errors, but got %v", errors)
}
if !(len(match.Entries) == 1) {
t.Errorf("Expected 1 entries, but got %v", len(match.Entries))
}
if !(match.Entries[0].Criteria.Type == MatchCriteriaTypeUser) {
t.Errorf("Expected User, but got %v", match.Entries[0])
}
if !(len(match.Entries[0].Values.Values) == 3) {
t.Errorf("Expected 3 values, but got %v", len(match.Entries[0].Values.Values))
}
if !(match.Entries[0].Start.Character == 0 && match.Entries[0].End.Character == 20) {
t.Errorf("Expected 0-20, but got %v", match.Entries[0])
}
}
func TestIncompleteBetweenValuesExample(
t *testing.T,
) {
input := "User "
match := NewMatch()
errors := match.Parse(input, 0, 0)
if len(errors) > 0 {
t.Fatalf("Expected no errors, but got %v", errors)
}
if !(len(match.Entries) == 1) {
t.Errorf("Expected 1 entries, but got %v", len(match.Entries))
}
if !(match.Entries[0].Criteria.Type == MatchCriteriaTypeUser) {
t.Errorf("Expected User, but got %v", match.Entries[0])
}
if !(match.Entries[0].Values == nil) {
t.Errorf("Expected 0 values, but got %v", match.Entries[0].Values)
}
}

View File

@ -1,5 +1,7 @@
package fields
import docvalues "config-lsp/doc-values"
var MatchAllowedOptions = map[string]struct{}{
"AcceptEnv": {},
"AllowAgentForwarding": {},
@ -61,3 +63,15 @@ var MatchAllowedOptions = map[string]struct{}{
"X11Forwarding": {},
"X11UseLocalhos": {},
}
var MatchUserField = docvalues.UserValue("", false)
var MatchGroupField = docvalues.GroupValue("", false)
var MatchHostField = docvalues.DomainValue()
var MatchLocalAddressField = docvalues.StringValue{}
var MatchLocalPortField = docvalues.StringValue{}
var MatchRDomainField = docvalues.StringValue{}
var MatchAddressField = docvalues.IPAddressValue{
AllowIPv4: true,
AllowIPv6: true,
AllowRange: true,
}

View File

@ -55,6 +55,7 @@ func GetRootCompletions(
func GetOptionCompletions(
d *sshdconfig.SSHDocument,
entry *ast.SSHOption,
matchBlock *ast.SSHMatchBlock,
cursor uint32,
) ([]protocol.CompletionItem, error) {
option, found := fields.Options[entry.Key.Value]
@ -63,12 +64,19 @@ func GetOptionCompletions(
return nil, nil
}
if entry.Key.Value == "Match" {
return getMatchCompletions(
d,
matchBlock.MatchValue,
cursor-matchBlock.MatchEntry.Start.Character,
)
}
line := entry.OptionValue.Value
if entry.OptionValue == nil {
return option.FetchCompletions("", 0), nil
}
relativeCursor := common.CursorToCharacterIndex(cursor - entry.OptionValue.Start.Character)
line := entry.OptionValue.Value
return option.FetchCompletions(line, relativeCursor), nil
return option.FetchCompletions(line, common.CursorToCharacterIndex(cursor)), nil
}

View File

@ -0,0 +1,111 @@
package handlers
import (
sshdconfig "config-lsp/handlers/sshd_config"
"config-lsp/handlers/sshd_config/fields"
match_parser "config-lsp/handlers/sshd_config/fields/match-parser"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func getMatchCompletions(
d *sshdconfig.SSHDocument,
match *match_parser.Match,
cursor uint32,
) ([]protocol.CompletionItem, error) {
if len(match.Entries) == 0 {
completions := getMatchCriteriaCompletions()
completions = append(completions, getMatchAllKeywordCompletion())
return completions, nil
}
entry := match.GetEntryByCursor(cursor)
if entry == nil || entry.Criteria.IsCursorBetween(cursor) {
return getMatchCriteriaCompletions(), nil
}
return getMatchValueCompletions(entry, cursor), nil
}
func getMatchCriteriaCompletions() []protocol.CompletionItem {
kind := protocol.CompletionItemKindEnum
return []protocol.CompletionItem{
{
Label: string(match_parser.MatchCriteriaTypeUser),
Kind: &kind,
},
{
Label: string(match_parser.MatchCriteriaTypeGroup),
Kind: &kind,
},
{
Label: string(match_parser.MatchCriteriaTypeHost),
Kind: &kind,
},
{
Label: string(match_parser.MatchCriteriaTypeAddress),
Kind: &kind,
},
{
Label: string(match_parser.MatchCriteriaTypeLocalAddress),
Kind: &kind,
},
{
Label: string(match_parser.MatchCriteriaTypeLocalPort),
Kind: &kind,
},
{
Label: string(match_parser.MatchCriteriaTypeRDomain),
Kind: &kind,
},
}
}
func getMatchAllKeywordCompletion() protocol.CompletionItem {
kind := protocol.CompletionItemKindKeyword
return protocol.CompletionItem{
Label: "all",
Kind: &kind,
}
}
func getMatchValueCompletions(
entry *match_parser.MatchEntry,
cursor uint32,
) []protocol.CompletionItem {
value := entry.GetValueByCursor(entry.End.Character)
var line string
var relativeCursor uint32
if value != nil {
line = value.Value
relativeCursor = cursor - value.Start.Character
} else {
line = ""
relativeCursor = 0
}
switch entry.Criteria.Type {
case match_parser.MatchCriteriaTypeUser:
return fields.MatchUserField.FetchCompletions(line, relativeCursor)
case match_parser.MatchCriteriaTypeGroup:
return fields.MatchGroupField.FetchCompletions(line, relativeCursor)
case match_parser.MatchCriteriaTypeHost:
return fields.MatchHostField.FetchCompletions(line, relativeCursor)
case match_parser.MatchCriteriaTypeAddress:
return fields.MatchAddressField.FetchCompletions(line, relativeCursor)
case match_parser.MatchCriteriaTypeLocalAddress:
return fields.MatchLocalAddressField.FetchCompletions(line, relativeCursor)
case match_parser.MatchCriteriaTypeLocalPort:
return fields.MatchLocalPortField.FetchCompletions(line, relativeCursor)
case match_parser.MatchCriteriaTypeRDomain:
return fields.MatchRDomainField.FetchCompletions(line, relativeCursor)
}
return nil
}

View File

@ -14,7 +14,7 @@ var isEmptyPattern = regexp.MustCompile(`^\s*$`)
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
line := params.Position.Line
cursor := params.Position.Character
cursor := common.CursorToCharacterIndex(params.Position.Character)
d := sshdconfig.DocumentParserMap[params.TextDocument.URI]
@ -27,7 +27,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
if entry == nil ||
entry.Separator == nil ||
entry.Key == nil ||
(common.CursorToCharacterIndex(cursor)) <= entry.Key.End.Character {
cursor <= entry.Key.End.Character {
return handlers.GetRootCompletions(
d,
@ -37,10 +37,11 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
)
}
if entry.Separator != nil && cursor > entry.Separator.End.Character {
if entry.Separator != nil && cursor >= entry.Separator.End.Character {
return handlers.GetOptionCompletions(
d,
entry,
matchBlock,
cursor,
)
}