Skip to content

Context Sensitive Commands

Ben Meyer edited this page Dec 9, 2015 · 25 revisions

VoiceCode gives you a lot of power when it comes to 'context-sensitive' commands.

Every VoiceCode command is a JavaScript function that runs in real-time, calling out to the command API to perform actions. This means any arbitrary logic can be performed to check for things like the current application, the current 'mode' or any other state or global variable. You can even configure your IDE to send relevant or contextual information to VoiceCode in the background, so when a command is executed, it has the relevant context needed to make decisions about what actions to perform.

Application Context

Whenever you switch applications, whether or not you do it using VoiceCode commands, the Application component of context will always stay in sync with your current foreground application.

You can configure any command to operate differently in different application contexts. For example, the following command definition declares a command called shackle, which is intended to 'select' the entire line of text that the cursor is currently in. Sublime Text it will press command-L, while in every other application it will default to pressing command-LeftArrow then command-shift-RightArrow

  "shackle":
    description: "selects the entire line"
    action: ->
      if @currentApplication() is "Sublime Text"
        @key "L", ['command']
      else
        @key "Left", ['command']
        @key "Right", ['command', 'shift']

Modal Context

The second part of context is the Mode you are in. You can add as many modes as you want. A mode might be something like: emacs or latex, etc.

Unlike Application context, modal context does not change automatically. It is explicitly set using the voice command: set mode <spoken argument>, where <spoken argument> is one of your predefined modes.

Here is an example of defining some modes. The left-hand side is what you say to activate the mode, and the right and side is the actual name of the mode.

Settings.extend 'modes',
  emacs: 'emacs'
  latex: 'latex'
  'mark down': 'markdown'

And here is an example of a command that changes its actions based on modal context.

  "steffi":
    description: "delete a partial word at a time"
    action: ->
      if @mode is "emacs"
        @key "Delete", ["control"]
      else
        @key "Delete", ["option"]

The different kinds of context can also be combined to give you very flexible control over which actions should be performed when. An example would be if I use the mac standalone Emacs application as well as emacs in the terminal. I don't want to have to repeat my command definitions for each of these scenarios. Instead I can combine the scenarios as follows:

  "steffi":
    description: "delete a partial word at a time"
    action: ->
      current = @currentApplication()
      if current is "Sublime Text"
        @key "Delete", ["control"]
      else if current is "iTerm" and @mode is "vim"
        @key "Delete", ["option"]
      else if current is "Emacs" or (current is "iTerm" and @mode is "emacs")
        @key "Delete", ["option"]
      else
        @key "Delete", ["option"]

Create Your Own

There are no limits on how you can use context. For example, you could set your IDE to always inform VoiceCode of the type of file you are editing, then make certain commands operate differently for different types of files. Or you could tell your IDE to to send the current buffer and cursor position to VoiceCode so that commands can be "content-aware". I will be exploring lots more applications of this in the future.

Current Status

Not very many of the core commands have been converted to use the context features yet, but I will be working to start incorporating support for standard IDE's such as Sublime Text, Vim, Emacs, XCode, etc. It is a big job, so if you want to help work on supporting commands for your favorite IDE, let me know :)

Imagine the possibilities

Once the core commands are all set up to work the same in each popular IDE, when you switch IDE's you don't have to relearn all the commands. The same voice commands can trigger the correct key-combos for that IDE, without you having to switch mental context.

Dragon Trigger Context (Application-Specific)

Some commands need to operate differently in different applications or contexts. However, other commands are very specific and only used within a single application or possibly several applications but not "globally". An example might be a command such as "Show All Tabs" in the Safari browser. Suppose Safari is the only browser which has this feature; you would want this command to be 'trigger-able' only in Safari - as it would have no meaning in other applications.

By default, any command you add will be "global". To assign it instead to a specific application or several applications, just set the parameter triggerScopes to an array of application names.

The following is a custom command I made to open the browser inspector for the element my mouse is hovering over on a page in Google Chrome.

Commands.create "inspector",
  description: "show web inspector"
  triggerScopes: ["Google Chrome"]
  continuous: false
  action: ->
    @rightClick()
    @string "inspect"
    @key "Return"

I do not want this command to be available anywhere but Google Chrome, and furthermore I do not want it to be "chain-able" or "nest-able" - because I cannot think of a situation where this command would appear inside a command phrase... It would always be the first element of the phrase. To achieve these restrictions, the continuous parameter is set to false and the 'triggerScopes' is set to an array containing 'Google Chrome', so the command can only be triggered from this application.

Here is an example of a "go-to-line" command that works differently in several IDE's. It takes a number as an argument and positions the cursor on that line number in the current file. If no line number argument is spoken, it just opens the relevant "go-to-line" dialogue if available in the IDE.

Commands.create
  "spring":
    grammarType: "numberCapture"
    description: "go to line number."
    triggerScopes: ["Sublime Text", "Xcode", "Atom"]
    action: (input) ->
      switch @currentApplication()
        when "Sublime Text"
          if input
            @exec "subl --command 'goto_line {\"line\": #{input}}'"
          else
            @key "G", ['control']
        when "Atom"
          @key "G", ['control']
          if input
            @string input
            @key "Return"
        when "Xcode"
          @key "L", ['command']
          if input?
            @delay 200
            @string input
            @delay 100
            @key "Return"

Clone this wiki locally