diff --git a/server/common-documentation/mnt-btrfs.go b/server/common-documentation/mnt-btrfs.go index 46a7fbf..2b8efc0 100644 --- a/server/common-documentation/mnt-btrfs.go +++ b/server/common-documentation/mnt-btrfs.go @@ -16,16 +16,31 @@ var BtrfsDocumentationAssignable = map[docvalues.EnumString]docvalues.Deprecated docvalues.CreateEnumStringWithDoc( "compress", "Control BTRFS file data compression. Type may be specified as zlib, lzo, zstd or no (for no compression, used for remounting). If no type is specified, zlib is used. If compress-force is specified, then compression will always be attempted, but the data may end up uncompressed if the compression would make them larger.", - ): docvalues.EnumValue{ - EnforceValues: true, - Values: []docvalues.EnumString{ - docvalues.CreateEnumString("zlib"), - docvalues.CreateEnumString("lzo"), - docvalues.CreateEnumString("zstd"), - docvalues.CreateEnumStringWithDoc( - "no", - "No compression, used for remounting.", - ), + ): docvalues.OrValue{ + Values: []docvalues.DeprecatedValue{ + docvalues.EnumValue{ + Values: []docvalues.EnumString{ + docvalues.CreateEnumStringWithDoc( + "no", + "No compression, used for remounting.", + ), + }, + }, + docvalues.KeyEnumAssignmentValue{ + Values: map[docvalues.EnumString]docvalues.DeprecatedValue{ + docvalues.CreateEnumString("zlib"): docvalues.NumberRangeValue( + 0, + 9, + ), + docvalues.CreateEnumString("lzo"): docvalues.NumberValue{}, + docvalues.CreateEnumString("zstd"): docvalues.NumberRangeValue( + 0, + 15, + ), + }, + Separator: ":", + ValueIsOptional: true, + }, }, }, docvalues.CreateEnumStringWithDoc( diff --git a/server/doc-values/extra-values.go b/server/doc-values/extra-values.go index 8daed43..dbc16ea 100644 --- a/server/doc-values/extra-values.go +++ b/server/doc-values/extra-values.go @@ -66,6 +66,13 @@ func GroupValue(separatorForMultiple string, enforceValues bool) DeprecatedValue } } +func NumberRangeValue(min int, max int) DeprecatedValue { + return NumberValue{ + Min: &min, + Max: &max, + } +} + func PositiveNumberValue() DeprecatedValue { zero := 0 return NumberValue{ diff --git a/server/doc-values/value-path.go b/server/doc-values/value-path.go index fa7d5df..1aced7c 100644 --- a/server/doc-values/value-path.go +++ b/server/doc-values/value-path.go @@ -52,8 +52,7 @@ func (v PathValue) DeprecatedCheckIsValid(value string) []*InvalidValue { Err: PathDoesNotExistError{}, Start: 0, End: uint32(len(value)), - }, - } + }} } isValid := false diff --git a/server/handlers/fstab/documentation/documentation-mountoptions.go b/server/handlers/fstab/documentation/documentation-mountoptions.go index 8c26b5f..191bd3d 100644 --- a/server/handlers/fstab/documentation/documentation-mountoptions.go +++ b/server/handlers/fstab/documentation/documentation-mountoptions.go @@ -191,6 +191,52 @@ var defaultOptions = []docvalues.EnumString{ "nosymfollow", "Do not follow symlinks when resolving paths. Symlinks can still be created, and readlink(1), readlink(2), realpath(1), and realpath(3) all still work properly.", ), + docvalues.CreateEnumStringWithDoc( + "x-systemd.automount", + `An automount unit will be created for the file system. See systemd.automount(5) for details. + +Added in version 215.`, + ), + docvalues.CreateEnumStringWithDoc( + "x-systemd.makefs", + `The file system will be initialized on the device. If the device is not "empty", i.e. it contains any signature, the operation will be skipped. It is hence expected that this option remains set even after the device has been initialized. + +Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file. + +See systemd-makefs@.service(8). + +wipefs(8) may be used to remove any signatures from a block device to force x-systemd.makefs to reinitialize the device. + +Added in version 236.`, + ), + docvalues.CreateEnumStringWithDoc( + "x-systemd.growfs", + `The file system will be grown to occupy the full block device. If the file system is already at maximum size, no action will be performed. It is hence expected that this option remains set even after the file system has been grown. Only certain file system types are supported, see systemd-makefs@.service(8) for details. + +Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file. + +Added in version 236.`, + ), + docvalues.CreateEnumStringWithDoc( + "x-systemd.pcrfs", + `Measures file system identity information (mount point, type, label, UUID, partition label, partition UUID) into PCR 15 after the file system has been mounted. This ensures the systemd-pcrfs@.service(8) or systemd-pcrfs-root.service services are pulled in by the mount unit. + +Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file. It is also implied for the root and /usr/ partitions discovered by systemd-gpt-auto-generator(8). + +Added in version 253.`, + ), + docvalues.CreateEnumStringWithDoc( + "x-systemd.rw-only", + `If a mount operation fails to mount the file system read-write, it normally tries mounting the file system read-only instead. This option disables that behaviour, and causes the mount to fail immediately instead. This option is translated into the ReadWriteOnly= setting in a unit file. + +Added in version 246.`, + ), + docvalues.CreateEnumStringWithDoc( + "x-initrd.mount", + `An additional filesystem to be mounted in the initrd. See initrd-fs.target description in systemd.special(7). This is both an indicator to the initrd to mount this partition early and an indicator to the host to leave the partition mounted until final shutdown. Or in other words, if this flag is set it is assumed the mount shall be active during the entire regular runtime of the system, i.e. established before the initrd transitions into the host all the way until the host transitions to the final shutdown phase. + +Added in version 215.`, + ), } type assignOption struct { @@ -215,6 +261,90 @@ var defaultAssignOptions = map[docvalues.EnumString]docvalues.DeprecatedValue{ "rootcontext", "The rootcontext= option allows you to explicitly label the root inode of a FS being mounted before that FS or inode becomes visible to userspace. This was found to be useful for things like stateless Linux. The special value @target can be used to assign the current context of the target mountpoint location.", ): docvalues.StringValue{}, + docvalues.CreateEnumStringWithDoc( + "x-systemd.requires", + `Configures a Requires= and an After= dependency between the created mount unit and another systemd unit, such as a device or mount unit. The argument should be a unit name, or an absolute path to a device node or mount point. This option may be specified more than once. This option is particularly useful for mount point declarations that need an additional device to be around (such as an external journal device for journal file systems) or an additional mount to be in place (such as an overlay file system that merges multiple mount points). See After= and Requires= in systemd.unit(5) for details. + +Note that this option always applies to the created mount unit only regardless whether x-systemd.automount has been specified. + +Added in version 220.`, + ): docvalues.StringValue{}, + docvalues.CreateEnumStringWithDoc( + "x-systemd.before", + `In the created mount unit, configures a Before= or After= dependency on another systemd unit, such as a mount unit. The argument should be a unit name or an absolute path to a mount point. This option may be specified more than once. This option is particularly useful for mount point declarations with nofail option that are mounted asynchronously but need to be mounted before or after some unit start, for example, before local-fs.target unit. See Before= and After= in systemd.unit(5) for details. + +Note that these options always apply to the created mount unit only regardless whether x-systemd.automount has been specified. + +Added in version 233.`, + ): docvalues.StringValue{}, + docvalues.CreateEnumStringWithDoc( + "x-systemd.after", + `In the created mount unit, configures a Before= or After= dependency on another systemd unit, such as a mount unit. The argument should be a unit name or an absolute path to a mount point. This option may be specified more than once. This option is particularly useful for mount point declarations with nofail option that are mounted asynchronously but need to be mounted before or after some unit start, for example, before local-fs.target unit. See Before= and After= in systemd.unit(5) for details. + +Note that these options always apply to the created mount unit only regardless whether x-systemd.automount has been specified. + +Added in version 233.`, + ): docvalues.StringValue{}, + docvalues.CreateEnumStringWithDoc( + "x-systemd.wanted-by", + `In the created mount unit, configures a WantedBy= or RequiredBy= dependency on another unit. This option may be specified more than once. If this is specified, the default dependencies (see above) other than umount.target on the created mount unit, e.g. local-fs.target, are not automatically created. Hence it is likely that some ordering dependencies need to be set up manually through x-systemd.before= and x-systemd.after=. See WantedBy= and RequiredBy= in systemd.unit(5) for details. + +Added in version 245.`, + ): docvalues.StringValue{}, + docvalues.CreateEnumStringWithDoc( + "x-systemd.required-by", + `In the created mount unit, configures a WantedBy= or RequiredBy= dependency on another unit. This option may be specified more than once. If this is specified, the default dependencies (see above) other than umount.target on the created mount unit, e.g. local-fs.target, are not automatically created. Hence it is likely that some ordering dependencies need to be set up manually through x-systemd.before= and x-systemd.after=. See WantedBy= and RequiredBy= in systemd.unit(5) for details. + +Added in version 245.`, + ): docvalues.StringValue{}, + docvalues.CreateEnumStringWithDoc( + "x-systemd.wants-mounts-for", + `Configures a RequiresMountsFor= or WantsMountsFor= dependency between the created mount unit and other mount units. The argument must be an absolute path. This option may be specified more than once. See RequiresMountsFor= or WantsMountsFor= in systemd.unit(5) for details. + +Added in version 220.`, + ): docvalues.StringValue{}, + docvalues.CreateEnumStringWithDoc( + "x-systemd.requires-mounts-for", + `Configures a RequiresMountsFor= or WantsMountsFor= dependency between the created mount unit and other mount units. The argument must be an absolute path. This option may be specified more than once. See RequiresMountsFor= or WantsMountsFor= in systemd.unit(5) for details. + +Added in version 220.`, + ): docvalues.StringValue{}, + docvalues.CreateEnumStringWithDoc( + "x-systemd.device-bound", + `Takes a boolean argument. If true or no argument, a BindsTo= dependency on the backing device is set. If false, the mount unit is not stopped no matter whether the backing device is still present. This is useful when the file system is backed by volume managers. If not set, and the mount comes from unit fragments, i.e. generated from /etc/fstab by systemd-fstab-generator(8) or loaded from a manually configured mount unit, a combination of Requires= and StopPropagatedFrom= dependencies is set on the backing device. If doesn't, only Requires= is used. + +Added in version 233.`, + ): docvalues.EnumValue{ + EnforceValues: true, + Values: []docvalues.EnumString{ + docvalues.CreateEnumString("true"), + docvalues.CreateEnumString("false"), + }, + }, + docvalues.CreateEnumStringWithDoc( + "x-systemd.idle-timeout", + `Configures the idle timeout of the automount unit. See TimeoutIdleSec= in systemd.automount(5) for details. + +Added in version 220.`, + ): docvalues.StringValue{}, + docvalues.CreateEnumStringWithDoc( + "x-systemd.device-timeout", + `Configure how long systemd should wait for a device to show up before giving up on an entry from /etc/fstab. Specify a time in seconds or explicitly append a unit such as "s", "min", "h", "ms". + +Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file. + +Added in version 215.`, + ): docvalues.StringValue{}, + docvalues.CreateEnumStringWithDoc( + "x-systemd.mount-timeout", + `Configure how long systemd should wait for the mount command to finish before giving up on an entry from /etc/fstab. Specify a time in seconds or explicitly append a unit such as "s", "min", "h", "ms". + +Note that this option can only be used in /etc/fstab, and will be ignored when part of the Options= setting in a unit file. + +See TimeoutSec= below for details. + +Added in version 233.`, + ): docvalues.StringValue{}, } func createMountOptionField( diff --git a/server/handlers/fstab/documentation/documentation-spec.go b/server/handlers/fstab/documentation/documentation-spec.go index f3087f5..2cf7db2 100644 --- a/server/handlers/fstab/documentation/documentation-spec.go +++ b/server/handlers/fstab/documentation/documentation-spec.go @@ -14,9 +14,9 @@ var LabelField = docvalues.RegexValue{ var SpecField = docvalues.OrValue{ Values: []docvalues.DeprecatedValue{ - // docvalues.PathValue{ - // RequiredType: docvalues.PathTypeFile & docvalues.PathTypeExistenceOptional, - // }, + docvalues.PathValue{ + RequiredType: docvalues.PathTypeFile & docvalues.PathTypeExistenceOptional, + }, docvalues.KeyEnumAssignmentValue{ Separator: "=", ValueIsOptional: false, diff --git a/server/handlers/fstab/fstab_test.go b/server/handlers/fstab/fstab_test.go index da39d0c..629c690 100644 --- a/server/handlers/fstab/fstab_test.go +++ b/server/handlers/fstab/fstab_test.go @@ -124,3 +124,124 @@ LABEL=test /mnt/test btrfs subvol=backup,fat=32 0 0 } } } + +// func TestExample1(t *testing.T) { +// input := utils.Dedent(` +// /dev/disk/by-uuid/19ae4e13-1d6d-4833-965b-a20197aebf27 /mnt/RetroGames auto nosuid,nodev,nofail,x-gvfs-show 0 0 +// /dev/disk/by-uuid/02629e02-a66d-4290-8a67-717ec9db6acc /mnt/SteamGames1 auto nosuid,nodev,nofail,x-gvfs-show 0 0 +// /dev/disk/by-uuid/eb7c3d96-162f-41b7-b47f-add2c21b0220 /mnt/SteamGames2 auto nosuid,nodev,nofail,x-gvfs-show 0 0 +// /dev/disk/by-uuid/ae977b84-cb99-492c-b50c-0e8b1242789f /mnt/Multimedia auto nosuid,nodev,nofail,x-gvfs-show 0 0 +// /dev/disk/by-uuid/e5baf492-8653-415f-b0c4-bd88a81d61bd /mnt/Backup auto nosuid,nodev,nofail,x-gvfs-show 0 0 +// /dev/disk/by-uuid/a3eb3cce-a34f-4141-b604-9aa293cb40c5 /mnt/Data auto nosuid,nodev,nofail,x-gvfs-show 0 0 +// `) +// p := parser.FstabParser{} +// p.Clear() +// +// errors := p.ParseFromContent(input) +// +// if len(errors) > 0 { +// t.Fatalf("ParseFromContent failed with error %v", errors) +// } +// } + +func TestArchExample1(t *testing.T) { + input := utils.Dedent(` +UUID=0a3407de-014b-458b-b5c1-848e92a327a3 / ext4 defaults 0 1 +UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0 +UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2 +`) + p := parser.FstabParser{} + p.Clear() + + errors := p.ParseFromContent(input) + + if len(errors) > 0 { + t.Fatalf("ParseFromContent failed with error %v", errors) + } +} + +func TestArchExample2(t *testing.T) { + input := utils.Dedent(` +/dev/sda1 /boot vfat defaults 0 2 +/dev/sda2 / ext4 defaults 0 1 +/dev/sda3 /home ext4 defaults 0 2 +/dev/sda4 none swap defaults 0 0 +`) + p := parser.FstabParser{} + p.Clear() + + errors := p.ParseFromContent(input) + + if len(errors) > 0 { + t.Fatalf("ParseFromContent failed with error %v", errors) + } +} + +func TestArchExample3(t *testing.T) { + input := utils.Dedent(` +LABEL=ESP /boot vfat defaults 0 2 +LABEL=System / ext4 defaults 0 1 +LABEL=Data /home ext4 defaults 0 2 +LABEL=Swap none swap defaults 0 0 +`) + p := parser.FstabParser{} + p.Clear() + + errors := p.ParseFromContent(input) + + if len(errors) > 0 { + t.Fatalf("ParseFromContent failed with error %v", errors) + } +} + +func TestArchExample4(t *testing.T) { + input := utils.Dedent(` +UUID=CBB6-24F2 /boot vfat defaults 0 2 +UUID=0a3407de-014b-458b-b5c1-848e92a327a3 / ext4 defaults 0 1 +UUID=b411dc99-f0a0-4c87-9e05-184977be8539 /home ext4 defaults 0 2 +UUID=f9fe0b69-a280-415d-a03a-a32752370dee none swap defaults 0 0 +`) + p := parser.FstabParser{} + p.Clear() + + errors := p.ParseFromContent(input) + + if len(errors) > 0 { + t.Fatalf("ParseFromContent failed with error %v", errors) + } +} + +func TestArchExample5(t *testing.T) { + input := utils.Dedent(` +PARTLABEL=EFI\040system\040partition /boot vfat defaults 0 2 +PARTLABEL=GNU/Linux / ext4 defaults 0 1 +PARTLABEL=Home /home ext4 defaults 0 2 +PARTLABEL=Swap none swap defaults 0 0 +`) + p := parser.FstabParser{} + p.Clear() + + errors := p.ParseFromContent(input) + + if len(errors) > 0 { + t.Fatalf("ParseFromContent failed with error %v", errors) + } +} + +func TestArchExample6(t *testing.T) { + input := utils.Dedent(` +PARTUUID=d0d0d110-0a71-4ed6-936a-304969ea36af /boot vfat defaults 0 2 +PARTUUID=98a81274-10f7-40db-872a-03df048df366 / ext4 defaults 0 1 +PARTUUID=7280201c-fc5d-40f2-a9b2-466611d3d49e /home ext4 defaults 0 2 +PARTUUID=039b6c1c-7553-4455-9537-1befbc9fbc5b none swap defaults 0 0 +`) + p := parser.FstabParser{} + p.Clear() + + errors := p.ParseFromContent(input) + + if len(errors) > 0 { + t.Fatalf("ParseFromContent failed with error %v", errors) + } +} + diff --git a/server/handlers/fstab/lsp/text-document-completion.go b/server/handlers/fstab/lsp/text-document-completion.go index 55b585f..375fbd2 100644 --- a/server/handlers/fstab/lsp/text-document-completion.go +++ b/server/handlers/fstab/lsp/text-document-completion.go @@ -1,6 +1,7 @@ package lsp import ( + "config-lsp/common" fstabdocumentation "config-lsp/handlers/fstab/documentation" "config-lsp/handlers/fstab/handlers" "config-lsp/handlers/fstab/parser" @@ -27,7 +28,7 @@ func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionPa return nil, nil } - cursor := params.Position.Character + cursor := common.CursorToCharacterIndex(params.Position.Character) line := entry.Line return handlers.GetCompletion(line, cursor)