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; grammar Config;
lineStatement lineStatement
: (entry | (WHITESPACE? leadingComment) | WHITESPACE?) EOF : (entry | (leadingComment) | WHITESPACE?) EOF
; ;
entry entry
@ -17,7 +17,7 @@ key
; ;
value value
: (STRING WHITESPACE)? STRING WHITESPACE? : (STRING WHITESPACE)* STRING? WHITESPACE?
; ;
leadingComment leadingComment

View File

@ -95,31 +95,41 @@ func (s *sshParserListener) ExitEntry(ctx *parser.EntryContext) {
if s.sshContext.isKeyAMatchBlock { if s.sshContext.isKeyAMatchBlock {
// Add new match block // Add new match block
match := match_parser.NewMatch() var match *match_parser.Match
errors := match.Parse(s.sshContext.currentOption.OptionValue.Value, location.Start.Line)
if len(errors) > 0 { if s.sshContext.currentOption.OptionValue != nil {
for _, err := range errors { matchParser := match_parser.NewMatch()
s.Errors = append(s.Errors, common.LSPError{ errors := matchParser.Parse(
Range: err.Range.ShiftHorizontal(s.sshContext.currentOption.Start.Character), s.sshContext.currentOption.OptionValue.Value,
Err: err.Err,
})
}
} else {
matchBlock := &SSHMatchBlock{
LocationRange: location,
MatchEntry: s.sshContext.currentOption,
MatchValue: match,
Options: treemap.NewWith(gods.UInt32Comparator),
}
s.Config.Options.Put(
location.Start.Line, location.Start.Line,
matchBlock, s.sshContext.currentOption.OptionValue.Start.Character,
) )
s.sshContext.currentMatchBlock = matchBlock if len(errors) > 0 {
for _, err := range errors {
s.Errors = append(s.Errors, common.LSPError{
Range: err.Range.ShiftHorizontal(s.sshContext.currentOption.Start.Character),
Err: err.Err,
})
}
} else {
match = matchParser
}
} }
matchBlock := &SSHMatchBlock{
LocationRange: location,
MatchEntry: s.sshContext.currentOption,
MatchValue: match,
Options: treemap.NewWith(gods.UInt32Comparator),
}
s.Config.Options.Put(
location.Start.Line,
matchBlock,
)
s.sshContext.currentMatchBlock = matchBlock
s.sshContext.isKeyAMatchBlock = false s.sshContext.isKeyAMatchBlock = false
} else if s.sshContext.currentMatchBlock != nil { } else if s.sshContext.currentMatchBlock != nil {
s.sshContext.currentMatchBlock.Options.Put( s.sshContext.currentMatchBlock.Options.Put(

View File

@ -22,4 +22,4 @@ leadingComment
atn: 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.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{ 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, 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, 3, 0, 15, 8, 0, 1, 0, 1, 0, 3, 0, 19, 8, 0, 3, 2, 5, 7, 5, 1, 0, 1, 0, 1, 0, 3, 0, 16, 8, 0, 3, 0, 18, 8, 0, 1, 0, 1,
0, 21, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 26, 8, 1, 1, 1, 3, 1, 29, 8, 1, 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, 1, 3, 1, 38, 8, 1, 1, 2, 1, 1, 3, 1, 32, 8, 1, 1, 1, 3, 1, 35, 8, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4,
2, 1, 3, 1, 3, 1, 4, 1, 4, 3, 4, 46, 8, 4, 1, 4, 1, 4, 3, 4, 50, 8, 4, 1, 4, 5, 4, 43, 8, 4, 10, 4, 12, 4, 46, 9, 4, 1, 4, 3, 4, 49, 8, 4, 1,
1, 5, 1, 5, 3, 5, 54, 8, 5, 1, 5, 1, 5, 3, 5, 58, 8, 5, 4, 5, 60, 8, 5, 4, 3, 4, 52, 8, 4, 1, 5, 1, 5, 3, 5, 56, 8, 5, 1, 5, 1, 5, 3, 5, 60, 8,
11, 5, 12, 5, 61, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10, 0, 0, 71, 0, 20, 1, 5, 4, 5, 62, 8, 5, 11, 5, 12, 5, 63, 1, 5, 0, 0, 6, 0, 2, 4, 6, 8, 10,
0, 0, 0, 2, 25, 1, 0, 0, 0, 4, 39, 1, 0, 0, 0, 6, 41, 1, 0, 0, 0, 8, 45, 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, 10, 51, 1, 0, 0, 0, 12, 21, 3, 2, 1, 0, 13, 15, 5, 2, 0, 0, 1, 0, 0, 0, 8, 44, 1, 0, 0, 0, 10, 53, 1, 0, 0, 0, 12, 18, 3, 2, 1, 0,
14, 13, 1, 0, 0, 0, 14, 15, 1, 0, 0, 0, 15, 16, 1, 0, 0, 0, 16, 21, 3, 13, 18, 3, 10, 5, 0, 14, 16, 5, 2, 0, 0, 15, 14, 1, 0, 0, 0, 15, 16, 1,
10, 5, 0, 17, 19, 5, 2, 0, 0, 18, 17, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 0, 0, 0, 16, 18, 1, 0, 0, 0, 17, 12, 1, 0, 0, 0, 17, 13, 1, 0, 0, 0, 17,
21, 1, 0, 0, 0, 20, 12, 1, 0, 0, 0, 20, 14, 1, 0, 0, 0, 20, 18, 1, 0, 0, 15, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 20, 5, 0, 0, 1, 20, 1, 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, 0, 21, 23, 5, 2, 0, 0, 22, 21, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 25,
2, 0, 0, 25, 24, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 28, 1, 0, 0, 0, 27, 1, 0, 0, 0, 24, 26, 3, 6, 3, 0, 25, 24, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0,
29, 3, 6, 3, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, 0, 0, 0, 29, 31, 1, 0, 0, 26, 28, 1, 0, 0, 0, 27, 29, 3, 4, 2, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1,
0, 30, 32, 3, 4, 2, 0, 31, 30, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 34, 0, 0, 0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 8, 4, 0, 31, 30, 1, 0, 0, 0, 31,
1, 0, 0, 0, 33, 35, 3, 8, 4, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 32, 1, 0, 0, 0, 32, 34, 1, 0, 0, 0, 33, 35, 3, 10, 5, 0, 34, 33, 1, 0,
35, 37, 1, 0, 0, 0, 36, 38, 3, 10, 5, 0, 37, 36, 1, 0, 0, 0, 37, 38, 1, 0, 0, 34, 35, 1, 0, 0, 0, 35, 3, 1, 0, 0, 0, 36, 37, 5, 2, 0, 0, 37, 5,
0, 0, 0, 38, 3, 1, 0, 0, 0, 39, 40, 5, 2, 0, 0, 40, 5, 1, 0, 0, 0, 41, 1, 0, 0, 0, 38, 39, 5, 3, 0, 0, 39, 7, 1, 0, 0, 0, 40, 41, 5, 3, 0, 0,
42, 5, 3, 0, 0, 42, 7, 1, 0, 0, 0, 43, 44, 5, 3, 0, 0, 44, 46, 5, 2, 0, 41, 43, 5, 2, 0, 0, 42, 40, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1,
0, 45, 43, 1, 0, 0, 0, 45, 46, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 49, 0, 0, 0, 44, 45, 1, 0, 0, 0, 45, 48, 1, 0, 0, 0, 46, 44, 1, 0, 0, 0, 47,
5, 3, 0, 0, 48, 50, 5, 2, 0, 0, 49, 48, 1, 0, 0, 0, 49, 50, 1, 0, 0, 0, 49, 5, 3, 0, 0, 48, 47, 1, 0, 0, 0, 48, 49, 1, 0, 0, 0, 49, 51, 1, 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, 50, 52, 5, 2, 0, 0, 51, 50, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 9, 1,
0, 0, 53, 54, 1, 0, 0, 0, 54, 59, 1, 0, 0, 0, 55, 57, 5, 3, 0, 0, 56, 58, 0, 0, 0, 53, 55, 5, 1, 0, 0, 54, 56, 5, 2, 0, 0, 55, 54, 1, 0, 0, 0, 55,
5, 2, 0, 0, 57, 56, 1, 0, 0, 0, 57, 58, 1, 0, 0, 0, 58, 60, 1, 0, 0, 0, 56, 1, 0, 0, 0, 56, 61, 1, 0, 0, 0, 57, 59, 5, 3, 0, 0, 58, 60, 5, 2, 0,
59, 55, 1, 0, 0, 0, 60, 61, 1, 0, 0, 0, 61, 59, 1, 0, 0, 0, 61, 62, 1, 0, 59, 58, 1, 0, 0, 0, 59, 60, 1, 0, 0, 0, 60, 62, 1, 0, 0, 0, 61, 57,
0, 0, 0, 62, 11, 1, 0, 0, 0, 13, 14, 18, 20, 25, 28, 31, 34, 37, 45, 49, 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 61, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0,
53, 57, 61, 64, 11, 1, 0, 0, 0, 13, 15, 17, 22, 25, 28, 31, 34, 44, 48, 51, 55, 59,
63,
} }
deserializer := antlr.NewATNDeserializer(nil) deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN) staticData.atn = deserializer.Deserialize(staticData.serializedATN)
@ -240,13 +241,13 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) {
var _la int var _la int
p.EnterOuterAlt(localctx, 1) p.EnterOuterAlt(localctx, 1)
p.SetState(20) p.SetState(17)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit 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: case 1:
{ {
p.SetState(12) p.SetState(12)
@ -254,31 +255,13 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) {
} }
case 2: 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.SetState(13)
p.LeadingComment() p.LeadingComment()
} }
case 3: case 3:
p.SetState(18) p.SetState(15)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -287,7 +270,7 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) {
if _la == ConfigParserWHITESPACE { if _la == ConfigParserWHITESPACE {
{ {
p.SetState(17) p.SetState(14)
p.Match(ConfigParserWHITESPACE) p.Match(ConfigParserWHITESPACE)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
@ -301,7 +284,7 @@ func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) {
goto errorExit goto errorExit
} }
{ {
p.SetState(22) p.SetState(19)
p.Match(ConfigParserEOF) p.Match(ConfigParserEOF)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
@ -466,17 +449,29 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) {
var _la int var _la int
p.EnterOuterAlt(localctx, 1) 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.SetState(25)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 { if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 {
{ {
p.SetState(24) p.SetState(24)
p.Match(ConfigParserWHITESPACE) p.Key()
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
} }
} else if p.HasError() { // JIM } 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 { if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 4, p.GetParserRuleContext()) == 1 {
{ {
p.SetState(27) p.SetState(27)
p.Key() p.Separator()
} }
} else if p.HasError() { // JIM } else if p.HasError() { // JIM
@ -496,17 +491,15 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) {
} }
p.SetState(31) p.SetState(31)
p.GetErrorHandler().Sync(p) 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.SetState(30)
p.Separator() p.Value()
} }
} else if p.HasError() { // JIM
goto errorExit
} }
p.SetState(34) p.SetState(34)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
@ -515,23 +508,9 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) {
} }
_la = p.GetTokenStream().LA(1) _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 { if _la == ConfigParserHASH {
{ {
p.SetState(36) p.SetState(33)
p.LeadingComment() p.LeadingComment()
} }
@ -625,7 +604,7 @@ func (p *ConfigParser) Separator() (localctx ISeparatorContext) {
p.EnterRule(localctx, 4, ConfigParserRULE_separator) p.EnterRule(localctx, 4, ConfigParserRULE_separator)
p.EnterOuterAlt(localctx, 1) p.EnterOuterAlt(localctx, 1)
{ {
p.SetState(39) p.SetState(36)
p.Match(ConfigParserWHITESPACE) p.Match(ConfigParserWHITESPACE)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
@ -721,7 +700,7 @@ func (p *ConfigParser) Key() (localctx IKeyContext) {
p.EnterRule(localctx, 6, ConfigParserRULE_key) p.EnterRule(localctx, 6, ConfigParserRULE_key)
p.EnterOuterAlt(localctx, 1) p.EnterOuterAlt(localctx, 1)
{ {
p.SetState(41) p.SetState(38)
p.Match(ConfigParserSTRING) p.Match(ConfigParserSTRING)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
@ -832,40 +811,67 @@ func (p *ConfigParser) Value() (localctx IValueContext) {
p.EnterRule(localctx, 8, ConfigParserRULE_value) p.EnterRule(localctx, 8, ConfigParserRULE_value)
var _la int var _la int
p.EnterOuterAlt(localctx, 1) var _alt int
p.SetState(45)
p.GetErrorHandler().Sync(p)
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(40)
p.Match(ConfigParserSTRING)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
{
p.SetState(41)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
}
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(43) p.SetState(47)
p.Match(ConfigParserSTRING) p.Match(ConfigParserSTRING)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
goto errorExit goto errorExit
} }
} }
{
p.SetState(44)
p.Match(ConfigParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
} else if p.HasError() { // JIM
goto errorExit
} }
{ p.SetState(51)
p.SetState(47)
p.Match(ConfigParserSTRING)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
p.SetState(49)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -874,7 +880,7 @@ func (p *ConfigParser) Value() (localctx IValueContext) {
if _la == ConfigParserWHITESPACE { if _la == ConfigParserWHITESPACE {
{ {
p.SetState(48) p.SetState(50)
p.Match(ConfigParserWHITESPACE) p.Match(ConfigParserWHITESPACE)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
@ -994,14 +1000,14 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
p.EnterOuterAlt(localctx, 1) p.EnterOuterAlt(localctx, 1)
{ {
p.SetState(51) p.SetState(53)
p.Match(ConfigParserHASH) p.Match(ConfigParserHASH)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
goto errorExit goto errorExit
} }
} }
p.SetState(53) p.SetState(55)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -1010,7 +1016,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
if _la == ConfigParserWHITESPACE { if _la == ConfigParserWHITESPACE {
{ {
p.SetState(52) p.SetState(54)
p.Match(ConfigParserWHITESPACE) p.Match(ConfigParserWHITESPACE)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
@ -1019,7 +1025,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
} }
} }
p.SetState(59) p.SetState(61)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -1028,14 +1034,14 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
for ok := true; ok; ok = _la == ConfigParserSTRING { for ok := true; ok; ok = _la == ConfigParserSTRING {
{ {
p.SetState(55) p.SetState(57)
p.Match(ConfigParserSTRING) p.Match(ConfigParserSTRING)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
goto errorExit goto errorExit
} }
} }
p.SetState(57) p.SetState(59)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -1044,7 +1050,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
if _la == ConfigParserWHITESPACE { if _la == ConfigParserWHITESPACE {
{ {
p.SetState(56) p.SetState(58)
p.Match(ConfigParserWHITESPACE) p.Match(ConfigParserWHITESPACE)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
@ -1054,7 +1060,7 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) {
} }
p.SetState(61) p.SetState(63)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit 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) 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) 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( func TestMultipleMatchBlocks(
t *testing.T, t *testing.T,
) { ) {
@ -168,12 +227,12 @@ Match Address 192.168.0.2
} }
emptyOption, matchBlock := p.FindOption(uint32(5)) 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) 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)) 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) 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) 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) 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) 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) 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 OptionValue *SSHValue
} }
func (o SSHOption) GetType() SSHEntryType {
return SSHEntryTypeOption
}
func (o SSHOption) GetOption() SSHOption {
return o
}
type SSHMatchBlock struct { type SSHMatchBlock struct {
common.LocationRange common.LocationRange
MatchEntry *SSHOption MatchEntry *SSHOption
@ -59,66 +51,9 @@ type SSHMatchBlock struct {
Options *treemap.Map Options *treemap.Map
} }
func (m SSHMatchBlock) GetType() SSHEntryType {
return SSHEntryTypeMatchBlock
}
func (m SSHMatchBlock) GetOption() SSHOption {
return *m.MatchEntry
}
type SSHConfig struct { type SSHConfig struct {
// [uint32]SSHOption -> line number -> *SSHEntry // [uint32]SSHOption -> line number -> *SSHEntry
Options *treemap.Map Options *treemap.Map
// [uint32]{} -> line number -> {} // [uint32]{} -> line number -> {}
CommentLines map[uint32]struct{} 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 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. 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.`, 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{ Value: docvalues.StringValue{},
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{},
},
},
},
},
},
}, },
"MaxAuthTries": { "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.`, 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; grammar Match;
root root
: matchEntry? (WHITESPACE matchEntry)* EOF : matchEntry? (WHITESPACE matchEntry?)* EOF
; ;
matchEntry matchEntry
: criteria WHITESPACE? values? : criteria separator? values?
;
separator
: WHITESPACE
; ;
criteria criteria
: USER : (USER | GROUP | HOST| LOCALADDRESS | LOCALPORT | RDOMAIN | ADDRESS)
| GROUP
| HOST
| LOCALADDRESS
| LOCALPORT
| RDOMAIN
| ADDRESS
; ;
values values
: value? (COMMA value?)* : value (COMMA value?)*
; ;
value value

View File

@ -11,16 +11,19 @@ import (
func createMatchListenerContext( func createMatchListenerContext(
line uint32, line uint32,
startCharacter uint32,
) *matchListenerContext { ) *matchListenerContext {
return &matchListenerContext{ return &matchListenerContext{
currentEntry: nil, currentEntry: nil,
line: line, line: line,
startCharacter: startCharacter,
} }
} }
type matchListenerContext struct { type matchListenerContext struct {
currentEntry *MatchEntry currentEntry *MatchEntry
line uint32 line uint32
startCharacter uint32
} }
func createListener( func createListener(
@ -42,13 +45,12 @@ type matchParserListener struct {
} }
func (s *matchParserListener) EnterMatchEntry(ctx *parser.MatchEntryContext) { 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) location.ChangeBothLines(s.matchContext.line)
entry := &MatchEntry{ entry := &MatchEntry{
LocationRange: location, LocationRange: location,
Value: ctx.GetText(), Value: ctx.GetText(),
Values: make([]*MatchValue, 0),
} }
s.match.Entries = append(s.match.Entries, entry) s.match.Entries = append(s.match.Entries, entry)
@ -70,7 +72,7 @@ var availableCriteria = map[string]MatchCriteriaType{
} }
func (s *matchParserListener) EnterCriteria(ctx *parser.CriteriaContext) { 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) location.ChangeBothLines(s.matchContext.line)
criteria, found := availableCriteria[ctx.GetText()] criteria, found := availableCriteria[ctx.GetText()]
@ -83,11 +85,33 @@ func (s *matchParserListener) EnterCriteria(ctx *parser.CriteriaContext) {
return 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) { 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) location.ChangeBothLines(s.matchContext.line)
value := &MatchValue{ value := &MatchValue{
@ -95,5 +119,5 @@ func (s *matchParserListener) EnterValue(ctx *parser.ValueContext) {
Value: ctx.GetText(), 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 MatchCriteriaType
} }
type MatchSeparator struct {
common.LocationRange
}
type MatchValues struct {
common.LocationRange
Values []*MatchValue
}
type MatchEntry struct { type MatchEntry struct {
common.LocationRange common.LocationRange
Value string Value string
Criteria MatchCriteriaType Criteria MatchCriteria
Values []*MatchValue Separator *MatchSeparator
Values *MatchValues
} }
type MatchValue struct { 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( func (m *Match) Parse(
input string, input string,
line uint32, line uint32,
startCharacter uint32,
) []common.LSPError { ) []common.LSPError {
context := createMatchListenerContext(line) context := createMatchListenerContext(line, startCharacter)
stream := antlr.NewInputStream(input) stream := antlr.NewInputStream(input)

View File

@ -27,10 +27,11 @@ WHITESPACE
rule names: rule names:
root root
matchEntry matchEntry
separator
criteria criteria
values values
value value
atn: 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. // ExitMatchEntry is called when production matchEntry is exited.
func (s *BaseMatchListener) ExitMatchEntry(ctx *MatchEntryContext) {} 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. // EnterCriteria is called when production criteria is entered.
func (s *BaseMatchListener) EnterCriteria(ctx *CriteriaContext) {} func (s *BaseMatchListener) EnterCriteria(ctx *CriteriaContext) {}

View File

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

View File

@ -40,29 +40,30 @@ func matchParserInit() {
"ADDRESS", "COMMA", "STRING", "WHITESPACE", "ADDRESS", "COMMA", "STRING", "WHITESPACE",
} }
staticData.RuleNames = []string{ staticData.RuleNames = []string{
"root", "matchEntry", "criteria", "values", "value", "root", "matchEntry", "separator", "criteria", "values", "value",
} }
staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{ 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, 10, 50, 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, 4, 2, 5, 7, 5, 1, 0, 3, 0, 14, 8, 0, 1, 0, 1, 0, 3, 0, 18, 8, 0, 5, 0,
9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 25, 8, 1, 1, 1, 3, 1, 28, 8, 1, 1, 20, 8, 0, 10, 0, 12, 0, 23, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 29, 8,
2, 1, 2, 1, 3, 3, 3, 33, 8, 3, 1, 3, 1, 3, 3, 3, 37, 8, 3, 5, 3, 39, 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,
3, 10, 3, 12, 3, 42, 9, 3, 1, 4, 1, 4, 1, 4, 0, 0, 5, 0, 2, 4, 6, 8, 0, 41, 8, 4, 5, 4, 43, 8, 4, 10, 4, 12, 4, 46, 9, 4, 1, 5, 1, 5, 1, 5, 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, 0, 2, 4, 6, 8, 10, 0, 1, 1, 0, 1, 7, 50, 0, 13, 1, 0, 0, 0, 2, 26,
0, 6, 32, 1, 0, 0, 0, 8, 43, 1, 0, 0, 0, 10, 12, 3, 2, 1, 0, 11, 10, 1, 1, 0, 0, 0, 4, 33, 1, 0, 0, 0, 6, 35, 1, 0, 0, 0, 8, 37, 1, 0, 0, 0, 10,
0, 0, 0, 11, 12, 1, 0, 0, 0, 12, 17, 1, 0, 0, 0, 13, 14, 5, 10, 0, 0, 14, 47, 1, 0, 0, 0, 12, 14, 3, 2, 1, 0, 13, 12, 1, 0, 0, 0, 13, 14, 1, 0, 0,
16, 3, 2, 1, 0, 15, 13, 1, 0, 0, 0, 16, 19, 1, 0, 0, 0, 17, 15, 1, 0, 0, 0, 14, 21, 1, 0, 0, 0, 15, 17, 5, 10, 0, 0, 16, 18, 3, 2, 1, 0, 17, 16,
0, 17, 18, 1, 0, 0, 0, 18, 20, 1, 0, 0, 0, 19, 17, 1, 0, 0, 0, 20, 21, 1, 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 20, 1, 0, 0, 0, 19, 15, 1, 0, 0, 0,
5, 0, 0, 1, 21, 1, 1, 0, 0, 0, 22, 24, 3, 4, 2, 0, 23, 25, 5, 10, 0, 0, 20, 23, 1, 0, 0, 0, 21, 19, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 24, 1,
24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 27, 1, 0, 0, 0, 26, 28, 3, 0, 0, 0, 23, 21, 1, 0, 0, 0, 24, 25, 5, 0, 0, 1, 25, 1, 1, 0, 0, 0, 26,
6, 3, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 3, 1, 0, 0, 0, 29, 28, 3, 6, 3, 0, 27, 29, 3, 4, 2, 0, 28, 27, 1, 0, 0, 0, 28, 29, 1, 0, 0,
30, 7, 0, 0, 0, 30, 5, 1, 0, 0, 0, 31, 33, 3, 8, 4, 0, 32, 31, 1, 0, 0, 0, 29, 31, 1, 0, 0, 0, 30, 32, 3, 8, 4, 0, 31, 30, 1, 0, 0, 0, 31, 32,
0, 32, 33, 1, 0, 0, 0, 33, 40, 1, 0, 0, 0, 34, 36, 5, 8, 0, 0, 35, 37, 1, 0, 0, 0, 32, 3, 1, 0, 0, 0, 33, 34, 5, 10, 0, 0, 34, 5, 1, 0, 0, 0,
3, 8, 4, 0, 36, 35, 1, 0, 0, 0, 36, 37, 1, 0, 0, 0, 37, 39, 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,
38, 34, 1, 0, 0, 0, 39, 42, 1, 0, 0, 0, 40, 38, 1, 0, 0, 0, 40, 41, 1, 8, 0, 0, 39, 41, 3, 10, 5, 0, 40, 39, 1, 0, 0, 0, 40, 41, 1, 0, 0, 0, 41,
0, 0, 0, 41, 7, 1, 0, 0, 0, 42, 40, 1, 0, 0, 0, 43, 44, 5, 9, 0, 0, 44, 43, 1, 0, 0, 0, 42, 38, 1, 0, 0, 0, 43, 46, 1, 0, 0, 0, 44, 42, 1, 0, 0,
9, 1, 0, 0, 0, 7, 11, 17, 24, 27, 32, 36, 40, 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) deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN) staticData.atn = deserializer.Deserialize(staticData.serializedATN)
@ -117,9 +118,10 @@ const (
const ( const (
MatchParserRULE_root = 0 MatchParserRULE_root = 0
MatchParserRULE_matchEntry = 1 MatchParserRULE_matchEntry = 1
MatchParserRULE_criteria = 2 MatchParserRULE_separator = 2
MatchParserRULE_values = 3 MatchParserRULE_criteria = 3
MatchParserRULE_value = 4 MatchParserRULE_values = 4
MatchParserRULE_value = 5
) )
// IRootContext is an interface to support dynamic dispatch. // IRootContext is an interface to support dynamic dispatch.
@ -251,7 +253,7 @@ func (p *MatchParser) Root() (localctx IRootContext) {
var _la int var _la int
p.EnterOuterAlt(localctx, 1) p.EnterOuterAlt(localctx, 1)
p.SetState(11) p.SetState(13)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -260,12 +262,12 @@ func (p *MatchParser) Root() (localctx IRootContext) {
if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&254) != 0 { if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&254) != 0 {
{ {
p.SetState(10) p.SetState(12)
p.MatchEntry() p.MatchEntry()
} }
} }
p.SetState(17) p.SetState(21)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -274,19 +276,29 @@ func (p *MatchParser) Root() (localctx IRootContext) {
for _la == MatchParserWHITESPACE { for _la == MatchParserWHITESPACE {
{ {
p.SetState(13) p.SetState(15)
p.Match(MatchParserWHITESPACE) p.Match(MatchParserWHITESPACE)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
goto errorExit goto errorExit
} }
} }
{ p.SetState(17)
p.SetState(14) p.GetErrorHandler().Sync(p)
p.MatchEntry() if p.HasError() {
goto errorExit
}
_la = p.GetTokenStream().LA(1)
if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&254) != 0 {
{
p.SetState(16)
p.MatchEntry()
}
} }
p.SetState(19) p.SetState(23)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -294,7 +306,7 @@ func (p *MatchParser) Root() (localctx IRootContext) {
_la = p.GetTokenStream().LA(1) _la = p.GetTokenStream().LA(1)
} }
{ {
p.SetState(20) p.SetState(24)
p.Match(MatchParserEOF) p.Match(MatchParserEOF)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
@ -324,7 +336,7 @@ type IMatchEntryContext interface {
// Getter signatures // Getter signatures
Criteria() ICriteriaContext Criteria() ICriteriaContext
WHITESPACE() antlr.TerminalNode Separator() ISeparatorContext
Values() IValuesContext Values() IValuesContext
// IsMatchEntryContext differentiates from other interfaces. // IsMatchEntryContext differentiates from other interfaces.
@ -379,8 +391,20 @@ func (s *MatchEntryContext) Criteria() ICriteriaContext {
return t.(ICriteriaContext) return t.(ICriteriaContext)
} }
func (s *MatchEntryContext) WHITESPACE() antlr.TerminalNode { func (s *MatchEntryContext) Separator() ISeparatorContext {
return s.GetToken(MatchParserWHITESPACE, 0) 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 { func (s *MatchEntryContext) Values() IValuesContext {
@ -422,39 +446,135 @@ func (s *MatchEntryContext) ExitRule(listener antlr.ParseTreeListener) {
func (p *MatchParser) MatchEntry() (localctx IMatchEntryContext) { func (p *MatchParser) MatchEntry() (localctx IMatchEntryContext) {
localctx = NewMatchEntryContext(p, p.GetParserRuleContext(), p.GetState()) localctx = NewMatchEntryContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 2, MatchParserRULE_matchEntry) p.EnterRule(localctx, 2, MatchParserRULE_matchEntry)
var _la int
p.EnterOuterAlt(localctx, 1) p.EnterOuterAlt(localctx, 1)
{ {
p.SetState(22) p.SetState(26)
p.Criteria() p.Criteria()
} }
p.SetState(24) p.SetState(28)
p.GetErrorHandler().Sync(p)
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 2, p.GetParserRuleContext()) == 1 {
{
p.SetState(23)
p.Match(MatchParserWHITESPACE)
if p.HasError() {
// Recognition error - abort rule
goto errorExit
}
}
} else if p.HasError() { // JIM
goto errorExit
}
p.SetState(27)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 { if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 {
{ {
p.SetState(26) p.SetState(27)
p.Values() p.Separator()
} }
} else if p.HasError() { // JIM } else if p.HasError() { // JIM
goto errorExit 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
goto errorExit
}
}
errorExit: errorExit:
if p.HasError() { if p.HasError() {
@ -571,12 +691,12 @@ func (s *CriteriaContext) ExitRule(listener antlr.ParseTreeListener) {
func (p *MatchParser) Criteria() (localctx ICriteriaContext) { func (p *MatchParser) Criteria() (localctx ICriteriaContext) {
localctx = NewCriteriaContext(p, p.GetParserRuleContext(), p.GetState()) localctx = NewCriteriaContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 4, MatchParserRULE_criteria) p.EnterRule(localctx, 6, MatchParserRULE_criteria)
var _la int var _la int
p.EnterOuterAlt(localctx, 1) p.EnterOuterAlt(localctx, 1)
{ {
p.SetState(29) p.SetState(35)
_la = p.GetTokenStream().LA(1) _la = p.GetTokenStream().LA(1)
if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&254) != 0) { 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) { func (p *MatchParser) Values() (localctx IValuesContext) {
localctx = NewValuesContext(p, p.GetParserRuleContext(), p.GetState()) localctx = NewValuesContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 6, MatchParserRULE_values) p.EnterRule(localctx, 8, MatchParserRULE_values)
var _la int var _la int
p.EnterOuterAlt(localctx, 1) p.EnterOuterAlt(localctx, 1)
p.SetState(32) {
p.GetErrorHandler().Sync(p) p.SetState(37)
if p.HasError() { p.Value()
goto errorExit
} }
_la = p.GetTokenStream().LA(1) p.SetState(44)
if _la == MatchParserSTRING {
{
p.SetState(31)
p.Value()
}
}
p.SetState(40)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -747,14 +857,14 @@ func (p *MatchParser) Values() (localctx IValuesContext) {
for _la == MatchParserCOMMA { for _la == MatchParserCOMMA {
{ {
p.SetState(34) p.SetState(38)
p.Match(MatchParserCOMMA) p.Match(MatchParserCOMMA)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule
goto errorExit goto errorExit
} }
} }
p.SetState(36) p.SetState(40)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -763,13 +873,13 @@ func (p *MatchParser) Values() (localctx IValuesContext) {
if _la == MatchParserSTRING { if _la == MatchParserSTRING {
{ {
p.SetState(35) p.SetState(39)
p.Value() p.Value()
} }
} }
p.SetState(42) p.SetState(46)
p.GetErrorHandler().Sync(p) p.GetErrorHandler().Sync(p)
if p.HasError() { if p.HasError() {
goto errorExit goto errorExit
@ -862,10 +972,10 @@ func (s *ValueContext) ExitRule(listener antlr.ParseTreeListener) {
func (p *MatchParser) Value() (localctx IValueContext) { func (p *MatchParser) Value() (localctx IValueContext) {
localctx = NewValueContext(p, p.GetParserRuleContext(), p.GetState()) localctx = NewValueContext(p, p.GetParserRuleContext(), p.GetState())
p.EnterRule(localctx, 8, MatchParserRULE_value) p.EnterRule(localctx, 10, MatchParserRULE_value)
p.EnterOuterAlt(localctx, 1) p.EnterOuterAlt(localctx, 1)
{ {
p.SetState(43) p.SetState(47)
p.Match(MatchParserSTRING) p.Match(MatchParserSTRING)
if p.HasError() { if p.HasError() {
// Recognition error - abort rule // Recognition error - abort rule

View File

@ -7,10 +7,11 @@ import (
func TestComplexExample( func TestComplexExample(
t *testing.T, t *testing.T,
) { ) {
offset := uint32(5)
input := "User root,admin,alice Address *,!192.168.0.1" input := "User root,admin,alice Address *,!192.168.0.1"
match := NewMatch() match := NewMatch()
errors := match.Parse(input, 32) errors := match.Parse(input, 32, offset)
if len(errors) > 0 { if len(errors) > 0 {
t.Fatalf("Expected no errors, but got %v", errors) 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)) 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]) t.Fatalf("Expected User, but got %v", match.Entries[0])
} }
if !(match.Entries[0].Values[0].Value == "root") { 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.Fatalf("Expected root, but got %v", match.Entries[0].Values[0]) t.Errorf("Expected root, but got %v", match.Entries[0].Values.Values[0])
} }
if !(match.Entries[0].Values[1].Value == "admin") { 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.Fatalf("Expected admin, but got %v", match.Entries[0].Values[1]) t.Errorf("Expected admin, but got %v", match.Entries[0].Values.Values[1])
} }
if !(match.Entries[0].Values[2].Value == "alice") { 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.Fatalf("Expected alice, but got %v", match.Entries[0].Values[2]) t.Errorf("Expected alice, but got %v", match.Entries[0].Values.Values[2])
} }
if !(match.Entries[1].Criteria == MatchCriteriaTypeAddress) { if !(match.Entries[1].Criteria.Type == MatchCriteriaTypeAddress) {
t.Fatalf("Expected Address, but got %v", match.Entries[1]) t.Errorf("Expected Address, but got %v", match.Entries[1])
} }
if !(match.Entries[1].Values[0].Value == "*") { 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.Fatalf("Expected *, but got %v", match.Entries[1].Values[0]) t.Errorf("Expected *, but got %v", match.Entries[1].Values.Values[0])
} }
if !(match.Entries[1].Values[1].Value == "!192.168.0.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.Fatalf("Expected !192.168.0.1, but got %v", match.Entries[1].Values[1]) 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" input := "Address 172.22.100.0/24,172.22.5.0/24,127.0.0.1"
match := NewMatch() match := NewMatch()
errors := match.Parse(input, 0) errors := match.Parse(input, 0, 20)
if len(errors) > 0 { if len(errors) > 0 {
t.Fatalf("Expected no errors, but got %v", errors) 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)) 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]) t.Fatalf("Expected Address, but got %v", match.Entries[0])
} }
if !(len(match.Entries[0].Values) == 3) { if !(len(match.Entries[0].Values.Values) == 3) {
t.Fatalf("Expected 3 values, but got %v", len(match.Entries[0].Values)) 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") { 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[0]) 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 package fields
import docvalues "config-lsp/doc-values"
var MatchAllowedOptions = map[string]struct{}{ var MatchAllowedOptions = map[string]struct{}{
"AcceptEnv": {}, "AcceptEnv": {},
"AllowAgentForwarding": {}, "AllowAgentForwarding": {},
@ -61,3 +63,15 @@ var MatchAllowedOptions = map[string]struct{}{
"X11Forwarding": {}, "X11Forwarding": {},
"X11UseLocalhos": {}, "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( func GetOptionCompletions(
d *sshdconfig.SSHDocument, d *sshdconfig.SSHDocument,
entry *ast.SSHOption, entry *ast.SSHOption,
matchBlock *ast.SSHMatchBlock,
cursor uint32, cursor uint32,
) ([]protocol.CompletionItem, error) { ) ([]protocol.CompletionItem, error) {
option, found := fields.Options[entry.Key.Value] option, found := fields.Options[entry.Key.Value]
@ -63,12 +64,19 @@ func GetOptionCompletions(
return nil, nil 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 { if entry.OptionValue == nil {
return option.FetchCompletions("", 0), nil return option.FetchCompletions("", 0), nil
} }
relativeCursor := common.CursorToCharacterIndex(cursor - entry.OptionValue.Start.Character) return option.FetchCompletions(line, common.CursorToCharacterIndex(cursor)), nil
line := entry.OptionValue.Value
return option.FetchCompletions(line, relativeCursor), 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) { func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
line := params.Position.Line line := params.Position.Line
cursor := params.Position.Character cursor := common.CursorToCharacterIndex(params.Position.Character)
d := sshdconfig.DocumentParserMap[params.TextDocument.URI] d := sshdconfig.DocumentParserMap[params.TextDocument.URI]
@ -27,7 +27,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa
if entry == nil || if entry == nil ||
entry.Separator == nil || entry.Separator == nil ||
entry.Key == nil || entry.Key == nil ||
(common.CursorToCharacterIndex(cursor)) <= entry.Key.End.Character { cursor <= entry.Key.End.Character {
return handlers.GetRootCompletions( return handlers.GetRootCompletions(
d, 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( return handlers.GetOptionCompletions(
d, d,
entry, entry,
matchBlock,
cursor, cursor,
) )
} }