// This is an open source non-commercial project. Dear PVS-Studio, please check
// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
// Functions for creating a session file, i.e. implementing:
// :mkexrc
// :mkvimrc
// :mkview
// :mksession
#include <assert.h>
#include <inttypes.h>
#include <limits.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "nvim/arglist.h"
#include "nvim/ascii.h"
#include "nvim/buffer.h"
#include "nvim/eval.h"
#include "nvim/ex_cmds_defs.h"
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/ex_session.h"
#include "nvim/file_search.h"
#include "nvim/fileio.h"
#include "nvim/fold.h"
#include "nvim/garray.h"
#include "nvim/gettext.h"
#include "nvim/globals.h"
#include "nvim/macros.h"
#include "nvim/mapping.h"
#include "nvim/mark_defs.h"
#include "nvim/memory.h"
#include "nvim/message.h"
#include "nvim/option.h"
#include "nvim/os/os.h"
#include "nvim/path.h"
#include "nvim/pos.h"
#include "nvim/runtime.h"
#include "nvim/vim.h"
#include "nvim/window.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_session.c.generated.h"
#endif
/// Whether ":lcd" or ":tcd" was produced for a session.
static int did_lcd;
#define PUTLINE_FAIL(s) \
do { if (FAIL == put_line(fd, (s))) { return FAIL; } } while (0)
static int put_view_curpos(FILE *fd, const win_T *wp, char *spaces)
{
int r;
if (wp->w_curswant == MAXCOL) {
r = fprintf(fd, "%snormal! $\n", spaces);
} else {
r = fprintf(fd, "%snormal! 0%d|\n", spaces, wp->w_virtcol + 1);
}
return r >= 0;
}
static int ses_winsizes(FILE *fd, int restore_size, win_T *tab_firstwin)
{
int n = 0;
win_T *wp;
if (restore_size && (ssop_flags & SSOP_WINSIZE)) {
for (wp = tab_firstwin; wp != NULL; wp = wp->w_next) {
if (!ses_do_win(wp)) {
continue;
}
n++;
// restore height when not full height
if (wp->w_height + wp->w_hsep_height + wp->w_status_height < topframe->fr_height
&& (fprintf(fd,
"exe '%dresize ' . ((&lines * %" PRId64
" + %" PRId64 ") / %" PRId64 ")\n",
n, (int64_t)wp->w_height,
(int64_t)Rows / 2, (int64_t)Rows) < 0)) {
return FAIL;
}
// restore width when not full width
if (wp->w_width < Columns
&& (fprintf(fd,
"exe 'vert %dresize ' . ((&columns * %" PRId64
" + %" PRId64 ") / %" PRId64 ")\n",
n, (int64_t)wp->w_width, (int64_t)Columns / 2,
(int64_t)Columns) < 0)) {
return FAIL;
}
}
} else {
// Just equalize window sizes.
PUTLINE_FAIL("wincmd =");
}
return OK;
}
/// Write commands to "fd" to recursively create windows for frame "fr",
/// horizontally and vertically split.
/// After the commands the last window in the frame is the current window.
///
/// @return FAIL when writing the commands to "fd" fails.
static int ses_win_rec(FILE *fd, frame_T *fr)
{
frame_T *frc;
int count = 0;
if (fr->fr_layout != FR_LEAF) {
// Find first frame that's not skipped and then create a window for
// each following one (first frame is already there).
frc = ses_skipframe(fr->fr_child);
if (frc != NULL) {
while ((frc = ses_skipframe(frc->fr_next)) != NULL) {
// Make window as big as possible so that we have lots of room
// to split.
if (fprintf(fd, "%s%s",
"wincmd _ | wincmd |\n",
(fr->fr_layout == FR_COL ? "split\n" : "vsplit\n")) < 0) {
return FAIL;
}
count++;
}
}
// Go back to the first window.
if (count > 0 && (fprintf(fd, fr->fr_layout == FR_COL
? "%dwincmd k\n" : "%dwincmd h\n", count) < 0)) {
return FAIL;
}
// Recursively create frames/windows in each window of this column or row.
frc = ses_skipframe(fr->fr_child);
while (frc != NULL) {
ses_win_rec(fd, frc);
frc = ses_skipframe(frc->fr_next);
// Go to next window.
if (frc != NULL && put_line(fd, "wincmd w") == FAIL) {
return FAIL;
}
}
}
return OK;
}
/// Skip frames that don't contain windows we want to save in the Session.
///
/// @return NULL when there none.
static frame_T *ses_skipframe(frame_T *fr)
{
frame_T *frc;
FOR_ALL_FRAMES(frc, fr) {
if (ses_do_frame(frc)) {
break;
}
}
return frc;
}
/// @return true if frame "fr" has a window somewhere that we want to save in
/// the Session.
static bool ses_do_frame(const frame_T *fr)
FUNC_ATTR_NONNULL_ARG(1)
{
const frame_T *frc;
if (fr->fr_layout == FR_LEAF) {
return ses_do_win(fr->fr_win);
}
FOR_ALL_FRAMES(frc, fr->fr_child) {
if (ses_do_frame(frc)) {
return true;
}
}
return false;
}
/// @return non-zero if window "wp" is to be stored in the Session.
static int ses_do_win(win_T *wp)
{
// Skip floating windows to avoid issues when restoring the Session. #18432
if (wp->w_floating) {
return false;
}
if (wp->w_buffer->b_fname == NULL
// When 'buftype' is "nofile" can't restore the window contents.
|| (!wp->w_buffer->terminal && bt_nofilename(wp->w_buffer))) {
return ssop_flags & SSOP_BLANK;
}
if (bt_help(wp->w_buffer)) {
return ssop_flags & SSOP_HELP;
}
if (bt_terminal(wp->w_buffer)) {
return ssop_flags & SSOP_TERMINAL;
}
return true;
}
/// Writes an :argument list to the session file.
///
/// @param fd
/// @par
|