Archived
12e537d6ab
Round 1: Fix dependency paths (../el/crates → ../el/engrams), verify build Round 2: Enhanced syntax highlighting — function call detection, all El keywords (activate, sealed, parallel, deploy, etc.) Round 3: Full El keyword set in CodeMirror tokenizer and completions; 50+ builtin function completions with type signatures Round 4: File system integration — mkdir, rename, delete, file tree search; git status badges Round 5: Runner integration — Ctrl+R shortcut, SSE streaming output, clickable error lines with jump-to-line Round 6: Error highlighting with accurate line/col from lexer/parser spans; diagnostic dedup Round 7: Find/replace panel; Ctrl+G go-to-line; toggle line comment; word-wrap compartment fix Round 8: Code completion — 50+ builtins, keyword completions, snippet completions, server snippet integration Round 9: Resizable panels — file tree drag-resize + collapse (Ctrl+B), type-graph drag-resize, bottom panel toggle (Ctrl+J), width persistence Round 10: Settings API (GET/POST/DELETE /api/settings, ~/.el-ide/settings.json); frontend wired to API with debounced save; theme persistence Round 11: Minimap click-to-jump and drag-to-scroll Round 12: Command palette — added Go To Line, Toggle Word Wrap/Minimap/File Tree/Bottom Panel, font size commands, New File, Select Next Occurrence Round 13: Multi-cursor — Ctrl+D select next occurrence, EditorSelection exposed for multi-range selection
85 lines
2.3 KiB
Rust
85 lines
2.3 KiB
Rust
//! Format API — run `el fmt` on source content.
|
|
|
|
use axum::{
|
|
extract::State,
|
|
http::StatusCode,
|
|
Json,
|
|
};
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::AppState;
|
|
|
|
type ApiResult<T> = Result<Json<T>, (StatusCode, Json<serde_json::Value>)>;
|
|
|
|
fn api_err(msg: impl std::fmt::Display) -> (StatusCode, Json<serde_json::Value>) {
|
|
(
|
|
StatusCode::INTERNAL_SERVER_ERROR,
|
|
Json(serde_json::json!({ "error": msg.to_string() })),
|
|
)
|
|
}
|
|
|
|
#[derive(Debug, Deserialize)]
|
|
pub struct FormatRequest {
|
|
pub content: String,
|
|
/// Optional filename hint for language detection
|
|
pub path: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize)]
|
|
pub struct FormatResponse {
|
|
pub content: String,
|
|
pub changed: bool,
|
|
}
|
|
|
|
/// POST /api/format — format content via `el fmt`, falls back to identity.
|
|
pub async fn format(
|
|
State(state): State<AppState>,
|
|
Json(req): Json<FormatRequest>,
|
|
) -> ApiResult<FormatResponse> {
|
|
let project_path = &state.config.project_path;
|
|
|
|
// Write content to a temp file, run `el fmt`, read back
|
|
let tmp_path = format!("{project_path}/.el-ide-fmt-tmp.el");
|
|
|
|
tokio::fs::write(&tmp_path, &req.content)
|
|
.await
|
|
.map_err(|e| api_err(format!("write tmp: {e}")))?;
|
|
|
|
// Try to find `el` binary
|
|
let el_bin = if let Ok(path) = which::which("el") {
|
|
path.to_string_lossy().to_string()
|
|
} else {
|
|
// el not installed — return unchanged
|
|
tokio::fs::remove_file(&tmp_path).await.ok();
|
|
return Ok(Json(FormatResponse {
|
|
content: req.content.clone(),
|
|
changed: false,
|
|
}));
|
|
};
|
|
|
|
let output = tokio::process::Command::new(&el_bin)
|
|
.arg("fmt")
|
|
.arg(&tmp_path)
|
|
.current_dir(project_path)
|
|
.output()
|
|
.await;
|
|
|
|
match output {
|
|
Ok(out) if out.status.success() => {
|
|
let formatted = tokio::fs::read_to_string(&tmp_path)
|
|
.await
|
|
.unwrap_or_else(|_| req.content.clone());
|
|
tokio::fs::remove_file(&tmp_path).await.ok();
|
|
let changed = formatted != req.content;
|
|
Ok(Json(FormatResponse { content: formatted, changed }))
|
|
}
|
|
_ => {
|
|
tokio::fs::remove_file(&tmp_path).await.ok();
|
|
Ok(Json(FormatResponse {
|
|
content: req.content.clone(),
|
|
changed: false,
|
|
}))
|
|
}
|
|
}
|
|
}
|