S or /

SourceTalon Files

Overview

Voice commands are defined in files with the .talon file extension, located in the user directory inside Talon Home.

.talon files can:

  • match specific applications, window titles, or other criteria

  • define voice commands

  • define global hotkeys

  • reimplement global actions (for example, to change the behavior of Talon in a specific application)

  • set settings

  • enable tags

Creating a file in user named hello.talon with these contents will declare a voice command:

hello talon: "hello world"

This means when you say hello talon, Talon will type hello world.

This is a more advanced example:

# activate this .talon file if the current app name is "Chrome"
# you can find app names by running ui.apps() in the REPL
app.name: Chrome
-
# key_wait increases the delay when pressing keys (milliseconds)
# this is useful if an app seems to jumble or drop keys
settings():
    key_wait = 4.0

# activate the global tag "browser"
tag(): browser

# define some voice commands
hello chrome: "hello world"
switch tab: key(ctrl-tab)
go to google:
    # note: use key(cmd-t) on Mac
    key(ctrl-t)
    insert("google.com")
    key(enter)

The - on the line after after app.name is important.

  • Lines above the - are used to set criteria for activating the file.

  • Lines below the - declare things or activate tags.

Any line beginning with # is considered to be a comment and ignored.

Defining Commands

Location and Structure

.talon files let you define commands. Talon loads commands from the .talon files in your user directory.

A command has a trigger and a body. The trigger defines what triggers the command, such as what you say for a voice command or what key(s) you press for a hot key. A : goes after the trigger. The body defines what the command does.

The following example Talonscript defines two commands. The first is a voice command called test. Saying test would insert the text: "This is a test". The second is a hotkey command triggered by pressing the shift and escape keys together and inserts the text: "This one is triggered by a hot key!"

test: insert("This is a test")
key(shift-escape): insert("This one is triggered by a hot key!")

A command body lists the actions that a command should perform. If there is only one action, it can go on the same line as the trigger. If there are multiple actions, put them on indented lines starting on the line after the trigger. The following example shows a command that insert some text and then presses the up key. Because the command performs more than one action, the body is written on indented lines after the trigger.

test: 
    insert("This is a test")
    key(up)

Actions

Some useful actions include:

Action name Description Parameters
insert Insert text The text to insert
key Press the key(s) Key(s) to press.
mouse_click Click a mouse button The number for the mouse button. 0 to left click, 1 to right click, and 2 for the middle mouse button
mouse_move Move the mouse to the screen coordinates The x and y coordinates
mouse_scroll Scroll the mouse wheel The amount to scroll vertically (positive means down, negative means up), the amount to scroll horizontally (positive means right, negative means left), and an optional boolean argument to scroll by lines that is false by default
sleep Pause the voice command for the amount of time given An amount of time. If you just use a number, that is interpreted as a number of seconds
repeat Repeats the previous action The number of times to repeat the action
mouse_nudge Moves the mouse relative to the current position The amount to move the mouse horizontally and vertically
mouse_drag Holds down a mouse button The number of the mouse button to hold down
mouse_release Stops holding a mouse button down The number of the mouse button to stop holding down

For the key action, use a - to separate keys in a keystroke. Use a space character to separate keystrokes. Use a : and then a number to press the keystroke that many times. key(shift-escape a:3 ctrl-s) will press shift and escape, then press the a key 3 times, and then press ctrl-s.

You can write a comment in Talonscript by writing a line that starts with a #. A comment is a note for humans that does nothing.

# left click
tap: mouse_click(0)
# left click 2 times
double click:
    mouse_click(0)
    repeat(1)

# scroll the mouse up
scroll up: mouse_scroll(-300)
# scroll the mouse down
scroll down: mouse_scroll(300)

# move the mouse cursor to the upper left position on the screen
move mouse to upper left: mouse_move(0, 0)
# move the mouse cursor 100 pixels from the left and 500 pixels from the top
test: mouse_move(100, 500)

