mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 23:15:26 +02:00
merge
This commit is contained in:
commit
8517a722d1
20
.eslintrc.js
Normal file
20
.eslintrc.js
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
/**@type {import('eslint').Linter.Config} */
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
module.exports = {
|
||||||
|
root: true,
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
plugins: [
|
||||||
|
'@typescript-eslint',
|
||||||
|
],
|
||||||
|
extends: [
|
||||||
|
'eslint:recommended',
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
],
|
||||||
|
rules: {
|
||||||
|
'semi': [2, "always"],
|
||||||
|
'@typescript-eslint/no-unused-vars': 0,
|
||||||
|
'@typescript-eslint/no-explicit-any': 0,
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 0,
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 0,
|
||||||
|
}
|
||||||
|
};
|
2
.github/workflows/pr-tests.yaml
vendored
2
.github/workflows/pr-tests.yaml
vendored
@ -20,5 +20,5 @@ jobs:
|
|||||||
run: nix flake check
|
run: nix flake check
|
||||||
|
|
||||||
- name: Build app
|
- name: Build app
|
||||||
run: nix develop --command bash -c "go build"
|
run: nix develop --command bash -c "cd server && go build"
|
||||||
|
|
||||||
|
36
flake.nix
36
flake.nix
@ -27,27 +27,53 @@
|
|||||||
inputs = [
|
inputs = [
|
||||||
pkgs.go_1_22
|
pkgs.go_1_22
|
||||||
];
|
];
|
||||||
in {
|
server = pkgs.buildGoModule {
|
||||||
packages = {
|
|
||||||
default = pkgs.buildGoModule {
|
|
||||||
nativeBuildInputs = inputs;
|
nativeBuildInputs = inputs;
|
||||||
pname = "github.com/Myzel394/config-lsp";
|
pname = "github.com/Myzel394/config-lsp";
|
||||||
version = "v0.0.1";
|
version = "v0.0.1";
|
||||||
src = ./.;
|
src = ./server;
|
||||||
vendorHash = "sha256-PUVmhdbmfy1FaSzLt3SIK0MtWezsjb+PL+Z5YxMMhdw=";
|
vendorHash = "sha256-s+sjOVvqU20+mbEFKg+RCD+dhScqSatk9eseX2IioPI=";
|
||||||
checkPhase = ''
|
checkPhase = ''
|
||||||
go test -v $(pwd)/...
|
go test -v $(pwd)/...
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
in {
|
||||||
|
packages = {
|
||||||
|
default = server;
|
||||||
|
"vs-code-extension" = let
|
||||||
|
name = "config-lsp-vs-code-extension";
|
||||||
|
node-modules = pkgs.mkYarnPackage {
|
||||||
|
src = ./vs-code-extension;
|
||||||
|
name = name;
|
||||||
|
packageJSON = ./vs-code-extension/package.json;
|
||||||
|
yarnLock = ./vs-code-extension/yarn.lock;
|
||||||
|
yarnNix = ./vs-code-extension/yarn.nix;
|
||||||
|
|
||||||
|
buildPhase = ''
|
||||||
|
yarn --offline run compile
|
||||||
|
'';
|
||||||
|
installPhase = ''
|
||||||
|
mv deps/${name}/out $out
|
||||||
|
cp ${server}/bin/config-lsp $out/
|
||||||
|
'';
|
||||||
|
distPhase = "true";
|
||||||
|
};
|
||||||
|
in node-modules;
|
||||||
};
|
};
|
||||||
devShells.default = pkgs.mkShell {
|
devShells.default = pkgs.mkShell {
|
||||||
buildInputs = inputs ++ (with pkgs; [
|
buildInputs = inputs ++ (with pkgs; [
|
||||||
mailutils
|
mailutils
|
||||||
wireguard-tools
|
wireguard-tools
|
||||||
|
antlr
|
||||||
]) ++ (if pkgs.stdenv.isLinux then with pkgs; [
|
]) ++ (if pkgs.stdenv.isLinux then with pkgs; [
|
||||||
postfix
|
postfix
|
||||||
] else []);
|
] else []);
|
||||||
};
|
};
|
||||||
|
devShells."vs-code-extension" = pkgs.mkShell {
|
||||||
|
buildInputs = [
|
||||||
|
pkgs.nodejs
|
||||||
|
];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package lsp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tliron/glsp"
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TextDocumentDefinition(context *glsp.Context, params *protocol.DefinitionParams) ([]protocol.Location, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
@ -1,13 +0,0 @@
|
|||||||
package lsp
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/tliron/glsp"
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TextDocumentHover(
|
|
||||||
context *glsp.Context,
|
|
||||||
params *protocol.HoverParams,
|
|
||||||
) (*protocol.Hover, error) {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
package analyzer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"config-lsp/common"
|
|
||||||
"config-lsp/handlers/sshd_config"
|
|
||||||
"config-lsp/handlers/sshd_config/indexes"
|
|
||||||
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Analyze(
|
|
||||||
d *sshdconfig.SSHDDocument,
|
|
||||||
) []protocol.Diagnostic {
|
|
||||||
errors := analyzeStructureIsValid(d)
|
|
||||||
|
|
||||||
if len(errors) > 0 {
|
|
||||||
return common.ErrsToDiagnostics(errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
i, indexErrors := indexes.CreateIndexes(*d.Config)
|
|
||||||
|
|
||||||
d.Indexes = i
|
|
||||||
|
|
||||||
errors = append(errors, indexErrors...)
|
|
||||||
|
|
||||||
if len(errors) > 0 {
|
|
||||||
return common.ErrsToDiagnostics(errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
includeErrors := analyzeIncludeValues(d)
|
|
||||||
|
|
||||||
if len(includeErrors) > 0 {
|
|
||||||
errors = append(errors, includeErrors...)
|
|
||||||
} else {
|
|
||||||
for _, include := range d.Indexes.Includes {
|
|
||||||
for _, value := range include.Values {
|
|
||||||
for _, path := range value.Paths {
|
|
||||||
_, err := parseFile(string(path))
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
errors = append(errors, common.LSPError{
|
|
||||||
Range: value.LocationRange,
|
|
||||||
Err: err,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
errors = append(errors, analyzeMatchBlocks(d)...)
|
|
||||||
|
|
||||||
if len(errors) > 0 {
|
|
||||||
return common.ErrsToDiagnostics(errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,146 +0,0 @@
|
|||||||
package analyzer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"config-lsp/common"
|
|
||||||
docvalues "config-lsp/doc-values"
|
|
||||||
sshdconfig "config-lsp/handlers/sshd_config"
|
|
||||||
"config-lsp/handlers/sshd_config/fields"
|
|
||||||
"config-lsp/handlers/sshd_config/match-parser"
|
|
||||||
"config-lsp/utils"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func analyzeMatchBlocks(
|
|
||||||
d *sshdconfig.SSHDDocument,
|
|
||||||
) []common.LSPError {
|
|
||||||
errs := make([]common.LSPError, 0)
|
|
||||||
|
|
||||||
for matchBlock, options := range d.Indexes.AllOptionsPerName["Match"] {
|
|
||||||
option := options[0]
|
|
||||||
// Check if the match block has filled out all fields
|
|
||||||
if matchBlock == nil || matchBlock.MatchValue == nil || len(matchBlock.MatchValue.Entries) == 0 {
|
|
||||||
errs = append(errs, common.LSPError{
|
|
||||||
Range: option.LocationRange,
|
|
||||||
Err: errors.New("A match expression is required"),
|
|
||||||
})
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range matchBlock.MatchValue.Entries {
|
|
||||||
if entry.Values == nil {
|
|
||||||
errs = append(errs, common.LSPError{
|
|
||||||
Range: entry.LocationRange,
|
|
||||||
Err: errors.New(fmt.Sprintf("A value for %s is required", entry.Criteria.Type)),
|
|
||||||
})
|
|
||||||
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
errs = append(errs, analyzeMatchValuesContainsPositiveValue(entry.Values)...)
|
|
||||||
|
|
||||||
for _, value := range entry.Values.Values {
|
|
||||||
errs = append(errs, analyzeMatchValueNegation(value)...)
|
|
||||||
errs = append(errs, analyzeMatchValueIsValid(value, entry.Criteria.Type)...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if match blocks are not empty
|
|
||||||
if matchBlock.Options.Size() == 0 {
|
|
||||||
errs = append(errs, common.LSPError{
|
|
||||||
Range: option.LocationRange,
|
|
||||||
Err: errors.New("This match block is empty"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func analyzeMatchValueNegation(
|
|
||||||
value *matchparser.MatchValue,
|
|
||||||
) []common.LSPError {
|
|
||||||
errs := make([]common.LSPError, 0)
|
|
||||||
|
|
||||||
positionsAsList := utils.AllIndexes(value.Value.Raw, "!")
|
|
||||||
positions := utils.SliceToMap(positionsAsList, struct{}{})
|
|
||||||
|
|
||||||
delete(positions, 0)
|
|
||||||
|
|
||||||
for position := range positions {
|
|
||||||
errs = append(errs, common.LSPError{
|
|
||||||
Range: common.LocationRange{
|
|
||||||
Start: common.Location{
|
|
||||||
Line: value.Start.Line,
|
|
||||||
Character: uint32(position) + value.Start.Character,
|
|
||||||
},
|
|
||||||
End: common.Location{
|
|
||||||
Line: value.End.Line,
|
|
||||||
Character: uint32(position) + value.End.Character,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Err: errors.New("The negation operator (!) may only occur at the beginning of a value"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func analyzeMatchValuesContainsPositiveValue(
|
|
||||||
values *matchparser.MatchValues,
|
|
||||||
) []common.LSPError {
|
|
||||||
if len(values.Values) == 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
containsPositive := false
|
|
||||||
|
|
||||||
for _, value := range values.Values {
|
|
||||||
if !strings.HasPrefix(value.Value.Value, "!") {
|
|
||||||
containsPositive = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !containsPositive {
|
|
||||||
return []common.LSPError{
|
|
||||||
{
|
|
||||||
Range: values.LocationRange,
|
|
||||||
Err: errors.New("At least one positive value is required. A negated match will never produce a positive result by itself"),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func analyzeMatchValueIsValid(
|
|
||||||
value *matchparser.MatchValue,
|
|
||||||
criteria matchparser.MatchCriteriaType,
|
|
||||||
) []common.LSPError {
|
|
||||||
errs := make([]common.LSPError, 0)
|
|
||||||
|
|
||||||
if value.Value.Raw == "" {
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
docOption := fields.MatchValueFieldMap[criteria]
|
|
||||||
invalidValues := docOption.DeprecatedCheckIsValid(value.Value.Raw)
|
|
||||||
|
|
||||||
errs = append(
|
|
||||||
errs,
|
|
||||||
utils.Map(
|
|
||||||
invalidValues,
|
|
||||||
func(invalidValue *docvalues.InvalidValue) common.LSPError {
|
|
||||||
err := docvalues.LSPErrorFromInvalidValue(value.Start.Line, *invalidValue)
|
|
||||||
err.ShiftCharacter(value.Start.Character)
|
|
||||||
|
|
||||||
return err
|
|
||||||
},
|
|
||||||
)...,
|
|
||||||
)
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
@ -1,121 +0,0 @@
|
|||||||
package analyzer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"config-lsp/common"
|
|
||||||
docvalues "config-lsp/doc-values"
|
|
||||||
sshdconfig "config-lsp/handlers/sshd_config"
|
|
||||||
"config-lsp/handlers/sshd_config/ast"
|
|
||||||
"config-lsp/handlers/sshd_config/fields"
|
|
||||||
"config-lsp/utils"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func analyzeStructureIsValid(
|
|
||||||
d *sshdconfig.SSHDDocument,
|
|
||||||
) []common.LSPError {
|
|
||||||
errs := make([]common.LSPError, 0)
|
|
||||||
it := d.Config.Options.Iterator()
|
|
||||||
|
|
||||||
for it.Next() {
|
|
||||||
entry := it.Value().(ast.SSHDEntry)
|
|
||||||
|
|
||||||
switch entry.(type) {
|
|
||||||
case *ast.SSHDOption:
|
|
||||||
errs = append(errs, checkOption(entry.(*ast.SSHDOption), false)...)
|
|
||||||
case *ast.SSHDMatchBlock:
|
|
||||||
matchBlock := entry.(*ast.SSHDMatchBlock)
|
|
||||||
errs = append(errs, checkMatchBlock(matchBlock)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkOption(
|
|
||||||
option *ast.SSHDOption,
|
|
||||||
isInMatchBlock bool,
|
|
||||||
) []common.LSPError {
|
|
||||||
errs := make([]common.LSPError, 0)
|
|
||||||
|
|
||||||
if option.Key == nil {
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
errs = append(errs, checkIsUsingDoubleQuotes(option.Key.Value, option.Key.LocationRange)...)
|
|
||||||
errs = append(errs, checkQuotesAreClosed(option.Key.Value, option.Key.LocationRange)...)
|
|
||||||
|
|
||||||
docOption, found := fields.Options[option.Key.Key]
|
|
||||||
|
|
||||||
if !found {
|
|
||||||
errs = append(errs, common.LSPError{
|
|
||||||
Range: option.Key.LocationRange,
|
|
||||||
Err: errors.New(fmt.Sprintf("Unknown option: %s", option.Key.Key)),
|
|
||||||
})
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, found := fields.MatchAllowedOptions[option.Key.Key]; !found && isInMatchBlock {
|
|
||||||
errs = append(errs, common.LSPError{
|
|
||||||
Range: option.Key.LocationRange,
|
|
||||||
Err: errors.New(fmt.Sprintf("Option '%s' is not allowed inside Match blocks", option.Key.Key)),
|
|
||||||
})
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
if option.OptionValue == nil || option.OptionValue.Value.Value == "" {
|
|
||||||
errs = append(errs, common.LSPError{
|
|
||||||
Range: option.Key.LocationRange,
|
|
||||||
Err: errors.New(fmt.Sprintf("Option '%s' requires a value", option.Key.Key)),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
errs = append(errs, checkIsUsingDoubleQuotes(option.OptionValue.Value, option.OptionValue.LocationRange)...)
|
|
||||||
errs = append(errs, checkQuotesAreClosed(option.OptionValue.Value, option.OptionValue.LocationRange)...)
|
|
||||||
|
|
||||||
invalidValues := docOption.DeprecatedCheckIsValid(option.OptionValue.Value.Value)
|
|
||||||
|
|
||||||
errs = append(
|
|
||||||
errs,
|
|
||||||
utils.Map(
|
|
||||||
invalidValues,
|
|
||||||
func(invalidValue *docvalues.InvalidValue) common.LSPError {
|
|
||||||
err := docvalues.LSPErrorFromInvalidValue(option.Start.Line, *invalidValue)
|
|
||||||
err.ShiftCharacter(option.OptionValue.Start.Character)
|
|
||||||
|
|
||||||
return err
|
|
||||||
},
|
|
||||||
)...,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if option.Separator == nil || option.Separator.Value.Value == "" {
|
|
||||||
errs = append(errs, common.LSPError{
|
|
||||||
Range: option.Key.LocationRange,
|
|
||||||
Err: errors.New(fmt.Sprintf("There should be a separator between an option and its value")),
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
errs = append(errs, checkIsUsingDoubleQuotes(option.Separator.Value, option.Separator.LocationRange)...)
|
|
||||||
errs = append(errs, checkQuotesAreClosed(option.Separator.Value, option.Separator.LocationRange)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkMatchBlock(
|
|
||||||
matchBlock *ast.SSHDMatchBlock,
|
|
||||||
) []common.LSPError {
|
|
||||||
errs := make([]common.LSPError, 0)
|
|
||||||
|
|
||||||
it := matchBlock.Options.Iterator()
|
|
||||||
|
|
||||||
for it.Next() {
|
|
||||||
option := it.Value().(*ast.SSHDOption)
|
|
||||||
|
|
||||||
errs = append(errs, checkOption(option, true)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
@ -1,59 +0,0 @@
|
|||||||
package analyzer
|
|
||||||
|
|
||||||
import (
|
|
||||||
"config-lsp/common"
|
|
||||||
commonparser "config-lsp/common/parser"
|
|
||||||
sshdconfig "config-lsp/handlers/sshd_config"
|
|
||||||
"errors"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
func analyzeQuotesAreValid(
|
|
||||||
d *sshdconfig.SSHDDocument,
|
|
||||||
) []common.LSPError {
|
|
||||||
errs := make([]common.LSPError, 0)
|
|
||||||
|
|
||||||
for _, option := range d.Config.GetAllOptions() {
|
|
||||||
errs = append(errs, checkIsUsingDoubleQuotes(option.Key.Value, option.Key.LocationRange)...)
|
|
||||||
errs = append(errs, checkIsUsingDoubleQuotes(option.OptionValue.Value, option.OptionValue.LocationRange)...)
|
|
||||||
|
|
||||||
errs = append(errs, checkQuotesAreClosed(option.Key.Value, option.Key.LocationRange)...)
|
|
||||||
errs = append(errs, checkQuotesAreClosed(option.OptionValue.Value, option.OptionValue.LocationRange)...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkIsUsingDoubleQuotes(
|
|
||||||
value commonparser.ParsedString,
|
|
||||||
valueRange common.LocationRange,
|
|
||||||
) []common.LSPError {
|
|
||||||
singleQuotePosition := strings.Index(value.Raw, "'")
|
|
||||||
|
|
||||||
if singleQuotePosition != -1 {
|
|
||||||
return []common.LSPError{
|
|
||||||
{
|
|
||||||
Range: valueRange,
|
|
||||||
Err: errors.New("sshd_config does not support single quotes. Use double quotes (\") instead."),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkQuotesAreClosed(
|
|
||||||
value commonparser.ParsedString,
|
|
||||||
valueRange common.LocationRange,
|
|
||||||
) []common.LSPError {
|
|
||||||
if strings.Count(value.Raw, "\"")%2 != 0 {
|
|
||||||
return []common.LSPError{
|
|
||||||
{
|
|
||||||
Range: valueRange,
|
|
||||||
Err: errors.New("There are unclosed quotes here. Make sure all quotes are closed."),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,62 +0,0 @@
|
|||||||
package analyzer
|
|
||||||
|
|
||||||
import (
|
|
||||||
testutils_test "config-lsp/handlers/sshd_config/test_utils"
|
|
||||||
"testing"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSimpleInvalidQuotesExample(
|
|
||||||
t *testing.T,
|
|
||||||
) {
|
|
||||||
d := testutils_test.DocumentFromInput(t, `
|
|
||||||
PermitRootLogin 'yes'
|
|
||||||
`)
|
|
||||||
|
|
||||||
errors := analyzeQuotesAreValid(d)
|
|
||||||
|
|
||||||
if !(len(errors) == 1) {
|
|
||||||
t.Errorf("Expected 1 error, got %v", len(errors))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSingleQuotesKeyAndOptionExample(
|
|
||||||
t *testing.T,
|
|
||||||
) {
|
|
||||||
d := testutils_test.DocumentFromInput(t, `
|
|
||||||
'Port' '22'
|
|
||||||
`)
|
|
||||||
|
|
||||||
errors := analyzeQuotesAreValid(d)
|
|
||||||
|
|
||||||
if !(len(errors) == 2) {
|
|
||||||
t.Errorf("Expected 2 errors, got %v", len(errors))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestSimpleUnclosedQuoteExample(
|
|
||||||
t *testing.T,
|
|
||||||
) {
|
|
||||||
d := testutils_test.DocumentFromInput(t, `
|
|
||||||
PermitRootLogin "yes
|
|
||||||
`)
|
|
||||||
|
|
||||||
errors := analyzeQuotesAreValid(d)
|
|
||||||
|
|
||||||
if !(len(errors) == 1) {
|
|
||||||
t.Errorf("Expected 1 error, got %v", len(errors))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIncompleteQuotesExample(
|
|
||||||
t *testing.T,
|
|
||||||
) {
|
|
||||||
d := testutils_test.DocumentFromInput(t, `
|
|
||||||
"Port
|
|
||||||
`)
|
|
||||||
|
|
||||||
errors := analyzeQuotesAreValid(d)
|
|
||||||
|
|
||||||
if !(len(errors) == 1) {
|
|
||||||
t.Errorf("Expected 1 error, got %v", len(errors))
|
|
||||||
}
|
|
||||||
}
|
|
@ -31,7 +31,7 @@ func (v *InvalidValue) GetRange(line uint32, characterStart uint32) protocol.Ran
|
|||||||
},
|
},
|
||||||
End: protocol.Position{
|
End: protocol.Position{
|
||||||
Line: line,
|
Line: line,
|
||||||
Character: characterStart + v.End,
|
Character: characterStart + v.End + 1,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -176,7 +176,6 @@ func (v ArrayValue) getCurrentValue(line string, cursor uint32) (string, uint32)
|
|||||||
func (v ArrayValue) DeprecatedFetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
|
func (v ArrayValue) DeprecatedFetchCompletions(line string, cursor uint32) []protocol.CompletionItem {
|
||||||
value, cursor := v.getCurrentValue(line, cursor)
|
value, cursor := v.getCurrentValue(line, cursor)
|
||||||
|
|
||||||
println("after array", value, cursor)
|
|
||||||
return v.SubValue.DeprecatedFetchCompletions(value, cursor)
|
return v.SubValue.DeprecatedFetchCompletions(value, cursor)
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user