feat: add SQL syntax highlighting in query editor
This commit is contained in:
+45
-1
@@ -11,7 +11,7 @@ use crate::{
|
||||
Result,
|
||||
connection::Input,
|
||||
db::{Column, Database, PAGE_SIZE, Page, QueryResult, Relation},
|
||||
error_text,
|
||||
error_text, sql,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
@@ -33,6 +33,7 @@ pub struct App {
|
||||
column_offset: usize,
|
||||
show_schema: bool,
|
||||
query: Input,
|
||||
query_highlights: sql::Highlights,
|
||||
query_result: Option<QueryResult>,
|
||||
error: Option<String>,
|
||||
}
|
||||
@@ -56,6 +57,7 @@ impl App {
|
||||
column_offset: 0,
|
||||
show_schema: false,
|
||||
query: Input::default(),
|
||||
query_highlights: Vec::new(),
|
||||
query_result: None,
|
||||
error: None,
|
||||
};
|
||||
@@ -104,16 +106,27 @@ impl App {
|
||||
}
|
||||
|
||||
pub(crate) fn handle_query_key(&mut self, key: KeyEvent) {
|
||||
let previous_len = self.query.value.len();
|
||||
match key.code {
|
||||
KeyCode::F(5) => self.execute_query(),
|
||||
KeyCode::Enter if key.modifiers.contains(KeyModifiers::CONTROL) => self.execute_query(),
|
||||
KeyCode::Enter => self.query.paste_multiline("\n"),
|
||||
_ => self.query.edit(key),
|
||||
}
|
||||
self.highlight_query(previous_len);
|
||||
}
|
||||
|
||||
pub(crate) fn paste_query(&mut self, text: &str) {
|
||||
let previous_len = self.query.value.len();
|
||||
self.query.paste_multiline(text);
|
||||
self.highlight_query(previous_len);
|
||||
}
|
||||
|
||||
fn highlight_query(&mut self, previous_len: usize) {
|
||||
// Editing inserts or removes bytes; cursor movement does not invalidate syntax.
|
||||
if self.query.value.len() != previous_len {
|
||||
self.query_highlights = sql::highlight(&self.query.value, self.db.name() == "SQLite");
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_query(&mut self) {
|
||||
@@ -232,6 +245,7 @@ impl App {
|
||||
" Query · F5 execute ",
|
||||
false,
|
||||
focus == Focus::Query,
|
||||
Some(sql::text(&self.query.value, &self.query_highlights)),
|
||||
);
|
||||
let title = if self.query_result.is_some() {
|
||||
" Results "
|
||||
@@ -432,6 +446,36 @@ mod tests {
|
||||
assert_eq!(moved_index(Some(1), 3, isize::MAX), Some(2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn query_colors_survive_editing_and_scrolling() -> crate::Result<()> {
|
||||
use super::{App, Focus};
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
use ratatui::{Terminal, backend::TestBackend, style::Color};
|
||||
let file = tempfile::NamedTempFile::new()?;
|
||||
let mut app = App::new(crate::db::Database::open_sqlite(file.path())?)?;
|
||||
let mut terminal = Terminal::new(TestBackend::new(60, 14))?;
|
||||
app.paste_query("SELECT '界', 42;\n-- comment");
|
||||
terminal.draw(|frame| app.draw(frame, frame.area(), Focus::Query))?;
|
||||
let buffer = terminal.backend().buffer();
|
||||
assert_eq!(buffer[(1, 2)].fg, Color::Cyan);
|
||||
assert_eq!(buffer[(8, 2)].fg, Color::Green);
|
||||
assert_eq!(buffer[(1, 3)].fg, Color::DarkGray);
|
||||
app.handle_query_key(KeyEvent::new(KeyCode::Char('u'), KeyModifiers::CONTROL));
|
||||
app.handle_query_key(KeyEvent::new(KeyCode::Char('\''), KeyModifiers::NONE));
|
||||
app.paste_query("unfinished\nstring");
|
||||
terminal.draw(|frame| app.draw(frame, frame.area(), Focus::Query))?;
|
||||
assert_eq!(terminal.backend().buffer()[(1, 3)].fg, Color::Green);
|
||||
app.handle_query_key(KeyEvent::new(KeyCode::Char('u'), KeyModifiers::CONTROL));
|
||||
app.paste_query(&format!("{}--{}", "-- line\n".repeat(12), "x".repeat(90)));
|
||||
terminal.draw(|frame| app.draw(frame, frame.area(), Focus::Query))?;
|
||||
let cursor = terminal.get_cursor_position()?;
|
||||
let buffer = terminal.backend().buffer();
|
||||
assert_eq!(cursor.x, 58);
|
||||
assert_eq!(buffer[(cursor.x - 1, cursor.y)].symbol(), "x");
|
||||
assert_eq!(buffer[(1, cursor.y)].fg, Color::DarkGray);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn queries_replace_rows_and_preserve_results_on_error() -> crate::Result<()> {
|
||||
use crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
|
||||
|
||||
Reference in New Issue
Block a user