mirror of
https://github.com/Myzel394/config-lsp.git
synced 2025-06-18 15:05:28 +02:00
Merge pull request #40 from Myzel394/improve-wireguard
fix(server): Overall bugfixes & improvements
This commit is contained in:
commit
9b306f339e
12
flake.lock
generated
12
flake.lock
generated
@ -26,11 +26,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1741396135,
|
"lastModified": 1742209644,
|
||||||
"narHash": "sha256-wqmdLr7h4Bk8gyKutgaApJKOM8JVvywI5P48NuqJ9Jg=",
|
"narHash": "sha256-jMy1XqXqD0/tJprEbUmKilTkvbDY/C0ZGSsJJH4TNCE=",
|
||||||
"owner": "tweag",
|
"owner": "tweag",
|
||||||
"repo": "gomod2nix",
|
"repo": "gomod2nix",
|
||||||
"rev": "0983848bf2a7ccbfe24d874065adb8fd0f23729b",
|
"rev": "8f3534eb8f6c5c3fce799376dc3b91bae6b11884",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@ -41,11 +41,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1741513245,
|
"lastModified": 1742669843,
|
||||||
"narHash": "sha256-7rTAMNTY1xoBwz0h7ZMtEcd8LELk9R5TzBPoHuhNSCk=",
|
"narHash": "sha256-G5n+FOXLXcRx+3hCJ6Rt6ZQyF1zqQ0DL0sWAMn2Nk0w=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "e3e32b642a31e6714ec1b712de8c91a3352ce7e1",
|
"rev": "1e5b653dff12029333a6546c11e108ede13052eb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
59
flake.nix
59
flake.nix
@ -3,12 +3,12 @@
|
|||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||||
|
utils.url = "github:numtide/flake-utils";
|
||||||
gomod2nix = {
|
gomod2nix = {
|
||||||
url = "github:tweag/gomod2nix";
|
url = "github:tweag/gomod2nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
inputs.utils.follows = "utils";
|
inputs.utils.follows = "utils";
|
||||||
};
|
};
|
||||||
utils.url = "github:numtide/flake-utils";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = { self, nixpkgs, utils, gomod2nix }:
|
outputs = { self, nixpkgs, utils, gomod2nix }:
|
||||||
@ -23,7 +23,7 @@
|
|||||||
"aarch64-windows"
|
"aarch64-windows"
|
||||||
] (system:
|
] (system:
|
||||||
let
|
let
|
||||||
version = "0.2.0"; # CI:CD-VERSION
|
version = "0.2.1"; # CI:CD-VERSION
|
||||||
pkgs = import nixpkgs {
|
pkgs = import nixpkgs {
|
||||||
inherit system;
|
inherit system;
|
||||||
overlays = [
|
overlays = [
|
||||||
@ -131,21 +131,48 @@
|
|||||||
};
|
};
|
||||||
in node-modules;
|
in node-modules;
|
||||||
};
|
};
|
||||||
devShells.default = pkgs.mkShell {
|
|
||||||
buildInputs = inputs ++ (with pkgs; [
|
devShells.default = let
|
||||||
mailutils
|
version = "0.16.2";
|
||||||
wireguard-tools
|
ourGopls = pkgs.buildGoModule {
|
||||||
antlr
|
pname = "gopls";
|
||||||
just
|
inherit version;
|
||||||
]) ++ (if pkgs.stdenv.isLinux then with pkgs; [
|
modRoot = "gopls";
|
||||||
postfix
|
vendorHash = "sha256-ta94xPboFtSxFeuMtPX76XiC1O7osNl4oLk64wIyyz4=";
|
||||||
] else []);
|
|
||||||
};
|
# https://github.com/golang/tools/blob/9ed98faa/gopls/main.go#L27-L30
|
||||||
|
ldflags = [ "-X main.version=v${version}" ];
|
||||||
|
|
||||||
|
doCheck = false;
|
||||||
|
|
||||||
|
# Only build gopls, and not the integration tests or documentation generator.
|
||||||
|
subPackages = [ "." ];
|
||||||
|
|
||||||
|
src = pkgs.fetchFromGitHub {
|
||||||
|
owner = "golang";
|
||||||
|
repo = "tools";
|
||||||
|
rev = "gopls/v${version}";
|
||||||
|
hash = "sha256-amy00VMUcmyjDoZ4d9/+YswfcZ+1/cGvFsA4sAmc1dA=";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
pkgs.mkShell {
|
||||||
|
buildInputs = inputs ++ (with pkgs; [
|
||||||
|
mailutils
|
||||||
|
wireguard-tools
|
||||||
|
antlr
|
||||||
|
just
|
||||||
|
ourGopls
|
||||||
|
]) ++ (if pkgs.stdenv.isLinux then with pkgs; [
|
||||||
|
postfix
|
||||||
|
] else []);
|
||||||
|
};
|
||||||
|
|
||||||
devShells."vs-code-extension" = pkgs.mkShell {
|
devShells."vs-code-extension" = pkgs.mkShell {
|
||||||
buildInputs = [
|
buildInputs = with pkgs; [
|
||||||
pkgs.nodejs
|
nodejs
|
||||||
pkgs.vsce
|
vsce
|
||||||
pkgs.yarn2nix
|
yarn2nix
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
75
server/common-documentation/mnt-apfs.go
Normal file
75
server/common-documentation/mnt-apfs.go
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package commondocumentation
|
||||||
|
|
||||||
|
import (
|
||||||
|
docvalues "config-lsp/doc-values"
|
||||||
|
)
|
||||||
|
|
||||||
|
var APFSDocumentationAssignable = map[docvalues.EnumString]docvalues.DeprecatedValue{
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"user",
|
||||||
|
"Set the owner of the files in the file system to user. The default owner is the owner of the directory on which the file system is being mounted. The user may be a user-name, or a numeric value.",
|
||||||
|
): docvalues.UIDValue{},
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"group",
|
||||||
|
"Set the group of the files in the file system to group. The default group is the group of the directory on which the file system is being mounted. The group may be a group-name, or a numeric value.",
|
||||||
|
): docvalues.GIDValue{},
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"snapshot",
|
||||||
|
"The name of the snapshot to mount. In this usage pathname is the mounted root directory of the base volume containing the snapshot.",
|
||||||
|
): docvalues.StringValue{},
|
||||||
|
}
|
||||||
|
|
||||||
|
var APFSDocumentationEnums = []docvalues.EnumString{
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"async",
|
||||||
|
"All I/O to the file system should be done asynchronously. This can be somewhat dangerous with respect to losing data when faced with system crashes and power outages. This is also the default. It can be avoided with the noasync option.",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"noauto",
|
||||||
|
"This filesystem should be skipped when mount is run with the -a flag.",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"nodev",
|
||||||
|
"Do not interpret character or block special devices on the file system. This option is useful for a server that has file systems containing special devices for architectures other than its own.",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"noexec",
|
||||||
|
"Do not allow execution of any binaries on the mounted file system. This option is useful for a server that has file systems containing binaries for architectures other than its own.",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"noowners",
|
||||||
|
"Ignore the ownership field for the entire volume. This causes all objects to appear as owned by user ID 99 and group ID 99. User ID 99 is interpreted as the current effective user ID, while group ID 99 is used directly and translates to ``unknown''.",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"nosuid",
|
||||||
|
"Do not allow set-user-identifier or set-group-identifier bits to take effect.",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"rdonly",
|
||||||
|
"The same as -r; mount the file system read-only (even the super-user may not write it).",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"update",
|
||||||
|
"The same as -u; indicate that the status of an already mounted file system should be changed.",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"union",
|
||||||
|
"Causes the namespace to appear as the union of directories of the mounted filesystem with corresponding directories in the underlying filesystem. Lookups will be done in the mounted filesystem first. If those operations fail due to a non-existent file the underlying directory is then accessed.",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"noatime",
|
||||||
|
"Do not update the file access time when reading from a file. This option is useful on file systems where there are large numbers of files and performance is more critical than updating the file access time (which is rarely ever important).",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"strictatime",
|
||||||
|
"Always update the file access time when reading from a file. Without this option the filesystem may default to a less strict update mode, where some access time updates are skipped for performance reasons. This option could be ignored if it is not supported by the filesystem.",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"nobrowse",
|
||||||
|
"This option indicates that the mount point should not be visible via the GUI (i.e., appear on the Desktop as a separate volume).",
|
||||||
|
),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"nofollow",
|
||||||
|
"This option indicates that in the course of the mount system call, the kernel should not follow any symlinks that may be present in the provided mount-on directory. This is the same as the -k option.",
|
||||||
|
),
|
||||||
|
}
|
195
server/common-documentation/mnt-bcachefs.go
Normal file
195
server/common-documentation/mnt-bcachefs.go
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
package commondocumentation
|
||||||
|
|
||||||
|
import docvalues "config-lsp/doc-values"
|
||||||
|
|
||||||
|
var checksumType = docvalues.EnumValue{
|
||||||
|
EnforceValues: true,
|
||||||
|
Values: []docvalues.EnumString{
|
||||||
|
docvalues.CreateEnumString("none"),
|
||||||
|
docvalues.CreateEnumString("crc32c"),
|
||||||
|
docvalues.CreateEnumString("crc64"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var compressionType = docvalues.EnumValue{
|
||||||
|
EnforceValues: true,
|
||||||
|
Values: []docvalues.EnumString{
|
||||||
|
docvalues.CreateEnumStringWithDoc("none", "(default)"),
|
||||||
|
docvalues.CreateEnumString("lz4"),
|
||||||
|
docvalues.CreateEnumString("gzip"),
|
||||||
|
docvalues.CreateEnumString("zstd"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// No idea if those enums are correct,
|
||||||
|
// the documentation does not provide any information
|
||||||
|
var booleanEnumValue = docvalues.EnumValue{
|
||||||
|
EnforceValues: true,
|
||||||
|
Values: []docvalues.EnumString{
|
||||||
|
docvalues.CreateEnumString("yes"),
|
||||||
|
docvalues.CreateEnumString("no"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
var BcacheFSDocumentationAssignable = map[docvalues.EnumString]docvalues.DeprecatedValue{
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"errors",
|
||||||
|
"Action to take on filesystem error. The errors option is used for inconsistencies that indicate some sort of a bug",
|
||||||
|
): docvalues.EnumValue{
|
||||||
|
EnforceValues: true,
|
||||||
|
Values: []docvalues.EnumString{
|
||||||
|
docvalues.CreateEnumStringWithDoc("continue", "Log the error but continue normal operation"),
|
||||||
|
docvalues.CreateEnumStringWithDoc("ro", "Emergency read only, immediately halting any changes to the filesystem on disk"),
|
||||||
|
docvalues.CreateEnumStringWithDoc("panic", "Immediately halt the entire machine, printing a backtrace on the system console"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"metadata_replicas",
|
||||||
|
"Number of replicas for metadata (journal and btree)",
|
||||||
|
): docvalues.PositiveNumberValue(),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"data_replicas",
|
||||||
|
"Number of replicas for user data",
|
||||||
|
): docvalues.PositiveNumberValue(),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"metadata_checksum",
|
||||||
|
"Checksum type for metadata writes",
|
||||||
|
): checksumType,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"data_checksum",
|
||||||
|
"Checksum type for data writes",
|
||||||
|
): checksumType,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"compression",
|
||||||
|
"Compression type",
|
||||||
|
): compressionType,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"background_compression",
|
||||||
|
"Background compression type",
|
||||||
|
): compressionType,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"str_hash",
|
||||||
|
"Hash function for string hash tables (directories and xattrs)",
|
||||||
|
): docvalues.EnumValue{
|
||||||
|
EnforceValues: true,
|
||||||
|
Values: []docvalues.EnumString{
|
||||||
|
docvalues.CreateEnumString("crc32c"),
|
||||||
|
docvalues.CreateEnumString("crc64"),
|
||||||
|
docvalues.CreateEnumString("siphash"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"metadata_target",
|
||||||
|
"Preferred target for metadata writes",
|
||||||
|
): docvalues.StringValue{},
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"foreground_target",
|
||||||
|
"Preferred target for foreground writes",
|
||||||
|
): docvalues.StringValue{},
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"background_target",
|
||||||
|
"Target for data to be moved to in the background",
|
||||||
|
): docvalues.StringValue{},
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"promote_target",
|
||||||
|
"Target for data to be copied to on read",
|
||||||
|
): docvalues.StringValue{},
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"erasure_code",
|
||||||
|
"Enable erasure coding",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"inodes_32bit",
|
||||||
|
"Restrict new inode numbers to 32 bits",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"shard_inode_numbers",
|
||||||
|
"Use CPU id for high bits of new inode numbers.",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"wide_macs",
|
||||||
|
"Store full 128 bit cryptographic MACs (default 80)",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"inline_data",
|
||||||
|
"Enable inline data extents (default on)",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"journal_flush_delay",
|
||||||
|
"Delay in milliseconds before automatic journal commit (default 1000)",
|
||||||
|
): docvalues.PositiveNumberValue(),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"journal_flush_disabled",
|
||||||
|
"Disables journal flush on sync/fsync. `journal_flush_delay` remains in effect, thus with the default setting not more than 1 second of work will be lost",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"journal_reclaim",
|
||||||
|
"Reclaim journal space after a certain amount of time",
|
||||||
|
): docvalues.PositiveNumberValue(),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"journal_reclaim_delay",
|
||||||
|
"Delay in milliseconds before automatic journal reclaim",
|
||||||
|
): docvalues.PositiveNumberValue(),
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"acl",
|
||||||
|
"Enable POSIX ACLs",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"usrquota",
|
||||||
|
"Enable user quotas",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"grpquota",
|
||||||
|
"Enable group quotas",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"prjquota",
|
||||||
|
"Enable project quotas",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"degraded",
|
||||||
|
"Allow mounting with data degraded",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"very_degraded",
|
||||||
|
"Allow mounting with data missing",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"verbose",
|
||||||
|
"Extra debugging info during mount/recovery",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"fsck",
|
||||||
|
"Run fsck during mount",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"fix_errors",
|
||||||
|
"Fix errors without asking during fsck",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"ratelimit_errors",
|
||||||
|
"Ratelimit error messages during fsck",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"read_only",
|
||||||
|
"Mount in read only mode",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"nochanges",
|
||||||
|
"Issue no writes, even for journal replay",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"norecovery",
|
||||||
|
"Don’t replay the journal (not recommended)",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"noexcl",
|
||||||
|
"Don’t open devices in exclusive mode",
|
||||||
|
): booleanEnumValue,
|
||||||
|
docvalues.CreateEnumStringWithDoc(
|
||||||
|
"version_upgrade",
|
||||||
|
"Upgrade on disk format to latest version",
|
||||||
|
): booleanEnumValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
var BcacheFSDocumentationEnums = []docvalues.EnumString{}
|
43
server/common/levenshtein.go
Normal file
43
server/common/levenshtein.go
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
package common
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/hbollon/go-edlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Find items that are similar to the given input.
|
||||||
|
// This is used to find typos & suggest the correct item.
|
||||||
|
// Once an item is found that has a Damerau-Levenshtein distance of 1, it is immediately returned.
|
||||||
|
// If not, then the next 2 items of similarity 2, or 3 items of similarity 3 are returned.
|
||||||
|
// If no items with similarity <= 3 are found, then an empty slice is returned.
|
||||||
|
func FindSimilarItems[T ~string](
|
||||||
|
input T,
|
||||||
|
items []T,
|
||||||
|
) []T {
|
||||||
|
itemsPerSimilarity := map[uint8][]T{
|
||||||
|
2: make([]T, 0, 2),
|
||||||
|
3: make([]T, 0, 3),
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, item := range items {
|
||||||
|
similarity := edlib.DamerauLevenshteinDistance(string(item), string(input))
|
||||||
|
|
||||||
|
switch similarity {
|
||||||
|
case 1:
|
||||||
|
return []T{item}
|
||||||
|
case 2:
|
||||||
|
itemsPerSimilarity[2] = append(itemsPerSimilarity[2], item)
|
||||||
|
|
||||||
|
if len(itemsPerSimilarity[2]) >= 2 {
|
||||||
|
return itemsPerSimilarity[2]
|
||||||
|
}
|
||||||
|
case 3:
|
||||||
|
itemsPerSimilarity[3] = append(itemsPerSimilarity[3], item)
|
||||||
|
|
||||||
|
if len(itemsPerSimilarity[3]) >= 3 {
|
||||||
|
return itemsPerSimilarity[3]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return append(itemsPerSimilarity[2], itemsPerSimilarity[3]...)
|
||||||
|
}
|
@ -177,7 +177,7 @@ func (v KeyEnumAssignmentValue) DeprecatedFetchCompletions(line string, cursor u
|
|||||||
)
|
)
|
||||||
|
|
||||||
if found {
|
if found {
|
||||||
relativePosition := max(1, foundPosition) - 1
|
relativePosition := min(foundPosition, len(line)-1)
|
||||||
selectedKey := line[:uint32(relativePosition)]
|
selectedKey := line[:uint32(relativePosition)]
|
||||||
line = line[uint32(relativePosition+len(v.Separator)):]
|
line = line[uint32(relativePosition+len(v.Separator)):]
|
||||||
cursor -= uint32(relativePosition)
|
cursor -= uint32(relativePosition)
|
||||||
|
@ -47,16 +47,16 @@ func (v PathValue) GetTypeDescription() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v PathValue) DeprecatedCheckIsValid(value string) []*InvalidValue {
|
func (v PathValue) DeprecatedCheckIsValid(value string) []*InvalidValue {
|
||||||
|
if v.RequiredType == PathTypeExistenceOptional {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if !utils.DoesPathExist(value) {
|
if !utils.DoesPathExist(value) {
|
||||||
if v.RequiredType == PathTypeExistenceOptional {
|
return []*InvalidValue{{
|
||||||
return nil
|
Err: PathDoesNotExistError{},
|
||||||
} else {
|
Start: 0,
|
||||||
return []*InvalidValue{{
|
End: uint32(len(value)),
|
||||||
Err: PathDoesNotExistError{},
|
}}
|
||||||
Start: 0,
|
|
||||||
End: uint32(len(value)),
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
isValid := false
|
isValid := false
|
||||||
|
@ -141,8 +141,8 @@ func (e FstabEntry) FetchMountOptionsField(includeDefaults bool) docvalues.Depre
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var enums []docvalues.EnumString
|
var enums []docvalues.EnumString = make([]docvalues.EnumString, 0)
|
||||||
var assignable map[docvalues.EnumString]docvalues.DeprecatedValue
|
var assignable map[docvalues.EnumString]docvalues.DeprecatedValue = make(map[docvalues.EnumString]docvalues.DeprecatedValue, 0)
|
||||||
|
|
||||||
if includeDefaults {
|
if includeDefaults {
|
||||||
enums = append(option.Enums, fields.DefaultOptions...)
|
enums = append(option.Enums, fields.DefaultOptions...)
|
||||||
|
@ -6,6 +6,31 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func createMountOptionField(
|
||||||
|
options []docvalues.EnumString,
|
||||||
|
assignOption map[docvalues.EnumString]docvalues.DeprecatedValue,
|
||||||
|
) docvalues.DeprecatedValue {
|
||||||
|
// dynamicOptions := docvalues.MergeKeyEnumAssignmentMaps(defaultAssignOptions, assignOption)
|
||||||
|
|
||||||
|
return docvalues.ArrayValue{
|
||||||
|
Separator: ",",
|
||||||
|
DuplicatesExtractor: &MountOptionsExtractor,
|
||||||
|
SubValue: docvalues.OrValue{
|
||||||
|
Values: []docvalues.DeprecatedValue{
|
||||||
|
docvalues.KeyEnumAssignmentValue{
|
||||||
|
Values: assignOption,
|
||||||
|
ValueIsOptional: false,
|
||||||
|
Separator: "=",
|
||||||
|
},
|
||||||
|
docvalues.EnumValue{
|
||||||
|
EnforceValues: true,
|
||||||
|
Values: options,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var MountOptionsExtractor = func(value string) string {
|
var MountOptionsExtractor = func(value string) string {
|
||||||
separatorIndex := strings.Index(value, "=")
|
separatorIndex := strings.Index(value, "=")
|
||||||
|
|
||||||
@ -339,31 +364,6 @@ Added in version 233.`,
|
|||||||
): docvalues.StringValue{},
|
): docvalues.StringValue{},
|
||||||
}
|
}
|
||||||
|
|
||||||
func createMountOptionField(
|
|
||||||
options []docvalues.EnumString,
|
|
||||||
assignOption map[docvalues.EnumString]docvalues.DeprecatedValue,
|
|
||||||
) docvalues.DeprecatedValue {
|
|
||||||
// dynamicOptions := docvalues.MergeKeyEnumAssignmentMaps(defaultAssignOptions, assignOption)
|
|
||||||
|
|
||||||
return docvalues.ArrayValue{
|
|
||||||
Separator: ",",
|
|
||||||
DuplicatesExtractor: &MountOptionsExtractor,
|
|
||||||
SubValue: docvalues.OrValue{
|
|
||||||
Values: []docvalues.DeprecatedValue{
|
|
||||||
docvalues.KeyEnumAssignmentValue{
|
|
||||||
Values: assignOption,
|
|
||||||
ValueIsOptional: false,
|
|
||||||
Separator: "=",
|
|
||||||
},
|
|
||||||
docvalues.EnumValue{
|
|
||||||
EnforceValues: true,
|
|
||||||
Values: options,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type optionField struct {
|
type optionField struct {
|
||||||
Assignable map[docvalues.EnumString]docvalues.DeprecatedValue
|
Assignable map[docvalues.EnumString]docvalues.DeprecatedValue
|
||||||
Enums []docvalues.EnumString
|
Enums []docvalues.EnumString
|
||||||
@ -376,6 +376,10 @@ var MountOptionsMapField = map[string]optionField{
|
|||||||
Enums: commondocumentation.AdfsDocumentationEnums,
|
Enums: commondocumentation.AdfsDocumentationEnums,
|
||||||
Assignable: commondocumentation.AdfsDocumentationAssignable,
|
Assignable: commondocumentation.AdfsDocumentationAssignable,
|
||||||
},
|
},
|
||||||
|
"apfs": {
|
||||||
|
Enums: commondocumentation.APFSDocumentationEnums,
|
||||||
|
Assignable: commondocumentation.APFSDocumentationAssignable,
|
||||||
|
},
|
||||||
"affs": {
|
"affs": {
|
||||||
Enums: commondocumentation.AffsDocumentationEnums,
|
Enums: commondocumentation.AffsDocumentationEnums,
|
||||||
Assignable: commondocumentation.AffsDocumentationAssignable,
|
Assignable: commondocumentation.AffsDocumentationAssignable,
|
||||||
@ -478,4 +482,8 @@ var MountOptionsMapField = map[string]optionField{
|
|||||||
Enums: commondocumentation.VfatDocumentationEnums,
|
Enums: commondocumentation.VfatDocumentationEnums,
|
||||||
Assignable: commondocumentation.VfatDocumentationAssignable,
|
Assignable: commondocumentation.VfatDocumentationAssignable,
|
||||||
},
|
},
|
||||||
|
"bcachefs": {
|
||||||
|
Enums: commondocumentation.BcacheFSDocumentationEnums,
|
||||||
|
Assignable: commondocumentation.BcacheFSDocumentationAssignable,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
@ -44,39 +44,29 @@ func GetCompletion(
|
|||||||
fileSystemType := entry.Fields.FilesystemType.Value.Value
|
fileSystemType := entry.Fields.FilesystemType.Value.Value
|
||||||
completions := make([]protocol.CompletionItem, 0, 50)
|
completions := make([]protocol.CompletionItem, 0, 50)
|
||||||
|
|
||||||
for _, completion := range fields.DefaultMountOptionsField.DeprecatedFetchCompletions(line, cursor) {
|
optionsValue := entry.FetchMountOptionsField(false)
|
||||||
var documentation string
|
|
||||||
|
|
||||||
switch completion.Documentation.(type) {
|
if optionsValue != nil {
|
||||||
case string:
|
for _, completion := range optionsValue.DeprecatedFetchCompletions(line, cursor) {
|
||||||
documentation = completion.Documentation.(string)
|
var documentation string
|
||||||
case *string:
|
|
||||||
documentation = *completion.Documentation.(*string)
|
|
||||||
}
|
|
||||||
|
|
||||||
completion.Documentation = protocol.MarkupContent{
|
switch completion.Documentation.(type) {
|
||||||
Kind: protocol.MarkupKindMarkdown,
|
case string:
|
||||||
Value: documentation + "\n\n" + "From: _Default Mount Options_",
|
documentation = completion.Documentation.(string)
|
||||||
|
case *string:
|
||||||
|
documentation = *completion.Documentation.(*string)
|
||||||
|
}
|
||||||
|
|
||||||
|
completion.Documentation = protocol.MarkupContent{
|
||||||
|
Kind: protocol.MarkupKindMarkdown,
|
||||||
|
Value: documentation + "\n\n" + fmt.Sprintf("From: _%s_", fileSystemType),
|
||||||
|
}
|
||||||
|
completions = append(completions, completion)
|
||||||
}
|
}
|
||||||
completions = append(completions, completion)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, completion := range entry.FetchMountOptionsField(false).DeprecatedFetchCompletions(line, cursor) {
|
// Add defaults
|
||||||
var documentation string
|
completions = append(completions, fields.DefaultMountOptionsField.DeprecatedFetchCompletions(line, cursor)...)
|
||||||
|
|
||||||
switch completion.Documentation.(type) {
|
|
||||||
case string:
|
|
||||||
documentation = completion.Documentation.(string)
|
|
||||||
case *string:
|
|
||||||
documentation = *completion.Documentation.(*string)
|
|
||||||
}
|
|
||||||
|
|
||||||
completion.Documentation = protocol.MarkupContent{
|
|
||||||
Kind: protocol.MarkupKindMarkdown,
|
|
||||||
Value: documentation + "\n\n" + fmt.Sprintf("From: _%s_", fileSystemType),
|
|
||||||
}
|
|
||||||
completions = append(completions, completion)
|
|
||||||
}
|
|
||||||
|
|
||||||
return completions, nil
|
return completions, nil
|
||||||
case ast.FstabFieldFreq:
|
case ast.FstabFieldFreq:
|
||||||
|
@ -5,9 +5,9 @@ import (
|
|||||||
sshconfig "config-lsp/handlers/ssh_config"
|
sshconfig "config-lsp/handlers/ssh_config"
|
||||||
"config-lsp/handlers/ssh_config/diagnostics"
|
"config-lsp/handlers/ssh_config/diagnostics"
|
||||||
"config-lsp/handlers/ssh_config/fields"
|
"config-lsp/handlers/ssh_config/fields"
|
||||||
|
"config-lsp/utils"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/hbollon/go-edlib"
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -24,7 +24,8 @@ func getKeywordTypoFixes(
|
|||||||
if typoOption, found := d.Indexes.UnknownOptions[line]; found {
|
if typoOption, found := d.Indexes.UnknownOptions[line]; found {
|
||||||
name := typoOption.Option.Key.Value.Value
|
name := typoOption.Option.Key.Value.Value
|
||||||
|
|
||||||
suggestedOptions := findSimilarOptions(name)
|
opts := utils.KeysOfMap(fields.Options)
|
||||||
|
suggestedOptions := common.FindSimilarItems(fields.CreateNormalizedName(name), opts)
|
||||||
|
|
||||||
actions := make([]protocol.CodeAction, 0, len(suggestedOptions))
|
actions := make([]protocol.CodeAction, 0, len(suggestedOptions))
|
||||||
|
|
||||||
@ -61,43 +62,3 @@ func getKeywordTypoFixes(
|
|||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find options that are similar to the given option name.
|
|
||||||
// This is used to find typos & suggest the correct option name.
|
|
||||||
// Once an option is found that has a Damerau-Levenshtein distance of 1, it is immediately returned.
|
|
||||||
// If not, then the next 2 options of similarity 2, or 3 options of similarity 3 are returned.
|
|
||||||
// If no options with similarity <= 3 are found, then an empty slice is returned.
|
|
||||||
func findSimilarOptions(
|
|
||||||
optionName string,
|
|
||||||
) []fields.NormalizedOptionName {
|
|
||||||
normalizedOptionName := string(fields.CreateNormalizedName(optionName))
|
|
||||||
|
|
||||||
optionsPerSimilarity := map[uint8][]fields.NormalizedOptionName{
|
|
||||||
2: make([]fields.NormalizedOptionName, 0, 2),
|
|
||||||
3: make([]fields.NormalizedOptionName, 0, 3),
|
|
||||||
}
|
|
||||||
|
|
||||||
for name := range fields.Options {
|
|
||||||
normalizedName := string(name)
|
|
||||||
similarity := edlib.DamerauLevenshteinDistance(normalizedName, normalizedOptionName)
|
|
||||||
|
|
||||||
switch similarity {
|
|
||||||
case 1:
|
|
||||||
return []fields.NormalizedOptionName{name}
|
|
||||||
case 2:
|
|
||||||
optionsPerSimilarity[2] = append(optionsPerSimilarity[2], name)
|
|
||||||
|
|
||||||
if len(optionsPerSimilarity[2]) >= 2 {
|
|
||||||
return optionsPerSimilarity[2]
|
|
||||||
}
|
|
||||||
case 3:
|
|
||||||
optionsPerSimilarity[3] = append(optionsPerSimilarity[3], name)
|
|
||||||
|
|
||||||
if len(optionsPerSimilarity[3]) >= 3 {
|
|
||||||
return optionsPerSimilarity[3]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return append(optionsPerSimilarity[2], optionsPerSimilarity[3]...)
|
|
||||||
}
|
|
||||||
|
@ -20,7 +20,6 @@ func TextDocumentDidChange(
|
|||||||
document := sshconfig.DocumentParserMap[params.TextDocument.URI]
|
document := sshconfig.DocumentParserMap[params.TextDocument.URI]
|
||||||
document.Config.Clear()
|
document.Config.Clear()
|
||||||
|
|
||||||
println("reparsing everything")
|
|
||||||
diagnostics := make([]protocol.Diagnostic, 0)
|
diagnostics := make([]protocol.Diagnostic, 0)
|
||||||
errors := document.Config.Parse(content)
|
errors := document.Config.Parse(content)
|
||||||
|
|
||||||
|
@ -35,6 +35,12 @@ func Analyze(
|
|||||||
|
|
||||||
d.Indexes = i
|
d.Indexes = i
|
||||||
|
|
||||||
|
analyzeProperties(ctx)
|
||||||
|
|
||||||
|
if len(ctx.diagnostics) > 0 {
|
||||||
|
return ctx.diagnostics
|
||||||
|
}
|
||||||
|
|
||||||
analyzeInterfaceSection(ctx)
|
analyzeInterfaceSection(ctx)
|
||||||
analyzeDNSPropertyContainsFallback(ctx)
|
analyzeDNSPropertyContainsFallback(ctx)
|
||||||
analyzeKeepAlivePropertyIsSet(ctx)
|
analyzeKeepAlivePropertyIsSet(ctx)
|
||||||
|
89
server/handlers/wireguard/analyzer/properties.go
Normal file
89
server/handlers/wireguard/analyzer/properties.go
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
package analyzer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
docvalues "config-lsp/doc-values"
|
||||||
|
"config-lsp/handlers/wireguard/ast"
|
||||||
|
"config-lsp/handlers/wireguard/diagnostics"
|
||||||
|
"config-lsp/handlers/wireguard/fields"
|
||||||
|
"config-lsp/handlers/wireguard/indexes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func analyzeProperties(
|
||||||
|
ctx *analyzerContext,
|
||||||
|
) {
|
||||||
|
for _, section := range ctx.document.Config.Sections {
|
||||||
|
normalizedHeaderName := fields.CreateNormalizedName(section.Header.Name)
|
||||||
|
|
||||||
|
// Whether to check if the property is allowed in the section
|
||||||
|
checkAllowedProperty := true
|
||||||
|
existingProperties := make(map[fields.NormalizedName]*ast.WGProperty)
|
||||||
|
|
||||||
|
it := section.Properties.Iterator()
|
||||||
|
for it.Next() {
|
||||||
|
property := it.Value().(*ast.WGProperty)
|
||||||
|
normalizedPropertyName := fields.CreateNormalizedName(property.Key.Name)
|
||||||
|
|
||||||
|
if property.Key.Name == "" {
|
||||||
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
|
Message: "This property is missing a name",
|
||||||
|
Range: property.Key.ToLSPRange(),
|
||||||
|
Severity: &common.SeverityError,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if property.Value == nil || property.Value.Value == "" {
|
||||||
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
|
Message: "This property is missing a value",
|
||||||
|
Range: property.ToLSPRange(),
|
||||||
|
Severity: &common.SeverityError,
|
||||||
|
})
|
||||||
|
checkAllowedProperty = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkAllowedProperty {
|
||||||
|
availableOptions := fields.OptionsHeaderMap[normalizedHeaderName]
|
||||||
|
|
||||||
|
// Duplicate check
|
||||||
|
if existingProperty, found := existingProperties[normalizedPropertyName]; found {
|
||||||
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
|
Message: fmt.Sprintf("Property '%s' has already been defined on line %d", property.Key.Name, existingProperty.Start.Line+1),
|
||||||
|
Severity: &common.SeverityError,
|
||||||
|
Range: existingProperty.ToLSPRange(),
|
||||||
|
})
|
||||||
|
// Check if value is valid
|
||||||
|
} else if option, found := availableOptions[normalizedPropertyName]; found {
|
||||||
|
invalidValues := option.DeprecatedCheckIsValid(property.Value.Value)
|
||||||
|
|
||||||
|
for _, invalidValue := range invalidValues {
|
||||||
|
err := docvalues.LSPErrorFromInvalidValue(property.Start.Line, *invalidValue).ShiftCharacter(property.Value.Start.Character)
|
||||||
|
|
||||||
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
|
Range: err.Range.ToLSPRange(),
|
||||||
|
Message: err.Err.Error(),
|
||||||
|
Severity: &common.SeverityError,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Unknown property
|
||||||
|
} else {
|
||||||
|
ctx.diagnostics = append(ctx.diagnostics,
|
||||||
|
diagnostics.GenerateUnknownOption(
|
||||||
|
property.ToLSPRange(),
|
||||||
|
property.Key.Name,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
ctx.document.Indexes.UnknownProperties[property.Key.Start.Line] = indexes.WGIndexPropertyInfo{
|
||||||
|
Section: section,
|
||||||
|
Property: property,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
existingProperties[normalizedPropertyName] = property
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -22,7 +22,7 @@ func analyzeDNSPropertyContainsFallback(
|
|||||||
|
|
||||||
interfaceSection := sections[0]
|
interfaceSection := sections[0]
|
||||||
|
|
||||||
property := interfaceSection.FindFirstPropertyByName("DNS")
|
_, property := interfaceSection.FindFirstPropertyByName("DNS")
|
||||||
|
|
||||||
if property == nil {
|
if property == nil {
|
||||||
return
|
return
|
||||||
@ -44,7 +44,10 @@ func analyzeKeepAlivePropertyIsSet(
|
|||||||
) {
|
) {
|
||||||
for _, section := range ctx.document.Indexes.SectionsByName["Peer"] {
|
for _, section := range ctx.document.Indexes.SectionsByName["Peer"] {
|
||||||
// If an endpoint is set, then we should only check for the keepalive property
|
// If an endpoint is set, then we should only check for the keepalive property
|
||||||
if section.FindFirstPropertyByName("Endpoint") != nil && section.FindFirstPropertyByName("PersistentKeepalive") == nil {
|
_, endpoint := section.FindFirstPropertyByName("Endpoint")
|
||||||
|
_, persistentKeepAlive := section.FindFirstPropertyByName("PersistentKeepalive")
|
||||||
|
|
||||||
|
if endpoint != nil && persistentKeepAlive == nil {
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
Message: "PersistentKeepalive is not set. It is recommended to set this property, as it helps to maintain the connection when users are behind NAT",
|
Message: "PersistentKeepalive is not set. It is recommended to set this property, as it helps to maintain the connection when users are behind NAT",
|
||||||
Severity: &common.SeverityHint,
|
Severity: &common.SeverityHint,
|
||||||
@ -58,11 +61,11 @@ func analyzeSymmetricPropertiesSet(
|
|||||||
ctx *analyzerContext,
|
ctx *analyzerContext,
|
||||||
) {
|
) {
|
||||||
for _, section := range ctx.document.Indexes.SectionsByName["Interface"] {
|
for _, section := range ctx.document.Indexes.SectionsByName["Interface"] {
|
||||||
preUpProperty := section.FindFirstPropertyByName("PreUp")
|
_, preUpProperty := section.FindFirstPropertyByName("PreUp")
|
||||||
preDownProperty := section.FindFirstPropertyByName("PreDown")
|
_, preDownProperty := section.FindFirstPropertyByName("PreDown")
|
||||||
|
|
||||||
postUpProperty := section.FindFirstPropertyByName("PostUp")
|
_, postUpProperty := section.FindFirstPropertyByName("PostUp")
|
||||||
postDownProperty := section.FindFirstPropertyByName("PostDown")
|
_, postDownProperty := section.FindFirstPropertyByName("PostDown")
|
||||||
|
|
||||||
if preUpProperty != nil && preDownProperty == nil {
|
if preUpProperty != nil && preDownProperty == nil {
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
@ -94,7 +97,7 @@ func analyzeSymmetricPropertiesSet(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type key int
|
type key uint8
|
||||||
|
|
||||||
const (
|
const (
|
||||||
lineKey key = iota
|
lineKey key = iota
|
||||||
@ -109,7 +112,7 @@ func analyzeDuplicateAllowedIPs(
|
|||||||
ipHostSet := utils.CreateIPv4HostSet()
|
ipHostSet := utils.CreateIPv4HostSet()
|
||||||
|
|
||||||
for _, section := range ctx.document.Indexes.SectionsByName["Peer"] {
|
for _, section := range ctx.document.Indexes.SectionsByName["Peer"] {
|
||||||
property := section.FindFirstPropertyByName("AllowedIPs")
|
_, property := section.FindFirstPropertyByName("AllowedIPs")
|
||||||
|
|
||||||
if property == nil {
|
if property == nil {
|
||||||
continue
|
continue
|
||||||
@ -123,12 +126,15 @@ func analyzeDuplicateAllowedIPs(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ipContext, _ := ipHostSet.ContainsIP(ipAddress); ipContext != nil {
|
if ipContext, _ := ipHostSet.ContainsIP(ipAddress); ipContext != nil {
|
||||||
definedLine := (*ipContext).Value(lineKey).(uint32)
|
ctxx := *ipContext
|
||||||
|
definedLineRaw := ctxx.Value(lineKey)
|
||||||
|
|
||||||
|
definedLine := definedLineRaw.(uint32)
|
||||||
|
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
Message: fmt.Sprintf("This IP range is already covered on line %d", definedLine+1),
|
Message: fmt.Sprintf("This IP range is already covered on line %d", definedLine+1),
|
||||||
Severity: &common.SeverityError,
|
Severity: &common.SeverityError,
|
||||||
Range: property.ToLSPRange(),
|
Range: property.Value.ToLSPRange(),
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
ipContext := context.WithValue(
|
ipContext := context.WithValue(
|
||||||
|
@ -2,8 +2,6 @@ package analyzer
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/common"
|
"config-lsp/common"
|
||||||
docvalues "config-lsp/doc-values"
|
|
||||||
"config-lsp/handlers/wireguard/ast"
|
|
||||||
"config-lsp/handlers/wireguard/fields"
|
"config-lsp/handlers/wireguard/fields"
|
||||||
"config-lsp/utils"
|
"config-lsp/utils"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -14,8 +12,6 @@ import (
|
|||||||
func analyzeStructureIsValid(ctx *analyzerContext) {
|
func analyzeStructureIsValid(ctx *analyzerContext) {
|
||||||
for _, section := range ctx.document.Config.Sections {
|
for _, section := range ctx.document.Config.Sections {
|
||||||
normalizedHeaderName := fields.CreateNormalizedName(section.Header.Name)
|
normalizedHeaderName := fields.CreateNormalizedName(section.Header.Name)
|
||||||
// Whether to check if the property is allowed in the section
|
|
||||||
checkAllowedProperty := true
|
|
||||||
|
|
||||||
if section.Header.Name == "" {
|
if section.Header.Name == "" {
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
||||||
@ -29,8 +25,6 @@ func analyzeStructureIsValid(ctx *analyzerContext) {
|
|||||||
Range: section.Header.ToLSPRange(),
|
Range: section.Header.ToLSPRange(),
|
||||||
Severity: &common.SeverityError,
|
Severity: &common.SeverityError,
|
||||||
})
|
})
|
||||||
// Do not check as the section is unknown
|
|
||||||
checkAllowedProperty = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if section.Properties.Size() == 0 {
|
if section.Properties.Size() == 0 {
|
||||||
@ -42,66 +36,6 @@ func analyzeStructureIsValid(ctx *analyzerContext) {
|
|||||||
protocol.DiagnosticTagUnnecessary,
|
protocol.DiagnosticTagUnnecessary,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} else {
|
|
||||||
existingProperties := make(map[fields.NormalizedName]*ast.WGProperty)
|
|
||||||
|
|
||||||
it := section.Properties.Iterator()
|
|
||||||
for it.Next() {
|
|
||||||
property := it.Value().(*ast.WGProperty)
|
|
||||||
normalizedPropertyName := fields.CreateNormalizedName(property.Key.Name)
|
|
||||||
|
|
||||||
if property.Key.Name == "" {
|
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
|
||||||
Message: "This property is missing a name",
|
|
||||||
Range: property.Key.ToLSPRange(),
|
|
||||||
Severity: &common.SeverityError,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if property.Value == nil || property.Value.Value == "" {
|
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
|
||||||
Message: "This property is missing a value",
|
|
||||||
Range: property.ToLSPRange(),
|
|
||||||
Severity: &common.SeverityError,
|
|
||||||
})
|
|
||||||
checkAllowedProperty = false
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkAllowedProperty {
|
|
||||||
availableOptions := fields.OptionsHeaderMap[normalizedHeaderName]
|
|
||||||
|
|
||||||
// Duplicate check
|
|
||||||
if existingProperty, found := existingProperties[normalizedPropertyName]; found {
|
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
|
||||||
Message: fmt.Sprintf("Property '%s' has already been defined on line %d", property.Key.Name, existingProperty.Start.Line+1),
|
|
||||||
Severity: &common.SeverityError,
|
|
||||||
Range: existingProperty.ToLSPRange(),
|
|
||||||
})
|
|
||||||
// Check if value is valid
|
|
||||||
} else if option, found := availableOptions[normalizedPropertyName]; found {
|
|
||||||
invalidValues := option.DeprecatedCheckIsValid(property.Value.Value)
|
|
||||||
|
|
||||||
for _, invalidValue := range invalidValues {
|
|
||||||
err := docvalues.LSPErrorFromInvalidValue(property.Start.Line, *invalidValue).ShiftCharacter(property.Value.Start.Character)
|
|
||||||
|
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
|
||||||
Range: err.Range.ToLSPRange(),
|
|
||||||
Message: err.Err.Error(),
|
|
||||||
Severity: &common.SeverityError,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
// Unknown property
|
|
||||||
} else {
|
|
||||||
ctx.diagnostics = append(ctx.diagnostics, protocol.Diagnostic{
|
|
||||||
Message: fmt.Sprintf("Unknown property '%s'", property.Key.Name),
|
|
||||||
Range: property.Key.ToLSPRange(),
|
|
||||||
Severity: &common.SeverityError,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
existingProperties[normalizedPropertyName] = property
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ type WGHeader struct {
|
|||||||
type WGSection struct {
|
type WGSection struct {
|
||||||
common.LocationRange
|
common.LocationRange
|
||||||
Header WGHeader
|
Header WGHeader
|
||||||
// [uint32]WGProperty: line number -> WGProperty
|
// [uint32]*WGProperty: line number -> *WGProperty
|
||||||
Properties *treemap.Map
|
Properties *treemap.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,11 +10,11 @@ func (c *WGConfig) FindSectionByLine(line uint32) *WGSection {
|
|||||||
line,
|
line,
|
||||||
func(current *WGSection, target uint32) int {
|
func(current *WGSection, target uint32) int {
|
||||||
if target < current.Start.Line {
|
if target < current.Start.Line {
|
||||||
return -1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
if target > current.End.Line {
|
if target > current.End.Line {
|
||||||
return 1
|
return -1
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@ -42,28 +42,17 @@ func (c *WGConfig) FindPropertyByLine(line uint32) *WGProperty {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WGSection) FindFirstPropertyByName(name string) *WGProperty {
|
func (s *WGSection) FindFirstPropertyByName(name string) (uint32, *WGProperty) {
|
||||||
it := s.Properties.Iterator()
|
it := s.Properties.Iterator()
|
||||||
for it.Next() {
|
for it.Next() {
|
||||||
|
line := it.Key().(uint32)
|
||||||
property := it.Value().(*WGProperty)
|
property := it.Value().(*WGProperty)
|
||||||
if property.Key.Name == name {
|
if property.Key.Name == name {
|
||||||
return property
|
return line, property
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return 0, nil
|
||||||
}
|
|
||||||
|
|
||||||
func (s *WGSection) FindPropertyByName(name string) *WGProperty {
|
|
||||||
it := s.Properties.Iterator()
|
|
||||||
for it.Next() {
|
|
||||||
property := it.Value().(*WGProperty)
|
|
||||||
if property.Key.Name == name {
|
|
||||||
return property
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *WGSection) GetLastProperty() *WGProperty {
|
func (s *WGSection) GetLastProperty() *WGProperty {
|
||||||
|
19
server/handlers/wireguard/diagnostics/diagnostics.go
Normal file
19
server/handlers/wireguard/diagnostics/diagnostics.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package diagnostics
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GenerateUnknownOption(
|
||||||
|
diagnosticRange protocol.Range,
|
||||||
|
propertyName string,
|
||||||
|
) protocol.Diagnostic {
|
||||||
|
return protocol.Diagnostic{
|
||||||
|
Range: diagnosticRange,
|
||||||
|
Message: fmt.Sprintf("Unknown property: %s", propertyName),
|
||||||
|
Severity: &common.SeverityError,
|
||||||
|
}
|
||||||
|
}
|
@ -1,9 +1,7 @@
|
|||||||
package handlers
|
package handlers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"config-lsp/handlers/wireguard"
|
|
||||||
"config-lsp/handlers/wireguard/ast"
|
"config-lsp/handlers/wireguard/ast"
|
||||||
wgcommands "config-lsp/handlers/wireguard/commands"
|
|
||||||
|
|
||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
@ -13,6 +11,7 @@ type CodeActionName string
|
|||||||
const (
|
const (
|
||||||
CodeActionGeneratePrivateKey CodeActionName = "generatePrivateKey"
|
CodeActionGeneratePrivateKey CodeActionName = "generatePrivateKey"
|
||||||
CodeActionGeneratePresharedKey CodeActionName = "generatePresharedKey"
|
CodeActionGeneratePresharedKey CodeActionName = "generatePresharedKey"
|
||||||
|
CodeActionCreatePeer CodeActionName = "createPeer"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CodeAction interface {
|
type CodeAction interface {
|
||||||
@ -20,105 +19,3 @@ type CodeAction interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type CodeActionArgs interface{}
|
type CodeActionArgs interface{}
|
||||||
|
|
||||||
type CodeActionGeneratePrivateKeyArgs struct {
|
|
||||||
URI protocol.DocumentUri
|
|
||||||
Line uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func CodeActionGeneratePrivateKeyArgsFromArguments(arguments map[string]any) CodeActionGeneratePrivateKeyArgs {
|
|
||||||
return CodeActionGeneratePrivateKeyArgs{
|
|
||||||
URI: arguments["URI"].(protocol.DocumentUri),
|
|
||||||
Line: uint32(arguments["Line"].(float64)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args CodeActionGeneratePrivateKeyArgs) RunCommand(d *wireguard.WGDocument) (*protocol.ApplyWorkspaceEditParams, error) {
|
|
||||||
privateKey, err := wgcommands.CreateNewPrivateKey()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return &protocol.ApplyWorkspaceEditParams{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
section := d.Config.FindSectionByLine(args.Line)
|
|
||||||
property := d.Config.FindPropertyByLine(args.Line)
|
|
||||||
|
|
||||||
if section == nil || property == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
label := "Generate Private Key"
|
|
||||||
return &protocol.ApplyWorkspaceEditParams{
|
|
||||||
Label: &label,
|
|
||||||
Edit: protocol.WorkspaceEdit{
|
|
||||||
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
|
||||||
args.URI: {
|
|
||||||
{
|
|
||||||
NewText: " " + privateKey,
|
|
||||||
Range: protocol.Range{
|
|
||||||
Start: protocol.Position{
|
|
||||||
Line: property.End.Line,
|
|
||||||
Character: property.End.Character,
|
|
||||||
},
|
|
||||||
End: protocol.Position{
|
|
||||||
Line: property.End.Line,
|
|
||||||
Character: property.End.Character,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type CodeActionGeneratePresharedKeyArgs struct {
|
|
||||||
URI protocol.DocumentUri
|
|
||||||
Line uint32
|
|
||||||
}
|
|
||||||
|
|
||||||
func CodeActionGeneratePresharedKeyArgsFromArguments(arguments map[string]any) CodeActionGeneratePresharedKeyArgs {
|
|
||||||
return CodeActionGeneratePresharedKeyArgs{
|
|
||||||
URI: arguments["URI"].(protocol.DocumentUri),
|
|
||||||
Line: uint32(arguments["Line"].(float64)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (args CodeActionGeneratePresharedKeyArgs) RunCommand(d *wireguard.WGDocument) (*protocol.ApplyWorkspaceEditParams, error) {
|
|
||||||
presharedKey, err := wgcommands.CreatePresharedKey()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return &protocol.ApplyWorkspaceEditParams{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
section := d.Config.FindSectionByLine(args.Line)
|
|
||||||
property := d.Config.FindPropertyByLine(args.Line)
|
|
||||||
|
|
||||||
if section == nil || property == nil {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
label := "Generate Preshared Key"
|
|
||||||
return &protocol.ApplyWorkspaceEditParams{
|
|
||||||
Label: &label,
|
|
||||||
Edit: protocol.WorkspaceEdit{
|
|
||||||
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
|
||||||
args.URI: {
|
|
||||||
{
|
|
||||||
NewText: " " + presharedKey,
|
|
||||||
Range: protocol.Range{
|
|
||||||
Start: protocol.Position{
|
|
||||||
Line: property.End.Line,
|
|
||||||
Character: property.End.Character,
|
|
||||||
},
|
|
||||||
End: protocol.Position{
|
|
||||||
Line: property.End.Line,
|
|
||||||
Character: property.End.Character,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
191
server/handlers/wireguard/handlers/code-actions_create-peer.go
Normal file
191
server/handlers/wireguard/handlers/code-actions_create-peer.go
Normal file
@ -0,0 +1,191 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"config-lsp/handlers/wireguard"
|
||||||
|
"config-lsp/handlers/wireguard/ast"
|
||||||
|
wgcommands "config-lsp/handlers/wireguard/commands"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeActionCreatePeerArgs struct {
|
||||||
|
URI protocol.DocumentUri
|
||||||
|
Line uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func CodeActionCreatePeerArgsFromArguments(arguments map[string]any) CodeActionCreatePeerArgs {
|
||||||
|
return CodeActionCreatePeerArgs{
|
||||||
|
URI: arguments["URI"].(protocol.DocumentUri),
|
||||||
|
Line: uint32(arguments["Line"].(float64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args CodeActionCreatePeerArgs) RunCommand(d *wireguard.WGDocument) (*protocol.ApplyWorkspaceEditParams, error) {
|
||||||
|
interfaceSection := d.Indexes.SectionsByName["Interface"][0]
|
||||||
|
section := d.Config.FindSectionByLine(args.Line)
|
||||||
|
|
||||||
|
label := fmt.Sprintf("Add Peer based on peer on line %d", args.Line)
|
||||||
|
|
||||||
|
newSection := section
|
||||||
|
|
||||||
|
// IP Address
|
||||||
|
ipAddressLine, ipAddress := newSection.FindFirstPropertyByName("AllowedIPs")
|
||||||
|
_, address := interfaceSection.FindFirstPropertyByName("Address")
|
||||||
|
if ipAddress != nil && address != nil {
|
||||||
|
_, network, err := net.ParseCIDR(address.Value.Value)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
newIPAddress := createNewIP(*network, ipAddress.Value.Value)
|
||||||
|
|
||||||
|
valueEnd := common.Location{
|
||||||
|
Line: ipAddress.End.Line,
|
||||||
|
Character: ipAddress.Value.Start.Character + uint32(len(newIPAddress)) + 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
newSection.Properties.Put(
|
||||||
|
ipAddressLine,
|
||||||
|
&ast.WGProperty{
|
||||||
|
LocationRange: common.LocationRange{
|
||||||
|
Start: ipAddress.Start,
|
||||||
|
End: valueEnd,
|
||||||
|
},
|
||||||
|
Key: ipAddress.Key,
|
||||||
|
RawValue: newIPAddress,
|
||||||
|
Separator: address.Separator,
|
||||||
|
Value: &ast.WGPropertyValue{
|
||||||
|
LocationRange: common.LocationRange{
|
||||||
|
Start: ipAddress.Value.Start,
|
||||||
|
End: valueEnd,
|
||||||
|
},
|
||||||
|
Value: newIPAddress,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Preshared Key
|
||||||
|
presharedKeyLine, presharedKey := newSection.FindFirstPropertyByName("PresharedKey")
|
||||||
|
|
||||||
|
if presharedKey != nil {
|
||||||
|
var newKey string
|
||||||
|
|
||||||
|
if wgcommands.AreWireguardToolsAvailable() {
|
||||||
|
createdKey, err := wgcommands.CreatePresharedKey()
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
newKey = createdKey
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
newKey = "[preshared key]"
|
||||||
|
}
|
||||||
|
|
||||||
|
valueEnd := common.Location{
|
||||||
|
Line: presharedKey.End.Line,
|
||||||
|
Character: presharedKey.Value.Start.Character + uint32(len(newKey)) + 1,
|
||||||
|
}
|
||||||
|
newSection.Properties.Put(
|
||||||
|
presharedKeyLine,
|
||||||
|
&ast.WGProperty{
|
||||||
|
LocationRange: common.LocationRange{
|
||||||
|
Start: presharedKey.Start,
|
||||||
|
End: valueEnd,
|
||||||
|
},
|
||||||
|
Key: presharedKey.Key,
|
||||||
|
RawValue: newKey,
|
||||||
|
Separator: presharedKey.Separator,
|
||||||
|
Value: &ast.WGPropertyValue{
|
||||||
|
LocationRange: common.LocationRange{
|
||||||
|
Start: presharedKey.Value.Start,
|
||||||
|
End: valueEnd,
|
||||||
|
},
|
||||||
|
Value: newKey,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
lastProperty := newSection.GetLastProperty()
|
||||||
|
println("last line")
|
||||||
|
println(lastProperty.End.Line)
|
||||||
|
println(fmt.Sprintf("~~~%s~~~", writeSection(*newSection)))
|
||||||
|
newText := writeSection(*newSection)
|
||||||
|
return &protocol.ApplyWorkspaceEditParams{
|
||||||
|
Label: &label,
|
||||||
|
Edit: protocol.WorkspaceEdit{
|
||||||
|
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||||
|
args.URI: {
|
||||||
|
{
|
||||||
|
Range: protocol.Range{
|
||||||
|
Start: protocol.Position{
|
||||||
|
Line: lastProperty.End.Line,
|
||||||
|
Character: lastProperty.End.Character,
|
||||||
|
},
|
||||||
|
End: protocol.Position{
|
||||||
|
Line: lastProperty.End.Line,
|
||||||
|
Character: lastProperty.End.Character,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NewText: newText,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func writeSection(section ast.WGSection) string {
|
||||||
|
text := "\n\n"
|
||||||
|
|
||||||
|
text += fmt.Sprintf("[%s]\n", section.Header.Name)
|
||||||
|
|
||||||
|
it := section.Properties.Iterator()
|
||||||
|
for it.Next() {
|
||||||
|
property := it.Value().(*ast.WGProperty)
|
||||||
|
text += fmt.Sprintf("%s = %s\n", property.Key.Name, property.Value.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try incrementing the IP address
|
||||||
|
func createNewIP(
|
||||||
|
network net.IPNet,
|
||||||
|
rawIP string,
|
||||||
|
) string {
|
||||||
|
parsedIP, _, err := net.ParseCIDR(rawIP)
|
||||||
|
parsedIP = parsedIP.To4()
|
||||||
|
|
||||||
|
if parsedIP == nil {
|
||||||
|
// IPv6 is not supported
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
lastAddress := uint32(network.IP[0])<<24 | uint32(network.IP[1])<<16 | uint32(network.IP[2])<<8 | uint32(network.IP[3])
|
||||||
|
|
||||||
|
networkMask, _ := network.Mask.Size()
|
||||||
|
for index := range 32 - networkMask {
|
||||||
|
lastAddress |= 1 << index
|
||||||
|
}
|
||||||
|
|
||||||
|
newIP := uint32(parsedIP[0])<<24 | uint32(parsedIP[1])<<16 | uint32(parsedIP[2])<<8 | uint32(parsedIP[3])
|
||||||
|
newIP += 1
|
||||||
|
|
||||||
|
if newIP >= lastAddress || newIP == 0 {
|
||||||
|
// The IP is the last one, which can't be used
|
||||||
|
// or even worse, it did a whole overflow
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// Here, we successfully incremented the IP correctly
|
||||||
|
|
||||||
|
// Let's return the formatted IP now.
|
||||||
|
return fmt.Sprintf("%d.%d.%d.%d/32", newIP>>24, newIP>>16&0xFF, newIP>>8&0xFF, newIP&0xFF)
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestCreateNewIPSimple24Mask(t *testing.T) {
|
||||||
|
_, network, _ := net.ParseCIDR("10.0.0.0/24")
|
||||||
|
newIP := createNewIP(*network, "10.0.0.1/32")
|
||||||
|
|
||||||
|
if newIP != "10.0.0.2/32" {
|
||||||
|
t.Errorf("Expected 10.0.0.2/32, got %s", newIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateNewIPDoesNotWorkWithLast24Mask(t *testing.T) {
|
||||||
|
_, network, _ := net.ParseCIDR("10.0.0.0/24")
|
||||||
|
newIP := createNewIP(*network, "10.0.0.254/32")
|
||||||
|
|
||||||
|
if newIP != "" {
|
||||||
|
t.Errorf("Expected empty string, got %s", newIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateNewIPDoesNotWorkWithLast24Mask2(t *testing.T) {
|
||||||
|
_, network, _ := net.ParseCIDR("10.0.0.0/24")
|
||||||
|
newIP := createNewIP(*network, "10.0.0.255/32")
|
||||||
|
|
||||||
|
if newIP != "" {
|
||||||
|
t.Errorf("Expected empty string, got %s", newIP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCreateNewIPComplex20Mask(t *testing.T) {
|
||||||
|
_, network, _ := net.ParseCIDR("10.0.0.0/20")
|
||||||
|
newIP := createNewIP(*network, "10.0.0.255/32")
|
||||||
|
|
||||||
|
if newIP != "10.0.1.0/32" {
|
||||||
|
t.Errorf("Expected 10.0.1.0/32, got %s", newIP)
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/handlers/wireguard"
|
||||||
|
wgcommands "config-lsp/handlers/wireguard/commands"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeActionGeneratePrivateKeyArgs struct {
|
||||||
|
URI protocol.DocumentUri
|
||||||
|
Line uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func CodeActionGeneratePrivateKeyArgsFromArguments(arguments map[string]any) CodeActionGeneratePrivateKeyArgs {
|
||||||
|
return CodeActionGeneratePrivateKeyArgs{
|
||||||
|
URI: arguments["URI"].(protocol.DocumentUri),
|
||||||
|
Line: uint32(arguments["Line"].(float64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args CodeActionGeneratePrivateKeyArgs) RunCommand(d *wireguard.WGDocument) (*protocol.ApplyWorkspaceEditParams, error) {
|
||||||
|
privateKey, err := wgcommands.CreateNewPrivateKey()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return &protocol.ApplyWorkspaceEditParams{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
section := d.Config.FindSectionByLine(args.Line)
|
||||||
|
property := d.Config.FindPropertyByLine(args.Line)
|
||||||
|
|
||||||
|
if section == nil || property == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
label := "Generate Private Key"
|
||||||
|
return &protocol.ApplyWorkspaceEditParams{
|
||||||
|
Label: &label,
|
||||||
|
Edit: protocol.WorkspaceEdit{
|
||||||
|
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||||
|
args.URI: {
|
||||||
|
{
|
||||||
|
NewText: " " + privateKey,
|
||||||
|
Range: protocol.Range{
|
||||||
|
Start: protocol.Position{
|
||||||
|
Line: property.End.Line,
|
||||||
|
Character: property.End.Character,
|
||||||
|
},
|
||||||
|
End: protocol.Position{
|
||||||
|
Line: property.End.Line,
|
||||||
|
Character: property.End.Character,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/handlers/wireguard"
|
||||||
|
wgcommands "config-lsp/handlers/wireguard/commands"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CodeActionGeneratePresharedKeyArgs struct {
|
||||||
|
URI protocol.DocumentUri
|
||||||
|
Line uint32
|
||||||
|
}
|
||||||
|
|
||||||
|
func CodeActionGeneratePresharedKeyArgsFromArguments(arguments map[string]any) CodeActionGeneratePresharedKeyArgs {
|
||||||
|
return CodeActionGeneratePresharedKeyArgs{
|
||||||
|
URI: arguments["URI"].(protocol.DocumentUri),
|
||||||
|
Line: uint32(arguments["Line"].(float64)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (args CodeActionGeneratePresharedKeyArgs) RunCommand(d *wireguard.WGDocument) (*protocol.ApplyWorkspaceEditParams, error) {
|
||||||
|
presharedKey, err := wgcommands.CreatePresharedKey()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return &protocol.ApplyWorkspaceEditParams{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
section := d.Config.FindSectionByLine(args.Line)
|
||||||
|
property := d.Config.FindPropertyByLine(args.Line)
|
||||||
|
|
||||||
|
if section == nil || property == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
label := "Generate Preshared Key"
|
||||||
|
return &protocol.ApplyWorkspaceEditParams{
|
||||||
|
Label: &label,
|
||||||
|
Edit: protocol.WorkspaceEdit{
|
||||||
|
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||||
|
args.URI: {
|
||||||
|
{
|
||||||
|
NewText: presharedKey,
|
||||||
|
Range: property.Value.ToLSPRange(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
@ -0,0 +1,42 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/handlers/wireguard"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetAddPeerLikeThis(
|
||||||
|
d *wireguard.WGDocument,
|
||||||
|
params *protocol.CodeActionParams,
|
||||||
|
) []protocol.CodeAction {
|
||||||
|
// First, check if is on peer line
|
||||||
|
|
||||||
|
line := params.Range.Start.Line
|
||||||
|
|
||||||
|
section := d.Config.FindSectionByLine(line)
|
||||||
|
|
||||||
|
// Check if section can be copied
|
||||||
|
if section == nil || section.Start.Line != line || section.Header.Name != "Peer" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then add option
|
||||||
|
commandID := "wireguard." + CodeActionCreatePeer
|
||||||
|
command := protocol.Command{
|
||||||
|
Title: "Create new Peer based on this one",
|
||||||
|
Command: string(commandID),
|
||||||
|
Arguments: []any{
|
||||||
|
CodeActionCreatePeerArgs{
|
||||||
|
URI: params.TextDocument.URI,
|
||||||
|
Line: line,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return []protocol.CodeAction{
|
||||||
|
{
|
||||||
|
Title: "Create new Peer based on this one",
|
||||||
|
Command: &command,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,59 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/handlers/wireguard"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetKeepaliveCodeActions(
|
||||||
|
d *wireguard.WGDocument,
|
||||||
|
params *protocol.CodeActionParams,
|
||||||
|
) []protocol.CodeAction {
|
||||||
|
line := params.Range.Start.Line
|
||||||
|
|
||||||
|
for _, section := range d.Indexes.SectionsByName["Peer"] {
|
||||||
|
if section.Start.Line >= line && line <= section.End.Line {
|
||||||
|
_, endpoint := section.FindFirstPropertyByName("Endpoint")
|
||||||
|
_, persistentKeepAlive := section.FindFirstPropertyByName("PersistentKeepalive")
|
||||||
|
|
||||||
|
if endpoint != nil && persistentKeepAlive == nil {
|
||||||
|
var insertionLine uint32
|
||||||
|
lastProperty := section.GetLastProperty()
|
||||||
|
|
||||||
|
if lastProperty == nil {
|
||||||
|
insertionLine = section.End.Line
|
||||||
|
} else {
|
||||||
|
insertionLine = lastProperty.End.Line + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return []protocol.CodeAction{
|
||||||
|
{
|
||||||
|
Title: "Add PersistentKeepalive",
|
||||||
|
Edit: &protocol.WorkspaceEdit{
|
||||||
|
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||||
|
params.TextDocument.URI: {
|
||||||
|
{
|
||||||
|
Range: protocol.Range{
|
||||||
|
Start: protocol.Position{
|
||||||
|
Line: insertionLine,
|
||||||
|
Character: 0,
|
||||||
|
},
|
||||||
|
End: protocol.Position{
|
||||||
|
Line: insertionLine,
|
||||||
|
Character: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NewText: "PersistentKeepalive = 25\n",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -7,55 +7,6 @@ import (
|
|||||||
protocol "github.com/tliron/glsp/protocol_3_16"
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetKeepaliveCodeActions(
|
|
||||||
d *wireguard.WGDocument,
|
|
||||||
params *protocol.CodeActionParams,
|
|
||||||
) []protocol.CodeAction {
|
|
||||||
line := params.Range.Start.Line
|
|
||||||
|
|
||||||
for _, section := range d.Indexes.SectionsByName["Peer"] {
|
|
||||||
if section.Start.Line >= line && line <= section.End.Line {
|
|
||||||
if section.FindPropertyByName("Endpoint") != nil && section.FindFirstPropertyByName("PersistentKeepalive") == nil {
|
|
||||||
var insertionLine uint32
|
|
||||||
lastProperty := section.GetLastProperty()
|
|
||||||
|
|
||||||
if lastProperty == nil {
|
|
||||||
insertionLine = section.End.Line
|
|
||||||
} else {
|
|
||||||
insertionLine = lastProperty.End.Line + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
return []protocol.CodeAction{
|
|
||||||
{
|
|
||||||
Title: "Add PersistentKeepalive",
|
|
||||||
Edit: &protocol.WorkspaceEdit{
|
|
||||||
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
|
||||||
params.TextDocument.URI: {
|
|
||||||
{
|
|
||||||
Range: protocol.Range{
|
|
||||||
Start: protocol.Position{
|
|
||||||
Line: insertionLine,
|
|
||||||
Character: 0,
|
|
||||||
},
|
|
||||||
End: protocol.Position{
|
|
||||||
Line: insertionLine,
|
|
||||||
Character: 0,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
NewText: "PersistentKeepalive = 25\n",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetKeyGenerationCodeActions(
|
func GetKeyGenerationCodeActions(
|
||||||
d *wireguard.WGDocument,
|
d *wireguard.WGDocument,
|
||||||
params *protocol.CodeActionParams,
|
params *protocol.CodeActionParams,
|
@ -0,0 +1,66 @@
|
|||||||
|
package handlers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"config-lsp/common"
|
||||||
|
"config-lsp/handlers/wireguard"
|
||||||
|
"config-lsp/handlers/wireguard/diagnostics"
|
||||||
|
"config-lsp/handlers/wireguard/fields"
|
||||||
|
"config-lsp/utils"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
protocol "github.com/tliron/glsp/protocol_3_16"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetPropertyKeywordTypoFixes(
|
||||||
|
d *wireguard.WGDocument,
|
||||||
|
params *protocol.CodeActionParams,
|
||||||
|
) []protocol.CodeAction {
|
||||||
|
if common.ServerOptions.NoTypoSuggestions {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
line := params.Range.Start.Line
|
||||||
|
|
||||||
|
if typoInfo, found := d.Indexes.UnknownProperties[line]; found {
|
||||||
|
if options, found := fields.OptionsHeaderMap[fields.CreateNormalizedName(typoInfo.Section.Header.Name)]; found {
|
||||||
|
normalizedPropertyKey := fields.CreateNormalizedName(typoInfo.Property.Key.Name)
|
||||||
|
opts := utils.KeysOfMap(options)
|
||||||
|
|
||||||
|
suggestedProperties := common.FindSimilarItems(normalizedPropertyKey, opts)
|
||||||
|
|
||||||
|
actions := make([]protocol.CodeAction, 0, len(suggestedProperties))
|
||||||
|
|
||||||
|
kind := protocol.CodeActionKindQuickFix
|
||||||
|
for index, normalizedPropertyName := range suggestedProperties {
|
||||||
|
isPreferred := index == 0
|
||||||
|
optionName := fields.AllOptionsFormatted[normalizedPropertyName]
|
||||||
|
|
||||||
|
actions = append(actions, protocol.CodeAction{
|
||||||
|
Title: fmt.Sprintf("Typo Fix: %s", optionName),
|
||||||
|
IsPreferred: &isPreferred,
|
||||||
|
Kind: &kind,
|
||||||
|
Diagnostics: []protocol.Diagnostic{
|
||||||
|
diagnostics.GenerateUnknownOption(
|
||||||
|
typoInfo.Property.ToLSPRange(),
|
||||||
|
typoInfo.Property.Key.Name,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
Edit: &protocol.WorkspaceEdit{
|
||||||
|
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
|
||||||
|
params.TextDocument.URI: {
|
||||||
|
{
|
||||||
|
Range: typoInfo.Property.Key.ToLSPRange(),
|
||||||
|
NewText: optionName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return actions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -2,7 +2,15 @@ package indexes
|
|||||||
|
|
||||||
import "config-lsp/handlers/wireguard/ast"
|
import "config-lsp/handlers/wireguard/ast"
|
||||||
|
|
||||||
type WGIndexes struct {
|
type WGIndexPropertyInfo struct {
|
||||||
// map of: section name -> WGSection
|
Section *ast.WGSection
|
||||||
SectionsByName map[string][]*ast.WGSection
|
Property *ast.WGProperty
|
||||||
|
}
|
||||||
|
|
||||||
|
type WGIndexes struct {
|
||||||
|
// map of: section name -> *WGSection
|
||||||
|
SectionsByName map[string][]*ast.WGSection
|
||||||
|
|
||||||
|
// map of: line number -> *WGIndexPropertyInfo
|
||||||
|
UnknownProperties map[uint32]WGIndexPropertyInfo
|
||||||
}
|
}
|
||||||
|
@ -8,7 +8,8 @@ import (
|
|||||||
func CreateIndexes(config *ast.WGConfig) (*WGIndexes, []common.LSPError) {
|
func CreateIndexes(config *ast.WGConfig) (*WGIndexes, []common.LSPError) {
|
||||||
errs := make([]common.LSPError, 0)
|
errs := make([]common.LSPError, 0)
|
||||||
indexes := &WGIndexes{
|
indexes := &WGIndexes{
|
||||||
SectionsByName: make(map[string][]*ast.WGSection),
|
SectionsByName: make(map[string][]*ast.WGSection),
|
||||||
|
UnknownProperties: make(map[uint32]WGIndexPropertyInfo),
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, section := range config.Sections {
|
for _, section := range config.Sections {
|
||||||
|
@ -15,6 +15,8 @@ func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionPa
|
|||||||
|
|
||||||
actions = append(actions, handlers.GetKeyGenerationCodeActions(d, params)...)
|
actions = append(actions, handlers.GetKeyGenerationCodeActions(d, params)...)
|
||||||
actions = append(actions, handlers.GetKeepaliveCodeActions(d, params)...)
|
actions = append(actions, handlers.GetKeepaliveCodeActions(d, params)...)
|
||||||
|
actions = append(actions, handlers.GetAddPeerLikeThis(d, params)...)
|
||||||
|
actions = append(actions, handlers.GetPropertyKeywordTypoFixes(d, params)...)
|
||||||
|
|
||||||
if len(actions) > 0 {
|
if len(actions) > 0 {
|
||||||
return actions, nil
|
return actions, nil
|
||||||
|
@ -24,6 +24,12 @@ func WorkspaceExecuteCommand(context *glsp.Context, params *protocol.ExecuteComm
|
|||||||
|
|
||||||
d := wireguard.DocumentParserMap[args.URI]
|
d := wireguard.DocumentParserMap[args.URI]
|
||||||
|
|
||||||
|
return args.RunCommand(d)
|
||||||
|
case string(handlers.CodeActionCreatePeer):
|
||||||
|
args := handlers.CodeActionCreatePeerArgsFromArguments(params.Arguments[0].(map[string]any))
|
||||||
|
|
||||||
|
d := wireguard.DocumentParserMap[args.URI]
|
||||||
|
|
||||||
return args.RunCommand(d)
|
return args.RunCommand(d)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,4 +2,4 @@ package roothandler
|
|||||||
|
|
||||||
// The comment below at the end of the line is required for the CI:CD to work.
|
// The comment below at the end of the line is required for the CI:CD to work.
|
||||||
// Do not remove it
|
// Do not remove it
|
||||||
var Version = "0.2.0" // CI:CD-VERSION
|
var Version = "0.2.1" // CI:CD-VERSION
|
||||||
|
@ -35,10 +35,14 @@ func (t *iPv4Tree) addHostBits(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *iPv4Tree) getFromHostBits(hostBits []bool) *context.Context {
|
func (t *iPv4Tree) getFromHostBits(hostBits []bool) *context.Context {
|
||||||
if t.Context != nil || len(hostBits) == 0 {
|
if t.Context != nil {
|
||||||
return &t.Context
|
return &t.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if len(hostBits) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
if hostBits[0] {
|
if hostBits[0] {
|
||||||
if t.TrueNode == nil {
|
if t.TrueNode == nil {
|
||||||
return nil
|
return nil
|
||||||
|
@ -31,8 +31,8 @@ func TestPartialHostIpAddresses(t *testing.T) {
|
|||||||
hostSet.AddIP(netip.MustParsePrefix("10.0.0.2/32"), context.Background())
|
hostSet.AddIP(netip.MustParsePrefix("10.0.0.2/32"), context.Background())
|
||||||
hostSet.AddIP(netip.MustParsePrefix("10.0.0.3/32"), context.Background())
|
hostSet.AddIP(netip.MustParsePrefix("10.0.0.3/32"), context.Background())
|
||||||
|
|
||||||
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("10.0.0.1/16")); ctx == nil {
|
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("10.0.0.1/16")); ctx != nil {
|
||||||
t.Fatalf("Expected to find 10.0.0.1/16 in the host set")
|
t.Fatalf("Didn't expect to find 10.0.0.1/16 in the host set")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("192.168.0.1/16")); ctx != nil {
|
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("192.168.0.1/16")); ctx != nil {
|
||||||
@ -48,12 +48,24 @@ func TestMixedHostIpAddresses(t *testing.T) {
|
|||||||
hostSet.AddIP(netip.MustParsePrefix("192.168.0.1/32"), context.Background())
|
hostSet.AddIP(netip.MustParsePrefix("192.168.0.1/32"), context.Background())
|
||||||
|
|
||||||
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("10.0.0.2/32")); ctx == nil {
|
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("10.0.0.2/32")); ctx == nil {
|
||||||
t.Fatalf("Expected to find 10.0.0.3/32 in the host set")
|
t.Fatalf("Expected to find 10.0.0.1/32 in the host set")
|
||||||
}
|
}
|
||||||
|
|
||||||
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("192.168.0.2/32")); ctx != nil {
|
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("192.168.0.2/32")); ctx != nil {
|
||||||
t.Fatalf("Expected NOT to find 192.168.0.2/32 in the host set")
|
t.Fatalf("Expected NOT to find 192.168.0.2/32 in the host set")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("10.0.0.2/32")); ctx == nil {
|
||||||
|
t.Fatalf("Expected to find 10.0.0.2/32 in the host set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("10.0.1.2/32")); ctx == nil {
|
||||||
|
t.Fatalf("Expected to find 10.0.1.2/32 in the host set")
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx, _ := hostSet.ContainsIP(netip.MustParsePrefix("10.0.0.1/30")); ctx == nil {
|
||||||
|
t.Fatalf("Expected to find 10.0.0.1/30 in the host set")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSimpleExactCheck(t *testing.T) {
|
func TestSimpleExactCheck(t *testing.T) {
|
||||||
|
@ -6,16 +6,10 @@ and definition requests.
|
|||||||
|
|
||||||
Install this extension and load your config files in VS Code to get started.
|
Install this extension and load your config files in VS Code to get started.
|
||||||
|
|
||||||
If `config-lsp` is unable to detect the language of your config file, you can manually
|
_config-lsp_ adds the new languages directly to VS Code, so you can use the built-in language selector
|
||||||
specify it by adding a line in the form of:
|
at the bottom right of the window to switch between different config files.
|
||||||
|
|
||||||
```plaintext
|
**Please note that this extension is still in beta. There are still bugs and missing features.**
|
||||||
#?lsp.language=<language>
|
I'm working on this as a hobby project in my free time, so I can't provide you with a timeline
|
||||||
|
as to when a stable version will be released.
|
||||||
# For example
|
|
||||||
|
|
||||||
#?lsp.language=sshconfig
|
|
||||||
#?lsp.language=fstab
|
|
||||||
#?lsp.language=aliases
|
|
||||||
```
|
|
||||||
|
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
"name": "config-lsp",
|
"name": "config-lsp",
|
||||||
"description": "Language Features (completions, diagnostics, etc.) for your config files: gitconfig, fstab, aliases, hosts, wireguard, ssh_config, sshd_config, and more to come!",
|
"description": "Language Features (completions, diagnostics, etc.) for your config files: gitconfig, fstab, aliases, hosts, wireguard, ssh_config, sshd_config, and more to come!",
|
||||||
"author": "Myzel394",
|
"author": "Myzel394",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/Myzel394/config-lsp"
|
"url": "https://github.com/Myzel394/config-lsp"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user