/Sublime-AngularJS-Coffee-Completions

A Sublime Text Package for AngularJS when using CoffeeScript

Logo

AngularJs Sublime Text 2 Bundle

This package provides snippets for all the available AngularJS api calls. The snippets are activated both in HTML and CoffeeScript.

Is this a major perversion of the snippet system? In a way, yes.

Think of it more as a poor mans Intellisense, rather than a series of snippets. The snippets intentionally overlap with one another, so that only a few simple mnemonics require memorization, rather than hundreds.

Using this approach, instead of providing a xyz,TAB style snippet expansion, xyz,TAB will load a context-sensitive Sublime completion overlay where the appropriate snippet can be picked with the arrow keys and an additional TAB. For example, ng,TAB in an HTML tag will show all the available ng-* attributes. This is a little slower approach than the one typically taken with snippets, but decreases the learning curve over the API surface. Note that Sublime appears to have different conditions for showing the completion overlay. Simply typing ng in a CoffeeScript document will show the completion menu, while the TAB is required in HTML. (This is potentially related to other installed packages)

Installation

Automatic

Ensure that you have installed Sublime Package Control following these instructions

Open the Sublime command palette with Ctrl + Shift + P, type / select Package Control: Install Package, then from the package control list, type / select AngularJS (CoffeeScript)

Note that packages are auto-updating, so as new modifications are made they will automatically be installed.

Manual tweaking of Package Control

This is not recommended, but Package control can be pointed directly at this GitHub repository rather than using the registry. Add to Packages\User\Package Control.sublime-settings, under the appropriate keys in the JSON config file.

This file can be opened via the Sublime menu: Preferences -> Package Settings -> Package Control -> Settings -- User

{
  "installed_packages":
  [
    "AngularJS (CoffeeScript)"
  ],
  "package_name_map":
  {
    "Sublime-AngularJS-Coffee-Completions": "AngularJS (CoffeeScript)"
  },
  "repositories":
  [
    "https://github.com/EastPoint/Sublime-AngularJS-Coffee-Completions"
  ]
}

Keybindings

Snippet triggers in Sublime are effectively implicit key bindings, that are always mapped to a series of characters + TAB. Alternatively, they can be found on the Ctrl + Shift + P menu by typing Ang to filter the list.

It is further recommended that a keybinding similar to the following is added to provide single key combo access to all the currently available snippets.

[
  { "keys": ["shift+f3"], "command": "show_overlay",
    "args": {"overlay": "command_palette", "text": "Angular"} }
]

To allow for the $ character to trigger the menu automatically, the following must be added to the Preferences.sublime-settings file. This file can be opened by the Preferences -> Settings -- User menu item.

{
	"auto_complete_triggers":
	[
		{
			"characters": "$",
			"selector": "source.coffee, source.js, source.js.embedded.html"
		}
	]
}

If you want to disable duplicate $s from showing up in the editor when completing, then you must change the default word_separators to the following, in the same user Preferences.sublime-settings

{
	"word_separators": "./\\()\"'-:,.;<>~!@#%^&*|+=[]{}`~?"
}

NOTE: auto_complete_triggers and word_separators are siblings in the same JSON config object.

CoffeeScript

The bindings have been selected so that they don't interfere with the standard CoffeeScript bindings, namely =, -, cla, log, elif, el, if, ifel, #, forof, forx, fori, forin, req, swi, ter, try or unl

Sublime Bugs

Unfortunately there are a couple of issues with Sublime at the moment preventing it from doing a couple of things that would help us out:

  • Filtering within the completions overlay doesn't work right when the $ character is involved.

  • There is no way to add names to a completion like you can for a snippet, so the Ctrl + Space overlay only shows identifiers.

  • The tab trigger system in Sublime does not support regular expressions. So when dealing with a member function such as scope.$watch, there is no way to reduce the noise in the list when pressing .$. We see all completions starting with $ rather than only those starting with .$, lengthening the list.

  • There is no way that I can find to limit the scope of the ng-* attributes to only the HTML tags that they are valid for (i.e. ng-csp only belong on html) The best we can do here is to segregate snippets that are attributes vs tags.

  • Sublime doesn't have a convention for optional parameters / blocks in snippets. This is faked by highlighting the block on the first TAB so that it may be deleted, and allowing subsequent TABs to change specific values in the block if the block is kept. However, Sublime still honors the tabs inside the block after it's been deleted. In practical terms, this means it may require extra TAB presses to send the cursor to the next replacement point after an optional block has been deleted.

Please vote up issues 124225 and 124217 if you want to see these issues resolved!

Hopefully a future version of Sublime will address these issues, but for now there are some work-arounds.

The solution at the moment is to provide placeholder snippets for top-level services such as $filter.

A workflow for this would be the following:

`$`, `TAB`
  -> select Angular filter from the menu with `TAB`
  -> `$filter` is inserted into document
  -> `TAB` brings up completions against `$filter`
  -> select specific filter from the menu with `TAB` (such as currency)
  -> `$filter('currency') currency, 'symbol-eg-USD$'` is inserted into document
  and supports `TAB` completion (or chunk deletion)

Tab Triggers

The number of tab triggers is intentionally limited to increase discoverability. As a convention, most parameters have been stubbed with a value that indicates the value that should be replaced. Where functions take multiple possible parameters, the | has been used by convention - i.e. true|false

tl;dr Version

These are the only triggers used - $, .$, ng, for, is, mod, dir, fil, mock, $cookieStore, $filter., $http., $httpBackend., $injector., $interpolate., $location., $log., $provide., $q., $route., $routeProvider., .error, .expect, .other, .success and .when ... PHEW

Directive

All HTML based directives are keyed off the ng,TAB binding.

Directive Binding Context
form ng,TAB HTML Element
input ng,TAB HTML Element
input [checkbox] ng,TAB HTML Element
input [email] ng,TAB HTML Element
input [number] ng,TAB HTML Element
input [radio] ng,TAB HTML Element
input [text] ng,TAB HTML Element
input [url] ng,TAB HTML Element
input [email] ng,TAB HTML Element
ngApp ng,TAB HTML Attribute
ngBind ng,TAB HTML Attribute
ngBindHtml ng,TAB HTML Attribute
ngBindHtmlUnsafe ng,TAB HTML Attribute
ngBindTemplate ng,TAB HTML Attribute
ngChange ng,TAB HTML Attribute
ngChecked ng,TAB HTML Attribute
ngClass ng,TAB HTML Attribute
ngClassEven ng,TAB HTML Attribute
ngClassOdd ng,TAB HTML Attribute
ngClick ng,TAB HTML Attribute
ngCloak ng,TAB HTML Attribute
ngController ng,TAB HTML Attribute
ngCsp ng,TAB HTML Attribute
ngDblClick ng,TAB HTML Attribute
ngDisabled ng,TAB HTML Attribute
ngForm ng,TAB HTML Element
ngHide ng,TAB HTML Attribute
ngHref ng,TAB HTML Attribute
ngInclude ng,TAB HTML Attribute
ngInclude ng,TAB HTML Element
ngInit ng,TAB HTML Attribute
ngList ng,TAB HTML Attribute
ngModel ng,TAB HTML Attribute
ngMousedown ng,TAB HTML Attribute
ngMouseenter ng,TAB HTML Attribute
ngMouseleave ng,TAB HTML Attribute
ngMousemove ng,TAB HTML Attribute
ngMouseover ng,TAB HTML Attribute
ngMouseup ng,TAB HTML Attribute
ngMultiple ng,TAB HTML Attribute
ngNonBindable ng,TAB HTML Attribute
ngPluralize ng,TAB HTML Attribute
ngPluralize ng,TAB HTML Element
ngReadonly ng,TAB HTML Attribute
ngRepeat ng,TAB HTML Attribute
ngSelected ng,TAB HTML Attribute
ngShow ng,TAB HTML Attribute
ngSrc ng,TAB HTML Attribute
ngStyle ng,TAB HTML Attribute
ngSubmit ng,TAB HTML Attribute
ngSwitch ng,TAB HTML Attribute
ngSwitch ng,TAB HTML Element
ngSwitch-default ng,TAB HTML Attribute
ngSwitch-when ng,TAB HTML Attribute
ngTransclude ng,TAB HTML Attribute
ngView ng,TAB HTML Attribute
ngView ng,TAB HTML Element
script ng,TAB HTML Element
select ng,TAB HTML Element
textarea ng,TAB HTML Element

Module

Module Method Binding Context
config mod,TAB CoffeeScript
constant mod,TAB CoffeeScript
controller mod,TAB CoffeeScript
directive (to chain) dir,TAB CoffeeScript
directive (complete) dir,TAB CoffeeScript
factory mod,TAB CoffeeScript
filter mod,TAB CoffeeScript
provider mod,TAB CoffeeScript
run mod,TAB CoffeeScript
service mod,TAB CoffeeScript
value mod,TAB CoffeeScript

Scope

Scope Method Binding Context
$rootScope $,TAB CoffeeScript
$apply .$,TAB CoffeeScript
$broadcast .$,TAB CoffeeScript
$destroy .$,TAB CoffeeScript
$digest .$,TAB CoffeeScript
$emit .$,TAB CoffeeScript
$eval .$,TAB CoffeeScript
$evalAsync .$,TAB CoffeeScript
$id .$,TAB CoffeeScript
$new .$,TAB CoffeeScript
$on .$,TAB CoffeeScript
$watch .$,TAB CoffeeScript

