2021-01-06 14:41:25 +00:00
|
|
|
package main
|
|
|
|
|
2021-01-06 16:38:12 +00:00
|
|
|
import (
|
2021-01-06 16:47:34 +00:00
|
|
|
"fmt"
|
2021-01-06 16:38:12 +00:00
|
|
|
"io/ioutil"
|
|
|
|
"regexp"
|
|
|
|
"strings"
|
2021-01-06 16:47:34 +00:00
|
|
|
"unicode"
|
2021-01-06 16:38:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type TodoFinder struct {
|
2021-01-06 17:08:15 +00:00
|
|
|
// regex related fields
|
2021-01-06 16:59:11 +00:00
|
|
|
entityTracker *entityTracker
|
|
|
|
todoRegex *regexp.Regexp
|
|
|
|
lineRegex *regexp.Regexp
|
|
|
|
|
2021-01-06 17:08:15 +00:00
|
|
|
// results store
|
2021-01-06 16:29:03 +00:00
|
|
|
FoundTable []*todo
|
|
|
|
foundTree *node
|
2021-01-06 16:38:12 +00:00
|
|
|
|
|
|
|
// openTodo tracks if subsequent comment lines should be included in the last to-do's description
|
|
|
|
openTodo *todo
|
2021-01-06 16:47:34 +00:00
|
|
|
|
|
|
|
keywords []string
|
2021-01-06 14:41:25 +00:00
|
|
|
}
|
|
|
|
|
2021-01-06 17:10:58 +00:00
|
|
|
func NewTodoFinder() (TodoFinder, error) {
|
|
|
|
tf := TodoFinder{
|
2021-01-06 16:29:03 +00:00
|
|
|
FoundTable: []*todo{},
|
|
|
|
foundTree: &node{Name: "root", Type: "dir"},
|
2021-01-06 16:47:34 +00:00
|
|
|
keywords: []string{"todo", "fixme"},
|
2021-01-06 14:41:25 +00:00
|
|
|
}
|
2021-01-06 17:10:58 +00:00
|
|
|
|
|
|
|
return tf, tf.init()
|
2021-01-06 14:41:25 +00:00
|
|
|
}
|
|
|
|
|
2021-01-06 17:10:58 +00:00
|
|
|
func (tf *TodoFinder) init() (err error) {
|
2021-01-06 16:59:11 +00:00
|
|
|
tf.todoRegex, err = regexp.Compile(tf.buildRegexPattern())
|
2021-01-06 16:38:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-01-06 16:59:11 +00:00
|
|
|
tf.lineRegex, err = regexp.Compile("\n")
|
2021-01-06 16:38:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-01-06 16:59:11 +00:00
|
|
|
tf.entityTracker, err = NewEntityTracker()
|
2021-01-06 16:38:12 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-01-06 16:59:11 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tf *TodoFinder) AddTodo(t *todo) {
|
|
|
|
tf.FoundTable = append(tf.FoundTable, t)
|
|
|
|
tf.foundTree.AddToTree(t.Path(), t)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tf *TodoFinder) FindInDir(dir string) error {
|
2021-01-06 16:38:12 +00:00
|
|
|
files, err := ioutil.ReadDir(dir)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, f := range files {
|
|
|
|
filepath := dir + "/" + f.Name()
|
|
|
|
|
|
|
|
if filepath == ignore {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
if f.IsDir() {
|
|
|
|
err = tf.FindInDir(filepath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-06 16:47:34 +00:00
|
|
|
if !tf.isGoFile(f.Name()) {
|
2021-01-06 16:38:12 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
file, err := ioutil.ReadFile(filepath)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2021-01-06 16:59:11 +00:00
|
|
|
lines := tf.lineRegex.Split(string(file), -1)
|
2021-01-06 16:38:12 +00:00
|
|
|
for i, l := range lines {
|
2021-01-06 16:59:11 +00:00
|
|
|
tf.entityTracker.Track(l)
|
2021-01-06 16:38:12 +00:00
|
|
|
|
2021-01-06 16:59:11 +00:00
|
|
|
results := tf.todoRegex.FindSubmatch([]byte(l))
|
2021-01-06 16:38:12 +00:00
|
|
|
if results == nil {
|
|
|
|
if len(l) < 3 {
|
|
|
|
tf.openTodo = nil
|
|
|
|
}
|
|
|
|
|
|
|
|
if tf.openTodo != nil {
|
|
|
|
l = strings.TrimSpace(l)
|
|
|
|
if l[:2] == "//" {
|
|
|
|
tf.openTodo.Description += "\n" + l[2:]
|
|
|
|
} else {
|
|
|
|
tf.openTodo = nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
td := &todo{
|
|
|
|
Filepath: filepath,
|
|
|
|
Description: string(results[1]),
|
|
|
|
LineNumber: i + 1,
|
2021-01-06 16:59:11 +00:00
|
|
|
RelatedFuncOrType: tf.entityTracker.Current(),
|
2021-01-06 16:38:12 +00:00
|
|
|
}
|
|
|
|
tf.AddTodo(td)
|
|
|
|
tf.openTodo = td
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2021-01-06 16:47:34 +00:00
|
|
|
func (tf TodoFinder) isGoFile(name string) bool {
|
|
|
|
if len(name) < 3 {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
last := name[len(name)-3:]
|
|
|
|
return last == ".go"
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tf TodoFinder) buildRegexPattern() string {
|
|
|
|
kwp := tf.makeRegexKeywords()
|
|
|
|
return fmt.Sprintf("//.*((%s)(.*))", kwp)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (tf TodoFinder) makeRegexKeywords() (out string) {
|
|
|
|
for i, kw := range tf.keywords {
|
|
|
|
for _, r := range kw {
|
|
|
|
lower := unicode.ToLower(r)
|
|
|
|
upper := unicode.ToUpper(r)
|
|
|
|
|
|
|
|
out += fmt.Sprintf("[%s,%s]", string(lower), string(upper))
|
|
|
|
}
|
|
|
|
|
|
|
|
if i+1 < len(tf.keywords) {
|
|
|
|
out += "|"
|
|
|
|
}
|
2021-01-06 14:41:25 +00:00
|
|
|
}
|
|
|
|
|
2021-01-06 16:47:34 +00:00
|
|
|
return
|
2021-01-06 14:41:25 +00:00
|
|
|
}
|