Created
September 9, 2020 09:54
-
-
Save maeste/a63eba81523ed8e189f5695ff562563d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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