cmd/guru: clean ups to command-line interface

-pos is now a positional argument; -scope is now a flag.
Tidy up usage message.
Update Vim and Emacs scripts and test
Remove call to GOMAXPROCS.
Use log.{SetPrefix,SetFlags} and Fatalf

Change-Id: I3786ed83aecb17d622b29d2a538a374f813f0adc
Reviewed-on: https://go-review.googlesource.com/19438
Reviewed-by: Michael Matloob <matloob@golang.org>
This commit is contained in:
Alan Donovan 2016-02-12 00:04:56 -05:00 committed by Alan Donovan
parent 789265387f
commit 00b8b1eb95
4 changed files with 55 additions and 78 deletions

View File

@ -26,15 +26,14 @@ mv -f $GOPATH/bin/guru $GOROOT/bin/
$GOROOT/bin/guru >$log 2>&1 || true # (prints usage and exits 1) $GOROOT/bin/guru >$log 2>&1 || true # (prints usage and exits 1)
grep -q "Run.*help" $log || die "$GOROOT/bin/guru not installed" grep -q "Run.*help" $log || die "$GOROOT/bin/guru not installed"
# Run Emacs, set the scope to the guru tool itself, # Run Emacs, set the scope to the guru tool itself,
# load ./main.go, and describe the "fmt" import. # load ./main.go, and describe the "fmt" import.
emacs --batch --no-splash --no-window-system --no-init \ emacs --batch --no-splash --no-window-system --no-init \
--load $GOROOT/misc/emacs/go-mode.el \ --load $GOPATH/src/github.com/dominikh/go-mode.el/go-mode.el \
--load $thisdir/guru.el \ --load $thisdir/guru.el \
--eval ' --eval '
(progn (progn
(setq go-guru-scope "golang.org/x/tools/cmd/guru") (princ (emacs-version)) ; requires Emacs v23
(find-file "'$thisdir'/main.go") (find-file "'$thisdir'/main.go")
(search-forward "\"fmt\"") (search-forward "\"fmt\"")
(backward-char) (backward-char)

View File

@ -2,20 +2,16 @@
;;; Integration of the Go 'guru' analysis tool into Emacs. ;;; Integration of the Go 'guru' analysis tool into Emacs.
;;; ;;;
;;; To install the Go guru, run: ;;; To install the Go guru, run:
;;; % export GOROOT=... GOPATH=... ;;; $ go get golang.org/x/tools/cmd/guru
;;; % go get golang.org/x/tools/cmd/guru
;;; % mv $GOPATH/bin/guru $GOROOT/bin/
;;; ;;;
;;; Load this file into Emacs and set go-guru-scope to your ;;; Load this file into Emacs and set go-guru-scope to your
;;; configuration. Then, find a file of Go source code, enable ;;; configuration. Then, find a file of Go source code, enable
;;; go-guru-mode, select an expression of interest, and press `C-c C-o d' ;;; go-guru-mode, select an expression of interest, and press `C-c C-o d'
;;; (for "describe") or run one of the other go-guru-xxx commands. ;;; (for "describe") or run one of the other go-guru-xxx commands.
;;;
;;; TODO(adonovan): simplify installation and configuration by making
;;; guru a subcommand of 'go tool'.
(require 'compile) (require 'compile)
(require 'go-mode) (require 'go-mode)
(require 'simple)
(require 'cl) (require 'cl)
(defgroup go-guru nil (defgroup go-guru nil
@ -99,14 +95,14 @@ file name with a small hyperlink. Display the result."
(string-equal "" go-guru-scope) (string-equal "" go-guru-scope)
(go-guru-set-scope)) (go-guru-set-scope))
(let* ((filename (file-truename buffer-file-name)) (let* ((filename (file-truename buffer-file-name))
(posflag (if (use-region-p) (posn (if (use-region-p)
(format "-pos=%s:#%d,#%d" (format "%s:#%d,#%d"
filename filename
(1- (go--position-bytes (region-beginning))) (1- (go--position-bytes (region-beginning)))
(1- (go--position-bytes (region-end)))) (1- (go--position-bytes (region-end))))
(format "-pos=%s:#%d" (format "%s:#%d"
filename filename
(1- (position-bytes (point)))))) (1- (position-bytes (point))))))
(env-vars (go-root-and-paths)) (env-vars (go-root-and-paths))
(goroot-env (concat "GOROOT=" (car env-vars))) (goroot-env (concat "GOROOT=" (car env-vars)))
(gopath-env (concat "GOPATH=" (mapconcat #'identity (cdr env-vars) ":")))) (gopath-env (concat "GOPATH=" (mapconcat #'identity (cdr env-vars) ":"))))
@ -114,8 +110,8 @@ file name with a small hyperlink. Display the result."
(setq buffer-read-only nil) (setq buffer-read-only nil)
(erase-buffer) (erase-buffer)
(insert "Go Guru\n") (insert "Go Guru\n")
(let ((args (append (list go-guru-command nil t nil posflag mode) (let ((args (list go-guru-command nil t nil
(split-string go-guru-scope " " t)))) "-scope" go-guru-scope mode posn)))
;; Log the command to *Messages*, for debugging. ;; Log the command to *Messages*, for debugging.
(message "Command: %s:" args) (message "Command: %s:" args)
(message nil) ; clears/shrinks minibuffer (message nil) ; clears/shrinks minibuffer

View File

@ -68,14 +68,14 @@ func! s:RunGuru(mode, selected) range abort
if a:selected != -1 if a:selected != -1
let pos1 = s:getpos(line("'<"), col("'<")) let pos1 = s:getpos(line("'<"), col("'<"))
let pos2 = s:getpos(line("'>"), col("'>")) let pos2 = s:getpos(line("'>"), col("'>"))
let cmd = printf('%s -pos=%s:#%d,#%d %s %s', let cmd = printf('%s -scope=\'%s\' %s %s:#%d,#%d',
\ s:go_guru, \ s:go_guru,
\ shellescape(fname), pos1, pos2, a:mode, shellescape(sname)) \ shellescape(fname), shellescape(sname), a:mode, pos1, pos2)
else else
let pos = s:getpos(line('.'), col('.')) let pos = s:getpos(line('.'), col('.'))
let cmd = printf('%s -pos=%s:#%d %s %s', let cmd = printf('%s -scope=\'%s\' %s %s:#%d',
\ s:go_guru, \ s:go_guru,
\ shellescape(fname), pos, a:mode, shellescape(sname)) \ shellescape(fname), shellescape(sname), a:mode, pos)
endif endif
call s:qflist(system(cmd)) call s:qflist(system(cmd))
endfun endfun

View File

@ -21,23 +21,20 @@ import (
"io" "io"
"log" "log"
"os" "os"
"runtime"
"runtime/pprof" "runtime/pprof"
"strings"
"golang.org/x/tools/go/buildutil" "golang.org/x/tools/go/buildutil"
"golang.org/x/tools/go/loader"
) )
var posFlag = flag.String("pos", "", // flags
"Filename and byte offset or extent of a syntax element about which to query, "+ var (
"e.g. foo.go:#123,#456, bar.go:#123.") scopeFlag = flag.String("scope", "", "comma-separated list of `packages` to which the analysis should be limited (default=all)")
ptalogFlag = flag.String("ptalog", "", "write points-to analysis log to `file`")
var ptalogFlag = flag.String("ptalog", "", formatFlag = flag.String("format", "plain", "output `format`; one of {plain,json,xml}")
"Location of the points-to analysis log file, or empty to disable logging.") reflectFlag = flag.Bool("reflect", false, "analyze reflection soundly (slow)")
cpuprofileFlag = flag.String("cpuprofile", "", "write CPU profile to `file`")
var formatFlag = flag.String("format", "plain", "Output format. One of {plain,json,xml}.") )
var reflectFlag = flag.Bool("reflect", false, "Analyze reflection soundly (slow).")
func init() { func init() {
flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc) flag.Var((*buildutil.TagsFlag)(&build.Default.BuildTags), "tags", buildutil.TagsFlagDoc)
@ -46,15 +43,7 @@ func init() {
const useHelp = "Run 'guru -help' for more information.\n" const useHelp = "Run 'guru -help' for more information.\n"
const helpMessage = `Go source code guru. const helpMessage = `Go source code guru.
Usage: guru [<flag> ...] <mode> <args> ... Usage: guru [flags] <mode> <position>
The -format flag controls the output format:
plain an editor-friendly format in which every line of output
is of the form "pos: text", where pos is "-" if unknown.
json structured data in JSON syntax.
xml structured data in XML syntax.
The -pos flag is required in all modes.
The mode argument determines the query to perform: The mode argument determines the query to perform:
@ -71,29 +60,24 @@ The mode argument determines the query to perform:
what show basic information about the selected syntax node what show basic information about the selected syntax node
whicherrs show possible values of the selected error variable whicherrs show possible values of the selected error variable
The user manual is available here: http://golang.org/s/guru-user-manual The position argument specifies the filename and byte offset (or range)
of the syntax element to query. For example:
Examples: foo.go:#123,#128
bar.go:#123
Describe the syntax at offset 530 in this file (an import spec): The -format flag controls the output format:
% guru -pos=src/golang.org/x/tools/cmd/guru/main.go:#530 describe \ plain an editor-friendly format in which every line of output
golang.org/x/tools/cmd/guru is of the form "pos: text", where pos is "-" if unknown.
json structured data in JSON syntax.
xml structured data in XML syntax.
` + loader.FromArgsUsage User manual: http://golang.org/s/oracle-user-manual
var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file") Example: describe syntax at offset 530 in this file (an import spec):
func init() { $ guru describe src/golang.org/x/tools/cmd/guru/main.go:#530
// If $GOMAXPROCS isn't set, use the full capacity of the machine. `
// For small machines, use at least 4 threads.
if os.Getenv("GOMAXPROCS") == "" {
n := runtime.NumCPU()
if n < 4 {
n = 4
}
runtime.GOMAXPROCS(n)
}
}
func printHelp() { func printHelp() {
fmt.Fprintln(os.Stderr, helpMessage) fmt.Fprintln(os.Stderr, helpMessage)
@ -102,6 +86,9 @@ func printHelp() {
} }
func main() { func main() {
log.SetPrefix("guru: ")
log.SetFlags(0)
// Don't print full help unless -help was requested. // Don't print full help unless -help was requested.
// Just gently remind users that it's there. // Just gently remind users that it's there.
flag.Usage = func() { fmt.Fprint(os.Stderr, useHelp) } flag.Usage = func() { fmt.Fprint(os.Stderr, useHelp) }
@ -115,13 +102,12 @@ func main() {
} }
args := flag.Args() args := flag.Args()
if len(args) == 0 || args[0] == "" { if len(args) != 2 {
fmt.Fprint(os.Stderr, "guru: a mode argument is required.\n"+useHelp) flag.Usage()
os.Exit(2) os.Exit(2)
} }
mode, posn := args[0], args[1]
mode := args[0]
args = args[1:]
if mode == "help" { if mode == "help" {
printHelp() printHelp()
os.Exit(2) os.Exit(2)
@ -147,8 +133,8 @@ func main() {
} }
// Profiling support. // Profiling support.
if *cpuprofile != "" { if *cpuprofileFlag != "" {
f, err := os.Create(*cpuprofile) f, err := os.Create(*cpuprofileFlag)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -161,23 +147,21 @@ func main() {
case "json", "plain", "xml": case "json", "plain", "xml":
// ok // ok
default: default:
fmt.Fprintf(os.Stderr, "guru: illegal -format value: %q.\n"+useHelp, *formatFlag) log.Fatalf("illegal -format value: %q.\n"+useHelp, *formatFlag)
os.Exit(2)
} }
// Ask the guru. // Ask the guru.
query := Query{ query := Query{
Mode: mode, Mode: mode,
Pos: *posFlag, Pos: posn,
Build: &build.Default, Build: &build.Default,
Scope: args, Scope: strings.Split(*scopeFlag, ","),
PTALog: ptalog, PTALog: ptalog,
Reflection: *reflectFlag, Reflection: *reflectFlag,
} }
if err := Run(&query); err != nil { if err := Run(&query); err != nil {
fmt.Fprintf(os.Stderr, "guru: %s\n", err) log.Fatal(err)
os.Exit(1)
} }
// Print the result. // Print the result.
@ -185,16 +169,14 @@ func main() {
case "json": case "json":
b, err := json.MarshalIndent(query.Serial(), "", "\t") b, err := json.MarshalIndent(query.Serial(), "", "\t")
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "guru: JSON error: %s\n", err) log.Fatalf("JSON error: %s", err)
os.Exit(1)
} }
os.Stdout.Write(b) os.Stdout.Write(b)
case "xml": case "xml":
b, err := xml.MarshalIndent(query.Serial(), "", "\t") b, err := xml.MarshalIndent(query.Serial(), "", "\t")
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "guru: XML error: %s\n", err) log.Fatalf("XML error: %s", err)
os.Exit(1)
} }
os.Stdout.Write(b) os.Stdout.Write(b)