From 1049acbacc83a4828317f85501c42c8b350954a5 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Tue, 22 Nov 2016 13:26:58 -0500 Subject: [PATCH] building the site feature list page --- 4ed.cpp | 6 +- build.cpp | 2 +- site/abstract_document.cpp | 169 +++++++++---- site/build.sh | 6 + site/sitegen.cpp | 336 +++++++++++++++----------- site/source_material/feature_list.txt | 30 +++ 6 files changed, 353 insertions(+), 196 deletions(-) create mode 100755 site/build.sh create mode 100644 site/source_material/feature_list.txt diff --git a/4ed.cpp b/4ed.cpp index 5b2f3b89..786cbeb2 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -188,8 +188,7 @@ restore_state(Application_Links *app, App_Coroutine_State state){ } inline Coroutine* -app_launch_coroutine(System_Functions *system, Application_Links *app, Coroutine_Type type, - Coroutine *co, void *in, void *out){ +app_launch_coroutine(System_Functions *system, Application_Links *app, Coroutine_Type type, Coroutine *co, void *in, void *out){ Coroutine* result = 0; App_Coroutine_State prev_state = get_state(app); @@ -203,8 +202,7 @@ app_launch_coroutine(System_Functions *system, Application_Links *app, Coroutine } inline Coroutine* -app_resume_coroutine(System_Functions *system, Application_Links *app, Coroutine_Type type, - Coroutine *co, void *in, void *out){ +app_resume_coroutine(System_Functions *system, Application_Links *app, Coroutine_Type type, Coroutine *co, void *in, void *out){ Coroutine* result = 0; App_Coroutine_State prev_state = get_state(app); diff --git a/build.cpp b/build.cpp index f8a54446..b5404fdc 100644 --- a/build.cpp +++ b/build.cpp @@ -935,7 +935,7 @@ site_build(char *cdir, uint32_t flags){ linux_popd(temp); #endif - END_TIME_SECTION("run metagen"); + END_TIME_SECTION("run sitegen"); } } diff --git a/site/abstract_document.cpp b/site/abstract_document.cpp index f7d94884..c1bb57ed 100644 --- a/site/abstract_document.cpp +++ b/site/abstract_document.cpp @@ -90,6 +90,7 @@ static Document_Item null_document_item = {0}; struct Abstract_Document{ // Document value members Document_Item *root_item; + char *name; // Document building members Partition *part; @@ -127,6 +128,11 @@ begin_document_description(Abstract_Document *doc, Partition *part, char *title) set_section_name(doc->part, doc->root_item, title); } +static void +set_document_name(Abstract_Document *doc, char *name){ + doc->name = name; +} + static void end_document_description(Abstract_Document *doc){ Assert(doc->section_top == 0); @@ -296,10 +302,10 @@ struct Section_Counter{ }; static void -append_section_number_reduced(String *out, Section_Counter section_counter, int32_t reduce){ - int32_t level = section_counter.nest_level-reduce; +append_section_number_reduced(String *out, Section_Counter *section_counter, int32_t reduce){ + int32_t level = section_counter->nest_level-reduce; for (int32_t i = 1; i <= level; ++i){ - append_int_to_str(out, section_counter.counter[i]); + append_int_to_str(out, section_counter->counter[i]); if (i != level){ append_sc(out, "."); } @@ -307,7 +313,7 @@ append_section_number_reduced(String *out, Section_Counter section_counter, int3 } static void -append_section_number(String *out, Section_Counter section_counter){ +append_section_number(String *out, Section_Counter *section_counter){ append_section_number_reduced(out, section_counter, 0); } @@ -360,7 +366,39 @@ extract_command_body(String *out, String l, int32_t *i_in_out, int32_t *body_sta } static void -write_enriched_text_html(String *out, Enriched_Text *text){ +html_render_section_header(String *out, String section_name, String section_id, Section_Counter *section_counter){ + if (section_counter->nest_level <= 1){ + if (section_id.size > 0){ + append_sc(out, "\n

§"); + } + else{ + append_sc(out, "\n

§"); + } + append_section_number(out, section_counter); + append_sc(out, " "); + append_ss(out, section_name); + append_sc(out, "

"); + } + else{ + if (section_id.size > 0){ + append_sc(out, "

§"); + } + else{ + append_sc(out, "

§"); + } + append_section_number(out, section_counter); + append_sc(out, " "); + append_ss(out, section_name); + append_sc(out, "

"); + } +} + +static void +write_enriched_text_html(String *out, Enriched_Text *text, Section_Counter *section_counter){ String source = text->source; append_sc(out, "
"); @@ -393,33 +431,54 @@ write_enriched_text_html(String *out, Enriched_Text *text){ String command_string = substr(l, command_start, command_end - command_start); - static String enriched_commands[] = { - make_lit_string("\\"), - make_lit_string("VERSION"), - make_lit_string("CODE_STYLE"), - make_lit_string("DOC_LINK"), + enum Command_Types{ + Cmd_BackSlash, + Cmd_Version, + Cmd_CodeStyle, + Cmd_DocLink, + Cmd_BeginList, + Cmd_EndList, + Cmd_BeginItem, + Cmd_EndItem, + Cmd_BoldFace, + Cmd_Section, + // never below this + Cmd_COUNT, }; + static String enriched_commands[Cmd_COUNT]; + + enriched_commands[Cmd_BackSlash] = make_lit_string("\\"); + enriched_commands[Cmd_Version] = make_lit_string("VERSION"); + enriched_commands[Cmd_CodeStyle] = make_lit_string("CODE_STYLE"); + enriched_commands[Cmd_DocLink] = make_lit_string("DOC_LINK"); + enriched_commands[Cmd_BeginList] = make_lit_string("BEGIN_LIST"); + enriched_commands[Cmd_EndList] = make_lit_string("END_LIST"); + enriched_commands[Cmd_BeginItem] = make_lit_string("BEGIN_ITEM"); + enriched_commands[Cmd_EndItem] = make_lit_string("END_ITEM"); + enriched_commands[Cmd_BoldFace] = make_lit_string("BOLD_FACE"); + enriched_commands[Cmd_Section] = make_lit_string("SECTION"); + i = command_end; int32_t match_index = 0; if (string_set_match(enriched_commands, ArrayCount(enriched_commands), command_string, &match_index)){ switch (match_index){ - case 0: append_sc(out, "\\"); break; - case 1: append_sc(out, VERSION); break; - case 2: + case Cmd_BackSlash: append_sc(out, "\\"); break; + case Cmd_Version: append_sc(out, VERSION); break; + case Cmd_CodeStyle: { int32_t body_start = 0, body_end = 0; - int32_t has_body = extract_command_body(out, l, &i, &body_start, &body_end, command_string); + int32_t has_body = extract_command_body(out, l, &i, &body_start, &body_end, command_string); if (has_body){ String body_text = substr(l, body_start, body_end - body_start); - append_sc(out, ""); - append_ss(out, body_text); - append_sc(out, ""); - } + append_sc(out, ""); + append_ss(out, body_text); + append_sc(out, ""); + } }break; - case 3: + case Cmd_DocLink: { int32_t body_start = 0, body_end = 0; int32_t has_body = extract_command_body(out, l, &i, &body_start, &body_end, command_string); @@ -432,6 +491,38 @@ write_enriched_text_html(String *out, Enriched_Text *text){ append_sc(out, ""); } }break; + + case Cmd_BeginList: + { + append_sc(out, ""); + }break; + + case Cmd_BeginItem: + { + append_sc(out, "
  • "); + }break; + + case Cmd_EndItem: + { + append_sc(out, "
  • "); + }break; + + case Cmd_Section: + { + int32_t body_start = 0, body_end = 0; + int32_t has_body = extract_command_body(out, l, &i, &body_start, &body_end, command_string); + if (has_body){ + String body_text = substr(l, body_start, body_end - body_start); + + html_render_section_header(out, body_text, null_string, section_counter); + ++section_counter->counter[section_counter->nest_level]; + } + }break; } } else{ @@ -1060,7 +1151,7 @@ write_enriched_text_html(String *out, Enriched_Text *text){ } static void - doc_item_head_html(String *out, Partition *part, Used_Links *used_links, Document_Item *item, Section_Counter section_counter){ + doc_item_head_html(String *out, Partition *part, Used_Links *used_links, Document_Item *item, Section_Counter *section_counter){ switch (item->type){ case Doc_Root: { @@ -1131,34 +1222,7 @@ static void case Doc_Section: { - if (section_counter.nest_level <= 1){ - if (item->section.id.size > 0){ - append_sc(out, "\n

    §"); - } - else{ - append_sc(out, "\n

    §"); - } - append_section_number(out, section_counter); - append_sc(out, " "); - append_ss(out, item->section.name); - append_sc(out, "

    "); - } - else{ - if (item->section.id.size > 0){ - append_sc(out, "

    §"); - } - else{ - append_sc(out, "

    §"); - } - append_section_number(out, section_counter); - append_sc(out, " "); - append_ss(out, item->section.name); - append_sc(out, "

    "); - } + html_render_section_header(out, item->section.name, item->section.id, section_counter); }break; case Doc_Todo: @@ -1168,7 +1232,7 @@ static void case Doc_Enriched_Text: { - write_enriched_text_html(out, item->text.text); + write_enriched_text_html(out, item->text.text, section_counter); }break; case Doc_Element_List: @@ -1261,7 +1325,7 @@ static void } static void -doc_item_foot_html(String *out, Partition *part, Used_Links *used_links, Document_Item *item, Section_Counter section_counter){ +doc_item_foot_html(String *out, Partition *part, Used_Links *used_links, Document_Item *item, Section_Counter *section_counter){ switch (item->type){ case Doc_Root: { @@ -1276,8 +1340,7 @@ doc_item_foot_html(String *out, Partition *part, Used_Links *used_links, Documen static void generate_item_html(String *out, Partition *part, Used_Links *used_links, Document_Item *item, Section_Counter *section_counter){ - Section_Counter sc = *section_counter; - doc_item_head_html(out, part, used_links, item, sc); + doc_item_head_html(out, part, used_links, item, section_counter); if (item->type == Doc_Root || item->type == Doc_Section){ int32_t level = ++section_counter->nest_level; @@ -1291,7 +1354,7 @@ generate_item_html(String *out, Partition *part, Used_Links *used_links, Documen ++section_counter->counter[section_counter->nest_level]; } - doc_item_foot_html(out, part, used_links, item, sc); + doc_item_foot_html(out, part, used_links, item, section_counter); } static void diff --git a/site/build.sh b/site/build.sh new file mode 100755 index 00000000..4048cb1d --- /dev/null +++ b/site/build.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +pushd .. +make clean +make site +popd \ No newline at end of file diff --git a/site/sitegen.cpp b/site/sitegen.cpp index d6b48b24..7e521f58 100644 --- a/site/sitegen.cpp +++ b/site/sitegen.cpp @@ -25,7 +25,7 @@ #define InvalidPath Assert(!"Invalid path of execution") -////////////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////// // // Meta Parse Rules @@ -151,150 +151,210 @@ assert_files_are_equal(char *directory, char *filename1, char *filename2){ } } + static Abstract_Document* + generate_4coder_API(Partition *part, char *code_directory, char *src_directory, char *dst_directory){ +#define API_DOC "4coder_API.html" + + static Meta_Keywords meta_keywords[] = { + {make_lit_string("API_EXPORT") , Item_Function } , + {make_lit_string("API_EXPORT_INLINE") , Item_Function } , + {make_lit_string("API_EXPORT_MACRO") , Item_Macro } , + {make_lit_string("CPP_NAME") , Item_CppName } , + {make_lit_string("TYPEDEF") , Item_Typedef } , + {make_lit_string("STRUCT") , Item_Struct } , + {make_lit_string("UNION") , Item_Union } , + {make_lit_string("ENUM") , Item_Enum } , + }; + +#define ExpandArray(a) (a), (ArrayCount(a)) + + Meta_Unit *custom_types_unit = push_struct(part, Meta_Unit); + Meta_Unit *lexer_funcs_unit = push_struct(part, Meta_Unit); + Meta_Unit *lexer_types_unit = push_struct(part, Meta_Unit); + Meta_Unit *string_unit = push_struct(part, Meta_Unit); + Meta_Unit *custom_funcs_unit = push_struct(part, Meta_Unit); + + Alternate_Names_Array *custom_func_names = push_struct(part, Alternate_Names_Array); + + Enriched_Text *introduction = push_struct(part, Enriched_Text); + Enriched_Text *lexer_introduction = push_struct(part, Enriched_Text); + + Abstract_Document *doc = push_struct(part, Abstract_Document); + + // NOTE(allen): Parse the important code. + *custom_types_unit = compile_meta_unit(part, code_directory, "4coder_types.h", ExpandArray(meta_keywords)); + + *lexer_funcs_unit = compile_meta_unit(part, code_directory, "4cpp_lexer.h", ExpandArray(meta_keywords)); + + *lexer_types_unit = compile_meta_unit(part, code_directory, "4cpp_lexer_types.h", ExpandArray(meta_keywords)); + + *string_unit = compile_meta_unit(part, code_directory, "internal_4coder_string.cpp", ExpandArray(meta_keywords)); + + static char *functions_files[] = { + "4ed_api_implementation.cpp", + "win32_api_impl.cpp", + 0 + }; + + *custom_funcs_unit = compile_meta_unit(part, code_directory, functions_files, ExpandArray(meta_keywords)); + + + // NOTE(allen): Compute and store variations of the custom function names + *custom_func_names = allocate_app_api(part, custom_funcs_unit->set.count); + + for (int32_t i = 0; i < custom_funcs_unit->set.count; ++i){ + String name_string = custom_funcs_unit->set.items[i].name; + String *macro = &custom_func_names->names[i].macro; + String *public_name = &custom_func_names->names[i].public_name; + + *macro = str_alloc(part, name_string.size+4); + to_upper_ss(macro, name_string); + append_ss(macro, make_lit_string("_SIG")); + + *public_name = str_alloc(part, name_string.size); + to_lower_ss(public_name, name_string); + + partition_align(part, 4); + } + + // NOTE(allen): Load enriched text materials + *introduction = load_enriched_text(part, src_directory, "introduction.txt"); + *lexer_introduction = load_enriched_text(part, src_directory, "lexer_introduction.txt"); + + // NOTE(allen): Put together the abstract document + memset(doc, 0, sizeof(*doc)); + begin_document_description(doc, part, "4coder API Docs"); + set_document_name(doc, API_DOC); + + add_table_of_contents(doc); + + begin_section(doc, "Introduction", "introduction"); + add_enriched_text(doc, introduction); + end_section(doc); + + begin_section(doc, "4coder Systems", "4coder_systems"); + add_todo(doc); + end_section(doc); + + begin_section(doc, "Types and Functions", "types_and_functions"); + { + begin_section(doc, "Function List", 0); + add_element_list(doc, custom_funcs_unit, custom_func_names, AltName_Public_Name); + end_section(doc); + begin_section(doc, "Type List", 0); + add_element_list(doc, custom_types_unit); + end_section(doc); + begin_section(doc, "Function Descriptions", 0); + add_full_elements(doc, custom_funcs_unit, custom_func_names, AltName_Public_Name); + end_section(doc); + begin_section(doc, "Type Descriptions", 0); + add_full_elements(doc, custom_types_unit); + end_section(doc); + } + end_section(doc); + + begin_section(doc, "String Library", "string_library"); + { + begin_section(doc, "String Library Intro", 0); + add_todo(doc); + end_section(doc); + begin_section(doc, "String Function List", 0); + add_element_list(doc, string_unit); + end_section(doc); + begin_section(doc, "String Function Descriptions", 0); + add_full_elements(doc, string_unit); + end_section(doc); + } + end_section(doc); + + begin_section(doc, "Lexer Library", "lexer_library"); + { + begin_section(doc, "Lexer Intro", 0); + add_enriched_text(doc, lexer_introduction); + end_section(doc); + begin_section(doc, "Lexer Function List", 0); + add_element_list(doc, lexer_funcs_unit); + end_section(doc); + begin_section(doc, "Lexer Type List", 0); + add_element_list(doc, lexer_types_unit); + end_section(doc); + begin_section(doc, "Lexer Function Descriptions", 0); + add_full_elements(doc, lexer_funcs_unit); + end_section(doc); + begin_section(doc, "Lexer Type Descriptions", 0); + add_full_elements(doc, lexer_types_unit); + end_section(doc); + } + end_section(doc); + + end_document_description(doc); + + // NOTE(allen): Output + Temp_Memory temp = begin_temp_memory(part); + String out = str_alloc(part, 10 << 20); + Out_Context context = {0}; + set_context_directory(&context, dst_directory); + + // Output Docs + if (begin_file_out(&context, doc->name, &out)){ + generate_document_html(&out, part, doc); + end_file_out(context); + } + else{ + fprintf(stderr, "Failed to open %s", doc->name); + } + end_temp_memory(temp); + + return(doc); + } + + static Abstract_Document* + generate_feature_list(Partition *part, char *src_directory, char *dst_directory){ + Enriched_Text *feature_list = push_struct(part, Enriched_Text); + Abstract_Document *doc = push_struct(part, Abstract_Document); + + *feature_list = load_enriched_text(part, src_directory, "feature_list.txt"); + + // NOTE(allen): Put together the abstract document + memset(doc, 0, sizeof(*doc)); + begin_document_description(doc, part, "4coder Feature List"); + set_document_name(doc, "4coder_features.html"); + + add_enriched_text(doc, feature_list); + + end_document_description(doc); + + // NOTE(allen): Output + Temp_Memory temp = begin_temp_memory(part); + String out = str_alloc(part, 10 << 20); + Out_Context context = {0}; + set_context_directory(&context, dst_directory); + + // Output Docs + if (begin_file_out(&context, doc->name, &out)){ + generate_document_html(&out, part, doc); + end_file_out(context); + } + else{ + fprintf(stderr, "Failed to open %s", doc->name); + } + end_temp_memory(temp); + + return(doc); + } + static void generate_site(char *code_directory, char *src_directory, char *dst_directory){ -#define API_DOC "4coder_API.html" - int32_t size = (512 << 20); void *mem = malloc(size); memset(mem, 0, size); - + Partition part_ = make_part(mem, size); Partition *part = &part_; - - static Meta_Keywords meta_keywords[] = { - {make_lit_string("API_EXPORT") , Item_Function } , - {make_lit_string("API_EXPORT_INLINE") , Item_Function } , - {make_lit_string("API_EXPORT_MACRO") , Item_Macro } , - {make_lit_string("CPP_NAME") , Item_CppName } , - {make_lit_string("TYPEDEF") , Item_Typedef } , - {make_lit_string("STRUCT") , Item_Struct } , - {make_lit_string("UNION") , Item_Union } , - {make_lit_string("ENUM") , Item_Enum } , - }; - -#define ExpandArray(a) (a), (ArrayCount(a)) - - // NOTE(allen): Parse the important code. - Meta_Unit custom_types_unit = compile_meta_unit(part, code_directory, "4coder_types.h", ExpandArray(meta_keywords)); - - Meta_Unit lexer_funcs_unit = compile_meta_unit(part, code_directory, "4cpp_lexer.h", ExpandArray(meta_keywords)); - - Meta_Unit lexer_types_unit = compile_meta_unit(part, code_directory, "4cpp_lexer_types.h", ExpandArray(meta_keywords)); - - Meta_Unit string_unit = compile_meta_unit(part, code_directory, "internal_4coder_string.cpp", ExpandArray(meta_keywords)); - - static char *functions_files[] = { - "4ed_api_implementation.cpp", - "win32_api_impl.cpp", - 0 - }; - - Meta_Unit custom_funcs_unit = compile_meta_unit(part, code_directory, functions_files, ExpandArray(meta_keywords)); - - - // NOTE(allen): Compute and store variations of the custom function names - Alternate_Names_Array custom_func_names = allocate_app_api(part, custom_funcs_unit.set.count); - - for (int32_t i = 0; i < custom_funcs_unit.set.count; ++i){ - String name_string = custom_funcs_unit.set.items[i].name; - String *macro = &custom_func_names.names[i].macro; - String *public_name = &custom_func_names.names[i].public_name; - - *macro = str_alloc(part, name_string.size+4); - to_upper_ss(macro, name_string); - append_ss(macro, make_lit_string("_SIG")); - - *public_name = str_alloc(part, name_string.size); - to_lower_ss(public_name, name_string); - - partition_align(part, 4); - } - - // NOTE(allen): Load enriched text materials - Enriched_Text introduction = load_enriched_text(part, src_directory, "introduction.txt"); - Enriched_Text lexer_introduction = load_enriched_text(part, src_directory, "lexer_introduction.txt"); - - // NOTE(allen): Put together the abstract document - Abstract_Document doc = {0}; - begin_document_description(&doc, part, "4coder API Docs"); - - add_table_of_contents(&doc); - - begin_section(&doc, "Introduction", "introduction"); - add_enriched_text(&doc, &introduction); - end_section(&doc); - - begin_section(&doc, "4coder Systems", "4coder_systems"); - add_todo(&doc); - end_section(&doc); - - begin_section(&doc, "Types and Functions", "types_and_functions"); - { - begin_section(&doc, "Function List", 0); - add_element_list(&doc, &custom_funcs_unit, &custom_func_names, AltName_Public_Name); - end_section(&doc); - begin_section(&doc, "Type List", 0); - add_element_list(&doc, &custom_types_unit); - end_section(&doc); - begin_section(&doc, "Function Descriptions", 0); - add_full_elements(&doc, &custom_funcs_unit, &custom_func_names, AltName_Public_Name); - end_section(&doc); - begin_section(&doc, "Type Descriptions", 0); - add_full_elements(&doc, &custom_types_unit); - end_section(&doc); - } - end_section(&doc); - - begin_section(&doc, "String Library", "string_library"); - { - begin_section(&doc, "String Library Intro", 0); - add_todo(&doc); - end_section(&doc); - begin_section(&doc, "String Function List", 0); - add_element_list(&doc, &string_unit); - end_section(&doc); - begin_section(&doc, "String Function Descriptions", 0); - add_full_elements(&doc, &string_unit); - end_section(&doc); - } - end_section(&doc); - - begin_section(&doc, "Lexer Library", "lexer_library"); - { - begin_section(&doc, "Lexer Intro", 0); - add_enriched_text(&doc, &lexer_introduction); - end_section(&doc); - begin_section(&doc, "Lexer Function List", 0); - add_element_list(&doc, &lexer_funcs_unit); - end_section(&doc); - begin_section(&doc, "Lexer Type List", 0); - add_element_list(&doc, &lexer_types_unit); - end_section(&doc); - begin_section(&doc, "Lexer Function Descriptions", 0); - add_full_elements(&doc, &lexer_funcs_unit); - end_section(&doc); - begin_section(&doc, "Lexer Type Descriptions", 0); - add_full_elements(&doc, &lexer_types_unit); - end_section(&doc); - } - end_section(&doc); - - end_document_description(&doc); - - // NOTE(allen): Output - String out = str_alloc(part, 10 << 20); - Out_Context context = {0}; - set_context_directory(&context, dst_directory); - - // Output Docs - if (begin_file_out(&context, API_DOC, &out)){ - generate_document_html(&out, part, &doc); - end_file_out(context); - } - else{ - fprintf(stderr, "Failed to open %s", API_DOC); - } + + Abstract_Document *api_document = generate_4coder_API(part, code_directory, src_directory, dst_directory); + + Abstract_Document *feature_list = generate_feature_list(part, src_directory, dst_directory); } diff --git a/site/source_material/feature_list.txt b/site/source_material/feature_list.txt new file mode 100644 index 00000000..a9eb6290 --- /dev/null +++ b/site/source_material/feature_list.txt @@ -0,0 +1,30 @@ + +\SECTION{Text Editing} + \BEGIN_LIST + \BEGIN_ITEM Cursor and mark editing paradigm \END_ITEM + \BEGIN_ITEM Navigation by characters, words, tokens, lines, and blank lines \END_ITEM + \BEGIN_ITEM Modern style undo and redo \END_ITEM + \BEGIN_ITEM Word complete with matching words in open files \END_ITEM + \BEGIN_ITEM Incremental word search \END_ITEM + \BEGIN_ITEM Word replace \END_ITEM + \BEGIN_ITEM List all occurences of words in open buffers \END_ITEM + \END_LIST + +\SECTION{C/C++ Editing} + \BEGIN_LIST + \BEGIN_ITEM Code highlighting for C/C++ code \END_ITEM + \BEGIN_ITEM Code auto-layout rendering engine \END_ITEM + \BEGIN_ITEM Text level auto-indenting \END_ITEM + \BEGIN_ITEM Build in editor \END_ITEM + \BEGIN_ITEM Jump to error \END_ITEM + \END_LIST + +\SECTION{The Customization API} + \BEGIN_LIST + \BEGIN_ITEM Easy key rebinding \END_ITEM + \BEGIN_ITEM Create custom commands \END_ITEM + \BEGIN_ITEM Buffer reading and editing \END_ITEM + \BEGIN_ITEM Buffer reading and editing \END_ITEM + \BEGIN_ITEM View opening, closing, positioning \END_ITEM + \END_LIST +