add auto-indent on enter and content-based lang detection for untitled docs

This commit is contained in:
jess 2026-04-07 21:02:24 -07:00
parent 1d3c03e23f
commit 8a4a585374
1 changed files with 81 additions and 0 deletions

View File

@ -144,8 +144,66 @@ impl EditorState {
match message {
Message::EditorAction(action) => {
let is_edit = action.is_edit();
let auto_indent = if let text_editor::Action::Edit(text_editor::Edit::Enter) = &action {
let cursor = self.content.cursor();
let line_text = self.content.line(cursor.position.line)
.map(|l| l.text.to_string())
.unwrap_or_default();
let base = leading_whitespace(&line_text).to_string();
let trimmed = line_text.trim_end();
let extra = matches!(trimmed.as_bytes().last(), Some(b'{' | b'[' | b'('));
if extra {
Some(format!("{base} "))
} else {
Some(base)
}
} else {
None
};
let dedent = if let text_editor::Action::Edit(text_editor::Edit::Insert(ch)) = &action {
matches!(ch, '}' | ']' | ')').then(|| {
let cursor = self.content.cursor();
let line_text = self.content.line(cursor.position.line)
.map(|l| l.text.to_string())
.unwrap_or_default();
let prefix = &line_text[..cursor.position.column];
if prefix.chars().all(|c| c == ' ' || c == '\t') && prefix.len() >= 4 {
Some(prefix.len())
} else {
None
}
}).flatten()
} else {
None
};
self.content.perform(action);
if let Some(indent) = auto_indent {
if !indent.is_empty() {
self.content.perform(text_editor::Action::Edit(
text_editor::Edit::Paste(Arc::new(indent)),
));
}
}
if let Some(col) = dedent {
let remove = col.min(4);
self.content.perform(text_editor::Action::Move(Motion::Left));
for _ in 0..remove {
self.content.perform(text_editor::Action::Edit(
text_editor::Edit::Backspace,
));
}
self.content.perform(text_editor::Action::Move(Motion::Right));
}
if is_edit {
if self.lang.is_none() {
self.lang = detect_lang_from_content(&self.content.text());
}
self.reparse();
self.run_eval();
}
@ -426,7 +484,30 @@ fn lang_from_extension(ext: &str) -> Option<String> {
"zig" => "zig",
"sql" => "sql",
"mk" => "make",
"cord" | "cordial" => "rust",
_ => return None,
};
Some(lang.to_string())
}
fn detect_lang_from_content(text: &str) -> Option<String> {
let keywords = ["fn ", "let ", "if ", "else ", "while ", "for ", "/="];
let mut hits = 0;
for line in text.lines().take(50) {
let trimmed = line.trim();
for kw in &keywords {
if trimmed.starts_with(kw) || trimmed.contains(&format!(" {kw}")) {
hits += 1;
}
}
if hits >= 2 {
return Some("rust".into());
}
}
None
}
fn leading_whitespace(line: &str) -> &str {
let end = line.len() - line.trim_start().len();
&line[..end]
}