Skip to content

Latest commit

 

History

History
413 lines (307 loc) · 9.45 KB

File metadata and controls

413 lines (307 loc) · 9.45 KB

Contributing to Skim

Thank you for your interest in contributing to Skim! We welcome contributions from the community.

Code of Conduct

Be respectful, constructive, and professional. We're here to build great tools together.

How to Contribute

Reporting Bugs

Before creating a bug report:

  1. Check existing issues to avoid duplicates
  2. Verify you're using the latest version
  3. Test with minimal example to isolate the issue

Good bug report includes:

  • Skim version (skim --version)
  • Operating system and architecture
  • Minimal code example that reproduces the issue
  • Expected vs actual behavior
  • Error messages (if any)

Suggesting Features

Before suggesting a feature:

  1. Check if it aligns with project scope (see CLAUDE.md for design constraints)
  2. Search existing issues for similar requests
  3. Consider if it can be implemented as a separate tool

Good feature request includes:

  • Clear use case and motivation
  • Example of how it would work
  • Consideration of edge cases
  • Willingness to contribute implementation

Asking Questions

  • Use GitHub Discussions for questions
  • Use Issues only for bugs and feature requests
  • Check existing discussions first

Development Setup

Prerequisites

  • Rust 1.70+ (rustup update)
  • Git
  • Optional: cargo-watch for auto-rebuild during development

Clone and Build

git clone https://github.com/dean0x/skim.git
cd skim
cargo build
cargo test

Project Structure

skim/
├── crates/
│   ├── rskim-core/         # Core library (no I/O, pure transforms)
│   │   ├── src/
│   │   │   ├── lib.rs      # Public API
│   │   │   ├── types.rs    # Core types (Language, Mode, etc.)
│   │   │   ├── parser/     # tree-sitter wrapper
│   │   │   └── transform/  # Transformation logic
│   │   ├── tests/          # Integration tests
│   │   └── benches/        # Benchmarks (criterion)
│   └── rskim/              # CLI binary (I/O layer)
│       └── src/main.rs     # Argument parsing, file I/O
├── tests/fixtures/         # Test files for each language
├── .docs/                  # Internal documentation
└── CLAUDE.md               # AI assistant instructions

Design principles:

  • rskim-core: No I/O, no CLI dependencies, pure library
  • rskim: Thin I/O wrapper, delegates to core
  • All business logic in core, tested there

Development Workflow

# Watch mode (auto-rebuild on changes)
cargo watch -x build -x test

# Run specific test
cargo test test_name

# Run with debug output
RUST_LOG=debug cargo run -- file.ts

# Format code
cargo fmt

# Lint
cargo clippy -- -D warnings

# Check before commit
cargo fmt --check && cargo clippy -- -D warnings && cargo test

Making Changes

1. Create a Branch

git checkout -b feature/my-feature
# or
git checkout -b fix/my-bugfix

2. Write Code

Follow existing patterns:

  • Use Result<T, SkimError> for error handling (no panics)
  • Add doc comments for public APIs
  • Keep functions focused and testable
  • Prefer &str over String for zero-copy
  • Update tests alongside code changes

Lint requirements:

// ✅ GOOD: Explicit error handling
let result = parse(source)?;

// ❌ BAD: Will fail clippy
let result = parse(source).unwrap();  // unwrap_used = deny

3. Write Tests

Every change needs tests:

#[test]
fn test_my_feature() {
    let source = "test code";
    let result = transform(source, Language::TypeScript, Mode::Structure);
    assert!(result.is_ok());
}

Test organization:

  • Unit tests: #[cfg(test)] mod tests in same file
  • Integration tests: tests/ directory
  • Fixtures: tests/fixtures/<language>/

4. Update Documentation

  • Add/update doc comments for public APIs
  • Update README.md if adding user-facing features
  • Update CHANGELOG.md under [Unreleased]
  • Add examples if introducing new functionality

5. Commit

Follow existing commit style:

git commit -m "Add signature extraction for Python decorators

- Parse decorator nodes in AST traversal
- Preserve @decorator syntax in output
- Add test fixtures for common decorator patterns

Fixes #123"

Commit message format:

<Action> <what> (<context>)

- Bullet points explaining changes
- Focus on WHY, not just WHAT
- Reference issue numbers if applicable

6. Push and Create PR

git push origin feature/my-feature

Then create a Pull Request on GitHub with:

  • Clear description of what changed and why
  • Link to related issues
  • Screenshots/examples if UI/output changed
  • Confirmation that tests pass

Adding a New Language

