Merge pull request #24 from Myzel394/improvements

This commit is contained in:
Myzel394 2024-10-27 12:14:22 +01:00 committed by GitHub
commit fcdd70030f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
30 changed files with 630 additions and 429 deletions

View File

@ -29,7 +29,7 @@ jobs:
- name: Check version in code matches flake version - name: Check version in code matches flake version
shell: bash shell: bash
run: | run: |
if ! [ $(grep '// CI:CD-VERSION$' server/root-handler/handler.go | cut -d'"' -f 2) = $(grep '# CI:CD-VERSION$' flake.nix | cut -d'"' -f 2) ]; if ! [ $(grep '// CI:CD-VERSION$' server/root-handler/common.go | cut -d'"' -f 2) = $(grep '# CI:CD-VERSION$' flake.nix | cut -d'"' -f 2) ];
then then
echo "Version mismatch between code and flake" echo "Version mismatch between code and flake"
exit 1 exit 1

View File

@ -28,11 +28,52 @@ You are welcome to request any config file, as far as it's fairly well known.
## Installation ## Installation
### VS Code Extension
[Install the extension from the marketplace](https://marketplace.visualstudio.com/items?itemName=myzel394.config-lsp)
Alternatively, you can also manually install the extension:
1. Download the latest extension version from the [release page](https://github.com/Myzel394/config-lsp/releases) - You can find the extension under the "assets" section. The filename ends with `.vsix`
2. Open VS Code
3. Open the extensions sidebar
4. In the top bar, click on the three dots and select "Install from VSIX..."
5. Select the just downloaded `.vsix` file
6. You may need to restart VS Code
7. Enjoy!
### Manual installation
To use `config-lsp` in any other editor, you'll need to install it manually.
Don't worry, it's easy!
#### Installing the latest Binary
##### Brew
```sh
brew install myzel394/formulae/config-lsp
```
##### Manual Binary
Download the latest binary from the [releases page](https://github.com/Myzel394/config-lsp/releases) and put it in your PATH. Download the latest binary from the [releases page](https://github.com/Myzel394/config-lsp/releases) and put it in your PATH.
Follow the instructions for your editor below. ##### Compiling
### Neovim installation You can either compile the binary using go:
```sh
go build -o config-lsp
```
or build it using Nix:
```sh
nix flake build
```
#### Neovim installation
Using [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) you can add `config-lsp` by adding the following to your `lsp.lua` (filename might differ): Using [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) you can add `config-lsp` by adding the following to your `lsp.lua` (filename might differ):
@ -57,14 +98,6 @@ end
lspconfig.config_lsp.setup {} lspconfig.config_lsp.setup {}
````` `````
### VS Code installation
The VS Code extension is currently in development. An official extension will be released soon.
However, at the moment you can also compile the extension yourself and run it in development mode.
**Do not create an extension and publish it yourself. Contribute to the official extension instead.**
## Supporting config-lsp ## Supporting config-lsp
You can either contribute to the project, [see CONTRIBUTING.md](CONTRIBUTING.md), or you can sponsor me via [GitHub Sponsors](https://github.com/sponsors/Myzel394) or via [crypto currencies](https://github.com/Myzel394/contact-me?tab=readme-ov-file#donations). You can either contribute to the project, [see CONTRIBUTING.md](CONTRIBUTING.md), or you can sponsor me via [GitHub Sponsors](https://github.com/sponsors/Myzel394) or via [crypto currencies](https://github.com/Myzel394/contact-me?tab=readme-ov-file#donations).

View File

@ -23,7 +23,7 @@
"aarch64-windows" "aarch64-windows"
] (system: ] (system:
let let
version = "0.1.1"; # CI:CD-VERSION version = "0.1.2"; # CI:CD-VERSION
pkgs = import nixpkgs { pkgs = import nixpkgs {
inherit system; inherit system;
overlays = [ overlays = [

View File

@ -2,6 +2,8 @@ package main
import ( import (
roothandler "config-lsp/root-handler" roothandler "config-lsp/root-handler"
"fmt"
"os"
"github.com/tliron/commonlog" "github.com/tliron/commonlog"
@ -11,6 +13,13 @@ import (
) )
func main() { func main() {
if len(os.Args) > 1 && (os.Args[1] == "--version" || os.Args[1] == "version") {
fmt.Println(roothandler.Version)
os.Exit(0)
return
}
// This increases logging verbosity (optional) // This increases logging verbosity (optional)
commonlog.Configure(1, nil) commonlog.Configure(1, nil)

View File

@ -0,0 +1,5 @@
package roothandler
// The comment below at the end of the line is required for the CI:CD to work.
// Do not remove it
var Version = "0.1.2" // CI:CD-VERSION

View File

@ -1,6 +1,8 @@
package roothandler package roothandler
import ( import (
"config-lsp/root-handler/lsp"
"config-lsp/root-handler/shared"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
@ -9,32 +11,29 @@ import (
const lsName = "config-lsp" const lsName = "config-lsp"
// The comment below at the end of the line is required for the CI:CD to work.
// Do not remove it
var version = "0.1.1" // CI:CD-VERSION
var lspHandler protocol.Handler var lspHandler protocol.Handler
// The root handler which handles all the LSP requests and then forwards them to the appropriate handler // The root handler which handles all the LSP requests and then forwards them to the appropriate handler
func SetUpRootHandler() { func SetUpRootHandler() {
rootHandler = NewRootHandler() shared.Handler = shared.NewRootHandler()
lspHandler = protocol.Handler{ lspHandler = protocol.Handler{
Initialize: initialize, Initialize: initialize,
Initialized: initialized, Initialized: initialized,
Shutdown: shutdown, Shutdown: shutdown,
SetTrace: setTrace, SetTrace: setTrace,
TextDocumentDidOpen: TextDocumentDidOpen, TextDocumentDidOpen: lsp.TextDocumentDidOpen,
TextDocumentDidChange: TextDocumentDidChange, TextDocumentDidChange: lsp.TextDocumentDidChange,
TextDocumentCompletion: TextDocumentCompletion, TextDocumentCompletion: lsp.TextDocumentCompletion,
TextDocumentHover: TextDocumentHover, TextDocumentHover: lsp.TextDocumentHover,
TextDocumentDidClose: TextDocumentDidClose, TextDocumentDidClose: lsp.TextDocumentDidClose,
TextDocumentCodeAction: TextDocumentCodeAction, TextDocumentCodeAction: lsp.TextDocumentCodeAction,
TextDocumentDefinition: TextDocumentDefinition, TextDocumentDefinition: lsp.TextDocumentDefinition,
WorkspaceExecuteCommand: WorkspaceExecuteCommand, WorkspaceExecuteCommand: lsp.WorkspaceExecuteCommand,
TextDocumentRename: TextDocumentRename, TextDocumentRename: lsp.TextDocumentRename,
TextDocumentPrepareRename: TextDocumentPrepareRename, TextDocumentPrepareRename: lsp.TextDocumentPrepareRename,
TextDocumentSignatureHelp: TextDocumentSignatureHelp, TextDocumentSignatureHelp: lsp.TextDocumentSignatureHelp,
TextDocumentRangeFormatting: TextDocumentRangeFormattingFunc, TextDocumentRangeFormatting: lsp.TextDocumentRangeFormattingFunc,
} }
server := server.NewServer(&lspHandler, lsName, false) server := server.NewServer(&lspHandler, lsName, false)
@ -72,7 +71,7 @@ func initialize(context *glsp.Context, params *protocol.InitializeParams) (any,
Capabilities: capabilities, Capabilities: capabilities,
ServerInfo: &protocol.InitializeResultServerInfo{ ServerInfo: &protocol.InitializeResultServerInfo{
Name: lsName, Name: lsName,
Version: &version, Version: &Version,
}, },
}, nil }, nil
} }

View File

@ -1,149 +0,0 @@
package roothandler
import (
"config-lsp/common"
"config-lsp/utils"
"fmt"
"regexp"
"strings"
protocol "github.com/tliron/glsp/protocol_3_16"
)
type SupportedLanguage string
const (
LanguageSSHConfig SupportedLanguage = "ssh_config"
LanguageSSHDConfig SupportedLanguage = "sshd_config"
LanguageFstab SupportedLanguage = "fstab"
LanguageWireguard SupportedLanguage = "languagewireguard"
LanguageHosts SupportedLanguage = "hosts"
LanguageAliases SupportedLanguage = "aliases"
)
var AllSupportedLanguages = []string{
string(LanguageSSHConfig),
string(LanguageSSHDConfig),
string(LanguageFstab),
string(LanguageWireguard),
string(LanguageHosts),
string(LanguageAliases),
}
type FatalFileNotReadableError struct {
FileURI protocol.DocumentUri
Err error
}
func (e FatalFileNotReadableError) Error() string {
return fmt.Sprintf("Fatal error! config-lsp was unable to read the file (%s); error: %s", e.FileURI, e.Err.Error())
}
type UnsupportedLanguageError struct {
SuggestedLanguage string
}
func (e UnsupportedLanguageError) Error() string {
return fmt.Sprintf("Language '%s' is not supported. Choose one of: %s", e.SuggestedLanguage, strings.Join(AllSupportedLanguages, ", "))
}
type LanguageUndetectableError struct{}
func (e LanguageUndetectableError) Error() string {
return "Please add: '#?lsp.language=<language>' to the top of the file. config-lsp was unable to detect the appropriate language for this file."
}
var valueToLanguageMap = map[string]SupportedLanguage{
"sshd_config": LanguageSSHDConfig,
"sshdconfig": LanguageSSHDConfig,
"ssh_config": LanguageSSHConfig,
"sshconfig": LanguageSSHConfig,
".ssh/config": LanguageSSHConfig,
"~/.ssh/config": LanguageSSHConfig,
"fstab": LanguageFstab,
"etc/fstab": LanguageFstab,
"wireguard": LanguageWireguard,
"wg": LanguageWireguard,
"languagewireguard": LanguageWireguard,
"host": LanguageHosts,
"hosts": LanguageHosts,
"etc/hosts": LanguageHosts,
"aliases": LanguageAliases,
"mailaliases": LanguageAliases,
"etc/aliases": LanguageAliases,
}
var typeOverwriteRegex = regexp.MustCompile(`#\?\s*lsp\.language\s*=\s*(\w+)\s*`)
var wireguardPattern = regexp.MustCompile(`/wg\d+\.conf$`)
var undetectableError = common.ParseError{
Line: 0,
Err: LanguageUndetectableError{},
}
func DetectLanguage(
content string,
advertisedLanguage string,
uri protocol.DocumentUri,
) (SupportedLanguage, error) {
if match := typeOverwriteRegex.FindStringSubmatch(content); match != nil {
suggestedLanguage := strings.ToLower(match[1])
foundLanguage, ok := valueToLanguageMap[suggestedLanguage]
if ok {
return foundLanguage, nil
}
matchIndex := strings.Index(content, match[0])
contentUntilMatch := content[:matchIndex]
return "", common.ParseError{
Line: uint32(utils.CountCharacterOccurrences(contentUntilMatch, '\n')),
Err: UnsupportedLanguageError{
SuggestedLanguage: suggestedLanguage,
},
}
}
if language, ok := valueToLanguageMap[advertisedLanguage]; ok {
return language, nil
}
switch uri {
case "file:///etc/ssh/sshd_config":
fallthrough
case "file:///etc/ssh/ssh_config":
return LanguageSSHDConfig, nil
case "file:///etc/fstab":
return LanguageFstab, nil
// Darwin
case "file:///private/etc/hosts":
fallthrough
case "file:///etc/hosts":
return LanguageHosts, nil
// Darwin
case "file:///private/etc/aliases":
fallthrough
case "file:///etc/aliases":
return LanguageAliases, nil
}
if strings.HasPrefix(uri, "file:///etc/wireguard/") || wireguardPattern.MatchString(uri) {
return LanguageWireguard, nil
}
if strings.HasSuffix(uri, ".ssh/config") {
return LanguageSSHConfig, nil
}
return "", undetectableError
}

View File

@ -1,40 +1,36 @@
package roothandler package lsp
import ( import (
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"
hosts "config-lsp/handlers/hosts/lsp" hosts "config-lsp/handlers/hosts/lsp"
sshconfig "config-lsp/handlers/ssh_config/lsp" sshconfig "config-lsp/handlers/ssh_config/lsp"
wireguard "config-lsp/handlers/wireguard/lsp" wireguard "config-lsp/handlers/wireguard/lsp"
"config-lsp/root-handler/shared"
utils "config-lsp/root-handler/utils"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) (any, error) { func TextDocumentCodeAction(context *glsp.Context, params *protocol.CodeActionParams) (any, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) language := shared.Handler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil { if language == nil {
showParseError( return utils.FetchAddLanguageActions(params.TextDocument.URI)
context,
params.TextDocument.URI,
undetectableError,
)
return nil, nil
} }
switch *language { switch *language {
case LanguageFstab: case shared.LanguageFstab:
return nil, nil return nil, nil
case LanguageHosts: case shared.LanguageHosts:
return hosts.TextDocumentCodeAction(context, params) return hosts.TextDocumentCodeAction(context, params)
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return nil, nil return nil, nil
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentCodeAction(context, params) return sshconfig.TextDocumentCodeAction(context, params)
case LanguageWireguard: case shared.LanguageWireguard:
return wireguard.TextDocumentCodeAction(context, params) return wireguard.TextDocumentCodeAction(context, params)
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentCodeAction(context, params) return aliases.TextDocumentCodeAction(context, params)
} }

View File

@ -1,4 +1,4 @@
package roothandler package lsp
import ( import (
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"
@ -7,36 +7,30 @@ import (
sshconfig "config-lsp/handlers/ssh_config/lsp" sshconfig "config-lsp/handlers/ssh_config/lsp"
sshdconfig "config-lsp/handlers/sshd_config/lsp" sshdconfig "config-lsp/handlers/sshd_config/lsp"
wireguard "config-lsp/handlers/wireguard/lsp" wireguard "config-lsp/handlers/wireguard/lsp"
"config-lsp/root-handler/shared"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) { func TextDocumentCompletion(context *glsp.Context, params *protocol.CompletionParams) (any, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) language := shared.Handler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil { if language == nil {
showParseError( return nil, nil
context,
params.TextDocument.URI,
undetectableError,
)
return nil, undetectableError.Err
} }
switch *language { switch *language {
case LanguageFstab: case shared.LanguageFstab:
return fstab.TextDocumentCompletion(context, params) return fstab.TextDocumentCompletion(context, params)
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return sshdconfig.TextDocumentCompletion(context, params) return sshdconfig.TextDocumentCompletion(context, params)
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentCompletion(context, params) return sshconfig.TextDocumentCompletion(context, params)
case LanguageWireguard: case shared.LanguageWireguard:
return wireguard.TextDocumentCompletion(context, params) return wireguard.TextDocumentCompletion(context, params)
case LanguageHosts: case shared.LanguageHosts:
return hosts.TextDocumentCompletion(context, params) return hosts.TextDocumentCompletion(context, params)
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentCompletion(context, params) return aliases.TextDocumentCompletion(context, params)
} }

View File

@ -1,39 +1,35 @@
package roothandler package lsp
import ( import (
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"
sshconfig "config-lsp/handlers/ssh_config/lsp" sshconfig "config-lsp/handlers/ssh_config/lsp"
sshdconfig "config-lsp/handlers/sshd_config/lsp" sshdconfig "config-lsp/handlers/sshd_config/lsp"
"config-lsp/root-handler/shared"
"config-lsp/root-handler/utils"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
func TextDocumentDefinition(context *glsp.Context, params *protocol.DefinitionParams) (any, error) { func TextDocumentDefinition(context *glsp.Context, params *protocol.DefinitionParams) (any, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) language := shared.Handler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil { if language == nil {
showParseError( return nil, utils.LanguageUndetectableError{}
context,
params.TextDocument.URI,
undetectableError,
)
return nil, undetectableError.Err
} }
switch *language { switch *language {
case LanguageHosts: case shared.LanguageHosts:
return nil, nil return nil, nil
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return sshdconfig.TextDocumentDefinition(context, params) return sshdconfig.TextDocumentDefinition(context, params)
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentDefinition(context, params) return sshconfig.TextDocumentDefinition(context, params)
case LanguageFstab: case shared.LanguageFstab:
return nil, nil return nil, nil
case LanguageWireguard: case shared.LanguageWireguard:
return nil, nil return nil, nil
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentDefinition(context, params) return aliases.TextDocumentDefinition(context, params)
} }

View File

@ -1,4 +1,4 @@
package roothandler package lsp
import ( import (
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"
@ -7,27 +7,27 @@ import (
sshconfig "config-lsp/handlers/ssh_config/lsp" sshconfig "config-lsp/handlers/ssh_config/lsp"
sshdconfig "config-lsp/handlers/sshd_config/lsp" sshdconfig "config-lsp/handlers/sshd_config/lsp"
wireguard "config-lsp/handlers/wireguard/lsp" wireguard "config-lsp/handlers/wireguard/lsp"
"config-lsp/root-handler/shared"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
func TextDocumentDidChange(context *glsp.Context, params *protocol.DidChangeTextDocumentParams) error { func TextDocumentDidChange(context *glsp.Context, params *protocol.DidChangeTextDocumentParams) error {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) language := shared.Handler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil { content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text
content := params.ContentChanges[0].(protocol.TextDocumentContentChangeEventWhole).Text newLanguage, err := initFile(
newLanguage, err := initFile( context,
context, content,
content, params.TextDocument.URI,
params.TextDocument.URI, "",
"", )
)
if err != nil { if err != nil {
return err return err
} }
if newLanguage != language {
language = newLanguage language = newLanguage
params := &protocol.DidOpenTextDocumentParams{ params := &protocol.DidOpenTextDocumentParams{
@ -40,33 +40,33 @@ func TextDocumentDidChange(context *glsp.Context, params *protocol.DidChangeText
} }
switch *language { switch *language {
case LanguageFstab: case shared.LanguageFstab:
return fstab.TextDocumentDidOpen(context, params) return fstab.TextDocumentDidOpen(context, params)
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return sshdconfig.TextDocumentDidOpen(context, params) return sshdconfig.TextDocumentDidOpen(context, params)
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentDidOpen(context, params) return sshconfig.TextDocumentDidOpen(context, params)
case LanguageWireguard: case shared.LanguageWireguard:
return wireguard.TextDocumentDidOpen(context, params) return wireguard.TextDocumentDidOpen(context, params)
case LanguageHosts: case shared.LanguageHosts:
return hosts.TextDocumentDidOpen(context, params) return hosts.TextDocumentDidOpen(context, params)
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentDidOpen(context, params) return aliases.TextDocumentDidOpen(context, params)
} }
} }
switch *language { switch *language {
case LanguageFstab: case shared.LanguageFstab:
return fstab.TextDocumentDidChange(context, params) return fstab.TextDocumentDidChange(context, params)
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return sshdconfig.TextDocumentDidChange(context, params) return sshdconfig.TextDocumentDidChange(context, params)
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentDidChange(context, params) return sshconfig.TextDocumentDidChange(context, params)
case LanguageWireguard: case shared.LanguageWireguard:
return wireguard.TextDocumentDidChange(context, params) return wireguard.TextDocumentDidChange(context, params)
case LanguageHosts: case shared.LanguageHosts:
return hosts.TextDocumentDidChange(context, params) return hosts.TextDocumentDidChange(context, params)
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentDidChange(context, params) return aliases.TextDocumentDidChange(context, params)
} }

View File

@ -1,4 +1,4 @@
package roothandler package lsp
import ( import (
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"
@ -7,39 +7,34 @@ import (
sshconfig "config-lsp/handlers/ssh_config/lsp" sshconfig "config-lsp/handlers/ssh_config/lsp"
sshdconfig "config-lsp/handlers/sshd_config/lsp" sshdconfig "config-lsp/handlers/sshd_config/lsp"
wireguard "config-lsp/handlers/wireguard/lsp" wireguard "config-lsp/handlers/wireguard/lsp"
"config-lsp/root-handler/shared"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error { func TextDocumentDidClose(context *glsp.Context, params *protocol.DidCloseTextDocumentParams) error {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) language := shared.Handler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil { if language == nil {
showParseError( return nil
context,
params.TextDocument.URI,
undetectableError,
)
return undetectableError.Err
} }
delete(openedFiles, params.TextDocument.URI) delete(shared.OpenedFiles, params.TextDocument.URI)
rootHandler.RemoveDocument(params.TextDocument.URI) delete(shared.LanguagesOverwrites, params.TextDocument.URI)
shared.Handler.RemoveDocument(params.TextDocument.URI)
switch *language { switch *language {
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return sshdconfig.TextDocumentDidClose(context, params) return sshdconfig.TextDocumentDidClose(context, params)
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentDidClose(context, params) return sshconfig.TextDocumentDidClose(context, params)
case LanguageFstab: case shared.LanguageFstab:
return fstab.TextDocumentDidClose(context, params) return fstab.TextDocumentDidClose(context, params)
case LanguageWireguard: case shared.LanguageWireguard:
return wireguard.TextDocumentDidClose(context, params) return wireguard.TextDocumentDidClose(context, params)
case LanguageHosts: case shared.LanguageHosts:
return hosts.TextDocumentDidClose(context, params) return hosts.TextDocumentDidClose(context, params)
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentDidClose(context, params) return aliases.TextDocumentDidClose(context, params)
default: default:
} }

View File

@ -1,7 +1,9 @@
package roothandler package lsp
import ( import (
"config-lsp/common" "config-lsp/common"
"config-lsp/root-handler/shared"
"config-lsp/root-handler/utils"
"fmt" "fmt"
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"
@ -32,60 +34,43 @@ func TextDocumentDidOpen(context *glsp.Context, params *protocol.DidOpenTextDocu
} }
switch *language { switch *language {
case LanguageFstab: case shared.LanguageFstab:
return fstab.TextDocumentDidOpen(context, params) return fstab.TextDocumentDidOpen(context, params)
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return sshdconfig.TextDocumentDidOpen(context, params) return sshdconfig.TextDocumentDidOpen(context, params)
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentDidOpen(context, params) return sshconfig.TextDocumentDidOpen(context, params)
case LanguageWireguard: case shared.LanguageWireguard:
return wireguard.TextDocumentDidOpen(context, params) return wireguard.TextDocumentDidOpen(context, params)
case LanguageHosts: case shared.LanguageHosts:
return hosts.TextDocumentDidOpen(context, params) return hosts.TextDocumentDidOpen(context, params)
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentDidOpen(context, params) return aliases.TextDocumentDidOpen(context, params)
} }
panic(fmt.Sprintf("unexpected roothandler.SupportedLanguage: %#v", language)) panic(fmt.Sprintf("unexpected roothandler.SupportedLanguage: %#v", language))
} }
func showParseError(
context *glsp.Context,
uri protocol.DocumentUri,
err common.ParseError,
) {
context.Notify(
"window/showMessage",
protocol.ShowMessageParams{
Type: protocol.MessageTypeError,
Message: err.Err.Error(),
},
)
}
func initFile( func initFile(
context *glsp.Context, context *glsp.Context,
content string, content string,
uri protocol.DocumentUri, uri protocol.DocumentUri,
advertisedLanguage string, advertisedLanguage string,
) (*SupportedLanguage, error) { ) (*shared.SupportedLanguage, error) {
language, err := DetectLanguage(content, advertisedLanguage, uri) language, err := utils.DetectLanguage(content, advertisedLanguage, uri)
if err != nil { if err != nil {
parseError := err.(common.ParseError) utils.NotifyLanguageUndetectable(context, uri)
showParseError(
context,
uri,
parseError,
)
return nil, parseError.Err return nil, utils.LanguageUndetectableError{}
} else {
utils.NotifyDetectedLanguage(context, uri, language)
} }
openedFiles[uri] = struct{}{} shared.OpenedFiles[uri] = struct{}{}
// Everything okay, now we can handle the file // Everything okay, now we can handle the file
rootHandler.AddDocument(uri, language) shared.Handler.AddDocument(uri, language)
return &language, nil return &language, nil
} }

View File

@ -1,4 +1,4 @@
package roothandler package lsp
import ( import (
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"
@ -7,36 +7,30 @@ import (
sshconfig "config-lsp/handlers/ssh_config/lsp" sshconfig "config-lsp/handlers/ssh_config/lsp"
sshdconfig "config-lsp/handlers/sshd_config/lsp" sshdconfig "config-lsp/handlers/sshd_config/lsp"
wireguard "config-lsp/handlers/wireguard/lsp" wireguard "config-lsp/handlers/wireguard/lsp"
"config-lsp/root-handler/shared"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*protocol.Hover, error) { func TextDocumentHover(context *glsp.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) language := shared.Handler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil { if language == nil {
showParseError( return nil, nil
context,
params.TextDocument.URI,
undetectableError,
)
return nil, undetectableError.Err
} }
switch *language { switch *language {
case LanguageHosts: case shared.LanguageHosts:
return hosts.TextDocumentHover(context, params) return hosts.TextDocumentHover(context, params)
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return sshdconfig.TextDocumentHover(context, params) return sshdconfig.TextDocumentHover(context, params)
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentHover(context, params) return sshconfig.TextDocumentHover(context, params)
case LanguageFstab: case shared.LanguageFstab:
return fstab.TextDocumentHover(context, params) return fstab.TextDocumentHover(context, params)
case LanguageWireguard: case shared.LanguageWireguard:
return wireguard.TextDocumentHover(context, params) return wireguard.TextDocumentHover(context, params)
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentHover(context, params) return aliases.TextDocumentHover(context, params)
} }

View File

@ -1,8 +1,10 @@
package roothandler package lsp
import ( import (
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"
sshconfig "config-lsp/handlers/ssh_config/lsp" sshconfig "config-lsp/handlers/ssh_config/lsp"
"config-lsp/root-handler/shared"
"config-lsp/root-handler/utils"
"github.com/tliron/glsp" "github.com/tliron/glsp"
@ -10,30 +12,24 @@ import (
) )
func TextDocumentPrepareRename(context *glsp.Context, params *protocol.PrepareRenameParams) (any, error) { func TextDocumentPrepareRename(context *glsp.Context, params *protocol.PrepareRenameParams) (any, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) language := shared.Handler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil { if language == nil {
showParseError( return nil, utils.LanguageUndetectableError{}
context,
params.TextDocument.URI,
undetectableError,
)
return nil, undetectableError.Err
} }
switch *language { switch *language {
case LanguageHosts: case shared.LanguageHosts:
return nil, nil return nil, nil
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return nil, nil return nil, nil
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentPrepareRename(context, params) return sshconfig.TextDocumentPrepareRename(context, params)
case LanguageFstab: case shared.LanguageFstab:
return nil, nil return nil, nil
case LanguageWireguard: case shared.LanguageWireguard:
return nil, nil return nil, nil
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentPrepareRename(context, params) return aliases.TextDocumentPrepareRename(context, params)
} }

View File

@ -1,8 +1,10 @@
package roothandler package lsp
import ( import (
sshconfig "config-lsp/handlers/ssh_config/lsp" sshconfig "config-lsp/handlers/ssh_config/lsp"
sshdconfig "config-lsp/handlers/sshd_config/lsp" sshdconfig "config-lsp/handlers/sshd_config/lsp"
"config-lsp/root-handler/shared"
"config-lsp/root-handler/utils"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
@ -12,30 +14,24 @@ func TextDocumentRangeFormattingFunc(
context *glsp.Context, context *glsp.Context,
params *protocol.DocumentRangeFormattingParams, params *protocol.DocumentRangeFormattingParams,
) ([]protocol.TextEdit, error) { ) ([]protocol.TextEdit, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) language := shared.Handler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil { if language == nil {
showParseError( return nil, utils.LanguageUndetectableError{}
context,
params.TextDocument.URI,
undetectableError,
)
return nil, undetectableError.Err
} }
switch *language { switch *language {
case LanguageHosts: case shared.LanguageHosts:
return nil, nil return nil, nil
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return sshdconfig.TextDocumentRangeFormatting(context, params) return sshdconfig.TextDocumentRangeFormatting(context, params)
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentRangeFormatting(context, params) return sshconfig.TextDocumentRangeFormatting(context, params)
case LanguageFstab: case shared.LanguageFstab:
return nil, nil return nil, nil
case LanguageWireguard: case shared.LanguageWireguard:
return nil, nil return nil, nil
case LanguageAliases: case shared.LanguageAliases:
return nil, nil return nil, nil
} }

View File

@ -1,38 +1,34 @@
package roothandler package lsp
import ( import (
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"
sshconfig "config-lsp/handlers/ssh_config/lsp" sshconfig "config-lsp/handlers/ssh_config/lsp"
"config-lsp/root-handler/shared"
"config-lsp/root-handler/utils"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
func TextDocumentRename(context *glsp.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) { func TextDocumentRename(context *glsp.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) language := shared.Handler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil { if language == nil {
showParseError( return nil, utils.LanguageUndetectableError{}
context,
params.TextDocument.URI,
undetectableError,
)
return nil, undetectableError.Err
} }
switch *language { switch *language {
case LanguageHosts: case shared.LanguageHosts:
return nil, nil return nil, nil
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return nil, nil return nil, nil
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentRename(context, params) return sshconfig.TextDocumentRename(context, params)
case LanguageFstab: case shared.LanguageFstab:
return nil, nil return nil, nil
case LanguageWireguard: case shared.LanguageWireguard:
return nil, nil return nil, nil
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentRename(context, params) return aliases.TextDocumentRename(context, params)
} }

View File

@ -1,39 +1,33 @@
package roothandler package lsp
import ( import (
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"
sshconfig "config-lsp/handlers/ssh_config/lsp" sshconfig "config-lsp/handlers/ssh_config/lsp"
sshdconfig "config-lsp/handlers/sshd_config/lsp" sshdconfig "config-lsp/handlers/sshd_config/lsp"
"config-lsp/root-handler/shared"
"github.com/tliron/glsp" "github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
func TextDocumentSignatureHelp(context *glsp.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) { func TextDocumentSignatureHelp(context *glsp.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) {
language := rootHandler.GetLanguageForDocument(params.TextDocument.URI) language := shared.Handler.GetLanguageForDocument(params.TextDocument.URI)
if language == nil { if language == nil {
showParseError( return nil, nil
context,
params.TextDocument.URI,
undetectableError,
)
return nil, undetectableError.Err
} }
switch *language { switch *language {
case LanguageHosts: case shared.LanguageHosts:
return nil, nil return nil, nil
case LanguageSSHDConfig: case shared.LanguageSSHDConfig:
return sshdconfig.TextDocumentSignatureHelp(context, params) return sshdconfig.TextDocumentSignatureHelp(context, params)
case LanguageSSHConfig: case shared.LanguageSSHConfig:
return sshconfig.TextDocumentSignatureHelp(context, params) return sshconfig.TextDocumentSignatureHelp(context, params)
case LanguageFstab: case shared.LanguageFstab:
return nil, nil return nil, nil
case LanguageWireguard: case shared.LanguageWireguard:
return nil, nil return nil, nil
case LanguageAliases: case shared.LanguageAliases:
return aliases.TextDocumentSignatureHelp(context, params) return aliases.TextDocumentSignatureHelp(context, params)
} }

View File

@ -1,4 +1,4 @@
package roothandler package lsp
import ( import (
aliases "config-lsp/handlers/aliases/lsp" aliases "config-lsp/handlers/aliases/lsp"

View File

@ -1,5 +0,0 @@
package roothandler
import protocol "github.com/tliron/glsp/protocol_3_16"
var openedFiles = make(map[protocol.DocumentUri]struct{})

View File

@ -0,0 +1,21 @@
package shared
type SupportedLanguage string
const (
LanguageSSHConfig SupportedLanguage = "ssh_config"
LanguageSSHDConfig SupportedLanguage = "sshd_config"
LanguageFstab SupportedLanguage = "fstab"
LanguageWireguard SupportedLanguage = "languagewireguard"
LanguageHosts SupportedLanguage = "hosts"
LanguageAliases SupportedLanguage = "aliases"
)
var AllSupportedLanguages = []string{
string(LanguageSSHConfig),
string(LanguageSSHDConfig),
string(LanguageFstab),
string(LanguageWireguard),
string(LanguageHosts),
string(LanguageAliases),
}

View File

@ -0,0 +1,16 @@
package shared
import (
protocol "github.com/tliron/glsp/protocol_3_16"
)
type LanguageOverwrite struct {
Language SupportedLanguage
// The start of the overwrite
Raw string
Line uint32
Character uint32
}
var LanguagesOverwrites = map[protocol.DocumentUri]LanguageOverwrite{}

View File

@ -1,10 +1,12 @@
package roothandler package shared
import ( import (
protocol "github.com/tliron/glsp/protocol_3_16" protocol "github.com/tliron/glsp/protocol_3_16"
) )
var rootHandler RootHandler var Handler RootHandler
var OpenedFiles = make(map[protocol.DocumentUri]struct{})
type RootHandler struct { type RootHandler struct {
languageMap map[protocol.DocumentUri]SupportedLanguage languageMap map[protocol.DocumentUri]SupportedLanguage

View File

@ -0,0 +1,44 @@
package utils
import (
"config-lsp/root-handler/shared"
"fmt"
protocol "github.com/tliron/glsp/protocol_3_16"
)
func FetchAddLanguageActions(uri protocol.DocumentUri) ([]protocol.CodeAction, error) {
actions := make([]protocol.CodeAction, 0, len(shared.AllSupportedLanguages))
kind := protocol.CodeActionKindQuickFix
isPreferred := true
for _, language := range shared.AllSupportedLanguages {
actions = append(actions, protocol.CodeAction{
Title: fmt.Sprintf("Use %s for this file", language),
Kind: &kind,
IsPreferred: &isPreferred,
Edit: &protocol.WorkspaceEdit{
Changes: map[protocol.DocumentUri][]protocol.TextEdit{
uri: {
{
Range: protocol.Range{
Start: protocol.Position{
Line: 0,
Character: 0,
},
End: protocol.Position{
Line: 0,
Character: 0,
},
},
NewText: fmt.Sprintf("#?lsp.language=%s\n", language),
},
},
},
},
})
}
return actions, nil
}

View File

@ -0,0 +1,158 @@
package utils
import (
"config-lsp/common"
"config-lsp/root-handler/shared"
"config-lsp/utils"
"fmt"
"path"
"regexp"
"strings"
protocol "github.com/tliron/glsp/protocol_3_16"
)
type UnsupportedLanguageError struct {
SuggestedLanguage string
}
func (e UnsupportedLanguageError) Error() string {
return fmt.Sprintf("Language '%s' is not supported. Choose one of: %s", e.SuggestedLanguage, strings.Join(shared.AllSupportedLanguages, ", "))
}
type LanguageUndetectableError struct{}
func (e LanguageUndetectableError) Error() string {
return "Please add: '#?lsp.language=<language>' to the top of the file. config-lsp was unable to detect the appropriate language for this file."
}
var valueToLanguageMap = map[string]shared.SupportedLanguage{
"sshd_config": shared.LanguageSSHDConfig,
"sshdconfig": shared.LanguageSSHDConfig,
"ssh_config": shared.LanguageSSHConfig,
"sshconfig": shared.LanguageSSHConfig,
".ssh/config": shared.LanguageSSHConfig,
"~/.ssh/config": shared.LanguageSSHConfig,
"fstab": shared.LanguageFstab,
"etc/fstab": shared.LanguageFstab,
"wireguard": shared.LanguageWireguard,
"wg": shared.LanguageWireguard,
"languagewireguard": shared.LanguageWireguard,
"host": shared.LanguageHosts,
"hosts": shared.LanguageHosts,
"etc/hosts": shared.LanguageHosts,
"aliases": shared.LanguageAliases,
"mailaliases": shared.LanguageAliases,
"etc/aliases": shared.LanguageAliases,
}
var filenameToLanguageMap = map[string]shared.SupportedLanguage{
"sshd_config": shared.LanguageSSHDConfig,
"sshdconfig": shared.LanguageSSHDConfig,
"sshd": shared.LanguageSSHDConfig,
"sshd_conf": shared.LanguageSSHDConfig,
"sshdconf": shared.LanguageSSHDConfig,
"ssh_config": shared.LanguageSSHConfig,
"sshconfig": shared.LanguageSSHConfig,
"ssh": shared.LanguageSSHConfig,
"ssh_conf": shared.LanguageSSHConfig,
"sshconf": shared.LanguageSSHConfig,
"fstab": shared.LanguageFstab,
"hosts": shared.LanguageHosts,
"aliases": shared.LanguageAliases,
"mailaliases": shared.LanguageAliases,
}
var typeOverwriteRegex = regexp.MustCompile(`#\?\s*lsp\.language\s*=\s*(\w+)\s*`)
var wireguardPattern = regexp.MustCompile(`wg(\d+)?(\.conf)?$`)
var undetectableError = common.ParseError{
Line: 0,
Err: LanguageUndetectableError{},
}
func DetectLanguage(
content string,
advertisedLanguage string,
uri protocol.DocumentUri,
) (shared.SupportedLanguage, error) {
if match := typeOverwriteRegex.FindStringSubmatchIndex(content); match != nil {
text := content[match[0]:match[1]]
language := content[match[2]:match[3]]
suggestedLanguage := strings.ToLower(language)
foundLanguage, ok := valueToLanguageMap[suggestedLanguage]
contentUntilMatch := content[:match[0]]
if ok {
line := uint32(utils.CountCharacterOccurrences(contentUntilMatch, '\n'))
shared.LanguagesOverwrites[uri] = shared.LanguageOverwrite{
Language: foundLanguage,
Raw: text,
Line: line,
Character: uint32(match[0]),
}
return foundLanguage, nil
}
return "", common.ParseError{
Line: uint32(utils.CountCharacterOccurrences(contentUntilMatch, '\n')),
Err: UnsupportedLanguageError{
SuggestedLanguage: suggestedLanguage,
},
}
}
if language, ok := valueToLanguageMap[advertisedLanguage]; ok {
return language, nil
}
switch uri {
case "file:///etc/ssh/sshd_config":
fallthrough
case "file:///etc/ssh/ssh_config":
return shared.LanguageSSHDConfig, nil
case "file:///etc/fstab":
return shared.LanguageFstab, nil
// Darwin
case "file:///private/etc/hosts":
fallthrough
case "file:///etc/hosts":
return shared.LanguageHosts, nil
// Darwin
case "file:///private/etc/aliases":
fallthrough
case "file:///etc/aliases":
return shared.LanguageAliases, nil
}
filename := path.Base(string(uri))
if language, found := filenameToLanguageMap[filename]; found {
return language, nil
}
if strings.HasPrefix(uri, "file:///etc/wireguard/") || wireguardPattern.MatchString(uri) {
return shared.LanguageWireguard, nil
}
if strings.HasSuffix(uri, ".ssh/config") {
return shared.LanguageSSHConfig, nil
}
return "", undetectableError
}

View File

@ -0,0 +1,46 @@
package utils
import (
"config-lsp/root-handler/shared"
"github.com/tliron/glsp"
protocol "github.com/tliron/glsp/protocol_3_16"
)
type lspNotification struct {
Uri string
}
type lspDetectedLanguage struct {
lspNotification
Language string
}
func NotifyLanguageUndetectable(context *glsp.Context, uri protocol.DocumentUri) {
go context.Notify(
"$/config-lsp/languageUndetectable",
lspNotification{
Uri: string(uri),
},
)
go context.Notify(
"window/showMessage",
protocol.ShowMessageParams{
Type: protocol.MessageTypeError,
Message: "config-lsp was unable to detect the appropriate language for this file. Please add: '#?lsp.language=<language>'.",
},
)
}
func NotifyDetectedLanguage(context *glsp.Context, uri protocol.DocumentUri, language shared.SupportedLanguage) {
go context.Notify(
"$/config-lsp/detectedLanguage",
lspDetectedLanguage{
lspNotification: lspNotification{
Uri: string(uri),
},
Language: string(language),
},
)
}

View File

@ -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.1.1", "version": "0.1.2",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/Myzel394/config-lsp" "url": "https://github.com/Myzel394/config-lsp"
@ -13,6 +13,62 @@
"Formatters" "Formatters"
], ],
"preview": true, "preview": true,
"contributes": {
"languages": [
{
"id": "sshconfig",
"extensions": ["sshconfig", "ssh_config"],
"aliases": ["SSH Config", "sshconfig"],
"filenames": ["sshconfig", "ssh_config"],
"filenamePatterns": ["~/.ssh/config", "**/sshconfig", "**/ssh_config"]
},
{
"id": "sshdconfig",
"extensions": ["sshdconfig", "sshd_config"],
"aliases": ["SSH Daemon Config", "sshdconfig"],
"filenames": ["sshdconfig", "sshd_config"],
"filenamePatterns": ["/etc/ssh/sshd_config", "**/sshdconfig", "**/sshd_config"]
},
{
"id": "aliases",
"extensions": ["aliases", "mailaliases"],
"aliases": ["Mail Aliases", "aliases", "mailaliases"],
"filenames": ["aliases", "mailaliases"],
"filenamePatterns": ["/etc/aliases", "**/aliases", "**/mailaliases"]
},
{
"id": "fstab",
"extensions": ["fstab"],
"aliases": ["fstab"],
"filenames": ["fstab"],
"filenamePatterns": ["/etc/fstab", "**/fstab", "**/etc/fstab"]
},
{
"id": "hosts",
"extensions": ["hosts"],
"aliases": ["hosts"],
"filenames": ["hosts"],
"filenamePatterns": ["/etc/hosts", "**/hosts", "**/etc/hosts"]
},
{
"id": "wireguard",
"extensions": ["wireguard", "wg"],
"aliases": ["WireGuard", "wireguard", "wg"],
"filenames": ["wireguard", "wg0.conf", "wg1.conf", "wg0", "wg1"],
"filenamePatterns": ["/etc/wireguard/*.conf", "**/wireguard", "**/wireguard.conf"]
}
]
},
"activationEvents": [
"onLanguage:plaintext",
"onLanguage:yaml",
"onLanguage:sshconfig",
"onLanguage:sshdconfig",
"onLanguage:aliases",
"onLanguage:fstab",
"onLanguage:hosts",
"onLanguage:wireguard"
],
"sponsor": { "sponsor": {
"url": "https://github.com/Myzel394/contact-me" "url": "https://github.com/Myzel394/contact-me"
}, },
@ -50,16 +106,12 @@
"engines": { "engines": {
"vscode": "^1.74.0" "vscode": "^1.74.0"
}, },
"activationEvents": [
"onLanguage:plaintext",
"onLanguage:yaml"
],
"main": "./out/extension.js", "main": "./out/extension.js",
"scripts": { "scripts": {
"compile": "node esbuild.js", "compile": "node esbuild.js",
"compile:prod": "node esbuild.js --production", "compile:prod": "node esbuild.js --production",
"watch": "tsc -b -w", "watch": "tsc -b -w",
"lint": "eslint ./src --ext .ts,.tsx" "lint": "eslint ./src"
}, },
"dependencies": { "dependencies": {
"vscode-languageclient": "^9.0.1", "vscode-languageclient": "^9.0.1",

View File

@ -0,0 +1,34 @@
import { GenericNotificationHandler } from "vscode-languageclient";
import * as vscode from "vscode";
const ACTION_SELECT_LANGUAGE = "Select Language";
const ACTION_DISABLE = "Ignore for this file";
const ignoredFiled = new Set<string>();
export const onUndetectable: GenericNotificationHandler = async (params: LSPLanguageUndetectable) => {
if (ignoredFiled.has(params.Uri)) {
return;
}
const result = await vscode.window.showWarningMessage(
"config-lsp was unable to detect the appropriate language for this file",
{
detail: "Either select a language or add '#?lsp.language=<language>' to the top of the file",
},
ACTION_SELECT_LANGUAGE,
ACTION_DISABLE,
)
switch (result) {
case ACTION_SELECT_LANGUAGE:
vscode.commands.executeCommand("workbench.action.editor.changeLanguageMode");
break;
case ACTION_DISABLE:
ignoredFiled.add(params.Uri);
break;
undefined:
break;
}
}

View File

@ -0,0 +1,6 @@
interface LSPNotification {
Uri: string;
}
interface LSPLanguageUndetectable extends LSPNotification {}

View File

@ -1,32 +1,30 @@
import * as path from "path" import * as path from "path";
import { ExtensionContext, workspace } from 'vscode'; import { ExtensionContext, workspace } from "vscode";
import { import {
Executable, Executable,
LanguageClient, LanguageClient,
type LanguageClientOptions, type LanguageClientOptions,
type ServerOptions, type ServerOptions,
} from 'vscode-languageclient/node'; } from "vscode-languageclient/node";
import { onUndetectable } from "./events/on-undetectable";
const IS_DEBUG = process.env.VSCODE_DEBUG_MODE === 'true' || process.env.NODE_ENV === 'development'; const IS_DEBUG =
process.env.VSCODE_DEBUG_MODE === "true" ||
process.env.NODE_ENV === "development";
let client: LanguageClient; let client: LanguageClient;
export function activate(context: ExtensionContext) { export async function activate({subscriptions}: ExtensionContext) {
console.info("config-lsp activated"); console.info("config-lsp activated");
const initOptions = workspace.getConfiguration('config-lsp'); const initOptions = workspace.getConfiguration("config-lsp");
const clientOptions: LanguageClientOptions = { const clientOptions: LanguageClientOptions = {
documentSelector: [ documentSelector: [
{ {language: "sshconfig"},
scheme: 'file', {language: "sshdconfig"},
language: 'plaintext', {language: "aliases"},
pattern: "**/{config,sshconfig,sshd_config,sshdconfig,fstab,hosts,aliases}", {language: "fstab"},
}, {language: "hosts"},
// Some configs seem to be incorrectly detected as yaml {language: "wireguard"},
{
scheme: 'file',
language: 'yaml',
pattern: "**/{config,sshconfig,sshd_config,sshdconfig,fstab,hosts,aliases}",
},
], ],
initializationOptions: initOptions, initializationOptions: initOptions,
}; };
@ -34,39 +32,29 @@ export function activate(context: ExtensionContext) {
const path = getBundledPath(); const path = getBundledPath();
console.info(`Found config-lsp path at ${path}`); console.info(`Found config-lsp path at ${path}`);
const run: Executable = { const run: Executable = {
command: getBundledPath(), command: getBundledPath() ,
} };
const serverOptions: ServerOptions = { const serverOptions: ServerOptions = {
run, run,
debug: run, debug: run,
} };
client = new LanguageClient( client = new LanguageClient(
'config-lsp', "config-lsp",
serverOptions, serverOptions,
clientOptions, clientOptions,
IS_DEBUG, IS_DEBUG
); );
console.info("Starting config-lsp...");
await client.start();
console.info("Started config-lsp");
client.start(); subscriptions.push(client.onNotification("$/config-lsp/languageUndetectable", onUndetectable))
console.info("config-lsp started");
// const serverOptions: ServerOptions = {
// }
//
// // Create the language client and start the client.
// client = new LanguageClient(
// 'languageServerExample',
// clientOptions
// );
//
// // Start the client. This will also launch the server
// client.start();
} }
function getBundledPath(): string { function getBundledPath(): string {
const filePath = path.resolve(__dirname, "config-lsp") const filePath = path.resolve(__dirname, "config-lsp");
return filePath; return filePath;
} }