diff --git a/README.md b/README.md
index 622460d2..18110510 100755
--- a/README.md
+++ b/README.md
@@ -104,6 +104,12 @@ To uninstall mbed CLI, run:
pip uninstall mbed-cli
```
+### Adding Bash tab completion
+
+To install mbed-cli bash tab completion navigate to the `tools/bash_completion` directory. Then copy the `mbed` script into your `/etc/bash_completion.d/` or `/usr/local/etc/bash_completion.d` directory and reload your terminal.
+
+[Full documentation here](tools/bash_completion/install.md)
+
## Quickstart video
[![Video tutorial](http://img.youtube.com/vi/PI1Kq9RSN_Y/0.jpg)](https://www.youtube.com/watch?v=PI1Kq9RSN_Y)
diff --git a/tools/bash_completion/generator.py b/tools/bash_completion/generator.py
new file mode 100644
index 00000000..98fc9e7c
--- /dev/null
+++ b/tools/bash_completion/generator.py
@@ -0,0 +1,218 @@
+#!/usr/bin/env python
+# Michael Bartling (michael.bartling@arm.com)
+
+from collections import defaultdict
+import pystache
+import re
+import subprocess
+
+# Top level --version is a pain to deal with so ignoring for now
+# This one extracts single commands and the help txt
+commandRegex = r"^\s+(?P\w+)\s+(?P[a-zA-Z ]*)$"
+
+# Why the hell do spaces get regexed in command1 ?
+subcommandRegex = r"^\s+(?P-+[a-zA-Z_\-]+(?P\s+[A-Z_\-]+)?)"\
+ r"(?P,\s+-+[a-zA-Z_-]+(?P\s+[A-Z_-]+)?)?"\
+ r"\s+(?P.*)$"
+
+
+def getHelpTxt(command=None):
+ if command:
+ p = subprocess.Popen(["mbed", command, "-h"], stdout=subprocess.PIPE)
+ else:
+ p = subprocess.Popen(["mbed", "-h"], stdout=subprocess.PIPE)
+ out, err = p.communicate()
+ return out
+
+def getTargetCode():
+ txt = ''
+ with open("templates/target.tmplt") as fp:
+ txt = fp.read()
+ return txt
+
+def getToolchainCode():
+ txt = ''
+ with open("templates/toolchain.tmplt") as fp:
+ txt = fp.read()
+ return txt
+
+def getSCMCode():
+ txt = ''
+ with open("templates/scm.tmplt") as fp:
+ txt = fp.read()
+ return txt
+
+def getIDECode():
+ txt = ''
+ with open("templates/ide.tmplt") as fp:
+ txt = fp.read()
+ return txt
+
+def getProtocolCode():
+ txt = ''
+ with open("templates/protocol.tmplt") as fp:
+ txt = fp.read()
+ return txt
+
+def parseCommands():
+ commands = defaultdict(defaultdict)
+ commands["COMMAND"] = []
+ helpTxt = getHelpTxt()
+ # print helpTxt
+ for line in helpTxt.split('\n'):
+ match = re.search(commandRegex, line)
+ if match:
+ g = match.groupdict()
+ commands[g["command"]]["helptxt"] = g["helptxt"]
+ commands[g["command"]]["subcommands"] = []
+
+ # Subcommand mustache generation
+ commands[g["command"]]["DDASH_COMMANDS"] = []
+ commands[g["command"]]["DASH_COMMANDS"] = []
+ commands[g["command"]]["COMMAND"] = g["command"]
+
+ commands[g["command"]]["HAVE_PREV"] = {"PREV_CASE": []}
+
+ # Main function generation
+ commands["COMMAND"].append({"name": g["command"]})
+
+ for commandKey in commands:
+ # Skip
+ if commandKey == "COMMAND":
+ continue
+
+ helpTxt = getHelpTxt(commandKey)
+ for line in helpTxt.split('\n'):
+ match = re.search(subcommandRegex, line)
+ if match:
+ commandMatch = match.groupdict()
+
+ # Clean up the subcommands
+ command1 = commandMatch["command1"]
+ command2 = commandMatch["command2"]
+
+ if command1:
+ command1 = re.sub(",", "", command1)
+ command1.strip()
+ command1 = command1.split()[0]
+ if command2:
+ command2 = re.sub(",", "", command2)
+ command2.strip()
+ command2 = command2.split()[0]
+
+ # Not sure why the cleaning is even necessary,
+ # the regex looks correct
+ commandMatch["command1"] = command1
+ commandMatch["command2"] = command2
+
+ commands[commandKey]["subcommands"].append(commandMatch)
+
+ # Push format for mustache
+ if command1 and '--' in command1:
+ commands[commandKey]["DDASH_COMMANDS"].append(
+ {"name": command1})
+ if command2 and '--' in command2:
+ commands[commandKey]["DDASH_COMMANDS"].append(
+ {"name": command2})
+
+ if command1:
+ m = re.match("^-[a-zA-Z]{1,2}", command1)
+ if m:
+ commands[commandKey]["DASH_COMMANDS"].append(
+ {"name": command1})
+ else:
+ command1 = ""
+
+ if command2:
+ m = re.match("^-[a-zA-Z]{1,2}", command2)
+ if m:
+ commands[commandKey]["DASH_COMMANDS"].append(
+ {"name": command2})
+ else:
+ command2 = ""
+
+ # Adding the dependent command handlers
+ if "target" in command1 or "target" in command2:
+ commands[commandKey]["HAVE_PREV"]["PREV_CASE"].append({"case": "|".join(filter(None, [command1, command2])), "code": getTargetCode()})
+
+ if "toolchain" in command1 or "toolchain" in command2:
+ commands[commandKey]["HAVE_PREV"]["PREV_CASE"].append({"case": "|".join(filter(None, [command1, command2])), "code": getToolchainCode()})
+
+
+ if "--ide" in command1 or "--ide" in command2:
+ commands[commandKey]["HAVE_PREV"]["PREV_CASE"].append({"case": "|".join(filter(None, [command1, command2])), "code": getIDECode()})
+
+ if "scm" in command1 or "scm" in command2:
+ commands[commandKey]["HAVE_PREV"]["PREV_CASE"].append({"case": "|".join(filter(None, [command1, command2])), "code": getSCMCode()})
+
+ if "protocol" in command1 or "protocol" in command2:
+ commands[commandKey]["HAVE_PREV"]["PREV_CASE"].append({"case": "|".join(filter(None, [command1, command2])), "code": getProtocolCode()})
+
+ # Adding the dependent command handlers for target and toolchain
+ if "target" in commandKey:
+ commands[commandKey]["HAVE_PREV"]["PREV_CASE"].append({"case": commandKey, "code": getTargetCode()})
+
+ if "toolchain" in commandKey:
+ commands[commandKey]["HAVE_PREV"]["PREV_CASE"].append({"case": commandKey, "code": getToolchainCode()})
+
+ return commands
+
+
+def generateMain(commands):
+ tmplt = ""
+
+ txt = []
+
+ with open("templates/mbed.tmplt") as fp:
+ tmplt = fp.read()
+
+ txt.append(pystache.render(tmplt, commands))
+
+ return txt
+
+
+def generateCompleters(commands):
+ tmplt = ""
+ txt = []
+
+ renderer = pystache.Renderer(escape=lambda u: u)
+
+ with open("templates/command.tmplt") as fp:
+ tmplt = fp.read()
+
+ for commandKey in commands:
+ txt.append(renderer.render(tmplt, commands[commandKey]))
+
+ # if need to add hacks add them here
+
+ return txt
+
+
+def generateBoilerPlate(commands):
+ txt = []
+
+ with open("templates/boilerplate.tmplt") as fp:
+ txt.append(fp.read())
+
+ return txt
+
+
+def generateScript(commands):
+ txt = []
+
+ txt.extend(generateBoilerPlate(commands))
+ txt.extend(generateCompleters(commands))
+ txt.extend(generateMain(commands))
+
+ with open("mbed-completion", "w") as fp:
+ for x in txt:
+ fp.write("%s\n" % x)
+
+
+if __name__ == '__main__':
+ commands = parseCommands()
+
+ # At this point we have a list of all the commands and sub commands
+ # for each command create a Bash function
+ # register each subcommand
+ generateScript(commands)
diff --git a/tools/bash_completion/install.md b/tools/bash_completion/install.md
new file mode 100644
index 00000000..15e01324
--- /dev/null
+++ b/tools/bash_completion/install.md
@@ -0,0 +1,13 @@
+# Install Guide
+
+## System-wide Installation (root)
+
+- Copy or link the bash completion script, `mbed`, into your `/etc/bash_completion.d` or `/usr/local/etc/bash_completion.d` directory.
+- Reopen terminal
+
+## Local Installation
+
+- `mkdir ~/.bash_completion.d && cp mbed ~/.bash_completion.d/`
+- `echo "source ~/.bash_completion.d/mbed" >> ~/.bash_profile`
+- logout and login
+
diff --git a/tools/bash_completion/mbed b/tools/bash_completion/mbed
new file mode 100644
index 00000000..e2561f16
--- /dev/null
+++ b/tools/bash_completion/mbed
@@ -0,0 +1,750 @@
+# Based on mbed auto complete scripts
+
+__mbedcomp_words_include() {
+ local i=1
+ while [[ "$i" -lt "$COMP_CWORD" ]]
+ do
+ if [[ "${COMP_WORDS[i]}" = "$1" ]]
+ then
+ return 0
+ fi
+ i="$((++i))"
+ done
+ return 1
+}
+
+# Find the previous non-switch word
+__mbedcomp_prev() {
+ local idx="$((COMP_CWORD - 1))"
+ local prv="${COMP_WORDS[idx]}"
+ while [[ "$prv" = -* ]]
+ do
+ idx="$((--idx))"
+ prv="${COMP_WORDS[idx]}"
+ done
+ echo "$prv"
+}
+
+__mbedcomp() {
+# break $1 on space, tab, and newline characters,
+# and turn it into a newline separated list of words
+ local list s sep=$'\n' IFS=$' '$'\t'$'\n'
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ for s in $1
+ do
+ __mbedcomp_words_include "$s" && continue
+ list="$list$s$sep"
+ done
+
+ IFS="$sep"
+ COMPREPLY=($(compgen -W "$list" -- "$cur"))
+}
+
+__mbed_complete_new() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --scm
+ --program
+ --library
+ --mbedlib
+ --create-only
+ --depth
+ --protocol
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+ case "$prev" in
+ --scm)
+ __mbedcomp "bld git hg"
+ return
+ ;;
+ --protocol)
+ __mbedcomp "https http ssh git"
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_import() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --ignore
+ --depth
+ --protocol
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -I
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+ case "$prev" in
+ --protocol)
+ __mbedcomp "https http ssh git"
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_add() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --ignore
+ --depth
+ --protocol
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -I
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+ case "$prev" in
+ --protocol)
+ __mbedcomp "https http ssh git"
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_remove() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_deploy() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --ignore
+ --depth
+ --protocol
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -I
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+ case "$prev" in
+ --protocol)
+ __mbedcomp "https http ssh git"
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_publish() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --all
+ --message
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -A
+ -M
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_update() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --clean
+ --clean-files
+ --clean-deps
+ --ignore
+ --depth
+ --protocol
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -C
+ -I
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+ case "$prev" in
+ --protocol)
+ __mbedcomp "https http ssh git"
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_sync() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_ls() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --all
+ --ignore
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -a
+ -I
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_status() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --ignore
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -I
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_compile () {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --toolchain
+ --target
+ --profile
+ --library
+ --config
+ --prefix
+ --source
+ --build
+ --clean
+ --artifact-name
+ --supported
+ --app-config
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -t
+ -m
+ -c
+ -N
+ -S
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+ case "$prev" in
+ --target|-m)
+ declare TARGETS=$(mbed target --supported | cut -d '|' -f 2 | sed -n '/^+/!p' | sed -n '/^Supported/!p' | sed -n '/^ [A-Z][A-Z]/p')
+ __mbedcomp "${TARGETS}"
+ return
+ ;;
+ --toolchain|-t)
+ declare TOOLCHAINS=$(mbed target --supported | head -n 2 | tail -n 1 | tr '|' '\n' | sed -n '/^ Target/!p' | sed -n '/^ mbed/!p')
+ __mbedcomp "${TOOLCHAINS}"
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_test() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --toolchain
+ --target
+ --compile-list
+ --run-list
+ --compile
+ --run
+ --tests-by-name
+ --source
+ --build
+ --profile
+ --clean
+ --test-spec
+ --app-config
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -t
+ -m
+ -n
+ -c
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+ case "$prev" in
+ --target|-m)
+ declare TARGETS=$(mbed target --supported | cut -d '|' -f 2 | sed -n '/^+/!p' | sed -n '/^Supported/!p' | sed -n '/^ [A-Z][A-Z]/p')
+ __mbedcomp "${TARGETS}"
+ return
+ ;;
+ --toolchain|-t)
+ declare TOOLCHAINS=$(mbed target --supported | head -n 2 | tail -n 1 | tr '|' '\n' | sed -n '/^ Target/!p' | sed -n '/^ mbed/!p')
+ __mbedcomp "${TOOLCHAINS}"
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_export() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --ide
+ --target
+ --source
+ --clean
+ --supported
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -i
+ -m
+ -c
+ -S
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+ case "$prev" in
+ --target|-m)
+ declare TARGETS=$(mbed export --supported | cut -d '|' -f 2 | sed -n '/^+/!p' | sed -n '/^Platform/!p' | sed -n '/^Total/!p')
+ __mbedcomp "${TARGETS}"
+ return
+ ;;
+ --ide|-i)
+ declare IDES=$(mbed export --supported | tail -n +1 | head -n 2 | tr '|' '\n' | sed -n '/^+/!p' | sed -n '/^ Platform/!p')
+ __mbedcomp "${IDES}"
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_detect() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_config() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --global
+ --unset
+ --list
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -G
+ -U
+ -L
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_target() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --global
+ --supported
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -G
+ -S
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+ case "$prev" in
+ target|-G|--global|-v|--verbose|-vv|--very_verbose)
+ declare TARGETS=$(mbed target --supported | cut -d '|' -f 2 | sed -n '/^+/!p' | sed -n '/^Supported/!p' | sed -n '/^ [A-Z][A-Z]/p')
+ __mbedcomp "${TARGETS}"
+ return
+ ;;
+ esac
+}
+__mbed_complete_toolchain() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --global
+ --supported
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -G
+ -S
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+ case "$prev" in
+ toolchain|-G|--global|-v|--verbose|-vv|--very_verbose)
+ declare TOOLCHAINS=$(mbed target --supported | head -n 2 | tail -n 1 | tr '|' '\n' | sed -n '/^ Target/!p' | sed -n '/^ mbed/!p')
+ __mbedcomp "${TOOLCHAINS}"
+ return
+ ;;
+ esac
+}
+
+__mbed_complete_help() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ --help
+ --verbose
+ --very_verbose
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ -h
+ -v
+ -vv
+ "
+ return
+ ;;
+ esac
+}
+
+_mbed () {
+ local i=1 cmd prev
+
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+# find the subcommand
+ while [[ "$i" -lt "$COMP_CWORD" ]]
+ do
+ local s="${COMP_WORDS[i]}"
+ case "$s" in
+ --*)
+ cmd="$s"
+ break
+ ;;
+ -*)
+ ;;
+ *)
+ cmd="$s"
+ break
+ ;;
+ esac
+
+ i="$((++i))"
+ done
+
+ # Handle the main command completions
+ if [[ "$i" -eq "$COMP_CWORD" ]]
+ then
+ local cmds="
+ --version
+ new
+ import
+ add
+ remove
+ deploy
+ publish
+ update
+ sync
+ ls
+ status
+ compile
+ test
+ export
+ detect
+ config
+ target
+ toolchain
+ help
+ "
+
+ __mbedcomp "${cmds}"
+ fi
+
+ # Each subcommand has a completion function based on the parent
+ case "$cmd" in
+ new) __mbed_complete_new ;;
+ import) __mbed_complete_import ;;
+ add) __mbed_complete_add ;;
+ remove) __mbed_complete_remove ;;
+ deploy) __mbed_complete_deploy ;;
+ publish) __mbed_complete_publish ;;
+ update) __mbed_complete_update ;;
+ sync) __mbed_complete_sync ;;
+ ls) __mbed_complete_ls ;;
+ status) __mbed_complete_status ;;
+ compile) __mbed_complete_compile ;;
+ test) __mbed_complete_test ;;
+ export) __mbed_complete_export;;
+ detect) __mbed_complete_detect ;;
+ config) __mbed_complete_config ;;
+ target) __mbed_complete_target ;;
+ toolchain) __mbed_complete_toolchain ;;
+ *) ;;
+ esac
+
+}
+
+complete -o bashdefault -o default -F _mbed mbed
+complete -o bashdefault -o default -F _mbed mbed-cli
+
diff --git a/tools/bash_completion/templates/.README.md.swp b/tools/bash_completion/templates/.README.md.swp
new file mode 100644
index 00000000..8cb82c5c
Binary files /dev/null and b/tools/bash_completion/templates/.README.md.swp differ
diff --git a/tools/bash_completion/templates/README.md b/tools/bash_completion/templates/README.md
new file mode 100644
index 00000000..9c35ed54
--- /dev/null
+++ b/tools/bash_completion/templates/README.md
@@ -0,0 +1,3 @@
+# Templates
+
+Note these templates are inspired by the mustache syntax.
diff --git a/tools/bash_completion/templates/boilerplate.tmplt b/tools/bash_completion/templates/boilerplate.tmplt
new file mode 100644
index 00000000..deaa6076
--- /dev/null
+++ b/tools/bash_completion/templates/boilerplate.tmplt
@@ -0,0 +1,43 @@
+# Note this file is generated
+# Based on mbed auto complete scripts
+
+__mbedcomp_words_include() {
+ local i=1
+ while [[ "$i" -lt "$COMP_CWORD" ]]
+ do
+ if [[ "${COMP_WORDS[i]}" = "$1" ]]
+ then
+ return 0
+ fi
+ i="$((++i))"
+ done
+ return 1
+}
+
+# Find the previous non-switch word
+__mbedcomp_prev() {
+ local idx="$((COMP_CWORD - 1))"
+ local prv="${COMP_WORDS[idx]}"
+ while [[ "$prv" = -* ]]
+ do
+ idx="$((--idx))"
+ prv="${COMP_WORDS[idx]}"
+ done
+ echo "$prv"
+}
+
+__mbedcomp() {
+# break $1 on space, tab, and newline characters,
+# and turn it into a newline separated list of words
+ local list s sep=$'\n' IFS=$' '$'\t'$'\n'
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+
+ for s in $1
+ do
+ __mbedcomp_words_include "$s" && continue
+ list="$list$s$sep"
+ done
+
+ IFS="$sep"
+ COMPREPLY=($(compgen -W "$list" -- "$cur"))
+}
diff --git a/tools/bash_completion/templates/command.tmplt b/tools/bash_completion/templates/command.tmplt
new file mode 100644
index 00000000..726b5f0a
--- /dev/null
+++ b/tools/bash_completion/templates/command.tmplt
@@ -0,0 +1,37 @@
+__mbed_complete_{{COMMAND}}() {
+
+ local cur="${COMP_WORDS[COMP_CWORD]}"
+ local prev="${COMP_WORDS[COMP_CWORD-1]}"
+ case "$cur" in
+ --*)
+ __mbedcomp "
+ {{#DDASH_COMMANDS}}
+ {{name}}
+ {{/DDASH_COMMANDS}}
+ "
+
+ return
+ ;;
+ -*)
+ __mbedcomp "
+ {{#DASH_COMMANDS}}
+ {{name}}
+ {{/DASH_COMMANDS}}
+ "
+ return
+ ;;
+ esac
+
+ # Handle the dependent commands
+ {{#HAVE_PREV}}
+ case "$prev" in
+ {{#PREV_CASE}}
+ {{case}})
+ {{code}}
+ return
+ ;;
+ {{/PREV_CASE}}
+ esac
+ {{/HAVE_PREV}}
+
+}
diff --git a/tools/bash_completion/templates/ide.tmplt b/tools/bash_completion/templates/ide.tmplt
new file mode 100644
index 00000000..99381803
--- /dev/null
+++ b/tools/bash_completion/templates/ide.tmplt
@@ -0,0 +1,2 @@
+declare IDES=$(mbed export --supported ides)
+ __mbedcomp "${IDES}"
diff --git a/tools/bash_completion/templates/mbed.tmplt b/tools/bash_completion/templates/mbed.tmplt
new file mode 100644
index 00000000..134c5c97
--- /dev/null
+++ b/tools/bash_completion/templates/mbed.tmplt
@@ -0,0 +1,51 @@
+
+_mbed () {
+ local i=1 cmd prev
+
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
+
+# find the subcommand
+ while [[ "$i" -lt "$COMP_CWORD" ]]
+ do
+ local s="${COMP_WORDS[i]}"
+ case "$s" in
+ --*)
+ cmd="$s"
+ break
+ ;;
+ -*)
+ ;;
+ *)
+ cmd="$s"
+ break
+ ;;
+ esac
+
+ i="$((++i))"
+ done
+
+ # Handle the main command completions
+ if [[ "$i" -eq "$COMP_CWORD" ]]
+ then
+ local cmds="
+ {{#COMMAND}}
+ {{name}}
+ {{/COMMAND}}
+ "
+
+ __mbedcomp "${cmds}"
+ fi
+
+ # Each subcommand has a completion function based on the parent
+ case "$cmd" in
+ {{#COMMAND}}
+ {{name}}) __mbed_complete_{{name}} ;;
+ {{/COMMAND}}
+ *) ;;
+ esac
+
+}
+
+complete -o bashdefault -o default -F _mbed mbed
+complete -o bashdefault -o default -F _mbed mbed-cli
+
diff --git a/tools/bash_completion/templates/protocol.tmplt b/tools/bash_completion/templates/protocol.tmplt
new file mode 100644
index 00000000..50744929
--- /dev/null
+++ b/tools/bash_completion/templates/protocol.tmplt
@@ -0,0 +1 @@
+__mbedcomp "https http ssh git"
diff --git a/tools/bash_completion/templates/scm.tmplt b/tools/bash_completion/templates/scm.tmplt
new file mode 100644
index 00000000..cc023b66
--- /dev/null
+++ b/tools/bash_completion/templates/scm.tmplt
@@ -0,0 +1 @@
+__mbedcomp "bld git hg"
diff --git a/tools/bash_completion/templates/target.tmplt b/tools/bash_completion/templates/target.tmplt
new file mode 100644
index 00000000..938a9bc2
--- /dev/null
+++ b/tools/bash_completion/templates/target.tmplt
@@ -0,0 +1,2 @@
+declare TARGETS=$(mbed compile --supported targets)
+ __mbedcomp "${TARGETS}"
diff --git a/tools/bash_completion/templates/toolchain.tmplt b/tools/bash_completion/templates/toolchain.tmplt
new file mode 100644
index 00000000..42a2a311
--- /dev/null
+++ b/tools/bash_completion/templates/toolchain.tmplt
@@ -0,0 +1,2 @@
+declare TOOLCHAINS=$(mbed compile --supported toolchains)
+ __mbedcomp "${TOOLCHAINS}"