diff --git a/server/handlers/gitconfig/Config.g4 b/server/handlers/gitconfig/Config.g4 index b059136..7638fed 100644 --- a/server/handlers/gitconfig/Config.g4 +++ b/server/handlers/gitconfig/Config.g4 @@ -1,15 +1,15 @@ grammar Config; lineStatement - : entry EOF + : WHITESPACE? entry WHITESPACE? leadingComment? EOF ; entry - : WHITESPACE? key? separator? value? leadingComment? + : key? WHITESPACE? separator? WHITESPACE? value? ; leadingComment - : HASH WHITESPACE? (string WHITESPACE?)+ + : commentSymbol WHITESPACE? (string WHITESPACE?)+ ; key @@ -25,7 +25,11 @@ value ; string - : (QUOTED_STRING | STRING) + : (QUOTED_STRING | (WHITESPACE? (STRING WHITESPACE)* STRING)) + ; + +commentSymbol + : HASH | SEMICOLON ; /////////////////////////////////////////////// @@ -38,12 +42,16 @@ HASH : '#' ; +SEMICOLON + : ';' + ; + WHITESPACE : [ \t]+ ; STRING - : ~('#' | '\r' | '\n' | '"' | ' ' | '\t')+ + : ~('\r' | '\n' | '"' | ' ' | '\t')+ ; QUOTED_STRING diff --git a/server/handlers/gitconfig/ast/gitconfig.go b/server/handlers/gitconfig/ast/gitconfig.go index a9f8899..40778e7 100644 --- a/server/handlers/gitconfig/ast/gitconfig.go +++ b/server/handlers/gitconfig/ast/gitconfig.go @@ -17,31 +17,35 @@ type GitSeparator struct { Value parser.ParsedString } -type GitValue struct { +type GitValuePart struct { common.LocationRange Value parser.ParsedString } +type GitValue struct { + Raw common.VirtualLine + Value string +} + type GitEntry struct { common.LocationRange - Key *GitKey + Key *GitKey Separator *GitSeparator - Value *GitValue + Value *GitValue } type GitSectionHeader struct { common.LocationRange - Title *parser.ParsedString + Title string } type GitSection struct { common.LocationRange Entries *treemap.Map - Title *GitSectionHeader + Title *GitSectionHeader } type GitConfig struct { - Sections []*GitSection + Sections []*GitSection CommentLines map[uint32]struct{} } - diff --git a/server/handlers/gitconfig/ast/gitconfig_fields.go b/server/handlers/gitconfig/ast/gitconfig_fields.go index b5f59c1..854a446 100644 --- a/server/handlers/gitconfig/ast/gitconfig_fields.go +++ b/server/handlers/gitconfig/ast/gitconfig_fields.go @@ -2,5 +2,5 @@ package ast func (c *GitConfig) Clear() { c.Sections = []*GitSection{} + c.CommentLines = map[uint32]struct{}{} } - diff --git a/server/handlers/gitconfig/ast/listener.go b/server/handlers/gitconfig/ast/listener.go index 75f92f4..6040c02 100644 --- a/server/handlers/gitconfig/ast/listener.go +++ b/server/handlers/gitconfig/ast/listener.go @@ -7,21 +7,23 @@ import ( ) type gitconfigListenerContext struct { - line uint32 - currentSection *GitSection - currentEntry *GitEntry + line uint32 + currentSection *GitSection + currentEntry *GitEntry + isWaitingForNextSection bool + virtualLine common.VirtualLine } func createListenerContext() *gitconfigListenerContext { - context := new(gitconfigListenerContext) - - return context + return &gitconfigListenerContext{ + isWaitingForNextSection: false, + } } type gitconfigParserListener struct { *parser.BaseConfigListener - Config *GitConfig - Errors []common.LSPError + Config *GitConfig + Errors []common.LSPError gitconfigContext *gitconfigListenerContext } @@ -30,8 +32,8 @@ func createListener( context *gitconfigListenerContext, ) gitconfigParserListener { return gitconfigParserListener{ - Config: config, - Errors: make([]common.LSPError, 0), + Config: config, + Errors: make([]common.LSPError, 0), gitconfigContext: context, } } @@ -51,6 +53,10 @@ func (s *gitconfigParserListener) EnterEntry(ctx *parser.EntryContext) { } func (s *gitconfigParserListener) ExitEntry(ctx *parser.EntryContext) { + if s.gitconfigContext.currentEntry.Value != nil { + s.gitconfigContext.currentEntry.End = s.gitconfigContext.currentEntry.Value.Raw.End + } + s.gitconfigContext.currentEntry = nil } @@ -63,7 +69,7 @@ func (s *gitconfigParserListener) EnterKey(ctx *parser.KeyContext) { s.gitconfigContext.currentEntry.Key = &GitKey{ LocationRange: location, - Value: value, + Value: value, } } @@ -76,7 +82,7 @@ func (s *gitconfigParserListener) EnterSeparator(ctx *parser.SeparatorContext) { s.gitconfigContext.currentEntry.Separator = &GitSeparator{ LocationRange: location, - Value: value, + Value: value, } } @@ -84,12 +90,18 @@ func (s *gitconfigParserListener) EnterValue(ctx *parser.ValueContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.gitconfigContext.line) - text := ctx.GetText() - value := commonparser.ParseRawString(text, commonparser.FullFeatures) - + virtualLine := s.gitconfigContext.virtualLine.GetSubset(location.Start.Character, location.End.Character).AsTrimmed() + value := commonparser.ParseRawString( + virtualLine.GetText(), + commonparser.ParseFeatures{ + ParseDoubleQuotes: true, + TrimWhitespace: false, + ParseEscapedCharacters: false, + Replacements: &map[string]string{}, + }, + ).Value s.gitconfigContext.currentEntry.Value = &GitValue{ - LocationRange: location, + Raw: virtualLine, Value: value, } } - diff --git a/server/handlers/gitconfig/ast/parser.go b/server/handlers/gitconfig/ast/parser.go index 65dcd7d..6f8fb70 100644 --- a/server/handlers/gitconfig/ast/parser.go +++ b/server/handlers/gitconfig/ast/parser.go @@ -2,11 +2,15 @@ package ast import ( "config-lsp/common" - "config-lsp/utils" + "config-lsp/handlers/gitconfig/ast/parser" + "errors" "regexp" "strings" "github.com/antlr4-go/antlr/v4" + "github.com/emirpasic/gods/maps/treemap" + + gods "github.com/emirpasic/gods/utils" protocol "github.com/tliron/glsp/protocol_3_16" ) @@ -17,17 +21,20 @@ func NewGitConfig() *GitConfig { return config } -var commentPattern = regexp.MustCompile(`^\s*#`) +var commentPattern = regexp.MustCompile(`^\s*[#;]`) var emptyLinePattern = regexp.MustCompile(`^\s*$`) var headerPattern = regexp.MustCompile(`^\s*\[`) func (c *GitConfig) Parse(input string) []common.LSPError { - errors := make([]common.LSPError, 0) - lines := utils.SplitIntoLines(input) + errs := make([]common.LSPError, 0) + lines := common.SplitIntoVirtualLines(input) context := createListenerContext() - for rawLineNumber, line := range lines { - lineNumber := uint32(rawLineNumber) + for _, virtualLine := range lines { + lineNumber := uint32(virtualLine.Parts[0].Start.Line) + line := virtualLine.GetText() + context.line = lineNumber + context.virtualLine = virtualLine if emptyLinePattern.MatchString(line) { continue @@ -40,15 +47,41 @@ func (c *GitConfig) Parse(input string) []common.LSPError { if headerPattern.MatchString(line) { c.parseHeader(context, line) + + continue } - errors = append( - errors, + if context.currentSection == nil { + if !context.isWaitingForNextSection { + context.isWaitingForNextSection = true + + errs = append(errs, common.LSPError{ + Range: common.LocationRange{ + Start: common.Location{ + Line: lineNumber, + Character: 0, + }, + End: common.Location{ + Line: lineNumber, + Character: uint32(len(line)), + }, + }, + Err: errors.New(`This section is missing a header (e.g. "[section]")`), + }) + } + + continue + } + + context.isWaitingForNextSection = false + + errs = append( + errs, c.parseStatement(context, line)..., ) } - return errors + return errs } func (c *GitConfig) parseHeader( @@ -112,6 +145,26 @@ func (c *GitConfig) parseHeader( } } + location := common.LocationRange{ + Start: common.Location{ + Line: context.line, + Character: uint32(leftBracketIndex), + }, + End: common.Location{ + Line: context.line, + Character: uint32(rightBracketIndex + 1), + }, + } + context.currentSection = &GitSection{ + LocationRange: location, + Title: &GitSectionHeader{ + LocationRange: location, + Title: input[leftBracketIndex+1 : rightBracketIndex], + }, + Entries: treemap.NewWith(gods.UInt32Comparator), + } + c.Sections = append(c.Sections, context.currentSection) + return nil } @@ -145,4 +198,3 @@ func (c *GitConfig) parseStatement( return errors } - diff --git a/server/handlers/gitconfig/ast/parser/Config.interp b/server/handlers/gitconfig/ast/parser/Config.interp index 832bd92..3648d06 100644 --- a/server/handlers/gitconfig/ast/parser/Config.interp +++ b/server/handlers/gitconfig/ast/parser/Config.interp @@ -2,6 +2,7 @@ token literal names: null '=' '#' +';' null null null @@ -10,6 +11,7 @@ token symbolic names: null EQUAL HASH +SEMICOLON WHITESPACE STRING QUOTED_STRING @@ -22,7 +24,8 @@ key separator value string +commentSymbol atn: -[4, 1, 5, 53, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 1, 0, 1, 0, 1, 0, 1, 1, 3, 1, 19, 8, 1, 1, 1, 3, 1, 22, 8, 1, 1, 1, 3, 1, 25, 8, 1, 1, 1, 3, 1, 28, 8, 1, 1, 1, 3, 1, 31, 8, 1, 1, 2, 1, 2, 3, 2, 35, 8, 2, 1, 2, 1, 2, 3, 2, 39, 8, 2, 4, 2, 41, 8, 2, 11, 2, 12, 2, 42, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 0, 0, 7, 0, 2, 4, 6, 8, 10, 12, 0, 1, 1, 0, 4, 5, 53, 0, 14, 1, 0, 0, 0, 2, 18, 1, 0, 0, 0, 4, 32, 1, 0, 0, 0, 6, 44, 1, 0, 0, 0, 8, 46, 1, 0, 0, 0, 10, 48, 1, 0, 0, 0, 12, 50, 1, 0, 0, 0, 14, 15, 3, 2, 1, 0, 15, 16, 5, 0, 0, 1, 16, 1, 1, 0, 0, 0, 17, 19, 5, 3, 0, 0, 18, 17, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 21, 1, 0, 0, 0, 20, 22, 3, 6, 3, 0, 21, 20, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 24, 1, 0, 0, 0, 23, 25, 3, 8, 4, 0, 24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 27, 1, 0, 0, 0, 26, 28, 3, 10, 5, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 30, 1, 0, 0, 0, 29, 31, 3, 4, 2, 0, 30, 29, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 3, 1, 0, 0, 0, 32, 34, 5, 2, 0, 0, 33, 35, 5, 3, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 40, 1, 0, 0, 0, 36, 38, 3, 12, 6, 0, 37, 39, 5, 3, 0, 0, 38, 37, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 41, 1, 0, 0, 0, 40, 36, 1, 0, 0, 0, 41, 42, 1, 0, 0, 0, 42, 40, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 5, 1, 0, 0, 0, 44, 45, 3, 12, 6, 0, 45, 7, 1, 0, 0, 0, 46, 47, 5, 1, 0, 0, 47, 9, 1, 0, 0, 0, 48, 49, 3, 12, 6, 0, 49, 11, 1, 0, 0, 0, 50, 51, 7, 0, 0, 0, 51, 13, 1, 0, 0, 0, 8, 18, 21, 24, 27, 30, 34, 38, 42] \ No newline at end of file +[4, 1, 6, 78, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 1, 0, 3, 0, 18, 8, 0, 1, 0, 1, 0, 3, 0, 22, 8, 0, 1, 0, 3, 0, 25, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 30, 8, 1, 1, 1, 3, 1, 33, 8, 1, 1, 1, 3, 1, 36, 8, 1, 1, 1, 3, 1, 39, 8, 1, 1, 1, 3, 1, 42, 8, 1, 1, 2, 1, 2, 3, 2, 46, 8, 2, 1, 2, 1, 2, 3, 2, 50, 8, 2, 4, 2, 52, 8, 2, 11, 2, 12, 2, 53, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 3, 6, 64, 8, 6, 1, 6, 1, 6, 5, 6, 68, 8, 6, 10, 6, 12, 6, 71, 9, 6, 1, 6, 3, 6, 74, 8, 6, 1, 7, 1, 7, 1, 7, 0, 0, 8, 0, 2, 4, 6, 8, 10, 12, 14, 0, 1, 1, 0, 2, 3, 83, 0, 17, 1, 0, 0, 0, 2, 29, 1, 0, 0, 0, 4, 43, 1, 0, 0, 0, 6, 55, 1, 0, 0, 0, 8, 57, 1, 0, 0, 0, 10, 59, 1, 0, 0, 0, 12, 73, 1, 0, 0, 0, 14, 75, 1, 0, 0, 0, 16, 18, 5, 4, 0, 0, 17, 16, 1, 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 21, 3, 2, 1, 0, 20, 22, 5, 4, 0, 0, 21, 20, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 24, 1, 0, 0, 0, 23, 25, 3, 4, 2, 0, 24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 27, 5, 0, 0, 1, 27, 1, 1, 0, 0, 0, 28, 30, 3, 6, 3, 0, 29, 28, 1, 0, 0, 0, 29, 30, 1, 0, 0, 0, 30, 32, 1, 0, 0, 0, 31, 33, 5, 4, 0, 0, 32, 31, 1, 0, 0, 0, 32, 33, 1, 0, 0, 0, 33, 35, 1, 0, 0, 0, 34, 36, 3, 8, 4, 0, 35, 34, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 38, 1, 0, 0, 0, 37, 39, 5, 4, 0, 0, 38, 37, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 41, 1, 0, 0, 0, 40, 42, 3, 10, 5, 0, 41, 40, 1, 0, 0, 0, 41, 42, 1, 0, 0, 0, 42, 3, 1, 0, 0, 0, 43, 45, 3, 14, 7, 0, 44, 46, 5, 4, 0, 0, 45, 44, 1, 0, 0, 0, 45, 46, 1, 0, 0, 0, 46, 51, 1, 0, 0, 0, 47, 49, 3, 12, 6, 0, 48, 50, 5, 4, 0, 0, 49, 48, 1, 0, 0, 0, 49, 50, 1, 0, 0, 0, 50, 52, 1, 0, 0, 0, 51, 47, 1, 0, 0, 0, 52, 53, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0, 53, 54, 1, 0, 0, 0, 54, 5, 1, 0, 0, 0, 55, 56, 3, 12, 6, 0, 56, 7, 1, 0, 0, 0, 57, 58, 5, 1, 0, 0, 58, 9, 1, 0, 0, 0, 59, 60, 3, 12, 6, 0, 60, 11, 1, 0, 0, 0, 61, 74, 5, 6, 0, 0, 62, 64, 5, 4, 0, 0, 63, 62, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0, 64, 69, 1, 0, 0, 0, 65, 66, 5, 5, 0, 0, 66, 68, 5, 4, 0, 0, 67, 65, 1, 0, 0, 0, 68, 71, 1, 0, 0, 0, 69, 67, 1, 0, 0, 0, 69, 70, 1, 0, 0, 0, 70, 72, 1, 0, 0, 0, 71, 69, 1, 0, 0, 0, 72, 74, 5, 5, 0, 0, 73, 61, 1, 0, 0, 0, 73, 63, 1, 0, 0, 0, 74, 13, 1, 0, 0, 0, 75, 76, 7, 0, 0, 0, 76, 15, 1, 0, 0, 0, 14, 17, 21, 24, 29, 32, 35, 38, 41, 45, 49, 53, 63, 69, 73] \ No newline at end of file diff --git a/server/handlers/gitconfig/ast/parser/Config.tokens b/server/handlers/gitconfig/ast/parser/Config.tokens index 0ee5167..426a900 100644 --- a/server/handlers/gitconfig/ast/parser/Config.tokens +++ b/server/handlers/gitconfig/ast/parser/Config.tokens @@ -1,7 +1,9 @@ EQUAL=1 HASH=2 -WHITESPACE=3 -STRING=4 -QUOTED_STRING=5 +SEMICOLON=3 +WHITESPACE=4 +STRING=5 +QUOTED_STRING=6 '='=1 '#'=2 +';'=3 diff --git a/server/handlers/gitconfig/ast/parser/ConfigLexer.interp b/server/handlers/gitconfig/ast/parser/ConfigLexer.interp index 0daf76c..7b53b78 100644 --- a/server/handlers/gitconfig/ast/parser/ConfigLexer.interp +++ b/server/handlers/gitconfig/ast/parser/ConfigLexer.interp @@ -2,6 +2,7 @@ token literal names: null '=' '#' +';' null null null @@ -10,6 +11,7 @@ token symbolic names: null EQUAL HASH +SEMICOLON WHITESPACE STRING QUOTED_STRING @@ -17,6 +19,7 @@ QUOTED_STRING rule names: EQUAL HASH +SEMICOLON WHITESPACE STRING QUOTED_STRING @@ -29,4 +32,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 5, 43, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 4, 2, 17, 8, 2, 11, 2, 12, 2, 18, 1, 3, 4, 3, 22, 8, 3, 11, 3, 12, 3, 23, 1, 4, 1, 4, 3, 4, 28, 8, 4, 1, 4, 1, 4, 1, 4, 5, 4, 33, 8, 4, 10, 4, 12, 4, 36, 9, 4, 1, 4, 3, 4, 39, 8, 4, 1, 4, 3, 4, 42, 8, 4, 0, 0, 5, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 1, 0, 2, 2, 0, 9, 9, 32, 32, 4, 0, 9, 10, 13, 13, 32, 32, 34, 35, 48, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 1, 11, 1, 0, 0, 0, 3, 13, 1, 0, 0, 0, 5, 16, 1, 0, 0, 0, 7, 21, 1, 0, 0, 0, 9, 25, 1, 0, 0, 0, 11, 12, 5, 61, 0, 0, 12, 2, 1, 0, 0, 0, 13, 14, 5, 35, 0, 0, 14, 4, 1, 0, 0, 0, 15, 17, 7, 0, 0, 0, 16, 15, 1, 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 16, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 6, 1, 0, 0, 0, 20, 22, 8, 1, 0, 0, 21, 20, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 21, 1, 0, 0, 0, 23, 24, 1, 0, 0, 0, 24, 8, 1, 0, 0, 0, 25, 27, 5, 34, 0, 0, 26, 28, 3, 5, 2, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 34, 1, 0, 0, 0, 29, 30, 3, 7, 3, 0, 30, 31, 3, 5, 2, 0, 31, 33, 1, 0, 0, 0, 32, 29, 1, 0, 0, 0, 33, 36, 1, 0, 0, 0, 34, 32, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 38, 1, 0, 0, 0, 36, 34, 1, 0, 0, 0, 37, 39, 3, 7, 3, 0, 38, 37, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 41, 1, 0, 0, 0, 40, 42, 5, 34, 0, 0, 41, 40, 1, 0, 0, 0, 41, 42, 1, 0, 0, 0, 42, 10, 1, 0, 0, 0, 7, 0, 18, 23, 27, 34, 38, 41, 0] \ No newline at end of file +[4, 0, 6, 47, 6, -1, 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, 1, 1, 1, 1, 2, 1, 2, 1, 3, 4, 3, 21, 8, 3, 11, 3, 12, 3, 22, 1, 4, 4, 4, 26, 8, 4, 11, 4, 12, 4, 27, 1, 5, 1, 5, 3, 5, 32, 8, 5, 1, 5, 1, 5, 1, 5, 5, 5, 37, 8, 5, 10, 5, 12, 5, 40, 9, 5, 1, 5, 3, 5, 43, 8, 5, 1, 5, 3, 5, 46, 8, 5, 0, 0, 6, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 1, 0, 2, 2, 0, 9, 9, 32, 32, 4, 0, 9, 10, 13, 13, 32, 32, 34, 34, 52, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 1, 13, 1, 0, 0, 0, 3, 15, 1, 0, 0, 0, 5, 17, 1, 0, 0, 0, 7, 20, 1, 0, 0, 0, 9, 25, 1, 0, 0, 0, 11, 29, 1, 0, 0, 0, 13, 14, 5, 61, 0, 0, 14, 2, 1, 0, 0, 0, 15, 16, 5, 35, 0, 0, 16, 4, 1, 0, 0, 0, 17, 18, 5, 59, 0, 0, 18, 6, 1, 0, 0, 0, 19, 21, 7, 0, 0, 0, 20, 19, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 20, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 8, 1, 0, 0, 0, 24, 26, 8, 1, 0, 0, 25, 24, 1, 0, 0, 0, 26, 27, 1, 0, 0, 0, 27, 25, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 10, 1, 0, 0, 0, 29, 31, 5, 34, 0, 0, 30, 32, 3, 7, 3, 0, 31, 30, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 38, 1, 0, 0, 0, 33, 34, 3, 9, 4, 0, 34, 35, 3, 7, 3, 0, 35, 37, 1, 0, 0, 0, 36, 33, 1, 0, 0, 0, 37, 40, 1, 0, 0, 0, 38, 36, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 42, 1, 0, 0, 0, 40, 38, 1, 0, 0, 0, 41, 43, 3, 9, 4, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 45, 1, 0, 0, 0, 44, 46, 5, 34, 0, 0, 45, 44, 1, 0, 0, 0, 45, 46, 1, 0, 0, 0, 46, 12, 1, 0, 0, 0, 7, 0, 22, 27, 31, 38, 42, 45, 0] \ No newline at end of file diff --git a/server/handlers/gitconfig/ast/parser/ConfigLexer.tokens b/server/handlers/gitconfig/ast/parser/ConfigLexer.tokens index 0ee5167..426a900 100644 --- a/server/handlers/gitconfig/ast/parser/ConfigLexer.tokens +++ b/server/handlers/gitconfig/ast/parser/ConfigLexer.tokens @@ -1,7 +1,9 @@ EQUAL=1 HASH=2 -WHITESPACE=3 -STRING=4 -QUOTED_STRING=5 +SEMICOLON=3 +WHITESPACE=4 +STRING=5 +QUOTED_STRING=6 '='=1 '#'=2 +';'=3 diff --git a/server/handlers/gitconfig/ast/parser/config_base_listener.go b/server/handlers/gitconfig/ast/parser/config_base_listener.go index fc6b18a..7c94c03 100644 --- a/server/handlers/gitconfig/ast/parser/config_base_listener.go +++ b/server/handlers/gitconfig/ast/parser/config_base_listener.go @@ -62,3 +62,9 @@ func (s *BaseConfigListener) EnterString(ctx *StringContext) {} // ExitString is called when production string is exited. func (s *BaseConfigListener) ExitString(ctx *StringContext) {} + +// EnterCommentSymbol is called when production commentSymbol is entered. +func (s *BaseConfigListener) EnterCommentSymbol(ctx *CommentSymbolContext) {} + +// ExitCommentSymbol is called when production commentSymbol is exited. +func (s *BaseConfigListener) ExitCommentSymbol(ctx *CommentSymbolContext) {} diff --git a/server/handlers/gitconfig/ast/parser/config_lexer.go b/server/handlers/gitconfig/ast/parser/config_lexer.go index 6ac413d..2a3769d 100644 --- a/server/handlers/gitconfig/ast/parser/config_lexer.go +++ b/server/handlers/gitconfig/ast/parser/config_lexer.go @@ -43,36 +43,37 @@ func configlexerLexerInit() { "DEFAULT_MODE", } staticData.LiteralNames = []string{ - "", "'='", "'#'", + "", "'='", "'#'", "';'", } staticData.SymbolicNames = []string{ - "", "EQUAL", "HASH", "WHITESPACE", "STRING", "QUOTED_STRING", + "", "EQUAL", "HASH", "SEMICOLON", "WHITESPACE", "STRING", "QUOTED_STRING", } staticData.RuleNames = []string{ - "EQUAL", "HASH", "WHITESPACE", "STRING", "QUOTED_STRING", + "EQUAL", "HASH", "SEMICOLON", "WHITESPACE", "STRING", "QUOTED_STRING", } staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.serializedATN = []int32{ - 4, 0, 5, 43, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, - 4, 7, 4, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 4, 2, 17, 8, 2, 11, 2, 12, 2, 18, - 1, 3, 4, 3, 22, 8, 3, 11, 3, 12, 3, 23, 1, 4, 1, 4, 3, 4, 28, 8, 4, 1, - 4, 1, 4, 1, 4, 5, 4, 33, 8, 4, 10, 4, 12, 4, 36, 9, 4, 1, 4, 3, 4, 39, - 8, 4, 1, 4, 3, 4, 42, 8, 4, 0, 0, 5, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 1, 0, - 2, 2, 0, 9, 9, 32, 32, 4, 0, 9, 10, 13, 13, 32, 32, 34, 35, 48, 0, 1, 1, - 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, - 0, 0, 0, 1, 11, 1, 0, 0, 0, 3, 13, 1, 0, 0, 0, 5, 16, 1, 0, 0, 0, 7, 21, - 1, 0, 0, 0, 9, 25, 1, 0, 0, 0, 11, 12, 5, 61, 0, 0, 12, 2, 1, 0, 0, 0, - 13, 14, 5, 35, 0, 0, 14, 4, 1, 0, 0, 0, 15, 17, 7, 0, 0, 0, 16, 15, 1, - 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 16, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, - 6, 1, 0, 0, 0, 20, 22, 8, 1, 0, 0, 21, 20, 1, 0, 0, 0, 22, 23, 1, 0, 0, - 0, 23, 21, 1, 0, 0, 0, 23, 24, 1, 0, 0, 0, 24, 8, 1, 0, 0, 0, 25, 27, 5, - 34, 0, 0, 26, 28, 3, 5, 2, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, - 34, 1, 0, 0, 0, 29, 30, 3, 7, 3, 0, 30, 31, 3, 5, 2, 0, 31, 33, 1, 0, 0, - 0, 32, 29, 1, 0, 0, 0, 33, 36, 1, 0, 0, 0, 34, 32, 1, 0, 0, 0, 34, 35, - 1, 0, 0, 0, 35, 38, 1, 0, 0, 0, 36, 34, 1, 0, 0, 0, 37, 39, 3, 7, 3, 0, - 38, 37, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 41, 1, 0, 0, 0, 40, 42, 5, - 34, 0, 0, 41, 40, 1, 0, 0, 0, 41, 42, 1, 0, 0, 0, 42, 10, 1, 0, 0, 0, 7, - 0, 18, 23, 27, 34, 38, 41, 0, + 4, 0, 6, 47, 6, -1, 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, 1, 1, 1, 1, 2, 1, 2, 1, 3, 4, 3, 21, + 8, 3, 11, 3, 12, 3, 22, 1, 4, 4, 4, 26, 8, 4, 11, 4, 12, 4, 27, 1, 5, 1, + 5, 3, 5, 32, 8, 5, 1, 5, 1, 5, 1, 5, 5, 5, 37, 8, 5, 10, 5, 12, 5, 40, + 9, 5, 1, 5, 3, 5, 43, 8, 5, 1, 5, 3, 5, 46, 8, 5, 0, 0, 6, 1, 1, 3, 2, + 5, 3, 7, 4, 9, 5, 11, 6, 1, 0, 2, 2, 0, 9, 9, 32, 32, 4, 0, 9, 10, 13, + 13, 32, 32, 34, 34, 52, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, + 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 1, 13, 1, + 0, 0, 0, 3, 15, 1, 0, 0, 0, 5, 17, 1, 0, 0, 0, 7, 20, 1, 0, 0, 0, 9, 25, + 1, 0, 0, 0, 11, 29, 1, 0, 0, 0, 13, 14, 5, 61, 0, 0, 14, 2, 1, 0, 0, 0, + 15, 16, 5, 35, 0, 0, 16, 4, 1, 0, 0, 0, 17, 18, 5, 59, 0, 0, 18, 6, 1, + 0, 0, 0, 19, 21, 7, 0, 0, 0, 20, 19, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, + 20, 1, 0, 0, 0, 22, 23, 1, 0, 0, 0, 23, 8, 1, 0, 0, 0, 24, 26, 8, 1, 0, + 0, 25, 24, 1, 0, 0, 0, 26, 27, 1, 0, 0, 0, 27, 25, 1, 0, 0, 0, 27, 28, + 1, 0, 0, 0, 28, 10, 1, 0, 0, 0, 29, 31, 5, 34, 0, 0, 30, 32, 3, 7, 3, 0, + 31, 30, 1, 0, 0, 0, 31, 32, 1, 0, 0, 0, 32, 38, 1, 0, 0, 0, 33, 34, 3, + 9, 4, 0, 34, 35, 3, 7, 3, 0, 35, 37, 1, 0, 0, 0, 36, 33, 1, 0, 0, 0, 37, + 40, 1, 0, 0, 0, 38, 36, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 42, 1, 0, 0, + 0, 40, 38, 1, 0, 0, 0, 41, 43, 3, 9, 4, 0, 42, 41, 1, 0, 0, 0, 42, 43, + 1, 0, 0, 0, 43, 45, 1, 0, 0, 0, 44, 46, 5, 34, 0, 0, 45, 44, 1, 0, 0, 0, + 45, 46, 1, 0, 0, 0, 46, 12, 1, 0, 0, 0, 7, 0, 22, 27, 31, 38, 42, 45, 0, } deserializer := antlr.NewATNDeserializer(nil) staticData.atn = deserializer.Deserialize(staticData.serializedATN) @@ -115,7 +116,8 @@ func NewConfigLexer(input antlr.CharStream) *ConfigLexer { const ( ConfigLexerEQUAL = 1 ConfigLexerHASH = 2 - ConfigLexerWHITESPACE = 3 - ConfigLexerSTRING = 4 - ConfigLexerQUOTED_STRING = 5 + ConfigLexerSEMICOLON = 3 + ConfigLexerWHITESPACE = 4 + ConfigLexerSTRING = 5 + ConfigLexerQUOTED_STRING = 6 ) diff --git a/server/handlers/gitconfig/ast/parser/config_listener.go b/server/handlers/gitconfig/ast/parser/config_listener.go index 83428cd..633ca49 100644 --- a/server/handlers/gitconfig/ast/parser/config_listener.go +++ b/server/handlers/gitconfig/ast/parser/config_listener.go @@ -29,6 +29,9 @@ type ConfigListener interface { // EnterString is called when entering the string production. EnterString(c *StringContext) + // EnterCommentSymbol is called when entering the commentSymbol production. + EnterCommentSymbol(c *CommentSymbolContext) + // ExitLineStatement is called when exiting the lineStatement production. ExitLineStatement(c *LineStatementContext) @@ -49,4 +52,7 @@ type ConfigListener interface { // ExitString is called when exiting the string production. ExitString(c *StringContext) + + // ExitCommentSymbol is called when exiting the commentSymbol production. + ExitCommentSymbol(c *CommentSymbolContext) } diff --git a/server/handlers/gitconfig/ast/parser/config_parser.go b/server/handlers/gitconfig/ast/parser/config_parser.go index 79a75b9..7b6610b 100644 --- a/server/handlers/gitconfig/ast/parser/config_parser.go +++ b/server/handlers/gitconfig/ast/parser/config_parser.go @@ -33,38 +33,50 @@ var ConfigParserStaticData struct { func configParserInit() { staticData := &ConfigParserStaticData staticData.LiteralNames = []string{ - "", "'='", "'#'", + "", "'='", "'#'", "';'", } staticData.SymbolicNames = []string{ - "", "EQUAL", "HASH", "WHITESPACE", "STRING", "QUOTED_STRING", + "", "EQUAL", "HASH", "SEMICOLON", "WHITESPACE", "STRING", "QUOTED_STRING", } staticData.RuleNames = []string{ "lineStatement", "entry", "leadingComment", "key", "separator", "value", - "string", + "string", "commentSymbol", } staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.serializedATN = []int32{ - 4, 1, 5, 53, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, - 2, 5, 7, 5, 2, 6, 7, 6, 1, 0, 1, 0, 1, 0, 1, 1, 3, 1, 19, 8, 1, 1, 1, 3, - 1, 22, 8, 1, 1, 1, 3, 1, 25, 8, 1, 1, 1, 3, 1, 28, 8, 1, 1, 1, 3, 1, 31, - 8, 1, 1, 2, 1, 2, 3, 2, 35, 8, 2, 1, 2, 1, 2, 3, 2, 39, 8, 2, 4, 2, 41, - 8, 2, 11, 2, 12, 2, 42, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, - 1, 6, 0, 0, 7, 0, 2, 4, 6, 8, 10, 12, 0, 1, 1, 0, 4, 5, 53, 0, 14, 1, 0, - 0, 0, 2, 18, 1, 0, 0, 0, 4, 32, 1, 0, 0, 0, 6, 44, 1, 0, 0, 0, 8, 46, 1, - 0, 0, 0, 10, 48, 1, 0, 0, 0, 12, 50, 1, 0, 0, 0, 14, 15, 3, 2, 1, 0, 15, - 16, 5, 0, 0, 1, 16, 1, 1, 0, 0, 0, 17, 19, 5, 3, 0, 0, 18, 17, 1, 0, 0, - 0, 18, 19, 1, 0, 0, 0, 19, 21, 1, 0, 0, 0, 20, 22, 3, 6, 3, 0, 21, 20, - 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 24, 1, 0, 0, 0, 23, 25, 3, 8, 4, 0, - 24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, 27, 1, 0, 0, 0, 26, 28, 3, - 10, 5, 0, 27, 26, 1, 0, 0, 0, 27, 28, 1, 0, 0, 0, 28, 30, 1, 0, 0, 0, 29, - 31, 3, 4, 2, 0, 30, 29, 1, 0, 0, 0, 30, 31, 1, 0, 0, 0, 31, 3, 1, 0, 0, - 0, 32, 34, 5, 2, 0, 0, 33, 35, 5, 3, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, - 1, 0, 0, 0, 35, 40, 1, 0, 0, 0, 36, 38, 3, 12, 6, 0, 37, 39, 5, 3, 0, 0, - 38, 37, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 41, 1, 0, 0, 0, 40, 36, 1, - 0, 0, 0, 41, 42, 1, 0, 0, 0, 42, 40, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, - 5, 1, 0, 0, 0, 44, 45, 3, 12, 6, 0, 45, 7, 1, 0, 0, 0, 46, 47, 5, 1, 0, - 0, 47, 9, 1, 0, 0, 0, 48, 49, 3, 12, 6, 0, 49, 11, 1, 0, 0, 0, 50, 51, - 7, 0, 0, 0, 51, 13, 1, 0, 0, 0, 8, 18, 21, 24, 27, 30, 34, 38, 42, + 4, 1, 6, 78, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, + 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 1, 0, 3, 0, 18, 8, 0, 1, 0, 1, 0, 3, + 0, 22, 8, 0, 1, 0, 3, 0, 25, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 30, 8, 1, 1, + 1, 3, 1, 33, 8, 1, 1, 1, 3, 1, 36, 8, 1, 1, 1, 3, 1, 39, 8, 1, 1, 1, 3, + 1, 42, 8, 1, 1, 2, 1, 2, 3, 2, 46, 8, 2, 1, 2, 1, 2, 3, 2, 50, 8, 2, 4, + 2, 52, 8, 2, 11, 2, 12, 2, 53, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, + 1, 6, 3, 6, 64, 8, 6, 1, 6, 1, 6, 5, 6, 68, 8, 6, 10, 6, 12, 6, 71, 9, + 6, 1, 6, 3, 6, 74, 8, 6, 1, 7, 1, 7, 1, 7, 0, 0, 8, 0, 2, 4, 6, 8, 10, + 12, 14, 0, 1, 1, 0, 2, 3, 83, 0, 17, 1, 0, 0, 0, 2, 29, 1, 0, 0, 0, 4, + 43, 1, 0, 0, 0, 6, 55, 1, 0, 0, 0, 8, 57, 1, 0, 0, 0, 10, 59, 1, 0, 0, + 0, 12, 73, 1, 0, 0, 0, 14, 75, 1, 0, 0, 0, 16, 18, 5, 4, 0, 0, 17, 16, + 1, 0, 0, 0, 17, 18, 1, 0, 0, 0, 18, 19, 1, 0, 0, 0, 19, 21, 3, 2, 1, 0, + 20, 22, 5, 4, 0, 0, 21, 20, 1, 0, 0, 0, 21, 22, 1, 0, 0, 0, 22, 24, 1, + 0, 0, 0, 23, 25, 3, 4, 2, 0, 24, 23, 1, 0, 0, 0, 24, 25, 1, 0, 0, 0, 25, + 26, 1, 0, 0, 0, 26, 27, 5, 0, 0, 1, 27, 1, 1, 0, 0, 0, 28, 30, 3, 6, 3, + 0, 29, 28, 1, 0, 0, 0, 29, 30, 1, 0, 0, 0, 30, 32, 1, 0, 0, 0, 31, 33, + 5, 4, 0, 0, 32, 31, 1, 0, 0, 0, 32, 33, 1, 0, 0, 0, 33, 35, 1, 0, 0, 0, + 34, 36, 3, 8, 4, 0, 35, 34, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 38, 1, + 0, 0, 0, 37, 39, 5, 4, 0, 0, 38, 37, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, + 41, 1, 0, 0, 0, 40, 42, 3, 10, 5, 0, 41, 40, 1, 0, 0, 0, 41, 42, 1, 0, + 0, 0, 42, 3, 1, 0, 0, 0, 43, 45, 3, 14, 7, 0, 44, 46, 5, 4, 0, 0, 45, 44, + 1, 0, 0, 0, 45, 46, 1, 0, 0, 0, 46, 51, 1, 0, 0, 0, 47, 49, 3, 12, 6, 0, + 48, 50, 5, 4, 0, 0, 49, 48, 1, 0, 0, 0, 49, 50, 1, 0, 0, 0, 50, 52, 1, + 0, 0, 0, 51, 47, 1, 0, 0, 0, 52, 53, 1, 0, 0, 0, 53, 51, 1, 0, 0, 0, 53, + 54, 1, 0, 0, 0, 54, 5, 1, 0, 0, 0, 55, 56, 3, 12, 6, 0, 56, 7, 1, 0, 0, + 0, 57, 58, 5, 1, 0, 0, 58, 9, 1, 0, 0, 0, 59, 60, 3, 12, 6, 0, 60, 11, + 1, 0, 0, 0, 61, 74, 5, 6, 0, 0, 62, 64, 5, 4, 0, 0, 63, 62, 1, 0, 0, 0, + 63, 64, 1, 0, 0, 0, 64, 69, 1, 0, 0, 0, 65, 66, 5, 5, 0, 0, 66, 68, 5, + 4, 0, 0, 67, 65, 1, 0, 0, 0, 68, 71, 1, 0, 0, 0, 69, 67, 1, 0, 0, 0, 69, + 70, 1, 0, 0, 0, 70, 72, 1, 0, 0, 0, 71, 69, 1, 0, 0, 0, 72, 74, 5, 5, 0, + 0, 73, 61, 1, 0, 0, 0, 73, 63, 1, 0, 0, 0, 74, 13, 1, 0, 0, 0, 75, 76, + 7, 0, 0, 0, 76, 15, 1, 0, 0, 0, 14, 17, 21, 24, 29, 32, 35, 38, 41, 45, + 49, 53, 63, 69, 73, } deserializer := antlr.NewATNDeserializer(nil) staticData.atn = deserializer.Deserialize(staticData.serializedATN) @@ -105,9 +117,10 @@ const ( ConfigParserEOF = antlr.TokenEOF ConfigParserEQUAL = 1 ConfigParserHASH = 2 - ConfigParserWHITESPACE = 3 - ConfigParserSTRING = 4 - ConfigParserQUOTED_STRING = 5 + ConfigParserSEMICOLON = 3 + ConfigParserWHITESPACE = 4 + ConfigParserSTRING = 5 + ConfigParserQUOTED_STRING = 6 ) // ConfigParser rules. @@ -119,6 +132,7 @@ const ( ConfigParserRULE_separator = 4 ConfigParserRULE_value = 5 ConfigParserRULE_string = 6 + ConfigParserRULE_commentSymbol = 7 ) // ILineStatementContext is an interface to support dynamic dispatch. @@ -131,6 +145,9 @@ type ILineStatementContext interface { // Getter signatures Entry() IEntryContext EOF() antlr.TerminalNode + AllWHITESPACE() []antlr.TerminalNode + WHITESPACE(i int) antlr.TerminalNode + LeadingComment() ILeadingCommentContext // IsLineStatementContext differentiates from other interfaces. IsLineStatementContext() @@ -188,6 +205,30 @@ func (s *LineStatementContext) EOF() antlr.TerminalNode { return s.GetToken(ConfigParserEOF, 0) } +func (s *LineStatementContext) AllWHITESPACE() []antlr.TerminalNode { + return s.GetTokens(ConfigParserWHITESPACE) +} + +func (s *LineStatementContext) WHITESPACE(i int) antlr.TerminalNode { + return s.GetToken(ConfigParserWHITESPACE, i) +} + +func (s *LineStatementContext) LeadingComment() ILeadingCommentContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ILeadingCommentContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ILeadingCommentContext) +} + func (s *LineStatementContext) GetRuleContext() antlr.RuleContext { return s } @@ -211,13 +252,63 @@ func (s *LineStatementContext) ExitRule(listener antlr.ParseTreeListener) { func (p *ConfigParser) LineStatement() (localctx ILineStatementContext) { localctx = NewLineStatementContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 0, ConfigParserRULE_lineStatement) + var _la int + p.EnterOuterAlt(localctx, 1) - { - p.SetState(14) - p.Entry() + p.SetState(17) + p.GetErrorHandler().Sync(p) + + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 0, p.GetParserRuleContext()) == 1 { + { + p.SetState(16) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } else if p.HasError() { // JIM + goto errorExit } { - p.SetState(15) + p.SetState(19) + p.Entry() + } + p.SetState(21) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == ConfigParserWHITESPACE { + { + p.SetState(20) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(24) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == ConfigParserHASH || _la == ConfigParserSEMICOLON { + { + p.SetState(23) + p.LeadingComment() + } + + } + { + p.SetState(26) p.Match(ConfigParserEOF) if p.HasError() { // Recognition error - abort rule @@ -246,11 +337,11 @@ type IEntryContext interface { GetParser() antlr.Parser // Getter signatures - WHITESPACE() antlr.TerminalNode Key() IKeyContext + AllWHITESPACE() []antlr.TerminalNode + WHITESPACE(i int) antlr.TerminalNode Separator() ISeparatorContext Value() IValueContext - LeadingComment() ILeadingCommentContext // IsEntryContext differentiates from other interfaces. IsEntryContext() @@ -288,10 +379,6 @@ func NewEntryContext(parser antlr.Parser, parent antlr.ParserRuleContext, invoki func (s *EntryContext) GetParser() antlr.Parser { return s.parser } -func (s *EntryContext) WHITESPACE() antlr.TerminalNode { - return s.GetToken(ConfigParserWHITESPACE, 0) -} - func (s *EntryContext) Key() IKeyContext { var t antlr.RuleContext for _, ctx := range s.GetChildren() { @@ -308,6 +395,14 @@ func (s *EntryContext) Key() IKeyContext { return t.(IKeyContext) } +func (s *EntryContext) AllWHITESPACE() []antlr.TerminalNode { + return s.GetTokens(ConfigParserWHITESPACE) +} + +func (s *EntryContext) WHITESPACE(i int) antlr.TerminalNode { + return s.GetToken(ConfigParserWHITESPACE, i) +} + func (s *EntryContext) Separator() ISeparatorContext { var t antlr.RuleContext for _, ctx := range s.GetChildren() { @@ -340,22 +435,6 @@ func (s *EntryContext) Value() IValueContext { return t.(IValueContext) } -func (s *EntryContext) LeadingComment() ILeadingCommentContext { - var t antlr.RuleContext - for _, ctx := range s.GetChildren() { - if _, ok := ctx.(ILeadingCommentContext); ok { - t = ctx.(antlr.RuleContext) - break - } - } - - if t == nil { - return nil - } - - return t.(ILeadingCommentContext) -} - func (s *EntryContext) GetRuleContext() antlr.RuleContext { return s } @@ -382,16 +461,24 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) { var _la int p.EnterOuterAlt(localctx, 1) - p.SetState(18) + p.SetState(29) p.GetErrorHandler().Sync(p) - if p.HasError() { + + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) == 1 { + { + p.SetState(28) + p.Key() + } + + } else if p.HasError() { // JIM goto errorExit } - _la = p.GetTokenStream().LA(1) + p.SetState(32) + p.GetErrorHandler().Sync(p) - if _la == ConfigParserWHITESPACE { + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 4, p.GetParserRuleContext()) == 1 { { - p.SetState(17) + p.SetState(31) p.Match(ConfigParserWHITESPACE) if p.HasError() { // Recognition error - abort rule @@ -399,20 +486,10 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) { } } - } - p.SetState(21) - p.GetErrorHandler().Sync(p) - - if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 1, p.GetParserRuleContext()) == 1 { - { - p.SetState(20) - p.Key() - } - } else if p.HasError() { // JIM goto errorExit } - p.SetState(24) + p.SetState(35) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -421,39 +498,39 @@ func (p *ConfigParser) Entry() (localctx IEntryContext) { if _la == ConfigParserEQUAL { { - p.SetState(23) + p.SetState(34) p.Separator() } } - p.SetState(27) + p.SetState(38) p.GetErrorHandler().Sync(p) - if p.HasError() { + + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 6, p.GetParserRuleContext()) == 1 { + { + p.SetState(37) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } else if p.HasError() { // JIM goto errorExit } - _la = p.GetTokenStream().LA(1) + p.SetState(41) + p.GetErrorHandler().Sync(p) - if _la == ConfigParserSTRING || _la == ConfigParserQUOTED_STRING { + if p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 7, p.GetParserRuleContext()) == 1 { { - p.SetState(26) + p.SetState(40) p.Value() } - } - p.SetState(30) - p.GetErrorHandler().Sync(p) - if p.HasError() { + } else if p.HasError() { // JIM goto errorExit } - _la = p.GetTokenStream().LA(1) - - if _la == ConfigParserHASH { - { - p.SetState(29) - p.LeadingComment() - } - - } errorExit: if p.HasError() { @@ -476,7 +553,7 @@ type ILeadingCommentContext interface { GetParser() antlr.Parser // Getter signatures - HASH() antlr.TerminalNode + CommentSymbol() ICommentSymbolContext AllWHITESPACE() []antlr.TerminalNode WHITESPACE(i int) antlr.TerminalNode AllString_() []IStringContext @@ -518,8 +595,20 @@ func NewLeadingCommentContext(parser antlr.Parser, parent antlr.ParserRuleContex func (s *LeadingCommentContext) GetParser() antlr.Parser { return s.parser } -func (s *LeadingCommentContext) HASH() antlr.TerminalNode { - return s.GetToken(ConfigParserHASH, 0) +func (s *LeadingCommentContext) CommentSymbol() ICommentSymbolContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ICommentSymbolContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ICommentSymbolContext) } func (s *LeadingCommentContext) AllWHITESPACE() []antlr.TerminalNode { @@ -598,23 +687,15 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(32) - p.Match(ConfigParserHASH) - if p.HasError() { - // Recognition error - abort rule - goto errorExit - } + p.SetState(43) + p.CommentSymbol() } - p.SetState(34) + p.SetState(45) 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(), 8, p.GetParserRuleContext()) == 1 { { - p.SetState(33) + p.SetState(44) p.Match(ConfigParserWHITESPACE) if p.HasError() { // Recognition error - abort rule @@ -622,29 +703,27 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) { } } + } else if p.HasError() { // JIM + goto errorExit } - p.SetState(40) + p.SetState(51) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit } _la = p.GetTokenStream().LA(1) - for ok := true; ok; ok = _la == ConfigParserSTRING || _la == ConfigParserQUOTED_STRING { + for ok := true; ok; ok = ((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&112) != 0) { { - p.SetState(36) + p.SetState(47) p.String_() } - p.SetState(38) + p.SetState(49) 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(), 9, p.GetParserRuleContext()) == 1 { { - p.SetState(37) + p.SetState(48) p.Match(ConfigParserWHITESPACE) if p.HasError() { // Recognition error - abort rule @@ -652,9 +731,11 @@ func (p *ConfigParser) LeadingComment() (localctx ILeadingCommentContext) { } } + } else if p.HasError() { // JIM + goto errorExit } - p.SetState(42) + p.SetState(53) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -762,7 +843,7 @@ func (p *ConfigParser) Key() (localctx IKeyContext) { p.EnterRule(localctx, 6, ConfigParserRULE_key) p.EnterOuterAlt(localctx, 1) { - p.SetState(44) + p.SetState(55) p.String_() } @@ -854,7 +935,7 @@ func (p *ConfigParser) Separator() (localctx ISeparatorContext) { p.EnterRule(localctx, 8, ConfigParserRULE_separator) p.EnterOuterAlt(localctx, 1) { - p.SetState(46) + p.SetState(57) p.Match(ConfigParserEQUAL) if p.HasError() { // Recognition error - abort rule @@ -962,7 +1043,7 @@ func (p *ConfigParser) Value() (localctx IValueContext) { p.EnterRule(localctx, 10, ConfigParserRULE_value) p.EnterOuterAlt(localctx, 1) { - p.SetState(48) + p.SetState(59) p.String_() } @@ -988,7 +1069,10 @@ type IStringContext interface { // Getter signatures QUOTED_STRING() antlr.TerminalNode - STRING() antlr.TerminalNode + AllSTRING() []antlr.TerminalNode + STRING(i int) antlr.TerminalNode + AllWHITESPACE() []antlr.TerminalNode + WHITESPACE(i int) antlr.TerminalNode // IsStringContext differentiates from other interfaces. IsStringContext() @@ -1030,8 +1114,20 @@ func (s *StringContext) QUOTED_STRING() antlr.TerminalNode { return s.GetToken(ConfigParserQUOTED_STRING, 0) } -func (s *StringContext) STRING() antlr.TerminalNode { - return s.GetToken(ConfigParserSTRING, 0) +func (s *StringContext) AllSTRING() []antlr.TerminalNode { + return s.GetTokens(ConfigParserSTRING) +} + +func (s *StringContext) STRING(i int) antlr.TerminalNode { + return s.GetToken(ConfigParserSTRING, i) +} + +func (s *StringContext) AllWHITESPACE() []antlr.TerminalNode { + return s.GetTokens(ConfigParserWHITESPACE) +} + +func (s *StringContext) WHITESPACE(i int) antlr.TerminalNode { + return s.GetToken(ConfigParserWHITESPACE, i) } func (s *StringContext) GetRuleContext() antlr.RuleContext { @@ -1059,12 +1155,197 @@ func (p *ConfigParser) String_() (localctx IStringContext) { p.EnterRule(localctx, 12, ConfigParserRULE_string) var _la int + var _alt int + p.EnterOuterAlt(localctx, 1) - { - p.SetState(50) + p.SetState(73) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + + switch p.GetTokenStream().LA(1) { + case ConfigParserQUOTED_STRING: + { + p.SetState(61) + p.Match(ConfigParserQUOTED_STRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + case ConfigParserWHITESPACE, ConfigParserSTRING: + p.SetState(63) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } _la = p.GetTokenStream().LA(1) - if !(_la == ConfigParserSTRING || _la == ConfigParserQUOTED_STRING) { + if _la == ConfigParserWHITESPACE { + { + p.SetState(62) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(69) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 12, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + if _alt == 1 { + { + p.SetState(65) + p.Match(ConfigParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(66) + p.Match(ConfigParserWHITESPACE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(71) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 12, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + } + { + p.SetState(72) + p.Match(ConfigParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + default: + p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + goto errorExit + } + +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 +} + +// ICommentSymbolContext is an interface to support dynamic dispatch. +type ICommentSymbolContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + HASH() antlr.TerminalNode + SEMICOLON() antlr.TerminalNode + + // IsCommentSymbolContext differentiates from other interfaces. + IsCommentSymbolContext() +} + +type CommentSymbolContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyCommentSymbolContext() *CommentSymbolContext { + var p = new(CommentSymbolContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_commentSymbol + return p +} + +func InitEmptyCommentSymbolContext(p *CommentSymbolContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = ConfigParserRULE_commentSymbol +} + +func (*CommentSymbolContext) IsCommentSymbolContext() {} + +func NewCommentSymbolContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *CommentSymbolContext { + var p = new(CommentSymbolContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = ConfigParserRULE_commentSymbol + + return p +} + +func (s *CommentSymbolContext) GetParser() antlr.Parser { return s.parser } + +func (s *CommentSymbolContext) HASH() antlr.TerminalNode { + return s.GetToken(ConfigParserHASH, 0) +} + +func (s *CommentSymbolContext) SEMICOLON() antlr.TerminalNode { + return s.GetToken(ConfigParserSEMICOLON, 0) +} + +func (s *CommentSymbolContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *CommentSymbolContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *CommentSymbolContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.EnterCommentSymbol(s) + } +} + +func (s *CommentSymbolContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(ConfigListener); ok { + listenerT.ExitCommentSymbol(s) + } +} + +func (p *ConfigParser) CommentSymbol() (localctx ICommentSymbolContext) { + localctx = NewCommentSymbolContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 14, ConfigParserRULE_commentSymbol) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(75) + _la = p.GetTokenStream().LA(1) + + if !(_la == ConfigParserHASH || _la == ConfigParserSEMICOLON) { p.GetErrorHandler().RecoverInline(p) } else { p.GetErrorHandler().ReportMatch(p) diff --git a/server/handlers/gitconfig/ast/parser_test.go b/server/handlers/gitconfig/ast/parser_test.go new file mode 100644 index 0000000..bdeb887 --- /dev/null +++ b/server/handlers/gitconfig/ast/parser_test.go @@ -0,0 +1,138 @@ +package ast + +import ( + "config-lsp/utils" + "testing" +) + +func TestValidOneSectionExample(t *testing.T) { + input := utils.Dedent(` +[core] + repositoryformatversion = 0 + filemode = true + bare = false +`) + + config := NewGitConfig() + errors := config.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + if len(config.Sections) != 1 { + t.Fatalf("Expected 1 section, got %d", len(config.Sections)) + } + + section := config.Sections[0] + + rawFirstOption, _ := section.Entries.Get(uint32(1)) + firstOption := rawFirstOption.(*GitEntry) + if !(firstOption.Key.Value.Value == "repositoryformatversion" && firstOption.Value.Value == "0") { + t.Errorf("Expected repositoryformatversion, got %s", firstOption.Key.Value.Value) + } + + rawSecondOption, _ := section.Entries.Get(uint32(2)) + secondOption := rawSecondOption.(*GitEntry) + if !(secondOption.Key.Value.Value == "filemode" && secondOption.Value.Value == "true") { + t.Errorf("Expected filemode, got %s", secondOption.Key.Value.Value) + } + + rawThirdOption, _ := section.Entries.Get(uint32(3)) + thirdOption := rawThirdOption.(*GitEntry) + if !(thirdOption.Key.Value.Value == "bare" && thirdOption.Value.Value == "false") { + t.Errorf("Expected bare, got %s", thirdOption.Key.Value.Value) + } +} + +func TestComplexExample(t *testing.T) { + input := utils.Dedent(` +[core] + repositoryformatversion = 0 # This contains 4 spaces instead of a tab at the beginning + filemode = true # Hello World + + # Hello World + bare = false ; Trailing comment + +# Test +[remote "origin"] + url = https://github.com/github/repo.git + fetch = +refs/heads/*:refs/remotes/origin/* + +[alias] + ours = "!f() { git checkout --ours $@ && git add $@; }; f" ; This is a comment +; Hello +`) + + config := NewGitConfig() + errors := config.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + if len(config.Sections) != 3 { + t.Fatalf("Expected 3 sections, got %d", len(config.Sections)) + } + + section := config.Sections[0] + + if !(section.Title.Title == "core") { + t.Errorf("Expected core, got %s", section.Title.Title) + } + + if !(section.Entries.Size() == 3) { + t.Errorf("Expected 3 entries, got %d", section.Entries.Size()) + } + + rawFirstOption, _ := section.Entries.Get(uint32(1)) + firstOption := rawFirstOption.(*GitEntry) + if !(firstOption.Key.Value.Value == "repositoryformatversion" && firstOption.Value.Value == "0" && firstOption.Start.Line == 1 && firstOption.End.Line == 1 && firstOption.Start.Character == 4 && firstOption.End.Character == 31) { + t.Errorf("Expected 0, got %s", firstOption) + } + + _, found := section.Entries.Get(uint32(3)) + if found { + t.Errorf("Expected no entry at line 3") + } + + section = config.Sections[1] + + if !(section.Title.Title == `remote "origin"`) { + t.Errorf("Expected remote \"origin\", got %s", section.Title.Title) + } + + if !(section.Entries.Size() == 2) { + t.Errorf("Expected 2 entries, got %d", section.Entries.Size()) + } + + section = config.Sections[2] + + if !(section.Title.Title == "alias") { + t.Errorf("Expected alias, got %s", section.Title.Title) + } + + rawSecondOption, _ := section.Entries.Get(uint32(13)) + secondOption := rawSecondOption.(*GitEntry) + if !(secondOption.Key.Value.Value == "ours" && secondOption.Value.Value == "!f() { git checkout --ours $@ && git add $@; }; f" && secondOption.Start.Character == 1 && secondOption.End.Character == 59) { + t.Errorf("Expected ours, got %s", secondOption.Key.Value.Value) + } +} + +func TestMissingSectionHeader(t *testing.T) { + input := utils.Dedent(` +repositoryformatversion = 0 +filemode = true +bare = false + +[core] + bare = false +`) + + config := NewGitConfig() + errors := config.Parse(input) + + if len(errors) != 1 { + t.Fatalf("Expected 1 error, got %d", len(errors)) + } +}