diff --git a/cmd/nashdoc/doc.go b/cmd/nashdoc/doc.go new file mode 100644 index 00000000..83b7dc28 --- /dev/null +++ b/cmd/nashdoc/doc.go @@ -0,0 +1,172 @@ +package main + +import ( + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" + + "github.com/NeowayLabs/nash/ast" + "github.com/NeowayLabs/nash/parser" +) + +func docUsage(out io.Writer) { + fmt.Fprintf(out, "Usage: %s .\n", filepath.Base(os.Args[0])) +} + +func printDoc(stdout, _ io.Writer, docs []*ast.CommentNode, fn *ast.FnDeclNode) { + fmt.Fprintf(stdout, "fn %s(%s)\n", fn.Name(), strings.Join(fn.Args(), ", ")) + + for _, doc := range docs { + fmt.Fprintf(stdout, "\t%s\n", doc.String()[2:]) + } + + fmt.Println() +} + +func lookFn(stdout, stderr io.Writer, fname string, pack string, pattern *regexp.Regexp) { + content, err := ioutil.ReadFile(fname) + + if err != nil { + fmt.Fprintf(stderr, "error: %s\n", err.Error()) + return + } + + parser := parser.NewParser(fname, string(content)) + + tree, err := parser.Parse() + + if err != nil { + return + } + + nodelen := len(tree.Root.Nodes) + + for i, j := 0, 1; j < nodelen; i, j = i+1, j+1 { + var comments []*ast.CommentNode + + node := tree.Root.Nodes[i] + next := tree.Root.Nodes[j] + + if node.Type() == ast.NodeComment { + comments = append(comments, node.(*ast.CommentNode)) + last := node + + // process comments + for i = i + 1; i < nodelen-1; i++ { + node = tree.Root.Nodes[i] + + if node.Type() == ast.NodeComment && + node.Line() == last.Line()+1 { + comments = append(comments, node.(*ast.CommentNode)) + last = node + } else { + break + } + } + + i-- + j = i + 1 + next = tree.Root.Nodes[j] + + if next.Line() != last.Line()+1 { + comments = []*ast.CommentNode{} + } + + if next.Type() == ast.NodeFnDecl { + fn := next.(*ast.FnDeclNode) + + if pattern.MatchString(fn.Name()) { + printDoc(stdout, stderr, comments, next.(*ast.FnDeclNode)) + + } + } + } else if node.Type() == ast.NodeFnDecl { + fn := node.(*ast.FnDeclNode) + + if pattern.MatchString(fn.Name()) { + // found func, but no docs :-( + printDoc(stdout, stderr, []*ast.CommentNode{}, fn) + } + } + + } +} + +func walk(stdout, stderr io.Writer, nashpath, pkg string, pattern *regexp.Regexp) error { + return filepath.Walk(nashpath+"/lib", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + dirpath := filepath.Dir(path) + dirname := filepath.Base(dirpath) + ext := filepath.Ext(path) + + if ext != "" && ext != ".sh" { + return nil + } + + if dirname != pkg { + return nil + } + + lookFn(stdout, stderr, path, pkg, pattern) + + return nil + }) +} + +func doc(stdout, stderr io.Writer, args []string) error { + if len(args) < 1 { + docUsage(stderr) + return nil + } + + packfn := args[0] + parts := strings.Split(packfn, ".") + + if len(parts) != 2 { + docUsage(stderr) + return nil + } + + pkg := parts[0] + + if strings.ContainsAny(parts[1], ".[]()") { + return fmt.Errorf("Only wildcards * and ? supported") + } + + patternStr := strings.Replace(parts[1], "*", ".*", -1) + patternStr = strings.Replace(patternStr, "?", ".?", -1) + patternStr = "^" + patternStr + "$" + + pattern, err := regexp.Compile(patternStr) + + if err != nil { + return fmt.Errorf("invalid pattern: %s", err.Error()) + } + + nashpath := os.Getenv("NASHPATH") + + if nashpath == "" { + homepath := os.Getenv("HOME") + + if homepath == "" { + return fmt.Errorf("NASHPATH not set...\n") + } + + fmt.Fprintf(stderr, "NASHPATH not set. Using ~/.nash\n") + + nashpath = homepath + "/.nash" + } + + return walk(stdout, stderr, nashpath, pkg, pattern) +} diff --git a/cmd/nashdoc/doc_test.go b/cmd/nashdoc/doc_test.go new file mode 100644 index 00000000..b8766f2a --- /dev/null +++ b/cmd/nashdoc/doc_test.go @@ -0,0 +1,102 @@ +package main + +import ( + "bytes" + "os" + "testing" +) + +type ( + testTbl struct { + lookFor string + outExpected string + errExpected string + errStr string + } +) + +func dotest(t *testing.T, test testTbl) { + testdir := "/src/github.com/NeowayLabs/nash/cmd/nashdoc/testcases/" + nashpath := os.Getenv("GOPATH") + testdir + os.Setenv("NASHPATH", nashpath) + + var ( + stdout, stderr bytes.Buffer + ) + + err := doc(&stdout, &stderr, []string{test.lookFor}) + + if err != nil { + if test.errStr != "" { + if err.Error() != test.errStr { + t.Fatalf("Expected error '%s', but got '%s'", + test.errStr, err.Error()) + } + } else { + t.Fatal(err) + } + } + + gotOut := string(stdout.Bytes()) + gotErr := string(stderr.Bytes()) + + if test.outExpected != gotOut { + t.Fatalf("Stdout differs: '%s' != '%s'", test.outExpected, gotOut) + } + + if test.errExpected != gotErr { + t.Fatalf("Stderr differs: '%s' != '%s'", test.errExpected, gotErr) + } +} + +func TestDoc(t *testing.T) { + for _, test := range []testTbl{ + { + "somepkg.somefn", + `fn somefn(a, b, c, d) + somefn is a testcase function + multiples comments + etc + etc +`, ``, "", + }, + // Test empty pkg and func + { + "", + "", + "Usage: nashdoc.test .\n", + "", + }, + + // test non existent package + { + "blahbleh.bli", + "", + "", + "", + }, + + { + "a.a", + `fn a() +`, ``, "", + }, + + { + "a.b", + `fn b(a) + bleh +`, + ``, "", + }, + { + "a.c", + `fn c(a, b) + carrr +`, + ``, "", + }, + } { + dotest(t, test) + } +} diff --git a/cmd/nashdoc/idoc.go b/cmd/nashdoc/idoc.go new file mode 100644 index 00000000..d26e09a1 --- /dev/null +++ b/cmd/nashdoc/idoc.go @@ -0,0 +1,50 @@ +package main + +import ( + "fmt" + "io" + "os" + "path/filepath" + "regexp" +) + +const banner = `888b 888 888 888 888888 +8888b 888 888 888 888888 +88888b 888 888 888 888888 +888Y88b 888 8888b. .d8888b 88888b. .d8888b 88888b. .d88b. 888888 +888 Y88b888 "88b88K 888 "88b 88K 888 "88bd8P Y8b888888 +888 Y88888.d888888"Y8888b.888 888 "Y8888b.888 88888888888888888 +888 Y8888888 888 X88888 888 X88888 888Y8b. 888888 +888 Y888"Y888888 88888P'888 888 88888P'888 888 "Y8888 888888 +==================================================================== +|| Documentation of ~/.nash/lib projects +` + +func idoc(stdout, stderr io.Writer) error { + fmt.Fprintf(stdout, "%s\n", banner) + return walkAll(stdout, stderr, "/home/i4k/.nash", regexp.MustCompile(".*")) +} + +func walkAll(stdout, stderr io.Writer, nashpath string, pattern *regexp.Regexp) error { + return filepath.Walk(nashpath+"/lib", func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if info.IsDir() { + return nil + } + + dirpath := filepath.Dir(path) + dirname := filepath.Base(dirpath) + ext := filepath.Ext(path) + + if ext != "" && ext != ".sh" { + return nil + } + + lookFn(stdout, stderr, path, dirname, pattern) + + return nil + }) +} diff --git a/cmd/nashdoc/main.go b/cmd/nashdoc/main.go new file mode 100644 index 00000000..f55088f2 --- /dev/null +++ b/cmd/nashdoc/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "flag" + "fmt" + "io" + "os" +) + +var ( + help *bool +) + +func init() { + help = flag.Bool("h", false, "Show this help") +} + +func usage() { + flag.PrintDefaults() +} + +func main() { + flag.Parse() + + if *help { + usage() + os.Exit(0) + } + + if err := do(flag.Args(), os.Stdout, os.Stderr); err != nil { + fmt.Printf("error: %s\n", err.Error()) + os.Exit(1) + } +} + +func do(args []string, stdout, stderr io.Writer) error { + if len(args) == 0 { + return idoc(stdout, stderr) + } + + return doc(stdout, stderr, args) +} diff --git a/cmd/nashdoc/testcases/lib/a/a.sh b/cmd/nashdoc/testcases/lib/a/a.sh new file mode 100644 index 00000000..9db5c8c7 --- /dev/null +++ b/cmd/nashdoc/testcases/lib/a/a.sh @@ -0,0 +1,13 @@ +fn a() { + +} + +# bleh +fn b(a) { + +} + +# carrr +fn c(a, b) { + +} diff --git a/cmd/nashdoc/testcases/lib/somepkg/test.sh b/cmd/nashdoc/testcases/lib/somepkg/test.sh new file mode 100644 index 00000000..046638fe --- /dev/null +++ b/cmd/nashdoc/testcases/lib/somepkg/test.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env nash + +# not relateed comment + +# somefn is a testcase function +# multiples comments +# etc +# etc +fn somefn(a, b, c, d) { + echo $a $b $c $d +}