add auto-indent on enter and content-based lang detection for untitled docs
This commit is contained in:
parent
1d3c03e23f
commit
8a4a585374
|
|
@ -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]
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue