Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add kulala.nvim v3 support #11

Merged
merged 1 commit into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 37 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,9 +85,44 @@ kulala-fmt --check --verbose file1.http file2.rest http/*.http
- Checks if the file is formatted and valid
- Removes extraneous newlines
- Makes sure document variables are at the top of the file
- Lowercases all headers
- Puts all metadata right after the headers
- Lowercases all headers (when HTTP/2 or HTTP/3) else it will uppercase the first letter
- Puts all metadata right before the request line
- Ensures all comments are using `#` and not `//`
- Ensures all comments are at the top of the request

So a perfect request would look like this:

```http
@variables1=value1

# This is a comment
# This is another comment
# @someother metatag
# @name REQUEST_NAME_ONE
GET http://localhost:8080/api/v1/health HTTP/1.1
Content-Type: application/json

{
"key": "value"
}
```

or this:

```http
@variables1=value1

# This is a comment
# This is another comment
# @someother metatag
# @name REQUEST_NAME_ONE
GET http://localhost:8080/api/v1/health HTTP/2
content-type: application/json

{
"key": "value"
}
```

If run on all files it also warns when it finds both `.env` and `http-client.env.json`
files in the same directory, because that might cause unexpected behavior.
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/exp v0.0.0-20231006140011-7918f672742d // indirect
golang.org/x/sys v0.20.0 // indirect
golang.org/x/text v0.17.0 // indirect
)
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQz
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
89 changes: 70 additions & 19 deletions internal/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,32 @@ package parser

import (
"os"
"regexp"
"strings"

"github.com/charmbracelet/log"
"github.com/mistweaverco/kulala-fmt/internal/config"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)

type Header struct {
Name string
Value string
}

type Metadata struct {
Name string
Value string
}

type Section struct {
Comments []string
Method string
URL string
Version string
Headers []string
Metadata []string
Headers []Header
Metadata []Metadata
Body string
}

Expand All @@ -24,6 +37,33 @@ type Document struct {
Valid bool
}

var caser = cases.Title(language.Und)
var metaDataRegex = regexp.MustCompile("^# @")

func parseHTTPLine(line string) (method string, url string, version string) {
method = ""
url = ""
version = ""

pattern := `^(GET|POST|PUT|DELETE|PATCH|HEAD|OPTIONS)\s+(\S+)\s*(.*?)(HTTP/\d+\.?\d?)?$`

re := regexp.MustCompile(pattern)

matches := re.FindStringSubmatch(line)

if len(matches) > 0 {
method = matches[1]
url = matches[2]
if matches[4] != "" {
version = matches[4]
} else {
version = "HTTP/1.1" // Default to HTTP/1.1 if not provided
}
}

return method, url, version
}

func isRequestLine(line string) bool {
return strings.HasPrefix(line, "GET") || strings.HasPrefix(line, "POST") || strings.HasPrefix(line, "PUT") || strings.HasPrefix(line, "DELETE")
}
Expand All @@ -37,8 +77,8 @@ func parseSection(section string, document *Document) Section {
Method: "",
URL: "",
Version: "",
Headers: []string{},
Metadata: []string{},
Headers: []Header{},
Metadata: []Metadata{},
Body: "",
}
lines := strings.Split(section, "\n")
Expand All @@ -53,7 +93,13 @@ func parseSection(section string, document *Document) Section {
document.Variables = append(document.Variables, line)
continue
} else if strings.HasPrefix(line, "# @") {
parsedSection.Metadata = append(parsedSection.Metadata, line)
metadata := strings.Split(metaDataRegex.ReplaceAllString(line, ""), " ")
metaDataName := metadata[0]
metaDataValue := strings.Join(metadata[1:], " ")
parsedSection.Metadata = append(parsedSection.Metadata, Metadata{
Name: metaDataName,
Value: metaDataValue,
})
continue
} else if strings.HasPrefix(line, "#") || strings.HasPrefix(line, "//") {
if strings.HasPrefix(line, "//") {
Expand All @@ -64,22 +110,27 @@ func parseSection(section string, document *Document) Section {
parsedSection.Comments = append(parsedSection.Comments, line)
continue
} else if isRequestLine(line) {
splits := strings.Split(line, " ")
parsedSection.Method = splits[0]
parsedSection.URL = splits[1]
if len(splits) > 2 {
parsedSection.Version = splits[2]
}
reqMethod, reqURL, reqVersion := parseHTTPLine(line)
parsedSection.Method = reqMethod
parsedSection.URL = reqURL
parsedSection.Version = reqVersion
in_request = false
in_header = true
continue
} else if in_header {
if strings.Contains(line, ":") {
httpVersion := parsedSection.Version
line = strings.Trim(line, " ")
splits := strings.Split(line, ":")
splits[0] = strings.ToLower(splits[0])
line = strings.Join(splits, ":")
parsedSection.Headers = append(parsedSection.Headers, line)
headerName := strings.ToLower(splits[0])
headerValue := strings.Join(splits[1:], ":")
if httpVersion != "HTTP/2" && httpVersion != "HTTP/3" {
headerName = caser.String(headerName)
}
parsedSection.Headers = append(parsedSection.Headers, Header{
Name: headerName,
Value: headerValue,
})
}
} else if in_body {
parsedSection.Body += line
Expand Down Expand Up @@ -117,14 +168,14 @@ func documentToString(document Document) string {
documentString += comment + "\n"
}
for _, metadata := range section.Metadata {
if strings.HasPrefix(metadata, "# @name ") {
if metadata.Name == "name" {
continue
}
documentString += metadata + "\n"
documentString += "# @" + metadata.Name + " " + metadata.Value + "\n"
}
for _, metadata := range section.Metadata {
if strings.HasPrefix(metadata, "# @name ") {
documentString += metadata + "\n"
if metadata.Name == "name" {
documentString += "# @" + metadata.Name + " " + metadata.Value + "\n"
}
}
documentString += section.Method + " " + section.URL
Expand All @@ -133,7 +184,7 @@ func documentToString(document Document) string {
}
documentString += "\n"
for _, header := range section.Headers {
documentString += header + "\n"
documentString += header.Name + ": " + header.Value + "\n"
}
if section.Body != "" {
documentString += "\n" + section.Body + "\n"
Expand Down
Loading