# move the mouse up 10 pixels
move mouse up: mouse_nudge(0, -10)
# move the mouse down 10 pixels
move mouse down: mouse_nudge(0, 10)
# move the mouse left 10 pixels
move mouse left: mouse_nudge(-10, 0)
# move the mouse right 10 pixels
move mouse right: mouse_nudge(10, 0)

# hold the left mouse button down
start dragging the mouse: mouse_drag(0)
# stop holding the left mouse button down
stop dragging the mouse: mouse_release(0)

# type: hello world
# pause for 1 second
# then press the enter key
hello world:
    insert("hello world")
    sleep(1)
    key(enter)

Spoken Forms

You can use some symbols in the spoken form for a voice command. - Putting part of a spoken form in square brackets makes it optional. The word the is optional when using the following spoken form: stop dragging [the] mouse. - The | symbol means "or". You can say what is before it or what is after. When using the spoken form stop dragging | release, you could choose to say either stop dragging or release. - You can use parentheses for grouping. When using the spoken form stop (dragging | holding) the mouse, the "or" logic of the | symbol is applied to the inside of the parentheses. You could either say stop dragging the mouse or stop holding the mouse. - ^ and $ are used for anchoring. You can normally chain Talon commands together in a single utterance. This is different from voice command systems that require saying a single command at a time and having to wait for it to finish before you can say the next command. Anchoring puts limits on this. ^ at the start of a command means the command must be the first command in an utterance. $ at the end of a command means that the command must be the last command in an utterance. ^test$ means that you can only use the test command by itself. ^test means you can only use the test command if it is the first command you say in an utterance. (test)$ means you can only use the test command if it is the last command you say in an utterance. Anchoring is sometimes useful for preventing command misrecognitions. - + after something means you can repeat it as many times as you want. You can use the spoken form command test+ by saying the word command once followed by saying the word test as many times as you want. - * after something means you can say it as many times as you want including not saying it at all. You can use the spoken form command test* by saying command. You could also use it by saying command test or command test test. You can leave out the word test or repeat it as many times as you want.

Examples:

# the word "the" is optional
start dragging [the] mouse: mouse_drag(0)
# you can say "dragging" or "holding"
stop (dragging | holding) [the] mouse: mouse_release(0)
# you can say "mouse click" or "tap"
mouse click | tap: mouse_click(0)

# you can only say "test" in the command chain to match this
^test$: insert("Hello World!")
# this must be used at the start of the command chain
^start only: insert("Start")
# this must be used at the end of the command chain
(ending only)$: insert("End")

# say "click" as much as you want
double click+:
    mouse_click(0)
    repeat(1)
# say "click" as much as you want or not at all
triple click*:
    mouse_click(0)
    repeat(2)

Using Lists and Captures

You might want a voice command to move the mouse down but let the user give the number of pixels. You would want the user to say something like move mouse down five to move the mouse down five pixels and move mouse down ten to move the mouse down ten pixels. You would not want to do this by having to define a separate command for each number! Lists and captures address this problem.

The following command does this:

move mouse down <number>: mouse_nudge(0, number)

The user can say move mouse down and then a number to move the cursor down the given number of pixels!

You do not need to know much about the difference between lists and captures unless you want to make your own. The only difference you need to know when writing Talonscript is that you put a list in curly braces (such as: {list_name}) and put a capture in angle brackets (such as: <capture_name>). You can use the value of a list or capture by using the name of it in the command body as if the name was a regular value.

If you use the same list/capture name multiple times in a spoken form, you refer to the list/capture at the specific part of the spoken form by putting a _ after it and then a number. The number refers to the position from the left. _1 refers to the first match from the left and _2 refers to the second. See the following command for an example:

move mouse down <number> and right <number>: mouse_nudge(number_2, number_1)

This moves the mouse down by the number of pixels given by the first number and right by the number of pixels given by the second number.

Some useful captures include:

Name Description
<number> A positive integer
<number_small> An integer between 0 and 99
<number_signed> An integer
<modifiers> Modifier key(s)
<letter> A letter
<key> A keystroke
<symbol> A symbol

If you let users of your command repeat a capture or list, you use _list after the list or capture name to access the list consisting of everything the user said using the capture/list. The following command lets the user insert a list of letters. Using a repeat symbol lets the user provide a different value for the list or capture on each repetition, such as referring to different letters.

letters <letter>+:
    insert(letter_list)

Statements and Expressions

Like many programming languages, Talon lets you write expressions using operators and assign values to variables.

The available math operators are: +,-,*, % (modulus) and /. You can also use + for string concatenation.

Talonscript supports string interpolation where you can put the value of an identifier in a string by surrounding the identifier with curly braces. You can double curly braces to escape them. Talonscript strings support some escape characters like \n to represent a line break and \t to represent a tab character. The backslash character can be escaped with another backslash: \\.

You can use the or operator to handle a case where an optional list or capture is not used. number or 1 defaults to 1 if the user does not provide a value for <number>.

= is the assignment operator.

In Talonscript, you can only use one operator per expression. You need to do more complex combinations of operations across multiple lines.

demonstrate math operators:
    # store the result of "5+7" inside variable a
    a = 5+7
    # insert the value of "a" and press enter
    insert("{a}\n")
    # multiply a by 2 and store the result inside variable b
    b = a*2
    # insert the value of "b" and press enter
    insert("{b}\n")
    # divide a by 2 and store the result inside variable c
    c = a/2
    # insert the value of "c" and press enter
    insert("{c}\n")
    # subtract 7 from c and store the result inside variable d
    d = c-7
    # insert the value of "d" and press enter
    insert("{d}\n")
    # store the remainder of 5 divided by 2 inside variable e
    e = 5%2
    # insert the value of "e" and press enter
    insert("{e}\n")

# escape the braces to insert "{a}"
insert escaped braces: insert("{{a}}")

# press left the given number of times
left [<number>]:
    # n is number if the user says a number
    # n is otherwise 1
    n = number or 1
    key(left)
    repeat(n - 1)

# insert "\n"
escaped backslash: insert("\\n")

Talonscript allows inline if and for statements.

# Type the letters individually
letters <letter>+:
    for l in letter_list: insert("{l}")

if test:
    if true: insert("true") 
    if false: insert("false")

Debugging

You can open the Talon log from the Talon menu by clicking Scripting and then View Log. It is useful to keep the log open while scripting because it will show helpful error messages.

The log shows messages when a configuration file (such as a .talon, .py or .talon-list file) is added, deleted, or changed. The message will end with the filepath. The message will start with [+] when you add a file, [-] when you delete a file, and [~] when you change a file. If you think you added a file to your user directory but do not see the corresponding message, double check that you are putting the file in the right place.

The log shows error messages when you make mistakes in your Talonscript. You usually want to look at the bottom of those messages to figure out what the problem is.

Suppose you wrote a command like this:

test: insert(testing)

This will fail when you try to use the command because there are no quotation marks around the word testing.

The log will show you a long error message ending with something like this:

talon.scripting.talon_script.TalonScriptError: 
 in script at /Users/sam/.talon/user/my custom stuff/experimentation.talon:24:
 > insert(testing)
NameError: testing

This is because Talon interprets the word testing as a variable, but you have not defined that variable. You can fix the mistake by putting quotation marks around the word so it is interpreted as a string.

As another example, suppose you wrote a command like this:

test: insert "testing"

This command is badly formed, so the log shows an error message right away! It will end with something like this:

talon.scripting.talon_script.CompileError:   Line: 1, Column: 8 - unexpected token
      insert "testing"
             ^
  Expected: (,=,__ANON_1
 in script at /Users/sam/.talon/user/my custom stuff/experimentation.talon:24:
  insert "testing"

This means that the quotation mark after the word insert was not expected. Talon was expecting an opening parentheses or a variable assignment operator in between the word insert and the string.

These messages can help you find where the problem is and what the problem is. If you do not understand an error message, you can ask about it in the Slack channel.

REPL

Talon provides a REPL (Read Eval Print Loop), which lets you run actions in a command line interface. This can give useful information for scripting and debugging. You can open it from the Talon menu by clicking Scripting and then Console (REPL).

You can see a list of all Talon actions available by typing actions.list() in the REPL and then pressing enter. Actions that start with user. are defined by scripts in your user directory. If you downloaded a starting voice command set, a lot of those actions may have came from there. You might not want to depend on actions defined by someone else's project unless they have advertised that those actions are intended to be usable outside of their project and can be relied on as stable features that will be maintained long term.

Context Headers

You can limit when a .talon file is activated based on many factors including the application, operating system, and the window title. You do this at the top of a .talon file in a section called the context header. The context header must end in a line with nothing but a -.

Example:

tag: browser
os: windows
-

go back: key(alt-left)

Each line of the context header includes what kind of thing to match on, a : and then what specifically to match on. The line tag: browser matches on a tag called browser. The line os: windows matches on the Windows operating system. This .talon file is therefore only active if the browser tag is active and the operating system is windows.

Things you can match on include:

Name Description
title The title of the active window
tag A tag is something that can be conditionally activated such as for certain kinds of applications. See the Activating Tags section for more information
os The operating system. windows, mac, linux
mode Matches a Talon mode, such as sleep, command, dictation
language The current language of the speech recognition engine such as en for english
code.language The active programming language. A common use for this is activating commands for coding in a programming language when the user is editing a file in that language
hostname The name of the computer
app The active application
app.name The name of the active application
app.bundle The bundle of the active application
app.exe The name of the executable file of the active application
app.exe_path The path to the executable file of the active application
app.path The path to the active application
speech.engine The name of the active speech engine
win.filename The name of the current file if available for the current application
win.file_ext The extension of the current file if available for the current application

If you write multiple context header lines matching the same kind of thing, then by default this will match if any one of those lines matches. For instance, the following example will match on the Windows or Linux operating systems.

tag: browser
os: windows
os: linux
-

go back: key(alt-left)

If you write multiple context header lines matching different things, then by default this will match if the context header matches on every kind of thing. For instance, the above examples must match on both the tag and operating system.

You can put not at the start of a context header line to match the opposite. not tag: browser would match the browser tag not being active.

You can group lines with the and keyword. Each group starts with the first line not using and and ends at the last consecutive line using and. The context header will match if any group matches. The following example will match if the operating system is Linux or both the browser tag is active and the operating system is windows. If you want to use and and not on the same line, put and not.

tag: browser
and os: windows
os: linux
-

go back: key(alt-left)

In addition to exact matching, you can also match on a regular expression using /'s around the expression and optional python regular expression flags after the closing /. title: /.txt/ will match any window with .txt in the title. title: /talon/i will match any window with talon in the title ignoring capitalization.

Settings

A .talon file can set settings in a settings block. A settings block starts with the text settings(): and then has indented lines setting settings of the form setting_name = value.

Example:

app.name: Google Chrome
-
settings():
    # insert_wait increases the delay when pressing keys using the insert action (milliseconds)
    # consider increasing this if an app seems to jumble or drop keys when you insert text
    insert_wait = 2.0
    # key_hold increases how long keys are held down for. 
    # This can be useful for applications where key presses are not registering
    key_hold = 16.0
    # key_wait increases the delay when pressing keys (milliseconds)
    # you usually want to try the other settings first before trying key_wait
    key_wait = 4.0
    # speech.timeout decides how long you have to go without speaking
    # before Talon decides you are done with the current utterance (seconds)
    # increasing this can be useful if you seem to get cut off in the middle of dictating
    speech.timeout = 0.5

Activating Tags

As explained above, a tag is something you can match on in a context header. Talon itself provides the browser tag, but user Python scripts can define their own tags as well. You activate a tag in .talon files by writing an unindented line starting with tag(): and then the name of the tag, such as writing tag(): browser to activate the browser tag.

Example:

app.name: Google Chrome
-

tag(): browser
Source

File formats

Vocabulary entries

Reference