8 Commits

Author SHA1 Message Date
514c55367e typo 2026-03-13 21:32:58 -04:00
22dc9b0165 Fix badge links. 2026-03-13 21:19:07 -04:00
234e6a168e Add mirror badges. 2026-03-13 21:17:50 -04:00
ae5d1c7e22 Align tui and gui menubars. 2026-03-13 17:46:15 -04:00
a92844b8e5 Add syntax highlighter. 2026-02-28 23:00:01 -05:00
7a0b2d197f Fix logger scrolling. 2026-02-28 21:45:42 -05:00
a9564493cb Remove unused fonts. 2026-02-28 20:10:11 -05:00
dd11aeca54 Format. 2026-02-28 19:51:11 -05:00
15 changed files with 116 additions and 109 deletions

1
Cargo.lock generated
View File

@@ -1174,6 +1174,7 @@ dependencies = [
"libclide-macros", "libclide-macros",
"log", "log",
"strum", "strum",
"syntect",
] ]
[[package]] [[package]]

View File

@@ -1,4 +1,7 @@
# CLIDE # CLIDE
[![Github](https://img.shields.io/badge/GitHub-100000?style=for-the-badge&logo=github&logoColor=white)](https://github.com/shaunrd0/clide)
[![Gitlab](https://img.shields.io/badge/GitLab-330F63?style=for-the-badge&logo=gitlab&logoColor=white)](https://gitlab.com/shaunrd0/clide)
[![Gitea](https://img.shields.io/badge/Gitea-609926?style=for-the-badge&logo=gitea&logoColor=white)](https://git.shaunreed.com/shaunrd0/clide)
[![Check](https://github.com/shaunrd0/clide/actions/workflows/check.yaml/badge.svg)](https://github.com/shaunrd0/clide/actions/workflows/check.yaml) [![Check](https://github.com/shaunrd0/clide/actions/workflows/check.yaml/badge.svg)](https://github.com/shaunrd0/clide/actions/workflows/check.yaml)
@@ -38,7 +41,7 @@ export QMAKE=$HOME/Qt/6.7.3/gcc_64/bin/qmake6
export LD_LIBRARY_PATH=$HOME/Qt/6.7.3/gcc_64/lib export LD_LIBRARY_PATH=$HOME/Qt/6.7.3/gcc_64/lib
``` ```
Though environment variables set using `export` will take precedence, these can also be set in [.cargo/config.toml](./.cargo/config.toml) for conveinence Though environment variables set using `export` will take precedence, these can also be set in [.cargo/config.toml](./.cargo/config.toml) for convenience
```toml ```toml
[env] [env]

View File

@@ -9,3 +9,4 @@ strum = { workspace = true }
log = { workspace = true } log = { workspace = true }
devicons = { workspace = true } devicons = { workspace = true }
libclide-macros = { path = "../libclide-macros" } libclide-macros = { path = "../libclide-macros" }
syntect = "5.3.0"

View File

@@ -4,4 +4,21 @@
pub mod entry_meta; pub mod entry_meta;
use anyhow::Context;
pub use entry_meta::icon; pub use entry_meta::icon;
use std::fs;
use std::path::Path;
pub fn read_file<P: AsRef<Path>>(p: P) -> anyhow::Result<String> {
let path = p.as_ref();
let meta =
fs::metadata(path).unwrap_or_else(|_| panic!("Failed to get file metadata {path:?}"));
if !meta.is_file() {
crate::warn!(target:"FileSystem", "Attempted to open file {path:?} that is not a valid file");
Err(anyhow::anyhow!(
"Attempted to open file {path:?} that is not a valid file"
))?;
}
let path_str = path.to_string_lossy().to_string();
fs::read_to_string(path_str.as_str()).context(format!("Failed to read file {path:?}"))
}

View File

@@ -3,3 +3,4 @@
// SPDX-License-Identifier: GNU General Public License v3.0 or later // SPDX-License-Identifier: GNU General Public License v3.0 or later
pub mod colors; pub mod colors;
pub mod highlighter;

View File

@@ -0,0 +1,66 @@
use std::fs;
use std::path::Path;
use syntect::easy::HighlightLines;
use syntect::highlighting::ThemeSet;
use syntect::html::{IncludeBackground, append_highlighted_html_for_styled_line};
use syntect::parsing::SyntaxSet;
use syntect::util::LinesWithEndings;
pub struct Highlighter {
path: String,
ss: SyntaxSet,
ts: ThemeSet,
}
impl Highlighter {
pub fn new<P: AsRef<Path>>(p: P) -> anyhow::Result<Highlighter> {
let path = p.as_ref();
let meta =
fs::metadata(path).unwrap_or_else(|_| panic!("Failed to get file metadata {path:?}"));
let ss = SyntaxSet::load_defaults_nonewlines();
let ts = ThemeSet::load_defaults();
if !meta.is_file() {
crate::error!(target:"FileSystem", "Attempted to open file {path:?} that is not a valid file");
Err(anyhow::anyhow!(
"Attempted to open file {path:?} that is not a valid file"
))?;
}
Ok(Highlighter {
path: path.to_string_lossy().to_string(),
ss,
ts,
})
}
pub fn syntax_highlight_text<P: AsRef<str>>(&self, p: P) -> String {
let text = p.as_ref();
let theme = &self.ts.themes["base16-ocean.dark"];
let lang = self
.ss
.find_syntax_by_extension(
Path::new(self.path.as_str())
.extension()
.map(|s| s.to_str())
.unwrap_or_else(|| Some("md"))
.expect("Failed to get file extension"),
)
.unwrap_or_else(|| self.ss.find_syntax_plain_text());
let mut highlighter = HighlightLines::new(lang, theme);
// If you care about the background, see `start_highlighted_html_snippet(theme);`.
let mut output = String::from("<pre>\n");
for line in LinesWithEndings::from(text) {
let regions = highlighter
.highlight_line(line, &self.ss)
.expect("Failed to highlight");
append_highlighted_html_for_styled_line(
&regions[..],
IncludeBackground::No,
&mut output,
)
.expect("Failed to insert highlighted html");
}
output.push_str("</pre>\n");
output
}
}

View File

@@ -14,7 +14,6 @@ Rectangle {
ListModel { ListModel {
id: model id: model
} }
ListView { ListView {
id: listView id: listView
@@ -38,10 +37,10 @@ Rectangle {
anchors.fill: parent anchors.fill: parent
model: model model: model
verticalLayoutDirection: ListView.BottomToTop
delegate: Text { delegate: Text {
color: listView.getLogColor(level) color: listView.getLogColor(level)
font.family: "monospace"
text: `[${level}] ${message}` text: `[${level}] ${message}`
} }

View File

@@ -18,22 +18,6 @@ MenuBar {
ClideMenu { ClideMenu {
title: qsTr("&File") title: qsTr("&File")
ClideMenuItem {
action: Action {
id: actionNewProject
text: qsTr("&New Project...")
}
}
ClideMenuItem {
action: Action {
id: actionOpen
text: qsTr("&Open...")
}
onTriggered: FileSystem.setDirectory(FileSystem.filePath)
}
ClideMenuItem { ClideMenuItem {
action: Action { action: Action {
id: actionSave id: actionSave
@@ -41,6 +25,13 @@ MenuBar {
text: qsTr("&Save") text: qsTr("&Save")
} }
} }
ClideMenuItem {
action: Action {
id: actionReload
text: qsTr("&Reload")
}
}
MenuSeparator { MenuSeparator {
background: Rectangle { background: Rectangle {
border.color: color border.color: color
@@ -67,37 +58,9 @@ MenuBar {
ClideMenuItem { ClideMenuItem {
action: Action { action: Action {
id: actionUndo id: actionCloseTab
text: qsTr("&Undo") text: qsTr("&Close Tab")
}
}
ClideMenuItem {
action: Action {
id: actionRedo
text: qsTr("&Redo")
}
}
ClideMenuItem {
action: Action {
id: actionCut
text: qsTr("&Cut")
}
}
ClideMenuItem {
action: Action {
id: actionCopy
text: qsTr("&Copy")
}
}
ClideMenuItem {
action: Action {
id: actionPaste
text: qsTr("&Paste")
} }
} }
} }
@@ -132,13 +95,6 @@ MenuBar {
ClideMenu { ClideMenu {
title: qsTr("&Help") title: qsTr("&Help")
ClideMenuItem {
action: Action {
id: actionDocumentation
text: qsTr("&Documentation")
}
}
ClideMenuItem { ClideMenuItem {
action: Action { action: Action {
id: actionAbout id: actionAbout

View File

@@ -3,8 +3,6 @@
<file alias="kilroy.png">resources/images/kilroy-256.png</file> <file alias="kilroy.png">resources/images/kilroy-256.png</file>
</qresource> </qresource>
<qresource prefix="/fonts"> <qresource prefix="/fonts">
<file alias="saucecodepro.ttf">resources/SauceCodeProNerdFont-Black.ttf</file>
<file alias="saucecodepro-light.ttf">resources/SauceCodeProNerdFont-Light.ttf</file>
<file alias="saucecodepro-xlight.ttf">resources/SauceCodeProNerdFont-ExtraLight.ttf</file> <file alias="saucecodepro-xlight.ttf">resources/SauceCodeProNerdFont-ExtraLight.ttf</file>
</qresource> </qresource>
</RCC> </RCC>

View File

@@ -4,13 +4,9 @@
use cxx_qt_lib::{QModelIndex, QString}; use cxx_qt_lib::{QModelIndex, QString};
use dirs; use dirs;
use libclide::error;
use libclide::theme::highlighter::Highlighter;
use std::fs; use std::fs;
use std::path::Path;
use syntect::easy::HighlightLines;
use syntect::highlighting::ThemeSet;
use syntect::html::{IncludeBackground, append_highlighted_html_for_styled_line};
use syntect::parsing::SyntaxSet;
use syntect::util::LinesWithEndings;
#[cxx_qt::bridge] #[cxx_qt::bridge]
pub mod qobject { pub mod qobject {
@@ -70,49 +66,15 @@ impl Default for FileSystemImpl {
impl qobject::FileSystem { impl qobject::FileSystem {
fn read_file(&self, path: &QString) -> QString { fn read_file(&self, path: &QString) -> QString {
if path.is_empty() { let text = libclide::fs::read_file(path.to_string()).unwrap_or_else(|_| {
return QString::default(); error!(target: "qobject::FileSystem", "Failed to read file at path {path:?}");
} String::default()
let meta = fs::metadata(path.to_string()) });
.unwrap_or_else(|_| panic!("Failed to get file metadata {path:?}")); if let Ok(highlighter) = Highlighter::new(path.to_string()) {
if !meta.is_file() { QString::from(highlighter.syntax_highlight_text(text))
libclide::warn!(target:"FileSystem", "Attempted to open file {path:?} that is not a valid file");
return QString::default();
}
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);
// If you care about the background, see `start_highlighted_html_snippet(theme);`.
let mut output = String::from("<pre>\n");
for line in LinesWithEndings::from(lines.as_str()) {
let regions = highlighter
.highlight_line(line, &ss)
.expect("Failed to highlight");
append_highlighted_html_for_styled_line(
&regions[..],
IncludeBackground::No,
&mut output,
)
.expect("Failed to insert highlighted html");
}
output.push_str("</pre>\n");
QString::from(output)
} else { } else {
QString::default() error!(target: "qobject::FileSystem", "Failed to create highlighter");
QString::from(text)
} }
} }

View File

@@ -13,9 +13,9 @@ mod menu_bar;
use crate::AppContext; use crate::AppContext;
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use libclide::log::Loggable;
use log::LevelFilter; use log::LevelFilter;
use ratatui::Terminal; use ratatui::Terminal;
use libclide::log::Loggable;
use ratatui::backend::CrosstermBackend; use ratatui::backend::CrosstermBackend;
use ratatui::crossterm::event::{ use ratatui::crossterm::event::{
DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture, DisableBracketedPaste, DisableMouseCapture, EnableBracketedPaste, EnableMouseCapture,

View File

@@ -7,8 +7,8 @@
use crate::tui::component::Focus::Inactive; use crate::tui::component::Focus::Inactive;
use Focus::Active; use Focus::Active;
use anyhow::Result; use anyhow::Result;
use libclide::theme::colors::Colors;
use libclide::log::Loggable; use libclide::log::Loggable;
use libclide::theme::colors::Colors;
use ratatui::crossterm::event::{Event, KeyEvent, MouseEvent}; use ratatui::crossterm::event::{Event, KeyEvent, MouseEvent};
use ratatui::style::Color; use ratatui::style::Color;

View File

@@ -21,6 +21,7 @@ use strum::{EnumIter, FromRepr, IntoEnumIterator};
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr, EnumIter)] #[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr, EnumIter)]
enum MenuBarItem { enum MenuBarItem {
File, File,
Edit,
View, View,
Help, Help,
} }
@@ -68,12 +69,14 @@ impl MenuBarItem {
MenuBarItem::File => "File", MenuBarItem::File => "File",
MenuBarItem::View => "View", MenuBarItem::View => "View",
MenuBarItem::Help => "Help", MenuBarItem::Help => "Help",
MenuBarItem::Edit => "Edit",
} }
} }
pub fn options(&self) -> &[MenuBarItemOption] { pub fn options(&self) -> &[MenuBarItemOption] {
match self { match self {
MenuBarItem::File => &[Save, CloseTab, Reload, Exit], MenuBarItem::File => &[Save, Reload, Exit],
MenuBarItem::Edit => &[CloseTab],
MenuBarItem::View => &[ShowHideExplorer, ShowHideLogger], MenuBarItem::View => &[ShowHideExplorer, ShowHideLogger],
MenuBarItem::Help => &[About], MenuBarItem::Help => &[About],
} }