GoSublime icon indicating copy to clipboard operation
GoSublime copied to clipboard

Autocomplete doesn't seem to work with vendored dependencies

Open code-merc opened this issue 7 years ago • 10 comments

I'm vendoring my dependencies using go modules, but I can't seem to get autocompletion working for them. I can get completion for builtins like fmt.

peek 2018-10-10 15-39

The golang.Gocode section of my margo.go file looks like this:

&golang.Gocode{
	// Whether or not to do gocode completion using source code
	// instead of the pre-compiled package files.
	// Using source is often slower but offer more up-to-date completions.
	Source: true,

	// show the function parameters. this can take up a lot of space
	ShowFuncParams: true,

	// whether or not to include Test*, Benchmark* and Example* functions in the auto-completion list
	// gs: this replaces the `autocomplete_tests` setting
	ProposeTests: false,

	// whether or not builtin types and functions should be shown in the auto-completion list
	// gs: this replaces the `autocomplete_builtins` setting
	ProposeBuiltins: true,
},

The actual reference works too, so when I hover over I get the vendored dependency like this: screenshot from 2018-10-10 15-42-32

Edit: I installed for the first time today and I'm running on the develop branch like I'm supposed to.

code-merc avatar Oct 10 '18 19:10 code-merc

There's currently no support for modules beyond the limited amount of magic inside of the go/build package.

The definition is provided by Sublime Text's file indexing.

DisposaBoy avatar Oct 10 '18 20:10 DisposaBoy

You can use go-dep vendored pacakges though correct?

code-merc avatar Oct 10 '18 20:10 code-merc

The vendor directory is a general feature of the Go tools so it should work, but IIUC, it's not used by Go modules. They only work in the old GOPATH world.

DisposaBoy avatar Oct 10 '18 20:10 DisposaBoy

So if you type go mod vendor it will put those dependencies into a vendor folder screenshot from 2018-10-10 16-50-45

Edit: So I currently have a vendor folder and it doesn't seem to work, and I'm still working inside my existing GOPATH.

code-merc avatar Oct 10 '18 20:10 code-merc

It doesn't work because you're in a module and modules don't support the vendor directory.

AFAIK go mod vendor is only there to provide backwards compatibility for older versions of Go and when you're not using modules.

DisposaBoy avatar Oct 10 '18 20:10 DisposaBoy

Yeah the backwards compatibility was kinda my hope with getting go sublime to work here, it was my strategy when using Atom before.

code-merc avatar Oct 10 '18 21:10 code-merc

That would involve writing custom support which makes me nervous - the new packages package which is supposed to fix all this is giving me enough headaches already. They're supposed to be fixing it in gocode, maybe I can merge that when it's ready.

DisposaBoy avatar Oct 10 '18 21:10 DisposaBoy

I was just reading that part of the gocode actually, it looks like a quick fix is globally disabling modules by setting the environment variable to off, and I get auto completion.

Is there any way to pass in custom environment vars to MarGo to make it not use gomodules without having to globally disable it or setup environment variables when I launch sublime?

code-merc avatar Oct 10 '18 21:10 code-merc

The problem with setting the env var is that it's a global variable without synchronization, so changing it is racy. But reducers are run synchronously so it might not be a problem.

To set it you can create a couple reducers that turn modules on where appropriate and then temporarily turn it off when doing auto-completion.

Bearing in mind that the vendor directory only works if your code is inside GOPATH, you can add the following code to your margo.go file.

Note: I didn't do much testing of this code...

type modOff struct{ mg.Reducer }

func (mo *modOff) ReducerCond(mx *mg.Ctx) bool {
	return mx.LangIs(mg.Go)
}

func (mo *modOff) Reduce(mx *mg.Ctx) *mg.State {
	if inGoMod(mx.View.Dir()) {
		k := "GO111MODULE"
		v := os.Getenv(k)
		os.Setenv(k, "off")
		defer func() { os.Setenv(k, v) }()
	}
	return mo.Reducer.Reduce(mx)
}

type modOn struct {
	mg.ReducerType
}

func (mo *modOn) ReducerCond(mx *mg.Ctx) bool {
	return mx.LangIs(mg.Go)
}

func (mo *modOn) Reduce(mx *mg.Ctx) *mg.State {
	if k := "GO111MODULE"; inGoMod(mx.View.Dir()) {
		os.Setenv(k, "on")
	} else {
		os.Unsetenv(k)
	}
	return mx.State
}

func inGoMod(dir string) bool {
	fn := filepath.Join(dir, "go.mod")
	fi, err := os.Stat(fn)
	if err == nil && fi.Mode().IsRegular() {
		return true
	}

	d := filepath.Dir(dir)
	if len(d) < len(dir) {
		return inGoMod(d)
	}

	return false
}

Then in the func Margo(m *mg.Args) function add m.Before(&modOn{}). This will make sure modules are turned on before things like the go command's reducer is run.

You can then optionally turn it off again for a particular reducer by wrapping it with &modOff{} e.g.

&modOff{&golang.GocodeCalltips{
	...
}},
&modOff{&golang.Gocode{
	...
}},

DisposaBoy avatar Oct 11 '18 09:10 DisposaBoy

I'll try that out today, for now I just aliased subl to GO111MODULE=off /usr/bin/subl and that seems to work if I do go mod vendor when I add/change a dependency.

code-merc avatar Oct 11 '18 17:10 code-merc