Skip to content

Instantly share code, notes, and snippets.

@maeste
Created September 9, 2020 09:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save maeste/a63eba81523ed8e189f5695ff562563d to your computer and use it in GitHub Desktop.
Save maeste/a63eba81523ed8e189f5695ff562563d to your computer and use it in GitHub Desktop.
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#define CHANGE_COMMAND 0
#define DELETE_COMMAND 1
/*
this struct stores all the contents of a line from its birth
the first element of the list is the first text written in the line,
then we have all the changes, element by element
we will use this structure to enable undo and redo
when we delete text of a line, we will create a new element in the list, with an empty text
*/
char* line[300000]={NULL};
int last_line = 1;
/*
maybe a list for the editor is not the best choice
the need is to access the line in a short time, so the best choice would be an array,
but we don't know how many lines can editor have
maybe a tree or an hash are the ones
/*
this structure is stack storing the commands. a command type
*/
typedef struct command_stack {
int command_type;
int line_number;
// char* previous_text;
char** changed_texts;
int group_lenght;
struct command_stack * next;
}command_stack_t;
command_stack_t * undo_stack = NULL;
command_stack_t * undone_queue[500] = {NULL};
command_stack_t * redo_stack = NULL;
int undone_head= 0;
int undone_tail=0;
void empty_redo_stack();
void empty_undone_stack();
void push_text(char* previous_text, struct command_stack** stack, int index);
void push(int command_type, int line_number, int group_length, struct command_stack** stack);
void pop(struct command_stack** stack);
void change_text_to_line(int line_number, char text[1024]);
/*
this will add new element to the list of the line chosen with the given text
*/
void add_text_to_line(int line_number, char text[1024], int group_length, int index);
/*
this will handle the change command
*/
void change(int start_line, int end_line);
/*
this will handle the delete command
*/
void delete(int start_line, int end_line);
/*
this will handle the print command
*/
void print(int start_line, int end_line, int index);
/*
this will handle the undo command
*/
void undo(int number_of_moves);
void exec_undo();
/*
this will handle the redo command
*/
void redo(int number_of_moves);
/*
this will handle the split of the command into start and end line
*/
int* get_start_and_end_line_by_command(char* command, int length);
void shift_forward(int line_number, int number_of_shift);
void shift_backward(int line_number, int number_of_shift);
void empty_redo_stack(){
while(redo_stack != NULL){
pop(&redo_stack);
}
}
// Append the new element to the start of the stack
void push(int command_type, int line_number, int group_length, struct command_stack** stack){
struct command_stack* Element = (struct command_stack*)malloc(sizeof(struct command_stack));
Element->command_type = command_type;
Element->line_number = line_number;
Element->group_lenght = group_length;
Element->changed_texts = (char**)malloc(sizeof(char*)*group_length);
// Element->previous_text = previous_text;
Element->next = *stack;
(*stack) = Element;
}
void push_text(char* previous_text, struct command_stack** stack, int index){
(*stack)->changed_texts[index] = previous_text;
}
// Remove element from the top of the stack
void pop(struct command_stack** stack){
if(*stack != NULL){
struct command_stack* tempPtr = (*stack);
if((*stack)->next != NULL){
*stack = (*stack)->next;
} else {
*stack = NULL;
}
free(tempPtr->changed_texts);
free(tempPtr);
}
}
int* get_start_and_end_line_by_command(char* command, int length)
{
static int s_e_lines[2];
char* ptr;
ptr = strrchr(command, ',');
int index = ptr-command+1;
s_e_lines[0] = atoi(command);
char endofcommand[length-index];
strncpy(endofcommand, &command[index], length-index);
s_e_lines[1] = atoi(endofcommand);
return s_e_lines;
}
void change(int start_line, int end_line)
{
//I think we need to have a redo stack empty for each call of undo (to be verified)
empty_undone_stack();
int group_length = (end_line - start_line)+1;
push(CHANGE_COMMAND, start_line, group_length, &undo_stack);
for (int line_number = start_line; line_number < end_line+1; line_number++)
{
char text[1024];
//char* scan = fgets (text, 1024, stdin);
char temp;
int scan = scanf("%c",&temp); // temp statement to clear buffer
scan = scanf("%1024[^\n]",text);
add_text_to_line(line_number, text, group_length, (line_number-start_line));
}
}
void delete(int start_line, int end_line)
{
//I think we need to have a redo stack empty for each call of undo (to be verified)
//empty_undone_stack();
exec_undo();
push(DELETE_COMMAND, start_line, (end_line - start_line)+1, &undo_stack);
for (int line_number = start_line; line_number < end_line+1; line_number++)
{
push_text(line[line_number], &undo_stack, (line_number-start_line));
}
int number_of_shift = (end_line-start_line)+1;
shift_backward(start_line, number_of_shift);
}
void print(int start_line, int end_line, int index)
{
exec_undo();
for (int i = start_line; i <= end_line; i++)
{
if(line[i] != NULL){
printf("%s\n", line[i]);
} else {
printf(".\n");
}
}
}
void undo(int number_of_moves)
{
for (int i =0 ; i < number_of_moves; i++) {
//TODO check if stack is empty before operations
if(undo_stack != NULL) {
undone_queue[undone_tail] = (struct command_stack*)malloc(sizeof(struct command_stack));
undone_queue[undone_tail] -> command_type = undo_stack -> command_type;
undone_queue[undone_tail] -> line_number = undo_stack -> line_number;
undone_queue[undone_tail] -> group_lenght = undo_stack -> group_lenght;
int group_lenght = undo_stack->group_lenght;
undone_queue[undone_tail]->changed_texts = (char**)malloc(sizeof(char*)*group_lenght);
for (int j=0; j < group_lenght; j++) {
if (undo_stack->command_type == CHANGE_COMMAND) {
undone_queue[undone_tail] -> changed_texts[j] = undo_stack -> changed_texts[j];
}
}
undone_tail = undone_tail + 1;
pop(&undo_stack);
} else{
return;
}
}
}
void empty_undone_stack()
{
for (int i=undone_head; i<undone_tail; i++) {
//TODO check if stack is empty before operations
if (undone_queue[i] -> command_type == DELETE_COMMAND) {
shift_forward(undone_queue[i]->line_number, undone_queue[i]->group_lenght);
}
int group_lenght = undone_queue[i]->group_lenght;
for (int j=0; j < group_lenght; j++) {
line[undone_queue[i]->line_number+j] = undone_queue[i]->changed_texts[j];
}
free(undone_queue[i]);
}
undone_tail = undone_head;
}
void exec_undo()
{
for (int i=undone_head; i<undone_tail; i++) {
//printf("undo:%d,%d %d",undone_queue[i]->line_number, undone_queue[i]->group_lenght,undone_queue[i]->command_type);
//TODO check if stack is empty before operations
if (undone_queue[i] -> command_type == DELETE_COMMAND) {
shift_forward(undone_queue[i]->line_number, undone_queue[i]->group_lenght);
}
push(undone_queue[i] -> command_type, undone_queue[i]->line_number, undone_queue[i]->group_lenght, &redo_stack);
int group_lenght = undone_queue[i]->group_lenght;
for (int j=0; j < group_lenght; j++) {
if (undone_queue[i]->command_type == CHANGE_COMMAND) {
push_text(line[undone_queue[i]->line_number+j], &redo_stack, j);
line[undone_queue[i]->line_number+j] = undone_queue[i]->changed_texts[j];
}
}
free(undone_queue[i]);
}
undone_tail = undone_head;
}
void redo(int number_of_moves)
{
for (int i =0 ; i < number_of_moves; i++) {
if(undone_tail != undone_head) {
//if it's a grouped command repeat on the stack for the number of elements in the group
push(undone_queue[undone_tail -1]->command_type, undone_queue[undone_tail -1]->line_number, undone_queue[undone_tail -1]->group_lenght, &undo_stack);
free(undone_queue[undone_tail -1]);
undone_tail = undone_tail -1;
} else {
if(redo_stack != NULL) {
//if it's a grouped command repeat on the stack for the number of elements in the group
push(redo_stack->command_type, redo_stack->line_number, redo_stack->group_lenght, &undo_stack);
int group_lenght = redo_stack->group_lenght;
for (int j=0; j < group_lenght; j++) {
push_text(line[redo_stack->line_number+j], &undo_stack, j);
if (redo_stack->command_type == CHANGE_COMMAND) {
line[redo_stack->line_number+j] = redo_stack->changed_texts[j];
}
}
if (redo_stack -> command_type == DELETE_COMMAND) {
shift_backward(redo_stack->line_number, redo_stack->group_lenght);
}
pop(&redo_stack);
} else {
return;
}
}
}
}
void add_text_to_line(int line_number, char text[1024], int group_length, int index)
{
push_text(line[line_number], &undo_stack, index);
change_text_to_line(line_number, text);
}
void change_text_to_line(int line_number, char text[1024]) {
//CHECK change text to line
if(text == NULL){
line[line_number] = NULL;
} else {
int n=strlen(text);
line[line_number] = (char*)malloc(n+1);
strcpy(line[line_number], text);
}
if(line_number > last_line){
last_line = line_number;
}
}
void shift_forward(int line_number, int number_of_shift){
memmove(&line[line_number+number_of_shift], &line[line_number], sizeof(char*)*(last_line-line_number+1));
}
void shift_backward(int line_number, int number_of_shift){
memmove(&line[line_number], &line[line_number+number_of_shift], sizeof(char*)*(last_line-line_number+1));
}
int main ()
{
char command[128];
int scan = scanf("%s", command);
while (command[0] != 'q')
{
//printf("%s\n", command);
int length = (int)strlen(command);
switch (command[length-1])
{
case 'c':
{
int* s_e_lines = get_start_and_end_line_by_command(command, length);
change(*(s_e_lines), *(s_e_lines+1));
}
break;
case 'd':
{
int* s_e_lines = get_start_and_end_line_by_command(command, length);
delete(*(s_e_lines), *(s_e_lines+1));
}
break;
case 'p':
{
int* s_e_lines = get_start_and_end_line_by_command(command, length);
print(*(s_e_lines), *(s_e_lines+1), 1);
}
break;
case 'u':
{
int number_of_moves = atoi(command);
undo(number_of_moves);
}
break;
case 'r':
{
int number_of_moves = atoi(command);
redo(number_of_moves);
}
break;
default:
break;
}
scan = scanf("%s", command);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment