func listTopics() {
	files, err := ioutil.ReadDir(directory)
	if err != nil {
		fmt.Println("Oops, couldn't read that directory:", directory)
		os.Exit(1)
	}
	fmt.Println()
	toPrint := []string{}
	for _, file := range files {
		path := filepath.Join(directory, file.Name())
		if isDir(path) && !strings.HasPrefix(file.Name(), ".git") {
			toPrint = append(toPrint, file.Name())
		}
	}
	if len(toPrint) == 0 {
		fmt.Println("looks like there aren't any topics to list!")
	} else {
		fmt.Println("all topics:")
	}
	for _, file := range toPrint {
		fmt.Println("  ", file)
	}
	fmt.Println()
}
listTopics lists the existing topics, while avoiding .git artifacts
package main
import (
	"bufio"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"os/user"
	"path/filepath"
	"strings"
)
var NotesSubDir = "notes"
var DefaultEditor = "vim"
var helpText = `
nogo - the notes helper
actions:
  nogo help
  nogo new
  nogo new [topic]
  nogo new [topic] [event]
  nogo ls
  nogo ls [topic]
  nogo edit
  nogo edit [topic]
  nogo edit [topic] [note name substring]
`
var directory string
var editor string
var visualEditor string
var useVisualEditor bool
func init() {
	directory = os.Getenv("NOGODIR")
	user, _ := user.Current()
	if directory == "" {
		directory = filepath.Join(user.HomeDir, NotesSubDir)
	}
	editor = os.Getenv("EDITOR")
	if editor == "" {
		editor = DefaultEditor
	}
	visualEditor = os.Getenv("NOGOVISUALEDITOR")
	if visualEditor != "" {
		useVisualEditor = true
	}
}
func help(exit int) {
	fmt.Println(helpText)
	os.Exit(exit)
}
func acceptInput(question string) string {
	reader := bufio.NewReader(os.Stdin)
	var (
		response string
		err      error
	)
	for response == "" {
		fmt.Print(question)
		response, err = reader.ReadString('\n')
		if err != nil {
			fmt.Println("Oops, what was that?")
			os.Exit(1)
		}
		response = strings.TrimSpace(response)
	}
	return response
}
func normalizeString(s *string) {
	*s = strings.Replace(*s, " ", "-", -1)
}
func openFile(fileName string) {
	var cmd *exec.Cmd
	if !useVisualEditor {
		cmd = exec.Command(editor, fileName)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	} else {
		cmd = exec.Command("open", fileName, "-a", visualEditor)
	}
	err := cmd.Run()
	if err != nil {
		fmt.Println("Couldn't open the file:", err)
		os.Exit(1)
	}
}
func createFile(fileName string) {
	_, err := os.Create(fileName)
	if err != nil {
		fmt.Println("Oops, I couldn't create the file:", fileName)
	}
}
func createDir(dirName string) {
	err := os.MkdirAll(dirName, 0755)
	if err != nil {
		fmt.Println("Oops, I couldn't make the directory:", dirName)
		os.Exit(1)
	}
}
func handleNew() {
	topic := acceptInput("Enter the notes topic: ")
	normalizeString(&topic)
	handleNewTopic(topic)
}
func handleNewTopic(topic string) {
	event := acceptInput("Enter the event name: ")
	normalizeString(&event)
	handleNewEvent(topic, event)
}
func handleNewEvent(topic, event string) {
	notesDirectory := filepath.Join(directory, topic)
	createDir(notesDirectory)
	fileName := fmt.Sprintf("%s.md", event)
	fullPath := filepath.Join(notesDirectory, fileName)
	createFile(fullPath)
	openFile(fullPath)
}
func parseFilename(filename string) string {
	return strings.Replace(strings.TrimSuffix(filename, ".md"), "-", " ", -1)
}
func isDir(path string) bool {
	f, err := os.Open(path)
	if err != nil {
		return false
	}
	defer f.Close()
	fi, err := f.Stat()
	if err != nil {
		return false
	}
	return fi.IsDir()
}