diff options
Diffstat (limited to 'misc')
31 files changed, 712 insertions, 208 deletions
diff --git a/misc/bbedit/Go.plist b/misc/bbedit/Go.plist index 791f93d16..791f93d16 100755..100644 --- a/misc/bbedit/Go.plist +++ b/misc/bbedit/Go.plist diff --git a/misc/cgo/gmp/gmp.go b/misc/cgo/gmp/gmp.go index 3bcf99151..7b7a9b3c9 100644 --- a/misc/cgo/gmp/gmp.go +++ b/misc/cgo/gmp/gmp.go @@ -33,7 +33,7 @@ field; unrepresentable fields are replaced with opaque byte arrays. A C union translates into a struct containing the first union member and perhaps additional padding. C arrays become Go arrays. C pointers become Go pointers. C function pointers become Go's uintptr. -C void pointer's become Go's unsafe.Pointer. +C void pointers become Go's unsafe.Pointer. For example, mpz_t is defined in <gmp.h> as: diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go index 4f5d3f855..b6e2e3c1c 100644 --- a/misc/cgo/test/callback.go +++ b/misc/cgo/test/callback.go @@ -12,7 +12,9 @@ import "C" import ( "./backdoor" + "path" "runtime" + "strings" "testing" "unsafe" ) @@ -136,3 +138,44 @@ func testBlocking(t *testing.T) { } }) } + +// Test that the stack can be unwound through a call out and call back +// into Go. +func testCallbackCallers(t *testing.T) { + pc := make([]uintptr, 100) + n := 0 + name := []string{ + "test.goCallback", + "runtime.cgocallbackg", + "runtime.cgocallback_gofunc", + "return", + "runtime.cgocall", + "test._Cfunc_callback", + "test.nestedCall", + "test.testCallbackCallers", + "test.TestCallbackCallers", + "testing.tRunner", + "runtime.goexit", + } + nestedCall(func() { + n = runtime.Callers(2, pc) + }) + if n != len(name) { + t.Errorf("expected %d frames, got %d", len(name), n) + } + for i := 0; i < n; i++ { + f := runtime.FuncForPC(pc[i]) + if f == nil { + t.Fatalf("expected non-nil Func for pc %p", pc[i]) + } + fname := f.Name() + // Remove the prepended pathname from automatically + // generated cgo function names. + if strings.HasPrefix(fname, "_") { + fname = path.Base(f.Name()[1:]) + } + if fname != name[i] { + t.Errorf("expected function name %s, got %s", name[i], fname) + } + } +} diff --git a/misc/cgo/test/cflags.go b/misc/cgo/test/cflags.go new file mode 100644 index 000000000..24caab471 --- /dev/null +++ b/misc/cgo/test/cflags.go @@ -0,0 +1,32 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Test that the #cgo CFLAGS directive works, +// with and without platform filters. +// See http://code.google.com/p/go/issues/detail?id=5224 for details. +package cgotest + +/* +#cgo CFLAGS: -DCOMMON_VALUE=123 +#cgo windows CFLAGS: -DIS_WINDOWS=1 +#cgo !windows CFLAGS: -DIS_WINDOWS=0 +int common = COMMON_VALUE; +int is_windows = IS_WINDOWS; +*/ +import "C" + +import ( + "runtime" + "testing" +) + +func testCflags(t *testing.T) { + is_windows := C.is_windows == 1 + if is_windows != (runtime.GOOS == "windows") { + t.Errorf("is_windows: %v, runtime.GOOS: %s", is_windows, runtime.GOOS) + } + if C.common != 123 { + t.Errorf("common: %v (expected 123)", C.common) + } +} diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go index 536fa507a..56e1a0625 100644 --- a/misc/cgo/test/cgo_test.go +++ b/misc/cgo/test/cgo_test.go @@ -36,5 +36,8 @@ func TestBoolAlign(t *testing.T) { testBoolAlign(t) } func Test3729(t *testing.T) { test3729(t) } func Test3775(t *testing.T) { test3775(t) } func TestCthread(t *testing.T) { testCthread(t) } +func TestCallbackCallers(t *testing.T) { testCallbackCallers(t) } +func Test5227(t *testing.T) { test5227(t) } +func TestCflags(t *testing.T) { testCflags(t) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } diff --git a/misc/cgo/test/cthread.go b/misc/cgo/test/cthread.go index d918d033f..68d4a03ea 100644 --- a/misc/cgo/test/cthread.go +++ b/misc/cgo/test/cthread.go @@ -8,7 +8,6 @@ package cgotest import "C" import ( - "runtime" "sync" "testing" ) @@ -31,10 +30,7 @@ func Add(x int) { } func testCthread(t *testing.T) { - if runtime.GOARCH == "arm" { - t.Skip("testCthread disabled on arm") - } - + sum.i = 0 C.doAdd(10, 6) want := 10 * (10 - 1) / 2 * 6 diff --git a/misc/cgo/test/issue4029.go b/misc/cgo/test/issue4029.go index 7495d38fe..b0385eb85 100644 --- a/misc/cgo/test/issue4029.go +++ b/misc/cgo/test/issue4029.go @@ -47,14 +47,15 @@ func test4029(t *testing.T) { func loadThySelf(t *testing.T, symbol string) { this_process := C.dlopen(nil, C.RTLD_NOW) if this_process == nil { - t.Fatal("dlopen:", C.GoString(C.dlerror())) + t.Error("dlopen:", C.GoString(C.dlerror())) + return } defer C.dlclose(this_process) symbol_address := C.dlsym(this_process, C.CString(symbol)) if symbol_address == nil { - t.Fatal("dlsym:", C.GoString(C.dlerror())) - } else { - t.Log(symbol, symbol_address) + t.Error("dlsym:", C.GoString(C.dlerror())) + return } + t.Log(symbol, symbol_address) } diff --git a/misc/cgo/test/issue5227.go b/misc/cgo/test/issue5227.go new file mode 100644 index 000000000..336c4c609 --- /dev/null +++ b/misc/cgo/test/issue5227.go @@ -0,0 +1,38 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Issue 5227: linker incorrectly treats common symbols and +// leaves them undefined. + +package cgotest + +/* +typedef struct { + int Count; +} Fontinfo; + +Fontinfo SansTypeface; + +extern void init(); + +Fontinfo loadfont() { + Fontinfo f = {0}; + return f; +} + +void init() { + SansTypeface = loadfont(); +} +*/ +import "C" + +import "testing" + +func test5227(t *testing.T) { + C.init() +} + +func selectfont() C.Fontinfo { + return C.SansTypeface +} diff --git a/misc/cgo/testso/cgoso.c b/misc/cgo/testso/cgoso.c new file mode 100644 index 000000000..917f472d3 --- /dev/null +++ b/misc/cgo/testso/cgoso.c @@ -0,0 +1,14 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "_cgo_export.h" + +#ifdef WIN32 +extern void setCallback(void *); +void init() { + setCallback(goCallback); +} +#else +void init() {} +#endif diff --git a/misc/cgo/testso/cgoso.go b/misc/cgo/testso/cgoso.go index 44fb616c1..216cb1f05 100644 --- a/misc/cgo/testso/cgoso.go +++ b/misc/cgo/testso/cgoso.go @@ -6,11 +6,13 @@ package cgosotest /* #cgo LDFLAGS: -L. -lcgosotest +void init(void); void sofunc(void); */ import "C" func Test() { + C.init() C.sofunc() } diff --git a/misc/cgo/testso/cgoso_c.c b/misc/cgo/testso/cgoso_c.c index 8c15a6b9f..27155c27f 100644 --- a/misc/cgo/testso/cgoso_c.c +++ b/misc/cgo/testso/cgoso_c.c @@ -4,8 +4,22 @@ // +build ignore +#ifdef WIN32 +// A Windows DLL is unable to call an arbitrary function in +// the main executable. Work around that by making the main +// executable pass the callback function pointer to us. +void (*goCallback)(void); +__declspec(dllexport) void setCallback(void *f) +{ + goCallback = (void (*)())f; +} +__declspec(dllexport) void sofunc(void); +#else +extern void goCallback(void); +void setCallback(void *f) { (void)f; } +#endif + void sofunc(void) { - extern void goCallback(void); goCallback(); } diff --git a/misc/cgo/testso/test.bat b/misc/cgo/testso/test.bat new file mode 100644 index 000000000..b8cc3842b --- /dev/null +++ b/misc/cgo/testso/test.bat @@ -0,0 +1,18 @@ +:: Copyright 2013 The Go Authors. All rights reserved. +:: Use of this source code is governed by a BSD-style +:: license that can be found in the LICENSE file. + +@echo off + +gcc -c cgoso_c.c +gcc -shared -o libcgosotest.dll cgoso_c.o +if not exist libcgosotest.dll goto fail +go build main.go +if not exist main.exe goto fail +main.exe +goto :end + +:fail +set FAIL=1 +:end +del /F cgoso_c.o libcgosotest.dll main.exe 2>NUL diff --git a/misc/cgo/testtls/tls.go b/misc/cgo/testtls/tls.go new file mode 100644 index 000000000..a9546a61c --- /dev/null +++ b/misc/cgo/testtls/tls.go @@ -0,0 +1,28 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package cgotlstest + +// #include <pthread.h> +// extern void setTLS(int); +// extern int getTLS(); +import "C" + +import ( + "runtime" + "testing" +) + +func testTLS(t *testing.T) { + var keyVal C.int = 1234 + + runtime.LockOSThread() + defer runtime.UnlockOSThread() + C.setTLS(C.int(keyVal)) + storedVal := C.getTLS() + + if storedVal != keyVal { + t.Fatalf("stored %d want %d", storedVal, keyVal) + } +} diff --git a/misc/cgo/testtls/tls_test.go b/misc/cgo/testtls/tls_test.go new file mode 100644 index 000000000..3076c2d59 --- /dev/null +++ b/misc/cgo/testtls/tls_test.go @@ -0,0 +1,13 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !windows + +package cgotlstest + +import "testing" + +func TestTLS(t *testing.T) { + testTLS(t) +} diff --git a/misc/cgo/testtls/tls_unix.c b/misc/cgo/testtls/tls_unix.c new file mode 100644 index 000000000..545951631 --- /dev/null +++ b/misc/cgo/testtls/tls_unix.c @@ -0,0 +1,19 @@ +// Copyright 2013 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include <pthread.h> + +static __thread int tls; + +void +setTLS(int v) +{ + tls = v; +} + +int +getTLS() +{ + return tls; +} diff --git a/misc/dashboard/README b/misc/dashboard/README index c00311ef7..d599f3d06 100644 --- a/misc/dashboard/README +++ b/misc/dashboard/README @@ -4,8 +4,8 @@ The files in this directory constitute the continuous builder: -godashboard/: an AppEngine server -builder/: gobuilder, a Go continuous build client +app/: an AppEngine server +builder/: gobuilder, a Go continuous build client If you wish to run a Go builder, please email golang-dev@googlegroups.com diff --git a/misc/dashboard/app/build/init.go b/misc/dashboard/app/build/init.go index 85a766b9d..505f96fc4 100644 --- a/misc/dashboard/app/build/init.go +++ b/misc/dashboard/app/build/init.go @@ -20,12 +20,13 @@ var defaultPackages = []*Package{ // subRepos specifies the Go project sub-repositories. var subRepos = []string{ + "blog", "codereview", "crypto", + "exp", "image", "net", "talks", - "exp", } // Put subRepos into defaultPackages. diff --git a/misc/dashboard/builder/main.go b/misc/dashboard/builder/main.go index b2b8f43a6..9a155bd0c 100644 --- a/misc/dashboard/builder/main.go +++ b/misc/dashboard/builder/main.go @@ -33,10 +33,9 @@ const ( var extraEnv = []string{ "CC", "GOARM", - "GOHOSTARCH", - "GOHOSTOS", "PATH", "TMPDIR", + "USER", } type Builder struct { @@ -64,6 +63,7 @@ var ( binaryTagRe = regexp.MustCompile(`^(release\.r|weekly\.)[0-9\-.]+`) releaseRe = regexp.MustCompile(`^release\.r[0-9\-.]+`) allCmd = "all" + suffix + raceCmd = "race" + suffix cleanCmd = "clean" + suffix suffix = defaultSuffix() ) @@ -211,6 +211,16 @@ func NewBuilder(goroot *Repo, name string) (*Builder, error) { return b, nil } +// buildCmd returns the build command to invoke. +// Builders which contain the string '-race' in their +// name will override *buildCmd and return raceCmd. +func (b *Builder) buildCmd() string { + if strings.Contains(b.name, "-race") { + return raceCmd + } + return *buildCmd +} + // build checks for a new commit for this builder // and builds it if one is found. // It returns true if a build was attempted. @@ -262,7 +272,7 @@ func (b *Builder) buildHash(hash string) error { defer f.Close() w := io.MultiWriter(f, &buildlog) - cmd := *buildCmd + cmd := b.buildCmd() if !filepath.IsAbs(cmd) { cmd = filepath.Join(srcDir, cmd) } @@ -397,7 +407,9 @@ func (b *Builder) envv() []string { } e := []string{ "GOOS=" + b.goos, + "GOHOSTOS=" + b.goos, "GOARCH=" + b.goarch, + "GOHOSTARCH=" + b.goarch, "GOROOT_FINAL=/usr/local/go", } for _, k := range extraEnv { @@ -412,7 +424,9 @@ func (b *Builder) envv() []string { func (b *Builder) envvWindows() []string { start := map[string]string{ "GOOS": b.goos, + "GOHOSTOS": b.goos, "GOARCH": b.goarch, + "GOHOSTARCH": b.goarch, "GOROOT_FINAL": `c:\go`, "GOBUILDEXIT": "1", // exit all.bat with completion status. } diff --git a/misc/dashboard/codereview/dashboard/cl.go b/misc/dashboard/codereview/dashboard/cl.go index e150ea123..0ef3303e9 100644 --- a/misc/dashboard/codereview/dashboard/cl.go +++ b/misc/dashboard/codereview/dashboard/cl.go @@ -178,8 +178,14 @@ func handleAssign(w http.ResponseWriter, r *http.Request) { return } defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + c.Errorf("Failed reading body: %v", err) + http.Error(w, err.Error(), 500) + return + } if resp.StatusCode != 200 { - c.Errorf("Retrieving CL reviewer list failed: got HTTP response %d", resp.StatusCode) + c.Errorf("Retrieving CL reviewer list failed: got HTTP response %d\nBody: %s", resp.StatusCode, body) http.Error(w, "Failed contacting Rietveld", 500) return } @@ -187,7 +193,7 @@ func handleAssign(w http.ResponseWriter, r *http.Request) { var apiResp struct { Reviewers []string `json:"reviewers"` } - if err := json.NewDecoder(resp.Body).Decode(&apiResp); err != nil { + if err := json.Unmarshal(body, &apiResp); err != nil { // probably can't be retried msg := fmt.Sprintf("Malformed JSON from %v: %v", url, err) c.Errorf("%s", msg) @@ -212,8 +218,14 @@ func handleAssign(w http.ResponseWriter, r *http.Request) { return } defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + c.Errorf("Failed reading Gobot body: %v", err) + http.Error(w, err.Error(), 500) + return + } if resp.StatusCode != 200 { - c.Errorf("Gobot GET failed: got HTTP response %d", resp.StatusCode) + c.Errorf("Gobot GET failed: got HTTP response %d\nBody: %s", resp.StatusCode, body) http.Error(w, "Failed contacting Gobot", 500) return } diff --git a/misc/dashboard/codereview/dashboard/people.go b/misc/dashboard/codereview/dashboard/people.go index facda7baf..21bd8f89c 100644 --- a/misc/dashboard/codereview/dashboard/people.go +++ b/misc/dashboard/codereview/dashboard/people.go @@ -21,6 +21,7 @@ func init() { // and prefer to use their golang.org address for code review. gophers := [...]string{ "adg", + "agl", "bradfitz", "campoy", "dsymonds", @@ -37,6 +38,25 @@ func init() { emailToPerson[p+"@google.com"] = p preferredEmail[p] = p + "@golang.org" } + // Other people. + others := map[string]string{ + "adonovan": "adonovan@google.com", + "brainman": "alex.brainman@gmail.com", + "ality": "ality@pbrane.org", + "dfc": "dave@cheney.net", + "dvyukov": "dvyukov@google.com", + "gustavo": "gustavo@niemeyer.net", + "jsing": "jsing@google.com", + "mikio": "mikioh.mikioh@gmail.com", + "minux": "minux.ma@gmail.com", + "remy": "remyoudompheng@gmail.com", + "rminnich": "rminnich@gmail.com", + } + for p, e := range others { + personList = append(personList, p) + emailToPerson[e] = p + preferredEmail[p] = e + } sort.Strings(personList) } diff --git a/misc/dist/bindist.go b/misc/dist/bindist.go index 29454c73c..19b0baac2 100644 --- a/misc/dist/bindist.go +++ b/misc/dist/bindist.go @@ -29,13 +29,15 @@ import ( ) var ( - tag = flag.String("tag", "release", "mercurial tag to check out") - repo = flag.String("repo", "https://code.google.com/p/go", "repo URL") - tourPath = flag.String("tour", "code.google.com/p/go-tour", "Go tour repo import path") - verbose = flag.Bool("v", false, "verbose output") - upload = flag.Bool("upload", true, "upload resulting files to Google Code") - wxsFile = flag.String("wxs", "", "path to custom installer.wxs") - addLabel = flag.String("label", "", "additional label to apply to file when uploading") + tag = flag.String("tag", "release", "mercurial tag to check out") + repo = flag.String("repo", "https://code.google.com/p/go", "repo URL") + tourPath = flag.String("tour", "code.google.com/p/go-tour", "Go tour repo import path") + verbose = flag.Bool("v", false, "verbose output") + upload = flag.Bool("upload", true, "upload resulting files to Google Code") + wxsFile = flag.String("wxs", "", "path to custom installer.wxs") + addLabel = flag.String("label", "", "additional label to apply to file when uploading") + includeRace = flag.Bool("race", true, "build race detector packages") + versionOverride = flag.String("version", "", "override version name") username, password string // for Google Code upload ) @@ -72,6 +74,7 @@ var tourPackages = []string{ } var tourContent = []string{ + "js", "prog", "solutions", "static", @@ -79,7 +82,14 @@ var tourContent = []string{ "tour.article", } -var fileRe = regexp.MustCompile(`^go\.([a-z0-9-.]+)\.(src|([a-z0-9]+)-([a-z0-9]+))\.`) +// The os-arches that support the race toolchain. +var raceAvailable = []string{ + "darwin-amd64", + "linux-amd64", + "windows-amd64", +} + +var fileRe = regexp.MustCompile(`^(go[a-z0-9-.]+)\.(src|([a-z0-9]+)-([a-z0-9]+))\.`) func main() { flag.Usage = func() { @@ -130,6 +140,13 @@ func main() { } b.OS = p[0] b.Arch = p[1] + if *includeRace { + for _, t := range raceAvailable { + if t == targ { + b.Race = true + } + } + } } if err := b.Do(); err != nil { log.Printf("%s: %v", targ, err) @@ -139,6 +156,7 @@ func main() { type Build struct { Source bool // if true, OS and Arch must be empty + Race bool // build race toolchain OS string Arch string root string @@ -183,15 +201,31 @@ func (b *Build) Do() error { } else { _, err = b.run(src, "bash", "make.bash") } + if b.Race { + if err != nil { + return err + } + goCmd := filepath.Join(b.root, "bin", "go") + if b.OS == "windows" { + goCmd += ".exe" + } + _, err = b.run(src, goCmd, "install", "-race", "std") + if err != nil { + return err + } + // Re-install std without -race, so that we're not left + // with a slower, race-enabled cmd/go, cmd/godoc, etc. + _, err = b.run(src, goCmd, "install", "-a", "std") + } + if err != nil { + return err + } + err = b.tour() } if err != nil { return err } - if err := b.tour(); err != nil { - return err - } - // Get version strings. var ( version string // "weekly.2012-03-04" @@ -212,6 +246,9 @@ func (b *Build) Do() error { fullVersion = bytes.TrimSpace(fullVersion) v := bytes.SplitN(fullVersion, []byte(" "), 2) version = string(v[0]) + if *versionOverride != "" { + version = *versionOverride + } // Write VERSION file. err = ioutil.WriteFile(filepath.Join(b.root, "VERSION"), fullVersion, 0644) @@ -386,9 +423,13 @@ func (b *Build) tour() error { } // Copy gotour binary to tool directory as "tour"; invoked as "go tool tour". + ext := "" + if runtime.GOOS == "windows" { + ext = ".exe" + } return cp( - filepath.Join(b.root, "pkg", "tool", b.OS+"_"+b.Arch, "tour"), - filepath.Join(b.gopath, "bin", "gotour"), + filepath.Join(b.root, "pkg", "tool", b.OS+"_"+b.Arch, "tour"+ext), + filepath.Join(b.gopath, "bin", "gotour"+ext), ) } @@ -501,7 +542,12 @@ func (b *Build) Upload(version string, filename string) error { ftype = "Source" summary = fmt.Sprintf("%s (source only)", version) } - labels = append(labels, "OpSys-"+opsys, "Type-"+ftype) + if opsys != "" { + labels = append(labels, "OpSys-"+opsys) + } + if ftype != "" { + labels = append(labels, "Type-"+ftype) + } if *addLabel != "" { labels = append(labels, *addLabel) } @@ -620,8 +666,11 @@ func cp(dst, src string) error { return err } defer df.Close() - if err := df.Chmod(fi.Mode()); err != nil { - return err + // Windows doesn't currently implement Fchmod + if runtime.GOOS != "windows" { + if err := df.Chmod(fi.Mode()); err != nil { + return err + } } _, err = io.Copy(df, sf) return err diff --git a/misc/dist/darwin/scripts/preinstall b/misc/dist/darwin/scripts/preinstall index 4cdaaa4bc..4cdaaa4bc 100644..100755 --- a/misc/dist/darwin/scripts/preinstall +++ b/misc/dist/darwin/scripts/preinstall diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el index 8a16d8a4f..6f0442aff 100644 --- a/misc/emacs/go-mode.el +++ b/misc/emacs/go-mode.el @@ -5,18 +5,79 @@ ;; license that can be found in the LICENSE file. (require 'cl) -(require 'diff-mode) (require 'ffap) -(require 'find-lisp) (require 'url) +;; XEmacs compatibility guidelines +;; - Minimum required version of XEmacs: 21.5.32 +;; - Feature that cannot be backported: POSIX character classes in +;; regular expressions +;; - Functions that could be backported but won't because 21.5.32 +;; covers them: plenty. +;; - Features that are still partly broken: +;; - godef will not work correctly if multibyte characters are +;; being used +;; - Fontification will not handle unicode correctly +;; +;; - Do not use \_< and \_> regexp delimiters directly; use +;; go--regexp-enclose-in-symbol +;; +;; - The character `_` must not be a symbol constituent but a +;; character constituent +;; +;; - Do not use process-lines +;; +;; - Use go--old-completion-list-style when using a plain list as the +;; collection for completing-read +;; +;; - Use go--kill-whole-line instead of kill-whole-line (called +;; kill-entire-line in XEmacs) +;; +;; - Use go--position-bytes instead of position-bytes +(defmacro go--xemacs-p () + `(featurep 'xemacs)) + +(defalias 'go--kill-whole-line + (if (fboundp 'kill-whole-line) + 'kill-whole-line + 'kill-entire-line)) + +;; XEmacs unfortunately does not offer position-bytes. We can fall +;; back to just using (point), but it will be incorrect as soon as +;; multibyte characters are being used. +(if (fboundp 'position-bytes) + (defalias 'go--position-bytes 'position-bytes) + (defun go--position-bytes (point) point)) + +(defun go--old-completion-list-style (list) + (mapcar (lambda (x) (cons x nil)) list)) + +;; GNU Emacs 24 has prog-mode, older GNU Emacs and XEmacs do not. +;; Ideally we'd use defalias instead, but that breaks in XEmacs. +;; +;; TODO: If XEmacs decides to add prog-mode, change this to use +;; defalias to alias prog-mode or fundamental-mode to go--prog-mode +;; and use that in define-derived-mode. +(if (not (fboundp 'prog-mode)) + (define-derived-mode prog-mode fundamental-mode "" "")) + +(defun go--regexp-enclose-in-symbol (s) + ;; XEmacs does not support \_<, GNU Emacs does. In GNU Emacs we make + ;; extensive use of \_< to support unicode in identifiers. Until we + ;; come up with a better solution for XEmacs, this solution will + ;; break fontification in XEmacs for identifiers such as "typeµ". + ;; XEmacs will consider "type" a keyword, GNU Emacs won't. + + (if (go--xemacs-p) + (concat "\\<" s "\\>") + (concat "\\_<" s "\\_>"))) + (defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]") -(defconst gofmt-stdin-tag "<standard input>") -(defconst go-identifier-regexp "[[:word:][:multibyte:]_]+") +(defconst go-identifier-regexp "[[:word:][:multibyte:]]+") (defconst go-label-regexp go-identifier-regexp) -(defconst go-type-regexp "[[:word:][:multibyte:]_*]+") -(defconst go-func-regexp (concat "\\<func\\>\\s *\\(" go-identifier-regexp "\\)")) -(defconst go-func-meth-regexp (concat "\\<func\\>\\s *\\(?:(\\s *" go-identifier-regexp "\\s +" go-type-regexp "\\s *)\\s *\\)?\\(" go-identifier-regexp "\\)(")) +(defconst go-type-regexp "[[:word:][:multibyte:]*]+") +(defconst go-func-regexp (concat (go--regexp-enclose-in-symbol "func") "\\s *\\(" go-identifier-regexp "\\)")) +(defconst go-func-meth-regexp (concat (go--regexp-enclose-in-symbol "func") "\\s *\\(?:(\\s *" go-identifier-regexp "\\s +" go-type-regexp "\\s *)\\s *\\)?\\(" go-identifier-regexp "\\)(")) (defconst go-builtins '("append" "cap" "close" "complex" "copy" "delete" "imag" "len" "make" "new" @@ -35,6 +96,7 @@ (defconst go-type-name-regexp (concat "\\(?:[*(]\\)*\\(?:" go-identifier-regexp "\\.\\)?\\(" go-identifier-regexp "\\)")) (defvar go-dangling-cache) +(defvar go-godoc-history nil) (defgroup go nil "Major mode for editing Go code" @@ -57,23 +119,27 @@ (modify-syntax-entry ?= "." st) (modify-syntax-entry ?< "." st) (modify-syntax-entry ?> "." st) - (modify-syntax-entry ?/ ". 124b" st) + (modify-syntax-entry ?/ (if (go--xemacs-p) ". 1456" ". 124b") st) (modify-syntax-entry ?* ". 23" st) (modify-syntax-entry ?\n "> b" st) (modify-syntax-entry ?\" "\"" st) (modify-syntax-entry ?\' "\"" st) (modify-syntax-entry ?` "\"" st) (modify-syntax-entry ?\\ "\\" st) - (modify-syntax-entry ?_ "_" st) + ;; It would be nicer to have _ as a symbol constituent, but that + ;; would trip up XEmacs, which does not support the \_< anchor + (modify-syntax-entry ?_ "w" st) st) "Syntax table for Go mode.") (defun go--build-font-lock-keywords () + ;; we cannot use 'symbols in regexp-opt because emacs <24 doesn't + ;; understand that (append - `((,(regexp-opt go-mode-keywords 'symbols) . font-lock-keyword-face) - (,(regexp-opt go-builtins 'symbols) . font-lock-builtin-face) - (,(regexp-opt go-constants 'symbols) . font-lock-constant-face) + `((,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) . font-lock-keyword-face) + (,(go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) . font-lock-builtin-face) + (,(go--regexp-enclose-in-symbol (regexp-opt go-constants t)) . font-lock-constant-face) (,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name (if go-fontify-function-calls @@ -82,22 +148,22 @@ `((,go-func-meth-regexp 1 font-lock-function-name-face))) ;; method name `( - ("\\<type\\>[[:space:]]*\\([^[:space:]]+\\)" 1 font-lock-type-face) ;; types - (,(concat "\\<type\\>[[:space:]]*" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types - (,(concat "\\(?:[[:space:]]+\\|\\]\\)\\[\\([[:digit:]]+\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 2 font-lock-type-face) ;; Arrays/slices - (,(concat "map\\[[^]]+\\]" go-type-name-regexp) 1 font-lock-type-face) ;; map value type + (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*\\([^[:space:]]+\\)") 1 font-lock-type-face) ;; types + (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]*" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types + (,(concat "[^[:word:][:multibyte:]]\\[\\([[:digit:]]+\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 2 font-lock-type-face) ;; Arrays/slices (,(concat "\\(" go-identifier-regexp "\\)" "{") 1 font-lock-type-face) - (,(concat "\\<map\\[" go-type-name-regexp) 1 font-lock-type-face) ;; map key type - (,(concat "\\<chan\\>[[:space:]]*\\(?:<-\\)?" go-type-name-regexp) 1 font-lock-type-face) ;; channel type - (,(concat "\\<\\(?:new\\|make\\)\\>\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) ;; new/make type + (,(concat (go--regexp-enclose-in-symbol "map") "\\[[^]]+\\]" go-type-name-regexp) 1 font-lock-type-face) ;; map value type + (,(concat (go--regexp-enclose-in-symbol "map") "\\[" go-type-name-regexp) 1 font-lock-type-face) ;; map key type + (,(concat (go--regexp-enclose-in-symbol "chan") "[[:space:]]*\\(?:<-\\)?" go-type-name-regexp) 1 font-lock-type-face) ;; channel type + (,(concat (go--regexp-enclose-in-symbol "\\(?:new\\|make\\)") "\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) ;; new/make type ;; TODO do we actually need this one or isn't it just a function call? (,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) ;; Type conversion - (,(concat "\\<func\\>[[:space:]]+(" go-identifier-regexp "[[:space:]]+" go-type-name-regexp ")") 1 font-lock-type-face) ;; Method receiver + (,(concat (go--regexp-enclose-in-symbol "func") "[[:space:]]+(" go-identifier-regexp "[[:space:]]+" go-type-name-regexp ")") 1 font-lock-type-face) ;; Method receiver ;; Like the original go-mode this also marks compound literal ;; fields. There, it was marked as to fix, but I grew quite ;; accustomed to it, so it'll stay for now. (,(concat "^[[:space:]]*\\(" go-label-regexp "\\)[[:space:]]*:\\(\\S.\\|$\\)") 1 font-lock-constant-face) ;; Labels and compound literal fields - (,(concat "\\<\\(goto\\|break\\|continue\\)\\>[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face)))) ;; labels in goto/break/continue + (,(concat (go--regexp-enclose-in-symbol "\\(goto\\|break\\|continue\\)") "[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face)))) ;; labels in goto/break/continue (defvar go-mode-map (let ((m (make-sparse-keymap))) @@ -107,6 +173,8 @@ (define-key m ":" 'go-mode-insert-and-indent) (define-key m "=" 'go-mode-insert-and-indent) (define-key m (kbd "C-c C-a") 'go-import-add) + (define-key m (kbd "C-c C-j") 'godef-jump) + (define-key m (kbd "C-c C-d") 'godef-describe) m) "Keymap used by Go mode to implement electric keys.") @@ -140,7 +208,7 @@ It skips over whitespace, comments, cases and labels and, if STOP-AT-STRING is not true, over strings." (let (pos (start-pos (point))) - (skip-chars-backward "\n[:blank:]") + (skip-chars-backward "\n\s\t") (if (and (save-excursion (beginning-of-line) (go-in-string-p)) (looking-back "`") (not stop-at-string)) (backward-char)) (if (and (go-in-string-p) (not stop-at-string)) @@ -177,6 +245,28 @@ STOP-AT-STRING is not true, over strings." (puthash cur-line val go-dangling-cache)))) val)) +(defun go--at-function-definition () + "Return non-nil if point is on the opening curly brace of a +function definition. + +We do this by first calling (beginning-of-defun), which will take +us to the start of *some* function. We then look for the opening +curly brace of that function and compare its position against the +curly brace we are checking. If they match, we return non-nil." + (if (= (char-after) ?\{) + (save-excursion + (let ((old-point (point)) + start-nesting) + (beginning-of-defun) + (when (looking-at "func ") + (setq start-nesting (go-paren-level)) + (skip-chars-forward "^{") + (while (> (go-paren-level) start-nesting) + (forward-char) + (skip-chars-forward "^{") 0) + (if (and (= (go-paren-level) start-nesting) (= old-point (point))) + t)))))) + (defun go-goto-opening-parenthesis (&optional char) (let ((start-nesting (go-paren-level))) (while (and (not (bobp)) @@ -189,6 +279,20 @@ STOP-AT-STRING is not true, over strings." (go-goto-beginning-of-string-or-comment) (backward-char)))))) +(defun go--indentation-for-opening-parenthesis () + "Return the semantic indentation for the current opening parenthesis. + +If point is on an opening curly brace and said curly brace +belongs to a function declaration, the indentation of the func +keyword will be returned. Otherwise the indentation of the +current line will be returned." + (save-excursion + (if (go--at-function-definition) + (progn + (beginning-of-defun) + (current-indentation)) + (current-indentation)))) + (defun go-indentation-at-point () (save-excursion (let (start-nesting (outindent 0)) @@ -202,7 +306,7 @@ STOP-AT-STRING is not true, over strings." (go-goto-opening-parenthesis (char-after)) (if (go-previous-line-has-dangling-op-p) (- (current-indentation) tab-width) - (current-indentation))) + (go--indentation-for-opening-parenthesis))) ((progn (go--backward-irrelevant t) (looking-back go-dangling-operators-regexp)) ;; only one nesting for all dangling operators in one operation (if (go-previous-line-has-dangling-op-p) @@ -213,7 +317,7 @@ STOP-AT-STRING is not true, over strings." ((progn (go-goto-opening-parenthesis) (< (go-paren-level) start-nesting)) (if (go-previous-line-has-dangling-op-p) (current-indentation) - (+ (current-indentation) tab-width))) + (+ (go--indentation-for-opening-parenthesis) tab-width))) (t (current-indentation)))))) @@ -274,7 +378,7 @@ STOP-AT-STRING is not true, over strings." (forward-char)))) ;;;###autoload -(define-derived-mode go-mode fundamental-mode "Go" +(define-derived-mode go-mode prog-mode "Go" "Major mode for editing Go source text. This mode provides (not just) basic editing capabilities for @@ -299,12 +403,26 @@ The following extra functions are defined: - `go-goto-imports' - `go-play-buffer' and `go-play-region' - `go-download-play' +- `godef-describe' and `godef-jump' If you want to automatically run `gofmt' before saving a file, add the following hook to your emacs configuration: \(add-hook 'before-save-hook 'gofmt-before-save) +If you want to use `godef-jump' instead of etags (or similar), +consider binding godef-jump to `M-.', which is the default key +for `find-tag': + +\(add-hook 'go-mode-hook (lambda () + (local-set-key (kbd \"M-.\") 'godef-jump))) + +Please note that godef is an external dependency. You can install +it with + +go get code.google.com/p/rog-go/exp/cmd/godef + + If you're looking for even more integration with Go, namely on-the-fly syntax checking, auto-completion and snippets, it is recommended that you look at goflymake @@ -360,101 +478,95 @@ recommended that you look at goflymake ;;;###autoload (add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode)) +(defun go--apply-rcs-patch (patch-buffer) + "Apply an RCS-formatted diff from PATCH-BUFFER to the current +buffer." + (let ((target-buffer (current-buffer)) + ;; Relative offset between buffer line numbers and line numbers + ;; in patch. + ;; + ;; Line numbers in the patch are based on the source file, so + ;; we have to keep an offset when making changes to the + ;; buffer. + ;; + ;; Appending lines decrements the offset (possibly making it + ;; negative), deleting lines increments it. This order + ;; simplifies the forward-line invocations. + (line-offset 0)) + (save-excursion + (with-current-buffer patch-buffer + (goto-char (point-min)) + (while (not (eobp)) + (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)") + (error "invalid rcs patch or internal error in go--apply-rcs-patch")) + (forward-line) + (let ((action (match-string 1)) + (from (string-to-number (match-string 2))) + (len (string-to-number (match-string 3)))) + (cond + ((equal action "a") + (let ((start (point))) + (forward-line len) + (let ((text (buffer-substring start (point)))) + (with-current-buffer target-buffer + (decf line-offset len) + (goto-char (point-min)) + (forward-line (- from len line-offset)) + (insert text))))) + ((equal action "d") + (with-current-buffer target-buffer + (goto-char (point-min)) + (forward-line (- from line-offset 1)) + (incf line-offset len) + (go--kill-whole-line len))) + (t + (error "invalid rcs patch or internal error in go--apply-rcs-patch"))))))))) + (defun gofmt () - "Pipe the current buffer through the external tool `gofmt`. -Replace the current buffer on success; display errors on failure." + "Formats the current buffer according to the gofmt tool." (interactive) - (let ((currconf (current-window-configuration))) - (let ((srcbuf (current-buffer)) - (filename buffer-file-name) - (patchbuf (get-buffer-create "*Gofmt patch*"))) - (with-current-buffer patchbuf - (let ((errbuf (get-buffer-create "*Gofmt Errors*")) - ;; use utf-8 with subprocesses - (coding-system-for-read 'utf-8) - (coding-system-for-write 'utf-8)) - (with-current-buffer errbuf - (setq buffer-read-only nil) - (erase-buffer)) - (with-current-buffer srcbuf - (save-restriction - (let (deactivate-mark) - (widen) - ;; If this is a new file, diff-mode can't apply a - ;; patch to a non-exisiting file, so replace the buffer - ;; completely with the output of 'gofmt'. - ;; If the file exists, patch it to keep the 'undo' list happy. - (let* ((newfile (not (file-exists-p filename))) - (flag (if newfile "" " -d"))) - - ;; diff-mode doesn't work too well with missing - ;; end-of-file newline, so add one - (if (/= (char-after (1- (point-max))) ?\n) - (save-excursion - (goto-char (point-max)) - (insert ?\n))) - - (if (zerop (shell-command-on-region (point-min) (point-max) - (concat "gofmt" flag) - patchbuf nil errbuf)) - ;; gofmt succeeded: replace buffer or apply patch hunks. - (let ((old-point (point)) - (old-mark (mark t))) - (kill-buffer errbuf) - (if newfile - ;; New file, replace it (diff-mode won't work) - (gofmt--replace-buffer srcbuf patchbuf) - ;; Existing file, patch it - (gofmt--apply-patch filename srcbuf patchbuf)) - (goto-char (min old-point (point-max))) - ;; Restore the mark and point - (if old-mark (push-mark (min old-mark (point-max)) t)) - (set-window-configuration currconf)) - - ;; gofmt failed: display the errors - (message "Could not apply gofmt. Check errors for details") - (gofmt--process-errors filename errbuf)))))) - - ;; Collapse any window opened on outbuf if shell-command-on-region - ;; displayed it. - (delete-windows-on patchbuf))) - (kill-buffer patchbuf)))) - -(defun gofmt--replace-buffer (srcbuf patchbuf) - (with-current-buffer srcbuf - (erase-buffer) - (insert-buffer-substring patchbuf)) - (message "Applied gofmt")) - -(defun gofmt--apply-patch (filename srcbuf patchbuf) - ;; apply all the patch hunks - (let (changed) + (let ((tmpfile (make-temp-file "gofmt" nil ".go")) + (patchbuf (get-buffer-create "*Gofmt patch*")) + (errbuf (get-buffer-create "*Gofmt Errors*")) + (coding-system-for-read 'utf-8) + (coding-system-for-write 'utf-8)) + + (with-current-buffer errbuf + (setq buffer-read-only nil) + (erase-buffer)) (with-current-buffer patchbuf - (goto-char (point-min)) - ;; The .* is for TMPDIR, but to avoid dealing with TMPDIR - ;; having a trailing / or not, it's easier to just search for .* - ;; especially as we're only replacing the first instance. - (if (re-search-forward "^--- \\(.*/gofmt[0-9]*\\)" nil t) - (replace-match filename nil nil nil 1)) - (condition-case nil - (while t - (diff-hunk-next) - (diff-apply-hunk) - (setq changed t)) - ;; When there's no more hunks, diff-hunk-next signals an error, ignore it - (error nil))) - (if changed (message "Applied gofmt") (message "Buffer was already gofmted")))) - -(defun gofmt--process-errors (filename errbuf) + (erase-buffer)) + + (write-region nil nil tmpfile) + + ;; We're using errbuf for the mixed stdout and stderr output. This + ;; is not an issue because gofmt -w does not produce any stdout + ;; output in case of success. + (if (zerop (call-process "gofmt" nil errbuf nil "-w" tmpfile)) + (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile)) + (progn + (kill-buffer errbuf) + (message "Buffer is already gofmted")) + (go--apply-rcs-patch patchbuf) + (kill-buffer errbuf) + (message "Applied gofmt")) + (message "Could not apply gofmt. Check errors for details") + (gofmt--process-errors (buffer-file-name) tmpfile errbuf)) + + (kill-buffer patchbuf) + (delete-file tmpfile))) + + +(defun gofmt--process-errors (filename tmpfile errbuf) ;; Convert the gofmt stderr to something understood by the compilation mode. (with-current-buffer errbuf (goto-char (point-min)) (insert "gofmt errors:\n") - (if (search-forward gofmt-stdin-tag nil t) - (replace-match (file-name-nondirectory filename) nil t)) - (display-buffer errbuf) - (compilation-mode))) + (while (search-forward-regexp (concat "^\\(" (regexp-quote tmpfile) "\\):") nil t) + (replace-match (file-name-nondirectory filename) t t nil 1)) + (compilation-mode) + (display-buffer errbuf))) ;;;###autoload (defun gofmt-before-save () @@ -476,10 +588,10 @@ you save any file, kind of defeating the point of autoloading." (symbol (if bounds (buffer-substring-no-properties (car bounds) (cdr bounds))))) - (read-string (if symbol - (format "godoc (default %s): " symbol) - "godoc: ") - nil nil symbol))) + (completing-read (if symbol + (format "godoc (default %s): " symbol) + "godoc: ") + (go--old-completion-list-style (go-packages)) nil nil nil 'go-godoc-history symbol))) (defun godoc--get-buffer (query) "Get an empty buffer for a godoc query." @@ -628,7 +740,7 @@ uncommented, otherwise a new import will be added." (interactive (list current-prefix-arg - (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go-packages))))) + (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go--old-completion-list-style (go-packages)))))) (save-excursion (let (as line import-start) (if arg @@ -653,11 +765,34 @@ uncommented, otherwise a new import will be added." ('none (insert "\nimport (\n\t" line "\n)\n"))))))) (defun go-root-and-paths () - (let* ((output (process-lines "go" "env" "GOROOT" "GOPATH")) + (let* ((output (split-string (shell-command-to-string "go env GOROOT GOPATH") "\n")) (root (car output)) - (paths (split-string (car (cdr output)) ":"))) + (paths (split-string (cadr output) ":"))) (append (list root) paths))) +(defun go--string-prefix-p (s1 s2 &optional ignore-case) + "Return non-nil if S1 is a prefix of S2. +If IGNORE-CASE is non-nil, the comparison is case-insensitive." + (eq t (compare-strings s1 nil nil + s2 0 (length s1) ignore-case))) + +(defun go--directory-dirs (dir) + "Recursively return all subdirectories in DIR." + (if (file-directory-p dir) + (let ((dir (directory-file-name dir)) + (dirs '()) + (files (directory-files dir nil nil t))) + (dolist (file files) + (unless (member file '("." "..")) + (let ((file (concat dir "/" file))) + (if (file-directory-p file) + (setq dirs (append (cons file + (go--directory-dirs file)) + dirs)))))) + dirs) + '())) + + (defun go-packages () (sort (delete-dups @@ -667,12 +802,12 @@ uncommented, otherwise a new import will be added." (mapcan (lambda (dir) (mapcar (lambda (file) (let ((sub (substring file (length pkgdir) -2))) - (unless (or (string-prefix-p "obj/" sub) (string-prefix-p "tool/" sub)) + (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub)) (mapconcat 'identity (cdr (split-string sub "/")) "/")))) (if (file-directory-p dir) (directory-files dir t "\\.a$")))) (if (file-directory-p pkgdir) - (find-lisp-find-files-internal pkgdir 'find-lisp-file-predicate-is-directory 'find-lisp-default-directory-predicate))))) + (go--directory-dirs pkgdir))))) (go-root-and-paths))) 'string<)) @@ -712,9 +847,63 @@ will be commented, otherwise they will be removed completely." (beginning-of-line) (if arg (comment-region (line-beginning-position) (line-end-position)) - (let ((kill-whole-line t)) - (kill-line)))) + (go--kill-whole-line))) (message "Removed %d imports" (length lines))) (if flymake-state (flymake-mode-on))))) +(defun godef--find-file-line-column (specifier) + "Given a file name in the format of `filename:line:column', +visit FILENAME and go to line LINE and column COLUMN." + (let* ((components (split-string specifier ":")) + (line (string-to-number (nth 1 components))) + (column (string-to-number (nth 2 components)))) + (with-current-buffer (find-file (car components)) + (goto-char (point-min)) + (forward-line (1- line)) + (beginning-of-line) + (forward-char (1- column)) + (if (buffer-modified-p) + (message "Buffer is modified, file position might not have been correct"))))) + +(defun godef--call (point) + "Call godef, acquiring definition position and expression +description at POINT." + (if (go--xemacs-p) + (message "godef does not reliably work in XEmacs, expect bad results")) + (if (not buffer-file-name) + (message "Cannot use godef on a buffer without a file name") + (let ((outbuf (get-buffer-create "*godef*"))) + (with-current-buffer outbuf + (erase-buffer)) + (call-process-region (point-min) (point-max) "godef" nil outbuf nil "-i" "-t" "-f" (file-truename buffer-file-name) "-o" (number-to-string (go--position-bytes (point)))) + (with-current-buffer outbuf + (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n"))))) + +(defun godef-describe (point) + "Describe the expression at POINT." + (interactive "d") + (condition-case nil + (let ((description (nth 1 (godef--call point)))) + (if (string= "" description) + (message "No description found for expression at point") + (message "%s" description))) + (file-error (message "Could not run godef binary")))) + +(defun godef-jump (point) + "Jump to the definition of the expression at POINT." + (interactive "d") + (condition-case nil + (let ((file (car (godef--call point)))) + (cond + ((string= "-" file) + (message "godef: expression is not defined anywhere")) + ((string= "godef: no identifier found" file) + (message "%s" file)) + ((go--string-prefix-p "godef: no declaration found for " file) + (message "%s" file)) + (t + (push-mark) + (godef--find-file-line-column file)))) + (file-error (message "Could not run godef binary")))) + (provide 'go-mode) diff --git a/misc/kate/go.xml b/misc/kate/go.xml index 1e00cfcbf..c187eda82 100644 --- a/misc/kate/go.xml +++ b/misc/kate/go.xml @@ -95,7 +95,7 @@ <HlCHex attribute="Hex" context="#stay"/> <HlCChar attribute="Char" context="#stay"/> <DetectChar attribute="String" context="String" char="""/> - <DetectChar attribute="Multiline String" context="Multiline String" char="`"/> + <DetectChar attribute="String" context="Multiline String" char="`"/> <Detect2Chars attribute="Comment" context="Comment 1" char="/" char1="/"/> <Detect2Chars attribute="Comment" context="Comment 2" char="/" char1="*" beginRegion="Comment"/> <AnyChar attribute="Symbol" context="#stay" String=":!%&()+,-/.*<=>?[]|~^;"/> diff --git a/misc/notepadplus/README b/misc/notepadplus/README index 000d31746..000d31746 100755..100644 --- a/misc/notepadplus/README +++ b/misc/notepadplus/README diff --git a/misc/notepadplus/go.xml b/misc/notepadplus/go.xml index 237ef6b4b..237ef6b4b 100755..100644 --- a/misc/notepadplus/go.xml +++ b/misc/notepadplus/go.xml diff --git a/misc/notepadplus/userDefineLang.xml b/misc/notepadplus/userDefineLang.xml index 2954aad48..2954aad48 100755..100644 --- a/misc/notepadplus/userDefineLang.xml +++ b/misc/notepadplus/userDefineLang.xml diff --git a/misc/pprof b/misc/pprof index 7c379acbe..f471c7395 100755 --- a/misc/pprof +++ b/misc/pprof @@ -81,6 +81,11 @@ use Getopt::Long; my $PPROF_VERSION = "1.5"; +# NOTE: All mentions of c++filt have been expunged from this script +# because (1) we don't use C++, and (2) the copy of c++filt that ships +# on OS X is from 2007 and destroys nm output by "demangling" the +# first two columns (address and symbol type). + # These are the object tools we use which can come from a # user-specified location using --tools, from the PPROF_TOOLS # environment variable, or from the environment. @@ -88,7 +93,6 @@ my %obj_tool_map = ( "objdump" => "objdump", "nm" => "nm", "addr2line" => "addr2line", - "c++filt" => "c++filt", ## ConfigureObjTools may add architecture-specific entries: #"nm_pdb" => "nm-pdb", # for reading windows (PDB-format) executables #"addr2line_pdb" => "addr2line-pdb", # ditto @@ -3093,9 +3097,7 @@ sub FetchSymbols { my $url = SymbolPageURL(); $url = ResolveRedirectionForCurl($url); my $command_line = "$CURL -sd '\@$main::tmpfile_sym' '$url'"; - # We use c++filt in case $SYMBOL_PAGE gives us mangled symbols. - my $cppfilt = $obj_tool_map{"c++filt"}; - open(SYMBOL, "$command_line | $cppfilt |") or error($command_line); + open(SYMBOL, "$command_line |") or error($command_line); ReadSymbols(*SYMBOL{IO}, $symbol_map); close(SYMBOL); } @@ -4415,11 +4417,9 @@ sub MapToSymbols { $cmd = "$addr2line --demangle -f -C -e $image"; } - if (system("$addr2line --help >/dev/null 2>&1") != 0) { - # addr2line must not exist. Fall back to go tool addr2line. - $addr2line = "go tool addr2line"; - $cmd = "$addr2line $image"; - } + # Use the go version because we know it works on all platforms + $addr2line = "go tool addr2line"; + $cmd = "$addr2line $image"; # If "addr2line" isn't installed on the system at all, just use # nm to get what info we can (function names, but not line numbers). @@ -4790,7 +4790,6 @@ sub GetProcedureBoundaries { } my $nm = $obj_tool_map{"nm"}; - my $cppfilt = $obj_tool_map{"c++filt"}; # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm # binary doesn't support --demangle. In addition, for OS X we need @@ -4799,27 +4798,21 @@ sub GetProcedureBoundaries { # in an incompatible way. So first we test whether our nm supports # --demangle and -f. my $demangle_flag = ""; - my $cppfilt_flag = ""; if (system("$nm --demangle $image >/dev/null 2>&1") == 0) { # In this mode, we do "nm --demangle <foo>" $demangle_flag = "--demangle"; - $cppfilt_flag = ""; - } elsif (system("$cppfilt $image >/dev/null 2>&1") == 0) { - # In this mode, we do "nm <foo> | c++filt" - $cppfilt_flag = " | $cppfilt"; - }; + } my $flatten_flag = ""; if (system("$nm -f $image >/dev/null 2>&1") == 0) { $flatten_flag = "-f"; } - # Finally, in the case $imagie isn't a debug library, we try again with - # -D to at least get *exported* symbols. If we can't use --demangle, - # we use c++filt instead, if it exists on this system. + # Finally, in the case $image isn't a debug library, we try again with + # -D to at least get *exported* symbols. If we can't use --demangle, too bad. my @nm_commands = ("$nm -n $flatten_flag $demangle_flag" . - " $image 2>/dev/null $cppfilt_flag", + " $image 2>/dev/null", "$nm -D -n $flatten_flag $demangle_flag" . - " $image 2>/dev/null $cppfilt_flag", + " $image 2>/dev/null", # go tool nm is for Go binaries "go tool nm $image 2>/dev/null | sort"); diff --git a/misc/swig/stdio/Makefile b/misc/swig/stdio/Makefile deleted file mode 100644 index 0f23345e4..000000000 --- a/misc/swig/stdio/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2011 The Go Authors. All rights reserved. -# Use of this source code is governed by a BSD-style -# license that can be found in the LICENSE file. - -include ../../../src/Make.inc - -TARG=swig/file -SWIGFILES=\ - file.swig - -CLEANFILES+=hello - -include ../../../src/Make.pkg - -%: install %.go - $(GC) $(GCFLAGS) $(GCIMPORTS) $*.go - $(LD) $(SWIG_RPATH) -o $@ $*.$O diff --git a/misc/vim/plugin/godoc.vim b/misc/vim/plugin/godoc.vim index a9abb7ae6..a7b84de74 100644 --- a/misc/vim/plugin/godoc.vim +++ b/misc/vim/plugin/godoc.vim @@ -70,13 +70,26 @@ endfunction function! s:Godoc(...) let word = join(a:000, ' ') if !len(word) + let oldiskeyword = &iskeyword + setlocal iskeyword+=. let word = expand('<cword>') + let &iskeyword = oldiskeyword endif let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g') - if !len(word) + let words = split(word, '\.') + if !len(words) return endif - call s:GodocWord(word) + call s:GodocWord(words[0]) + if len(words) > 1 + if search('^\%(const\|var\|type\|\s\+\) ' . words[1] . '\s\+=\s') + return + endif + if search('^func ' . words[1] . '(') + return + endif + echo 'No documentation found for "' . word . '".' + endif endfunction command! -nargs=* -range -complete=customlist,go#complete#Package Godoc :call s:Godoc(<q-args>) diff --git a/misc/zsh/go b/misc/zsh/go index dce25547d..18bcaaff2 100644 --- a/misc/zsh/go +++ b/misc/zsh/go @@ -20,6 +20,7 @@ __go_tool_complete() { 'build[compile packages and dependencies]' 'clean[remove object files]' 'doc[run godoc on package sources]' + 'env[print Go environment information]' 'fix[run go tool fix on packages]' 'fmt[run gofmt on package sources]' 'get[download and install packages and dependencies]' @@ -40,12 +41,17 @@ __go_tool_complete() { build_flags=( '-a[force reinstallation of packages that are already up-to-date]' '-n[print the commands but do not run them]' - "-p[number of parallel builds]:number" + '-p[number of parallel builds]:number' + '-race[enable data race detection]' '-x[print the commands]' - "-work[print temporary directory name and keep it]" - "-gcflags[flags for 5g/6g/8g]:flags" - "-ldflags[flags for 5l/6l/8l]:flags" - "-gccgoflags[flags for gccgo]:flags" + '-work[print temporary directory name and keep it]' + '-ccflags[flags for 5c/6c/8c]:flags' + '-gcflags[flags for 5g/6g/8g]:flags' + '-ldflags[flags for 5l/6l/8l]:flags' + '-gccgoflags[flags for gccgo]:flags' + '-compiler[name of compiler to use]:name' + '-installsuffix[suffix to add to package directory]:suffix' + '-tags[list of build tags to consider satisfied]:tags' ) __go_list() { local expl importpaths @@ -63,7 +69,7 @@ __go_tool_complete() { install) _arguments -s -w : ${build_flags[@]} \ "-v[show package names]" \ - '*:importpaths:__go_list' + '*:importpaths:__go_list' ;; get) _arguments -s -w : \ @@ -88,7 +94,10 @@ __go_tool_complete() { "-cpu[values of GOMAXPROCS to use]:number list" \ "-run[run tests and examples matching regexp]:regexp" \ "-bench[run benchmarks matching regexp]:regexp" \ + "-benchmem[print memory allocation stats]" \ "-benchtime[run each benchmark until taking this long]:duration" \ + "-blockprofile[write goroutine blocking profile to file]:file" \ + "-blockprofilerate[set sampling rate of goroutine blocking profile]:number" \ "-timeout[kill test after that duration]:duration" \ "-cpuprofile[write CPU profile to file]:file:_files" \ "-memprofile[write heap profile to file]:file:_files" \ @@ -98,7 +107,7 @@ __go_tool_complete() { help) _values "${commands[@]}" \ 'gopath[GOPATH environment variable]' \ - 'importpath[description of import paths]' \ + 'packages[description of package lists]' \ 'remote[remote import path syntax]' \ 'testflag[description of testing flags]' \ 'testfunc[description of testing functions]' |