cmd

package
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 26, 2020 License: Apache-2.0, Apache-2.0 Imports: 32 Imported by: 0

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	GetCollection *entries.Collection
	GetList       entries.List
)

These are global variables that is set once the get command is called. This is to allow commands in the `actions/` subdirectory to access filtered entries.

View Source
var ActionAnkifyCmd = &cobra.Command{
	Use:   "ankify",
	Short: "create anki flashcards",
	Long: `ankify converts entries into anki flashcards.

Ankify will process all entries matched and convert headings with two
question marks (??) into flashcards. For example:

	$ albatross get -p path/to/entries ankify

	##### What's the point in living??
	The point in living is to...

Will become:

	1st side:
	┌──────────────────────────────┐
	│ What's the point in living?  │
	└──────────────────────────────┘

	2nd side:
	┌──────────────────────────────┐
	│ The point in living is to... │
	└──────────────────────────────┘

It outputs a TSV file, which can then be redirected into a file:

	$ albatross get -t "My Flashcards" ankify > ~/.local/decks/entries.tsv

The format of the TSV file is:

    <HEADING>	<QUESTION>	<PATH>

Importing Into Anki
-------------------

In order to import this into Anki, open the application and click "Import File" at the bottom.
You will need to create a new Note Type so that Anki handles the path correctly before you import. To do this:

    - Click 'Add' at the top.
    - Press 'Manage', then Add.
    - Click 'Add: Basic', and enter a suitable name like 'Ankify'.
    - This should open the note entry window.
    - Then click 'Fields...', press 'Add' and name it 'Path'.

That should be it. Now when you import the TSV file, select the Note Type as being 'Ankify', or the name that you entered.

As a suggestion, decks should cover broad topics as a whole. So instead of creating "School::A-Level::Physics::Topic 1::Electromagnetism",
it's better to have a deck that's more like "School::A-Level::Physics". If you need to study a specific section of an Albatross store,
you can leverage the search field and create a filtered deck (Tools->Create Filtered Deck):

	# Revise a single topic
	path:*school/a-level/physics/topic1*

	# Revise a specific piece of knowledge
	path:*school/a-level/physics/topic8/electromagnetism
	
Fine Tuning
-----------

Sometimes you want to convert the '$'s around an expression to something else. For example:

	What are the dimensions of $\begin{matrix} 3 & 3 & 3 \\ 3 & 3 & 3 \end{matrix}$??
	$$
	2 \times 3
	$$

If you want to use MathJax instead of Latex in Anki, the syntax is not '[$$]' but '\[' and '\]'. In order to achieve this you can use the
4 flags:
	
	--double-open "\[" (convert the start of a '$$...$$' block to '\[')
	--double-close "\]" (convert the end of a '$$...$$' block to '\]')

The following flags are also available:

	--single-open "X" (convert the start of a '$...$' block to 'X')
	--single-close "Y" (convert the start of a '$...$' block to 'Y')

Bugs
----

At the moment there is an issue with how backslashes are handled. The markdown processor, Goldmark, will convert something like '\\' to '\'.
This means Latex expressions involving '\\', which triggers a line break, will not work properly. In order to temporarily fix this, it's
necesary to double up and do 4: '\\\\'. For example:

	What are the dimensions of $\begin{matrix} 3 & 3 & 3 \\\\ 3 & 3 & 3 \end{matrix}$??
														 ^^^^
`,

	Run: func(cmd *cobra.Command, args []string) {
		_, _, list := getFromCommand(cmd)

		fixLatex, err := cmd.Flags().GetBool("fix-latex")
		if err != nil {
			log.Fatalf("Error getting 'fix-latex' flag: %s", err)
		}

		singleOpen, err := cmd.Flags().GetString("single-open")
		checkArg(err)
		singleClose, err := cmd.Flags().GetString("single-close")
		checkArg(err)
		doubleOpen, err := cmd.Flags().GetString("double-open")
		checkArg(err)
		doubleClose, err := cmd.Flags().GetString("double-close")
		checkArg(err)

		generateAnkiFlashcards(list.Slice(), fixLatex, singleOpen, singleClose, doubleOpen, doubleClose)
	},
}

ActionAnkifyCmd represents the 'tags' action.

