-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathtago.go
156 lines (138 loc) · 3.82 KB
/
tago.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
/*
Tago "Emacs etags for Go"
Author: Alex Combas
Website: www.goplexian.com
Email: [email protected]
Version: 0.2
© Alex Combas 2010
Initial release: January 03 2010
See README for usage, compiling, and other info.
*/
package main
import (
"bufio"
"bytes"
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"os"
)
// Get working directory and set it for savePath flag default
func whereAmI() string {
var r string = ""
if dir, err := os.Getwd(); err != nil {
fmt.Printf("Error getting working directory: %s\n", err.Error())
} else {
r = dir + "/"
}
return r
}
// Setup flag variables
var saveDir = flag.String("d", whereAmI(), "Change save directory: -d=/path/to/my/tags/")
var tagsName = flag.String("n", "TAGS", "Change TAGS name: -n=MyTagsFile")
var appendMode = flag.Bool("a", false, "Append mode: -a")
type Tea struct {
bag bytes.Buffer
}
func (t *Tea) String() string { return t.bag.String() }
func (t *Tea) Write(p []byte) (n int, err error) {
t.bag.Write(p)
return len(p), nil
}
// Writes a TAGS line to a Tea buffer
func (t *Tea) drink(leaf *ast.Ident, fileSet *token.FileSet) {
if leaf.Obj == nil {
return
}
position := fileSet.Position(leaf.Pos())
s := scoop(position.Filename, position.Line)
fmt.Fprintf(t, "%s%s%d,%d\n", s, leaf.Obj.Name, position.Line, position.Offset)
}
// TAGS file is either appended or created, not overwritten.
func (t *Tea) savor() {
location := fmt.Sprintf("%s%s", *saveDir, *tagsName)
if *appendMode {
if file, err := os.OpenFile(location, os.O_APPEND|os.O_WRONLY, 0666); err != nil {
fmt.Printf("Error appending file \"%s\": %s\n", location, err.Error())
} else {
b := t.bag.Len()
file.WriteAt(t.bag.Bytes(), int64(b))
file.Close()
}
} else {
if file, err := os.OpenFile(location, os.O_CREATE|os.O_WRONLY, 0666); err != nil {
fmt.Printf("Error writing file \"%s\": %s\n", location, err.Error())
} else {
file.WriteString(t.bag.String())
file.Close()
}
}
}
// Returns the full line of source on which *ast.Ident.Name appears
func scoop(name string, n int) []byte {
var newline byte = '\n'
var line []byte // holds a line of source code
if file, err := os.OpenFile(name, os.O_RDONLY, 0666); err != nil {
fmt.Printf("Error opening file: %s\n", err.Error())
} else {
r := bufio.NewReader(file)
// iterate until reaching line #n
for i := 1; i <= n; i++ {
if sought, err := r.ReadBytes(newline); err != nil {
fmt.Printf("Error reading bytes: %s\n", err.Error())
} else {
line = sought[0:(len(sought) - 1)] //strip the newline
}
}
file.Close()
}
return line
}
// Parses the source files given on the commandline, returns a TAGS chunk for each file
func brew() string {
teaPot := new(Tea)
fileSet := token.NewFileSet()
for i := 0; i < len(flag.Args()); i++ {
teaCup := new(Tea)
if ptree, perr := parser.ParseFile(fileSet, flag.Arg(i), nil, 0); perr != nil {
fmt.Println("Error parsing file: ", perr.Error())
return ""
} else {
// if there were no parsing errors then process normally
for _, l := range ptree.Decls {
switch leaf := l.(type) {
case *ast.FuncDecl:
teaCup.drink(leaf.Name, fileSet)
case *ast.GenDecl:
for _, c := range leaf.Specs {
switch cell := c.(type) {
case *ast.TypeSpec:
teaCup.drink(cell.Name, fileSet)
case *ast.ValueSpec:
for _, atom := range cell.Names {
teaCup.drink(atom, fileSet)
}
}
}
}
}
totalBytes := teaCup.bag.Len()
position := fileSet.Position(ptree.Pos())
fmt.Fprintf(teaPot, "\f\n%s,%d\n%s", position.Filename, totalBytes, teaCup)
}
}
return teaPot.String()
}
func main() {
flag.Parse()
tea := new(Tea)
fmt.Fprint(tea, brew())
// if the string is empty there were parsing errors, abort
if tea.String() == "" {
fmt.Println("Parsing errors experienced, aborting...")
} else {
tea.savor()
}
}