PageRenderTime 50ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/liteidex/src/liteide_stub/command.go

https://github.com/t0trader/liteide
Go | 692 lines | 518 code | 71 blank | 103 comment | 148 complexity | 893effcf20a1f01f4bddc6cc751c3b5f MD5 | raw file
Possible License(s): LGPL-2.0, LGPL-2.1
  1. // Copyright 2011 The Go Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style
  3. // license that can be found in the LICENSE file.
  4. //modify 2013-2014 visualfc
  5. package main
  6. import (
  7. "bytes"
  8. "flag"
  9. "fmt"
  10. "go/build"
  11. "io"
  12. "log"
  13. "os"
  14. "os/exec"
  15. "path"
  16. "path/filepath"
  17. "regexp"
  18. "runtime"
  19. "strings"
  20. "sync"
  21. "text/template"
  22. "unicode"
  23. "unicode/utf8"
  24. )
  25. var (
  26. buildContext = build.Default
  27. )
  28. var (
  29. goroot = filepath.Clean(runtime.GOROOT())
  30. gobin = os.Getenv("GOBIN")
  31. gorootBin = filepath.Join(goroot, "bin")
  32. gorootSrcPkg = filepath.Join(goroot, "src/pkg")
  33. gorootPkg = filepath.Join(goroot, "pkg")
  34. gorootSrc = filepath.Join(goroot, "src")
  35. )
  36. // A Command is an implementation of a go command
  37. // like go build or go fix.
  38. type Command struct {
  39. // Run runs the command.
  40. // The args are the arguments after the command name.
  41. Run func(cmd *Command, args []string)
  42. // UsageLine is the one-line usage message.
  43. // The first word in the line is taken to be the command name.
  44. UsageLine string
  45. // Short is the short description shown in the 'go help' output.
  46. Short string
  47. // Long is the long message shown in the 'go help <this-command>' output.
  48. Long string
  49. // Flag is a set of flags specific to this command.
  50. Flag flag.FlagSet
  51. // CustomFlags indicates that the command will do its own
  52. // flag parsing.
  53. CustomFlags bool
  54. }
  55. // Name returns the command's name: the first word in the usage line.
  56. func (c *Command) Name() string {
  57. name := c.UsageLine
  58. i := strings.Index(name, " ")
  59. if i >= 0 {
  60. name = name[:i]
  61. }
  62. return name
  63. }
  64. func (c *Command) Usage() {
  65. fmt.Fprintf(os.Stderr, "usage: liteide_stub %s\n", c.UsageLine)
  66. c.Flag.SetOutput(os.Stderr)
  67. c.Flag.PrintDefaults()
  68. //fmt.Fprintf(os.Stderr, "%s\n", strings.TrimSpace(c.Long))
  69. os.Exit(2)
  70. }
  71. // Runnable reports whether the command can be run; otherwise
  72. // it is a documentation pseudo-command such as importpath.
  73. func (c *Command) Runnable() bool {
  74. return c.Run != nil
  75. }
  76. // Commands lists the available commands and help topics.
  77. // The order here is the order in which they are printed by 'go help'.
  78. var commands = []*Command{
  79. cmdVersion,
  80. cmdExec,
  81. cmdAstView,
  82. cmdDocView,
  83. cmdPresent,
  84. cmdApi,
  85. cmdDoc,
  86. cmdType,
  87. }
  88. var exitStatus = 0
  89. var exitMu sync.Mutex
  90. func setExitStatus(n int) {
  91. exitMu.Lock()
  92. if exitStatus < n {
  93. exitStatus = n
  94. }
  95. exitMu.Unlock()
  96. }
  97. func main_app() {
  98. flag.Usage = usage
  99. flag.Parse()
  100. log.SetFlags(0)
  101. args := flag.Args()
  102. if len(args) < 1 {
  103. usage()
  104. }
  105. if len(args) == 1 && strings.TrimSpace(args[0]) == "" {
  106. usage()
  107. }
  108. if args[0] == "help" {
  109. help(args[1:])
  110. return
  111. }
  112. for _, cmd := range commands {
  113. if cmd.Name() == args[0] && cmd.Run != nil {
  114. cmd.Flag.Usage = func() { cmd.Usage() }
  115. if cmd.CustomFlags {
  116. args = args[1:]
  117. } else {
  118. cmd.Flag.Parse(args[1:])
  119. args = cmd.Flag.Args()
  120. }
  121. cmd.Run(cmd, args)
  122. exit()
  123. return
  124. }
  125. }
  126. fmt.Fprintf(os.Stderr, "liteide_stub: unknown subcommand %q\nRun 'liteide_stub help' for usage.\n", args[0])
  127. setExitStatus(2)
  128. exit()
  129. }
  130. var usageTemplate = `LiteIDE golang tool.
  131. Usage:
  132. liteide_stub command [arguments]
  133. The commands are:
  134. {{range .}}{{if .Runnable}}
  135. {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
  136. Use "liteide_stub help [command]" for more information about a command.
  137. Additional help topics:
  138. {{range .}}{{if not .Runnable}}
  139. {{.Name | printf "%-11s"}} {{.Short}}{{end}}{{end}}
  140. Use "liteide_stub help [topic]" for more information about that topic.
  141. `
  142. var helpTemplate = `{{if .Runnable}}usage: liteide_stub {{.UsageLine}}
  143. {{end}}{{.Long | trim}}
  144. `
  145. var documentationTemplate = `//
  146. /*
  147. {{range .}}{{if .Short}}{{.Short | capitalize}}
  148. {{end}}{{if .Runnable}}Usage:
  149. liteide_stub {{.UsageLine}}
  150. {{end}}{{.Long | trim}}
  151. {{end}}*/
  152. package main
  153. `
  154. // tmpl executes the given template text on data, writing the result to w.
  155. func tmpl(w io.Writer, text string, data interface{}) {
  156. t := template.New("top")
  157. t.Funcs(template.FuncMap{"trim": strings.TrimSpace, "capitalize": capitalize})
  158. template.Must(t.Parse(text))
  159. if err := t.Execute(w, data); err != nil {
  160. panic(err)
  161. }
  162. }
  163. func capitalize(s string) string {
  164. if s == "" {
  165. return s
  166. }
  167. r, n := utf8.DecodeRuneInString(s)
  168. return string(unicode.ToTitle(r)) + s[n:]
  169. }
  170. func printUsage(w io.Writer) {
  171. tmpl(w, usageTemplate, commands)
  172. }
  173. func usage() {
  174. printUsage(os.Stderr)
  175. os.Exit(2)
  176. }
  177. // help implements the 'help' command.
  178. func help(args []string) {
  179. if len(args) == 0 {
  180. printUsage(os.Stdout)
  181. // not exit 2: succeeded at 'go help'.
  182. return
  183. }
  184. if len(args) != 1 {
  185. fmt.Fprintf(os.Stderr, "usage: liteide_stub help command\n\nToo many arguments given.\n")
  186. os.Exit(2) // failed at 'go help'
  187. }
  188. arg := args[0]
  189. // 'go help documentation' generates doc.go.
  190. if arg == "documentation" {
  191. buf := new(bytes.Buffer)
  192. printUsage(buf)
  193. usage := &Command{Long: buf.String()}
  194. tmpl(os.Stdout, documentationTemplate, append([]*Command{usage}, commands...))
  195. return
  196. }
  197. for _, cmd := range commands {
  198. if cmd.Name() == arg {
  199. tmpl(os.Stdout, helpTemplate, cmd)
  200. // not exit 2: succeeded at 'go help cmd'.
  201. return
  202. }
  203. }
  204. fmt.Fprintf(os.Stderr, "Unknown help topic %#q. Run 'liteide_stub help'.\n", arg)
  205. os.Exit(2) // failed at 'go help cmd'
  206. }
  207. // importPathsNoDotExpansion returns the import paths to use for the given
  208. // command line, but it does no ... expansion.
  209. func importPathsNoDotExpansion(args []string) []string {
  210. if len(args) == 0 {
  211. return []string{"."}
  212. }
  213. var out []string
  214. for _, a := range args {
  215. // Arguments are supposed to be import paths, but
  216. // as a courtesy to Windows developers, rewrite \ to /
  217. // in command-line arguments. Handles .\... and so on.
  218. if filepath.Separator == '\\' {
  219. a = strings.Replace(a, `\`, `/`, -1)
  220. }
  221. // Put argument in canonical form, but preserve leading ./.
  222. if strings.HasPrefix(a, "./") {
  223. a = "./" + path.Clean(a)
  224. if a == "./." {
  225. a = "."
  226. }
  227. } else {
  228. a = path.Clean(a)
  229. }
  230. if a == "all" || a == "std" {
  231. out = append(out, allPackages(a)...)
  232. continue
  233. }
  234. out = append(out, a)
  235. }
  236. return out
  237. }
  238. // importPaths returns the import paths to use for the given command line.
  239. func importPaths(args []string) []string {
  240. args = importPathsNoDotExpansion(args)
  241. var out []string
  242. for _, a := range args {
  243. if strings.Contains(a, "...") {
  244. if build.IsLocalImport(a) {
  245. out = append(out, allPackagesInFS(a)...)
  246. } else {
  247. out = append(out, allPackages(a)...)
  248. }
  249. continue
  250. }
  251. out = append(out, a)
  252. }
  253. return out
  254. }
  255. var atexitFuncs []func()
  256. func atexit(f func()) {
  257. atexitFuncs = append(atexitFuncs, f)
  258. }
  259. func exit() {
  260. for _, f := range atexitFuncs {
  261. f()
  262. }
  263. os.Exit(exitStatus)
  264. }
  265. func fatalf(format string, args ...interface{}) {
  266. errorf(format, args...)
  267. exit()
  268. }
  269. func errorf(format string, args ...interface{}) {
  270. log.Printf(format, args...)
  271. setExitStatus(1)
  272. }
  273. var logf = log.Printf
  274. func exitIfErrors() {
  275. if exitStatus != 0 {
  276. exit()
  277. }
  278. }
  279. func run(cmdargs ...interface{}) {
  280. cmdline := stringList(cmdargs...)
  281. //if buildN || buildX {
  282. // fmt.Printf("%s\n", strings.Join(cmdline, " "))
  283. // if buildN {
  284. // return
  285. // }
  286. //}
  287. cmd := exec.Command(cmdline[0], cmdline[1:]...)
  288. cmd.Stdout = os.Stdout
  289. cmd.Stderr = os.Stderr
  290. if err := cmd.Run(); err != nil {
  291. errorf("%v", err)
  292. }
  293. }
  294. func runOut(dir string, cmdargs ...interface{}) []byte {
  295. cmdline := stringList(cmdargs...)
  296. cmd := exec.Command(cmdline[0], cmdline[1:]...)
  297. cmd.Dir = dir
  298. out, err := cmd.CombinedOutput()
  299. if err != nil {
  300. os.Stderr.Write(out)
  301. errorf("%v", err)
  302. out = nil
  303. }
  304. return out
  305. }
  306. // envForDir returns a copy of the environment
  307. // suitable for running in the given directory.
  308. // The environment is the current process's environment
  309. // but with an updated $PWD, so that an os.Getwd in the
  310. // child will be faster.
  311. func envForDir(dir string) []string {
  312. env := os.Environ()
  313. // Internally we only use rooted paths, so dir is rooted.
  314. // Even if dir is not rooted, no harm done.
  315. return mergeEnvLists([]string{"PWD=" + dir}, env)
  316. }
  317. // mergeEnvLists merges the two environment lists such that
  318. // variables with the same name in "in" replace those in "out".
  319. func mergeEnvLists(in, out []string) []string {
  320. NextVar:
  321. for _, inkv := range in {
  322. k := strings.SplitAfterN(inkv, "=", 2)[0]
  323. for i, outkv := range out {
  324. if strings.HasPrefix(outkv, k) {
  325. out[i] = inkv
  326. continue NextVar
  327. }
  328. }
  329. out = append(out, inkv)
  330. }
  331. return out
  332. }
  333. // matchPattern(pattern)(name) reports whether
  334. // name matches pattern. Pattern is a limited glob
  335. // pattern in which '...' means 'any string' and there
  336. // is no other special syntax.
  337. func matchPattern(pattern string) func(name string) bool {
  338. re := regexp.QuoteMeta(pattern)
  339. re = strings.Replace(re, `\.\.\.`, `.*`, -1)
  340. // Special case: foo/... matches foo too.
  341. if strings.HasSuffix(re, `/.*`) {
  342. re = re[:len(re)-len(`/.*`)] + `(/.*)?`
  343. }
  344. reg := regexp.MustCompile(`^` + re + `$`)
  345. return func(name string) bool {
  346. return reg.MatchString(name)
  347. }
  348. }
  349. // hasPathPrefix reports whether the path s begins with the
  350. // elements in prefix.
  351. func hasPathPrefix(s, prefix string) bool {
  352. switch {
  353. default:
  354. return false
  355. case len(s) == len(prefix):
  356. return s == prefix
  357. case len(s) > len(prefix):
  358. if prefix != "" && prefix[len(prefix)-1] == '/' {
  359. return strings.HasPrefix(s, prefix)
  360. }
  361. return s[len(prefix)] == '/' && s[:len(prefix)] == prefix
  362. }
  363. }
  364. // treeCanMatchPattern(pattern)(name) reports whether
  365. // name or children of name can possibly match pattern.
  366. // Pattern is the same limited glob accepted by matchPattern.
  367. func treeCanMatchPattern(pattern string) func(name string) bool {
  368. wildCard := false
  369. if i := strings.Index(pattern, "..."); i >= 0 {
  370. wildCard = true
  371. pattern = pattern[:i]
  372. }
  373. return func(name string) bool {
  374. return len(name) <= len(pattern) && hasPathPrefix(pattern, name) ||
  375. wildCard && strings.HasPrefix(name, pattern)
  376. }
  377. }
  378. // allPackages returns all the packages that can be found
  379. // under the $GOPATH directories and $GOROOT matching pattern.
  380. // The pattern is either "all" (all packages), "std" (standard packages)
  381. // or a path including "...".
  382. func allPackages(pattern string) []string {
  383. pkgs := matchPackages(pattern)
  384. if len(pkgs) == 0 {
  385. fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
  386. }
  387. return pkgs
  388. }
  389. func matchPackages(pattern string) []string {
  390. match := func(string) bool { return true }
  391. treeCanMatch := func(string) bool { return true }
  392. if pattern != "all" && pattern != "std" {
  393. match = matchPattern(pattern)
  394. treeCanMatch = treeCanMatchPattern(pattern)
  395. }
  396. have := map[string]bool{
  397. "builtin": true, // ignore pseudo-package that exists only for documentation
  398. }
  399. if !buildContext.CgoEnabled {
  400. have["runtime/cgo"] = true // ignore during walk
  401. }
  402. var pkgs []string
  403. // Commands
  404. cmd := filepath.Join(goroot, "src/cmd") + string(filepath.Separator)
  405. filepath.Walk(cmd, func(path string, fi os.FileInfo, err error) error {
  406. if err != nil || !fi.IsDir() || path == cmd {
  407. return nil
  408. }
  409. name := path[len(cmd):]
  410. if !treeCanMatch(name) {
  411. return filepath.SkipDir
  412. }
  413. // Commands are all in cmd/, not in subdirectories.
  414. if strings.Contains(name, string(filepath.Separator)) {
  415. return filepath.SkipDir
  416. }
  417. // We use, e.g., cmd/gofmt as the pseudo import path for gofmt.
  418. name = "cmd/" + name
  419. if have[name] {
  420. return nil
  421. }
  422. have[name] = true
  423. if !match(name) {
  424. return nil
  425. }
  426. _, err = buildContext.ImportDir(path, 0)
  427. if err != nil {
  428. if _, noGo := err.(*build.NoGoError); !noGo {
  429. log.Print(err)
  430. }
  431. return nil
  432. }
  433. pkgs = append(pkgs, name)
  434. return nil
  435. })
  436. for _, src := range buildContext.SrcDirs() {
  437. if pattern == "std" && src != gorootSrcPkg {
  438. continue
  439. }
  440. src = filepath.Clean(src) + string(filepath.Separator)
  441. filepath.Walk(src, func(path string, fi os.FileInfo, err error) error {
  442. if err != nil || !fi.IsDir() || path == src {
  443. return nil
  444. }
  445. // Avoid .foo, _foo, and testdata directory trees.
  446. _, elem := filepath.Split(path)
  447. if strings.HasPrefix(elem, ".") || strings.HasPrefix(elem, "_") || elem == "testdata" {
  448. return filepath.SkipDir
  449. }
  450. name := filepath.ToSlash(path[len(src):])
  451. if pattern == "std" && strings.Contains(name, ".") {
  452. return filepath.SkipDir
  453. }
  454. if !treeCanMatch(name) {
  455. return filepath.SkipDir
  456. }
  457. if have[name] {
  458. return nil
  459. }
  460. have[name] = true
  461. if !match(name) {
  462. return nil
  463. }
  464. _, err = buildContext.ImportDir(path, 0)
  465. if err != nil {
  466. if _, noGo := err.(*build.NoGoError); noGo {
  467. return nil
  468. }
  469. }
  470. pkgs = append(pkgs, name)
  471. return nil
  472. })
  473. }
  474. return pkgs
  475. }
  476. // allPackagesInFS is like allPackages but is passed a pattern
  477. // beginning ./ or ../, meaning it should scan the tree rooted
  478. // at the given directory. There are ... in the pattern too.
  479. func allPackagesInFS(pattern string) []string {
  480. pkgs := matchPackagesInFS(pattern)
  481. if len(pkgs) == 0 {
  482. fmt.Fprintf(os.Stderr, "warning: %q matched no packages\n", pattern)
  483. }
  484. return pkgs
  485. }
  486. func matchPackagesInFS(pattern string) []string {
  487. // Find directory to begin the scan.
  488. // Could be smarter but this one optimization
  489. // is enough for now, since ... is usually at the
  490. // end of a path.
  491. i := strings.Index(pattern, "...")
  492. dir, _ := path.Split(pattern[:i])
  493. // pattern begins with ./ or ../.
  494. // path.Clean will discard the ./ but not the ../.
  495. // We need to preserve the ./ for pattern matching
  496. // and in the returned import paths.
  497. prefix := ""
  498. if strings.HasPrefix(pattern, "./") {
  499. prefix = "./"
  500. }
  501. match := matchPattern(pattern)
  502. var pkgs []string
  503. filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
  504. if err != nil || !fi.IsDir() {
  505. return nil
  506. }
  507. if path == dir {
  508. // filepath.Walk starts at dir and recurses. For the recursive case,
  509. // the path is the result of filepath.Join, which calls filepath.Clean.
  510. // The initial case is not Cleaned, though, so we do this explicitly.
  511. //
  512. // This converts a path like "./io/" to "io". Without this step, running
  513. // "cd $GOROOT/src/pkg; go list ./io/..." would incorrectly skip the io
  514. // package, because prepending the prefix "./" to the unclean path would
  515. // result in "././io", and match("././io") returns false.
  516. path = filepath.Clean(path)
  517. }
  518. // Avoid .foo, _foo, and testdata directory trees, but do not avoid "." or "..".
  519. _, elem := filepath.Split(path)
  520. dot := strings.HasPrefix(elem, ".") && elem != "." && elem != ".."
  521. if dot || strings.HasPrefix(elem, "_") || elem == "testdata" {
  522. return filepath.SkipDir
  523. }
  524. name := prefix + filepath.ToSlash(path)
  525. if !match(name) {
  526. return nil
  527. }
  528. if _, err = build.ImportDir(path, 0); err != nil {
  529. if _, noGo := err.(*build.NoGoError); !noGo {
  530. log.Print(err)
  531. }
  532. return nil
  533. }
  534. pkgs = append(pkgs, name)
  535. return nil
  536. })
  537. return pkgs
  538. }
  539. // stringList's arguments should be a sequence of string or []string values.
  540. // stringList flattens them into a single []string.
  541. func stringList(args ...interface{}) []string {
  542. var x []string
  543. for _, arg := range args {
  544. switch arg := arg.(type) {
  545. case []string:
  546. x = append(x, arg...)
  547. case string:
  548. x = append(x, arg)
  549. default:
  550. panic("stringList: invalid argument")
  551. }
  552. }
  553. return x
  554. }
  555. // toFold returns a string with the property that
  556. // strings.EqualFold(s, t) iff toFold(s) == toFold(t)
  557. // This lets us test a large set of strings for fold-equivalent
  558. // duplicates without making a quadratic number of calls
  559. // to EqualFold. Note that strings.ToUpper and strings.ToLower
  560. // have the desired property in some corner cases.
  561. func toFold(s string) string {
  562. // Fast path: all ASCII, no upper case.
  563. // Most paths look like this already.
  564. for i := 0; i < len(s); i++ {
  565. c := s[i]
  566. if c >= utf8.RuneSelf || 'A' <= c && c <= 'Z' {
  567. goto Slow
  568. }
  569. }
  570. return s
  571. Slow:
  572. var buf bytes.Buffer
  573. for _, r := range s {
  574. // SimpleFold(x) cycles to the next equivalent rune > x
  575. // or wraps around to smaller values. Iterate until it wraps,
  576. // and we've found the minimum value.
  577. for {
  578. r0 := r
  579. r = unicode.SimpleFold(r0)
  580. if r <= r0 {
  581. break
  582. }
  583. }
  584. // Exception to allow fast path above: A-Z => a-z
  585. if 'A' <= r && r <= 'Z' {
  586. r += 'a' - 'A'
  587. }
  588. buf.WriteRune(r)
  589. }
  590. return buf.String()
  591. }
  592. // foldDup reports a pair of strings from the list that are
  593. // equal according to strings.EqualFold.
  594. // It returns "", "" if there are no such strings.
  595. func foldDup(list []string) (string, string) {
  596. clash := map[string]string{}
  597. for _, s := range list {
  598. fold := toFold(s)
  599. if t := clash[fold]; t != "" {
  600. if s > t {
  601. s, t = t, s
  602. }
  603. return s, t
  604. }
  605. clash[fold] = s
  606. }
  607. return "", ""
  608. }