From a5f834a38bcead806166e44d3d5858ae25323cd4 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 29 Aug 2024 21:54:17 +0200 Subject: [PATCH 01/24] feat(aliases): Add first aliases --- common/location.go | 24 +- handlers/aliases/Aliases.g4 | 102 + handlers/aliases/parser/Aliases.interp | 45 + handlers/aliases/parser/Aliases.tokens | 15 + handlers/aliases/parser/AliasesLexer.interp | 44 + handlers/aliases/parser/AliasesLexer.tokens | 15 + .../aliases/parser/aliases_base_listener.go | 118 + handlers/aliases/parser/aliases_lexer.go | 129 + handlers/aliases/parser/aliases_listener.go | 106 + handlers/aliases/parser/aliases_parser.go | 2424 +++++++++++++++++ handlers/aliases/tree/aliases.go | 39 + handlers/aliases/tree/handler.go | 1 + handlers/aliases/tree/listener.go | 208 ++ handlers/aliases/tree/parser.go | 79 + handlers/aliases/tree/parser_test.go | 107 + handlers/aliases/tree/values.go | 55 + handlers/hosts/handlers/analyzer/hosts.go | 21 - handlers/hosts/handlers/analyzer/listener.go | 32 +- 18 files changed, 3526 insertions(+), 38 deletions(-) create mode 100644 handlers/aliases/Aliases.g4 create mode 100644 handlers/aliases/parser/Aliases.interp create mode 100644 handlers/aliases/parser/Aliases.tokens create mode 100644 handlers/aliases/parser/AliasesLexer.interp create mode 100644 handlers/aliases/parser/AliasesLexer.tokens create mode 100644 handlers/aliases/parser/aliases_base_listener.go create mode 100644 handlers/aliases/parser/aliases_lexer.go create mode 100644 handlers/aliases/parser/aliases_listener.go create mode 100644 handlers/aliases/parser/aliases_parser.go create mode 100644 handlers/aliases/tree/aliases.go create mode 100644 handlers/aliases/tree/handler.go create mode 100644 handlers/aliases/tree/listener.go create mode 100644 handlers/aliases/tree/parser.go create mode 100644 handlers/aliases/tree/parser_test.go create mode 100644 handlers/aliases/tree/values.go diff --git a/common/location.go b/common/location.go index fd20677..fc7b1c8 100644 --- a/common/location.go +++ b/common/location.go @@ -1,6 +1,9 @@ package common -import protocol "github.com/tliron/glsp/protocol_3_16" +import ( + "github.com/antlr4-go/antlr/v4" + protocol "github.com/tliron/glsp/protocol_3_16" +) type Location struct { Line uint32 @@ -63,3 +66,22 @@ func CreateSingleCharRange(line uint32, character uint32) LocationRange { }, } } + +func CharacterRangeFromCtx( + ctx antlr.BaseParserRuleContext, +) LocationRange { + line := uint32(ctx.GetStart().GetLine()) + start := uint32(ctx.GetStart().GetStart()) + end := uint32(ctx.GetStop().GetStop()) + + return LocationRange{ + Start: Location{ + Line: line, + Character: start, + }, + End: Location{ + Line: line, + Character: end + 1, + }, + } +} diff --git a/handlers/aliases/Aliases.g4 b/handlers/aliases/Aliases.g4 new file mode 100644 index 0000000..1bf7649 --- /dev/null +++ b/handlers/aliases/Aliases.g4 @@ -0,0 +1,102 @@ +grammar Aliases; + +lineStatement + : entry SEPARATOR? comment? EOF + ; + +entry + : SEPARATOR? key SEPARATOR? separator SEPARATOR? values + ; + +separator + : COLON + ; + +key + : STRING + ; + +// // Values // // +values + : (value COMMA SEPARATOR)* value + ; + +value + : (user | file | command | include | email) + ; + +user + : STRING + ; + +file + : (SLASH STRING)+ SLASH? + ; + +command + : VERTLINE STRING + ; + +include + : COLON INCLUDE COLON file + ; + +comment + : NUMBER_SIGN (SEPARATOR? STRING)+ SEPARATOR? + ; + +email + : STRING AT STRING + ; + +error + : errorStatus COLON errorCode SEPARATOR errorMessage + ; + +errorStatus + : STRING + ; + +errorCode + : STRING + ; + +errorMessage + : STRING + ; + +SEPARATOR + : [ \t]+ + ; + +AT + : '@' + ; + +INCLUDE + : 'i' 'n' 'c' 'l' 'u' 'd' 'e' + ; + +VERTLINE + : '|' + ; + +COLON + : ':' + ; + +COMMA + : ',' + ; + +NUMBER_SIGN + : '#' + ; + +SLASH + : '/' + ; + +STRING + : ~(' ' | '\t' | '\n' | '\r' | ':' | ',' | '#' | '@' | '|' | '/')+ + ; diff --git a/handlers/aliases/parser/Aliases.interp b/handlers/aliases/parser/Aliases.interp new file mode 100644 index 0000000..ad9deb5 --- /dev/null +++ b/handlers/aliases/parser/Aliases.interp @@ -0,0 +1,45 @@ +token literal names: +null +null +'@' +null +'|' +':' +',' +'#' +'/' +null + +token symbolic names: +null +SEPARATOR +AT +INCLUDE +VERTLINE +COLON +COMMA +NUMBER_SIGN +SLASH +STRING + +rule names: +lineStatement +entry +separator +key +values +value +user +file +command +include +comment +email +error +errorStatus +errorCode +errorMessage + + +atn: +[4, 1, 9, 124, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 1, 0, 1, 0, 3, 0, 35, 8, 0, 1, 0, 3, 0, 38, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 43, 8, 1, 1, 1, 1, 1, 3, 1, 47, 8, 1, 1, 1, 1, 1, 3, 1, 51, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 63, 8, 4, 10, 4, 12, 4, 66, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 75, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 4, 7, 81, 8, 7, 11, 7, 12, 7, 82, 1, 7, 3, 7, 86, 8, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 3, 10, 98, 8, 10, 1, 10, 4, 10, 101, 8, 10, 11, 10, 12, 10, 102, 1, 10, 3, 10, 106, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 0, 0, 16, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 0, 0, 122, 0, 32, 1, 0, 0, 0, 2, 42, 1, 0, 0, 0, 4, 54, 1, 0, 0, 0, 6, 56, 1, 0, 0, 0, 8, 64, 1, 0, 0, 0, 10, 74, 1, 0, 0, 0, 12, 76, 1, 0, 0, 0, 14, 80, 1, 0, 0, 0, 16, 87, 1, 0, 0, 0, 18, 90, 1, 0, 0, 0, 20, 95, 1, 0, 0, 0, 22, 107, 1, 0, 0, 0, 24, 111, 1, 0, 0, 0, 26, 117, 1, 0, 0, 0, 28, 119, 1, 0, 0, 0, 30, 121, 1, 0, 0, 0, 32, 34, 3, 2, 1, 0, 33, 35, 5, 1, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 37, 1, 0, 0, 0, 36, 38, 3, 20, 10, 0, 37, 36, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 5, 0, 0, 1, 40, 1, 1, 0, 0, 0, 41, 43, 5, 1, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 46, 3, 6, 3, 0, 45, 47, 5, 1, 0, 0, 46, 45, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 50, 3, 4, 2, 0, 49, 51, 5, 1, 0, 0, 50, 49, 1, 0, 0, 0, 50, 51, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 53, 3, 8, 4, 0, 53, 3, 1, 0, 0, 0, 54, 55, 5, 5, 0, 0, 55, 5, 1, 0, 0, 0, 56, 57, 5, 9, 0, 0, 57, 7, 1, 0, 0, 0, 58, 59, 3, 10, 5, 0, 59, 60, 5, 6, 0, 0, 60, 61, 5, 1, 0, 0, 61, 63, 1, 0, 0, 0, 62, 58, 1, 0, 0, 0, 63, 66, 1, 0, 0, 0, 64, 62, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 67, 1, 0, 0, 0, 66, 64, 1, 0, 0, 0, 67, 68, 3, 10, 5, 0, 68, 9, 1, 0, 0, 0, 69, 75, 3, 12, 6, 0, 70, 75, 3, 14, 7, 0, 71, 75, 3, 16, 8, 0, 72, 75, 3, 18, 9, 0, 73, 75, 3, 22, 11, 0, 74, 69, 1, 0, 0, 0, 74, 70, 1, 0, 0, 0, 74, 71, 1, 0, 0, 0, 74, 72, 1, 0, 0, 0, 74, 73, 1, 0, 0, 0, 75, 11, 1, 0, 0, 0, 76, 77, 5, 9, 0, 0, 77, 13, 1, 0, 0, 0, 78, 79, 5, 8, 0, 0, 79, 81, 5, 9, 0, 0, 80, 78, 1, 0, 0, 0, 81, 82, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 82, 83, 1, 0, 0, 0, 83, 85, 1, 0, 0, 0, 84, 86, 5, 8, 0, 0, 85, 84, 1, 0, 0, 0, 85, 86, 1, 0, 0, 0, 86, 15, 1, 0, 0, 0, 87, 88, 5, 4, 0, 0, 88, 89, 5, 9, 0, 0, 89, 17, 1, 0, 0, 0, 90, 91, 5, 5, 0, 0, 91, 92, 5, 3, 0, 0, 92, 93, 5, 5, 0, 0, 93, 94, 3, 14, 7, 0, 94, 19, 1, 0, 0, 0, 95, 100, 5, 7, 0, 0, 96, 98, 5, 1, 0, 0, 97, 96, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 101, 5, 9, 0, 0, 100, 97, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 100, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 105, 1, 0, 0, 0, 104, 106, 5, 1, 0, 0, 105, 104, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, 106, 21, 1, 0, 0, 0, 107, 108, 5, 9, 0, 0, 108, 109, 5, 2, 0, 0, 109, 110, 5, 9, 0, 0, 110, 23, 1, 0, 0, 0, 111, 112, 3, 26, 13, 0, 112, 113, 5, 5, 0, 0, 113, 114, 3, 28, 14, 0, 114, 115, 5, 1, 0, 0, 115, 116, 3, 30, 15, 0, 116, 25, 1, 0, 0, 0, 117, 118, 5, 9, 0, 0, 118, 27, 1, 0, 0, 0, 119, 120, 5, 9, 0, 0, 120, 29, 1, 0, 0, 0, 121, 122, 5, 9, 0, 0, 122, 31, 1, 0, 0, 0, 12, 34, 37, 42, 46, 50, 64, 74, 82, 85, 97, 102, 105] \ No newline at end of file diff --git a/handlers/aliases/parser/Aliases.tokens b/handlers/aliases/parser/Aliases.tokens new file mode 100644 index 0000000..9efc836 --- /dev/null +++ b/handlers/aliases/parser/Aliases.tokens @@ -0,0 +1,15 @@ +SEPARATOR=1 +AT=2 +INCLUDE=3 +VERTLINE=4 +COLON=5 +COMMA=6 +NUMBER_SIGN=7 +SLASH=8 +STRING=9 +'@'=2 +'|'=4 +':'=5 +','=6 +'#'=7 +'/'=8 diff --git a/handlers/aliases/parser/AliasesLexer.interp b/handlers/aliases/parser/AliasesLexer.interp new file mode 100644 index 0000000..d626d2b --- /dev/null +++ b/handlers/aliases/parser/AliasesLexer.interp @@ -0,0 +1,44 @@ +token literal names: +null +null +'@' +null +'|' +':' +',' +'#' +'/' +null + +token symbolic names: +null +SEPARATOR +AT +INCLUDE +VERTLINE +COLON +COMMA +NUMBER_SIGN +SLASH +STRING + +rule names: +SEPARATOR +AT +INCLUDE +VERTLINE +COLON +COMMA +NUMBER_SIGN +SLASH +STRING + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[4, 0, 9, 49, 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, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 1, 0, 4, 0, 21, 8, 0, 11, 0, 12, 0, 22, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 4, 8, 46, 8, 8, 11, 8, 12, 8, 47, 0, 0, 9, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 1, 0, 2, 2, 0, 9, 9, 32, 32, 9, 0, 9, 10, 13, 13, 32, 32, 35, 35, 44, 44, 47, 47, 58, 58, 64, 64, 124, 124, 50, 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, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 1, 20, 1, 0, 0, 0, 3, 24, 1, 0, 0, 0, 5, 26, 1, 0, 0, 0, 7, 34, 1, 0, 0, 0, 9, 36, 1, 0, 0, 0, 11, 38, 1, 0, 0, 0, 13, 40, 1, 0, 0, 0, 15, 42, 1, 0, 0, 0, 17, 45, 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, 2, 1, 0, 0, 0, 24, 25, 5, 64, 0, 0, 25, 4, 1, 0, 0, 0, 26, 27, 5, 105, 0, 0, 27, 28, 5, 110, 0, 0, 28, 29, 5, 99, 0, 0, 29, 30, 5, 108, 0, 0, 30, 31, 5, 117, 0, 0, 31, 32, 5, 100, 0, 0, 32, 33, 5, 101, 0, 0, 33, 6, 1, 0, 0, 0, 34, 35, 5, 124, 0, 0, 35, 8, 1, 0, 0, 0, 36, 37, 5, 58, 0, 0, 37, 10, 1, 0, 0, 0, 38, 39, 5, 44, 0, 0, 39, 12, 1, 0, 0, 0, 40, 41, 5, 35, 0, 0, 41, 14, 1, 0, 0, 0, 42, 43, 5, 47, 0, 0, 43, 16, 1, 0, 0, 0, 44, 46, 8, 1, 0, 0, 45, 44, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 45, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 18, 1, 0, 0, 0, 3, 0, 22, 47, 0] \ No newline at end of file diff --git a/handlers/aliases/parser/AliasesLexer.tokens b/handlers/aliases/parser/AliasesLexer.tokens new file mode 100644 index 0000000..9efc836 --- /dev/null +++ b/handlers/aliases/parser/AliasesLexer.tokens @@ -0,0 +1,15 @@ +SEPARATOR=1 +AT=2 +INCLUDE=3 +VERTLINE=4 +COLON=5 +COMMA=6 +NUMBER_SIGN=7 +SLASH=8 +STRING=9 +'@'=2 +'|'=4 +':'=5 +','=6 +'#'=7 +'/'=8 diff --git a/handlers/aliases/parser/aliases_base_listener.go b/handlers/aliases/parser/aliases_base_listener.go new file mode 100644 index 0000000..c93be05 --- /dev/null +++ b/handlers/aliases/parser/aliases_base_listener.go @@ -0,0 +1,118 @@ +// Code generated from Aliases.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser // Aliases + +import "github.com/antlr4-go/antlr/v4" + +// BaseAliasesListener is a complete listener for a parse tree produced by AliasesParser. +type BaseAliasesListener struct{} + +var _ AliasesListener = &BaseAliasesListener{} + +// VisitTerminal is called when a terminal node is visited. +func (s *BaseAliasesListener) VisitTerminal(node antlr.TerminalNode) {} + +// VisitErrorNode is called when an error node is visited. +func (s *BaseAliasesListener) VisitErrorNode(node antlr.ErrorNode) {} + +// EnterEveryRule is called when any rule is entered. +func (s *BaseAliasesListener) EnterEveryRule(ctx antlr.ParserRuleContext) {} + +// ExitEveryRule is called when any rule is exited. +func (s *BaseAliasesListener) ExitEveryRule(ctx antlr.ParserRuleContext) {} + +// EnterLineStatement is called when production lineStatement is entered. +func (s *BaseAliasesListener) EnterLineStatement(ctx *LineStatementContext) {} + +// ExitLineStatement is called when production lineStatement is exited. +func (s *BaseAliasesListener) ExitLineStatement(ctx *LineStatementContext) {} + +// EnterEntry is called when production entry is entered. +func (s *BaseAliasesListener) EnterEntry(ctx *EntryContext) {} + +// ExitEntry is called when production entry is exited. +func (s *BaseAliasesListener) ExitEntry(ctx *EntryContext) {} + +// EnterSeparator is called when production separator is entered. +func (s *BaseAliasesListener) EnterSeparator(ctx *SeparatorContext) {} + +// ExitSeparator is called when production separator is exited. +func (s *BaseAliasesListener) ExitSeparator(ctx *SeparatorContext) {} + +// EnterKey is called when production key is entered. +func (s *BaseAliasesListener) EnterKey(ctx *KeyContext) {} + +// ExitKey is called when production key is exited. +func (s *BaseAliasesListener) ExitKey(ctx *KeyContext) {} + +// EnterValues is called when production values is entered. +func (s *BaseAliasesListener) EnterValues(ctx *ValuesContext) {} + +// ExitValues is called when production values is exited. +func (s *BaseAliasesListener) ExitValues(ctx *ValuesContext) {} + +// EnterValue is called when production value is entered. +func (s *BaseAliasesListener) EnterValue(ctx *ValueContext) {} + +// ExitValue is called when production value is exited. +func (s *BaseAliasesListener) ExitValue(ctx *ValueContext) {} + +// EnterUser is called when production user is entered. +func (s *BaseAliasesListener) EnterUser(ctx *UserContext) {} + +// ExitUser is called when production user is exited. +func (s *BaseAliasesListener) ExitUser(ctx *UserContext) {} + +// EnterFile is called when production file is entered. +func (s *BaseAliasesListener) EnterFile(ctx *FileContext) {} + +// ExitFile is called when production file is exited. +func (s *BaseAliasesListener) ExitFile(ctx *FileContext) {} + +// EnterCommand is called when production command is entered. +func (s *BaseAliasesListener) EnterCommand(ctx *CommandContext) {} + +// ExitCommand is called when production command is exited. +func (s *BaseAliasesListener) ExitCommand(ctx *CommandContext) {} + +// EnterInclude is called when production include is entered. +func (s *BaseAliasesListener) EnterInclude(ctx *IncludeContext) {} + +// ExitInclude is called when production include is exited. +func (s *BaseAliasesListener) ExitInclude(ctx *IncludeContext) {} + +// EnterComment is called when production comment is entered. +func (s *BaseAliasesListener) EnterComment(ctx *CommentContext) {} + +// ExitComment is called when production comment is exited. +func (s *BaseAliasesListener) ExitComment(ctx *CommentContext) {} + +// EnterEmail is called when production email is entered. +func (s *BaseAliasesListener) EnterEmail(ctx *EmailContext) {} + +// ExitEmail is called when production email is exited. +func (s *BaseAliasesListener) ExitEmail(ctx *EmailContext) {} + +// EnterError is called when production error is entered. +func (s *BaseAliasesListener) EnterError(ctx *ErrorContext) {} + +// ExitError is called when production error is exited. +func (s *BaseAliasesListener) ExitError(ctx *ErrorContext) {} + +// EnterErrorStatus is called when production errorStatus is entered. +func (s *BaseAliasesListener) EnterErrorStatus(ctx *ErrorStatusContext) {} + +// ExitErrorStatus is called when production errorStatus is exited. +func (s *BaseAliasesListener) ExitErrorStatus(ctx *ErrorStatusContext) {} + +// EnterErrorCode is called when production errorCode is entered. +func (s *BaseAliasesListener) EnterErrorCode(ctx *ErrorCodeContext) {} + +// ExitErrorCode is called when production errorCode is exited. +func (s *BaseAliasesListener) ExitErrorCode(ctx *ErrorCodeContext) {} + +// EnterErrorMessage is called when production errorMessage is entered. +func (s *BaseAliasesListener) EnterErrorMessage(ctx *ErrorMessageContext) {} + +// ExitErrorMessage is called when production errorMessage is exited. +func (s *BaseAliasesListener) ExitErrorMessage(ctx *ErrorMessageContext) {} diff --git a/handlers/aliases/parser/aliases_lexer.go b/handlers/aliases/parser/aliases_lexer.go new file mode 100644 index 0000000..dd72d4b --- /dev/null +++ b/handlers/aliases/parser/aliases_lexer.go @@ -0,0 +1,129 @@ +// Code generated from Aliases.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser + +import ( + "fmt" + "github.com/antlr4-go/antlr/v4" + "sync" + "unicode" +) + +// Suppress unused import error +var _ = fmt.Printf +var _ = sync.Once{} +var _ = unicode.IsLetter + +type AliasesLexer struct { + *antlr.BaseLexer + channelNames []string + modeNames []string + // TODO: EOF string +} + +var AliasesLexerLexerStaticData struct { + once sync.Once + serializedATN []int32 + ChannelNames []string + ModeNames []string + LiteralNames []string + SymbolicNames []string + RuleNames []string + PredictionContextCache *antlr.PredictionContextCache + atn *antlr.ATN + decisionToDFA []*antlr.DFA +} + +func aliaseslexerLexerInit() { + staticData := &AliasesLexerLexerStaticData + staticData.ChannelNames = []string{ + "DEFAULT_TOKEN_CHANNEL", "HIDDEN", + } + staticData.ModeNames = []string{ + "DEFAULT_MODE", + } + staticData.LiteralNames = []string{ + "", "", "'@'", "", "'|'", "':'", "','", "'#'", "'/'", + } + staticData.SymbolicNames = []string{ + "", "SEPARATOR", "AT", "INCLUDE", "VERTLINE", "COLON", "COMMA", "NUMBER_SIGN", + "SLASH", "STRING", + } + staticData.RuleNames = []string{ + "SEPARATOR", "AT", "INCLUDE", "VERTLINE", "COLON", "COMMA", "NUMBER_SIGN", + "SLASH", "STRING", + } + staticData.PredictionContextCache = antlr.NewPredictionContextCache() + staticData.serializedATN = []int32{ + 4, 0, 9, 49, 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, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 1, 0, 4, 0, 21, + 8, 0, 11, 0, 12, 0, 22, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, + 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, + 1, 8, 4, 8, 46, 8, 8, 11, 8, 12, 8, 47, 0, 0, 9, 1, 1, 3, 2, 5, 3, 7, 4, + 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 1, 0, 2, 2, 0, 9, 9, 32, 32, 9, 0, 9, + 10, 13, 13, 32, 32, 35, 35, 44, 44, 47, 47, 58, 58, 64, 64, 124, 124, 50, + 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, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, + 0, 0, 17, 1, 0, 0, 0, 1, 20, 1, 0, 0, 0, 3, 24, 1, 0, 0, 0, 5, 26, 1, 0, + 0, 0, 7, 34, 1, 0, 0, 0, 9, 36, 1, 0, 0, 0, 11, 38, 1, 0, 0, 0, 13, 40, + 1, 0, 0, 0, 15, 42, 1, 0, 0, 0, 17, 45, 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, 2, 1, 0, 0, 0, 24, 25, 5, 64, 0, 0, 25, 4, 1, 0, 0, 0, 26, + 27, 5, 105, 0, 0, 27, 28, 5, 110, 0, 0, 28, 29, 5, 99, 0, 0, 29, 30, 5, + 108, 0, 0, 30, 31, 5, 117, 0, 0, 31, 32, 5, 100, 0, 0, 32, 33, 5, 101, + 0, 0, 33, 6, 1, 0, 0, 0, 34, 35, 5, 124, 0, 0, 35, 8, 1, 0, 0, 0, 36, 37, + 5, 58, 0, 0, 37, 10, 1, 0, 0, 0, 38, 39, 5, 44, 0, 0, 39, 12, 1, 0, 0, + 0, 40, 41, 5, 35, 0, 0, 41, 14, 1, 0, 0, 0, 42, 43, 5, 47, 0, 0, 43, 16, + 1, 0, 0, 0, 44, 46, 8, 1, 0, 0, 45, 44, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, + 47, 45, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 18, 1, 0, 0, 0, 3, 0, 22, 47, + 0, + } + deserializer := antlr.NewATNDeserializer(nil) + staticData.atn = deserializer.Deserialize(staticData.serializedATN) + atn := staticData.atn + staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState)) + decisionToDFA := staticData.decisionToDFA + for index, state := range atn.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(state, index) + } +} + +// AliasesLexerInit initializes any static state used to implement AliasesLexer. By default the +// static state used to implement the lexer is lazily initialized during the first call to +// NewAliasesLexer(). You can call this function if you wish to initialize the static state ahead +// of time. +func AliasesLexerInit() { + staticData := &AliasesLexerLexerStaticData + staticData.once.Do(aliaseslexerLexerInit) +} + +// NewAliasesLexer produces a new lexer instance for the optional input antlr.CharStream. +func NewAliasesLexer(input antlr.CharStream) *AliasesLexer { + AliasesLexerInit() + l := new(AliasesLexer) + l.BaseLexer = antlr.NewBaseLexer(input) + staticData := &AliasesLexerLexerStaticData + l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache) + l.channelNames = staticData.ChannelNames + l.modeNames = staticData.ModeNames + l.RuleNames = staticData.RuleNames + l.LiteralNames = staticData.LiteralNames + l.SymbolicNames = staticData.SymbolicNames + l.GrammarFileName = "Aliases.g4" + // TODO: l.EOF = antlr.TokenEOF + + return l +} + +// AliasesLexer tokens. +const ( + AliasesLexerSEPARATOR = 1 + AliasesLexerAT = 2 + AliasesLexerINCLUDE = 3 + AliasesLexerVERTLINE = 4 + AliasesLexerCOLON = 5 + AliasesLexerCOMMA = 6 + AliasesLexerNUMBER_SIGN = 7 + AliasesLexerSLASH = 8 + AliasesLexerSTRING = 9 +) diff --git a/handlers/aliases/parser/aliases_listener.go b/handlers/aliases/parser/aliases_listener.go new file mode 100644 index 0000000..db833c1 --- /dev/null +++ b/handlers/aliases/parser/aliases_listener.go @@ -0,0 +1,106 @@ +// Code generated from Aliases.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser // Aliases + +import "github.com/antlr4-go/antlr/v4" + +// AliasesListener is a complete listener for a parse tree produced by AliasesParser. +type AliasesListener interface { + antlr.ParseTreeListener + + // EnterLineStatement is called when entering the lineStatement production. + EnterLineStatement(c *LineStatementContext) + + // EnterEntry is called when entering the entry production. + EnterEntry(c *EntryContext) + + // EnterSeparator is called when entering the separator production. + EnterSeparator(c *SeparatorContext) + + // EnterKey is called when entering the key production. + EnterKey(c *KeyContext) + + // EnterValues is called when entering the values production. + EnterValues(c *ValuesContext) + + // EnterValue is called when entering the value production. + EnterValue(c *ValueContext) + + // EnterUser is called when entering the user production. + EnterUser(c *UserContext) + + // EnterFile is called when entering the file production. + EnterFile(c *FileContext) + + // EnterCommand is called when entering the command production. + EnterCommand(c *CommandContext) + + // EnterInclude is called when entering the include production. + EnterInclude(c *IncludeContext) + + // EnterComment is called when entering the comment production. + EnterComment(c *CommentContext) + + // EnterEmail is called when entering the email production. + EnterEmail(c *EmailContext) + + // EnterError is called when entering the error production. + EnterError(c *ErrorContext) + + // EnterErrorStatus is called when entering the errorStatus production. + EnterErrorStatus(c *ErrorStatusContext) + + // EnterErrorCode is called when entering the errorCode production. + EnterErrorCode(c *ErrorCodeContext) + + // EnterErrorMessage is called when entering the errorMessage production. + EnterErrorMessage(c *ErrorMessageContext) + + // ExitLineStatement is called when exiting the lineStatement production. + ExitLineStatement(c *LineStatementContext) + + // ExitEntry is called when exiting the entry production. + ExitEntry(c *EntryContext) + + // ExitSeparator is called when exiting the separator production. + ExitSeparator(c *SeparatorContext) + + // ExitKey is called when exiting the key production. + ExitKey(c *KeyContext) + + // ExitValues is called when exiting the values production. + ExitValues(c *ValuesContext) + + // ExitValue is called when exiting the value production. + ExitValue(c *ValueContext) + + // ExitUser is called when exiting the user production. + ExitUser(c *UserContext) + + // ExitFile is called when exiting the file production. + ExitFile(c *FileContext) + + // ExitCommand is called when exiting the command production. + ExitCommand(c *CommandContext) + + // ExitInclude is called when exiting the include production. + ExitInclude(c *IncludeContext) + + // ExitComment is called when exiting the comment production. + ExitComment(c *CommentContext) + + // ExitEmail is called when exiting the email production. + ExitEmail(c *EmailContext) + + // ExitError is called when exiting the error production. + ExitError(c *ErrorContext) + + // ExitErrorStatus is called when exiting the errorStatus production. + ExitErrorStatus(c *ErrorStatusContext) + + // ExitErrorCode is called when exiting the errorCode production. + ExitErrorCode(c *ErrorCodeContext) + + // ExitErrorMessage is called when exiting the errorMessage production. + ExitErrorMessage(c *ErrorMessageContext) +} diff --git a/handlers/aliases/parser/aliases_parser.go b/handlers/aliases/parser/aliases_parser.go new file mode 100644 index 0000000..c9f47a8 --- /dev/null +++ b/handlers/aliases/parser/aliases_parser.go @@ -0,0 +1,2424 @@ +// Code generated from Aliases.g4 by ANTLR 4.13.0. DO NOT EDIT. + +package parser // Aliases + +import ( + "fmt" + "strconv" + "sync" + + "github.com/antlr4-go/antlr/v4" +) + +// Suppress unused import errors +var _ = fmt.Printf +var _ = strconv.Itoa +var _ = sync.Once{} + +type AliasesParser struct { + *antlr.BaseParser +} + +var AliasesParserStaticData struct { + once sync.Once + serializedATN []int32 + LiteralNames []string + SymbolicNames []string + RuleNames []string + PredictionContextCache *antlr.PredictionContextCache + atn *antlr.ATN + decisionToDFA []*antlr.DFA +} + +func aliasesParserInit() { + staticData := &AliasesParserStaticData + staticData.LiteralNames = []string{ + "", "", "'@'", "", "'|'", "':'", "','", "'#'", "'/'", + } + staticData.SymbolicNames = []string{ + "", "SEPARATOR", "AT", "INCLUDE", "VERTLINE", "COLON", "COMMA", "NUMBER_SIGN", + "SLASH", "STRING", + } + staticData.RuleNames = []string{ + "lineStatement", "entry", "separator", "key", "values", "value", "user", + "file", "command", "include", "comment", "email", "error", "errorStatus", + "errorCode", "errorMessage", + } + staticData.PredictionContextCache = antlr.NewPredictionContextCache() + staticData.serializedATN = []int32{ + 4, 1, 9, 124, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, + 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, + 1, 0, 1, 0, 3, 0, 35, 8, 0, 1, 0, 3, 0, 38, 8, 0, 1, 0, 1, 0, 1, 1, 3, + 1, 43, 8, 1, 1, 1, 1, 1, 3, 1, 47, 8, 1, 1, 1, 1, 1, 3, 1, 51, 8, 1, 1, + 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 63, 8, 4, + 10, 4, 12, 4, 66, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, + 75, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 4, 7, 81, 8, 7, 11, 7, 12, 7, 82, 1, + 7, 3, 7, 86, 8, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, + 1, 10, 3, 10, 98, 8, 10, 1, 10, 4, 10, 101, 8, 10, 11, 10, 12, 10, 102, + 1, 10, 3, 10, 106, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, + 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, + 0, 0, 16, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 0, + 0, 122, 0, 32, 1, 0, 0, 0, 2, 42, 1, 0, 0, 0, 4, 54, 1, 0, 0, 0, 6, 56, + 1, 0, 0, 0, 8, 64, 1, 0, 0, 0, 10, 74, 1, 0, 0, 0, 12, 76, 1, 0, 0, 0, + 14, 80, 1, 0, 0, 0, 16, 87, 1, 0, 0, 0, 18, 90, 1, 0, 0, 0, 20, 95, 1, + 0, 0, 0, 22, 107, 1, 0, 0, 0, 24, 111, 1, 0, 0, 0, 26, 117, 1, 0, 0, 0, + 28, 119, 1, 0, 0, 0, 30, 121, 1, 0, 0, 0, 32, 34, 3, 2, 1, 0, 33, 35, 5, + 1, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 37, 1, 0, 0, 0, 36, + 38, 3, 20, 10, 0, 37, 36, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 39, 1, 0, + 0, 0, 39, 40, 5, 0, 0, 1, 40, 1, 1, 0, 0, 0, 41, 43, 5, 1, 0, 0, 42, 41, + 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 46, 3, 6, 3, 0, + 45, 47, 5, 1, 0, 0, 46, 45, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 48, 1, + 0, 0, 0, 48, 50, 3, 4, 2, 0, 49, 51, 5, 1, 0, 0, 50, 49, 1, 0, 0, 0, 50, + 51, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 53, 3, 8, 4, 0, 53, 3, 1, 0, 0, + 0, 54, 55, 5, 5, 0, 0, 55, 5, 1, 0, 0, 0, 56, 57, 5, 9, 0, 0, 57, 7, 1, + 0, 0, 0, 58, 59, 3, 10, 5, 0, 59, 60, 5, 6, 0, 0, 60, 61, 5, 1, 0, 0, 61, + 63, 1, 0, 0, 0, 62, 58, 1, 0, 0, 0, 63, 66, 1, 0, 0, 0, 64, 62, 1, 0, 0, + 0, 64, 65, 1, 0, 0, 0, 65, 67, 1, 0, 0, 0, 66, 64, 1, 0, 0, 0, 67, 68, + 3, 10, 5, 0, 68, 9, 1, 0, 0, 0, 69, 75, 3, 12, 6, 0, 70, 75, 3, 14, 7, + 0, 71, 75, 3, 16, 8, 0, 72, 75, 3, 18, 9, 0, 73, 75, 3, 22, 11, 0, 74, + 69, 1, 0, 0, 0, 74, 70, 1, 0, 0, 0, 74, 71, 1, 0, 0, 0, 74, 72, 1, 0, 0, + 0, 74, 73, 1, 0, 0, 0, 75, 11, 1, 0, 0, 0, 76, 77, 5, 9, 0, 0, 77, 13, + 1, 0, 0, 0, 78, 79, 5, 8, 0, 0, 79, 81, 5, 9, 0, 0, 80, 78, 1, 0, 0, 0, + 81, 82, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 82, 83, 1, 0, 0, 0, 83, 85, 1, + 0, 0, 0, 84, 86, 5, 8, 0, 0, 85, 84, 1, 0, 0, 0, 85, 86, 1, 0, 0, 0, 86, + 15, 1, 0, 0, 0, 87, 88, 5, 4, 0, 0, 88, 89, 5, 9, 0, 0, 89, 17, 1, 0, 0, + 0, 90, 91, 5, 5, 0, 0, 91, 92, 5, 3, 0, 0, 92, 93, 5, 5, 0, 0, 93, 94, + 3, 14, 7, 0, 94, 19, 1, 0, 0, 0, 95, 100, 5, 7, 0, 0, 96, 98, 5, 1, 0, + 0, 97, 96, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 101, + 5, 9, 0, 0, 100, 97, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 100, 1, 0, + 0, 0, 102, 103, 1, 0, 0, 0, 103, 105, 1, 0, 0, 0, 104, 106, 5, 1, 0, 0, + 105, 104, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, 106, 21, 1, 0, 0, 0, 107, 108, + 5, 9, 0, 0, 108, 109, 5, 2, 0, 0, 109, 110, 5, 9, 0, 0, 110, 23, 1, 0, + 0, 0, 111, 112, 3, 26, 13, 0, 112, 113, 5, 5, 0, 0, 113, 114, 3, 28, 14, + 0, 114, 115, 5, 1, 0, 0, 115, 116, 3, 30, 15, 0, 116, 25, 1, 0, 0, 0, 117, + 118, 5, 9, 0, 0, 118, 27, 1, 0, 0, 0, 119, 120, 5, 9, 0, 0, 120, 29, 1, + 0, 0, 0, 121, 122, 5, 9, 0, 0, 122, 31, 1, 0, 0, 0, 12, 34, 37, 42, 46, + 50, 64, 74, 82, 85, 97, 102, 105, + } + deserializer := antlr.NewATNDeserializer(nil) + staticData.atn = deserializer.Deserialize(staticData.serializedATN) + atn := staticData.atn + staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState)) + decisionToDFA := staticData.decisionToDFA + for index, state := range atn.DecisionToState { + decisionToDFA[index] = antlr.NewDFA(state, index) + } +} + +// AliasesParserInit initializes any static state used to implement AliasesParser. By default the +// static state used to implement the parser is lazily initialized during the first call to +// NewAliasesParser(). You can call this function if you wish to initialize the static state ahead +// of time. +func AliasesParserInit() { + staticData := &AliasesParserStaticData + staticData.once.Do(aliasesParserInit) +} + +// NewAliasesParser produces a new parser instance for the optional input antlr.TokenStream. +func NewAliasesParser(input antlr.TokenStream) *AliasesParser { + AliasesParserInit() + this := new(AliasesParser) + this.BaseParser = antlr.NewBaseParser(input) + staticData := &AliasesParserStaticData + this.Interpreter = antlr.NewParserATNSimulator(this, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache) + this.RuleNames = staticData.RuleNames + this.LiteralNames = staticData.LiteralNames + this.SymbolicNames = staticData.SymbolicNames + this.GrammarFileName = "Aliases.g4" + + return this +} + +// AliasesParser tokens. +const ( + AliasesParserEOF = antlr.TokenEOF + AliasesParserSEPARATOR = 1 + AliasesParserAT = 2 + AliasesParserINCLUDE = 3 + AliasesParserVERTLINE = 4 + AliasesParserCOLON = 5 + AliasesParserCOMMA = 6 + AliasesParserNUMBER_SIGN = 7 + AliasesParserSLASH = 8 + AliasesParserSTRING = 9 +) + +// AliasesParser rules. +const ( + AliasesParserRULE_lineStatement = 0 + AliasesParserRULE_entry = 1 + AliasesParserRULE_separator = 2 + AliasesParserRULE_key = 3 + AliasesParserRULE_values = 4 + AliasesParserRULE_value = 5 + AliasesParserRULE_user = 6 + AliasesParserRULE_file = 7 + AliasesParserRULE_command = 8 + AliasesParserRULE_include = 9 + AliasesParserRULE_comment = 10 + AliasesParserRULE_email = 11 + AliasesParserRULE_error = 12 + AliasesParserRULE_errorStatus = 13 + AliasesParserRULE_errorCode = 14 + AliasesParserRULE_errorMessage = 15 +) + +// ILineStatementContext is an interface to support dynamic dispatch. +type ILineStatementContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + Entry() IEntryContext + EOF() antlr.TerminalNode + SEPARATOR() antlr.TerminalNode + Comment() ICommentContext + + // IsLineStatementContext differentiates from other interfaces. + IsLineStatementContext() +} + +type LineStatementContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyLineStatementContext() *LineStatementContext { + var p = new(LineStatementContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_lineStatement + return p +} + +func InitEmptyLineStatementContext(p *LineStatementContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_lineStatement +} + +func (*LineStatementContext) IsLineStatementContext() {} + +func NewLineStatementContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *LineStatementContext { + var p = new(LineStatementContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_lineStatement + + return p +} + +func (s *LineStatementContext) GetParser() antlr.Parser { return s.parser } + +func (s *LineStatementContext) Entry() IEntryContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IEntryContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IEntryContext) +} + +func (s *LineStatementContext) EOF() antlr.TerminalNode { + return s.GetToken(AliasesParserEOF, 0) +} + +func (s *LineStatementContext) SEPARATOR() antlr.TerminalNode { + return s.GetToken(AliasesParserSEPARATOR, 0) +} + +func (s *LineStatementContext) Comment() ICommentContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ICommentContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ICommentContext) +} + +func (s *LineStatementContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *LineStatementContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *LineStatementContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterLineStatement(s) + } +} + +func (s *LineStatementContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitLineStatement(s) + } +} + +func (p *AliasesParser) LineStatement() (localctx ILineStatementContext) { + localctx = NewLineStatementContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 0, AliasesParserRULE_lineStatement) + var _la int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(32) + p.Entry() + } + p.SetState(34) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSEPARATOR { + { + p.SetState(33) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(37) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserNUMBER_SIGN { + { + p.SetState(36) + p.Comment() + } + + } + { + p.SetState(39) + p.Match(AliasesParserEOF) + if p.HasError() { + // Recognition error - abort rule + 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 +} + +// IEntryContext is an interface to support dynamic dispatch. +type IEntryContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + Key() IKeyContext + Separator() ISeparatorContext + Values() IValuesContext + AllSEPARATOR() []antlr.TerminalNode + SEPARATOR(i int) antlr.TerminalNode + + // IsEntryContext differentiates from other interfaces. + IsEntryContext() +} + +type EntryContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyEntryContext() *EntryContext { + var p = new(EntryContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_entry + return p +} + +func InitEmptyEntryContext(p *EntryContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_entry +} + +func (*EntryContext) IsEntryContext() {} + +func NewEntryContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *EntryContext { + var p = new(EntryContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_entry + + return p +} + +func (s *EntryContext) GetParser() antlr.Parser { return s.parser } + +func (s *EntryContext) Key() IKeyContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IKeyContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IKeyContext) +} + +func (s *EntryContext) Separator() ISeparatorContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ISeparatorContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ISeparatorContext) +} + +func (s *EntryContext) Values() IValuesContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IValuesContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IValuesContext) +} + +func (s *EntryContext) AllSEPARATOR() []antlr.TerminalNode { + return s.GetTokens(AliasesParserSEPARATOR) +} + +func (s *EntryContext) SEPARATOR(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserSEPARATOR, i) +} + +func (s *EntryContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *EntryContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *EntryContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterEntry(s) + } +} + +func (s *EntryContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitEntry(s) + } +} + +func (p *AliasesParser) Entry() (localctx IEntryContext) { + localctx = NewEntryContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 2, AliasesParserRULE_entry) + var _la int + + p.EnterOuterAlt(localctx, 1) + p.SetState(42) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSEPARATOR { + { + p.SetState(41) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(44) + p.Key() + } + p.SetState(46) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSEPARATOR { + { + p.SetState(45) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(48) + p.Separator() + } + p.SetState(50) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSEPARATOR { + { + p.SetState(49) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(52) + 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 + COLON() 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 = AliasesParserRULE_separator + return p +} + +func InitEmptySeparatorContext(p *SeparatorContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_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 = AliasesParserRULE_separator + + return p +} + +func (s *SeparatorContext) GetParser() antlr.Parser { return s.parser } + +func (s *SeparatorContext) COLON() antlr.TerminalNode { + return s.GetToken(AliasesParserCOLON, 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.(AliasesListener); ok { + listenerT.EnterSeparator(s) + } +} + +func (s *SeparatorContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitSeparator(s) + } +} + +func (p *AliasesParser) Separator() (localctx ISeparatorContext) { + localctx = NewSeparatorContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 4, AliasesParserRULE_separator) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(54) + p.Match(AliasesParserCOLON) + if p.HasError() { + // Recognition error - abort rule + 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 +} + +// IKeyContext is an interface to support dynamic dispatch. +type IKeyContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + STRING() antlr.TerminalNode + + // IsKeyContext differentiates from other interfaces. + IsKeyContext() +} + +type KeyContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyKeyContext() *KeyContext { + var p = new(KeyContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_key + return p +} + +func InitEmptyKeyContext(p *KeyContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_key +} + +func (*KeyContext) IsKeyContext() {} + +func NewKeyContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *KeyContext { + var p = new(KeyContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_key + + return p +} + +func (s *KeyContext) GetParser() antlr.Parser { return s.parser } + +func (s *KeyContext) STRING() antlr.TerminalNode { + return s.GetToken(AliasesParserSTRING, 0) +} + +func (s *KeyContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *KeyContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *KeyContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterKey(s) + } +} + +func (s *KeyContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitKey(s) + } +} + +func (p *AliasesParser) Key() (localctx IKeyContext) { + localctx = NewKeyContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 6, AliasesParserRULE_key) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(56) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + 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 +} + +// IValuesContext is an interface to support dynamic dispatch. +type IValuesContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllValue() []IValueContext + Value(i int) IValueContext + AllCOMMA() []antlr.TerminalNode + COMMA(i int) antlr.TerminalNode + AllSEPARATOR() []antlr.TerminalNode + SEPARATOR(i int) antlr.TerminalNode + + // IsValuesContext differentiates from other interfaces. + IsValuesContext() +} + +type ValuesContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyValuesContext() *ValuesContext { + var p = new(ValuesContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_values + return p +} + +func InitEmptyValuesContext(p *ValuesContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_values +} + +func (*ValuesContext) IsValuesContext() {} + +func NewValuesContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ValuesContext { + var p = new(ValuesContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_values + + return p +} + +func (s *ValuesContext) GetParser() antlr.Parser { return s.parser } + +func (s *ValuesContext) AllValue() []IValueContext { + children := s.GetChildren() + len := 0 + for _, ctx := range children { + if _, ok := ctx.(IValueContext); ok { + len++ + } + } + + tst := make([]IValueContext, len) + i := 0 + for _, ctx := range children { + if t, ok := ctx.(IValueContext); ok { + tst[i] = t.(IValueContext) + i++ + } + } + + return tst +} + +func (s *ValuesContext) Value(i int) IValueContext { + var t antlr.RuleContext + j := 0 + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IValueContext); ok { + if j == i { + t = ctx.(antlr.RuleContext) + break + } + j++ + } + } + + if t == nil { + return nil + } + + return t.(IValueContext) +} + +func (s *ValuesContext) AllCOMMA() []antlr.TerminalNode { + return s.GetTokens(AliasesParserCOMMA) +} + +func (s *ValuesContext) COMMA(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserCOMMA, i) +} + +func (s *ValuesContext) AllSEPARATOR() []antlr.TerminalNode { + return s.GetTokens(AliasesParserSEPARATOR) +} + +func (s *ValuesContext) SEPARATOR(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserSEPARATOR, i) +} + +func (s *ValuesContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ValuesContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ValuesContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterValues(s) + } +} + +func (s *ValuesContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitValues(s) + } +} + +func (p *AliasesParser) Values() (localctx IValuesContext) { + localctx = NewValuesContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 8, AliasesParserRULE_values) + var _alt int + + p.EnterOuterAlt(localctx, 1) + p.SetState(64) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + if _alt == 1 { + { + p.SetState(58) + p.Value() + } + { + p.SetState(59) + p.Match(AliasesParserCOMMA) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(60) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(66) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + } + { + p.SetState(67) + p.Value() + } + +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 +} + +// IValueContext is an interface to support dynamic dispatch. +type IValueContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + User() IUserContext + File() IFileContext + Command() ICommandContext + Include() IIncludeContext + Email() IEmailContext + + // IsValueContext differentiates from other interfaces. + IsValueContext() +} + +type ValueContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyValueContext() *ValueContext { + var p = new(ValueContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_value + return p +} + +func InitEmptyValueContext(p *ValueContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_value +} + +func (*ValueContext) IsValueContext() {} + +func NewValueContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ValueContext { + var p = new(ValueContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_value + + return p +} + +func (s *ValueContext) GetParser() antlr.Parser { return s.parser } + +func (s *ValueContext) User() IUserContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IUserContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IUserContext) +} + +func (s *ValueContext) File() IFileContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IFileContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IFileContext) +} + +func (s *ValueContext) Command() ICommandContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(ICommandContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(ICommandContext) +} + +func (s *ValueContext) Include() IIncludeContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IIncludeContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IIncludeContext) +} + +func (s *ValueContext) Email() IEmailContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IEmailContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IEmailContext) +} + +func (s *ValueContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ValueContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ValueContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterValue(s) + } +} + +func (s *ValueContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitValue(s) + } +} + +func (p *AliasesParser) Value() (localctx IValueContext) { + localctx = NewValueContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 10, AliasesParserRULE_value) + p.EnterOuterAlt(localctx, 1) + p.SetState(74) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + + switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 6, p.GetParserRuleContext()) { + case 1: + { + p.SetState(69) + p.User() + } + + case 2: + { + p.SetState(70) + p.File() + } + + case 3: + { + p.SetState(71) + p.Command() + } + + case 4: + { + p.SetState(72) + p.Include() + } + + case 5: + { + p.SetState(73) + p.Email() + } + + case antlr.ATNInvalidAltNumber: + 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 +} + +// IUserContext is an interface to support dynamic dispatch. +type IUserContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + STRING() antlr.TerminalNode + + // IsUserContext differentiates from other interfaces. + IsUserContext() +} + +type UserContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyUserContext() *UserContext { + var p = new(UserContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_user + return p +} + +func InitEmptyUserContext(p *UserContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_user +} + +func (*UserContext) IsUserContext() {} + +func NewUserContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *UserContext { + var p = new(UserContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_user + + return p +} + +func (s *UserContext) GetParser() antlr.Parser { return s.parser } + +func (s *UserContext) STRING() antlr.TerminalNode { + return s.GetToken(AliasesParserSTRING, 0) +} + +func (s *UserContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *UserContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *UserContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterUser(s) + } +} + +func (s *UserContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitUser(s) + } +} + +func (p *AliasesParser) User() (localctx IUserContext) { + localctx = NewUserContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 12, AliasesParserRULE_user) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(76) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + 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 +} + +// IFileContext is an interface to support dynamic dispatch. +type IFileContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllSLASH() []antlr.TerminalNode + SLASH(i int) antlr.TerminalNode + AllSTRING() []antlr.TerminalNode + STRING(i int) antlr.TerminalNode + + // IsFileContext differentiates from other interfaces. + IsFileContext() +} + +type FileContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyFileContext() *FileContext { + var p = new(FileContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_file + return p +} + +func InitEmptyFileContext(p *FileContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_file +} + +func (*FileContext) IsFileContext() {} + +func NewFileContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FileContext { + var p = new(FileContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_file + + return p +} + +func (s *FileContext) GetParser() antlr.Parser { return s.parser } + +func (s *FileContext) AllSLASH() []antlr.TerminalNode { + return s.GetTokens(AliasesParserSLASH) +} + +func (s *FileContext) SLASH(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserSLASH, i) +} + +func (s *FileContext) AllSTRING() []antlr.TerminalNode { + return s.GetTokens(AliasesParserSTRING) +} + +func (s *FileContext) STRING(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserSTRING, i) +} + +func (s *FileContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *FileContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *FileContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterFile(s) + } +} + +func (s *FileContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitFile(s) + } +} + +func (p *AliasesParser) File() (localctx IFileContext) { + localctx = NewFileContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 14, AliasesParserRULE_file) + var _la int + + var _alt int + + p.EnterOuterAlt(localctx, 1) + p.SetState(80) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = 1 + for ok := true; ok; ok = _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + switch _alt { + case 1: + { + p.SetState(78) + p.Match(AliasesParserSLASH) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(79) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + default: + p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + goto errorExit + } + + p.SetState(82) + p.GetErrorHandler().Sync(p) + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 7, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + } + p.SetState(85) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSLASH { + { + p.SetState(84) + p.Match(AliasesParserSLASH) + if p.HasError() { + // Recognition error - abort rule + 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 +} + +// ICommandContext is an interface to support dynamic dispatch. +type ICommandContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + VERTLINE() antlr.TerminalNode + STRING() antlr.TerminalNode + + // IsCommandContext differentiates from other interfaces. + IsCommandContext() +} + +type CommandContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyCommandContext() *CommandContext { + var p = new(CommandContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_command + return p +} + +func InitEmptyCommandContext(p *CommandContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_command +} + +func (*CommandContext) IsCommandContext() {} + +func NewCommandContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *CommandContext { + var p = new(CommandContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_command + + return p +} + +func (s *CommandContext) GetParser() antlr.Parser { return s.parser } + +func (s *CommandContext) VERTLINE() antlr.TerminalNode { + return s.GetToken(AliasesParserVERTLINE, 0) +} + +func (s *CommandContext) STRING() antlr.TerminalNode { + return s.GetToken(AliasesParserSTRING, 0) +} + +func (s *CommandContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *CommandContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *CommandContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterCommand(s) + } +} + +func (s *CommandContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitCommand(s) + } +} + +func (p *AliasesParser) Command() (localctx ICommandContext) { + localctx = NewCommandContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 16, AliasesParserRULE_command) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(87) + p.Match(AliasesParserVERTLINE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(88) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + 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 +} + +// IIncludeContext is an interface to support dynamic dispatch. +type IIncludeContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllCOLON() []antlr.TerminalNode + COLON(i int) antlr.TerminalNode + INCLUDE() antlr.TerminalNode + File() IFileContext + + // IsIncludeContext differentiates from other interfaces. + IsIncludeContext() +} + +type IncludeContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyIncludeContext() *IncludeContext { + var p = new(IncludeContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_include + return p +} + +func InitEmptyIncludeContext(p *IncludeContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_include +} + +func (*IncludeContext) IsIncludeContext() {} + +func NewIncludeContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *IncludeContext { + var p = new(IncludeContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_include + + return p +} + +func (s *IncludeContext) GetParser() antlr.Parser { return s.parser } + +func (s *IncludeContext) AllCOLON() []antlr.TerminalNode { + return s.GetTokens(AliasesParserCOLON) +} + +func (s *IncludeContext) COLON(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserCOLON, i) +} + +func (s *IncludeContext) INCLUDE() antlr.TerminalNode { + return s.GetToken(AliasesParserINCLUDE, 0) +} + +func (s *IncludeContext) File() IFileContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IFileContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IFileContext) +} + +func (s *IncludeContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *IncludeContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *IncludeContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterInclude(s) + } +} + +func (s *IncludeContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitInclude(s) + } +} + +func (p *AliasesParser) Include() (localctx IIncludeContext) { + localctx = NewIncludeContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 18, AliasesParserRULE_include) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(90) + p.Match(AliasesParserCOLON) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(91) + p.Match(AliasesParserINCLUDE) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(92) + p.Match(AliasesParserCOLON) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(93) + p.File() + } + +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 +} + +// ICommentContext is an interface to support dynamic dispatch. +type ICommentContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + NUMBER_SIGN() antlr.TerminalNode + AllSTRING() []antlr.TerminalNode + STRING(i int) antlr.TerminalNode + AllSEPARATOR() []antlr.TerminalNode + SEPARATOR(i int) antlr.TerminalNode + + // IsCommentContext differentiates from other interfaces. + IsCommentContext() +} + +type CommentContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyCommentContext() *CommentContext { + var p = new(CommentContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_comment + return p +} + +func InitEmptyCommentContext(p *CommentContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_comment +} + +func (*CommentContext) IsCommentContext() {} + +func NewCommentContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *CommentContext { + var p = new(CommentContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_comment + + return p +} + +func (s *CommentContext) GetParser() antlr.Parser { return s.parser } + +func (s *CommentContext) NUMBER_SIGN() antlr.TerminalNode { + return s.GetToken(AliasesParserNUMBER_SIGN, 0) +} + +func (s *CommentContext) AllSTRING() []antlr.TerminalNode { + return s.GetTokens(AliasesParserSTRING) +} + +func (s *CommentContext) STRING(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserSTRING, i) +} + +func (s *CommentContext) AllSEPARATOR() []antlr.TerminalNode { + return s.GetTokens(AliasesParserSEPARATOR) +} + +func (s *CommentContext) SEPARATOR(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserSEPARATOR, i) +} + +func (s *CommentContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *CommentContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *CommentContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterComment(s) + } +} + +func (s *CommentContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitComment(s) + } +} + +func (p *AliasesParser) Comment() (localctx ICommentContext) { + localctx = NewCommentContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 20, AliasesParserRULE_comment) + var _la int + + var _alt int + + p.EnterOuterAlt(localctx, 1) + { + p.SetState(95) + p.Match(AliasesParserNUMBER_SIGN) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + p.SetState(100) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = 1 + for ok := true; ok; ok = _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + switch _alt { + case 1: + p.SetState(97) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSEPARATOR { + { + p.SetState(96) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(99) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + default: + p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + goto errorExit + } + + p.SetState(102) + p.GetErrorHandler().Sync(p) + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 10, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + } + p.SetState(105) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSEPARATOR { + { + p.SetState(104) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + 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 +} + +// IEmailContext is an interface to support dynamic dispatch. +type IEmailContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + AllSTRING() []antlr.TerminalNode + STRING(i int) antlr.TerminalNode + AT() antlr.TerminalNode + + // IsEmailContext differentiates from other interfaces. + IsEmailContext() +} + +type EmailContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyEmailContext() *EmailContext { + var p = new(EmailContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_email + return p +} + +func InitEmptyEmailContext(p *EmailContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_email +} + +func (*EmailContext) IsEmailContext() {} + +func NewEmailContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *EmailContext { + var p = new(EmailContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_email + + return p +} + +func (s *EmailContext) GetParser() antlr.Parser { return s.parser } + +func (s *EmailContext) AllSTRING() []antlr.TerminalNode { + return s.GetTokens(AliasesParserSTRING) +} + +func (s *EmailContext) STRING(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserSTRING, i) +} + +func (s *EmailContext) AT() antlr.TerminalNode { + return s.GetToken(AliasesParserAT, 0) +} + +func (s *EmailContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *EmailContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *EmailContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterEmail(s) + } +} + +func (s *EmailContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitEmail(s) + } +} + +func (p *AliasesParser) Email() (localctx IEmailContext) { + localctx = NewEmailContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 22, AliasesParserRULE_email) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(107) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(108) + p.Match(AliasesParserAT) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(109) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + 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 +} + +// IErrorContext is an interface to support dynamic dispatch. +type IErrorContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + ErrorStatus() IErrorStatusContext + COLON() antlr.TerminalNode + ErrorCode() IErrorCodeContext + SEPARATOR() antlr.TerminalNode + ErrorMessage() IErrorMessageContext + + // IsErrorContext differentiates from other interfaces. + IsErrorContext() +} + +type ErrorContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyErrorContext() *ErrorContext { + var p = new(ErrorContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_error + return p +} + +func InitEmptyErrorContext(p *ErrorContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_error +} + +func (*ErrorContext) IsErrorContext() {} + +func NewErrorContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ErrorContext { + var p = new(ErrorContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_error + + return p +} + +func (s *ErrorContext) GetParser() antlr.Parser { return s.parser } + +func (s *ErrorContext) ErrorStatus() IErrorStatusContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IErrorStatusContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IErrorStatusContext) +} + +func (s *ErrorContext) COLON() antlr.TerminalNode { + return s.GetToken(AliasesParserCOLON, 0) +} + +func (s *ErrorContext) ErrorCode() IErrorCodeContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IErrorCodeContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IErrorCodeContext) +} + +func (s *ErrorContext) SEPARATOR() antlr.TerminalNode { + return s.GetToken(AliasesParserSEPARATOR, 0) +} + +func (s *ErrorContext) ErrorMessage() IErrorMessageContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IErrorMessageContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IErrorMessageContext) +} + +func (s *ErrorContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ErrorContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ErrorContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterError(s) + } +} + +func (s *ErrorContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitError(s) + } +} + +func (p *AliasesParser) Error_() (localctx IErrorContext) { + localctx = NewErrorContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 24, AliasesParserRULE_error) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(111) + p.ErrorStatus() + } + { + p.SetState(112) + p.Match(AliasesParserCOLON) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(113) + p.ErrorCode() + } + { + p.SetState(114) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(115) + p.ErrorMessage() + } + +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 +} + +// IErrorStatusContext is an interface to support dynamic dispatch. +type IErrorStatusContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + STRING() antlr.TerminalNode + + // IsErrorStatusContext differentiates from other interfaces. + IsErrorStatusContext() +} + +type ErrorStatusContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyErrorStatusContext() *ErrorStatusContext { + var p = new(ErrorStatusContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_errorStatus + return p +} + +func InitEmptyErrorStatusContext(p *ErrorStatusContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_errorStatus +} + +func (*ErrorStatusContext) IsErrorStatusContext() {} + +func NewErrorStatusContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ErrorStatusContext { + var p = new(ErrorStatusContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_errorStatus + + return p +} + +func (s *ErrorStatusContext) GetParser() antlr.Parser { return s.parser } + +func (s *ErrorStatusContext) STRING() antlr.TerminalNode { + return s.GetToken(AliasesParserSTRING, 0) +} + +func (s *ErrorStatusContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ErrorStatusContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ErrorStatusContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterErrorStatus(s) + } +} + +func (s *ErrorStatusContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitErrorStatus(s) + } +} + +func (p *AliasesParser) ErrorStatus() (localctx IErrorStatusContext) { + localctx = NewErrorStatusContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 26, AliasesParserRULE_errorStatus) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(117) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + 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 +} + +// IErrorCodeContext is an interface to support dynamic dispatch. +type IErrorCodeContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + STRING() antlr.TerminalNode + + // IsErrorCodeContext differentiates from other interfaces. + IsErrorCodeContext() +} + +type ErrorCodeContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyErrorCodeContext() *ErrorCodeContext { + var p = new(ErrorCodeContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_errorCode + return p +} + +func InitEmptyErrorCodeContext(p *ErrorCodeContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_errorCode +} + +func (*ErrorCodeContext) IsErrorCodeContext() {} + +func NewErrorCodeContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ErrorCodeContext { + var p = new(ErrorCodeContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_errorCode + + return p +} + +func (s *ErrorCodeContext) GetParser() antlr.Parser { return s.parser } + +func (s *ErrorCodeContext) STRING() antlr.TerminalNode { + return s.GetToken(AliasesParserSTRING, 0) +} + +func (s *ErrorCodeContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ErrorCodeContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ErrorCodeContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterErrorCode(s) + } +} + +func (s *ErrorCodeContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitErrorCode(s) + } +} + +func (p *AliasesParser) ErrorCode() (localctx IErrorCodeContext) { + localctx = NewErrorCodeContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 28, AliasesParserRULE_errorCode) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(119) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + 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 +} + +// IErrorMessageContext is an interface to support dynamic dispatch. +type IErrorMessageContext interface { + antlr.ParserRuleContext + + // GetParser returns the parser. + GetParser() antlr.Parser + + // Getter signatures + STRING() antlr.TerminalNode + + // IsErrorMessageContext differentiates from other interfaces. + IsErrorMessageContext() +} + +type ErrorMessageContext struct { + antlr.BaseParserRuleContext + parser antlr.Parser +} + +func NewEmptyErrorMessageContext() *ErrorMessageContext { + var p = new(ErrorMessageContext) + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_errorMessage + return p +} + +func InitEmptyErrorMessageContext(p *ErrorMessageContext) { + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) + p.RuleIndex = AliasesParserRULE_errorMessage +} + +func (*ErrorMessageContext) IsErrorMessageContext() {} + +func NewErrorMessageContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ErrorMessageContext { + var p = new(ErrorMessageContext) + + antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) + + p.parser = parser + p.RuleIndex = AliasesParserRULE_errorMessage + + return p +} + +func (s *ErrorMessageContext) GetParser() antlr.Parser { return s.parser } + +func (s *ErrorMessageContext) STRING() antlr.TerminalNode { + return s.GetToken(AliasesParserSTRING, 0) +} + +func (s *ErrorMessageContext) GetRuleContext() antlr.RuleContext { + return s +} + +func (s *ErrorMessageContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { + return antlr.TreesStringTree(s, ruleNames, recog) +} + +func (s *ErrorMessageContext) EnterRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.EnterErrorMessage(s) + } +} + +func (s *ErrorMessageContext) ExitRule(listener antlr.ParseTreeListener) { + if listenerT, ok := listener.(AliasesListener); ok { + listenerT.ExitErrorMessage(s) + } +} + +func (p *AliasesParser) ErrorMessage() (localctx IErrorMessageContext) { + localctx = NewErrorMessageContext(p, p.GetParserRuleContext(), p.GetState()) + p.EnterRule(localctx, 30, AliasesParserRULE_errorMessage) + p.EnterOuterAlt(localctx, 1) + { + p.SetState(121) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + 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 +} diff --git a/handlers/aliases/tree/aliases.go b/handlers/aliases/tree/aliases.go new file mode 100644 index 0000000..7fda400 --- /dev/null +++ b/handlers/aliases/tree/aliases.go @@ -0,0 +1,39 @@ +package tree + +import ( + "config-lsp/common" +) + +// Procedure +// Save options in fields +// Each type, such as Include, User etc is own type +// Each type inherits interface +// This interface has methods such as: +// - CheckIsUser() +// For user, this checks if the user is listed in passwd +// For include, this includes the file and parses it and validates it +// + +// Parse content manually as the /etc/aliases file is so simple + +type AliasKey struct { + Location common.LocationRange + Value string +} + +type AliasValues struct { + Location common.LocationRange + Values []AliasValueInterface +} + +type AliasEntry struct { + Location common.LocationRange + Key *AliasKey + Separator *common.LocationRange + Values *AliasValues +} + +type AliasesParser struct { + Aliases map[uint32]*AliasEntry + CommentLines map[uint32]struct{} +} diff --git a/handlers/aliases/tree/handler.go b/handlers/aliases/tree/handler.go new file mode 100644 index 0000000..c6545c1 --- /dev/null +++ b/handlers/aliases/tree/handler.go @@ -0,0 +1 @@ +package tree diff --git a/handlers/aliases/tree/listener.go b/handlers/aliases/tree/listener.go new file mode 100644 index 0000000..65a8315 --- /dev/null +++ b/handlers/aliases/tree/listener.go @@ -0,0 +1,208 @@ +package tree + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases/parser" + + "github.com/antlr4-go/antlr/v4" +) + +type aliasesListenerContext struct { + line uint32 + currentIncludeIndex *uint32 +} + +type aliasesParserListener struct { + *parser.BaseAliasesListener + Parser *AliasesParser + Errors []common.LSPError + aliasContext aliasesListenerContext +} + +func (s *aliasesParserListener) EnterEntry(ctx *parser.EntryContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + s.Parser.Aliases[location.Start.Line] = &AliasEntry{ + Location: location, + } +} + +func (s *aliasesParserListener) EnterSeparator(ctx *parser.SeparatorContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + entry := s.Parser.Aliases[location.Start.Line] + entry.Separator = &location +} + +func (s *aliasesParserListener) EnterKey(ctx *parser.KeyContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + entry := s.Parser.Aliases[location.Start.Line] + entry.Key = &AliasKey{ + Location: location, + Value: ctx.GetText(), + } +} + +func (s *aliasesParserListener) EnterValues(ctx *parser.ValuesContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + entry := s.Parser.Aliases[location.Start.Line] + entry.Values = &AliasValues{ + Location: location, + Values: make([]AliasValueInterface, 0, 5), + } +} + +// === Value === // + +func (s *aliasesParserListener) EnterUser(ctx *parser.UserContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + user := AliasValueUser{ + AliasValue: AliasValue{ + Location: location, + Value: ctx.GetText(), + }, + } + + entry := s.Parser.Aliases[location.Start.Line] + entry.Values.Values = append(entry.Values.Values, user) +} + +func (s *aliasesParserListener) EnterFile(ctx *parser.FileContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + if s.aliasContext.currentIncludeIndex != nil { + // This `file` is inside an `include`, so we need to set the path on the include + values := s.Parser.Aliases[location.Start.Line].Values + rawValue := values.Values[*s.aliasContext.currentIncludeIndex] + + // Set the path + include := rawValue.(AliasValueInclude) + include.Path = AliasValueIncludePath{ + Location: location, + Path: path(ctx.GetText()), + } + values.Values[*s.aliasContext.currentIncludeIndex] = include + + // Clean up + s.aliasContext.currentIncludeIndex = nil + + return + } + + // Raw file, process it + file := AliasValueFile{ + AliasValue: AliasValue{ + Location: location, + Value: ctx.GetText(), + }, + Path: path(ctx.GetText()), + } + + entry := s.Parser.Aliases[location.Start.Line] + entry.Values.Values = append(entry.Values.Values, file) +} + +func (s *aliasesParserListener) EnterCommand(ctx *parser.CommandContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + command := AliasValueCommand{ + AliasValue: AliasValue{ + Location: location, + Value: ctx.GetText(), + }, + Command: ctx.GetText()[1:], + } + + entry := s.Parser.Aliases[location.Start.Line] + entry.Values.Values = append(entry.Values.Values, command) +} + +func (s *aliasesParserListener) EnterInclude(ctx *parser.IncludeContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + include := AliasValueInclude{ + AliasValue: AliasValue{ + Location: location, + Value: ctx.GetText(), + }, + } + + entry := s.Parser.Aliases[location.Start.Line] + entry.Values.Values = append(entry.Values.Values, include) + + index := uint32(len(entry.Values.Values) - 1) + s.aliasContext.currentIncludeIndex = &index +} + +func (s *aliasesParserListener) EnterEmail(ctx *parser.EmailContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + email := AliasValueEmail{ + AliasValue: AliasValue{ + Location: location, + Value: ctx.GetText(), + }, + } + + entry := s.Parser.Aliases[location.Start.Line] + entry.Values.Values = append(entry.Values.Values, email) +} + +func createListener( + parser *AliasesParser, + line uint32, +) aliasesParserListener { + return aliasesParserListener{ + Parser: parser, + aliasContext: aliasesListenerContext{ + line: line, + }, + Errors: make([]common.LSPError, 0), + } +} + +type errorListener struct { + *antlr.DefaultErrorListener + Errors []common.LSPError + context aliasesListenerContext +} + +func (d *errorListener) SyntaxError( + recognizer antlr.Recognizer, + offendingSymbol interface{}, + _ int, + character int, + message string, + error antlr.RecognitionException, +) { + line := d.context.line + d.Errors = append(d.Errors, common.LSPError{ + Range: common.CreateSingleCharRange(uint32(line), uint32(character)), + Err: common.SyntaxError{ + Message: message, + }, + }) +} + +func createErrorListener( + line uint32, +) errorListener { + return errorListener{ + Errors: make([]common.LSPError, 0), + context: aliasesListenerContext{ + line: line, + }, + } +} diff --git a/handlers/aliases/tree/parser.go b/handlers/aliases/tree/parser.go new file mode 100644 index 0000000..a31971c --- /dev/null +++ b/handlers/aliases/tree/parser.go @@ -0,0 +1,79 @@ +package tree + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases/parser" + "config-lsp/utils" + "github.com/antlr4-go/antlr/v4" + "regexp" +) + +func NewAliasesParser() AliasesParser { + p := AliasesParser{} + p.Clear() + + return p +} + +func (p *AliasesParser) Clear() { + p.CommentLines = make(map[uint32]struct{}) + p.Aliases = make(map[uint32]*AliasEntry) +} + +var commentPattern = regexp.MustCompile(`^\s*#.*$`) +var emptyPattern = regexp.MustCompile(`^\s*$`) + +func (p *AliasesParser) Parse(input string) []common.LSPError { + errors := make([]common.LSPError, 0) + lines := utils.SplitIntoLines(input) + + for rawLineNumber, line := range lines { + lineNumber := uint32(rawLineNumber) + + if commentPattern.MatchString(line) { + p.CommentLines[lineNumber] = struct{}{} + continue + } + + if emptyPattern.MatchString(line) { + continue + } + + errors = append( + errors, + p.parseStatement(lineNumber, line)..., + ) + } + + return errors +} + +func (p *AliasesParser) parseStatement( + line uint32, + input string, +) []common.LSPError { + stream := antlr.NewInputStream(input) + + lexerErrorListener := createErrorListener(line) + lexer := parser.NewAliasesLexer(stream) + lexer.RemoveErrorListeners() + lexer.AddErrorListener(&lexerErrorListener) + + tokenStream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) + + parserErrorListener := createErrorListener(line) + antlrParser := parser.NewAliasesParser(tokenStream) + antlrParser.RemoveErrorListeners() + antlrParser.AddErrorListener(&parserErrorListener) + + listener := createListener(p, line) + antlr.ParseTreeWalkerDefault.Walk( + &listener, + antlrParser.LineStatement(), + ) + + errors := lexerErrorListener.Errors + errors = append(errors, parserErrorListener.Errors...) + + return errors +} diff --git a/handlers/aliases/tree/parser_test.go b/handlers/aliases/tree/parser_test.go new file mode 100644 index 0000000..9ae4de5 --- /dev/null +++ b/handlers/aliases/tree/parser_test.go @@ -0,0 +1,107 @@ +package tree + +import ( + "config-lsp/utils" + "testing" +) + +func TestSimpleExampleWorks( + t *testing.T, +) { + input := utils.Dedent(` +postmaster: root +`) + + parser := NewAliasesParser() + errors := parser.Parse(input) + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + if !(len(parser.Aliases) == 1) { + t.Fatalf("Expected 1 alias, got %v", len(parser.Aliases)) + } + + if !(parser.Aliases[0].Key.Value == "postmaster") { + t.Fatalf("Expected key to be 'postmaster', got %v", parser.Aliases[1].Key.Value) + } + + userValue := parser.Aliases[0].Values.Values[0].(AliasValueUser) + if !(userValue.Value == "root") { + t.Fatalf("Expected value to be 'root', got %v", userValue.Value) + } + + if !(userValue.Location.Start.Line == 0) { + t.Fatalf("Expected start line to be 0, got %v", userValue.Location.Start.Line) + } +} + +func TestMultipleValuesWorks( + t *testing.T, +) { + input := utils.Dedent(` +heinz: root, goli +michel: raiks@example.com +`) + parser := NewAliasesParser() + errors := parser.Parse(input) + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + if !(len(parser.Aliases) == 2) { + t.Fatalf("Expected 2 aliases, got %v", len(parser.Aliases)) + } + + if !(parser.Aliases[0].Key.Value == "heinz") { + t.Fatalf("Expected key to be 'heinz', got %v", parser.Aliases[1].Key.Value) + } + + if !(parser.Aliases[1].Key.Value == "michel") { + t.Fatalf("Expected key to be 'michel', got %v", parser.Aliases[1].Key.Value) + } + + emailValue := parser.Aliases[1].Values.Values[0].(AliasValueEmail) + + if !(emailValue.Value == "raiks@example.com") { + t.Fatalf("Expected value to be 'raiks@example.com', got %v", emailValue.Value) + } +} + +func TestIncludeWorks( + t *testing.T, +) { + input := utils.Dedent(` +luke: :include:/etc/other_aliases +`) + parser := NewAliasesParser() + errors := parser.Parse(input) + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + if !(len(parser.Aliases) == 1) { + t.Fatalf("Expected 1 alias, got %v", len(parser.Aliases)) + } + + if !(parser.Aliases[0].Key.Value == "luke") { + t.Fatalf("Expected key to be 'luke', got %v", parser.Aliases[1].Key.Value) + } + + includeValue := parser.Aliases[0].Values.Values[0].(AliasValueInclude) + + if !(includeValue.Path.Path == "/etc/other_aliases") { + t.Fatalf("Expected path to be '/etc/other_aliases', got %v", includeValue.Path.Path) + } + + if !(includeValue.Location.Start.Character == 6 && includeValue.Location.End.Character == 33) { + t.Fatalf("Expected location to be 6-33, got %v-%v", includeValue.Location.Start.Character, includeValue.Location.End.Character) + } + + if !(includeValue.Path.Location.Start.Character == 15 && includeValue.Path.Location.End.Character == 33) { + t.Fatalf("Expected path location to be 15-33, got %v-%v", includeValue.Path.Location.Start.Character, includeValue.Path.Location.End.Character) + } +} diff --git a/handlers/aliases/tree/values.go b/handlers/aliases/tree/values.go new file mode 100644 index 0000000..1233c83 --- /dev/null +++ b/handlers/aliases/tree/values.go @@ -0,0 +1,55 @@ +package tree + +import ( + "config-lsp/common" + docvalues "config-lsp/doc-values" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +type AliasValueInterface interface { + FetchCompletions(line string, cursor uint32) []protocol.CompletionItem + CheckIsValid() []*docvalues.InvalidValue +} + +func (a AliasValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { + return nil +} +func (a AliasValue) CheckIsValid() []*docvalues.InvalidValue { + return nil +} + +type AliasValue struct { + Location common.LocationRange + Value string +} + +type AliasValueUser struct { + AliasValue +} + +type path string + +type AliasValueFile struct { + AliasValue + Path path +} + +type AliasValueCommand struct { + AliasValue + Command string +} + +type AliasValueIncludePath struct { + Location common.LocationRange + Path path +} + +type AliasValueInclude struct { + AliasValue + Path AliasValueIncludePath +} + +type AliasValueEmail struct { + AliasValue +} diff --git a/handlers/hosts/handlers/analyzer/hosts.go b/handlers/hosts/handlers/analyzer/hosts.go index 41e2739..b853e55 100644 --- a/handlers/hosts/handlers/analyzer/hosts.go +++ b/handlers/hosts/handlers/analyzer/hosts.go @@ -4,29 +4,8 @@ import ( "config-lsp/common" "fmt" "net" - - "github.com/antlr4-go/antlr/v4" ) -func characterRangeFromCtx( - ctx antlr.BaseParserRuleContext, -) common.LocationRange { - line := uint32(ctx.GetStart().GetLine()) - start := uint32(ctx.GetStart().GetStart()) - end := uint32(ctx.GetStop().GetStop()) - - return common.LocationRange{ - Start: common.Location{ - Line: line, - Character: start, - }, - End: common.Location{ - Line: line, - Character: end + 1, - }, - } -} - type HostsParser struct { Tree HostsTree CommentLines map[uint32]struct{} diff --git a/handlers/hosts/handlers/analyzer/listener.go b/handlers/hosts/handlers/analyzer/listener.go index 9409533..0785908 100644 --- a/handlers/hosts/handlers/analyzer/listener.go +++ b/handlers/hosts/handlers/analyzer/listener.go @@ -26,7 +26,7 @@ func (s *hostsParserListener) EnterComment(ctx *parser.CommentContext) { } func (s *hostsParserListener) EnterEntry(ctx *parser.EntryContext) { - location := characterRangeFromCtx(ctx.BaseParserRuleContext) + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) s.Parser.Tree.Entries[location.Start.Line] = &HostsEntry{ @@ -35,7 +35,7 @@ func (s *hostsParserListener) EnterEntry(ctx *parser.EntryContext) { } func (s *hostsParserListener) EnterIpAddress(ctx *parser.IpAddressContext) { - location := characterRangeFromCtx(ctx.BaseParserRuleContext) + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) ip := net.ParseIP(ctx.GetText()) @@ -66,7 +66,7 @@ func (s *hostsParserListener) EnterIpAddress(ctx *parser.IpAddressContext) { } func (s *hostsParserListener) EnterHostname(ctx *parser.HostnameContext) { - location := characterRangeFromCtx(ctx.BaseParserRuleContext) + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) entry := s.Parser.Tree.Entries[location.Start.Line] @@ -80,7 +80,7 @@ func (s *hostsParserListener) EnterHostname(ctx *parser.HostnameContext) { } func (s *hostsParserListener) EnterAliases(ctx *parser.AliasesContext) { - location := characterRangeFromCtx(ctx.BaseParserRuleContext) + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) entry := s.Parser.Tree.Entries[location.Start.Line] @@ -91,7 +91,7 @@ func (s *hostsParserListener) EnterAliases(ctx *parser.AliasesContext) { } func (s *hostsParserListener) EnterAlias(ctx *parser.AliasContext) { - location := characterRangeFromCtx(ctx.BaseParserRuleContext) + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) entry := s.Parser.Tree.Entries[location.Start.Line] @@ -123,17 +123,6 @@ type errorListener struct { hostsContext hostsListenerContext } -func createErrorListener( - line uint32, -) errorListener { - return errorListener{ - Errors: make([]common.LSPError, 0), - hostsContext: hostsListenerContext{ - line: line, - }, - } -} - func (d *errorListener) SyntaxError( recognizer antlr.Recognizer, offendingSymbol interface{}, @@ -150,3 +139,14 @@ func (d *errorListener) SyntaxError( }, }) } + +func createErrorListener( + line uint32, +) errorListener { + return errorListener{ + Errors: make([]common.LSPError, 0), + hostsContext: hostsListenerContext{ + line: line, + }, + } +} From 48bbb0f68b89b34b394392484d892834a91c2c5f Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:40:39 +0200 Subject: [PATCH 02/24] refactor(hosts): Improve hosts structure --- handlers/hosts/handlers/analyzer/analyzer.go | 11 ++--- .../hosts/handlers/analyzer/double_ips.go | 12 +++--- .../handlers/analyzer/double_ips_test.go | 26 +++++++++--- .../hosts/handlers/analyzer/handler_test.go | 7 ++-- .../analyzer/{resolver.go => resolve.go} | 42 +++++++------------ .../hosts/handlers/analyzer/resolver_test.go | 7 ++-- handlers/hosts/handlers/analyzer/values.go | 5 ++- .../hosts/handlers/{analyzer => ast}/hosts.go | 5 +-- .../handlers/{analyzer => ast}/listener.go | 2 +- .../{analyzer/handler.go => ast/parser.go} | 4 +- handlers/hosts/handlers/code-actions.go | 8 ++-- handlers/hosts/handlers/fetch-code-actions.go | 6 +-- handlers/hosts/handlers/hover.go | 11 ++--- handlers/hosts/indexes/indexes.go | 15 +++++++ handlers/hosts/indexes/resolver.go | 23 ++++++++++ handlers/hosts/lsp/shared.go | 9 ---- .../hosts/lsp/text-document-code-action.go | 6 +-- .../hosts/lsp/text-document-did-change.go | 9 ++-- handlers/hosts/lsp/text-document-did-close.go | 3 +- handlers/hosts/lsp/text-document-did-open.go | 14 +++++-- handlers/hosts/lsp/text-document-hover.go | 13 +++--- .../hosts/lsp/workspace-execute-command.go | 5 ++- handlers/hosts/shared.go | 15 +++++++ .../{handlers/analyzer => shared}/errors.go | 2 +- 24 files changed, 160 insertions(+), 100 deletions(-) rename handlers/hosts/handlers/analyzer/{resolver.go => resolve.go} (65%) rename handlers/hosts/handlers/{analyzer => ast}/hosts.go (88%) rename handlers/hosts/handlers/{analyzer => ast}/listener.go (99%) rename handlers/hosts/handlers/{analyzer/handler.go => ast/parser.go} (96%) create mode 100644 handlers/hosts/indexes/indexes.go create mode 100644 handlers/hosts/indexes/resolver.go delete mode 100644 handlers/hosts/lsp/shared.go create mode 100644 handlers/hosts/shared.go rename handlers/hosts/{handlers/analyzer => shared}/errors.go (96%) diff --git a/handlers/hosts/handlers/analyzer/analyzer.go b/handlers/hosts/handlers/analyzer/analyzer.go index 3f512b3..9a1c617 100644 --- a/handlers/hosts/handlers/analyzer/analyzer.go +++ b/handlers/hosts/handlers/analyzer/analyzer.go @@ -2,13 +2,14 @@ package analyzer import ( "config-lsp/common" + "config-lsp/handlers/hosts" "config-lsp/utils" protocol "github.com/tliron/glsp/protocol_3_16" ) -func Analyze(parser *HostsParser) []protocol.Diagnostic { - errors := analyzeEntriesSetCorrectly(*parser) +func Analyze(document *hosts.HostsDocument) []protocol.Diagnostic { + errors := analyzeEntriesSetCorrectly(*document.Parser) if len(errors) > 0 { return utils.Map( @@ -19,7 +20,7 @@ func Analyze(parser *HostsParser) []protocol.Diagnostic { ) } - errors = analyzeEntriesAreValid(*parser) + errors = analyzeEntriesAreValid(*document.Parser) if len(errors) > 0 { return utils.Map( @@ -30,8 +31,8 @@ func Analyze(parser *HostsParser) []protocol.Diagnostic { ) } - errors = append(errors, analyzeDoubleIPs(parser)...) - errors = append(errors, analyzeDoubleHostNames(parser)...) + errors = append(errors, analyzeDoubleIPs(document)...) + errors = append(errors, analyzeDoubleHostNames(document)...) return utils.Map( errors, diff --git a/handlers/hosts/handlers/analyzer/double_ips.go b/handlers/hosts/handlers/analyzer/double_ips.go index b3a01e7..237d5a6 100644 --- a/handlers/hosts/handlers/analyzer/double_ips.go +++ b/handlers/hosts/handlers/analyzer/double_ips.go @@ -2,6 +2,8 @@ package analyzer import ( "config-lsp/common" + "config-lsp/handlers/hosts" + "config-lsp/handlers/hosts/shared" "net" ) @@ -9,22 +11,22 @@ func ipToString(ip net.IPAddr) string { return ip.IP.String() } -func analyzeDoubleIPs(p *HostsParser) []common.LSPError { +func analyzeDoubleIPs(d *hosts.HostsDocument) []common.LSPError { errors := make([]common.LSPError, 0) ips := make(map[string]uint32) - p.DoubleIPs = make(map[uint32]DuplicateIPDeclaration) + d.Indexes.DoubleIPs = make(map[uint32]shared.DuplicateIPDeclaration) - for lineNumber, entry := range p.Tree.Entries { + for lineNumber, entry := range d.Parser.Tree.Entries { if entry.IPAddress != nil { key := ipToString(entry.IPAddress.Value) if foundLine, found := ips[key]; found { - err := DuplicateIPDeclaration{ + err := shared.DuplicateIPDeclaration{ AlreadyFoundAt: foundLine, } - p.DoubleIPs[lineNumber] = err + d.Indexes.DoubleIPs[lineNumber] = err errors = append(errors, common.LSPError{ Range: entry.IPAddress.Location, Err: err, diff --git a/handlers/hosts/handlers/analyzer/double_ips_test.go b/handlers/hosts/handlers/analyzer/double_ips_test.go index 979dd0b..dda6c3f 100644 --- a/handlers/hosts/handlers/analyzer/double_ips_test.go +++ b/handlers/hosts/handlers/analyzer/double_ips_test.go @@ -1,6 +1,10 @@ package analyzer import ( + "config-lsp/handlers/hosts" + "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/indexes" + "config-lsp/handlers/hosts/shared" "config-lsp/utils" "testing" ) @@ -15,14 +19,19 @@ func TestWorksWithNonDoubleIPs( 1.2.3.6 bar.com `) - parser := CreateNewHostsParser() + parser := ast.NewHostsParser() + i := indexes.NewHostsIndexes() + document := hosts.HostsDocument{ + Parser: &parser, + Indexes: &i, + } errors := parser.Parse(input) if len(errors) != 0 { t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors) } - errors = analyzeDoubleIPs(&parser) + errors = analyzeDoubleIPs(&document) if len(errors) != 0 { t.Errorf("Expected no errors, but got %v", errors) @@ -38,14 +47,19 @@ func TestWorksWithDoubleIPs( 1.2.3.4 foo.com `) - parser := CreateNewHostsParser() + parser := ast.NewHostsParser() + i := indexes.NewHostsIndexes() + document := hosts.HostsDocument{ + Parser: &parser, + Indexes: &i, + } errors := parser.Parse(input) if len(errors) != 0 { t.Fatalf("PARER FAILED! Expected no errors, but got %v", errors) } - errors = analyzeDoubleIPs(&parser) + errors = analyzeDoubleIPs(&document) if !(len(errors) == 1) { t.Errorf("Expected 1 error, but got %v", len(errors)) @@ -55,7 +69,7 @@ func TestWorksWithDoubleIPs( t.Errorf("Expected error on line 2, but got %v", errors[0].Range.Start.Line) } - if !(errors[0].Err.(DuplicateIPDeclaration).AlreadyFoundAt == 0) { - t.Errorf("Expected error on line 0, but got %v", errors[0].Err.(DuplicateIPDeclaration).AlreadyFoundAt) + if !(errors[0].Err.(shared.DuplicateIPDeclaration).AlreadyFoundAt == 0) { + t.Errorf("Expected error on line 0, but got %v", errors[0].Err.(shared.DuplicateIPDeclaration).AlreadyFoundAt) } } diff --git a/handlers/hosts/handlers/analyzer/handler_test.go b/handlers/hosts/handlers/analyzer/handler_test.go index 3f1b00f..b4c674e 100644 --- a/handlers/hosts/handlers/analyzer/handler_test.go +++ b/handlers/hosts/handlers/analyzer/handler_test.go @@ -1,6 +1,7 @@ package analyzer import ( + "config-lsp/handlers/hosts/handlers/ast" "config-lsp/utils" "net" "testing" @@ -13,7 +14,7 @@ func TestValidSimpleExampleWorks( 1.2.3.4 hello.com `) - parser := CreateNewHostsParser() + parser := ast.NewHostsParser() errors := parser.Parse(input) if len(errors) != 0 { @@ -80,7 +81,7 @@ func TestValidComplexExampleWorks( 1.2.3.4 example.com check.com `) - parser := CreateNewHostsParser() + parser := ast.NewHostsParser() errors := parser.Parse(input) if len(errors) != 0 { @@ -115,7 +116,7 @@ func TestInvalidExampleWorks( 1.2.3.4 `) - parser := CreateNewHostsParser() + parser := ast.NewHostsParser() errors := parser.Parse(input) if len(errors) == 0 { diff --git a/handlers/hosts/handlers/analyzer/resolver.go b/handlers/hosts/handlers/analyzer/resolve.go similarity index 65% rename from handlers/hosts/handlers/analyzer/resolver.go rename to handlers/hosts/handlers/analyzer/resolve.go index c06ce96..b060700 100644 --- a/handlers/hosts/handlers/analyzer/resolver.go +++ b/handlers/hosts/handlers/analyzer/resolve.go @@ -2,33 +2,19 @@ package analyzer import ( "config-lsp/common" + "config-lsp/handlers/hosts" + "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/indexes" + "config-lsp/handlers/hosts/shared" "config-lsp/utils" "net" ) -type ResolverEntry struct { - IPv4Address net.IP - IPv6Address net.IP - Line uint32 -} - -func (e ResolverEntry) GetInfo() string { - if e.IPv4Address != nil { - return e.IPv4Address.String() - } - - return e.IPv6Address.String() -} - -type Resolver struct { - Entries map[string]ResolverEntry -} - func createEntry( line uint32, ip net.IP, -) ResolverEntry { - entry := ResolverEntry{ +) indexes.ResolverEntry { + entry := indexes.ResolverEntry{ Line: line, } @@ -46,10 +32,10 @@ type hostnameEntry struct { HostName string } -func createResolverFromParser(p HostsParser) (Resolver, []common.LSPError) { +func createResolverFromParser(p ast.HostsParser) (indexes.Resolver, []common.LSPError) { errors := make([]common.LSPError, 0) - resolver := Resolver{ - Entries: make(map[string]ResolverEntry), + resolver := indexes.Resolver{ + Entries: make(map[string]indexes.ResolverEntry), } for lineNumber, entry := range p.Tree.Entries { @@ -63,7 +49,7 @@ func createResolverFromParser(p HostsParser) (Resolver, []common.LSPError) { }, utils.Map( entry.Aliases, - func(alias *HostsHostname) hostnameEntry { + func(alias *ast.HostsHostname) hostnameEntry { return hostnameEntry{ Location: alias.Location, HostName: alias.Value, @@ -83,7 +69,7 @@ func createResolverFromParser(p HostsParser) (Resolver, []common.LSPError) { errors, common.LSPError{ Range: hostName.Location, - Err: DuplicateHostEntry{ + Err: shared.DuplicateHostEntry{ AlreadyFoundAt: resolv.Line, Hostname: hostName.HostName, }, @@ -99,10 +85,10 @@ func createResolverFromParser(p HostsParser) (Resolver, []common.LSPError) { return resolver, errors } -func analyzeDoubleHostNames(p *HostsParser) []common.LSPError { - resolver, errors := createResolverFromParser(*p) +func analyzeDoubleHostNames(d *hosts.HostsDocument) []common.LSPError { + resolver, errors := createResolverFromParser(*d.Parser) - p.Resolver = &resolver + d.Indexes.Resolver = &resolver return errors } diff --git a/handlers/hosts/handlers/analyzer/resolver_test.go b/handlers/hosts/handlers/analyzer/resolver_test.go index f2a7247..b0a2a4b 100644 --- a/handlers/hosts/handlers/analyzer/resolver_test.go +++ b/handlers/hosts/handlers/analyzer/resolver_test.go @@ -1,6 +1,7 @@ package analyzer import ( + "config-lsp/handlers/hosts/handlers/ast" "config-lsp/utils" "testing" ) @@ -13,7 +14,7 @@ func TestResolverEntriesWorksWithNonOverlapping( 5.5.5.5 world.com `) - parser := CreateNewHostsParser() + parser := ast.NewHostsParser() errors := parser.Parse(input) if len(errors) != 0 { @@ -55,7 +56,7 @@ func TestResolverEntriesWithSimpleOverlapping( 5.5.5.5 hello.com `) - parser := CreateNewHostsParser() + parser := ast.NewHostsParser() errors := parser.Parse(input) if len(errors) != 0 { @@ -85,7 +86,7 @@ func TestResolverEntriesWithComplexOverlapping( 5.5.5.5 check.com test.com `) - parser := CreateNewHostsParser() + parser := ast.NewHostsParser() errors := parser.Parse(input) if len(errors) != 0 { diff --git a/handlers/hosts/handlers/analyzer/values.go b/handlers/hosts/handlers/analyzer/values.go index d04c415..ed27721 100644 --- a/handlers/hosts/handlers/analyzer/values.go +++ b/handlers/hosts/handlers/analyzer/values.go @@ -4,12 +4,13 @@ import ( "config-lsp/common" docvalues "config-lsp/doc-values" "config-lsp/handlers/hosts/fields" + "config-lsp/handlers/hosts/handlers/ast" "config-lsp/utils" "errors" ) func analyzeEntriesSetCorrectly( - parser HostsParser, + parser ast.HostsParser, ) []common.LSPError { err := make([]common.LSPError, 0) @@ -35,7 +36,7 @@ func analyzeEntriesSetCorrectly( } func analyzeEntriesAreValid( - parser HostsParser, + parser ast.HostsParser, ) []common.LSPError { err := make([]common.LSPError, 0) diff --git a/handlers/hosts/handlers/analyzer/hosts.go b/handlers/hosts/handlers/ast/hosts.go similarity index 88% rename from handlers/hosts/handlers/analyzer/hosts.go rename to handlers/hosts/handlers/ast/hosts.go index b853e55..5f28219 100644 --- a/handlers/hosts/handlers/analyzer/hosts.go +++ b/handlers/hosts/handlers/ast/hosts.go @@ -1,4 +1,4 @@ -package analyzer +package ast import ( "config-lsp/common" @@ -9,9 +9,6 @@ import ( type HostsParser struct { Tree HostsTree CommentLines map[uint32]struct{} - Resolver *Resolver - // [line]error - DoubleIPs map[uint32]DuplicateIPDeclaration } type HostsTree struct { diff --git a/handlers/hosts/handlers/analyzer/listener.go b/handlers/hosts/handlers/ast/listener.go similarity index 99% rename from handlers/hosts/handlers/analyzer/listener.go rename to handlers/hosts/handlers/ast/listener.go index 0785908..c5f2782 100644 --- a/handlers/hosts/handlers/analyzer/listener.go +++ b/handlers/hosts/handlers/ast/listener.go @@ -1,4 +1,4 @@ -package analyzer +package ast import ( "config-lsp/common" diff --git a/handlers/hosts/handlers/analyzer/handler.go b/handlers/hosts/handlers/ast/parser.go similarity index 96% rename from handlers/hosts/handlers/analyzer/handler.go rename to handlers/hosts/handlers/ast/parser.go index 33adbcd..03f3c92 100644 --- a/handlers/hosts/handlers/analyzer/handler.go +++ b/handlers/hosts/handlers/ast/parser.go @@ -1,4 +1,4 @@ -package analyzer +package ast import ( "config-lsp/common" @@ -74,7 +74,7 @@ func (p *HostsParser) Parse(input string) []common.LSPError { return errors } -func CreateNewHostsParser() HostsParser { +func NewHostsParser() HostsParser { p := HostsParser{} p.Clear() diff --git a/handlers/hosts/handlers/code-actions.go b/handlers/hosts/handlers/code-actions.go index 7de425a..b2f6bc4 100644 --- a/handlers/hosts/handlers/code-actions.go +++ b/handlers/hosts/handlers/code-actions.go @@ -1,7 +1,7 @@ package handlers import ( - "config-lsp/handlers/hosts/handlers/analyzer" + "config-lsp/handlers/hosts/handlers/ast" "config-lsp/utils" "fmt" "strings" @@ -16,7 +16,7 @@ const ( ) type CodeAction interface { - RunCommand(analyzer.HostsParser) (*protocol.ApplyWorkspaceEditParams, error) + RunCommand(ast.HostsParser) (*protocol.ApplyWorkspaceEditParams, error) } type CodeActionArgs interface{} @@ -35,7 +35,7 @@ func CodeActionInlineAliasesArgsFromArguments(arguments map[string]any) CodeActi } } -func (args CodeActionInlineAliasesArgs) RunCommand(hostsParser analyzer.HostsParser) (*protocol.ApplyWorkspaceEditParams, error) { +func (args CodeActionInlineAliasesArgs) RunCommand(hostsParser ast.HostsParser) (*protocol.ApplyWorkspaceEditParams, error) { fromEntry := hostsParser.Tree.Entries[args.FromLine] toEntry := hostsParser.Tree.Entries[args.ToLine] @@ -58,7 +58,7 @@ func (args CodeActionInlineAliasesArgs) RunCommand(hostsParser analyzer.HostsPar }, utils.Map( fromEntry.Aliases, - func(alias *analyzer.HostsHostname) string { + func(alias *ast.HostsHostname) string { return alias.Value }, )..., diff --git a/handlers/hosts/handlers/fetch-code-actions.go b/handlers/hosts/handlers/fetch-code-actions.go index 6374c5e..59b13c4 100644 --- a/handlers/hosts/handlers/fetch-code-actions.go +++ b/handlers/hosts/handlers/fetch-code-actions.go @@ -1,17 +1,17 @@ package handlers import ( - "config-lsp/handlers/hosts/handlers/analyzer" + "config-lsp/handlers/hosts" protocol "github.com/tliron/glsp/protocol_3_16" ) func GetInlineAliasesCodeAction( - p analyzer.HostsParser, + d hosts.HostsDocument, params *protocol.CodeActionParams, ) []protocol.CodeAction { line := params.Range.Start.Line - if duplicateInfo, found := p.DoubleIPs[line]; found { + if duplicateInfo, found := d.Indexes.DoubleIPs[line]; found { commandID := "hosts." + CodeActionInlineAliases command := protocol.Command{ Title: "Inline Aliases", diff --git a/handlers/hosts/handlers/hover.go b/handlers/hosts/handlers/hover.go index 168ba47..2361408 100644 --- a/handlers/hosts/handlers/hover.go +++ b/handlers/hosts/handlers/hover.go @@ -1,7 +1,8 @@ package handlers import ( - "config-lsp/handlers/hosts/handlers/analyzer" + "config-lsp/handlers/hosts" + "config-lsp/handlers/hosts/handlers/ast" "fmt" ) @@ -14,7 +15,7 @@ const ( ) func GetHoverTargetInEntry( - e analyzer.HostsEntry, + e ast.HostsEntry, cursor uint32, ) *HoverTarget { if e.IPAddress != nil && e.IPAddress.Location.ContainsCursorByCharacter(cursor) { @@ -38,11 +39,11 @@ func GetHoverTargetInEntry( } func GetHoverInfoForHostname( - parser analyzer.HostsParser, - hostname analyzer.HostsHostname, + d hosts.HostsDocument, + hostname ast.HostsHostname, cursor uint32, ) []string { - ipAddress := parser.Resolver.Entries[hostname.Value] + ipAddress := d.Indexes.Resolver.Entries[hostname.Value] return []string{ fmt.Sprintf("**%s** maps to _%s_", hostname.Value, ipAddress.GetInfo()), diff --git a/handlers/hosts/indexes/indexes.go b/handlers/hosts/indexes/indexes.go new file mode 100644 index 0000000..512a724 --- /dev/null +++ b/handlers/hosts/indexes/indexes.go @@ -0,0 +1,15 @@ +package indexes + +import "config-lsp/handlers/hosts/shared" + +type HostsIndexes struct { + Resolver *Resolver + // [line]error + DoubleIPs map[uint32]shared.DuplicateIPDeclaration +} + +func NewHostsIndexes() HostsIndexes { + return HostsIndexes{ + DoubleIPs: make(map[uint32]shared.DuplicateIPDeclaration), + } +} diff --git a/handlers/hosts/indexes/resolver.go b/handlers/hosts/indexes/resolver.go new file mode 100644 index 0000000..aabb6f4 --- /dev/null +++ b/handlers/hosts/indexes/resolver.go @@ -0,0 +1,23 @@ +package indexes + +import ( + "net" +) + +type ResolverEntry struct { + IPv4Address net.IP + IPv6Address net.IP + Line uint32 +} + +func (e ResolverEntry) GetInfo() string { + if e.IPv4Address != nil { + return e.IPv4Address.String() + } + + return e.IPv6Address.String() +} + +type Resolver struct { + Entries map[string]ResolverEntry +} diff --git a/handlers/hosts/lsp/shared.go b/handlers/hosts/lsp/shared.go deleted file mode 100644 index c231057..0000000 --- a/handlers/hosts/lsp/shared.go +++ /dev/null @@ -1,9 +0,0 @@ -package lsp - -import ( - "config-lsp/handlers/hosts/handlers/analyzer" - - protocol "github.com/tliron/glsp/protocol_3_16" -) - -var documentParserMap = map[protocol.DocumentUri]*analyzer.HostsParser{} diff --git a/handlers/hosts/lsp/text-document-code-action.go b/handlers/hosts/lsp/text-document-code-action.go index d15740e..272e7cc 100644 --- a/handlers/hosts/lsp/text-document-code-action.go +++ b/handlers/hosts/lsp/text-document-code-action.go @@ -1,18 +1,18 @@ package lsp import ( + "config-lsp/handlers/hosts" "config-lsp/handlers/hosts/handlers" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) { - parser := documentParserMap[params.TextDocument.URI] + document := hosts.DocumentParserMap[params.TextDocument.URI] actions := make([]protocol.CodeAction, 0, 1) - actions = append(actions, handlers.GetInlineAliasesCodeAction(*parser, params)...) + actions = append(actions, handlers.GetInlineAliasesCodeAction(*document, params)...) if len(actions) > 0 { return actions, nil diff --git a/handlers/hosts/lsp/text-document-did-change.go b/handlers/hosts/lsp/text-document-did-change.go index 4aa141e..e6ba5e6 100644 --- a/handlers/hosts/lsp/text-document-did-change.go +++ b/handlers/hosts/lsp/text-document-did-change.go @@ -2,6 +2,7 @@ package lsp import ( "config-lsp/common" + "config-lsp/handlers/hosts" "config-lsp/handlers/hosts/handlers/analyzer" "config-lsp/utils" @@ -16,11 +17,11 @@ func TextDocumentDidChange( content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text common.ClearDiagnostics(context, params.TextDocument.URI) - parser := documentParserMap[params.TextDocument.URI] - parser.Clear() + document := hosts.DocumentParserMap[params.TextDocument.URI] + document.Parser.Clear() diagnostics := make([]protocol.Diagnostic, 0) - errors := parser.Parse(content) + errors := document.Parser.Parse(content) if len(errors) > 0 { diagnostics = append(diagnostics, utils.Map( @@ -31,7 +32,7 @@ func TextDocumentDidChange( )...) } - diagnostics = append(diagnostics, analyzer.Analyze(parser)...) + diagnostics = append(diagnostics, analyzer.Analyze(document)...) if len(diagnostics) > 0 { common.SendDiagnostics(context, params.TextDocument.URI, diagnostics) diff --git a/handlers/hosts/lsp/text-document-did-close.go b/handlers/hosts/lsp/text-document-did-close.go index 3961658..3283aad 100644 --- a/handlers/hosts/lsp/text-document-did-close.go +++ b/handlers/hosts/lsp/text-document-did-close.go @@ -1,12 +1,13 @@ package lsp import ( + "config-lsp/handlers/hosts" "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error { - delete(documentParserMap, params.TextDocument.URI) + delete(hosts.DocumentParserMap, params.TextDocument.URI) return nil } diff --git a/handlers/hosts/lsp/text-document-did-open.go b/handlers/hosts/lsp/text-document-did-open.go index 7be0663..a9886cf 100644 --- a/handlers/hosts/lsp/text-document-did-open.go +++ b/handlers/hosts/lsp/text-document-did-open.go @@ -2,7 +2,10 @@ package lsp import ( "config-lsp/common" + "config-lsp/handlers/hosts" "config-lsp/handlers/hosts/handlers/analyzer" + "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/indexes" "config-lsp/utils" "github.com/tliron/glsp" @@ -15,8 +18,13 @@ func TextDocumentDidOpen( ) error { common.ClearDiagnostics(context, params.TextDocument.URI) - parser := analyzer.CreateNewHostsParser() - documentParserMap[params.TextDocument.URI] = &parser + parser := ast.NewHostsParser() + i := indexes.NewHostsIndexes() + document := hosts.HostsDocument{ + Parser: &parser, + Indexes: &i, + } + hosts.DocumentParserMap[params.TextDocument.URI] = &document errors := parser.Parse(params.TextDocument.Text) @@ -29,7 +37,7 @@ func TextDocumentDidOpen( diagnostics = append( diagnostics, - analyzer.Analyze(&parser)..., + analyzer.Analyze(&document)..., ) if len(diagnostics) > 0 { diff --git a/handlers/hosts/lsp/text-document-hover.go b/handlers/hosts/lsp/text-document-hover.go index 39dc2aa..fd76edb 100644 --- a/handlers/hosts/lsp/text-document-hover.go +++ b/handlers/hosts/lsp/text-document-hover.go @@ -1,9 +1,10 @@ package lsp import ( + "config-lsp/handlers/hosts" "config-lsp/handlers/hosts/fields" "config-lsp/handlers/hosts/handlers" - "config-lsp/handlers/hosts/handlers/analyzer" + "config-lsp/handlers/hosts/handlers/ast" "strings" "github.com/tliron/glsp" @@ -14,17 +15,17 @@ func TextDocumentHover( context *glsp.Context, params *protocol.HoverParams, ) (*protocol.Hover, error) { - parser := documentParserMap[params.TextDocument.URI] + document := hosts.DocumentParserMap[params.TextDocument.URI] line := params.Position.Line character := params.Position.Character - if _, found := parser.CommentLines[line]; found { + if _, found := document.Parser.CommentLines[line]; found { // Comment return nil, nil } - entry, found := parser.Tree.Entries[line] + entry, found := document.Parser.Tree.Entries[line] if !found { // Empty line @@ -33,7 +34,7 @@ func TextDocumentHover( target := handlers.GetHoverTargetInEntry(*entry, character) - var hostname *analyzer.HostsHostname + var hostname *ast.HostsHostname switch *target { case handlers.HoverTargetIPAddress: @@ -74,7 +75,7 @@ func TextDocumentHover( ) contents = append( contents, - handlers.GetHoverInfoForHostname(*parser, *hostname, character)..., + handlers.GetHoverInfoForHostname(*document, *hostname, character)..., ) return &protocol.Hover{ diff --git a/handlers/hosts/lsp/workspace-execute-command.go b/handlers/hosts/lsp/workspace-execute-command.go index 75cb9b6..d67898f 100644 --- a/handlers/hosts/lsp/workspace-execute-command.go +++ b/handlers/hosts/lsp/workspace-execute-command.go @@ -1,6 +1,7 @@ package lsp import ( + "config-lsp/handlers/hosts" "config-lsp/handlers/hosts/handlers" "strings" @@ -15,9 +16,9 @@ func WorkspaceExecuteCommand(context *glsp.Context, params *protocol.ExecuteComm case string(handlers.CodeActionInlineAliases): args := handlers.CodeActionInlineAliasesArgsFromArguments(params.Arguments[0].(map[string]any)) - parser := documentParserMap[args.URI] + document := hosts.DocumentParserMap[args.URI] - return args.RunCommand(*parser) + return args.RunCommand(*document.Parser) } return nil, nil diff --git a/handlers/hosts/shared.go b/handlers/hosts/shared.go new file mode 100644 index 0000000..d17e04c --- /dev/null +++ b/handlers/hosts/shared.go @@ -0,0 +1,15 @@ +package hosts + +import ( + "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/indexes" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +type HostsDocument struct { + Parser *ast.HostsParser + Indexes *indexes.HostsIndexes +} + +var DocumentParserMap = map[protocol.DocumentUri]*HostsDocument{} diff --git a/handlers/hosts/handlers/analyzer/errors.go b/handlers/hosts/shared/errors.go similarity index 96% rename from handlers/hosts/handlers/analyzer/errors.go rename to handlers/hosts/shared/errors.go index 9b9a7ff..f8885ca 100644 --- a/handlers/hosts/handlers/analyzer/errors.go +++ b/handlers/hosts/shared/errors.go @@ -1,4 +1,4 @@ -package analyzer +package shared import "fmt" From 5cbf5be8ad4e334594d8178a18bd0190230e16c6 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 29 Aug 2024 22:51:39 +0200 Subject: [PATCH 03/24] refactor(hosts): Improve hosts structure --- handlers/aliases/{tree => ast}/aliases.go | 0 handlers/aliases/{tree => ast}/handler.go | 0 handlers/aliases/{tree => ast}/listener.go | 0 handlers/aliases/{tree => ast}/parser.go | 0 handlers/aliases/{tree => ast}/parser_test.go | 0 handlers/aliases/{tree => ast}/values.go | 0 .../hosts/{handlers => }/analyzer/analyzer.go | 0 .../hosts/{handlers => }/analyzer/double_ips.go | 0 .../{handlers => }/analyzer/double_ips_test.go | 2 +- .../{handlers => }/analyzer/handler_test.go | 2 +- .../hosts/{handlers => }/analyzer/resolve.go | 2 +- .../{handlers => }/analyzer/resolver_test.go | 2 +- handlers/hosts/{handlers => }/analyzer/values.go | 2 +- handlers/hosts/{handlers => }/ast/hosts.go | 0 handlers/hosts/{handlers => }/ast/listener.go | 16 ++++++++-------- handlers/hosts/{handlers => }/ast/parser.go | 6 +++--- handlers/hosts/{ => ast}/parser/Hosts.interp | 0 handlers/hosts/{ => ast}/parser/Hosts.tokens | 0 .../hosts/{ => ast}/parser/HostsLexer.interp | 0 .../hosts/{ => ast}/parser/HostsLexer.tokens | 0 .../{ => ast}/parser/hosts_base_listener.go | 0 handlers/hosts/{ => ast}/parser/hosts_lexer.go | 0 .../hosts/{ => ast}/parser/hosts_listener.go | 0 handlers/hosts/{ => ast}/parser/hosts_parser.go | 0 handlers/hosts/handlers/code-actions.go | 2 +- handlers/hosts/handlers/hover.go | 2 +- handlers/hosts/indexes/indexes.go | 4 +++- handlers/hosts/lsp/text-document-did-change.go | 2 +- handlers/hosts/lsp/text-document-did-open.go | 4 ++-- handlers/hosts/lsp/text-document-hover.go | 2 +- handlers/hosts/shared.go | 3 +-- 31 files changed, 26 insertions(+), 25 deletions(-) rename handlers/aliases/{tree => ast}/aliases.go (100%) rename handlers/aliases/{tree => ast}/handler.go (100%) rename handlers/aliases/{tree => ast}/listener.go (100%) rename handlers/aliases/{tree => ast}/parser.go (100%) rename handlers/aliases/{tree => ast}/parser_test.go (100%) rename handlers/aliases/{tree => ast}/values.go (100%) rename handlers/hosts/{handlers => }/analyzer/analyzer.go (100%) rename handlers/hosts/{handlers => }/analyzer/double_ips.go (100%) rename handlers/hosts/{handlers => }/analyzer/double_ips_test.go (97%) rename handlers/hosts/{handlers => }/analyzer/handler_test.go (98%) rename handlers/hosts/{handlers => }/analyzer/resolve.go (97%) rename handlers/hosts/{handlers => }/analyzer/resolver_test.go (98%) rename handlers/hosts/{handlers => }/analyzer/values.go (97%) rename handlers/hosts/{handlers => }/ast/hosts.go (100%) rename handlers/hosts/{handlers => }/ast/listener.go (85%) rename handlers/hosts/{handlers => }/ast/parser.go (91%) rename handlers/hosts/{ => ast}/parser/Hosts.interp (100%) rename handlers/hosts/{ => ast}/parser/Hosts.tokens (100%) rename handlers/hosts/{ => ast}/parser/HostsLexer.interp (100%) rename handlers/hosts/{ => ast}/parser/HostsLexer.tokens (100%) rename handlers/hosts/{ => ast}/parser/hosts_base_listener.go (100%) rename handlers/hosts/{ => ast}/parser/hosts_lexer.go (100%) rename handlers/hosts/{ => ast}/parser/hosts_listener.go (100%) rename handlers/hosts/{ => ast}/parser/hosts_parser.go (100%) diff --git a/handlers/aliases/tree/aliases.go b/handlers/aliases/ast/aliases.go similarity index 100% rename from handlers/aliases/tree/aliases.go rename to handlers/aliases/ast/aliases.go diff --git a/handlers/aliases/tree/handler.go b/handlers/aliases/ast/handler.go similarity index 100% rename from handlers/aliases/tree/handler.go rename to handlers/aliases/ast/handler.go diff --git a/handlers/aliases/tree/listener.go b/handlers/aliases/ast/listener.go similarity index 100% rename from handlers/aliases/tree/listener.go rename to handlers/aliases/ast/listener.go diff --git a/handlers/aliases/tree/parser.go b/handlers/aliases/ast/parser.go similarity index 100% rename from handlers/aliases/tree/parser.go rename to handlers/aliases/ast/parser.go diff --git a/handlers/aliases/tree/parser_test.go b/handlers/aliases/ast/parser_test.go similarity index 100% rename from handlers/aliases/tree/parser_test.go rename to handlers/aliases/ast/parser_test.go diff --git a/handlers/aliases/tree/values.go b/handlers/aliases/ast/values.go similarity index 100% rename from handlers/aliases/tree/values.go rename to handlers/aliases/ast/values.go diff --git a/handlers/hosts/handlers/analyzer/analyzer.go b/handlers/hosts/analyzer/analyzer.go similarity index 100% rename from handlers/hosts/handlers/analyzer/analyzer.go rename to handlers/hosts/analyzer/analyzer.go diff --git a/handlers/hosts/handlers/analyzer/double_ips.go b/handlers/hosts/analyzer/double_ips.go similarity index 100% rename from handlers/hosts/handlers/analyzer/double_ips.go rename to handlers/hosts/analyzer/double_ips.go diff --git a/handlers/hosts/handlers/analyzer/double_ips_test.go b/handlers/hosts/analyzer/double_ips_test.go similarity index 97% rename from handlers/hosts/handlers/analyzer/double_ips_test.go rename to handlers/hosts/analyzer/double_ips_test.go index dda6c3f..b095018 100644 --- a/handlers/hosts/handlers/analyzer/double_ips_test.go +++ b/handlers/hosts/analyzer/double_ips_test.go @@ -2,7 +2,7 @@ package analyzer import ( "config-lsp/handlers/hosts" - "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/ast" "config-lsp/handlers/hosts/indexes" "config-lsp/handlers/hosts/shared" "config-lsp/utils" diff --git a/handlers/hosts/handlers/analyzer/handler_test.go b/handlers/hosts/analyzer/handler_test.go similarity index 98% rename from handlers/hosts/handlers/analyzer/handler_test.go rename to handlers/hosts/analyzer/handler_test.go index b4c674e..38e34c1 100644 --- a/handlers/hosts/handlers/analyzer/handler_test.go +++ b/handlers/hosts/analyzer/handler_test.go @@ -1,7 +1,7 @@ package analyzer import ( - "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/ast" "config-lsp/utils" "net" "testing" diff --git a/handlers/hosts/handlers/analyzer/resolve.go b/handlers/hosts/analyzer/resolve.go similarity index 97% rename from handlers/hosts/handlers/analyzer/resolve.go rename to handlers/hosts/analyzer/resolve.go index b060700..846af98 100644 --- a/handlers/hosts/handlers/analyzer/resolve.go +++ b/handlers/hosts/analyzer/resolve.go @@ -3,7 +3,7 @@ package analyzer import ( "config-lsp/common" "config-lsp/handlers/hosts" - "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/ast" "config-lsp/handlers/hosts/indexes" "config-lsp/handlers/hosts/shared" "config-lsp/utils" diff --git a/handlers/hosts/handlers/analyzer/resolver_test.go b/handlers/hosts/analyzer/resolver_test.go similarity index 98% rename from handlers/hosts/handlers/analyzer/resolver_test.go rename to handlers/hosts/analyzer/resolver_test.go index b0a2a4b..df720e7 100644 --- a/handlers/hosts/handlers/analyzer/resolver_test.go +++ b/handlers/hosts/analyzer/resolver_test.go @@ -1,7 +1,7 @@ package analyzer import ( - "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/ast" "config-lsp/utils" "testing" ) diff --git a/handlers/hosts/handlers/analyzer/values.go b/handlers/hosts/analyzer/values.go similarity index 97% rename from handlers/hosts/handlers/analyzer/values.go rename to handlers/hosts/analyzer/values.go index ed27721..e6da88e 100644 --- a/handlers/hosts/handlers/analyzer/values.go +++ b/handlers/hosts/analyzer/values.go @@ -3,8 +3,8 @@ package analyzer import ( "config-lsp/common" docvalues "config-lsp/doc-values" + "config-lsp/handlers/hosts/ast" "config-lsp/handlers/hosts/fields" - "config-lsp/handlers/hosts/handlers/ast" "config-lsp/utils" "errors" ) diff --git a/handlers/hosts/handlers/ast/hosts.go b/handlers/hosts/ast/hosts.go similarity index 100% rename from handlers/hosts/handlers/ast/hosts.go rename to handlers/hosts/ast/hosts.go diff --git a/handlers/hosts/handlers/ast/listener.go b/handlers/hosts/ast/listener.go similarity index 85% rename from handlers/hosts/handlers/ast/listener.go rename to handlers/hosts/ast/listener.go index c5f2782..dd26bb6 100644 --- a/handlers/hosts/handlers/ast/listener.go +++ b/handlers/hosts/ast/listener.go @@ -3,7 +3,7 @@ package ast import ( "config-lsp/common" docvalues "config-lsp/doc-values" - "config-lsp/handlers/hosts/parser" + parser2 "config-lsp/handlers/hosts/ast/parser" "net" "github.com/antlr4-go/antlr/v4" @@ -14,18 +14,18 @@ type hostsListenerContext struct { } type hostsParserListener struct { - *parser.BaseHostsListener + *parser2.BaseHostsListener Parser *HostsParser Errors []common.LSPError hostsContext hostsListenerContext } -func (s *hostsParserListener) EnterComment(ctx *parser.CommentContext) { +func (s *hostsParserListener) EnterComment(ctx *parser2.CommentContext) { line := uint32(s.hostsContext.line) s.Parser.CommentLines[line] = struct{}{} } -func (s *hostsParserListener) EnterEntry(ctx *parser.EntryContext) { +func (s *hostsParserListener) EnterEntry(ctx *parser2.EntryContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) @@ -34,7 +34,7 @@ func (s *hostsParserListener) EnterEntry(ctx *parser.EntryContext) { } } -func (s *hostsParserListener) EnterIpAddress(ctx *parser.IpAddressContext) { +func (s *hostsParserListener) EnterIpAddress(ctx *parser2.IpAddressContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) @@ -65,7 +65,7 @@ func (s *hostsParserListener) EnterIpAddress(ctx *parser.IpAddressContext) { } } -func (s *hostsParserListener) EnterHostname(ctx *parser.HostnameContext) { +func (s *hostsParserListener) EnterHostname(ctx *parser2.HostnameContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) @@ -79,7 +79,7 @@ func (s *hostsParserListener) EnterHostname(ctx *parser.HostnameContext) { s.Parser.Tree.Entries[location.Start.Line] = entry } -func (s *hostsParserListener) EnterAliases(ctx *parser.AliasesContext) { +func (s *hostsParserListener) EnterAliases(ctx *parser2.AliasesContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) @@ -90,7 +90,7 @@ func (s *hostsParserListener) EnterAliases(ctx *parser.AliasesContext) { entry.Aliases = aliases } -func (s *hostsParserListener) EnterAlias(ctx *parser.AliasContext) { +func (s *hostsParserListener) EnterAlias(ctx *parser2.AliasContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.hostsContext.line) diff --git a/handlers/hosts/handlers/ast/parser.go b/handlers/hosts/ast/parser.go similarity index 91% rename from handlers/hosts/handlers/ast/parser.go rename to handlers/hosts/ast/parser.go index 03f3c92..bfdcf82 100644 --- a/handlers/hosts/handlers/ast/parser.go +++ b/handlers/hosts/ast/parser.go @@ -2,7 +2,7 @@ package ast import ( "config-lsp/common" - "config-lsp/handlers/hosts/parser" + parser2 "config-lsp/handlers/hosts/ast/parser" "config-lsp/utils" "regexp" @@ -26,7 +26,7 @@ func (p *HostsParser) parseStatement( stream := antlr.NewInputStream(input) errorListener := createErrorListener(line) - lexer := parser.NewHostsLexer(stream) + lexer := parser2.NewHostsLexer(stream) lexer.RemoveErrorListeners() lexer.AddErrorListener(&errorListener) @@ -34,7 +34,7 @@ func (p *HostsParser) parseStatement( errorListener = createErrorListener(line) tokenStream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel) - antlrParser := parser.NewHostsParser(tokenStream) + antlrParser := parser2.NewHostsParser(tokenStream) antlrParser.RemoveErrorListeners() antlrParser.AddErrorListener(&errorListener) diff --git a/handlers/hosts/parser/Hosts.interp b/handlers/hosts/ast/parser/Hosts.interp similarity index 100% rename from handlers/hosts/parser/Hosts.interp rename to handlers/hosts/ast/parser/Hosts.interp diff --git a/handlers/hosts/parser/Hosts.tokens b/handlers/hosts/ast/parser/Hosts.tokens similarity index 100% rename from handlers/hosts/parser/Hosts.tokens rename to handlers/hosts/ast/parser/Hosts.tokens diff --git a/handlers/hosts/parser/HostsLexer.interp b/handlers/hosts/ast/parser/HostsLexer.interp similarity index 100% rename from handlers/hosts/parser/HostsLexer.interp rename to handlers/hosts/ast/parser/HostsLexer.interp diff --git a/handlers/hosts/parser/HostsLexer.tokens b/handlers/hosts/ast/parser/HostsLexer.tokens similarity index 100% rename from handlers/hosts/parser/HostsLexer.tokens rename to handlers/hosts/ast/parser/HostsLexer.tokens diff --git a/handlers/hosts/parser/hosts_base_listener.go b/handlers/hosts/ast/parser/hosts_base_listener.go similarity index 100% rename from handlers/hosts/parser/hosts_base_listener.go rename to handlers/hosts/ast/parser/hosts_base_listener.go diff --git a/handlers/hosts/parser/hosts_lexer.go b/handlers/hosts/ast/parser/hosts_lexer.go similarity index 100% rename from handlers/hosts/parser/hosts_lexer.go rename to handlers/hosts/ast/parser/hosts_lexer.go diff --git a/handlers/hosts/parser/hosts_listener.go b/handlers/hosts/ast/parser/hosts_listener.go similarity index 100% rename from handlers/hosts/parser/hosts_listener.go rename to handlers/hosts/ast/parser/hosts_listener.go diff --git a/handlers/hosts/parser/hosts_parser.go b/handlers/hosts/ast/parser/hosts_parser.go similarity index 100% rename from handlers/hosts/parser/hosts_parser.go rename to handlers/hosts/ast/parser/hosts_parser.go diff --git a/handlers/hosts/handlers/code-actions.go b/handlers/hosts/handlers/code-actions.go index b2f6bc4..7618226 100644 --- a/handlers/hosts/handlers/code-actions.go +++ b/handlers/hosts/handlers/code-actions.go @@ -1,7 +1,7 @@ package handlers import ( - "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/ast" "config-lsp/utils" "fmt" "strings" diff --git a/handlers/hosts/handlers/hover.go b/handlers/hosts/handlers/hover.go index 2361408..50cc9cd 100644 --- a/handlers/hosts/handlers/hover.go +++ b/handlers/hosts/handlers/hover.go @@ -2,7 +2,7 @@ package handlers import ( "config-lsp/handlers/hosts" - "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/ast" "fmt" ) diff --git a/handlers/hosts/indexes/indexes.go b/handlers/hosts/indexes/indexes.go index 512a724..886619a 100644 --- a/handlers/hosts/indexes/indexes.go +++ b/handlers/hosts/indexes/indexes.go @@ -1,6 +1,8 @@ package indexes -import "config-lsp/handlers/hosts/shared" +import ( + "config-lsp/handlers/hosts/shared" +) type HostsIndexes struct { Resolver *Resolver diff --git a/handlers/hosts/lsp/text-document-did-change.go b/handlers/hosts/lsp/text-document-did-change.go index e6ba5e6..0b4bb34 100644 --- a/handlers/hosts/lsp/text-document-did-change.go +++ b/handlers/hosts/lsp/text-document-did-change.go @@ -3,7 +3,7 @@ package lsp import ( "config-lsp/common" "config-lsp/handlers/hosts" - "config-lsp/handlers/hosts/handlers/analyzer" + "config-lsp/handlers/hosts/analyzer" "config-lsp/utils" "github.com/tliron/glsp" diff --git a/handlers/hosts/lsp/text-document-did-open.go b/handlers/hosts/lsp/text-document-did-open.go index a9886cf..6e17715 100644 --- a/handlers/hosts/lsp/text-document-did-open.go +++ b/handlers/hosts/lsp/text-document-did-open.go @@ -3,8 +3,8 @@ package lsp import ( "config-lsp/common" "config-lsp/handlers/hosts" - "config-lsp/handlers/hosts/handlers/analyzer" - "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/analyzer" + "config-lsp/handlers/hosts/ast" "config-lsp/handlers/hosts/indexes" "config-lsp/utils" diff --git a/handlers/hosts/lsp/text-document-hover.go b/handlers/hosts/lsp/text-document-hover.go index fd76edb..1c6484e 100644 --- a/handlers/hosts/lsp/text-document-hover.go +++ b/handlers/hosts/lsp/text-document-hover.go @@ -2,9 +2,9 @@ package lsp import ( "config-lsp/handlers/hosts" + "config-lsp/handlers/hosts/ast" "config-lsp/handlers/hosts/fields" "config-lsp/handlers/hosts/handlers" - "config-lsp/handlers/hosts/handlers/ast" "strings" "github.com/tliron/glsp" diff --git a/handlers/hosts/shared.go b/handlers/hosts/shared.go index d17e04c..e71cbbb 100644 --- a/handlers/hosts/shared.go +++ b/handlers/hosts/shared.go @@ -1,9 +1,8 @@ package hosts import ( - "config-lsp/handlers/hosts/handlers/ast" + "config-lsp/handlers/hosts/ast" "config-lsp/handlers/hosts/indexes" - protocol "github.com/tliron/glsp/protocol_3_16" ) From 31faa9a96d71a5b0e1ecbfd78860299100c52c53 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 31 Aug 2024 19:18:57 +0200 Subject: [PATCH 04/24] fix(aliases): Improve aliases --- go.mod | 1 + go.sum | 2 + handlers/aliases/analyzer/analyzer.go | 13 +++++ handlers/aliases/analyzer/parser.go | 49 ++++++++++++++++ handlers/aliases/ast/aliases.go | 5 +- handlers/aliases/ast/handler.go | 2 +- handlers/aliases/ast/listener.go | 45 ++++++++++----- handlers/aliases/ast/parser.go | 9 ++- handlers/aliases/ast/parser_test.go | 80 +++++++++++++++++++++------ handlers/aliases/ast/values.go | 2 +- handlers/aliases/indexes/indexes.go | 43 ++++++++++++++ handlers/aliases/shared.go | 15 +++++ handlers/aliases/shared/errors.go | 12 ++++ handlers/fstab/fstab_test.go | 2 + handlers/hosts/analyzer/double_ips.go | 12 +++- 15 files changed, 253 insertions(+), 39 deletions(-) create mode 100644 handlers/aliases/analyzer/analyzer.go create mode 100644 handlers/aliases/analyzer/parser.go create mode 100644 handlers/aliases/indexes/indexes.go create mode 100644 handlers/aliases/shared.go create mode 100644 handlers/aliases/shared/errors.go diff --git a/go.mod b/go.mod index 0340385..661cc9a 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,7 @@ require ( require ( github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/emirpasic/gods v1.18.1 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/k0kubun/pp v3.0.1+incompatible // indirect diff --git a/go.sum b/go.sum index 4934342..60c2e89 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYW github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc= +github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= diff --git a/handlers/aliases/analyzer/analyzer.go b/handlers/aliases/analyzer/analyzer.go new file mode 100644 index 0000000..98cd418 --- /dev/null +++ b/handlers/aliases/analyzer/analyzer.go @@ -0,0 +1,13 @@ +package analyzer + +import ( + "config-lsp/handlers/hosts" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func Analyze( + d *hosts.HostsDocument, +) []protocol.Diagnostic { + return nil +} diff --git a/handlers/aliases/analyzer/parser.go b/handlers/aliases/analyzer/parser.go new file mode 100644 index 0000000..e1c5820 --- /dev/null +++ b/handlers/aliases/analyzer/parser.go @@ -0,0 +1,49 @@ +package analyzer + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases/ast" + + ers "errors" +) + +func analyzeValuesAreValid( + p ast.AliasesParser, +) []common.LSPError { + errors := make([]common.LSPError, 0) + + it := p.Aliases.Iterator() + + for it.Next() { + entry := it.Value().(*ast.AliasEntry) + + if entry.Key == nil { + errors = append(errors, common.LSPError{ + Range: entry.Location, + Err: ers.New("A name is required"), + }) + + continue + } + + if entry.Separator == nil { + errors = append(errors, common.LSPError{ + Range: entry.Location, + Err: ers.New("The separator is required"), + }) + + continue + } + + if entry.Values == nil || len(entry.Values.Values) == 0 { + errors = append(errors, common.LSPError{ + Range: entry.Location, + Err: ers.New("A value is required"), + }) + + continue + } + } + + return errors +} diff --git a/handlers/aliases/ast/aliases.go b/handlers/aliases/ast/aliases.go index 7fda400..5f40b4d 100644 --- a/handlers/aliases/ast/aliases.go +++ b/handlers/aliases/ast/aliases.go @@ -1,7 +1,8 @@ -package tree +package ast import ( "config-lsp/common" + "github.com/emirpasic/gods/maps/treemap" ) // Procedure @@ -34,6 +35,6 @@ type AliasEntry struct { } type AliasesParser struct { - Aliases map[uint32]*AliasEntry + Aliases *treemap.Map CommentLines map[uint32]struct{} } diff --git a/handlers/aliases/ast/handler.go b/handlers/aliases/ast/handler.go index c6545c1..bd41296 100644 --- a/handlers/aliases/ast/handler.go +++ b/handlers/aliases/ast/handler.go @@ -1 +1 @@ -package tree +package ast diff --git a/handlers/aliases/ast/listener.go b/handlers/aliases/ast/listener.go index 65a8315..aee0c7d 100644 --- a/handlers/aliases/ast/listener.go +++ b/handlers/aliases/ast/listener.go @@ -1,4 +1,4 @@ -package tree +package ast import ( "config-lsp/common" @@ -23,16 +23,21 @@ func (s *aliasesParserListener) EnterEntry(ctx *parser.EntryContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.aliasContext.line) - s.Parser.Aliases[location.Start.Line] = &AliasEntry{ - Location: location, - } + s.Parser.Aliases.Put( + location.Start.Line, + &AliasEntry{ + Location: location, + }, + ) } func (s *aliasesParserListener) EnterSeparator(ctx *parser.SeparatorContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.aliasContext.line) - entry := s.Parser.Aliases[location.Start.Line] + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + entry.Separator = &location } @@ -40,7 +45,9 @@ func (s *aliasesParserListener) EnterKey(ctx *parser.KeyContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.aliasContext.line) - entry := s.Parser.Aliases[location.Start.Line] + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + entry.Key = &AliasKey{ Location: location, Value: ctx.GetText(), @@ -51,7 +58,9 @@ func (s *aliasesParserListener) EnterValues(ctx *parser.ValuesContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.aliasContext.line) - entry := s.Parser.Aliases[location.Start.Line] + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + entry.Values = &AliasValues{ Location: location, Values: make([]AliasValueInterface, 0, 5), @@ -71,7 +80,9 @@ func (s *aliasesParserListener) EnterUser(ctx *parser.UserContext) { }, } - entry := s.Parser.Aliases[location.Start.Line] + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + entry.Values.Values = append(entry.Values.Values, user) } @@ -79,9 +90,12 @@ func (s *aliasesParserListener) EnterFile(ctx *parser.FileContext) { location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) location.ChangeBothLines(s.aliasContext.line) + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + if s.aliasContext.currentIncludeIndex != nil { // This `file` is inside an `include`, so we need to set the path on the include - values := s.Parser.Aliases[location.Start.Line].Values + values := entry.Values rawValue := values.Values[*s.aliasContext.currentIncludeIndex] // Set the path @@ -107,7 +121,6 @@ func (s *aliasesParserListener) EnterFile(ctx *parser.FileContext) { Path: path(ctx.GetText()), } - entry := s.Parser.Aliases[location.Start.Line] entry.Values.Values = append(entry.Values.Values, file) } @@ -123,7 +136,9 @@ func (s *aliasesParserListener) EnterCommand(ctx *parser.CommandContext) { Command: ctx.GetText()[1:], } - entry := s.Parser.Aliases[location.Start.Line] + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + entry.Values.Values = append(entry.Values.Values, command) } @@ -138,7 +153,9 @@ func (s *aliasesParserListener) EnterInclude(ctx *parser.IncludeContext) { }, } - entry := s.Parser.Aliases[location.Start.Line] + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + entry.Values.Values = append(entry.Values.Values, include) index := uint32(len(entry.Values.Values) - 1) @@ -156,7 +173,9 @@ func (s *aliasesParserListener) EnterEmail(ctx *parser.EmailContext) { }, } - entry := s.Parser.Aliases[location.Start.Line] + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + entry.Values.Values = append(entry.Values.Values, email) } diff --git a/handlers/aliases/ast/parser.go b/handlers/aliases/ast/parser.go index a31971c..2abe986 100644 --- a/handlers/aliases/ast/parser.go +++ b/handlers/aliases/ast/parser.go @@ -1,11 +1,14 @@ -package tree +package ast import ( "config-lsp/common" "config-lsp/handlers/aliases/parser" "config-lsp/utils" - "github.com/antlr4-go/antlr/v4" "regexp" + + "github.com/antlr4-go/antlr/v4" + "github.com/emirpasic/gods/maps/treemap" + gods "github.com/emirpasic/gods/utils" ) func NewAliasesParser() AliasesParser { @@ -17,7 +20,7 @@ func NewAliasesParser() AliasesParser { func (p *AliasesParser) Clear() { p.CommentLines = make(map[uint32]struct{}) - p.Aliases = make(map[uint32]*AliasEntry) + p.Aliases = treemap.NewWith(gods.UInt32Comparator) } var commentPattern = regexp.MustCompile(`^\s*#.*$`) diff --git a/handlers/aliases/ast/parser_test.go b/handlers/aliases/ast/parser_test.go index 9ae4de5..9ad9165 100644 --- a/handlers/aliases/ast/parser_test.go +++ b/handlers/aliases/ast/parser_test.go @@ -1,4 +1,4 @@ -package tree +package ast import ( "config-lsp/utils" @@ -19,15 +19,17 @@ postmaster: root t.Fatalf("Expected no errors, got %v", errors) } - if !(len(parser.Aliases) == 1) { - t.Fatalf("Expected 1 alias, got %v", len(parser.Aliases)) + if !(parser.Aliases.Size() == 1) { + t.Fatalf("Expected 1 alias, got %v", parser.Aliases.Size()) } - if !(parser.Aliases[0].Key.Value == "postmaster") { - t.Fatalf("Expected key to be 'postmaster', got %v", parser.Aliases[1].Key.Value) + rawEntry, _ := parser.Aliases.Get(uint32(0)) + entry := rawEntry.(*AliasEntry) + if !(entry.Key.Value == "postmaster") { + t.Fatalf("Expected key to be 'postmaster', got %v", entry.Key.Value) } - userValue := parser.Aliases[0].Values.Values[0].(AliasValueUser) + userValue := entry.Values.Values[0].(AliasValueUser) if !(userValue.Value == "root") { t.Fatalf("Expected value to be 'root', got %v", userValue.Value) } @@ -51,19 +53,23 @@ michel: raiks@example.com t.Fatalf("Expected no errors, got %v", errors) } - if !(len(parser.Aliases) == 2) { - t.Fatalf("Expected 2 aliases, got %v", len(parser.Aliases)) + if !(parser.Aliases.Size() == 2) { + t.Fatalf("Expected 2 aliases, got %v", parser.Aliases.Size()) } - if !(parser.Aliases[0].Key.Value == "heinz") { - t.Fatalf("Expected key to be 'heinz', got %v", parser.Aliases[1].Key.Value) + rawEntry, _ := parser.Aliases.Get(uint32(0)) + entry := rawEntry.(*AliasEntry) + if !(entry.Key.Value == "heinz") { + t.Fatalf("Expected key to be 'heinz', got %v", entry.Key.Value) } - if !(parser.Aliases[1].Key.Value == "michel") { - t.Fatalf("Expected key to be 'michel', got %v", parser.Aliases[1].Key.Value) + rawEntry, _ = parser.Aliases.Get(uint32(1)) + entry = rawEntry.(*AliasEntry) + if !(entry.Key.Value == "michel") { + t.Fatalf("Expected key to be 'michel', got %v", entry.Key.Value) } - emailValue := parser.Aliases[1].Values.Values[0].(AliasValueEmail) + emailValue := entry.Values.Values[0].(AliasValueEmail) if !(emailValue.Value == "raiks@example.com") { t.Fatalf("Expected value to be 'raiks@example.com', got %v", emailValue.Value) @@ -83,15 +89,17 @@ luke: :include:/etc/other_aliases t.Fatalf("Expected no errors, got %v", errors) } - if !(len(parser.Aliases) == 1) { - t.Fatalf("Expected 1 alias, got %v", len(parser.Aliases)) + if !(parser.Aliases.Size() == 1) { + t.Fatalf("Expected 1 alias, got %v", parser.Aliases.Size()) } - if !(parser.Aliases[0].Key.Value == "luke") { - t.Fatalf("Expected key to be 'luke', got %v", parser.Aliases[1].Key.Value) + rawEntry, _ := parser.Aliases.Get(uint32(0)) + entry := rawEntry.(*AliasEntry) + if !(entry.Key.Value == "luke") { + t.Fatalf("Expected key to be 'luke', got %v", entry.Key.Value) } - includeValue := parser.Aliases[0].Values.Values[0].(AliasValueInclude) + includeValue := entry.Values.Values[0].(AliasValueInclude) if !(includeValue.Path.Path == "/etc/other_aliases") { t.Fatalf("Expected path to be '/etc/other_aliases', got %v", includeValue.Path.Path) @@ -105,3 +113,39 @@ luke: :include:/etc/other_aliases t.Fatalf("Expected path location to be 15-33, got %v-%v", includeValue.Path.Location.Start.Character, includeValue.Path.Location.End.Character) } } + +func TestInvalidWithEmptyValuesWorks( + t *testing.T, +) { + input := utils.Dedent(` +luke: +`) + parser := NewAliasesParser() + errors := parser.Parse(input) + + if len(errors) == 0 { + t.Fatalf("Expected 1 error, got %v", errors) + } + + if !(errors[0].Range.Start.Character == 5 && errors[0].Range.End.Character == 5) { + t.Fatalf("Expected error to be at 6, got %v", errors[0].Range.Start.Character) + } +} + +func TestInvalidWithEmptyKeyWorks( + t *testing.T, +) { + input := utils.Dedent(` +: root +`) + parser := NewAliasesParser() + errors := parser.Parse(input) + + if len(errors) == 0 { + t.Fatalf("Expected 1 error, got %v", errors) + } + + if !(errors[0].Range.Start.Character == 0 && errors[0].Range.End.Character == 0) { + t.Fatalf("Expected error to be at 0, got %v", errors[0].Range.Start.Character) + } +} diff --git a/handlers/aliases/ast/values.go b/handlers/aliases/ast/values.go index 1233c83..ed6ab59 100644 --- a/handlers/aliases/ast/values.go +++ b/handlers/aliases/ast/values.go @@ -1,4 +1,4 @@ -package tree +package ast import ( "config-lsp/common" diff --git a/handlers/aliases/indexes/indexes.go b/handlers/aliases/indexes/indexes.go new file mode 100644 index 0000000..345c212 --- /dev/null +++ b/handlers/aliases/indexes/indexes.go @@ -0,0 +1,43 @@ +package indexes + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/shared" + "strings" +) + +type AliasesIndexes struct { + Keys map[string]*ast.AliasKey +} + +func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) { + errors := make([]common.LSPError, 0) + indexes := &AliasesIndexes{ + Keys: make(map[string]*ast.AliasKey), + } + + it := parser.Aliases.Iterator() + + for it.Next() { + entry := it.Value().(*ast.AliasEntry) + + normalizedAlias := strings.ToLower(entry.Key.Value) + + if existingEntry, found := indexes.Keys[normalizedAlias]; found { + errors = append(errors, common.LSPError{ + Range: entry.Location, + Err: shared.DuplicateKeyEntry{ + AlreadyFoundAt: existingEntry.Location.Start.Line, + Key: entry.Key.Value, + }, + }) + + continue + } + + indexes.Keys[normalizedAlias] = entry.Key + } + + return *indexes, errors +} diff --git a/handlers/aliases/shared.go b/handlers/aliases/shared.go new file mode 100644 index 0000000..86ef6be --- /dev/null +++ b/handlers/aliases/shared.go @@ -0,0 +1,15 @@ +package aliases + +import ( + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +type HostsDocument struct { + Parser *ast.AliasesParser + Indexes *indexes.AliasesIndexes +} + +var DocumentParserMap = map[protocol.DocumentUri]*HostsDocument{} diff --git a/handlers/aliases/shared/errors.go b/handlers/aliases/shared/errors.go new file mode 100644 index 0000000..92f2dce --- /dev/null +++ b/handlers/aliases/shared/errors.go @@ -0,0 +1,12 @@ +package shared + +import "fmt" + +type DuplicateKeyEntry struct { + AlreadyFoundAt uint32 + Key string +} + +func (d DuplicateKeyEntry) Error() string { + return fmt.Sprintf("Alias '%s' already defined on line %d", d.Key, d.AlreadyFoundAt+1) +} diff --git a/handlers/fstab/fstab_test.go b/handlers/fstab/fstab_test.go index 52585d3..4e6d47d 100644 --- a/handlers/fstab/fstab_test.go +++ b/handlers/fstab/fstab_test.go @@ -13,6 +13,8 @@ var sampleInvalidOptionsExample = ` LABEL=test /mnt/test btrfs subvol=backup,fat=32 0 0 ` +// TODO: Improve `entries`, sometimes the indexes seem +// to be wrong. Use a treemap instead of a map. func TestValidBasicExample(t *testing.T) { // Arrange parser := FstabParser{} diff --git a/handlers/hosts/analyzer/double_ips.go b/handlers/hosts/analyzer/double_ips.go index 237d5a6..4fcd2f4 100644 --- a/handlers/hosts/analyzer/double_ips.go +++ b/handlers/hosts/analyzer/double_ips.go @@ -4,7 +4,9 @@ import ( "config-lsp/common" "config-lsp/handlers/hosts" "config-lsp/handlers/hosts/shared" + "config-lsp/utils" "net" + "slices" ) func ipToString(ip net.IPAddr) string { @@ -17,7 +19,15 @@ func analyzeDoubleIPs(d *hosts.HostsDocument) []common.LSPError { d.Indexes.DoubleIPs = make(map[uint32]shared.DuplicateIPDeclaration) - for lineNumber, entry := range d.Parser.Tree.Entries { + // TODO: `range` does not seem to properly + // iterate in a sorted way. + // Instead, use a treemap + lines := utils.KeysOfMap(d.Parser.Tree.Entries) + slices.Sort(lines) + + for _, lineNumber := range lines { + entry := d.Parser.Tree.Entries[lineNumber] + if entry.IPAddress != nil { key := ipToString(entry.IPAddress.Value) From 4ea6f9f160515f111fab3cf869dfe9fd46304753 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 31 Aug 2024 20:52:46 +0200 Subject: [PATCH 05/24] fix(aliases): Add double keys analyzer --- handlers/aliases/analyzer/analyzer.go | 26 ++++++- handlers/aliases/analyzer/double_keys.go | 21 ++++++ handlers/aliases/analyzer/double_keys_test.go | 73 +++++++++++++++++++ handlers/aliases/shared.go | 4 +- 4 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 handlers/aliases/analyzer/double_keys.go create mode 100644 handlers/aliases/analyzer/double_keys_test.go diff --git a/handlers/aliases/analyzer/analyzer.go b/handlers/aliases/analyzer/analyzer.go index 98cd418..2f399d1 100644 --- a/handlers/aliases/analyzer/analyzer.go +++ b/handlers/aliases/analyzer/analyzer.go @@ -1,13 +1,33 @@ package analyzer import ( - "config-lsp/handlers/hosts" + "config-lsp/common" + "config-lsp/handlers/aliases" + "config-lsp/utils" protocol "github.com/tliron/glsp/protocol_3_16" ) func Analyze( - d *hosts.HostsDocument, + d *aliases.AliasesDocument, ) []protocol.Diagnostic { - return nil + errors := analyzeValuesAreValid(*d.Parser) + + if len(errors) > 0 { + return utils.Map( + errors, + func(err common.LSPError) protocol.Diagnostic { + return err.ToDiagnostic() + }, + ) + } + + errors = append(errors, analyzeDoubleKeys(d)...) + + return utils.Map( + errors, + func(err common.LSPError) protocol.Diagnostic { + return err.ToDiagnostic() + }, + ) } diff --git a/handlers/aliases/analyzer/double_keys.go b/handlers/aliases/analyzer/double_keys.go new file mode 100644 index 0000000..68afeb5 --- /dev/null +++ b/handlers/aliases/analyzer/double_keys.go @@ -0,0 +1,21 @@ +package analyzer + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/indexes" +) + +func analyzeDoubleKeys( + d *aliases.AliasesDocument, +) []common.LSPError { + indexes, errors := indexes.CreateIndexes(*d.Parser) + + d.Indexes = &indexes + + if len(errors) > 0 { + return errors + } + + return nil +} diff --git a/handlers/aliases/analyzer/double_keys_test.go b/handlers/aliases/analyzer/double_keys_test.go new file mode 100644 index 0000000..c0e9214 --- /dev/null +++ b/handlers/aliases/analyzer/double_keys_test.go @@ -0,0 +1,73 @@ +package analyzer + +import ( + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/ast" + "config-lsp/utils" + "testing" +) + +func TestWorks( + t *testing.T, +) { + input := utils.Dedent(` +support: michael +marketing: john +support: jane +`) + p := ast.NewAliasesParser() + errors := p.Parse(input) + + d := aliases.AliasesDocument{ + Parser: &p, + } + + if len(errors) != 0 { + t.Errorf("Expected no errors, got %v", errors) + } + + errors = analyzeDoubleKeys(&d) + + if !(len(errors) == 1) { + t.Errorf("Expected 1 error, got %v", errors) + } + + if d.Indexes == nil { + t.Errorf("Expected indexes to be set") + } +} + +func TestValidWorks( + t *testing.T, +) { + input := utils.Dedent(` +support: michael +marketing: john +supportgroup: jane +suppor: jane +`) + p := ast.NewAliasesParser() + errors := p.Parse(input) + + d := aliases.AliasesDocument{ + Parser: &p, + } + + if len(errors) != 0 { + t.Errorf("Expected no errors, got %v", errors) + } + + errors = analyzeDoubleKeys(&d) + + if !(len(errors) == 0) { + t.Errorf("Expected 0 errors, got %v", errors) + } + + if d.Indexes == nil { + t.Errorf("Expected indexes to be set") + } + + if d.Indexes.Keys["support"] == nil { + t.Errorf("Expected support to be in indexes") + } +} diff --git a/handlers/aliases/shared.go b/handlers/aliases/shared.go index 86ef6be..b47d360 100644 --- a/handlers/aliases/shared.go +++ b/handlers/aliases/shared.go @@ -7,9 +7,9 @@ import ( protocol "github.com/tliron/glsp/protocol_3_16" ) -type HostsDocument struct { +type AliasesDocument struct { Parser *ast.AliasesParser Indexes *indexes.AliasesIndexes } -var DocumentParserMap = map[protocol.DocumentUri]*HostsDocument{} +var DocumentParserMap = map[protocol.DocumentUri]*AliasesDocument{} From 4c2422c3da44f8c871611cfb6720f4d3920905ac Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 31 Aug 2024 21:17:13 +0200 Subject: [PATCH 06/24] feat: Add aliases lsp handlers --- handlers/aliases/analyzer/parser.go | 2 +- handlers/aliases/indexes/indexes.go | 2 +- .../aliases/lsp/text-document-code-action.go | 14 +++ .../aliases/lsp/text-document-completion.go | 12 +++ .../aliases/lsp/text-document-did-change.go | 42 +++++++++ .../aliases/lsp/text-document-did-close.go | 13 +++ .../aliases/lsp/text-document-did-open.go | 45 ++++++++++ handlers/aliases/lsp/text-document-hover.go | 86 +++++++++++++++++++ .../aliases/lsp/workspace-execute-command.go | 21 +++++ root-handler/lsp-utils.go | 7 ++ root-handler/text-document-code-action.go | 3 + root-handler/text-document-completion.go | 3 + root-handler/text-document-did-change.go | 5 ++ root-handler/text-document-did-close.go | 3 + root-handler/text-document-did-open.go | 3 + root-handler/text-document-hover.go | 3 + 16 files changed, 262 insertions(+), 2 deletions(-) create mode 100644 handlers/aliases/lsp/text-document-code-action.go create mode 100644 handlers/aliases/lsp/text-document-completion.go create mode 100644 handlers/aliases/lsp/text-document-did-change.go create mode 100644 handlers/aliases/lsp/text-document-did-close.go create mode 100644 handlers/aliases/lsp/text-document-did-open.go create mode 100644 handlers/aliases/lsp/text-document-hover.go create mode 100644 handlers/aliases/lsp/workspace-execute-command.go diff --git a/handlers/aliases/analyzer/parser.go b/handlers/aliases/analyzer/parser.go index e1c5820..1a11fde 100644 --- a/handlers/aliases/analyzer/parser.go +++ b/handlers/aliases/analyzer/parser.go @@ -29,7 +29,7 @@ func analyzeValuesAreValid( if entry.Separator == nil { errors = append(errors, common.LSPError{ Range: entry.Location, - Err: ers.New("The separator is required"), + Err: ers.New("A ':' is required as a separator"), }) continue diff --git a/handlers/aliases/indexes/indexes.go b/handlers/aliases/indexes/indexes.go index 345c212..e87cb47 100644 --- a/handlers/aliases/indexes/indexes.go +++ b/handlers/aliases/indexes/indexes.go @@ -26,7 +26,7 @@ func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) if existingEntry, found := indexes.Keys[normalizedAlias]; found { errors = append(errors, common.LSPError{ - Range: entry.Location, + Range: entry.Key.Location, Err: shared.DuplicateKeyEntry{ AlreadyFoundAt: existingEntry.Location.Start.Line, Key: entry.Key.Value, diff --git a/handlers/aliases/lsp/text-document-code-action.go b/handlers/aliases/lsp/text-document-code-action.go new file mode 100644 index 0000000..37950bd --- /dev/null +++ b/handlers/aliases/lsp/text-document-code-action.go @@ -0,0 +1,14 @@ +package lsp + +import ( + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) { + // document := hosts.DocumentParserMap[params.TextDocument.URI] + // + // actions := make([]protocol.CodeAction, 0, 1) + + return nil, nil +} diff --git a/handlers/aliases/lsp/text-document-completion.go b/handlers/aliases/lsp/text-document-completion.go new file mode 100644 index 0000000..8c0754b --- /dev/null +++ b/handlers/aliases/lsp/text-document-completion.go @@ -0,0 +1,12 @@ +package lsp + +import ( + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) { + // p := documentParserMap[params.TextDocument.URI] + + return nil, nil +} diff --git a/handlers/aliases/lsp/text-document-did-change.go b/handlers/aliases/lsp/text-document-did-change.go new file mode 100644 index 0000000..87673d4 --- /dev/null +++ b/handlers/aliases/lsp/text-document-did-change.go @@ -0,0 +1,42 @@ +package lsp + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/analyzer" + "config-lsp/utils" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentDidChange( + context *glsp.Context, + params *protocol.DidChangeTextDocumentParams, +) error { + content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text + common.ClearDiagnostics(context, params.TextDocument.URI) + + document := aliases.DocumentParserMap[params.TextDocument.URI] + document.Parser.Clear() + + diagnostics := make([]protocol.Diagnostic, 0) + errors := document.Parser.Parse(content) + + if len(errors) > 0 { + diagnostics = append(diagnostics, utils.Map( + errors, + func(err common.LSPError) protocol.Diagnostic { + return err.ToDiagnostic() + }, + )...) + } + + diagnostics = append(diagnostics, analyzer.Analyze(document)...) + + if len(diagnostics) > 0 { + common.SendDiagnostics(context, params.TextDocument.URI, diagnostics) + } + + return nil +} diff --git a/handlers/aliases/lsp/text-document-did-close.go b/handlers/aliases/lsp/text-document-did-close.go new file mode 100644 index 0000000..3283aad --- /dev/null +++ b/handlers/aliases/lsp/text-document-did-close.go @@ -0,0 +1,13 @@ +package lsp + +import ( + "config-lsp/handlers/hosts" + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error { + delete(hosts.DocumentParserMap, params.TextDocument.URI) + + return nil +} diff --git a/handlers/aliases/lsp/text-document-did-open.go b/handlers/aliases/lsp/text-document-did-open.go new file mode 100644 index 0000000..81f9c1e --- /dev/null +++ b/handlers/aliases/lsp/text-document-did-open.go @@ -0,0 +1,45 @@ +package lsp + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/analyzer" + "config-lsp/handlers/aliases/ast" + "config-lsp/utils" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentDidOpen( + context *glsp.Context, + params *protocol.DidOpenTextDocumentParams, +) error { + common.ClearDiagnostics(context, params.TextDocument.URI) + + parser := ast.NewAliasesParser() + document := aliases.AliasesDocument{ + Parser: &parser, + } + aliases.DocumentParserMap[params.TextDocument.URI] = &document + + errors := parser.Parse(params.TextDocument.Text) + + diagnostics := utils.Map( + errors, + func(err common.LSPError) protocol.Diagnostic { + return err.ToDiagnostic() + }, + ) + + diagnostics = append( + diagnostics, + analyzer.Analyze(&document)..., + ) + + if len(diagnostics) > 0 { + common.SendDiagnostics(context, params.TextDocument.URI, diagnostics) + } + + return nil +} diff --git a/handlers/aliases/lsp/text-document-hover.go b/handlers/aliases/lsp/text-document-hover.go new file mode 100644 index 0000000..0845504 --- /dev/null +++ b/handlers/aliases/lsp/text-document-hover.go @@ -0,0 +1,86 @@ +package lsp + +import ( + "config-lsp/handlers/hosts" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentHover( + context *glsp.Context, + params *protocol.HoverParams, +) (*protocol.Hover, error) { + document := hosts.DocumentParserMap[params.TextDocument.URI] + + line := params.Position.Line + // character := params.Position.Character + + if _, found := document.Parser.CommentLines[line]; found { + // Comment + return nil, nil + } + + // entry, found := document.Parser.Tree.Entries[line] + // + // if !found { + // // Empty line + // return nil, nil + // } + // + // target := handlers.GetHoverTargetInEntry(*entry, character) + // + // var hostname *ast.HostsHostname + // + // switch *target { + // case handlers.HoverTargetIPAddress: + // relativeCursor := character - entry.IPAddress.Location.Start.Character + // hover := fields.IPAddressField.FetchHoverInfo(entry.IPAddress.Value.String(), relativeCursor) + // + // return &protocol.Hover{ + // Contents: hover, + // }, nil + // case handlers.HoverTargetHostname: + // hostname = entry.Hostname + // case handlers.HoverTargetAlias: + // for _, alias := range entry.Aliases { + // if alias.Location.Start.Character <= character && character <= alias.Location.End.Character { + // hostname = alias + // break + // } + // } + // } + // + // if hostname != nil { + // contents := []string{ + // "## Hostname", + // } + // contents = append( + // contents, + // fields.HostnameField.GetTypeDescription()..., + // ) + // contents = append( + // contents, + // []string{ + // "", + // }..., + // ) + // contents = append( + // contents, + // fields.HostnameField.Documentation, + // ) + // contents = append( + // contents, + // handlers.GetHoverInfoForHostname(*document, *hostname, character)..., + // ) + // + // return &protocol.Hover{ + // Contents: &protocol.MarkupContent{ + // Kind: protocol.MarkupKindMarkdown, + // Value: strings.Join(contents, "\n"), + // }, + // }, nil + // } + + return nil, nil +} diff --git a/handlers/aliases/lsp/workspace-execute-command.go b/handlers/aliases/lsp/workspace-execute-command.go new file mode 100644 index 0000000..41f5298 --- /dev/null +++ b/handlers/aliases/lsp/workspace-execute-command.go @@ -0,0 +1,21 @@ +package lsp + +import ( + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func WorkspaceExecuteCommand(context *glsp.Context, params *protocol.ExecuteCommandParams) (*protocol.ApplyWorkspaceEditParams, error) { + // _, command, _ := strings.Cut(params.Command, ".") + // + // switch command { + // case string(handlers.CodeActionInlineAliases): + // args := handlers.CodeActionInlineAliasesArgsFromArguments(params.Arguments[0].(map[string]any)) + // + // document := hosts.DocumentParserMap[args.URI] + // + // return args.RunCommand(*document.Parser) + // } + + return nil, nil +} diff --git a/root-handler/lsp-utils.go b/root-handler/lsp-utils.go index a4337f3..8a5b7c6 100644 --- a/root-handler/lsp-utils.go +++ b/root-handler/lsp-utils.go @@ -17,6 +17,7 @@ const ( LanguageFstab SupportedLanguage = "fstab" LanguageWireguard SupportedLanguage = "languagewireguard" LanguageHosts SupportedLanguage = "hosts" + LanguageAliases SupportedLanguage = "aliases" ) var AllSupportedLanguages = []string{ @@ -24,6 +25,7 @@ var AllSupportedLanguages = []string{ string(LanguageFstab), string(LanguageWireguard), string(LanguageHosts), + string(LanguageAliases), } type FatalFileNotReadableError struct { @@ -64,6 +66,9 @@ var valueToLanguageMap = map[string]SupportedLanguage{ "host": LanguageHosts, "hosts": LanguageHosts, "etc/hosts": LanguageHosts, + + "aliases": LanguageAliases, + "etc/aliases": LanguageAliases, } var typeOverwriteRegex = regexp.MustCompile(`#\?\s*lsp\.language\s*=\s*(\w+)\s*`) @@ -111,6 +116,8 @@ func DetectLanguage( return LanguageFstab, nil case "file:///etc/hosts": return LanguageHosts, nil + case "file:///etc/aliases": + return LanguageAliases, nil } if strings.HasPrefix(uri, "file:///etc/wireguard/") || wireguardPattern.MatchString(uri) { diff --git a/root-handler/text-document-code-action.go b/root-handler/text-document-code-action.go index 6a01a26..a1f0642 100644 --- a/root-handler/text-document-code-action.go +++ b/root-handler/text-document-code-action.go @@ -1,6 +1,7 @@ package roothandler import ( + aliases "config-lsp/handlers/aliases/lsp" hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" @@ -30,6 +31,8 @@ func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionPa return nil, nil case LanguageWireguard: return wireguard.TextDocumentCodeAction(context, params) + case LanguageAliases: + return aliases.TextDocumentCodeAction(context, params) } panic("root-handler/TextDocumentCompletion: unexpected language" + *language) diff --git a/root-handler/text-document-completion.go b/root-handler/text-document-completion.go index 63eb383..bd37e79 100644 --- a/root-handler/text-document-completion.go +++ b/root-handler/text-document-completion.go @@ -1,6 +1,7 @@ package roothandler import ( + aliases "config-lsp/handlers/aliases/lsp" "config-lsp/handlers/fstab" hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" @@ -31,6 +32,8 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa return wireguard.TextDocumentCompletion(context, params) case LanguageHosts: return hosts.TextDocumentCompletion(context, params) + case LanguageAliases: + return aliases.TextDocumentCompletion(context, params) } panic("root-handler/TextDocumentCompletion: unexpected language" + *language) diff --git a/root-handler/text-document-did-change.go b/root-handler/text-document-did-change.go index 841dac3..087abc3 100644 --- a/root-handler/text-document-did-change.go +++ b/root-handler/text-document-did-change.go @@ -1,6 +1,7 @@ package roothandler import ( + aliases "config-lsp/handlers/aliases/lsp" "config-lsp/handlers/fstab" hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" @@ -45,6 +46,8 @@ func TextDocumentDidChange(context *glsp.Context, params *protocol.DidChangeText return wireguard.TextDocumentDidOpen(context, params) case LanguageHosts: return hosts.TextDocumentDidOpen(context, params) + case LanguageAliases: + return aliases.TextDocumentDidOpen(context, params) } } @@ -57,6 +60,8 @@ func TextDocumentDidChange(context *glsp.Context, params *protocol.DidChangeText return wireguard.TextDocumentDidChange(context, params) case LanguageHosts: return hosts.TextDocumentDidChange(context, params) + case LanguageAliases: + return aliases.TextDocumentDidChange(context, params) } panic("root-handler/TextDocumentDidChange: unexpected language" + *language) diff --git a/root-handler/text-document-did-close.go b/root-handler/text-document-did-close.go index 7185980..1790fca 100644 --- a/root-handler/text-document-did-close.go +++ b/root-handler/text-document-did-close.go @@ -1,6 +1,7 @@ package roothandler import ( + aliases "config-lsp/handlers/aliases/lsp" hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" @@ -31,6 +32,8 @@ func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDo return wireguard.TextDocumentDidClose(context, params) case LanguageHosts: return hosts.TextDocumentDidClose(context, params) + case LanguageAliases: + return aliases.TextDocumentDidClose(context, params) default: } diff --git a/root-handler/text-document-did-open.go b/root-handler/text-document-did-open.go index caaddd7..181afa2 100644 --- a/root-handler/text-document-did-open.go +++ b/root-handler/text-document-did-open.go @@ -2,6 +2,7 @@ package roothandler import ( "config-lsp/common" + aliases "config-lsp/handlers/aliases/lsp" fstab "config-lsp/handlers/fstab" hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" @@ -36,6 +37,8 @@ func TextDocumentDidOpen(context *glsp.Context, params *protocol.DidOpenTextDocu return wireguard.TextDocumentDidOpen(context, params) case LanguageHosts: return hosts.TextDocumentDidOpen(context, params) + case LanguageAliases: + return aliases.TextDocumentDidOpen(context, params) } panic(fmt.Sprintf("unexpected roothandler.SupportedLanguage: %#v", language)) diff --git a/root-handler/text-document-hover.go b/root-handler/text-document-hover.go index 31c67aa..3e94d15 100644 --- a/root-handler/text-document-hover.go +++ b/root-handler/text-document-hover.go @@ -1,6 +1,7 @@ package roothandler import ( + aliases "config-lsp/handlers/aliases/lsp" "config-lsp/handlers/fstab" hosts "config-lsp/handlers/hosts/lsp" wireguard "config-lsp/handlers/wireguard/lsp" @@ -31,6 +32,8 @@ func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*pr return fstab.TextDocumentHover(context, params) case LanguageWireguard: return wireguard.TextDocumentHover(context, params) + case LanguageAliases: + return aliases.TextDocumentHover(context, params) } panic("root-handler/TextDocumentHover: unexpected language" + *language) From 742dc486564b33b4de44ebc775db3231cd08d6f3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 31 Aug 2024 21:47:24 +0200 Subject: [PATCH 07/24] feat(aliases): Add required keys analyzer --- common/location.go | 11 ++++++++ handlers/aliases/analyzer/analyzer.go | 1 + handlers/aliases/analyzer/parser.go | 2 +- handlers/aliases/analyzer/required_keys.go | 33 ++++++++++++++++++++++ handlers/aliases/indexes/indexes.go | 6 +++- 5 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 handlers/aliases/analyzer/required_keys.go diff --git a/common/location.go b/common/location.go index fc7b1c8..344b5c1 100644 --- a/common/location.go +++ b/common/location.go @@ -15,6 +15,17 @@ type LocationRange struct { End Location } +var GlobalLocationRange = LocationRange{ + Start: Location{ + Line: 0, + Character: 0, + }, + End: Location{ + Line: 0, + Character: 0, + }, +} + func (l LocationRange) ToLSPRange() protocol.Range { return protocol.Range{ Start: protocol.Position{ diff --git a/handlers/aliases/analyzer/analyzer.go b/handlers/aliases/analyzer/analyzer.go index 2f399d1..abd32d0 100644 --- a/handlers/aliases/analyzer/analyzer.go +++ b/handlers/aliases/analyzer/analyzer.go @@ -23,6 +23,7 @@ func Analyze( } errors = append(errors, analyzeDoubleKeys(d)...) + errors = append(errors, analyzeContainsRequiredKeys(*d)...) return utils.Map( errors, diff --git a/handlers/aliases/analyzer/parser.go b/handlers/aliases/analyzer/parser.go index 1a11fde..25a1f70 100644 --- a/handlers/aliases/analyzer/parser.go +++ b/handlers/aliases/analyzer/parser.go @@ -20,7 +20,7 @@ func analyzeValuesAreValid( if entry.Key == nil { errors = append(errors, common.LSPError{ Range: entry.Location, - Err: ers.New("A name is required"), + Err: ers.New("An alias is required"), }) continue diff --git a/handlers/aliases/analyzer/required_keys.go b/handlers/aliases/analyzer/required_keys.go new file mode 100644 index 0000000..ea320a4 --- /dev/null +++ b/handlers/aliases/analyzer/required_keys.go @@ -0,0 +1,33 @@ +package analyzer + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/indexes" + "fmt" + + ers "errors" +) + +var requiredFields = []string{ + indexes.NormalizeKey("mailer-daemon"), + indexes.NormalizeKey("hostmaster"), + indexes.NormalizeKey("postmaster"), +} + +func analyzeContainsRequiredKeys( + d aliases.AliasesDocument, +) []common.LSPError { + errors := make([]common.LSPError, 0) + + for _, requiredField := range requiredFields { + if _, found := d.Indexes.Keys[requiredField]; !found { + errors = append(errors, common.LSPError{ + Range: common.GlobalLocationRange, + Err: ers.New(fmt.Sprintf("Please add the alias '%s'. It is required by the aliases file.", requiredField)), + }) + } + } + + return errors +} diff --git a/handlers/aliases/indexes/indexes.go b/handlers/aliases/indexes/indexes.go index e87cb47..682b0f6 100644 --- a/handlers/aliases/indexes/indexes.go +++ b/handlers/aliases/indexes/indexes.go @@ -11,6 +11,10 @@ type AliasesIndexes struct { Keys map[string]*ast.AliasKey } +func NormalizeKey(key string) string { + return strings.ToLower(key) +} + func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) { errors := make([]common.LSPError, 0) indexes := &AliasesIndexes{ @@ -22,7 +26,7 @@ func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) for it.Next() { entry := it.Value().(*ast.AliasEntry) - normalizedAlias := strings.ToLower(entry.Key.Value) + normalizedAlias := NormalizeKey(entry.Key.Value) if existingEntry, found := indexes.Keys[normalizedAlias]; found { errors = append(errors, common.LSPError{ From f4d20c8e25834d4d6a2621bf203c2f68add06036 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 1 Sep 2024 15:23:41 +0200 Subject: [PATCH 08/24] fix(aliases): Improvements --- handlers/aliases/analyzer/required_keys.go | 4 +- handlers/aliases/ast/aliases.go | 1 + handlers/aliases/ast/values.go | 5 ++ handlers/aliases/handlers/completions.go | 56 +++++++++++++++++++ handlers/aliases/handlers/get-value.go | 43 ++++++++++++++ .../aliases/lsp/text-document-completion.go | 26 ++++++++- handlers/aliases/shared.go | 1 + 7 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 handlers/aliases/handlers/completions.go create mode 100644 handlers/aliases/handlers/get-value.go diff --git a/handlers/aliases/analyzer/required_keys.go b/handlers/aliases/analyzer/required_keys.go index ea320a4..b7c0d32 100644 --- a/handlers/aliases/analyzer/required_keys.go +++ b/handlers/aliases/analyzer/required_keys.go @@ -9,7 +9,7 @@ import ( ers "errors" ) -var requiredFields = []string{ +var RequiredAliases = []string{ indexes.NormalizeKey("mailer-daemon"), indexes.NormalizeKey("hostmaster"), indexes.NormalizeKey("postmaster"), @@ -20,7 +20,7 @@ func analyzeContainsRequiredKeys( ) []common.LSPError { errors := make([]common.LSPError, 0) - for _, requiredField := range requiredFields { + for _, requiredField := range RequiredAliases { if _, found := d.Indexes.Keys[requiredField]; !found { errors = append(errors, common.LSPError{ Range: common.GlobalLocationRange, diff --git a/handlers/aliases/ast/aliases.go b/handlers/aliases/ast/aliases.go index 5f40b4d..7464edd 100644 --- a/handlers/aliases/ast/aliases.go +++ b/handlers/aliases/ast/aliases.go @@ -35,6 +35,7 @@ type AliasEntry struct { } type AliasesParser struct { + // uint32 -> *AliasEntry Aliases *treemap.Map CommentLines map[uint32]struct{} } diff --git a/handlers/aliases/ast/values.go b/handlers/aliases/ast/values.go index ed6ab59..6d4ab64 100644 --- a/handlers/aliases/ast/values.go +++ b/handlers/aliases/ast/values.go @@ -8,10 +8,15 @@ import ( ) type AliasValueInterface interface { + GetAliasValue() AliasValue FetchCompletions(line string, cursor uint32) []protocol.CompletionItem CheckIsValid() []*docvalues.InvalidValue } +func (a AliasValue) GetAliasValue() AliasValue { + return a +} + func (a AliasValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { return nil } diff --git a/handlers/aliases/handlers/completions.go b/handlers/aliases/handlers/completions.go new file mode 100644 index 0000000..f05bff1 --- /dev/null +++ b/handlers/aliases/handlers/completions.go @@ -0,0 +1,56 @@ +package handlers + +import ( + "config-lsp/handlers/aliases/analyzer" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + "fmt" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func GetAliasesCompletions( + i *indexes.AliasesIndexes, +) []protocol.CompletionItem { + completions := make([]protocol.CompletionItem, 0) + aliases := analyzer.RequiredAliases + + kind := protocol.CompletionItemKindValue + + for _, alias := range aliases { + if i != nil { + if _, found := i.Keys[alias]; found { + continue + } + } + + text := fmt.Sprintf("%s: ", alias) + completions = append(completions, protocol.CompletionItem{ + Label: alias, + Kind: &kind, + InsertText: &text, + Documentation: "This alias is required by the aliases file", + }) + } + + return completions +} + +func GetCompletionsForEntry( + cursor uint32, + entry *ast.AliasEntry, + i *indexes.AliasesIndexes, +) ([]protocol.CompletionItem, error) { + completions := make([]protocol.CompletionItem, 0) + + if entry.Key == nil { + return completions, nil + } + + value := getValueAtCursor(cursor, entry) + + println(fmt.Sprintf("Value: %v", value)) + + return completions, nil +} + diff --git a/handlers/aliases/handlers/get-value.go b/handlers/aliases/handlers/get-value.go new file mode 100644 index 0000000..3c7c206 --- /dev/null +++ b/handlers/aliases/handlers/get-value.go @@ -0,0 +1,43 @@ +package handlers + +import ( + "config-lsp/handlers/aliases/ast" + "fmt" + "slices" +) + +func getValueAtCursor( + cursor uint32, + entry *ast.AliasEntry, +) *ast.AliasValueInterface { + if entry.Values == nil || len(entry.Values.Values) == 0 { + return nil + } + + println(fmt.Sprintf("Values: %v", entry.Values.Values)) + index, found := slices.BinarySearchFunc( + entry.Values.Values, + cursor, + func(entry ast.AliasValueInterface, pos uint32) int { + println(fmt.Sprintf("Entry: %v", entry)) + value := entry.(ast.AliasValue) + + if value.Location.End.Character > pos { + return 1 + } + + if value.Location.Start.Character < pos { + return -1 + } + + return 0 + }, + ) + + if !found { + return nil + } + + return &entry.Values.Values[index] +} + diff --git a/handlers/aliases/lsp/text-document-completion.go b/handlers/aliases/lsp/text-document-completion.go index 8c0754b..af328c9 100644 --- a/handlers/aliases/lsp/text-document-completion.go +++ b/handlers/aliases/lsp/text-document-completion.go @@ -1,12 +1,34 @@ package lsp import ( + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/handlers" + "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) { - // p := documentParserMap[params.TextDocument.URI] + d := aliases.DocumentParserMap[params.TextDocument.URI] + cursor := params.Position.Character + line := params.Position.Line - return nil, nil + if _, found := d.Parser.CommentLines[line]; found { + return nil, nil + } + + rawEntry, found := d.Parser.Aliases.Get(line) + entry := rawEntry.(*ast.AliasEntry) + + if !found { + // For the key there are no completions available + return handlers.GetAliasesCompletions(d.Indexes), nil + } + + return handlers.GetCompletionsForEntry( + cursor, + entry, + d.Indexes, + ) } diff --git a/handlers/aliases/shared.go b/handlers/aliases/shared.go index b47d360..ec1e83d 100644 --- a/handlers/aliases/shared.go +++ b/handlers/aliases/shared.go @@ -13,3 +13,4 @@ type AliasesDocument struct { } var DocumentParserMap = map[protocol.DocumentUri]*AliasesDocument{} + From e6d396dba0b0f81166aa3b0b8ad128995970d4e8 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 1 Sep 2024 21:42:52 +0200 Subject: [PATCH 09/24] fix: Overall improvements --- common-documentation/rfc-5322.go | 5 + common/diagnostics.go | 17 -- common/errors.go | 7 + common/fetchers.go | 92 +++++++ common/index.go | 1 - common/location.go | 23 ++ common/parser.go | 147 ----------- doc-values/errors.go | 20 ++ doc-values/extra-values.go | 97 +------- doc-values/value-gid.go | 7 +- doc-values/value-uid.go | 7 +- handlers/aliases/Aliases.g4 | 6 +- handlers/aliases/analyzer/analyzer.go | 7 +- handlers/aliases/analyzer/parser.go | 49 ---- handlers/aliases/analyzer/value_valid.go | 87 +++++++ handlers/aliases/ast/handler.go | 1 - handlers/aliases/ast/values.go | 54 +++- handlers/aliases/fetchers/values.go | 68 ++++++ handlers/aliases/fields/fields.go | 18 ++ handlers/aliases/handlers/completions.go | 77 +++++- handlers/aliases/handlers/get-value.go | 10 +- handlers/aliases/indexes/indexes.go | 4 + .../aliases/lsp/text-document-completion.go | 3 +- handlers/aliases/parser/Aliases.interp | 2 +- handlers/aliases/parser/aliases_parser.go | 231 ++++++++++-------- handlers/aliases/shared.go | 1 - {common => handlers/openssh}/analyzer.go | 2 +- handlers/openssh/diagnose-ssh-options.go | 25 +- handlers/openssh/diagnostics.go | 3 +- .../openssh/documentation-common.go | 2 +- handlers/openssh/documentation.go | 197 ++++++++------- handlers/openssh/parser.go | 146 +++++++++++ handlers/openssh/shared.go | 9 +- handlers/openssh/text-document-completion.go | 3 +- 34 files changed, 872 insertions(+), 556 deletions(-) create mode 100644 common-documentation/rfc-5322.go create mode 100644 common/fetchers.go delete mode 100644 common/index.go delete mode 100644 handlers/aliases/analyzer/parser.go create mode 100644 handlers/aliases/analyzer/value_valid.go delete mode 100644 handlers/aliases/ast/handler.go create mode 100644 handlers/aliases/fetchers/values.go create mode 100644 handlers/aliases/fields/fields.go rename {common => handlers/openssh}/analyzer.go (97%) rename common/documentation.go => handlers/openssh/documentation-common.go (97%) create mode 100644 handlers/openssh/parser.go diff --git a/common-documentation/rfc-5322.go b/common-documentation/rfc-5322.go new file mode 100644 index 0000000..0e822ee --- /dev/null +++ b/common-documentation/rfc-5322.go @@ -0,0 +1,5 @@ +package commondocumentation + +import "regexp" + +var EmailRegex = regexp.MustCompile(`([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|"([]!#-[^-~ \t]|(\\[\t -~]))+")@([!#-'*+/-9=?A-Z^-~-]+(\.[!#-'*+/-9=?A-Z^-~-]+)*|\[[\t -Z^-~]*])`) diff --git a/common/diagnostics.go b/common/diagnostics.go index 193f1d3..06fcb71 100644 --- a/common/diagnostics.go +++ b/common/diagnostics.go @@ -24,20 +24,3 @@ func SendDiagnostics(context *glsp.Context, uri protocol.DocumentUri, diagnostic }, ) } - -func DiagnoseOption( - context *glsp.Context, - uri protocol.DocumentUri, - parser *SimpleConfigParser, - optionName string, - checkerFunc func(string, SimpleConfigPosition) []protocol.Diagnostic, -) []protocol.Diagnostic { - option, err := parser.GetOption(optionName) - - if err != nil { - // Nothing to diagnose - return nil - } - - return checkerFunc(option.Value, option.Position) -} diff --git a/common/errors.go b/common/errors.go index bac098b..57fc0b9 100644 --- a/common/errors.go +++ b/common/errors.go @@ -16,6 +16,13 @@ func (l LSPError) ToDiagnostic() protocol.Diagnostic { } } +func (l LSPError) ShiftCharacter(offset uint32) LSPError { + return LSPError{ + Range: l.Range.ShiftHorizontal(offset), + Err: l.Err, + } +} + type SyntaxError struct { Message string } diff --git a/common/fetchers.go b/common/fetchers.go new file mode 100644 index 0000000..040e61d --- /dev/null +++ b/common/fetchers.go @@ -0,0 +1,92 @@ +package common + +import ( + "os" + "strings" +) + +type PasswdInfo struct { + Name string + UID string + GID string + HomePath string +} + +var _cachedPasswdInfo []PasswdInfo + +func FetchPasswdInfo() ([]PasswdInfo, error) { + if len(_cachedPasswdInfo) > 0 { + return _cachedPasswdInfo, nil + } + + readBytes, err := os.ReadFile("/etc/passwd") + + if err != nil { + return []PasswdInfo{}, err + } + + lines := strings.Split(string(readBytes), "\n") + infos := make([]PasswdInfo, 0) + + for _, line := range lines { + splitted := strings.Split(line, ":") + + if len(splitted) < 6 { + continue + } + + info := PasswdInfo{ + Name: splitted[0], + UID: splitted[2], + GID: splitted[3], + HomePath: splitted[5], + } + + infos = append(infos, info) + } + + _cachedPasswdInfo = infos + + return infos, nil +} + +type GroupInfo struct { + Name string + GID string +} + +var _cachedGroupInfo []GroupInfo + +func FetchGroupInfo() ([]GroupInfo, error) { + if len(_cachedGroupInfo) > 0 { + return _cachedGroupInfo, nil + } + + readBytes, err := os.ReadFile("/etc/group") + + if err != nil { + return []GroupInfo{}, err + } + + lines := strings.Split(string(readBytes), "\n") + infos := make([]GroupInfo, 0) + + for _, line := range lines { + splitted := strings.Split(line, ":") + + if len(splitted) < 3 { + continue + } + + info := GroupInfo{ + Name: splitted[0], + GID: splitted[2], + } + + infos = append(infos, info) + } + + _cachedGroupInfo = infos + + return infos, nil +} diff --git a/common/index.go b/common/index.go deleted file mode 100644 index 805d0c7..0000000 --- a/common/index.go +++ /dev/null @@ -1 +0,0 @@ -package common diff --git a/common/location.go b/common/location.go index 344b5c1..5db293f 100644 --- a/common/location.go +++ b/common/location.go @@ -1,6 +1,8 @@ package common import ( + "fmt" + "github.com/antlr4-go/antlr/v4" protocol "github.com/tliron/glsp/protocol_3_16" ) @@ -15,6 +17,27 @@ type LocationRange struct { End Location } +func (l LocationRange) ShiftHorizontal(offset uint32) LocationRange { + return LocationRange{ + Start: Location{ + Line: l.Start.Line, + Character: l.Start.Character + offset, + }, + End: Location{ + Line: l.End.Line, + Character: l.End.Character + offset, + }, + } +} + +func (l LocationRange) String() string { + if l.Start.Line == l.End.Line { + return fmt.Sprintf("%d:%d-%d", l.Start.Line, l.Start.Character, l.End.Character) + } + + return fmt.Sprintf("%d:%d-%d:%d", l.Start.Line, l.Start.Character, l.End.Line, l.End.Character) +} + var GlobalLocationRange = LocationRange{ Start: Location{ Line: 0, diff --git a/common/parser.go b/common/parser.go index 68dc743..ab15a4d 100644 --- a/common/parser.go +++ b/common/parser.go @@ -1,17 +1,9 @@ package common import ( - docvalues "config-lsp/doc-values" protocol "github.com/tliron/glsp/protocol_3_16" - "regexp" - "strings" ) -type Parser interface { - ParseFromContent(content string) []ParseError - AnalyzeValues() []protocol.Diagnostic -} - type ParseError struct { Line uint32 Err error @@ -37,142 +29,3 @@ func (e ParseError) ToDiagnostic() protocol.Diagnostic { }, } } - -type SimpleConfigPosition struct { - Line uint32 -} - -type SimpleConfigLine struct { - Value string - Separator string - Position SimpleConfigPosition -} - -// Get the character positions of [Option End, Separator End, Value End] -func (l SimpleConfigLine) GetCharacterPositions(optionName string) [3]int { - return [3]int{len(optionName), len(optionName + l.Separator), len(optionName + l.Separator + l.Value)} -} - -type SimpleConfigOptions struct { - Separator regexp.Regexp - IgnorePattern regexp.Regexp - // This is the separator that will be used when adding a new line - IdealSeparator string - AvailableOptions *map[string]Option -} - -type SimpleConfigParser struct { - Lines map[string]SimpleConfigLine - Options SimpleConfigOptions -} - -func (p *SimpleConfigParser) AddLine(line string, lineNumber uint32) (string, error) { - var option string - var separator string - var value string - - re := p.Options.Separator - matches := re.FindStringSubmatch(line) - - if len(matches) == 0 { - return "", docvalues.MalformedLineError{} - } - - optionIndex := re.SubexpIndex("OptionName") - if optionIndex == -1 { - return "", docvalues.MalformedLineError{} - } - option = matches[optionIndex] - - if _, exists := (*p.Options.AvailableOptions)[option]; !exists { - return option, docvalues.OptionUnknownError{} - } - - separatorIndex := re.SubexpIndex("Separator") - if separatorIndex == -1 { - return option, docvalues.MalformedLineError{} - } - separator = matches[separatorIndex] - - valueIndex := re.SubexpIndex("Value") - if valueIndex == -1 { - return option, docvalues.MalformedLineError{} - } - value = matches[valueIndex] - - if _, exists := p.Lines[option]; exists { - return option, docvalues.OptionAlreadyExistsError{ - AlreadyLine: p.Lines[option].Position.Line, - } - } - - p.Lines[option] = SimpleConfigLine{ - Value: value, - Separator: separator, - Position: SimpleConfigPosition{ - Line: lineNumber, - }, - } - - return option, nil - -} - -func (p *SimpleConfigParser) ReplaceOption(option string, value string) { - p.Lines[option] = SimpleConfigLine{ - Value: value, - Position: SimpleConfigPosition{ - Line: p.Lines[option].Position.Line, - }, - } -} - -func (p *SimpleConfigParser) RemoveOption(option string) { - delete(p.Lines, option) -} - -func (p *SimpleConfigParser) GetOption(option string) (SimpleConfigLine, error) { - if _, exists := p.Lines[option]; exists { - return p.Lines[option], nil - } - - return SimpleConfigLine{}, docvalues.OptionUnknownError{} -} - -func (p *SimpleConfigParser) ParseFromFile(content string) []docvalues.OptionError { - lines := strings.Split(content, "\n") - errors := make([]docvalues.OptionError, 0) - - for index, line := range lines { - if p.Options.IgnorePattern.MatchString(line) { - continue - } - - option, err := p.AddLine(line, uint32(index)) - - if err != nil { - errors = append(errors, docvalues.OptionError{ - Line: uint32(index), - ProvidedOption: option, - DocError: err, - }) - } - } - - return errors -} - -func (p *SimpleConfigParser) Clear() { - clear(p.Lines) -} - -// TODO: Use better approach: Store an extra array of lines in order; with references to the SimpleConfigLine -func (p *SimpleConfigParser) FindByLineNumber(lineNumber uint32) (string, SimpleConfigLine, error) { - for option, line := range p.Lines { - if line.Position.Line == lineNumber { - return option, line, nil - } - } - - return "", SimpleConfigLine{Value: "", Position: SimpleConfigPosition{Line: 0}}, docvalues.LineNotFoundError{} -} diff --git a/doc-values/errors.go b/doc-values/errors.go index 7b093bc..c9a6d69 100644 --- a/doc-values/errors.go +++ b/doc-values/errors.go @@ -1,6 +1,7 @@ package docvalues import ( + "config-lsp/common" "fmt" "unicode/utf8" @@ -95,3 +96,22 @@ type LineNotFoundError struct{} func (e LineNotFoundError) Error() string { return "Line not found" } + +func LSPErrorFromInvalidValue( + line uint32, + invaludValue InvalidValue, +) common.LSPError { + return common.LSPError{ + Range: common.LocationRange{ + Start: common.Location{ + Line: line, + Character: invaludValue.Start, + }, + End: common.Location{ + Line: line, + Character: invaludValue.End, + }, + }, + Err: invaludValue.Err, + } +} diff --git a/doc-values/extra-values.go b/doc-values/extra-values.go index d804c31..dcf82d3 100644 --- a/doc-values/extra-values.go +++ b/doc-values/extra-values.go @@ -1,63 +1,17 @@ package docvalues import ( + "config-lsp/common" "config-lsp/utils" - "os" "regexp" - "strings" ) -type passwdInfo struct { - Name string - UID string - GID string - HomePath string -} - -var _cachedPasswdInfo []passwdInfo - -func fetchPasswdInfo() ([]passwdInfo, error) { - if len(_cachedPasswdInfo) > 0 { - return _cachedPasswdInfo, nil - } - - readBytes, err := os.ReadFile("/etc/passwd") - - if err != nil { - return []passwdInfo{}, err - } - - lines := strings.Split(string(readBytes), "\n") - infos := make([]passwdInfo, 0) - - for _, line := range lines { - splitted := strings.Split(line, ":") - - if len(splitted) < 6 { - continue - } - - info := passwdInfo{ - Name: splitted[0], - UID: splitted[2], - GID: splitted[3], - HomePath: splitted[5], - } - - infos = append(infos, info) - } - - _cachedPasswdInfo = infos - - return infos, nil -} - // UserValue returns a Value that fetches user names from /etc/passwd // if `separatorForMultiple` is not empty, it will return an ArrayValue func UserValue(separatorForMultiple string, enforceValues bool) Value { return CustomValue{ FetchValue: func(context CustomValueContext) Value { - infos, err := fetchPasswdInfo() + infos, err := common.FetchPasswdInfo() if err != nil { return StringValue{} @@ -65,7 +19,7 @@ func UserValue(separatorForMultiple string, enforceValues bool) Value { enumValues := EnumValue{ EnforceValues: enforceValues, - Values: utils.Map(infos, func(info passwdInfo) EnumString { + Values: utils.Map(infos, func(info common.PasswdInfo) EnumString { return CreateEnumString(info.Name) }), } @@ -83,51 +37,10 @@ func UserValue(separatorForMultiple string, enforceValues bool) Value { } } -type groupInfo struct { - Name string - GID string -} - -var _cachedGroupInfo []groupInfo - -func fetchGroupInfo() ([]groupInfo, error) { - if len(_cachedGroupInfo) > 0 { - return _cachedGroupInfo, nil - } - - readBytes, err := os.ReadFile("/etc/group") - - if err != nil { - return []groupInfo{}, err - } - - lines := strings.Split(string(readBytes), "\n") - infos := make([]groupInfo, 0) - - for _, line := range lines { - splitted := strings.Split(line, ":") - - if len(splitted) < 3 { - continue - } - - info := groupInfo{ - Name: splitted[0], - GID: splitted[2], - } - - infos = append(infos, info) - } - - _cachedGroupInfo = infos - - return infos, nil -} - func GroupValue(separatorForMultiple string, enforceValues bool) Value { return CustomValue{ FetchValue: func(context CustomValueContext) Value { - infos, err := fetchGroupInfo() + infos, err := common.FetchGroupInfo() if err != nil { return StringValue{} @@ -135,7 +48,7 @@ func GroupValue(separatorForMultiple string, enforceValues bool) Value { enumValues := EnumValue{ EnforceValues: enforceValues, - Values: utils.Map(infos, func(info groupInfo) EnumString { + Values: utils.Map(infos, func(info common.GroupInfo) EnumString { return CreateEnumString(info.Name) }), } diff --git a/doc-values/value-gid.go b/doc-values/value-gid.go index 183767b..103592c 100644 --- a/doc-values/value-gid.go +++ b/doc-values/value-gid.go @@ -1,6 +1,7 @@ package docvalues import ( + "config-lsp/common" "config-lsp/utils" "fmt" "strconv" @@ -54,7 +55,7 @@ func (v GIDValue) CheckIsValid(value string) []*InvalidValue { } if v.EnforceUsingExisting { - infos, err := fetchPasswdInfo() + infos, err := common.FetchPasswdInfo() if err != nil { return []*InvalidValue{} @@ -90,7 +91,7 @@ var defaultGIDsExplanation = []EnumString{ } func (v GIDValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { - infos, err := fetchGroupInfo() + infos, err := common.FetchGroupInfo() if err != nil { return utils.Map(defaultUIDsExplanation, func(enum EnumString) protocol.CompletionItem { @@ -134,7 +135,7 @@ func (v GIDValue) FetchHoverInfo(line string, cursor uint32) []string { return []string{} } - infos, err := fetchGroupInfo() + infos, err := common.FetchGroupInfo() if err != nil { return []string{} diff --git a/doc-values/value-uid.go b/doc-values/value-uid.go index 6de101b..bcbf7a4 100644 --- a/doc-values/value-uid.go +++ b/doc-values/value-uid.go @@ -1,6 +1,7 @@ package docvalues import ( + "config-lsp/common" "config-lsp/utils" "fmt" "strconv" @@ -54,7 +55,7 @@ func (v UIDValue) CheckIsValid(value string) []*InvalidValue { } if v.EnforceUsingExisting { - infos, err := fetchPasswdInfo() + infos, err := common.FetchPasswdInfo() if err != nil { return []*InvalidValue{} @@ -90,7 +91,7 @@ var defaultUIDsExplanation = []EnumString{ } func (v UIDValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { - infos, err := fetchPasswdInfo() + infos, err := common.FetchPasswdInfo() if err != nil { return utils.Map(defaultUIDsExplanation, func(enum EnumString) protocol.CompletionItem { @@ -135,7 +136,7 @@ func (v UIDValue) FetchHoverInfo(line string, cursor uint32) []string { return []string{} } - infos, err := fetchPasswdInfo() + infos, err := common.FetchPasswdInfo() if err != nil { return []string{} diff --git a/handlers/aliases/Aliases.g4 b/handlers/aliases/Aliases.g4 index 1bf7649..31c54fd 100644 --- a/handlers/aliases/Aliases.g4 +++ b/handlers/aliases/Aliases.g4 @@ -30,15 +30,15 @@ user ; file - : (SLASH STRING)+ SLASH? + : SLASH (STRING SLASH)* STRING? ; command - : VERTLINE STRING + : VERTLINE STRING? ; include - : COLON INCLUDE COLON file + : COLON INCLUDE COLON file? ; comment diff --git a/handlers/aliases/analyzer/analyzer.go b/handlers/aliases/analyzer/analyzer.go index abd32d0..6b7d0f8 100644 --- a/handlers/aliases/analyzer/analyzer.go +++ b/handlers/aliases/analyzer/analyzer.go @@ -11,7 +11,11 @@ import ( func Analyze( d *aliases.AliasesDocument, ) []protocol.Diagnostic { - errors := analyzeValuesAreValid(*d.Parser) + // Double keys must be checked first so + // that the index is populated for the + // other checks + errors := analyzeDoubleKeys(d) + errors = append(errors, analyzeValuesAreValid(d)...) if len(errors) > 0 { return utils.Map( @@ -22,7 +26,6 @@ func Analyze( ) } - errors = append(errors, analyzeDoubleKeys(d)...) errors = append(errors, analyzeContainsRequiredKeys(*d)...) return utils.Map( diff --git a/handlers/aliases/analyzer/parser.go b/handlers/aliases/analyzer/parser.go deleted file mode 100644 index 25a1f70..0000000 --- a/handlers/aliases/analyzer/parser.go +++ /dev/null @@ -1,49 +0,0 @@ -package analyzer - -import ( - "config-lsp/common" - "config-lsp/handlers/aliases/ast" - - ers "errors" -) - -func analyzeValuesAreValid( - p ast.AliasesParser, -) []common.LSPError { - errors := make([]common.LSPError, 0) - - it := p.Aliases.Iterator() - - for it.Next() { - entry := it.Value().(*ast.AliasEntry) - - if entry.Key == nil { - errors = append(errors, common.LSPError{ - Range: entry.Location, - Err: ers.New("An alias is required"), - }) - - continue - } - - if entry.Separator == nil { - errors = append(errors, common.LSPError{ - Range: entry.Location, - Err: ers.New("A ':' is required as a separator"), - }) - - continue - } - - if entry.Values == nil || len(entry.Values.Values) == 0 { - errors = append(errors, common.LSPError{ - Range: entry.Location, - Err: ers.New("A value is required"), - }) - - continue - } - } - - return errors -} diff --git a/handlers/aliases/analyzer/value_valid.go b/handlers/aliases/analyzer/value_valid.go new file mode 100644 index 0000000..59e4f45 --- /dev/null +++ b/handlers/aliases/analyzer/value_valid.go @@ -0,0 +1,87 @@ +package analyzer + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/fetchers" + "config-lsp/handlers/aliases/indexes" + "config-lsp/utils" + "fmt" + + ers "errors" +) + +func analyzeValuesAreValid( + d *aliases.AliasesDocument, +) []common.LSPError { + errors := make([]common.LSPError, 0) + + it := d.Parser.Aliases.Iterator() + + for it.Next() { + entry := it.Value().(*ast.AliasEntry) + + if entry.Key == nil { + errors = append(errors, common.LSPError{ + Range: entry.Location, + Err: ers.New("An alias is required"), + }) + + continue + } + + if entry.Separator == nil { + errors = append(errors, common.LSPError{ + Range: entry.Location, + Err: ers.New("A ':' is required as a separator"), + }) + + continue + } + + if entry.Values == nil || len(entry.Values.Values) == 0 { + errors = append(errors, common.LSPError{ + Range: entry.Location, + Err: ers.New("A value is required"), + }) + + continue + } + + for _, value := range entry.Values.Values { + newErrors := checkValue(d.Indexes, value) + newErrors = utils.Map( + newErrors, + func(e common.LSPError) common.LSPError { + startPosition := value.GetAliasValue().Location.Start.Character + return e.ShiftCharacter(-startPosition) + }, + ) + + errors = append(errors, newErrors...) + } + } + + return errors +} + +func checkValue( + i *indexes.AliasesIndexes, + value ast.AliasValueInterface, +) []common.LSPError { + switch value.(type) { + case ast.AliasValueUser: + aliasValue := value.(ast.AliasValueUser) + + users := fetchers.GetAvailableUserValues(i) + + if _, found := users[aliasValue.Value]; !found { + return []common.LSPError{{ + Range: aliasValue.Location, + Err: ers.New(fmt.Sprintf("User '%s' not found", aliasValue.Value)), + }} + } + } + return nil +} diff --git a/handlers/aliases/ast/handler.go b/handlers/aliases/ast/handler.go deleted file mode 100644 index bd41296..0000000 --- a/handlers/aliases/ast/handler.go +++ /dev/null @@ -1 +0,0 @@ -package ast diff --git a/handlers/aliases/ast/values.go b/handlers/aliases/ast/values.go index 6d4ab64..3764755 100644 --- a/handlers/aliases/ast/values.go +++ b/handlers/aliases/ast/values.go @@ -3,27 +3,23 @@ package ast import ( "config-lsp/common" docvalues "config-lsp/doc-values" - - protocol "github.com/tliron/glsp/protocol_3_16" + "config-lsp/handlers/aliases/fields" + "config-lsp/utils" + "fmt" ) type AliasValueInterface interface { GetAliasValue() AliasValue - FetchCompletions(line string, cursor uint32) []protocol.CompletionItem - CheckIsValid() []*docvalues.InvalidValue +} + +func (a AliasValue) String() string { + return fmt.Sprintf("%s %s", a.Location, a.Value) } func (a AliasValue) GetAliasValue() AliasValue { return a } -func (a AliasValue) FetchCompletions(line string, cursor uint32) []protocol.CompletionItem { - return nil -} -func (a AliasValue) CheckIsValid() []*docvalues.InvalidValue { - return nil -} - type AliasValue struct { Location common.LocationRange Value string @@ -40,11 +36,29 @@ type AliasValueFile struct { Path path } +func (a AliasValueFile) CheckIsValid() []common.LSPError { + return utils.Map( + fields.PathField.CheckIsValid(string(a.Path)), + func(invalidValue *docvalues.InvalidValue) common.LSPError { + return docvalues.LSPErrorFromInvalidValue(a.Location.Start.Line, *invalidValue) + }, + ) +} + type AliasValueCommand struct { AliasValue Command string } +func (a AliasValueCommand) CheckIsValid() []common.LSPError { + return utils.Map( + fields.CommandField.CheckIsValid(a.Command), + func(invalidValue *docvalues.InvalidValue) common.LSPError { + return docvalues.LSPErrorFromInvalidValue(a.Location.Start.Line, *invalidValue) + }, + ) +} + type AliasValueIncludePath struct { Location common.LocationRange Path path @@ -55,6 +69,24 @@ type AliasValueInclude struct { Path AliasValueIncludePath } +func (a AliasValueInclude) CheckIsValid() []common.LSPError { + return utils.Map( + fields.PathField.CheckIsValid(string(a.Path.Path)), + func(invalidValue *docvalues.InvalidValue) common.LSPError { + return docvalues.LSPErrorFromInvalidValue(a.Location.Start.Line, *invalidValue) + }, + ) +} + type AliasValueEmail struct { AliasValue } + +func (a AliasValueEmail) CheckIsValid() []common.LSPError { + return utils.Map( + fields.PathField.CheckIsValid(a.Value), + func(invalidValue *docvalues.InvalidValue) common.LSPError { + return docvalues.LSPErrorFromInvalidValue(a.Location.Start.Line, *invalidValue) + }, + ) +} diff --git a/handlers/aliases/fetchers/values.go b/handlers/aliases/fetchers/values.go new file mode 100644 index 0000000..e557d7a --- /dev/null +++ b/handlers/aliases/fetchers/values.go @@ -0,0 +1,68 @@ +package fetchers + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases/indexes" + "fmt" + "strings" +) + +type aliasesUser struct { + DefinedOnLine uint32 +} + +type User struct { + PasswdInfo *common.PasswdInfo + + AliasInfo *aliasesUser +} + +func (u User) Documentation() string { + if u.PasswdInfo != nil { + return strings.Join( + []string{ + fmt.Sprintf("%s (%s:%s)", u.PasswdInfo.Name, u.PasswdInfo.UID, u.PasswdInfo.GID), + fmt.Sprintf("Home: `%s`", u.PasswdInfo.HomePath), + }, + "\n", + ) + } + + if u.AliasInfo != nil { + return fmt.Sprintf("Defined on line %d", u.AliasInfo.DefinedOnLine+1) + } + + return "" +} + +// Returns a map of [username]user +// The username is normalized +func GetAvailableUserValues( + i *indexes.AliasesIndexes, +) map[string]User { + users := make(map[string]User) + + passwdUsers, err := common.FetchPasswdInfo() + + if err == nil { + for _, info := range passwdUsers { + key := indexes.NormalizeKey(info.Name) + users[key] = User{ + PasswdInfo: &info, + } + } + } + + if i != nil && i.Keys != nil { + for name, key := range i.Keys { + // Indexes keys are already normalized + users[name] = User{ + AliasInfo: &aliasesUser{ + DefinedOnLine: key.Location.Start.Line, + }, + } + } + } + + return users +} diff --git a/handlers/aliases/fields/fields.go b/handlers/aliases/fields/fields.go new file mode 100644 index 0000000..940ce3e --- /dev/null +++ b/handlers/aliases/fields/fields.go @@ -0,0 +1,18 @@ +package fields + +import ( + commondocumentation "config-lsp/common-documentation" + docvalues "config-lsp/doc-values" +) + +var UserField = docvalues.UserValue("", false) + +var PathField = docvalues.PathValue{ + RequiredType: docvalues.PathTypeFile, +} + +var CommandField = docvalues.StringValue{} + +var EmailField = docvalues.RegexValue{ + Regex: *commondocumentation.EmailRegex, +} diff --git a/handlers/aliases/handlers/completions.go b/handlers/aliases/handlers/completions.go index f05bff1..4c2d107 100644 --- a/handlers/aliases/handlers/completions.go +++ b/handlers/aliases/handlers/completions.go @@ -3,7 +3,9 @@ package handlers import ( "config-lsp/handlers/aliases/analyzer" "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/fetchers" "config-lsp/handlers/aliases/indexes" + "config-lsp/utils" "fmt" protocol "github.com/tliron/glsp/protocol_3_16" @@ -26,9 +28,9 @@ func GetAliasesCompletions( text := fmt.Sprintf("%s: ", alias) completions = append(completions, protocol.CompletionItem{ - Label: alias, - Kind: &kind, - InsertText: &text, + Label: alias, + Kind: &kind, + InsertText: &text, Documentation: "This alias is required by the aliases file", }) } @@ -48,9 +50,76 @@ func GetCompletionsForEntry( } value := getValueAtCursor(cursor, entry) + relativeCursor := cursor - entry.Key.Location.Start.Character - println(fmt.Sprintf("Value: %v", value)) + if value == nil { + completions = append(completions, getCommandCompletion()) + completions = append(completions, getIncludeCompletion()) + + completions = append(completions, getUserCompletions(i, "", 0)...) + + println("la completions etaient", completions) + return completions, nil + } + + switch (*value).(type) { + case ast.AliasValueUser: + userValue := (*value).(ast.AliasValueUser) + + return getUserCompletions( + i, + userValue.Value, + relativeCursor, + ), nil + } return completions, nil } +func getCommandCompletion() protocol.CompletionItem { + kind := protocol.CompletionItemKindKeyword + textFormat := protocol.InsertTextFormatSnippet + insertText := "|" + + return protocol.CompletionItem{ + Label: "|", + Documentation: "Pipe the message to command on its standard input. The command is run under the privileges of the daemon's unprivileged account.", + Kind: &kind, + InsertTextFormat: &textFormat, + InsertText: &insertText, + } +} + +func getIncludeCompletion() protocol.CompletionItem { + kind := protocol.CompletionItemKindKeyword + textFormat := protocol.InsertTextFormatSnippet + insertText := ":include:" + + return protocol.CompletionItem{ + Label: ":include:", + Documentation: " Include any definitions in file as alias entries. The format of the file is identical to this one.", + Kind: &kind, + InsertTextFormat: &textFormat, + InsertText: &insertText, + } +} + +func getUserCompletions( + i *indexes.AliasesIndexes, + line string, + cursor uint32, +) []protocol.CompletionItem { + users := fetchers.GetAvailableUserValues(i) + + kind := protocol.CompletionItemKindValue + return utils.MapMapToSlice( + users, + func(name string, user fetchers.User) protocol.CompletionItem { + return protocol.CompletionItem{ + Label: name, + Kind: &kind, + Documentation: user.Documentation(), + } + }, + ) +} diff --git a/handlers/aliases/handlers/get-value.go b/handlers/aliases/handlers/get-value.go index 3c7c206..0a3b9bf 100644 --- a/handlers/aliases/handlers/get-value.go +++ b/handlers/aliases/handlers/get-value.go @@ -2,7 +2,6 @@ package handlers import ( "config-lsp/handlers/aliases/ast" - "fmt" "slices" ) @@ -14,19 +13,17 @@ func getValueAtCursor( return nil } - println(fmt.Sprintf("Values: %v", entry.Values.Values)) index, found := slices.BinarySearchFunc( entry.Values.Values, cursor, func(entry ast.AliasValueInterface, pos uint32) int { - println(fmt.Sprintf("Entry: %v", entry)) - value := entry.(ast.AliasValue) + value := entry.GetAliasValue() - if value.Location.End.Character > pos { + if pos > value.Location.End.Character { return 1 } - if value.Location.Start.Character < pos { + if pos < value.Location.Start.Character { return -1 } @@ -40,4 +37,3 @@ func getValueAtCursor( return &entry.Values.Values[index] } - diff --git a/handlers/aliases/indexes/indexes.go b/handlers/aliases/indexes/indexes.go index 682b0f6..359cf31 100644 --- a/handlers/aliases/indexes/indexes.go +++ b/handlers/aliases/indexes/indexes.go @@ -26,6 +26,10 @@ func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) for it.Next() { entry := it.Value().(*ast.AliasEntry) + if entry.Key == nil || entry.Key.Value == "" { + continue + } + normalizedAlias := NormalizeKey(entry.Key.Value) if existingEntry, found := indexes.Keys[normalizedAlias]; found { diff --git a/handlers/aliases/lsp/text-document-completion.go b/handlers/aliases/lsp/text-document-completion.go index af328c9..5795dad 100644 --- a/handlers/aliases/lsp/text-document-completion.go +++ b/handlers/aliases/lsp/text-document-completion.go @@ -19,13 +19,14 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa } rawEntry, found := d.Parser.Aliases.Get(line) - entry := rawEntry.(*ast.AliasEntry) if !found { // For the key there are no completions available return handlers.GetAliasesCompletions(d.Indexes), nil } + entry := rawEntry.(*ast.AliasEntry) + return handlers.GetCompletionsForEntry( cursor, entry, diff --git a/handlers/aliases/parser/Aliases.interp b/handlers/aliases/parser/Aliases.interp index ad9deb5..4431f32 100644 --- a/handlers/aliases/parser/Aliases.interp +++ b/handlers/aliases/parser/Aliases.interp @@ -42,4 +42,4 @@ errorMessage atn: -[4, 1, 9, 124, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 1, 0, 1, 0, 3, 0, 35, 8, 0, 1, 0, 3, 0, 38, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 43, 8, 1, 1, 1, 1, 1, 3, 1, 47, 8, 1, 1, 1, 1, 1, 3, 1, 51, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 63, 8, 4, 10, 4, 12, 4, 66, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 75, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 4, 7, 81, 8, 7, 11, 7, 12, 7, 82, 1, 7, 3, 7, 86, 8, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 3, 10, 98, 8, 10, 1, 10, 4, 10, 101, 8, 10, 11, 10, 12, 10, 102, 1, 10, 3, 10, 106, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 0, 0, 16, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 0, 0, 122, 0, 32, 1, 0, 0, 0, 2, 42, 1, 0, 0, 0, 4, 54, 1, 0, 0, 0, 6, 56, 1, 0, 0, 0, 8, 64, 1, 0, 0, 0, 10, 74, 1, 0, 0, 0, 12, 76, 1, 0, 0, 0, 14, 80, 1, 0, 0, 0, 16, 87, 1, 0, 0, 0, 18, 90, 1, 0, 0, 0, 20, 95, 1, 0, 0, 0, 22, 107, 1, 0, 0, 0, 24, 111, 1, 0, 0, 0, 26, 117, 1, 0, 0, 0, 28, 119, 1, 0, 0, 0, 30, 121, 1, 0, 0, 0, 32, 34, 3, 2, 1, 0, 33, 35, 5, 1, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 37, 1, 0, 0, 0, 36, 38, 3, 20, 10, 0, 37, 36, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 5, 0, 0, 1, 40, 1, 1, 0, 0, 0, 41, 43, 5, 1, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 46, 3, 6, 3, 0, 45, 47, 5, 1, 0, 0, 46, 45, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 50, 3, 4, 2, 0, 49, 51, 5, 1, 0, 0, 50, 49, 1, 0, 0, 0, 50, 51, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 53, 3, 8, 4, 0, 53, 3, 1, 0, 0, 0, 54, 55, 5, 5, 0, 0, 55, 5, 1, 0, 0, 0, 56, 57, 5, 9, 0, 0, 57, 7, 1, 0, 0, 0, 58, 59, 3, 10, 5, 0, 59, 60, 5, 6, 0, 0, 60, 61, 5, 1, 0, 0, 61, 63, 1, 0, 0, 0, 62, 58, 1, 0, 0, 0, 63, 66, 1, 0, 0, 0, 64, 62, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 67, 1, 0, 0, 0, 66, 64, 1, 0, 0, 0, 67, 68, 3, 10, 5, 0, 68, 9, 1, 0, 0, 0, 69, 75, 3, 12, 6, 0, 70, 75, 3, 14, 7, 0, 71, 75, 3, 16, 8, 0, 72, 75, 3, 18, 9, 0, 73, 75, 3, 22, 11, 0, 74, 69, 1, 0, 0, 0, 74, 70, 1, 0, 0, 0, 74, 71, 1, 0, 0, 0, 74, 72, 1, 0, 0, 0, 74, 73, 1, 0, 0, 0, 75, 11, 1, 0, 0, 0, 76, 77, 5, 9, 0, 0, 77, 13, 1, 0, 0, 0, 78, 79, 5, 8, 0, 0, 79, 81, 5, 9, 0, 0, 80, 78, 1, 0, 0, 0, 81, 82, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 82, 83, 1, 0, 0, 0, 83, 85, 1, 0, 0, 0, 84, 86, 5, 8, 0, 0, 85, 84, 1, 0, 0, 0, 85, 86, 1, 0, 0, 0, 86, 15, 1, 0, 0, 0, 87, 88, 5, 4, 0, 0, 88, 89, 5, 9, 0, 0, 89, 17, 1, 0, 0, 0, 90, 91, 5, 5, 0, 0, 91, 92, 5, 3, 0, 0, 92, 93, 5, 5, 0, 0, 93, 94, 3, 14, 7, 0, 94, 19, 1, 0, 0, 0, 95, 100, 5, 7, 0, 0, 96, 98, 5, 1, 0, 0, 97, 96, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 101, 5, 9, 0, 0, 100, 97, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 100, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 105, 1, 0, 0, 0, 104, 106, 5, 1, 0, 0, 105, 104, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, 106, 21, 1, 0, 0, 0, 107, 108, 5, 9, 0, 0, 108, 109, 5, 2, 0, 0, 109, 110, 5, 9, 0, 0, 110, 23, 1, 0, 0, 0, 111, 112, 3, 26, 13, 0, 112, 113, 5, 5, 0, 0, 113, 114, 3, 28, 14, 0, 114, 115, 5, 1, 0, 0, 115, 116, 3, 30, 15, 0, 116, 25, 1, 0, 0, 0, 117, 118, 5, 9, 0, 0, 118, 27, 1, 0, 0, 0, 119, 120, 5, 9, 0, 0, 120, 29, 1, 0, 0, 0, 121, 122, 5, 9, 0, 0, 122, 31, 1, 0, 0, 0, 12, 34, 37, 42, 46, 50, 64, 74, 82, 85, 97, 102, 105] \ No newline at end of file +[4, 1, 9, 128, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 1, 0, 1, 0, 3, 0, 35, 8, 0, 1, 0, 3, 0, 38, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 43, 8, 1, 1, 1, 1, 1, 3, 1, 47, 8, 1, 1, 1, 1, 1, 3, 1, 51, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 63, 8, 4, 10, 4, 12, 4, 66, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 75, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 82, 8, 7, 10, 7, 12, 7, 85, 9, 7, 1, 7, 3, 7, 88, 8, 7, 1, 8, 1, 8, 3, 8, 92, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 98, 8, 9, 1, 10, 1, 10, 3, 10, 102, 8, 10, 1, 10, 4, 10, 105, 8, 10, 11, 10, 12, 10, 106, 1, 10, 3, 10, 110, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 0, 0, 16, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 0, 0, 128, 0, 32, 1, 0, 0, 0, 2, 42, 1, 0, 0, 0, 4, 54, 1, 0, 0, 0, 6, 56, 1, 0, 0, 0, 8, 64, 1, 0, 0, 0, 10, 74, 1, 0, 0, 0, 12, 76, 1, 0, 0, 0, 14, 78, 1, 0, 0, 0, 16, 89, 1, 0, 0, 0, 18, 93, 1, 0, 0, 0, 20, 99, 1, 0, 0, 0, 22, 111, 1, 0, 0, 0, 24, 115, 1, 0, 0, 0, 26, 121, 1, 0, 0, 0, 28, 123, 1, 0, 0, 0, 30, 125, 1, 0, 0, 0, 32, 34, 3, 2, 1, 0, 33, 35, 5, 1, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 37, 1, 0, 0, 0, 36, 38, 3, 20, 10, 0, 37, 36, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 5, 0, 0, 1, 40, 1, 1, 0, 0, 0, 41, 43, 5, 1, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 46, 3, 6, 3, 0, 45, 47, 5, 1, 0, 0, 46, 45, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 50, 3, 4, 2, 0, 49, 51, 5, 1, 0, 0, 50, 49, 1, 0, 0, 0, 50, 51, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 53, 3, 8, 4, 0, 53, 3, 1, 0, 0, 0, 54, 55, 5, 5, 0, 0, 55, 5, 1, 0, 0, 0, 56, 57, 5, 9, 0, 0, 57, 7, 1, 0, 0, 0, 58, 59, 3, 10, 5, 0, 59, 60, 5, 6, 0, 0, 60, 61, 5, 1, 0, 0, 61, 63, 1, 0, 0, 0, 62, 58, 1, 0, 0, 0, 63, 66, 1, 0, 0, 0, 64, 62, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 67, 1, 0, 0, 0, 66, 64, 1, 0, 0, 0, 67, 68, 3, 10, 5, 0, 68, 9, 1, 0, 0, 0, 69, 75, 3, 12, 6, 0, 70, 75, 3, 14, 7, 0, 71, 75, 3, 16, 8, 0, 72, 75, 3, 18, 9, 0, 73, 75, 3, 22, 11, 0, 74, 69, 1, 0, 0, 0, 74, 70, 1, 0, 0, 0, 74, 71, 1, 0, 0, 0, 74, 72, 1, 0, 0, 0, 74, 73, 1, 0, 0, 0, 75, 11, 1, 0, 0, 0, 76, 77, 5, 9, 0, 0, 77, 13, 1, 0, 0, 0, 78, 83, 5, 8, 0, 0, 79, 80, 5, 9, 0, 0, 80, 82, 5, 8, 0, 0, 81, 79, 1, 0, 0, 0, 82, 85, 1, 0, 0, 0, 83, 81, 1, 0, 0, 0, 83, 84, 1, 0, 0, 0, 84, 87, 1, 0, 0, 0, 85, 83, 1, 0, 0, 0, 86, 88, 5, 9, 0, 0, 87, 86, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 15, 1, 0, 0, 0, 89, 91, 5, 4, 0, 0, 90, 92, 5, 9, 0, 0, 91, 90, 1, 0, 0, 0, 91, 92, 1, 0, 0, 0, 92, 17, 1, 0, 0, 0, 93, 94, 5, 5, 0, 0, 94, 95, 5, 3, 0, 0, 95, 97, 5, 5, 0, 0, 96, 98, 3, 14, 7, 0, 97, 96, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 19, 1, 0, 0, 0, 99, 104, 5, 7, 0, 0, 100, 102, 5, 1, 0, 0, 101, 100, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 105, 5, 9, 0, 0, 104, 101, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, 106, 104, 1, 0, 0, 0, 106, 107, 1, 0, 0, 0, 107, 109, 1, 0, 0, 0, 108, 110, 5, 1, 0, 0, 109, 108, 1, 0, 0, 0, 109, 110, 1, 0, 0, 0, 110, 21, 1, 0, 0, 0, 111, 112, 5, 9, 0, 0, 112, 113, 5, 2, 0, 0, 113, 114, 5, 9, 0, 0, 114, 23, 1, 0, 0, 0, 115, 116, 3, 26, 13, 0, 116, 117, 5, 5, 0, 0, 117, 118, 3, 28, 14, 0, 118, 119, 5, 1, 0, 0, 119, 120, 3, 30, 15, 0, 120, 25, 1, 0, 0, 0, 121, 122, 5, 9, 0, 0, 122, 27, 1, 0, 0, 0, 123, 124, 5, 9, 0, 0, 124, 29, 1, 0, 0, 0, 125, 126, 5, 9, 0, 0, 126, 31, 1, 0, 0, 0, 14, 34, 37, 42, 46, 50, 64, 74, 83, 87, 91, 97, 101, 106, 109] \ No newline at end of file diff --git a/handlers/aliases/parser/aliases_parser.go b/handlers/aliases/parser/aliases_parser.go index c9f47a8..3708a9b 100644 --- a/handlers/aliases/parser/aliases_parser.go +++ b/handlers/aliases/parser/aliases_parser.go @@ -46,55 +46,57 @@ func aliasesParserInit() { } staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.serializedATN = []int32{ - 4, 1, 9, 124, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, + 4, 1, 9, 128, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 1, 0, 1, 0, 3, 0, 35, 8, 0, 1, 0, 3, 0, 38, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 43, 8, 1, 1, 1, 1, 1, 3, 1, 47, 8, 1, 1, 1, 1, 1, 3, 1, 51, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 63, 8, 4, 10, 4, 12, 4, 66, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, - 75, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 4, 7, 81, 8, 7, 11, 7, 12, 7, 82, 1, - 7, 3, 7, 86, 8, 7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 1, 9, 1, 9, 1, 10, - 1, 10, 3, 10, 98, 8, 10, 1, 10, 4, 10, 101, 8, 10, 11, 10, 12, 10, 102, - 1, 10, 3, 10, 106, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, - 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, - 0, 0, 16, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 0, - 0, 122, 0, 32, 1, 0, 0, 0, 2, 42, 1, 0, 0, 0, 4, 54, 1, 0, 0, 0, 6, 56, - 1, 0, 0, 0, 8, 64, 1, 0, 0, 0, 10, 74, 1, 0, 0, 0, 12, 76, 1, 0, 0, 0, - 14, 80, 1, 0, 0, 0, 16, 87, 1, 0, 0, 0, 18, 90, 1, 0, 0, 0, 20, 95, 1, - 0, 0, 0, 22, 107, 1, 0, 0, 0, 24, 111, 1, 0, 0, 0, 26, 117, 1, 0, 0, 0, - 28, 119, 1, 0, 0, 0, 30, 121, 1, 0, 0, 0, 32, 34, 3, 2, 1, 0, 33, 35, 5, - 1, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 37, 1, 0, 0, 0, 36, - 38, 3, 20, 10, 0, 37, 36, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 39, 1, 0, - 0, 0, 39, 40, 5, 0, 0, 1, 40, 1, 1, 0, 0, 0, 41, 43, 5, 1, 0, 0, 42, 41, - 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 46, 3, 6, 3, 0, - 45, 47, 5, 1, 0, 0, 46, 45, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 48, 1, - 0, 0, 0, 48, 50, 3, 4, 2, 0, 49, 51, 5, 1, 0, 0, 50, 49, 1, 0, 0, 0, 50, - 51, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 53, 3, 8, 4, 0, 53, 3, 1, 0, 0, - 0, 54, 55, 5, 5, 0, 0, 55, 5, 1, 0, 0, 0, 56, 57, 5, 9, 0, 0, 57, 7, 1, - 0, 0, 0, 58, 59, 3, 10, 5, 0, 59, 60, 5, 6, 0, 0, 60, 61, 5, 1, 0, 0, 61, - 63, 1, 0, 0, 0, 62, 58, 1, 0, 0, 0, 63, 66, 1, 0, 0, 0, 64, 62, 1, 0, 0, - 0, 64, 65, 1, 0, 0, 0, 65, 67, 1, 0, 0, 0, 66, 64, 1, 0, 0, 0, 67, 68, - 3, 10, 5, 0, 68, 9, 1, 0, 0, 0, 69, 75, 3, 12, 6, 0, 70, 75, 3, 14, 7, - 0, 71, 75, 3, 16, 8, 0, 72, 75, 3, 18, 9, 0, 73, 75, 3, 22, 11, 0, 74, - 69, 1, 0, 0, 0, 74, 70, 1, 0, 0, 0, 74, 71, 1, 0, 0, 0, 74, 72, 1, 0, 0, - 0, 74, 73, 1, 0, 0, 0, 75, 11, 1, 0, 0, 0, 76, 77, 5, 9, 0, 0, 77, 13, - 1, 0, 0, 0, 78, 79, 5, 8, 0, 0, 79, 81, 5, 9, 0, 0, 80, 78, 1, 0, 0, 0, - 81, 82, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 82, 83, 1, 0, 0, 0, 83, 85, 1, - 0, 0, 0, 84, 86, 5, 8, 0, 0, 85, 84, 1, 0, 0, 0, 85, 86, 1, 0, 0, 0, 86, - 15, 1, 0, 0, 0, 87, 88, 5, 4, 0, 0, 88, 89, 5, 9, 0, 0, 89, 17, 1, 0, 0, - 0, 90, 91, 5, 5, 0, 0, 91, 92, 5, 3, 0, 0, 92, 93, 5, 5, 0, 0, 93, 94, - 3, 14, 7, 0, 94, 19, 1, 0, 0, 0, 95, 100, 5, 7, 0, 0, 96, 98, 5, 1, 0, - 0, 97, 96, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 101, - 5, 9, 0, 0, 100, 97, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 100, 1, 0, - 0, 0, 102, 103, 1, 0, 0, 0, 103, 105, 1, 0, 0, 0, 104, 106, 5, 1, 0, 0, - 105, 104, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, 106, 21, 1, 0, 0, 0, 107, 108, - 5, 9, 0, 0, 108, 109, 5, 2, 0, 0, 109, 110, 5, 9, 0, 0, 110, 23, 1, 0, - 0, 0, 111, 112, 3, 26, 13, 0, 112, 113, 5, 5, 0, 0, 113, 114, 3, 28, 14, - 0, 114, 115, 5, 1, 0, 0, 115, 116, 3, 30, 15, 0, 116, 25, 1, 0, 0, 0, 117, - 118, 5, 9, 0, 0, 118, 27, 1, 0, 0, 0, 119, 120, 5, 9, 0, 0, 120, 29, 1, - 0, 0, 0, 121, 122, 5, 9, 0, 0, 122, 31, 1, 0, 0, 0, 12, 34, 37, 42, 46, - 50, 64, 74, 82, 85, 97, 102, 105, + 75, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 82, 8, 7, 10, 7, 12, 7, 85, + 9, 7, 1, 7, 3, 7, 88, 8, 7, 1, 8, 1, 8, 3, 8, 92, 8, 8, 1, 9, 1, 9, 1, + 9, 1, 9, 3, 9, 98, 8, 9, 1, 10, 1, 10, 3, 10, 102, 8, 10, 1, 10, 4, 10, + 105, 8, 10, 11, 10, 12, 10, 106, 1, 10, 3, 10, 110, 8, 10, 1, 11, 1, 11, + 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, + 14, 1, 14, 1, 15, 1, 15, 1, 15, 0, 0, 16, 0, 2, 4, 6, 8, 10, 12, 14, 16, + 18, 20, 22, 24, 26, 28, 30, 0, 0, 128, 0, 32, 1, 0, 0, 0, 2, 42, 1, 0, + 0, 0, 4, 54, 1, 0, 0, 0, 6, 56, 1, 0, 0, 0, 8, 64, 1, 0, 0, 0, 10, 74, + 1, 0, 0, 0, 12, 76, 1, 0, 0, 0, 14, 78, 1, 0, 0, 0, 16, 89, 1, 0, 0, 0, + 18, 93, 1, 0, 0, 0, 20, 99, 1, 0, 0, 0, 22, 111, 1, 0, 0, 0, 24, 115, 1, + 0, 0, 0, 26, 121, 1, 0, 0, 0, 28, 123, 1, 0, 0, 0, 30, 125, 1, 0, 0, 0, + 32, 34, 3, 2, 1, 0, 33, 35, 5, 1, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, + 0, 0, 0, 35, 37, 1, 0, 0, 0, 36, 38, 3, 20, 10, 0, 37, 36, 1, 0, 0, 0, + 37, 38, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 5, 0, 0, 1, 40, 1, 1, 0, + 0, 0, 41, 43, 5, 1, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, + 1, 0, 0, 0, 44, 46, 3, 6, 3, 0, 45, 47, 5, 1, 0, 0, 46, 45, 1, 0, 0, 0, + 46, 47, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 50, 3, 4, 2, 0, 49, 51, 5, + 1, 0, 0, 50, 49, 1, 0, 0, 0, 50, 51, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, + 53, 3, 8, 4, 0, 53, 3, 1, 0, 0, 0, 54, 55, 5, 5, 0, 0, 55, 5, 1, 0, 0, + 0, 56, 57, 5, 9, 0, 0, 57, 7, 1, 0, 0, 0, 58, 59, 3, 10, 5, 0, 59, 60, + 5, 6, 0, 0, 60, 61, 5, 1, 0, 0, 61, 63, 1, 0, 0, 0, 62, 58, 1, 0, 0, 0, + 63, 66, 1, 0, 0, 0, 64, 62, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 67, 1, + 0, 0, 0, 66, 64, 1, 0, 0, 0, 67, 68, 3, 10, 5, 0, 68, 9, 1, 0, 0, 0, 69, + 75, 3, 12, 6, 0, 70, 75, 3, 14, 7, 0, 71, 75, 3, 16, 8, 0, 72, 75, 3, 18, + 9, 0, 73, 75, 3, 22, 11, 0, 74, 69, 1, 0, 0, 0, 74, 70, 1, 0, 0, 0, 74, + 71, 1, 0, 0, 0, 74, 72, 1, 0, 0, 0, 74, 73, 1, 0, 0, 0, 75, 11, 1, 0, 0, + 0, 76, 77, 5, 9, 0, 0, 77, 13, 1, 0, 0, 0, 78, 83, 5, 8, 0, 0, 79, 80, + 5, 9, 0, 0, 80, 82, 5, 8, 0, 0, 81, 79, 1, 0, 0, 0, 82, 85, 1, 0, 0, 0, + 83, 81, 1, 0, 0, 0, 83, 84, 1, 0, 0, 0, 84, 87, 1, 0, 0, 0, 85, 83, 1, + 0, 0, 0, 86, 88, 5, 9, 0, 0, 87, 86, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, + 15, 1, 0, 0, 0, 89, 91, 5, 4, 0, 0, 90, 92, 5, 9, 0, 0, 91, 90, 1, 0, 0, + 0, 91, 92, 1, 0, 0, 0, 92, 17, 1, 0, 0, 0, 93, 94, 5, 5, 0, 0, 94, 95, + 5, 3, 0, 0, 95, 97, 5, 5, 0, 0, 96, 98, 3, 14, 7, 0, 97, 96, 1, 0, 0, 0, + 97, 98, 1, 0, 0, 0, 98, 19, 1, 0, 0, 0, 99, 104, 5, 7, 0, 0, 100, 102, + 5, 1, 0, 0, 101, 100, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 103, 1, 0, + 0, 0, 103, 105, 5, 9, 0, 0, 104, 101, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, + 106, 104, 1, 0, 0, 0, 106, 107, 1, 0, 0, 0, 107, 109, 1, 0, 0, 0, 108, + 110, 5, 1, 0, 0, 109, 108, 1, 0, 0, 0, 109, 110, 1, 0, 0, 0, 110, 21, 1, + 0, 0, 0, 111, 112, 5, 9, 0, 0, 112, 113, 5, 2, 0, 0, 113, 114, 5, 9, 0, + 0, 114, 23, 1, 0, 0, 0, 115, 116, 3, 26, 13, 0, 116, 117, 5, 5, 0, 0, 117, + 118, 3, 28, 14, 0, 118, 119, 5, 1, 0, 0, 119, 120, 3, 30, 15, 0, 120, 25, + 1, 0, 0, 0, 121, 122, 5, 9, 0, 0, 122, 27, 1, 0, 0, 0, 123, 124, 5, 9, + 0, 0, 124, 29, 1, 0, 0, 0, 125, 126, 5, 9, 0, 0, 126, 31, 1, 0, 0, 0, 14, + 34, 37, 42, 46, 50, 64, 74, 83, 87, 91, 97, 101, 106, 109, } deserializer := antlr.NewATNDeserializer(nil) staticData.atn = deserializer.Deserialize(staticData.serializedATN) @@ -1333,23 +1335,25 @@ func (p *AliasesParser) File() (localctx IFileContext) { var _alt int p.EnterOuterAlt(localctx, 1) - p.SetState(80) + { + p.SetState(78) + p.Match(AliasesParserSLASH) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + p.SetState(83) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit } - _alt = 1 - for ok := true; ok; ok = _alt != 2 && _alt != antlr.ATNInvalidAltNumber { - switch _alt { - case 1: - { - p.SetState(78) - p.Match(AliasesParserSLASH) - if p.HasError() { - // Recognition error - abort rule - 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(79) p.Match(AliasesParserSTRING) @@ -1358,30 +1362,37 @@ func (p *AliasesParser) File() (localctx IFileContext) { goto errorExit } } + { + p.SetState(80) + p.Match(AliasesParserSLASH) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } - default: - p.SetError(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil)) + } + p.SetState(85) + p.GetErrorHandler().Sync(p) + if p.HasError() { goto errorExit } - - p.SetState(82) - p.GetErrorHandler().Sync(p) _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 7, p.GetParserRuleContext()) if p.HasError() { goto errorExit } } - p.SetState(85) + p.SetState(87) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit } _la = p.GetTokenStream().LA(1) - if _la == AliasesParserSLASH { + if _la == AliasesParserSTRING { { - p.SetState(84) - p.Match(AliasesParserSLASH) + p.SetState(86) + p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule goto errorExit @@ -1481,22 +1492,34 @@ func (s *CommandContext) ExitRule(listener antlr.ParseTreeListener) { func (p *AliasesParser) Command() (localctx ICommandContext) { localctx = NewCommandContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 16, AliasesParserRULE_command) + var _la int + p.EnterOuterAlt(localctx, 1) { - p.SetState(87) + p.SetState(89) p.Match(AliasesParserVERTLINE) if p.HasError() { // Recognition error - abort rule goto errorExit } } - { - p.SetState(88) - p.Match(AliasesParserSTRING) - if p.HasError() { - // Recognition error - abort rule - goto errorExit + p.SetState(91) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSTRING { + { + p.SetState(90) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } } + } errorExit: @@ -1612,9 +1635,11 @@ func (s *IncludeContext) ExitRule(listener antlr.ParseTreeListener) { func (p *AliasesParser) Include() (localctx IIncludeContext) { localctx = NewIncludeContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 18, AliasesParserRULE_include) + var _la int + p.EnterOuterAlt(localctx, 1) { - p.SetState(90) + p.SetState(93) p.Match(AliasesParserCOLON) if p.HasError() { // Recognition error - abort rule @@ -1622,7 +1647,7 @@ func (p *AliasesParser) Include() (localctx IIncludeContext) { } } { - p.SetState(91) + p.SetState(94) p.Match(AliasesParserINCLUDE) if p.HasError() { // Recognition error - abort rule @@ -1630,16 +1655,26 @@ func (p *AliasesParser) Include() (localctx IIncludeContext) { } } { - p.SetState(92) + p.SetState(95) p.Match(AliasesParserCOLON) if p.HasError() { // Recognition error - abort rule goto errorExit } } - { - p.SetState(93) - p.File() + p.SetState(97) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSLASH { + { + p.SetState(96) + p.File() + } + } errorExit: @@ -1754,14 +1789,14 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(95) + p.SetState(99) p.Match(AliasesParserNUMBER_SIGN) if p.HasError() { // Recognition error - abort rule goto errorExit } } - p.SetState(100) + p.SetState(104) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1770,7 +1805,7 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { for ok := true; ok; ok = _alt != 2 && _alt != antlr.ATNInvalidAltNumber { switch _alt { case 1: - p.SetState(97) + p.SetState(101) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1779,7 +1814,7 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { if _la == AliasesParserSEPARATOR { { - p.SetState(96) + p.SetState(100) p.Match(AliasesParserSEPARATOR) if p.HasError() { // Recognition error - abort rule @@ -1789,7 +1824,7 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { } { - p.SetState(99) + p.SetState(103) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -1802,14 +1837,14 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { goto errorExit } - p.SetState(102) + p.SetState(106) p.GetErrorHandler().Sync(p) - _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 10, p.GetParserRuleContext()) + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 12, p.GetParserRuleContext()) if p.HasError() { goto errorExit } } - p.SetState(105) + p.SetState(109) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1818,7 +1853,7 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { if _la == AliasesParserSEPARATOR { { - p.SetState(104) + p.SetState(108) p.Match(AliasesParserSEPARATOR) if p.HasError() { // Recognition error - abort rule @@ -1926,7 +1961,7 @@ func (p *AliasesParser) Email() (localctx IEmailContext) { p.EnterRule(localctx, 22, AliasesParserRULE_email) p.EnterOuterAlt(localctx, 1) { - p.SetState(107) + p.SetState(111) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -1934,7 +1969,7 @@ func (p *AliasesParser) Email() (localctx IEmailContext) { } } { - p.SetState(108) + p.SetState(112) p.Match(AliasesParserAT) if p.HasError() { // Recognition error - abort rule @@ -1942,7 +1977,7 @@ func (p *AliasesParser) Email() (localctx IEmailContext) { } } { - p.SetState(109) + p.SetState(113) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -2094,11 +2129,11 @@ func (p *AliasesParser) Error_() (localctx IErrorContext) { p.EnterRule(localctx, 24, AliasesParserRULE_error) p.EnterOuterAlt(localctx, 1) { - p.SetState(111) + p.SetState(115) p.ErrorStatus() } { - p.SetState(112) + p.SetState(116) p.Match(AliasesParserCOLON) if p.HasError() { // Recognition error - abort rule @@ -2106,11 +2141,11 @@ func (p *AliasesParser) Error_() (localctx IErrorContext) { } } { - p.SetState(113) + p.SetState(117) p.ErrorCode() } { - p.SetState(114) + p.SetState(118) p.Match(AliasesParserSEPARATOR) if p.HasError() { // Recognition error - abort rule @@ -2118,7 +2153,7 @@ func (p *AliasesParser) Error_() (localctx IErrorContext) { } } { - p.SetState(115) + p.SetState(119) p.ErrorMessage() } @@ -2210,7 +2245,7 @@ func (p *AliasesParser) ErrorStatus() (localctx IErrorStatusContext) { p.EnterRule(localctx, 26, AliasesParserRULE_errorStatus) p.EnterOuterAlt(localctx, 1) { - p.SetState(117) + p.SetState(121) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -2306,7 +2341,7 @@ func (p *AliasesParser) ErrorCode() (localctx IErrorCodeContext) { p.EnterRule(localctx, 28, AliasesParserRULE_errorCode) p.EnterOuterAlt(localctx, 1) { - p.SetState(119) + p.SetState(123) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -2402,7 +2437,7 @@ func (p *AliasesParser) ErrorMessage() (localctx IErrorMessageContext) { p.EnterRule(localctx, 30, AliasesParserRULE_errorMessage) p.EnterOuterAlt(localctx, 1) { - p.SetState(121) + p.SetState(125) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule diff --git a/handlers/aliases/shared.go b/handlers/aliases/shared.go index ec1e83d..b47d360 100644 --- a/handlers/aliases/shared.go +++ b/handlers/aliases/shared.go @@ -13,4 +13,3 @@ type AliasesDocument struct { } var DocumentParserMap = map[protocol.DocumentUri]*AliasesDocument{} - diff --git a/common/analyzer.go b/handlers/openssh/analyzer.go similarity index 97% rename from common/analyzer.go rename to handlers/openssh/analyzer.go index e859a1a..52ad3d5 100644 --- a/common/analyzer.go +++ b/handlers/openssh/analyzer.go @@ -1,4 +1,4 @@ -package common +package openssh import ( docvalues "config-lsp/doc-values" diff --git a/handlers/openssh/diagnose-ssh-options.go b/handlers/openssh/diagnose-ssh-options.go index 5805248..3591318 100644 --- a/handlers/openssh/diagnose-ssh-options.go +++ b/handlers/openssh/diagnose-ssh-options.go @@ -1,8 +1,6 @@ package openssh import ( - "config-lsp/common" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) @@ -10,18 +8,18 @@ import ( func DiagnoseSSHOptions( context *glsp.Context, documentURI protocol.DocumentUri, - parser *common.SimpleConfigParser, + parser *SimpleConfigParser, ) []protocol.Diagnostic { diagnostics := make([]protocol.Diagnostic, 0) diagnostics = append( diagnostics, - common.DiagnoseOption( + DiagnoseOption( context, documentURI, parser, "Port", - func(value string, position common.SimpleConfigPosition) []protocol.Diagnostic { + func(value string, position SimpleConfigPosition) []protocol.Diagnostic { if value == "22" { severity := protocol.DiagnosticSeverityWarning @@ -50,3 +48,20 @@ func DiagnoseSSHOptions( return diagnostics } + +func DiagnoseOption( + context *glsp.Context, + uri protocol.DocumentUri, + parser *SimpleConfigParser, + optionName string, + checkerFunc func(string, SimpleConfigPosition) []protocol.Diagnostic, +) []protocol.Diagnostic { + option, err := parser.GetOption(optionName) + + if err != nil { + // Nothing to diagnose + return nil + } + + return checkerFunc(option.Value, option.Position) +} diff --git a/handlers/openssh/diagnostics.go b/handlers/openssh/diagnostics.go index 8921dbd..f38bb13 100644 --- a/handlers/openssh/diagnostics.go +++ b/handlers/openssh/diagnostics.go @@ -1,7 +1,6 @@ package openssh import ( - "config-lsp/common" docvalues "config-lsp/doc-values" "config-lsp/utils" @@ -29,7 +28,7 @@ func DiagnoseParser( diagnostics = append( diagnostics, utils.Map( - common.AnalyzeValues(Parser, Options), + AnalyzeValues(Parser, Options), func(err docvalues.ValueError) protocol.Diagnostic { return err.GetPublishDiagnosticsParams() }, diff --git a/common/documentation.go b/handlers/openssh/documentation-common.go similarity index 97% rename from common/documentation.go rename to handlers/openssh/documentation-common.go index 2500108..5158146 100644 --- a/common/documentation.go +++ b/handlers/openssh/documentation-common.go @@ -1,4 +1,4 @@ -package common +package openssh import ( docvalues "config-lsp/doc-values" diff --git a/handlers/openssh/documentation.go b/handlers/openssh/documentation.go index 9226464..adb61d8 100644 --- a/handlers/openssh/documentation.go +++ b/handlers/openssh/documentation.go @@ -1,7 +1,6 @@ package openssh import ( - "config-lsp/common" docvalues "config-lsp/doc-values" "regexp" ) @@ -10,12 +9,12 @@ var ZERO = 0 var MAX_PORT = 65535 var MAX_FILE_MODE = 0777 -var Options = map[string]common.Option{ - "AcceptEnv": common.NewOption( +var Options = map[string]Option{ + "AcceptEnv": NewOption( `Specifies what environment variables sent by the client will be copied into the session's environ(7). See SendEnv and SetEnv in ssh_config(5) for how to configure the client. The TERM environment variable is always accepted whenever the client requests a pseudo-terminal as it is required by the protocol. Variables are specified by name, which may contain the wildcard characters ‘*’ and ‘?’. Multiple environment variables may be separated by whitespace or spread across multiple AcceptEnv directives. Be warned that some environment variables could be used to bypass restricted user environments. For this reason, care should be taken in the use of this directive. The default is not to accept any environment variables.`, docvalues.StringValue{}, ), - "AddressFamily": common.NewOption( + "AddressFamily": NewOption( `Specifies which address family should be used by sshd(8). Valid arguments are any (the default), inet (use IPv4 only), or inet6 (use IPv6 only).`, docvalues.EnumValue{ EnforceValues: true, @@ -26,17 +25,17 @@ var Options = map[string]common.Option{ }, }, ), - "AllowAgentForwarding": common.NewOption( + "AllowAgentForwarding": NewOption( `Specifies whether ssh-agent(1) forwarding is permitted. The default is yes. Note that disabling agent forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders.`, BooleanEnumValue, ), - "AllowGroups": common.NewOption( + "AllowGroups": NewOption( `This keyword can be followed by a list of group name patterns, separated by spaces. If specified, login is allowed only for users whose primary group or supplementary group list matches one of the patterns. Only group names are valid; a numerical group ID is not recognized. By default, login is allowed for all groups. The allow/deny groups directives are processed in the following order: DenyGroups, AllowGroups. See PATTERNS in ssh_config(5) for more information on patterns. This keyword may appear multiple times in sshd_config with each instance appending to the list.`, docvalues.GroupValue(" ", false), ), - "AllowStreamLocalForwarding": common.NewOption( + "AllowStreamLocalForwarding": NewOption( `Specifies whether StreamLocal (Unix-domain socket) forwarding is permitted. The available options are yes (the default) or all to allow StreamLocal forwarding, no to prevent all StreamLocal forwarding, local to allow local (from the perspective of ssh(1)) forwarding only or remote to allow remote forwarding only. Note that disabling StreamLocal forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders.`, docvalues.EnumValue{ EnforceValues: true, @@ -49,7 +48,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "AllowTcpForwarding": common.NewOption( + "AllowTcpForwarding": NewOption( `Specifies whether TCP forwarding is permitted. The available options are yes (the default) or all to allow TCP forwarding, no to prevent all TCP forwarding, local to allow local (from the perspective of ssh(1)) forwarding only or remote to allow remote forwarding only. Note that disabling TCP forwarding does not improve security unless users are also denied shell access, as they can always install their own forwarders.`, docvalues.EnumValue{ EnforceValues: true, @@ -62,12 +61,12 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "AllowUsers": common.NewOption( + "AllowUsers": NewOption( `This keyword can be followed by a list of user name patterns, separated by spaces. If specified, login is allowed only for user names that match one of the patterns. Only user names are valid; a numerical user ID is not recognized. By default, login is allowed for all users. If the pattern takes the form USER@HOST then USER and HOST are separately checked, restricting logins to particular users from particular hosts. HOST criteria may additionally contain addresses to match in CIDR address/masklen format. The allow/deny users directives are processed in the following order: DenyUsers, AllowUsers. See PATTERNS in ssh_config(5) for more information on patterns. This keyword may appear multiple times in sshd_config with each instance appending to the list.`, docvalues.UserValue(" ", false), ), - "AuthenticationMethods": common.NewOption( + "AuthenticationMethods": NewOption( `Specifies the authentication methods that must be successfully completed for a user to be granted access. This option must be followed by one or more lists of comma-separated authentication method names, or by the single string any to indicate the default behaviour of accepting any single authentication method. If the default is overridden, then successful authentication requires completion of every method in at least one of these lists. For example, "publickey,password publickey,keyboard-interactive" would require the user to complete public key authentication, followed by either password or keyboard interactive authentication. Only methods that are next in one or more lists are offered at each stage, so for this example it would not be possible to attempt password or keyboard-interactive authentication before public key. For keyboard interactive authentication it is also possible to restrict authentication to a specific device by appending a colon followed by the device identifier bsdauth or pam. depending on the server configuration. For example, "keyboard-interactive:bsdauth" would restrict keyboard interactive authentication to the bsdauth device. @@ -111,17 +110,17 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "AuthorizedKeysCommand": common.NewOption( + "AuthorizedKeysCommand": NewOption( `Specifies a program to be used to look up the user's public keys. The program must be owned by root, not writable by group or others and specified by an absolute path. Arguments to AuthorizedKeysCommand accept the tokens described in the “TOKENS” section. If no arguments are specified then the username of the target user is used. The program should produce on standard output zero or more lines of authorized_keys output (see “AUTHORIZED_KEYS” in sshd(8)). AuthorizedKeysCommand is tried after the usual AuthorizedKeysFile files and will not be executed if a matching key is found there. By default, no AuthorizedKeysCommand is run.`, docvalues.StringValue{}, ), - "AuthorizedKeysCommandUser": common.NewOption( + "AuthorizedKeysCommandUser": NewOption( `Specifies the user under whose account the AuthorizedKeysCommand is run. It is recommended to use a dedicated user that has no other role on the host than running authorized keys commands. If AuthorizedKeysCommand is specified but AuthorizedKeysCommandUser is not, then sshd(8) will refuse to start.`, docvalues.UserValue("", true), ), - "AuthorizedKeysFile": common.NewOption( + "AuthorizedKeysFile": NewOption( `Specifies the file that contains the public keys used for user authentication. The format is described in the AUTHORIZED_KEYS FILE FORMAT section of sshd(8). Arguments to AuthorizedKeysFile accept the tokens described in the “TOKENS” section. After expansion, AuthorizedKeysFile is taken to be an absolute path or one relative to the user's home directory. Multiple files may be listed, separated by whitespace. Alternately this option may be set to none to skip checking for user keys in files. The default is ".ssh/authorized_keys .ssh/authorized_keys2".`, docvalues.ArrayValue{ SubValue: docvalues.StringValue{}, @@ -129,16 +128,16 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may DuplicatesExtractor: &docvalues.DuplicatesAllowedExtractor, }, ), - "AuthorizedPrincipalsCommand": common.NewOption( + "AuthorizedPrincipalsCommand": NewOption( `Specifies a program to be used to generate the list of allowed certificate principals as per AuthorizedPrincipalsFile. The program must be owned by root, not writable by group or others and specified by an absolute path. Arguments to AuthorizedPrincipalsCommand accept the tokens described in the “TOKENS” section. If no arguments are specified then the username of the target user is used. The program should produce on standard output zero or more lines of AuthorizedPrincipalsFile output. If either AuthorizedPrincipalsCommand or AuthorizedPrincipalsFile is specified, then certificates offered by the client for authentication must contain a principal that is listed. By default, no AuthorizedPrincipalsCommand is run.`, docvalues.StringValue{}, ), - "AuthorizedPrincipalsCommandUser": common.NewOption( + "AuthorizedPrincipalsCommandUser": NewOption( `Specifies the user under whose account the AuthorizedPrincipalsCommand is run. It is recommended to use a dedicated user that has no other role on the host than running authorized principals commands. If AuthorizedPrincipalsCommand is specified but AuthorizedPrincipalsCommandUser is not, then sshd(8) will refuse to start.`, docvalues.UserValue("", true), ), - "AuthorizedPrincipalsFile": common.NewOption( + "AuthorizedPrincipalsFile": NewOption( `Specifies a file that lists principal names that are accepted for certificate authentication. When using certificates signed by a key listed in TrustedUserCAKeys, this file lists names, one of which must appear in the certificate for it to be accepted for authentication. Names are listed one per line preceded by key options (as described in “AUTHORIZED_KEYS FILE FORMAT” in sshd(8)). Empty lines and comments starting with ‘#’ are ignored. Arguments to AuthorizedPrincipalsFile accept the tokens described in the “TOKENS” section. After expansion, AuthorizedPrincipalsFile is taken to be an absolute path or one relative to the user's home directory. The default is none, i.e. not to use a principals file – in this case, the username of the user must appear in a certificate's principals list for it to be accepted. Note that AuthorizedPrincipalsFile is only used when authentication proceeds using a CA listed in TrustedUserCAKeys and is not consulted for certification authorities trusted via ~/.ssh/authorized_keys, though the principals= key option offers a similar facility (see sshd(8) for details).`, @@ -146,12 +145,12 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may RequiredType: docvalues.PathTypeFile, }, ), - "Banner": common.NewOption(`The contents of the specified file are sent to the remote user before authentication is allowed. If the argument is none then no banner is displayed. By default, no banner is displayed.`, + "Banner": NewOption(`The contents of the specified file are sent to the remote user before authentication is allowed. If the argument is none then no banner is displayed. By default, no banner is displayed.`, docvalues.PathValue{ RequiredType: docvalues.PathTypeFile, }, ), - "CASignatureAlgorithms": common.NewOption( + "CASignatureAlgorithms": NewOption( `Specifies which algorithms are allowed for signing of certificates by certificate authorities (CAs). The default is: ssh-ed25519,ecdsa-sha2-nistp256, ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, rsa-sha2-512,rsa-sha2-256 If the specified list begins with a ‘+’ character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. @@ -175,7 +174,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "ChannelTimeout": common.NewOption(`Specifies whether and how quickly sshd(8) should close inactive channels. Timeouts are specified as one or more “type=interval” pairs separated by whitespace, where the “type” must be the special keyword “global” or a channel type name from the list below, optionally containing wildcard characters. + "ChannelTimeout": NewOption(`Specifies whether and how quickly sshd(8) should close inactive channels. Timeouts are specified as one or more “type=interval” pairs separated by whitespace, where the “type” must be the special keyword “global” or a channel type name from the list below, optionally containing wildcard characters. The timeout value “interval” is specified in seconds or may use any of the units documented in the “TIME FORMATS” section. For example, “session=5m” would cause interactive sessions to terminate after five minutes of inactivity. Specifying a zero value disables the inactivity timeout. The special timeout “global” applies to all active channels, taken together. Traffic on any active channel will reset the timeout, but when the timeout expires then all open channels will be closed. Note that this global timeout is not matched by wildcards and must be specified explicitly. The available channel type names include: @@ -213,13 +212,13 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "ChrootDirectory": common.NewOption(`Specifies the pathname of a directory to chroot(2) to after authentication. At session startup sshd(8) checks that all components of the pathname are root-owned directories which are not writable by group or others. After the chroot, sshd(8) changes the working directory to the user's home directory. Arguments to ChrootDirectory accept the tokens described in the “TOKENS” section. + "ChrootDirectory": NewOption(`Specifies the pathname of a directory to chroot(2) to after authentication. At session startup sshd(8) checks that all components of the pathname are root-owned directories which are not writable by group or others. After the chroot, sshd(8) changes the working directory to the user's home directory. Arguments to ChrootDirectory accept the tokens described in the “TOKENS” section. The ChrootDirectory must contain the necessary files and directories to support the user's session. For an interactive session this requires at least a shell, typically sh(1), and basic /dev nodes such as null(4), zero(4), stdin(4), stdout(4), stderr(4), and tty(4) devices. For file transfer sessions using SFTP no additional configuration of the environment is necessary if the in-process sftp-server is used, though sessions which use logging may require /dev/log inside the chroot directory on some operating systems (see sftp-server(8) for details). For safety, it is very important that the directory hierarchy be prevented from modification by other processes on the system (especially those outside the jail). Misconfiguration can lead to unsafe environments which sshd(8) cannot detect. The default is none, indicating not to chroot(2).`, docvalues.StringValue{}, ), - "Ciphers": common.NewOption(`Specifies the ciphers allowed. Multiple ciphers must be comma-separated. If the specified list begins with a ‘+’ character, then the specified ciphers will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified ciphers (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a ‘^’ character, then the specified ciphers will be placed at the head of the default set. + "Ciphers": NewOption(`Specifies the ciphers allowed. Multiple ciphers must be comma-separated. If the specified list begins with a ‘+’ character, then the specified ciphers will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified ciphers (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a ‘^’ character, then the specified ciphers will be placed at the head of the default set. The supported ciphers are: 3des-cbc aes128-cbc aes192-cbc aes256-cbc aes128-ctr aes192-ctr aes256-ctr aes128-gcm@openssh.com aes256-gcm@openssh.com chacha20-poly1305@openssh.com The default is: @@ -238,15 +237,15 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may docvalues.CreateEnumString("chacha20-poly1305@openssh.com"), }), ), - "ClientAliveCountMax": common.NewOption(`Sets the number of client alive messages which may be sent without sshd(8) receiving any messages back from the client. If this threshold is reached while client alive messages are being sent, sshd will disconnect the client, terminating the session. It is important to note that the use of client alive messages is very different from TCPKeepAlive. The client alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by TCPKeepAlive is spoofable. The client alive mechanism is valuable when the client or server depend on knowing when a connection has become unresponsive. + "ClientAliveCountMax": NewOption(`Sets the number of client alive messages which may be sent without sshd(8) receiving any messages back from the client. If this threshold is reached while client alive messages are being sent, sshd will disconnect the client, terminating the session. It is important to note that the use of client alive messages is very different from TCPKeepAlive. The client alive messages are sent through the encrypted channel and therefore will not be spoofable. The TCP keepalive option enabled by TCPKeepAlive is spoofable. The client alive mechanism is valuable when the client or server depend on knowing when a connection has become unresponsive. The default value is 3. If ClientAliveInterval is set to 15, and ClientAliveCountMax is left at the default, unresponsive SSH clients will be disconnected after approximately 45 seconds. Setting a zero ClientAliveCountMax disables connection termination.`, docvalues.NumberValue{Min: &ZERO}, ), - "ClientAliveInterval": common.NewOption( + "ClientAliveInterval": NewOption( `Sets a timeout interval in seconds after which if no data has been received from the client, sshd(8) will send a message through the encrypted channel to request a response from the client. The default is 0, indicating that these messages will not be sent to the client.`, docvalues.NumberValue{Min: &ZERO}, ), - "Compression": common.NewOption( + "Compression": NewOption( `Specifies whether compression is enabled after the user has authenticated successfully. The argument must be yes, delayed (a legacy synonym for yes) or no. The default is yes.`, docvalues.EnumValue{ EnforceValues: true, @@ -257,23 +256,23 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "DenyGroups": common.NewOption(`This keyword can be followed by a list of group name patterns, separated by spaces. Login is disallowed for users whose primary group or supplementary group list matches one of the patterns. Only group names are valid; a numerical group ID is not recognized. By default, login is allowed for all groups. The allow/deny groups directives are processed in the following order: DenyGroups, AllowGroups. + "DenyGroups": NewOption(`This keyword can be followed by a list of group name patterns, separated by spaces. Login is disallowed for users whose primary group or supplementary group list matches one of the patterns. Only group names are valid; a numerical group ID is not recognized. By default, login is allowed for all groups. The allow/deny groups directives are processed in the following order: DenyGroups, AllowGroups. See PATTERNS in ssh_config(5) for more information on patterns. This keyword may appear multiple times in sshd_config with each instance appending to the list.`, docvalues.GroupValue(" ", false), ), - "DenyUsers": common.NewOption(`This keyword can be followed by a list of user name patterns, separated by spaces. Login is disallowed for user names that match one of the patterns. Only user names are valid; a numerical user ID is not recognized. By default, login is allowed for all users. If the pattern takes the form USER@HOST then USER and HOST are separately checked, restricting logins to particular users from particular hosts. HOST criteria may additionally contain addresses to match in CIDR address/masklen format. The allow/deny users directives are processed in the following order: DenyUsers, AllowUsers. + "DenyUsers": NewOption(`This keyword can be followed by a list of user name patterns, separated by spaces. Login is disallowed for user names that match one of the patterns. Only user names are valid; a numerical user ID is not recognized. By default, login is allowed for all users. If the pattern takes the form USER@HOST then USER and HOST are separately checked, restricting logins to particular users from particular hosts. HOST criteria may additionally contain addresses to match in CIDR address/masklen format. The allow/deny users directives are processed in the following order: DenyUsers, AllowUsers. See PATTERNS in ssh_config(5) for more information on patterns. This keyword may appear multiple times in sshd_config with each instance appending to the list.`, docvalues.UserValue(" ", false), ), - "DisableForwarding": common.NewOption( + "DisableForwarding": NewOption( `Disables all forwarding features, including X11, ssh-agent(1), TCP and StreamLocal. This option overrides all other forwarding-related options and may simplify restricted configurations.`, BooleanEnumValue, ), - "ExposeAuthInfo": common.NewOption( + "ExposeAuthInfo": NewOption( `Writes a temporary file containing a list of authentication methods and public credentials (e.g. keys) used to authenticate the user. The location of the file is exposed to the user session through the SSH_USER_AUTH environment variable. The default is no.`, BooleanEnumValue, ), - "FingerprintHash": common.NewOption( + "FingerprintHash": NewOption( `Specifies the hash algorithm used when logging key fingerprints. Valid options are: md5 and sha256. The default is sha256.`, docvalues.EnumValue{ EnforceValues: true, @@ -283,27 +282,27 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "ForceCommand": common.NewOption( + "ForceCommand": NewOption( `Forces the execution of the command specified by ForceCommand, ignoring any command supplied by the client and ~/.ssh/rc if present. The command is invoked by using the user's login shell with the -c option. This applies to shell, command, or subsystem execution. It is most useful inside a Match block. The command originally supplied by the client is available in the SSH_ORIGINAL_COMMAND environment variable. Specifying a command of internal-sftp will force the use of an in- process SFTP server that requires no support files when used with ChrootDirectory. The default is none.`, docvalues.StringValue{}, ), - "GatewayPorts": common.NewOption( + "GatewayPorts": NewOption( `Specifies whether remote hosts are allowed to connect to ports forwarded for the client. By default, sshd(8) binds remote port forwardings to the loopback address. This prevents other remote hosts from connecting to forwarded ports. GatewayPorts can be used to specify that sshd should allow remote port forwardings to bind to non-loopback addresses, thus allowing other hosts to connect. The argument may be no to force remote port forwardings to be available to the local host only, yes to force remote port forwardings to bind to the wildcard address, or clientspecified to allow the client to select the address to which the forwarding is bound. The default is no.`, BooleanEnumValue, ), - "GSSAPIAuthentication": common.NewOption( + "GSSAPIAuthentication": NewOption( `Specifies whether user authentication based on GSSAPI is allowed. The default is no.`, BooleanEnumValue, ), - "GSSAPICleanupCredentials": common.NewOption( + "GSSAPICleanupCredentials": NewOption( `Specifies whether to automatically destroy the user's credentials cache on logout. The default is yes.`, BooleanEnumValue, ), - "GSSAPIStrictAcceptorCheck": common.NewOption( + "GSSAPIStrictAcceptorCheck": NewOption( `Determines whether to be strict about the identity of the GSSAPI acceptor a client authenticates against. If set to yes then the client must authenticate against the host service on the current hostname. If set to no then the client may authenticate against any service key stored in the machine's default store. This facility is provided to assist with operation on multi homed machines. The default is yes.`, BooleanEnumValue, ), - "HostbasedAcceptedAlgorithms": common.NewOption(`Specifies the signature algorithms that will be accepted for hostbased authentication as a list of comma-separated patterns. Alternately if the specified list begins with a ‘+’ character, then the specified signature algorithms will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified signature algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a ‘^’ character, then the specified signature algorithms will be placed at the head of the default set. The default for this option is: + "HostbasedAcceptedAlgorithms": NewOption(`Specifies the signature algorithms that will be accepted for hostbased authentication as a list of comma-separated patterns. Alternately if the specified list begins with a ‘+’ character, then the specified signature algorithms will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified signature algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a ‘^’ character, then the specified signature algorithms will be placed at the head of the default set. The default for this option is: ssh-ed25519-cert-v01@openssh.com, ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com, rsa-sha2-256-cert-v01@openssh.com, ssh-ed25519, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, rsa-sha2-512,rsa-sha2-256 The list of available signature algorithms may also be obtained using "ssh -Q HostbasedAcceptedAlgorithms". This was formerly named HostbasedAcceptedKeyTypes.`, docvalues.CustomValue{ @@ -319,24 +318,24 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "HostbasedAuthentication": common.NewOption( + "HostbasedAuthentication": NewOption( `Specifies whether rhosts or /etc/hosts.equiv authentication together with successful public key client host authentication is allowed (host-based authentication). The default is no.`, BooleanEnumValue, ), - "HostbasedUsesNameFromPacketOnly": common.NewOption( + "HostbasedUsesNameFromPacketOnly": NewOption( `Specifies whether or not the server will attempt to perform a reverse name lookup when matching the name in the ~/.shosts, ~/.rhosts, and /etc/hosts.equiv files during HostbasedAuthentication. A setting of yes means that sshd(8) uses the name supplied by the client rather than attempting to resolve the name from the TCP connection itself. The default is no.`, BooleanEnumValue, ), - "HostCertificate": common.NewOption( + "HostCertificate": NewOption( `Specifies a file containing a public host certificate. The certificate's public key must match a private host key already specified by HostKey. The default behaviour of sshd(8) is not to load any certificates.`, docvalues.PathValue{}, ), - "HostKey": common.NewOption(`Specifies a file containing a private host key used by SSH. The defaults are /etc/ssh/ssh_host_ecdsa_key, /etc/ssh/ssh_host_ed25519_key and /etc/ssh/ssh_host_rsa_key. + "HostKey": NewOption(`Specifies a file containing a private host key used by SSH. The defaults are /etc/ssh/ssh_host_ecdsa_key, /etc/ssh/ssh_host_ed25519_key and /etc/ssh/ssh_host_rsa_key. Note that sshd(8) will refuse to use a file if it is group/world-accessible and that the HostKeyAlgorithms option restricts which of the keys are actually used by sshd(8). It is possible to have multiple host key files. It is also possible to specify public host key files instead. In this case operations on the private key will be delegated to an ssh-agent(1).`, docvalues.PathValue{}, ), - "HostKeyAgent": common.NewOption( + "HostKeyAgent": NewOption( `Identifies the UNIX-domain socket used to communicate with an agent that has access to the private host keys. If the string "SSH_AUTH_SOCK" is specified, the location of the socket will be read from the SSH_AUTH_SOCK environment variable.`, docvalues.OrValue{ Values: []docvalues.Value{ @@ -350,7 +349,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "HostKeyAlgorithms": common.NewOption(`Specifies the host key signature algorithms that the server offers. The default for this option is: + "HostKeyAlgorithms": NewOption(`Specifies the host key signature algorithms that the server offers. The default for this option is: ssh-ed25519-cert-v01@openssh.com, ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com, rsa-sha2-256-cert-v01@openssh.com, ssh-ed25519, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, rsa-sha2-512,rsa-sha2-256 The list of available signature algorithms may also be obtained using "ssh -Q HostKeyAlgorithms".`, docvalues.CustomValue{ @@ -361,7 +360,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "IgnoreRhosts": common.NewOption(`Specifies whether to ignore per-user .rhosts and .shosts files during HostbasedAuthentication. The system-wide /etc/hosts.equiv and /etc/shosts.equiv are still used regardless of this setting. + "IgnoreRhosts": NewOption(`Specifies whether to ignore per-user .rhosts and .shosts files during HostbasedAuthentication. The system-wide /etc/hosts.equiv and /etc/shosts.equiv are still used regardless of this setting. Accepted values are yes (the default) to ignore all per- user files, shosts-only to allow the use of .shosts but to ignore .rhosts or no to allow both .shosts and rhosts.`, docvalues.EnumValue{ EnforceValues: true, @@ -372,11 +371,11 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "IgnoreUserKnownHosts": common.NewOption( + "IgnoreUserKnownHosts": NewOption( `Specifies whether sshd(8) should ignore the user's ~/.ssh/known_hosts during HostbasedAuthentication and use only the system-wide known hosts file /etc/ssh/ssh_known_hosts. The default is “no”.`, BooleanEnumValue, ), - "Include": common.NewOption(`Include the specified configuration file(s). Multiple pathnames may be specified and each pathname may contain glob(7) wildcards that will be expanded and processed in lexical order. Files without absolute paths are assumed to be in /etc/ssh. An Include directive may appear inside a Match block to perform conditional inclusion.`, + "Include": NewOption(`Include the specified configuration file(s). Multiple pathnames may be specified and each pathname may contain glob(7) wildcards that will be expanded and processed in lexical order. Files without absolute paths are assumed to be in /etc/ssh. An Include directive may appear inside a Match block to perform conditional inclusion.`, docvalues.ArrayValue{ Separator: " ", DuplicatesExtractor: &docvalues.SimpleDuplicatesExtractor, @@ -387,7 +386,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may // TODO: Add extra check ), - "IPQoS": common.NewOption(`Specifies the IPv4 type-of-service or DSCP class for the connection. Accepted values are af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43, cs0, cs1, cs2, cs3, cs4, cs5, cs6, cs7, ef, le, lowdelay, throughput, reliability, a numeric value, or none to use the operating system default. This option may take one or two arguments, separated by whitespace. If one argument is specified, it is used as the packet class unconditionally. If two values are specified, the first is automatically selected for interactive sessions and the second for non-interactive sessions. The default is af21 (Low-Latency Data) for interactive sessions and cs1 (Lower Effort) for non-interactive sessions.`, + "IPQoS": NewOption(`Specifies the IPv4 type-of-service or DSCP class for the connection. Accepted values are af11, af12, af13, af21, af22, af23, af31, af32, af33, af41, af42, af43, cs0, cs1, cs2, cs3, cs4, cs5, cs6, cs7, ef, le, lowdelay, throughput, reliability, a numeric value, or none to use the operating system default. This option may take one or two arguments, separated by whitespace. If one argument is specified, it is used as the packet class unconditionally. If two values are specified, the first is automatically selected for interactive sessions and the second for non-interactive sessions. The default is af21 (Low-Latency Data) for interactive sessions and cs1 (Lower Effort) for non-interactive sessions.`, docvalues.OrValue{ Values: []docvalues.Value{ docvalues.NumberValue{}, @@ -433,23 +432,23 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "KbdInteractiveAuthentication": common.NewOption( + "KbdInteractiveAuthentication": NewOption( `Specifies whether to allow keyboard-interactive authentication. All authentication styles from login.conf(5) are supported. The default is yes. The argument to this keyword must be yes or no. ChallengeResponseAuthentication is a deprecated alias for this.`, BooleanEnumValue, ), - "KerberosAuthentication": common.NewOption(`Specifies whether the password provided by the user for PasswordAuthentication will be validated through the Kerberos KDC. To use this option, the server needs a Kerberos servtab which allows the verification of the KDC's identity. The default is no.`, + "KerberosAuthentication": NewOption(`Specifies whether the password provided by the user for PasswordAuthentication will be validated through the Kerberos KDC. To use this option, the server needs a Kerberos servtab which allows the verification of the KDC's identity. The default is no.`, BooleanEnumValue, ), - "KerberosGetAFSToken": common.NewOption(`If AFS is active and the user has a Kerberos 5 TGT, attempt to acquire an AFS token before accessing the user's home directory. The default is no.`, + "KerberosGetAFSToken": NewOption(`If AFS is active and the user has a Kerberos 5 TGT, attempt to acquire an AFS token before accessing the user's home directory. The default is no.`, BooleanEnumValue, ), - "KerberosOrLocalPasswd": common.NewOption(`If password authentication through Kerberos fails then the password will be validated via any additional local mechanism such as /etc/passwd. The default is yes.`, + "KerberosOrLocalPasswd": NewOption(`If password authentication through Kerberos fails then the password will be validated via any additional local mechanism such as /etc/passwd. The default is yes.`, BooleanEnumValue, ), - "KerberosTicketCleanup": common.NewOption(`Specifies whether to automatically destroy the user's ticket cache file on logout. The default is yes.`, + "KerberosTicketCleanup": NewOption(`Specifies whether to automatically destroy the user's ticket cache file on logout. The default is yes.`, BooleanEnumValue, ), - "KexAlgorithms": common.NewOption(`Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must be comma-separated. Alternately if the specified list begins with a ‘+’ character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a ‘^’ character, then the specified algorithms will be placed at the head of the default set. The supported algorithms are: + "KexAlgorithms": NewOption(`Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must be comma-separated. Alternately if the specified list begins with a ‘+’ character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a ‘^’ character, then the specified algorithms will be placed at the head of the default set. The supported algorithms are: curve25519-sha256 curve25519-sha256@libssh.org diffie-hellman-group1-sha1 diffie-hellman-group14-sha1 diffie-hellman-group14-sha256 diffie-hellman-group16-sha512 diffie-hellman-group18-sha512 diffie-hellman-group-exchange-sha1 diffie-hellman-group-exchange-sha256 ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 sntrup761x25519-sha512@openssh.com The default is: sntrup761x25519-sha512@openssh.com, curve25519-sha256,curve25519-sha256@libssh.org, ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521, diffie-hellman-group-exchange-sha256, diffie-hellman-group16-sha512,diffie-hellman-group18-sha512, diffie-hellman-group14-sha256 @@ -470,7 +469,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may docvalues.CreateEnumString("sntrup761x25519-sha512@openssh.com"), }), ), - "ListenAddress": common.NewOption(`Specifies the local addresses sshd(8) should listen on. The following forms may be used: + "ListenAddress": NewOption(`Specifies the local addresses sshd(8) should listen on. The following forms may be used: ListenAddress hostname|address [rdomain domain] ListenAddress hostname:port [rdomain domain] ListenAddress IPv4_address:port [rdomain domain] ListenAddress [hostname|address]:port [rdomain domain] The optional rdomain qualifier requests sshd(8) listen in an explicit routing domain. If port is not specified, sshd will listen on the address and all Port options specified. The default is to listen on all local addresses on the current default routing domain. Multiple ListenAddress options are permitted. For more information on routing domains, see rdomain(4).`, docvalues.KeyValueAssignmentValue{ @@ -485,10 +484,10 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may Value: docvalues.NumberValue{Min: &ZERO, Max: &MAX_PORT}, }, ), - "LoginGraceTime": common.NewOption(`The server disconnects after this time if the user has not successfully logged in. If the value is 0, there is no time limit. The default is 120 seconds.`, + "LoginGraceTime": NewOption(`The server disconnects after this time if the user has not successfully logged in. If the value is 0, there is no time limit. The default is 120 seconds.`, TimeFormatValue{}, ), - "LogLevel": common.NewOption(`Gives the verbosity level that is used when logging messages from sshd(8). The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is INFO. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of debugging output. Logging with a DEBUG level violates the privacy of users and is not recommended.`, + "LogLevel": NewOption(`Gives the verbosity level that is used when logging messages from sshd(8). The possible values are: QUIET, FATAL, ERROR, INFO, VERBOSE, DEBUG, DEBUG1, DEBUG2, and DEBUG3. The default is INFO. DEBUG and DEBUG1 are equivalent. DEBUG2 and DEBUG3 each specify higher levels of debugging output. Logging with a DEBUG level violates the privacy of users and is not recommended.`, docvalues.EnumValue{ EnforceValues: true, Values: []docvalues.EnumString{ @@ -504,13 +503,13 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "LogVerbose": common.NewOption(`Specify one or more overrides to LogLevel. An override consists of a pattern lists that matches the source file, function and line number to force detailed logging for. For example, an override pattern of: + "LogVerbose": NewOption(`Specify one or more overrides to LogLevel. An override consists of a pattern lists that matches the source file, function and line number to force detailed logging for. For example, an override pattern of: kex.c:*:1000,*:kex_exchange_identification():*,packet.c:* would enable detailed logging for line 1000 of kex.c, everything in the kex_exchange_identification() function, and all code in the packet.c file. This option is intended for debugging and no overrides are enabled by default.`, docvalues.StringValue{}, ), - "MACs": common.NewOption(`Specifies the available MAC (message authentication code) algorithms. The MAC algorithm is used for data integrity protection. Multiple algorithms must be comma-separated. If the specified list begins with a ‘+’ character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a ‘^’ character, then the specified algorithms will be placed at the head of the default set. + "MACs": NewOption(`Specifies the available MAC (message authentication code) algorithms. The MAC algorithm is used for data integrity protection. Multiple algorithms must be comma-separated. If the specified list begins with a ‘+’ character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a ‘^’ character, then the specified algorithms will be placed at the head of the default set. The algorithms that contain "-etm" calculate the MAC after encryption (encrypt-then-mac). These are considered safer and their use recommended. The supported MACs are: hmac-md5 hmac-md5-96 hmac-sha1 hmac-sha1-96 hmac-sha2-256 hmac-sha2-512 umac-64@openssh.com umac-128@openssh.com hmac-md5-etm@openssh.com hmac-md5-96-etm@openssh.com hmac-sha1-etm@openssh.com hmac-sha1-96-etm@openssh.com hmac-sha2-256-etm@openssh.com hmac-sha2-512-etm@openssh.com umac-64-etm@openssh.com umac-128-etm@openssh.com The default is: @@ -542,31 +541,31 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may // The match patterns may consist of single entries or comma-separated lists and may use the wildcard and negation operators described in the “PATTERNS” section of ssh_config(5). // The patterns in an Address criteria may additionally contain addresses to match in CIDR address/masklen format, such as 192.0.2.0/24 or 2001:db8::/32. Note that the mask length provided must be consistent with the address - it is an error to specify a mask length that is too long for the address or one with bits set in this host portion of the address. For example, 192.0.2.0/33 and 192.0.2.0/8, respectively. // Only a subset of keywords may be used on the lines following a Match keyword. Available keywords are AcceptEnv, AllowAgentForwarding, AllowGroups, AllowStreamLocalForwarding, AllowTcpForwarding, AllowUsers, AuthenticationMethods, AuthorizedKeysCommand, AuthorizedKeysCommandUser, AuthorizedKeysFile, AuthorizedPrincipalsCommand, AuthorizedPrincipalsCommandUser, AuthorizedPrincipalsFile, Banner, CASignatureAlgorithms, ChannelTimeout, ChrootDirectory, ClientAliveCountMax, ClientAliveInterval, DenyGroups, DenyUsers, DisableForwarding, ExposeAuthInfo, ForceCommand, GatewayPorts, GSSAPIAuthentication, HostbasedAcceptedAlgorithms, HostbasedAuthentication, HostbasedUsesNameFromPacketOnly, IgnoreRhosts, Include, IPQoS, KbdInteractiveAuthentication, KerberosAuthentication, LogLevel, MaxAuthTries, MaxSessions, PasswordAuthentication, PermitEmptyPasswords, PermitListen, PermitOpen, PermitRootLogin, PermitTTY, PermitTunnel, PermitUserRC, PubkeyAcceptedAlgorithms, PubkeyAuthentication, PubkeyAuthOptions, RekeyLimit, RevokedKeys, RDomain, SetEnv, StreamLocalBindMask, StreamLocalBindUnlink, TrustedUserCAKeys, UnusedConnectionTimeout, X11DisplayOffset, X11Forwarding and X11UseLocalhost.`, - "MaxAuthTries": common.NewOption(`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.`, + "MaxAuthTries": NewOption(`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.`, docvalues.NumberValue{Min: &ZERO}, ), - "MaxSessions": common.NewOption(`Specifies the maximum number of open shell, login or subsystem (e.g. sftp) sessions permitted per network connection. Multiple sessions may be established by clients that support connection multiplexing. Setting MaxSessions to 1 will effectively disable session multiplexing, whereas setting it to 0 will prevent all shell, login and subsystem sessions while still permitting forwarding. The default is 10.`, + "MaxSessions": NewOption(`Specifies the maximum number of open shell, login or subsystem (e.g. sftp) sessions permitted per network connection. Multiple sessions may be established by clients that support connection multiplexing. Setting MaxSessions to 1 will effectively disable session multiplexing, whereas setting it to 0 will prevent all shell, login and subsystem sessions while still permitting forwarding. The default is 10.`, docvalues.NumberValue{Min: &ZERO}, ), - "MaxStartups": common.NewOption(`Specifies the maximum number of concurrent unauthenticated connections to the SSH daemon. Additional connections will be dropped until authentication succeeds or the LoginGraceTime expires for a connection. The default is 10:30:100. + "MaxStartups": NewOption(`Specifies the maximum number of concurrent unauthenticated connections to the SSH daemon. Additional connections will be dropped until authentication succeeds or the LoginGraceTime expires for a connection. The default is 10:30:100. Alternatively, random early drop can be enabled by specifying the three colon separated values start:rate:full (e.g. "10:30:60"). sshd(8) will refuse connection attempts with a probability of rate/100 (30%) if there are currently start (10) unauthenticated connections. The probability increases linearly and all connection attempts are refused if the number of unauthenticated connections reaches full (60).`, // TODO: Add custom value `SeapartorValue` that takes an array of values and separators docvalues.RegexValue{ Regex: *regexp.MustCompile(`^(\d+):(\d+):(\d+)$`), }, ), - "ModuliFile": common.NewOption(`Specifies the moduli(5) file that contains the Diffie- Hellman groups used for the “diffie-hellman-group-exchange-sha1” and “diffie-hellman-group-exchange-sha256” key exchange methods. The default is /etc/moduli.`, + "ModuliFile": NewOption(`Specifies the moduli(5) file that contains the Diffie- Hellman groups used for the “diffie-hellman-group-exchange-sha1” and “diffie-hellman-group-exchange-sha256” key exchange methods. The default is /etc/moduli.`, docvalues.PathValue{ RequiredType: docvalues.PathTypeFile, }, ), - "PasswordAuthentication": common.NewOption(`Specifies whether password authentication is allowed. The default is yes.`, + "PasswordAuthentication": NewOption(`Specifies whether password authentication is allowed. The default is yes.`, BooleanEnumValue, ), - "PermitEmptyPasswords": common.NewOption(`When password authentication is allowed, it specifies whether the server allows login to accounts with empty password strings. The default is no.`, + "PermitEmptyPasswords": NewOption(`When password authentication is allowed, it specifies whether the server allows login to accounts with empty password strings. The default is no.`, BooleanEnumValue, ), - "PermitListen": common.NewOption(`Specifies the addresses/ports on which a remote TCP port forwarding may listen. The listen specification must be one of the following forms: + "PermitListen": NewOption(`Specifies the addresses/ports on which a remote TCP port forwarding may listen. The listen specification must be one of the following forms: PermitListen port PermitListen host:port Multiple permissions may be specified by separating them with whitespace. An argument of any can be used to remove all restrictions and permit any listen requests. An argument of none can be used to prohibit all listen requests. The host name may contain wildcards as described in the PATTERNS section in ssh_config(5). The wildcard ‘*’ can also be used in place of a port number to allow all ports. By default all port forwarding listen requests are permitted. Note that the GatewayPorts option may further restrict which addresses may be listened on. Note also that ssh(1) will request a listen host of “localhost” if no listen host was specifically requested, and this name is treated differently to explicit localhost addresses of “127.0.0.1” and “::1”.`, docvalues.ArrayValue{ @@ -599,7 +598,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "PermitOpen": common.NewOption(`Specifies the destinations to which TCP port forwarding is permitted. The forwarding specification must be one of the following forms: + "PermitOpen": NewOption(`Specifies the destinations to which TCP port forwarding is permitted. The forwarding specification must be one of the following forms: PermitOpen host:port PermitOpen IPv4_addr:port PermitOpen [IPv6_addr]:port Multiple forwards may be specified by separating them with whitespace. An argument of any can be used to remove all restrictions and permit any forwarding requests. An argument of none can be used to prohibit all forwarding requests. The wildcard ‘*’ can be used for host or port to allow all hosts or ports respectively. Otherwise, no pattern matching or address lookups are performed on supplied names. By default all port forwarding requests are permitted.`, docvalues.ArrayValue{ @@ -645,7 +644,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "PermitRootLogin": common.NewOption(`Specifies whether root can log in using ssh(1). The argument must be yes, prohibit-password, forced-commands-only, or no. The default is prohibit-password. + "PermitRootLogin": NewOption(`Specifies whether root can log in using ssh(1). The argument must be yes, prohibit-password, forced-commands-only, or no. The default is prohibit-password. If this option is set to prohibit-password (or its deprecated alias, without-password), password and keyboard-interactive authentication are disabled for root. If this option is set to forced-commands-only, root login with public key authentication will be allowed, but only if the command option has been specified (which may be useful for taking remote backups even if root login is normally not allowed). All other authentication methods are disabled for root. If this option is set to no, root is not allowed to log in.`, @@ -659,10 +658,10 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "PermitTTY": common.NewOption(`Specifies whether pty(4) allocation is permitted. The default is yes.`, + "PermitTTY": NewOption(`Specifies whether pty(4) allocation is permitted. The default is yes.`, BooleanEnumValue, ), - "PermitTunnel": common.NewOption(`Specifies whether tun(4) device forwarding is allowed. The argument must be yes, point-to-point (layer 3), ethernet (layer 2), or no. Specifying yes permits both point-to-point and ethernet. The default is no. + "PermitTunnel": NewOption(`Specifies whether tun(4) device forwarding is allowed. The argument must be yes, point-to-point (layer 3), ethernet (layer 2), or no. Specifying yes permits both point-to-point and ethernet. The default is no. Independent of this setting, the permissions of the selected tun(4) device must allow access to the user.`, docvalues.EnumValue{ EnforceValues: true, @@ -674,7 +673,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "PermitUserEnvironment": common.NewOption(`Specifies whether ~/.ssh/environment and environment= options in ~/.ssh/authorized_keys are processed by sshd(8). Valid options are yes, no or a pattern-list specifying which environment variable names to accept (for example "LANG,LC_*"). The default is no. Enabling environment processing may enable users to bypass access restrictions in some configurations using mechanisms such as LD_PRELOAD.`, + "PermitUserEnvironment": NewOption(`Specifies whether ~/.ssh/environment and environment= options in ~/.ssh/authorized_keys are processed by sshd(8). Valid options are yes, no or a pattern-list specifying which environment variable names to accept (for example "LANG,LC_*"). The default is no. Enabling environment processing may enable users to bypass access restrictions in some configurations using mechanisms such as LD_PRELOAD.`, docvalues.OrValue{ Values: []docvalues.Value{ docvalues.EnumValue{ @@ -691,10 +690,10 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "PermitUserRC": common.NewOption(`Specifies whether any ~/.ssh/rc file is executed. The default is yes.`, + "PermitUserRC": NewOption(`Specifies whether any ~/.ssh/rc file is executed. The default is yes.`, BooleanEnumValue, ), - "PerSourceMaxStartups": common.NewOption(`Specifies the number of unauthenticated connections allowed from a given source address, or “none” if there is no limit. This limit is applied in addition to MaxStartups, whichever is lower. The default is none.`, + "PerSourceMaxStartups": NewOption(`Specifies the number of unauthenticated connections allowed from a given source address, or “none” if there is no limit. This limit is applied in addition to MaxStartups, whichever is lower. The default is none.`, docvalues.OrValue{ Values: []docvalues.Value{ docvalues.EnumValue{ @@ -711,7 +710,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "PerSourceNetBlockSize": common.NewOption(`Specifies the number of bits of source address that are grouped together for the purposes of applying PerSourceMaxStartups limits. Values for IPv4 and optionally IPv6 may be specified, separated by a colon. The default is 32:128, which means each address is considered individually.`, + "PerSourceNetBlockSize": NewOption(`Specifies the number of bits of source address that are grouped together for the purposes of applying PerSourceMaxStartups limits. Values for IPv4 and optionally IPv6 may be specified, separated by a colon. The default is 32:128, which means each address is considered individually.`, docvalues.KeyValueAssignmentValue{ Separator: ":", ValueIsOptional: false, @@ -719,20 +718,20 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may Value: docvalues.NumberValue{Min: &ZERO}, }, ), - "PidFile": common.NewOption(`Specifies the file that contains the process ID of the SSH daemon, or none to not write one. The default is /var/run/sshd.pid.`, + "PidFile": NewOption(`Specifies the file that contains the process ID of the SSH daemon, or none to not write one. The default is /var/run/sshd.pid.`, docvalues.StringValue{}, ), - "Port": common.NewOption(`Specifies the port number that sshd(8) listens on. The default is 22. Multiple options of this type are permitted. See also ListenAddress.`, + "Port": NewOption(`Specifies the port number that sshd(8) listens on. The default is 22. Multiple options of this type are permitted. See also ListenAddress.`, docvalues.NumberValue{Min: &ZERO, Max: &MAX_PORT}, ), - "PrintLastLog": common.NewOption(`Specifies whether sshd(8) should print the date and time of the last user login when a user logs in interactively. The default is yes.`, + "PrintLastLog": NewOption(`Specifies whether sshd(8) should print the date and time of the last user login when a user logs in interactively. The default is yes.`, BooleanEnumValue, ), - "PrintMotd": common.NewOption(`Specifies whether sshd(8) should print /etc/motd when a user logs in interactively. (On some systems it is also printed by the shell, /etc/profile, or equivalent.) The default is yes.`, + "PrintMotd": NewOption(`Specifies whether sshd(8) should print /etc/motd when a user logs in interactively. (On some systems it is also printed by the shell, /etc/profile, or equivalent.) The default is yes.`, BooleanEnumValue, ), - "PubkeyAcceptedAlgorithms": common.NewOption(`Specifies the signature algorithms that will be accepted for public key authentication as a list of comma- separated patterns. Alternately if the specified list begins with a ‘+’ character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a ‘^’ character, then the specified algorithms will be placed at the head of the default set. The default for this option is: + "PubkeyAcceptedAlgorithms": NewOption(`Specifies the signature algorithms that will be accepted for public key authentication as a list of comma- separated patterns. Alternately if the specified list begins with a ‘+’ character, then the specified algorithms will be appended to the default set instead of replacing them. If the specified list begins with a ‘-’ character, then the specified algorithms (including wildcards) will be removed from the default set instead of replacing them. If the specified list begins with a ‘^’ character, then the specified algorithms will be placed at the head of the default set. The default for this option is: ssh-ed25519-cert-v01@openssh.com, ecdsa-sha2-nistp256-cert-v01@openssh.com, ecdsa-sha2-nistp384-cert-v01@openssh.com, ecdsa-sha2-nistp521-cert-v01@openssh.com, sk-ssh-ed25519-cert-v01@openssh.com, sk-ecdsa-sha2-nistp256-cert-v01@openssh.com, rsa-sha2-512-cert-v01@openssh.com, rsa-sha2-256-cert-v01@openssh.com, ssh-ed25519, ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521, sk-ssh-ed25519@openssh.com, sk-ecdsa-sha2-nistp256@openssh.com, rsa-sha2-512,rsa-sha2-256 The list of available signature algorithms may also be obtained using "ssh -Q PubkeyAcceptedAlgorithms".`, docvalues.CustomValue{ @@ -743,7 +742,7 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "PubkeyAuthOptions": common.NewOption(`Sets one or more public key authentication options. The supported keywords are: none (the default; indicating no additional options are enabled), touch-required and verify-required. + "PubkeyAuthOptions": NewOption(`Sets one or more public key authentication options. The supported keywords are: none (the default; indicating no additional options are enabled), touch-required and verify-required. The touch-required option causes public key authentication using a FIDO authenticator algorithm (i.e. ecdsa-sk or ed25519-sk) to always require the signature to attest that a physically present user explicitly confirmed the authentication (usually by touching the authenticator). By default, sshd(8) requires user presence unless overridden with an authorized_keys option. The touch-required flag disables this override. The verify-required option requires a FIDO key signature attest that the user was verified, e.g. via a PIN. Neither the touch-required or verify-required options have any effect for other, non-FIDO, public key types.`, @@ -759,10 +758,10 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "PubkeyAuthentication": common.NewOption(`Specifies whether public key authentication is allowed. The default is yes.`, + "PubkeyAuthentication": NewOption(`Specifies whether public key authentication is allowed. The default is yes.`, BooleanEnumValue, ), - "RekeyLimit": common.NewOption(`Specifies the maximum amount of data that may be transmitted or received before the session key is renegotiated, optionally followed by a maximum amount of time that may pass before the session key is renegotiated. The first argument is specified in bytes and may have a suffix of ‘K’, ‘M’, or ‘G’ to indicate Kilobytes, Megabytes, or Gigabytes, respectively. The default is between ‘1G’ and ‘4G’, depending on the cipher. The optional second value is specified in seconds and may use any of the units documented in the “TIME FORMATS” section. The default value for RekeyLimit is default none, which means that rekeying is performed after the cipher's default amount of data has been sent or received and no time based rekeying is done.`, + "RekeyLimit": NewOption(`Specifies the maximum amount of data that may be transmitted or received before the session key is renegotiated, optionally followed by a maximum amount of time that may pass before the session key is renegotiated. The first argument is specified in bytes and may have a suffix of ‘K’, ‘M’, or ‘G’ to indicate Kilobytes, Megabytes, or Gigabytes, respectively. The default is between ‘1G’ and ‘4G’, depending on the cipher. The optional second value is specified in seconds and may use any of the units documented in the “TIME FORMATS” section. The default value for RekeyLimit is default none, which means that rekeying is performed after the cipher's default amount of data has been sent or received and no time based rekeying is done.`, docvalues.KeyValueAssignmentValue{ Separator: " ", ValueIsOptional: true, @@ -770,13 +769,13 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may Value: TimeFormatValue{}, }, ), - "RequiredRSASize": common.NewOption(`Specifies the minimum RSA key size (in bits) that sshd(8) will accept. User and host-based authentication keys smaller than this limit will be refused. The default is 1024 bits. Note that this limit may only be raised from the default.`, + "RequiredRSASize": NewOption(`Specifies the minimum RSA key size (in bits) that sshd(8) will accept. User and host-based authentication keys smaller than this limit will be refused. The default is 1024 bits. Note that this limit may only be raised from the default.`, docvalues.NumberValue{Min: &ZERO}, ), - "RevokedKeys": common.NewOption(`Specifies revoked public keys file, or none to not use one. Keys listed in this file will be refused for public key authentication. Note that if this file is not readable, then public key authentication will be refused for all users. Keys may be specified as a text file, listing one public key per line, or as an OpenSSH Key Revocation List (KRL) as generated by ssh-keygen(1). For more information on KRLs, see the KEY REVOCATION LISTS section in ssh-keygen(1).`, + "RevokedKeys": NewOption(`Specifies revoked public keys file, or none to not use one. Keys listed in this file will be refused for public key authentication. Note that if this file is not readable, then public key authentication will be refused for all users. Keys may be specified as a text file, listing one public key per line, or as an OpenSSH Key Revocation List (KRL) as generated by ssh-keygen(1). For more information on KRLs, see the KEY REVOCATION LISTS section in ssh-keygen(1).`, docvalues.StringValue{}, ), - "RDomain": common.NewOption(`Specifies an explicit routing domain that is applied after authentication has completed. The user session, as well as any forwarded or listening IP sockets, will be bound to this rdomain(4). If the routing domain is set to %D, then the domain in which the incoming connection was received will be applied.`, + "RDomain": NewOption(`Specifies an explicit routing domain that is applied after authentication has completed. The user session, as well as any forwarded or listening IP sockets, will be bound to this rdomain(4). If the routing domain is set to %D, then the domain in which the incoming connection was received will be applied.`, docvalues.OrValue{ Values: []docvalues.Value{ docvalues.EnumValue{ @@ -792,13 +791,13 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "SecurityKeyProvider": common.NewOption(`Specifies a path to a library that will be used when loading FIDO authenticator-hosted keys, overriding the default of using the built-in USB HID support.`, + "SecurityKeyProvider": NewOption(`Specifies a path to a library that will be used when loading FIDO authenticator-hosted keys, overriding the default of using the built-in USB HID support.`, docvalues.PathValue{ RequiredType: docvalues.PathTypeFile, }, ), - "SetEnv": common.NewOption(`Specifies one or more environment variables to set in child sessions started by sshd(8) as “NAME=VALUE”. The environment value may be quoted (e.g. if it contains whitespace characters). Environment variables set by SetEnv override the default environment and any variables specified by the user via AcceptEnv or PermitUserEnvironment.`, + "SetEnv": NewOption(`Specifies one or more environment variables to set in child sessions started by sshd(8) as “NAME=VALUE”. The environment value may be quoted (e.g. if it contains whitespace characters). Environment variables set by SetEnv override the default environment and any variables specified by the user via AcceptEnv or PermitUserEnvironment.`, docvalues.ArrayValue{ Separator: " ", DuplicatesExtractor: &SetEnvExtractor, @@ -810,24 +809,24 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "StreamLocalBindMask": common.NewOption(`Sets the octal file creation mode mask (umask) used when creating a Unix-domain socket file for local or remote port forwarding. This option is only used for port forwarding to a Unix-domain socket file. + "StreamLocalBindMask": NewOption(`Sets the octal file creation mode mask (umask) used when creating a Unix-domain socket file for local or remote port forwarding. This option is only used for port forwarding to a Unix-domain socket file. The default value is 0177, which creates a Unix-domain socket file that is readable and writable only by the owner. Note that not all operating systems honor the file mode on Unix-domain socket files.`, docvalues.NumberValue{Min: &ZERO, Max: &MAX_FILE_MODE}, ), - "StreamLocalBindUnlink": common.NewOption(`Specifies whether to remove an existing Unix-domain socket file for local or remote port forwarding before creating a new one. If the socket file already exists and StreamLocalBindUnlink is not enabled, sshd will be unable to forward the port to the Unix-domain socket file. This option is only used for port forwarding to a Unix-domain socket file. + "StreamLocalBindUnlink": NewOption(`Specifies whether to remove an existing Unix-domain socket file for local or remote port forwarding before creating a new one. If the socket file already exists and StreamLocalBindUnlink is not enabled, sshd will be unable to forward the port to the Unix-domain socket file. This option is only used for port forwarding to a Unix-domain socket file. The argument must be yes or no. The default is no.`, BooleanEnumValue, ), - "StrictModes": common.NewOption(`Specifies whether sshd(8) should check file modes and ownership of the user's files and home directory before accepting login. This is normally desirable because novices sometimes accidentally leave their directory or files world-writable. The default is yes. Note that this does not apply to ChrootDirectory, whose permissions and ownership are checked unconditionally.`, + "StrictModes": NewOption(`Specifies whether sshd(8) should check file modes and ownership of the user's files and home directory before accepting login. This is normally desirable because novices sometimes accidentally leave their directory or files world-writable. The default is yes. Note that this does not apply to ChrootDirectory, whose permissions and ownership are checked unconditionally.`, BooleanEnumValue, ), - "Subsystem": common.NewOption(`Configures an external subsystem (e.g. file transfer daemon). Arguments should be a subsystem name and a command (with optional arguments) to execute upon subsystem request. + "Subsystem": NewOption(`Configures an external subsystem (e.g. file transfer daemon). Arguments should be a subsystem name and a command (with optional arguments) to execute upon subsystem request. The command sftp-server implements the SFTP file transfer subsystem. Alternately the name internal-sftp implements an in- process SFTP server. This may simplify configurations using ChrootDirectory to force a different filesystem root on clients. It accepts the same command line arguments as sftp-server and even though it is in- process, settings such as LogLevel or SyslogFacility do not apply to it and must be set explicitly via command line arguments. By default no subsystems are defined.`, docvalues.StringValue{}, ), - "SyslogFacility": common.NewOption(`Gives the facility code that is used when logging messages from sshd(8). The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The default is AUTH.`, + "SyslogFacility": NewOption(`Gives the facility code that is used when logging messages from sshd(8). The possible values are: DAEMON, USER, AUTH, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7. The default is AUTH.`, docvalues.EnumValue{ EnforceValues: true, Values: []docvalues.EnumString{ @@ -845,30 +844,30 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "TCPKeepAlive": common.NewOption(`Specifies whether the system should send TCP keepalive messages to the other side. If they are sent, death of the connection or crash of one of the machines will be properly noticed. However, this means that connections will die if the route is down temporarily, and some people find it annoying. On the other hand, if TCP keepalives are not sent, sessions may hang indefinitely on the server, leaving "ghost" users and consuming server resources. + "TCPKeepAlive": NewOption(`Specifies whether the system should send TCP keepalive messages to the other side. If they are sent, death of the connection or crash of one of the machines will be properly noticed. However, this means that connections will die if the route is down temporarily, and some people find it annoying. On the other hand, if TCP keepalives are not sent, sessions may hang indefinitely on the server, leaving "ghost" users and consuming server resources. The default is yes (to send TCP keepalive messages), and the server will notice if the network goes down or the client host crashes. This avoids infinitely hanging sessions. To disable TCP keepalive messages, the value should be set to no.`, BooleanEnumValue, ), - "TrustedUserCAKeys": common.NewOption(`Specifies a file containing public keys of certificate authorities that are trusted to sign user certificates for authentication, or none to not use one. Keys are listed one per line; empty lines and comments starting with ‘#’ are allowed. If a certificate is presented for authentication and has its signing CA key listed in this file, then it may be used for authentication for any user listed in the certificate's principals list. Note that certificates that lack a list of principals will not be permitted for authentication using TrustedUserCAKeys. For more details on certificates, see the CERTIFICATES section in ssh-keygen(1).`, + "TrustedUserCAKeys": NewOption(`Specifies a file containing public keys of certificate authorities that are trusted to sign user certificates for authentication, or none to not use one. Keys are listed one per line; empty lines and comments starting with ‘#’ are allowed. If a certificate is presented for authentication and has its signing CA key listed in this file, then it may be used for authentication for any user listed in the certificate's principals list. Note that certificates that lack a list of principals will not be permitted for authentication using TrustedUserCAKeys. For more details on certificates, see the CERTIFICATES section in ssh-keygen(1).`, docvalues.StringValue{}, ), - "UnusedConnectionTimeout": common.NewOption(`Specifies whether and how quickly sshd(8) should close client connections with no open channels. Open channels include active shell, command execution or subsystem sessions, connected network, socket, agent or X11 forwardings. Forwarding listeners, such as those from the ssh(1) -R flag, are not considered as open channels and do not prevent the timeout. The timeout value is specified in seconds or may use any of the units documented in the “TIME FORMATS” section. + "UnusedConnectionTimeout": NewOption(`Specifies whether and how quickly sshd(8) should close client connections with no open channels. Open channels include active shell, command execution or subsystem sessions, connected network, socket, agent or X11 forwardings. Forwarding listeners, such as those from the ssh(1) -R flag, are not considered as open channels and do not prevent the timeout. The timeout value is specified in seconds or may use any of the units documented in the “TIME FORMATS” section. Note that this timeout starts when the client connection completes user authentication but before the client has an opportunity to open any channels. Caution should be used when using short timeout values, as they may not provide sufficient time for the client to request and open its channels before terminating the connection. The default none is to never expire connections for having no open channels. This option may be useful in conjunction with ChannelTimeout.`, TimeFormatValue{}, ), - "UseDNS": common.NewOption(`Specifies whether sshd(8) should look up the remote host name, and to check that the resolved host name for the remote IP address maps back to the very same IP address. + "UseDNS": NewOption(`Specifies whether sshd(8) should look up the remote host name, and to check that the resolved host name for the remote IP address maps back to the very same IP address. If this option is set to no (the default) then only addresses and not host names may be used in ~/.ssh/authorized_keys from and sshd_config Match Host directives.`, BooleanEnumValue, ), - "UsePAM": common.NewOption(`Enables the Pluggable Authentication Module interface. If set to yes this will enable PAM authentication using KbdInteractiveAuthentication and PasswordAuthentication in addition to PAM account and session module processing for all authentication types. + "UsePAM": NewOption(`Enables the Pluggable Authentication Module interface. If set to yes this will enable PAM authentication using KbdInteractiveAuthentication and PasswordAuthentication in addition to PAM account and session module processing for all authentication types. Because PAM keyboard-interactive authentication usually serves an equivalent role to password authentication, you should disable either PasswordAuthentication or KbdInteractiveAuthentication. If UsePAM is enabled, you will not be able to run sshd(8) as a non-root user. The default is no.`, BooleanEnumValue, ), - "VersionAddendum": common.NewOption(`Optionally specifies additional text to append to the SSH protocol banner sent by the server upon connection. The default is none.`, + "VersionAddendum": NewOption(`Optionally specifies additional text to append to the SSH protocol banner sent by the server upon connection. The default is none.`, docvalues.OrValue{ Values: []docvalues.Value{ docvalues.EnumValue{ @@ -881,18 +880,18 @@ See PATTERNS in ssh_config(5) for more information on patterns. This keyword may }, }, ), - "X11DisplayOffset": common.NewOption(`Specifies the first display number available for sshd(8)'s X11 forwarding. This prevents sshd from interfering with real X11 servers. The default is 10.`, + "X11DisplayOffset": NewOption(`Specifies the first display number available for sshd(8)'s X11 forwarding. This prevents sshd from interfering with real X11 servers. The default is 10.`, docvalues.NumberValue{Min: &ZERO}, ), - "X11Forwarding": common.NewOption(`Specifies whether X11 forwarding is permitted. The argument must be yes or no. The default is no. + "X11Forwarding": NewOption(`Specifies whether X11 forwarding is permitted. The argument must be yes or no. The default is no. When X11 forwarding is enabled, there may be additional exposure to the server and to client displays if the sshd(8) proxy display is configured to listen on the wildcard address (see X11UseLocalhost), though this is not the default. Additionally, the authentication spoofing and authentication data verification and substitution occur on the client side. The security risk of using X11 forwarding is that the client's X11 display server may be exposed to attack when the SSH client requests forwarding (see the warnings for ForwardX11 in ssh_config(5)). A system administrator may have a stance in which they want to protect clients that may expose themselves to attack by unwittingly requesting X11 forwarding, which can warrant a no setting. Note that disabling X11 forwarding does not prevent users from forwarding X11 traffic, as users can always install their own forwarders.`, BooleanEnumValue, ), - "X11UseLocalhost": common.NewOption(`Specifies whether sshd(8) should bind the X11 forwarding server to the loopback address or to the wildcard address. By default, sshd binds the forwarding server to the loopback address and sets the hostname part of the DISPLAY environment variable to localhost. This prevents remote hosts from connecting to the proxy display. However, some older X11 clients may not function with this configuration. X11UseLocalhost may be set to no to specify that the forwarding server should be bound to the wildcard address. The argument must be yes or no. The default is yes.`, + "X11UseLocalhost": NewOption(`Specifies whether sshd(8) should bind the X11 forwarding server to the loopback address or to the wildcard address. By default, sshd binds the forwarding server to the loopback address and sets the hostname part of the DISPLAY environment variable to localhost. This prevents remote hosts from connecting to the proxy display. However, some older X11 clients may not function with this configuration. X11UseLocalhost may be set to no to specify that the forwarding server should be bound to the wildcard address. The argument must be yes or no. The default is yes.`, BooleanEnumValue, ), - "XAuthLocation": common.NewOption(`Specifies the full pathname of the xauth(1) program, or none to not use one. The default is /usr/X11R6/bin/xauth.`, + "XAuthLocation": NewOption(`Specifies the full pathname of the xauth(1) program, or none to not use one. The default is /usr/X11R6/bin/xauth.`, docvalues.StringValue{}, ), } diff --git a/handlers/openssh/parser.go b/handlers/openssh/parser.go new file mode 100644 index 0000000..21a6219 --- /dev/null +++ b/handlers/openssh/parser.go @@ -0,0 +1,146 @@ +package openssh + +import ( + docvalues "config-lsp/doc-values" + "regexp" + "strings" +) + +type SimpleConfigPosition struct { + Line uint32 +} + +type SimpleConfigLine struct { + Value string + Separator string + Position SimpleConfigPosition +} + +// Get the character positions of [Option End, Separator End, Value End] +func (l SimpleConfigLine) GetCharacterPositions(optionName string) [3]int { + return [3]int{len(optionName), len(optionName + l.Separator), len(optionName + l.Separator + l.Value)} +} + +type SimpleConfigOptions struct { + Separator regexp.Regexp + IgnorePattern regexp.Regexp + // This is the separator that will be used when adding a new line + IdealSeparator string + AvailableOptions *map[string]Option +} + +type SimpleConfigParser struct { + Lines map[string]SimpleConfigLine + Options SimpleConfigOptions +} + +func (p *SimpleConfigParser) AddLine(line string, lineNumber uint32) (string, error) { + var option string + var separator string + var value string + + re := p.Options.Separator + matches := re.FindStringSubmatch(line) + + if len(matches) == 0 { + return "", docvalues.MalformedLineError{} + } + + optionIndex := re.SubexpIndex("OptionName") + if optionIndex == -1 { + return "", docvalues.MalformedLineError{} + } + option = matches[optionIndex] + + if _, exists := (*p.Options.AvailableOptions)[option]; !exists { + return option, docvalues.OptionUnknownError{} + } + + separatorIndex := re.SubexpIndex("Separator") + if separatorIndex == -1 { + return option, docvalues.MalformedLineError{} + } + separator = matches[separatorIndex] + + valueIndex := re.SubexpIndex("Value") + if valueIndex == -1 { + return option, docvalues.MalformedLineError{} + } + value = matches[valueIndex] + + if _, exists := p.Lines[option]; exists { + return option, docvalues.OptionAlreadyExistsError{ + AlreadyLine: p.Lines[option].Position.Line, + } + } + + p.Lines[option] = SimpleConfigLine{ + Value: value, + Separator: separator, + Position: SimpleConfigPosition{ + Line: lineNumber, + }, + } + + return option, nil + +} + +func (p *SimpleConfigParser) ReplaceOption(option string, value string) { + p.Lines[option] = SimpleConfigLine{ + Value: value, + Position: SimpleConfigPosition{ + Line: p.Lines[option].Position.Line, + }, + } +} + +func (p *SimpleConfigParser) RemoveOption(option string) { + delete(p.Lines, option) +} + +func (p *SimpleConfigParser) GetOption(option string) (SimpleConfigLine, error) { + if _, exists := p.Lines[option]; exists { + return p.Lines[option], nil + } + + return SimpleConfigLine{}, docvalues.OptionUnknownError{} +} + +func (p *SimpleConfigParser) ParseFromFile(content string) []docvalues.OptionError { + lines := strings.Split(content, "\n") + errors := make([]docvalues.OptionError, 0) + + for index, line := range lines { + if p.Options.IgnorePattern.MatchString(line) { + continue + } + + option, err := p.AddLine(line, uint32(index)) + + if err != nil { + errors = append(errors, docvalues.OptionError{ + Line: uint32(index), + ProvidedOption: option, + DocError: err, + }) + } + } + + return errors +} + +func (p *SimpleConfigParser) Clear() { + clear(p.Lines) +} + +// TODO: Use better approach: Store an extra array of lines in order; with references to the SimpleConfigLine +func (p *SimpleConfigParser) FindByLineNumber(lineNumber uint32) (string, SimpleConfigLine, error) { + for option, line := range p.Lines { + if line.Position.Line == lineNumber { + return option, line, nil + } + } + + return "", SimpleConfigLine{Value: "", Position: SimpleConfigPosition{Line: 0}}, docvalues.LineNotFoundError{} +} diff --git a/handlers/openssh/shared.go b/handlers/openssh/shared.go index 877485e..779c937 100644 --- a/handlers/openssh/shared.go +++ b/handlers/openssh/shared.go @@ -1,14 +1,13 @@ package openssh import ( - "config-lsp/common" "regexp" ) -func createOpenSSHConfigParser() common.SimpleConfigParser { - return common.SimpleConfigParser{ - Lines: make(map[string]common.SimpleConfigLine), - Options: common.SimpleConfigOptions{ +func createOpenSSHConfigParser() SimpleConfigParser { + return SimpleConfigParser{ + Lines: make(map[string]SimpleConfigLine), + Options: SimpleConfigOptions{ Separator: *regexp.MustCompile(`(?m)^\s*(?P\w+)(?P\s*)(?P.*)\s*$`), IgnorePattern: *regexp.MustCompile(`^(?:#|\s*$)`), IdealSeparator: " ", diff --git a/handlers/openssh/text-document-completion.go b/handlers/openssh/text-document-completion.go index 96f7b67..a6cec50 100644 --- a/handlers/openssh/text-document-completion.go +++ b/handlers/openssh/text-document-completion.go @@ -1,7 +1,6 @@ package openssh import ( - "config-lsp/common" docvalues "config-lsp/doc-values" "errors" @@ -44,7 +43,7 @@ func getRootCompletions() []protocol.CompletionItem { completions[index] = protocol.CompletionItem{ Label: label, - Documentation: common.GetDocumentation(&option), + Documentation: GetDocumentation(&option), InsertText: &insertText, InsertTextFormat: &format, Kind: &kind, From c47ed48d195c79dcb6e49502a651fbf39ce42ce9 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:08:42 +0200 Subject: [PATCH 10/24] fix(aliases): Improve analyzer --- handlers/aliases/analyzer/value_valid.go | 32 ++++++++++++----- handlers/aliases/handlers/completions.go | 36 ++++++++++++------- .../aliases/lsp/text-document-completion.go | 26 +++++++++++--- 3 files changed, 69 insertions(+), 25 deletions(-) diff --git a/handlers/aliases/analyzer/value_valid.go b/handlers/aliases/analyzer/value_valid.go index 59e4f45..bc19351 100644 --- a/handlers/aliases/analyzer/value_valid.go +++ b/handlers/aliases/analyzer/value_valid.go @@ -6,8 +6,9 @@ import ( "config-lsp/handlers/aliases/ast" "config-lsp/handlers/aliases/fetchers" "config-lsp/handlers/aliases/indexes" - "config-lsp/utils" "fmt" + "net/mail" + "path" ers "errors" ) @@ -51,13 +52,6 @@ func analyzeValuesAreValid( for _, value := range entry.Values.Values { newErrors := checkValue(d.Indexes, value) - newErrors = utils.Map( - newErrors, - func(e common.LSPError) common.LSPError { - startPosition := value.GetAliasValue().Location.Start.Character - return e.ShiftCharacter(-startPosition) - }, - ) errors = append(errors, newErrors...) } @@ -82,6 +76,28 @@ func checkValue( Err: ers.New(fmt.Sprintf("User '%s' not found", aliasValue.Value)), }} } + case ast.AliasValueEmail: + emailValue := value.(ast.AliasValueEmail) + + if _, error := mail.ParseAddress(emailValue.Value); error != nil { + return []common.LSPError{{ + Range: emailValue.Location, + Err: ers.New(fmt.Sprintf("This does not seem to be a valid email: %s", error.Error())), + }} + } + case ast.AliasValueFile: + fileValue := value.(ast.AliasValueFile) + + // I'm not sure if the path really needs to be absolute + // The docs say: + // "Append messages to file, specified by its absolute pathname." + // + if !path.IsAbs(fileValue.Value) { + return []common.LSPError{{ + Range: fileValue.Location, + Err: ers.New("This path must be absolute"), + }} + } } return nil } diff --git a/handlers/aliases/handlers/completions.go b/handlers/aliases/handlers/completions.go index 4c2d107..2cb9e07 100644 --- a/handlers/aliases/handlers/completions.go +++ b/handlers/aliases/handlers/completions.go @@ -5,7 +5,6 @@ import ( "config-lsp/handlers/aliases/ast" "config-lsp/handlers/aliases/fetchers" "config-lsp/handlers/aliases/indexes" - "config-lsp/utils" "fmt" protocol "github.com/tliron/glsp/protocol_3_16" @@ -56,7 +55,12 @@ func GetCompletionsForEntry( completions = append(completions, getCommandCompletion()) completions = append(completions, getIncludeCompletion()) - completions = append(completions, getUserCompletions(i, "", 0)...) + completions = append(completions, getUserCompletions( + i, + indexes.NormalizeKey(entry.Key.Value), + "", + 0, + )...) println("la completions etaient", completions) return completions, nil @@ -68,6 +72,7 @@ func GetCompletionsForEntry( return getUserCompletions( i, + indexes.NormalizeKey(entry.Key.Value), userValue.Value, relativeCursor, ), nil @@ -106,20 +111,27 @@ func getIncludeCompletion() protocol.CompletionItem { func getUserCompletions( i *indexes.AliasesIndexes, + excludeKey string, line string, cursor uint32, ) []protocol.CompletionItem { users := fetchers.GetAvailableUserValues(i) kind := protocol.CompletionItemKindValue - return utils.MapMapToSlice( - users, - func(name string, user fetchers.User) protocol.CompletionItem { - return protocol.CompletionItem{ - Label: name, - Kind: &kind, - Documentation: user.Documentation(), - } - }, - ) + + completions := make([]protocol.CompletionItem, 0) + + for name, user := range users { + if name == excludeKey { + continue + } + + completions = append(completions, protocol.CompletionItem{ + Label: name, + Kind: &kind, + Documentation: user.Documentation(), + }) + } + + return completions } diff --git a/handlers/aliases/lsp/text-document-completion.go b/handlers/aliases/lsp/text-document-completion.go index 5795dad..0240c8f 100644 --- a/handlers/aliases/lsp/text-document-completion.go +++ b/handlers/aliases/lsp/text-document-completion.go @@ -27,9 +27,25 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa entry := rawEntry.(*ast.AliasEntry) - return handlers.GetCompletionsForEntry( - cursor, - entry, - d.Indexes, - ) + if entry.Key == nil { + return handlers.GetAliasesCompletions(d.Indexes), nil + } + + if cursor >= entry.Key.Location.Start.Character && cursor <= entry.Key.Location.End.Character { + return handlers.GetAliasesCompletions(d.Indexes), nil + } + + if entry.Separator == nil && cursor > entry.Key.Location.End.Character { + return nil, nil + } + + if cursor > entry.Separator.End.Character { + return handlers.GetCompletionsForEntry( + cursor, + entry, + d.Indexes, + ) + } + + return nil, nil } From 4254e0a8fe550c3782fbaa523fd9e32285c61abe Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 1 Sep 2024 23:47:42 +0200 Subject: [PATCH 11/24] feat(aliases): Add error code value --- handlers/aliases/Aliases.g4 | 20 +- handlers/aliases/analyzer/value_valid.go | 26 + handlers/aliases/ast/listener.go | 77 ++- handlers/aliases/ast/values.go | 15 + handlers/aliases/handlers/completions.go | 15 + handlers/aliases/parser/Aliases.interp | 7 +- handlers/aliases/parser/Aliases.tokens | 32 +- handlers/aliases/parser/AliasesLexer.interp | 8 +- handlers/aliases/parser/AliasesLexer.tokens | 32 +- .../aliases/parser/aliases_base_listener.go | 6 - handlers/aliases/parser/aliases_lexer.go | 80 +-- handlers/aliases/parser/aliases_listener.go | 6 - handlers/aliases/parser/aliases_parser.go | 594 ++++++++---------- 13 files changed, 477 insertions(+), 441 deletions(-) diff --git a/handlers/aliases/Aliases.g4 b/handlers/aliases/Aliases.g4 index 31c54fd..4c598eb 100644 --- a/handlers/aliases/Aliases.g4 +++ b/handlers/aliases/Aliases.g4 @@ -1,7 +1,7 @@ grammar Aliases; lineStatement - : entry SEPARATOR? comment? EOF + : entry EOF ; entry @@ -22,7 +22,7 @@ values ; value - : (user | file | command | include | email) + : (user | file | command | include | email | error) ; user @@ -50,21 +50,25 @@ email ; error - : errorStatus COLON errorCode SEPARATOR errorMessage - ; - -errorStatus - : STRING + : ERROR COLON errorCode? SEPARATOR? errorMessage? ; errorCode - : STRING + : DIGITS ; errorMessage : STRING ; +DIGITS + : [0-9]+ + ; + +ERROR + : 'e' 'r' 'r' 'o' 'r' + ; + SEPARATOR : [ \t]+ ; diff --git a/handlers/aliases/analyzer/value_valid.go b/handlers/aliases/analyzer/value_valid.go index bc19351..1e640bb 100644 --- a/handlers/aliases/analyzer/value_valid.go +++ b/handlers/aliases/analyzer/value_valid.go @@ -9,6 +9,7 @@ import ( "fmt" "net/mail" "path" + "strconv" ers "errors" ) @@ -98,6 +99,31 @@ func checkValue( Err: ers.New("This path must be absolute"), }} } + case ast.AliasValueError: + errorValue := value.(ast.AliasValueError) + + if errorValue.Code == nil { + return []common.LSPError{{ + Range: errorValue.Location, + Err: ers.New("An error code in the form of 4XX or 5XX is required"), + }} + } + + errorCode, err := strconv.Atoi(errorValue.Code.Value) + + if err != nil || (errorCode < 400 || errorCode > 599) { + return []common.LSPError{{ + Range: errorValue.Code.Location, + Err: ers.New("This error code is invalid. It must be in the form of 4XX or 5XX"), + }} + } + + if errorValue.Message == nil || errorValue.Message.Value == "" { + return []common.LSPError{{ + Range: errorValue.Location, + Err: ers.New("An error message is required"), + }} + } } return nil } diff --git a/handlers/aliases/ast/listener.go b/handlers/aliases/ast/listener.go index aee0c7d..74d5835 100644 --- a/handlers/aliases/ast/listener.go +++ b/handlers/aliases/ast/listener.go @@ -8,8 +8,9 @@ import ( ) type aliasesListenerContext struct { - line uint32 - currentIncludeIndex *uint32 + line uint32 + currentIncludeIndex *uint32 + currentErrorValueIndex *uint32 } type aliasesParserListener struct { @@ -176,7 +177,77 @@ func (s *aliasesParserListener) EnterEmail(ctx *parser.EmailContext) { rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) entry := rawEntry.(*AliasEntry) - entry.Values.Values = append(entry.Values.Values, email) + entry.Values.Values = append(entry.Values.Values, &email) +} + +func (s *aliasesParserListener) EnterError(ctx *parser.ErrorContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + errorValue := AliasValueError{ + AliasValue: AliasValue{ + Location: location, + Value: ctx.GetText(), + }, + } + + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + + entry.Values.Values = append(entry.Values.Values, errorValue) + + index := uint32(len(entry.Values.Values) - 1) + s.aliasContext.currentErrorValueIndex = &index +} + +func (s *aliasesParserListener) ExitError(ctx *parser.ErrorContext) { + s.aliasContext.currentErrorValueIndex = nil +} + +// EnterErrorCode is called when production errorCode is entered. +func (s *aliasesParserListener) EnterErrorCode(ctx *parser.ErrorCodeContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + + values := entry.Values.Values + + rawValue := values[*s.aliasContext.currentErrorValueIndex] + value := rawValue.(AliasValueError) + + value.Code = &AliasValueErrorCode{ + AliasValue: AliasValue{ + Location: location, + Value: ctx.GetText(), + }, + } + + values[*s.aliasContext.currentErrorValueIndex] = value +} + +// EnterErrorMessage is called when production errorMessage is entered. +func (s *aliasesParserListener) EnterErrorMessage(ctx *parser.ErrorMessageContext) { + location := common.CharacterRangeFromCtx(ctx.BaseParserRuleContext) + location.ChangeBothLines(s.aliasContext.line) + + rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) + entry := rawEntry.(*AliasEntry) + + values := entry.Values.Values + + rawValue := values[*s.aliasContext.currentErrorValueIndex] + value := rawValue.(AliasValueError) + + value.Message = &AliasValueErrorMessage{ + AliasValue: AliasValue{ + Location: location, + Value: ctx.GetText(), + }, + } + + values[*s.aliasContext.currentErrorValueIndex] = value } func createListener( diff --git a/handlers/aliases/ast/values.go b/handlers/aliases/ast/values.go index 3764755..bae4526 100644 --- a/handlers/aliases/ast/values.go +++ b/handlers/aliases/ast/values.go @@ -90,3 +90,18 @@ func (a AliasValueEmail) CheckIsValid() []common.LSPError { }, ) } + +type AliasValueError struct { + AliasValue + + Code *AliasValueErrorCode + Message *AliasValueErrorMessage +} + +type AliasValueErrorCode struct { + AliasValue +} + +type AliasValueErrorMessage struct { + AliasValue +} diff --git a/handlers/aliases/handlers/completions.go b/handlers/aliases/handlers/completions.go index 2cb9e07..8df8d3f 100644 --- a/handlers/aliases/handlers/completions.go +++ b/handlers/aliases/handlers/completions.go @@ -54,6 +54,7 @@ func GetCompletionsForEntry( if value == nil { completions = append(completions, getCommandCompletion()) completions = append(completions, getIncludeCompletion()) + completions = append(completions, getErrorCompletion()) completions = append(completions, getUserCompletions( i, @@ -109,6 +110,20 @@ func getIncludeCompletion() protocol.CompletionItem { } } +func getErrorCompletion() protocol.CompletionItem { + kind := protocol.CompletionItemKindKeyword + textFormat := protocol.InsertTextFormatSnippet + insertText := "error:" + + return protocol.CompletionItem{ + Label: "error:", + Documentation: "A status code and message to return. The code must be 3 digits, starting 4XX (TempFail) or 5XX (PermFail). The message must be present and can be freely chosen.", + Kind: &kind, + InsertTextFormat: &textFormat, + InsertText: &insertText, + } +} + func getUserCompletions( i *indexes.AliasesIndexes, excludeKey string, diff --git a/handlers/aliases/parser/Aliases.interp b/handlers/aliases/parser/Aliases.interp index 4431f32..3fd0e3e 100644 --- a/handlers/aliases/parser/Aliases.interp +++ b/handlers/aliases/parser/Aliases.interp @@ -1,6 +1,8 @@ token literal names: null null +null +null '@' null '|' @@ -12,6 +14,8 @@ null token symbolic names: null +DIGITS +ERROR SEPARATOR AT INCLUDE @@ -36,10 +40,9 @@ include comment email error -errorStatus errorCode errorMessage atn: -[4, 1, 9, 128, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 1, 0, 1, 0, 3, 0, 35, 8, 0, 1, 0, 3, 0, 38, 8, 0, 1, 0, 1, 0, 1, 1, 3, 1, 43, 8, 1, 1, 1, 1, 1, 3, 1, 47, 8, 1, 1, 1, 1, 1, 3, 1, 51, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 63, 8, 4, 10, 4, 12, 4, 66, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 75, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 82, 8, 7, 10, 7, 12, 7, 85, 9, 7, 1, 7, 3, 7, 88, 8, 7, 1, 8, 1, 8, 3, 8, 92, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 98, 8, 9, 1, 10, 1, 10, 3, 10, 102, 8, 10, 1, 10, 4, 10, 105, 8, 10, 11, 10, 12, 10, 106, 1, 10, 3, 10, 110, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 0, 0, 16, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 0, 0, 128, 0, 32, 1, 0, 0, 0, 2, 42, 1, 0, 0, 0, 4, 54, 1, 0, 0, 0, 6, 56, 1, 0, 0, 0, 8, 64, 1, 0, 0, 0, 10, 74, 1, 0, 0, 0, 12, 76, 1, 0, 0, 0, 14, 78, 1, 0, 0, 0, 16, 89, 1, 0, 0, 0, 18, 93, 1, 0, 0, 0, 20, 99, 1, 0, 0, 0, 22, 111, 1, 0, 0, 0, 24, 115, 1, 0, 0, 0, 26, 121, 1, 0, 0, 0, 28, 123, 1, 0, 0, 0, 30, 125, 1, 0, 0, 0, 32, 34, 3, 2, 1, 0, 33, 35, 5, 1, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 37, 1, 0, 0, 0, 36, 38, 3, 20, 10, 0, 37, 36, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 5, 0, 0, 1, 40, 1, 1, 0, 0, 0, 41, 43, 5, 1, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 46, 3, 6, 3, 0, 45, 47, 5, 1, 0, 0, 46, 45, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 50, 3, 4, 2, 0, 49, 51, 5, 1, 0, 0, 50, 49, 1, 0, 0, 0, 50, 51, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, 53, 3, 8, 4, 0, 53, 3, 1, 0, 0, 0, 54, 55, 5, 5, 0, 0, 55, 5, 1, 0, 0, 0, 56, 57, 5, 9, 0, 0, 57, 7, 1, 0, 0, 0, 58, 59, 3, 10, 5, 0, 59, 60, 5, 6, 0, 0, 60, 61, 5, 1, 0, 0, 61, 63, 1, 0, 0, 0, 62, 58, 1, 0, 0, 0, 63, 66, 1, 0, 0, 0, 64, 62, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 67, 1, 0, 0, 0, 66, 64, 1, 0, 0, 0, 67, 68, 3, 10, 5, 0, 68, 9, 1, 0, 0, 0, 69, 75, 3, 12, 6, 0, 70, 75, 3, 14, 7, 0, 71, 75, 3, 16, 8, 0, 72, 75, 3, 18, 9, 0, 73, 75, 3, 22, 11, 0, 74, 69, 1, 0, 0, 0, 74, 70, 1, 0, 0, 0, 74, 71, 1, 0, 0, 0, 74, 72, 1, 0, 0, 0, 74, 73, 1, 0, 0, 0, 75, 11, 1, 0, 0, 0, 76, 77, 5, 9, 0, 0, 77, 13, 1, 0, 0, 0, 78, 83, 5, 8, 0, 0, 79, 80, 5, 9, 0, 0, 80, 82, 5, 8, 0, 0, 81, 79, 1, 0, 0, 0, 82, 85, 1, 0, 0, 0, 83, 81, 1, 0, 0, 0, 83, 84, 1, 0, 0, 0, 84, 87, 1, 0, 0, 0, 85, 83, 1, 0, 0, 0, 86, 88, 5, 9, 0, 0, 87, 86, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, 15, 1, 0, 0, 0, 89, 91, 5, 4, 0, 0, 90, 92, 5, 9, 0, 0, 91, 90, 1, 0, 0, 0, 91, 92, 1, 0, 0, 0, 92, 17, 1, 0, 0, 0, 93, 94, 5, 5, 0, 0, 94, 95, 5, 3, 0, 0, 95, 97, 5, 5, 0, 0, 96, 98, 3, 14, 7, 0, 97, 96, 1, 0, 0, 0, 97, 98, 1, 0, 0, 0, 98, 19, 1, 0, 0, 0, 99, 104, 5, 7, 0, 0, 100, 102, 5, 1, 0, 0, 101, 100, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 105, 5, 9, 0, 0, 104, 101, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, 106, 104, 1, 0, 0, 0, 106, 107, 1, 0, 0, 0, 107, 109, 1, 0, 0, 0, 108, 110, 5, 1, 0, 0, 109, 108, 1, 0, 0, 0, 109, 110, 1, 0, 0, 0, 110, 21, 1, 0, 0, 0, 111, 112, 5, 9, 0, 0, 112, 113, 5, 2, 0, 0, 113, 114, 5, 9, 0, 0, 114, 23, 1, 0, 0, 0, 115, 116, 3, 26, 13, 0, 116, 117, 5, 5, 0, 0, 117, 118, 3, 28, 14, 0, 118, 119, 5, 1, 0, 0, 119, 120, 3, 30, 15, 0, 120, 25, 1, 0, 0, 0, 121, 122, 5, 9, 0, 0, 122, 27, 1, 0, 0, 0, 123, 124, 5, 9, 0, 0, 124, 29, 1, 0, 0, 0, 125, 126, 5, 9, 0, 0, 126, 31, 1, 0, 0, 0, 14, 34, 37, 42, 46, 50, 64, 74, 83, 87, 91, 97, 101, 106, 109] \ No newline at end of file +[4, 1, 11, 124, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 1, 0, 1, 0, 1, 0, 1, 1, 3, 1, 35, 8, 1, 1, 1, 1, 1, 3, 1, 39, 8, 1, 1, 1, 1, 1, 3, 1, 43, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 55, 8, 4, 10, 4, 12, 4, 58, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 68, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 75, 8, 7, 10, 7, 12, 7, 78, 9, 7, 1, 7, 3, 7, 81, 8, 7, 1, 8, 1, 8, 3, 8, 85, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 91, 8, 9, 1, 10, 1, 10, 3, 10, 95, 8, 10, 1, 10, 4, 10, 98, 8, 10, 11, 10, 12, 10, 99, 1, 10, 3, 10, 103, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 3, 12, 112, 8, 12, 1, 12, 3, 12, 115, 8, 12, 1, 12, 3, 12, 118, 8, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 0, 0, 15, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 0, 0, 127, 0, 30, 1, 0, 0, 0, 2, 34, 1, 0, 0, 0, 4, 46, 1, 0, 0, 0, 6, 48, 1, 0, 0, 0, 8, 56, 1, 0, 0, 0, 10, 67, 1, 0, 0, 0, 12, 69, 1, 0, 0, 0, 14, 71, 1, 0, 0, 0, 16, 82, 1, 0, 0, 0, 18, 86, 1, 0, 0, 0, 20, 92, 1, 0, 0, 0, 22, 104, 1, 0, 0, 0, 24, 108, 1, 0, 0, 0, 26, 119, 1, 0, 0, 0, 28, 121, 1, 0, 0, 0, 30, 31, 3, 2, 1, 0, 31, 32, 5, 0, 0, 1, 32, 1, 1, 0, 0, 0, 33, 35, 5, 3, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 38, 3, 6, 3, 0, 37, 39, 5, 3, 0, 0, 38, 37, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 1, 0, 0, 0, 40, 42, 3, 4, 2, 0, 41, 43, 5, 3, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 45, 3, 8, 4, 0, 45, 3, 1, 0, 0, 0, 46, 47, 5, 7, 0, 0, 47, 5, 1, 0, 0, 0, 48, 49, 5, 11, 0, 0, 49, 7, 1, 0, 0, 0, 50, 51, 3, 10, 5, 0, 51, 52, 5, 8, 0, 0, 52, 53, 5, 3, 0, 0, 53, 55, 1, 0, 0, 0, 54, 50, 1, 0, 0, 0, 55, 58, 1, 0, 0, 0, 56, 54, 1, 0, 0, 0, 56, 57, 1, 0, 0, 0, 57, 59, 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, 60, 9, 1, 0, 0, 0, 61, 68, 3, 12, 6, 0, 62, 68, 3, 14, 7, 0, 63, 68, 3, 16, 8, 0, 64, 68, 3, 18, 9, 0, 65, 68, 3, 22, 11, 0, 66, 68, 3, 24, 12, 0, 67, 61, 1, 0, 0, 0, 67, 62, 1, 0, 0, 0, 67, 63, 1, 0, 0, 0, 67, 64, 1, 0, 0, 0, 67, 65, 1, 0, 0, 0, 67, 66, 1, 0, 0, 0, 68, 11, 1, 0, 0, 0, 69, 70, 5, 11, 0, 0, 70, 13, 1, 0, 0, 0, 71, 76, 5, 10, 0, 0, 72, 73, 5, 11, 0, 0, 73, 75, 5, 10, 0, 0, 74, 72, 1, 0, 0, 0, 75, 78, 1, 0, 0, 0, 76, 74, 1, 0, 0, 0, 76, 77, 1, 0, 0, 0, 77, 80, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, 79, 81, 5, 11, 0, 0, 80, 79, 1, 0, 0, 0, 80, 81, 1, 0, 0, 0, 81, 15, 1, 0, 0, 0, 82, 84, 5, 6, 0, 0, 83, 85, 5, 11, 0, 0, 84, 83, 1, 0, 0, 0, 84, 85, 1, 0, 0, 0, 85, 17, 1, 0, 0, 0, 86, 87, 5, 7, 0, 0, 87, 88, 5, 5, 0, 0, 88, 90, 5, 7, 0, 0, 89, 91, 3, 14, 7, 0, 90, 89, 1, 0, 0, 0, 90, 91, 1, 0, 0, 0, 91, 19, 1, 0, 0, 0, 92, 97, 5, 9, 0, 0, 93, 95, 5, 3, 0, 0, 94, 93, 1, 0, 0, 0, 94, 95, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 98, 5, 11, 0, 0, 97, 94, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 97, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0, 100, 102, 1, 0, 0, 0, 101, 103, 5, 3, 0, 0, 102, 101, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 21, 1, 0, 0, 0, 104, 105, 5, 11, 0, 0, 105, 106, 5, 4, 0, 0, 106, 107, 5, 11, 0, 0, 107, 23, 1, 0, 0, 0, 108, 109, 5, 2, 0, 0, 109, 111, 5, 7, 0, 0, 110, 112, 3, 26, 13, 0, 111, 110, 1, 0, 0, 0, 111, 112, 1, 0, 0, 0, 112, 114, 1, 0, 0, 0, 113, 115, 5, 3, 0, 0, 114, 113, 1, 0, 0, 0, 114, 115, 1, 0, 0, 0, 115, 117, 1, 0, 0, 0, 116, 118, 3, 28, 14, 0, 117, 116, 1, 0, 0, 0, 117, 118, 1, 0, 0, 0, 118, 25, 1, 0, 0, 0, 119, 120, 5, 1, 0, 0, 120, 27, 1, 0, 0, 0, 121, 122, 5, 11, 0, 0, 122, 29, 1, 0, 0, 0, 15, 34, 38, 42, 56, 67, 76, 80, 84, 90, 94, 99, 102, 111, 114, 117] \ No newline at end of file diff --git a/handlers/aliases/parser/Aliases.tokens b/handlers/aliases/parser/Aliases.tokens index 9efc836..e6a860a 100644 --- a/handlers/aliases/parser/Aliases.tokens +++ b/handlers/aliases/parser/Aliases.tokens @@ -1,15 +1,17 @@ -SEPARATOR=1 -AT=2 -INCLUDE=3 -VERTLINE=4 -COLON=5 -COMMA=6 -NUMBER_SIGN=7 -SLASH=8 -STRING=9 -'@'=2 -'|'=4 -':'=5 -','=6 -'#'=7 -'/'=8 +DIGITS=1 +ERROR=2 +SEPARATOR=3 +AT=4 +INCLUDE=5 +VERTLINE=6 +COLON=7 +COMMA=8 +NUMBER_SIGN=9 +SLASH=10 +STRING=11 +'@'=4 +'|'=6 +':'=7 +','=8 +'#'=9 +'/'=10 diff --git a/handlers/aliases/parser/AliasesLexer.interp b/handlers/aliases/parser/AliasesLexer.interp index d626d2b..d426830 100644 --- a/handlers/aliases/parser/AliasesLexer.interp +++ b/handlers/aliases/parser/AliasesLexer.interp @@ -1,6 +1,8 @@ token literal names: null null +null +null '@' null '|' @@ -12,6 +14,8 @@ null token symbolic names: null +DIGITS +ERROR SEPARATOR AT INCLUDE @@ -23,6 +27,8 @@ SLASH STRING rule names: +DIGITS +ERROR SEPARATOR AT INCLUDE @@ -41,4 +47,4 @@ mode names: DEFAULT_MODE atn: -[4, 0, 9, 49, 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, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 1, 0, 4, 0, 21, 8, 0, 11, 0, 12, 0, 22, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 4, 8, 46, 8, 8, 11, 8, 12, 8, 47, 0, 0, 9, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 1, 0, 2, 2, 0, 9, 9, 32, 32, 9, 0, 9, 10, 13, 13, 32, 32, 35, 35, 44, 44, 47, 47, 58, 58, 64, 64, 124, 124, 50, 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, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 1, 20, 1, 0, 0, 0, 3, 24, 1, 0, 0, 0, 5, 26, 1, 0, 0, 0, 7, 34, 1, 0, 0, 0, 9, 36, 1, 0, 0, 0, 11, 38, 1, 0, 0, 0, 13, 40, 1, 0, 0, 0, 15, 42, 1, 0, 0, 0, 17, 45, 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, 2, 1, 0, 0, 0, 24, 25, 5, 64, 0, 0, 25, 4, 1, 0, 0, 0, 26, 27, 5, 105, 0, 0, 27, 28, 5, 110, 0, 0, 28, 29, 5, 99, 0, 0, 29, 30, 5, 108, 0, 0, 30, 31, 5, 117, 0, 0, 31, 32, 5, 100, 0, 0, 32, 33, 5, 101, 0, 0, 33, 6, 1, 0, 0, 0, 34, 35, 5, 124, 0, 0, 35, 8, 1, 0, 0, 0, 36, 37, 5, 58, 0, 0, 37, 10, 1, 0, 0, 0, 38, 39, 5, 44, 0, 0, 39, 12, 1, 0, 0, 0, 40, 41, 5, 35, 0, 0, 41, 14, 1, 0, 0, 0, 42, 43, 5, 47, 0, 0, 43, 16, 1, 0, 0, 0, 44, 46, 8, 1, 0, 0, 45, 44, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, 47, 45, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 18, 1, 0, 0, 0, 3, 0, 22, 47, 0] \ No newline at end of file +[4, 0, 11, 64, 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, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 1, 0, 4, 0, 25, 8, 0, 11, 0, 12, 0, 26, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 4, 2, 36, 8, 2, 11, 2, 12, 2, 37, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 4, 10, 61, 8, 10, 11, 10, 12, 10, 62, 0, 0, 11, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 1, 0, 3, 1, 0, 48, 57, 2, 0, 9, 9, 32, 32, 9, 0, 9, 10, 13, 13, 32, 32, 35, 35, 44, 44, 47, 47, 58, 58, 64, 64, 124, 124, 66, 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, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 1, 24, 1, 0, 0, 0, 3, 28, 1, 0, 0, 0, 5, 35, 1, 0, 0, 0, 7, 39, 1, 0, 0, 0, 9, 41, 1, 0, 0, 0, 11, 49, 1, 0, 0, 0, 13, 51, 1, 0, 0, 0, 15, 53, 1, 0, 0, 0, 17, 55, 1, 0, 0, 0, 19, 57, 1, 0, 0, 0, 21, 60, 1, 0, 0, 0, 23, 25, 7, 0, 0, 0, 24, 23, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 24, 1, 0, 0, 0, 26, 27, 1, 0, 0, 0, 27, 2, 1, 0, 0, 0, 28, 29, 5, 101, 0, 0, 29, 30, 5, 114, 0, 0, 30, 31, 5, 114, 0, 0, 31, 32, 5, 111, 0, 0, 32, 33, 5, 114, 0, 0, 33, 4, 1, 0, 0, 0, 34, 36, 7, 1, 0, 0, 35, 34, 1, 0, 0, 0, 36, 37, 1, 0, 0, 0, 37, 35, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 6, 1, 0, 0, 0, 39, 40, 5, 64, 0, 0, 40, 8, 1, 0, 0, 0, 41, 42, 5, 105, 0, 0, 42, 43, 5, 110, 0, 0, 43, 44, 5, 99, 0, 0, 44, 45, 5, 108, 0, 0, 45, 46, 5, 117, 0, 0, 46, 47, 5, 100, 0, 0, 47, 48, 5, 101, 0, 0, 48, 10, 1, 0, 0, 0, 49, 50, 5, 124, 0, 0, 50, 12, 1, 0, 0, 0, 51, 52, 5, 58, 0, 0, 52, 14, 1, 0, 0, 0, 53, 54, 5, 44, 0, 0, 54, 16, 1, 0, 0, 0, 55, 56, 5, 35, 0, 0, 56, 18, 1, 0, 0, 0, 57, 58, 5, 47, 0, 0, 58, 20, 1, 0, 0, 0, 59, 61, 8, 2, 0, 0, 60, 59, 1, 0, 0, 0, 61, 62, 1, 0, 0, 0, 62, 60, 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, 63, 22, 1, 0, 0, 0, 4, 0, 26, 37, 62, 0] \ No newline at end of file diff --git a/handlers/aliases/parser/AliasesLexer.tokens b/handlers/aliases/parser/AliasesLexer.tokens index 9efc836..e6a860a 100644 --- a/handlers/aliases/parser/AliasesLexer.tokens +++ b/handlers/aliases/parser/AliasesLexer.tokens @@ -1,15 +1,17 @@ -SEPARATOR=1 -AT=2 -INCLUDE=3 -VERTLINE=4 -COLON=5 -COMMA=6 -NUMBER_SIGN=7 -SLASH=8 -STRING=9 -'@'=2 -'|'=4 -':'=5 -','=6 -'#'=7 -'/'=8 +DIGITS=1 +ERROR=2 +SEPARATOR=3 +AT=4 +INCLUDE=5 +VERTLINE=6 +COLON=7 +COMMA=8 +NUMBER_SIGN=9 +SLASH=10 +STRING=11 +'@'=4 +'|'=6 +':'=7 +','=8 +'#'=9 +'/'=10 diff --git a/handlers/aliases/parser/aliases_base_listener.go b/handlers/aliases/parser/aliases_base_listener.go index c93be05..6818fb1 100644 --- a/handlers/aliases/parser/aliases_base_listener.go +++ b/handlers/aliases/parser/aliases_base_listener.go @@ -99,12 +99,6 @@ func (s *BaseAliasesListener) EnterError(ctx *ErrorContext) {} // ExitError is called when production error is exited. func (s *BaseAliasesListener) ExitError(ctx *ErrorContext) {} -// EnterErrorStatus is called when production errorStatus is entered. -func (s *BaseAliasesListener) EnterErrorStatus(ctx *ErrorStatusContext) {} - -// ExitErrorStatus is called when production errorStatus is exited. -func (s *BaseAliasesListener) ExitErrorStatus(ctx *ErrorStatusContext) {} - // EnterErrorCode is called when production errorCode is entered. func (s *BaseAliasesListener) EnterErrorCode(ctx *ErrorCodeContext) {} diff --git a/handlers/aliases/parser/aliases_lexer.go b/handlers/aliases/parser/aliases_lexer.go index dd72d4b..3c0522b 100644 --- a/handlers/aliases/parser/aliases_lexer.go +++ b/handlers/aliases/parser/aliases_lexer.go @@ -43,40 +43,46 @@ func aliaseslexerLexerInit() { "DEFAULT_MODE", } staticData.LiteralNames = []string{ - "", "", "'@'", "", "'|'", "':'", "','", "'#'", "'/'", + "", "", "", "", "'@'", "", "'|'", "':'", "','", "'#'", "'/'", } staticData.SymbolicNames = []string{ - "", "SEPARATOR", "AT", "INCLUDE", "VERTLINE", "COLON", "COMMA", "NUMBER_SIGN", - "SLASH", "STRING", + "", "DIGITS", "ERROR", "SEPARATOR", "AT", "INCLUDE", "VERTLINE", "COLON", + "COMMA", "NUMBER_SIGN", "SLASH", "STRING", } staticData.RuleNames = []string{ - "SEPARATOR", "AT", "INCLUDE", "VERTLINE", "COLON", "COMMA", "NUMBER_SIGN", - "SLASH", "STRING", + "DIGITS", "ERROR", "SEPARATOR", "AT", "INCLUDE", "VERTLINE", "COLON", + "COMMA", "NUMBER_SIGN", "SLASH", "STRING", } staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.serializedATN = []int32{ - 4, 0, 9, 49, 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, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 1, 0, 4, 0, 21, - 8, 0, 11, 0, 12, 0, 22, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, - 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, - 1, 8, 4, 8, 46, 8, 8, 11, 8, 12, 8, 47, 0, 0, 9, 1, 1, 3, 2, 5, 3, 7, 4, - 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 1, 0, 2, 2, 0, 9, 9, 32, 32, 9, 0, 9, - 10, 13, 13, 32, 32, 35, 35, 44, 44, 47, 47, 58, 58, 64, 64, 124, 124, 50, - 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, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, - 0, 0, 17, 1, 0, 0, 0, 1, 20, 1, 0, 0, 0, 3, 24, 1, 0, 0, 0, 5, 26, 1, 0, - 0, 0, 7, 34, 1, 0, 0, 0, 9, 36, 1, 0, 0, 0, 11, 38, 1, 0, 0, 0, 13, 40, - 1, 0, 0, 0, 15, 42, 1, 0, 0, 0, 17, 45, 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, 2, 1, 0, 0, 0, 24, 25, 5, 64, 0, 0, 25, 4, 1, 0, 0, 0, 26, - 27, 5, 105, 0, 0, 27, 28, 5, 110, 0, 0, 28, 29, 5, 99, 0, 0, 29, 30, 5, - 108, 0, 0, 30, 31, 5, 117, 0, 0, 31, 32, 5, 100, 0, 0, 32, 33, 5, 101, - 0, 0, 33, 6, 1, 0, 0, 0, 34, 35, 5, 124, 0, 0, 35, 8, 1, 0, 0, 0, 36, 37, - 5, 58, 0, 0, 37, 10, 1, 0, 0, 0, 38, 39, 5, 44, 0, 0, 39, 12, 1, 0, 0, - 0, 40, 41, 5, 35, 0, 0, 41, 14, 1, 0, 0, 0, 42, 43, 5, 47, 0, 0, 43, 16, - 1, 0, 0, 0, 44, 46, 8, 1, 0, 0, 45, 44, 1, 0, 0, 0, 46, 47, 1, 0, 0, 0, - 47, 45, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 18, 1, 0, 0, 0, 3, 0, 22, 47, - 0, + 4, 0, 11, 64, 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, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, + 10, 7, 10, 1, 0, 4, 0, 25, 8, 0, 11, 0, 12, 0, 26, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 2, 4, 2, 36, 8, 2, 11, 2, 12, 2, 37, 1, 3, 1, 3, 1, 4, + 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, + 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 4, 10, 61, 8, 10, 11, 10, 12, 10, + 62, 0, 0, 11, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, + 19, 10, 21, 11, 1, 0, 3, 1, 0, 48, 57, 2, 0, 9, 9, 32, 32, 9, 0, 9, 10, + 13, 13, 32, 32, 35, 35, 44, 44, 47, 47, 58, 58, 64, 64, 124, 124, 66, 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, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, + 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 1, 24, 1, 0, 0, + 0, 3, 28, 1, 0, 0, 0, 5, 35, 1, 0, 0, 0, 7, 39, 1, 0, 0, 0, 9, 41, 1, 0, + 0, 0, 11, 49, 1, 0, 0, 0, 13, 51, 1, 0, 0, 0, 15, 53, 1, 0, 0, 0, 17, 55, + 1, 0, 0, 0, 19, 57, 1, 0, 0, 0, 21, 60, 1, 0, 0, 0, 23, 25, 7, 0, 0, 0, + 24, 23, 1, 0, 0, 0, 25, 26, 1, 0, 0, 0, 26, 24, 1, 0, 0, 0, 26, 27, 1, + 0, 0, 0, 27, 2, 1, 0, 0, 0, 28, 29, 5, 101, 0, 0, 29, 30, 5, 114, 0, 0, + 30, 31, 5, 114, 0, 0, 31, 32, 5, 111, 0, 0, 32, 33, 5, 114, 0, 0, 33, 4, + 1, 0, 0, 0, 34, 36, 7, 1, 0, 0, 35, 34, 1, 0, 0, 0, 36, 37, 1, 0, 0, 0, + 37, 35, 1, 0, 0, 0, 37, 38, 1, 0, 0, 0, 38, 6, 1, 0, 0, 0, 39, 40, 5, 64, + 0, 0, 40, 8, 1, 0, 0, 0, 41, 42, 5, 105, 0, 0, 42, 43, 5, 110, 0, 0, 43, + 44, 5, 99, 0, 0, 44, 45, 5, 108, 0, 0, 45, 46, 5, 117, 0, 0, 46, 47, 5, + 100, 0, 0, 47, 48, 5, 101, 0, 0, 48, 10, 1, 0, 0, 0, 49, 50, 5, 124, 0, + 0, 50, 12, 1, 0, 0, 0, 51, 52, 5, 58, 0, 0, 52, 14, 1, 0, 0, 0, 53, 54, + 5, 44, 0, 0, 54, 16, 1, 0, 0, 0, 55, 56, 5, 35, 0, 0, 56, 18, 1, 0, 0, + 0, 57, 58, 5, 47, 0, 0, 58, 20, 1, 0, 0, 0, 59, 61, 8, 2, 0, 0, 60, 59, + 1, 0, 0, 0, 61, 62, 1, 0, 0, 0, 62, 60, 1, 0, 0, 0, 62, 63, 1, 0, 0, 0, + 63, 22, 1, 0, 0, 0, 4, 0, 26, 37, 62, 0, } deserializer := antlr.NewATNDeserializer(nil) staticData.atn = deserializer.Deserialize(staticData.serializedATN) @@ -117,13 +123,15 @@ func NewAliasesLexer(input antlr.CharStream) *AliasesLexer { // AliasesLexer tokens. const ( - AliasesLexerSEPARATOR = 1 - AliasesLexerAT = 2 - AliasesLexerINCLUDE = 3 - AliasesLexerVERTLINE = 4 - AliasesLexerCOLON = 5 - AliasesLexerCOMMA = 6 - AliasesLexerNUMBER_SIGN = 7 - AliasesLexerSLASH = 8 - AliasesLexerSTRING = 9 + AliasesLexerDIGITS = 1 + AliasesLexerERROR = 2 + AliasesLexerSEPARATOR = 3 + AliasesLexerAT = 4 + AliasesLexerINCLUDE = 5 + AliasesLexerVERTLINE = 6 + AliasesLexerCOLON = 7 + AliasesLexerCOMMA = 8 + AliasesLexerNUMBER_SIGN = 9 + AliasesLexerSLASH = 10 + AliasesLexerSTRING = 11 ) diff --git a/handlers/aliases/parser/aliases_listener.go b/handlers/aliases/parser/aliases_listener.go index db833c1..69e142a 100644 --- a/handlers/aliases/parser/aliases_listener.go +++ b/handlers/aliases/parser/aliases_listener.go @@ -47,9 +47,6 @@ type AliasesListener interface { // EnterError is called when entering the error production. EnterError(c *ErrorContext) - // EnterErrorStatus is called when entering the errorStatus production. - EnterErrorStatus(c *ErrorStatusContext) - // EnterErrorCode is called when entering the errorCode production. EnterErrorCode(c *ErrorCodeContext) @@ -95,9 +92,6 @@ type AliasesListener interface { // ExitError is called when exiting the error production. ExitError(c *ErrorContext) - // ExitErrorStatus is called when exiting the errorStatus production. - ExitErrorStatus(c *ErrorStatusContext) - // ExitErrorCode is called when exiting the errorCode production. ExitErrorCode(c *ErrorCodeContext) diff --git a/handlers/aliases/parser/aliases_parser.go b/handlers/aliases/parser/aliases_parser.go index 3708a9b..d5030d8 100644 --- a/handlers/aliases/parser/aliases_parser.go +++ b/handlers/aliases/parser/aliases_parser.go @@ -33,70 +33,70 @@ var AliasesParserStaticData struct { func aliasesParserInit() { staticData := &AliasesParserStaticData staticData.LiteralNames = []string{ - "", "", "'@'", "", "'|'", "':'", "','", "'#'", "'/'", + "", "", "", "", "'@'", "", "'|'", "':'", "','", "'#'", "'/'", } staticData.SymbolicNames = []string{ - "", "SEPARATOR", "AT", "INCLUDE", "VERTLINE", "COLON", "COMMA", "NUMBER_SIGN", - "SLASH", "STRING", + "", "DIGITS", "ERROR", "SEPARATOR", "AT", "INCLUDE", "VERTLINE", "COLON", + "COMMA", "NUMBER_SIGN", "SLASH", "STRING", } staticData.RuleNames = []string{ "lineStatement", "entry", "separator", "key", "values", "value", "user", - "file", "command", "include", "comment", "email", "error", "errorStatus", - "errorCode", "errorMessage", + "file", "command", "include", "comment", "email", "error", "errorCode", + "errorMessage", } staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.serializedATN = []int32{ - 4, 1, 9, 128, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, + 4, 1, 11, 124, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, - 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, - 1, 0, 1, 0, 3, 0, 35, 8, 0, 1, 0, 3, 0, 38, 8, 0, 1, 0, 1, 0, 1, 1, 3, - 1, 43, 8, 1, 1, 1, 1, 1, 3, 1, 47, 8, 1, 1, 1, 1, 1, 3, 1, 51, 8, 1, 1, - 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 63, 8, 4, - 10, 4, 12, 4, 66, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, - 75, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 82, 8, 7, 10, 7, 12, 7, 85, - 9, 7, 1, 7, 3, 7, 88, 8, 7, 1, 8, 1, 8, 3, 8, 92, 8, 8, 1, 9, 1, 9, 1, - 9, 1, 9, 3, 9, 98, 8, 9, 1, 10, 1, 10, 3, 10, 102, 8, 10, 1, 10, 4, 10, - 105, 8, 10, 11, 10, 12, 10, 106, 1, 10, 3, 10, 110, 8, 10, 1, 11, 1, 11, - 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1, 13, 1, - 14, 1, 14, 1, 15, 1, 15, 1, 15, 0, 0, 16, 0, 2, 4, 6, 8, 10, 12, 14, 16, - 18, 20, 22, 24, 26, 28, 30, 0, 0, 128, 0, 32, 1, 0, 0, 0, 2, 42, 1, 0, - 0, 0, 4, 54, 1, 0, 0, 0, 6, 56, 1, 0, 0, 0, 8, 64, 1, 0, 0, 0, 10, 74, - 1, 0, 0, 0, 12, 76, 1, 0, 0, 0, 14, 78, 1, 0, 0, 0, 16, 89, 1, 0, 0, 0, - 18, 93, 1, 0, 0, 0, 20, 99, 1, 0, 0, 0, 22, 111, 1, 0, 0, 0, 24, 115, 1, - 0, 0, 0, 26, 121, 1, 0, 0, 0, 28, 123, 1, 0, 0, 0, 30, 125, 1, 0, 0, 0, - 32, 34, 3, 2, 1, 0, 33, 35, 5, 1, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, - 0, 0, 0, 35, 37, 1, 0, 0, 0, 36, 38, 3, 20, 10, 0, 37, 36, 1, 0, 0, 0, - 37, 38, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 5, 0, 0, 1, 40, 1, 1, 0, - 0, 0, 41, 43, 5, 1, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, - 1, 0, 0, 0, 44, 46, 3, 6, 3, 0, 45, 47, 5, 1, 0, 0, 46, 45, 1, 0, 0, 0, - 46, 47, 1, 0, 0, 0, 47, 48, 1, 0, 0, 0, 48, 50, 3, 4, 2, 0, 49, 51, 5, - 1, 0, 0, 50, 49, 1, 0, 0, 0, 50, 51, 1, 0, 0, 0, 51, 52, 1, 0, 0, 0, 52, - 53, 3, 8, 4, 0, 53, 3, 1, 0, 0, 0, 54, 55, 5, 5, 0, 0, 55, 5, 1, 0, 0, - 0, 56, 57, 5, 9, 0, 0, 57, 7, 1, 0, 0, 0, 58, 59, 3, 10, 5, 0, 59, 60, - 5, 6, 0, 0, 60, 61, 5, 1, 0, 0, 61, 63, 1, 0, 0, 0, 62, 58, 1, 0, 0, 0, - 63, 66, 1, 0, 0, 0, 64, 62, 1, 0, 0, 0, 64, 65, 1, 0, 0, 0, 65, 67, 1, - 0, 0, 0, 66, 64, 1, 0, 0, 0, 67, 68, 3, 10, 5, 0, 68, 9, 1, 0, 0, 0, 69, - 75, 3, 12, 6, 0, 70, 75, 3, 14, 7, 0, 71, 75, 3, 16, 8, 0, 72, 75, 3, 18, - 9, 0, 73, 75, 3, 22, 11, 0, 74, 69, 1, 0, 0, 0, 74, 70, 1, 0, 0, 0, 74, - 71, 1, 0, 0, 0, 74, 72, 1, 0, 0, 0, 74, 73, 1, 0, 0, 0, 75, 11, 1, 0, 0, - 0, 76, 77, 5, 9, 0, 0, 77, 13, 1, 0, 0, 0, 78, 83, 5, 8, 0, 0, 79, 80, - 5, 9, 0, 0, 80, 82, 5, 8, 0, 0, 81, 79, 1, 0, 0, 0, 82, 85, 1, 0, 0, 0, - 83, 81, 1, 0, 0, 0, 83, 84, 1, 0, 0, 0, 84, 87, 1, 0, 0, 0, 85, 83, 1, - 0, 0, 0, 86, 88, 5, 9, 0, 0, 87, 86, 1, 0, 0, 0, 87, 88, 1, 0, 0, 0, 88, - 15, 1, 0, 0, 0, 89, 91, 5, 4, 0, 0, 90, 92, 5, 9, 0, 0, 91, 90, 1, 0, 0, - 0, 91, 92, 1, 0, 0, 0, 92, 17, 1, 0, 0, 0, 93, 94, 5, 5, 0, 0, 94, 95, - 5, 3, 0, 0, 95, 97, 5, 5, 0, 0, 96, 98, 3, 14, 7, 0, 97, 96, 1, 0, 0, 0, - 97, 98, 1, 0, 0, 0, 98, 19, 1, 0, 0, 0, 99, 104, 5, 7, 0, 0, 100, 102, - 5, 1, 0, 0, 101, 100, 1, 0, 0, 0, 101, 102, 1, 0, 0, 0, 102, 103, 1, 0, - 0, 0, 103, 105, 5, 9, 0, 0, 104, 101, 1, 0, 0, 0, 105, 106, 1, 0, 0, 0, - 106, 104, 1, 0, 0, 0, 106, 107, 1, 0, 0, 0, 107, 109, 1, 0, 0, 0, 108, - 110, 5, 1, 0, 0, 109, 108, 1, 0, 0, 0, 109, 110, 1, 0, 0, 0, 110, 21, 1, - 0, 0, 0, 111, 112, 5, 9, 0, 0, 112, 113, 5, 2, 0, 0, 113, 114, 5, 9, 0, - 0, 114, 23, 1, 0, 0, 0, 115, 116, 3, 26, 13, 0, 116, 117, 5, 5, 0, 0, 117, - 118, 3, 28, 14, 0, 118, 119, 5, 1, 0, 0, 119, 120, 3, 30, 15, 0, 120, 25, - 1, 0, 0, 0, 121, 122, 5, 9, 0, 0, 122, 27, 1, 0, 0, 0, 123, 124, 5, 9, - 0, 0, 124, 29, 1, 0, 0, 0, 125, 126, 5, 9, 0, 0, 126, 31, 1, 0, 0, 0, 14, - 34, 37, 42, 46, 50, 64, 74, 83, 87, 91, 97, 101, 106, 109, + 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 1, 0, 1, 0, + 1, 0, 1, 1, 3, 1, 35, 8, 1, 1, 1, 1, 1, 3, 1, 39, 8, 1, 1, 1, 1, 1, 3, + 1, 43, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, + 5, 4, 55, 8, 4, 10, 4, 12, 4, 58, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, + 5, 1, 5, 1, 5, 3, 5, 68, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 75, + 8, 7, 10, 7, 12, 7, 78, 9, 7, 1, 7, 3, 7, 81, 8, 7, 1, 8, 1, 8, 3, 8, 85, + 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 91, 8, 9, 1, 10, 1, 10, 3, 10, 95, + 8, 10, 1, 10, 4, 10, 98, 8, 10, 11, 10, 12, 10, 99, 1, 10, 3, 10, 103, + 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 3, 12, 112, 8, + 12, 1, 12, 3, 12, 115, 8, 12, 1, 12, 3, 12, 118, 8, 12, 1, 13, 1, 13, 1, + 14, 1, 14, 1, 14, 0, 0, 15, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, + 24, 26, 28, 0, 0, 127, 0, 30, 1, 0, 0, 0, 2, 34, 1, 0, 0, 0, 4, 46, 1, + 0, 0, 0, 6, 48, 1, 0, 0, 0, 8, 56, 1, 0, 0, 0, 10, 67, 1, 0, 0, 0, 12, + 69, 1, 0, 0, 0, 14, 71, 1, 0, 0, 0, 16, 82, 1, 0, 0, 0, 18, 86, 1, 0, 0, + 0, 20, 92, 1, 0, 0, 0, 22, 104, 1, 0, 0, 0, 24, 108, 1, 0, 0, 0, 26, 119, + 1, 0, 0, 0, 28, 121, 1, 0, 0, 0, 30, 31, 3, 2, 1, 0, 31, 32, 5, 0, 0, 1, + 32, 1, 1, 0, 0, 0, 33, 35, 5, 3, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, + 0, 0, 35, 36, 1, 0, 0, 0, 36, 38, 3, 6, 3, 0, 37, 39, 5, 3, 0, 0, 38, 37, + 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 1, 0, 0, 0, 40, 42, 3, 4, 2, 0, + 41, 43, 5, 3, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, + 0, 0, 0, 44, 45, 3, 8, 4, 0, 45, 3, 1, 0, 0, 0, 46, 47, 5, 7, 0, 0, 47, + 5, 1, 0, 0, 0, 48, 49, 5, 11, 0, 0, 49, 7, 1, 0, 0, 0, 50, 51, 3, 10, 5, + 0, 51, 52, 5, 8, 0, 0, 52, 53, 5, 3, 0, 0, 53, 55, 1, 0, 0, 0, 54, 50, + 1, 0, 0, 0, 55, 58, 1, 0, 0, 0, 56, 54, 1, 0, 0, 0, 56, 57, 1, 0, 0, 0, + 57, 59, 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, 60, 9, 1, + 0, 0, 0, 61, 68, 3, 12, 6, 0, 62, 68, 3, 14, 7, 0, 63, 68, 3, 16, 8, 0, + 64, 68, 3, 18, 9, 0, 65, 68, 3, 22, 11, 0, 66, 68, 3, 24, 12, 0, 67, 61, + 1, 0, 0, 0, 67, 62, 1, 0, 0, 0, 67, 63, 1, 0, 0, 0, 67, 64, 1, 0, 0, 0, + 67, 65, 1, 0, 0, 0, 67, 66, 1, 0, 0, 0, 68, 11, 1, 0, 0, 0, 69, 70, 5, + 11, 0, 0, 70, 13, 1, 0, 0, 0, 71, 76, 5, 10, 0, 0, 72, 73, 5, 11, 0, 0, + 73, 75, 5, 10, 0, 0, 74, 72, 1, 0, 0, 0, 75, 78, 1, 0, 0, 0, 76, 74, 1, + 0, 0, 0, 76, 77, 1, 0, 0, 0, 77, 80, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, 79, + 81, 5, 11, 0, 0, 80, 79, 1, 0, 0, 0, 80, 81, 1, 0, 0, 0, 81, 15, 1, 0, + 0, 0, 82, 84, 5, 6, 0, 0, 83, 85, 5, 11, 0, 0, 84, 83, 1, 0, 0, 0, 84, + 85, 1, 0, 0, 0, 85, 17, 1, 0, 0, 0, 86, 87, 5, 7, 0, 0, 87, 88, 5, 5, 0, + 0, 88, 90, 5, 7, 0, 0, 89, 91, 3, 14, 7, 0, 90, 89, 1, 0, 0, 0, 90, 91, + 1, 0, 0, 0, 91, 19, 1, 0, 0, 0, 92, 97, 5, 9, 0, 0, 93, 95, 5, 3, 0, 0, + 94, 93, 1, 0, 0, 0, 94, 95, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 98, 5, + 11, 0, 0, 97, 94, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 97, 1, 0, 0, 0, 99, + 100, 1, 0, 0, 0, 100, 102, 1, 0, 0, 0, 101, 103, 5, 3, 0, 0, 102, 101, + 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 21, 1, 0, 0, 0, 104, 105, 5, 11, + 0, 0, 105, 106, 5, 4, 0, 0, 106, 107, 5, 11, 0, 0, 107, 23, 1, 0, 0, 0, + 108, 109, 5, 2, 0, 0, 109, 111, 5, 7, 0, 0, 110, 112, 3, 26, 13, 0, 111, + 110, 1, 0, 0, 0, 111, 112, 1, 0, 0, 0, 112, 114, 1, 0, 0, 0, 113, 115, + 5, 3, 0, 0, 114, 113, 1, 0, 0, 0, 114, 115, 1, 0, 0, 0, 115, 117, 1, 0, + 0, 0, 116, 118, 3, 28, 14, 0, 117, 116, 1, 0, 0, 0, 117, 118, 1, 0, 0, + 0, 118, 25, 1, 0, 0, 0, 119, 120, 5, 1, 0, 0, 120, 27, 1, 0, 0, 0, 121, + 122, 5, 11, 0, 0, 122, 29, 1, 0, 0, 0, 15, 34, 38, 42, 56, 67, 76, 80, + 84, 90, 94, 99, 102, 111, 114, 117, } deserializer := antlr.NewATNDeserializer(nil) staticData.atn = deserializer.Deserialize(staticData.serializedATN) @@ -135,15 +135,17 @@ func NewAliasesParser(input antlr.TokenStream) *AliasesParser { // AliasesParser tokens. const ( AliasesParserEOF = antlr.TokenEOF - AliasesParserSEPARATOR = 1 - AliasesParserAT = 2 - AliasesParserINCLUDE = 3 - AliasesParserVERTLINE = 4 - AliasesParserCOLON = 5 - AliasesParserCOMMA = 6 - AliasesParserNUMBER_SIGN = 7 - AliasesParserSLASH = 8 - AliasesParserSTRING = 9 + AliasesParserDIGITS = 1 + AliasesParserERROR = 2 + AliasesParserSEPARATOR = 3 + AliasesParserAT = 4 + AliasesParserINCLUDE = 5 + AliasesParserVERTLINE = 6 + AliasesParserCOLON = 7 + AliasesParserCOMMA = 8 + AliasesParserNUMBER_SIGN = 9 + AliasesParserSLASH = 10 + AliasesParserSTRING = 11 ) // AliasesParser rules. @@ -161,9 +163,8 @@ const ( AliasesParserRULE_comment = 10 AliasesParserRULE_email = 11 AliasesParserRULE_error = 12 - AliasesParserRULE_errorStatus = 13 - AliasesParserRULE_errorCode = 14 - AliasesParserRULE_errorMessage = 15 + AliasesParserRULE_errorCode = 13 + AliasesParserRULE_errorMessage = 14 ) // ILineStatementContext is an interface to support dynamic dispatch. @@ -176,8 +177,6 @@ type ILineStatementContext interface { // Getter signatures Entry() IEntryContext EOF() antlr.TerminalNode - SEPARATOR() antlr.TerminalNode - Comment() ICommentContext // IsLineStatementContext differentiates from other interfaces. IsLineStatementContext() @@ -235,26 +234,6 @@ func (s *LineStatementContext) EOF() antlr.TerminalNode { return s.GetToken(AliasesParserEOF, 0) } -func (s *LineStatementContext) SEPARATOR() antlr.TerminalNode { - return s.GetToken(AliasesParserSEPARATOR, 0) -} - -func (s *LineStatementContext) Comment() ICommentContext { - var t antlr.RuleContext - for _, ctx := range s.GetChildren() { - if _, ok := ctx.(ICommentContext); ok { - t = ctx.(antlr.RuleContext) - break - } - } - - if t == nil { - return nil - } - - return t.(ICommentContext) -} - func (s *LineStatementContext) GetRuleContext() antlr.RuleContext { return s } @@ -278,47 +257,13 @@ func (s *LineStatementContext) ExitRule(listener antlr.ParseTreeListener) { func (p *AliasesParser) LineStatement() (localctx ILineStatementContext) { localctx = NewLineStatementContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 0, AliasesParserRULE_lineStatement) - var _la int - p.EnterOuterAlt(localctx, 1) { - p.SetState(32) + p.SetState(30) p.Entry() } - p.SetState(34) - p.GetErrorHandler().Sync(p) - if p.HasError() { - goto errorExit - } - _la = p.GetTokenStream().LA(1) - - if _la == AliasesParserSEPARATOR { - { - p.SetState(33) - p.Match(AliasesParserSEPARATOR) - if p.HasError() { - // Recognition error - abort rule - goto errorExit - } - } - - } - p.SetState(37) - p.GetErrorHandler().Sync(p) - if p.HasError() { - goto errorExit - } - _la = p.GetTokenStream().LA(1) - - if _la == AliasesParserNUMBER_SIGN { - { - p.SetState(36) - p.Comment() - } - - } { - p.SetState(39) + p.SetState(31) p.Match(AliasesParserEOF) if p.HasError() { // Recognition error - abort rule @@ -471,6 +416,50 @@ func (p *AliasesParser) Entry() (localctx IEntryContext) { var _la int p.EnterOuterAlt(localctx, 1) + p.SetState(34) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSEPARATOR { + { + p.SetState(33) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(36) + p.Key() + } + p.SetState(38) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSEPARATOR { + { + p.SetState(37) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + { + p.SetState(40) + p.Separator() + } p.SetState(42) p.GetErrorHandler().Sync(p) if p.HasError() { @@ -491,50 +480,6 @@ func (p *AliasesParser) Entry() (localctx IEntryContext) { } { p.SetState(44) - p.Key() - } - p.SetState(46) - p.GetErrorHandler().Sync(p) - if p.HasError() { - goto errorExit - } - _la = p.GetTokenStream().LA(1) - - if _la == AliasesParserSEPARATOR { - { - p.SetState(45) - p.Match(AliasesParserSEPARATOR) - if p.HasError() { - // Recognition error - abort rule - goto errorExit - } - } - - } - { - p.SetState(48) - p.Separator() - } - p.SetState(50) - p.GetErrorHandler().Sync(p) - if p.HasError() { - goto errorExit - } - _la = p.GetTokenStream().LA(1) - - if _la == AliasesParserSEPARATOR { - { - p.SetState(49) - p.Match(AliasesParserSEPARATOR) - if p.HasError() { - // Recognition error - abort rule - goto errorExit - } - } - - } - { - p.SetState(52) p.Values() } @@ -626,7 +571,7 @@ func (p *AliasesParser) Separator() (localctx ISeparatorContext) { p.EnterRule(localctx, 4, AliasesParserRULE_separator) p.EnterOuterAlt(localctx, 1) { - p.SetState(54) + p.SetState(46) p.Match(AliasesParserCOLON) if p.HasError() { // Recognition error - abort rule @@ -722,7 +667,7 @@ func (p *AliasesParser) Key() (localctx IKeyContext) { p.EnterRule(localctx, 6, AliasesParserRULE_key) p.EnterOuterAlt(localctx, 1) { - p.SetState(56) + p.SetState(48) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -877,23 +822,23 @@ func (p *AliasesParser) Values() (localctx IValuesContext) { var _alt int p.EnterOuterAlt(localctx, 1) - p.SetState(64) + p.SetState(56) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit } - _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext()) + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) if p.HasError() { goto errorExit } for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { if _alt == 1 { { - p.SetState(58) + p.SetState(50) p.Value() } { - p.SetState(59) + p.SetState(51) p.Match(AliasesParserCOMMA) if p.HasError() { // Recognition error - abort rule @@ -901,7 +846,7 @@ func (p *AliasesParser) Values() (localctx IValuesContext) { } } { - p.SetState(60) + p.SetState(52) p.Match(AliasesParserSEPARATOR) if p.HasError() { // Recognition error - abort rule @@ -910,18 +855,18 @@ func (p *AliasesParser) Values() (localctx IValuesContext) { } } - p.SetState(66) + p.SetState(58) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit } - _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext()) + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 3, p.GetParserRuleContext()) if p.HasError() { goto errorExit } } { - p.SetState(67) + p.SetState(59) p.Value() } @@ -951,6 +896,7 @@ type IValueContext interface { Command() ICommandContext Include() IIncludeContext Email() IEmailContext + Error_() IErrorContext // IsValueContext differentiates from other interfaces. IsValueContext() @@ -1068,6 +1014,22 @@ func (s *ValueContext) Email() IEmailContext { return t.(IEmailContext) } +func (s *ValueContext) Error_() IErrorContext { + var t antlr.RuleContext + for _, ctx := range s.GetChildren() { + if _, ok := ctx.(IErrorContext); ok { + t = ctx.(antlr.RuleContext) + break + } + } + + if t == nil { + return nil + } + + return t.(IErrorContext) +} + func (s *ValueContext) GetRuleContext() antlr.RuleContext { return s } @@ -1092,43 +1054,49 @@ func (p *AliasesParser) Value() (localctx IValueContext) { localctx = NewValueContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 10, AliasesParserRULE_value) p.EnterOuterAlt(localctx, 1) - p.SetState(74) + p.SetState(67) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit } - switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 6, p.GetParserRuleContext()) { + switch p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 4, p.GetParserRuleContext()) { case 1: { - p.SetState(69) + p.SetState(61) p.User() } case 2: { - p.SetState(70) + p.SetState(62) p.File() } case 3: { - p.SetState(71) + p.SetState(63) p.Command() } case 4: { - p.SetState(72) + p.SetState(64) p.Include() } case 5: { - p.SetState(73) + p.SetState(65) p.Email() } + case 6: + { + p.SetState(66) + p.Error_() + } + case antlr.ATNInvalidAltNumber: goto errorExit } @@ -1221,7 +1189,7 @@ func (p *AliasesParser) User() (localctx IUserContext) { p.EnterRule(localctx, 12, AliasesParserRULE_user) p.EnterOuterAlt(localctx, 1) { - p.SetState(76) + p.SetState(69) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -1336,26 +1304,26 @@ func (p *AliasesParser) File() (localctx IFileContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(78) + p.SetState(71) p.Match(AliasesParserSLASH) if p.HasError() { // Recognition error - abort rule goto errorExit } } - p.SetState(83) + p.SetState(76) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit } - _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 7, p.GetParserRuleContext()) + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext()) if p.HasError() { goto errorExit } for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { if _alt == 1 { { - p.SetState(79) + p.SetState(72) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -1363,7 +1331,7 @@ func (p *AliasesParser) File() (localctx IFileContext) { } } { - p.SetState(80) + p.SetState(73) p.Match(AliasesParserSLASH) if p.HasError() { // Recognition error - abort rule @@ -1372,17 +1340,17 @@ func (p *AliasesParser) File() (localctx IFileContext) { } } - p.SetState(85) + p.SetState(78) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit } - _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 7, p.GetParserRuleContext()) + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 5, p.GetParserRuleContext()) if p.HasError() { goto errorExit } } - p.SetState(87) + p.SetState(80) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1391,7 +1359,7 @@ func (p *AliasesParser) File() (localctx IFileContext) { if _la == AliasesParserSTRING { { - p.SetState(86) + p.SetState(79) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -1496,14 +1464,14 @@ func (p *AliasesParser) Command() (localctx ICommandContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(89) + p.SetState(82) p.Match(AliasesParserVERTLINE) if p.HasError() { // Recognition error - abort rule goto errorExit } } - p.SetState(91) + p.SetState(84) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1512,7 +1480,7 @@ func (p *AliasesParser) Command() (localctx ICommandContext) { if _la == AliasesParserSTRING { { - p.SetState(90) + p.SetState(83) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -1639,7 +1607,7 @@ func (p *AliasesParser) Include() (localctx IIncludeContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(93) + p.SetState(86) p.Match(AliasesParserCOLON) if p.HasError() { // Recognition error - abort rule @@ -1647,7 +1615,7 @@ func (p *AliasesParser) Include() (localctx IIncludeContext) { } } { - p.SetState(94) + p.SetState(87) p.Match(AliasesParserINCLUDE) if p.HasError() { // Recognition error - abort rule @@ -1655,14 +1623,14 @@ func (p *AliasesParser) Include() (localctx IIncludeContext) { } } { - p.SetState(95) + p.SetState(88) p.Match(AliasesParserCOLON) if p.HasError() { // Recognition error - abort rule goto errorExit } } - p.SetState(97) + p.SetState(90) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1671,7 +1639,7 @@ func (p *AliasesParser) Include() (localctx IIncludeContext) { if _la == AliasesParserSLASH { { - p.SetState(96) + p.SetState(89) p.File() } @@ -1789,14 +1757,14 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { p.EnterOuterAlt(localctx, 1) { - p.SetState(99) + p.SetState(92) p.Match(AliasesParserNUMBER_SIGN) if p.HasError() { // Recognition error - abort rule goto errorExit } } - p.SetState(104) + p.SetState(97) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1805,7 +1773,7 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { for ok := true; ok; ok = _alt != 2 && _alt != antlr.ATNInvalidAltNumber { switch _alt { case 1: - p.SetState(101) + p.SetState(94) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1814,7 +1782,7 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { if _la == AliasesParserSEPARATOR { { - p.SetState(100) + p.SetState(93) p.Match(AliasesParserSEPARATOR) if p.HasError() { // Recognition error - abort rule @@ -1824,7 +1792,7 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { } { - p.SetState(103) + p.SetState(96) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -1837,14 +1805,14 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { goto errorExit } - p.SetState(106) + p.SetState(99) p.GetErrorHandler().Sync(p) - _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 12, p.GetParserRuleContext()) + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 10, p.GetParserRuleContext()) if p.HasError() { goto errorExit } } - p.SetState(109) + p.SetState(102) p.GetErrorHandler().Sync(p) if p.HasError() { goto errorExit @@ -1853,7 +1821,7 @@ func (p *AliasesParser) Comment() (localctx ICommentContext) { if _la == AliasesParserSEPARATOR { { - p.SetState(108) + p.SetState(101) p.Match(AliasesParserSEPARATOR) if p.HasError() { // Recognition error - abort rule @@ -1961,7 +1929,7 @@ func (p *AliasesParser) Email() (localctx IEmailContext) { p.EnterRule(localctx, 22, AliasesParserRULE_email) p.EnterOuterAlt(localctx, 1) { - p.SetState(111) + p.SetState(104) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -1969,7 +1937,7 @@ func (p *AliasesParser) Email() (localctx IEmailContext) { } } { - p.SetState(112) + p.SetState(105) p.Match(AliasesParserAT) if p.HasError() { // Recognition error - abort rule @@ -1977,7 +1945,7 @@ func (p *AliasesParser) Email() (localctx IEmailContext) { } } { - p.SetState(113) + p.SetState(106) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule @@ -2006,7 +1974,7 @@ type IErrorContext interface { GetParser() antlr.Parser // Getter signatures - ErrorStatus() IErrorStatusContext + ERROR() antlr.TerminalNode COLON() antlr.TerminalNode ErrorCode() IErrorCodeContext SEPARATOR() antlr.TerminalNode @@ -2048,20 +2016,8 @@ func NewErrorContext(parser antlr.Parser, parent antlr.ParserRuleContext, invoki func (s *ErrorContext) GetParser() antlr.Parser { return s.parser } -func (s *ErrorContext) ErrorStatus() IErrorStatusContext { - var t antlr.RuleContext - for _, ctx := range s.GetChildren() { - if _, ok := ctx.(IErrorStatusContext); ok { - t = ctx.(antlr.RuleContext) - break - } - } - - if t == nil { - return nil - } - - return t.(IErrorStatusContext) +func (s *ErrorContext) ERROR() antlr.TerminalNode { + return s.GetToken(AliasesParserERROR, 0) } func (s *ErrorContext) COLON() antlr.TerminalNode { @@ -2127,130 +2083,70 @@ func (s *ErrorContext) ExitRule(listener antlr.ParseTreeListener) { func (p *AliasesParser) Error_() (localctx IErrorContext) { localctx = NewErrorContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 24, AliasesParserRULE_error) + var _la int + p.EnterOuterAlt(localctx, 1) { - p.SetState(115) - p.ErrorStatus() + p.SetState(108) + p.Match(AliasesParserERROR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } } { - p.SetState(116) + p.SetState(109) p.Match(AliasesParserCOLON) if p.HasError() { // Recognition error - abort rule goto errorExit } } - { - p.SetState(117) - p.ErrorCode() - } - { - p.SetState(118) - p.Match(AliasesParserSEPARATOR) - if p.HasError() { - // Recognition error - abort rule - goto errorExit - } - } - { - p.SetState(119) - p.ErrorMessage() - } - -errorExit: + p.SetState(111) + p.GetErrorHandler().Sync(p) if p.HasError() { - v := p.GetError() - localctx.SetException(v) - p.GetErrorHandler().ReportError(p, v) - p.GetErrorHandler().Recover(p, v) - p.SetError(nil) + goto errorExit } - p.ExitRule() - return localctx - goto errorExit // Trick to prevent compiler error if the label is not used -} + _la = p.GetTokenStream().LA(1) -// IErrorStatusContext is an interface to support dynamic dispatch. -type IErrorStatusContext interface { - antlr.ParserRuleContext - - // GetParser returns the parser. - GetParser() antlr.Parser - - // Getter signatures - STRING() antlr.TerminalNode - - // IsErrorStatusContext differentiates from other interfaces. - IsErrorStatusContext() -} - -type ErrorStatusContext struct { - antlr.BaseParserRuleContext - parser antlr.Parser -} - -func NewEmptyErrorStatusContext() *ErrorStatusContext { - var p = new(ErrorStatusContext) - antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) - p.RuleIndex = AliasesParserRULE_errorStatus - return p -} - -func InitEmptyErrorStatusContext(p *ErrorStatusContext) { - antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, nil, -1) - p.RuleIndex = AliasesParserRULE_errorStatus -} - -func (*ErrorStatusContext) IsErrorStatusContext() {} - -func NewErrorStatusContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ErrorStatusContext { - var p = new(ErrorStatusContext) - - antlr.InitBaseParserRuleContext(&p.BaseParserRuleContext, parent, invokingState) - - p.parser = parser - p.RuleIndex = AliasesParserRULE_errorStatus - - return p -} - -func (s *ErrorStatusContext) GetParser() antlr.Parser { return s.parser } - -func (s *ErrorStatusContext) STRING() antlr.TerminalNode { - return s.GetToken(AliasesParserSTRING, 0) -} - -func (s *ErrorStatusContext) GetRuleContext() antlr.RuleContext { - return s -} - -func (s *ErrorStatusContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string { - return antlr.TreesStringTree(s, ruleNames, recog) -} - -func (s *ErrorStatusContext) EnterRule(listener antlr.ParseTreeListener) { - if listenerT, ok := listener.(AliasesListener); ok { - listenerT.EnterErrorStatus(s) - } -} - -func (s *ErrorStatusContext) ExitRule(listener antlr.ParseTreeListener) { - if listenerT, ok := listener.(AliasesListener); ok { - listenerT.ExitErrorStatus(s) - } -} - -func (p *AliasesParser) ErrorStatus() (localctx IErrorStatusContext) { - localctx = NewErrorStatusContext(p, p.GetParserRuleContext(), p.GetState()) - p.EnterRule(localctx, 26, AliasesParserRULE_errorStatus) - p.EnterOuterAlt(localctx, 1) - { - p.SetState(121) - p.Match(AliasesParserSTRING) - if p.HasError() { - // Recognition error - abort rule - goto errorExit + if _la == AliasesParserDIGITS { + { + p.SetState(110) + p.ErrorCode() } + + } + p.SetState(114) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSEPARATOR { + { + p.SetState(113) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(117) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _la = p.GetTokenStream().LA(1) + + if _la == AliasesParserSTRING { + { + p.SetState(116) + p.ErrorMessage() + } + } errorExit: @@ -2274,7 +2170,7 @@ type IErrorCodeContext interface { GetParser() antlr.Parser // Getter signatures - STRING() antlr.TerminalNode + DIGITS() antlr.TerminalNode // IsErrorCodeContext differentiates from other interfaces. IsErrorCodeContext() @@ -2312,8 +2208,8 @@ func NewErrorCodeContext(parser antlr.Parser, parent antlr.ParserRuleContext, in func (s *ErrorCodeContext) GetParser() antlr.Parser { return s.parser } -func (s *ErrorCodeContext) STRING() antlr.TerminalNode { - return s.GetToken(AliasesParserSTRING, 0) +func (s *ErrorCodeContext) DIGITS() antlr.TerminalNode { + return s.GetToken(AliasesParserDIGITS, 0) } func (s *ErrorCodeContext) GetRuleContext() antlr.RuleContext { @@ -2338,11 +2234,11 @@ func (s *ErrorCodeContext) ExitRule(listener antlr.ParseTreeListener) { func (p *AliasesParser) ErrorCode() (localctx IErrorCodeContext) { localctx = NewErrorCodeContext(p, p.GetParserRuleContext(), p.GetState()) - p.EnterRule(localctx, 28, AliasesParserRULE_errorCode) + p.EnterRule(localctx, 26, AliasesParserRULE_errorCode) p.EnterOuterAlt(localctx, 1) { - p.SetState(123) - p.Match(AliasesParserSTRING) + p.SetState(119) + p.Match(AliasesParserDIGITS) if p.HasError() { // Recognition error - abort rule goto errorExit @@ -2434,10 +2330,10 @@ func (s *ErrorMessageContext) ExitRule(listener antlr.ParseTreeListener) { func (p *AliasesParser) ErrorMessage() (localctx IErrorMessageContext) { localctx = NewErrorMessageContext(p, p.GetParserRuleContext(), p.GetState()) - p.EnterRule(localctx, 30, AliasesParserRULE_errorMessage) + p.EnterRule(localctx, 28, AliasesParserRULE_errorMessage) p.EnterOuterAlt(localctx, 1) { - p.SetState(125) + p.SetState(121) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule From 3a1b38400cea209008e5fd6936c87b6f94789280 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Mon, 2 Sep 2024 00:12:05 +0200 Subject: [PATCH 12/24] fix(aliases): Improve file value --- handlers/aliases/analyzer/value_valid.go | 17 +++++++++++++++++ handlers/aliases/ast/listener.go | 2 +- handlers/aliases/ast/values.go | 2 +- 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/handlers/aliases/analyzer/value_valid.go b/handlers/aliases/analyzer/value_valid.go index 1e640bb..8b97e46 100644 --- a/handlers/aliases/analyzer/value_valid.go +++ b/handlers/aliases/analyzer/value_valid.go @@ -124,6 +124,23 @@ func checkValue( Err: ers.New("An error message is required"), }} } + case ast.AliasValueInclude: + incldueValue := value.(ast.AliasValueInclude) + + if incldueValue.Path == nil { + return []common.LSPError{{ + Range: incldueValue.Location, + Err: ers.New("A path is required"), + }} + } + + // Again, I'm not sure if the path really needs to be absolute + if !path.IsAbs(string(incldueValue.Path.Path)) { + return []common.LSPError{{ + Range: incldueValue.Path.Location, + Err: ers.New("This path must be absolute"), + }} + } } return nil } diff --git a/handlers/aliases/ast/listener.go b/handlers/aliases/ast/listener.go index 74d5835..8743415 100644 --- a/handlers/aliases/ast/listener.go +++ b/handlers/aliases/ast/listener.go @@ -101,7 +101,7 @@ func (s *aliasesParserListener) EnterFile(ctx *parser.FileContext) { // Set the path include := rawValue.(AliasValueInclude) - include.Path = AliasValueIncludePath{ + include.Path = &AliasValueIncludePath{ Location: location, Path: path(ctx.GetText()), } diff --git a/handlers/aliases/ast/values.go b/handlers/aliases/ast/values.go index bae4526..ac94fc0 100644 --- a/handlers/aliases/ast/values.go +++ b/handlers/aliases/ast/values.go @@ -66,7 +66,7 @@ type AliasValueIncludePath struct { type AliasValueInclude struct { AliasValue - Path AliasValueIncludePath + Path *AliasValueIncludePath } func (a AliasValueInclude) CheckIsValid() []common.LSPError { From 3538a37203e9b3f1c70b6747966dc28b9e70196b Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Thu, 5 Sep 2024 23:24:48 +0200 Subject: [PATCH 13/24] feat(aliases): Add hover info --- flake.nix | 5 +- handlers/aliases/ast/values.go | 11 ++ handlers/aliases/handlers/completions.go | 2 +- handlers/aliases/handlers/get-value.go | 2 +- handlers/aliases/handlers/hover.go | 158 ++++++++++++++++++++ handlers/aliases/lsp/text-document-hover.go | 104 +++++-------- 6 files changed, 216 insertions(+), 66 deletions(-) create mode 100644 handlers/aliases/handlers/hover.go diff --git a/flake.nix b/flake.nix index 08e129f..337a65a 100644 --- a/flake.nix +++ b/flake.nix @@ -42,7 +42,10 @@ }; }; devShells.default = pkgs.mkShell { - buildInputs = inputs; + buildInputs = inputs ++ (with pkgs; [ + mailutils + swaks + ]); }; } ); diff --git a/handlers/aliases/ast/values.go b/handlers/aliases/ast/values.go index ac94fc0..d468b62 100644 --- a/handlers/aliases/ast/values.go +++ b/handlers/aliases/ast/values.go @@ -6,6 +6,7 @@ import ( "config-lsp/handlers/aliases/fields" "config-lsp/utils" "fmt" + "strconv" ) type AliasValueInterface interface { @@ -102,6 +103,16 @@ type AliasValueErrorCode struct { AliasValue } +func (a AliasValueErrorCode) ErrorCodeAsInt() uint16 { + code, err := strconv.Atoi(a.Value) + + if err != nil { + return 0 + } + + return uint16(code) +} + type AliasValueErrorMessage struct { AliasValue } diff --git a/handlers/aliases/handlers/completions.go b/handlers/aliases/handlers/completions.go index 8df8d3f..615c765 100644 --- a/handlers/aliases/handlers/completions.go +++ b/handlers/aliases/handlers/completions.go @@ -48,7 +48,7 @@ func GetCompletionsForEntry( return completions, nil } - value := getValueAtCursor(cursor, entry) + value := GetValueAtCursor(cursor, entry) relativeCursor := cursor - entry.Key.Location.Start.Character if value == nil { diff --git a/handlers/aliases/handlers/get-value.go b/handlers/aliases/handlers/get-value.go index 0a3b9bf..eab77ba 100644 --- a/handlers/aliases/handlers/get-value.go +++ b/handlers/aliases/handlers/get-value.go @@ -5,7 +5,7 @@ import ( "slices" ) -func getValueAtCursor( +func GetValueAtCursor( cursor uint32, entry *ast.AliasEntry, ) *ast.AliasValueInterface { diff --git a/handlers/aliases/handlers/hover.go b/handlers/aliases/handlers/hover.go new file mode 100644 index 0000000..52ced93 --- /dev/null +++ b/handlers/aliases/handlers/hover.go @@ -0,0 +1,158 @@ +package handlers + +import ( + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + "config-lsp/utils" + "fmt" + "strings" +) + +// Get hover information for an alias entry +// Expects `entry` to contain at least a key +func GetAliasHoverInfo( + i indexes.AliasesIndexes, + entry ast.AliasEntry, +) string { + header := []string{ + fmt.Sprintf("Emails targeted for `%s` will be passed to:", entry.Key.Value), + "", + } + + var forwards []string + + if entry.Values == nil { + forwards = []string{ + "No forwards configured", + } + } else { + if len(entry.Values.Values) == 1 { + forwards = []string{ + GetAliasValueHoverInfo( + i, + entry.Values.Values[0], + ), + } + } else { + forwards = utils.Map( + entry.Values.Values, + func(value ast.AliasValueInterface) string { + return fmt.Sprintf( + "* %s", + GetAliasValueHoverInfo( + i, + value, + ), + ) + }, + ) + } + } + + content := append(header, forwards...) + return strings.Join( + content, + "\n", + ) +} + +func GetAliasValueHoverInfo( + i indexes.AliasesIndexes, + value ast.AliasValueInterface, +) string { + switch value.(type) { + case ast.AliasValueUser: + return fmt.Sprintf("User: **%s**", value.GetAliasValue().Value) + case ast.AliasValueEmail: + return fmt.Sprintf("Email: **%s**", value.GetAliasValue().Value) + case ast.AliasValueInclude: + includeValue := value.(ast.AliasValueInclude) + return fmt.Sprintf("Included file: `%s`", string(includeValue.Path.Path)) + case ast.AliasValueFile: + fileValue := value.(ast.AliasValueFile) + return fmt.Sprintf("File: Email will be written to `%s`", string(fileValue.Path)) + case ast.AliasValueCommand: + commandValue := value.(ast.AliasValueCommand) + return fmt.Sprintf("Command: Will be passed as stdin to `%s`", commandValue.Command) + case ast.AliasValueError: + errorValue := value.(ast.AliasValueError) + + if errorValue.Code == nil || errorValue.Message == nil { + return "Error: An error will show up" + } + + return fmt.Sprintf( + "Error: An error will show up; code: **%s** (%s), message: '%s'", + errorValue.Code.Value, + getErrorCodeInfo(errorValue.Code.ErrorCodeAsInt()), + errorValue.Message.Value, + ) + } + + panic("Unknown value type") +} + +func GetAliasValueTypeInfo( + value ast.AliasValueInterface, +) []string { + switch value.(type) { + case ast.AliasValueUser: + return []string{ + "### User", + "`user`", + "", + "A user on the host machine. The user must have a valid entry in the passwd(5) database file.", + } + case ast.AliasValueEmail: + return []string{ + "### Email", + "`user-part@domain-part`", + "", + "An email address in RFC 5322 format. If an address extension is appended to the user-part, it is first compared for an exact match. It is then stripped so that an address such as user+ext@example.com will only use the part that precedes ‘+’ as a key.", + } + case ast.AliasValueInclude: + return []string{ + "### Include", + "`include:/path/to/file`", + "", + "Include any definitions in file as alias entries. The format of the file is identical to this one.", + } + case ast.AliasValueFile: + return []string{ + "### File", + "`/path/to/file`", + "", + "Append messages to file, specified by its absolute pathname.", + } + case ast.AliasValueCommand: + return []string{ + "### Command", + "`|command`", + "", + "Pipe the message to command on its standard input. The command is run under the privileges of the daemon's unprivileged account.", + } + case ast.AliasValueError: + return []string{ + "### Error", + "`error:code message`", + "", + "A status code and message to return. The code must be 3 digits, starting 4XX (TempFail) or 5XX (PermFail). The message must be present and can be freely chosen.", + } + } + + panic("Unknown value type") +} + +func getErrorCodeInfo( + code uint16, +) string { + if code >= 400 && code <= 499 { + return "4XX: TempFail" + } + + if code >= 500 && code <= 599 { + return "5XX: PermFail" + } + + return "Unknown code" +} diff --git a/handlers/aliases/lsp/text-document-hover.go b/handlers/aliases/lsp/text-document-hover.go index 0845504..51acd2c 100644 --- a/handlers/aliases/lsp/text-document-hover.go +++ b/handlers/aliases/lsp/text-document-hover.go @@ -1,7 +1,10 @@ package lsp import ( - "config-lsp/handlers/hosts" + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/handlers" + "strings" "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" @@ -11,76 +14,51 @@ func TextDocumentHover( context *glsp.Context, params *protocol.HoverParams, ) (*protocol.Hover, error) { - document := hosts.DocumentParserMap[params.TextDocument.URI] + document := aliases.DocumentParserMap[params.TextDocument.URI] line := params.Position.Line - // character := params.Position.Character + character := params.Position.Character if _, found := document.Parser.CommentLines[line]; found { // Comment return nil, nil } - // entry, found := document.Parser.Tree.Entries[line] - // - // if !found { - // // Empty line - // return nil, nil - // } - // - // target := handlers.GetHoverTargetInEntry(*entry, character) - // - // var hostname *ast.HostsHostname - // - // switch *target { - // case handlers.HoverTargetIPAddress: - // relativeCursor := character - entry.IPAddress.Location.Start.Character - // hover := fields.IPAddressField.FetchHoverInfo(entry.IPAddress.Value.String(), relativeCursor) - // - // return &protocol.Hover{ - // Contents: hover, - // }, nil - // case handlers.HoverTargetHostname: - // hostname = entry.Hostname - // case handlers.HoverTargetAlias: - // for _, alias := range entry.Aliases { - // if alias.Location.Start.Character <= character && character <= alias.Location.End.Character { - // hostname = alias - // break - // } - // } - // } - // - // if hostname != nil { - // contents := []string{ - // "## Hostname", - // } - // contents = append( - // contents, - // fields.HostnameField.GetTypeDescription()..., - // ) - // contents = append( - // contents, - // []string{ - // "", - // }..., - // ) - // contents = append( - // contents, - // fields.HostnameField.Documentation, - // ) - // contents = append( - // contents, - // handlers.GetHoverInfoForHostname(*document, *hostname, character)..., - // ) - // - // return &protocol.Hover{ - // Contents: &protocol.MarkupContent{ - // Kind: protocol.MarkupKindMarkdown, - // Value: strings.Join(contents, "\n"), - // }, - // }, nil - // } + rawEntry, found := document.Parser.Aliases.Get(line) + + if !found { + return nil, nil + } + + entry := rawEntry.(*ast.AliasEntry) + + if entry.Key != nil && character >= entry.Key.Location.Start.Character && character <= entry.Key.Location.End.Character { + text := handlers.GetAliasHoverInfo(*document.Indexes, *entry) + + return &protocol.Hover{ + Contents: text, + }, nil + } + + if entry.Values != nil && character >= entry.Values.Location.Start.Character && character <= entry.Values.Location.End.Character { + value := handlers.GetValueAtCursor(character, entry) + + if value == nil { + return nil, nil + } + + contents := []string{} + contents = append(contents, handlers.GetAliasValueTypeInfo(*value)...) + contents = append(contents, "") + contents = append(contents, "#### Value") + contents = append(contents, handlers.GetAliasValueHoverInfo(*document.Indexes, *value)) + + text := strings.Join(contents, "\n") + + return &protocol.Hover{ + Contents: text, + }, nil + } return nil, nil } From 77cf960559aa18c365d99d2be302fee504d72dd6 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 11:51:37 +0200 Subject: [PATCH 14/24] fix(aliases): Fix values --- handlers/aliases/ast/listener.go | 2 +- handlers/aliases/handlers/get-value.go | 4 ++-- handlers/aliases/handlers/hover.go | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/handlers/aliases/ast/listener.go b/handlers/aliases/ast/listener.go index 8743415..0ccd6b9 100644 --- a/handlers/aliases/ast/listener.go +++ b/handlers/aliases/ast/listener.go @@ -177,7 +177,7 @@ func (s *aliasesParserListener) EnterEmail(ctx *parser.EmailContext) { rawEntry, _ := s.Parser.Aliases.Get(location.Start.Line) entry := rawEntry.(*AliasEntry) - entry.Values.Values = append(entry.Values.Values, &email) + entry.Values.Values = append(entry.Values.Values, email) } func (s *aliasesParserListener) EnterError(ctx *parser.ErrorContext) { diff --git a/handlers/aliases/handlers/get-value.go b/handlers/aliases/handlers/get-value.go index eab77ba..3e2347b 100644 --- a/handlers/aliases/handlers/get-value.go +++ b/handlers/aliases/handlers/get-value.go @@ -20,11 +20,11 @@ func GetValueAtCursor( value := entry.GetAliasValue() if pos > value.Location.End.Character { - return 1 + return -1 } if pos < value.Location.Start.Character { - return -1 + return 1 } return 0 diff --git a/handlers/aliases/handlers/hover.go b/handlers/aliases/handlers/hover.go index 52ced93..84206e7 100644 --- a/handlers/aliases/handlers/hover.go +++ b/handlers/aliases/handlers/hover.go @@ -95,6 +95,7 @@ func GetAliasValueHoverInfo( func GetAliasValueTypeInfo( value ast.AliasValueInterface, ) []string { + println(fmt.Sprintf("value: %v, value type: %T", value, value)) switch value.(type) { case ast.AliasValueUser: return []string{ From bbe083621e970fa3d84a9a589cfd6e9152955214 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:06:49 +0200 Subject: [PATCH 15/24] fix(aliases): Fix antlr parser --- handlers/aliases/Aliases.g4 | 2 +- handlers/aliases/parser/Aliases.interp | 2 +- handlers/aliases/parser/aliases_parser.go | 147 +++++++++++++++------- 3 files changed, 105 insertions(+), 46 deletions(-) diff --git a/handlers/aliases/Aliases.g4 b/handlers/aliases/Aliases.g4 index 4c598eb..5219b1a 100644 --- a/handlers/aliases/Aliases.g4 +++ b/handlers/aliases/Aliases.g4 @@ -58,7 +58,7 @@ errorCode ; errorMessage - : STRING + : (STRING SEPARATOR)* STRING ; DIGITS diff --git a/handlers/aliases/parser/Aliases.interp b/handlers/aliases/parser/Aliases.interp index 3fd0e3e..6b6abf7 100644 --- a/handlers/aliases/parser/Aliases.interp +++ b/handlers/aliases/parser/Aliases.interp @@ -45,4 +45,4 @@ errorMessage atn: -[4, 1, 11, 124, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 1, 0, 1, 0, 1, 0, 1, 1, 3, 1, 35, 8, 1, 1, 1, 1, 1, 3, 1, 39, 8, 1, 1, 1, 1, 1, 3, 1, 43, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 55, 8, 4, 10, 4, 12, 4, 58, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 68, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 75, 8, 7, 10, 7, 12, 7, 78, 9, 7, 1, 7, 3, 7, 81, 8, 7, 1, 8, 1, 8, 3, 8, 85, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 91, 8, 9, 1, 10, 1, 10, 3, 10, 95, 8, 10, 1, 10, 4, 10, 98, 8, 10, 11, 10, 12, 10, 99, 1, 10, 3, 10, 103, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 3, 12, 112, 8, 12, 1, 12, 3, 12, 115, 8, 12, 1, 12, 3, 12, 118, 8, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 0, 0, 15, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 0, 0, 127, 0, 30, 1, 0, 0, 0, 2, 34, 1, 0, 0, 0, 4, 46, 1, 0, 0, 0, 6, 48, 1, 0, 0, 0, 8, 56, 1, 0, 0, 0, 10, 67, 1, 0, 0, 0, 12, 69, 1, 0, 0, 0, 14, 71, 1, 0, 0, 0, 16, 82, 1, 0, 0, 0, 18, 86, 1, 0, 0, 0, 20, 92, 1, 0, 0, 0, 22, 104, 1, 0, 0, 0, 24, 108, 1, 0, 0, 0, 26, 119, 1, 0, 0, 0, 28, 121, 1, 0, 0, 0, 30, 31, 3, 2, 1, 0, 31, 32, 5, 0, 0, 1, 32, 1, 1, 0, 0, 0, 33, 35, 5, 3, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 38, 3, 6, 3, 0, 37, 39, 5, 3, 0, 0, 38, 37, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 1, 0, 0, 0, 40, 42, 3, 4, 2, 0, 41, 43, 5, 3, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 45, 3, 8, 4, 0, 45, 3, 1, 0, 0, 0, 46, 47, 5, 7, 0, 0, 47, 5, 1, 0, 0, 0, 48, 49, 5, 11, 0, 0, 49, 7, 1, 0, 0, 0, 50, 51, 3, 10, 5, 0, 51, 52, 5, 8, 0, 0, 52, 53, 5, 3, 0, 0, 53, 55, 1, 0, 0, 0, 54, 50, 1, 0, 0, 0, 55, 58, 1, 0, 0, 0, 56, 54, 1, 0, 0, 0, 56, 57, 1, 0, 0, 0, 57, 59, 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, 60, 9, 1, 0, 0, 0, 61, 68, 3, 12, 6, 0, 62, 68, 3, 14, 7, 0, 63, 68, 3, 16, 8, 0, 64, 68, 3, 18, 9, 0, 65, 68, 3, 22, 11, 0, 66, 68, 3, 24, 12, 0, 67, 61, 1, 0, 0, 0, 67, 62, 1, 0, 0, 0, 67, 63, 1, 0, 0, 0, 67, 64, 1, 0, 0, 0, 67, 65, 1, 0, 0, 0, 67, 66, 1, 0, 0, 0, 68, 11, 1, 0, 0, 0, 69, 70, 5, 11, 0, 0, 70, 13, 1, 0, 0, 0, 71, 76, 5, 10, 0, 0, 72, 73, 5, 11, 0, 0, 73, 75, 5, 10, 0, 0, 74, 72, 1, 0, 0, 0, 75, 78, 1, 0, 0, 0, 76, 74, 1, 0, 0, 0, 76, 77, 1, 0, 0, 0, 77, 80, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, 79, 81, 5, 11, 0, 0, 80, 79, 1, 0, 0, 0, 80, 81, 1, 0, 0, 0, 81, 15, 1, 0, 0, 0, 82, 84, 5, 6, 0, 0, 83, 85, 5, 11, 0, 0, 84, 83, 1, 0, 0, 0, 84, 85, 1, 0, 0, 0, 85, 17, 1, 0, 0, 0, 86, 87, 5, 7, 0, 0, 87, 88, 5, 5, 0, 0, 88, 90, 5, 7, 0, 0, 89, 91, 3, 14, 7, 0, 90, 89, 1, 0, 0, 0, 90, 91, 1, 0, 0, 0, 91, 19, 1, 0, 0, 0, 92, 97, 5, 9, 0, 0, 93, 95, 5, 3, 0, 0, 94, 93, 1, 0, 0, 0, 94, 95, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 98, 5, 11, 0, 0, 97, 94, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 97, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0, 100, 102, 1, 0, 0, 0, 101, 103, 5, 3, 0, 0, 102, 101, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 21, 1, 0, 0, 0, 104, 105, 5, 11, 0, 0, 105, 106, 5, 4, 0, 0, 106, 107, 5, 11, 0, 0, 107, 23, 1, 0, 0, 0, 108, 109, 5, 2, 0, 0, 109, 111, 5, 7, 0, 0, 110, 112, 3, 26, 13, 0, 111, 110, 1, 0, 0, 0, 111, 112, 1, 0, 0, 0, 112, 114, 1, 0, 0, 0, 113, 115, 5, 3, 0, 0, 114, 113, 1, 0, 0, 0, 114, 115, 1, 0, 0, 0, 115, 117, 1, 0, 0, 0, 116, 118, 3, 28, 14, 0, 117, 116, 1, 0, 0, 0, 117, 118, 1, 0, 0, 0, 118, 25, 1, 0, 0, 0, 119, 120, 5, 1, 0, 0, 120, 27, 1, 0, 0, 0, 121, 122, 5, 11, 0, 0, 122, 29, 1, 0, 0, 0, 15, 34, 38, 42, 56, 67, 76, 80, 84, 90, 94, 99, 102, 111, 114, 117] \ No newline at end of file +[4, 1, 11, 131, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 1, 0, 1, 0, 1, 0, 1, 1, 3, 1, 35, 8, 1, 1, 1, 1, 1, 3, 1, 39, 8, 1, 1, 1, 1, 1, 3, 1, 43, 8, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 4, 5, 4, 55, 8, 4, 10, 4, 12, 4, 58, 9, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 68, 8, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 5, 7, 75, 8, 7, 10, 7, 12, 7, 78, 9, 7, 1, 7, 3, 7, 81, 8, 7, 1, 8, 1, 8, 3, 8, 85, 8, 8, 1, 9, 1, 9, 1, 9, 1, 9, 3, 9, 91, 8, 9, 1, 10, 1, 10, 3, 10, 95, 8, 10, 1, 10, 4, 10, 98, 8, 10, 11, 10, 12, 10, 99, 1, 10, 3, 10, 103, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 3, 12, 112, 8, 12, 1, 12, 3, 12, 115, 8, 12, 1, 12, 3, 12, 118, 8, 12, 1, 13, 1, 13, 1, 14, 1, 14, 5, 14, 124, 8, 14, 10, 14, 12, 14, 127, 9, 14, 1, 14, 1, 14, 1, 14, 0, 0, 15, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 0, 0, 135, 0, 30, 1, 0, 0, 0, 2, 34, 1, 0, 0, 0, 4, 46, 1, 0, 0, 0, 6, 48, 1, 0, 0, 0, 8, 56, 1, 0, 0, 0, 10, 67, 1, 0, 0, 0, 12, 69, 1, 0, 0, 0, 14, 71, 1, 0, 0, 0, 16, 82, 1, 0, 0, 0, 18, 86, 1, 0, 0, 0, 20, 92, 1, 0, 0, 0, 22, 104, 1, 0, 0, 0, 24, 108, 1, 0, 0, 0, 26, 119, 1, 0, 0, 0, 28, 125, 1, 0, 0, 0, 30, 31, 3, 2, 1, 0, 31, 32, 5, 0, 0, 1, 32, 1, 1, 0, 0, 0, 33, 35, 5, 3, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, 35, 36, 1, 0, 0, 0, 36, 38, 3, 6, 3, 0, 37, 39, 5, 3, 0, 0, 38, 37, 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 1, 0, 0, 0, 40, 42, 3, 4, 2, 0, 41, 43, 5, 3, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, 0, 44, 45, 3, 8, 4, 0, 45, 3, 1, 0, 0, 0, 46, 47, 5, 7, 0, 0, 47, 5, 1, 0, 0, 0, 48, 49, 5, 11, 0, 0, 49, 7, 1, 0, 0, 0, 50, 51, 3, 10, 5, 0, 51, 52, 5, 8, 0, 0, 52, 53, 5, 3, 0, 0, 53, 55, 1, 0, 0, 0, 54, 50, 1, 0, 0, 0, 55, 58, 1, 0, 0, 0, 56, 54, 1, 0, 0, 0, 56, 57, 1, 0, 0, 0, 57, 59, 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, 60, 9, 1, 0, 0, 0, 61, 68, 3, 12, 6, 0, 62, 68, 3, 14, 7, 0, 63, 68, 3, 16, 8, 0, 64, 68, 3, 18, 9, 0, 65, 68, 3, 22, 11, 0, 66, 68, 3, 24, 12, 0, 67, 61, 1, 0, 0, 0, 67, 62, 1, 0, 0, 0, 67, 63, 1, 0, 0, 0, 67, 64, 1, 0, 0, 0, 67, 65, 1, 0, 0, 0, 67, 66, 1, 0, 0, 0, 68, 11, 1, 0, 0, 0, 69, 70, 5, 11, 0, 0, 70, 13, 1, 0, 0, 0, 71, 76, 5, 10, 0, 0, 72, 73, 5, 11, 0, 0, 73, 75, 5, 10, 0, 0, 74, 72, 1, 0, 0, 0, 75, 78, 1, 0, 0, 0, 76, 74, 1, 0, 0, 0, 76, 77, 1, 0, 0, 0, 77, 80, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, 79, 81, 5, 11, 0, 0, 80, 79, 1, 0, 0, 0, 80, 81, 1, 0, 0, 0, 81, 15, 1, 0, 0, 0, 82, 84, 5, 6, 0, 0, 83, 85, 5, 11, 0, 0, 84, 83, 1, 0, 0, 0, 84, 85, 1, 0, 0, 0, 85, 17, 1, 0, 0, 0, 86, 87, 5, 7, 0, 0, 87, 88, 5, 5, 0, 0, 88, 90, 5, 7, 0, 0, 89, 91, 3, 14, 7, 0, 90, 89, 1, 0, 0, 0, 90, 91, 1, 0, 0, 0, 91, 19, 1, 0, 0, 0, 92, 97, 5, 9, 0, 0, 93, 95, 5, 3, 0, 0, 94, 93, 1, 0, 0, 0, 94, 95, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 98, 5, 11, 0, 0, 97, 94, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 97, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0, 100, 102, 1, 0, 0, 0, 101, 103, 5, 3, 0, 0, 102, 101, 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 21, 1, 0, 0, 0, 104, 105, 5, 11, 0, 0, 105, 106, 5, 4, 0, 0, 106, 107, 5, 11, 0, 0, 107, 23, 1, 0, 0, 0, 108, 109, 5, 2, 0, 0, 109, 111, 5, 7, 0, 0, 110, 112, 3, 26, 13, 0, 111, 110, 1, 0, 0, 0, 111, 112, 1, 0, 0, 0, 112, 114, 1, 0, 0, 0, 113, 115, 5, 3, 0, 0, 114, 113, 1, 0, 0, 0, 114, 115, 1, 0, 0, 0, 115, 117, 1, 0, 0, 0, 116, 118, 3, 28, 14, 0, 117, 116, 1, 0, 0, 0, 117, 118, 1, 0, 0, 0, 118, 25, 1, 0, 0, 0, 119, 120, 5, 1, 0, 0, 120, 27, 1, 0, 0, 0, 121, 122, 5, 11, 0, 0, 122, 124, 5, 3, 0, 0, 123, 121, 1, 0, 0, 0, 124, 127, 1, 0, 0, 0, 125, 123, 1, 0, 0, 0, 125, 126, 1, 0, 0, 0, 126, 128, 1, 0, 0, 0, 127, 125, 1, 0, 0, 0, 128, 129, 5, 11, 0, 0, 129, 29, 1, 0, 0, 0, 16, 34, 38, 42, 56, 67, 76, 80, 84, 90, 94, 99, 102, 111, 114, 117, 125] \ No newline at end of file diff --git a/handlers/aliases/parser/aliases_parser.go b/handlers/aliases/parser/aliases_parser.go index d5030d8..62919e8 100644 --- a/handlers/aliases/parser/aliases_parser.go +++ b/handlers/aliases/parser/aliases_parser.go @@ -46,7 +46,7 @@ func aliasesParserInit() { } staticData.PredictionContextCache = antlr.NewPredictionContextCache() staticData.serializedATN = []int32{ - 4, 1, 11, 124, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, + 4, 1, 11, 131, 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, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 1, 0, 1, 0, 1, 0, 1, 1, 3, 1, 35, 8, 1, 1, 1, 1, 1, 3, 1, 39, 8, 1, 1, 1, 1, 1, 3, @@ -58,45 +58,48 @@ func aliasesParserInit() { 8, 10, 1, 10, 4, 10, 98, 8, 10, 11, 10, 12, 10, 99, 1, 10, 3, 10, 103, 8, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 3, 12, 112, 8, 12, 1, 12, 3, 12, 115, 8, 12, 1, 12, 3, 12, 118, 8, 12, 1, 13, 1, 13, 1, - 14, 1, 14, 1, 14, 0, 0, 15, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, - 24, 26, 28, 0, 0, 127, 0, 30, 1, 0, 0, 0, 2, 34, 1, 0, 0, 0, 4, 46, 1, - 0, 0, 0, 6, 48, 1, 0, 0, 0, 8, 56, 1, 0, 0, 0, 10, 67, 1, 0, 0, 0, 12, - 69, 1, 0, 0, 0, 14, 71, 1, 0, 0, 0, 16, 82, 1, 0, 0, 0, 18, 86, 1, 0, 0, - 0, 20, 92, 1, 0, 0, 0, 22, 104, 1, 0, 0, 0, 24, 108, 1, 0, 0, 0, 26, 119, - 1, 0, 0, 0, 28, 121, 1, 0, 0, 0, 30, 31, 3, 2, 1, 0, 31, 32, 5, 0, 0, 1, - 32, 1, 1, 0, 0, 0, 33, 35, 5, 3, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, - 0, 0, 35, 36, 1, 0, 0, 0, 36, 38, 3, 6, 3, 0, 37, 39, 5, 3, 0, 0, 38, 37, - 1, 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 1, 0, 0, 0, 40, 42, 3, 4, 2, 0, - 41, 43, 5, 3, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, - 0, 0, 0, 44, 45, 3, 8, 4, 0, 45, 3, 1, 0, 0, 0, 46, 47, 5, 7, 0, 0, 47, - 5, 1, 0, 0, 0, 48, 49, 5, 11, 0, 0, 49, 7, 1, 0, 0, 0, 50, 51, 3, 10, 5, - 0, 51, 52, 5, 8, 0, 0, 52, 53, 5, 3, 0, 0, 53, 55, 1, 0, 0, 0, 54, 50, - 1, 0, 0, 0, 55, 58, 1, 0, 0, 0, 56, 54, 1, 0, 0, 0, 56, 57, 1, 0, 0, 0, - 57, 59, 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, 60, 9, 1, - 0, 0, 0, 61, 68, 3, 12, 6, 0, 62, 68, 3, 14, 7, 0, 63, 68, 3, 16, 8, 0, - 64, 68, 3, 18, 9, 0, 65, 68, 3, 22, 11, 0, 66, 68, 3, 24, 12, 0, 67, 61, - 1, 0, 0, 0, 67, 62, 1, 0, 0, 0, 67, 63, 1, 0, 0, 0, 67, 64, 1, 0, 0, 0, - 67, 65, 1, 0, 0, 0, 67, 66, 1, 0, 0, 0, 68, 11, 1, 0, 0, 0, 69, 70, 5, - 11, 0, 0, 70, 13, 1, 0, 0, 0, 71, 76, 5, 10, 0, 0, 72, 73, 5, 11, 0, 0, - 73, 75, 5, 10, 0, 0, 74, 72, 1, 0, 0, 0, 75, 78, 1, 0, 0, 0, 76, 74, 1, - 0, 0, 0, 76, 77, 1, 0, 0, 0, 77, 80, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, 79, - 81, 5, 11, 0, 0, 80, 79, 1, 0, 0, 0, 80, 81, 1, 0, 0, 0, 81, 15, 1, 0, - 0, 0, 82, 84, 5, 6, 0, 0, 83, 85, 5, 11, 0, 0, 84, 83, 1, 0, 0, 0, 84, - 85, 1, 0, 0, 0, 85, 17, 1, 0, 0, 0, 86, 87, 5, 7, 0, 0, 87, 88, 5, 5, 0, - 0, 88, 90, 5, 7, 0, 0, 89, 91, 3, 14, 7, 0, 90, 89, 1, 0, 0, 0, 90, 91, - 1, 0, 0, 0, 91, 19, 1, 0, 0, 0, 92, 97, 5, 9, 0, 0, 93, 95, 5, 3, 0, 0, - 94, 93, 1, 0, 0, 0, 94, 95, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 98, 5, - 11, 0, 0, 97, 94, 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 97, 1, 0, 0, 0, 99, - 100, 1, 0, 0, 0, 100, 102, 1, 0, 0, 0, 101, 103, 5, 3, 0, 0, 102, 101, - 1, 0, 0, 0, 102, 103, 1, 0, 0, 0, 103, 21, 1, 0, 0, 0, 104, 105, 5, 11, - 0, 0, 105, 106, 5, 4, 0, 0, 106, 107, 5, 11, 0, 0, 107, 23, 1, 0, 0, 0, - 108, 109, 5, 2, 0, 0, 109, 111, 5, 7, 0, 0, 110, 112, 3, 26, 13, 0, 111, - 110, 1, 0, 0, 0, 111, 112, 1, 0, 0, 0, 112, 114, 1, 0, 0, 0, 113, 115, - 5, 3, 0, 0, 114, 113, 1, 0, 0, 0, 114, 115, 1, 0, 0, 0, 115, 117, 1, 0, - 0, 0, 116, 118, 3, 28, 14, 0, 117, 116, 1, 0, 0, 0, 117, 118, 1, 0, 0, - 0, 118, 25, 1, 0, 0, 0, 119, 120, 5, 1, 0, 0, 120, 27, 1, 0, 0, 0, 121, - 122, 5, 11, 0, 0, 122, 29, 1, 0, 0, 0, 15, 34, 38, 42, 56, 67, 76, 80, - 84, 90, 94, 99, 102, 111, 114, 117, + 14, 1, 14, 5, 14, 124, 8, 14, 10, 14, 12, 14, 127, 9, 14, 1, 14, 1, 14, + 1, 14, 0, 0, 15, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, + 0, 0, 135, 0, 30, 1, 0, 0, 0, 2, 34, 1, 0, 0, 0, 4, 46, 1, 0, 0, 0, 6, + 48, 1, 0, 0, 0, 8, 56, 1, 0, 0, 0, 10, 67, 1, 0, 0, 0, 12, 69, 1, 0, 0, + 0, 14, 71, 1, 0, 0, 0, 16, 82, 1, 0, 0, 0, 18, 86, 1, 0, 0, 0, 20, 92, + 1, 0, 0, 0, 22, 104, 1, 0, 0, 0, 24, 108, 1, 0, 0, 0, 26, 119, 1, 0, 0, + 0, 28, 125, 1, 0, 0, 0, 30, 31, 3, 2, 1, 0, 31, 32, 5, 0, 0, 1, 32, 1, + 1, 0, 0, 0, 33, 35, 5, 3, 0, 0, 34, 33, 1, 0, 0, 0, 34, 35, 1, 0, 0, 0, + 35, 36, 1, 0, 0, 0, 36, 38, 3, 6, 3, 0, 37, 39, 5, 3, 0, 0, 38, 37, 1, + 0, 0, 0, 38, 39, 1, 0, 0, 0, 39, 40, 1, 0, 0, 0, 40, 42, 3, 4, 2, 0, 41, + 43, 5, 3, 0, 0, 42, 41, 1, 0, 0, 0, 42, 43, 1, 0, 0, 0, 43, 44, 1, 0, 0, + 0, 44, 45, 3, 8, 4, 0, 45, 3, 1, 0, 0, 0, 46, 47, 5, 7, 0, 0, 47, 5, 1, + 0, 0, 0, 48, 49, 5, 11, 0, 0, 49, 7, 1, 0, 0, 0, 50, 51, 3, 10, 5, 0, 51, + 52, 5, 8, 0, 0, 52, 53, 5, 3, 0, 0, 53, 55, 1, 0, 0, 0, 54, 50, 1, 0, 0, + 0, 55, 58, 1, 0, 0, 0, 56, 54, 1, 0, 0, 0, 56, 57, 1, 0, 0, 0, 57, 59, + 1, 0, 0, 0, 58, 56, 1, 0, 0, 0, 59, 60, 3, 10, 5, 0, 60, 9, 1, 0, 0, 0, + 61, 68, 3, 12, 6, 0, 62, 68, 3, 14, 7, 0, 63, 68, 3, 16, 8, 0, 64, 68, + 3, 18, 9, 0, 65, 68, 3, 22, 11, 0, 66, 68, 3, 24, 12, 0, 67, 61, 1, 0, + 0, 0, 67, 62, 1, 0, 0, 0, 67, 63, 1, 0, 0, 0, 67, 64, 1, 0, 0, 0, 67, 65, + 1, 0, 0, 0, 67, 66, 1, 0, 0, 0, 68, 11, 1, 0, 0, 0, 69, 70, 5, 11, 0, 0, + 70, 13, 1, 0, 0, 0, 71, 76, 5, 10, 0, 0, 72, 73, 5, 11, 0, 0, 73, 75, 5, + 10, 0, 0, 74, 72, 1, 0, 0, 0, 75, 78, 1, 0, 0, 0, 76, 74, 1, 0, 0, 0, 76, + 77, 1, 0, 0, 0, 77, 80, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, 79, 81, 5, 11, + 0, 0, 80, 79, 1, 0, 0, 0, 80, 81, 1, 0, 0, 0, 81, 15, 1, 0, 0, 0, 82, 84, + 5, 6, 0, 0, 83, 85, 5, 11, 0, 0, 84, 83, 1, 0, 0, 0, 84, 85, 1, 0, 0, 0, + 85, 17, 1, 0, 0, 0, 86, 87, 5, 7, 0, 0, 87, 88, 5, 5, 0, 0, 88, 90, 5, + 7, 0, 0, 89, 91, 3, 14, 7, 0, 90, 89, 1, 0, 0, 0, 90, 91, 1, 0, 0, 0, 91, + 19, 1, 0, 0, 0, 92, 97, 5, 9, 0, 0, 93, 95, 5, 3, 0, 0, 94, 93, 1, 0, 0, + 0, 94, 95, 1, 0, 0, 0, 95, 96, 1, 0, 0, 0, 96, 98, 5, 11, 0, 0, 97, 94, + 1, 0, 0, 0, 98, 99, 1, 0, 0, 0, 99, 97, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0, + 100, 102, 1, 0, 0, 0, 101, 103, 5, 3, 0, 0, 102, 101, 1, 0, 0, 0, 102, + 103, 1, 0, 0, 0, 103, 21, 1, 0, 0, 0, 104, 105, 5, 11, 0, 0, 105, 106, + 5, 4, 0, 0, 106, 107, 5, 11, 0, 0, 107, 23, 1, 0, 0, 0, 108, 109, 5, 2, + 0, 0, 109, 111, 5, 7, 0, 0, 110, 112, 3, 26, 13, 0, 111, 110, 1, 0, 0, + 0, 111, 112, 1, 0, 0, 0, 112, 114, 1, 0, 0, 0, 113, 115, 5, 3, 0, 0, 114, + 113, 1, 0, 0, 0, 114, 115, 1, 0, 0, 0, 115, 117, 1, 0, 0, 0, 116, 118, + 3, 28, 14, 0, 117, 116, 1, 0, 0, 0, 117, 118, 1, 0, 0, 0, 118, 25, 1, 0, + 0, 0, 119, 120, 5, 1, 0, 0, 120, 27, 1, 0, 0, 0, 121, 122, 5, 11, 0, 0, + 122, 124, 5, 3, 0, 0, 123, 121, 1, 0, 0, 0, 124, 127, 1, 0, 0, 0, 125, + 123, 1, 0, 0, 0, 125, 126, 1, 0, 0, 0, 126, 128, 1, 0, 0, 0, 127, 125, + 1, 0, 0, 0, 128, 129, 5, 11, 0, 0, 129, 29, 1, 0, 0, 0, 16, 34, 38, 42, + 56, 67, 76, 80, 84, 90, 94, 99, 102, 111, 114, 117, 125, } deserializer := antlr.NewATNDeserializer(nil) staticData.atn = deserializer.Deserialize(staticData.serializedATN) @@ -2266,7 +2269,10 @@ type IErrorMessageContext interface { GetParser() antlr.Parser // Getter signatures - STRING() antlr.TerminalNode + AllSTRING() []antlr.TerminalNode + STRING(i int) antlr.TerminalNode + AllSEPARATOR() []antlr.TerminalNode + SEPARATOR(i int) antlr.TerminalNode // IsErrorMessageContext differentiates from other interfaces. IsErrorMessageContext() @@ -2304,8 +2310,20 @@ func NewErrorMessageContext(parser antlr.Parser, parent antlr.ParserRuleContext, func (s *ErrorMessageContext) GetParser() antlr.Parser { return s.parser } -func (s *ErrorMessageContext) STRING() antlr.TerminalNode { - return s.GetToken(AliasesParserSTRING, 0) +func (s *ErrorMessageContext) AllSTRING() []antlr.TerminalNode { + return s.GetTokens(AliasesParserSTRING) +} + +func (s *ErrorMessageContext) STRING(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserSTRING, i) +} + +func (s *ErrorMessageContext) AllSEPARATOR() []antlr.TerminalNode { + return s.GetTokens(AliasesParserSEPARATOR) +} + +func (s *ErrorMessageContext) SEPARATOR(i int) antlr.TerminalNode { + return s.GetToken(AliasesParserSEPARATOR, i) } func (s *ErrorMessageContext) GetRuleContext() antlr.RuleContext { @@ -2331,9 +2349,50 @@ func (s *ErrorMessageContext) ExitRule(listener antlr.ParseTreeListener) { func (p *AliasesParser) ErrorMessage() (localctx IErrorMessageContext) { localctx = NewErrorMessageContext(p, p.GetParserRuleContext(), p.GetState()) p.EnterRule(localctx, 28, AliasesParserRULE_errorMessage) + var _alt int + p.EnterOuterAlt(localctx, 1) + p.SetState(125) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 15, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + for _alt != 2 && _alt != antlr.ATNInvalidAltNumber { + if _alt == 1 { + { + p.SetState(121) + p.Match(AliasesParserSTRING) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + { + p.SetState(122) + p.Match(AliasesParserSEPARATOR) + if p.HasError() { + // Recognition error - abort rule + goto errorExit + } + } + + } + p.SetState(127) + p.GetErrorHandler().Sync(p) + if p.HasError() { + goto errorExit + } + _alt = p.GetInterpreter().AdaptivePredict(p.BaseParser, p.GetTokenStream(), 15, p.GetParserRuleContext()) + if p.HasError() { + goto errorExit + } + } { - p.SetState(121) + p.SetState(128) p.Match(AliasesParserSTRING) if p.HasError() { // Recognition error - abort rule From d7f8b905a4472db3a3c2451b6506b9419b9b43c3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:35:47 +0200 Subject: [PATCH 16/24] feat(aliases): Add duplicates analyzer --- handlers/aliases/analyzer/analyzer.go | 1 + handlers/aliases/analyzer/double_values.go | 193 ++++++++++++++++++ .../aliases/analyzer/double_values_test.go | 119 +++++++++++ handlers/aliases/ast/values.go | 39 ++-- 4 files changed, 338 insertions(+), 14 deletions(-) create mode 100644 handlers/aliases/analyzer/double_values.go create mode 100644 handlers/aliases/analyzer/double_values_test.go diff --git a/handlers/aliases/analyzer/analyzer.go b/handlers/aliases/analyzer/analyzer.go index 6b7d0f8..68b6382 100644 --- a/handlers/aliases/analyzer/analyzer.go +++ b/handlers/aliases/analyzer/analyzer.go @@ -27,6 +27,7 @@ func Analyze( } errors = append(errors, analyzeContainsRequiredKeys(*d)...) + errors = append(errors, analyzeContainsNoDoubleValues(*d.Parser)...) return utils.Map( errors, diff --git a/handlers/aliases/analyzer/double_values.go b/handlers/aliases/analyzer/double_values.go new file mode 100644 index 0000000..da42351 --- /dev/null +++ b/handlers/aliases/analyzer/double_values.go @@ -0,0 +1,193 @@ +package analyzer + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + "config-lsp/utils" + "errors" + "fmt" +) + +var valueHandlerMap = map[string]func( + rawValue []ast.AliasValueInterface, +) []common.LSPError{ + "AliasValueUser": analyzeValueUser, + "AliasValueEmail": analyzeValueEmail, + "AliasValueCommand": analyzeValueCommand, + "AliasValueFile": analyzeValueFile, + "AliasValueInclude": analyzeValueInclude, + "AliasValueError": analyzeValueError, +} + +func analyzeContainsNoDoubleValues( + p ast.AliasesParser, +) []common.LSPError { + errors := make([]common.LSPError, 0) + + it := p.Aliases.Iterator() + + for it.Next() { + entry := it.Value().(*ast.AliasEntry) + + valuesPerType := utils.Group( + entry.Values.Values, + func(entry ast.AliasValueInterface) string { + return entry.GetStructName() + }, + ) + + for valueType, values := range valuesPerType { + handler := valueHandlerMap[valueType] + + newErrors := handler(values) + errors = append(errors, newErrors...) + } + } + + return errors +} + +func analyzeValueUser( + rawValues []ast.AliasValueInterface, +) []common.LSPError { + users := make(map[string]struct{}) + errs := make([]common.LSPError, 0) + + // Simple double value check + for _, rawValue := range rawValues { + value := rawValue.(ast.AliasValueUser) + key := indexes.NormalizeKey(value.Value) + + if _, found := users[key]; found { + errs = append(errs, common.LSPError{ + Range: value.Location, + Err: errors.New(fmt.Sprintf("User '%s' is defined multiple times", key)), + }) + } else { + users[key] = struct{}{} + } + + } + + return errs +} + +func analyzeValueEmail( + rawValues []ast.AliasValueInterface, +) []common.LSPError { + emails := make(map[string]struct{}) + errs := make([]common.LSPError, 0) + + for _, rawValue := range rawValues { + value := rawValue.(ast.AliasValueEmail) + + // Simple double value check + if _, found := emails[value.Value]; found { + errs = append(errs, common.LSPError{ + Range: value.Location, + Err: errors.New(fmt.Sprintf("Email '%s' is defined multiple times", value.Value)), + }) + } else { + emails[value.Value] = struct{}{} + } + } + + return errs +} + +func analyzeValueCommand( + rawValues []ast.AliasValueInterface, +) []common.LSPError { + commands := make(map[string]struct{}) + errs := make([]common.LSPError, 0) + + for _, rawValue := range rawValues { + value := rawValue.(ast.AliasValueCommand) + command := value.Command + + // Simple double value check + if _, found := commands[command]; found { + errs = append(errs, common.LSPError{ + Range: value.Location, + Err: errors.New(fmt.Sprintf("Command '%s' is defined multiple times", command)), + }) + } else { + commands[command] = struct{}{} + } + } + + return errs +} + +func analyzeValueFile( + rawValues []ast.AliasValueInterface, +) []common.LSPError { + files := make(map[string]struct{}) + errs := make([]common.LSPError, 0) + + for _, rawValue := range rawValues { + value := rawValue.(ast.AliasValueFile) + path := string(value.Path) + + // Simple double value check + if _, found := files[path]; found { + errs = append(errs, common.LSPError{ + Range: value.Location, + Err: errors.New(fmt.Sprintf("File '%s' is defined multiple times", path)), + }) + } else { + files[path] = struct{}{} + } + } + + return errs +} + +func analyzeValueInclude( + rawValues []ast.AliasValueInterface, +) []common.LSPError { + files := make(map[string]struct{}) + errs := make([]common.LSPError, 0) + + for _, rawValue := range rawValues { + value := rawValue.(ast.AliasValueInclude) + path := string(value.Path.Path) + + // Simple double value check + if _, found := files[path]; found { + errs = append(errs, common.LSPError{ + Range: value.Location, + Err: errors.New(fmt.Sprintf("Inclusion '%s' is included multiple times", path)), + }) + } else { + files[path] = struct{}{} + } + } + + return errs +} + +func analyzeValueError( + rawValues []ast.AliasValueInterface, +) []common.LSPError { + codes := make(map[uint16]struct{}) + errs := make([]common.LSPError, 0) + + for _, rawValue := range rawValues { + value := rawValue.(ast.AliasValueError) + code := value.Code.ErrorCodeAsInt() + + // Simple double value check + if _, found := codes[code]; found { + errs = append(errs, common.LSPError{ + Range: value.Location, + Err: errors.New(fmt.Sprintf("Error code '%d' is defined multiple times", code)), + }) + } else { + codes[code] = struct{}{} + } + } + + return errs +} diff --git a/handlers/aliases/analyzer/double_values_test.go b/handlers/aliases/analyzer/double_values_test.go new file mode 100644 index 0000000..57ae1b6 --- /dev/null +++ b/handlers/aliases/analyzer/double_values_test.go @@ -0,0 +1,119 @@ +package analyzer + +import ( + "config-lsp/handlers/aliases/ast" + "config-lsp/utils" + "testing" +) + +func TestContainsDoubleUsers( + t *testing.T, +) { + input := utils.Dedent(` +alice: root +master: alice, alice +`) + p := ast.NewAliasesParser() + errors := p.Parse(input) + + // d := aliases.AliasesDocument{ + // Parser: &p, + // } + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + errors = analyzeContainsNoDoubleValues(p) + + if !(len(errors) == 1) { + t.Errorf("Expected errors, got none") + } +} + +func TestContainsDoubleEmails( + t *testing.T, +) { + input := utils.Dedent(` +alice: root@localhost, some, noise, here, root@localhost +`) + p := ast.NewAliasesParser() + errors := p.Parse(input) + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + errors = analyzeContainsNoDoubleValues(p) + + if !(len(errors) == 1) { + t.Errorf("Expected errors, got none") + } +} + +func TestContainsDoubleCommands( + t *testing.T, +) { + input := utils.Dedent(` +alice: |echo, |test, |echo +`) + p := ast.NewAliasesParser() + errors := p.Parse(input) + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + errors = analyzeContainsNoDoubleValues(p) + + if !(len(errors) == 1) { + t.Errorf("Expected errors, got none") + } +} + +func TestContainsDoubleErrors( + t *testing.T, +) { + input := utils.Dedent(` +alice: error:450 Nonono, error:450 Some other message +root: error:450 Nonon, error:451 This is not okay +`) + p := ast.NewAliasesParser() + errors := p.Parse(input) + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + errors = analyzeContainsNoDoubleValues(p) + + if !(len(errors) == 1) { + t.Errorf("Expected no errors, got %v", errors) + } + + if !(errors[0].Range.Start.Line == 0 && errors[0].Range.End.Line == 0) { + t.Errorf("Expected error to be on line 0, got %v-%v", errors[0].Range.Start.Line, errors[0].Range.End.Line) + } +} + +func TestComplexExampleContainsNoDoubleValues( + t *testing.T, +) { + input := utils.Dedent(` +alice: root@localhost, user@localhost +master: alice, root +noreply: error:450 Nonono +`) + p := ast.NewAliasesParser() + errors := p.Parse(input) + + if len(errors) != 0 { + t.Fatalf("Expected no errors, got %v", errors) + } + + errors = analyzeContainsNoDoubleValues(p) + + if !(len(errors) == 0) { + t.Errorf("Expected no errors, got %v", errors) + } +} diff --git a/handlers/aliases/ast/values.go b/handlers/aliases/ast/values.go index d468b62..accc7ab 100644 --- a/handlers/aliases/ast/values.go +++ b/handlers/aliases/ast/values.go @@ -11,6 +11,7 @@ import ( type AliasValueInterface interface { GetAliasValue() AliasValue + GetStructName() string } func (a AliasValue) String() string { @@ -21,6 +22,10 @@ func (a AliasValue) GetAliasValue() AliasValue { return a } +func (a AliasValue) GetStructName() string { + return "AliasValue" +} + type AliasValue struct { Location common.LocationRange Value string @@ -30,6 +35,10 @@ type AliasValueUser struct { AliasValue } +func (a AliasValueUser) GetStructName() string { + return "AliasValueUser" +} + type path string type AliasValueFile struct { @@ -37,13 +46,8 @@ type AliasValueFile struct { Path path } -func (a AliasValueFile) CheckIsValid() []common.LSPError { - return utils.Map( - fields.PathField.CheckIsValid(string(a.Path)), - func(invalidValue *docvalues.InvalidValue) common.LSPError { - return docvalues.LSPErrorFromInvalidValue(a.Location.Start.Line, *invalidValue) - }, - ) +func (a AliasValueFile) GetStructName() string { + return "AliasValueFile" } type AliasValueCommand struct { @@ -51,13 +55,8 @@ type AliasValueCommand struct { Command string } -func (a AliasValueCommand) CheckIsValid() []common.LSPError { - return utils.Map( - fields.CommandField.CheckIsValid(a.Command), - func(invalidValue *docvalues.InvalidValue) common.LSPError { - return docvalues.LSPErrorFromInvalidValue(a.Location.Start.Line, *invalidValue) - }, - ) +func (a AliasValueCommand) GetStructName() string { + return "AliasValueCommand" } type AliasValueIncludePath struct { @@ -79,6 +78,10 @@ func (a AliasValueInclude) CheckIsValid() []common.LSPError { ) } +func (a AliasValueInclude) GetStructName() string { + return "AliasValueInclude" +} + type AliasValueEmail struct { AliasValue } @@ -92,6 +95,10 @@ func (a AliasValueEmail) CheckIsValid() []common.LSPError { ) } +func (a AliasValueEmail) GetStructName() string { + return "AliasValueEmail" +} + type AliasValueError struct { AliasValue @@ -103,6 +110,10 @@ type AliasValueErrorCode struct { AliasValue } +func (a AliasValueError) GetStructName() string { + return "AliasValueError" +} + func (a AliasValueErrorCode) ErrorCodeAsInt() uint16 { code, err := strconv.Atoi(a.Value) From 89d11a16dc27648e920e1fea0263d369cfa0e955 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:41:54 +0200 Subject: [PATCH 17/24] refactor: Improve utils package structure --- doc-values/base-value.go | 1 - doc-values/utils.go | 1 - handlers/aliases/analyzer/analyzer.go | 1 - .../aliases/lsp/text-document-did-change.go | 1 - .../aliases/lsp/text-document-did-open.go | 1 - handlers/fstab/text-document-did-change.go | 1 - handlers/fstab/text-document-did-open.go | 1 - handlers/hosts/analyzer/analyzer.go | 1 - .../hosts/lsp/text-document-did-change.go | 1 - handlers/hosts/lsp/text-document-did-open.go | 1 - handlers/openssh/diagnostics.go | 1 - .../wireguard/lsp/text-document-did-change.go | 1 - .../wireguard/lsp/text-document-did-open.go | 1 - utils/common.go | 166 ------------------ utils/ip-host.go | 2 +- utils/slices.go | 143 +++++++++++++++ utils/strings.go | 32 ++++ utils/text.go | 13 -- 18 files changed, 176 insertions(+), 193 deletions(-) create mode 100644 utils/slices.go delete mode 100644 utils/text.go diff --git a/doc-values/base-value.go b/doc-values/base-value.go index b2c2ba4..2a2d3d6 100644 --- a/doc-values/base-value.go +++ b/doc-values/base-value.go @@ -2,7 +2,6 @@ package docvalues import ( "config-lsp/utils" - protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/doc-values/utils.go b/doc-values/utils.go index 68e7326..8d5a403 100644 --- a/doc-values/utils.go +++ b/doc-values/utils.go @@ -2,7 +2,6 @@ package docvalues import ( "config-lsp/utils" - protocol "github.com/tliron/glsp/protocol_3_16" "golang.org/x/exp/slices" ) diff --git a/handlers/aliases/analyzer/analyzer.go b/handlers/aliases/analyzer/analyzer.go index 68b6382..dd1fc38 100644 --- a/handlers/aliases/analyzer/analyzer.go +++ b/handlers/aliases/analyzer/analyzer.go @@ -4,7 +4,6 @@ import ( "config-lsp/common" "config-lsp/handlers/aliases" "config-lsp/utils" - protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/handlers/aliases/lsp/text-document-did-change.go b/handlers/aliases/lsp/text-document-did-change.go index 87673d4..b8dd484 100644 --- a/handlers/aliases/lsp/text-document-did-change.go +++ b/handlers/aliases/lsp/text-document-did-change.go @@ -5,7 +5,6 @@ import ( "config-lsp/handlers/aliases" "config-lsp/handlers/aliases/analyzer" "config-lsp/utils" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/handlers/aliases/lsp/text-document-did-open.go b/handlers/aliases/lsp/text-document-did-open.go index 81f9c1e..bccf0aa 100644 --- a/handlers/aliases/lsp/text-document-did-open.go +++ b/handlers/aliases/lsp/text-document-did-open.go @@ -6,7 +6,6 @@ import ( "config-lsp/handlers/aliases/analyzer" "config-lsp/handlers/aliases/ast" "config-lsp/utils" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/handlers/fstab/text-document-did-change.go b/handlers/fstab/text-document-did-change.go index b192dcf..8b465b3 100644 --- a/handlers/fstab/text-document-did-change.go +++ b/handlers/fstab/text-document-did-change.go @@ -3,7 +3,6 @@ package fstab import ( "config-lsp/common" "config-lsp/utils" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/handlers/fstab/text-document-did-open.go b/handlers/fstab/text-document-did-open.go index 2380cb9..d64f3ea 100644 --- a/handlers/fstab/text-document-did-open.go +++ b/handlers/fstab/text-document-did-open.go @@ -3,7 +3,6 @@ package fstab import ( "config-lsp/common" "config-lsp/utils" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/handlers/hosts/analyzer/analyzer.go b/handlers/hosts/analyzer/analyzer.go index 9a1c617..0063a64 100644 --- a/handlers/hosts/analyzer/analyzer.go +++ b/handlers/hosts/analyzer/analyzer.go @@ -4,7 +4,6 @@ import ( "config-lsp/common" "config-lsp/handlers/hosts" "config-lsp/utils" - protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/handlers/hosts/lsp/text-document-did-change.go b/handlers/hosts/lsp/text-document-did-change.go index 0b4bb34..af39187 100644 --- a/handlers/hosts/lsp/text-document-did-change.go +++ b/handlers/hosts/lsp/text-document-did-change.go @@ -5,7 +5,6 @@ import ( "config-lsp/handlers/hosts" "config-lsp/handlers/hosts/analyzer" "config-lsp/utils" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/handlers/hosts/lsp/text-document-did-open.go b/handlers/hosts/lsp/text-document-did-open.go index 6e17715..2e07550 100644 --- a/handlers/hosts/lsp/text-document-did-open.go +++ b/handlers/hosts/lsp/text-document-did-open.go @@ -7,7 +7,6 @@ import ( "config-lsp/handlers/hosts/ast" "config-lsp/handlers/hosts/indexes" "config-lsp/utils" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/handlers/openssh/diagnostics.go b/handlers/openssh/diagnostics.go index f38bb13..2fe3c92 100644 --- a/handlers/openssh/diagnostics.go +++ b/handlers/openssh/diagnostics.go @@ -3,7 +3,6 @@ package openssh import ( docvalues "config-lsp/doc-values" "config-lsp/utils" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/handlers/wireguard/lsp/text-document-did-change.go b/handlers/wireguard/lsp/text-document-did-change.go index 103755c..c5ec400 100644 --- a/handlers/wireguard/lsp/text-document-did-change.go +++ b/handlers/wireguard/lsp/text-document-did-change.go @@ -4,7 +4,6 @@ import ( "config-lsp/common" "config-lsp/handlers/wireguard/handlers" "config-lsp/utils" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/handlers/wireguard/lsp/text-document-did-open.go b/handlers/wireguard/lsp/text-document-did-open.go index fe003d4..fc9e0b2 100644 --- a/handlers/wireguard/lsp/text-document-did-open.go +++ b/handlers/wireguard/lsp/text-document-did-open.go @@ -4,7 +4,6 @@ import ( "config-lsp/common" "config-lsp/handlers/wireguard/parser" "config-lsp/utils" - "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) diff --git a/utils/common.go b/utils/common.go index d5f7642..ba45c9c 100644 --- a/utils/common.go +++ b/utils/common.go @@ -2,142 +2,8 @@ package utils import ( "os" - "strings" ) -func GetLine(path string, line int) (string, error) { - path = path[len("file://"):] - readBytes, err := os.ReadFile(path) - - if err != nil { - return "", err - } - - // Split file into lines - lines := strings.Split(string(readBytes), "\n") - - return lines[line], nil -} - -func Map[T any, O any](values []T, f func(T) O) []O { - result := make([]O, len(values)) - - for index, value := range values { - result[index] = f(value) - } - - return result -} - -func MapMap[T comparable, O any, P any](values map[T]O, f func(T, O) P) map[T]P { - result := make(map[T]P) - - for key, value := range values { - result[key] = f(key, value) - } - - return result -} - -func MapMapToSlice[T comparable, O any, P any](values map[T]O, f func(T, O) P) []P { - result := make([]P, 0) - - for key, value := range values { - result = append(result, f(key, value)) - } - - return result -} - -func SliceToSet[T comparable](values []T) map[T]struct{} { - set := make(map[T]struct{}) - - for _, value := range values { - set[value] = struct{}{} - } - - return set -} - -func SliceToMap[T comparable, O any](values []T, defaultValue O) map[T]O { - set := make(map[T]O) - - for _, value := range values { - set[value] = defaultValue - } - - return set -} - -func FilterWhere[T any](values []T, f func(T) bool) []T { - result := make([]T, 0) - - for _, value := range values { - if f(value) { - result = append(result, value) - } - } - - return result -} - -func FilterMap[T comparable, O any]( - values map[T]O, - f func(T, O) bool, -) map[T]O { - result := make(map[T]O) - - for key, value := range values { - if f(key, value) { - result[key] = value - } - } - - return result -} - -func FilterMapWhere[T comparable, O any](values map[T]O, f func(T, O) bool) map[T]O { - result := make(map[T]O) - - for key, value := range values { - if f(key, value) { - result[key] = value - } - } - - return result -} - -func KeysAsSet[T comparable, O any](values map[T]O) map[T]struct{} { - set := make(map[T]struct{}) - - for key := range values { - set[key] = struct{}{} - } - - return set -} - -func KeysOfMap[T comparable, O any](values map[T]O) []T { - keys := make([]T, 0) - - for key := range values { - keys = append(keys, key) - } - - return keys -} - -func ValuesOfMap[T comparable, O any](values map[T]O) []O { - keys := make([]O, 0) - - for _, value := range values { - keys = append(keys, value) - } - - return keys -} - func DoesPathExist(path string) bool { _, err := os.Stat(path) @@ -155,35 +21,3 @@ func IsPathFile(path string) bool { return err == nil && !info.IsDir() } - -func FindPreviousCharacter(line string, character string, startIndex int) (int, bool) { - for index := startIndex; index >= 0; index-- { - if string(line[index]) == character { - return index, true - } - } - - return 0, false -} - -func FindNextCharacter(line string, character string, startIndex int) (int, bool) { - for index := startIndex; index < len(line); index++ { - if string(line[index]) == character { - return index, true - } - } - - return 0, false -} - -func MergeMaps[T comparable, O any](maps ...map[T]O) map[T]O { - result := make(map[T]O) - - for _, m := range maps { - for key, value := range m { - result[key] = value - } - } - - return result -} diff --git a/utils/ip-host.go b/utils/ip-host.go index 4fab166..84d3a05 100644 --- a/utils/ip-host.go +++ b/utils/ip-host.go @@ -74,7 +74,7 @@ func CreateIPv4HostSet() IPv4HostSet { } } -// Add a new ip to the host set +// AddIP Add a new ip to the host set // `hostAmount`: Amount of host bits // Return: (, ) func (h *IPv4HostSet) AddIP( diff --git a/utils/slices.go b/utils/slices.go new file mode 100644 index 0000000..0b74dab --- /dev/null +++ b/utils/slices.go @@ -0,0 +1,143 @@ +package utils + +func Group[T any, V comparable](values []T, f func(T) V) map[V][]T { + result := make(map[V][]T) + + for _, value := range values { + key := f(value) + result[key] = append(result[key], value) + } + + return result +} + +func Map[T any, O any](values []T, f func(T) O) []O { + result := make([]O, len(values)) + + for index, value := range values { + result[index] = f(value) + } + + return result +} + +func MapMap[T comparable, O any, P any](values map[T]O, f func(T, O) P) map[T]P { + result := make(map[T]P) + + for key, value := range values { + result[key] = f(key, value) + } + + return result +} + +func MapMapToSlice[T comparable, O any, P any](values map[T]O, f func(T, O) P) []P { + result := make([]P, 0) + + for key, value := range values { + result = append(result, f(key, value)) + } + + return result +} + +func SliceToSet[T comparable](values []T) map[T]struct{} { + set := make(map[T]struct{}) + + for _, value := range values { + set[value] = struct{}{} + } + + return set +} + +func SliceToMap[T comparable, O any](values []T, defaultValue O) map[T]O { + set := make(map[T]O) + + for _, value := range values { + set[value] = defaultValue + } + + return set +} + +func FilterWhere[T any](values []T, f func(T) bool) []T { + result := make([]T, 0) + + for _, value := range values { + if f(value) { + result = append(result, value) + } + } + + return result +} + +func FilterMap[T comparable, O any]( + values map[T]O, + f func(T, O) bool, +) map[T]O { + result := make(map[T]O) + + for key, value := range values { + if f(key, value) { + result[key] = value + } + } + + return result +} + +func FilterMapWhere[T comparable, O any](values map[T]O, f func(T, O) bool) map[T]O { + result := make(map[T]O) + + for key, value := range values { + if f(key, value) { + result[key] = value + } + } + + return result +} + +func KeysAsSet[T comparable, O any](values map[T]O) map[T]struct{} { + set := make(map[T]struct{}) + + for key := range values { + set[key] = struct{}{} + } + + return set +} + +func KeysOfMap[T comparable, O any](values map[T]O) []T { + keys := make([]T, 0) + + for key := range values { + keys = append(keys, key) + } + + return keys +} + +func ValuesOfMap[T comparable, O any](values map[T]O) []O { + keys := make([]O, 0) + + for _, value := range values { + keys = append(keys, value) + } + + return keys +} + +func MergeMaps[T comparable, O any](maps ...map[T]O) map[T]O { + result := make(map[T]O) + + for _, m := range maps { + for key, value := range m { + result[key] = value + } + } + + return result +} diff --git a/utils/strings.go b/utils/strings.go index 4e85934..1e4f429 100644 --- a/utils/strings.go +++ b/utils/strings.go @@ -19,3 +19,35 @@ func GetTrimIndex(s string) []int { func SplitIntoLines(s string) []string { return regexp.MustCompile("\r?\n").Split(s, -1) } + +func FindPreviousCharacter(line string, character string, startIndex int) (int, bool) { + for index := startIndex; index >= 0; index-- { + if string(line[index]) == character { + return index, true + } + } + + return 0, false +} + +func FindNextCharacter(line string, character string, startIndex int) (int, bool) { + for index := startIndex; index < len(line); index++ { + if string(line[index]) == character { + return index, true + } + } + + return 0, false +} + +func CountCharacterOccurrences(line string, character rune) int { + count := 0 + + for _, c := range line { + if c == character { + count++ + } + } + + return count +} diff --git a/utils/text.go b/utils/text.go deleted file mode 100644 index 26183b3..0000000 --- a/utils/text.go +++ /dev/null @@ -1,13 +0,0 @@ -package utils - -func CountCharacterOccurrences(line string, character rune) int { - count := 0 - - for _, c := range line { - if c == character { - count++ - } - } - - return count -} From c5d9634ab3c411915960159997cec33e4ee72ace Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 14:49:45 +0200 Subject: [PATCH 18/24] fix(aliases): Do not provide completions for already defined users --- handlers/aliases/handlers/completions.go | 33 ++++++++++++++++++++---- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/handlers/aliases/handlers/completions.go b/handlers/aliases/handlers/completions.go index 615c765..61ca11d 100644 --- a/handlers/aliases/handlers/completions.go +++ b/handlers/aliases/handlers/completions.go @@ -51,6 +51,8 @@ func GetCompletionsForEntry( value := GetValueAtCursor(cursor, entry) relativeCursor := cursor - entry.Key.Location.Start.Character + excludedUsers := getUsersFromEntry(entry) + if value == nil { completions = append(completions, getCommandCompletion()) completions = append(completions, getIncludeCompletion()) @@ -58,12 +60,11 @@ func GetCompletionsForEntry( completions = append(completions, getUserCompletions( i, - indexes.NormalizeKey(entry.Key.Value), + excludedUsers, "", 0, )...) - println("la completions etaient", completions) return completions, nil } @@ -73,7 +74,7 @@ func GetCompletionsForEntry( return getUserCompletions( i, - indexes.NormalizeKey(entry.Key.Value), + excludedUsers, userValue.Value, relativeCursor, ), nil @@ -126,7 +127,7 @@ func getErrorCompletion() protocol.CompletionItem { func getUserCompletions( i *indexes.AliasesIndexes, - excludeKey string, + excluded map[string]struct{}, line string, cursor uint32, ) []protocol.CompletionItem { @@ -137,7 +138,7 @@ func getUserCompletions( completions := make([]protocol.CompletionItem, 0) for name, user := range users { - if name == excludeKey { + if _, found := excluded[name]; found { continue } @@ -150,3 +151,25 @@ func getUserCompletions( return completions } + +func getUsersFromEntry( + entry *ast.AliasEntry, +) map[string]struct{} { + users := map[string]struct{}{ + indexes.NormalizeKey(entry.Key.Value): {}, + } + + if entry.Values != nil { + for _, value := range entry.Values.Values { + switch (value).(type) { + case ast.AliasValueUser: + userValue := value.(ast.AliasValueUser) + + users[indexes.NormalizeKey(userValue.Value)] = struct{}{} + } + } + } + + return users +} + From 654675524cb0a0648e09cb2ddbc99d2196e5c3b0 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 15:45:52 +0200 Subject: [PATCH 19/24] feat(aliases): Add support for textDocument/definition --- common/fetchers.go | 4 +- handlers/aliases/handlers/completions.go | 3 +- handlers/aliases/handlers/go_to_definition.go | 94 +++++++++++++++++++ .../aliases/lsp/text-document-definition.go | 40 ++++++++ root-handler/handler.go | 1 + root-handler/text-document-definition.go | 37 ++++++++ 6 files changed, 176 insertions(+), 3 deletions(-) create mode 100644 handlers/aliases/handlers/go_to_definition.go create mode 100644 handlers/aliases/lsp/text-document-definition.go create mode 100644 root-handler/text-document-definition.go diff --git a/common/fetchers.go b/common/fetchers.go index 040e61d..9243d45 100644 --- a/common/fetchers.go +++ b/common/fetchers.go @@ -10,6 +10,7 @@ type PasswdInfo struct { UID string GID string HomePath string + Line uint32 } var _cachedPasswdInfo []PasswdInfo @@ -28,7 +29,7 @@ func FetchPasswdInfo() ([]PasswdInfo, error) { lines := strings.Split(string(readBytes), "\n") infos := make([]PasswdInfo, 0) - for _, line := range lines { + for lineNumber, line := range lines { splitted := strings.Split(line, ":") if len(splitted) < 6 { @@ -40,6 +41,7 @@ func FetchPasswdInfo() ([]PasswdInfo, error) { UID: splitted[2], GID: splitted[3], HomePath: splitted[5], + Line: uint32(lineNumber), } infos = append(infos, info) diff --git a/handlers/aliases/handlers/completions.go b/handlers/aliases/handlers/completions.go index 61ca11d..91f424b 100644 --- a/handlers/aliases/handlers/completions.go +++ b/handlers/aliases/handlers/completions.go @@ -164,7 +164,7 @@ func getUsersFromEntry( switch (value).(type) { case ast.AliasValueUser: userValue := value.(ast.AliasValueUser) - + users[indexes.NormalizeKey(userValue.Value)] = struct{}{} } } @@ -172,4 +172,3 @@ func getUsersFromEntry( return users } - diff --git a/handlers/aliases/handlers/go_to_definition.go b/handlers/aliases/handlers/go_to_definition.go new file mode 100644 index 0000000..e7ab96c --- /dev/null +++ b/handlers/aliases/handlers/go_to_definition.go @@ -0,0 +1,94 @@ +package handlers + +import ( + "config-lsp/common" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + "config-lsp/utils" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func GetDefinitionLocationForValue( + i indexes.AliasesIndexes, + value ast.AliasValueInterface, + params *protocol.DefinitionParams, +) []protocol.Location { + switch value.(type) { + case ast.AliasValueUser: + userValue := value.(ast.AliasValueUser) + + // Own defined alias + if entry, found := i.Keys[indexes.NormalizeKey(userValue.Value)]; found { + return []protocol.Location{ + { + URI: params.TextDocument.URI, + Range: entry.Location.ToLSPRange(), + }, + } + } + + // System user + systemUsers, _ := getSystemUserMap() + if user, found := systemUsers[userValue.Value]; found { + return []protocol.Location{ + { + URI: "file:///etc/passwd", + Range: protocol.Range{ + Start: protocol.Position{ + Line: user.Line, + Character: 0, + }, + End: protocol.Position{ + Line: user.Line, + Character: uint32(len(user.Name)), + }, + }, + }, + } + } + case ast.AliasValueFile: + fileValue := value.(ast.AliasValueFile) + path := string(fileValue.Path) + + if utils.DoesPathExist(path) { + return []protocol.Location{ + { + URI: "file://" + path, + }, + } + } + case ast.AliasValueInclude: + includeValue := value.(ast.AliasValueInclude) + + if includeValue.Path != nil { + path := string(includeValue.Path.Path) + + if utils.DoesPathExist(path) { + return []protocol.Location{ + { + URI: "file://" + path, + }, + } + } + } + } + + return nil +} + +func getSystemUserMap() (map[string]common.PasswdInfo, error) { + users, err := common.FetchPasswdInfo() + + if err != nil { + return nil, err + } + + userMap := make(map[string]common.PasswdInfo) + + for _, user := range users { + userMap[user.Name] = user + } + + return userMap, nil +} diff --git a/handlers/aliases/lsp/text-document-definition.go b/handlers/aliases/lsp/text-document-definition.go new file mode 100644 index 0000000..67454ae --- /dev/null +++ b/handlers/aliases/lsp/text-document-definition.go @@ -0,0 +1,40 @@ +package lsp + +import ( + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/handlers" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentDefinition(context *glsp.Context, params *protocol.DefinitionParams) ([]protocol.Location, error) { + d := aliases.DocumentParserMap[params.TextDocument.URI] + character := params.Position.Character + line := params.Position.Line + + rawEntry, found := d.Parser.Aliases.Get(line) + + if !found { + return nil, nil + } + + entry := rawEntry.(*ast.AliasEntry) + + if entry.Values != nil && character >= entry.Values.Location.Start.Character && character <= entry.Values.Location.End.Character { + rawValue := handlers.GetValueAtCursor(character, entry) + + if rawValue == nil { + return nil, nil + } + + return handlers.GetDefinitionLocationForValue( + *d.Indexes, + *rawValue, + params, + ), nil + } + + return nil, nil +} diff --git a/root-handler/handler.go b/root-handler/handler.go index 565413e..ca19922 100644 --- a/root-handler/handler.go +++ b/root-handler/handler.go @@ -27,6 +27,7 @@ func SetUpRootHandler() { TextDocumentHover: TextDocumentHover, TextDocumentDidClose: TextDocumentDidClose, TextDocumentCodeAction: TextDocumentCodeAction, + TextDocumentDefinition: TextDocumentDefinition, WorkspaceExecuteCommand: WorkspaceExecuteCommand, } diff --git a/root-handler/text-document-definition.go b/root-handler/text-document-definition.go new file mode 100644 index 0000000..194bcb8 --- /dev/null +++ b/root-handler/text-document-definition.go @@ -0,0 +1,37 @@ +package roothandler + +import ( + aliases "config-lsp/handlers/aliases/lsp" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentDefinition(context *glsp.Context, params *protocol.DefinitionParams) (any, error) { + language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) + + if language == nil { + showParseError( + context, + params.TextDocument.URI, + undetectableError, + ) + + return nil, undetectableError.Err + } + + switch *language { + case LanguageHosts: + return nil, nil + case LanguageSSHDConfig: + return nil, nil + case LanguageFstab: + return nil, nil + case LanguageWireguard: + return nil, nil + case LanguageAliases: + return aliases.TextDocumentDefinition(context, params) + } + + panic("root-handler/TextDocumentDefinition: unexpected language" + *language) +} From d56eef43bf4a082825db72fce20716aa157675b3 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 18:02:34 +0200 Subject: [PATCH 20/24] feat(aliases): Add support for renaming --- handlers/aliases/handlers/go_to_definition.go | 2 +- handlers/aliases/handlers/rename.go | 33 ++++++++++ handlers/aliases/handlers/rename_test.go | 48 +++++++++++++++ handlers/aliases/indexes/indexes.go | 22 ++++++- handlers/aliases/indexes/indexes_test.go | 49 +++++++++++++++ handlers/aliases/lsp/text-document-rename.go | 60 +++++++++++++++++++ root-handler/handler.go | 1 + root-handler/text-document-rename.go | 36 +++++++++++ 8 files changed, 247 insertions(+), 4 deletions(-) create mode 100644 handlers/aliases/handlers/rename.go create mode 100644 handlers/aliases/handlers/rename_test.go create mode 100644 handlers/aliases/indexes/indexes_test.go create mode 100644 handlers/aliases/lsp/text-document-rename.go create mode 100644 root-handler/text-document-rename.go diff --git a/handlers/aliases/handlers/go_to_definition.go b/handlers/aliases/handlers/go_to_definition.go index e7ab96c..31d43e7 100644 --- a/handlers/aliases/handlers/go_to_definition.go +++ b/handlers/aliases/handlers/go_to_definition.go @@ -23,7 +23,7 @@ func GetDefinitionLocationForValue( return []protocol.Location{ { URI: params.TextDocument.URI, - Range: entry.Location.ToLSPRange(), + Range: entry.Key.Location.ToLSPRange(), }, } } diff --git a/handlers/aliases/handlers/rename.go b/handlers/aliases/handlers/rename.go new file mode 100644 index 0000000..9b1cfab --- /dev/null +++ b/handlers/aliases/handlers/rename.go @@ -0,0 +1,33 @@ +package handlers + +import ( + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func RenameAlias( + i indexes.AliasesIndexes, + oldEntry *ast.AliasEntry, + newName string, +) []protocol.TextEdit { + occurrences := i.UserOccurrences[indexes.NormalizeKey(oldEntry.Key.Value)] + changes := make([]protocol.TextEdit, 0, len(occurrences)) + + // Own rename + changes = append(changes, protocol.TextEdit{ + Range: oldEntry.Key.Location.ToLSPRange(), + NewText: newName, + }) + + // Other AliasValueUser occurrences + for _, value := range occurrences { + changes = append(changes, protocol.TextEdit{ + Range: value.Location.ToLSPRange(), + NewText: newName, + }) + } + + return changes +} diff --git a/handlers/aliases/handlers/rename_test.go b/handlers/aliases/handlers/rename_test.go new file mode 100644 index 0000000..601a37a --- /dev/null +++ b/handlers/aliases/handlers/rename_test.go @@ -0,0 +1,48 @@ +package handlers + +import ( + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + "config-lsp/utils" + "testing" +) + +func TestComplexExample( + t *testing.T, +) { + input := utils.Dedent(` +alice: alice +bob: root +support: alice, bob +`) + parser := ast.NewAliasesParser() + errors := parser.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Unexpected errors: %v", errors) + } + + i, errors := indexes.CreateIndexes(parser) + + if len(errors) > 0 { + t.Fatalf("Expected no errors, but got: %v", errors) + } + + edits := RenameAlias(i, i.Keys["alice"], "amelie") + + if !(len(edits) == 3) { + t.Errorf("Expected 2 edits, but got %v", len(edits)) + } + + if !(edits[0].Range.Start.Line == 0 && edits[0].Range.Start.Character == 0 && edits[0].Range.End.Line == 0 && edits[0].Range.End.Character == 5) { + t.Errorf("Unexpected edit: %v", edits[0]) + } + + if !(edits[1].Range.Start.Line == 0 && edits[1].Range.Start.Character == 7 && edits[1].Range.End.Line == 0 && edits[1].Range.End.Character == 12) { + t.Errorf("Unexpected edit: %v", edits[1]) + } + + if !(edits[2].Range.Start.Line == 2 && edits[2].Range.Start.Character == 9 && edits[2].Range.End.Line == 2 && edits[2].Range.End.Character == 14) { + t.Errorf("Unexpected edit: %v", edits[2]) + } +} diff --git a/handlers/aliases/indexes/indexes.go b/handlers/aliases/indexes/indexes.go index 359cf31..5d9cf10 100644 --- a/handlers/aliases/indexes/indexes.go +++ b/handlers/aliases/indexes/indexes.go @@ -8,7 +8,8 @@ import ( ) type AliasesIndexes struct { - Keys map[string]*ast.AliasKey + Keys map[string]*ast.AliasEntry + UserOccurrences map[string][]*ast.AliasValueUser } func NormalizeKey(key string) string { @@ -18,7 +19,8 @@ func NormalizeKey(key string) string { func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) { errors := make([]common.LSPError, 0) indexes := &AliasesIndexes{ - Keys: make(map[string]*ast.AliasKey), + Keys: make(map[string]*ast.AliasEntry), + UserOccurrences: make(map[string][]*ast.AliasValueUser), } it := parser.Aliases.Iterator() @@ -26,6 +28,20 @@ func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) for it.Next() { entry := it.Value().(*ast.AliasEntry) + if entry.Values != nil { + for _, value := range entry.Values.Values { + switch value.(type) { + case ast.AliasValueUser: + userValue := value.(ast.AliasValueUser) + + indexes.UserOccurrences[userValue.Value] = append( + indexes.UserOccurrences[userValue.Value], + &userValue, + ) + } + } + } + if entry.Key == nil || entry.Key.Value == "" { continue } @@ -44,7 +60,7 @@ func CreateIndexes(parser ast.AliasesParser) (AliasesIndexes, []common.LSPError) continue } - indexes.Keys[normalizedAlias] = entry.Key + indexes.Keys[normalizedAlias] = entry } return *indexes, errors diff --git a/handlers/aliases/indexes/indexes_test.go b/handlers/aliases/indexes/indexes_test.go new file mode 100644 index 0000000..d8d7891 --- /dev/null +++ b/handlers/aliases/indexes/indexes_test.go @@ -0,0 +1,49 @@ +package indexes + +import ( + "config-lsp/handlers/aliases/ast" + "config-lsp/utils" + "testing" +) + +func TestComplexExample( + t *testing.T, +) { + input := utils.Dedent(` +postmaster: alice, bob +alice: root +bob: root +`) + parser := ast.NewAliasesParser() + errors := parser.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Unexpected errors: %v", errors) + } + + indexes, errors := CreateIndexes(parser) + + if len(errors) > 0 { + t.Fatalf("Expected no errors, but got: %v", errors) + } + + if !(len(indexes.Keys) == 3) { + t.Errorf("Expected 3 keys, but got %v", len(indexes.Keys)) + } + + if !(len(indexes.UserOccurrences) == 3) { + t.Errorf("Expected 3 user occurrences, but got %v", len(indexes.UserOccurrences)) + } + + if !(len(indexes.UserOccurrences["root"]) == 2) { + t.Errorf("Expected 2 occurrences of root, but got %v", len(indexes.UserOccurrences["root"])) + } + + if !(len(indexes.UserOccurrences["alice"]) == 1) { + t.Errorf("Expected 1 occurrence of alice, but got %v", len(indexes.UserOccurrences["alice"])) + } + + if !(len(indexes.UserOccurrences["bob"]) == 1) { + t.Errorf("Expected 1 occurrence of bob, but got %v", len(indexes.UserOccurrences["bob"])) + } +} diff --git a/handlers/aliases/lsp/text-document-rename.go b/handlers/aliases/lsp/text-document-rename.go new file mode 100644 index 0000000..be529ed --- /dev/null +++ b/handlers/aliases/lsp/text-document-rename.go @@ -0,0 +1,60 @@ +package lsp + +import ( + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/handlers" + "config-lsp/handlers/aliases/indexes" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentRename(context *glsp.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) { + d := aliases.DocumentParserMap[params.TextDocument.URI] + character := params.Position.Character + line := params.Position.Line + + rawEntry, found := d.Parser.Aliases.Get(line) + + if !found { + return nil, nil + } + + entry := rawEntry.(*ast.AliasEntry) + + if character >= entry.Key.Location.Start.Character && character <= entry.Key.Location.End.Character { + changes := handlers.RenameAlias(*d.Indexes, entry, params.NewName) + + return &protocol.WorkspaceEdit{ + Changes: map[protocol.DocumentUri][]protocol.TextEdit{ + params.TextDocument.URI: changes, + }, + }, nil + } + + if entry.Values != nil && character >= entry.Values.Location.Start.Character && character <= entry.Values.Location.End.Character { + rawValue := handlers.GetValueAtCursor(character, entry) + + if rawValue == nil { + return nil, nil + } + + switch (*rawValue).(type) { + case ast.AliasValueUser: + userValue := (*rawValue).(ast.AliasValueUser) + + definitionEntry := d.Indexes.Keys[indexes.NormalizeKey(userValue.Value)] + + changes := handlers.RenameAlias(*d.Indexes, definitionEntry, params.NewName) + + return &protocol.WorkspaceEdit{ + Changes: map[protocol.DocumentUri][]protocol.TextEdit{ + params.TextDocument.URI: changes, + }, + }, nil + } + } + + return nil, nil +} diff --git a/root-handler/handler.go b/root-handler/handler.go index ca19922..9b74ea4 100644 --- a/root-handler/handler.go +++ b/root-handler/handler.go @@ -29,6 +29,7 @@ func SetUpRootHandler() { TextDocumentCodeAction: TextDocumentCodeAction, TextDocumentDefinition: TextDocumentDefinition, WorkspaceExecuteCommand: WorkspaceExecuteCommand, + TextDocumentRename: TextDocumentRename, } server := server.NewServer(&lspHandler, lsName, false) diff --git a/root-handler/text-document-rename.go b/root-handler/text-document-rename.go new file mode 100644 index 0000000..5a41135 --- /dev/null +++ b/root-handler/text-document-rename.go @@ -0,0 +1,36 @@ +package roothandler + +import ( + aliases "config-lsp/handlers/aliases/lsp" + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentRename(context *glsp.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) { + language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) + + if language == nil { + showParseError( + context, + params.TextDocument.URI, + undetectableError, + ) + + return nil, undetectableError.Err + } + + switch *language { + case LanguageHosts: + return nil, nil + case LanguageSSHDConfig: + return nil, nil + case LanguageFstab: + return nil, nil + case LanguageWireguard: + return nil, nil + case LanguageAliases: + return aliases.TextDocumentRename(context, params) + } + + panic("root-handler/TextDocumentRename: unexpected language" + *language) +} From 40a570809d5fce4a58c44b3c5af16fe620c75a31 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 18:10:36 +0200 Subject: [PATCH 21/24] chore(aliases): Add more tests --- handlers/aliases/handlers/go_to_definition.go | 4 +- .../aliases/handlers/go_to_definition_test.go | 54 +++++++++++++++++++ handlers/aliases/handlers/rename_test.go | 2 +- .../aliases/lsp/text-document-definition.go | 2 +- 4 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 handlers/aliases/handlers/go_to_definition_test.go diff --git a/handlers/aliases/handlers/go_to_definition.go b/handlers/aliases/handlers/go_to_definition.go index 31d43e7..b4fd44f 100644 --- a/handlers/aliases/handlers/go_to_definition.go +++ b/handlers/aliases/handlers/go_to_definition.go @@ -12,7 +12,7 @@ import ( func GetDefinitionLocationForValue( i indexes.AliasesIndexes, value ast.AliasValueInterface, - params *protocol.DefinitionParams, + documentURI string, ) []protocol.Location { switch value.(type) { case ast.AliasValueUser: @@ -22,7 +22,7 @@ func GetDefinitionLocationForValue( if entry, found := i.Keys[indexes.NormalizeKey(userValue.Value)]; found { return []protocol.Location{ { - URI: params.TextDocument.URI, + URI: documentURI, Range: entry.Key.Location.ToLSPRange(), }, } diff --git a/handlers/aliases/handlers/go_to_definition_test.go b/handlers/aliases/handlers/go_to_definition_test.go new file mode 100644 index 0000000..1961ee7 --- /dev/null +++ b/handlers/aliases/handlers/go_to_definition_test.go @@ -0,0 +1,54 @@ +package handlers + +import ( + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/indexes" + "config-lsp/utils" + "testing" +) + +func TestGoToDefinitionSimpleExample( + t *testing.T, +) { + input := utils.Dedent(` +alice: root +bob: root +steve: alice@example.com, bob +david: alice +`) + parser := ast.NewAliasesParser() + errors := parser.Parse(input) + + if len(errors) > 0 { + t.Fatalf("Unexpected errors: %v", errors) + } + + i, errors := indexes.CreateIndexes(parser) + + if len(errors) > 0 { + t.Fatalf("Expected no errors, but got: %v", errors) + } + + rawEntry, _ := parser.Aliases.Get(uint32(3)) + entry := rawEntry.(*ast.AliasEntry) + rawValue := entry.Values.Values[0] + value := rawValue.(ast.AliasValueUser) + + locations := GetDefinitionLocationForValue( + i, + value, + "file:///etc/aliases", + ) + + if !(len(locations) == 1) { + t.Errorf("Expected 1 location, but got %v", len(locations)) + } + + if !(locations[0].URI == "file:///etc/aliases") { + t.Errorf("Unexpected location: %v", locations[0]) + } + + if !(locations[0].Range.Start.Line == 0 && locations[0].Range.Start.Character == 0 && locations[0].Range.End.Line == 0 && locations[0].Range.End.Character == 5) { + t.Errorf("Unexpected location: %v", locations[0]) + } +} diff --git a/handlers/aliases/handlers/rename_test.go b/handlers/aliases/handlers/rename_test.go index 601a37a..eb6606a 100644 --- a/handlers/aliases/handlers/rename_test.go +++ b/handlers/aliases/handlers/rename_test.go @@ -7,7 +7,7 @@ import ( "testing" ) -func TestComplexExample( +func TestRenameSimpleExample( t *testing.T, ) { input := utils.Dedent(` diff --git a/handlers/aliases/lsp/text-document-definition.go b/handlers/aliases/lsp/text-document-definition.go index 67454ae..4798e16 100644 --- a/handlers/aliases/lsp/text-document-definition.go +++ b/handlers/aliases/lsp/text-document-definition.go @@ -32,7 +32,7 @@ func TextDocumentDefinition(context *glsp.Context, params *protocol.DefinitionPa return handlers.GetDefinitionLocationForValue( *d.Indexes, *rawValue, - params, + params.TextDocument.URI, ), nil } From ad1a8e0d95c6171ab28ce27f794ebe9afe260a3a Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 21:02:43 +0200 Subject: [PATCH 22/24] feat(aliases): Add support for prepare rename --- .../lsp/text-document-prepare-rename.go | 45 +++++++++++++++++++ root-handler/handler.go | 9 ++++ root-handler/text-document-prepare-rename.go | 38 ++++++++++++++++ 3 files changed, 92 insertions(+) create mode 100644 handlers/aliases/lsp/text-document-prepare-rename.go create mode 100644 root-handler/text-document-prepare-rename.go diff --git a/handlers/aliases/lsp/text-document-prepare-rename.go b/handlers/aliases/lsp/text-document-prepare-rename.go new file mode 100644 index 0000000..2b2d03e --- /dev/null +++ b/handlers/aliases/lsp/text-document-prepare-rename.go @@ -0,0 +1,45 @@ +package lsp + +import ( + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/handlers" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentPrepareRename(context *glsp.Context, params *protocol.PrepareRenameParams) (any, error) { + d := aliases.DocumentParserMap[params.TextDocument.URI] + character := params.Position.Character + line := params.Position.Line + + rawEntry, found := d.Parser.Aliases.Get(line) + + if !found { + return nil, nil + } + + entry := rawEntry.(*ast.AliasEntry) + + if character >= entry.Key.Location.Start.Character && character <= entry.Key.Location.End.Character { + return entry.Key.Location.ToLSPRange(), nil + } + + if entry.Values != nil && character >= entry.Values.Location.Start.Character && character <= entry.Values.Location.End.Character { + rawValue := handlers.GetValueAtCursor(character, entry) + + if rawValue == nil { + return nil, nil + } + + switch (*rawValue).(type) { + case ast.AliasValueUser: + userValue := (*rawValue).(ast.AliasValueUser) + + return userValue.Location.ToLSPRange(), nil + } + } + + return nil, nil +} diff --git a/root-handler/handler.go b/root-handler/handler.go index 9b74ea4..9dff13c 100644 --- a/root-handler/handler.go +++ b/root-handler/handler.go @@ -30,6 +30,7 @@ func SetUpRootHandler() { TextDocumentDefinition: TextDocumentDefinition, WorkspaceExecuteCommand: WorkspaceExecuteCommand, TextDocumentRename: TextDocumentRename, + TextDocumentPrepareRename: TextDocumentPrepareRename, } server := server.NewServer(&lspHandler, lsName, false) @@ -41,6 +42,14 @@ func initialize(context *glsp.Context, params *protocol.InitializeParams) (any, capabilities := lspHandler.CreateServerCapabilities() capabilities.TextDocumentSync = protocol.TextDocumentSyncKindFull + if (*params.Capabilities.TextDocument.Rename.PrepareSupport) == true { + // Client supports rename preparation + prepareRename := true + capabilities.RenameProvider = protocol.RenameOptions{ + PrepareProvider: &prepareRename, + } + } + return protocol.InitializeResult{ Capabilities: capabilities, ServerInfo: &protocol.InitializeResultServerInfo{ diff --git a/root-handler/text-document-prepare-rename.go b/root-handler/text-document-prepare-rename.go new file mode 100644 index 0000000..fc4489b --- /dev/null +++ b/root-handler/text-document-prepare-rename.go @@ -0,0 +1,38 @@ +package roothandler + +import ( + "github.com/tliron/glsp" + aliases "config-lsp/handlers/aliases/lsp" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentPrepareRename(context *glsp.Context, params *protocol.PrepareRenameParams) (any, error) { + language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) + + if language == nil { + showParseError( + context, + params.TextDocument.URI, + undetectableError, + ) + + return nil, undetectableError.Err + } + + switch *language { + case LanguageHosts: + return nil, nil + case LanguageSSHDConfig: + return nil, nil + case LanguageFstab: + return nil, nil + case LanguageWireguard: + return nil, nil + case LanguageAliases: + return aliases.TextDocumentPrepareRename(context, params) + } + + panic("root-handler/TextDocumentPrepareRename: unexpected language" + *language) +} + From e1af64f2c04680b29477188f92ae232e03d81144 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sat, 7 Sep 2024 23:19:44 +0200 Subject: [PATCH 23/24] feat(aliases): Add completion support for error values --- handlers/aliases/handlers/completions.go | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/handlers/aliases/handlers/completions.go b/handlers/aliases/handlers/completions.go index 91f424b..246c219 100644 --- a/handlers/aliases/handlers/completions.go +++ b/handlers/aliases/handlers/completions.go @@ -78,6 +78,38 @@ func GetCompletionsForEntry( userValue.Value, relativeCursor, ), nil + case ast.AliasValueError: + errorValue := (*value).(ast.AliasValueError) + + isAtErrorCode := errorValue.Code == nil && + relativeCursor >= errorValue.Location.Start.Character && + (errorValue.Message == nil || + relativeCursor <= errorValue.Message.Location.Start.Character) + + if isAtErrorCode { + kind := protocol.CompletionItemKindValue + + detail_4 := "4XX (TempFail)" + insertText_4 := "400" + + detail_5 := "5XX (PermFail)" + insertText_5 := "500" + + return []protocol.CompletionItem{ + { + Label: "4XX", + InsertText: &insertText_4, + Kind: &kind, + Detail: &detail_4, + }, + { + Label: "5XX", + InsertText: &insertText_5, + Kind: &kind, + Detail: &detail_5, + }, + }, nil + } } return completions, nil From 2768f99d2f730e9b327eaaa294a697dac4fe27e2 Mon Sep 17 00:00:00 2001 From: Myzel394 <50424412+Myzel394@users.noreply.github.com> Date: Sun, 8 Sep 2024 16:59:01 +0200 Subject: [PATCH 24/24] feat(aliases): Add signature help support; Improvements --- handlers/aliases/fields/fields.go | 50 ++- handlers/aliases/handlers/hover.go | 25 +- handlers/aliases/handlers/signature_help.go | 309 ++++++++++++++++++ .../lsp/text-document-signature-help.go | 52 +++ root-handler/handler.go | 36 +- root-handler/text-document-prepare-rename.go | 3 +- root-handler/text-document-signature-help.go | 37 +++ utils/strings.go | 6 + 8 files changed, 484 insertions(+), 34 deletions(-) create mode 100644 handlers/aliases/handlers/signature_help.go create mode 100644 handlers/aliases/lsp/text-document-signature-help.go create mode 100644 root-handler/text-document-signature-help.go diff --git a/handlers/aliases/fields/fields.go b/handlers/aliases/fields/fields.go index 940ce3e..f29804e 100644 --- a/handlers/aliases/fields/fields.go +++ b/handlers/aliases/fields/fields.go @@ -5,14 +5,50 @@ import ( docvalues "config-lsp/doc-values" ) -var UserField = docvalues.UserValue("", false) - -var PathField = docvalues.PathValue{ - RequiredType: docvalues.PathTypeFile, +var UserField = docvalues.DocumentationValue{ + Documentation: "A user on the host machine. The user must have a valid entry in the passwd(5) database file.", + Value: docvalues.UserValue("", false), } -var CommandField = docvalues.StringValue{} +var UserDeclaration = "`user`" -var EmailField = docvalues.RegexValue{ - Regex: *commondocumentation.EmailRegex, +var PathField = docvalues.DocumentationValue{ + Documentation: "Append messages to file, specified by its absolute pathname", + Value: docvalues.PathValue{ + RequiredType: docvalues.PathTypeFile, + }, } + +var PathDeclaration = "`/path/to/file`" + +var CommandField = docvalues.DocumentationValue{ + Documentation: "Pipe the message to command on its standard input. The command is run under the privileges of the daemon's unprivileged account.", + Value: docvalues.StringValue{}, +} + +var CommandDeclaration = "`|command`" + +var EmailField = docvalues.DocumentationValue{ + Documentation: "An email address in RFC 5322 format. If an address extension is appended to the user-part, it is first compared for an exact match. It is then stripped so that an address such as user+ext@example.com will only use the part that precedes ‘+’ as a key.", + Value: docvalues.RegexValue{ + Regex: *commondocumentation.EmailRegex, + }, +} + +var EmailDeclaration = "`user-part@domain-part`" + +var IncludeField = docvalues.DocumentationValue{ + Documentation: "Include any definitions in file as alias entries. The format of the file is identical to this one.", + Value: docvalues.PathValue{ + RequiredType: docvalues.PathTypeFile, + }, +} + +var IncludeDeclaration = "`include:/path/to/file`" + +var ErrorMessageField = docvalues.DocumentationValue{ + Documentation: "A status code and message to return. The code must be 3 digits, starting 4XX (TempFail) or 5XX (PermFail). The message must be present and can be freely chosen.", + Value: docvalues.StringValue{}, +} + +var ErrorDeclaration = "`error:code message`" diff --git a/handlers/aliases/handlers/hover.go b/handlers/aliases/handlers/hover.go index 84206e7..8cd5a8e 100644 --- a/handlers/aliases/handlers/hover.go +++ b/handlers/aliases/handlers/hover.go @@ -2,6 +2,7 @@ package handlers import ( "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/fields" "config-lsp/handlers/aliases/indexes" "config-lsp/utils" "fmt" @@ -100,44 +101,44 @@ func GetAliasValueTypeInfo( case ast.AliasValueUser: return []string{ "### User", - "`user`", + fields.UserDeclaration, "", - "A user on the host machine. The user must have a valid entry in the passwd(5) database file.", + fields.UserField.Documentation, } case ast.AliasValueEmail: return []string{ "### Email", - "`user-part@domain-part`", + fields.EmailDeclaration, "", - "An email address in RFC 5322 format. If an address extension is appended to the user-part, it is first compared for an exact match. It is then stripped so that an address such as user+ext@example.com will only use the part that precedes ‘+’ as a key.", + fields.EmailField.Documentation, } case ast.AliasValueInclude: return []string{ "### Include", - "`include:/path/to/file`", + fields.IncludeDeclaration, "", - "Include any definitions in file as alias entries. The format of the file is identical to this one.", + fields.IncludeField.Documentation, } case ast.AliasValueFile: return []string{ "### File", - "`/path/to/file`", + fields.PathDeclaration, "", - "Append messages to file, specified by its absolute pathname.", + fields.PathField.Documentation, } case ast.AliasValueCommand: return []string{ "### Command", - "`|command`", + fields.CommandDeclaration, "", - "Pipe the message to command on its standard input. The command is run under the privileges of the daemon's unprivileged account.", + fields.CommandField.Documentation, } case ast.AliasValueError: return []string{ "### Error", - "`error:code message`", + fields.ErrorDeclaration, "", - "A status code and message to return. The code must be 3 digits, starting 4XX (TempFail) or 5XX (PermFail). The message must be present and can be freely chosen.", + fields.ErrorMessageField.Documentation, } } diff --git a/handlers/aliases/handlers/signature_help.go b/handlers/aliases/handlers/signature_help.go new file mode 100644 index 0000000..7da7f06 --- /dev/null +++ b/handlers/aliases/handlers/signature_help.go @@ -0,0 +1,309 @@ +package handlers + +import ( + "config-lsp/handlers/aliases/ast" + "strings" + + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func GetRootSignatureHelp( + activeParameter uint32, +) *protocol.SignatureHelp { + index := uint32(0) + return &protocol.SignatureHelp{ + ActiveSignature: &index, + Signatures: []protocol.SignatureInformation{ + { + Label: ": , , ...", + ActiveParameter: &activeParameter, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len("") + 1), + }, + Documentation: "The alias to define", + }, + { + Label: []uint32{ + uint32(len(":")), + uint32(len(":") + len("")), + }, + Documentation: "A value to associate with the alias", + }, + }, + }, + }, + } +} + +func GetAllValuesSignatureHelp() *protocol.SignatureHelp { + index := uint32(0) + return &protocol.SignatureHelp{ + Signatures: []protocol.SignatureInformation{ + { + Label: "", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len("")), + }, + }, + }, + }, + { + Label: "@", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len("")), + }, + }, + { + Label: []uint32{ + uint32(len("@")), + uint32(len("@")), + }, + }, + }, + }, + { + Label: "", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len("")), + }, + }, + }, + }, + { + Label: ":include:", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len(":include:")), + }, + }, + }, + }, + { + Label: "|", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + 1, + }, + }, + }, + }, + { + Label: "error: ", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len("error")), + }, + }, + }, + }, + }, + } +} + +func GetValueSignatureHelp( + value ast.AliasValueInterface, + cursor uint32, +) *protocol.SignatureHelp { + switch value.(type) { + case ast.AliasValueUser: + index := uint32(0) + return &protocol.SignatureHelp{ + Signatures: []protocol.SignatureInformation{ + { + Label: "", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len("")), + }, + }, + }, + }, + { + Label: "@", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len("")), + }, + }, + { + Label: []uint32{ + uint32(len("@")), + uint32(len("@") + len("")), + }, + }, + }, + }, + }, + } + case ast.AliasValueEmail: + isBeforeAtSymbol := cursor <= uint32(strings.Index(value.GetAliasValue().Value, "@")) + + var index uint32 + + if isBeforeAtSymbol { + index = 0 + } else { + index = 1 + } + + return &protocol.SignatureHelp{ + Signatures: []protocol.SignatureInformation{ + { + Label: "@", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len("")), + }, + }, + { + Label: []uint32{ + uint32(len("@")), + uint32(len("@") + len("")), + }, + }, + }, + }, + }, + } + case ast.AliasValueFile: + index := uint32(0) + return &protocol.SignatureHelp{ + Signatures: []protocol.SignatureInformation{ + { + Label: "", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len("")), + }, + }, + }, + }, + }, + } + case ast.AliasValueInclude: + index := uint32(0) + return &protocol.SignatureHelp{ + Signatures: []protocol.SignatureInformation{ + { + Label: "include:", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + uint32(len("include:")), + uint32(len("include:")), + }, + }, + }, + }, + }, + } + case ast.AliasValueCommand: + var index uint32 + + if cursor == 0 { + index = 0 + } else { + index = 1 + } + + return &protocol.SignatureHelp{ + Signatures: []protocol.SignatureInformation{ + { + Label: "|", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + 1, + }, + }, + { + Label: []uint32{ + 1, + uint32(1 + len("")), + }, + }, + }, + }, + }, + } + case ast.AliasValueError: + errorValue := value.(ast.AliasValueError) + var index uint32 + + if errorValue.Code == nil || cursor <= errorValue.Code.Location.End.Character { + index = 1 + } else { + index = 2 + } + + return &protocol.SignatureHelp{ + Signatures: []protocol.SignatureInformation{ + { + Label: "error: ", + ActiveParameter: &index, + Parameters: []protocol.ParameterInformation{ + { + Label: []uint32{ + 0, + uint32(len("error:")), + }, + }, + { + Label: []uint32{ + uint32(len("error:")), + uint32(len("error:")), + }, + }, + { + Label: []uint32{ + uint32(len("error: ")), + uint32(len("error: ")), + }, + }, + }, + }, + }, + } + } + + return nil +} diff --git a/handlers/aliases/lsp/text-document-signature-help.go b/handlers/aliases/lsp/text-document-signature-help.go new file mode 100644 index 0000000..ce3a9b0 --- /dev/null +++ b/handlers/aliases/lsp/text-document-signature-help.go @@ -0,0 +1,52 @@ +package lsp + +import ( + "config-lsp/handlers/aliases" + "config-lsp/handlers/aliases/ast" + "config-lsp/handlers/aliases/handlers" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentSignatureHelp(context *glsp.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) { + document := aliases.DocumentParserMap[params.TextDocument.URI] + + line := params.Position.Line + character := params.Position.Character + + if _, found := document.Parser.CommentLines[line]; found { + // Comment + return nil, nil + } + + rawEntry, found := document.Parser.Aliases.Get(line) + + if !found { + return handlers.GetRootSignatureHelp(0), nil + } + + entry := rawEntry.(*ast.AliasEntry) + + if entry.Key != nil && character >= entry.Key.Location.Start.Character && character <= entry.Key.Location.End.Character { + return handlers.GetRootSignatureHelp(0), nil + } + + if entry.Values != nil && character >= entry.Values.Location.Start.Character && character <= entry.Values.Location.End.Character { + value := handlers.GetValueAtCursor(character, entry) + + if value == nil { + // For some reason, this does not really work, + // When we return all, and then a user value is entered + // and the `GetValueSignatureHelp` is called, still the old + // signatures with all signature are shown + // return handlers.GetAllValuesSignatureHelp(), nil + + return nil, nil + } + + return handlers.GetValueSignatureHelp(*value, character), nil + } + + return nil, nil +} diff --git a/root-handler/handler.go b/root-handler/handler.go index 9dff13c..4a7cd3c 100644 --- a/root-handler/handler.go +++ b/root-handler/handler.go @@ -17,20 +17,21 @@ var lspHandler protocol.Handler func SetUpRootHandler() { rootHandler = NewRootHandler() lspHandler = protocol.Handler{ - Initialize: initialize, - Initialized: initialized, - Shutdown: shutdown, - SetTrace: setTrace, - TextDocumentDidOpen: TextDocumentDidOpen, - TextDocumentDidChange: TextDocumentDidChange, - TextDocumentCompletion: TextDocumentCompletion, - TextDocumentHover: TextDocumentHover, - TextDocumentDidClose: TextDocumentDidClose, - TextDocumentCodeAction: TextDocumentCodeAction, - TextDocumentDefinition: TextDocumentDefinition, - WorkspaceExecuteCommand: WorkspaceExecuteCommand, - TextDocumentRename: TextDocumentRename, + Initialize: initialize, + Initialized: initialized, + Shutdown: shutdown, + SetTrace: setTrace, + TextDocumentDidOpen: TextDocumentDidOpen, + TextDocumentDidChange: TextDocumentDidChange, + TextDocumentCompletion: TextDocumentCompletion, + TextDocumentHover: TextDocumentHover, + TextDocumentDidClose: TextDocumentDidClose, + TextDocumentCodeAction: TextDocumentCodeAction, + TextDocumentDefinition: TextDocumentDefinition, + WorkspaceExecuteCommand: WorkspaceExecuteCommand, + TextDocumentRename: TextDocumentRename, TextDocumentPrepareRename: TextDocumentPrepareRename, + TextDocumentSignatureHelp: TextDocumentSignatureHelp, } server := server.NewServer(&lspHandler, lsName, false) @@ -41,6 +42,15 @@ func SetUpRootHandler() { func initialize(context *glsp.Context, params *protocol.InitializeParams) (any, error) { capabilities := lspHandler.CreateServerCapabilities() capabilities.TextDocumentSync = protocol.TextDocumentSyncKindFull + capabilities.SignatureHelpProvider = &protocol.SignatureHelpOptions{ + TriggerCharacters: []string{ + "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", + "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", + "_", "-", ".", "/", ":", "@", "#", "!", "$", "%", "^", "&", "*", "(", ")", "+", "=", "[", "]", "{", "}", "<", ">", "?", ";", ",", "|", + " ", + }, + } if (*params.Capabilities.TextDocument.Rename.PrepareSupport) == true { // Client supports rename preparation diff --git a/root-handler/text-document-prepare-rename.go b/root-handler/text-document-prepare-rename.go index fc4489b..6dbc75a 100644 --- a/root-handler/text-document-prepare-rename.go +++ b/root-handler/text-document-prepare-rename.go @@ -1,8 +1,8 @@ package roothandler import ( - "github.com/tliron/glsp" aliases "config-lsp/handlers/aliases/lsp" + "github.com/tliron/glsp" protocol "github.com/tliron/glsp/protocol_3_16" ) @@ -35,4 +35,3 @@ func TextDocumentPrepareRename(context *glsp.Context, params *protocol.PrepareRe panic("root-handler/TextDocumentPrepareRename: unexpected language" + *language) } - diff --git a/root-handler/text-document-signature-help.go b/root-handler/text-document-signature-help.go new file mode 100644 index 0000000..a3a7396 --- /dev/null +++ b/root-handler/text-document-signature-help.go @@ -0,0 +1,37 @@ +package roothandler + +import ( + aliases "config-lsp/handlers/aliases/lsp" + + "github.com/tliron/glsp" + protocol "github.com/tliron/glsp/protocol_3_16" +) + +func TextDocumentSignatureHelp(context *glsp.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) { + language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) + + if language == nil { + showParseError( + context, + params.TextDocument.URI, + undetectableError, + ) + + return nil, undetectableError.Err + } + + switch *language { + case LanguageHosts: + return nil, nil + case LanguageSSHDConfig: + return nil, nil + case LanguageFstab: + return nil, nil + case LanguageWireguard: + return nil, nil + case LanguageAliases: + return aliases.TextDocumentSignatureHelp(context, params) + } + + panic("root-handler/TextDocumentSignatureHelp: unexpected language" + *language) +} diff --git a/utils/strings.go b/utils/strings.go index 1e4f429..bc32f88 100644 --- a/utils/strings.go +++ b/utils/strings.go @@ -51,3 +51,9 @@ func CountCharacterOccurrences(line string, character rune) int { return count } + +var emptyRegex = regexp.MustCompile(`^\s*$`) + +func IsEmpty(s string) bool { + return emptyRegex.MatchString(s) +}