/src/buffer.c
https://bitbucket.org/ultra_iter/vim-qt · C · 5722 lines · 4589 code · 367 blank · 766 comment · 1200 complexity · 8a2df500a792f7f65efd467f4a83acac MD5 · raw file
Large files are truncated click here to view the full file
- /* vi:set ts=8 sts=4 sw=4:
- *
- * VIM - Vi IMproved by Bram Moolenaar
- *
- * Do ":help uganda" in Vim to read copying and usage conditions.
- * Do ":help credits" in Vim to see a list of people who contributed.
- * See README.txt for an overview of the Vim source code.
- */
- /*
- * buffer.c: functions for dealing with the buffer structure
- */
- /*
- * The buffer list is a double linked list of all buffers.
- * Each buffer can be in one of these states:
- * never loaded: BF_NEVERLOADED is set, only the file name is valid
- * not loaded: b_ml.ml_mfp == NULL, no memfile allocated
- * hidden: b_nwindows == 0, loaded but not displayed in a window
- * normal: loaded and displayed in a window
- *
- * Instead of storing file names all over the place, each file name is
- * stored in the buffer list. It can be referenced by a number.
- *
- * The current implementation remembers all file names ever used.
- */
- #include "vim.h"
- #if defined(FEAT_CMDL_COMPL) || defined(FEAT_LISTCMDS) || defined(FEAT_EVAL) || defined(FEAT_PERL)
- static char_u *buflist_match __ARGS((regprog_T *prog, buf_T *buf));
- # define HAVE_BUFLIST_MATCH
- static char_u *fname_match __ARGS((regprog_T *prog, char_u *name));
- #endif
- static void buflist_setfpos __ARGS((buf_T *buf, win_T *win, linenr_T lnum, colnr_T col, int copy_options));
- static wininfo_T *find_wininfo __ARGS((buf_T *buf, int skip_diff_buffer));
- #ifdef UNIX
- static buf_T *buflist_findname_stat __ARGS((char_u *ffname, struct stat *st));
- static int otherfile_buf __ARGS((buf_T *buf, char_u *ffname, struct stat *stp));
- static int buf_same_ino __ARGS((buf_T *buf, struct stat *stp));
- #else
- static int otherfile_buf __ARGS((buf_T *buf, char_u *ffname));
- #endif
- #ifdef FEAT_TITLE
- static int ti_change __ARGS((char_u *str, char_u **last));
- #endif
- static int append_arg_number __ARGS((win_T *wp, char_u *buf, int buflen, int add_file));
- static void free_buffer __ARGS((buf_T *));
- static void free_buffer_stuff __ARGS((buf_T *buf, int free_options));
- static void clear_wininfo __ARGS((buf_T *buf));
- #ifdef UNIX
- # define dev_T dev_t
- #else
- # define dev_T unsigned
- #endif
- #if defined(FEAT_SIGNS)
- static void insert_sign __ARGS((buf_T *buf, signlist_T *prev, signlist_T *next, int id, linenr_T lnum, int typenr));
- static void buf_delete_signs __ARGS((buf_T *buf));
- #endif
- #if defined(FEAT_WINDOWS) && defined(FEAT_QUICKFIX)
- static char *msg_loclist = N_("[Location List]");
- static char *msg_qflist = N_("[Quickfix List]");
- #endif
- #ifdef FEAT_AUTOCMD
- static char *e_auabort = N_("E855: Autocommands caused command to abort");
- #endif
- /*
- * Open current buffer, that is: open the memfile and read the file into
- * memory.
- * Return FAIL for failure, OK otherwise.
- */
- int
- open_buffer(read_stdin, eap, flags)
- int read_stdin; /* read file from stdin */
- exarg_T *eap; /* for forced 'ff' and 'fenc' or NULL */
- int flags; /* extra flags for readfile() */
- {
- int retval = OK;
- #ifdef FEAT_AUTOCMD
- buf_T *old_curbuf;
- #endif
- /*
- * The 'readonly' flag is only set when BF_NEVERLOADED is being reset.
- * When re-entering the same buffer, it should not change, because the
- * user may have reset the flag by hand.
- */
- if (readonlymode && curbuf->b_ffname != NULL
- && (curbuf->b_flags & BF_NEVERLOADED))
- curbuf->b_p_ro = TRUE;
- if (ml_open(curbuf) == FAIL)
- {
- /*
- * There MUST be a memfile, otherwise we can't do anything
- * If we can't create one for the current buffer, take another buffer
- */
- close_buffer(NULL, curbuf, 0, FALSE);
- for (curbuf = firstbuf; curbuf != NULL; curbuf = curbuf->b_next)
- if (curbuf->b_ml.ml_mfp != NULL)
- break;
- /*
- * if there is no memfile at all, exit
- * This is OK, since there are no changes to lose.
- */
- if (curbuf == NULL)
- {
- EMSG(_("E82: Cannot allocate any buffer, exiting..."));
- getout(2);
- }
- EMSG(_("E83: Cannot allocate buffer, using other one..."));
- enter_buffer(curbuf);
- return FAIL;
- }
- #ifdef FEAT_AUTOCMD
- /* The autocommands in readfile() may change the buffer, but only AFTER
- * reading the file. */
- old_curbuf = curbuf;
- modified_was_set = FALSE;
- #endif
- /* mark cursor position as being invalid */
- curwin->w_valid = 0;
- if (curbuf->b_ffname != NULL
- #ifdef FEAT_NETBEANS_INTG
- && netbeansReadFile
- #endif
- )
- {
- #ifdef FEAT_NETBEANS_INTG
- int oldFire = netbeansFireChanges;
- netbeansFireChanges = 0;
- #endif
- retval = readfile(curbuf->b_ffname, curbuf->b_fname,
- (linenr_T)0, (linenr_T)0, (linenr_T)MAXLNUM, eap,
- flags | READ_NEW);
- #ifdef FEAT_NETBEANS_INTG
- netbeansFireChanges = oldFire;
- #endif
- /* Help buffer is filtered. */
- if (curbuf->b_help)
- fix_help_buffer();
- }
- else if (read_stdin)
- {
- int save_bin = curbuf->b_p_bin;
- linenr_T line_count;
- /*
- * First read the text in binary mode into the buffer.
- * Then read from that same buffer and append at the end. This makes
- * it possible to retry when 'fileformat' or 'fileencoding' was
- * guessed wrong.
- */
- curbuf->b_p_bin = TRUE;
- retval = readfile(NULL, NULL, (linenr_T)0,
- (linenr_T)0, (linenr_T)MAXLNUM, NULL,
- flags | (READ_NEW + READ_STDIN));
- curbuf->b_p_bin = save_bin;
- if (retval == OK)
- {
- line_count = curbuf->b_ml.ml_line_count;
- retval = readfile(NULL, NULL, (linenr_T)line_count,
- (linenr_T)0, (linenr_T)MAXLNUM, eap,
- flags | READ_BUFFER);
- if (retval == OK)
- {
- /* Delete the binary lines. */
- while (--line_count >= 0)
- ml_delete((linenr_T)1, FALSE);
- }
- else
- {
- /* Delete the converted lines. */
- while (curbuf->b_ml.ml_line_count > line_count)
- ml_delete(line_count, FALSE);
- }
- /* Put the cursor on the first line. */
- curwin->w_cursor.lnum = 1;
- curwin->w_cursor.col = 0;
- /* Set or reset 'modified' before executing autocommands, so that
- * it can be changed there. */
- if (!readonlymode && !bufempty())
- changed();
- else if (retval != FAIL)
- unchanged(curbuf, FALSE);
- #ifdef FEAT_AUTOCMD
- # ifdef FEAT_EVAL
- apply_autocmds_retval(EVENT_STDINREADPOST, NULL, NULL, FALSE,
- curbuf, &retval);
- # else
- apply_autocmds(EVENT_STDINREADPOST, NULL, NULL, FALSE, curbuf);
- # endif
- #endif
- }
- }
- /* if first time loading this buffer, init b_chartab[] */
- if (curbuf->b_flags & BF_NEVERLOADED)
- (void)buf_init_chartab(curbuf, FALSE);
- /*
- * Set/reset the Changed flag first, autocmds may change the buffer.
- * Apply the automatic commands, before processing the modelines.
- * So the modelines have priority over auto commands.
- */
- /* When reading stdin, the buffer contents always needs writing, so set
- * the changed flag. Unless in readonly mode: "ls | gview -".
- * When interrupted and 'cpoptions' contains 'i' set changed flag. */
- if ((got_int && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
- #ifdef FEAT_AUTOCMD
- || modified_was_set /* ":set modified" used in autocmd */
- # ifdef FEAT_EVAL
- || (aborting() && vim_strchr(p_cpo, CPO_INTMOD) != NULL)
- # endif
- #endif
- )
- changed();
- else if (retval != FAIL && !read_stdin)
- unchanged(curbuf, FALSE);
- save_file_ff(curbuf); /* keep this fileformat */
- /* require "!" to overwrite the file, because it wasn't read completely */
- #ifdef FEAT_EVAL
- if (aborting())
- #else
- if (got_int)
- #endif
- curbuf->b_flags |= BF_READERR;
- #ifdef FEAT_FOLDING
- /* Need to update automatic folding. Do this before the autocommands,
- * they may use the fold info. */
- foldUpdateAll(curwin);
- #endif
- #ifdef FEAT_AUTOCMD
- /* need to set w_topline, unless some autocommand already did that. */
- if (!(curwin->w_valid & VALID_TOPLINE))
- {
- curwin->w_topline = 1;
- # ifdef FEAT_DIFF
- curwin->w_topfill = 0;
- # endif
- }
- # ifdef FEAT_EVAL
- apply_autocmds_retval(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf, &retval);
- # else
- apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
- # endif
- #endif
- if (retval != FAIL)
- {
- #ifdef FEAT_AUTOCMD
- /*
- * The autocommands may have changed the current buffer. Apply the
- * modelines to the correct buffer, if it still exists and is loaded.
- */
- if (buf_valid(old_curbuf) && old_curbuf->b_ml.ml_mfp != NULL)
- {
- aco_save_T aco;
- /* Go to the buffer that was opened. */
- aucmd_prepbuf(&aco, old_curbuf);
- #endif
- do_modelines(0);
- curbuf->b_flags &= ~(BF_CHECK_RO | BF_NEVERLOADED);
- #ifdef FEAT_AUTOCMD
- # ifdef FEAT_EVAL
- apply_autocmds_retval(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf,
- &retval);
- # else
- apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
- # endif
- /* restore curwin/curbuf and a few other things */
- aucmd_restbuf(&aco);
- }
- #endif
- }
- return retval;
- }
- /*
- * Return TRUE if "buf" points to a valid buffer (in the buffer list).
- */
- int
- buf_valid(buf)
- buf_T *buf;
- {
- buf_T *bp;
- for (bp = firstbuf; bp != NULL; bp = bp->b_next)
- if (bp == buf)
- return TRUE;
- return FALSE;
- }
- /*
- * Close the link to a buffer.
- * "action" is used when there is no longer a window for the buffer.
- * It can be:
- * 0 buffer becomes hidden
- * DOBUF_UNLOAD buffer is unloaded
- * DOBUF_DELETE buffer is unloaded and removed from buffer list
- * DOBUF_WIPE buffer is unloaded and really deleted
- * When doing all but the first one on the current buffer, the caller should
- * get a new buffer very soon!
- *
- * The 'bufhidden' option can force freeing and deleting.
- *
- * When "abort_if_last" is TRUE then do not close the buffer if autocommands
- * cause there to be only one window with this buffer. e.g. when ":quit" is
- * supposed to close the window but autocommands close all other windows.
- */
- void
- close_buffer(win, buf, action, abort_if_last)
- win_T *win; /* if not NULL, set b_last_cursor */
- buf_T *buf;
- int action;
- int abort_if_last UNUSED;
- {
- #ifdef FEAT_AUTOCMD
- int is_curbuf;
- int nwindows;
- #endif
- int unload_buf = (action != 0);
- int del_buf = (action == DOBUF_DEL || action == DOBUF_WIPE);
- int wipe_buf = (action == DOBUF_WIPE);
- #ifdef FEAT_QUICKFIX
- /*
- * Force unloading or deleting when 'bufhidden' says so.
- * The caller must take care of NOT deleting/freeing when 'bufhidden' is
- * "hide" (otherwise we could never free or delete a buffer).
- */
- if (buf->b_p_bh[0] == 'd') /* 'bufhidden' == "delete" */
- {
- del_buf = TRUE;
- unload_buf = TRUE;
- }
- else if (buf->b_p_bh[0] == 'w') /* 'bufhidden' == "wipe" */
- {
- del_buf = TRUE;
- unload_buf = TRUE;
- wipe_buf = TRUE;
- }
- else if (buf->b_p_bh[0] == 'u') /* 'bufhidden' == "unload" */
- unload_buf = TRUE;
- #endif
- if (win != NULL)
- {
- /* Set b_last_cursor when closing the last window for the buffer.
- * Remember the last cursor position and window options of the buffer.
- * This used to be only for the current window, but then options like
- * 'foldmethod' may be lost with a ":only" command. */
- if (buf->b_nwindows == 1)
- set_last_cursor(win);
- buflist_setfpos(buf, win,
- win->w_cursor.lnum == 1 ? 0 : win->w_cursor.lnum,
- win->w_cursor.col, TRUE);
- }
- #ifdef FEAT_AUTOCMD
- /* When the buffer is no longer in a window, trigger BufWinLeave */
- if (buf->b_nwindows == 1)
- {
- buf->b_closing = TRUE;
- apply_autocmds(EVENT_BUFWINLEAVE, buf->b_fname, buf->b_fname,
- FALSE, buf);
- if (!buf_valid(buf))
- {
- /* Autocommands deleted the buffer. */
- aucmd_abort:
- EMSG(_(e_auabort));
- return;
- }
- buf->b_closing = FALSE;
- if (abort_if_last && one_window())
- /* Autocommands made this the only window. */
- goto aucmd_abort;
- /* When the buffer becomes hidden, but is not unloaded, trigger
- * BufHidden */
- if (!unload_buf)
- {
- buf->b_closing = TRUE;
- apply_autocmds(EVENT_BUFHIDDEN, buf->b_fname, buf->b_fname,
- FALSE, buf);
- if (!buf_valid(buf))
- /* Autocommands deleted the buffer. */
- goto aucmd_abort;
- buf->b_closing = FALSE;
- if (abort_if_last && one_window())
- /* Autocommands made this the only window. */
- goto aucmd_abort;
- }
- # ifdef FEAT_EVAL
- if (aborting()) /* autocmds may abort script processing */
- return;
- # endif
- }
- nwindows = buf->b_nwindows;
- #endif
- /* decrease the link count from windows (unless not in any window) */
- if (buf->b_nwindows > 0)
- --buf->b_nwindows;
- /* Return when a window is displaying the buffer or when it's not
- * unloaded. */
- if (buf->b_nwindows > 0 || !unload_buf)
- return;
- /* Always remove the buffer when there is no file name. */
- if (buf->b_ffname == NULL)
- del_buf = TRUE;
- /*
- * Free all things allocated for this buffer.
- * Also calls the "BufDelete" autocommands when del_buf is TRUE.
- */
- #ifdef FEAT_AUTOCMD
- /* Remember if we are closing the current buffer. Restore the number of
- * windows, so that autocommands in buf_freeall() don't get confused. */
- is_curbuf = (buf == curbuf);
- buf->b_nwindows = nwindows;
- #endif
- buf_freeall(buf, (del_buf ? BFA_DEL : 0) + (wipe_buf ? BFA_WIPE : 0));
- if (
- #ifdef FEAT_WINDOWS
- win_valid(win) &&
- #else
- win != NULL &&
- #endif
- win->w_buffer == buf)
- win->w_buffer = NULL; /* make sure we don't use the buffer now */
- #ifdef FEAT_AUTOCMD
- /* Autocommands may have deleted the buffer. */
- if (!buf_valid(buf))
- return;
- # ifdef FEAT_EVAL
- if (aborting()) /* autocmds may abort script processing */
- return;
- # endif
- /* Autocommands may have opened or closed windows for this buffer.
- * Decrement the count for the close we do here. */
- if (buf->b_nwindows > 0)
- --buf->b_nwindows;
- /*
- * It's possible that autocommands change curbuf to the one being deleted.
- * This might cause the previous curbuf to be deleted unexpectedly. But
- * in some cases it's OK to delete the curbuf, because a new one is
- * obtained anyway. Therefore only return if curbuf changed to the
- * deleted buffer.
- */
- if (buf == curbuf && !is_curbuf)
- return;
- #endif
- /* Change directories when the 'acd' option is set. */
- DO_AUTOCHDIR
- /*
- * Remove the buffer from the list.
- */
- if (wipe_buf)
- {
- #ifdef FEAT_SUN_WORKSHOP
- if (usingSunWorkShop)
- workshop_file_closed_lineno((char *)buf->b_ffname,
- (int)buf->b_last_cursor.lnum);
- #endif
- vim_free(buf->b_ffname);
- vim_free(buf->b_sfname);
- if (buf->b_prev == NULL)
- firstbuf = buf->b_next;
- else
- buf->b_prev->b_next = buf->b_next;
- if (buf->b_next == NULL)
- lastbuf = buf->b_prev;
- else
- buf->b_next->b_prev = buf->b_prev;
- free_buffer(buf);
- }
- else
- {
- if (del_buf)
- {
- /* Free all internal variables and reset option values, to make
- * ":bdel" compatible with Vim 5.7. */
- free_buffer_stuff(buf, TRUE);
- /* Make it look like a new buffer. */
- buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
- /* Init the options when loaded again. */
- buf->b_p_initialized = FALSE;
- }
- buf_clear_file(buf);
- if (del_buf)
- buf->b_p_bl = FALSE;
- }
- }
- /*
- * Make buffer not contain a file.
- */
- void
- buf_clear_file(buf)
- buf_T *buf;
- {
- buf->b_ml.ml_line_count = 1;
- unchanged(buf, TRUE);
- #ifndef SHORT_FNAME
- buf->b_shortname = FALSE;
- #endif
- buf->b_p_eol = TRUE;
- buf->b_start_eol = TRUE;
- #ifdef FEAT_MBYTE
- buf->b_p_bomb = FALSE;
- buf->b_start_bomb = FALSE;
- #endif
- buf->b_ml.ml_mfp = NULL;
- buf->b_ml.ml_flags = ML_EMPTY; /* empty buffer */
- #ifdef FEAT_NETBEANS_INTG
- netbeans_deleted_all_lines(buf);
- #endif
- }
- /*
- * buf_freeall() - free all things allocated for a buffer that are related to
- * the file. flags:
- * BFA_DEL buffer is going to be deleted
- * BFA_WIPE buffer is going to be wiped out
- * BFA_KEEP_UNDO do not free undo information
- */
- void
- buf_freeall(buf, flags)
- buf_T *buf;
- int flags;
- {
- #ifdef FEAT_AUTOCMD
- int is_curbuf = (buf == curbuf);
- buf->b_closing = TRUE;
- apply_autocmds(EVENT_BUFUNLOAD, buf->b_fname, buf->b_fname, FALSE, buf);
- if (!buf_valid(buf)) /* autocommands may delete the buffer */
- return;
- if ((flags & BFA_DEL) && buf->b_p_bl)
- {
- apply_autocmds(EVENT_BUFDELETE, buf->b_fname, buf->b_fname, FALSE, buf);
- if (!buf_valid(buf)) /* autocommands may delete the buffer */
- return;
- }
- if (flags & BFA_WIPE)
- {
- apply_autocmds(EVENT_BUFWIPEOUT, buf->b_fname, buf->b_fname,
- FALSE, buf);
- if (!buf_valid(buf)) /* autocommands may delete the buffer */
- return;
- }
- buf->b_closing = FALSE;
- # ifdef FEAT_EVAL
- if (aborting()) /* autocmds may abort script processing */
- return;
- # endif
- /*
- * It's possible that autocommands change curbuf to the one being deleted.
- * This might cause curbuf to be deleted unexpectedly. But in some cases
- * it's OK to delete the curbuf, because a new one is obtained anyway.
- * Therefore only return if curbuf changed to the deleted buffer.
- */
- if (buf == curbuf && !is_curbuf)
- return;
- #endif
- #ifdef FEAT_DIFF
- diff_buf_delete(buf); /* Can't use 'diff' for unloaded buffer. */
- #endif
- #ifdef FEAT_SYN_HL
- /* Remove any ownsyntax, unless exiting. */
- if (firstwin != NULL && curwin->w_buffer == buf)
- reset_synblock(curwin);
- #endif
- #ifdef FEAT_FOLDING
- /* No folds in an empty buffer. */
- # ifdef FEAT_WINDOWS
- {
- win_T *win;
- tabpage_T *tp;
- FOR_ALL_TAB_WINDOWS(tp, win)
- if (win->w_buffer == buf)
- clearFolding(win);
- }
- # else
- if (curwin->w_buffer == buf)
- clearFolding(curwin);
- # endif
- #endif
- #ifdef FEAT_TCL
- tcl_buffer_free(buf);
- #endif
- ml_close(buf, TRUE); /* close and delete the memline/memfile */
- buf->b_ml.ml_line_count = 0; /* no lines in buffer */
- if ((flags & BFA_KEEP_UNDO) == 0)
- {
- u_blockfree(buf); /* free the memory allocated for undo */
- u_clearall(buf); /* reset all undo information */
- }
- #ifdef FEAT_SYN_HL
- syntax_clear(&buf->b_s); /* reset syntax info */
- #endif
- buf->b_flags &= ~BF_READERR; /* a read error is no longer relevant */
- }
- /*
- * Free a buffer structure and the things it contains related to the buffer
- * itself (not the file, that must have been done already).
- */
- static void
- free_buffer(buf)
- buf_T *buf;
- {
- free_buffer_stuff(buf, TRUE);
- #ifdef FEAT_LUA
- lua_buffer_free(buf);
- #endif
- #ifdef FEAT_MZSCHEME
- mzscheme_buffer_free(buf);
- #endif
- #ifdef FEAT_PERL
- perl_buf_free(buf);
- #endif
- #ifdef FEAT_PYTHON
- python_buffer_free(buf);
- #endif
- #ifdef FEAT_PYTHON3
- python3_buffer_free(buf);
- #endif
- #ifdef FEAT_RUBY
- ruby_buffer_free(buf);
- #endif
- #ifdef FEAT_AUTOCMD
- aubuflocal_remove(buf);
- #endif
- vim_free(buf);
- }
- /*
- * Free stuff in the buffer for ":bdel" and when wiping out the buffer.
- */
- static void
- free_buffer_stuff(buf, free_options)
- buf_T *buf;
- int free_options; /* free options as well */
- {
- if (free_options)
- {
- clear_wininfo(buf); /* including window-local options */
- free_buf_options(buf, TRUE);
- #ifdef FEAT_SPELL
- ga_clear(&buf->b_s.b_langp);
- #endif
- }
- #ifdef FEAT_EVAL
- vars_clear(&buf->b_vars.dv_hashtab); /* free all internal variables */
- hash_init(&buf->b_vars.dv_hashtab);
- #endif
- #ifdef FEAT_USR_CMDS
- uc_clear(&buf->b_ucmds); /* clear local user commands */
- #endif
- #ifdef FEAT_SIGNS
- buf_delete_signs(buf); /* delete any signs */
- #endif
- #ifdef FEAT_NETBEANS_INTG
- netbeans_file_killed(buf);
- #endif
- #ifdef FEAT_LOCALMAP
- map_clear_int(buf, MAP_ALL_MODES, TRUE, FALSE); /* clear local mappings */
- map_clear_int(buf, MAP_ALL_MODES, TRUE, TRUE); /* clear local abbrevs */
- #endif
- #ifdef FEAT_MBYTE
- vim_free(buf->b_start_fenc);
- buf->b_start_fenc = NULL;
- #endif
- }
- /*
- * Free the b_wininfo list for buffer "buf".
- */
- static void
- clear_wininfo(buf)
- buf_T *buf;
- {
- wininfo_T *wip;
- while (buf->b_wininfo != NULL)
- {
- wip = buf->b_wininfo;
- buf->b_wininfo = wip->wi_next;
- if (wip->wi_optset)
- {
- clear_winopt(&wip->wi_opt);
- #ifdef FEAT_FOLDING
- deleteFoldRecurse(&wip->wi_folds);
- #endif
- }
- vim_free(wip);
- }
- }
- #if defined(FEAT_LISTCMDS) || defined(PROTO)
- /*
- * Go to another buffer. Handles the result of the ATTENTION dialog.
- */
- void
- goto_buffer(eap, start, dir, count)
- exarg_T *eap;
- int start;
- int dir;
- int count;
- {
- # if defined(FEAT_WINDOWS) && defined(HAS_SWAP_EXISTS_ACTION)
- buf_T *old_curbuf = curbuf;
- swap_exists_action = SEA_DIALOG;
- # endif
- (void)do_buffer(*eap->cmd == 's' ? DOBUF_SPLIT : DOBUF_GOTO,
- start, dir, count, eap->forceit);
- # if defined(FEAT_WINDOWS) && defined(HAS_SWAP_EXISTS_ACTION)
- if (swap_exists_action == SEA_QUIT && *eap->cmd == 's')
- {
- # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- cleanup_T cs;
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a window. */
- enter_cleanup(&cs);
- # endif
- /* Quitting means closing the split window, nothing else. */
- win_close(curwin, TRUE);
- swap_exists_action = SEA_NONE;
- swap_exists_did_quit = TRUE;
- # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- /* Restore the error/interrupt/exception state if not discarded by a
- * new aborting error, interrupt, or uncaught exception. */
- leave_cleanup(&cs);
- # endif
- }
- else
- handle_swap_exists(old_curbuf);
- # endif
- }
- #endif
- #if defined(HAS_SWAP_EXISTS_ACTION) || defined(PROTO)
- /*
- * Handle the situation of swap_exists_action being set.
- * It is allowed for "old_curbuf" to be NULL or invalid.
- */
- void
- handle_swap_exists(old_curbuf)
- buf_T *old_curbuf;
- {
- # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- cleanup_T cs;
- # endif
- if (swap_exists_action == SEA_QUIT)
- {
- # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a buffer. */
- enter_cleanup(&cs);
- # endif
- /* User selected Quit at ATTENTION prompt. Go back to previous
- * buffer. If that buffer is gone or the same as the current one,
- * open a new, empty buffer. */
- swap_exists_action = SEA_NONE; /* don't want it again */
- swap_exists_did_quit = TRUE;
- close_buffer(curwin, curbuf, DOBUF_UNLOAD, FALSE);
- if (!buf_valid(old_curbuf) || old_curbuf == curbuf)
- old_curbuf = buflist_new(NULL, NULL, 1L, BLN_CURBUF | BLN_LISTED);
- if (old_curbuf != NULL)
- enter_buffer(old_curbuf);
- /* If "old_curbuf" is NULL we are in big trouble here... */
- # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- /* Restore the error/interrupt/exception state if not discarded by a
- * new aborting error, interrupt, or uncaught exception. */
- leave_cleanup(&cs);
- # endif
- }
- else if (swap_exists_action == SEA_RECOVER)
- {
- # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- /* Reset the error/interrupt/exception state here so that
- * aborting() returns FALSE when closing a buffer. */
- enter_cleanup(&cs);
- # endif
- /* User selected Recover at ATTENTION prompt. */
- msg_scroll = TRUE;
- ml_recover();
- MSG_PUTS("\n"); /* don't overwrite the last message */
- cmdline_row = msg_row;
- do_modelines(0);
- # if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- /* Restore the error/interrupt/exception state if not discarded by a
- * new aborting error, interrupt, or uncaught exception. */
- leave_cleanup(&cs);
- # endif
- }
- swap_exists_action = SEA_NONE;
- }
- #endif
- #if defined(FEAT_LISTCMDS) || defined(PROTO)
- /*
- * do_bufdel() - delete or unload buffer(s)
- *
- * addr_count == 0: ":bdel" - delete current buffer
- * addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete
- * buffer "end_bnr", then any other arguments.
- * addr_count == 2: ":N,N bdel" - delete buffers in range
- *
- * command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or
- * DOBUF_DEL (":bdel")
- *
- * Returns error message or NULL
- */
- char_u *
- do_bufdel(command, arg, addr_count, start_bnr, end_bnr, forceit)
- int command;
- char_u *arg; /* pointer to extra arguments */
- int addr_count;
- int start_bnr; /* first buffer number in a range */
- int end_bnr; /* buffer nr or last buffer nr in a range */
- int forceit;
- {
- int do_current = 0; /* delete current buffer? */
- int deleted = 0; /* number of buffers deleted */
- char_u *errormsg = NULL; /* return value */
- int bnr; /* buffer number */
- char_u *p;
- if (addr_count == 0)
- {
- (void)do_buffer(command, DOBUF_CURRENT, FORWARD, 0, forceit);
- }
- else
- {
- if (addr_count == 2)
- {
- if (*arg) /* both range and argument is not allowed */
- return (char_u *)_(e_trailing);
- bnr = start_bnr;
- }
- else /* addr_count == 1 */
- bnr = end_bnr;
- for ( ;!got_int; ui_breakcheck())
- {
- /*
- * delete the current buffer last, otherwise when the
- * current buffer is deleted, the next buffer becomes
- * the current one and will be loaded, which may then
- * also be deleted, etc.
- */
- if (bnr == curbuf->b_fnum)
- do_current = bnr;
- else if (do_buffer(command, DOBUF_FIRST, FORWARD, (int)bnr,
- forceit) == OK)
- ++deleted;
- /*
- * find next buffer number to delete/unload
- */
- if (addr_count == 2)
- {
- if (++bnr > end_bnr)
- break;
- }
- else /* addr_count == 1 */
- {
- arg = skipwhite(arg);
- if (*arg == NUL)
- break;
- if (!VIM_ISDIGIT(*arg))
- {
- p = skiptowhite_esc(arg);
- bnr = buflist_findpat(arg, p, command == DOBUF_WIPE, FALSE);
- if (bnr < 0) /* failed */
- break;
- arg = p;
- }
- else
- bnr = getdigits(&arg);
- }
- }
- if (!got_int && do_current && do_buffer(command, DOBUF_FIRST,
- FORWARD, do_current, forceit) == OK)
- ++deleted;
- if (deleted == 0)
- {
- if (command == DOBUF_UNLOAD)
- STRCPY(IObuff, _("E515: No buffers were unloaded"));
- else if (command == DOBUF_DEL)
- STRCPY(IObuff, _("E516: No buffers were deleted"));
- else
- STRCPY(IObuff, _("E517: No buffers were wiped out"));
- errormsg = IObuff;
- }
- else if (deleted >= p_report)
- {
- if (command == DOBUF_UNLOAD)
- {
- if (deleted == 1)
- MSG(_("1 buffer unloaded"));
- else
- smsg((char_u *)_("%d buffers unloaded"), deleted);
- }
- else if (command == DOBUF_DEL)
- {
- if (deleted == 1)
- MSG(_("1 buffer deleted"));
- else
- smsg((char_u *)_("%d buffers deleted"), deleted);
- }
- else
- {
- if (deleted == 1)
- MSG(_("1 buffer wiped out"));
- else
- smsg((char_u *)_("%d buffers wiped out"), deleted);
- }
- }
- }
- return errormsg;
- }
- /*
- * Implementation of the commands for the buffer list.
- *
- * action == DOBUF_GOTO go to specified buffer
- * action == DOBUF_SPLIT split window and go to specified buffer
- * action == DOBUF_UNLOAD unload specified buffer(s)
- * action == DOBUF_DEL delete specified buffer(s) from buffer list
- * action == DOBUF_WIPE delete specified buffer(s) really
- *
- * start == DOBUF_CURRENT go to "count" buffer from current buffer
- * start == DOBUF_FIRST go to "count" buffer from first buffer
- * start == DOBUF_LAST go to "count" buffer from last buffer
- * start == DOBUF_MOD go to "count" modified buffer from current buffer
- *
- * Return FAIL or OK.
- */
- int
- do_buffer(action, start, dir, count, forceit)
- int action;
- int start;
- int dir; /* FORWARD or BACKWARD */
- int count; /* buffer number or number of buffers */
- int forceit; /* TRUE for :...! */
- {
- buf_T *buf;
- buf_T *bp;
- int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
- || action == DOBUF_WIPE);
- switch (start)
- {
- case DOBUF_FIRST: buf = firstbuf; break;
- case DOBUF_LAST: buf = lastbuf; break;
- default: buf = curbuf; break;
- }
- if (start == DOBUF_MOD) /* find next modified buffer */
- {
- while (count-- > 0)
- {
- do
- {
- buf = buf->b_next;
- if (buf == NULL)
- buf = firstbuf;
- }
- while (buf != curbuf && !bufIsChanged(buf));
- }
- if (!bufIsChanged(buf))
- {
- EMSG(_("E84: No modified buffer found"));
- return FAIL;
- }
- }
- else if (start == DOBUF_FIRST && count) /* find specified buffer number */
- {
- while (buf != NULL && buf->b_fnum != count)
- buf = buf->b_next;
- }
- else
- {
- bp = NULL;
- while (count > 0 || (!unload && !buf->b_p_bl && bp != buf))
- {
- /* remember the buffer where we start, we come back there when all
- * buffers are unlisted. */
- if (bp == NULL)
- bp = buf;
- if (dir == FORWARD)
- {
- buf = buf->b_next;
- if (buf == NULL)
- buf = firstbuf;
- }
- else
- {
- buf = buf->b_prev;
- if (buf == NULL)
- buf = lastbuf;
- }
- /* don't count unlisted buffers */
- if (unload || buf->b_p_bl)
- {
- --count;
- bp = NULL; /* use this buffer as new starting point */
- }
- if (bp == buf)
- {
- /* back where we started, didn't find anything. */
- EMSG(_("E85: There is no listed buffer"));
- return FAIL;
- }
- }
- }
- if (buf == NULL) /* could not find it */
- {
- if (start == DOBUF_FIRST)
- {
- /* don't warn when deleting */
- if (!unload)
- EMSGN(_("E86: Buffer %ld does not exist"), count);
- }
- else if (dir == FORWARD)
- EMSG(_("E87: Cannot go beyond last buffer"));
- else
- EMSG(_("E88: Cannot go before first buffer"));
- return FAIL;
- }
- #ifdef FEAT_GUI
- need_mouse_correct = TRUE;
- #endif
- #ifdef FEAT_LISTCMDS
- /*
- * delete buffer buf from memory and/or the list
- */
- if (unload)
- {
- int forward;
- int retval;
- /* When unloading or deleting a buffer that's already unloaded and
- * unlisted: fail silently. */
- if (action != DOBUF_WIPE && buf->b_ml.ml_mfp == NULL && !buf->b_p_bl)
- return FAIL;
- if (!forceit && bufIsChanged(buf))
- {
- #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
- if ((p_confirm || cmdmod.confirm) && p_write)
- {
- dialog_changed(buf, FALSE);
- # ifdef FEAT_AUTOCMD
- if (!buf_valid(buf))
- /* Autocommand deleted buffer, oops! It's not changed
- * now. */
- return FAIL;
- # endif
- /* If it's still changed fail silently, the dialog already
- * mentioned why it fails. */
- if (bufIsChanged(buf))
- return FAIL;
- }
- else
- #endif
- {
- EMSGN(_("E89: No write since last change for buffer %ld (add ! to override)"),
- buf->b_fnum);
- return FAIL;
- }
- }
- /*
- * If deleting the last (listed) buffer, make it empty.
- * The last (listed) buffer cannot be unloaded.
- */
- for (bp = firstbuf; bp != NULL; bp = bp->b_next)
- if (bp->b_p_bl && bp != buf)
- break;
- if (bp == NULL && buf == curbuf)
- {
- if (action == DOBUF_UNLOAD)
- {
- EMSG(_("E90: Cannot unload last buffer"));
- return FAIL;
- }
- /* Close any other windows on this buffer, then make it empty. */
- #ifdef FEAT_WINDOWS
- close_windows(buf, TRUE);
- #endif
- setpcmark();
- retval = do_ecmd(0, NULL, NULL, NULL, ECMD_ONE,
- forceit ? ECMD_FORCEIT : 0, curwin);
- /*
- * do_ecmd() may create a new buffer, then we have to delete
- * the old one. But do_ecmd() may have done that already, check
- * if the buffer still exists.
- */
- if (buf != curbuf && buf_valid(buf) && buf->b_nwindows == 0)
- close_buffer(NULL, buf, action, FALSE);
- return retval;
- }
- #ifdef FEAT_WINDOWS
- /*
- * If the deleted buffer is the current one, close the current window
- * (unless it's the only window). Repeat this so long as we end up in
- * a window with this buffer.
- */
- while (buf == curbuf
- # ifdef FEAT_AUTOCMD
- && !(curwin->w_closing || curwin->w_buffer->b_closing)
- # endif
- && (firstwin != lastwin || first_tabpage->tp_next != NULL))
- win_close(curwin, FALSE);
- #endif
- /*
- * If the buffer to be deleted is not the current one, delete it here.
- */
- if (buf != curbuf)
- {
- #ifdef FEAT_WINDOWS
- close_windows(buf, FALSE);
- #endif
- if (buf != curbuf && buf_valid(buf) && buf->b_nwindows <= 0)
- close_buffer(NULL, buf, action, FALSE);
- return OK;
- }
- /*
- * Deleting the current buffer: Need to find another buffer to go to.
- * There must be another, otherwise it would have been handled above.
- * First use au_new_curbuf, if it is valid.
- * Then prefer the buffer we most recently visited.
- * Else try to find one that is loaded, after the current buffer,
- * then before the current buffer.
- * Finally use any buffer.
- */
- buf = NULL; /* selected buffer */
- bp = NULL; /* used when no loaded buffer found */
- #ifdef FEAT_AUTOCMD
- if (au_new_curbuf != NULL && buf_valid(au_new_curbuf))
- buf = au_new_curbuf;
- # ifdef FEAT_JUMPLIST
- else
- # endif
- #endif
- #ifdef FEAT_JUMPLIST
- if (curwin->w_jumplistlen > 0)
- {
- int jumpidx;
- jumpidx = curwin->w_jumplistidx - 1;
- if (jumpidx < 0)
- jumpidx = curwin->w_jumplistlen - 1;
- forward = jumpidx;
- while (jumpidx != curwin->w_jumplistidx)
- {
- buf = buflist_findnr(curwin->w_jumplist[jumpidx].fmark.fnum);
- if (buf != NULL)
- {
- if (buf == curbuf || !buf->b_p_bl)
- buf = NULL; /* skip current and unlisted bufs */
- else if (buf->b_ml.ml_mfp == NULL)
- {
- /* skip unloaded buf, but may keep it for later */
- if (bp == NULL)
- bp = buf;
- buf = NULL;
- }
- }
- if (buf != NULL) /* found a valid buffer: stop searching */
- break;
- /* advance to older entry in jump list */
- if (!jumpidx && curwin->w_jumplistidx == curwin->w_jumplistlen)
- break;
- if (--jumpidx < 0)
- jumpidx = curwin->w_jumplistlen - 1;
- if (jumpidx == forward) /* List exhausted for sure */
- break;
- }
- }
- #endif
- if (buf == NULL) /* No previous buffer, Try 2'nd approach */
- {
- forward = TRUE;
- buf = curbuf->b_next;
- for (;;)
- {
- if (buf == NULL)
- {
- if (!forward) /* tried both directions */
- break;
- buf = curbuf->b_prev;
- forward = FALSE;
- continue;
- }
- /* in non-help buffer, try to skip help buffers, and vv */
- if (buf->b_help == curbuf->b_help && buf->b_p_bl)
- {
- if (buf->b_ml.ml_mfp != NULL) /* found loaded buffer */
- break;
- if (bp == NULL) /* remember unloaded buf for later */
- bp = buf;
- }
- if (forward)
- buf = buf->b_next;
- else
- buf = buf->b_prev;
- }
- }
- if (buf == NULL) /* No loaded buffer, use unloaded one */
- buf = bp;
- if (buf == NULL) /* No loaded buffer, find listed one */
- {
- for (buf = firstbuf; buf != NULL; buf = buf->b_next)
- if (buf->b_p_bl && buf != curbuf)
- break;
- }
- if (buf == NULL) /* Still no buffer, just take one */
- {
- if (curbuf->b_next != NULL)
- buf = curbuf->b_next;
- else
- buf = curbuf->b_prev;
- }
- }
- /*
- * make buf current buffer
- */
- if (action == DOBUF_SPLIT) /* split window first */
- {
- # ifdef FEAT_WINDOWS
- /* If 'switchbuf' contains "useopen": jump to first window containing
- * "buf" if one exists */
- if ((swb_flags & SWB_USEOPEN) && buf_jump_open_win(buf))
- return OK;
- /* If 'switchbuf' contains "usetab": jump to first window in any tab
- * page containing "buf" if one exists */
- if ((swb_flags & SWB_USETAB) && buf_jump_open_tab(buf))
- return OK;
- if (win_split(0, 0) == FAIL)
- # endif
- return FAIL;
- }
- #endif
- /* go to current buffer - nothing to do */
- if (buf == curbuf)
- return OK;
- /*
- * Check if the current buffer may be abandoned.
- */
- if (action == DOBUF_GOTO && !can_abandon(curbuf, forceit))
- {
- #if defined(FEAT_GUI_DIALOG) || defined(FEAT_CON_DIALOG)
- if ((p_confirm || cmdmod.confirm) && p_write)
- {
- dialog_changed(curbuf, FALSE);
- # ifdef FEAT_AUTOCMD
- if (!buf_valid(buf))
- /* Autocommand deleted buffer, oops! */
- return FAIL;
- # endif
- }
- if (bufIsChanged(curbuf))
- #endif
- {
- EMSG(_(e_nowrtmsg));
- return FAIL;
- }
- }
- /* Go to the other buffer. */
- set_curbuf(buf, action);
- #if defined(FEAT_LISTCMDS) \
- && (defined(FEAT_SCROLLBIND) || defined(FEAT_CURSORBIND))
- if (action == DOBUF_SPLIT)
- {
- RESET_BINDING(curwin); /* reset 'scrollbind' and 'cursorbind' */
- }
- #endif
- #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- if (aborting()) /* autocmds may abort script processing */
- return FAIL;
- #endif
- return OK;
- }
- #endif /* FEAT_LISTCMDS */
- /*
- * Set current buffer to "buf". Executes autocommands and closes current
- * buffer. "action" tells how to close the current buffer:
- * DOBUF_GOTO free or hide it
- * DOBUF_SPLIT nothing
- * DOBUF_UNLOAD unload it
- * DOBUF_DEL delete it
- * DOBUF_WIPE wipe it out
- */
- void
- set_curbuf(buf, action)
- buf_T *buf;
- int action;
- {
- buf_T *prevbuf;
- int unload = (action == DOBUF_UNLOAD || action == DOBUF_DEL
- || action == DOBUF_WIPE);
- setpcmark();
- if (!cmdmod.keepalt)
- curwin->w_alt_fnum = curbuf->b_fnum; /* remember alternate file */
- buflist_altfpos(curwin); /* remember curpos */
- #ifdef FEAT_VISUAL
- /* Don't restart Select mode after switching to another buffer. */
- VIsual_reselect = FALSE;
- #endif
- /* close_windows() or apply_autocmds() may change curbuf */
- prevbuf = curbuf;
- #ifdef FEAT_AUTOCMD
- apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, FALSE, curbuf);
- # ifdef FEAT_EVAL
- if (buf_valid(prevbuf) && !aborting())
- # else
- if (buf_valid(prevbuf))
- # endif
- #endif
- {
- #ifdef FEAT_SYN_HL
- if (prevbuf == curwin->w_buffer)
- reset_synblock(curwin);
- #endif
- #ifdef FEAT_WINDOWS
- if (unload)
- close_windows(prevbuf, FALSE);
- #endif
- #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- if (buf_valid(prevbuf) && !aborting())
- #else
- if (buf_valid(prevbuf))
- #endif
- {
- if (prevbuf == curbuf)
- u_sync(FALSE);
- close_buffer(prevbuf == curwin->w_buffer ? curwin : NULL, prevbuf,
- unload ? action : (action == DOBUF_GOTO
- && !P_HID(prevbuf)
- && !bufIsChanged(prevbuf)) ? DOBUF_UNLOAD : 0, FALSE);
- }
- }
- #ifdef FEAT_AUTOCMD
- /* An autocommand may have deleted "buf", already entered it (e.g., when
- * it did ":bunload") or aborted the script processing! */
- # ifdef FEAT_EVAL
- if (buf_valid(buf) && buf != curbuf && !aborting())
- # else
- if (buf_valid(buf) && buf != curbuf)
- # endif
- #endif
- enter_buffer(buf);
- }
- /*
- * Enter a new current buffer.
- * Old curbuf must have been abandoned already!
- */
- void
- enter_buffer(buf)
- buf_T *buf;
- {
- /* Copy buffer and window local option values. Not for a help buffer. */
- buf_copy_options(buf, BCO_ENTER | BCO_NOHELP);
- if (!buf->b_help)
- get_winopts(buf);
- #ifdef FEAT_FOLDING
- else
- /* Remove all folds in the window. */
- clearFolding(curwin);
- foldUpdateAll(curwin); /* update folds (later). */
- #endif
- /* Get the buffer in the current window. */
- curwin->w_buffer = buf;
- curbuf = buf;
- ++curbuf->b_nwindows;
- #ifdef FEAT_DIFF
- if (curwin->w_p_diff)
- diff_buf_add(curbuf);
- #endif
- #ifdef FEAT_SYN_HL
- curwin->w_s = &(buf->b_s);
- #endif
- /* Cursor on first line by default. */
- curwin->w_cursor.lnum = 1;
- curwin->w_cursor.col = 0;
- #ifdef FEAT_VIRTUALEDIT
- curwin->w_cursor.coladd = 0;
- #endif
- curwin->w_set_curswant = TRUE;
- #ifdef FEAT_AUTOCMD
- curwin->w_topline_was_set = FALSE;
- #endif
- /* mark cursor position as being invalid */
- curwin->w_valid = 0;
- /* Make sure the buffer is loaded. */
- if (curbuf->b_ml.ml_mfp == NULL) /* need to load the file */
- {
- #ifdef FEAT_AUTOCMD
- /* If there is no filetype, allow for detecting one. Esp. useful for
- * ":ball" used in a autocommand. If there already is a filetype we
- * might prefer to keep it. */
- if (*curbuf->b_p_ft == NUL)
- did_filetype = FALSE;
- #endif
- open_buffer(FALSE, NULL, 0);
- }
- else
- {
- if (!msg_silent)
- need_fileinfo = TRUE; /* display file info after redraw */
- (void)buf_check_timestamp(curbuf, FALSE); /* check if file changed */
- #ifdef FEAT_AUTOCMD
- curwin->w_topline = 1;
- # ifdef FEAT_DIFF
- curwin->w_topfill = 0;
- # endif
- apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf);
- apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, FALSE, curbuf);
- #endif
- }
- /* If autocommands did not change the cursor position, restore cursor lnum
- * and possibly cursor col. */
- if (curwin->w_cursor.lnum == 1 && inindent(0))
- buflist_getfpos();
- check_arg_idx(curwin); /* check for valid arg_idx */
- #ifdef FEAT_TITLE
- maketitle();
- #endif
- #ifdef FEAT_AUTOCMD
- /* when autocmds didn't change it */
- if (curwin->w_topline == 1 && !curwin->w_topline_was_set)
- #endif
- scroll_cursor_halfway(FALSE); /* redisplay at correct position */
- #ifdef FEAT_NETBEANS_INTG
- /* Send fileOpened event because we've changed buffers. */
- netbeans_file_activated(curbuf);
- #endif
- /* Change directories when the 'acd' option is set. */
- DO_AUTOCHDIR
- #ifdef FEAT_KEYMAP
- if (curbuf->b_kmap_state & KEYMAP_INIT)
- (void)keymap_init();
- #endif
- #ifdef FEAT_SPELL
- /* May need to set the spell language. Can only do this after the buffer
- * has been properly setup. */
- if (!curbuf->b_help && curwin->w_p_spell && *curwin->w_s->b_p_spl != NUL)
- (void)did_set_spelllang(curwin);
- #endif
- redraw_later(NOT_VALID);
- }
- #if defined(FEAT_AUTOCHDIR) || defined(PROTO)
- /*
- * Change to the directory of the current buffer.
- */
- void
- do_autochdir()
- {
- if (curbuf->b_ffname != NULL && vim_chdirfile(curbuf->b_ffname) == OK)
- shorten_fnames(TRUE);
- }
- #endif
- /*
- * functions for dealing with the buffer list
- */
- /*
- * Add a file name to the buffer list. Return a pointer to the buffer.
- * If the same file name already exists return a pointer to that buffer.
- * If it does not exist, or if fname == NULL, a new entry is created.
- * If (flags & BLN_CURBUF) is TRUE, may use current buffer.
- * If (flags & BLN_LISTED) is TRUE, add new buffer to buffer list.
- * If (flags & BLN_DUMMY) is TRUE, don't count it as a real buffer.
- * This is the ONLY way to create a new buffer.
- */
- static int top_file_num = 1; /* highest file number */
- buf_T *
- buflist_new(ffname, sfname, lnum, flags)
- char_u *ffname; /* full path of fname or relative */
- char_u *sfname; /* short fname or NULL */
- linenr_T lnum; /* preferred cursor line */
- int flags; /* BLN_ defines */
- {
- buf_T *buf;
- #ifdef UNIX
- struct stat st;
- #endif
- fname_expand(curbuf, &ffname, &sfname); /* will allocate ffname */
- /*
- * If file name already exists in the list, update the entry.
- */
- #ifdef UNIX
- /* On Unix we can use inode numbers when the file exists. Works better
- * for hard links. */
- if (sfname == NULL || mch_stat((char *)sfname, &st) < 0)
- st.st_dev = (dev_T)-1;
- #endif
- if (ffname != NULL && !(flags & BLN_DUMMY) && (buf =
- #ifdef UNIX
- buflist_findname_stat(ffname, &st)
- #else
- buflist_findname(ffname)
- #endif
- ) != NULL)
- {
- vim_free(ffname);
- if (lnum != 0)
- buflist_setfpos(buf, curwin, lnum, (colnr_T)0, FALSE);
- /* copy the options now, if 'cpo' doesn't have 's' and not done
- * already */
- buf_copy_options(buf, 0);
- if ((flags & BLN_LISTED) && !buf->b_p_bl)
- {
- buf->b_p_bl = TRUE;
- #ifdef FEAT_AUTOCMD
- if (!(flags & BLN_DUMMY))
- apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf);
- #endif
- }
- return buf;
- }
- /*
- * If the current buffer has no name and no contents, use the current
- * buffer. Otherwise: Need to allocate a new buffer structure.
- *
- * This is the ONLY place where a new buffer structure is allocated!
- * (A spell file buffer is allocated in spell.c, but that's not a normal
- * buffer.)
- */
- buf = NULL;
- if ((flags & BLN_CURBUF)
- && curbuf != NULL
- && curbuf->b_ffname == NULL
- && curbuf->b_nwindows <= 1
- && (curbuf->b_ml.ml_mfp == NULL || bufempty()))
- {
- buf = curbuf;
- #ifdef FEAT_AUTOCMD
- /* It's like this buffer is deleted. Watch out for autocommands that
- * change curbuf! If that happens, allocate a new buffer anyway. */
- if (curbuf->b_p_bl)
- apply_autocmds(EVENT_BUFDELETE, NULL, NULL, FALSE, curbuf);
- if (buf == curbuf)
- apply_autocmds(EVENT_BUFWIPEOUT, NULL, NULL, FALSE, curbuf);
- # ifdef FEAT_EVAL
- if (aborting()) /* autocmds may abort script processing */
- return NULL;
- # endif
- #endif
- #ifdef FEAT_QUICKFIX
- # ifdef FEAT_AUTOCMD
- if (buf == curbuf)
- # endif
- {
- /* Make sure 'bufhidden' and 'buftype' are empty */
- clear_string_option(&buf->b_p_bh);
- clear_string_option(&buf->b_p_bt);
- }
- #endif
- }
- if (buf != curbuf || curbuf == NULL)
- {
- buf = (buf_T *)alloc_clear((unsigned)sizeof(buf_T));
- if (buf == NULL)
- {
- vim_free(ffname);
- return NULL;
- }
- }
- if (ffname != NULL)
- {
- buf->b_ffname = ffname;
- buf->b_sfname = vim_strsave(sfname);
- }
- clear_wininfo(buf);
- buf->b_wininfo = (wininfo_T *)alloc_clear((unsigned)sizeof(wininfo_T));
- if ((ffname != NULL && (buf->b_ffname == NULL || buf->b_sfname == NULL))
- || buf->b_wininfo == NULL)
- {
- vim_free(buf->b_ffname);
- buf->b_ffname = NULL;
- vim_free(buf->b_sfname);
- buf->b_sfname = NULL;
- if (buf != curbuf)
- free_buffer(buf);
- return NULL;
- }
- if (buf == curbuf)
- {
- /* free all things allocated for this buffer */
- buf_freeall(buf, 0);
- if (buf != curbuf) /* autocommands deleted the buffer! */
- return NULL;
- #if defined(FEAT_AUTOCMD) && defined(FEAT_EVAL)
- if (aborting()) /* autocmds may abort script processing */
- return NULL;
- #endif
- /* buf->b_nwindows = 0; why was this here? */
- free_buffer_stuff(buf, FALSE); /* delete local variables et al. */
- #ifdef FEAT_KEYMAP
- /* need to reload lmaps and set b:keymap_name */
- curbuf->b_kmap_state |= KEYMAP_INIT;
- #endif
- }
- else
- {
- /*
- * put new buffer at the end of the buffer list
- */
- buf->b_next = NULL;
- if (firstbuf == NULL) /* buffer list is empty */
- {
- buf->b_prev = NULL;
- firstbuf = buf;
- }
- else /* append new buffer at end of list */
- {
- lastbuf->b_next = buf;
- buf->b_prev = lastbuf;
- }
- lastbuf = buf;
- buf->b_fnum = top_file_num++;
- if (top_file_num < 0) /* wrap around (may cause duplicates) */
- {
- EMSG(_("W14: Warning: List of file names overflow"));
- if (emsg_silent == 0)
- {
- out_flush();
- ui_delay(3000L, TRUE); /* make sure it is noticed */
- }
- top_file_num = 1;
- }
- /*
- * Always copy the options from the current buffer.
- */
- buf_copy_options(buf, BCO_ALWAYS);
- }
- buf->b_wininfo->wi_fpos.lnum = lnum;
- buf->b_wininfo->wi_win = curwin;
- #ifdef FEAT_EVAL
- init_var_dict(&buf->b_vars, &buf->b_bufvar); /* init b: variables */
- #endif
- #ifdef FEAT_SYN_HL
- hash_init(&buf->b_s.b_keywtab);
- hash_init(&buf->b_s.b_keywtab_ic);
- #endif
- buf->b_fname = buf->b_sfname;
- #ifdef UNIX
- if (st.st_dev == (dev_T)-1)
- buf->b_dev_valid = FALSE;
- else
- {
- buf->b_dev_valid = TRUE;
- buf->b_dev = st.st_dev;
- buf->b_ino = st.st_ino;
- }
- #endif
- buf->b_u_synced = TRUE;
- buf->b_flags = BF_CHECK_RO | BF_NEVERLOADED;
- if (flags & BLN_DUMMY)
- buf->b_flags |= BF_DUMMY;
- buf_clear_file(buf);
- clrallmarks(buf); /* clear marks */
- fmarks_check_names(buf); /* check file marks for this file */
- buf->b_p_bl = (flags & BLN_LISTED) ? TRUE : FALSE; /* init 'buflisted' */
- #ifdef FEAT_AUTOCMD
- if (!(flags & BLN_DUMMY))
- {
- apply_autocmds(EVENT_BUFNEW, NULL, NULL, FALSE, buf);
- if (flags & BLN_LISTED)
- apply_autocmds(EVENT_BUFADD, NULL, NULL, FALSE, buf);
- # ifdef FEAT_EVAL
- if (aborting()) /* autocmds may abort script processing */
- return NULL;
- # endif
- }
- #endif
- return buf;
- }
- /*
- * Free the memory for the options of a buffer.
- * If "free_p_ff" is TRUE also free 'fileformat', 'buftype' and
- * 'fileencoding'.
- */
- void
- free_buf_options(buf, free_p_ff)
- buf_T *buf;
- int free_p_ff;
- {
- if (free_p_ff)
- {
- #ifdef FEAT_MBYTE
- clear_string_option(&buf->b_p_fenc);
- #endif
- clear_string_option(&buf->b_p_ff);
- #ifdef FEAT_QUICKFIX
- clear_string_option(&buf->b_p_bh);
- clear_string_option(&buf->b_p_bt);
- #endif
- }
- #ifdef FEAT_FIND_ID
- clear_string_option(&buf->b_p_def);
- clear_string_option(&buf->b_p_inc);
- # ifdef FEAT_EVAL
- clear_string_option(&buf->b_p_inex);
- # endif
- #endif
- #if defined(FEAT_CINDENT) && defined(FEAT_EVAL)
- clear_string_option(&buf->b_p_inde);
- clear_string_option(&buf->b_p_indk);
- #endif
- #if defined(FEAT_BEVAL) && defined(FEAT_EVAL)
- clear_string_option(&buf->b_p_bexpr);
- #endif
- #if defined(FEAT_CRYPT)
- clear_string_option(&buf->b_p_cm);
- #endif
- #if defined(FEAT_EVAL)
- clear_string_option(&buf->b_p_fex);
- #endif
- #ifdef FEAT_CRYPT
- clear_string_option(&buf->b_p_key);
- #endif
- clear_string_option(&buf->b_p_kp);
- clear_string_option(&buf->b_p_mps);
- clear_string_option(&buf->b_p_fo);
- clear_string_option(&buf->b_p_flp);
- clear_string_option(&buf->b_p_isk);
- #ifdef FEAT_KEYMAP
- clear_string_option(&buf->b_p_keymap);
- ga_clear(&buf->b_kmap_ga);
- #endif
- #ifdef FEAT_COMMENTS
- clear_string_option(&buf->b_p_com);
- #endif
- #ifdef FEAT_FOLDING
- clear_string_option(&buf->b_p_cms);
- #endif
- clear_string_option(&buf->b_p_nf);
- #ifdef FEAT_SYN_HL
- clear_string_option(&buf->b_p_syn);
- #endif
- #ifdef FEAT_SPELL
- clear_string_option(&buf->b_s.b_p_spc);
- clear_string_option(&buf->b_s.b_p_spf);
- vim_free(buf->b_s.b_cap_prog);
- buf->b_s.b_cap_prog = NULL;
- clear_string_option(&buf->b_s.b_p_spl);
- #endif
- #ifdef FEAT_SEARCHPATH
- clear_string_option(&buf->b_p_sua);
- #endif
- #ifdef FEAT_AUTOCMD
- clear_string_option(&buf->b_p_ft);
- #endif
- #ifdef FEAT_CINDENT
- clear_string_option(&buf->b_p_cink);
- clear_string_option(&buf->b_p_cino);
- #endif
- #if defined(FEAT_CINDENT) || defined(FEAT_SMARTINDENT)
- clear_string_option(&buf->b_p_cinw);
- #endif
- #ifdef FEAT_INS_EXPAND
- clear_string_option(&buf->b_p_cpt);
- #endif
- #ifdef FEAT_COMPL_FUNC
- clear_string_option(&buf->b_p_cfu);
- clear_string_option(&buf->b_p_ofu);
- #endif
- #ifdef FEAT_QUICKFIX
- clear_string_option(&buf->b_p_gp);
- clear_string_option(&buf->b_p_mp);
- clear_string_option(&buf->b_p_efm);
- #endif
- clear_string_option(&buf->b_p_ep);
- clear_string_option(&buf->b_p_path);
- clear_string_option(&buf->b_p_tags);
- #ifdef FEAT_INS_EXPAND
- clear_string_option(&buf->b_p_dict);
- clear_string_option(&buf->b_p_tsr);
- #endif
- #ifdef FEAT_TEXTOBJ
- clear_string_option(&buf-…