Controller

Covers both FormController and NgModelController

Controller Method Binding Context
$render .$,TAB CoffeeScript
$setValidity .$,TAB CoffeeScript
$setViewValue .$,TAB CoffeeScript
$viewValue .$,TAB CoffeeScript
$modelValue .$,TAB CoffeeScript
$parsers .$,TAB CoffeeScript
$formatters .$,TAB CoffeeScript
$error .$,TAB CoffeeScript
$pristine .$,TAB CoffeeScript
$dirty .$,TAB CoffeeScript
$valid .$,TAB CoffeeScript
$invalid .$,TAB CoffeeScript

Resource

Resource Methods Binding Context
$resource $,TAB CoffeeScript
$delete .$,TAB CoffeeScript
$get .$,TAB CoffeeScript
$query .$,TAB CoffeeScript
$remove .$,TAB CoffeeScript
$save .$,TAB CoffeeScript

Filter

Filter Method Binding Context
$filter $,TAB CoffeeScript
currency $filter,TAB CoffeeScript
currency fil,TAB HTML
date $filter,TAB CoffeeScript
date fil,TAB HTML
filter $filter,TAB CoffeeScript
filter fil,TAB HTML
json $filter,TAB CoffeeScript
json fil,TAB HTML
limitTo $filter,TAB CoffeeScript
limitTo fil,TAB HTML
linky $filter,TAB CoffeeScript
linky fil,TAB HTML
lowercase $filter,TAB CoffeeScript
lowercase fil,TAB HTML
number $filter,TAB CoffeeScript
number fil,TAB HTML
orderBy $filter,TAB CoffeeScript
orderBy fil,TAB HTML
uppercase $filter,TAB CoffeeScript
uppercase fil,TAB HTML

Global API

Global API Binding Context
angular.bind ng,TAB CoffeeScript
angular.bootstrap ng,TAB CoffeeScript
angular.copy ng,TAB CoffeeScript
angular.element ng,TAB CoffeeScript
angular.equals ng,TAB CoffeeScript
angular.extend ng,TAB CoffeeScript
angular.foreach for,TAB CoffeeScript
angular.fromJson ng,TAB CoffeeScript
angular.identity ng,TAB CoffeeScript
angular.injector ng,TAB CoffeeScript
angular.isArray is,TAB CoffeeScript
angular.isDate is,TAB CoffeeScript
angular.isDefined is,TAB CoffeeScript
angular.isElement is,TAB CoffeeScript
angular.isFunction is,TAB CoffeeScript
angular.isNumber is,TAB CoffeeScript
angular.isObject is,TAB CoffeeScript
angular.isString is,TAB CoffeeScript
angular.isUndefined is,TAB CoffeeScript
angular.lowercase ng,TAB CoffeeScript
angular.module mod,TAB CoffeeScript
angular.noop ng,TAB CoffeeScript
angular.toJson ng,TAB CoffeeScript
angular.uppercase ng,TAB CoffeeScript
angular.version ng,TAB CoffeeScript

Http

Http Methods Binding Context
$http $,TAB CoffeeScript
$http (configured) $,TAB CoffeeScript
delete $http.,TAB CoffeeScript
get $http.,TAB CoffeeScript
head $http.,TAB CoffeeScript
jsonp $http.,TAB CoffeeScript
post $http.,TAB CoffeeScript
put $http.,TAB CoffeeScript
defaults $http.,TAB CoffeeScript
pendingRequests $http.,TAB CoffeeScript
.error .error,TAB CoffeeScript
.success .success,TAB CoffeeScript

HttpBackend

Note that .expect and .when are designed to chain, so we don't bind to $httpBackend

HttpBackend Methods Binding Context
$httpBackend $,TAB CoffeeScript
expect .expect,TAB CoffeeScript
expectDELETE .expect,TAB CoffeeScript
expectGET .expect,TAB CoffeeScript
expectHEAD .expect,TAB CoffeeScript
expectJSONP .expect,TAB CoffeeScript
expectPATCH .expect,TAB CoffeeScript
expectPOST .expect,TAB CoffeeScript
expectPUT .expect,TAB CoffeeScript
flush $httpBackend.,TAB CoffeeScript
resetExpectations $httpBackend.,TAB CoffeeScript
verifyNoOutstandingExceptions $httpBackend.,TAB CoffeeScript
verifyNoOutstandingRequests $httpBackend.,TAB CoffeeScript
when .when,TAB CoffeeScript
whenDELETE .when,TAB CoffeeScript
whenGET .when,TAB CoffeeScript
whenHEAD .when,TAB CoffeeScript
whenJSONP .when,TAB CoffeeScript
whenPATCH .when,TAB CoffeeScript
whenPOST .when,TAB CoffeeScript
whenPUT .when,TAB CoffeeScript

