Skip to content

Commit

Permalink
feat(ssh_config): Improve match file so that it works with single cri…
Browse files Browse the repository at this point in the history
…terias
  • Loading branch information
Myzel394 committed Sep 28, 2024
1 parent a4a8e88 commit b1a5898
Show file tree
Hide file tree
Showing 27 changed files with 1,265 additions and 260 deletions.
1 change: 0 additions & 1 deletion handlers/ssh_config/analyzer/dependents.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,3 @@ func checkIsDependent(

return errs
}

2 changes: 0 additions & 2 deletions handlers/ssh_config/analyzer/options_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,5 +50,3 @@ User root
t.Fatalf("Expected 1 error, got %v", errors)
}
}


2 changes: 1 addition & 1 deletion handlers/ssh_config/analyzer/quotes.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func analyzeQuotesAreValid(
func checkIsUsingDoubleQuotes(
value commonparser.ParsedString,
valueRange common.LocationRange,
) []common.LSPError {
) []common.LSPError {
singleQuotePosition := strings.Index(value.Raw, "'")

if singleQuotePosition != -1 {
Expand Down
6 changes: 3 additions & 3 deletions handlers/ssh_config/ast/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ func (s *sshParserListener) ExitEntry(ctx *parser.EntryContext) {

hostBlock := &SSHHostBlock{
LocationRange: location,
HostOption: s.sshContext.currentOption,
HostValue: host,
Options: treemap.NewWith(gods.UInt32Comparator),
HostOption: s.sshContext.currentOption,
HostValue: host,
Options: treemap.NewWith(gods.UInt32Comparator),
}

s.Config.Options.Put(
Expand Down
1 change: 0 additions & 1 deletion handlers/ssh_config/document_fields.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,3 @@ func (d SSHDocument) DoesOptionExist(
) bool {
return d.FindOptionByNameAndBlock(name, block) != nil
}

2 changes: 0 additions & 2 deletions handlers/ssh_config/fields/match.go
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
package fields


9 changes: 4 additions & 5 deletions handlers/ssh_config/fields/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,19 @@ package fields

var AllowedDuplicateOptions = map[string]struct{}{
"CertificateFile": {},
"Match": {},
"Host": {},
"Match": {},
"Host": {},
}

// A list of
// A list of
// <Option name> -> <List of fields that need to be present for the option>
var DependentFields = map[string][]string{
"CanonicalDomains": {"CanonicalizeHostname"},
"ControlPersist": {"ControlMaster"},
"ControlPersist": {"ControlMaster"},
}

var HostDisallowedOptions = map[string]struct{}{
"EnableSSHKeysign": {},
}

var GlobalDisallowedOptions = map[string]struct{}{}

123 changes: 123 additions & 0 deletions handlers/ssh_config/handlers/completions_match.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
package handlers

import (
"config-lsp/common"
sshconfig "config-lsp/handlers/ssh_config"
matchparser "config-lsp/handlers/ssh_config/match-parser"
"config-lsp/handlers/sshd_config/fields"
protocol "github.com/tliron/glsp/protocol_3_16"
)

func getMatchCompletions(
d *sshconfig.SSHDocument,
cursor common.CursorPosition,
match *matchparser.Match,
) ([]protocol.CompletionItem, error) {
if match == nil || len(match.Entries) == 0 {
completions := getMatchCriteriaCompletions()
completions = append(completions, getMatchAllKeywordCompletion())

return completions, nil
}

entry := match.GetEntryAtPosition(cursor)

if entry == nil || entry.Criteria.ContainsPosition(cursor) {
return getMatchCriteriaCompletions(), nil
}

return getMatchValueCompletions(entry, cursor), nil
}

func getMatchCriteriaCompletions() []protocol.CompletionItem {
kind := protocol.CompletionItemKindEnum

return []protocol.CompletionItem{
{
Label: string(matchparser.MatchCriteriaTypeCanonical),
Kind: &kind,
},
{
Label: string(matchparser.MatchCriteriaTypeFinal),
Kind: &kind,
},
{
Label: string(matchparser.MatchCriteriaTypeExec),
Kind: &kind,
},
{
Label: string(matchparser.MatchCriteriaTypeLocalNetwork),
Kind: &kind,
},
{
Label: string(matchparser.MatchCriteriaTypeHost),
Kind: &kind,
},
{
Label: string(matchparser.MatchCriteriaTypeOriginalHost),
Kind: &kind,
},
{
Label: string(matchparser.MatchCriteriaTypeTagged),
Kind: &kind,
},
{
Label: string(matchparser.MatchCriteriaTypeUser),
Kind: &kind,
},
{
Label: string(matchparser.MatchCriteriaTypeLocalUser),
Kind: &kind,
},
}
}

func getMatchAllKeywordCompletion() protocol.CompletionItem {
kind := protocol.CompletionItemKindKeyword

return protocol.CompletionItem{
Label: "all",
Kind: &kind,
}
}

func getMatchValueCompletions(
entry *matchparser.MatchEntry,
cursor common.CursorPosition,
) []protocol.CompletionItem {
value := entry.GetValueAtPosition(cursor)

var line string
var relativeCursor uint32

if value != nil {
line = value.Value.Raw
relativeCursor = common.DeprecatedImprovedCursorToIndex(
cursor,
line,
value.Start.Character,
)
} else {
line = ""
relativeCursor = 0
}

switch entry.Criteria.Type {
case matchparser.MatchCriteriaTypeUser:
return fields.MatchUserField.DeprecatedFetchCompletions(line, relativeCursor)
case matchparser.MatchCriteriaTypeGroup:
return fields.MatchGroupField.DeprecatedFetchCompletions(line, relativeCursor)
case matchparser.MatchCriteriaTypeHost:
return fields.MatchHostField.DeprecatedFetchCompletions(line, relativeCursor)
case matchparser.MatchCriteriaTypeAddress:
return fields.MatchAddressField.DeprecatedFetchCompletions(line, relativeCursor)
case matchparser.MatchCriteriaTypeLocalAddress:
return fields.MatchLocalAddressField.DeprecatedFetchCompletions(line, relativeCursor)
case matchparser.MatchCriteriaTypeLocalPort:
return fields.MatchLocalPortField.DeprecatedFetchCompletions(line, relativeCursor)
case matchparser.MatchCriteriaTypeRDomain:
return fields.MatchRDomainField.DeprecatedFetchCompletions(line, relativeCursor)
}

return nil
}
1 change: 0 additions & 1 deletion handlers/ssh_config/host-parser/host_ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,3 @@ type HostValue struct {
common.LocationRange
Value commonparser.ParsedString
}

6 changes: 3 additions & 3 deletions handlers/ssh_config/host-parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package hostparser

import (
"config-lsp/common"
"regexp"
commonparser "config-lsp/common/parser"
"regexp"
)

func NewHost() *Host {
Expand Down Expand Up @@ -36,11 +36,11 @@ func (h *Host) Parse(
host := HostValue{
LocationRange: common.LocationRange{
Start: common.Location{
Line: line,
Line: line,
Character: startCharacter + uint32(startIndex),
},
End: common.Location{
Line: line,
Line: line,
Character: startCharacter + uint32(endIndex),
},
},
Expand Down
8 changes: 3 additions & 5 deletions handlers/ssh_config/indexes/indexes.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"config-lsp/handlers/ssh_config/ast"
)


type ValidPath string

func (v ValidPath) AsURI() string {
Expand All @@ -21,9 +20,9 @@ type SSHIndexIncludeValue struct {
}

type SSHIndexIncludeLine struct {
Values []*SSHIndexIncludeValue
Option *ast.SSHOption
Block ast.SSHBlock
Values []*SSHIndexIncludeValue
Option *ast.SSHOption
Block ast.SSHBlock
}

type SSHIndexes struct {
Expand All @@ -33,4 +32,3 @@ type SSHIndexes struct {

BlockRanges map[uint32]ast.SSHBlock
}

8 changes: 4 additions & 4 deletions handlers/ssh_config/indexes/indexes_handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ var whitespacePattern = regexp.MustCompile(`\S+`)
func NewSSHIndexes() *SSHIndexes {
return &SSHIndexes{
AllOptionsPerName: make(map[string](map[ast.SSHBlock]([]*ast.SSHOption)), 0),
Includes: make([]*SSHIndexIncludeLine, 0),
Includes: make([]*SSHIndexIncludeLine, 0),
}
}

Expand Down Expand Up @@ -78,9 +78,9 @@ func CreateIndexes(config ast.SSHConfig) (*SSHIndexes, []common.LSPError) {
}

indexes.Includes[includeOption.Start.Line] = &SSHIndexIncludeLine{
Values: paths,
Option: includeOption,
Block: block,
Values: paths,
Option: includeOption,
Block: block,
}
}

Expand Down
2 changes: 1 addition & 1 deletion handlers/ssh_config/indexes/indexes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ Host nas01
opts[firstMatchBlock][0].OptionValue.Value.Value == "/nfs/shared/users/nixcraft/keys/server1/id_rsa") {
t.Errorf("Expected 3 IdentityFile options, but got %v", opts)
}

}

// TODO: Add check for options that require other options to be present
Expand Down
2 changes: 1 addition & 1 deletion handlers/ssh_config/lsp/text-document-did-open.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package lsp
import (
"config-lsp/common"
"config-lsp/handlers/ssh_config"
"config-lsp/handlers/ssh_config/ast"
"config-lsp/handlers/ssh_config/analyzer"
"config-lsp/handlers/ssh_config/ast"
"config-lsp/utils"

"github.com/tliron/glsp"
Expand Down
68 changes: 62 additions & 6 deletions handlers/ssh_config/match-parser/Match.g4
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,19 @@ root
;

matchEntry
: criteria separator? values?
: entrySingle | entryWithValue
;

separator
: WHITESPACE
entrySingle
: criteriaSingle
;

criteria
: string
entryWithValue
: criteriaWithValue separator? values?
;

separator
: WHITESPACE
;

values
Expand All @@ -24,6 +28,14 @@ value
: string
;

criteriaSingle
: QUOTE? (ALL | CANONICAL | FINAL) QUOTE?
;

criteriaWithValue
: QUOTE? (EXEC | LOCALNETWORK | HOST | ORIGINALHOST | TAGGED | USER | LOCALUSER) QUOTE?
;

string
: (QUOTED_STRING | STRING)
;
Expand All @@ -32,6 +44,46 @@ COMMA
: ','
;

ALL
: ('a' | 'A') ('l' | 'L') ('l' | 'L')
;

CANONICAL
: ('c' | 'C') ('a' | 'A') ('n' | 'N') ('o' | 'O') ('n' | 'N') ('i' | 'I') ('c' | 'C') ('a' | 'A') ('l' | 'L')
;

FINAL
: ('f' | 'F') ('i' | 'I') ('n' | 'N') ('a' | 'A') ('l' | 'L')
;

EXEC
: ('e' | 'E') ('x' | 'X') ('e' | 'E') ('c' | 'C')
;

LOCALNETWORK
: ('l' | 'L') ('o' | 'O') ('c' | 'C') ('a' | 'A') ('l' | 'L') ('n' | 'N') ('e' | 'E') ('t' | 'T') ('w' | 'W') ('o' | 'O') ('r' | 'R') ('k' | 'K')
;

HOST
: ('h' | 'H') ('o' | 'O') ('s' | 'S') ('t' | 'T')
;

ORIGINALHOST
: ('o' | 'O') ('r' | 'R') ('i' | 'I') ('g' | 'G') ('i' | 'I') ('n' | 'N') ('a' | 'A') ('l' | 'L') ('h' | 'H') ('o' | 'O') ('s' | 'S') ('t' | 'T')
;

TAGGED
: ('t' | 'T') ('a' | 'A') ('g' | 'G') ('g' | 'G') ('e' | 'E') ('d' | 'D')
;

USER
: ('u' | 'U') ('s' | 'S') ('e' | 'E') ('r' | 'R')
;

LOCALUSER
: ('l' | 'L') ('o' | 'O') ('c' | 'C') ('a' | 'A') ('l' | 'L') ('u' | 'U') ('s' | 'S') ('e' | 'E') ('r' | 'R')
;

STRING
: ~(' ' | '\t' | '\r' | '\n' | '#' | ',')+
;
Expand All @@ -41,5 +93,9 @@ WHITESPACE
;

QUOTED_STRING
: '"' WHITESPACE? (STRING WHITESPACE)* STRING? ('"')?
: QUOTE WHITESPACE? (STRING WHITESPACE)* STRING? QUOTE?
;

QUOTE
: '"'
;
Loading

0 comments on commit b1a5898

Please sign in to comment.