Compare commits
8 Commits
ed31ba7600
...
shared-mod
| Author | SHA1 | Date | |
|---|---|---|---|
| 514c55367e | |||
| 22dc9b0165 | |||
| 234e6a168e | |||
| ae5d1c7e22 | |||
| a92844b8e5 | |||
| 7a0b2d197f | |||
| a9564493cb | |||
| dd11aeca54 |
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1174,6 +1174,7 @@ dependencies = [
|
|||||||
"libclide-macros",
|
"libclide-macros",
|
||||||
"log",
|
"log",
|
||||||
"strum",
|
"strum",
|
||||||
|
"syntect",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
# CLIDE
|
# CLIDE
|
||||||
|
[](https://github.com/shaunrd0/clide)
|
||||||
|
[](https://gitlab.com/shaunrd0/clide)
|
||||||
|
[](https://git.shaunreed.com/shaunrd0/clide)
|
||||||
|
|
||||||
[](https://github.com/shaunrd0/clide/actions/workflows/check.yaml)
|
[](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]
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
@@ -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:?}"))
|
||||||
|
}
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
66
libclide/src/theme/highlighter.rs
Normal file
66
libclide/src/theme/highlighter.rs
Normal 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(
|
||||||
|
®ions[..],
|
||||||
|
IncludeBackground::No,
|
||||||
|
&mut output,
|
||||||
|
)
|
||||||
|
.expect("Failed to insert highlighted html");
|
||||||
|
}
|
||||||
|
output.push_str("</pre>\n");
|
||||||
|
output
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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>
|
||||||
Binary file not shown.
Binary file not shown.
@@ -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(
|
|
||||||
®ions[..],
|
|
||||||
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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],
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user