19 Commits

Author SHA1 Message Date
fed1d43ac9 Loggable trait and derive macro. 2026-02-22 21:56:32 -05:00
b0ed2e6e1f Move icon helper. 2026-02-22 21:56:32 -05:00
2e67c01377 Add icons to TUI. 2026-02-22 21:56:32 -05:00
5b356781ba Fix comments. 2026-02-22 21:56:32 -05:00
be969ef335 Add macros for logging. 2026-02-22 21:56:32 -05:00
c21ede292c Share colors for GUI and TUI.
The shim is there but it isn't used yet.
2026-02-22 21:56:32 -05:00
11fd130171 Update CI badge. 2026-02-22 21:56:32 -05:00
65242c2ea9 Add workspace dependencies. 2026-02-22 21:56:32 -05:00
73c467e19e Renames. 2026-02-22 21:56:32 -05:00
8fd0bb48de Set up workspace, add formatting CI. 2026-02-22 21:56:32 -05:00
d5671a5590 Fix clide lints. 2026-02-22 21:56:32 -05:00
7490e36a2f Fix libclide lints. 2026-02-22 21:56:32 -05:00
0e8910807e Add CI. 2026-02-22 21:56:19 -05:00
289f94300c Move EntryMeta to libclide. 2026-02-21 14:59:48 -05:00
d95aa680ff Add missing headers. 2026-02-21 14:56:28 -05:00
1119b3db9b Add libclide. 2026-02-21 14:41:17 -05:00
7ad25af13d Ignore .qmlls.ini. 2026-02-21 14:40:22 -05:00
7d4f23d82a Fix bug showing project name in explorer. 2026-02-21 14:24:44 -05:00
b4e14f7f27 Fix bug preventing TUI editors from opening.
Also fix bugs building file tree for paths including `../`.
2026-02-21 14:21:28 -05:00
23 changed files with 120 additions and 123 deletions

1
Cargo.lock generated
View File

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

View File

@@ -1,7 +1,4 @@
# 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)
@@ -41,7 +38,7 @@ export QMAKE=$HOME/Qt/6.7.3/gcc_64/bin/qmake6
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 convenience
Though environment variables set using `export` will take precedence, these can also be set in [.cargo/config.toml](./.cargo/config.toml) for conveinence
```toml
[env]

View File

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

View File

@@ -4,21 +4,4 @@
pub mod entry_meta;
use anyhow::Context;
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

@@ -4,7 +4,6 @@
pub mod macros;
pub use libclide_macros::Loggable;
pub trait Loggable {
const ID: &'static str;
}

View File

@@ -11,7 +11,7 @@
//!
//! The Loggable trait can be implemented to automatically associate log messages with a struct.
//! ```
//! use libclide::log::Loggable;
//! use libclide_macros::Loggable;
//!
//! #[derive(Loggable)]
//! struct MyStruct;
@@ -35,7 +35,7 @@ macro_rules! info {
log::info!(target: $target, $($arg)+)
});
($($arg:tt)+) => (log::info!(target: Self::ID, $($arg)+))
($($arg:tt)+) => (log::info!(target: <Self as libclide::log::Loggable>::ID, $($arg)+))
}
#[macro_export]
@@ -44,7 +44,7 @@ macro_rules! debug {
log::debug!(target: $target, $($arg)+)
});
($($arg:tt)+) => (log::debug!(target: Self::ID, $($arg)+))
($($arg:tt)+) => (log::debug!(target: <Self as libclide::log::Loggable>::ID, $($arg)+))
}
#[macro_export]
@@ -53,7 +53,7 @@ macro_rules! warn {
log::warn!(target: $target, $($arg)+)
});
($($arg:tt)+) => (log::warn!(target: Self::ID, $($arg)+))
($($arg:tt)+) => (log::warn!(target: <Self as libclide::log::Loggable>::ID, $($arg)+))
}
#[macro_export]
@@ -62,7 +62,7 @@ macro_rules! error {
log::error!(target: $target, $($arg)+)
});
($($arg:tt)+) => (log::error!(target: Self::ID, $($arg)+))
($($arg:tt)+) => (log::error!(target: <Self as libclide::log::Loggable>::ID, $($arg)+))
}
#[macro_export]
@@ -71,5 +71,5 @@ macro_rules! trace {
log::trace!(target: $target, $($arg)+)
});
($($arg:tt)+) => (log::trace!(target: Self::ID, $($arg)+))
($($arg:tt)+) => (log::trace!(target: <Self as libclide::log::Loggable>::ID, $($arg)+))
}

View File

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

View File

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

View File

