Undo, redo, line numbers
This commit is contained in:
34
README.md
34
README.md
@@ -8,6 +8,8 @@ ve is a vi(m) like editor which runs inside your terminal, using ncurses. At pre
|
|||||||
* Insert mode works in the way you'd expect
|
* Insert mode works in the way you'd expect
|
||||||
* Scroll through large files
|
* Scroll through large files
|
||||||
* Saving and quitting in the same way as Vim
|
* Saving and quitting in the same way as Vim
|
||||||
|
* Delimiter/action-based undo
|
||||||
|
* Line number counter
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
@@ -15,4 +17,36 @@ ve is a vi(m) like editor which runs inside your terminal, using ncurses. At pre
|
|||||||
g++ src/main.cpp -Lncurses -o ve
|
g++ src/main.cpp -Lncurses -o ve
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Keybinds
|
||||||
|
|
||||||
|
### Normal Mode
|
||||||
|
|
||||||
|
**Moving around:** Use `h` to go left, `j` to go up, `k` to go down, and `l` top go right (or arrow keys if you're boring).
|
||||||
|
|
||||||
|
**Enter Insert mode:** Use `i` to enter insert mode
|
||||||
|
|
||||||
|
**Enter Command mode:** Use `:` to enter command mode
|
||||||
|
|
||||||
|
**Undo/Redo:** Press `u` to undo a mistake, and `r` to redo.
|
||||||
|
|
||||||
|
### Insert Mode
|
||||||
|
|
||||||
|
**Typing:** Type things in just like in any other editor.
|
||||||
|
|
||||||
|
**Moving around:** Use the arrow keys
|
||||||
|
|
||||||
|
**Enter Normal mode:** Press `ESC`.
|
||||||
|
|
||||||
|
### Command Mode
|
||||||
|
|
||||||
|
There are currently three commands for command mode:
|
||||||
|
|
||||||
|
`q`: Quits the editor, unless you have unsaved changes.
|
||||||
|
|
||||||
|
`q!`: Force quits the editor.
|
||||||
|
|
||||||
|
`w`: Saves changes to the file. If a filename is specified, changes are saved to that file.
|
||||||
|
|
||||||
|
`wq`: Saves changes to the file, and quits the editor. If a filename is specified, changes are saved to that file.
|
||||||
|
|
||||||
|
Press enter to submit your command.
|
||||||
|
|||||||
110
src/main.cpp
110
src/main.cpp
@@ -3,6 +3,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
enum class mode {
|
enum class mode {
|
||||||
INSERT, COMMAND, NORMAL
|
INSERT, COMMAND, NORMAL
|
||||||
@@ -13,6 +14,27 @@ void quit(int exitcode = 0) {
|
|||||||
exit(exitcode);
|
exit(exitcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct cursor {
|
||||||
|
int x;
|
||||||
|
int y;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UndoState {
|
||||||
|
std::vector<std::string> lines;
|
||||||
|
cursor pos;
|
||||||
|
UndoState(std::vector<std::string> inlines, cursor inpos) : lines(inlines), pos(inpos) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Configuration {
|
||||||
|
std::string tab = " ";
|
||||||
|
std::vector<char> undoDelimiters = {'{', '}', ';', '(', ')', '\n', '='};
|
||||||
|
};
|
||||||
|
|
||||||
|
Configuration conf;
|
||||||
|
|
||||||
|
std::vector<UndoState> undoHistory;
|
||||||
|
int undos = -1;
|
||||||
|
|
||||||
std::vector<std::string> readFile(std::string filename) {
|
std::vector<std::string> readFile(std::string filename) {
|
||||||
std::ifstream file(filename);
|
std::ifstream file(filename);
|
||||||
if (!file) {
|
if (!file) {
|
||||||
@@ -67,6 +89,15 @@ int writeFile(std::string filename, std::vector<std::string> content) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string linenum(int in) {
|
||||||
|
in++;
|
||||||
|
std::string result = std::to_string(in);
|
||||||
|
while (result.size() < 5) {
|
||||||
|
result = " " + result;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
|
||||||
std::string filename;
|
std::string filename;
|
||||||
@@ -90,8 +121,9 @@ int main(int argc, char** argv) {
|
|||||||
keypad(stdscr, TRUE);
|
keypad(stdscr, TRUE);
|
||||||
|
|
||||||
// remember the position of the cursor
|
// remember the position of the cursor
|
||||||
struct {int x = 0; int y = 1;} pos;
|
cursor pos;
|
||||||
pos.x = lines[0].size();
|
pos.x = lines[0].size();
|
||||||
|
pos.y = 1;
|
||||||
|
|
||||||
// set current mode
|
// set current mode
|
||||||
mode currMode = mode::NORMAL;
|
mode currMode = mode::NORMAL;
|
||||||
@@ -118,20 +150,23 @@ int main(int argc, char** argv) {
|
|||||||
// make sure we keep track of whether we've written to the file
|
// make sure we keep track of whether we've written to the file
|
||||||
bool fileChanged = false;
|
bool fileChanged = false;
|
||||||
|
|
||||||
|
// create initial undo history
|
||||||
|
undoHistory.push_back(UndoState(lines, pos));
|
||||||
|
|
||||||
// initial rendering before doing anything
|
// initial rendering before doing anything
|
||||||
// display the current mode at the bottom
|
// display the current mode at the bottom
|
||||||
switch (currMode) {
|
switch (currMode) {
|
||||||
case mode::NORMAL:
|
case mode::NORMAL:
|
||||||
mvprintw(windowSize.y, 0, "NORMAL");
|
mvprintw(windowSize.y, 1, "normal");
|
||||||
break;
|
break;
|
||||||
case mode::COMMAND:
|
case mode::COMMAND:
|
||||||
mvprintw(windowSize.y, 0, "COMMAND");
|
mvprintw(windowSize.y, 1, "command");
|
||||||
break;
|
break;
|
||||||
case mode::INSERT:
|
case mode::INSERT:
|
||||||
mvprintw(windowSize.y, 0, "INSERT");
|
mvprintw(windowSize.y, 1, "insert");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mvprintw(windowSize.y, 0, "Unknown Mode! Press any key to exit");
|
mvprintw(windowSize.y, 1, "Unknown Mode! Press any key to exit");
|
||||||
getch();
|
getch();
|
||||||
quit();
|
quit();
|
||||||
break;
|
break;
|
||||||
@@ -139,11 +174,12 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
// display what's in the buffer
|
// display what's in the buffer
|
||||||
for (size_t i = viewpoint.top; i < lines.size() && i < viewpoint.bottom && (i - viewpoint.top) < windowSize.y; i++) {
|
for (size_t i = viewpoint.top; i < lines.size() && i < viewpoint.bottom && (i - viewpoint.top) < windowSize.y; i++) {
|
||||||
mvprintw((i - viewpoint.top) + 1, 0, "%s", lines[i].c_str());
|
mvprintw((i - viewpoint.top) + 1, 6, "%s", lines[i].c_str());
|
||||||
|
mvprintw((i - viewpoint.top) + 1, 0, "%s", linenum(i).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// go to where our position is
|
// go to where our position is
|
||||||
move(pos.y, pos.x);
|
move(pos.y, pos.x + 6);
|
||||||
|
|
||||||
// put what's in the buffer on screen
|
// put what's in the buffer on screen
|
||||||
refresh();
|
refresh();
|
||||||
@@ -165,13 +201,38 @@ int main(int argc, char** argv) {
|
|||||||
case ':':
|
case ':':
|
||||||
currMode = mode::COMMAND;
|
currMode = mode::COMMAND;
|
||||||
break;
|
break;
|
||||||
|
case 'u':
|
||||||
|
if (undos <= -1) {
|
||||||
|
undos = undoHistory.size() - 1;
|
||||||
|
} else {
|
||||||
|
undos --;
|
||||||
|
}
|
||||||
|
if (undos < 0) {
|
||||||
|
undos = 0;
|
||||||
|
statusMessage = "No changes to undo";
|
||||||
|
statusCode = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
lines = undoHistory[undos].lines;
|
||||||
|
pos = undoHistory[undos].pos;
|
||||||
|
break;
|
||||||
|
case 'r':
|
||||||
|
if (undos + 1 >= undoHistory.size()) {
|
||||||
|
statusMessage = "No changes to redo";
|
||||||
|
statusCode = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
undos ++;
|
||||||
|
lines = undoHistory[undos].lines;
|
||||||
|
pos = undoHistory[undos].pos;
|
||||||
|
break;
|
||||||
case 'h':
|
case 'h':
|
||||||
case KEY_LEFT:
|
case KEY_LEFT:
|
||||||
if (pos.x > 0) {
|
if (pos.x > 0) {
|
||||||
pos.x--;
|
pos.x--;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'j':
|
case 'k':
|
||||||
case KEY_DOWN:
|
case KEY_DOWN:
|
||||||
if (pos.y < lines.size()) {
|
if (pos.y < lines.size()) {
|
||||||
pos.y++;
|
pos.y++;
|
||||||
@@ -190,7 +251,7 @@ int main(int argc, char** argv) {
|
|||||||
pos.x++;
|
pos.x++;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'k':
|
case 'j':
|
||||||
case KEY_UP:
|
case KEY_UP:
|
||||||
if (pos.y > 1) {
|
if (pos.y > 1) {
|
||||||
pos.y--;
|
pos.y--;
|
||||||
@@ -215,6 +276,8 @@ int main(int argc, char** argv) {
|
|||||||
// escape has been pressed (code 27)
|
// escape has been pressed (code 27)
|
||||||
case 27:
|
case 27:
|
||||||
currMode = mode::NORMAL;
|
currMode = mode::NORMAL;
|
||||||
|
undoHistory.push_back(UndoState(lines, pos));
|
||||||
|
undos = -1;
|
||||||
break;
|
break;
|
||||||
// backspace has been pressed
|
// backspace has been pressed
|
||||||
// boy there are a lot of ways to say backspace
|
// boy there are a lot of ways to say backspace
|
||||||
@@ -310,6 +373,11 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
pos.x++;
|
pos.x++;
|
||||||
if (!fileChanged) fileChanged = true;
|
if (!fileChanged) fileChanged = true;
|
||||||
|
|
||||||
|
// Do undo
|
||||||
|
if (std::find(conf.undoDelimiters.begin(), conf.undoDelimiters.end(), static_cast<char>(pressed)) != conf.undoDelimiters.end()) {
|
||||||
|
undoHistory.push_back(UndoState(lines, pos));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -362,11 +430,11 @@ int main(int argc, char** argv) {
|
|||||||
|
|
||||||
if (commandbuf.substr(0, 2) == "wq") {
|
if (commandbuf.substr(0, 2) == "wq") {
|
||||||
if (filename.empty()) {
|
if (filename.empty()) {
|
||||||
if (commandbuf.size() > 1) {
|
if (commandbuf.size() > 2) {
|
||||||
if (commandbuf[1] == ' ') {
|
if (commandbuf[2] == ' ') {
|
||||||
filename = commandbuf.substr(2);
|
filename = commandbuf.substr(3);
|
||||||
} else {
|
} else {
|
||||||
filename = commandbuf.substr(1);
|
filename = commandbuf.substr(2);
|
||||||
}
|
}
|
||||||
writeFile(filename, lines);
|
writeFile(filename, lines);
|
||||||
quit();
|
quit();
|
||||||
@@ -387,6 +455,7 @@ int main(int argc, char** argv) {
|
|||||||
default:
|
default:
|
||||||
// add to the command buffer
|
// add to the command buffer
|
||||||
commandbuf += pressed;
|
commandbuf += pressed;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -394,25 +463,26 @@ int main(int argc, char** argv) {
|
|||||||
// erase our buffer ready to rerender
|
// erase our buffer ready to rerender
|
||||||
erase();
|
erase();
|
||||||
|
|
||||||
// display what's in the buffer
|
// display what's in the buffer as well as the line counter
|
||||||
for (size_t i = viewpoint.top; i < lines.size() && i < viewpoint.bottom && (i - viewpoint.top) < windowSize.y; i++) {
|
for (size_t i = viewpoint.top; i < lines.size() && i < viewpoint.bottom && (i - viewpoint.top) < windowSize.y; i++) {
|
||||||
mvprintw((i - viewpoint.top) + 1, 0, "%s", lines[i].c_str());
|
mvprintw((i - viewpoint.top) + 1, 6, "%s", lines[i].c_str());
|
||||||
|
mvprintw((i - viewpoint.top) + 1, 0, "%s", linenum(i).c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// display the current mode at the bottom
|
// display the current mode at the bottom
|
||||||
switch (currMode) {
|
switch (currMode) {
|
||||||
case mode::NORMAL:
|
case mode::NORMAL:
|
||||||
mvprintw(windowSize.y, 0, "NORMAL");
|
mvprintw(windowSize.y, 1, "normal");
|
||||||
break;
|
break;
|
||||||
case mode::COMMAND:
|
case mode::COMMAND:
|
||||||
mvprintw(windowSize.y, 0, "COMMAND");
|
mvprintw(windowSize.y, 1, "command");
|
||||||
mvprintw(windowSize.y - 1, 0, (":" + commandbuf).c_str());
|
mvprintw(windowSize.y - 1, 0, (":" + commandbuf).c_str());
|
||||||
break;
|
break;
|
||||||
case mode::INSERT:
|
case mode::INSERT:
|
||||||
mvprintw(windowSize.y, 0, "INSERT");
|
mvprintw(windowSize.y, 1, "insert");
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
mvprintw(windowSize.y, 0, "Unknown Mode! Press any key to exit");
|
mvprintw(windowSize.y, 1, "Unknown Mode! Press any key to exit");
|
||||||
getch();
|
getch();
|
||||||
quit();
|
quit();
|
||||||
break;
|
break;
|
||||||
@@ -426,7 +496,7 @@ int main(int argc, char** argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// put the cursor in position
|
// put the cursor in position
|
||||||
move(pos.y - viewpoint.top, pos.x);
|
move(pos.y - viewpoint.top, pos.x + 6);
|
||||||
|
|
||||||
// put what's in the buffer on screen
|
// put what's in the buffer on screen
|
||||||
refresh();
|
refresh();
|
||||||
|
|||||||
Reference in New Issue
Block a user