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 {
|
match message {
|
||||||
Message::EditorAction(action) => {
|
Message::EditorAction(action) => {
|
||||||
let is_edit = action.is_edit();
|
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);
|
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 is_edit {
|
||||||
|
if self.lang.is_none() {
|
||||||
|
self.lang = detect_lang_from_content(&self.content.text());
|
||||||
|
}
|
||||||
self.reparse();
|
self.reparse();
|
||||||
self.run_eval();
|
self.run_eval();
|
||||||
}
|
}
|
||||||
|
|
@ -426,7 +484,30 @@ fn lang_from_extension(ext: &str) -> Option<String> {
|
||||||
"zig" => "zig",
|
"zig" => "zig",
|
||||||
"sql" => "sql",
|
"sql" => "sql",
|
||||||
"mk" => "make",
|
"mk" => "make",
|
||||||
|
"cord" | "cordial" => "rust",
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(lang.to_string())
|
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