Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 27 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Release

on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: write
pull-requests: write

concurrency:
group: release-${{ github.ref }}
cancel-in-progress: false

jobs:
release-please:
name: Conventional Commit Release
runs-on: ubuntu-latest
steps:
- name: Run release automation
uses: googleapis/release-please-action@v4
with:
token: ${{ secrets.GH_TOKEN }}
config-file: .release-please-config.json
manifest-file: .release-please-manifest.json
12 changes: 12 additions & 0 deletions .release-please-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"release-type": "dart",
"include-v-in-tag": true,
"pull-request-title-pattern": "chore(main): release ${version}",
"packages": {
".": {
"component": "nocterm_lints",
"changelog-path": "CHANGELOG.md"
}
}
}
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "0.3.0+beta.3"
}
7 changes: 4 additions & 3 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,18 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [

{
"name": "nocterm_lints",
"request": "launch",
"type": "dart"
"type": "dart",
"console": "debugConsole"
},
{
"name": "example",
"cwd": "example",
"request": "launch",
"type": "dart"
"type": "dart",
"console": "debugConsole"
}
]
}
254 changes: 83 additions & 171 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,21 @@
<!-- <p align="center">
<img src="https://raw.githubusercontent.com/zoocityboy/nocterm_lints/refs/heads/main/assets/nocterm_lints.png" height="100" alt="Bloc">
</p> -->

# nocterm_lints

[![ZOOCITYBOY][logo_white]][zoocityboy_link_dark]
[![ZOOCITYBOY][logo_black]][zoocityboy_link_light]

