Espresso

CodeSense

Languages

CodeSense is the name of the auto-completion system in Espresso. CodeSense provides contextually-aware completions based on syntax zones.

There are two components for CodeSense: libraries and providers.

CodeSenseLibraries

XML files within your CodeSenseLibraries folder define the possible completions for your plug-in, grouped into sets. Libraries contain no logic regarding when various sets should be shown to the user, but they do offer the ability to specify text snippets for use with specific sets or strings (see snippet syntax for more info about text snippets).

XML files within CodeSenseLibraries can be named anything you wish, and use this basic syntax:

<?xml version="1.0" encoding="UTF-8"?>
<codesense version="1.0">

	<set name="com.yourdomain.yoursugar.fruits">
		<completion string="apple" />
		<completion string="pear" />
		<completion string="orange" />
	</set>

	<set name="com.yourdomain.yoursugar.vegetables">
		<completion string="carrot" />
		<completion string="cabbage" />
		<completion string="potato" />
	</set>

</codesense>

Each <set> has a name (which will be used in the CodeSenseProviders to determine when to use these completions), and each <completion> element has a string attribute that will be suggested to the user.

To use text snippets, you must add an additional <behavior> element. Behaviors cascade, so if you place a behavior in the root <codesense> element it will be used by default for all sets. If you have a behavior within a <set>, it will be used instead of the default behavior (if there is one) for all completion strings in the set. And if you nest a behavior within a <completion> it will be used only for that string instead of any behavior in the set or root element.

There are two types of behaviors, but they use very similar syntax:

<behavior>
	<!-- append-static inserts the snippet immediately after the completion string -->
	<append-static>snippet here</append-static>
</behavior>

<behavior>
	<!-- append-dynamic allows dynamically generated snippets -->
	<append-dynamic>
		<!-- matched-suffix captures information about following text via regex -->
		<matched-suffix>(\s*\{)|\s*[^{]</matched-suffix>

		<!-- transform-into generates a snippet based on the groups in matched-suffix -->
		<transform-into>(?1:: {$0})</transform-into>
	</append-dynamic>

	<!-- confirm is optional and can be used for any type of behavior -->
	<confirm characters="{"/>

	<!-- partial-confirm is optional and can be used for any type of behavior -->
	<partial-confirm characters="_"/>
</behavior>

Static appends are straight-forward: if the user chooses the related CodeSense completion string, the snippet in <append-static> is inserted after the string no matter what.

Dynamic appends are more complicated. The <matched-suffix> element is a standard regular expression that can optionally contain capture groups, and it is tested on the characters immediately after the string the user is inserting via CodeSense. Like all regex in plug-ins, <matched-suffix> is constrained to a single line.

The <transform-into> element typically uses regex conditional logic based on the capture groups in the <matched-suffix> element. This uses this syntax:

(?x:a:b)

Which means: if capture group number "x" has contents, insert "a". Otherwise, insert "b".

So in the example, if the string is followed by zero or more spaces and a curly brace, there will be something in capture group 1. Then <transform-into> specifies that if something is inside capture group 1, nothing is inserted (the location of "a" is blank). If the string is not followed by optional whitespace and a curly brace, though, a snippet with matched curly braces is inserted with a tab stop in between them (see snippet syntax for in-depth info about tab stops and other snippet features).

This system can be tricky to get your head around. For real-world examples, make sure to open up the plug-ins bundled with Espresso and see how they are using dynamic appends to insert different snippets based on context.

CodeSenseProviders

The XML files in your CodeSenseProviders folder define the rules that tell Espresso when to display the sets you defined in your CodeSenseLibraries and use the following syntax:

<?xml version="1.0" encoding="UTF-8"?>
<codesense>
	<provider>
		<!-- The selector when this provider is active -->
		<selector>js, js *:not(string,string *,comment)</selector>

		<!-- complete-match determines what characters are required for this provider -->
		<complete-match>\b[a-zA-Z]*</complete-match>

		<!-- completions use the set names you defined in your libraries -->
		<completions>com.macrabbit.js.DOM-support.variables</completions>
		<completions>com.macrabbit.js.functions</completions>
	</provider>

	<!-- Many providers can be defined in the same XML file -->
	<provider>
		<!-- (selector, completions, etc. here) -->
	</provider>
</codesense>

In the example, zero or more letters are required and the syntax context must be JavaScript source code (but not a string or comment). If both of these requirements are met, the completions in the two sets listed will be displayed to the user.

You can also use the :capture() pseudo-element in selectors to attach sets to a provider dynamically. For instance, in the built-in CSS.espressoplugin this is how attribute values are defined in the CodeSenseProviders:

<provider>
	<selector>css > property-name:capture(name) + property-value punctuation.separator</selector>

	<!-- The 'name' variable is the textual contents of the captured zone -->
	<!-- So in this case, it will be things like "display", "float", etc. -->
	<completions>com.macrabbit.css.property.${name}</completions>

	<complete-match>[a-zA-Z0-9-]*</complete-match>

	<!-- (Optional) require-suffix defines a regex text *after* completion has to match -->
	<require-suffix>[^:]|</require-suffix>
</provider>

A named capture is being used to map one provider onto an arbitrary number of set names. For instance, the CSS CodeSenseLibraries will contain sets named "com.macrabbit.css.property.display", "com.macrabbit.css.property.float", etc. to define completion strings for different CSS properties.

The <require-suffix> element is an optional element that restricts CodeSense depending on whether or not the characters to the right of the cursor match the provided regex. In this instance, the rule will only be shown if the character to the cursor's right is not a colon (:).