skip to Main Content

How I got tired of writing boilerplate in Compose and wrote my own Emmet-like plugin for Android…

January 24, 202611 minute read

  

How I got tired of writing boilerplate in Compose and wrote my own Emmet-like plugin for Android Studio

Jetpack Compose 1.2 is now stable!

The “Nesting Hell” Problem

Jetpack Compose is actually a breath of fresh air after XML. It is declarative, modern, and powerful. But it has one characteristic that starts to get tiring over time: verbosity.

To lay out even the simplest list element — an image on the left and two lines of text on the right — you have to write a whole bunch of boilerplate code:

  • Column
  • Row
  • Box
  • Endless Modifier.padding(…)
  • Modifier.fillMaxWidth(…)

And the most frightening thing is the finale of any complex file: a “Christmas tree” of curly brackets in which it is easy to get lost.

I caught myself thinking: why was this problem solved in web development 10 years ago using Emmet (when you write ul>li*5 and press Tab), but in the most modern UI framework for Android, do we still write every component manually?

Solution: YARC

I got tired of wasting time typing brackets, so I created YARC (Yet Another Rapid Compose).

This is an Android Studio plugin that works as a code generator. You give it a short abbreviation, and it instantly expands it into a finished component tree.

TL;DR;

Instead of writing 15 lines of code by hand, you write one: col/row/txt*3

And you get:

https://medium.com/media/cf83e5062d239d7b677faeb64d276699/href

This project is inspired by the philosophy and syntax of the wonderful Emmet tool. Huge thanks to the authors of the original for an idea that saves developers thousands of hours. YARC adapts this concept to the specifics of Jetpack Compose.

Intellij Marketplace

Why didn’t standard tools cope?

It would seem that we are living in 2025: IDEs have become smart, and AI writes code for us. Why create another plugin? I honestly tried to use what is already available, but everything hit a limit.

1. Live Templates

Android Studio has a built-in template system. We can type comp, press Enter, and get a @Composable fun boilerplate. But Live Templates are static macros:

  • They don’t do math: You cannot type col*5 and get five columns.
  • They don’t understand dynamic nesting: Creating a template for a Scaffold with a Column inside, and a Button inside that, is difficult and inflexible.

It is a “copy-paste” tool, but we needed a “generator.”

2. AI (Copilot, Gemini, and others): Too smart for simple work

AI is great for generating algorithms or writing tests. But for rapid layout, it often becomes a “bottleneck”:

  • Speed: While you wait for a response from the server (even 1–2 seconds), you lose your rhythm.
  • Convenience: Describing a UI structure in human language takes a long time. “Make me a row with an image on the left and a column of two texts on the right…” — by the time you write this, you would have already coded it by hand.
  • Hallucinations: AI might use the wrong imports, outdated APIs, or invent parameters that don’t exist.

Environment Setup (Technical Deep Dive)

Before diving into the code, let’s talk about the setup. Or rather — about the pain. Creating an IntelliJ plugin through the eyes of an Android developer is a journey into a parallel universe. Everything seems familiar, but the differences are enough to make you constantly trip over flat ground.

I am, first and foremost, an Android developer. This plugin was a weekend project born out of personal need. The solutions I describe below are the result of pragmatic coding. The goal was to get a working, useful tool, not to follow strict corporate plugin development guidelines.

If you are an experienced plugin author, you might smile. If you are an Android developer like me — you will probably nod in understanding.

The 2025 Trap: Kotlin K2 Compiler

Full of enthusiasm, I took the official intellij-platform-plugin-template. It’s a good starting point, but it likes to live on the “bleeding edge.” By default, the template configured my project to use the EAP (Early Access Program) version of the IntelliJ platform — something like 2025.1.

I built the starter plugin, launched the IDE “sandbox,” and… crash. The plugin didn’t even load, throwing obscure errors related to the Kotlin plugin.

After a brief investigation, it became clear: the 2025.1 platform runs by default on the new Kotlin K2 compiler mode, and there was a subtle incompatibility in how my project was being built. The “fix,” at least for the sake of my mental health, was not to debug the K2 compiler, but to retreat to solid ground. I rolled back the platform version.

Lesson learned: If you don’t plan on testing new platform features, stick to the latest stable release. This ensures that the plugin is compiled with the same JDK version that the IDE uses, eliminating those horrible class version errors and finally allowing you to move on to writing features.

Under the Hood of YARC: Architecture and Syntax

Essentially, YARC is a mini-compiler. It takes a Domain-Specific Language (your abbreviation) and transpiles it into Kotlin. The architecture is as simple and predictable as possible:

Input Capture -> Parsing -> Tree Construction -> Code Generation.

Syntax Cheat Sheet

  • / — Child. Example: box/txt. Result: Text insideBox.
  • , — Sibling. Example: img,txt. Result: Elements next to each other on the same level.
  • * — Multiplication. Example: txt*3. Result: Three text components in a row.
  • ;— Step Up. Example: col/row/txt;btn. Result: Button next toRow.

Standard Templates (Dictionary)

  • comp — @Composable fun MyComponent() { %CONTENT% }
  • scfld — Scaffold(modifier = Modifier) { /* innerPadding -> */ %CONTENT% }
  • col / clmn — Column(modifier = Modifier) { %CONTENT% }
  • row — Row(modifier = Modifier) { %CONTENT% }
  • box — Box(modifier = Modifier) { %CONTENT% }
  • txt — Text(%ARGUMENT%)
  • img — Image(painter = , contentDescription = )
  • btn — Button(onClick = {}) { Text(“Click”) }
  • spcr — Spacer(modifier = Modifier)

Data Structure (AST)

The heart of the plugin is a classic Abstract Syntax Tree (AST). All the nesting magic relies on a single, simple recursive data class:

