diff --git a/handlers/ssh_config/analyzer/quotes.go b/handlers/ssh_config/analyzer/quotes.go index 68a5747..3609dde 100644 --- a/handlers/ssh_config/analyzer/quotes.go +++ b/handlers/ssh_config/analyzer/quotes.go @@ -4,6 +4,7 @@ import ( "config-lsp/common" commonparser "config-lsp/common/parser" sshconfig "config-lsp/handlers/ssh_config" + "config-lsp/utils" "errors" "strings" ) @@ -28,9 +29,11 @@ func checkIsUsingDoubleQuotes( value commonparser.ParsedString, valueRange common.LocationRange, ) []common.LSPError { + quoteRanges := utils.GetQuoteRanges(value.Raw) singleQuotePosition := strings.Index(value.Raw, "'") - if singleQuotePosition != -1 { + // Single quoe + if singleQuotePosition != -1 && !quoteRanges.IsCharInside(singleQuotePosition) { return []common.LSPError{ { Range: valueRange, diff --git a/utils/quotes.go b/utils/quotes.go new file mode 100644 index 0000000..5d318ad --- /dev/null +++ b/utils/quotes.go @@ -0,0 +1,51 @@ +package utils + +import "slices" + +type quoteRange [2]int + +func (q quoteRange) IsCharInside(index int) bool { + return index >= q[0] && index <= q[1] +} + +type quoteRanges []quoteRange + +func (q quoteRanges) IsCharInside(index int) bool { + _, found := slices.BinarySearchFunc( + q, + index, + func(current quoteRange, target int) int { + if target < current[0] { + return -1 + } + + if target > current[1] { + return 1 + } + + return 0 + }, + ) + + return found +} + +func GetQuoteRanges(s string) quoteRanges { + quoteRanges := make(quoteRanges, 0, 2) + inQuote := false + var quoteStart int + + for index, c := range s { + if c == '"' && (index == 0 || s[index-1] != '\\') { + if inQuote { + quoteRanges = append(quoteRanges, [2]int{quoteStart, index}) + inQuote = false + } else { + quoteStart = index + inQuote = true + } + } + } + + return quoteRanges +} diff --git a/utils/quotes_test.go b/utils/quotes_test.go new file mode 100644 index 0000000..929ae0d --- /dev/null +++ b/utils/quotes_test.go @@ -0,0 +1,23 @@ +package utils + +import "testing" + +func TestQuotesSimpleExample( + t *testing.T, +) { + quoteRanges := GetQuoteRanges(`"hello" "world"`) + + if !(len(quoteRanges) == 2 && quoteRanges[0][0] == 0 && quoteRanges[0][1] == 6 && quoteRanges[1][0] == 8 && quoteRanges[1][1] == 14) { + t.Fatalf("Unexpected quote ranges: %v", quoteRanges) + } +} + +func TestQuotesEscapedQuotes( + t *testing.T, +) { + quoteRanges := GetQuoteRanges(`"hello \"world\""`) + + if !(len(quoteRanges) == 1 && quoteRanges[0][0] == 0 && quoteRanges[0][1] == 16) { + t.Fatalf("Unexpected quote ranges: %v", quoteRanges) + } +}