This plugin is loosely inspired by the template support in TextMate and YASnippet. It is not intended to be a straight port and will utilize the power of clojurescript when more advanced features are added. For simple yasnippets it might be possible to write a simple conversion util without to much hassle.
The plugin can be installed using the LightTable plugin manager (or clone this repo to your plugins folder, make sure you call the folder Snippets !). You probably will need to reload behaviors for the plugin to work.
(If you installed by cloning, you'll need to touch one of the cljs files from within lighttable to make sure you get compiled version of the latest source)
NOTE
If you have a version prior to 0.1.0, you will need to uninstall that before using this plugin.
Pull requests are welcome. However pls don't include the compiled files (snippets_compiled.js and snippets_compiled.js.map) in the pr.
You just open the command pane (ctrl-space) and select the Snippets: Select snippet command. A list of defined snippets will be presented. Each item presented with name, key and keyboard shortcut (if defined, see below).
Its quite easy setting up shortcuts, just edit your user.keymap file.
[:editor.javascript "ctrl-t ctrl-c" (:snippet.by-key "tc")]
[:editor.javascript "ctrl-a ctrl-e" (:snippet.by-key "ae")]
Type the key in your editor and then select the command Snippets: Expand by editor token.
Snippets that are applicable for the current editor will be shown in the autocomplete/hints resultlist. When you select an snippet item, it will invoke the snippet.
Caveats:
Should you find in annoying, just disable the behavior:
;; in your user.behaviors file
;; note the colon-minus `:-` prefix which removes behaviors
[:editor :-lt.plugins.snippets/use-local-hints]
When more than one snippet matches a given key (for a given editor type), a select list will popup inline prompting you to select one of the alternatives.
Default location for snippets are set to $lthome/User/snippets
. Currently reads any .edn file residing in that directory. It will also walk any subdirectories.
There is no limits to how many files you can have. You can put all snippets in one file, or split them into several.
Just modify your user.behaviors to include:
[:snippets.loader :lt.plugins.snippets.loader/set-snippet-dir
"/Users/mrundberget/.lighttable/snippets"]
;; absolute path to where you'd like your snippet root directory to be
If the directory does not exist it will be created.
NOTE: Will most likely change a bit in upcoming versions
{
:modes {:+ #{:editor.javascript}}
:helper "clojure-helper.js" ; optional
:snippets [
{:name "Buster TestCase"
:key "tc"
:snippet-file "testcase.snip"
:modes {:+ #{:editor.typescript}} ; optional (probably rarely used)
{:name "Buster Assert Equals"
:key "ae"
:no-indent true ; optional
:snippet "assert.equals($1, $2);$0"}]}
($path of edn file$/$snippet.file$)
isn't found the snippet will just return the path.Snippet modes uses a syntax similar to Light Table behaviors. The modes specified will be matched against the defined tags for the editor in which you are about to expand a snippet.
{ :modes {:+ #{:editor.foo} :- #{:editor.bar}} }
;; Means the snippet will resolve if:
;; * the editor has a tag :editor.foo
;; * but not if the editor also has a tag :editor.bar
Modes can be specified at "group" level, meaning the :modes property in the top level map of your snippet definition file. This will the apply to all snippets in the definition file, unless you specify :modes at the snippet level.
{:+ #{:editor.foo :editor.bar}
:- #{:editor.transient :editor.plugin }} ;; modes at "group" level
{:+ #{:editor.baz :editor.transient}
:- #{:editor.foo :editor.foobar}} ;; modes at "snippet" level
;; will resolve to the following modes for the snippet:
{:+ #{:editor.bar :editor.baz :editor.transient}
:- #{:editor.plugin :editor.foo : editor.foobar}}
Linebreaks/indentations is currently important in your snippet templates for nice display in snippet completion mode. Another reason to have templates beyond one liners in a separate file.
You can have placeholder values for your tabstops. The syntax is ${1:placeholder}
When prompted to complete a snippet the placeholder value will be highlighted (if you wish to keep it just tab to next tabstop).
Warning: Scripts are being executed using JavaScript eval. Use at your own risk !
/** Created: ${__new Date()__} **/
When the snippet is expanded any blocks with ${something} will be replaced with what the expression resolves to (using javascript eval). In the example above the current date will be shown. You should note that eval only return the result of the last statement. To keep your snippets understandable, you're better off creating helper functions that you bundle in a helper script with your snippet definitions
package ${1:__snip$.groovy.suggestPackage()__}
/** Name: $2 **/
class ${2:__snip$.currFileNameSansExt()__} {
${__snip$.wrapSelection()__}$0
}
Tabstops with code take precedence of tabstops with just numbering. The code within tabstops are resolved and shown as a placeholde value when the snippet is displayed for completion. Mirrored tabstops work as normal. In the example the snippet for completion with show the filename (if editor has been saved previously!) as classname, and its value mirrored in the comment section.
Hello ${1:Dill} results in: ${1:__snip$.groovy.toUpper__}
When this snippet is displayed for user input, it will display Hello dill results in: DILL
Changing the text of the tabstop ${1:Dill} will invoke the transformation funtion of the mirror.
Transformation functions should accept one parameter, the value of the "master" tabstop at any given time. The function should most likely also return av value.
For the simple example above you could actually do this
Hello ${1:Dill} results in: ${1:__(function (txt) { return txt.toUpperCase(); })__}
(function(window) {
function suggestPackage() {
var p = snip$.currPath();
if(p) {
var parts = snip$.path.dirname(p).split(/src\/main\/groovy\/|src\/test\/groovy\//);
if(parts.length === 2) {
return parts[1].replace(/\//g, ".");
}
}
}
function toUpper(txt) {
return txt ? txt.toUpperCase() : txt;
}
snip$.groovy = {
suggestPackage: suggestPackage,
toUpper: toUpper
};
})(window);
There are few limits to what you can put in your helper files. You may use require, call javascript compiled from ClojureScript from LightTable. You may wish to call CodeMirror directly for some things etc. Calling the javascript outputted from the clojure compiler might not always be for the faint hearted though.
It the sample above, the custom object has been added to the already globally available snip$ object.
The following functions are included by default exposed through snip$ (on the window object)
currPath: currPath, currFileName: currFileName, currFileNameSansExt: currFileNameSansExt, path: path, wrapSelection: wrapSelection, wrapSelectionEager: wrapSelectionEager
Function/Property | Description |
---|---|
currFileName | Name of file for current active editor. Returns null editor is transient |
currFileNameSansExt | Same as above but without file extension |
path | Node path object. Handy for working with paths |
wrapSelection | Useful in templates when you wish your snippet to wrap-around an expression/selection you have made prior to expanding a snippet. If you esc from a snippet completion, the selection is not restored, but its in your clipboard so it's not lost ! |
wrapSelectionEager | Same as above, but will select current line if no selection has been made prior to expanding the snippet |
0.1.1 Fix to work with LT 0.8-alpha
0.1.0 Breaking changes!
$lthome/User/snippets
. If dir not exists, its created.0.0.8 Refactored autocomplete impl with after some help from Chris
0.0.7 Incubating feature: Show applicable snippets in autocomplete results
0.0.6 Small bugfixes
0.0.5 Allow inline scripts and placeholders with code to have braces
0.0.4 Scripting inside your snippets for tabstops, mirrors and arbitrary script code. A few bugs were squashed along the way
0.0.3 Fix for behaviors not loading
0.0.2 Support placeholders, escape html, prefer $lthome/settings/snippets to $lthome/snippets and a few other minor fixes. Some breaking changes. Pls see Release notes
0.0.1 Initial release with fairly usable features
MIT license, same as Light Table. See LICENSE.md for the full text.