View Source
var ActionDateCmd = &cobra.Command{
	Use:   "date",
	Short: "print entry dates",
	Long: `date will print the date of all matched entries. If you want more complex formatting, it may be a good idea to
use the template command instead.
	
	$ albatross get date
	2020-03-16 17:28
	2020-05-31 15:42
	2020-08-31 07:27
	2020-07-27 10:32

Is equivalent too...

	$ albatross get template '{{.Date | date "2006-01-02 15:01"}}'
	2020-03-16 17:28
	2020-05-31 15:42
	2020-08-31 07:27
	2020-07-27 10:32

You can specify a date format using the --print-date-format flag. This is not to be confused with --date-format,
which specifies how "albatross get" should parse the --from and --until flags.`,

	Run: func(cmd *cobra.Command, args []string) {
		_, _, list := getFromCommand(cmd)
		dateFormat, err := cmd.Flags().GetString("print-date-format")
		checkArg(err)

		for _, entry := range list.Slice() {
			fmt.Println(entry.Date.Format(dateFormat))
		}
	},
}

ActionDateCmd represents the 'date' action.

View Source
var ActionExportCmd = &cobra.Command{
	Use:     "export",
	Aliases: []string{"json"},
	Short:   "export entries into different formats",
	Long: `export will export entries in different formats, like JSON or as an EPUB file.

By default, the command will output a JSON serialised array of all the entries that were matched in the search.

	$ albatross get --sort 'date' export
	# Export all entries chronologically in JSON.
	
For help with EPUB export, see

	$ albatross get export epub --help
`,

	Run: func(cmd *cobra.Command, args []string) {
		_, _, list := getFromCommand(cmd)

		format, err := cmd.Flags().GetString("format")
		checkArg(err)

		var out []byte

		switch format {
		case "json":
			out, err = json.Marshal(list.Slice())
		case "epub":
			fmt.Println("The correct command is: albatross get export epub")
			os.Exit(1)
		default:
			fmt.Println("Invalid output format:", format)
			fmt.Println("Currently supported are: json, epub")
			os.Exit(1)
		}

		if err != nil {
			fmt.Println("error marshalling entries:")
			fmt.Println(err)
			os.Exit(1)
		}

		fmt.Println(string(out))
	},
}

ActionExportCmd represents the 'tags' action.

View Source
var ActionExportEpubCmd = &cobra.Command{
	Use:   "epub",
	Short: "generate EPUBs from matched entries",
	Long: `epub converts matched entries into an EPUB format, ready for loading on to a device like a Kindle.
	
It's often a good idea to sort entries when creating an EPUB because then the Chapters will be in the correct order in the export.

	$ albatross get -p school --sort 'date' export epub -o book.epub
	
The title is 'Albatross YYYY-MM-DD' by default and can be specified using the --book-title flag.

The author is the command that was used to generate the book, 'such as albatross get -p school', though something different can
be specified using the --book-author flag.

The --output/-o flag controls the output location of the EPUB. If no location is specified, it will cause an error.

Contents
--------

The generated EPUB has the following structure:

- Info: A page containing information such as the number of entries matched and links to the other sections of the book.
- Table of Contents: A chronological list of all entries with month and year headings.
- Tags: A list of all entries grouped by tags.
- Paths: A list of all entries grouped by path.
- Entries: Each entry is then written as its own chapter. It contains the entry's content, as well as it's metadata and 
  all links to different entries will work. It also contains a list of other entries that link to this entry (backlinks)
  if any are present.

Links
-----

Links will only work if the entry being linked to is also inside the entries matched by the search. For example, if you had
a collection of journal entries with the path "journal/" and you generate an epub like so:

	$ albatross get -p journal --sort 'date' export epub -o book.epub

If you link to any recipes which are located at the "recipes/" path, the links will not work because they are not included in
the original search. Furthermore, if you instead generated a cookbook with the command:

	$ albatross get -p recipes --sort 'alpha' export epub -o book.epub

Then the "Links to this Entry" section for all the recipes will not contain the journal entries which link to the original entry.
This is because the journal entries are now not included in the search. If you wanted this, you could use the OR filter for path.

	$ albatorss get -p "recipes OR journal" --sort 'date' export book.epub

Which would generate a EPUB containing all entries beginning with the path 'recipes/' or 'journal/'

Examples
--------

	$ albatross get -p recipes --sort 'alpha' export epub --book-title "Cookbook" --book-author "John Doe"
	# An alphabetical cookbook containing entries at the path 'recipes/'

	$ albatross get -p school --sort 'alpha' export epub --book-title "Notes"
	# All the entries associated with school notes, if all your notes are located at the 'school/' path.

	$ albatross get --sort 'date' export epub
	# Every entry into the store, sorted chornologically.
`,

	Run: func(cmd *cobra.Command, args []string) {
		_, collection, list := getFromCommand(cmd)
		command := "albatross " + strings.Join(os.Args[1:], " ")

		author, err := cmd.Flags().GetString("book-author")
		checkArg(err)

		title, err := cmd.Flags().GetString("book-title")
		checkArg(err)

		if author == "" {
			author = command
		}

		if title == "" {
			title = "Albatross " + time.Now().Format("2006-01-02 15:01")
		}

		outputDest, err := cmd.Flags().GetString("output")
		checkArg(err)

		if outputDest == "" {
			fmt.Println("Please specify an output location using the -o flag.")
			fmt.Println("For example: albatross get export epub -o book.epub")
			os.Exit(1)
		}

		output, err := convertToEpub(collection, list, title, author, command)
		if err != nil {
			fmt.Println("Error when creating the EPUB:")
			fmt.Println(err)
			os.Exit(1)
		}

		err = ioutil.WriteFile(outputDest, output, 0644)
		if err != nil {
			fmt.Println("Couldn't write to output destination:")
			fmt.Println(err)
			os.Exit(1)
		}
	},
}

