diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..ad240cf --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,29 @@ +name: Test + +on: + push: + branches: [master] + pull_request: + branches: [master] + +jobs: + build: + name: Build + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Go + uses: actions/setup-go@v5 + with: + go-version: 1.23 + + - name: Check formatting + run: make check-fmt + + - name: Test + run: make diff --git a/.gitignore b/.gitignore index ff95e9e..f9fce8f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ log.txt main +Session.vim diff --git a/CHANGELOG.md b/CHANGELOG.md index 4edfaf7..f05984b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,18 @@ # Release Notes +## v0.4.0 (2025-11-08) +- ✨ Loop directives like `@break`, `@continue`, `@breakIf`, `@continueIf` are now getting suggested only inside of loops. +- 🧑💻 Added testing GitHub Actions job to run tests. + ## v0.3.1 (2025-06-14) -- 🐛 Update LSP textwire to the latest version which will fix `@break($1)` snippet to `@break` +- 🐛 Updated LSP textwire to the latest version which will fix `@break($1)` snippet to `@break` ## v0.3.0 (2025-06-14) -- 🐛 Update LSP textwire to the latest version which will fix crashing LSP when you make a syntax error in your Textwire code +- 🐛 Updated LSP textwire to the latest version which will fix crashing LSP when you make a syntax error in your Textwire code - ✨ Autocomplete snippets that appear after you hit enter are now more complex. Instead of simple autocomple like `@if` you know get the full if statement and the cursor placed inside condition. It allows you to hit tab to move to the next place in a snippet ## v0.2.0 (2025-05-30) -- ✨ Add autocompletion for loop object. Now if you type `loop.` inside of a loop, it will show available properties on the object +- ✨ Added autocompletion for loop object. Now if you type `loop.` inside of a loop, it will show available properties on the object ## v0.1.4 (2025-05-17) - ✨ Autocomplete suggestions show code example with syntax highlighting. Before, it was just displayed as text diff --git a/Containerfile b/Containerfile index 928bbad..2df5b52 100644 --- a/Containerfile +++ b/Containerfile @@ -1,13 +1,13 @@ -FROM golang:1.24-alpine +FROM golang:1.25-alpine WORKDIR /app RUN apk add --no-cache make -COPY go.mod go.sum . +COPY go.* . RUN go mod download COPY . . -CMD ["sh"] \ No newline at end of file +CMD ["sh"] diff --git a/Makefile b/Makefile index 1699f4e..83d8899 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -w.PHONY: test +.PHONY: test test: @echo "🚀 Running tests..." go test ./... @@ -8,4 +8,13 @@ test: build: go build main.go +.PHONE: check-fmt +check-fmt: + unformatted=$$(gofmt -l .); \ + if [ -n "$$unformatted" ]; then \ + echo "The following files are not formatted properly:"; \ + echo "$$unformatted"; \ + exit 1; \ + fi + .DEFAULT_GOAL := test diff --git a/analysis/completion.go b/analysis/completion.go index ef8489f..9b5bdf0 100644 --- a/analysis/completion.go +++ b/analysis/completion.go @@ -2,6 +2,7 @@ package analysis import ( "regexp" + "slices" "strings" "github.com/textwire/lsp/internal/logger" @@ -35,18 +36,9 @@ func (s *State) Completion(id int, uri string, pos lsp.Position) (lsp.Completion var err error if directiveMatch != nil { - completionItems, err = completions.GetDirectives(locale) + completionItems, err = handleDirectivesAutocomplete(doc, pos, uri) } else if strings.HasSuffix(textBeforeCursor, "loop.") { - doc = removeTrailingChar(doc, pos.Line, pos.Character, '.') - - isInsideLoop, errors := twLsp.IsInLoop(doc, uri, pos.Line, pos.Character) - if len(errors) > 0 { - logger.Error.Println(errors[0]) - } - - if isInsideLoop { - completionItems, err = completions.GetLoopObjFields(locale) - } + completionItems, err = handleLoopObjectAutocomplete(doc, pos, uri) } if err != nil { @@ -57,6 +49,55 @@ func (s *State) Completion(id int, uri string, pos lsp.Position) (lsp.Completion return s.completionResponse(id, s.makeCompletions(completionItems)), nil } +func handleDirectivesAutocomplete(doc string, pos lsp.Position, uri string) ([]completions.Completion, error) { + dirs, err := completions.GetDirectives(locale) + if err != nil { + return []completions.Completion{}, err + } + + isInsideLoop, errors := twLsp.IsInLoop(doc, uri, pos.Line, pos.Character) + if len(errors) > 0 { + logger.Error.Println(errors[0]) + } + + if isInsideLoop { + return dirs, nil + } + + filteredDirs := make([]completions.Completion, 0, len(dirs)) + loopDirs := []string{"@break", "@breakIf", "@continue", "@continueIf"} + + for _, dir := range dirs { + if slices.Contains(loopDirs, dir.Label) { + continue + } + + filteredDirs = append(filteredDirs, dir) + } + + return filteredDirs, nil +} + +func handleLoopObjectAutocomplete(doc string, pos lsp.Position, uri string) ([]completions.Completion, error) { + doc = removeTrailingChar(doc, pos.Line, pos.Character, '.') + + isInsideLoop, errors := twLsp.IsInLoop(doc, uri, pos.Line, pos.Character) + if len(errors) > 0 { + logger.Error.Println(errors[0]) + } + + if !isInsideLoop { + return []completions.Completion{}, nil + } + + fields, err := completions.GetLoopObjFields(locale) + if err != nil { + return []completions.Completion{}, err + } + + return fields, nil +} + func removeTrailingChar(doc string, line, col uint, char byte) string { lines := strings.Split(doc, "\n") if int(line) >= len(lines) { diff --git a/rpc/rpc_test.go b/rpc/rpc_test.go index 60f9e19..d6073a3 100644 --- a/rpc/rpc_test.go +++ b/rpc/rpc_test.go @@ -22,7 +22,7 @@ func TestDecodeMessage(t *testing.T) { expectMethod := "hi" incomingMsg := fmt.Sprintf("Content-Length: %d\r\n\r\n{\"Method\":\"%s\"}", expectLen, expectMethod) - method, content, err := DecodeMessage([]byte(incomingMsg) ) + method, content, err := DecodeMessage([]byte(incomingMsg)) if err != nil { t.Fatal(err) } diff --git a/textwire.tw b/textwire.tw deleted file mode 100644 index e705063..0000000 --- a/textwire.tw +++ /dev/null @@ -1,17 +0,0 @@ -