@@ -20,16 +20,25 @@ MenuBar {
ClideMenuItem {
action: Action {
id: actionSave
id: actionNewProject
text: qsTr("&Save")
text: qsTr("&New Project...")
}
}
ClideMenuItem {
action: Action {
id: actionReload
id: actionOpen
text: qsTr("&Reload")
text: qsTr("&Open...")
}
onTriggered: FileSystem.setDirectory(FileSystem.filePath)
}
ClideMenuItem {
action: Action {
id: actionSave
text: qsTr("&Save")
}
}
MenuSeparator {
@@ -58,9 +67,37 @@ MenuBar {
ClideMenuItem {
action: Action {
id: actionCloseTab
id: actionUndo
text: qsTr("&Close Tab")
text: qsTr("&Undo")
}
}
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")
}
}
}
@@ -95,6 +132,13 @@ MenuBar {
ClideMenu {
title: qsTr("&Help")
ClideMenuItem {
action: Action {
id: actionDocumentation
text: qsTr("&Documentation")
}
}
ClideMenuItem {
action: Action {
id: actionAbout

View File

@@ -3,6 +3,8 @@
<file alias="kilroy.png">resources/images/kilroy-256.png</file>
</qresource>
<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>
</qresource>
</RCC>

Binary file not shown.

Binary file not shown.

View File

@@ -4,9 +4,13 @@
use cxx_qt_lib::{QModelIndex, QString};
use dirs;
use libclide::error;
use libclide::theme::highlighter::Highlighter;
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]
pub mod qobject {
@@ -66,15 +70,49 @@ impl Default for FileSystemImpl {
impl qobject::FileSystem {
fn read_file(&self, path: &QString) -> QString {
let text = libclide::fs::read_file(path.to_string()).unwrap_or_else(|_| {
error!(target: "qobject::FileSystem", "Failed to read file at path {path:?}");
String::default()
});
if let Ok(highlighter) = Highlighter::new(path.to_string()) {
QString::from(highlighter.syntax_highlight_text(text))
if path.is_empty() {
return QString::default();
}
let meta = fs::metadata(path.to_string())
.unwrap_or_else(|_| panic!("Failed to get file metadata {path:?}"));
if !meta.is_file() {
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 {
error!(target: "qobject::FileSystem", "Failed to create highlighter");
QString::from(text)
QString::default()
}
}

View File

@@ -13,7 +13,7 @@ mod menu_bar;
use crate::AppContext;
use anyhow::{Context, Result};
use libclide::log::Loggable;
use libclide_macros::Loggable;
use log::LevelFilter;
use ratatui::Terminal;
use ratatui::backend::CrosstermBackend;

View File

@@ -2,7 +2,7 @@
//
// SPDX-License-Identifier: GNU General Public License v3.0 or later
use libclide::log::Loggable;
use libclide_macros::Loggable;
use ratatui::buffer::Buffer;
use ratatui::layout::{Constraint, Direction, Layout, Rect};
use ratatui::text::{Line, Span};

View File

@@ -10,6 +10,7 @@ use crate::tui::logger::Logger;
use crate::tui::menu_bar::MenuBar;
use anyhow::{Context, Result};
use libclide::log::Loggable;
use libclide_macros::Loggable;
use ratatui::DefaultTerminal;
use ratatui::buffer::Buffer;
use ratatui::crossterm::event;
@@ -42,7 +43,7 @@ pub struct App<'a> {
impl<'a> App<'a> {
pub fn new(root_path: PathBuf) -> Result<Self> {
libclide::trace!("Building {}", Self::ID);
libclide::trace!("Building {}", <Self as Loggable>::ID);
let app = Self {
editor_tab: EditorTab::new(),
explorer: Explorer::new(&root_path)?,

View File

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

View File

@@ -8,6 +8,7 @@ use edtui::{
EditorEventHandler, EditorState, EditorTheme, EditorView, LineNumbers, Lines, SyntaxHighlighter,
};
use libclide::log::Loggable;
use libclide_macros::Loggable;
use ratatui::buffer::Buffer;
use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
use ratatui::layout::{Alignment, Rect};

View File

@@ -6,6 +6,7 @@ use crate::tui::component::{Action, Component, Focus, FocusState};
use crate::tui::editor::Editor;
use anyhow::{Context, Result, anyhow};
use libclide::log::Loggable;
use libclide_macros::Loggable;
use ratatui::buffer::Buffer;
use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
use ratatui::layout::Rect;

View File

@@ -6,6 +6,7 @@ use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState
use anyhow::{Context, Result, bail};
use libclide::fs::entry_meta::EntryMeta;
use libclide::log::Loggable;
use libclide_macros::Loggable;
use ratatui::buffer::Buffer;
use ratatui::crossterm::event::{Event, KeyCode, KeyEvent, MouseEvent, MouseEventKind};
use ratatui::layout::{Alignment, Position, Rect};

View File

@@ -4,6 +4,7 @@
use crate::tui::component::{Action, Component, ComponentState, Focus, FocusState};
use libclide::log::Loggable;
use libclide_macros::Loggable;
use log::LevelFilter;
use ratatui::buffer::Buffer;
use ratatui::crossterm::event::{Event, KeyCode, KeyEvent};

View File

@@ -7,7 +7,7 @@ use crate::tui::menu_bar::MenuBarItemOption::{
About, CloseTab, Exit, Reload, Save, ShowHideExplorer, ShowHideLogger,
};
use anyhow::Context;
use libclide::log::Loggable;
use libclide_macros::Loggable;
use ratatui::buffer::Buffer;
use ratatui::crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::Rect;
@@ -21,7 +21,6 @@ use strum::{EnumIter, FromRepr, IntoEnumIterator};
#[derive(Debug, Clone, Copy, PartialEq, Eq, FromRepr, EnumIter)]
enum MenuBarItem {
File,
Edit,
View,
Help,
}
@@ -69,14 +68,12 @@ impl MenuBarItem {
MenuBarItem::File => "File",
MenuBarItem::View => "View",
MenuBarItem::Help => "Help",
MenuBarItem::Edit => "Edit",
}
}
pub fn options(&self) -> &[MenuBarItemOption] {
match self {
MenuBarItem::File => &[Save, Reload, Exit],
MenuBarItem::Edit => &[CloseTab],
MenuBarItem::File => &[Save, CloseTab, Reload, Exit],
MenuBarItem::View => &[ShowHideExplorer, ShowHideLogger],
MenuBarItem::Help => &[About],
}