Skip to content

Commit

Permalink
Merge pull request #167 from curlpipe/dev
Browse files Browse the repository at this point in the history
0.6.5
  • Loading branch information
curlpipe authored Oct 7, 2024
2 parents 20cf565 + d0d1f52 commit d5b4bf8
Show file tree
Hide file tree
Showing 20 changed files with 432 additions and 150 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ exclude = ["cactus"]

[package]
name = "ox"
version = "0.6.4"
version = "0.6.5"
edition = "2021"
authors = ["Curlpipe <[email protected]>"]
description = "A Rust powered text editor."
Expand Down
34 changes: 20 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ If you're looking for a text editor that...
- search and replace
- line numbers
- opening multiple files
- full mouse cursor interaction

...then Ox is right up your street

Expand Down Expand Up @@ -62,6 +63,8 @@ It is mainly used on linux systems, but macOS and Windows users (via WSL) are fr
- :mag: Search and replace text
- :file_folder: Opening multiple files at once
- :eye: UI that shows you the state of the editor and file
- :computer_mouse: You can move the cursor and select text with your mouse
- :writing_hand: Convenient shortcuts when writing code

### Robustness

Expand All @@ -70,7 +73,7 @@ It is mainly used on linux systems, but macOS and Windows users (via WSL) are fr

## Installation

> :warning: Huge Warning: A lot of these (except manual) are quite out of date, it is quite a huge task having to push to all these sources each time I update and I'd rather focus on creating high quality updates
> :warning: Huge Warning: A lot of these (except manual, AUR and homebrew) are quite out of date, it is quite a huge task having to push to all these sources each time I update and I'd rather focus on creating high-quality updates
### Prerequisites

Expand All @@ -87,7 +90,7 @@ If you are not using Arch, you can easily set it up on other distros by running
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
/usr/bin/rustup toolchain install stable
```
You must have `curl` installed in order to run this command.
You must have `curl` installed to run this command.

### Installation Routes

Expand Down Expand Up @@ -116,11 +119,11 @@ sudo dnf copr enable atim/ox -y
sudo dnf install ox
```

You can also find an rpm file in the releases page
You can also find an RPM file in the releases page

#### Debian

You can find a deb file in the releases page, which can be installed via dpkg:
You can find a deb file on the releases page, which can be installed via dpkg:

