-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: go to definition and find references for document refs
- Loading branch information
Showing
31 changed files
with
3,286 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
# yaml-language-server: $schema=https://json.schemastore.org/github-workflow | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
pull_request: {} | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-go@v5 | ||
with: | ||
go-version: "1.22" | ||
- run: | | ||
go test -cover ./... | ||
lint: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-go@v5 | ||
with: | ||
go-version: "1.22" | ||
- uses: golangci/golangci-lint-action@v4 | ||
with: | ||
version: v1.59.1 | ||
|
||
install: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-go@v5 | ||
with: | ||
go-version: "1.22" | ||
- run: go install . |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
*.out | ||
/openapiv3-lsp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
# See https://golangci-lint.run/usage/configuration/ | ||
|
||
linters: | ||
disable-all: true | ||
enable: | ||
# See https://golangci-lint.run/usage/linters/ | ||
- asasalint # Check for pass []any as any in variadic func(...any). | ||
- bodyclose # Checks whether HTTP response body is closed successfully. | ||
- contextcheck # Check whether the function uses a non-inherited context. | ||
- durationcheck # Check for two durations multiplied together. | ||
- errcheck # Checks whether Rows.Err of rows is checked successfully. | ||
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and reports occations, where the check for the returned error can be omitted. | ||
- errorlint # Errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. | ||
- forbidigo # Forbids identifiers. | ||
- gci # Gci controls Go package import order and makes it always deterministic. | ||
- gocritic # Provides diagnostics that check for bugs, performance and style issues. Extensible without recompilation through dynamic rules. Dynamic rules are written declaratively with AST patterns, filters, report message and optional suggestion. | ||
- godot # Check if comments end in a period. | ||
- gosec # Inspects source code for security problems. | ||
- gosimple # Linter for Go source code that specializes in simplifying code. | ||
- govet # Vet examines Go source code and reports suspicious constructs, such as Printf calls whose arguments do not align with the format string. | ||
- inamedparam # Reports interfaces with unnamed method parameters. | ||
- ineffassign # Detects when assignments to existing variables are not used. | ||
- mirror # Reports wrong mirror patterns of bytes/strings usage. | ||
- musttag # Enforce field tags in (un)marshaled structs. | ||
- nilerr # Finds the code that returns nil even if it checks that the error is not nil. | ||
- nilnil # Checks that there is no simultaneous return of nil error and an invalid value. | ||
- noctx # Finds sending http request without context.Context. | ||
- nolintlint # Reports ill-formed or insufficient nolint directives. | ||
- nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL. | ||
- perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. | ||
- protogetter # Reports direct reads from proto message fields when getters should be used. | ||
- reassign # Checks that package variables are not reassigned. | ||
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. | ||
- staticcheck # It's a set of rules from staticcheck. It's not the same thing as the staticcheck binary. The author of staticcheck doesn't support or approve the use of staticcheck as a library inside golangci-lint. | ||
- tenv # # Tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17. | ||
- unconvert # Remove unnecessary type conversions. | ||
- unused # Checks Go code for unused constants, variables, functions and types. | ||
|
||
linters-settings: | ||
# See https://golangci-lint.run/usage/linters/#linters-configuration | ||
forbidigo: | ||
forbid: | ||
- 'fmt\.Print.*' # Should be using a logger | ||
gci: | ||
sections: | ||
- standard | ||
- default | ||
- prefix(github.com/armsnyder) | ||
gocritic: | ||
enabled-tags: | ||
- performance | ||
- opinionated | ||
- experimental | ||
disabled-checks: | ||
- whyNoLint # False positives, use nolintlint instead | ||
govet: | ||
enable-all: true | ||
disable: | ||
- fieldalignment # Too struct | ||
nolintlint: | ||
require-specific: true | ||
revive: | ||
enable-all-rules: true | ||
rules: | ||
# See https://revive.run/r | ||
- name: add-constant # too strict | ||
disabled: true | ||
- name: argument-limit # too strict | ||
disabled: true | ||
- name: cognitive-complexity | ||
arguments: | ||
- 30 | ||
- name: cyclomatic | ||
arguments: | ||
- 30 | ||
- name: file-header # too strict | ||
disabled: true | ||
- name: function-length | ||
arguments: | ||
- 50 # statements | ||
- 0 # lines (0 to disable) | ||
- name: function-result-limit # too strict | ||
disabled: true | ||
- name: import-shadowing # too strict, results in uglier code | ||
disabled: true | ||
- name: line-length-limit # too strict | ||
disabled: true | ||
- name: max-public-structs # too strict | ||
disabled: true | ||
- name: modifies-parameter # too strict | ||
disabled: true | ||
- name: modifies-value-receiver # too strict | ||
disabled: true | ||
- name: nested-structs # too strict | ||
disabled: true | ||
- name: package-comments # too strict | ||
disabled: true | ||
- name: unhandled-error | ||
disabled: true # not as good as errcheck | ||
|
||
issues: | ||
exclude-rules: | ||
- path: _test\.go$ | ||
linters: | ||
- gosec # too strict | ||
- noctx # too strict | ||
- path: _test\.go$ | ||
text: (cognitive-complexity|function-length|dot-imports|import-alias-naming) # too strict | ||
linters: | ||
- revive | ||
# main.go is allowed to contain early bootstrapping print statements. | ||
# TestMain is allowed to log. | ||
- path: \/main(_test)?\.go$ | ||
text: fmt.Print | ||
linters: | ||
- forbidigo | ||
# Shadowing err is common. | ||
- text: 'shadow: declaration of "err"' | ||
linters: | ||
- govet | ||
- text: "^exported:.+stutters" # too strict and gets in the way of combining types like handlers | ||
linters: | ||
- revive | ||
- path: _test\.go$ | ||
text: "unused-parameter" # too strict |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
# OpenAPI Language Server | ||
|
||
This is a language server for OpenAPI v3.0.0. It is based on the [Language | ||
Server Protocol](https://microsoft.github.io/language-server-protocol/). | ||
|
||
I created this language server because I do a lot of manual OpenAPI/Swagger | ||
file editing, and I wanted a quick way to jump to definitions and find | ||
references of schema definitions. | ||
|
||
I personally use | ||
[yaml-language-server](https://github.com/redhat-developer/yaml-language-server) | ||
for schema validation and code completion, so these features are not a priority | ||
for me to implement in this language server. | ||
|
||
## Features | ||
|
||
### Language Features | ||
|
||
- [x] Jump to definition | ||
- [x] Find references | ||
- [ ] Code completion | ||
- [ ] Diagnostics | ||
- [ ] Hover | ||
- [ ] Rename | ||
- [ ] Document symbols | ||
- [ ] Code actions | ||
|
||
### Other Features | ||
|
||
- [x] YAML filetype support | ||
- [ ] JSON filetype support | ||
- [ ] VSCode extension | ||
|
||
## Installation | ||
|
||
```bash | ||
go install github.com/armsnyder/openapiv3-lsp@latest | ||
``` | ||
|
||
### Neovim Configuration Example | ||
|
||
Assuming you are using Neovim and have the installed openapiv3-lsp binary in | ||
your PATH, you can use the following Lua code to your Neovim configuration: | ||
|
||
```lua | ||
vim.api.nvim_create_autocmd('FileType', { | ||
pattern = 'yaml', | ||
callback = function() | ||
vim.lsp.start { | ||
cmd = { 'openapiv3-lsp' }, | ||
filetypes = { 'yaml' }, | ||
} | ||
end, | ||
}) | ||
``` | ||
|
||
This is just a basic working example. You will probably want to further | ||
customize the configuration to your needs. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
package main | ||
|
||
//go:generate go run go.uber.org/mock/[email protected] -source internal/lsp/handler.go -destination internal/lsp/testutil/handler.go -package testutil |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
module github.com/armsnyder/openapiv3-lsp | ||
|
||
go 1.22.4 | ||
|
||
require go.uber.org/mock v0.4.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= | ||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
// Package anaylsis contains the OpenAPI Lanuage Server business logic. | ||
package analysis |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
package analysis | ||
|
||
import ( | ||
"bytes" | ||
"fmt" | ||
|
||
"github.com/armsnyder/openapiv3-lsp/internal/analysis/yaml" | ||
"github.com/armsnyder/openapiv3-lsp/internal/lsp" | ||
"github.com/armsnyder/openapiv3-lsp/internal/lsp/types" | ||
) | ||
|
||
// Handler implements the LSP handler for the OpenAPI Language Server. It | ||
// contains the business logic for the server. | ||
type Handler struct { | ||
lsp.NopHandler | ||
|
||
files map[string]*annotatedFile | ||
} | ||
|
||
type annotatedFile struct { | ||
file lsp.File | ||
document yaml.Document | ||
} | ||
|
||
func (h *Handler) getDocument(uri string) (yaml.Document, error) { | ||
f := h.files[uri] | ||
if f == nil { | ||
return yaml.Document{}, fmt.Errorf("unknown file: %s", uri) | ||
} | ||
|
||
if f.document.Lines == nil { | ||
document, err := yaml.Parse(bytes.NewReader(f.file.Bytes())) | ||
if err != nil { | ||
return yaml.Document{}, err | ||
} | ||
f.document = document | ||
} | ||
|
||
return f.document, nil | ||
} | ||
|
||
func (*Handler) Capabilities() types.ServerCapabilities { | ||
return types.ServerCapabilities{ | ||
TextDocumentSync: types.TextDocumentSyncOptions{ | ||
OpenClose: true, | ||
Change: types.SyncIncremental, | ||
}, | ||
DefinitionProvider: true, | ||
ReferencesProvider: true, | ||
} | ||
} | ||
|
||
func (h *Handler) HandleOpen(params types.DidOpenTextDocumentParams) error { | ||
if h.files == nil { | ||
h.files = make(map[string]*annotatedFile) | ||
} | ||
|
||
var f annotatedFile | ||
|
||
f.file.Reset([]byte(params.TextDocument.Text)) | ||
h.files[params.TextDocument.URI] = &f | ||
|
||
return nil | ||
} | ||
|
||
func (h *Handler) HandleClose(params types.DidCloseTextDocumentParams) error { | ||
delete(h.files, params.TextDocument.URI) | ||
return nil | ||
} | ||
|
||
func (h *Handler) HandleChange(params types.DidChangeTextDocumentParams) error { | ||
f, ok := h.files[params.TextDocument.URI] | ||
if !ok { | ||
return fmt.Errorf("unknown file: %s", params.TextDocument.URI) | ||
} | ||
|
||
for _, change := range params.ContentChanges { | ||
if err := f.file.ApplyChange(change); err != nil { | ||
return err | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (h *Handler) HandleDefinition(params types.DefinitionParams) ([]types.Location, error) { | ||
document, err := h.getDocument(params.TextDocument.URI) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if params.Position.Line >= len(document.Lines) { | ||
return nil, nil | ||
} | ||
|
||
ref := document.Lines[params.Position.Line].Value | ||
|
||
referencedLine := document.Locate(ref) | ||
if referencedLine == nil { | ||
return nil, nil | ||
} | ||
|
||
return []types.Location{{ | ||
URI: params.TextDocument.URI, | ||
Range: referencedLine.KeyRange, | ||
}}, nil | ||
} | ||
|
||
func (h *Handler) HandleReferences(params types.ReferenceParams) ([]types.Location, error) { | ||
document, err := h.getDocument(params.TextDocument.URI) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if params.Position.Line >= len(document.Lines) { | ||
return nil, nil | ||
} | ||
|
||
ref := document.Lines[params.Position.Line].KeyRef() | ||
|
||
var locations []types.Location | ||
|
||
for _, line := range document.Lines { | ||
if line.Value == ref { | ||
locations = append(locations, types.Location{ | ||
URI: params.TextDocument.URI, | ||
Range: line.ValueRange, | ||
}) | ||
} | ||
} | ||
return locations, nil | ||
} | ||
|
||
var _ lsp.Handler = (*Handler)(nil) |
Oops, something went wrong.