Thank you for your interest in contributing! This document provides guidelines and instructions for contributing to the project.
- Code of Conduct
- Getting Started
- Development Setup
- Architecture Overview
- Making Changes
- Testing
- Submitting Changes
- Code Style
- Adding New Features
- Be respectful and constructive
- Welcome newcomers and help them get started
- Focus on what is best for the community
- Show empathy towards other community members
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USERNAME/universal-language-connector.git - Add upstream remote:
git remote add upstream https://github.com/universal-connector/universal-language-connector.git - Create a branch:
git checkout -b feature/your-feature-name
- Rust 1.75+ (install via rustup)
- Node.js 18+ (for VS Code client development)
- Docker/Podman (optional, for container testing)
# Install development tools
make setup
# Build the project
make build
# Run tests
make test
# Start development server
make devuniversal-language-server-plugin/
├── server/ # Rust server implementation
├── clients/ # Editor clients (<100 LOC each)
├── web/ # Web UI
├── deployment/ # Docker and deployment configs
├── docs/ # Documentation
└── examples/ # Usage examples
- All logic in server - Clients are thin wrappers (<100 LOC)
- LSP 3.17 strict compliance - No custom extensions without justification
- Performance targets - <100ms response, <50MB memory, <500ms startup
- Server-first thinking - When in doubt, implement in the server
Server:
- Rust with tokio (async runtime)
- tower-lsp (LSP implementation)
- axum (HTTP API)
- dashmap (concurrent document storage)
Clients:
- Language-specific LSP client libraries
- Minimal code, maximum delegation to server
- LSP Handler (
server/src/lsp.rs) - Language Server Protocol implementation - HTTP API (
server/src/http.rs) - REST endpoints - WebSocket (
server/src/websocket.rs) - Real-time updates - Conversion Core (
server/src/core.rs) - Document format conversion - Document Store (
server/src/document_store.rs) - Concurrent document management
-
Update your fork:
git fetch upstream git rebase upstream/main
-
Make your changes:
- Write code following style guidelines
- Add tests for new functionality
- Update documentation
-
Test your changes:
make test make lint make fmt -
Commit your changes:
git add . git commit -m "feat: add new conversion format"
Follow Conventional Commits:
<type>(<scope>): <description>
[optional body]
[optional footer]
Types:
feat: New featurefix: Bug fixdocs: Documentation changesstyle: Code style changes (formatting, etc.)refactor: Code refactoringtest: Adding or updating testschore: Maintenance tasks
Examples:
feat(core): add YAML conversion support
fix(lsp): handle UTF-16 position correctly
docs(api): update WebSocket examples
# All tests
make test
# Specific test file
cd server && cargo test --test core_tests
# With output
cd server && cargo test -- --nocapture
# Integration tests
make test-integrationUnit tests (same file as code):
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_conversion() {
// Test code here
}
}Integration tests (server/tests/):
#[tokio::test]
async fn test_http_endpoint() {
// Test code here
}Aim for >80% code coverage:
make test-coverage-
Push to your fork:
git push origin feature/your-feature-name
-
Create Pull Request:
- Go to GitHub and create a PR from your fork
- Fill in the PR template
- Link related issues
-
PR Requirements:
- All tests must pass
- Code must be formatted (
make fmt) - Linter must pass (
make lint) - Documentation updated if needed
- At least one review approval
-
After Approval:
- Squash commits if requested
- Rebase on main if needed
- Maintainers will merge
Use conventional commit format:
feat: add support for YAML conversion
fix: resolve UTF-16 position handling bug
docs: improve API documentation
Follow standard Rust conventions:
// Use rustfmt
cargo fmt
// Pass clippy
cargo clippy -- -D warnings
// Naming
const MAX_SIZE: usize = 100;
fn convert_document() {}
struct DocumentStore {}
// Error handling
fn process() -> Result<Output, Error> {
// Use ? operator
let data = fetch_data()?;
Ok(process_data(data))
}
// Documentation
/// Converts a document between formats.
///
/// # Arguments
/// * `request` - Conversion request with content and formats
///
/// # Returns
/// Converted content or error
pub fn convert(request: ConversionRequest) -> Result<ConversionResponse> {
// Implementation
}For client code:
// Use consistent formatting
// Prefer async/await over callbacks
// Document public APIs
/**
* Converts the current document to HTML
*/
async function convertToHtml(): Promise<void> {
// Implementation
}This is a hard constraint. If a client exceeds 100 lines:
- Move logic to the server
- Simplify the implementation
- Remove unnecessary code
- Add parser dependency to
server/Cargo.toml - Implement converter in
server/src/core.rs - Add tests in
server/tests/core_tests.rs - Update documentation in
docs/API.md - Add examples to
examples/conversions/
Example:
// In core.rs
pub enum Format {
Markdown,
Html,
Json,
Yaml, // New format
}
impl ConversionCore {
fn markdown_to_yaml(markdown: &str) -> Result<String> {
// Implementation
}
}- Create directory:
clients/<editor-name>/ - Implement LSP client using editor's native API
- Keep under 100 LOC
- Add README with installation instructions
- Test end-to-end with real editor
Template structure:
clients/myeditor/
├── plugin.ext # Main plugin file (<100 LOC)
├── README.md # Installation guide
└── package.json/config # Package metadata (if applicable)
- Define route in
server/src/http.rs - Add handler function
- Add tests in
server/tests/http_api_tests.rs - Update API docs in
docs/API.md
Example:
async fn new_endpoint(
State(state): State<Arc<ServerState>>,
Json(payload): Json<Request>,
) -> Result<Json<Response>, ApiError> {
// Implementation
}
// In create_router()
.route("/api/new-endpoint", post(new_endpoint))- Implement method in
server/src/lsp.rs - Update capabilities in
initialize() - Add tests in
server/tests/lsp_compliance.rs - Document in
docs/API.md
Example:
#[tower_lsp::async_trait]
impl LanguageServer for UniversalConnectorBackend {
async fn new_method(&self, params: Params) -> LspResult<Response> {
// Implementation
}
}Always consider performance:
- Use async/await for I/O operations
- Avoid blocking operations on main thread
- Use
dashmapfor concurrent access - Profile changes with
cargo bench - Target <100ms response times
Update documentation for:
- New features
- API changes
- Configuration options
- Breaking changes
Documentation locations:
- API:
docs/API.md - Architecture:
docs/ARCHITECTURE.md - User guide:
README.md - Code comments: Inline documentation
- Issues: GitHub Issues
- Discussions: GitHub Discussions
- Documentation: See
docs/directory
Contributors are recognized in:
- README.md contributors section
- Release notes
- Git commit history
Thank you for contributing to Universal Language Connector!