Ticket #4193: OtherFileConcept_v3.5.patch
File OtherFileConcept_v3.5.patch, 20.2 KB (added by psprint, 4 years ago) |
---|
-
doc/man/mcedit.1.in
From 770d3d9f530e93b6795e97a7cce8cab500225aa2 Mon Sep 17 00:00:00 2001 From: Sebastian Gniazdowski <sgniazdowski@gmail.com> Date: Tue, 2 Feb 2021 12:50:19 -0600 Subject: Other File concept: jumping between headers and sources. --- doc/man/mcedit.1.in | 25 +++++- lib/keybind.c | 1 + lib/keybind.h | 1 + lib/vfs/path.c | 43 ++++++++++ lib/vfs/path.h | 2 + misc/mc.default.keymap | 1 + misc/mc.emacs.keymap | 1 + src/editor/edit-impl.h | 13 ++- src/editor/edit.c | 30 +++++++ src/editor/edit.h | 5 ++ src/editor/editcmd.c | 11 ++- src/editor/editwidget.c | 185 +++++++++++++++++++++++++++++++++++++++- src/editor/editwidget.h | 1 + src/keybind-defaults.c | 1 + src/setup.c | 5 ++ 15 files changed, 317 insertions(+), 8 deletions(-) diff --git a/doc/man/mcedit.1.in b/doc/man/mcedit.1.in index 33e5f34b7..acd46ca8a 100644
a b supports are: block copy, move, delete, cut, paste; key for key undo; 62 62 pull\-down menus; file insertion; macro commands; regular expression 63 63 search and replace; shift\-arrow text highlighting (if supported by 64 64 the terminal); insert\-overwrite toggle; autoindent; tunable tab size; 65 syntax highlighting for various file types; and an option to pipe text 65 syntax highlighting for various file types; switching between headers 66 and sources (Alt-a by default), and an option to pipe text 66 67 blocks through shell commands like indent and ispell. 67 68 .PP 68 69 Each file is opened in its own window in full\-screen mode. Window control … … The macro is executed when you press the assigned key. 111 112 .PP 112 113 The macro commands are stored in section 113 114 .B [editor] 114 i tthe file115 in the file 115 116 .BR ~/.local/share/mc/mc.macros . 116 117 .PP 117 118 External scripts (filters) can be assigned into the any hotkey by edit … … will be untouched. Default value is 603 604 .I editor_state_full_filename 604 605 Show full path name in the status line. If disabled (default), only base name of the 605 606 file is shown. 607 .TP 608 .I editor_other_file_1_exts 609 MCEdit can quickly switch between 610 .BR source " and " interface 611 files – in case of the latter, being typically e.g.: the header files of 612 C/C++. An example pair of such files is, e.g.: 613 .BR main.c " and " main.h 614 and pressing Alt-a (the default binding of the feature's action) would switch 615 between them back and forth, first loading the file into the editor, if 616 needed. This option allows to configure the feature by specifying a comma 617 separated list of the name extensions of the files (including dots) to be 618 recognized as the interface-kind files. Its default value supports C/C++ 619 headers 620 .RB ( .h ", " .hpp ", etc.)." 621 .TP 622 .I editor_other_file_2_exts 623 This option allows to configure the other-file feature (called also: alternate 624 file) by specifying the file name extensions for the source-kind files. Its 625 default value supports typical sources written in C/C++ languages 626 .RB ( .c ", " .cpp ", " .cxx ", etc.)." 606 627 .SH MISCELLANEOUS 607 628 The editor also displays non\-us characters (160+). When editing 608 629 binary files, you should set -
lib/keybind.c
diff --git a/lib/keybind.c b/lib/keybind.c index abd44d3e2..2ec715bfa 100644
a b static name_keymap_t command_names[] = { 277 277 ADD_KEYMAP_NAME (ParagraphUp), 278 278 ADD_KEYMAP_NAME (ParagraphDown), 279 279 ADD_KEYMAP_NAME (EditFile), 280 ADD_KEYMAP_NAME (OtherFile), 280 281 ADD_KEYMAP_NAME (MarkWord), 281 282 ADD_KEYMAP_NAME (MarkLine), 282 283 ADD_KEYMAP_NAME (MarkAll), -
lib/keybind.h
diff --git a/lib/keybind.h b/lib/keybind.h index af019df09..cd7192d06 100644
a b enum 246 246 CK_ParagraphDown, 247 247 /* file commands */ 248 248 CK_EditFile, 249 CK_OtherFile, 249 250 CK_InsertFile, 250 251 CK_EditSyntaxFile, 251 252 CK_Close, -
lib/vfs/path.c
diff --git a/lib/vfs/path.c b/lib/vfs/path.c index 49553198f..22d4a81e6 100644
a b vfs_path_strip_home (const char *dir) 593 593 /*** public functions ****************************************************************************/ 594 594 /* --------------------------------------------------------------------------------------------- */ 595 595 596 /** 597 * Returns TRUE if path has one of the given extensions (in a NULL terminated array). 598 * The extension strings should include the dot. 599 */ 600 gboolean 601 vfs_path_has_extension (const vfs_path_t * fs_path, const char **exts, gboolean ignore_case) 602 { 603 const char *path, **cur_ext; 604 char *upcase_path, *upcase_ext; 605 gboolean ret = TRUE; 606 607 path = vfs_path_as_str (fs_path); 608 609 /* 610 * The extensions should be only ASCII, so use g_ascii_strup() which also 611 * safely leaves non-ASCII chars unchanged. 612 */ 613 upcase_path = g_ascii_strup (path, -1); 614 615 for (cur_ext = exts; cur_ext != NULL && *cur_ext != NULL; cur_ext++) 616 { 617 if (ignore_case) 618 { 619 upcase_ext = g_ascii_strup (*cur_ext, -1); 620 if (g_str_has_suffix (upcase_path, upcase_ext)) 621 goto ret_true; 622 g_free (upcase_ext); 623 upcase_ext = NULL; 624 } 625 else if (g_str_has_suffix (path, *cur_ext)) 626 goto ret_true; 627 } 628 629 ret = FALSE; 630 ret_true: 631 if (upcase_ext != NULL) 632 g_free (upcase_ext); 633 g_free (upcase_path); 634 return ret; 635 } 636 637 /* --------------------------------------------------------------------------------------------- */ 638 596 639 #define vfs_append_from_path(appendfrom, is_relative) \ 597 640 { \ 598 641 if ((flags & VPF_STRIP_HOME) && element_index == 0 && \ -
lib/vfs/path.h
diff --git a/lib/vfs/path.h b/lib/vfs/path.h index c5dc4f5a4..802d0b17f 100644
a b char *vfs_path_to_str_elements_count (const vfs_path_t * path, int elements_coun 67 67 char *vfs_path_to_str_flags (const vfs_path_t * vpath, int elements_count, vfs_path_flag_t flags); 68 68 vfs_path_t *vfs_path_from_str (const char *path_str); 69 69 vfs_path_t *vfs_path_from_str_flags (const char *path_str, vfs_path_flag_t flags); 70 gboolean vfs_path_has_extension (const vfs_path_t * fs_path, const char **exts, 71 gboolean ignore_case); 70 72 vfs_path_t *vfs_path_build_filename (const char *first_element, ...); 71 73 vfs_path_t *vfs_path_append_new (const vfs_path_t * vpath, const char *first_element, ...); 72 74 vfs_path_t *vfs_path_append_vpath_new (const vfs_path_t * first_vpath, ...); -
misc/mc.default.keymap
diff --git a/misc/mc.default.keymap b/misc/mc.default.keymap index 2931ddd0a..075c8e4ce 100644
a b DeleteToEnd = ctrl-k 291 291 Save = f2 292 292 # EditFile = 293 293 EditNew = ctrl-n 294 OtherFile = alt-a 294 295 SaveAs = f12; ctrl-f2 295 296 # Close = 296 297 History = alt-shift-e -
misc/mc.emacs.keymap
diff --git a/misc/mc.emacs.keymap b/misc/mc.emacs.keymap index 7cc305db7..bff42b0a0 100644
a b DeleteToEnd = ctrl-k 290 290 # ParagraphDown = 291 291 Save = f2 292 292 # EditFile = 293 OtherFile = alt-a 293 294 SaveAs = f12; ctrl-f2 294 295 # Close = 295 296 History = alt-shift-e -
src/editor/edit-impl.h
diff --git a/src/editor/edit-impl.h b/src/editor/edit-impl.h index 3ad04dbea..90dc5dedb 100644
a b typedef enum 94 94 EDIT_DO_BACKUP 95 95 } edit_save_mode_t; 96 96 97 98 /* Describes how well a file suits for editing. Symlinks and empty files get average grade. */ 99 typedef enum 100 { 101 FILE_RANK_INVALID = -1, 102 FILE_RANK_NOT_SUITABLE = 0, 103 FILE_RANK_AVERAGE_SUITABLE, 104 FILE_RANK_SUITABLE 105 } file_suitable_rank_t; 106 97 107 /*** structures declarations (and typedefs of structures)*****************************************/ 98 108 99 109 /* search/replace options */ … … WEdit *edit_init (WEdit * edit, int y, int x, int lines, int cols, 185 195 const vfs_path_t * filename_vpath, long line); 186 196 gboolean edit_clean (WEdit * edit); 187 197 gboolean edit_ok_to_exit (WEdit * edit); 188 gboolean edit_load_cmd (WDialog * h); 198 file_suitable_rank_t edit_check_file_suitable (const vfs_path_t * fs_path); 199 gboolean edit_load_cmd (WDialog * h, const void *data); 189 200 gboolean edit_load_file_from_history (WDialog * h); 190 201 gboolean edit_load_syntax_file (WDialog * h); 191 202 gboolean edit_load_menu_file (WDialog * h); -
src/editor/edit.c
diff --git a/src/editor/edit.c b/src/editor/edit.c index edda1f832..98882b241 100644
a b int option_line_state_width = 0; 91 91 gboolean option_cursor_after_inserted_block = FALSE; 92 92 gboolean option_state_full_filename = FALSE; 93 93 94 char *option_other_file_1_exts; 95 char *option_other_file_2_exts; 96 94 97 gboolean enable_show_tabs_tws = TRUE; 95 98 gboolean option_check_nl_at_eof = FALSE; 96 99 gboolean option_group_undo = FALSE; … … edit_insert_stream (WEdit * edit, FILE * f) 277 280 return i; 278 281 } 279 282 283 /* --------------------------------------------------------------------------------------------- */ 284 /** 285 * Gives a 3-level evaluation of how well given file is looking as a suitable input to the editor. 286 */ 287 file_suitable_rank_t 288 edit_check_file_suitable (const vfs_path_t * fs_path) 289 { 290 struct stat lst, st; 291 292 if (fs_path == NULL) 293 return FILE_RANK_INVALID; 294 295 if (exist_file (vfs_path_as_str (fs_path))) 296 { 297 if (mc_lstat (fs_path, &lst) == 0 && mc_stat (fs_path, &st) == 0) 298 { 299 if (st.st_size != 0 && S_ISREG (st.st_mode) && !S_ISLNK (lst.st_mode)) 300 return FILE_RANK_SUITABLE; 301 else 302 return FILE_RANK_AVERAGE_SUITABLE; 303 } 304 } 305 return FILE_RANK_NOT_SUITABLE; 306 } 307 280 308 /* --------------------------------------------------------------------------------------------- */ 281 309 /** 282 310 * Open file and create it if necessary. … … edit_init (WEdit * edit, int y, int x, int lines, int cols, const vfs_path_t * f 2108 2136 w->options |= WOP_SELECTABLE | WOP_TOP_SELECT | WOP_WANT_CURSOR; 2109 2137 w->keymap = editor_map; 2110 2138 w->ext_keymap = editor_x_map; 2139 edit->otherfile_vpath = NULL; 2111 2140 edit->fullscreen = TRUE; 2112 2141 edit_save_size (edit); 2113 2142 } … … edit_clean (WEdit * edit) 2204 2233 g_free (edit->redo_stack); 2205 2234 vfs_path_free (edit->filename_vpath); 2206 2235 vfs_path_free (edit->dir_vpath); 2236 vfs_path_free (edit->otherfile_vpath); 2207 2237 mc_search_free (edit->search); 2208 2238 g_free (edit->last_search_string); 2209 2239 -
src/editor/edit.h
diff --git a/src/editor/edit.h b/src/editor/edit.h index 6c519e9d3..e3b9b7fd8 100644
a b extern char *option_stop_format_chars; 53 53 54 54 extern gboolean edit_confirm_save; 55 55 56 extern char *option_other_file_1_exts; 57 extern char *option_other_file_2_exts; 58 56 59 extern gboolean visible_tabs; 57 60 extern gboolean visible_tws; 58 61 … … extern gboolean show_right_margin; 66 69 void edit_stack_init (void); 67 70 void edit_stack_free (void); 68 71 72 /* If file is open, switch to it, otherwise it is loaded */ 73 gboolean edit_switch_to_file (WDialog * h, const vfs_path_t * file); 69 74 gboolean edit_file (const vfs_path_t * file_vpath, long line); 70 75 gboolean edit_files (const GList * files); 71 76 -
src/editor/editcmd.c
diff --git a/src/editor/editcmd.c b/src/editor/editcmd.c index 0d2caa923..661655d58 100644
a b edit_save_confirm_cmd (WEdit * edit) 2063 2063 */ 2064 2064 2065 2065 gboolean 2066 edit_load_cmd (WDialog * h )2066 edit_load_cmd (WDialog * h, const void *data) 2067 2067 { 2068 2068 char *exp; 2069 2069 gboolean ret = TRUE; /* possible cancel */ 2070 2070 2071 exp = input_expand_dialog (_("Load"), _("Enter file name:"), 2072 MC_HISTORY_EDIT_LOAD, INPUT_LAST_TEXT, 2073 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD); 2071 if (data != NULL) 2072 exp = g_strdup ((char *) data); 2073 else 2074 exp = input_expand_dialog (_("Load"), _("Enter file name:"), 2075 MC_HISTORY_EDIT_LOAD, INPUT_LAST_TEXT, 2076 INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD); 2074 2077 2075 2078 if (exp != NULL && *exp != '\0') 2076 2079 { -
src/editor/editwidget.c
diff --git a/src/editor/editwidget.c b/src/editor/editwidget.c index 18ac00e66..8547a44de 100644
a b static unsigned int edit_dlg_init_refcounter = 0; 88 88 static cb_ret_t edit_dialog_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, 89 89 void *data); 90 90 91 /* --------------------------------------------------------------------------------------------- */ 92 93 static char * 94 replace_suffix_with (const char *src_str, const char *old_suffix, const char *new_suffix) 95 { 96 char *new_str, *sufx_ptr; 97 new_str = g_strndup (src_str, strlen (src_str) + strlen (new_suffix)); 98 sufx_ptr = g_strrstr (new_str, old_suffix); 99 if (sufx_ptr != NULL) 100 { 101 g_stpcpy (sufx_ptr, new_suffix); 102 return new_str; 103 } 104 else 105 { 106 g_free (new_str); 107 return NULL; 108 } 109 } 110 111 /* --------------------------------------------------------------------------------------------- */ 112 /** 113 * Fills otherfile_vpath field with a detected and verified alternate (also called `other`) 114 * file path. In short, the other file is a header main.h when editing main.c and vice versa. 115 */ 116 static gboolean 117 edit_compute_other_file_vfs_path (WEdit * edit) 118 { 119 /* Default values, inspired by output of: ctags --list-map-extensions */ 120 char **headers; 121 char **sources; 122 char **exts[2] = { 0 }; 123 vfs_path_t *fs_path, *avg_path; 124 int idx, oth_type = -1, ext_index; 125 file_suitable_rank_t existing_rank; 126 gboolean ret = FALSE; 127 128 headers = g_strsplit (option_other_file_1_exts, ",", 0); 129 sources = g_strsplit (option_other_file_2_exts, ",", 0); 130 131 if ((headers == NULL || headers[0] == NULL) && (sources == NULL || sources[0] == NULL)) 132 goto other_ret; 133 134 exts[0] = headers; 135 exts[1] = sources; 136 137 /* Try the already computed one if it exists in otherfile_vpath field */ 138 fs_path = edit->otherfile_vpath; 139 avg_path = fs_path; 140 edit->otherfile_vpath = NULL; 141 142 /* Is it only an average match or no match? If yes, try to find a better one */ 143 existing_rank = edit_check_file_suitable (fs_path); 144 if (existing_rank <= FILE_RANK_AVERAGE_SUITABLE && edit->filename_vpath != NULL) 145 { 146 for (idx = 0; idx <= 1; idx++) 147 { 148 if (vfs_path_has_extension (edit->filename_vpath, (const char **) exts[idx], TRUE)) 149 { 150 oth_type = 1 - idx; 151 break; 152 } 153 } 154 if (oth_type >= 0) 155 fs_path = edit->filename_vpath; 156 } 157 /* No extension matched, or using the previously, highly suited file */ 158 if (oth_type == -1) 159 { 160 edit->otherfile_vpath = avg_path; 161 ret = (existing_rank >= FILE_RANK_AVERAGE_SUITABLE); 162 goto other_ret; 163 } 164 165 for (ext_index = 0; exts[oth_type][ext_index] != NULL; ext_index++) 166 { 167 char *try_path; 168 vfs_path_t *cand_fs_path = NULL; 169 file_suitable_rank_t rank; 170 171 try_path = replace_suffix_with (vfs_path_as_str (fs_path), ".", exts[oth_type][ext_index]); 172 173 if (try_path == NULL) 174 continue; 175 176 cand_fs_path = vfs_path_from_str (try_path); 177 g_free (try_path); 178 179 if (cand_fs_path == NULL) 180 continue; 181 182 rank = edit_check_file_suitable (cand_fs_path); 183 if (rank < FILE_RANK_AVERAGE_SUITABLE) 184 { 185 vfs_path_free (cand_fs_path); 186 cand_fs_path = NULL; 187 continue; 188 } 189 else if (rank == FILE_RANK_AVERAGE_SUITABLE) 190 { 191 if (existing_rank < rank) 192 { 193 existing_rank = rank; 194 if (avg_path != NULL) 195 vfs_path_free (avg_path); 196 avg_path = cand_fs_path; 197 cand_fs_path = NULL; 198 } 199 else 200 { 201 vfs_path_free (cand_fs_path); 202 cand_fs_path = NULL; 203 } 204 } 205 else if (rank >= FILE_RANK_SUITABLE) 206 { 207 if (avg_path != NULL) 208 vfs_path_free (avg_path); 209 edit->otherfile_vpath = cand_fs_path; 210 ret = TRUE; 211 goto other_ret; 212 } 213 } 214 215 /* Any side-saved average candidate? */ 216 if (avg_path != NULL) 217 { 218 edit->otherfile_vpath = avg_path; 219 ret = (existing_rank >= FILE_RANK_AVERAGE_SUITABLE); 220 } 221 222 other_ret: 223 if (sources != NULL) 224 g_strfreev (sources); 225 if (headers != NULL) 226 g_strfreev (headers); 227 return ret; 228 } 229 91 230 /* --------------------------------------------------------------------------------------------- */ 92 231 /** 93 232 * Init the 'edit' subsystem … … edit_dialog_command_execute (WDialog * h, long command) 391 530 edit_add_window (h, wh->y + 1, wh->x, wh->lines - 2, wh->cols, NULL, 0); 392 531 break; 393 532 case CK_EditFile: 394 edit_load_cmd (h); 533 edit_load_cmd (h, NULL); 534 break; 535 case CK_OtherFile: 536 { 537 WEdit *e = (WEdit *) g->current->data; 538 gboolean retflag = FALSE; 539 540 if (e != NULL && edit_widget_is_editor (CONST_WIDGET (e))) 541 { 542 retflag = edit_compute_other_file_vfs_path (e); 543 if (retflag) 544 retflag = edit_switch_to_file (h, e->otherfile_vpath); 545 } 546 if (!retflag) 547 ret = MSG_NOT_HANDLED; 548 } 395 549 break; 396 550 case CK_History: 397 551 edit_load_file_from_history (h); … … edit_mouse_callback (Widget * w, mouse_msg_t msg, mouse_event_t * event) 1178 1332 /* --------------------------------------------------------------------------------------------- */ 1179 1333 /*** public functions ****************************************************************************/ 1180 1334 /* --------------------------------------------------------------------------------------------- */ 1335 1336 gboolean 1337 edit_switch_to_file (WDialog * h, const vfs_path_t * file) 1338 { 1339 const WGroup *g = CONST_GROUP (h); 1340 GList *el; 1341 gboolean ret = FALSE; 1342 1343 for (el = g->widgets; el != NULL; el = g_list_next (el)) 1344 { 1345 if (edit_widget_is_editor (CONST_WIDGET (el->data))) 1346 { 1347 WEdit *e = (WEdit *) el->data; 1348 if (g_strcmp0 (edit_get_file_name (e), vfs_path_as_str (file)) == 0) 1349 { 1350 widget_select (WIDGET (e)); 1351 ret = TRUE; 1352 break; 1353 } 1354 } 1355 } 1356 /* File not loaded? -> If it is so, then open it. */ 1357 if (!ret) 1358 ret = edit_load_cmd (h, vfs_path_as_str (file)); 1359 return ret; 1360 } 1361 1362 /* --------------------------------------------------------------------------------------------- */ 1363 1181 1364 /** 1182 1365 * Edit one file. 1183 1366 * -
src/editor/editwidget.h
diff --git a/src/editor/editwidget.h b/src/editor/editwidget.h index 446ef07ac..b4b10692e 100644
a b struct WEdit 72 72 73 73 vfs_path_t *filename_vpath; /* Name of the file */ 74 74 vfs_path_t *dir_vpath; /* NULL if filename is absolute */ 75 vfs_path_t *otherfile_vpath; /* Name of the `other` file (e.g.: header, with .h extension) */ 75 76 76 77 /* dynamic buffers and cursor position for editor: */ 77 78 edit_buffer_t buffer; -
src/keybind-defaults.c
diff --git a/src/keybind-defaults.c b/src/keybind-defaults.c index 7b87c2f5a..aa4eb5ec0 100644
a b static const global_keymap_ini_t default_editor_keymap[] = { 393 393 {"InsertOverwrite", "insert"}, 394 394 {"Help", "f1"}, 395 395 {"Save", "f2"}, 396 {"OtherFile", "alt-a"}, 396 397 {"Mark", "f3"}, 397 398 {"Replace", "f4"}, 398 399 {"Copy", "f5"}, -
src/setup.c
diff --git a/src/setup.c b/src/setup.c index 77c07649d..c8fde7230 100644
a b static const struct 404 404 const char *opt_defval; 405 405 } str_options[] = { 406 406 #ifdef USE_INTERNAL_EDIT 407 { "editor_other_file_1_exts", &option_other_file_1_exts, 408 ".h,.hpp,.h++,.hh,.hp," 409 ".hxx,.inl,.inc,.def" }, 410 { "editor_other_file_2_exts", &option_other_file_2_exts, 411 ".c,.cpp,.c++,.cc,.cp,.cxx,.c+"}, 407 412 { "editor_backup_extension", &option_backup_ext, "~" }, 408 413 { "editor_filesize_threshold", &option_filesize_threshold, "64M" }, 409 414 { "editor_stop_format_chars", &option_stop_format_chars, "-+*\\,.;:&>" },