ActionExportEpubCmd represents the 'tags' action.

View Source
var ActionLinksCmd = &cobra.Command{
	Use:   "links",
	Short: "display the links inside an entry",
	Long: `links will display all the links inside an entry
	
This command is often used in conjunction with 'albatross get -i'. By default it will
display the paths to all linked entries:

    $ albatross get -p school/gcse/physics/topic7
	
	school/gcse/physics/topic7/motor-effect
    school/gcse/physics/topic7/electromagnetism
    school/gcse/physics/topic7/generator-effect
	school/gcse/physics/topic7/generators
	
You can then pipe this into 'albatross get -i' to see more info about those entries. For
example, to print the titles and dates of the linked entries:

	$ albatross get -p school/gcse/physics/topic7 | albatross get -i template '{{.Date | date "2006-01-02"}} - {{.Title}}'

	2020-08-09 The Motor Effect
	2020-08-09 Electromagnetism
	2020-08-09 The Generator Effect
	2020-08-09 Generators

Another common use case of this command could be a to-do list of things to write in the future by
checking to see links to entries that don't yet exist.

	$ albatross get links --dont-exist
	# Or, using shorthand flags:
	$ albatross get links -e
	
	You can also display the entry that is linking to the other entry using the --outbound flag:
	
	$ albatross get links --outbound

Combining the --dont-exist/-e and the --outbound/-o flags is useful for showing all entries which link
to nonexistant entries. For example:

	$ albatross get -p school links -eo
	school/a-level/computing/syllabus -> [[Computing - Harvard vs Von Neumann Architecture]]
	school/a-level/physics/lessons -> [[Physics - Calculating Acceleration Due to Gravity]]
	school/a-level/physics/lessons -> [[Physics - Calculating Acceleration Due to Gravity]] 

And finally to print the link text (such as [[Link]] or {{path/to/link}}) instead of the path itself,
you can use the --text flag:

	$ albatross get links --text

This behaviour is implicit for the --dont-exist flag because there's no real path for a link to an entry
using a title that doesn't exist. 
`,

	Run: func(cmd *cobra.Command, args []string) {
		collection, _, list := getFromCommand(cmd)

		outbound, err := cmd.Flags().GetBool("outbound")
		checkArg(err)

		dontExistOnly, err := cmd.Flags().GetBool("dont-exist")
		checkArg(err)

		displayText, err := cmd.Flags().GetBool("text")
		checkArg(err)

		for _, entry := range list.Slice() {
			for _, link := range entry.OutboundLinks {
				linkedEntry := collection.ResolveLink(link)
				if linkedEntry != nil && !dontExistOnly {
					text := ""

					if displayText {
						text = entry.Contents[link.Loc[0]:link.Loc[1]]
					} else {
						text = linkedEntry.Path
					}

					if outbound {
						fmt.Printf("%s -> %s\n", entry.Path, text)
					} else {
						fmt.Println(text)
					}

				} else if linkedEntry == nil && dontExistOnly {
					text := entry.Contents[link.Loc[0]:link.Loc[1]]

					if outbound {
						fmt.Printf("%s -> %s\n", entry.Path, text)
					} else {
						fmt.Println(text)
					}
				}
			}
		}
	},
}

