comparison src/ex_docmd.c @ 17536:e00d12c085a5 v8.1.1766

patch 8.1.1766: code for writing session file is spread out commit https://github.com/vim/vim/commit/845380791196aec7f991987ebf7b22de3779d106 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Jul 28 14:15:42 2019 +0200 patch 8.1.1766: code for writing session file is spread out Problem: Code for writing session file is spread out. Solution: Put it in one file. (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/4728)
author Bram Moolenaar <Bram@vim.org>
date Sun, 28 Jul 2019 14:30:07 +0200
parents c8152af9fa33
children 554240b9574b
comparison
equal deleted inserted replaced
17535:3015901aaaa6 17536:e00d12c085a5
255 static void ex_later(exarg_T *eap); 255 static void ex_later(exarg_T *eap);
256 static void ex_redir(exarg_T *eap); 256 static void ex_redir(exarg_T *eap);
257 static void ex_redrawstatus(exarg_T *eap); 257 static void ex_redrawstatus(exarg_T *eap);
258 static void ex_redrawtabline(exarg_T *eap); 258 static void ex_redrawtabline(exarg_T *eap);
259 static void close_redir(void); 259 static void close_redir(void);
260 static void ex_mkrc(exarg_T *eap);
261 static void ex_mark(exarg_T *eap); 260 static void ex_mark(exarg_T *eap);
262 static void ex_startinsert(exarg_T *eap); 261 static void ex_startinsert(exarg_T *eap);
263 static void ex_stopinsert(exarg_T *eap); 262 static void ex_stopinsert(exarg_T *eap);
264 #ifdef FEAT_FIND_ID 263 #ifdef FEAT_FIND_ID
265 static void ex_checkpath(exarg_T *eap); 264 static void ex_checkpath(exarg_T *eap);
304 # define ex_delfunction ex_ni 303 # define ex_delfunction ex_ni
305 # define ex_return ex_ni 304 # define ex_return ex_ni
306 # define ex_oldfiles ex_ni 305 # define ex_oldfiles ex_ni
307 #endif 306 #endif
308 static char_u *arg_all(void); 307 static char_u *arg_all(void);
309 #ifdef FEAT_SESSION 308 #ifndef FEAT_SESSION
310 static int makeopens(FILE *fd, char_u *dirnow);
311 static int put_view(FILE *fd, win_T *wp, int add_edit, unsigned *flagp, int current_arg_idx);
312 static void ex_loadview(exarg_T *eap);
313 static char_u *get_view_file(int c);
314 static int did_lcd; /* whether ":lcd" was produced for a session */
315 #else
316 # define ex_loadview ex_ni 309 # define ex_loadview ex_ni
310 # define ex_mkrc ex_ni
317 #endif 311 #endif
318 #ifndef FEAT_EVAL 312 #ifndef FEAT_EVAL
319 # define ex_compiler ex_ni 313 # define ex_compiler ex_ni
320 #endif 314 #endif
321 #ifndef FEAT_VIMINFO 315 #ifndef FEAT_VIMINFO
8437 redir_vname = 0; 8431 redir_vname = 0;
8438 } 8432 }
8439 #endif 8433 #endif
8440 } 8434 }
8441 8435
8442 #if defined(FEAT_SESSION) && defined(USE_CRNL)
8443 # define MKSESSION_NL
8444 static int mksession_nl = FALSE; /* use NL only in put_eol() */
8445 #endif
8446
8447 /*
8448 * ":mkexrc", ":mkvimrc", ":mkview" and ":mksession".
8449 */
8450 static void
8451 ex_mkrc(
8452 exarg_T *eap)
8453 {
8454 FILE *fd;
8455 int failed = FALSE;
8456 char_u *fname;
8457 #ifdef FEAT_BROWSE
8458 char_u *browseFile = NULL;
8459 #endif
8460 #ifdef FEAT_SESSION
8461 int view_session = FALSE;
8462 int using_vdir = FALSE; /* using 'viewdir'? */
8463 char_u *viewFile = NULL;
8464 unsigned *flagp;
8465 #endif
8466
8467 if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview)
8468 {
8469 #ifdef FEAT_SESSION
8470 view_session = TRUE;
8471 #else
8472 ex_ni(eap);
8473 return;
8474 #endif
8475 }
8476
8477 #ifdef FEAT_SESSION
8478 /* Use the short file name until ":lcd" is used. We also don't use the
8479 * short file name when 'acd' is set, that is checked later. */
8480 did_lcd = FALSE;
8481
8482 /* ":mkview" or ":mkview 9": generate file name with 'viewdir' */
8483 if (eap->cmdidx == CMD_mkview
8484 && (*eap->arg == NUL
8485 || (vim_isdigit(*eap->arg) && eap->arg[1] == NUL)))
8486 {
8487 eap->forceit = TRUE;
8488 fname = get_view_file(*eap->arg);
8489 if (fname == NULL)
8490 return;
8491 viewFile = fname;
8492 using_vdir = TRUE;
8493 }
8494 else
8495 #endif
8496 if (*eap->arg != NUL)
8497 fname = eap->arg;
8498 else if (eap->cmdidx == CMD_mkvimrc)
8499 fname = (char_u *)VIMRC_FILE;
8500 #ifdef FEAT_SESSION
8501 else if (eap->cmdidx == CMD_mksession)
8502 fname = (char_u *)SESSION_FILE;
8503 #endif
8504 else
8505 fname = (char_u *)EXRC_FILE;
8506
8507 #ifdef FEAT_BROWSE
8508 if (cmdmod.browse)
8509 {
8510 browseFile = do_browse(BROWSE_SAVE,
8511 # ifdef FEAT_SESSION
8512 eap->cmdidx == CMD_mkview ? (char_u *)_("Save View") :
8513 eap->cmdidx == CMD_mksession ? (char_u *)_("Save Session") :
8514 # endif
8515 (char_u *)_("Save Setup"),
8516 fname, (char_u *)"vim", NULL,
8517 (char_u *)_(BROWSE_FILTER_MACROS), NULL);
8518 if (browseFile == NULL)
8519 goto theend;
8520 fname = browseFile;
8521 eap->forceit = TRUE; /* since dialog already asked */
8522 }
8523 #endif
8524
8525 #if defined(FEAT_SESSION) && defined(vim_mkdir)
8526 /* When using 'viewdir' may have to create the directory. */
8527 if (using_vdir && !mch_isdir(p_vdir))
8528 vim_mkdir_emsg(p_vdir, 0755);
8529 #endif
8530
8531 fd = open_exfile(fname, eap->forceit, WRITEBIN);
8532 if (fd != NULL)
8533 {
8534 #ifdef FEAT_SESSION
8535 if (eap->cmdidx == CMD_mkview)
8536 flagp = &vop_flags;
8537 else
8538 flagp = &ssop_flags;
8539 #endif
8540
8541 #ifdef MKSESSION_NL
8542 /* "unix" in 'sessionoptions': use NL line separator */
8543 if (view_session && (*flagp & SSOP_UNIX))
8544 mksession_nl = TRUE;
8545 #endif
8546
8547 /* Write the version command for :mkvimrc */
8548 if (eap->cmdidx == CMD_mkvimrc)
8549 (void)put_line(fd, "version 6.0");
8550
8551 #ifdef FEAT_SESSION
8552 if (eap->cmdidx == CMD_mksession)
8553 {
8554 if (put_line(fd, "let SessionLoad = 1") == FAIL)
8555 failed = TRUE;
8556 }
8557
8558 if (eap->cmdidx != CMD_mkview)
8559 #endif
8560 {
8561 /* Write setting 'compatible' first, because it has side effects.
8562 * For that same reason only do it when needed. */
8563 if (p_cp)
8564 (void)put_line(fd, "if !&cp | set cp | endif");
8565 else
8566 (void)put_line(fd, "if &cp | set nocp | endif");
8567 }
8568
8569 #ifdef FEAT_SESSION
8570 if (!view_session
8571 || (eap->cmdidx == CMD_mksession
8572 && (*flagp & SSOP_OPTIONS)))
8573 #endif
8574 failed |= (makemap(fd, NULL) == FAIL
8575 || makeset(fd, OPT_GLOBAL, FALSE) == FAIL);
8576
8577 #ifdef FEAT_SESSION
8578 if (!failed && view_session)
8579 {
8580 if (put_line(fd, "let s:so_save = &so | let s:siso_save = &siso | set so=0 siso=0") == FAIL)
8581 failed = TRUE;
8582 if (eap->cmdidx == CMD_mksession)
8583 {
8584 char_u *dirnow; /* current directory */
8585
8586 dirnow = alloc(MAXPATHL);
8587 if (dirnow == NULL)
8588 failed = TRUE;
8589 else
8590 {
8591 /*
8592 * Change to session file's dir.
8593 */
8594 if (mch_dirname(dirnow, MAXPATHL) == FAIL
8595 || mch_chdir((char *)dirnow) != 0)
8596 *dirnow = NUL;
8597 if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR))
8598 {
8599 if (vim_chdirfile(fname, NULL) == OK)
8600 shorten_fnames(TRUE);
8601 }
8602 else if (*dirnow != NUL
8603 && (ssop_flags & SSOP_CURDIR) && globaldir != NULL)
8604 {
8605 if (mch_chdir((char *)globaldir) == 0)
8606 shorten_fnames(TRUE);
8607 }
8608
8609 failed |= (makeopens(fd, dirnow) == FAIL);
8610
8611 /* restore original dir */
8612 if (*dirnow != NUL && ((ssop_flags & SSOP_SESDIR)
8613 || ((ssop_flags & SSOP_CURDIR) && globaldir != NULL)))
8614 {
8615 if (mch_chdir((char *)dirnow) != 0)
8616 emsg(_(e_prev_dir));
8617 shorten_fnames(TRUE);
8618 }
8619 vim_free(dirnow);
8620 }
8621 }
8622 else
8623 {
8624 failed |= (put_view(fd, curwin, !using_vdir, flagp,
8625 -1) == FAIL);
8626 }
8627 if (put_line(fd, "let &so = s:so_save | let &siso = s:siso_save")
8628 == FAIL)
8629 failed = TRUE;
8630 # ifdef FEAT_SEARCH_EXTRA
8631 if (no_hlsearch && put_line(fd, "nohlsearch") == FAIL)
8632 failed = TRUE;
8633 # endif
8634 if (put_line(fd, "doautoall SessionLoadPost") == FAIL)
8635 failed = TRUE;
8636 if (eap->cmdidx == CMD_mksession)
8637 {
8638 if (put_line(fd, "unlet SessionLoad") == FAIL)
8639 failed = TRUE;
8640 }
8641 }
8642 #endif
8643 if (put_line(fd, "\" vim: set ft=vim :") == FAIL)
8644 failed = TRUE;
8645
8646 failed |= fclose(fd);
8647
8648 if (failed)
8649 emsg(_(e_write));
8650 #if defined(FEAT_EVAL) && defined(FEAT_SESSION)
8651 else if (eap->cmdidx == CMD_mksession)
8652 {
8653 /* successful session write - set this_session var */
8654 char_u *tbuf;
8655
8656 tbuf = alloc(MAXPATHL);
8657 if (tbuf != NULL)
8658 {
8659 if (vim_FullName(fname, tbuf, MAXPATHL, FALSE) == OK)
8660 set_vim_var_string(VV_THIS_SESSION, tbuf, -1);
8661 vim_free(tbuf);
8662 }
8663 }
8664 #endif
8665 #ifdef MKSESSION_NL
8666 mksession_nl = FALSE;
8667 #endif
8668 }
8669
8670 #ifdef FEAT_BROWSE
8671 theend:
8672 vim_free(browseFile);
8673 #endif
8674 #ifdef FEAT_SESSION
8675 vim_free(viewFile);
8676 #endif
8677 }
8678
8679 #if ((defined(FEAT_SESSION) || defined(FEAT_EVAL)) && defined(vim_mkdir)) \ 8436 #if ((defined(FEAT_SESSION) || defined(FEAT_EVAL)) && defined(vim_mkdir)) \
8680 || defined(PROTO) 8437 || defined(PROTO)
8681 int 8438 int
8682 vim_mkdir_emsg(char_u *name, int prot) 8439 vim_mkdir_emsg(char_u *name, int prot)
8683 { 8440 {
9715 } 9472 }
9716 9473
9717 return result; 9474 return result;
9718 } 9475 }
9719 9476
9720 #ifdef FEAT_SESSION
9721 static int ses_winsizes(FILE *fd, int restore_size,
9722 win_T *tab_firstwin);
9723 static int ses_win_rec(FILE *fd, frame_T *fr);
9724 static frame_T *ses_skipframe(frame_T *fr);
9725 static int ses_do_frame(frame_T *fr);
9726 static int ses_do_win(win_T *wp);
9727 static int ses_arglist(FILE *fd, char *cmd, garray_T *gap, int fullname, unsigned *flagp);
9728 static int ses_put_fname(FILE *fd, char_u *name, unsigned *flagp);
9729 static int ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, int add_eol);
9730
9731 /*
9732 * Write openfile commands for the current buffers to an .exrc file.
9733 * Return FAIL on error, OK otherwise.
9734 */
9735 static int
9736 makeopens(
9737 FILE *fd,
9738 char_u *dirnow) /* Current directory name */
9739 {
9740 buf_T *buf;
9741 int only_save_windows = TRUE;
9742 int nr;
9743 int restore_size = TRUE;
9744 win_T *wp;
9745 char_u *sname;
9746 win_T *edited_win = NULL;
9747 int tabnr;
9748 int restore_stal = FALSE;
9749 win_T *tab_firstwin;
9750 frame_T *tab_topframe;
9751 int cur_arg_idx = 0;
9752 int next_arg_idx = 0;
9753
9754 if (ssop_flags & SSOP_BUFFERS)
9755 only_save_windows = FALSE; /* Save ALL buffers */
9756
9757 /*
9758 * Begin by setting the this_session variable, and then other
9759 * sessionable variables.
9760 */
9761 #ifdef FEAT_EVAL
9762 if (put_line(fd, "let v:this_session=expand(\"<sfile>:p\")") == FAIL)
9763 return FAIL;
9764 if (ssop_flags & SSOP_GLOBALS)
9765 if (store_session_globals(fd) == FAIL)
9766 return FAIL;
9767 #endif
9768
9769 /*
9770 * Close all windows and tabs but one.
9771 */
9772 if (put_line(fd, "silent only") == FAIL)
9773 return FAIL;
9774 if ((ssop_flags & SSOP_TABPAGES)
9775 && put_line(fd, "silent tabonly") == FAIL)
9776 return FAIL;
9777
9778 /*
9779 * Now a :cd command to the session directory or the current directory
9780 */
9781 if (ssop_flags & SSOP_SESDIR)
9782 {
9783 if (put_line(fd, "exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')")
9784 == FAIL)
9785 return FAIL;
9786 }
9787 else if (ssop_flags & SSOP_CURDIR)
9788 {
9789 sname = home_replace_save(NULL, globaldir != NULL ? globaldir : dirnow);
9790 if (sname == NULL
9791 || fputs("cd ", fd) < 0
9792 || ses_put_fname(fd, sname, &ssop_flags) == FAIL
9793 || put_eol(fd) == FAIL)
9794 {
9795 vim_free(sname);
9796 return FAIL;
9797 }
9798 vim_free(sname);
9799 }
9800
9801 /*
9802 * If there is an empty, unnamed buffer we will wipe it out later.
9803 * Remember the buffer number.
9804 */
9805 if (put_line(fd, "if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''") == FAIL)
9806 return FAIL;
9807 if (put_line(fd, " let s:wipebuf = bufnr('%')") == FAIL)
9808 return FAIL;
9809 if (put_line(fd, "endif") == FAIL)
9810 return FAIL;
9811
9812 /*
9813 * Now save the current files, current buffer first.
9814 */
9815 if (put_line(fd, "set shortmess=aoO") == FAIL)
9816 return FAIL;
9817
9818 /* the global argument list */
9819 if (ses_arglist(fd, "argglobal", &global_alist.al_ga,
9820 !(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL)
9821 return FAIL;
9822
9823 if (ssop_flags & SSOP_RESIZE)
9824 {
9825 /* Note: after the restore we still check it worked!*/
9826 if (fprintf(fd, "set lines=%ld columns=%ld" , Rows, Columns) < 0
9827 || put_eol(fd) == FAIL)
9828 return FAIL;
9829 }
9830
9831 #ifdef FEAT_GUI
9832 if (gui.in_use && (ssop_flags & SSOP_WINPOS))
9833 {
9834 int x, y;
9835
9836 if (gui_mch_get_winpos(&x, &y) == OK)
9837 {
9838 /* Note: after the restore we still check it worked!*/
9839 if (fprintf(fd, "winpos %d %d", x, y) < 0 || put_eol(fd) == FAIL)
9840 return FAIL;
9841 }
9842 }
9843 #endif
9844
9845 /*
9846 * When there are two or more tabpages and 'showtabline' is 1 the tabline
9847 * will be displayed when creating the next tab. That resizes the windows
9848 * in the first tab, which may cause problems. Set 'showtabline' to 2
9849 * temporarily to avoid that.
9850 */
9851 if (p_stal == 1 && first_tabpage->tp_next != NULL)
9852 {
9853 if (put_line(fd, "set stal=2") == FAIL)
9854 return FAIL;
9855 restore_stal = TRUE;
9856 }
9857
9858 /*
9859 * May repeat putting Windows for each tab, when "tabpages" is in
9860 * 'sessionoptions'.
9861 * Don't use goto_tabpage(), it may change directory and trigger
9862 * autocommands.
9863 */
9864 tab_firstwin = firstwin; /* first window in tab page "tabnr" */
9865 tab_topframe = topframe;
9866 if ((ssop_flags & SSOP_TABPAGES))
9867 {
9868 tabpage_T *tp;
9869
9870 // Similar to ses_win_rec() below, populate the tab pages first so
9871 // later local options won't be copied to the new tabs.
9872 FOR_ALL_TABPAGES(tp)
9873 if (tp->tp_next != NULL && put_line(fd, "tabnew") == FAIL)
9874 return FAIL;
9875 if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL)
9876 return FAIL;
9877 }
9878 for (tabnr = 1; ; ++tabnr)
9879 {
9880 tabpage_T *tp = NULL;
9881 int need_tabnext = FALSE;
9882 int cnr = 1;
9883
9884 if ((ssop_flags & SSOP_TABPAGES))
9885 {
9886 tp = find_tabpage(tabnr);
9887
9888 if (tp == NULL)
9889 break; /* done all tab pages */
9890 if (tp == curtab)
9891 {
9892 tab_firstwin = firstwin;
9893 tab_topframe = topframe;
9894 }
9895 else
9896 {
9897 tab_firstwin = tp->tp_firstwin;
9898 tab_topframe = tp->tp_topframe;
9899 }
9900 if (tabnr > 1)
9901 need_tabnext = TRUE;
9902 }
9903
9904 /*
9905 * Before creating the window layout, try loading one file. If this
9906 * is aborted we don't end up with a number of useless windows.
9907 * This may have side effects! (e.g., compressed or network file).
9908 */
9909 for (wp = tab_firstwin; wp != NULL; wp = wp->w_next)
9910 {
9911 if (ses_do_win(wp)
9912 && wp->w_buffer->b_ffname != NULL
9913 && !bt_help(wp->w_buffer)
9914 #ifdef FEAT_QUICKFIX
9915 && !bt_nofilename(wp->w_buffer)
9916 #endif
9917 )
9918 {
9919 if (need_tabnext && put_line(fd, "tabnext") == FAIL)
9920 return FAIL;
9921 need_tabnext = FALSE;
9922
9923 if (fputs("edit ", fd) < 0
9924 || ses_fname(fd, wp->w_buffer, &ssop_flags, TRUE)
9925 == FAIL)
9926 return FAIL;
9927 if (!wp->w_arg_idx_invalid)
9928 edited_win = wp;
9929 break;
9930 }
9931 }
9932
9933 /* If no file got edited create an empty tab page. */
9934 if (need_tabnext && put_line(fd, "tabnext") == FAIL)
9935 return FAIL;
9936
9937 /*
9938 * Save current window layout.
9939 */
9940 if (put_line(fd, "set splitbelow splitright") == FAIL)
9941 return FAIL;
9942 if (ses_win_rec(fd, tab_topframe) == FAIL)
9943 return FAIL;
9944 if (!p_sb && put_line(fd, "set nosplitbelow") == FAIL)
9945 return FAIL;
9946 if (!p_spr && put_line(fd, "set nosplitright") == FAIL)
9947 return FAIL;
9948
9949 /*
9950 * Check if window sizes can be restored (no windows omitted).
9951 * Remember the window number of the current window after restoring.
9952 */
9953 nr = 0;
9954 for (wp = tab_firstwin; wp != NULL; wp = W_NEXT(wp))
9955 {
9956 if (ses_do_win(wp))
9957 ++nr;
9958 else
9959 restore_size = FALSE;
9960 if (curwin == wp)
9961 cnr = nr;
9962 }
9963
9964 /* Go to the first window. */
9965 if (put_line(fd, "wincmd t") == FAIL)
9966 return FAIL;
9967
9968 /*
9969 * If more than one window, see if sizes can be restored.
9970 * First set 'winheight' and 'winwidth' to 1 to avoid the windows being
9971 * resized when moving between windows.
9972 * Do this before restoring the view, so that the topline and the
9973 * cursor can be set. This is done again below.
9974 * winminheight and winminwidth need to be set to avoid an error if the
9975 * user has set winheight or winwidth.
9976 */
9977 if (put_line(fd, "set winminheight=0") == FAIL
9978 || put_line(fd, "set winheight=1") == FAIL
9979 || put_line(fd, "set winminwidth=0") == FAIL
9980 || put_line(fd, "set winwidth=1") == FAIL)
9981 return FAIL;
9982 if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
9983 return FAIL;
9984
9985 // Restore the tab-local working directory if specified
9986 // Do this before the windows, so that the window-local directory can
9987 // override the tab-local directory.
9988 if (tp != NULL && tp->tp_localdir != NULL && ssop_flags & SSOP_CURDIR)
9989 {
9990 if (fputs("tcd ", fd) < 0
9991 || ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL
9992 || put_eol(fd) == FAIL)
9993 return FAIL;
9994 did_lcd = TRUE;
9995 }
9996
9997 /*
9998 * Restore the view of the window (options, file, cursor, etc.).
9999 */
10000 for (wp = tab_firstwin; wp != NULL; wp = wp->w_next)
10001 {
10002 if (!ses_do_win(wp))
10003 continue;
10004 if (put_view(fd, wp, wp != edited_win, &ssop_flags,
10005 cur_arg_idx) == FAIL)
10006 return FAIL;
10007 if (nr > 1 && put_line(fd, "wincmd w") == FAIL)
10008 return FAIL;
10009 next_arg_idx = wp->w_arg_idx;
10010 }
10011
10012 /* The argument index in the first tab page is zero, need to set it in
10013 * each window. For further tab pages it's the window where we do
10014 * "tabedit". */
10015 cur_arg_idx = next_arg_idx;
10016
10017 /*
10018 * Restore cursor to the current window if it's not the first one.
10019 */
10020 if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0
10021 || put_eol(fd) == FAIL))
10022 return FAIL;
10023
10024 /*
10025 * Restore window sizes again after jumping around in windows, because
10026 * the current window has a minimum size while others may not.
10027 */
10028 if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL)
10029 return FAIL;
10030
10031 /* Don't continue in another tab page when doing only the current one
10032 * or when at the last tab page. */
10033 if (!(ssop_flags & SSOP_TABPAGES))
10034 break;
10035 }
10036
10037 if (ssop_flags & SSOP_TABPAGES)
10038 {
10039 if (fprintf(fd, "tabnext %d", tabpage_index(curtab)) < 0
10040 || put_eol(fd) == FAIL)
10041 return FAIL;
10042 }
10043 if (restore_stal && put_line(fd, "set stal=1") == FAIL)
10044 return FAIL;
10045
10046 // Now put the remaining buffers into the buffer list.
10047 // This is near the end, so that when 'hidden' is set we don't create extra
10048 // buffers. If the buffer was already created with another command the
10049 // ":badd" will have no effect.
10050 FOR_ALL_BUFFERS(buf)
10051 {
10052 if (!(only_save_windows && buf->b_nwindows == 0)
10053 && !(buf->b_help && !(ssop_flags & SSOP_HELP))
10054 #ifdef FEAT_TERMINAL
10055 // Skip terminal buffers: finished ones are not useful, others
10056 // will be resurrected and result in a new buffer.
10057 && !bt_terminal(buf)
10058 #endif
10059 && buf->b_fname != NULL
10060 && buf->b_p_bl)
10061 {
10062 if (fprintf(fd, "badd +%ld ", buf->b_wininfo == NULL ? 1L
10063 : buf->b_wininfo->wi_fpos.lnum) < 0
10064 || ses_fname(fd, buf, &ssop_flags, TRUE) == FAIL)
10065 return FAIL;
10066 }
10067 }
10068
10069 /*
10070 * Wipe out an empty unnamed buffer we started in.
10071 */
10072 if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0")
10073 == FAIL)
10074 return FAIL;
10075 if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL)
10076 return FAIL;
10077 if (put_line(fd, "endif") == FAIL)
10078 return FAIL;
10079 if (put_line(fd, "unlet! s:wipebuf") == FAIL)
10080 return FAIL;
10081
10082 /* Re-apply 'winheight', 'winwidth' and 'shortmess'. */
10083 if (fprintf(fd, "set winheight=%ld winwidth=%ld shortmess=%s",
10084 p_wh, p_wiw, p_shm) < 0 || put_eol(fd) == FAIL)
10085 return FAIL;
10086 /* Re-apply 'winminheight' and 'winminwidth'. */
10087 if (fprintf(fd, "set winminheight=%ld winminwidth=%ld",
10088 p_wmh, p_wmw) < 0 || put_eol(fd) == FAIL)
10089 return FAIL;
10090
10091 /*
10092 * Lastly, execute the x.vim file if it exists.
10093 */
10094 if (put_line(fd, "let s:sx = expand(\"<sfile>:p:r\").\"x.vim\"") == FAIL
10095 || put_line(fd, "if file_readable(s:sx)") == FAIL
10096 || put_line(fd, " exe \"source \" . fnameescape(s:sx)") == FAIL
10097 || put_line(fd, "endif") == FAIL)
10098 return FAIL;
10099
10100 return OK;
10101 }
10102
10103 static int
10104 ses_winsizes(
10105 FILE *fd,
10106 int restore_size,
10107 win_T *tab_firstwin)
10108 {
10109 int n = 0;
10110 win_T *wp;
10111
10112 if (restore_size && (ssop_flags & SSOP_WINSIZE))
10113 {
10114 for (wp = tab_firstwin; wp != NULL; wp = wp->w_next)
10115 {
10116 if (!ses_do_win(wp))
10117 continue;
10118 ++n;
10119
10120 /* restore height when not full height */
10121 if (wp->w_height + wp->w_status_height < topframe->fr_height
10122 && (fprintf(fd,
10123 "exe '%dresize ' . ((&lines * %ld + %ld) / %ld)",
10124 n, (long)wp->w_height, Rows / 2, Rows) < 0
10125 || put_eol(fd) == FAIL))
10126 return FAIL;
10127
10128 /* restore width when not full width */
10129 if (wp->w_width < Columns && (fprintf(fd,
10130 "exe 'vert %dresize ' . ((&columns * %ld + %ld) / %ld)",
10131 n, (long)wp->w_width, Columns / 2, Columns) < 0
10132 || put_eol(fd) == FAIL))
10133 return FAIL;
10134 }
10135 }
10136 else
10137 {
10138 /* Just equalise window sizes */
10139 if (put_line(fd, "wincmd =") == FAIL)
10140 return FAIL;
10141 }
10142 return OK;
10143 }
10144
10145 /*
10146 * Write commands to "fd" to recursively create windows for frame "fr",
10147 * horizontally and vertically split.
10148 * After the commands the last window in the frame is the current window.
10149 * Returns FAIL when writing the commands to "fd" fails.
10150 */
10151 static int
10152 ses_win_rec(FILE *fd, frame_T *fr)
10153 {
10154 frame_T *frc;
10155 int count = 0;
10156
10157 if (fr->fr_layout != FR_LEAF)
10158 {
10159 /* Find first frame that's not skipped and then create a window for
10160 * each following one (first frame is already there). */
10161 frc = ses_skipframe(fr->fr_child);
10162 if (frc != NULL)
10163 while ((frc = ses_skipframe(frc->fr_next)) != NULL)
10164 {
10165 /* Make window as big as possible so that we have lots of room
10166 * to split. */
10167 if (put_line(fd, "wincmd _ | wincmd |") == FAIL
10168 || put_line(fd, fr->fr_layout == FR_COL
10169 ? "split" : "vsplit") == FAIL)
10170 return FAIL;
10171 ++count;
10172 }
10173
10174 /* Go back to the first window. */
10175 if (count > 0 && (fprintf(fd, fr->fr_layout == FR_COL
10176 ? "%dwincmd k" : "%dwincmd h", count) < 0
10177 || put_eol(fd) == FAIL))
10178 return FAIL;
10179
10180 /* Recursively create frames/windows in each window of this column or
10181 * row. */
10182 frc = ses_skipframe(fr->fr_child);
10183 while (frc != NULL)
10184 {
10185 ses_win_rec(fd, frc);
10186 frc = ses_skipframe(frc->fr_next);
10187 /* Go to next window. */
10188 if (frc != NULL && put_line(fd, "wincmd w") == FAIL)
10189 return FAIL;
10190 }
10191 }
10192 return OK;
10193 }
10194
10195 /*
10196 * Skip frames that don't contain windows we want to save in the Session.
10197 * Returns NULL when there none.
10198 */
10199 static frame_T *
10200 ses_skipframe(frame_T *fr)
10201 {
10202 frame_T *frc;
10203
10204 FOR_ALL_FRAMES(frc, fr)
10205 if (ses_do_frame(frc))
10206 break;
10207 return frc;
10208 }
10209
10210 /*
10211 * Return TRUE if frame "fr" has a window somewhere that we want to save in
10212 * the Session.
10213 */
10214 static int
10215 ses_do_frame(frame_T *fr)
10216 {
10217 frame_T *frc;
10218
10219 if (fr->fr_layout == FR_LEAF)
10220 return ses_do_win(fr->fr_win);
10221 FOR_ALL_FRAMES(frc, fr->fr_child)
10222 if (ses_do_frame(frc))
10223 return TRUE;
10224 return FALSE;
10225 }
10226
10227 /*
10228 * Return non-zero if window "wp" is to be stored in the Session.
10229 */
10230 static int
10231 ses_do_win(win_T *wp)
10232 {
10233 #ifdef FEAT_TERMINAL
10234 if (bt_terminal(wp->w_buffer))
10235 return !term_is_finished(wp->w_buffer)
10236 && (ssop_flags & SSOP_TERMINAL)
10237 && term_should_restore(wp->w_buffer);
10238 #endif
10239 if (wp->w_buffer->b_fname == NULL
10240 #ifdef FEAT_QUICKFIX
10241 /* When 'buftype' is "nofile" can't restore the window contents. */
10242 || bt_nofilename(wp->w_buffer)
10243 #endif
10244 )
10245 return (ssop_flags & SSOP_BLANK);
10246 if (bt_help(wp->w_buffer))
10247 return (ssop_flags & SSOP_HELP);
10248 return TRUE;
10249 }
10250
10251 static int
10252 put_view_curpos(FILE *fd, win_T *wp, char *spaces)
10253 {
10254 int r;
10255
10256 if (wp->w_curswant == MAXCOL)
10257 r = fprintf(fd, "%snormal! $", spaces);
10258 else
10259 r = fprintf(fd, "%snormal! 0%d|", spaces, wp->w_virtcol + 1);
10260 return r < 0 || put_eol(fd) == FAIL ? FALSE : OK;
10261 }
10262
10263 /*
10264 * Write commands to "fd" to restore the view of a window.
10265 * Caller must make sure 'scrolloff' is zero.
10266 */
10267 static int
10268 put_view(
10269 FILE *fd,
10270 win_T *wp,
10271 int add_edit, /* add ":edit" command to view */
10272 unsigned *flagp, /* vop_flags or ssop_flags */
10273 int current_arg_idx) /* current argument index of the window, use
10274 * -1 if unknown */
10275 {
10276 win_T *save_curwin;
10277 int f;
10278 int do_cursor;
10279 int did_next = FALSE;
10280
10281 /* Always restore cursor position for ":mksession". For ":mkview" only
10282 * when 'viewoptions' contains "cursor". */
10283 do_cursor = (flagp == &ssop_flags || *flagp & SSOP_CURSOR);
10284
10285 /*
10286 * Local argument list.
10287 */
10288 if (wp->w_alist == &global_alist)
10289 {
10290 if (put_line(fd, "argglobal") == FAIL)
10291 return FAIL;
10292 }
10293 else
10294 {
10295 if (ses_arglist(fd, "arglocal", &wp->w_alist->al_ga,
10296 flagp == &vop_flags
10297 || !(*flagp & SSOP_CURDIR)
10298 || wp->w_localdir != NULL, flagp) == FAIL)
10299 return FAIL;
10300 }
10301
10302 /* Only when part of a session: restore the argument index. Some
10303 * arguments may have been deleted, check if the index is valid. */
10304 if (wp->w_arg_idx != current_arg_idx && wp->w_arg_idx < WARGCOUNT(wp)
10305 && flagp == &ssop_flags)
10306 {
10307 if (fprintf(fd, "%ldargu", (long)wp->w_arg_idx + 1) < 0
10308 || put_eol(fd) == FAIL)
10309 return FAIL;
10310 did_next = TRUE;
10311 }
10312
10313 /* Edit the file. Skip this when ":next" already did it. */
10314 if (add_edit && (!did_next || wp->w_arg_idx_invalid))
10315 {
10316 # ifdef FEAT_TERMINAL
10317 if (bt_terminal(wp->w_buffer))
10318 {
10319 if (term_write_session(fd, wp) == FAIL)
10320 return FAIL;
10321 }
10322 else
10323 # endif
10324 /*
10325 * Load the file.
10326 */
10327 if (wp->w_buffer->b_ffname != NULL
10328 # ifdef FEAT_QUICKFIX
10329 && !bt_nofilename(wp->w_buffer)
10330 # endif
10331 )
10332 {
10333 /*
10334 * Editing a file in this buffer: use ":edit file".
10335 * This may have side effects! (e.g., compressed or network file).
10336 *
10337 * Note, if a buffer for that file already exists, use :badd to
10338 * edit that buffer, to not lose folding information (:edit resets
10339 * folds in other buffers)
10340 */
10341 if (fputs("if bufexists(\"", fd) < 0
10342 || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL
10343 || fputs("\") | buffer ", fd) < 0
10344 || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL
10345 || fputs(" | else | edit ", fd) < 0
10346 || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL
10347 || fputs(" | endif", fd) < 0
10348 || put_eol(fd) == FAIL)
10349 return FAIL;
10350 }
10351 else
10352 {
10353 /* No file in this buffer, just make it empty. */
10354 if (put_line(fd, "enew") == FAIL)
10355 return FAIL;
10356 #ifdef FEAT_QUICKFIX
10357 if (wp->w_buffer->b_ffname != NULL)
10358 {
10359 /* The buffer does have a name, but it's not a file name. */
10360 if (fputs("file ", fd) < 0
10361 || ses_fname(fd, wp->w_buffer, flagp, TRUE) == FAIL)
10362 return FAIL;
10363 }
10364 #endif
10365 do_cursor = FALSE;
10366 }
10367 }
10368
10369 /*
10370 * Local mappings and abbreviations.
10371 */
10372 if ((*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS))
10373 && makemap(fd, wp->w_buffer) == FAIL)
10374 return FAIL;
10375
10376 /*
10377 * Local options. Need to go to the window temporarily.
10378 * Store only local values when using ":mkview" and when ":mksession" is
10379 * used and 'sessionoptions' doesn't include "options".
10380 * Some folding options are always stored when "folds" is included,
10381 * otherwise the folds would not be restored correctly.
10382 */
10383 save_curwin = curwin;
10384 curwin = wp;
10385 curbuf = curwin->w_buffer;
10386 if (*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS))
10387 f = makeset(fd, OPT_LOCAL,
10388 flagp == &vop_flags || !(*flagp & SSOP_OPTIONS));
10389 #ifdef FEAT_FOLDING
10390 else if (*flagp & SSOP_FOLDS)
10391 f = makefoldset(fd);
10392 #endif
10393 else
10394 f = OK;
10395 curwin = save_curwin;
10396 curbuf = curwin->w_buffer;
10397 if (f == FAIL)
10398 return FAIL;
10399
10400 #ifdef FEAT_FOLDING
10401 /*
10402 * Save Folds when 'buftype' is empty and for help files.
10403 */
10404 if ((*flagp & SSOP_FOLDS)
10405 && wp->w_buffer->b_ffname != NULL
10406 && (bt_normal(wp->w_buffer) || bt_help(wp->w_buffer)))
10407 {
10408 if (put_folds(fd, wp) == FAIL)
10409 return FAIL;
10410 }
10411 #endif
10412
10413 /*
10414 * Set the cursor after creating folds, since that moves the cursor.
10415 */
10416 if (do_cursor)
10417 {
10418
10419 /* Restore the cursor line in the file and relatively in the
10420 * window. Don't use "G", it changes the jumplist. */
10421 if (fprintf(fd, "let s:l = %ld - ((%ld * winheight(0) + %ld) / %ld)",
10422 (long)wp->w_cursor.lnum,
10423 (long)(wp->w_cursor.lnum - wp->w_topline),
10424 (long)wp->w_height / 2, (long)wp->w_height) < 0
10425 || put_eol(fd) == FAIL
10426 || put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL
10427 || put_line(fd, "exe s:l") == FAIL
10428 || put_line(fd, "normal! zt") == FAIL
10429 || fprintf(fd, "%ld", (long)wp->w_cursor.lnum) < 0
10430 || put_eol(fd) == FAIL)
10431 return FAIL;
10432 /* Restore the cursor column and left offset when not wrapping. */
10433 if (wp->w_cursor.col == 0)
10434 {
10435 if (put_line(fd, "normal! 0") == FAIL)
10436 return FAIL;
10437 }
10438 else
10439 {
10440 if (!wp->w_p_wrap && wp->w_leftcol > 0 && wp->w_width > 0)
10441 {
10442 if (fprintf(fd,
10443 "let s:c = %ld - ((%ld * winwidth(0) + %ld) / %ld)",
10444 (long)wp->w_virtcol + 1,
10445 (long)(wp->w_virtcol - wp->w_leftcol),
10446 (long)wp->w_width / 2, (long)wp->w_width) < 0
10447 || put_eol(fd) == FAIL
10448 || put_line(fd, "if s:c > 0") == FAIL
10449 || fprintf(fd,
10450 " exe 'normal! ' . s:c . '|zs' . %ld . '|'",
10451 (long)wp->w_virtcol + 1) < 0
10452 || put_eol(fd) == FAIL
10453 || put_line(fd, "else") == FAIL
10454 || put_view_curpos(fd, wp, " ") == FAIL
10455 || put_line(fd, "endif") == FAIL)
10456 return FAIL;
10457 }
10458 else if (put_view_curpos(fd, wp, "") == FAIL)
10459 return FAIL;
10460 }
10461 }
10462
10463 /*
10464 * Local directory, if the current flag is not view options or the "curdir"
10465 * option is included.
10466 */
10467 if (wp->w_localdir != NULL
10468 && (flagp != &vop_flags || (*flagp & SSOP_CURDIR)))
10469 {
10470 if (fputs("lcd ", fd) < 0
10471 || ses_put_fname(fd, wp->w_localdir, flagp) == FAIL
10472 || put_eol(fd) == FAIL)
10473 return FAIL;
10474 did_lcd = TRUE;
10475 }
10476
10477 return OK;
10478 }
10479
10480 /*
10481 * Write an argument list to the session file.
10482 * Returns FAIL if writing fails.
10483 */
10484 static int
10485 ses_arglist(
10486 FILE *fd,
10487 char *cmd,
10488 garray_T *gap,
10489 int fullname, /* TRUE: use full path name */
10490 unsigned *flagp)
10491 {
10492 int i;
10493 char_u *buf = NULL;
10494 char_u *s;
10495
10496 if (fputs(cmd, fd) < 0 || put_eol(fd) == FAIL)
10497 return FAIL;
10498 if (put_line(fd, "%argdel") == FAIL)
10499 return FAIL;
10500 for (i = 0; i < gap->ga_len; ++i)
10501 {
10502 /* NULL file names are skipped (only happens when out of memory). */
10503 s = alist_name(&((aentry_T *)gap->ga_data)[i]);
10504 if (s != NULL)
10505 {
10506 if (fullname)
10507 {
10508 buf = alloc(MAXPATHL);
10509 if (buf != NULL)
10510 {
10511 (void)vim_FullName(s, buf, MAXPATHL, FALSE);
10512 s = buf;
10513 }
10514 }
10515 if (fputs("$argadd ", fd) < 0
10516 || ses_put_fname(fd, s, flagp) == FAIL
10517 || put_eol(fd) == FAIL)
10518 {
10519 vim_free(buf);
10520 return FAIL;
10521 }
10522 vim_free(buf);
10523 }
10524 }
10525 return OK;
10526 }
10527
10528 /*
10529 * Write a buffer name to the session file.
10530 * Also ends the line, if "add_eol" is TRUE.
10531 * Returns FAIL if writing fails.
10532 */
10533 static int
10534 ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, int add_eol)
10535 {
10536 char_u *name;
10537
10538 /* Use the short file name if the current directory is known at the time
10539 * the session file will be sourced.
10540 * Don't do this for ":mkview", we don't know the current directory.
10541 * Don't do this after ":lcd", we don't keep track of what the current
10542 * directory is. */
10543 if (buf->b_sfname != NULL
10544 && flagp == &ssop_flags
10545 && (ssop_flags & (SSOP_CURDIR | SSOP_SESDIR))
10546 #ifdef FEAT_AUTOCHDIR
10547 && !p_acd
10548 #endif
10549 && !did_lcd)
10550 name = buf->b_sfname;
10551 else
10552 name = buf->b_ffname;
10553 if (ses_put_fname(fd, name, flagp) == FAIL
10554 || (add_eol && put_eol(fd) == FAIL))
10555 return FAIL;
10556 return OK;
10557 }
10558
10559 /*
10560 * Write a file name to the session file.
10561 * Takes care of the "slash" option in 'sessionoptions' and escapes special
10562 * characters.
10563 * Returns FAIL if writing fails or out of memory.
10564 */
10565 static int
10566 ses_put_fname(FILE *fd, char_u *name, unsigned *flagp)
10567 {
10568 char_u *sname;
10569 char_u *p;
10570 int retval = OK;
10571
10572 sname = home_replace_save(NULL, name);
10573 if (sname == NULL)
10574 return FAIL;
10575
10576 if (*flagp & SSOP_SLASH)
10577 {
10578 /* change all backslashes to forward slashes */
10579 for (p = sname; *p != NUL; MB_PTR_ADV(p))
10580 if (*p == '\\')
10581 *p = '/';
10582 }
10583
10584 /* escape special characters */
10585 p = vim_strsave_fnameescape(sname, FALSE);
10586 vim_free(sname);
10587 if (p == NULL)
10588 return FAIL;
10589
10590 /* write the result */
10591 if (fputs((char *)p, fd) < 0)
10592 retval = FAIL;
10593
10594 vim_free(p);
10595 return retval;
10596 }
10597
10598 /*
10599 * ":loadview [nr]"
10600 */
10601 static void
10602 ex_loadview(exarg_T *eap)
10603 {
10604 char_u *fname;
10605
10606 fname = get_view_file(*eap->arg);
10607 if (fname != NULL)
10608 {
10609 do_source(fname, FALSE, DOSO_NONE);
10610 vim_free(fname);
10611 }
10612 }
10613
10614 /*
10615 * Get the name of the view file for the current buffer.
10616 */
10617 static char_u *
10618 get_view_file(int c)
10619 {
10620 int len = 0;
10621 char_u *p, *s;
10622 char_u *retval;
10623 char_u *sname;
10624
10625 if (curbuf->b_ffname == NULL)
10626 {
10627 emsg(_(e_noname));
10628 return NULL;
10629 }
10630 sname = home_replace_save(NULL, curbuf->b_ffname);
10631 if (sname == NULL)
10632 return NULL;
10633
10634 /*
10635 * We want a file name without separators, because we're not going to make
10636 * a directory.
10637 * "normal" path separator -> "=+"
10638 * "=" -> "=="
10639 * ":" path separator -> "=-"
10640 */
10641 for (p = sname; *p; ++p)
10642 if (*p == '=' || vim_ispathsep(*p))
10643 ++len;
10644 retval = alloc(STRLEN(sname) + len + STRLEN(p_vdir) + 9);
10645 if (retval != NULL)
10646 {
10647 STRCPY(retval, p_vdir);
10648 add_pathsep(retval);
10649 s = retval + STRLEN(retval);
10650 for (p = sname; *p; ++p)
10651 {
10652 if (*p == '=')
10653 {
10654 *s++ = '=';
10655 *s++ = '=';
10656 }
10657 else if (vim_ispathsep(*p))
10658 {
10659 *s++ = '=';
10660 #if defined(BACKSLASH_IN_FILENAME) || defined(AMIGA) || defined(VMS)
10661 if (*p == ':')
10662 *s++ = '-';
10663 else
10664 #endif
10665 *s++ = '+';
10666 }
10667 else
10668 *s++ = *p;
10669 }
10670 *s++ = '=';
10671 *s++ = c;
10672 STRCPY(s, ".vim");
10673 }
10674
10675 vim_free(sname);
10676 return retval;
10677 }
10678
10679 #endif /* FEAT_SESSION */
10680
10681 /*
10682 * Write end-of-line character(s) for ":mkexrc", ":mkvimrc" and ":mksession".
10683 * Return FAIL for a write error.
10684 */
10685 int
10686 put_eol(FILE *fd)
10687 {
10688 if (
10689 #ifdef USE_CRNL
10690 (
10691 # ifdef MKSESSION_NL
10692 !mksession_nl &&
10693 # endif
10694 (putc('\r', fd) < 0)) ||
10695 #endif
10696 (putc('\n', fd) < 0))
10697 return FAIL;
10698 return OK;
10699 }
10700
10701 /*
10702 * Write a line to "fd".
10703 * Return FAIL for a write error.
10704 */
10705 int
10706 put_line(FILE *fd, char *s)
10707 {
10708 if (fputs(s, fd) < 0 || put_eol(fd) == FAIL)
10709 return FAIL;
10710 return OK;
10711 }
10712
10713 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO) 9477 #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG) || defined(PROTO)
10714 /* 9478 /*
10715 * Make a dialog message in "buff[DIALOG_MSG_SIZE]". 9479 * Make a dialog message in "buff[DIALOG_MSG_SIZE]".
10716 * "format" must contain "%s". 9480 * "format" must contain "%s".
10717 */ 9481 */