[![Pub](https://img.shields.io/pub/v/nocterm_lints.svg)](https://pub.dev/packages/nocterm_lints)
[![Dart](https://img.shields.io/badge/Dart-0175C2?logo=dart)](https://dart.dev)
[![Nocterm](https://img.shields.io/badge/NOCTERM-f2f2f2?logo=nocterm&logoColor=000000)](https://nocterm.dev)

**Productivity assists for Nocterm terminal UI development**

An analysis server plugin providing intelligent IDE assists and refactoring tools for building Nocterm terminal UI components. Works seamlessly in VS Code, IntelliJ IDEA, Android Studio, and other Dart-enabled editors.

> [!info]
> Built on the modern `analysis_server_plugin` framework (Dart 3.10+). Originally derived from Dart project foundations and enhanced for Nocterm-specific workflows.

## Features

**19 productivity assists** organized into four categories:

### Component Manipulation (5)
- **Move Up/Down** — Reorder components up or down in the tree
- **Swap with Child** — Exchange positions with immediate child
- **Swap with Parent** — Exchange positions with parent component
- **Remove Component** — Delete wrapper while preserving children
[![Nocterm](https://img.shields.io/badge/Nocterm-terminal%20UI-black)](https://pub.dev/packages/nocterm)

### Component Wrapping (10)
- **Wrap with Component** — Choose from available component types
- **Wrap with Generic** — Container with customizable child
- **Wrap with Center** — Center-align component
- **Wrap with Container** — Add styling container
- **Wrap with Padding** — Add spacing (default: 8dp)
- **Wrap with Row/Column** — Create horizontal/vertical layouts
- **Wrap with Expanded/Flexible** — Control sizing in flex contexts
- **Wrap with SizedBox** — Define explicit dimensions
`nocterm_lints` is a Dart analysis server plugin focused on Nocterm UI productivity.
It provides code actions for wrapping, moving, swapping, removing, and converting Nocterm components directly in your editor.

### Layout Builders (2)
- **Wrap with Builder** — Builder pattern wrapper
- **Wrap with ValueListenableBuilder** — Reactive state pattern
> [!NOTE]
> The plugin targets Dart SDK `>=3.10.0 <4.0.0` and uses the modern `analysis_server_plugin` stack.

### Component Conversion (2)
- **Convert to Stateful** — Refactor to StatefulComponent
- **Convert to Stateless** — Refactor to StatelessComponent
## Installation

## Getting Started

### For Development (Local Path)

Add to your project's `analysis_options.yaml`:
### Use from local path (development)

```yaml
include: package:nocterm_lints/recommended.yaml
Expand All @@ -58,157 +25,102 @@ plugins:
path: ../path/to/nocterm_lints
```

### For Published Package
### Use from pub package

```yaml
include: package:nocterm_lints/recommended.yaml

plugins:
nocterm_lints: ^0.1.0
nocterm_lints: ^0.3.0
```

### Activate Assists

After updating `analysis_options.yaml`, restart the Dart Analysis Server:

| Editor | Command |
|--------|---------|
| **VS Code** | `Cmd+Shift+P` → "Dart: Restart Analysis Server" |
| **IntelliJ / Android Studio** | Tools → Dart Analysis → Restart |

> [!tip]
> Use `Cmd+.` (macOS) or `Ctrl+.` (Windows/Linux) to see available assists when the cursor is on a component.

## Usage Examples

### Wrap with Padding
After updating `analysis_options.yaml`, restart the Dart Analysis Server.

## Compatibility Matrix

| Component | Supported / Tested Range |
| --- | --- |
| Dart SDK | `>=3.10.0 <4.0.0` |
| `analysis_server_plugin` | `>=0.3.8 <0.4.0` |
| `analyzer` | `>=10.0.2 <11.0.0` |
| `analyzer_plugin` | `>=0.14.2 <0.15.0` |
| `nocterm` | `^0.5.0` |
| Editors | VS Code, IntelliJ IDEA, Android Studio (via Dart Analysis Server) |

## All Assistants

The table below lists every registered assistant in this project.

| Category | Assistant | Code Action label | Description |
| --- | --- | --- | --- |
| Move | `MoveUp` | `Move component up` | Swaps the selected component with the previous sibling in a list. |
| Move | `MoveDown` | `Move component down` | Swaps the selected component with the next sibling in a list. |
| Swap | `SwapWithChild` | `Swap with child` | Swaps a parent component with its direct child when structure is valid. |
| Swap | `SwapWithParent` | `Swap with parent` | Swaps a child component with its direct parent when structure is valid. |
| Remove | `RemoveWidget` | `Remove this component` | Removes a wrapper and preserves valid `child` or `children` content when possible. |
| Wrap | `WrapComponent` | `Wrap with component...` | Wraps a selected list of components with a generic component wrapper. |
| Wrap | `WrapGeneric` | `Wrap with component...` | Wraps a single component with a generic configurable wrapper. |
| Wrap | `WrapCenter` | `Wrap with Center` | Wraps the selected component in `Center`. |
| Wrap | `WrapContainer` | `Wrap with Container` | Wraps the selected component in `Container`. |
| Wrap | `WrapExpanded` | `Wrap with Expanded` | Wraps the selected component in `Expanded` in compatible flex contexts. |
| Wrap | `WrapFlexible` | `Wrap with Flexible` | Wraps the selected component in `Flexible` in compatible flex contexts. |
| Wrap | `WrapPadding` | `Wrap with Padding` | Wraps the selected component in `Padding` with default edge insets. |
| Wrap | `WrapSizedBox` | `Wrap with SizedBox` | Wraps the selected component in `SizedBox`. |
| Wrap | `WrapRow` | `Wrap with Row` | Wraps selected component(s) in a `Row(children: [...])`. |
| Wrap | `WrapColumn` | `Wrap with Column` | Wraps selected component(s) in a `Column(children: [...])`. |
| Wrap | `WrapBuilder` | `Wrap with Builder` | Wraps the selected component in `Builder` and generates a builder closure. |
| Wrap | `WrapValueListenableBuilder` | `Wrap with ValueListenableBuilder` | Wraps the selected component in `ValueListenableBuilder`. |
| Convert | `ConvertToStatefulComponent` | `Convert to StatefulComponent` | Refactors a stateless component into StatefulComponent + State classes. |
| Convert | `ConvertToStatelessComponent` | `Convert to StatelessComponent` | Refactors eligible stateful component/state pair into a stateless component. |

## Usage

1. Place the cursor on, or inside, a Nocterm component expression.
2. Open code actions (`Cmd+.` on macOS, `Ctrl+.` on Windows/Linux).
3. Pick an assist from the list.

> [!TIP]
> If assists do not appear after install/config changes, run `Dart: Restart Analysis Server`.

## Example

Before:

**Before:**
```dart
final component = MyComponent(child: Text('Hello'));
return Text('hello');
```

**After:** Invoke "Wrap with Padding" assist
After `Wrap with Padding`:

```dart
final component = Padding(
return Padding(
padding: const EdgeInsets.all(8),
child: MyComponent(child: Text('Hello')),
child: Text('hello'),
);
```

### Convert to Stateless Component

**Before:**
```dart
class MyComponent extends StatefulComponent {
@override
State<MyComponent> createState() => _MyComponentState();
}

class _MyComponentState extends State<MyComponent> {
@override
Component build(BuildContext context) => Text('Hello');
}
```

**After:** Invoke "Convert to Stateless Component" assist
```dart
class MyComponent extends StatelessComponent {
const MyComponent({super.key});

@override
Component build(BuildContext context) => Text('Hello');
}
```

## Configuration

Enable or disable diagnostics in `analysis_options.yaml`:

```yaml
plugins:
nocterm_lints:
path: ../nocterm_lints
```

## Development

### Setup

Bootstrap dependencies:
```bash
dart pub get
```

### Run Tests

```bash
dart test
```

### Format Code

```bash
dart format .
```

### Analyze

```bash
dart analyze
dart test test/assistants
```

### Test Against Local Project

1. Add to test project's `analysis_options.yaml`:
```yaml
plugins:
nocterm_lints:
path: /path/to/nocterm_lints
```

2. Restart the Dart Analysis Server

## Project Structure

```
nocterm_lints/
├── lib/
│ ├── main.dart # Plugin entry point
│ ├── src/assistants/ # Individual assists
│ │ ├── wrap_*.dart
│ │ ├── move_*.dart
│ │ ├── convert_*.dart
│ │ └── ...
│ ├── services/ # Core services
│ └── utilities/ # Shared extensions
├── test/ # Unit tests
├── example/ # Example project
├── analysis_options.yaml
├── pubspec.yaml
└── recommended.yaml # Default lint config
## Project Layout

```text
lib/
main.dart
assistants/
services/
utilities/
test/
assistants/
example/
```

## Requirements

| Requirement | Version |
|-------------|---------|
| Dart SDK | >= 3.10.0 |
| analysis_server_plugin | ^0.3.4 |
| analyzer | >= 8.0.0, < 10.0.0 |

## Supported Editors

- VS Code (via Dart extension)
- IntelliJ IDEA
- Android Studio
- Other Dart analyzer-compatible editors

## Licensing

Dual-licensed for compatibility:

- **New code**: [MIT License](LICENSE)
- **Derived from Dart SDK**: [BSD-3-Clause License](https://github.com/dart-lang/sdk/blob/main/LICENSE)

See [ACKNOWLEDGMENTS.md](ACKNOWLEDGMENTS.md) and [LICENSE](LICENSE) for detailed attribution.
[logo_black]: https://raw.githubusercontent.com/zoocityboy/zoo_brand/main/styles/README/zoocityboy_dark.png#gh-light-mode-only
[logo_white]: https://raw.githubusercontent.com/zoocityboy/zoo_brand/main/styles/README/zoocityboy_light.png#gh-dark-mode-only
[zoocityboy_link_dark]: https://github.com/zoocityboy#gh-dark-mode-only
[zoocityboy_link_light]: https://github.com/zoocityboy#gh-light-mode-only
1 change: 1 addition & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ analyzer:
avoid_catches_without_on_clauses: ignore
dangling_library_doc_comments: ignore
no_runtimetype_tostring: ignore
lines_longer_than_80_chars: ignore

exclude:
- test/.test_coverage.dart
Expand Down
2 changes: 1 addition & 1 deletion example/lib/widgets/counter.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ class Counter extends StatelessComponent {

@override
Component build(BuildContext context) {
return const SizedBox.shrink();
return Center(child: const SizedBox.shrink());
}
}
Loading