Skip to content
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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
17 changes: 13 additions & 4 deletions .github/workflows/go.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ jobs:
Test:
strategy:
matrix:
go-version: [1.16.x, 1.17.x, 1.18.x, 1.19.x]
os: [ubuntu-latest, windows-latest, macos-11]
go-version: [1.25.x, 1.26.x, 1.27.x]
os: [ubuntu-latest, windows-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
Expand All @@ -21,6 +21,15 @@ jobs:
with:
go-version: ${{ matrix.go-version }}

- name: Build
run: go build -v
- name: Build (Unix)
if: runner.os != 'Windows'
run: go build -v -o _testing/gocode

- name: Build (Windows)
if: runner.os == 'Windows'
run: go build -v -o _testing/gocode.exe

- name: Regression tests
working-directory: _testing
run: go test -v

29 changes: 18 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ _LiteIDE is a simple, open source, cross-platform Go IDE._
_Gocode is a golang code autocomplete support for LiteIDE._


- support Go1.11 Go modules.
- support Go1.18 generics.
- support Go modules.
- support generics and current Go syntax.
- tested with Go 1.25, Go 1.26, and Go 1.27.

```
go install github.com/visualfc/gocode@latest
Expand Down Expand Up @@ -52,15 +53,7 @@ Also watch the [demo screencast](http://nosmileface.ru/images/gocode-demo.swf).

`export PATH=$PATH:$GOPATH/bin`

2. Then you need to get the appropriate version of the gocode, for 6g/8g/5g compiler you can do this:

`go get -u github.com/nsf/gocode` (-u flag for "update")

Windows users should consider doing this instead:

`go get -u -ldflags -H=windowsgui github.com/nsf/gocode`

That way on the Windows OS gocode will be built as a GUI application and doing so solves hanging window issues with some of the editors.
2. Install the latest gocode with `go install github.com/visualfc/gocode@latest`.

3. Next steps are editor specific. See below.

Expand Down Expand Up @@ -231,6 +224,20 @@ Please, report bugs, feature suggestions and other rants to the [github issue tr

### Developing

#### Running tests

The regression tests use the Go standard test runner. Build the test binary and run
the autocomplete and server-side type information tests from `_testing`:

```sh
go build -o _testing/gocode .
cd _testing
go test -v
```

On Windows, build the binary as `_testing/gocode.exe` instead. The test fixtures
include version- and platform-specific expected output where required.

There is [Guide for IDE/editor plugin developers](docs/IDE_integration.md).

If you have troubles, please, contact me and I will try to do my best answering your questions. You can contact me via <a href="mailto:no.smile.face@gmail.com">email</a>. Or for short question find me on IRC: #go-nuts @ freenode.
Expand Down
33 changes: 33 additions & 0 deletions _testing/DESC
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,36 @@ test.0060 - shortcut syntax for return value types in functions
test.0061 - function body vs struct literal cursor context detection
test.0062 - struct type alias embedding
test.0063 - fields autocompletion for a struct literal which is defined by a type alias
test.0064 - generic struct field completion
test.0065 - generic function instantiation result
test.0066 - generic function interface constraint
test.0067 - union type constraint
test.0068 - generic methods with receiver type parameters
test.0069 - multiple type parameters and instantiated struct
test.0070 - generic function with multiple type parameters
test.0071 - generic type inference from function result
test.0072 - generic value and pointer methods
test.0073 - instantiated multi-parameter pointer method
test.0074 - generic interface constraint implementation
test.0075 - embedded instantiated generic type
test.0076 - generic function inferred struct result
test.0077 - nested instantiated generic fields
test.0078 - multi-parameter generic method
test.0079 - generic slice field instantiation
test.0080 - constrained generic method
test.0081 - Go 1.27 generic method type parameter
test.0082 - generic method returning instantiated generic type
test.0083 - generic method with multiple own type parameters
test.0084 - unsafe package declarations
test.0085 - unsafe.Pointer type
test.0086 - unsafe.SliceData result
test.0087 - unsafe.StringData result
test.0088 - unsafe.Pointer type alias
typesinfo.0001 - instantiated generic method
typesinfo.0002 - local variable
typesinfo.0003 - standard library function and documentation
typesinfo.0004 - generic type parameter
typesinfo.0005 - instantiated generic composite literal field
typesinfo.0006 - generic function instantiation
typesinfo.0007 - instantiated generic selector field
typesinfo.0008 - interface method selector
8 changes: 8 additions & 0 deletions _testing/README
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ because usually you spend some time in editor between requests and
that time is wasted on a GC run, but testing app throws requests one
after another and it causes GC to eat a lot of CPU. So.. don't worry
about that.

Server-side type information tests can be run with:

go test -v -run TestTypesInfo

Autocomplete regression tests can be run with:

go test -v -run TestAutocomplete
12 changes: 0 additions & 12 deletions _testing/all.bash

This file was deleted.

176 changes: 176 additions & 0 deletions _testing/run.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
package main

import (
"bytes"
"flag"
"fmt"
"io"
"os"
"os/exec"
"path/filepath"
"regexp"
"runtime"
"sort"
"strconv"
"strings"
"time"
)

func main() {
mode := flag.String("mode", "autocomplete", "test mode: autocomplete or typesinfo")
flag.Parse()
root, _ := os.Getwd()
bin := filepath.Join(root, "gocode")
if runtime.GOOS == "windows" {
bin = filepath.Join(root, "gocode.exe")
}
if _, err := os.Stat(bin); err != nil {
fail("gocode binary not found: %v", err)
}
var ok bool
if *mode == "typesinfo" {
ok = runTypesInfo(root, bin)
} else {
ok = runAutocomplete(root, bin)
}
if !ok {
os.Exit(1)
}
}

func runAutocomplete(root, bin string) bool {
tests, _ := filepath.Glob(filepath.Join(root, "test.*"))
sort.Strings(tests)
version := goVersion()
platform := ""
if runtime.GOOS == "windows" {
platform = ".windows"
}
addr := fmt.Sprintf("127.0.0.1:%d", 41000+os.Getpid()%1000)
server := exec.Command(bin, "-s", "-sock", "tcp", "-addr", addr)
server.Stdout = io.Discard
server.Stderr = io.Discard
if err := server.Start(); err != nil {
fmt.Fprintf(os.Stderr, "start server: %v\n", err)
return false
}
defer func() {
exec.Command(bin, "-sock", "tcp", "-addr", addr, "close").Run()
server.Process.Kill()
server.Wait()
}()
time.Sleep(300 * time.Millisecond)
pass := 0
for _, dir := range tests {
cursor := cursorOf(dir)
expected := expectedPath(dir, version, platform)
file := filepath.Join(dir, "test.go.in")
got, _ := command(bin, "-sock", "tcp", "-addr", addr, "-in", file, "autocomplete", file, strconv.Itoa(cursor))
want, _ := os.ReadFile(expected)
if string(got) != string(want) {
fmt.Printf("%s: FAIL!\nGot:\n%sExpected:\n%s", filepath.Base(dir), got, want)
} else {
fmt.Printf("%s: PASS!\n", filepath.Base(dir))
pass++
}
}
fmt.Printf("\nSummary (total: %d)\n PASS: %d\n FAIL: %d\n", len(tests), pass, len(tests)-pass)
return pass == len(tests)
}

func runTypesInfo(root, bin string) bool {
addr := fmt.Sprintf("127.0.0.1:%d", 40000+os.Getpid()%1000)
server := exec.Command(bin, "-s", "-sock", "tcp", "-addr", addr)
server.Stdout = io.Discard
server.Stderr = io.Discard
if err := server.Start(); err != nil {
fmt.Fprintf(os.Stderr, "start server: %v\n", err)
return false
}
defer func() {
exec.Command(bin, "-sock", "tcp", "-addr", addr, "close").Run()
server.Process.Kill()
server.Wait()
}()
time.Sleep(300 * time.Millisecond)
tests, _ := filepath.Glob(filepath.Join(root, "typesinfo.[0-9]*"))
sort.Strings(tests)
pass := 0
for _, dir := range tests {
if st, err := os.Stat(dir); err != nil || !st.IsDir() {
continue
}
cursor := cursorOf(dir)
file := filepath.Join(dir, "test.go.in")
gotBytes, _ := command(bin, "-sock", "tcp", "-addr", addr, "-in", file, "liteide_typesinfo", file, strconv.Itoa(cursor))
got := normalize(string(gotBytes), file)
want, _ := os.ReadFile(filepath.Join(dir, "out.expected"))
if got != string(want) {
fmt.Printf("%s: FAIL!\nGot:\n%sExpected:\n%s", filepath.Base(dir), got, want)
} else {
fmt.Printf("%s: PASS!\n", filepath.Base(dir))
pass++
}
}
fmt.Printf("\nSummary (total: %d)\n PASS: %d\n FAIL: %d\n", len(tests), pass, len(tests)-pass)
return pass == len(tests)
}

func command(bin string, args ...string) ([]byte, error) { return exec.Command(bin, args...).Output() }
func cursorOf(dir string) int {
files, _ := filepath.Glob(filepath.Join(dir, "cursor.*"))
p := strings.Split(filepath.Base(files[0]), ".")
n, _ := strconv.Atoi(p[len(p)-1])
if source, err := os.ReadFile(filepath.Join(dir, "test.go.in")); err == nil && bytesBeforeCursor(source, n) {
for i := 0; i < n && i+1 < len(source); i++ {
if source[i] == '\r' && source[i+1] == '\n' {
n++
}
}
}
return n
}

func bytesBeforeCursor(source []byte, cursor int) bool {
return cursor >= 0 && cursor <= len(source) && bytes.Index(source[:cursor], []byte("\r\n")) >= 0
}
func goVersion() string {
b, _ := exec.Command("go", "env", "GOVERSION").Output()
return strings.TrimPrefix(strings.TrimSpace(string(b)), "go")
}
func expectedPath(dir, version, platform string) string {
parts := strings.Split(version, ".")
if len(parts) >= 2 {
mm := strings.Join(parts[:2], ".")
p := filepath.Join(dir, "out.expected.go"+mm+platform)
if _, err := os.Stat(p); err == nil {
return p
}
p = filepath.Join(dir, "out.expected.go"+mm)
if _, err := os.Stat(p); err == nil {
return p
}
}
return filepath.Join(dir, "out.expected")
}
func normalize(s, file string) string {
s = strings.ReplaceAll(s, "\\", "/")
if absolute, err := filepath.Abs(file); err == nil {
s = strings.ReplaceAll(s, filepath.ToSlash(absolute), "FILE")
}
for _, root := range []string{os.Getenv("GOROOT"), goroot()} {
if root != "" {
s = strings.ReplaceAll(s, filepath.ToSlash(root), "GOROOT")
}
}
s = regexp.MustCompile(`GOROOT/src/([^:]+):[0-9]+:[0-9]+`).ReplaceAllString(s, "GOROOT/src/$1:LINE:COLUMN")
return s
}
func goroot() string {
b, _ := exec.Command("go", "env", "GOROOT").Output()
return strings.TrimSpace(string(b))
}
func fail(format string, args ...interface{}) {
fmt.Fprintf(os.Stderr, format+"\n", args...)
os.Exit(1)
}
67 changes: 0 additions & 67 deletions _testing/run.py

This file was deleted.

Loading
Loading