Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1 | /* vi:set ts=8 sts=4 sw=4 noet: |
| 2 | * |
| 3 | * VIM - Vi IMproved by Bram Moolenaar |
| 4 | * |
| 5 | * Do ":help uganda" in Vim to read copying and usage conditions. |
| 6 | * Do ":help credits" in Vim to see a list of people who contributed. |
| 7 | * See README.txt for an overview of the Vim source code. |
| 8 | */ |
| 9 | |
| 10 | /* |
| 11 | * session.c: session related functions |
| 12 | */ |
| 13 | |
| 14 | #include "vim.h" |
| 15 | |
| 16 | #if defined(FEAT_SESSION) || defined(PROTO) |
| 17 | |
| 18 | static int did_lcd; // whether ":lcd" was produced for a session |
| 19 | |
| 20 | /* |
| 21 | * Write a file name to the session file. |
| 22 | * Takes care of the "slash" option in 'sessionoptions' and escapes special |
| 23 | * characters. |
| 24 | * Returns FAIL if writing fails or out of memory. |
| 25 | */ |
| 26 | static int |
| 27 | ses_put_fname(FILE *fd, char_u *name, unsigned *flagp) |
| 28 | { |
| 29 | char_u *sname; |
| 30 | char_u *p; |
| 31 | int retval = OK; |
| 32 | |
| 33 | sname = home_replace_save(NULL, name); |
| 34 | if (sname == NULL) |
| 35 | return FAIL; |
| 36 | |
| 37 | if (*flagp & SSOP_SLASH) |
| 38 | { |
| 39 | // change all backslashes to forward slashes |
| 40 | for (p = sname; *p != NUL; MB_PTR_ADV(p)) |
| 41 | if (*p == '\\') |
| 42 | *p = '/'; |
| 43 | } |
| 44 | |
| 45 | // escape special characters |
Bram Moolenaar | 21c1a0c | 2021-10-17 17:20:23 +0100 | [diff] [blame] | 46 | p = vim_strsave_fnameescape(sname, VSE_NONE); |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 47 | vim_free(sname); |
| 48 | if (p == NULL) |
| 49 | return FAIL; |
| 50 | |
| 51 | // write the result |
| 52 | if (fputs((char *)p, fd) < 0) |
| 53 | retval = FAIL; |
| 54 | |
| 55 | vim_free(p); |
| 56 | return retval; |
| 57 | } |
| 58 | |
| 59 | /* |
| 60 | * Write a buffer name to the session file. |
| 61 | * Also ends the line, if "add_eol" is TRUE. |
| 62 | * Returns FAIL if writing fails. |
| 63 | */ |
| 64 | static int |
| 65 | ses_fname(FILE *fd, buf_T *buf, unsigned *flagp, int add_eol) |
| 66 | { |
| 67 | char_u *name; |
| 68 | |
| 69 | // Use the short file name if the current directory is known at the time |
| 70 | // the session file will be sourced. |
| 71 | // Don't do this for ":mkview", we don't know the current directory. |
| 72 | // Don't do this after ":lcd", we don't keep track of what the current |
| 73 | // directory is. |
| 74 | if (buf->b_sfname != NULL |
| 75 | && flagp == &ssop_flags |
| 76 | && (ssop_flags & (SSOP_CURDIR | SSOP_SESDIR)) |
| 77 | #ifdef FEAT_AUTOCHDIR |
| 78 | && !p_acd |
| 79 | #endif |
| 80 | && !did_lcd) |
| 81 | name = buf->b_sfname; |
| 82 | else |
| 83 | name = buf->b_ffname; |
| 84 | if (ses_put_fname(fd, name, flagp) == FAIL |
| 85 | || (add_eol && put_eol(fd) == FAIL)) |
| 86 | return FAIL; |
| 87 | return OK; |
| 88 | } |
| 89 | |
| 90 | /* |
| 91 | * Write an argument list to the session file. |
| 92 | * Returns FAIL if writing fails. |
| 93 | */ |
| 94 | static int |
| 95 | ses_arglist( |
| 96 | FILE *fd, |
| 97 | char *cmd, |
| 98 | garray_T *gap, |
| 99 | int fullname, // TRUE: use full path name |
| 100 | unsigned *flagp) |
| 101 | { |
| 102 | int i; |
| 103 | char_u *buf = NULL; |
| 104 | char_u *s; |
| 105 | |
| 106 | if (fputs(cmd, fd) < 0 || put_eol(fd) == FAIL) |
| 107 | return FAIL; |
| 108 | if (put_line(fd, "%argdel") == FAIL) |
| 109 | return FAIL; |
| 110 | for (i = 0; i < gap->ga_len; ++i) |
| 111 | { |
| 112 | // NULL file names are skipped (only happens when out of memory). |
| 113 | s = alist_name(&((aentry_T *)gap->ga_data)[i]); |
| 114 | if (s != NULL) |
| 115 | { |
| 116 | if (fullname) |
| 117 | { |
| 118 | buf = alloc(MAXPATHL); |
| 119 | if (buf != NULL) |
| 120 | { |
| 121 | (void)vim_FullName(s, buf, MAXPATHL, FALSE); |
| 122 | s = buf; |
| 123 | } |
| 124 | } |
| 125 | if (fputs("$argadd ", fd) < 0 |
| 126 | || ses_put_fname(fd, s, flagp) == FAIL |
| 127 | || put_eol(fd) == FAIL) |
| 128 | { |
| 129 | vim_free(buf); |
| 130 | return FAIL; |
| 131 | } |
| 132 | vim_free(buf); |
| 133 | } |
| 134 | } |
| 135 | return OK; |
| 136 | } |
| 137 | |
| 138 | /* |
| 139 | * Return non-zero if window "wp" is to be stored in the Session. |
| 140 | */ |
| 141 | static int |
| 142 | ses_do_win(win_T *wp) |
| 143 | { |
| 144 | #ifdef FEAT_TERMINAL |
| 145 | if (bt_terminal(wp->w_buffer)) |
| 146 | return !term_is_finished(wp->w_buffer) |
| 147 | && (ssop_flags & SSOP_TERMINAL) |
| 148 | && term_should_restore(wp->w_buffer); |
| 149 | #endif |
| 150 | if (wp->w_buffer->b_fname == NULL |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 151 | // When 'buftype' is "nofile" can't restore the window contents. |
Bram Moolenaar | 6d4b2f5 | 2022-08-25 15:11:15 +0100 | [diff] [blame] | 152 | || bt_nofilename(wp->w_buffer)) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 153 | return (ssop_flags & SSOP_BLANK); |
| 154 | if (bt_help(wp->w_buffer)) |
| 155 | return (ssop_flags & SSOP_HELP); |
| 156 | return TRUE; |
| 157 | } |
| 158 | |
| 159 | /* |
| 160 | * Return TRUE if frame "fr" has a window somewhere that we want to save in |
| 161 | * the Session. |
| 162 | */ |
| 163 | static int |
| 164 | ses_do_frame(frame_T *fr) |
| 165 | { |
| 166 | frame_T *frc; |
| 167 | |
| 168 | if (fr->fr_layout == FR_LEAF) |
| 169 | return ses_do_win(fr->fr_win); |
| 170 | FOR_ALL_FRAMES(frc, fr->fr_child) |
| 171 | if (ses_do_frame(frc)) |
| 172 | return TRUE; |
| 173 | return FALSE; |
| 174 | } |
| 175 | |
| 176 | /* |
| 177 | * Skip frames that don't contain windows we want to save in the Session. |
| 178 | * Returns NULL when there none. |
| 179 | */ |
| 180 | static frame_T * |
| 181 | ses_skipframe(frame_T *fr) |
| 182 | { |
| 183 | frame_T *frc; |
| 184 | |
| 185 | FOR_ALL_FRAMES(frc, fr) |
| 186 | if (ses_do_frame(frc)) |
| 187 | break; |
| 188 | return frc; |
| 189 | } |
| 190 | |
| 191 | /* |
| 192 | * Write commands to "fd" to recursively create windows for frame "fr", |
| 193 | * horizontally and vertically split. |
| 194 | * After the commands the last window in the frame is the current window. |
| 195 | * Returns FAIL when writing the commands to "fd" fails. |
| 196 | */ |
| 197 | static int |
| 198 | ses_win_rec(FILE *fd, frame_T *fr) |
| 199 | { |
| 200 | frame_T *frc; |
| 201 | int count = 0; |
| 202 | |
Yegappan Lakshmanan | 6ec6666 | 2023-01-23 20:46:21 +0000 | [diff] [blame] | 203 | if (fr->fr_layout == FR_LEAF) |
| 204 | return OK; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 205 | |
Yegappan Lakshmanan | 6ec6666 | 2023-01-23 20:46:21 +0000 | [diff] [blame] | 206 | // Find first frame that's not skipped and then create a window for |
| 207 | // each following one (first frame is already there). |
| 208 | frc = ses_skipframe(fr->fr_child); |
| 209 | if (frc != NULL) |
| 210 | while ((frc = ses_skipframe(frc->fr_next)) != NULL) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 211 | { |
Yegappan Lakshmanan | 6ec6666 | 2023-01-23 20:46:21 +0000 | [diff] [blame] | 212 | // Make window as big as possible so that we have lots of room |
| 213 | // to split. |
| 214 | if (put_line(fd, "wincmd _ | wincmd |") == FAIL |
| 215 | || put_line(fd, fr->fr_layout == FR_COL |
| 216 | ? "split" : "vsplit") == FAIL) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 217 | return FAIL; |
Yegappan Lakshmanan | 6ec6666 | 2023-01-23 20:46:21 +0000 | [diff] [blame] | 218 | ++count; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 219 | } |
Yegappan Lakshmanan | 6ec6666 | 2023-01-23 20:46:21 +0000 | [diff] [blame] | 220 | |
| 221 | // Go back to the first window. |
| 222 | if (count > 0 && (fprintf(fd, fr->fr_layout == FR_COL |
| 223 | ? "%dwincmd k" : "%dwincmd h", count) < 0 |
| 224 | || put_eol(fd) == FAIL)) |
| 225 | return FAIL; |
| 226 | |
| 227 | // Recursively create frames/windows in each window of this column or |
| 228 | // row. |
| 229 | frc = ses_skipframe(fr->fr_child); |
| 230 | while (frc != NULL) |
| 231 | { |
| 232 | ses_win_rec(fd, frc); |
| 233 | frc = ses_skipframe(frc->fr_next); |
| 234 | // Go to next window. |
| 235 | if (frc != NULL && put_line(fd, "wincmd w") == FAIL) |
| 236 | return FAIL; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 237 | } |
Yegappan Lakshmanan | 6ec6666 | 2023-01-23 20:46:21 +0000 | [diff] [blame] | 238 | |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 239 | return OK; |
| 240 | } |
| 241 | |
| 242 | static int |
| 243 | ses_winsizes( |
| 244 | FILE *fd, |
| 245 | int restore_size, |
| 246 | win_T *tab_firstwin) |
| 247 | { |
| 248 | int n = 0; |
| 249 | win_T *wp; |
| 250 | |
| 251 | if (restore_size && (ssop_flags & SSOP_WINSIZE)) |
| 252 | { |
| 253 | for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) |
| 254 | { |
| 255 | if (!ses_do_win(wp)) |
| 256 | continue; |
| 257 | ++n; |
| 258 | |
| 259 | // restore height when not full height |
| 260 | if (wp->w_height + wp->w_status_height < topframe->fr_height |
| 261 | && (fprintf(fd, |
| 262 | "exe '%dresize ' . ((&lines * %ld + %ld) / %ld)", |
| 263 | n, (long)wp->w_height, Rows / 2, Rows) < 0 |
| 264 | || put_eol(fd) == FAIL)) |
| 265 | return FAIL; |
| 266 | |
| 267 | // restore width when not full width |
| 268 | if (wp->w_width < Columns && (fprintf(fd, |
| 269 | "exe 'vert %dresize ' . ((&columns * %ld + %ld) / %ld)", |
| 270 | n, (long)wp->w_width, Columns / 2, Columns) < 0 |
| 271 | || put_eol(fd) == FAIL)) |
| 272 | return FAIL; |
| 273 | } |
| 274 | } |
| 275 | else |
| 276 | { |
| 277 | // Just equalise window sizes |
| 278 | if (put_line(fd, "wincmd =") == FAIL) |
| 279 | return FAIL; |
| 280 | } |
| 281 | return OK; |
| 282 | } |
| 283 | |
| 284 | static int |
| 285 | put_view_curpos(FILE *fd, win_T *wp, char *spaces) |
| 286 | { |
| 287 | int r; |
| 288 | |
| 289 | if (wp->w_curswant == MAXCOL) |
| 290 | r = fprintf(fd, "%snormal! $", spaces); |
| 291 | else |
| 292 | r = fprintf(fd, "%snormal! 0%d|", spaces, wp->w_virtcol + 1); |
| 293 | return r < 0 || put_eol(fd) == FAIL ? FALSE : OK; |
| 294 | } |
| 295 | |
| 296 | /* |
| 297 | * Write commands to "fd" to restore the view of a window. |
| 298 | * Caller must make sure 'scrolloff' is zero. |
| 299 | */ |
| 300 | static int |
| 301 | put_view( |
| 302 | FILE *fd, |
| 303 | win_T *wp, |
zeertzjq | a304e49 | 2025-06-10 20:31:44 +0200 | [diff] [blame] | 304 | tabpage_T *tp, |
Bram Moolenaar | c2c8205 | 2020-09-11 22:10:22 +0200 | [diff] [blame] | 305 | int add_edit, // add ":edit" command to view |
| 306 | unsigned *flagp, // vop_flags or ssop_flags |
| 307 | int current_arg_idx, // current argument index of the window, |
| 308 | // use -1 if unknown |
| 309 | hashtab_T *terminal_bufs UNUSED) // already encountered terminal buffers, |
| 310 | // can be NULL |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 311 | { |
| 312 | win_T *save_curwin; |
| 313 | int f; |
| 314 | int do_cursor; |
| 315 | int did_next = FALSE; |
| 316 | |
| 317 | // Always restore cursor position for ":mksession". For ":mkview" only |
| 318 | // when 'viewoptions' contains "cursor". |
| 319 | do_cursor = (flagp == &ssop_flags || *flagp & SSOP_CURSOR); |
| 320 | |
| 321 | // Local argument list. |
| 322 | if (wp->w_alist == &global_alist) |
| 323 | { |
| 324 | if (put_line(fd, "argglobal") == FAIL) |
| 325 | return FAIL; |
| 326 | } |
| 327 | else |
| 328 | { |
| 329 | if (ses_arglist(fd, "arglocal", &wp->w_alist->al_ga, |
| 330 | flagp == &vop_flags |
| 331 | || !(*flagp & SSOP_CURDIR) |
zeertzjq | a304e49 | 2025-06-10 20:31:44 +0200 | [diff] [blame] | 332 | || tp->tp_localdir != NULL |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 333 | || wp->w_localdir != NULL, flagp) == FAIL) |
| 334 | return FAIL; |
| 335 | } |
| 336 | |
| 337 | // Only when part of a session: restore the argument index. Some |
| 338 | // arguments may have been deleted, check if the index is valid. |
| 339 | if (wp->w_arg_idx != current_arg_idx && wp->w_arg_idx < WARGCOUNT(wp) |
| 340 | && flagp == &ssop_flags) |
| 341 | { |
| 342 | if (fprintf(fd, "%ldargu", (long)wp->w_arg_idx + 1) < 0 |
| 343 | || put_eol(fd) == FAIL) |
| 344 | return FAIL; |
| 345 | did_next = TRUE; |
| 346 | } |
| 347 | |
| 348 | // Edit the file. Skip this when ":next" already did it. |
| 349 | if (add_edit && (!did_next || wp->w_arg_idx_invalid)) |
| 350 | { |
matveyt | 8e7d9db | 2022-01-05 14:01:30 +0000 | [diff] [blame] | 351 | if (bt_help(wp->w_buffer)) |
| 352 | { |
| 353 | char *curtag = ""; |
| 354 | |
| 355 | // A help buffer needs some options to be set. |
| 356 | // First, create a new empty buffer with "buftype=help". |
| 357 | // Then ":help" will re-use both the buffer and the window and set |
| 358 | // the options, even when "options" is not in 'sessionoptions'. |
| 359 | if (0 < wp->w_tagstackidx |
| 360 | && wp->w_tagstackidx <= wp->w_tagstacklen) |
| 361 | curtag = (char *)wp->w_tagstack[wp->w_tagstackidx - 1].tagname; |
| 362 | |
| 363 | if (put_line(fd, "enew | setl bt=help") == FAIL |
| 364 | || fprintf(fd, "help %s", curtag) < 0 |
| 365 | || put_eol(fd) == FAIL) |
| 366 | return FAIL; |
| 367 | } |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 368 | # ifdef FEAT_TERMINAL |
matveyt | 8e7d9db | 2022-01-05 14:01:30 +0000 | [diff] [blame] | 369 | else if (bt_terminal(wp->w_buffer)) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 370 | { |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 371 | if (term_write_session(fd, wp, terminal_bufs) == FAIL) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 372 | return FAIL; |
| 373 | } |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 374 | # endif |
| 375 | // Load the file. |
matveyt | 8e7d9db | 2022-01-05 14:01:30 +0000 | [diff] [blame] | 376 | else if (wp->w_buffer->b_ffname != NULL |
Bram Moolenaar | 6d4b2f5 | 2022-08-25 15:11:15 +0100 | [diff] [blame] | 377 | && !bt_nofilename(wp->w_buffer)) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 378 | { |
| 379 | // Editing a file in this buffer: use ":edit file". |
| 380 | // This may have side effects! (e.g., compressed or network file). |
| 381 | // |
| 382 | // Note, if a buffer for that file already exists, use :badd to |
| 383 | // edit that buffer, to not lose folding information (:edit resets |
| 384 | // folds in other buffers) |
James Cherti | 7d42840 | 2022-03-14 20:24:51 +0000 | [diff] [blame] | 385 | if (fputs("if bufexists(fnamemodify(\"", fd) < 0 |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 386 | || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL |
James Cherti | 7d42840 | 2022-03-14 20:24:51 +0000 | [diff] [blame] | 387 | || fputs("\", \":p\")) | buffer ", fd) < 0 |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 388 | || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL |
| 389 | || fputs(" | else | edit ", fd) < 0 |
| 390 | || ses_fname(fd, wp->w_buffer, flagp, FALSE) == FAIL |
| 391 | || fputs(" | endif", fd) < 0 |
| 392 | || put_eol(fd) == FAIL) |
| 393 | return FAIL; |
| 394 | } |
| 395 | else |
| 396 | { |
| 397 | // No file in this buffer, just make it empty. |
| 398 | if (put_line(fd, "enew") == FAIL) |
| 399 | return FAIL; |
| 400 | #ifdef FEAT_QUICKFIX |
| 401 | if (wp->w_buffer->b_ffname != NULL) |
| 402 | { |
| 403 | // The buffer does have a name, but it's not a file name. |
| 404 | if (fputs("file ", fd) < 0 |
| 405 | || ses_fname(fd, wp->w_buffer, flagp, TRUE) == FAIL) |
| 406 | return FAIL; |
| 407 | } |
| 408 | #endif |
| 409 | do_cursor = FALSE; |
| 410 | } |
| 411 | } |
| 412 | |
Bram Moolenaar | 59d8e56 | 2020-11-07 18:41:10 +0100 | [diff] [blame] | 413 | if (wp->w_alt_fnum) |
| 414 | { |
| 415 | buf_T *alt = buflist_findnr(wp->w_alt_fnum); |
| 416 | |
Bram Moolenaar | 0756f75 | 2021-03-13 13:52:33 +0100 | [diff] [blame] | 417 | // Set the alternate file if the buffer is listed. |
Bram Moolenaar | 139348f | 2021-02-05 21:55:53 +0100 | [diff] [blame] | 418 | if ((flagp == &ssop_flags) |
| 419 | && alt != NULL |
Bram Moolenaar | 59d8e56 | 2020-11-07 18:41:10 +0100 | [diff] [blame] | 420 | && alt->b_fname != NULL |
| 421 | && *alt->b_fname != NUL |
Bram Moolenaar | 0756f75 | 2021-03-13 13:52:33 +0100 | [diff] [blame] | 422 | && alt->b_p_bl |
Bram Moolenaar | 59d8e56 | 2020-11-07 18:41:10 +0100 | [diff] [blame] | 423 | && (fputs("balt ", fd) < 0 |
| 424 | || ses_fname(fd, alt, flagp, TRUE) == FAIL)) |
| 425 | return FAIL; |
| 426 | } |
| 427 | |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 428 | // Local mappings and abbreviations. |
| 429 | if ((*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS)) |
| 430 | && makemap(fd, wp->w_buffer) == FAIL) |
| 431 | return FAIL; |
| 432 | |
| 433 | // Local options. Need to go to the window temporarily. |
| 434 | // Store only local values when using ":mkview" and when ":mksession" is |
| 435 | // used and 'sessionoptions' doesn't include "options". |
| 436 | // Some folding options are always stored when "folds" is included, |
| 437 | // otherwise the folds would not be restored correctly. |
| 438 | save_curwin = curwin; |
| 439 | curwin = wp; |
| 440 | curbuf = curwin->w_buffer; |
| 441 | if (*flagp & (SSOP_OPTIONS | SSOP_LOCALOPTIONS)) |
| 442 | f = makeset(fd, OPT_LOCAL, |
| 443 | flagp == &vop_flags || !(*flagp & SSOP_OPTIONS)); |
| 444 | #ifdef FEAT_FOLDING |
| 445 | else if (*flagp & SSOP_FOLDS) |
| 446 | f = makefoldset(fd); |
| 447 | #endif |
| 448 | else |
| 449 | f = OK; |
| 450 | curwin = save_curwin; |
| 451 | curbuf = curwin->w_buffer; |
| 452 | if (f == FAIL) |
| 453 | return FAIL; |
| 454 | |
| 455 | #ifdef FEAT_FOLDING |
| 456 | // Save Folds when 'buftype' is empty and for help files. |
| 457 | if ((*flagp & SSOP_FOLDS) |
| 458 | && wp->w_buffer->b_ffname != NULL |
| 459 | && (bt_normal(wp->w_buffer) || bt_help(wp->w_buffer))) |
| 460 | { |
| 461 | if (put_folds(fd, wp) == FAIL) |
| 462 | return FAIL; |
| 463 | } |
| 464 | #endif |
| 465 | |
| 466 | // Set the cursor after creating folds, since that moves the cursor. |
| 467 | if (do_cursor) |
| 468 | { |
| 469 | |
| 470 | // Restore the cursor line in the file and relatively in the |
| 471 | // window. Don't use "G", it changes the jumplist. |
Bram Moolenaar | b6c2e9a | 2021-04-30 21:37:51 +0200 | [diff] [blame] | 472 | if (wp->w_height <= 0) |
| 473 | { |
| 474 | if (fprintf(fd, "let s:l = %ld", (long)wp->w_cursor.lnum) < 0) |
| 475 | return FAIL; |
| 476 | } |
| 477 | else if (fprintf(fd, |
| 478 | "let s:l = %ld - ((%ld * winheight(0) + %ld) / %ld)", |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 479 | (long)wp->w_cursor.lnum, |
| 480 | (long)(wp->w_cursor.lnum - wp->w_topline), |
Bram Moolenaar | b6c2e9a | 2021-04-30 21:37:51 +0200 | [diff] [blame] | 481 | (long)wp->w_height / 2, (long)wp->w_height) < 0) |
| 482 | return FAIL; |
| 483 | |
| 484 | if (put_eol(fd) == FAIL |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 485 | || put_line(fd, "if s:l < 1 | let s:l = 1 | endif") == FAIL |
Bram Moolenaar | 3482be6 | 2020-11-27 11:00:38 +0100 | [diff] [blame] | 486 | || put_line(fd, "keepjumps exe s:l") == FAIL |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 487 | || put_line(fd, "normal! zt") == FAIL |
Bram Moolenaar | 3482be6 | 2020-11-27 11:00:38 +0100 | [diff] [blame] | 488 | || fprintf(fd, "keepjumps %ld", (long)wp->w_cursor.lnum) < 0 |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 489 | || put_eol(fd) == FAIL) |
| 490 | return FAIL; |
| 491 | // Restore the cursor column and left offset when not wrapping. |
| 492 | if (wp->w_cursor.col == 0) |
| 493 | { |
| 494 | if (put_line(fd, "normal! 0") == FAIL) |
| 495 | return FAIL; |
| 496 | } |
| 497 | else |
| 498 | { |
| 499 | if (!wp->w_p_wrap && wp->w_leftcol > 0 && wp->w_width > 0) |
| 500 | { |
| 501 | if (fprintf(fd, |
| 502 | "let s:c = %ld - ((%ld * winwidth(0) + %ld) / %ld)", |
| 503 | (long)wp->w_virtcol + 1, |
| 504 | (long)(wp->w_virtcol - wp->w_leftcol), |
| 505 | (long)wp->w_width / 2, (long)wp->w_width) < 0 |
| 506 | || put_eol(fd) == FAIL |
| 507 | || put_line(fd, "if s:c > 0") == FAIL |
| 508 | || fprintf(fd, |
| 509 | " exe 'normal! ' . s:c . '|zs' . %ld . '|'", |
| 510 | (long)wp->w_virtcol + 1) < 0 |
| 511 | || put_eol(fd) == FAIL |
| 512 | || put_line(fd, "else") == FAIL |
| 513 | || put_view_curpos(fd, wp, " ") == FAIL |
| 514 | || put_line(fd, "endif") == FAIL) |
| 515 | return FAIL; |
| 516 | } |
| 517 | else if (put_view_curpos(fd, wp, "") == FAIL) |
| 518 | return FAIL; |
| 519 | } |
| 520 | } |
| 521 | |
| 522 | // Local directory, if the current flag is not view options or the "curdir" |
| 523 | // option is included. |
| 524 | if (wp->w_localdir != NULL |
| 525 | && (flagp != &vop_flags || (*flagp & SSOP_CURDIR))) |
| 526 | { |
| 527 | if (fputs("lcd ", fd) < 0 |
| 528 | || ses_put_fname(fd, wp->w_localdir, flagp) == FAIL |
| 529 | || put_eol(fd) == FAIL) |
| 530 | return FAIL; |
| 531 | did_lcd = TRUE; |
| 532 | } |
| 533 | |
| 534 | return OK; |
| 535 | } |
| 536 | |
| 537 | #ifdef FEAT_EVAL |
| 538 | static int |
| 539 | store_session_globals(FILE *fd) |
| 540 | { |
Bram Moolenaar | da6c033 | 2019-09-01 16:01:30 +0200 | [diff] [blame] | 541 | hashtab_T *gvht = get_globvar_ht(); |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 542 | hashitem_T *hi; |
| 543 | dictitem_T *this_var; |
| 544 | int todo; |
| 545 | char_u *p, *t; |
| 546 | |
Bram Moolenaar | da6c033 | 2019-09-01 16:01:30 +0200 | [diff] [blame] | 547 | todo = (int)gvht->ht_used; |
Yegappan Lakshmanan | 14113fd | 2023-03-07 17:13:51 +0000 | [diff] [blame] | 548 | FOR_ALL_HASHTAB_ITEMS(gvht, hi, todo) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 549 | { |
| 550 | if (!HASHITEM_EMPTY(hi)) |
| 551 | { |
| 552 | --todo; |
| 553 | this_var = HI2DI(hi); |
| 554 | if ((this_var->di_tv.v_type == VAR_NUMBER |
| 555 | || this_var->di_tv.v_type == VAR_STRING) |
| 556 | && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) |
| 557 | { |
| 558 | // Escape special characters with a backslash. Turn a LF and |
| 559 | // CR into \n and \r. |
| 560 | p = vim_strsave_escaped(tv_get_string(&this_var->di_tv), |
| 561 | (char_u *)"\\\"\n\r"); |
| 562 | if (p == NULL) // out of memory |
| 563 | break; |
| 564 | for (t = p; *t != NUL; ++t) |
| 565 | if (*t == '\n') |
| 566 | *t = 'n'; |
| 567 | else if (*t == '\r') |
| 568 | *t = 'r'; |
| 569 | if ((fprintf(fd, "let %s = %c%s%c", |
| 570 | this_var->di_key, |
| 571 | (this_var->di_tv.v_type == VAR_STRING) ? '"' |
| 572 | : ' ', |
| 573 | p, |
| 574 | (this_var->di_tv.v_type == VAR_STRING) ? '"' |
| 575 | : ' ') < 0) |
| 576 | || put_eol(fd) == FAIL) |
| 577 | { |
| 578 | vim_free(p); |
| 579 | return FAIL; |
| 580 | } |
| 581 | vim_free(p); |
| 582 | } |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 583 | else if (this_var->di_tv.v_type == VAR_FLOAT |
| 584 | && var_flavour(this_var->di_key) == VAR_FLAVOUR_SESSION) |
| 585 | { |
| 586 | float_T f = this_var->di_tv.vval.v_float; |
| 587 | int sign = ' '; |
| 588 | |
| 589 | if (f < 0) |
| 590 | { |
| 591 | f = -f; |
| 592 | sign = '-'; |
| 593 | } |
| 594 | if ((fprintf(fd, "let %s = %c%f", |
| 595 | this_var->di_key, sign, f) < 0) |
| 596 | || put_eol(fd) == FAIL) |
| 597 | return FAIL; |
| 598 | } |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 599 | } |
| 600 | } |
| 601 | return OK; |
| 602 | } |
| 603 | #endif |
| 604 | |
| 605 | /* |
| 606 | * Write openfile commands for the current buffers to an .exrc file. |
| 607 | * Return FAIL on error, OK otherwise. |
| 608 | */ |
| 609 | static int |
| 610 | makeopens( |
| 611 | FILE *fd, |
| 612 | char_u *dirnow) // Current directory name |
| 613 | { |
| 614 | buf_T *buf; |
| 615 | int only_save_windows = TRUE; |
| 616 | int nr; |
| 617 | int restore_size = TRUE; |
Bram Moolenaar | b4011af | 2022-05-01 00:42:24 +0100 | [diff] [blame] | 618 | int restore_height_width = FALSE; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 619 | win_T *wp; |
| 620 | char_u *sname; |
| 621 | win_T *edited_win = NULL; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 622 | int restore_stal = FALSE; |
| 623 | win_T *tab_firstwin; |
| 624 | frame_T *tab_topframe; |
| 625 | int cur_arg_idx = 0; |
| 626 | int next_arg_idx = 0; |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 627 | int ret = FAIL; |
LemonBoy | d7c9564 | 2022-04-30 16:10:27 +0100 | [diff] [blame] | 628 | tabpage_T *tp; |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 629 | #ifdef FEAT_TERMINAL |
| 630 | hashtab_T terminal_bufs; |
| 631 | |
| 632 | hash_init(&terminal_bufs); |
| 633 | #endif |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 634 | |
| 635 | if (ssop_flags & SSOP_BUFFERS) |
| 636 | only_save_windows = FALSE; // Save ALL buffers |
| 637 | |
| 638 | // Begin by setting the this_session variable, and then other |
| 639 | // sessionable variables. |
| 640 | #ifdef FEAT_EVAL |
| 641 | if (put_line(fd, "let v:this_session=expand(\"<sfile>:p\")") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 642 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 643 | if (ssop_flags & SSOP_GLOBALS) |
| 644 | if (store_session_globals(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 645 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 646 | #endif |
| 647 | |
| 648 | // Close all windows and tabs but one. |
| 649 | if (put_line(fd, "silent only") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 650 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 651 | if ((ssop_flags & SSOP_TABPAGES) |
| 652 | && put_line(fd, "silent tabonly") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 653 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 654 | |
| 655 | // Now a :cd command to the session directory or the current directory |
| 656 | if (ssop_flags & SSOP_SESDIR) |
| 657 | { |
| 658 | if (put_line(fd, "exe \"cd \" . escape(expand(\"<sfile>:p:h\"), ' ')") |
| 659 | == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 660 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 661 | } |
| 662 | else if (ssop_flags & SSOP_CURDIR) |
| 663 | { |
| 664 | sname = home_replace_save(NULL, globaldir != NULL ? globaldir : dirnow); |
| 665 | if (sname == NULL |
| 666 | || fputs("cd ", fd) < 0 |
| 667 | || ses_put_fname(fd, sname, &ssop_flags) == FAIL |
| 668 | || put_eol(fd) == FAIL) |
| 669 | { |
| 670 | vim_free(sname); |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 671 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 672 | } |
| 673 | vim_free(sname); |
| 674 | } |
| 675 | |
| 676 | // If there is an empty, unnamed buffer we will wipe it out later. |
| 677 | // Remember the buffer number. |
| 678 | if (put_line(fd, "if expand('%') == '' && !&modified && line('$') <= 1 && getline(1) == ''") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 679 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 680 | if (put_line(fd, " let s:wipebuf = bufnr('%')") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 681 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 682 | if (put_line(fd, "endif") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 683 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 684 | |
zeertzjq | 8f751d5 | 2025-06-05 20:25:51 +0200 | [diff] [blame] | 685 | // Save 'shortmess' if not storing options. |
James Cherti | fd01280 | 2022-03-29 12:02:57 +0100 | [diff] [blame] | 686 | if ((ssop_flags & SSOP_OPTIONS) == 0 |
| 687 | && put_line(fd, "let s:shortmess_save = &shortmess") == FAIL) |
| 688 | goto fail; |
| 689 | |
zeertzjq | 8f751d5 | 2025-06-05 20:25:51 +0200 | [diff] [blame] | 690 | // Set 'shortmess' for the following. |
| 691 | if (put_line(fd, "set shortmess+=aoO") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 692 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 693 | |
Bram Moolenaar | aaadb5b | 2022-05-18 22:07:47 +0100 | [diff] [blame] | 694 | // Now save the current files, current buffer first. |
Evgeni Chasnovski | 26ebf1f | 2022-01-14 13:19:43 +0000 | [diff] [blame] | 695 | // Put all buffers into the buffer list. |
| 696 | // Do it very early to preserve buffer order after loading session (which |
| 697 | // can be disrupted by prior `edit` or `tabedit` calls). |
| 698 | FOR_ALL_BUFFERS(buf) |
| 699 | { |
| 700 | if (!(only_save_windows && buf->b_nwindows == 0) |
| 701 | && !(buf->b_help && !(ssop_flags & SSOP_HELP)) |
Evgeni Chasnovski | 26ebf1f | 2022-01-14 13:19:43 +0000 | [diff] [blame] | 702 | // Skip terminal buffers: finished ones are not useful, others |
| 703 | // will be resurrected and result in a new buffer. |
| 704 | && !bt_terminal(buf) |
Evgeni Chasnovski | 26ebf1f | 2022-01-14 13:19:43 +0000 | [diff] [blame] | 705 | && buf->b_fname != NULL |
| 706 | && buf->b_p_bl) |
| 707 | { |
| 708 | if (fprintf(fd, "badd +%ld ", buf->b_wininfo == NULL ? 1L |
| 709 | : buf->b_wininfo->wi_fpos.lnum) < 0 |
| 710 | || ses_fname(fd, buf, &ssop_flags, TRUE) == FAIL) |
| 711 | goto fail; |
| 712 | } |
| 713 | } |
| 714 | |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 715 | // the global argument list |
| 716 | if (ses_arglist(fd, "argglobal", &global_alist.al_ga, |
| 717 | !(ssop_flags & SSOP_CURDIR), &ssop_flags) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 718 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 719 | |
| 720 | if (ssop_flags & SSOP_RESIZE) |
| 721 | { |
| 722 | // Note: after the restore we still check it worked! |
| 723 | if (fprintf(fd, "set lines=%ld columns=%ld" , Rows, Columns) < 0 |
| 724 | || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 725 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 726 | } |
| 727 | |
| 728 | #ifdef FEAT_GUI |
| 729 | if (gui.in_use && (ssop_flags & SSOP_WINPOS)) |
| 730 | { |
| 731 | int x, y; |
| 732 | |
| 733 | if (gui_mch_get_winpos(&x, &y) == OK) |
| 734 | { |
| 735 | // Note: after the restore we still check it worked! |
| 736 | if (fprintf(fd, "winpos %d %d", x, y) < 0 || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 737 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 738 | } |
| 739 | } |
| 740 | #endif |
| 741 | |
| 742 | // When there are two or more tabpages and 'showtabline' is 1 the tabline |
| 743 | // will be displayed when creating the next tab. That resizes the windows |
| 744 | // in the first tab, which may cause problems. Set 'showtabline' to 2 |
| 745 | // temporarily to avoid that. |
| 746 | if (p_stal == 1 && first_tabpage->tp_next != NULL) |
| 747 | { |
| 748 | if (put_line(fd, "set stal=2") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 749 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 750 | restore_stal = TRUE; |
| 751 | } |
| 752 | |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 753 | if ((ssop_flags & SSOP_TABPAGES)) |
| 754 | { |
LemonBoy | d7c9564 | 2022-04-30 16:10:27 +0100 | [diff] [blame] | 755 | // "tabpages" is in 'sessionoptions': Similar to ses_win_rec() below, |
| 756 | // populate the tab pages first so later local options won't be copied |
| 757 | // to the new tabs. |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 758 | FOR_ALL_TABPAGES(tp) |
Evgeni Chasnovski | 26ebf1f | 2022-01-14 13:19:43 +0000 | [diff] [blame] | 759 | // Use `bufhidden=wipe` to remove empty "placeholder" buffers once |
| 760 | // they are not needed. This prevents creating extra buffers (see |
| 761 | // cause of patch 8.1.0829) |
| 762 | if (tp->tp_next != NULL |
| 763 | && put_line(fd, "tabnew +setlocal\\ bufhidden=wipe") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 764 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 765 | if (first_tabpage->tp_next != NULL && put_line(fd, "tabrewind") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 766 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 767 | } |
LemonBoy | d7c9564 | 2022-04-30 16:10:27 +0100 | [diff] [blame] | 768 | |
| 769 | // Assume "tabpages" is in 'sessionoptions'. If not then we only do |
| 770 | // "curtab" and bail out of the loop. |
| 771 | FOR_ALL_TABPAGES(tp) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 772 | { |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 773 | int need_tabnext = FALSE; |
| 774 | int cnr = 1; |
| 775 | |
LemonBoy | d7c9564 | 2022-04-30 16:10:27 +0100 | [diff] [blame] | 776 | // May repeat putting Windows for each tab, when "tabpages" is in |
| 777 | // 'sessionoptions'. |
| 778 | // Don't use goto_tabpage(), it may change directory and trigger |
| 779 | // autocommands. |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 780 | if ((ssop_flags & SSOP_TABPAGES)) |
| 781 | { |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 782 | if (tp == curtab) |
| 783 | { |
| 784 | tab_firstwin = firstwin; |
| 785 | tab_topframe = topframe; |
| 786 | } |
| 787 | else |
| 788 | { |
| 789 | tab_firstwin = tp->tp_firstwin; |
| 790 | tab_topframe = tp->tp_topframe; |
| 791 | } |
LemonBoy | d7c9564 | 2022-04-30 16:10:27 +0100 | [diff] [blame] | 792 | if (tp != first_tabpage) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 793 | need_tabnext = TRUE; |
| 794 | } |
LemonBoy | d7c9564 | 2022-04-30 16:10:27 +0100 | [diff] [blame] | 795 | else |
| 796 | { |
| 797 | tp = curtab; |
| 798 | tab_firstwin = firstwin; |
| 799 | tab_topframe = topframe; |
| 800 | } |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 801 | |
| 802 | // Before creating the window layout, try loading one file. If this |
| 803 | // is aborted we don't end up with a number of useless windows. |
| 804 | // This may have side effects! (e.g., compressed or network file). |
| 805 | for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) |
| 806 | { |
| 807 | if (ses_do_win(wp) |
| 808 | && wp->w_buffer->b_ffname != NULL |
| 809 | && !bt_help(wp->w_buffer) |
Bram Moolenaar | 6d4b2f5 | 2022-08-25 15:11:15 +0100 | [diff] [blame] | 810 | && !bt_nofilename(wp->w_buffer)) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 811 | { |
| 812 | if (need_tabnext && put_line(fd, "tabnext") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 813 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 814 | need_tabnext = FALSE; |
| 815 | |
| 816 | if (fputs("edit ", fd) < 0 |
| 817 | || ses_fname(fd, wp->w_buffer, &ssop_flags, TRUE) |
| 818 | == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 819 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 820 | if (!wp->w_arg_idx_invalid) |
| 821 | edited_win = wp; |
| 822 | break; |
| 823 | } |
| 824 | } |
| 825 | |
| 826 | // If no file got edited create an empty tab page. |
| 827 | if (need_tabnext && put_line(fd, "tabnext") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 828 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 829 | |
Bram Moolenaar | 0995c81 | 2021-04-17 18:38:54 +0200 | [diff] [blame] | 830 | if (tab_topframe->fr_layout != FR_LEAF) |
| 831 | { |
| 832 | // Save current window layout. |
| 833 | if (put_line(fd, "let s:save_splitbelow = &splitbelow") == FAIL |
| 834 | || put_line(fd, "let s:save_splitright = &splitright") |
| 835 | == FAIL) |
| 836 | goto fail; |
| 837 | if (put_line(fd, "set splitbelow splitright") == FAIL) |
| 838 | goto fail; |
| 839 | if (ses_win_rec(fd, tab_topframe) == FAIL) |
| 840 | goto fail; |
| 841 | if (put_line(fd, "let &splitbelow = s:save_splitbelow") == FAIL |
| 842 | || put_line(fd, "let &splitright = s:save_splitright") |
| 843 | == FAIL) |
| 844 | goto fail; |
| 845 | } |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 846 | |
| 847 | // Check if window sizes can be restored (no windows omitted). |
| 848 | // Remember the window number of the current window after restoring. |
| 849 | nr = 0; |
| 850 | for (wp = tab_firstwin; wp != NULL; wp = W_NEXT(wp)) |
| 851 | { |
| 852 | if (ses_do_win(wp)) |
| 853 | ++nr; |
| 854 | else |
| 855 | restore_size = FALSE; |
| 856 | if (curwin == wp) |
| 857 | cnr = nr; |
| 858 | } |
| 859 | |
Bram Moolenaar | 0995c81 | 2021-04-17 18:38:54 +0200 | [diff] [blame] | 860 | if (tab_firstwin->w_next != NULL) |
| 861 | { |
| 862 | // Go to the first window. |
| 863 | if (put_line(fd, "wincmd t") == FAIL) |
| 864 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 865 | |
Bram Moolenaar | 0995c81 | 2021-04-17 18:38:54 +0200 | [diff] [blame] | 866 | // If more than one window, see if sizes can be restored. |
| 867 | // First set 'winheight' and 'winwidth' to 1 to avoid the windows |
| 868 | // being resized when moving between windows. |
| 869 | // Do this before restoring the view, so that the topline and the |
| 870 | // cursor can be set. This is done again below. |
| 871 | // winminheight and winminwidth need to be set to avoid an error if |
| 872 | // the user has set winheight or winwidth. |
| 873 | if (put_line(fd, "let s:save_winminheight = &winminheight") == FAIL |
| 874 | || put_line(fd, "let s:save_winminwidth = &winminwidth") |
| 875 | == FAIL) |
| 876 | goto fail; |
| 877 | if (put_line(fd, "set winminheight=0") == FAIL |
| 878 | || put_line(fd, "set winheight=1") == FAIL |
| 879 | || put_line(fd, "set winminwidth=0") == FAIL |
| 880 | || put_line(fd, "set winwidth=1") == FAIL) |
| 881 | goto fail; |
Bram Moolenaar | b4011af | 2022-05-01 00:42:24 +0100 | [diff] [blame] | 882 | restore_height_width = TRUE; |
Bram Moolenaar | 0995c81 | 2021-04-17 18:38:54 +0200 | [diff] [blame] | 883 | } |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 884 | if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 885 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 886 | |
| 887 | // Restore the tab-local working directory if specified |
| 888 | // Do this before the windows, so that the window-local directory can |
| 889 | // override the tab-local directory. |
LemonBoy | d7c9564 | 2022-04-30 16:10:27 +0100 | [diff] [blame] | 890 | if ((ssop_flags & SSOP_CURDIR) && tp->tp_localdir != NULL) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 891 | { |
| 892 | if (fputs("tcd ", fd) < 0 |
LemonBoy | d7c9564 | 2022-04-30 16:10:27 +0100 | [diff] [blame] | 893 | || ses_put_fname(fd, tp->tp_localdir, &ssop_flags) == FAIL |
| 894 | || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 895 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 896 | did_lcd = TRUE; |
| 897 | } |
| 898 | |
| 899 | // Restore the view of the window (options, file, cursor, etc.). |
| 900 | for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) |
| 901 | { |
| 902 | if (!ses_do_win(wp)) |
| 903 | continue; |
zeertzjq | a304e49 | 2025-06-10 20:31:44 +0200 | [diff] [blame] | 904 | if (put_view(fd, wp, tp, wp != edited_win, &ssop_flags, |
| 905 | cur_arg_idx, |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 906 | #ifdef FEAT_TERMINAL |
Bram Moolenaar | c2c8205 | 2020-09-11 22:10:22 +0200 | [diff] [blame] | 907 | &terminal_bufs |
| 908 | #else |
| 909 | NULL |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 910 | #endif |
| 911 | ) == FAIL) |
| 912 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 913 | if (nr > 1 && put_line(fd, "wincmd w") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 914 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 915 | next_arg_idx = wp->w_arg_idx; |
| 916 | } |
| 917 | |
| 918 | // The argument index in the first tab page is zero, need to set it in |
| 919 | // each window. For further tab pages it's the window where we do |
| 920 | // "tabedit". |
| 921 | cur_arg_idx = next_arg_idx; |
| 922 | |
| 923 | // Restore cursor to the current window if it's not the first one. |
| 924 | if (cnr > 1 && (fprintf(fd, "%dwincmd w", cnr) < 0 |
| 925 | || put_eol(fd) == FAIL)) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 926 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 927 | |
| 928 | // Restore window sizes again after jumping around in windows, because |
| 929 | // the current window has a minimum size while others may not. |
| 930 | if (nr > 1 && ses_winsizes(fd, restore_size, tab_firstwin) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 931 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 932 | |
| 933 | // Don't continue in another tab page when doing only the current one |
| 934 | // or when at the last tab page. |
| 935 | if (!(ssop_flags & SSOP_TABPAGES)) |
| 936 | break; |
| 937 | } |
| 938 | |
| 939 | if (ssop_flags & SSOP_TABPAGES) |
| 940 | { |
| 941 | if (fprintf(fd, "tabnext %d", tabpage_index(curtab)) < 0 |
| 942 | || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 943 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 944 | } |
| 945 | if (restore_stal && put_line(fd, "set stal=1") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 946 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 947 | |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 948 | // Wipe out an empty unnamed buffer we started in. |
| 949 | if (put_line(fd, "if exists('s:wipebuf') && len(win_findbuf(s:wipebuf)) == 0") |
| 950 | == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 951 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 952 | if (put_line(fd, " silent exe 'bwipe ' . s:wipebuf") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 953 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 954 | if (put_line(fd, "endif") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 955 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 956 | if (put_line(fd, "unlet! s:wipebuf") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 957 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 958 | |
James Cherti | fd01280 | 2022-03-29 12:02:57 +0100 | [diff] [blame] | 959 | // Re-apply 'winheight' and 'winwidth'. |
| 960 | if (fprintf(fd, "set winheight=%ld winwidth=%ld", |
| 961 | p_wh, p_wiw) < 0 || put_eol(fd) == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 962 | goto fail; |
James Cherti | fd01280 | 2022-03-29 12:02:57 +0100 | [diff] [blame] | 963 | |
| 964 | // Restore 'shortmess'. |
| 965 | if (ssop_flags & SSOP_OPTIONS) |
| 966 | { |
Bram Moolenaar | 6ed545e | 2022-05-09 20:09:23 +0100 | [diff] [blame] | 967 | if (fprintf(fd, "set shortmess=%s", p_shm) < 0 || put_eol(fd) == FAIL) |
| 968 | goto fail; |
James Cherti | fd01280 | 2022-03-29 12:02:57 +0100 | [diff] [blame] | 969 | } |
| 970 | else |
| 971 | { |
Bram Moolenaar | 6ed545e | 2022-05-09 20:09:23 +0100 | [diff] [blame] | 972 | if (put_line(fd, "let &shortmess = s:shortmess_save") == FAIL) |
| 973 | goto fail; |
James Cherti | fd01280 | 2022-03-29 12:02:57 +0100 | [diff] [blame] | 974 | } |
| 975 | |
Bram Moolenaar | b4011af | 2022-05-01 00:42:24 +0100 | [diff] [blame] | 976 | if (restore_height_width) |
Bram Moolenaar | 0995c81 | 2021-04-17 18:38:54 +0200 | [diff] [blame] | 977 | { |
| 978 | // Restore 'winminheight' and 'winminwidth'. |
| 979 | if (put_line(fd, "let &winminheight = s:save_winminheight") == FAIL |
| 980 | || put_line(fd, "let &winminwidth = s:save_winminwidth") == FAIL) |
| 981 | goto fail; |
| 982 | } |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 983 | |
| 984 | // Lastly, execute the x.vim file if it exists. |
| 985 | if (put_line(fd, "let s:sx = expand(\"<sfile>:p:r\").\"x.vim\"") == FAIL |
Bram Moolenaar | c5f33db | 2020-04-16 21:04:41 +0200 | [diff] [blame] | 986 | || put_line(fd, "if filereadable(s:sx)") == FAIL |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 987 | || put_line(fd, " exe \"source \" . fnameescape(s:sx)") == FAIL |
| 988 | || put_line(fd, "endif") == FAIL) |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 989 | goto fail; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 990 | |
Bram Moolenaar | 0e65511 | 2020-09-11 20:36:36 +0200 | [diff] [blame] | 991 | ret = OK; |
| 992 | fail: |
| 993 | #ifdef FEAT_TERMINAL |
| 994 | hash_clear_all(&terminal_bufs, 0); |
| 995 | #endif |
| 996 | return ret; |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 997 | } |
| 998 | |
| 999 | /* |
| 1000 | * Get the name of the view file for the current buffer. |
| 1001 | */ |
| 1002 | static char_u * |
| 1003 | get_view_file(int c) |
| 1004 | { |
| 1005 | int len = 0; |
| 1006 | char_u *p, *s; |
| 1007 | char_u *retval; |
| 1008 | char_u *sname; |
| 1009 | |
| 1010 | if (curbuf->b_ffname == NULL) |
| 1011 | { |
Bram Moolenaar | e29a27f | 2021-07-20 21:07:36 +0200 | [diff] [blame] | 1012 | emsg(_(e_no_file_name)); |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1013 | return NULL; |
| 1014 | } |
| 1015 | sname = home_replace_save(NULL, curbuf->b_ffname); |
| 1016 | if (sname == NULL) |
| 1017 | return NULL; |
| 1018 | |
| 1019 | // We want a file name without separators, because we're not going to make |
| 1020 | // a directory. |
| 1021 | // "normal" path separator -> "=+" |
| 1022 | // "=" -> "==" |
| 1023 | // ":" path separator -> "=-" |
| 1024 | for (p = sname; *p; ++p) |
| 1025 | if (*p == '=' || vim_ispathsep(*p)) |
| 1026 | ++len; |
| 1027 | retval = alloc(STRLEN(sname) + len + STRLEN(p_vdir) + 9); |
| 1028 | if (retval != NULL) |
| 1029 | { |
| 1030 | STRCPY(retval, p_vdir); |
| 1031 | add_pathsep(retval); |
| 1032 | s = retval + STRLEN(retval); |
| 1033 | for (p = sname; *p; ++p) |
| 1034 | { |
| 1035 | if (*p == '=') |
| 1036 | { |
| 1037 | *s++ = '='; |
| 1038 | *s++ = '='; |
| 1039 | } |
| 1040 | else if (vim_ispathsep(*p)) |
| 1041 | { |
| 1042 | *s++ = '='; |
| 1043 | #if defined(BACKSLASH_IN_FILENAME) || defined(AMIGA) || defined(VMS) |
| 1044 | if (*p == ':') |
| 1045 | *s++ = '-'; |
| 1046 | else |
| 1047 | #endif |
| 1048 | *s++ = '+'; |
| 1049 | } |
| 1050 | else |
| 1051 | *s++ = *p; |
| 1052 | } |
| 1053 | *s++ = '='; |
| 1054 | *s++ = c; |
| 1055 | STRCPY(s, ".vim"); |
| 1056 | } |
| 1057 | |
| 1058 | vim_free(sname); |
| 1059 | return retval; |
| 1060 | } |
| 1061 | |
| 1062 | /* |
| 1063 | * ":loadview [nr]" |
| 1064 | */ |
| 1065 | void |
| 1066 | ex_loadview(exarg_T *eap) |
| 1067 | { |
| 1068 | char_u *fname; |
| 1069 | |
| 1070 | fname = get_view_file(*eap->arg); |
Yegappan Lakshmanan | 6ec6666 | 2023-01-23 20:46:21 +0000 | [diff] [blame] | 1071 | if (fname == NULL) |
| 1072 | return; |
| 1073 | |
Christian Brabandt | d847b24 | 2025-04-22 20:04:28 +0200 | [diff] [blame] | 1074 | (void)do_source(fname, FALSE, DOSO_NONE, NULL); |
Yegappan Lakshmanan | 6ec6666 | 2023-01-23 20:46:21 +0000 | [diff] [blame] | 1075 | vim_free(fname); |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1076 | } |
| 1077 | |
Bram Moolenaar | d17a57a | 2019-09-29 20:53:55 +0200 | [diff] [blame] | 1078 | # if defined(FEAT_GUI_GNOME) \ |
Bram Moolenaar | ac02a63 | 2019-09-29 19:02:46 +0200 | [diff] [blame] | 1079 | || (defined(GUI_MAY_SPAWN) && defined(EXPERIMENTAL_GUI_CMD)) \ |
| 1080 | || defined(PROTO) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1081 | /* |
| 1082 | * Generate a script that can be used to restore the current editing session. |
| 1083 | * Save the value of v:this_session before running :mksession in order to make |
| 1084 | * automagic session save fully transparent. Return TRUE on success. |
| 1085 | */ |
| 1086 | int |
| 1087 | write_session_file(char_u *filename) |
| 1088 | { |
| 1089 | char_u *escaped_filename; |
| 1090 | char *mksession_cmdline; |
| 1091 | unsigned int save_ssop_flags; |
| 1092 | int failed; |
| 1093 | |
| 1094 | // Build an ex command line to create a script that restores the current |
| 1095 | // session if executed. Escape the filename to avoid nasty surprises. |
| 1096 | escaped_filename = vim_strsave_escaped(filename, escape_chars); |
| 1097 | if (escaped_filename == NULL) |
| 1098 | return FALSE; |
| 1099 | mksession_cmdline = alloc(10 + (int)STRLEN(escaped_filename) + 1); |
| 1100 | if (mksession_cmdline == NULL) |
| 1101 | { |
| 1102 | vim_free(escaped_filename); |
| 1103 | return FALSE; |
| 1104 | } |
| 1105 | strcpy(mksession_cmdline, "mksession "); |
| 1106 | STRCAT(mksession_cmdline, escaped_filename); |
| 1107 | vim_free(escaped_filename); |
| 1108 | |
| 1109 | // Use a reasonable hardcoded set of 'sessionoptions' flags to avoid |
| 1110 | // unpredictable effects when the session is saved automatically. Also, |
| 1111 | // we definitely need SSOP_GLOBALS to be able to restore v:this_session. |
| 1112 | // Don't use SSOP_BUFFERS to prevent the buffer list from becoming |
| 1113 | // enormously large if the GNOME session feature is used regularly. |
| 1114 | save_ssop_flags = ssop_flags; |
| 1115 | ssop_flags = (SSOP_BLANK|SSOP_CURDIR|SSOP_FOLDS|SSOP_GLOBALS |
| 1116 | |SSOP_HELP|SSOP_OPTIONS|SSOP_WINSIZE|SSOP_TABPAGES); |
| 1117 | |
| 1118 | do_cmdline_cmd((char_u *)"let Save_VV_this_session = v:this_session"); |
| 1119 | failed = (do_cmdline_cmd((char_u *)mksession_cmdline) == FAIL); |
| 1120 | do_cmdline_cmd((char_u *)"let v:this_session = Save_VV_this_session"); |
| 1121 | do_unlet((char_u *)"Save_VV_this_session", TRUE); |
| 1122 | |
| 1123 | ssop_flags = save_ssop_flags; |
| 1124 | vim_free(mksession_cmdline); |
| 1125 | |
| 1126 | // Reopen the file and append a command to restore v:this_session, |
| 1127 | // as if this save never happened. This is to avoid conflicts with |
| 1128 | // the user's own sessions. FIXME: It's probably less hackish to add |
| 1129 | // a "stealth" flag to 'sessionoptions' -- gotta ask Bram. |
| 1130 | if (!failed) |
| 1131 | { |
| 1132 | FILE *fd; |
| 1133 | |
| 1134 | fd = open_exfile(filename, TRUE, APPENDBIN); |
| 1135 | |
| 1136 | failed = (fd == NULL |
| 1137 | || put_line(fd, "let v:this_session = Save_VV_this_session") |
| 1138 | == FAIL |
| 1139 | || put_line(fd, "unlet Save_VV_this_session") == FAIL); |
| 1140 | |
| 1141 | if (fd != NULL && fclose(fd) != 0) |
| 1142 | failed = TRUE; |
| 1143 | |
| 1144 | if (failed) |
| 1145 | mch_remove(filename); |
| 1146 | } |
| 1147 | |
| 1148 | return !failed; |
| 1149 | } |
| 1150 | # endif |
| 1151 | |
| 1152 | #endif // FEAT_SESSION |
| 1153 | |
| 1154 | #if defined(FEAT_SESSION) && defined(USE_CRNL) |
| 1155 | # define MKSESSION_NL |
| 1156 | static int mksession_nl = FALSE; // use NL only in put_eol() |
| 1157 | #endif |
| 1158 | |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1159 | /* |
| 1160 | * ":mkexrc", ":mkvimrc", ":mkview" and ":mksession". |
| 1161 | */ |
| 1162 | void |
| 1163 | ex_mkrc(exarg_T *eap) |
| 1164 | { |
| 1165 | FILE *fd; |
| 1166 | int failed = FALSE; |
| 1167 | char_u *fname; |
| 1168 | #ifdef FEAT_BROWSE |
| 1169 | char_u *browseFile = NULL; |
| 1170 | #endif |
| 1171 | #ifdef FEAT_SESSION |
| 1172 | int view_session = FALSE; |
| 1173 | int using_vdir = FALSE; // using 'viewdir'? |
| 1174 | char_u *viewFile = NULL; |
| 1175 | unsigned *flagp; |
| 1176 | #endif |
| 1177 | |
| 1178 | if (eap->cmdidx == CMD_mksession || eap->cmdidx == CMD_mkview) |
| 1179 | { |
| 1180 | #ifdef FEAT_SESSION |
| 1181 | view_session = TRUE; |
| 1182 | #else |
| 1183 | ex_ni(eap); |
| 1184 | return; |
| 1185 | #endif |
| 1186 | } |
| 1187 | |
| 1188 | #ifdef FEAT_SESSION |
| 1189 | // Use the short file name until ":lcd" is used. We also don't use the |
| 1190 | // short file name when 'acd' is set, that is checked later. |
| 1191 | did_lcd = FALSE; |
| 1192 | |
| 1193 | // ":mkview" or ":mkview 9": generate file name with 'viewdir' |
| 1194 | if (eap->cmdidx == CMD_mkview |
| 1195 | && (*eap->arg == NUL |
| 1196 | || (vim_isdigit(*eap->arg) && eap->arg[1] == NUL))) |
| 1197 | { |
| 1198 | eap->forceit = TRUE; |
| 1199 | fname = get_view_file(*eap->arg); |
| 1200 | if (fname == NULL) |
| 1201 | return; |
| 1202 | viewFile = fname; |
| 1203 | using_vdir = TRUE; |
| 1204 | } |
| 1205 | else |
| 1206 | #endif |
| 1207 | if (*eap->arg != NUL) |
| 1208 | fname = eap->arg; |
| 1209 | else if (eap->cmdidx == CMD_mkvimrc) |
| 1210 | fname = (char_u *)VIMRC_FILE; |
| 1211 | #ifdef FEAT_SESSION |
| 1212 | else if (eap->cmdidx == CMD_mksession) |
| 1213 | fname = (char_u *)SESSION_FILE; |
| 1214 | #endif |
| 1215 | else |
| 1216 | fname = (char_u *)EXRC_FILE; |
| 1217 | |
| 1218 | #ifdef FEAT_BROWSE |
Bram Moolenaar | e100440 | 2020-10-24 20:49:43 +0200 | [diff] [blame] | 1219 | if (cmdmod.cmod_flags & CMOD_BROWSE) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1220 | { |
| 1221 | browseFile = do_browse(BROWSE_SAVE, |
| 1222 | # ifdef FEAT_SESSION |
| 1223 | eap->cmdidx == CMD_mkview ? (char_u *)_("Save View") : |
| 1224 | eap->cmdidx == CMD_mksession ? (char_u *)_("Save Session") : |
| 1225 | # endif |
| 1226 | (char_u *)_("Save Setup"), |
| 1227 | fname, (char_u *)"vim", NULL, |
| 1228 | (char_u *)_(BROWSE_FILTER_MACROS), NULL); |
| 1229 | if (browseFile == NULL) |
| 1230 | goto theend; |
| 1231 | fname = browseFile; |
| 1232 | eap->forceit = TRUE; // since dialog already asked |
| 1233 | } |
| 1234 | #endif |
| 1235 | |
| 1236 | #if defined(FEAT_SESSION) && defined(vim_mkdir) |
| 1237 | // When using 'viewdir' may have to create the directory. |
| 1238 | if (using_vdir && !mch_isdir(p_vdir)) |
| 1239 | vim_mkdir_emsg(p_vdir, 0755); |
| 1240 | #endif |
| 1241 | |
| 1242 | fd = open_exfile(fname, eap->forceit, WRITEBIN); |
| 1243 | if (fd != NULL) |
| 1244 | { |
| 1245 | #ifdef FEAT_SESSION |
| 1246 | if (eap->cmdidx == CMD_mkview) |
| 1247 | flagp = &vop_flags; |
| 1248 | else |
| 1249 | flagp = &ssop_flags; |
| 1250 | #endif |
| 1251 | |
| 1252 | #ifdef MKSESSION_NL |
| 1253 | // "unix" in 'sessionoptions': use NL line separator |
| 1254 | if (view_session && (*flagp & SSOP_UNIX)) |
| 1255 | mksession_nl = TRUE; |
| 1256 | #endif |
| 1257 | |
| 1258 | // Write the version command for :mkvimrc |
| 1259 | if (eap->cmdidx == CMD_mkvimrc) |
| 1260 | (void)put_line(fd, "version 6.0"); |
| 1261 | |
| 1262 | #ifdef FEAT_SESSION |
| 1263 | if (eap->cmdidx == CMD_mksession) |
| 1264 | { |
| 1265 | if (put_line(fd, "let SessionLoad = 1") == FAIL) |
| 1266 | failed = TRUE; |
| 1267 | } |
| 1268 | |
| 1269 | if (eap->cmdidx != CMD_mkview) |
| 1270 | #endif |
| 1271 | { |
| 1272 | // Write setting 'compatible' first, because it has side effects. |
| 1273 | // For that same reason only do it when needed. |
| 1274 | if (p_cp) |
| 1275 | (void)put_line(fd, "if !&cp | set cp | endif"); |
| 1276 | else |
| 1277 | (void)put_line(fd, "if &cp | set nocp | endif"); |
| 1278 | } |
| 1279 | |
| 1280 | #ifdef FEAT_SESSION |
| 1281 | if (!view_session |
| 1282 | || (eap->cmdidx == CMD_mksession |
| 1283 | && (*flagp & SSOP_OPTIONS))) |
| 1284 | #endif |
Bram Moolenaar | 635bd60 | 2021-04-16 19:58:22 +0200 | [diff] [blame] | 1285 | { |
| 1286 | int flags = OPT_GLOBAL; |
| 1287 | |
| 1288 | #ifdef FEAT_SESSION |
| 1289 | if (eap->cmdidx == CMD_mksession && (*flagp & SSOP_SKIP_RTP)) |
| 1290 | flags |= OPT_SKIPRTP; |
| 1291 | #endif |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1292 | failed |= (makemap(fd, NULL) == FAIL |
Bram Moolenaar | 635bd60 | 2021-04-16 19:58:22 +0200 | [diff] [blame] | 1293 | || makeset(fd, flags, FALSE) == FAIL); |
| 1294 | } |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1295 | |
| 1296 | #ifdef FEAT_SESSION |
| 1297 | if (!failed && view_session) |
| 1298 | { |
Bram Moolenaar | 3889083 | 2020-11-01 17:40:54 +0100 | [diff] [blame] | 1299 | if (put_line(fd, "let s:so_save = &g:so | let s:siso_save = &g:siso | setg so=0 siso=0 | setl so=-1 siso=-1") == FAIL) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1300 | failed = TRUE; |
| 1301 | if (eap->cmdidx == CMD_mksession) |
| 1302 | { |
| 1303 | char_u *dirnow; // current directory |
| 1304 | |
| 1305 | dirnow = alloc(MAXPATHL); |
| 1306 | if (dirnow == NULL) |
| 1307 | failed = TRUE; |
| 1308 | else |
| 1309 | { |
| 1310 | // Change to session file's dir. |
| 1311 | if (mch_dirname(dirnow, MAXPATHL) == FAIL |
| 1312 | || mch_chdir((char *)dirnow) != 0) |
| 1313 | *dirnow = NUL; |
| 1314 | if (*dirnow != NUL && (ssop_flags & SSOP_SESDIR)) |
| 1315 | { |
| 1316 | if (vim_chdirfile(fname, NULL) == OK) |
| 1317 | shorten_fnames(TRUE); |
| 1318 | } |
| 1319 | else if (*dirnow != NUL |
| 1320 | && (ssop_flags & SSOP_CURDIR) && globaldir != NULL) |
| 1321 | { |
| 1322 | if (mch_chdir((char *)globaldir) == 0) |
| 1323 | shorten_fnames(TRUE); |
| 1324 | } |
| 1325 | |
| 1326 | failed |= (makeopens(fd, dirnow) == FAIL); |
| 1327 | |
| 1328 | // restore original dir |
| 1329 | if (*dirnow != NUL && ((ssop_flags & SSOP_SESDIR) |
| 1330 | || ((ssop_flags & SSOP_CURDIR) && globaldir != NULL))) |
| 1331 | { |
| 1332 | if (mch_chdir((char *)dirnow) != 0) |
Bram Moolenaar | 460ae5d | 2022-01-01 14:19:49 +0000 | [diff] [blame] | 1333 | emsg(_(e_cannot_go_back_to_previous_directory)); |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1334 | shorten_fnames(TRUE); |
| 1335 | } |
| 1336 | vim_free(dirnow); |
| 1337 | } |
| 1338 | } |
| 1339 | else |
| 1340 | { |
zeertzjq | a304e49 | 2025-06-10 20:31:44 +0200 | [diff] [blame] | 1341 | failed |= (put_view(fd, curwin, curtab, !using_vdir, flagp, -1, |
| 1342 | NULL) == FAIL); |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1343 | } |
Bram Moolenaar | 3889083 | 2020-11-01 17:40:54 +0100 | [diff] [blame] | 1344 | if (put_line(fd, "let &g:so = s:so_save | let &g:siso = s:siso_save") |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1345 | == FAIL) |
| 1346 | failed = TRUE; |
Bram Moolenaar | f96ae0b | 2019-07-28 15:21:55 +0200 | [diff] [blame] | 1347 | #ifdef FEAT_SEARCH_EXTRA |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1348 | if (no_hlsearch && put_line(fd, "nohlsearch") == FAIL) |
| 1349 | failed = TRUE; |
Bram Moolenaar | f96ae0b | 2019-07-28 15:21:55 +0200 | [diff] [blame] | 1350 | #endif |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1351 | if (put_line(fd, "doautoall SessionLoadPost") == FAIL) |
| 1352 | failed = TRUE; |
| 1353 | if (eap->cmdidx == CMD_mksession) |
| 1354 | { |
| 1355 | if (put_line(fd, "unlet SessionLoad") == FAIL) |
| 1356 | failed = TRUE; |
| 1357 | } |
| 1358 | } |
| 1359 | #endif |
| 1360 | if (put_line(fd, "\" vim: set ft=vim :") == FAIL) |
| 1361 | failed = TRUE; |
| 1362 | |
| 1363 | failed |= fclose(fd); |
| 1364 | |
| 1365 | if (failed) |
Bram Moolenaar | 40bcec1 | 2021-12-05 22:19:27 +0000 | [diff] [blame] | 1366 | emsg(_(e_error_while_writing)); |
Bram Moolenaar | f96ae0b | 2019-07-28 15:21:55 +0200 | [diff] [blame] | 1367 | #if defined(FEAT_SESSION) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1368 | else if (eap->cmdidx == CMD_mksession) |
| 1369 | { |
| 1370 | // successful session write - set this_session var |
| 1371 | char_u *tbuf; |
| 1372 | |
| 1373 | tbuf = alloc(MAXPATHL); |
| 1374 | if (tbuf != NULL) |
| 1375 | { |
| 1376 | if (vim_FullName(fname, tbuf, MAXPATHL, FALSE) == OK) |
| 1377 | set_vim_var_string(VV_THIS_SESSION, tbuf, -1); |
| 1378 | vim_free(tbuf); |
| 1379 | } |
| 1380 | } |
| 1381 | #endif |
| 1382 | #ifdef MKSESSION_NL |
| 1383 | mksession_nl = FALSE; |
| 1384 | #endif |
| 1385 | } |
| 1386 | |
| 1387 | #ifdef FEAT_BROWSE |
| 1388 | theend: |
| 1389 | vim_free(browseFile); |
| 1390 | #endif |
| 1391 | #ifdef FEAT_SESSION |
| 1392 | vim_free(viewFile); |
| 1393 | #endif |
Colin Kennedy | e5f2280 | 2024-03-26 18:20:16 +0100 | [diff] [blame] | 1394 | |
| 1395 | apply_autocmds(EVENT_SESSIONWRITEPOST, NULL, NULL, FALSE, curbuf); |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1396 | } |
| 1397 | |
Bram Moolenaar | f96ae0b | 2019-07-28 15:21:55 +0200 | [diff] [blame] | 1398 | #if (defined(FEAT_VIMINFO) || defined(FEAT_SESSION)) || defined(PROTO) |
Bram Moolenaar | 8453807 | 2019-07-28 14:15:42 +0200 | [diff] [blame] | 1399 | var_flavour_T |
| 1400 | var_flavour(char_u *varname) |
| 1401 | { |
| 1402 | char_u *p = varname; |
| 1403 | |
| 1404 | if (ASCII_ISUPPER(*p)) |
| 1405 | { |
| 1406 | while (*(++p)) |
| 1407 | if (ASCII_ISLOWER(*p)) |
| 1408 | return VAR_FLAVOUR_SESSION; |
| 1409 | return VAR_FLAVOUR_VIMINFO; |
| 1410 | } |
| 1411 | else |
| 1412 | return VAR_FLAVOUR_DEFAULT; |
| 1413 | } |
| 1414 | #endif |
| 1415 | |
| 1416 | /* |
| 1417 | * Write end-of-line character(s) for ":mkexrc", ":mkvimrc" and ":mksession". |
| 1418 | * Return FAIL for a write error. |
| 1419 | */ |
| 1420 | int |
| 1421 | put_eol(FILE *fd) |
| 1422 | { |
| 1423 | if ( |
| 1424 | #ifdef USE_CRNL |
| 1425 | ( |
| 1426 | # ifdef MKSESSION_NL |
| 1427 | !mksession_nl && |
| 1428 | # endif |
| 1429 | (putc('\r', fd) < 0)) || |
| 1430 | #endif |
| 1431 | (putc('\n', fd) < 0)) |
| 1432 | return FAIL; |
| 1433 | return OK; |
| 1434 | } |
| 1435 | |
| 1436 | /* |
| 1437 | * Write a line to "fd". |
| 1438 | * Return FAIL for a write error. |
| 1439 | */ |
| 1440 | int |
| 1441 | put_line(FILE *fd, char *s) |
| 1442 | { |
| 1443 | if (fputs(s, fd) < 0 || put_eol(fd) == FAIL) |
| 1444 | return FAIL; |
| 1445 | return OK; |
| 1446 | } |