Q

Provider Method Binding Context
$q $,TAB CoffeeScript
all $q.,TAB CoffeeScript
defer $q.,TAB CoffeeScript
reject $q.,TAB CoffeeScript
when $q.,TAB CoffeeScript

Route

Route Method Binding Context
$route $,TAB CoffeeScript
current $route.,TAB CoffeeScript
reload $route.,TAB CoffeeScript
routes $route.,TAB CoffeeScript
$routeChangeError .$,TAB CoffeeScript
$routeChangeStart .$,TAB CoffeeScript
$routeChangeSuccess .$,TAB CoffeeScript
$routeUpdate .$,TAB CoffeeScript
$routeParams $,TAB CoffeeScript
$routeProvider $,TAB CoffeeScript
when $routeprovider.,TAB CoffeeScript
otherwise .other,TAB CoffeeScript

Cookie

Cookie Method Binding Context
$cookies $,TAB CoffeeScript
$cookieStore $,TAB CoffeeScript
get $cookiestore.,TAB CoffeeScript
put $cookiestore.,TAB CoffeeScript
remove $cookiestore.,TAB CoffeeScript

Location

Location Method Binding Context
$injector $,TAB CoffeeScript
absUrl $location.,TAB CoffeeScript
hash (get & set) $location.,TAB CoffeeScript
host $location.,TAB CoffeeScript
path (get & set) $location.,TAB CoffeeScript
port $location.,TAB CoffeeScript
protocol $location.,TAB CoffeeScript
replace $location.,TAB CoffeeScript
search (get & set) $location.,TAB CoffeeScript
url (get & set) $location.,TAB CoffeeScript

Log

Log Method Binding Context
$log $,TAB CoffeeScript
error $log.,TAB CoffeeScript
info $log.,TAB CoffeeScript
log $log.,TAB CoffeeScript
warn $log.,TAB CoffeeScript

Mock

Mock Method Binding Context
angular.mock.debug mock,TAB CoffeeScript
angular.mock.inject mock,TAB CoffeeScript
angular.mock.module mock,TAB CoffeeScript
angular.mock.TzDate mock,TAB CoffeeScript
$log.assertEmpty $log.,TAB CoffeeScript
$log.reset $log.,TAB CoffeeScript
$log.logs $log.,TAB CoffeeScript
$timeout.flush $timeout.,TAB CoffeeScript

Injector

Injector Method Binding Context
$injector $,TAB CoffeeScript
annotate $injector.,TAB CoffeeScript
get $injector.,TAB CoffeeScript
instantiate $injector.,TAB CoffeeScript
invoke $injector.,TAB CoffeeScript

Interpolate

Injector Method Binding Context
$interpolate $,TAB CoffeeScript
endSymbol $interpolate.,TAB CoffeeScript
startsymbol $interpolate.,TAB CoffeeScript

Provide

Provider Method Binding Context
$provide $,TAB CoffeeScript
constant $provide.,TAB CoffeeScript
decorator $provide.,TAB CoffeeScript
factory $provide.,TAB CoffeeScript
provider $provide.,TAB CoffeeScript
service $provide.,TAB CoffeeScript
value $provide.,TAB CoffeeScript

Other Services

Service Binding Context
$anchorScroll $,TAB CoffeeScript
$cacheFactory $,TAB CoffeeScript
$compile $,TAB CoffeeScript
$controller $,TAB CoffeeScript
$document $,TAB CoffeeScript
$exceptionHandler $,TAB CoffeeScript
$locale $,TAB CoffeeScript
$parse $,TAB CoffeeScript
$rootElement $,TAB CoffeeScript
$templateCache $,TAB CoffeeScript
$timeout $,TAB CoffeeScript
$window $,TAB CoffeeScript

Future Improvements

  • It would be nice to provide inline docs through SublimeCodeIntel in a vein similar to the ones provided for jQuery.

  • SublimeErl also provides some pretty fancy features which would be nice to integrate

Making Contributions

  • When editing .sublime-snippets files, always use real tab characters for indentation on a newline following a crlf/lf. Sublime will automatically insert spaces if your user settings specify spacing for indentation.

  • Write CoffeeScript that will pass coffeelint

Thanks

Original inspiration was from the AngularJS tmbundle, which was targeted at JavaScript and a small set of mnemonics for common operations. This is designed to be more comprehensive.