-
Notifications
You must be signed in to change notification settings - Fork 11
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add nashdoc tool #175
base: master
Are you sure you want to change the base?
add nashdoc tool #175
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <package>.<fn name or wildcard>\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) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
package main | ||
|
||
import ( | ||
"bytes" | ||
"os" | ||
"testing" | ||
) | ||
|
||
type ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just one type inside type() ? Some reason for this ? consistency ? |
||
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{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Show struct initialization with the field names some love :-), again kind'a hard to understand what is each of the fields. |
||
{ | ||
"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 <package>.<fn name or wildcard>\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) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"io" | ||
"os" | ||
"path/filepath" | ||
"regexp" | ||
) | ||
|
||
const banner = `888b 888 888 888 888888 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. AAAWWEESOMEEEEEEEE !!! lol |
||
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 | ||
}) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
fn a() { | ||
|
||
} | ||
|
||
# bleh | ||
fn b(a) { | ||
|
||
} | ||
|
||
# carrr | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. One a lisper always a lisper :-P |
||
fn c(a, b) { | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why just the first parameters omits type ?