Extending Lepiter with custom snippets and annotations

Lepiter is an extensible platform that is meant to be extended and customized for different domains. Here is how.

Case Study: Word Explanation

To start with, we pick a concrete example: a word explanation as provided by dictionaryapi.dev. We show two kinds of extensions: an annotation embedded in text, and a dedicated snippet.

Word Explanation Annotation

This is a word embedded in text:

refactoring

verb

To rewrite existing source code in order to improve its readability, reusability or structure without affecting its meaning or behaviour.

The code works, but I must refactor it before it is production quality.

(writing) To rewrite existing text in order to improve its readability, reusability or structure without intentionally affecting its meaning. Similar to, but sometimes involving more extensive restructuring than, copy editing.

Two significant activities which to contribute to community projects, such as Wikipedia, are to refactor complicated articles into simpler ones, and to refactor duplicated content into reusable templates.

noun

An act or process in which code is refactored.

.

Word Explanation Snippet

And this is a dedicated snippet:

refactoring

verb

To rewrite existing source code in order to improve its readability, reusability or structure without affecting its meaning or behaviour.

The code works, but I must refactor it before it is production quality.

(writing) To rewrite existing text in order to improve its readability, reusability or structure without intentionally affecting its meaning. Similar to, but sometimes involving more extensive restructuring than, copy editing.

Two significant activities which to contribute to community projects, such as Wikipedia, are to refactor complicated articles into simpler ones, and to refactor duplicated content into reusable templates.

noun

An act or process in which code is refactored.

How It Works

Annotation

To create a new annotation, first you need to create a GLR parser that has been annotated to create an abstract syntax tree. It should parse whatever the user can enter between the colon (:) after the annotation name and the ending right braces. For the gtWordExplanation annotation, the parser is defined in LeWordAnnotationParser SmaCCGLRParser subclass: #LeWordAnnotationParser instanceVariableNames: '' classVariableNames: '' package: 'Lepiter-Snippet-Words-Annotations' and the AST is defined by LeWordParseNode SmaCCParseNode subclass: #LeWordParseNode instanceVariableNames: '' classVariableNames: '' package: 'Lepiter-Snippet-Words-Annotations' and its subclasses.

Once the parser has been created, you need to register it with LeParser SmaCCGLRParser subclass: #LeParser instanceVariableNames: '' classVariableNames: 'AnnotationParsers' package: 'Lepiter-Parser' . In our example, LeWordAnnotationParser SmaCCGLRParser subclass: #LeWordAnnotationParser instanceVariableNames: '' classVariableNames: '' package: 'Lepiter-Snippet-Words-Annotations' is registered in the LeWordAnnotationParser>>#initialize initialize LeParser annotationParsers at: 'gtWordExplanation' put: [ :parser | parser spawnParser: self startingAt: self startingStateForWordAnnotation ] method which is executed automatically when the class is loaded.

LeWordAnnotationParser>>#obsolete obsolete LeParser annotationParsers removeKey: 'gtWordExplanation' ifAbsent: [ ]. ^ super obsolete is used to deregister the parser if it is unloaded.

After the parser has been created and registered, you need to define a LeComponentStyler Object subclass: #LeComponentStyler uses: TSmaCCComponentVisitor instanceVariableNames: '' classVariableNames: '' package: 'Lepiter-UI-Snippet-Text styler' subclass that inserts the attributes into the text. For the explanation annotation, the LeWordAnnotationStyler LeComponentStyler subclass: #LeWordAnnotationStyler uses: TLeWordParseNodeVisitor - {#acceptNode:} instanceVariableNames: '' classVariableNames: '' package: 'Lepiter-Snippet-Words-Annotations' was created using the visitor trait, TLeWordParseNodeVisitor Trait named: #TLeWordParseNodeVisitor uses: TSmaCCParseNodeVisitor instanceVariableNames: '' package: 'Lepiter-Snippet-Words-Annotations' . Like the parser, the styler needs to be registered, LeWordAnnotationStyler>>#initialize initialize LeSnippetStylerVisitor additionalStylers add: self and deregistered when removed, LeWordAnnotationStyler>>#obsolete obsolete LeSnippetStylerVisitor additionalStylers remove: self ifAbsent: [ ]. ^ super obsolete .

Finally, completion can be added to the Lepiter editor for the annotation by defining a visitor that is used when completion is activated. For our example, LeWordAnnotationCompletionVisitor Object subclass: #LeWordAnnotationCompletionVisitor uses: TSmaCCComponentVisitor + (TLeWordParseNodeVisitor - {#acceptNode:}) instanceVariableNames: '' classVariableNames: '' package: 'Lepiter-Snippet-Words-Annotations' was created to complete the expanded and height tags. The completion visitor needs to be registered, LeWordAnnotationCompletionVisitor>>#initialize initialize LeContentCompletionVisitor additionalCompletionVisitors add: self and deregestered, LeWordAnnotationCompletionVisitor>>#obsolete obsolete LeContentCompletionVisitor additionalCompletionVisitors remove: self ifAbsent: [ ]. ^ super obsolete .

Snippet

The snippet is defined in LeWordSnippet LeSnippet subclass: #LeWordSnippet instanceVariableNames: 'word explanation explanationAttachmentName' classVariableNames: '' package: 'Lepiter-Snippet-Words-Snippet' .