https://medium.com/media/8dee71087b13ca0cc3fabffcff03323e/href

When you write row/txt*2, the parser converts it into an object where row has a list of children containing two txt nodes.

Syntax and Parser

The parser uses a single-pass, top-down approach. It breaks the text into tokens and builds the tree using special operators. Here are the main tools in your arsenal:

  • Nesting (/): Creates a “parent-child” relationship. box/txt → Box { Text(…) }. Why /? It is often easier to type than > as it doesn’t require the Shift key.
  • Siblings (,): Adds elements to the same nesting level. img, txt → Image(…) and Text(…) will be placed side by side.
  • Multiplication (*): The most pleasant part. Instead of copy-pasting, you simply specify the quantity. txt*3 generates three text fields.
  • Step Up (;) — Killer Feature: This makes YARC unique. The ; operator allows you to “exit” the current container and move one level up. This solves the problem of finishing a nested block to continue in the parent.
    Example: col/row/txt; btn
    Result: btn is created after the Row, not inside it. Without this, you’d need manual bracket management, which kills speed.
  • Custom Components: If the parser sees a tag not in the dictionary (e.g., UserProfile), it treats it as a custom Composable function. This makes the plugin compatible with your design system out of the box.

UX: Why Action, not Auto-Complete?

This was the most difficult architectural decision. Intuitively, it seems that such a plugin should work through the standard auto-completion menu (Ctrl+Space), like Live Templates. But here we run into a fundamental limitation of the IDE.

The Tokenization Problem

The IntelliJ Platform perceives characters like /, >, and + as word delimiters. When you write row/col, the auto-completion mechanism sees it as two different words: row and col. Registering this entire expression as a single key for auto-completion is practically impossible without “dirty hacks.”

The Solution: Backtracking Action

That’s why I took a different path: Manual Backtracking. We implemented this as an AnAction triggered by a separate hotkey (Cmd+Opt+E on Mac or Ctrl+Alt+E on Windows).

The logic works as follows:

  1. The plugin gains access to the editor and the cursor position.
  2. We start “reading” the text backward from the cursor, character by character.
  3. Reading stops when we encounter a space or the beginning of a line.

Here is the pseudocode for this mechanism:

https://medium.com/media/b831311bc535899fc4b9b74155b01dfc/href

This simple trick gives us full control over the input. We are not dependent on the whims of the IDE and receive a “raw” string that we can parse however we want. This ensures reliability: you know exactly what will be expanded.

Result (Demo)

It is better to see it once than to read about ASTs a hundred times. Here is that same “moment of magic” when a set of characters turns into an expanded UI structure.

Let’s break down a real-life case. Suppose we need to lay out a Column that contains:

  1. A Row with text.
  2. Right below it (as a sibling, not a nested element) — another Column with an image.

Input: The Power of Step Up

Here we use the full power of the ; (Step Up) operator:

col/row/txt;col/img

  • col/row/txt — Creates an outer Column, a Row inside it, and a Text inside that.
  • ; — The key moment! This symbol tells the plugin: “Finish filling the Row and go back one level up (to the outer Column).”
  • col/img — Creates a new Column with an image as a sibling to the Row.

Press Cmd + Opt + E, and you get:

https://medium.com/media/df96e827c47bf143a0c5792a74e60990/href

Output

The plugin generates a correct component tree, automatically adding a Modifier and the necessary parameters for Image and Text.

As you can see, we described a non-trivial structure (a Column inside a Column) without a single extra cursor movement or manual closing of brackets.

How to try it?

The plugin has already passed moderation and is available in the official JetBrains repository.

Method 1: Marketplace (Recommended)

The easiest way is to install it directly from the IDE:

  1. Open Android StudioSettings (or Preferences on macOS).
  2. Go to the PluginsMarketplace section.
  3. Type “YARC” in the search bar.
  4. Click Install.

You can also download it via the web interface: YARC Plugin Page on JetBrains Marketplace

Method 2: Manual

If you want to install a specific version or do not have access to the marketplace, you can install the plugin from a ZIP archive:

  1. Download the latest yarc-x.x.x.zip from the Releases page on GitHub.
  2. In the Plugins settings, click on the gear icon (⚙️) in the upper right corner.
  3. Select “Install Plugin from Disk…”.
  4. Specify the path to the downloaded archive.

Conclusions

The main takeaway from this story is: don’t be afraid to create your own tools. Writing plugins for Android Studio might seem like something complicated (“it’s a platform, it’s not Android”), but in reality — it’s the same Kotlin that we love.

If you are fed up with some routine — automate it. This not only saves time but also levels you up as an engineer.

Open Source & Community

YARC is a fully Open Source project. I will be very grateful for your stars ⭐ on GitHub — it’s the best motivation to develop the project further.

If you have ideas for new abbreviations, found a bug, or want to add support for new components — Pull Requests and Issues are welcome!

GitHub Repository

Intellij Marketplace

Acknowledgements

A special kudos to my colleague Bogdan Moroz for the cool icon for the plugin.

Contact

If you have questions about plugin development or just want to discuss Jetpack Compose — add me on LinkedIn.

LinkedIn

Х

P.S. And one last thing: if you notice perfect long dashes in the text, it’s not an artificial intelligence hallucination, but just the magic of Medium’s auto-formatting 😂.


How I got tired of writing boilerplate in Compose and wrote my own Emmet-like plugin for Android… was originally published in ProAndroidDev on Medium, where people are continuing the conversation by highlighting and responding to this story.

 

Web Developer, Web Design, Web Builder, Project Manager, Business Analyst, .Net Developer

No Comments

This Post Has 0 Comments

Leave a Reply

Back To Top