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
|
||||
* Scroll through large files
|
||||
* Saving and quitting in the same way as Vim
|
||||
* Delimiter/action-based undo
|
||||
* Line number counter
|
||||
|
||||
## 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
|
||||
```
|
||||
|
||||
## 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 <vector>
|
||||
#include <fstream>
|
||||
#include <algorithm>
|
||||
|
||||
enum class mode {
|
||||
INSERT, COMMAND, NORMAL
|
||||
@@ -13,6 +14,27 @@ void quit(int exitcode = 0) {
|
||||
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::ifstream file(filename);
|
||||
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) {
|
||||
|
||||
std::string filename;
|
||||
@@ -90,8 +121,9 @@ int main(int argc, char** argv) {
|
||||
keypad(stdscr, TRUE);
|
||||
|
||||
// remember the position of the cursor
|
||||
struct {int x = 0; int y = 1;} pos;
|
||||
cursor pos;
|
||||
pos.x = lines[0].size();
|
||||
pos.y = 1;
|
||||
|
||||
// set current mode
|
||||
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
|
||||
bool fileChanged = false;
|
||||
|
||||
// create initial undo history
|
||||
undoHistory.push_back(UndoState(lines, pos));
|
||||
|
||||
// initial rendering before doing anything
|
||||
// display the current mode at the bottom
|
||||
switch (currMode) {
|
||||
case mode::NORMAL:
|
||||
mvprintw(windowSize.y, 0, "NORMAL");
|
||||
mvprintw(windowSize.y, 1, "normal");
|
||||
break;
|
||||
case mode::COMMAND:
|
||||
mvprintw(windowSize.y, 0, "COMMAND");
|
||||
mvprintw(windowSize.y, 1, "command");
|
||||
break;
|
||||
case mode::INSERT:
|
||||
mvprintw(windowSize.y, 0, "INSERT");
|
||||
mvprintw(windowSize.y, 1, "insert");
|
||||
break;
|
||||
default:
|
||||
mvprintw(windowSize.y, 0, "Unknown Mode! Press any key to exit");
|
||||
mvprintw(windowSize.y, 1, "Unknown Mode! Press any key to exit");
|
||||
getch();
|
||||
quit();
|
||||
break;
|
||||
@@ -139,11 +174,12 @@ int main(int argc, char** argv) {
|
||||
|
||||
// display what's in the buffer
|
||||
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
|
||||
move(pos.y, pos.x);
|
||||
move(pos.y, pos.x + 6);
|
||||
|
||||
// put what's in the buffer on screen
|
||||
refresh();
|
||||
@@ -165,13 +201,38 @@ int main(int argc, char** argv) {
|
||||
case ':':
|
||||
currMode = mode::COMMAND;
|
||||
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 KEY_LEFT:
|
||||
if (pos.x > 0) {
|
||||
pos.x--;
|
||||
}
|
||||
break;
|
||||
case 'j':
|
||||
case 'k':
|
||||
case KEY_DOWN:
|
||||
if (pos.y < lines.size()) {
|
||||
pos.y++;
|
||||
@@ -190,7 +251,7 @@ int main(int argc, char** argv) {
|
||||
pos.x++;
|
||||
}
|
||||
break;
|
||||
case 'k':
|
||||
case 'j':
|
||||
case KEY_UP:
|
||||
if (pos.y > 1) {
|
||||
pos.y--;
|
||||
@@ -215,6 +276,8 @@ int main(int argc, char** argv) {
|
||||
// escape has been pressed (code 27)
|
||||
case 27:
|
||||
currMode = mode::NORMAL;
|
||||
undoHistory.push_back(UndoState(lines, pos));
|
||||
undos = -1;
|
||||
break;
|
||||
// backspace has been pressed
|
||||
// boy there are a lot of ways to say backspace
|
||||
@@ -310,6 +373,11 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
pos.x++;
|
||||
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;
|
||||
@@ -362,11 +430,11 @@ int main(int argc, char** argv) {
|
||||
|
||||
if (commandbuf.substr(0, 2) == "wq") {
|
||||
if (filename.empty()) {
|
||||
if (commandbuf.size() > 1) {
|
||||
if (commandbuf[1] == ' ') {
|
||||
filename = commandbuf.substr(2);
|
||||
if (commandbuf.size() > 2) {
|
||||
if (commandbuf[2] == ' ') {
|
||||
filename = commandbuf.substr(3);
|
||||
} else {
|
||||
filename = commandbuf.substr(1);
|
||||
filename = commandbuf.substr(2);
|
||||
}
|
||||
writeFile(filename, lines);
|
||||
quit();
|
||||
@@ -387,6 +455,7 @@ int main(int argc, char** argv) {
|
||||
default:
|
||||
// add to the command buffer
|
||||
commandbuf += pressed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -394,25 +463,26 @@ int main(int argc, char** argv) {
|
||||
// erase our buffer ready to rerender
|
||||
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++) {
|
||||
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
|
||||
switch (currMode) {
|
||||
case mode::NORMAL:
|
||||
mvprintw(windowSize.y, 0, "NORMAL");
|
||||
mvprintw(windowSize.y, 1, "normal");
|
||||
break;
|
||||
case mode::COMMAND:
|
||||
mvprintw(windowSize.y, 0, "COMMAND");
|
||||
mvprintw(windowSize.y, 1, "command");
|
||||
mvprintw(windowSize.y - 1, 0, (":" + commandbuf).c_str());
|
||||
break;
|
||||
case mode::INSERT:
|
||||
mvprintw(windowSize.y, 0, "INSERT");
|
||||
mvprintw(windowSize.y, 1, "insert");
|
||||
break;
|
||||
default:
|
||||
mvprintw(windowSize.y, 0, "Unknown Mode! Press any key to exit");
|
||||
mvprintw(windowSize.y, 1, "Unknown Mode! Press any key to exit");
|
||||
getch();
|
||||
quit();
|
||||
break;
|
||||
@@ -426,7 +496,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// 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
|
||||
refresh();
|
||||
|
||||
Reference in New Issue
Block a user