MacRabbit

View CodeSense

CodeSense is a contextually-aware system for providing auto-completion for your Sugar. Adding CodeSense to your Sugar is easy provided that you have a well-written syntax with descriptive, granular syntax zones.

There are two components for CodeSense: libraries and providers.

CodeSense Libraries

XML files within your CodeSenseLibraries folder define the possible completions for your Sugar, grouped into sets. The library contains no logic about when various sets should be shown for auto-completion, but it does offer the ability to specify text snippets for use with specific sets of strings.

A typical CodeSenseLibraries XML file looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<codesense version="1.0">
    <!-- 
The behavior defined in the root codesense element serves is used for all sets,
         
unless the set defines its own behavior -->
    <
behavior>
        <!-- 
The append-static element defines a snippet that gets appended after the
             completion string
. -->
        <
append-static> is a fruit</append-static>
    </
behavior>
    
    <
set name="com.yourdomain.yoursugar.fruits">
        <
completion string="apple" />
        <
completion string="pear" />
        <
completion string="orange" />
    </
set>
    
    <
set name="com.yourdomain.yoursugar.vegetables">
        <!-- 
A behavior defined in a set applies only to that set. -->
        <
behavior>
            <
append-static> is a vegetable</append-static>
        </
behavior>
        
        <
completion string="carrot" />
        <
completion string="cabbage" />
        <
completion string="potato" />
    </
set>
</
codesense

Sets and their completions should be straight-forward; the set has a name (which will be used in the CodeSense Provider to determine when to use these completions), and each completion has a string attribute that is the completion. By convention, the name for each set should be in reverse domain format.

Behaviors are used to append text snippets after the completion. In the simple example above, when the user uses CodeSense to insert “apple” what will actually be inserted is “apple is a fruit”. Behaviors are powerful because they allow you to conditionally insert things based on what comes after the completion, and also use text snippets to create tab stops.

The above example showcases how to use a static snippet to be appended to the completion string, but for more advanced behaviors, you can use additional settings and a dynamic snippet:

<behavior>
    <!-- 
The <append-dynamicelement allows you to dynamically generate a snippet 
         to append after the actual completion
. -->
    <
append-dynamic>
        <!-- 
The matched suffix allows you to capture information about the text 
             after the completion
Specify a regular expression here. -->
        <
matched-suffix>(\s+)(is a vegetable)?</matched-suffix>
        
        <!-- 
The transform-into element is a regex replacement expression, and can use 
        
the captured strings from matched-suffix to generate a conditional snippet. -->
        <
transform-into>(?2:\1is a vegetable:\1is fantastic and )</transform-into>
    </
append-dynamic>
    
    <!-- (
optionalThe confirm-characters element specifies additional characters that 
         trigger the confirmation of a completion
. -->
    <
confirm characters=" " />
    
    <!-- (
optionalThe confirm-partial element specifies characters that are contained 
         within the completion
When they're pressed, the completion will confirm up to 
         that character (including the character itself) and continue completing. This 
         is useful for things like CSS properties or PHP functions. -->
    <confirm-partial characters="-" />
</behavior> 

The example above may be confusing at first glance. The matched-suffix should be comprehensible (it’s a simple regex that with two capture groups that grabs whitespace immediately after the completion and then looks for the optional string “is a vegetable”). However, the transform-into expression uses a conditional that outputs different contents based on whether the second capture is empty or not. The syntax for regex conditionals in transform-into elements is:

(?x:a:b

Which means: if the numbered capture group “x” has contents, insert “a”. Otherwise, insert “b”.

In the example, if the text “is a vegetable” does not fall after the name of the vegetable, then it will be inserted. If it does already exist, then “is fantastic and” will be inserted leaving the full completion reading something like “carrot is fantastic and is a vegetable”.

A real-life example is the CSS.sugar, which when auto-completing property names looks for a colon after the property, and if it isn’t there inserts it.

CodeSense Providers

Providers map contexts in a document to possible completions (reference based on the name of the sets you defined in CodeSenseLibraries). Providers should be defined in the CodeSense Providers subfolder of a sugar, as an XML file with the following structure:

<?xml version="1.0" encoding="UTF-8"?>
<codesense>
    <
provider>
        <!-- 
The selector element specifies in what document contexts the provider 
             is active
The :capture pseudo-class will save the contents of the zone in a
            variable
. For the example belowthe property-name is saved in the "name"
            
variable. -->
        <
selector>css property-name:capture(name) + property-value</selector>
        
        <!-- 
The completions element points to the CodeSense Library key that has to 
             be used
It can contain placeholders for captured pieces of text from 
             the selector
. -->
        <
completions>com.macrabbit.css.property.${name}</completions>
        
        <!-- 
The complete-match element defines a regular expression for the piece of 
             text that is completed by this provider
It is not necessary to append 
             an end
-of-line symbolthis happens automaticallyNote that even if this 
             provider is active in the current context
, and the expression is matched
             
it may still be ignored for a completion if other providers match a 
             different piece of the same text
. -->
        <
complete-match>[a-zA-Z0-9-]*</complete-match>
        
        <!-- (
OptionalThe require-suffix element defines a regular expression which 
             the text 
*afterthe completion has to matchIt is not necessary to 
             prepend a begin
-of-line symbolthis happens automatically. -->
        <require-
suffix>[^:]|</require-suffix>
    
    </
provider>

    <!-- 
Many providers can be defined in the same XML -->
    <
provider>
        ...
    </
provider>
</
codesense

The example above should be largely self-explanatory thanks to its comments, but one thing to keep in mind is that thanks to the :capture(VARIABLE) pseudo-class you do not have to directly refer to all sets in your CodeSenseLibraries directly. In the example above, both of these sets will be used depending on which CSS property the user is currently editing:

<set name="com.maccrabbit.css.property.display"> ... </set>
<
set name="com.macrabbit.css.property.float"> ... </set