ActionLinksCmd represents the 'tags' action.

View Source
var ActionLsCmd = &cobra.Command{
	Use:     "ls",
	Aliases: []string{"tree"},
	Short:   "list entries returned",
	Long: `list prints the entries matched in a search in a tree like format. For example:
	
	$ albatross get -p school/gcse ls
	.
	└── school
		└── gcse
			└── physics
			│   ├── topic7
			│   │   ├── electromagnetism
			│   │   ├── revision-questions
			│   │   ├── motor-effect
			│   │   ├── motors
			│   │   ├── transformers
			│   │   ├── generators
			│   │   ├── generator-effect
			│   │   ├── loudspeakers
			│   │   ├── microphones
			│   │   ├── magnets
			│   ├── topic8
			│   │   ├── red-shift-and-big-bang
			│   │   ├── solar-system-and-orbits
			│   │   ├── life-cycle-of-stars
			│   │   ├── revision-questions
			│   ├── topic4
			│       └── nuclear-fission
			│       └── nuclear-fusion
			└── results

Currently, only printing the paths for entries is supported. In a future version you should be able to show more information.
`,

	Run: func(cmd *cobra.Command, args []string) {
		_, _, list := getFromCommand(cmd)

		depth, err := cmd.Flags().GetInt("depth")
		checkArg(err)

		displayTitle, err := cmd.Flags().GetBool("display-title")
		checkArg(err)

		paths := [][]string{}
		stringTree := map[string]interface{}{}
		renderFunc := func(path string) string { return path[strings.LastIndex(path, "/")+1:] }

		if displayTitle {
			renderFunc = func(path string) string {
				for _, entry := range list.Slice() {
					if entry.Path == path {
						return entry.Title
					}
				}

				return "<not found>"
			}
		}

		for _, entry := range list.Slice() {
			paths = append(paths, strings.Split(entry.Path, "/"))
		}

		for _, path := range paths {
			subtree := stringTree

			for _, curr := range path {
				newSubtree, ok := subtree[curr].(map[string]interface{})

				if !ok {
					if subtree == nil {
						subtree = make(map[string]interface{})
					}

					subtree[curr] = map[string]interface{}{}
					subtree, _ = subtree[curr].(map[string]interface{})
				} else {
					subtree = newSubtree
				}
			}
		}

		tree := mapToTree(".", stringTree, depth, "", renderFunc)
		fmt.Println(tree.Print())
	},
}

ActionLsCmd represents the 'ls' action. This is some pretty ugly code, TODO.

Basically, it first converts a list of paths into a nested map[string]interface{}, like parsing a list of files into a tree. Then it uses the mapToTree command to recursivly convert the nested map structure into a GoTree structure. It uses the renderFunc to determine how it should display entries.

BUG: When using the --display-title option, if an entry itself contains other entries, the path will be printed instead of the title. TODO: Currently no sorting.

