Skip to content

Commit

Permalink
semi-automated improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
acharneski committed Nov 18, 2024
1 parent 0f109bb commit 9cfd53b
Show file tree
Hide file tree
Showing 47 changed files with 1,973 additions and 851 deletions.
306 changes: 201 additions & 105 deletions docs/actions_best_practices.md
Original file line number Diff line number Diff line change
@@ -1,177 +1,273 @@
Here's a best practices document for actions in this project:

# AI Coder Action Best Practices

## Action Types
## 1. Choosing the Right Base Class

Choose the appropriate base class for your action:

### BaseAction

Basic usage:

```kotlin
class SimpleAction : BaseAction(
name = "My Action",
description = "Does something simple"
) {
override fun handle(event: AnActionEvent) {
val project = event.project ?: return
UITools.run(project, "Processing", true) { progress ->
// Action logic here with progress indication
progress.text = "Doing work..."
}
}
}
```

### SelectionAction<T>

The project has several base action types that should be used appropriately:
* Provides automatic selection handling and language detection
* Example implementations: ApplyPatchAction, CodeFormatterAction
Advanced usage:

1. `BaseAction` - Base class for all actions
2. `SelectionAction<T>` - For actions that operate on selected text
3. `FileContextAction<T>` - For actions that operate on files/folders
```kotlin
class CodeFormatterAction : SelectionAction<FormatterConfig>() {
// Custom configuration class
data class FormatterConfig(
val style: String,
val indent: Int
)

override fun getConfig(project: Project?): FormatterConfig? {
return FormatterConfig(
style = "google",
indent = 4
)
}

Choose the appropriate base class based on your action's needs.
override fun processSelection(state: SelectionState, config: FormatterConfig?): String {
// Access selection context
val code = state.selectedText ?: return ""
val lang = state.language ?: return code
val indent = state.indent?.toString() ?: " "
// Process with configuration
return formatCode(code, lang, config?.style, config?.indent ?: 4)
}
}
```

### FileContextAction<T>

* Provides progress indication and cancellation support
* Manages file refresh and editor updates
Complete example:

```kotlin
class FileProcessorAction : FileContextAction<ProcessorConfig>() {
data class ProcessorConfig(
val outputDir: String,
val options: Map<String, String>
)

override fun getConfig(project: Project?, e: AnActionEvent): ProcessorConfig? {
val dir = UITools.chooseDirectory(project, "Select Output Directory")
return dir?.let { ProcessorConfig(it.path, mapOf()) }
}

override fun processSelection(state: SelectionState, config: ProcessorConfig?, progress: ProgressIndicator): Array<File> {
progress.text = "Processing ${state.selectedFile.name}"
val outputFile = File(config?.outputDir, "processed_${state.selectedFile.name}")
outputFile.parentFile.mkdirs()
// Process file with progress updates
progress.isIndeterminate = false
processFileWithProgress(state.selectedFile, outputFile, progress)
return arrayOf(outputFile)
}
}
```

## Core Principles

### 1. Thread Safety

- Always run long operations in background threads
- Use `WriteCommandAction` for document modifications
- Protect shared resources with appropriate synchronization
Best practices for thread management:

* Use WriteCommandAction for document modifications
* Avoid blocking EDT (Event Dispatch Thread)
* Handle background tasks properly

```kotlin
Thread {
try {
UITools.redoableTask(e) {
// Long running operation
// Good example * proper thread handling:
WriteCommandAction.runWriteCommandAction(project) {
try {
UITools.run(project, "Processing", true) { progress ->
progress.isIndeterminate = false
progress.text = "Processing..."
ApplicationManager.getApplication().executeOnPooledThread {
// Long running operation
progress.fraction = 0.5
}
}
} catch (e: Throwable) {
UITools.error(log, "Error", e)
}
} catch (e: Throwable) {
UITools.error(log, "Error", e)
}
}.start()
}

// Bad example * avoid:
Thread {
document.setText("new text") // Don't modify documents outside WriteCommandAction
}.start()
```

### 2. Error Handling

- Wrap operations in try-catch blocks
- Log errors appropriately using the logger
- Show user-friendly error messages
- Make actions fail gracefully
Comprehensive error handling strategy:

* Use structured error handling
* Provide user feedback
* Log errors appropriately

```kotlin
// Good example:
try {
// Action logic
} catch (e: Exception) {
log.error("Error in action", e)
UITools.showErrorDialog(project, e.message, "Error")
processFile(file)
} catch (e: IOException) {
log.error("Failed to process file: ${file.path}", e)
val choice = UITools.showErrorDialog(
project,
"Failed to process file: ${e.message}\nWould you like to retry?",
"Error",
arrayOf("Retry", "Cancel")
)
if (choice == 0) {
// Retry logic
processFile(file)
}
} finally {
cleanup()
}
```


### 3. Progress Indication

- Use progress indicators for long operations
- Show meaningful progress messages
- Allow cancellation where appropriate
Guidelines for progress feedback:

* Update progress frequently
* Include operation details in progress text