```sh
dpkg -i ox_version.deb
Expand Down Expand Up @@ -185,7 +188,7 @@ If you want to move tabs and look at other files that are open, you can use <kbd

### Editing Files

There are no modes in Ox, so you can just type straight into an open file, just as you would nano, or windows notepad.
There are no modes in Ox, so you can just type straight into an open file, just as you would Nano, or Windows notepad.

You can move the cursor around the file using the standard arrow keys.

Expand All @@ -207,10 +210,10 @@ We've covered most keyboard shortcuts, but there are some other features you mig

| Keybinding | What it does |
| ------------ | ------------ |
| `Ctrl + F` | Searches the document for a search query. Allows pressing of <kbd>←</kbd> to move the cursor to the previous occurance fof the query and <kbd>→</kbd> to move to the next occurance of the query. Press <kbd>Return</kbd> or <kbd>Esc</kbd> to leave the search. Note: you can use regular expressions for search queries. |
| `Ctrl + F` | Searches the document for a search query. Allows pressing of <kbd>←</kbd> to move the cursor to the previous occurrence of the query and <kbd>→</kbd> to move to the next occurrence of the query. Press <kbd>Return</kbd> or <kbd>Esc</kbd> to leave the search. Note: you can use regular expressions for search queries. |
| `Ctrl + Z` | Undoes your last action. The changes are committed to the undo stack every time you press the space bar, create / destroy a new line and when there is no activity after a certain period of time which can be used to capture points where you pause for thought or grab a coffee etc... |
| `Ctrl + Y` | Redoes your last action. The changes are committed to the undo stack every time you press the space bar, create / destroy a new line and when there is no activity after a certain period of time which can be used to capture points where you pause for thought or grab a coffee etc... |
| `Ctrl + R` | Allows replacing of occurances in the document. Uses the same keybindings as the search feature: <kbd>←</kbd> to move the cursor to the previous occurance of the query and <kbd>→</kbd> to move to the next occurance of the query. You can also press <kbd>Return</kbd> to carry out the replace action. To exit replace mode once you're finished, you can press <kbd>Esc</kbd>. You can also use <kbd>Tab</kbd> to replace every instance in the document at once. Note: you can use regular expressions for search queries. |
| `Ctrl + R` | Allows replacing of occurrences in the document. Uses the same keybindings as the search feature: <kbd>←</kbd> to move the cursor to the previous occurrence of the query and <kbd>→</kbd> to move to the next occurrence of the query. You can also press <kbd>Return</kbd> to carry out the replace action. To exit replace mode once you're finished, you can press <kbd>Esc</kbd>. You can also use <kbd>Tab</kbd> to replace every instance in the document at once. Note: you can use regular expressions for search queries. |
| `Ctrl + K` | Opens the command line. |
| `Ctrl + W` | Shortcut to delete a whole word. |
| `Alt + Up` | Move the current line up. |
Expand All @@ -222,29 +225,32 @@ We've covered most keyboard shortcuts, but there are some other features you mig

### Configuration

Ox features a configuration system that allows modification and personalization of the editor.
Ox features a configuration system that allows the editor to be modified and personalised.

By default, Ox will look for a file here: `$XDG_CONFIG_HOME/.oxrc` or `~/.oxrc`.

On Windows, Ox will try to look here `C:/Users/user/ox/.oxrc` (where `user` is the user name of your account)

Ox's configuration language is [Lua](https://lua.org).

There is a default config in the `config` folder in the repository for you to refer to. You can either download it and place it in the default config directory or create your own using the example ones as a reference.
For reference, there is a default config in the `config` folder in the repository. You can either download it and place it in the default config directory or create your own using the example ones as a reference.

If you don't have a config file, or don't want to mess around with it, don't worry, Ox has default settings it will use.
If you don't have a config file or don't want to mess around with it, don't worry, Ox has default settings it will use.

## Documentation

If you've been through the quick start guide above, but are looking for more detail, you can find in-depth documentation in the [wiki page](https://github.com/curlpipe/ox/wiki/)
If you've been through the quick start guide above, but are looking for more detail, you can find in-depth documentation on the [wiki page](https://github.com/curlpipe/ox/wiki/)

This will take you step-by-step in great detail through 5 different stages:

1. **Installation** - advice and how-tos on installation
2. **Starting** - using the command line interface
3. **Using** - editing a document and controlling the editor
4. **Configuring** - writing plug-ins, changing layout, adding to and changing the syntax highlighting
5. **Next Steps** - planned features and contributing
4. **Configuring** - writing plug-ins, changing the layout, adding to and changing the syntax highlighting
5. **Plugins** - installing or uninstalling community plug-ins and writing or distributing your own plug-ins
6. **Roadmap** - planned features

Hopefully it contains everything you need to take you from beginner to a power user.
Hopefully, it contains everything you need to take you from a beginner to a power user.

## License

Expand Down
22 changes: 21 additions & 1 deletion kaolinite/src/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ use crate::event::{Error, Event, Result, Status, UndoMgmt};
use crate::map::{form_map, CharMap};
use crate::searching::{Match, Searcher};
use crate::utils::{
get_range, tab_boundaries_backward, tab_boundaries_forward, trim, width, Loc, Size,
get_range, modeline, tab_boundaries_backward, tab_boundaries_forward, trim, width, Loc, Size,
};
use ropey::Rope;
use std::fs::File;
use std::io::{BufReader, BufWriter};
use std::ops::{Range, RangeBounds};
use std::path::Path;

/// A document info struct to store information about the file it represents
#[derive(Clone, PartialEq, Eq, Debug)]
Expand Down Expand Up @@ -129,6 +130,24 @@ impl Document {
Ok(this)
}

/// Determine the file type of this file (represented by an extension)
#[allow(clippy::missing_panics_doc)]
#[must_use]
pub fn get_file_type(&self) -> Option<&str> {
let mut result = None;
// Try to use modeline first off
if let Some(first_line) = self.lines.first() {
result = modeline(first_line);
}
// If an extension is available, use that instead
if let Some(file_name) = &self.file_name {
if let Some(extension) = Path::new(file_name).extension() {
result = extension.to_str();
}
}
result
}

/// Sets the tab display width measured in spaces, default being 4
pub fn set_tab_width(&mut self, tab_width: usize) {
self.tab_width = tab_width;
Expand Down Expand Up @@ -172,6 +191,7 @@ impl Document {
/// Will return an error if the event was unable to be completed.
pub fn exe(&mut self, ev: Event) -> Result<()> {
if !self.info.read_only {
self.undo_mgmt.last_event = ev.clone();
self.undo_mgmt.set_dirty();
self.forth(ev)?;
}
Expand Down
30 changes: 29 additions & 1 deletion kaolinite/src/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,20 @@ impl Event {
Event::InsertLine(loc, _) | Event::DeleteLine(loc, _) => Loc { x: 0, y: loc },
}
}

/// Work out if the event is of the same type
#[must_use]
pub fn same_type(&self, ev: &Self) -> bool {
matches!(
(self, ev),
(&Event::Insert(_, _), &Event::Insert(_, _))
| (&Event::Delete(_, _), &Event::Delete(_, _))
| (&Event::InsertLine(_, _), &Event::InsertLine(_, _))
| (&Event::DeleteLine(_, _), &Event::DeleteLine(_, _))
| (&Event::SplitDown(_), &Event::SplitDown(_))
| (&Event::SpliceUp(_), &Event::SpliceUp(_))
)
}
}

/// Represents various statuses of functions
Expand Down Expand Up @@ -82,7 +96,7 @@ quick_error! {
}

/// For managing events for purposes of undo and redo
#[derive(Default, Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct UndoMgmt {
/// Whether the file touched since the latest commit
pub is_dirty: bool,
Expand All @@ -92,6 +106,20 @@ pub struct UndoMgmt {
pub redo: Vec<Snapshot>,
/// Store where the file on the disk is currently at
pub on_disk: usize,
/// Store the last event to occur (so that we can see if there is a change)
pub last_event: Event,
}

impl Default for UndoMgmt {
fn default() -> Self {
Self {
is_dirty: false,
undo: vec![],
redo: vec![],
on_disk: 0,
last_event: Event::Insert(Loc { x: 0, y: 0 }, " ".to_string()),
}
}
}

impl Document {
Expand Down
81 changes: 81 additions & 0 deletions kaolinite/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,52 @@ pub fn get_file_ext(path: &str) -> Option<String> {
.map(std::string::ToString::to_string)
}

/// Will get the current working directory
#[must_use]
#[cfg(not(tarpaulin_include))]
pub fn get_cwd() -> Option<String> {
Some(std::env::current_dir().ok()?.display().to_string())
}

/// Will list a directory
#[must_use]
#[cfg(not(tarpaulin_include))]
pub fn list_dir(path: &str) -> Option<Vec<String>> {
Some(
std::fs::read_dir(path)
.ok()?
.filter_map(std::result::Result::ok)
.filter_map(|e| e.path().to_str().map(std::string::ToString::to_string))
.collect(),
)
}

/// Get the parent directory
#[must_use]
#[cfg(not(tarpaulin_include))]
pub fn get_parent(path: &str) -> Option<String> {
Path::new(path).parent().map(|p| p.display().to_string())
}

/// Determine if something is a directory or a file
#[must_use]
#[cfg(not(tarpaulin_include))]
pub fn file_or_dir(path: &str) -> &str {
let path = Path::new(path);
let metadata = std::fs::metadata(path);
if let Ok(metadata) = metadata {
if metadata.is_file() {
"file"
} else if metadata.is_dir() {
"directory"
} else {
"neither"
}
} else {
"neither"
}
}

/// Determine the filetype from the extension
#[allow(clippy::too_many_lines)]
#[must_use]
Expand Down Expand Up @@ -374,3 +420,38 @@ pub fn icon(language: &str) -> String {
}
.to_string()
}

/// Determine the file extension based off the magic modeline (if present)
#[must_use]
pub fn modeline(first_line: &str) -> Option<&str> {
// Create a regex to handle leading/trailing whitespaces and spaces between '#!' and path
let re = regex!(r"^#!\s*/\s*(\S+)(\s+\S+)?");

// Match the cleaned-up shebang
if let Some(caps) = re.captures(first_line) {
let shebang = caps
.get(0)
.map(|m| m.as_str().replace("#! ", "#!"))
.unwrap_or_default();
match shebang.as_str() {
"#!/bin/sh" | "#!/usr/bin/env bash" | "#!/bin/bash" => Some("sh"),
"#!/usr/bin/python"
| "#!/usr/bin/python3"
| "#!/usr/bin/env python"
| "#!/usr/bin/env python3" => Some("py"),
"#!/usr/bin/env ruby" | "#!/usr/bin/ruby" => Some("rb"),
"#!/usr/bin/perl" | "#!/usr/bin/env perl" => Some("pl"),
"#!/usr/bin/env node" | "#!/usr/bin/node" => Some("js"),
"#!/usr/bin/env lua" | "#!/usr/bin/lua" => Some("lua"),
"#!/usr/bin/env php" | "#!/usr/bin/php" => Some("php"),
"#!/usr/bin/env rust" => Some("rs"),
"#!/usr/bin/env tcl" => Some("tcl"),
"#!/bin/awk" | "#!/usr/bin/env awk" => Some("awk"),
"#!/bin/sed" | "#!/usr/bin/env sed" => Some("sed"),
"#!/usr/bin/env fish" => Some("fish"),
_ => None,
}
} else {
None
}
}
15 changes: 13 additions & 2 deletions kaolinite/tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ fn filetypes() {
assert_eq!(filetype("abcd"), None);
assert_eq!(icon("reStructuredText"), st!("󰊄"));
assert_eq!(icon("abcd"), st!("󰈙 "));
assert_eq!(modeline("#!/usr/bin/env ruby"), Some("rb"));
assert_eq!(modeline("#!/usr/bin/python3"), Some("py"));
assert_eq!(modeline("#! /usr/bin/env python3"), Some("py"));
assert_eq!(modeline("#!/usr/bin/env foo"), None);
assert_eq!(modeline("testing"), None);
}

#[test]
Expand Down Expand Up @@ -279,6 +284,10 @@ fn events() {
Event::SplitDown(Loc { x: 0, y: 3 }),
],
);
assert!(Event::Insert(Loc { x: 0, y: 1 }, st!("Test"))
.same_type(&Event::Insert(Loc { x: 2, y: 3 }, st!("334"))));
assert!(!Event::Delete(Loc { x: 0, y: 1 }, st!("Test"))
.same_type(&Event::Insert(Loc { x: 2, y: 3 }, st!("334"))));
}

#[test]
Expand Down Expand Up @@ -618,6 +627,8 @@ fn document_scrolling() {
fn document_utilities() {
let mut doc = Document::open(Size::is(100, 2), "tests/data/big.txt").unwrap();
doc.load_to(1000);
// File type
assert_eq!(doc.get_file_type(), Some("txt"));
// Cursor location
doc.move_to(&Loc { x: 5, y: 5 });
assert_eq!(doc.loc(), Loc { x: 5, y: 5 });
Expand Down Expand Up @@ -807,13 +818,13 @@ fn file_paths() {

#[test]
fn fuzz() {
for _ in 0..20 {
for _ in 0..10 {
println!("--");
let size = Size { w: 10, h: 8 };
let mut doc = Document::open(size, "tests/data/unicode.txt").unwrap();
doc.load_to(100);
println!("{} | {}", doc.loc().x, doc.char_ptr);
for _ in 0..500 {
for _ in 0..300 {
let e = rand::random::<u8>() % 25;
println!("{}", e);
match e {
Expand Down
4 changes: 3 additions & 1 deletion plugins/autoindent.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
--[[
Auto Indent v0.6
Auto Indent v0.7
Helps you when programming by guessing where indentation should go
and then automatically applying these guesses as you program
Expand Down Expand Up @@ -149,8 +149,10 @@ function autoindent:disperse_block()
local indenting_above = autoindent:causes_indent(editor.cursor.y - 1)
local current_dedenting = autoindent:causes_dedent(editor.cursor.y)
if indenting_above and current_dedenting then
local starting_indent = autoindent:get_indent(editor.cursor.y - 1)
local old_cursor = editor.cursor
editor:insert_line()
autoindent:set_indent(editor.cursor.y, starting_indent)
editor:move_to(old_cursor.x, old_cursor.y)
end
end
Expand Down
Loading

0 comments on commit d5b4bf8

Please sign in to comment.