MacRabbit

CodeSense

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 Sugar, 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 SnippetSyntax 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 Sugars, <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 SnippetSyntax for in-depth info about snippet syntax).

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

The <confirm> and <partial-confirm> elements allow you to specify characters that will complete or advance the CodeSense completion in addition to tab and enter. For instance, in the built-in CSS.sugar hyphens (-) are used as a partial-comfirm character so that you can type something like “bo-t” and end up with “border-top”. Then the colon (:) is used to confirm CodeSense entries, so you could type “bo-t:” to insert “border-top”. In general, you should use confirm and partial-confirm characters sparingly. They work well for speeding up CodeSense in languages like CSS where the user is not defining their own properties, but for other languages where the user might have a custom variable or function that starts the same as a built-in CodeSense completion string, confirm characters can be maddening.

CodeSenseProviders

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

<?xml version="1.0" encoding="UTF-8"?>
<codesense>
    <
provider>
        <!-- 
The selector when this provider is active -->
        <
selector>jsjs *: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>
        <!-- (
selectorcompletionsetchere) -->
    </
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 CSS.sugar 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 *aftercompletion 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 (:).