Adding language support is straightforward:

1. Find tree-sitter Grammar

Check https://github.com/tree-sitter for tree-sitter-<language>

2. Add Dependency

# Cargo.toml
[workspace.dependencies]
tree-sitter-newlang = "0.23"  # Must be 0.23.x
# crates/rskim-core/Cargo.toml
[dependencies]
tree-sitter-newlang = { workspace = true }

3. Update Language Enum

// crates/rskim-core/src/types.rs
pub enum Language {
    // ... existing
    NewLang,
}

impl Language {
    pub fn from_extension(ext: &str) -> Option<Self> {
        match ext {
            // ... existing
            "newext" => Some(Self::NewLang),
            _ => None,
        }
    }

    pub fn to_tree_sitter(self) -> tree_sitter::Language {
        match self {
            // ... existing
            Self::NewLang => tree_sitter_newlang::LANGUAGE.into(),
        }
    }
}

4. Add Node Types

// crates/rskim-core/src/transform/structure.rs
fn get_node_types_for_language(language: Language) -> NodeTypes {
    match language {
        // ... existing
        Language::NewLang => NodeTypes {
            function: "function_definition",  // Check grammar docs
            method: "method_definition",
        },
    }
}

5. Add Test Fixtures

mkdir -p tests/fixtures/newlang

Create tests/fixtures/newlang/simple.newext:

// Simple example with functions, classes, etc.

6. Add Tests

#[test]
fn test_parser_newlang() {
    let source = "function example() { }";
    let mut parser = Parser::new(Language::NewLang).unwrap();
    let result = parser.parse(source);
    assert!(result.is_ok());
}

7. Update Documentation

  • Add to language table in README.md
  • Update CHANGELOG.md
  • Add examples in README if syntax differs significantly

Total time: ~30 minutes for straightforward languages.

Testing Guidelines

Test Coverage

Run tests with coverage (requires cargo-tarpaulin):

cargo install cargo-tarpaulin
cargo tarpaulin --out Html

Coverage goals:

  • Core library: >80%
  • Transform logic: >90%
  • CLI: >60% (I/O makes 100% hard)

Test Categories

  1. Unit tests: Test individual functions
  2. Integration tests: Test full transformation pipeline
  3. Fixture tests: Test real-world code samples
  4. Snapshot tests: Use insta for output comparison (planned)

Writing Good Tests

// ✅ GOOD: Clear, focused test
#[test]
fn test_preserves_function_signature() {
    let source = "function add(a: number, b: number): number { return a + b; }";
    let result = transform(source, Language::TypeScript, Mode::Structure).unwrap();
    assert!(result.contains("function add(a: number, b: number): number"));
    assert!(result.contains("{ /* ... */ }"));
}

// ❌ BAD: Testing too many things
#[test]
fn test_everything() {
    // Tests 10 different features in one test
}

Performance Guidelines

Performance is a first-class concern.

Benchmarking

cargo bench  # Runs criterion benchmarks

Performance targets (verified):

  • Parse + transform: 14.6ms for 3000-line files ✅ (target was <50ms for 1000 lines)
  • Small files (<100 lines): 33-84µs depending on language
  • Memory: <10MB for typical files
  • Startup: <10ms

Performance Best Practices

// ✅ GOOD: Zero-copy with &str
let text = node.utf8_text(source.as_bytes())?;

// ❌ BAD: Unnecessary allocation
let text = node.text().to_string();
// ✅ GOOD: Pre-allocate with capacity
let mut result = String::with_capacity(source.len());

// ❌ BAD: Let it grow
let mut result = String::new();

Code Review Process

  1. Automated checks must pass:

    • cargo fmt --check
    • cargo clippy -- -D warnings
    • cargo test
    • CI pipeline (GitHub Actions)
  2. Manual review focuses on:

    • Correctness and safety
    • Performance implications
    • API design
    • Test coverage
    • Documentation quality
  3. Approval: At least one maintainer approval required

  4. Merge: Squash and merge (keep history clean)

Release Process

(For maintainers)

  1. Update version in Cargo.toml files
  2. Update CHANGELOG.md (move Unreleased to new version)
  3. Commit: git commit -m "Release v0.2.0"
  4. Tag: git tag v0.2.0
  5. Push: git push --tags
  6. CI will automatically publish to crates.io (when configured)

Need Help?

Recognition

Contributors are credited in:

  • Git commit history
  • Release notes in CHANGELOG.md
  • Special thanks in README.md (for significant contributions)

Thank you for contributing to Skim! 🎉