From a5bed9ed2c7cf6214dbdc1fd0ae2b5cdfc4c2ca4 Mon Sep 17 00:00:00 2001 From: Shaun Reed Date: Sun, 1 Feb 2026 17:15:21 -0500 Subject: [PATCH] Fix panic when loading bad text in the GUI. --- qml/ClideEditor.qml | 5 +-- qml/ClideProjectView.qml | 12 ++++--- qml/ClideTreeView.qml | 3 +- src/gui/colors.rs | 5 ++- src/gui/filesystem.rs | 70 ++++++++++++++++++++++------------------ 5 files changed, 54 insertions(+), 41 deletions(-) diff --git a/qml/ClideEditor.qml b/qml/ClideEditor.qml index 0e74911..464637c 100644 --- a/qml/ClideEditor.qml +++ b/qml/ClideEditor.qml @@ -153,12 +153,13 @@ SplitView { id: areaConsole height: 100 - placeholderText: qsTr("Placeholder for bash terminal.") + width: parent.width + placeholderText: qsTr("shaun@pc:~/Code/clide$ ") placeholderTextColor: "white" readOnly: true wrapMode: TextArea.Wrap background: Rectangle { - color: RustColors.editor_background + color: RustColors.terminal_background implicitHeight: 100 // border.color: control.enabled ? RustColors.active : RustColors.inactive } diff --git a/qml/ClideProjectView.qml b/qml/ClideProjectView.qml index 3cd08b4..a5a5c0c 100644 --- a/qml/ClideProjectView.qml +++ b/qml/ClideProjectView.qml @@ -45,22 +45,25 @@ SplitView { ColumnLayout { spacing: 2 + // TODO: Make a ClideBreadCrumb element to support select parent paths as root Rectangle { width: navigationView.width - height: breadCrumb.height + 5 - - color: RustColors.explorer_text + height: 25 + color: RustColors.explorer_background Text { id: breadCrumb + anchors.fill: parent text: clideTreeView.rootDirectory + color: RustColors.explorer_text elide: Text.ElideLeft horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter } } ClideTreeView { id: clideTreeView - onFileClicked: path => root.projectDir = path + onFileClicked: path => clideEditor.filePath = path width: navigationView.width height: navigationView.height @@ -70,6 +73,7 @@ SplitView { } } ClideEditor { + id: clideEditor SplitView.fillWidth: true // Provide a path to the file currently open in the text editor. diff --git a/qml/ClideTreeView.qml b/qml/ClideTreeView.qml index 9a58e0b..ad0cd4d 100644 --- a/qml/ClideTreeView.qml +++ b/qml/ClideTreeView.qml @@ -119,11 +119,10 @@ TreeView { switch (button) { case Qt.LeftButton: fileSystemTreeView.toggleExpanded(treeDelegate.row) - fileSystemTreeView.lastIndex = treeDelegate.index // If this model item doesn't have children, it means it's // representing a file. if (!treeDelegate.hasChildren) - root.fileClicked(treeDelegate.filePath) + fileSystemTreeView.fileClicked(treeDelegate.filePath) break; case Qt.RightButton: if (treeDelegate.hasChildren) diff --git a/src/gui/colors.rs b/src/gui/colors.rs index fea8ea1..33ed86b 100644 --- a/src/gui/colors.rs +++ b/src/gui/colors.rs @@ -36,6 +36,7 @@ pub mod qobject { #[qproperty(QColor, explorer_background)] #[qproperty(QColor, explorer_folder)] #[qproperty(QColor, explorer_folder_open)] + #[qproperty(QColor, terminal_background)] type RustColors = super::RustColorsImpl; } } @@ -65,6 +66,7 @@ pub struct RustColorsImpl { explorer_background: QColor, explorer_folder: QColor, explorer_folder_open: QColor, + terminal_background: QColor, } impl Default for RustColorsImpl { @@ -87,11 +89,12 @@ impl Default for RustColorsImpl { editor_highlight: QColor::try_from("#ccced3").unwrap(), gutter: QColor::try_from("#1e1f22").unwrap(), explorer_hovered: QColor::try_from("#4c5053").unwrap(), - explorer_text: QColor::try_from("#3b3b3b").unwrap(), + explorer_text: QColor::try_from("#FFF").unwrap(), explorer_text_selected: QColor::try_from("#262626").unwrap(), explorer_background: QColor::try_from("#1E1F22").unwrap(), explorer_folder: QColor::try_from("#54585b").unwrap(), explorer_folder_open: QColor::try_from("#2b2b2b").unwrap(), + terminal_background: QColor::try_from("#2C2E32").unwrap(), } } } diff --git a/src/gui/filesystem.rs b/src/gui/filesystem.rs index ffac3bf..09d7aa5 100644 --- a/src/gui/filesystem.rs +++ b/src/gui/filesystem.rs @@ -48,14 +48,16 @@ use dirs; use log::warn; use std::fs; use std::io::BufRead; -use syntect::easy::HighlightFile; +use std::path::Path; +use syntect::easy::{HighlightFile, HighlightLines}; use syntect::highlighting::ThemeSet; use syntect::html::{ IncludeBackground, append_highlighted_html_for_styled_line, start_highlighted_html_snippet, }; use syntect::parsing::SyntaxSet; +use syntect::util::LinesWithEndings; -// TODO: Impleent a provider for QFileSystemModel::setIconProvider for icons. +// TODO: Implement a provider for QFileSystemModel::setIconProvider for icons. pub struct FileSystemImpl { file_path: QString, root_index: QModelIndex, @@ -76,42 +78,46 @@ impl qobject::FileSystem { if path.is_empty() { return QString::default(); } - if !fs::metadata(path.to_string()) - .expect(format!("Failed to get file metadata {path:?}").as_str()) - .is_file() - { + let meta = fs::metadata(path.to_string()) + .expect(format!("Failed to get file metadata {path:?}").as_str()); + if !meta.is_file() { warn!(target:"FileSystem", "Attempted to open file {path:?} that is not a valid file"); return QString::default(); } - let ss = SyntaxSet::load_defaults_nonewlines(); - let ts = ThemeSet::load_defaults(); - let theme = &ts.themes["base16-ocean.dark"]; + let path_str = path.to_string(); + if let Ok(lines) = fs::read_to_string(path_str.as_str()) { + let ss = SyntaxSet::load_defaults_nonewlines(); + let ts = ThemeSet::load_defaults(); + let theme = &ts.themes["base16-ocean.dark"]; + let lang = ss + .find_syntax_by_extension( + Path::new(path_str.as_str()) + .extension() + .map(|s| s.to_str()) + .unwrap_or_else(|| Some("md")) + .expect("Failed to get file extension"), + ) + .unwrap_or_else(|| ss.find_syntax_plain_text()); + let mut highlighter = HighlightLines::new(lang, theme); + let (mut output, _bg) = start_highlighted_html_snippet(theme); + for line in LinesWithEndings::from(lines.as_str()) { + let regions = highlighter + .highlight_line(line, &ss) + .expect("Failed to highlight"); - let mut highlighter = - HighlightFile::new(path.to_string(), &ss, theme).expect("Failed to create highlighter"); - let (mut output, _bg) = start_highlighted_html_snippet(theme); - let mut line = String::new(); - while highlighter - .reader - .read_line(&mut line) - .expect("Failed to read file.") - > 0 - { - let regions = highlighter - .highlight_lines - .highlight_line(&line, &ss) - .expect("Failed to highlight"); + append_highlighted_html_for_styled_line( + ®ions[..], + IncludeBackground::Yes, + &mut output, + ) + .expect("Failed to insert highlighted html"); + } - append_highlighted_html_for_styled_line( - ®ions[..], - IncludeBackground::Yes, - &mut output, - ) - .expect("Failed to insert highlighted html"); - line.clear(); + output.push_str("\n"); + QString::from(output) + } else { + return QString::default(); } - output.push_str("\n"); - QString::from(output) } // There will never be more than one column.