View Source
var ActionPathCmd = &cobra.Command{
	Use:     "path",
	Aliases: []string{"paths"},
	Short:   "print paths",
	Long: `path will display the paths of all matched entries. For example:
	
	$ albatross get -p school/gcse path
	school/gcse/physics/topic7/electromagnetism
	school/gcse/physics/topic8/life-cycle-of-stars
	school/gcse/physics/topic4/nuclear-fission
	school/gcse/physics/topic8/solar-system-and-orbits
	...

Printing paths is the default when no subcommand is given to albatross get. You can always omit the 'path' part of the command:

	$ albatross get -p school/gcse
	school/gcse/physics/topic7/electromagnetism
	school/gcse/physics/topic8/life-cycle-of-stars
	school/gcse/physics/topic4/nuclear-fission
	school/gcse/physics/topic8/solar-system-and-orbits
	...

The functionalities of this command can also be achieved by the template command:

	$ albatross get -p school/gcse template {{.Path}}
	`,

	Run: func(cmd *cobra.Command, args []string) {
		_, _, list := getFromCommand(cmd)

		for _, entry := range list.Slice() {
			fmt.Println(entry.Path)
		}
	},
}

ActionPathCmd represents the 'tags' action.

View Source
var ActionServerCmd = &cobra.Command{
	Use:   "server",
	Short: "start HTTP server which serves JSON entries",
	Long:  `date will print the date of all matched entries`,

	Run: func(cmd *cobra.Command, args []string) {
		_, collection, _ := getFromCommand(cmd)

		port, err := cmd.Flags().GetInt("port")
		checkArg(err)

		s := server.NewServer(collection)
		err = s.Serve(port)

		if err != nil {
			log.Error(err)
		}
	},
}

ActionServerCmd represents the 'server' action.

View Source
var ActionTagsCmd = &cobra.Command{
	Use:   "tags",
	Short: "print tags",
	Long: `tags will display the tags in entries.
	
	$ albatross get -p school/a-level/further-maths tags
	@?further-maths, @?latex-block-alt
	@?further-maths, @?latex-block-alt
	...

The functionalities of this command can be achieved with the template command:

	$ albatross get -p school/a-level/further-maths template '{{.Tags | join ", "}}'
	@?further-maths, @?latex-block-alt
	@?further-maths, @?latex-block-alt
	...
`,

	Run: func(cmd *cobra.Command, args []string) {
		_, _, list := getFromCommand(cmd)

		for _, entry := range list.Slice() {
			fmt.Println(strings.Join(entry.Tags, ", "))
		}
	},
}

ActionTagsCmd represents the 'tags' action. TODO: option to lump all the tags together

View Source
var ActionTemplateCmd = &cobra.Command{
	Use:   "template",
	Short: "fill a template",
	Long: `template executes Go templates

This can be used to manipulate entries in a variety of ways. For example:

    # Print titles for all entries.
	$ albatross get template "{.Title}"
	
	# Print all entries in the format
	# YYYY-MM-DD (PATH) TITLE
	$ albatross get template '{{.Date | date "2006-01-02"}} ({{.Path}}) {{.Title}}'

You can also read templates in from stdin:

	$ cat template.txt | albatross get template
	
Sprig (https://github.com/Masterminds/sprig) helper functions/pipelines are available, such as:

    - date
    - toJSON
    - upper
	
By default, the template is run against every entry matched sequentially. If you wish to access the list
of entries itself, use the --all flag.

	$ albatross get template --all '{{range .}}{{.Title}}{{end}}'
	
Context
-------

The template context is an Entry struct. This contains the following fields:

	- .Path, string
	  The path to the entry file.

	- .Contents, string
	  The contents of the file without front matter.

	- .OriginalContents, string
	  The contents of the file, includiong the front matter.

	- .Tags, []string
	  All the tags present in the document. For example, "@!journal".

	- .OutboundLinks, []Link
	  Links going from this entry to another one.

	- .Date, time.Time
	  The date extracted from the entry.

	- .ModTime, time.Time
	  The modification time for the entry. This is not always accurate, since encrypting and decryting all the
	  files will "modify" them. Therefore it cannot be used for sorting accurately.

	- .Title, string
	  The title of the entry.

	- .Metadata, map[string]interface{}
	  All of the front matter. `,

	Run: func(cmd *cobra.Command, args []string) {
		_, _, list := getFromCommand(cmd)

		all, err := cmd.Flags().GetBool("all")
		checkArg(err)

		var tmpl = template.New("input").Funcs(sprig.TxtFuncMap())

		fi, err := os.Stdin.Stat()
		if err != nil {
			panic(err)
		}

		if len(args) == 1 {
			tmpl, err = tmpl.Parse(args[0])
		} else if fi.Mode()&os.ModeNamedPipe == 0 {
			fmt.Println("template takes one arg, the template, or reads from stdin:")
			fmt.Println("")
			fmt.Println("    $ albatross get template '{{.Title}}'")
			fmt.Println("    $ cat template.txt | albatross get template")
			os.Exit(1)
		} else {
			stdin, err := ioutil.ReadAll(os.Stdin)
			if err != nil {
				fmt.Println("Error reading template from stdin:")
				fmt.Println(err)
				os.Exit(1)
			}

			tmpl, err = tmpl.Parse(string(stdin))
			if err != nil {
				fmt.Println("Error parsing template from stdin:")
				fmt.Println(err)
				os.Exit(1)
			}
		}

		if err != nil {
			fmt.Println("Error parsing your template:")
			fmt.Println(err)
			fmt.Println("")
			fmt.Println("Please consult https://golang.org/pkg/text/template/ for valid template syntax.")
			os.Exit(1)
		}

		if all {
			err = tmpl.Execute(os.Stdout, list.Slice())
			if err != nil {
				fmt.Println("Error executing template:")
				fmt.Println(err)
				os.Exit(1)
			}
		} else {
			for _, entry := range list.Slice() {
				err = tmpl.Execute(os.Stdout, entry)
				if err != nil {
					fmt.Println("Error executing template:")
					fmt.Println(err)
					os.Exit(1)
				}

				fmt.Println("")
			}
		}
	},
}

ActionTemplateCmd represents the 'tags' action.

View Source
var ActionTitleCmd = &cobra.Command{
	Use:   "title",
	Short: "print titles",
	Long: `title will display the titles of all matched entries.

	$ albatross get -p school/a-level/further-maths title
	Further Maths - Lessons
	Further Maths - Solving Systems of Equations Using Triangle Method
	Further Maths - Cubics
	Further Maths - Integration
	Further Maths - Sums of Squares
	
The functionalities of this command can be achieved with the template command:

	$ albatross get -p school/a-level/further-maths template "{{.Title}}"`,

	Run: func(cmd *cobra.Command, args []string) {
		_, _, list := getFromCommand(cmd)

		for _, entry := range list.Slice() {
			fmt.Println(entry.Title)
		}
	},
}

ActionTitleCmd represents the 'tags' action.

View Source
var ActionUpdateCmd = &cobra.Command{
	Use:   "update",
	Short: "update an entry",
	Long: `update an entry from the command line
	
	$ albatross get -p food/pizza update

If multiple entries are matched, a list is displayed to choose from.`,
	Run: func(cmd *cobra.Command, args []string) {
		_, _, list := getFromCommand(cmd)
		var chosen *entries.Entry

		customEditor, err := cmd.Flags().GetString("editor")
		checkArg(err)

		length := len(list.Slice())

		if length == 0 {
			fmt.Println("No entries matched, nothing to update.")
			os.Exit(0)
		} else if length != 1 {
			paths := []string{}
			for _, entry := range list.Slice() {
				paths = append(paths, entry.Path)
			}

			fmt.Println("More than one entry matched, please select one.")
			prompt := promptui.Select{
				Label: "Select Entry",
				Items: paths,
			}

			_, result, err := prompt.Run()
			if err != nil {
				log.Fatalf("Couldn't choose entry: %s", err)
			}

			for _, entry := range list.Slice() {
				if entry.Path == result {
					chosen = entry
					break
				}
			}
		} else {
			chosen = list.Slice()[0]
		}

		updateEntry(chosen, customEditor)
	},
}

ActionUpdateCmd represents the update command

View Source
var ContentsCmd = &cobra.Command{
	Use:     "contents",
	Aliases: []string{"print", "contents"},
	Short:   "print entry contents",
	Long: `contents (also 'print' or 'read') will print the contents of entries to standard output.
	
	$ albatross get -p school/gcse/physics/topic4/nuclear-fusion contents | less
	# Opens a specific entry in less.
	
If you're using this to read entries, you'd probably be better off just simply using the update command
and not editing it whilst you're reading it. That way, you can get syntax highlighting.

This is more useful for doing stuff like word counts:

	$ albatross get contents | wc -w
	513362
	
	$ albatross get contents | tr -c '[:alnum:]' '[\n*]' | sort | uniq -c | sort -nr | head -100
	# Get the most common 100 words in the search`,

	Run: func(cmd *cobra.Command, args []string) {
		_, _, list := getFromCommand(cmd)

		raw, err := cmd.Flags().GetBool("raw")
		checkArg(err)

		between, err := cmd.Flags().GetString("between")
		checkArg(err)

		between += "\n"

		for _, entry := range list.Slice() {
			if raw {
				fmt.Println(entry.OriginalContents)
			} else {
				fmt.Println(entry.Contents)
			}

			fmt.Print(between)
		}
	},
}

ContentsCmd represents the contents command.

View Source
var CreateCmd = &cobra.Command{
	Use:     "create",
	Short:   "create a new entry",
	Aliases: []string{"new"},
	Long: `create a new entry from the command line
	
	$ albatross create food/pizza "The Most Amazing Pizza"

You can define templates which are placed in a "templates/" directory at the root of the store:

	.
	├── config.yaml
	├── entries/
	└── templates/
		└── exercise.tmpl

In the above example, the template could be used when creating the entry like so:

	$ albatross create logs/exercise/2020/08/30 -t exercise

You can use Go's template strings within the template files themselves to auto populate some values
to save typing:

	(exercise.tmpl)
	---
	title: 'Exercise Log'
	date: '<(.date | date "2006-01-02 15:04")>'
	---

	### [[Running]]
	* <(.distance)> mi @ !(.pace)!/mi

Notice the alternate syntax for templates, "<(" and ")>", opposed to Go's default "{{" and "}}". This is
to prevent interference with Albatross' path links.

As a context for the template, you can pass key values with the -c flag:

	$ albatross create logs/exercise/2020/08/30 -t exercise -c distance=3.24 -c pace=7:47

.date, as shown above, is set automatically to the current time. Sprig (https://github.com/Masterminds/sprig) helper
functions/pipelines are available, such as:

	- date
	- toJSON
	- upper

The default template is:

	---
	title: "<(default "Title" .title)>"
	date: "<(.date | date "2006-01-02 15:04")>"
	---

	`,
	Run: func(cmd *cobra.Command, args []string) {
		encrypted, err := store.Encrypted()
		if err != nil {
			log.Fatal(err)
		} else if encrypted {
			decryptStore()

			if !leaveDecrypted {
				defer encryptStore()
			}
		}

		editorName := getEditor("vim")
		customEditor, err := cmd.Flags().GetString("editor")
		checkArg(err)

		if customEditor != "" {
			editorName = customEditor
		}

		templateFile, err := cmd.Flags().GetString("template")
		checkArg(err)

		contextStrings, err := cmd.Flags().GetStringToString("context")
		checkArg(err)

		if len(args) == 0 {
			fmt.Println("Expecting exactly one or more arguments: path to entry and optional title")
			fmt.Println("For example:")
			fmt.Println("")
			fmt.Println("$ albatross create food/pizza Pizza")
		}

		contextStrings["title"] = strings.Join(args[1:], " ")

		contents := getTemplate(templateFile, contextStrings)

		err = store.Create(args[0], contents)
		if err != nil {
			log.Fatal("Couldn't create entry: ", err)
		}

		content, err := edit(editorName, contents)
		if err != nil {
			log.Fatal("Couldn't get content from editor: ", err)
		}

		err = store.Update(args[0], content)
		if err != nil {
			f, err := ioutil.TempFile("", "albatross-recover")
			if err != nil {
				logrus.Fatal("Couldn't get create temporary file to save recovery entry to. You're on your own! ", err)
			}

			_, err = f.Write([]byte(content))
			if err != nil {
				logrus.Fatal("Error writing to temporary file to save recovery entry to. You're on your own! ", err)
			}

			fmt.Println("Error creating entry. A copy has been saved to:", f.Name())
			os.Exit(1)
		}

		fmt.Println("Successfully created entry", args[0])
	},
}

CreateCmd represents the create command

View Source
var DecryptCmd = &cobra.Command{
	Use:   "decrypt",
	Short: "decrypt an albatross store",
	Long:  `decrypt will decrypt an albatross store`,
	Run: func(cmd *cobra.Command, args []string) {
		decryptStore()
	},
}