```kotlin
UITools.run(project, "Operation Name", true) { progress ->
progress.text = "Step description..."
progress.fraction = 0.5
// Operation logic
UITools.run(project, "Processing Files", true) { progress ->
progress.text = "Initializing..."
files.forEachIndexed { index, file ->
if (progress.isCanceled) throw InterruptedException()
progress.fraction = index.toDouble() / files.size
progress.text = "Processing ${file.name} (${index + 1}/${files.size})"
processFile(file)
}
}
```

### 4. Configuration

- Use `getConfig()` to get user input/settings
- Validate configurations before proceeding
- Store recent configurations where appropriate
- Use `AppSettingsState` for persistent settings

* Use `getConfig()` to get user input/settings
* Validate configurations before proceeding
* Store recent configurations where appropriate
* Use `AppSettingsState` for persistent settings

```kotlin
override fun getConfig(project: Project?, e: AnActionEvent): Settings {
return Settings(
UITools.showDialog(
project,
SettingsUI::class.java,
UserSettings::class.java,
"Dialog Title"
),
project
)
return Settings(
UITools.showDialog(
project,
SettingsUI::class.java,
UserSettings::class.java,
"Dialog Title"
),
project
)
}
```


### 5. Action Enablement

- Implement `isEnabled()` to control when action is available
- Check for null safety
- Verify required context (files, selection, etc)
- Consider language support requirements

* Implement `isEnabled()` to control when action is available
* Check for null safety
* Verify required context (files, selection, etc)
* Consider language support requirements

```kotlin
override fun isEnabled(event: AnActionEvent): Boolean {
if (!super.isEnabled(event)) return false
val file = UITools.getSelectedFile(event) ?: return false
return isLanguageSupported(getComputerLanguage(event))
if (!super.isEnabled(event)) return false
val file = UITools.getSelectedFile(event) ?: return false
return isLanguageSupported(getComputerLanguage(event))
}
```

### 6. UI Integration

- Use IntelliJ UI components and dialogs
- Follow IntelliJ UI guidelines
- Provide appropriate feedback to users
- Support undo/redo operations
* Use IntelliJ UI components and dialogs
* Follow IntelliJ UI guidelines
* Provide appropriate feedback to users
* Support undo/redo operations

### 7. AI Integration

- Use appropriate models for the task
- Handle API errors gracefully
- Provide meaningful prompts
- Process AI responses appropriately

* Use appropriate models for the task
* Handle API errors gracefully
* Provide meaningful prompts
* Process AI responses appropriately

```kotlin
val response = ChatProxy(
clazz = API::class.java,
api = api,
model = AppSettingsState.instance.smartModel.chatModel(),
temperature = AppSettingsState.instance.temperature
clazz = API::class.java,
api = api,
model = AppSettingsState.instance.smartModel.chatModel(),
temperature = AppSettingsState.instance.temperature
).create()
```

### 8. Code Organization

- Keep actions focused on a single responsibility
- Extract common functionality to utility classes
- Use appropriate inheritance hierarchy
- Follow Kotlin coding conventions
* Keep actions focused on a single responsibility
* Extract common functionality to utility classes
* Use appropriate inheritance hierarchy
* Follow Kotlin coding conventions

### 9. Documentation

- Document action purpose and usage
- Include example usage where helpful
- Document configuration options
- Explain any special requirements
* Document action purpose and usage
* Include example usage where helpful
* Document configuration options
* Explain any special requirements

### 10. Testing

- Test action enablement logic
- Test configuration validation
- Test error handling
- Test with different file types/languages
* Test action enablement logic
* Test configuration validation
* Test error handling
* Test with different file types/languages

## Common Patterns

### Chat Actions

- Use `SessionProxyServer` for chat interfaces
- Configure appropriate chat models
- Handle chat session lifecycle
- Support conversation context
* Use `SessionProxyServer` for chat interfaces
* Configure appropriate chat models
* Handle chat session lifecycle
* Support conversation context

### File Operations

- Use `FileContextAction` base class
- Handle file system operations safely
- Support undo/redo
- Refresh file system after changes
* Use `FileContextAction` base class
* Handle file system operations safely
* Support undo/redo
* Refresh file system after changes

### Code Modifications

- Use `SelectionAction` for code changes
- Support appropriate languages
- Handle code formatting
- Preserve indentation
* Use `SelectionAction` for code changes
* Support appropriate languages
* Handle code formatting
* Preserve indentation

## Specific Guidelines

Expand All @@ -198,13 +294,13 @@ val response = ChatProxy(

## Best Practices Checklist

- [ ] Extends appropriate base class
- [ ] Implements proper error handling
- [ ] Shows progress for long operations
- [ ] Validates configurations
- [ ] Implements proper enablement logic
- [ ] Follows UI guidelines
- [ ] Handles AI integration properly
- [ ] Includes proper documentation
- [ ] Supports undo/redo
- [ ] Includes appropriate tests
* [ ] Extends appropriate base class
* [ ] Implements proper error handling
* [ ] Shows progress for long operations
* [ ] Validates configurations
* [ ] Implements proper enablement logic
* [ ] Follows UI guidelines
* [ ] Handles AI integration properly
* [ ] Includes proper documentation
* [ ] Supports undo/redo
* [ ] Includes appropriate tests
Loading

0 comments on commit 9cfd53b

Please sign in to comment.