beorg comes with a scripting language based on Scheme. Some examples of what you can use this for:
The version of Scheme included in beorg is called BiwaScheme, you can find out more on the BiwaScheme home page.
The beorg REPL allows you to type Scheme expressions (commands) and see the results immediately.
To start the REPL,
Here is a very simple example of using the REPL which just parrots back “Hello, World”:
> "Hello, World"
"Hello, World"
You can use the beorg REPL to do simple calculations:
> (+ 3 4)
7
This is equivalent to writing 3 + 4.
Scheme is conceptually very simple. Most Scheme expressions are in
parenthesis and start with the name of a function followed by some
arguments. In the example of (+ 3 4)
the function is +
and the
arguments are 3 and 4.
You can nest Scheme expressions. For example:
> (+ 3 (* 2 5))
13
That adds 3 to the result of multiplying 5 and 2.
Scheme is based on lists (Scheme is in the Lisp family of languages and the name Lisp comes from LISt Processor). Both Scheme programs and data are written as lists. If you want a list where the first item isn’t a function to execute you need to write it like this:
> '(apple banana orange)
(apple banana orange)
In the above example you’ve created a list of the symbols apple, banana and orange. A symbol is different from a string in that it can be used to represent the name of a variable or function. If you wanted a list of strings you would write them like this:
'("apple" "banana" "orange")
There are lots of tutorials and books about Scheme and Lisp available on the web.
You’ll be using Scheme to customize beorg in ways you can’t on the Settings tab.
One example might be wanting to use a different font in the notes editor. It isn’t currently possible to change this from the settings tab, but you can by writing some Scheme. Here is what you’d write if you wanted to use the font Baskerville at size 14pt:
(set! editor-font "Baskerville 14")
You can try this by starting the beorg REPL, writing the above Scheme expression and then editing a note. Note that it won’t affect any editors currently open.
To get help on a particular variable you can use the function documentation. For example:
> (documentation 'editor-font)
The font to use in the REPL. The format is '<FONT NAME> <FONT SIZE>'. For example 'Menlo 15'.
Note that when using set! don’t put a '
before the name, however you
must put a '
when using the documentation function.
When beorg starts it looks for a file called init.org
. This is the
same sort of file you use for tasks and notes, however it contains Scheme
expressions. The Scheme expressions are in special blocks so
that they can be interspersed with normal text. Here is an example of
what a simple init.org might look like to change the editor font:
* Fonts
#+begin_src scheme
(set! editor-font "Baskerville 14")
#+end_src
The special blocks indicating the Scheme expressions start with
#+begin_src scheme
and end with #+end_src
.
You can edit your init.org using beorg and the outline editor, however you may find it easier doing so using a text editor on your computer and then syncing with beorg.
Changing the font used in the editor:
(set! editor-font "<font-name> <font-size>")
For example:
(set! editor-font "Menlo 15")
For a list of the available fonts in iOS check out iOS Fonts.
Changing the font used in the REPL:
(set! repl-font "<font-name> <font-size>")
A toolbar is shown above the keyboard when editing notes and using the REPL.
Here is an example of an editor toolbar definition:
(set! editor-toolbar-items '(("icon-left" (backward-char))
("icon-right" (forward-char))
("icon-list" (insert "+ "))
("icon-change" (show-transform-commands))
("icon-settings" (insert-code-snippet))))
This toolbar has five icons. You can add as many icons as you wish. If there are more than can be displayed on the screen they
will be scrolled horizontally. The variable editor-toolbar-items
is a
list of lists. Each list, which defines one toolbar item, has either an
icon name or some text to display followed by some Scheme to execute.
Changing the REPL toolbar is exactly the same, just use the variable
name repl-toolbar-items
.
By default the toolbar will only be shown in a full screen editor. If you would like it shown always then add the following to your init.org:
(set! editor-toolbar-show-in-mini #t)
The text shown in the editor and REPL is referred to as the buffer.
There are various functions which operate on the buffer and retrieve
information from the buffer either about the text or the cursor (known
as the point). To get the current size of the buffer write
(buffer-size)
.
The cursor (or point) shows the user where any typed characters will appear. beorg can change the position of the point and get information about its current position.
(point)
returns the current position of the point.(point-min)
returns the position at the start of the buffer. This
is always 1.(point-max)
returns the position after the last character in the
buffer.(goto-char n)
moves the point to n, e.g. (goto-char 1)
moves the
point to the start of the buffer.(backward-char)
moves the point back one character.(forward-char)
moves the point forward one character.In addition to the point (cursor) there is also the text currently selected - referred to here as the region.
(region-beginning)
returns the start position of any selected text(region-end)
returns the end position of any selected textNOTE: That if no text is selected then both (region-beginning)
and
(region-end)
will be the same as (point)
.
To insert text into the buffer use the insert
function. For example…
(insert "Hello")
… inserts the text Hello
at the position of the point (i.e. the
cursor). The point will now be at the end of the text just inserted.
To get some text, so you can manipulate it, use the buffer-substring
function which takes two arguments - from and to. For example…
(buffer-substring (point-min) (point-max))
… gets all of the text in the buffer. This can also be achieved using
(region)
.
To delete text in the buffer use delete-region
which also takes the
arguments from and to. For example…
(delete-region (point-min) (point-max))
… will delete all of the text in the buffer.
To replace the currently selected text use region-text-set
. For
example…
(region-text-set (string-upcase (region)))
…will convert any selected text into uppercase.
You can show an alert to the user using the alert
function. It
takes two arguments, the first is the title of the alert and the
second the message. For example…
(alert "Welcome" "Hi there!")
… will show an alert with the text Hi there!
to the user in an
alerted titled Welcome
.
An alert requires the user to tap the OK button to dismiss. An alternative to an alert is a toast. This is a message which slides down from the top of the screen and then disappears shortly afterwards. The toast
function has the same parameters as an alert, a title and a message. For example…
(toast "Calculation Result" (number->string (+ 2 3)))
… will show a toast with the title “Calculation Result” and the message “5”.
To show the user a set of options from which to choose you can create a sheet. A sheet is similar to a toolbar in that it is a list of lists where the first item in each sublist is the text to display to the user (icons not supported here) followed by some Scheme code to run. For example…
(define (show-transform-commands)
(if (string=? (region) "")
(alert "" "No text is selected.")
(sheet '(("Uppercase" (region-text-set (string-upcase (region))))
("Lowercase" (region-text-set (string-downcase (region))))))))
…creates a Scheme function which, if the user has some text selected, show a sheet with the options Uppercase and Lowercase. If the user selects Uppercase then their selected text will be converted to uppercase.
Sheets are a great way to extend the editor with functionality that fits your workflow.
You can request that the user provide a date using the date picker. This will present a date picker and then execute a provided callback. In addition to the callback the date picker function takes the default date to display and whether the date includes a time component. When the user has picked a date the callback will be provided a boolean saying whether or not the user cancelled/removed the date, the date itself and whether the user included a time with the date.
Here is an example:
(date-picker (current-date)
#f
(lambda (removed date includes-time)
(if (not removed)
(alert "Date" (date->string date)))))
This will ask the user for a date and then if a date was picked (i.e. the user didn’t tap the trash can to remove the date) then the date will be displayed in an alert.
On the TODO tab you can filter tasks. There are a number of built-in filters (for example only show tasks which have a priority set). Here is an example of a custom filter called “Top Priority” which only shows tasks which have their priority set to A:
(filter-add "Top Priority" (lambda (item) (string=? (item-priority item) "A")))
The first argument to the function filter-add
is the name of the
filter to show in the filter list. The second argument is a procedure
which returns either true or false. In this case true is returned if the
priority is A. The procedure will be passed an Org item. The following
functions are available to retrieve data from the item:
You can configure beorg to start on a specific tab (agenda, TODO or files). For example…
(set! ui-start-tab "todo")
…will tell beorg to start on the TODO tab. Other valid options are agenda and files.
(Requires beorg 3.19.0 or newer)
The item editor is the primary way to add and edit individual items. An example of an item in a .org
file:
* TODO [#A] Plan next months newsletter :newsletter:
SCHEDULED: <2022-01-09 Mon>
Talk with Sheila about what she needs to include.
You can see the item has:
TODO
A
Plan next months newsletter
You can add functionality to the item editor to update most properties of an item with some Scheme code.
In library.org
, the file which is read on startup by beorg, the following code creates a menu in the item editor with some useful quick actions:
(define (add-location-to-current-item)
(location-get-lat-lon (lambda (lat lon) (set-current-item-property! "location" (string-append lat "," lon)))))
(define (make-current-item-top-priority-today)
(begin (set-current-item-scheduled! (current-date))
(set-current-item-priority! "A")))
(define (schedule-current-item-for-tomorrow)
(set-current-item-scheduled! (date-adjust (current-date) 1 'days)))
(define (remove-all-dates-from-current-item)
(begin (delete-current-item-scheduled!)
(delete-current-item-deadline!)
(delete-current-item-active-date!)))
(defvar item-editor-menu
'(("Assign current location" (add-location-to-current-item))
("Make top priority today" (make-current-item-top-priority-today))
("Schedule for tomorrow" (schedule-current-item-for-tomorrow))
("Remove all dates" (remove-all-dates-from-current-item)))
"The items defined here can be run directly from the item editor screen to make quick adjustments.")
The variable item-editor-menu
has a list of menu items. Each list contains the name of the menu item, and then the code to execute when the item is selected.
For example the default menu includes an item with the title “Make top priority today” which executes the function make-current-item-top-priority-today
which is defined as follows:
(define (make-current-item-top-priority-today)
(begin (set-current-item-scheduled! (current-date))
(set-current-item-priority! "A")))
The above code sets the scheduled date of the current item being shown in the item editor to the current date, and sets the priority to A.
If you want to define your own menu of quick actions use set!
to change the variable item-editor-menu
:
(set! item-editor-menu
'(("Make top priority today" (make-current-item-top-priority-today))
("Remove all dates" (remove-all-dates-from-current-item))))
Here are the functions which can be used to modify the current item:
Function | Example |
---|---|
set-current-item-headline! |
(set-current-item-headline! "Buy shoes") |
set-current-item-state! |
(set-current-item-state! "IN-PROGRESS") |
set-current-item-priority! |
(set-current-item-priority! "A") |
set-current-item-scheduled! |
(set-current-item-scheduled! (date-adjust (current-date) 1 'months')) |
set-current-item-deadline! |
(set-current-item-deadline! (current-date)) |
set-current-item-active-date! |
(set-current-item-active-date! (current-date)) |
set-current-item-scheduled-with-time! |
(set-current-item-scheduled-with-time! (current-date)) |
set-current-item-deadline-with-time! |
(set-current-item-deadline-with-time! (current-date)) |
set-current-item-active-date-with-time! |
(set-current-item-active-date-with-time! (current-date)) |
delete-current-item-scheduled! |
(delete-current-item-scheduled!) |
delete-current-item-deadline! |
(delete-current-item-deadline!) |
delete-current-item-active-date! |
(delete-current-item-active-date!) |
set-current-item-tags! |
(set-current-item-tags! '("food" "shopping")) |
set-current-item-property! |
(set-current-item-property! "effort" "2:00") |
If you have purchased the Export Themes in-app purchase extension then you can add your own themes for exporting documents.
Your themes need to be in a file which is then loaded using the
load-themes
Scheme function. You can add this to your init.org so that
your themes get loaded everytime beorg starts.
A themes file must contain one or more headings under each being a source block of CSS. For example here is a very minimal theme file:
* My minimal theme
This is a description of my minimal theme
#+begin_src css
@include "beorg default theme";
body {
font-family: monospace;
}
#+end_src
The above theme extends the default theme by setting the font to monospace.
As you can see above you can include another theme using the @include
syntax. Note that the theme must have been read by beorg already, i.e.
it cannot occur later in the file or in another file which hasn’t been
read yet.
Each theme should have a unique name. In the above example the name of
the theme is My minimal theme
. If a theme is loaded with the name of
another than it will override that theme.
If the name of a theme starts with an underscore then it won’t be shown in the theme selector UI.
beorg defines a number of variables with details about your device. These may be useful for conditionally customising beorg for iPhone and iPad differently. The variables are:
Here are details of some other scheme functions defined by beorg which aren’t mentioned above.
Opens a URL, usually in the browser but may be handled by another app.
(open-url "https://beorg.app")
Grabs the current contents of the clipboard.
(pasteboard)
Sets the clipboard.
(pasteboard-set! "Hello")
Turns the editor autocorrect on or off.
(autocorrect-set! #t)
Returns the point index of the next instance of a string in the editor from the current cursor position.
(string-search-forward "word")
Returns the point index of the last instance of a string in the editor from the current cursor position.
(string-search-background "word")
Returns a date adjusted by the specified amount.
;; Return the current date + 1 year
(date-adjust (current-date) 1 'years)
You can provide the symbols 'years
, 'months
, 'days
, 'hours
, 'minutes
, 'seconds
to adjust by.
Changes a date component to a specific value and returns a new date.
;; Returns a date with the current month, year, etc but with the day set to the 1st
(date-set (current-date) 1 'year)
You can provide the symbols 'year
, 'month
, 'day
, 'hour
, 'minute
, 'second
to adjust by.
When beorg starts it reads a Scheme file which comes bundled with the app. This file contains a number of defaults and helper functions used by beorg. If you want to get serious about beorg scripting then you should take a look at this file to help you realise what’s possible.
View library.org (Please note the version with the current release of beorg may vary from this.)
You can view the version of library.org that is bundled with your installed version of beorg by tapping the help icon in the REPL.