DecryptCmd represents the decrypt command

View Source
var EncryptCmd = &cobra.Command{
	Use:   "encrypt",
	Short: "encrypt an albatross store",
	Long: `encrypt will encrypt an albatross store

For example:

$ albatross encrypt
Encrypting... done in 45ms`,
	Run: func(cmd *cobra.Command, args []string) {
		encryptStore()
	},
}

EncryptCmd represents the encrypt command

View Source
var GetCmd = &cobra.Command{
	Use:     "get <filters> [action]",
	Short:   "get entries matching specific criteria and perform actions on them",
	Aliases: []string{"search", "query", "g"},
	Long: `get finds entries matching specific criteria and allows you to run actions on them, such as

- Printing their links
- Printing their paths
- Exporting them as JSON or YAML
- Generating flashcards

Some examples:

	# Get the entry about pizza.
	$ albatross get --path food/pizza

	# Get all journal entries between Jan 2020 and Feb 2020.
	$ albatross get --tag "@!journal" --from "2020-01-01 00:00AM" --until "2020-02-01 00:00AM"

	# Get all entries tagged "gcse", "physics" and "ankify".
	$ albatross get --tag "@?gcse" --tag "@?physics" --tag "@?ankify"

	# Sort all entries where you mention cats in reverse alphabetical order.
	$ albatross get --substring "cat" --sort "alpha" --rev

The syntax of a get command is:

	albatross get --<filters> [action]

Filters are flags which allow or disallow entries. For example:

	--tag "@?food"

Will only allow entries containing the tag "@?food". Furthermore,

	--tag "@?food" --path "notes"

Will only allow entries containing the tag "@?food" AND that are from the "notes/" path. So each subsequent
filter further restricts the amount of entries that can be matched. However, this leads to a difficulty: what
if you wish to specify multiple filters which will allow entries matching either criteria? In other words, what if
you want OR instead of AND?

In order to achieve this, some flags allow syntax like this:

	--path "notes OR school"

This will match entries from the path "notes/" or "school/". The filters that support this feature are:

	--path     --path-exact     --path-not     --path-exact-not
	--title    --title-exact    --title-not    --title-exact-not
	--contents --contents-exact --contents-not --contents-exact-not

You can also change the delimeter used from " OR " using the --delimeter flag.

By default, the command will print all the entries to all the paths that it matched. However, you can do
much more. 'Actions' are mini-programs that operate on lists of entries. For all available entries, see
the available subcommands.`,
	Run: func(cmd *cobra.Command, args []string) {
		ActionPathCmd.Run(cmd, args)
	},
}

GetCmd represents the get command

View Source
var GitCmd = &cobra.Command{
	Use:   "git",
	Short: "interface with git in a store",
	Long: `git lets you access git version control within the store.

Basically, it's a shorthand for doing

	$ albatross decrypt && cd $ALBATROSS_DIR && git... && albatross encrypt

For example:

	$ albatross git add .
	$ albatross git commit
	$ albatross git push

To pass flags to git, use the "--" seperator.

	$ albatross git -- log --oneline
	$ albatross git -- commit -m "commit message"`,
	Run: func(cmd *cobra.Command, args []string) {
		encrypted, err := store.Encrypted()
		if err != nil {
			log.Fatal(err)
		} else if encrypted {
			decryptStore()

			if !leaveDecrypted {
				defer encryptStore()
			}
		}

		if !store.UsingGit() {
			fmt.Printf("Store '%s' not using Git.\n", storeName)
			os.Exit(0)
		}

		newArgs := []string{"--git-dir", filepath.Join(storePath, "entries", ".git"), "--work-tree", filepath.Join(storePath, "entries")}
		c := exec.Command("git", append(newArgs, args...)...)
		c.Stdin = os.Stdin
		c.Stdout = os.Stdout
		c.Stderr = os.Stderr

		err = c.Run()
		if err != nil {
			log.Fatal(err)
		}
	},
}

GitCmd represents the git command

Functions

func Execute

func Execute()

Execute adds all child commands to the root command and sets flags appropriately. This is called by main.main(). It only needs to happen once to the rootCmd.

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL