indent: Apply indentation to newly loaded edc text

When an edc text is newly loaded, the edc text is indendted if its
indentation is not correct.
The indented text creation function, indent_text_create(), utilizes
the logic of indent_text_auto_format().
This commit is contained in:
Jaehyun Cho 2016-02-17 22:37:28 +09:00
parent 26b84340fe
commit 02a4549906
3 changed files with 484 additions and 13 deletions

View File

@ -1025,16 +1025,48 @@ edit_edc_load(edit_data *ed, const char *file_path)
utf8_edit = eina_file_map_all(file, EINA_FILE_POPULATE);
if (!utf8_edit) goto err;
//Append line numbers
//Check indentation.
indent_data *id = syntax_indent_data_get(ed->sh);
Eina_Bool indent_correct
= indent_text_check(id, (const char *)utf8_edit);
//Set edc text to entry.
if (edit_auto_indent_get(ed) && !indent_correct)
//Create indented markup text from utf8 text of EDC file.
markup_edit = indent_text_create(id, (const char *)utf8_edit,
&line_num);
else
markup_edit = elm_entry_utf8_to_markup(utf8_edit);
if (!markup_edit) goto err;
elm_entry_entry_set(ed->en_edit, markup_edit);
if (edit_auto_indent_get(ed) && !indent_correct)
edit_changed_set(ed, EINA_TRUE);
free(markup_edit);
//Append line numbers.
if (!eina_strbuf_append_char(strbuf_line, '1')) goto err;
char *p = utf8_edit;
int len = strlen(p);
while ((p = strchr(p, '\n')) && p < (utf8_edit + len))
if (edit_auto_indent_get(ed) && !indent_correct)
{
line_num++;
++p;
sprintf(buf, "\n%d", line_num);
if (!eina_strbuf_append(strbuf_line, buf)) goto err;
int num = 2;
//Use line_num given by indent_text_create().
while (num <= line_num)
{
sprintf(buf, "\n%d", num);
if (!eina_strbuf_append(strbuf_line, buf)) goto err;
num++;
}
}
else
{
char *p = utf8_edit;
int len = strlen(p);
while ((p = strchr(p, '\n')) && p < (utf8_edit + len))
{
line_num++;
++p;
sprintf(buf, "\n%d", line_num);
if (!eina_strbuf_append(strbuf_line, buf)) goto err;
}
}
markup_line = elm_entry_utf8_to_markup(eina_strbuf_string_get(strbuf_line));
@ -1042,11 +1074,6 @@ edit_edc_load(edit_data *ed, const char *file_path)
elm_entry_entry_append(ed->en_line, markup_line);
free(markup_line);
markup_edit = elm_entry_utf8_to_markup(utf8_edit);
if (!markup_edit) goto err;
elm_entry_entry_set(ed->en_edit, markup_edit);
free(markup_edit);
ed->line_max = line_num;
group_name = parser_first_group_name_get(ed->pd, ed->en_edit);

View File

@ -160,6 +160,8 @@ void indent_term(indent_data *id);
int indent_space_get(indent_data *id, Evas_Object *entry);
int indent_insert_apply(indent_data *id, Evas_Object *entry, const char *insert, int cur_line);
Eina_Bool indent_delete_apply(indent_data *id, Evas_Object *entry, const char *del, int cur_line);
Eina_Bool indent_text_check(indent_data *id EINA_UNUSED, const char *utf8);
char * indent_text_create(indent_data *id, const char *utf8, int *indented_line_cnt);
/* build */

View File

@ -563,3 +563,445 @@ indent_insert_apply(indent_data *id, Evas_Object *entry, const char *insert,
return indent_text_auto_format(id, entry, insert);
}
}
/* Check if indentation of input text is correct.
Return EINA_TRUE if indentation is correct.
Return EINA_FALSE if indentation is not correct. */
Eina_Bool
indent_text_check(indent_data *id EINA_UNUSED, const char *utf8)
{
char *utf8_ptr = NULL;
char *utf8_end = NULL;
int utf8_size = 0;
int depth = 0;
int saved_depth = 0;
int space = 0;
Eina_Bool nonspace_found = EINA_FALSE;
Eina_Bool single_comment_found = EINA_FALSE;
Eina_Bool multi_comment_found = EINA_FALSE;
Eina_Bool macro_found = EINA_FALSE;
if (!utf8) return EINA_TRUE;
utf8_ptr = (char *)utf8;
utf8_size = strlen(utf8);
utf8_end = (char *)utf8 + utf8_size;
/* Check spaces based on depth before nonspace character in each line.
Check a new line after the followings.
'{', '}', ';', "*\/"
Depth is not calculated within line comment.
*/
while (utf8_ptr < utf8_end)
{
if (*utf8_ptr == '}')
{
depth--;
if (depth < 0) depth = 0;
}
//Tab is not allowed.
if (*utf8_ptr == '\t')
return EINA_FALSE;
else if (*utf8_ptr == ' ')
{
if (!nonspace_found)
space++;
}
else if (*utf8_ptr == '\n')
{
//End of single line comment.
if (single_comment_found)
{
depth = saved_depth;
single_comment_found = EINA_FALSE;
}
//End of macro.
else if (macro_found)
{
//Macro ends with "\n" but continues with "\\\n".
if (!(utf8_ptr - 1 >= utf8 && *(utf8_ptr - 1) == '\\'))
{
depth = saved_depth;
macro_found = EINA_FALSE;
}
}
space = 0;
nonspace_found = EINA_FALSE;
}
else
{
if (!nonspace_found)
{
if (space != depth * TAB_SPACE) return EINA_FALSE;
nonspace_found = EINA_TRUE;
}
if (multi_comment_found)
{
//End of multi line comment.
if (*utf8_ptr == '/' && (utf8_ptr - 1) >= utf8 &&
*(utf8_ptr - 1) == '*')
{
if ((utf8_ptr + 1 < utf8_end) &&
(*(utf8_ptr + 1) != '\n'))
return EINA_FALSE;
depth = saved_depth;
multi_comment_found = EINA_FALSE;
}
}
//No line comment and No macro.
else if (!single_comment_found && !macro_found)
{
//Check line comment.
if (*utf8_ptr == '/' && utf8_ptr + 1 < utf8_end)
{
//Start of single line comment.
if (*(utf8_ptr + 1) == '/')
{
saved_depth = depth;
single_comment_found = EINA_TRUE;
}
//Start of multi line comment.
else if (*(utf8_ptr + 1) == '*')
{
saved_depth = depth;
multi_comment_found = EINA_TRUE;
}
}
//Check macro.
else if (*utf8_ptr == '#')
{
saved_depth = depth;
macro_found = EINA_TRUE;
}
else if (*utf8_ptr == '}')
{
if ((utf8_ptr + 1 < utf8_end) &&
(*(utf8_ptr + 1) != '\n'))
return EINA_FALSE;
}
else if (*utf8_ptr == ';')
{
if (utf8_ptr + 1 < utf8_end &&
*(utf8_ptr + 1) != '\n')
return EINA_FALSE;
}
if (*utf8_ptr == '{')
{
char *bracket_right_ptr = utf8_ptr + 1;
while (bracket_right_ptr < utf8_end)
{
if (*bracket_right_ptr != ' ' &&
*bracket_right_ptr != '\t')
break;
bracket_right_ptr++;
}
if (bracket_right_ptr < utf8_end &&
*bracket_right_ptr != '\n')
{
//Check block name after '{'.
Eina_Bool block_name_found = EINA_FALSE;
if (*bracket_right_ptr == '\"')
block_name_found = EINA_TRUE;
else if (bracket_right_ptr + 4 < utf8_end)
{
if (!strncmp(bracket_right_ptr,
"name:", 5))
block_name_found = EINA_TRUE;
else if (!strncmp(bracket_right_ptr,
"state:", 5))
block_name_found = EINA_TRUE;
}
if (!block_name_found)
return EINA_FALSE;
}
}
}
}
if (*utf8_ptr == '{') depth++;
utf8_ptr++;
}
return EINA_TRUE;
}
/* Create indented markup text from input utf8 text.
Count the number of lines of indented text.
Return created indented markup text. */
char *
indent_text_create(indent_data *id,
const char *utf8, int *indented_line_cnt)
{
if (!utf8)
{
if (indented_line_cnt) *indented_line_cnt = 0;
return NULL;
}
int utf8_size = strlen(utf8);
char *utf8_ptr = (char *)utf8;
char *utf8_lexem = NULL;
char *utf8_end = (char *)utf8 + utf8_size;
char *utf8_append_ptr = NULL;
Eina_List *code_lines = NULL;
Eina_Strbuf *buf = eina_strbuf_new();
Eina_Bool keep_lexem_start_pos = EINA_FALSE;
Eina_Bool single_comment_found = EINA_FALSE;
Eina_Bool multi_comment_found = EINA_FALSE;
Eina_Bool macro_found = EINA_FALSE;
/* Create a list of code line strings from inserted string.
Each code line string is generated based on lexeme.
Here, lexeme starts with nonspace character and ends with the followings.
'{', '}', ';', "//", "*\/"
*/
while (utf8_ptr < utf8_end)
{
if (*utf8_ptr != ' ' && *utf8_ptr != '\t' && *utf8_ptr != '\n' )
{
//Renew the start position of lexeme.
if (!keep_lexem_start_pos) utf8_lexem = utf8_ptr;
//Check line comment.
if (*utf8_ptr == '/' && utf8_ptr + 1 < utf8_end)
{
//Start of single line comment.
if (*(utf8_ptr + 1) == '/')
single_comment_found = EINA_TRUE;
//Start of multi line comment.
else if (*(utf8_ptr + 1) == '*')
multi_comment_found = EINA_TRUE;
if (single_comment_found || multi_comment_found)
utf8_ptr += 2;
}
//Check macro.
if (*utf8_ptr == '#')
{
macro_found = EINA_TRUE;
utf8_ptr++;
}
while (utf8_ptr < utf8_end)
{
if (*utf8_ptr == '\n')
{
//End of single line comment.
if (single_comment_found)
single_comment_found = EINA_FALSE;
//End of macro.
else if (macro_found)
{
//Macro ends with "\n" but continues with "\\\n".
if (!(utf8_ptr - 1 >= utf8 &&
*(utf8_ptr - 1) == '\\'))
macro_found = EINA_FALSE;
}
code_lines = eina_list_append(code_lines,
eina_stringshare_add_length(utf8_lexem,
utf8_ptr - utf8_lexem));
utf8_append_ptr = utf8_ptr;
break;
}
else if (multi_comment_found)
{
//End of multi line comment.
if (*utf8_ptr == '/' && utf8_ptr - 1 >= utf8 &&
*(utf8_ptr - 1) == '*')
{
if (utf8_ptr + 1 == utf8_end)
code_lines = eina_list_append(code_lines,
eina_stringshare_add(utf8_lexem));
else
code_lines =
eina_list_append(code_lines,
eina_stringshare_add_length(utf8_lexem,
utf8_ptr - utf8_lexem + 1));
utf8_append_ptr = utf8_ptr;
multi_comment_found = EINA_FALSE;
break;
}
}
//No line comment and No macro.
else if (!single_comment_found && !macro_found)
{
if (*utf8_ptr == '{' || *utf8_ptr == '}' ||
*utf8_ptr == ';')
{
if (*utf8_ptr == '{')
{
char *bracket_right_ptr = utf8_ptr + 1;
while (bracket_right_ptr < utf8_end)
{
if (*bracket_right_ptr != ' ' &&
*bracket_right_ptr != '\t')
break;
bracket_right_ptr++;
}
if (bracket_right_ptr < utf8_end)
{
/* To preserve code line until block name,
keep start position of lexeme and
append code line until ';'. */
Eina_Bool block_name_found = EINA_FALSE;
if (*bracket_right_ptr == '\"')
block_name_found = EINA_TRUE;
else if (bracket_right_ptr + 4 < utf8_end)
{
if (!strncmp(bracket_right_ptr,
"name:", 5))
block_name_found = EINA_TRUE;
else if (!strncmp(bracket_right_ptr,
"state:", 5))
block_name_found = EINA_TRUE;
}
if (block_name_found)
{
keep_lexem_start_pos = EINA_TRUE;
break;
}
}
}
else if (*utf8_ptr == ';')
keep_lexem_start_pos = EINA_FALSE;
if (utf8_ptr + 1 == utf8_end)
code_lines = eina_list_append(code_lines,
eina_stringshare_add(utf8_lexem));
else
code_lines =
eina_list_append(code_lines,
eina_stringshare_add_length(utf8_lexem,
utf8_ptr - utf8_lexem + 1));
utf8_append_ptr = utf8_ptr;
break;
}
}
utf8_ptr++;
}
}
utf8_ptr++;
}
//Append rest of the input string.
if (utf8_lexem > utf8_append_ptr)
code_lines = eina_list_append(code_lines,
eina_stringshare_add(utf8_lexem));
if (!code_lines)
{
if (indented_line_cnt) *indented_line_cnt = 0;
return NULL;
}
Eina_List *l = NULL;
Eina_List *l_last = eina_list_last(code_lines);
Eina_Stringshare *line;
int line_cnt = 1;
int space = 0;
int saved_space = 0;
single_comment_found = EINA_FALSE;
multi_comment_found = EINA_FALSE;
macro_found = EINA_FALSE;
EINA_LIST_FOREACH(code_lines, l, line)
{
if (!single_comment_found && !multi_comment_found && !macro_found)
{
if (!strncmp(line, "//", 2))
{
single_comment_found = EINA_TRUE;
saved_space = space;
}
else if (!strncmp(line, "/*", 2))
{
multi_comment_found = EINA_TRUE;
saved_space = space;
}
else if (line[0] == '#')
{
macro_found = EINA_TRUE;
saved_space = space;
}
}
if ((line[0] == '}') && (space > 0))
space -= TAB_SPACE;
char *p = alloca(space + 1);
memset(p, ' ', space);
p[space] = '\0';
if (strcmp(line, "\n"))
{
if (l == l_last)
eina_strbuf_append_printf(buf, "%s%s", p, line);
else
{
eina_strbuf_append_printf(buf, "%s%s\n", p, line);
line_cnt++;
}
}
else
{
eina_strbuf_append(buf, "\n");
line_cnt++;
}
memset(p, 0x0, space);
/* Based on the code line generation logic, "{" and "}" can exist
multiple times in a line within line comment and macro. */
char *bracket_ptr = strstr(line, "{");
while (bracket_ptr)
{
space += TAB_SPACE;
bracket_ptr++;
bracket_ptr = strstr(bracket_ptr, "{");
}
//Restore space.
if (line[0] == '}') space += TAB_SPACE;
bracket_ptr = strstr(line, "}");
while (bracket_ptr && space > 0)
{
space -= TAB_SPACE;
bracket_ptr++;
bracket_ptr = strstr(bracket_ptr, "}");
}
if (single_comment_found)
{
single_comment_found = EINA_FALSE;
space = saved_space;
}
else if (multi_comment_found && strstr(line, "*/"))
{
multi_comment_found = EINA_FALSE;
space = saved_space;
}
else if (macro_found && line[strlen(line) - 1] != '\\')
{
macro_found = EINA_FALSE;
space = saved_space;
}
eina_stringshare_del(line);
}
char *utf8_buf = eina_strbuf_string_steal(buf);
//FIXME: This translation may cause low performance.
char *indented_markup = evas_textblock_text_utf8_to_markup(NULL, utf8_buf);
eina_strbuf_free(buf);
free(utf8_buf);
if (indented_line_cnt) *indented_line_cnt = line_cnt;
return indented_markup;
}