diff options
author | Justin M. Keyes <justinkz@gmail.com> | 2018-12-27 22:49:44 +0100 |
---|---|---|
committer | Justin M. Keyes <justinkz@gmail.com> | 2018-12-27 22:49:44 +0100 |
commit | c1015121ec626cab6cb384f544bc0be1a1760c0e (patch) | |
tree | 6cc9a5d1899a4486a24c491e07d17a7dd01f9503 /src | |
parent | 4f030ec24e0e148bbb83aedaef7dd629e5fef130 (diff) | |
parent | e1876c7ad1b5e30c0a9919e2c4587d11550c8507 (diff) | |
download | rneovim-c1015121ec626cab6cb384f544bc0be1a1760c0e.tar.gz rneovim-c1015121ec626cab6cb384f544bc0be1a1760c0e.tar.bz2 rneovim-c1015121ec626cab6cb384f544bc0be1a1760c0e.zip |
Merge 'upstream/master' into pr-win-erw7
Diffstat (limited to 'src')
104 files changed, 13822 insertions, 1138 deletions
diff --git a/src/.clang-format b/src/.clang-format deleted file mode 100644 index 5a910ff34b..0000000000 --- a/src/.clang-format +++ /dev/null @@ -1,18 +0,0 @@ -BasedOnStyle: Google -Language: Cpp -ColumnLimit: 80 -IndentWidth: 2 -TabWidth: 2 -UseTab: Never -IndentCaseLabels: true -BreakBeforeBraces: Linux -AlignEscapedNewlinesLeft: false -AllowShortFunctionsOnASingleLine: false -SpacesBeforeTrailingComments: 2 -PenaltyReturnTypeOnItsOwnLine: 200 -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortIfStatementsOnASingleLine: false -AllowShortLoopsOnASingleLine: false -BinPackParameters: false -BreakBeforeBinaryOperators: true -ContinuationIndentWidth: 4 diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 29a4e1e163..a2c4e677d4 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -79,6 +79,8 @@ file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src) file(GLOB NVIM_SOURCES *.c) file(GLOB NVIM_HEADERS *.h) +file(GLOB XDIFF_SOURCES xdiff/*.c) +file(GLOB XDIFF_HEADERS xdiff/*.h) foreach(subdir os @@ -148,12 +150,13 @@ set(CONV_SOURCES syntax.c tag.c window.c) - foreach(sfile ${CONV_SOURCES}) if(NOT EXISTS "${CMAKE_CURRENT_LIST_DIR}/${sfile}") message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") endif() endforeach() +# xdiff: inlined external project, we don't maintain it. #9306 +list(APPEND CONV_SOURCES ${XDIFF_SOURCES}) if(NOT MSVC) set_source_files_properties( @@ -404,7 +407,8 @@ if(POLICY CMP0069) endif() add_executable(nvim ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} - ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS}) + ${NVIM_GENERATED_SOURCES} ${NVIM_SOURCES} ${NVIM_HEADERS} + ${XDIFF_SOURCES} ${XDIFF_HEADERS}) target_link_libraries(nvim ${NVIM_EXEC_LINK_LIBRARIES}) install_helper(TARGETS nvim) @@ -490,6 +494,7 @@ add_library( EXCLUDE_FROM_ALL ${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${XDIFF_SOURCES} ${XDIFF_HEADERS} ) set_property(TARGET libnvim APPEND PROPERTY INCLUDE_DIRECTORIES ${LUA_PREFERRED_INCLUDE_DIRS}) @@ -514,6 +519,7 @@ else() EXCLUDE_FROM_ALL ${NVIM_SOURCES} ${NVIM_GENERATED_SOURCES} ${NVIM_HEADERS} ${NVIM_GENERATED_FOR_SOURCES} ${NVIM_GENERATED_FOR_HEADERS} + ${XDIFF_SOURCES} ${XDIFF_HEADERS} ${UNIT_TEST_FIXTURES} ) target_link_libraries(nvim-test ${NVIM_TEST_LINK_LIBRARIES}) diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 39330690e8..5df0f0bb47 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -51,8 +51,7 @@ /// /// @param buffer Buffer handle /// @param[out] err Error details, if any -/// @return Line count, or \`0` if the buffer has been unloaded (see -/// |api-buffer|). +/// @return Line count, or 0 for unloaded buffer. |api-buffer| Integer nvim_buf_line_count(Buffer buffer, Error *err) FUNC_API_SINCE(1) { @@ -227,8 +226,7 @@ ArrayOf(String) buffer_get_line_slice(Buffer buffer, /// @param end Last line index (exclusive) /// @param strict_indexing Whether out-of-bounds should be an error. /// @param[out] err Error details, if any -/// @return Array of lines. If the buffer has been unloaded then an empty array -/// will be returned instead. (See |api-buffer|.) +/// @return Array of lines, or empty array for unloaded buffer. ArrayOf(String) nvim_buf_get_lines(uint64_t channel_id, Buffer buffer, Integer start, @@ -491,12 +489,12 @@ end: try_end(err); } -/// Return the byte offset for a line. -// -/// The first line returns 0. UTF-8 bytes are counted, and EOL counts as one -/// byte. 'fileformat' and 'fileencoding' are ignored. Sending in the index -/// just below the last line gives the total byte count for the entire buffer. -/// A final EOL is included if it would be written, see 'eol'. +/// Returns the byte offset for a line. +/// +/// Line 1 (index=0) has offset 0. UTF-8 bytes are counted. EOL is one byte. +/// 'fileformat' and 'fileencoding' are ignored. The line index just after the +/// last line gives the total byte-count of the buffer. A final EOL byte is +/// counted if it would be written, see 'eol'. /// /// Unlike |line2byte()|, throws error for out-of-bounds indexing. /// Returns -1 for unloaded buffer. @@ -504,7 +502,7 @@ end: /// @param buffer Buffer handle /// @param index Line index /// @param[out] err Error details, if any -/// @return Integer Byte offset +/// @return Integer byte offset, or -1 for unloaded buffer. Integer nvim_buf_get_offset(Buffer buffer, Integer index, Error *err) FUNC_API_SINCE(5) { @@ -907,34 +905,34 @@ ArrayOf(Integer, 2) nvim_buf_get_mark(Buffer buffer, String name, Error *err) /// /// Useful for plugins that dynamically generate highlights to a buffer /// (like a semantic highlighter or linter). The function adds a single -/// highlight to a buffer. Unlike matchaddpos() highlights follow changes to +/// highlight to a buffer. Unlike |matchaddpos()| highlights follow changes to /// line numbering (as lines are inserted/removed above the highlighted line), /// like signs and marks do. /// -/// `src_id` is useful for batch deletion/updating of a set of highlights. When -/// called with `src_id = 0`, an unique source id is generated and returned. -/// Successive calls can pass that `src_id` to associate new highlights with -/// the same source group. All highlights in the same group can be cleared -/// with `nvim_buf_clear_highlight`. If the highlight never will be manually -/// deleted, pass `src_id = -1`. +/// Namespaces are used for batch deletion/updating of a set of highlights. To +/// create a namespace, use |nvim_create_namespace| which returns a namespace +/// id. Pass it in to this function as `ns_id` to add highlights to the +/// namespace. All highlights in the same namespace can then be cleared with +/// single call to |nvim_buf_clear_namespace|. If the highlight never will be +/// deleted by an API call, pass `ns_id = -1`. /// -/// If `hl_group` is the empty string no highlight is added, but a new `src_id` -/// is still returned. This is useful for an external plugin to synchrounously -/// request an unique `src_id` at initialization, and later asynchronously add -/// and clear highlights in response to buffer changes. +/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the +/// highlight, the allocated id is then returned. If `hl_group` is the empty +/// string no highlight is added, but a new `ns_id` is still returned. This is +/// supported for backwards compatibility, new code should use +/// |nvim_create_namespace| to create a new empty namespace. /// /// @param buffer Buffer handle -/// @param src_id Source group to use or 0 to use a new group, -/// or -1 for ungrouped highlight +/// @param ns_id namespace to use or -1 for ungrouped highlight /// @param hl_group Name of the highlight group to use /// @param line Line to highlight (zero-indexed) /// @param col_start Start of (byte-indexed) column range to highlight /// @param col_end End of (byte-indexed) column range to highlight, /// or -1 to highlight to end of line /// @param[out] err Error details, if any -/// @return The src_id that was used +/// @return The ns_id that was used Integer nvim_buf_add_highlight(Buffer buffer, - Integer src_id, + Integer ns_id, String hl_group, Integer line, Integer col_start, @@ -964,28 +962,28 @@ Integer nvim_buf_add_highlight(Buffer buffer, hlg_id = syn_check_group((char_u *)hl_group.data, (int)hl_group.size); } - src_id = bufhl_add_hl(buf, (int)src_id, hlg_id, (linenr_T)line+1, - (colnr_T)col_start+1, (colnr_T)col_end); - return src_id; + ns_id = bufhl_add_hl(buf, (int)ns_id, hlg_id, (linenr_T)line+1, + (colnr_T)col_start+1, (colnr_T)col_end); + return ns_id; } -/// Clears highlights and virtual text from a given source id and range of lines +/// Clears namespaced objects, highlights and virtual text, from a line range /// -/// To clear a source group in the entire buffer, pass in 0 and -1 to +/// To clear the namespace in the entire buffer, pass in 0 and -1 to /// line_start and line_end respectively. /// /// @param buffer Buffer handle -/// @param src_id Highlight source group to clear, or -1 to clear all. +/// @param ns_id Namespace to clear, or -1 to clear all namespaces. /// @param line_start Start of range of lines to clear /// @param line_end End of range of lines to clear (exclusive) or -1 to clear -/// to end of file. +/// to end of buffer. /// @param[out] err Error details, if any -void nvim_buf_clear_highlight(Buffer buffer, - Integer src_id, +void nvim_buf_clear_namespace(Buffer buffer, + Integer ns_id, Integer line_start, Integer line_end, Error *err) - FUNC_API_SINCE(1) + FUNC_API_SINCE(5) { buf_T *buf = find_buffer_by_handle(buffer, err); if (!buf) { @@ -1000,7 +998,27 @@ void nvim_buf_clear_highlight(Buffer buffer, line_end = MAXLNUM; } - bufhl_clear_line_range(buf, (int)src_id, (int)line_start+1, (int)line_end); + bufhl_clear_line_range(buf, (int)ns_id, (int)line_start+1, (int)line_end); +} + +/// Clears highlights and virtual text from namespace and range of lines +/// +/// @deprecated use |nvim_buf_clear_namespace|. +/// +/// @param buffer Buffer handle +/// @param ns_id Namespace to clear, or -1 to clear all. +/// @param line_start Start of range of lines to clear +/// @param line_end End of range of lines to clear (exclusive) or -1 to clear +/// to end of file. +/// @param[out] err Error details, if any +void nvim_buf_clear_highlight(Buffer buffer, + Integer ns_id, + Integer line_start, + Integer line_end, + Error *err) + FUNC_API_SINCE(1) +{ + nvim_buf_clear_namespace(buffer, ns_id, line_start, line_end, err); } @@ -1009,15 +1027,20 @@ void nvim_buf_clear_highlight(Buffer buffer, /// By default (and currently the only option) the text will be placed after /// the buffer text. Virtual text will never cause reflow, rather virtual /// text will be truncated at the end of the screen line. The virtual text will -/// begin after one cell to the right of the ordinary text, this will contain -/// the |lcs-eol| char if set, otherwise just be a space. +/// begin one cell (|lcs-eol| or space) after the ordinary text. +/// +/// Namespaces are used to support batch deletion/updating of virtual text. +/// To create a namespace, use |nvim_create_namespace|. Virtual text is +/// cleared using |nvim_buf_clear_namespace|. The same `ns_id` can be used for +/// both virtual text and highlights added by |nvim_buf_add_highlight|, both +/// can then be cleared with a single call to |nvim_buf_clear_namespace|. If the +/// virtual text never will be cleared by an API call, pass `ns_id = -1`. /// -/// The same src_id can be used for both virtual text and highlights added by -/// nvim_buf_add_highlight. Virtual text is cleared using -/// nvim_buf_clear_highlight. +/// As a shorthand, `ns_id = 0` can be used to create a new namespace for the +/// virtual text, the allocated id is then returned. /// /// @param buffer Buffer handle -/// @param src_id Source group to use or 0 to use a new group, +/// @param ns_id Namespace to use or 0 to create a namespace, /// or -1 for a ungrouped annotation /// @param line Line to annotate with virtual text (zero-indexed) /// @param chunks A list of [text, hl_group] arrays, each representing a @@ -1025,9 +1048,9 @@ void nvim_buf_clear_highlight(Buffer buffer, /// can be omitted for no highlight. /// @param opts Optional parameters. Currently not used. /// @param[out] err Error details, if any -/// @return The src_id that was used +/// @return The ns_id that was used Integer nvim_buf_set_virtual_text(Buffer buffer, - Integer src_id, + Integer ns_id, Integer line, Array chunks, Dictionary opts, @@ -1077,9 +1100,9 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id })); } - src_id = bufhl_add_virt_text(buf, (int)src_id, (linenr_T)line+1, - virt_text); - return src_id; + ns_id = bufhl_add_virt_text(buf, (int)ns_id, (linenr_T)line+1, + virt_text); + return ns_id; free_exit: kv_destroy(virt_text); diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index fed20a272a..4da61a30ef 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -46,6 +46,24 @@ # include "api/vim.c.generated.h" #endif +void api_vim_init(void) + FUNC_API_NOEXPORT +{ + namespace_ids = map_new(String, handle_T)(); +} + +void api_vim_free_all_mem(void) + FUNC_API_NOEXPORT +{ + String name; + handle_T id; + map_foreach(namespace_ids, name, id, { + (void)id; + xfree(name.data); + }) + map_free(String, handle_T)(namespace_ids); +} + /// Executes an ex-command. /// /// On execution error: fails with VimL error, does not update v:errmsg. @@ -725,6 +743,9 @@ void nvim_err_writeln(String str) /// Gets the current list of buffer handles /// +/// Includes unlisted (unloaded/deleted) buffers, like `:ls!`. +/// Use |nvim_buf_is_loaded()| to check if a buffer is loaded. +/// /// @return List of buffer handles ArrayOf(Buffer) nvim_list_bufs(void) FUNC_API_SINCE(1) @@ -884,6 +905,49 @@ void nvim_set_current_tabpage(Tabpage tabpage, Error *err) } } +/// create a new namespace, or get one with an exisiting name +/// +/// Namespaces are currently used for buffer highlighting and virtual text, see +/// |nvim_buf_add_highlight| and |nvim_buf_set_virtual_text|. +/// +/// Namespaces can have a name of be anonymous. If `name` is a non-empty string, +/// and a namespace already exists with that name,the existing namespace id is +/// returned. If an empty string is used, a new anonymous namespace is returned. +/// +/// @param name Name of the namespace or empty string +/// @return the namespace id +Integer nvim_create_namespace(String name) + FUNC_API_SINCE(5) +{ + handle_T id = map_get(String, handle_T)(namespace_ids, name); + if (id > 0) { + return id; + } + id = next_namespace_id++; + if (name.size > 0) { + String name_alloc = copy_string(name); + map_put(String, handle_T)(namespace_ids, name_alloc, id); + } + return (Integer)id; +} + +/// Get existing named namespaces +/// +/// @return dict that maps from names to namespace ids. +Dictionary nvim_get_namespaces(void) + FUNC_API_SINCE(5) +{ + Dictionary retval = ARRAY_DICT_INIT; + String name; + handle_T id; + + map_foreach(namespace_ids, name, id, { + PUT(retval, name.data, INTEGER_OBJ(id)); + }) + + return retval; +} + /// Subscribes to event broadcasts /// /// @param channel_id Channel id (passed automatically by the dispatcher) @@ -1028,7 +1092,7 @@ Array nvim_get_api_info(uint64_t channel_id) /// @param attributes Informal attributes describing the client. Clients might /// define their own keys, but the following are suggested: /// - "website" Website of client (for instance github repository) -/// - "license" Informal descripton of the license, such as "Apache 2", +/// - "license" Informal description of the license, such as "Apache 2", /// "GPLv3" or "MIT" /// - "logo" URI or path to image, preferably small logo or icon. /// .png or .svg format is preferred. diff --git a/src/nvim/api/vim.h b/src/nvim/api/vim.h index 5e0b35b562..d6873da6d2 100644 --- a/src/nvim/api/vim.h +++ b/src/nvim/api/vim.h @@ -4,6 +4,10 @@ #include <stdint.h> #include "nvim/api/private/defs.h" +#include "nvim/map.h" + +EXTERN Map(String, handle_T) *namespace_ids INIT(= NULL); +EXTERN handle_T next_namespace_id INIT(= 1); #ifdef INCLUDE_GENERATED_DECLARATIONS # include "api/vim.h.generated.h" diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 5281a7c1f4..cf1d1f5e45 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -10,6 +10,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/vim.h" +#include "nvim/buffer.h" #include "nvim/cursor.h" #include "nvim/window.h" #include "nvim/screen.h" @@ -33,6 +34,41 @@ Buffer nvim_win_get_buf(Window window, Error *err) return win->w_buffer->handle; } +/// Sets the current buffer in a window, without side-effects +/// +/// @param window Window handle +/// @param buffer Buffer handle +/// @param[out] err Error details, if any +void nvim_win_set_buf(Window window, Buffer buffer, Error *err) + FUNC_API_SINCE(5) +{ + win_T *win = find_window_by_handle(window, err), *save_curwin = curwin; + buf_T *buf = find_buffer_by_handle(buffer, err); + tabpage_T *tab = win_find_tabpage(win), *save_curtab = curtab; + + if (!win || !buf) { + return; + } + + if (switch_win(&save_curwin, &save_curtab, win, tab, false) == FAIL) { + api_set_error(err, + kErrorTypeException, + "Failed to switch to window %d", + window); + } + + try_start(); + int result = do_buffer(DOBUF_GOTO, DOBUF_FIRST, FORWARD, buf->b_fnum, 0); + if (!try_end(err) && result == FAIL) { + api_set_error(err, + kErrorTypeException, + "Failed to set buffer %d", + buffer); + } + + restore_win(save_curwin, save_curtab, false); +} + /// Gets the cursor position in the window /// /// @param window Window handle diff --git a/src/nvim/aucmd.c b/src/nvim/aucmd.c index fc421116ea..9ad3414b79 100644 --- a/src/nvim/aucmd.c +++ b/src/nvim/aucmd.c @@ -26,7 +26,6 @@ void aucmd_schedule_focusgained(bool gained) } static void do_autocmd_focusgained(bool gained) - FUNC_ATTR_NONNULL_ALL { static bool recursive = false; diff --git a/src/nvim/auevents.lua b/src/nvim/auevents.lua index a6290aaac1..3cffd66dee 100644 --- a/src/nvim/auevents.lua +++ b/src/nvim/auevents.lua @@ -21,6 +21,7 @@ return { 'BufWritePre', -- before writing a buffer 'ChanInfo', -- info was received about channel 'ChanOpen', -- channel was opened + 'CmdLineChanged', -- command line was modified 'CmdLineEnter', -- after entering cmdline mode 'CmdLineLeave', -- before leaving cmdline mode 'CmdUndefined', -- command undefined @@ -33,6 +34,7 @@ return { 'CursorHoldI', -- idem, in Insert mode 'CursorMoved', -- cursor was moved 'CursorMovedI', -- cursor was moved in Insert mode + 'DiffUpdated', -- diffs have been updated 'DirChanged', -- directory changed 'EncodingChanged', -- after changing the 'encoding' option 'ExitPre', -- before exiting diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index cd77f48320..8b107041b1 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -26,6 +26,7 @@ #include "nvim/api/private/handle.h" #include "nvim/api/private/helpers.h" +#include "nvim/api/vim.h" #include "nvim/ascii.h" #include "nvim/assert.h" #include "nvim/vim.h" @@ -5327,10 +5328,10 @@ int bufhl_add_hl(buf_T *buf, int hl_id, linenr_T lnum, colnr_T col_start, - colnr_T col_end) { - static int next_src_id = 1; + colnr_T col_end) +{ if (src_id == 0) { - src_id = next_src_id++; + src_id = (int)nvim_create_namespace((String)STRING_INIT); } if (hl_id <= 0) { // no highlight group or invalid line, just return src_id @@ -5395,9 +5396,8 @@ int bufhl_add_virt_text(buf_T *buf, linenr_T lnum, VirtText virt_text) { - static int next_src_id = 1; if (src_id == 0) { - src_id = next_src_id++; + src_id = (int)nvim_create_namespace((String)STRING_INIT); } BufhlLine *lineinfo = bufhl_tree_ref(&buf->b_bufhl_info, lnum, true); diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index e86eaf010f..d270714611 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -58,10 +58,10 @@ typedef struct { * functions that set or reset the flags. * * VALID_BOTLINE VALID_BOTLINE_AP - * on on w_botline valid - * off on w_botline approximated - * off off w_botline not valid - * on off not possible + * on on w_botline valid + * off on w_botline approximated + * off off w_botline not valid + * on off not possible */ #define VALID_WROW 0x01 /* w_wrow (window row) is valid */ #define VALID_WCOL 0x02 /* w_wcol (window col) is valid */ @@ -159,7 +159,7 @@ typedef struct { int wo_arab; # define w_p_arab w_onebuf_opt.wo_arab /* 'arabic' */ int wo_bri; -# define w_p_bri w_onebuf_opt.wo_bri /* 'breakindent' */ +# define w_p_bri w_onebuf_opt.wo_bri // 'breakindent' char_u *wo_briopt; # define w_p_briopt w_onebuf_opt.wo_briopt /* 'breakindentopt' */ int wo_diff; @@ -412,13 +412,13 @@ typedef struct { * syntax state too often. * b_sst_array[] is allocated to hold the state for all displayed lines, * and states for 1 out of about 20 other lines. - * b_sst_array pointer to an array of synstate_T - * b_sst_len number of entries in b_sst_array[] - * b_sst_first pointer to first used entry in b_sst_array[] or NULL - * b_sst_firstfree pointer to first free entry in b_sst_array[] or NULL - * b_sst_freecount number of free entries in b_sst_array[] - * b_sst_check_lnum entries after this lnum need to be checked for - * validity (MAXLNUM means no check needed) + * b_sst_array pointer to an array of synstate_T + * b_sst_len number of entries in b_sst_array[] + * b_sst_first pointer to first used entry in b_sst_array[] or NULL + * b_sst_firstfree pointer to first free entry in b_sst_array[] or NULL + * b_sst_freecount number of free entries in b_sst_array[] + * b_sst_check_lnum entries after this lnum need to be checked for + * validity (MAXLNUM means no check needed) */ synstate_T *b_sst_array; int b_sst_len; @@ -475,15 +475,15 @@ struct file_buffer { int b_locked; // Buffer is being closed or referenced, don't // let autocommands wipe it out. - /* - * b_ffname has the full path of the file (NULL for no name). - * b_sfname is the name as the user typed it (or NULL). - * b_fname is the same as b_sfname, unless ":cd" has been done, - * then it is the same as b_ffname (NULL for no name). - */ - char_u *b_ffname; /* full path file name */ - char_u *b_sfname; /* short file name */ - char_u *b_fname; /* current file name */ + // + // b_ffname has the full path of the file (NULL for no name). + // b_sfname is the name as the user typed it (or NULL). + // b_fname is the same as b_sfname, unless ":cd" has been done, + // then it is the same as b_ffname (NULL for no name). + // + char_u *b_ffname; // full path file name + char_u *b_sfname; // short file name + char_u *b_fname; // current file name bool file_id_valid; FileID file_id; @@ -587,12 +587,12 @@ struct file_buffer { bool b_scanned; /* ^N/^P have scanned this buffer */ - /* flags for use of ":lmap" and IM control */ - long b_p_iminsert; /* input mode for insert */ - long b_p_imsearch; /* input mode for search */ -#define B_IMODE_USE_INSERT -1 /* Use b_p_iminsert value for search */ -#define B_IMODE_NONE 0 /* Input via none */ -#define B_IMODE_LMAP 1 /* Input via langmap */ + // flags for use of ":lmap" and IM control + long b_p_iminsert; // input mode for insert + long b_p_imsearch; // input mode for search +#define B_IMODE_USE_INSERT -1 // Use b_p_iminsert value for search +#define B_IMODE_NONE 0 // Input via none +#define B_IMODE_LMAP 1 // Input via langmap # define B_IMODE_LAST 1 short b_kmap_state; /* using "lmap" mappings */ @@ -760,7 +760,7 @@ struct file_buffer { /* Two special kinds of buffers: * help buffer - used for help files, won't use a swap file. * spell buffer - used for spell info, never displayed and doesn't have a - * file name. + * file name. */ bool b_help; /* TRUE for help file buffer (when set b_p_bt is "help") */ @@ -787,6 +787,8 @@ struct file_buffer { // array of channelids which have asked to receive updates for this // buffer. kvec_t(uint64_t) update_channels; + + int b_diff_failed; // internal diff failed for this buffer }; /* @@ -840,6 +842,7 @@ struct tabpage_S { diff_T *tp_first_diff; buf_T *(tp_diffbuf[DB_COUNT]); int tp_diff_invalid; ///< list of diffs is outdated + int tp_diff_update; ///< update diffs before redrawing frame_T *(tp_snapshot[SNAP_COUNT]); ///< window layout snapshots ScopeDictDictItem tp_winvar; ///< Variable for "t:" Dictionary. dict_T *tp_vars; ///< Internal variables, local to tab page. @@ -916,9 +919,9 @@ typedef struct { /// Same as lpos_T, but with additional field len. typedef struct { - linenr_T lnum; ///< line number - colnr_T col; ///< column number - int len; ///< length: 0 - to the end of line + linenr_T lnum; ///< line number + colnr_T col; ///< column number + int len; ///< length: 0 - to the end of line } llpos_T; /// posmatch_T provides an array for storing match items for matchaddpos() @@ -926,10 +929,10 @@ typedef struct typedef struct posmatch posmatch_T; struct posmatch { - llpos_T pos[MAXPOSMATCH]; ///< array of positions - int cur; ///< internal position counter - linenr_T toplnum; ///< top buffer line - linenr_T botlnum; ///< bottom buffer line + llpos_T pos[MAXPOSMATCH]; ///< array of positions + int cur; ///< internal position counter + linenr_T toplnum; ///< top buffer line + linenr_T botlnum; ///< bottom buffer line }; /* @@ -1036,7 +1039,7 @@ struct window_S { * Recomputing is minimized by storing the result of computations. * Use functions in screen.c to check if they are valid and to update. * w_valid is a bitfield of flags, which indicate if specific values are - * valid or need to be recomputed. + * valid or need to be recomputed. */ int w_valid; pos_T w_valid_cursor; /* last known position of w_cursor, used diff --git a/src/nvim/channel.c b/src/nvim/channel.c index 259f1cc600..8b8d27affd 100644 --- a/src/nvim/channel.c +++ b/src/nvim/channel.c @@ -161,7 +161,6 @@ void channel_init(void) channels = pmap_new(uint64_t)(); channel_alloc(kChannelStreamStderr); rpc_init(); - remote_ui_init(); } /// Allocates a channel. @@ -284,6 +283,8 @@ Channel *channel_job_start(char **argv, CallbackReader on_stdout, uint16_t pty_width, uint16_t pty_height, char *term_name, varnumber_T *status_out) { + assert(cwd == NULL || os_isdir_executable(cwd)); + Channel *chan = channel_alloc(kChannelStreamProc); chan->on_stdout = on_stdout; chan->on_stderr = on_stderr; @@ -605,12 +606,15 @@ static void on_channel_output(Stream *stream, Channel *chan, RBuffer *buf, } rbuffer_consumed(buf, count); - // if buffer wasn't consumed, a pending callback is stalled. Aggregate the - // received data and avoid a "burst" of multiple callbacks. - bool buffer_set = reader->buffer.ga_len > 0; - ga_concat_len(&reader->buffer, ptr, count); - if (!reader->buffered && !buffer_set && callback_reader_set(*reader)) { - process_channel_event(chan, &reader->cb, type, reader, 0); + + if (callback_reader_set(*reader) || reader->buffered) { + // if buffer wasn't consumed, a pending callback is stalled. Aggregate the + // received data and avoid a "burst" of multiple callbacks. + bool buffer_set = reader->buffer.ga_len > 0; + ga_concat_len(&reader->buffer, ptr, count); + if (callback_reader_set(*reader) && !reader->buffered && !buffer_set) { + process_channel_event(chan, &reader->cb, type, reader, 0); + } } } diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 4bfe67c9d3..2e0b198c13 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -4,11 +4,17 @@ /// @file diff.c /// /// Code for diff'ing two, three or four buffers. +/// +/// There are three ways to diff: +/// - Shell out to an external diff program, using files. +/// - Use the compiled-in xdiff library. +/// - Let 'diffexpr' do the work, using files. #include <inttypes.h> #include <stdbool.h> #include "nvim/vim.h" +#include "xdiff/xdiff.h" #include "nvim/ascii.h" #include "nvim/diff.h" #include "nvim/buffer.h" @@ -36,16 +42,24 @@ #include "nvim/os/os.h" #include "nvim/os/shell.h" -static int diff_busy = FALSE; // ex_diffgetput() is busy +static int diff_busy = false; // using diff structs, don't change them +static int diff_need_update = false; // ex_diffupdate needs to be called // Flags obtained from the 'diffopt' option -#define DIFF_FILLER 1 // display filler lines -#define DIFF_ICASE 2 // ignore case -#define DIFF_IWHITE 4 // ignore change in white space -#define DIFF_HORIZONTAL 8 // horizontal splits -#define DIFF_VERTICAL 16 // vertical splits -#define DIFF_HIDDEN_OFF 32 // diffoff when hidden -static int diff_flags = DIFF_FILLER; +#define DIFF_FILLER 0x001 // display filler lines +#define DIFF_IBLANK 0x002 // ignore empty lines +#define DIFF_ICASE 0x004 // ignore case +#define DIFF_IWHITE 0x008 // ignore change in white space +#define DIFF_IWHITEALL 0x010 // ignore all white space changes +#define DIFF_IWHITEEOL 0x020 // ignore change in white space at EOL +#define DIFF_HORIZONTAL 0x040 // horizontal splits +#define DIFF_VERTICAL 0x080 // vertical splits +#define DIFF_HIDDEN_OFF 0x100 // diffoff when hidden +#define DIFF_INTERNAL 0x200 // use internal xdiff algorithm +#define ALL_WHITE_DIFF (DIFF_IWHITE | DIFF_IWHITEALL | DIFF_IWHITEEOL) +static int diff_flags = DIFF_INTERNAL | DIFF_FILLER; + +static long diff_algorithm = 0; #define LBUFLEN 50 // length of line in diff file @@ -53,6 +67,25 @@ static int diff_flags = DIFF_FILLER; // kNone when not checked yet static TriState diff_a_works = kNone; +// used for diff input +typedef struct { + char_u *din_fname; // used for external diff + mmfile_t din_mmfile; // used for internal diff +} diffin_T; + +// used for diff result +typedef struct { + char_u *dout_fname; // used for external diff + garray_T dout_ga; // used for internal diff +} diffout_T; + +// two diff inputs and one result +typedef struct { + diffin_T dio_orig; // original file input + diffin_T dio_new; // new file input + diffout_T dio_diff; // diff result + int dio_internal; // using internal diff +} diffio_T; #ifdef INCLUDE_GENERATED_DECLARATIONS # include "diff.c.generated.h" @@ -68,10 +101,10 @@ void diff_buf_delete(buf_T *buf) if (i != DB_COUNT) { tp->tp_diffbuf[i] = NULL; - tp->tp_diff_invalid = TRUE; + tp->tp_diff_invalid = true; if (tp == curtab) { - diff_redraw(TRUE); + diff_redraw(true); } } } @@ -98,8 +131,8 @@ void diff_buf_adjust(win_T *win) int i = diff_buf_idx(win->w_buffer); if (i != DB_COUNT) { curtab->tp_diffbuf[i] = NULL; - curtab->tp_diff_invalid = TRUE; - diff_redraw(TRUE); + curtab->tp_diff_invalid = true; + diff_redraw(true); } } } else { @@ -127,8 +160,8 @@ void diff_buf_add(buf_T *buf) for (i = 0; i < DB_COUNT; ++i) { if (curtab->tp_diffbuf[i] == NULL) { curtab->tp_diffbuf[i] = buf; - curtab->tp_diff_invalid = TRUE; - diff_redraw(TRUE); + curtab->tp_diff_invalid = true; + diff_redraw(true); return; } } @@ -192,9 +225,9 @@ void diff_invalidate(buf_T *buf) FOR_ALL_TABS(tp) { int i = diff_buf_idx_tp(buf, tp); if (i != DB_COUNT) { - tp->tp_diff_invalid = TRUE; + tp->tp_diff_invalid = true; if (tp == curtab) { - diff_redraw(TRUE); + diff_redraw(true); } } } @@ -234,6 +267,15 @@ void diff_mark_adjust(linenr_T line1, linenr_T line2, long amount, static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, linenr_T line2, long amount, long amount_after) { + if (diff_internal()) { + // Will update diffs before redrawing. Set _invalid to update the + // diffs themselves, set _update to also update folds properly just + // before redrawing. + // Do update marks here, it is needed for :%diffput. + tp->tp_diff_invalid = true; + tp->tp_diff_update = true; + } + int inserted; int deleted; if (line2 == MAXLNUM) { @@ -293,14 +335,14 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, // // Check for these situations: - // 1 2 3 - // 1 2 3 - // line1 2 3 4 5 - // 2 3 4 5 - // 2 3 4 5 - // line2 2 3 4 5 - // 3 5 6 - // 3 5 6 + // 1 2 3 + // 1 2 3 + // line1 2 3 4 5 + // 2 3 4 5 + // 2 3 4 5 + // line2 2 3 4 5 + // 3 5 6 + // 3 5 6 // compute last line of this change last = dp->df_lnum[idx] + dp->df_count[idx] - 1; @@ -316,7 +358,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, } dp->df_lnum[idx] += amount_after; } else { - int check_unchanged = FALSE; + int check_unchanged = false; // 2. 3. 4. 5.: inserted/deleted lines touching this diff. if (deleted > 0) { @@ -341,7 +383,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, // 5. delete lines at or just before top of diff n = off; dp->df_count[idx] -= line2 - dp->df_lnum[idx] + 1; - check_unchanged = TRUE; + check_unchanged = true; } dp->df_lnum[idx] = line1; } else { @@ -361,7 +403,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, } else { n = line2 - last; } - check_unchanged = TRUE; + check_unchanged = true; } else { // 3. delete lines inside the diff n = 0; @@ -380,7 +422,7 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, if (dp->df_lnum[idx] <= line1) { // inserted lines somewhere in this diff dp->df_count[idx] += inserted; - check_unchanged = TRUE; + check_unchanged = true; } else { // inserted lines somewhere above this diff dp->df_lnum[idx] += inserted; @@ -447,12 +489,12 @@ static void diff_mark_adjust_tp(tabpage_T *tp, int idx, linenr_T line1, } if (tp == curtab) { - diff_redraw(TRUE); + diff_redraw(true); // Need to recompute the scroll binding, may remove or add filler // lines (e.g., when adding lines above w_topline). But it's slow when // making many changes, postpone until redrawing. - diff_need_scrollbind = TRUE; + diff_need_scrollbind = true; } } @@ -519,7 +561,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) } char_u *line_org = vim_strsave(ml_get_buf(tp->tp_diffbuf[i_org], dp->df_lnum[i_org] + off_org, - FALSE)); + false)); int i_new; for (i_new = i_org + 1; i_new < DB_COUNT; ++i_new) { @@ -538,7 +580,7 @@ static void diff_check_unchanged(tabpage_T *tp, diff_T *dp) if (diff_cmp(line_org, ml_get_buf(tp->tp_diffbuf[i_new], dp->df_lnum[i_new] + off_new, - FALSE)) != 0) { + false)) != 0) { break; } } @@ -612,42 +654,243 @@ static void diff_redraw(int dofold) } else if ((n > 0) && (n > wp->w_topfill)) { wp->w_topfill = n; } - check_topfill(wp, FALSE); + check_topfill(wp, false); + } + } +} + +static void clear_diffin(diffin_T *din) +{ + if (din->din_fname == NULL) { + xfree(din->din_mmfile.ptr); + din->din_mmfile.ptr = NULL; + } else { + os_remove((char *)din->din_fname); + } +} + +static void clear_diffout(diffout_T *dout) +{ + if (dout->dout_fname == NULL) { + ga_clear_strings(&dout->dout_ga); + } else { + os_remove((char *)dout->dout_fname); + } +} + +/// Write buffer "buf" to a memory buffer. +/// +/// @param buf +/// @param din +/// +/// @return FAIL for failure. +static int diff_write_buffer(buf_T *buf, diffin_T *din) +{ + linenr_T lnum; + char_u *s; + long len = 0; + char_u *ptr; + + // xdiff requires one big block of memory with all the text. + for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + len += (long)STRLEN(ml_get_buf(buf, lnum, false)) + 1; + } + ptr = xmalloc(len); + if (ptr == NULL) { + // Allocating memory failed. This can happen, because we try to read + // the whole buffer text into memory. Set the failed flag, the diff + // will be retried with external diff. The flag is never reset. + buf->b_diff_failed = true; + if (p_verbose > 0) { + verbose_enter(); + smsg(_("Not enough memory to use internal diff for buffer \"%s\""), + buf->b_fname); + verbose_leave(); + } + return FAIL; + } + din->din_mmfile.ptr = (char *)ptr; + din->din_mmfile.size = len; + + len = 0; + for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { + for (s = ml_get_buf(buf, lnum, false); *s != NUL; ) { + if (diff_flags & DIFF_ICASE) { + int c; + + // xdiff doesn't support ignoring case, fold-case the text. + int orig_len; + char_u cbuf[MB_MAXBYTES + 1]; + + c = PTR2CHAR(s); + c = enc_utf8 ? utf_fold(c) : TOLOWER_LOC(c); + orig_len = MB_PTR2LEN(s); + if (utf_char2bytes(c, cbuf) != orig_len) { + // TODO(Bram): handle byte length difference + memmove(ptr + len, s, orig_len); + } else { + memmove(ptr + len, cbuf, orig_len); + } + + s += orig_len; + len += orig_len; + } else { + ptr[len++] = *s++; + } } + ptr[len++] = NL; } + return OK; } -/// Write buffer "buf" to file "name". +/// Write buffer "buf" to file or memory buffer. /// /// Always use 'fileformat' set to "unix". /// /// @param buf -/// @param fname +/// @param din /// /// @return FAIL for failure -static int diff_write(buf_T *buf, char_u *fname) +static int diff_write(buf_T *buf, diffin_T *din) { + if (din->din_fname == NULL) { + return diff_write_buffer(buf, din); + } + + // Always use 'fileformat' set to "unix". char_u *save_ff = buf->b_p_ff; buf->b_p_ff = vim_strsave((char_u *)FF_UNIX); - int r = buf_write(buf, fname, NULL, (linenr_T)1, buf->b_ml.ml_line_count, - NULL, FALSE, FALSE, FALSE, TRUE); + int r = buf_write(buf, din->din_fname, NULL, + (linenr_T)1, buf->b_ml.ml_line_count, + NULL, false, false, false, true); free_string_option(buf->b_p_ff); buf->b_p_ff = save_ff; return r; } +/// +/// Update the diffs for all buffers involved. +/// +/// @param dio +/// @param idx_orig +/// @param eap can be NULL +static void diff_try_update(diffio_T *dio, + int idx_orig, + exarg_T *eap) +{ + buf_T *buf; + int idx_new; + + if (dio->dio_internal) { + ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000); + } else { + // We need three temp file names. + dio->dio_orig.din_fname = vim_tempname(); + dio->dio_new.din_fname = vim_tempname(); + dio->dio_diff.dout_fname = vim_tempname(); + if (dio->dio_orig.din_fname == NULL + || dio->dio_new.din_fname == NULL + || dio->dio_diff.dout_fname == NULL) { + goto theend; + } + } + + // Check external diff is actually working. + if (!dio->dio_internal && check_external_diff(dio) == FAIL) { + goto theend; + } + + // :diffupdate! + if (eap != NULL && eap->forceit) { + for (idx_new = idx_orig; idx_new < DB_COUNT; idx_new++) { + buf = curtab->tp_diffbuf[idx_new]; + if (buf_valid(buf)) { + buf_check_timestamp(buf, false); + } + } + } + + // Write the first buffer to a tempfile or mmfile_t. + buf = curtab->tp_diffbuf[idx_orig]; + if (diff_write(buf, &dio->dio_orig) == FAIL) { + goto theend; + } + + // Make a difference between the first buffer and every other. + for (idx_new = idx_orig + 1; idx_new < DB_COUNT; idx_new++) { + buf = curtab->tp_diffbuf[idx_new]; + if (buf == NULL || buf->b_ml.ml_mfp == NULL) { + continue; // skip buffer that isn't loaded + } + + // Write the other buffer and diff with the first one. + if (diff_write(buf, &dio->dio_new) == FAIL) { + continue; + } + if (diff_file(dio) == FAIL) { + continue; + } + + // Read the diff output and add each entry to the diff list. + diff_read(idx_orig, idx_new, &dio->dio_diff); + + clear_diffin(&dio->dio_new); + clear_diffout(&dio->dio_diff); + } + clear_diffin(&dio->dio_orig); + +theend: + xfree(dio->dio_orig.din_fname); + xfree(dio->dio_new.din_fname); + xfree(dio->dio_diff.dout_fname); +} + +/// +/// Return true if the options are set to use the internal diff library. +/// Note that if the internal diff failed for one of the buffers, the external +/// diff will be used anyway. +/// +int diff_internal(void) +{ + return (diff_flags & DIFF_INTERNAL) != 0 && *p_dex == NUL; +} + +/// +/// Return true if the internal diff failed for one of the diff buffers. +/// +static int diff_internal_failed(void) +{ + int idx; + + // Only need to do something when there is another buffer. + for (idx = 0; idx < DB_COUNT; idx++) { + if (curtab->tp_diffbuf[idx] != NULL + && curtab->tp_diffbuf[idx]->b_diff_failed) { + return true; + } + } + return false; +} + /// Completely update the diffs for the buffers involved. /// -/// This uses the ordinary "diff" command. -/// The buffers are written to a file, also for unmodified buffers (the file -/// could have been produced by autocommands, e.g. the netrw plugin). +/// When using the external "diff" command the buffers are written to a file, +/// also for unmodified buffers (the file could have been produced by +/// autocommands, e.g. the netrw plugin). /// /// @param eap can be NULL void ex_diffupdate(exarg_T *eap) { + if (diff_busy) { + diff_need_update = true; + return; + } + + int had_diffs = curtab->tp_first_diff != NULL; + // Delete all diffblocks. diff_clear(curtab); - curtab->tp_diff_invalid = FALSE; + curtab->tp_diff_invalid = false; // Use the first buffer as the original text. int idx_orig; @@ -658,7 +901,7 @@ void ex_diffupdate(exarg_T *eap) } if (idx_orig == DB_COUNT) { - return; + goto theend; } // Only need to do something when there is another buffer. @@ -670,49 +913,70 @@ void ex_diffupdate(exarg_T *eap) } if (idx_new == DB_COUNT) { - return; + goto theend; } - // We need three temp file names. - char *tmp_orig = (char *) vim_tempname(); - char *tmp_new = (char *) vim_tempname(); - char *tmp_diff = (char *) vim_tempname(); + // Only use the internal method if it did not fail for one of the buffers. + diffio_T diffio; + memset(&diffio, 0, sizeof(diffio)); + diffio.dio_internal = diff_internal() && !diff_internal_failed(); - if ((tmp_orig == NULL) || (tmp_new == NULL) || (tmp_diff == NULL)) { - goto theend; + diff_try_update(&diffio, idx_orig, eap); + if (diffio.dio_internal && diff_internal_failed()) { + // Internal diff failed, use external diff instead. + memset(&diffio, 0, sizeof(diffio)); + diff_try_update(&diffio, idx_orig, eap); + } + + // force updating cursor position on screen + curwin->w_valid_cursor.lnum = 0; + +theend: + // A redraw is needed if there were diffs and they were cleared, or there + // are diffs now, which means they got updated. + if (had_diffs || curtab->tp_first_diff != NULL) { + diff_redraw(true); + apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); } +} - // Do a quick test if "diff" really works. Otherwise it looks like there - // are no differences. Can't use the return value, it's non-zero when - // there are differences. +/// +/// Do a quick test if "diff" really works. Otherwise it looks like there +/// are no differences. Can't use the return value, it's non-zero when +/// there are differences. +/// +static int check_external_diff(diffio_T *diffio) +{ // May try twice, first with "-a" and then without. int io_error = false; TriState ok = kFalse; for (;;) { ok = kFalse; - FILE *fd = mch_fopen(tmp_orig, "w"); + FILE *fd = mch_fopen((char *)diffio->dio_orig.din_fname, "w"); if (fd == NULL) { - io_error = TRUE; + io_error = true; } else { if (fwrite("line1\n", (size_t)6, (size_t)1, fd) != 1) { - io_error = TRUE; + io_error = true; } fclose(fd); - fd = mch_fopen(tmp_new, "w"); + fd = mch_fopen((char *)diffio->dio_new.din_fname, "w"); if (fd == NULL) { - io_error = TRUE; + io_error = true; } else { if (fwrite("line2\n", (size_t)6, (size_t)1, fd) != 1) { - io_error = TRUE; + io_error = true; } fclose(fd); - diff_file(tmp_orig, tmp_new, tmp_diff); - fd = mch_fopen(tmp_diff, "r"); + fd = NULL; + if (diff_file(diffio) == OK) { + fd = mch_fopen((char *)diffio->dio_diff.dout_fname, "r"); + } if (fd == NULL) { - io_error = TRUE; + io_error = true; } else { char_u linebuf[LBUFLEN]; @@ -728,10 +992,10 @@ void ex_diffupdate(exarg_T *eap) } fclose(fd); } - os_remove(tmp_diff); - os_remove(tmp_new); + os_remove((char *)diffio->dio_diff.dout_fname); + os_remove((char *)diffio->dio_new.din_fname); } - os_remove(tmp_orig); + os_remove((char *)diffio->dio_orig.din_fname); } // When using 'diffexpr' break here. @@ -757,83 +1021,92 @@ void ex_diffupdate(exarg_T *eap) } EMSG(_("E97: Cannot create diffs")); diff_a_works = kNone; - goto theend; - } - - // :diffupdate! - if ((eap != NULL) && eap->forceit) { - for (idx_new = idx_orig; idx_new < DB_COUNT; ++idx_new) { - buf_T *buf = curtab->tp_diffbuf[idx_new]; - if (buf_valid(buf)) { - buf_check_timestamp(buf, FALSE); - } - } + return FAIL; } + return OK; +} - // Write the first buffer to a tempfile. - buf_T *buf = curtab->tp_diffbuf[idx_orig]; - if (diff_write(buf, (char_u *) tmp_orig) == FAIL) { - goto theend; - } +/// +/// Invoke the xdiff function. +/// +static int diff_file_internal(diffio_T *diffio) +{ + xpparam_t param; + xdemitconf_t emit_cfg; + xdemitcb_t emit_cb; - // Make a difference between the first buffer and every other. - for (idx_new = idx_orig + 1; idx_new < DB_COUNT; ++idx_new) { - buf_T *buf = curtab->tp_diffbuf[idx_new]; - if (buf == NULL || buf->b_ml.ml_mfp == NULL) { - continue; // skip buffer that isn't loaded - } + memset(¶m, 0, sizeof(param)); + memset(&emit_cfg, 0, sizeof(emit_cfg)); + memset(&emit_cb, 0, sizeof(emit_cb)); - if (diff_write(buf, (char_u *) tmp_new) == FAIL) { - continue; - } - diff_file(tmp_orig, tmp_new, tmp_diff); + param.flags = diff_algorithm; - // Read the diff output and add each entry to the diff list. - diff_read(idx_orig, idx_new, (char_u *) tmp_diff); - os_remove(tmp_diff); - os_remove(tmp_new); + if (diff_flags & DIFF_IWHITE) { + param.flags |= XDF_IGNORE_WHITESPACE_CHANGE; + } + if (diff_flags & DIFF_IWHITEALL) { + param.flags |= XDF_IGNORE_WHITESPACE; + } + if (diff_flags & DIFF_IWHITEEOL) { + param.flags |= XDF_IGNORE_WHITESPACE_AT_EOL; + } + if (diff_flags & DIFF_IBLANK) { + param.flags |= XDF_IGNORE_BLANK_LINES; } - os_remove(tmp_orig); - - // force updating cursor position on screen - curwin->w_valid_cursor.lnum = 0; - - diff_redraw(TRUE); -theend: - xfree(tmp_orig); - xfree(tmp_new); - xfree(tmp_diff); + emit_cfg.ctxlen = 0; // don't need any diff_context here + emit_cb.priv = &diffio->dio_diff; + emit_cb.outf = xdiff_out; + if (xdl_diff(&diffio->dio_orig.din_mmfile, + &diffio->dio_new.din_mmfile, + ¶m, &emit_cfg, &emit_cb) < 0) { + EMSG(_("E960: Problem creating the internal diff")); + return FAIL; + } + return OK; } /// Make a diff between files "tmp_orig" and "tmp_new", results in "tmp_diff". /// -/// @param tmp_orig -/// @param tmp_new -/// @param tmp_diff -static void diff_file(const char *const tmp_orig, const char *const tmp_new, - const char *const tmp_diff) +/// @param dio +/// +/// @return OK or FAIL +static int diff_file(diffio_T *dio) { + char *tmp_orig = (char *)dio->dio_orig.din_fname; + char *tmp_new = (char *)dio->dio_new.din_fname; + char *tmp_diff = (char *)dio->dio_diff.dout_fname; if (*p_dex != NUL) { // Use 'diffexpr' to generate the diff file. eval_diff(tmp_orig, tmp_new, tmp_diff); + return OK; + } + // Use xdiff for generating the diff. + if (dio->dio_internal) { + return diff_file_internal(dio); } else { const size_t len = (strlen(tmp_orig) + strlen(tmp_new) + strlen(tmp_diff) + STRLEN(p_srr) + 27); char *const cmd = xmalloc(len); + if (cmd == NULL) { + return FAIL; + } - /* We don't want $DIFF_OPTIONS to get in the way. */ + // We don't want $DIFF_OPTIONS to get in the way. if (os_getenv("DIFF_OPTIONS")) { os_unsetenv("DIFF_OPTIONS"); } - /* Build the diff command and execute it. Always use -a, binary - * differences are of no use. Ignore errors, diff returns - * non-zero when differences have been found. */ - vim_snprintf(cmd, len, "diff %s%s%s%s%s %s", + // Build the diff command and execute it. Always use -a, binary + // differences are of no use. Ignore errors, diff returns + // non-zero when differences have been found. + vim_snprintf((char *)cmd, len, "diff %s%s%s%s%s%s%s%s %s", diff_a_works == kFalse ? "" : "-a ", "", (diff_flags & DIFF_IWHITE) ? "-b " : "", + (diff_flags & DIFF_IWHITEALL) ? "-w " : "", + (diff_flags & DIFF_IWHITEEOL) ? "-Z " : "", + (diff_flags & DIFF_IBLANK) ? "-B " : "", (diff_flags & DIFF_ICASE) ? "-i " : "", tmp_orig, tmp_new); append_redir(cmd, len, (char *) p_srr, tmp_diff); @@ -843,6 +1116,7 @@ static void diff_file(const char *const tmp_orig, const char *const tmp_new, NULL); unblock_autocmds(); xfree(cmd); + return OK; } } @@ -876,7 +1150,7 @@ void ex_diffpatch(exarg_T *eap) // Write the current buffer to "tmp_orig". if (buf_write(curbuf, tmp_orig, NULL, (linenr_T)1, curbuf->b_ml.ml_line_count, - NULL, FALSE, FALSE, FALSE, TRUE) == FAIL) { + NULL, false, false, false, true) == FAIL) { goto theend; } @@ -907,7 +1181,7 @@ void ex_diffpatch(exarg_T *eap) tempdir = "/tmp"; } os_chdir(tempdir); - shorten_fnames(TRUE); + shorten_fnames(true); } #endif @@ -934,7 +1208,7 @@ void ex_diffpatch(exarg_T *eap) if (os_chdir((char *)dirbuf) != 0) { EMSG(_(e_prev_dir)); } - shorten_fnames(TRUE); + shorten_fnames(true); } #endif @@ -971,8 +1245,8 @@ void ex_diffpatch(exarg_T *eap) // check that split worked and editing tmp_new if ((curwin != old_curwin) && win_valid(old_curwin)) { // Set 'diff', 'scrollbind' on and 'wrap' off. - diff_win_options(curwin, TRUE); - diff_win_options(old_curwin, TRUE); + diff_win_options(curwin, true); + diff_win_options(old_curwin, true); if (newname != NULL) { // do a ":file filename.new" on the patched buffer @@ -1025,7 +1299,7 @@ void ex_diffsplit(exarg_T *eap) if (win_split(0, (diff_flags & DIFF_VERTICAL) ? WSP_VERT : 0) != FAIL) { // Pretend it was a ":split fname" command eap->cmdidx = CMD_split; - curwin->w_p_diff = TRUE; + curwin->w_p_diff = true; do_exedit(eap, old_curwin); // split must have worked @@ -1052,7 +1326,7 @@ void ex_diffsplit(exarg_T *eap) void ex_diffthis(exarg_T *eap) { // Set 'diff', 'scrollbind' on and 'wrap' off. - diff_win_options(curwin, TRUE); + diff_win_options(curwin, true); } static void set_diff_option(win_T *wp, int value) @@ -1085,12 +1359,12 @@ void diff_win_options(win_T *wp, int addbuf) if (!wp->w_p_diff) { wp->w_p_scb_save = wp->w_p_scb; } - wp->w_p_scb = TRUE; + wp->w_p_scb = true; if (!wp->w_p_diff) { wp->w_p_crb_save = wp->w_p_crb; } - wp->w_p_crb = TRUE; + wp->w_p_crb = true; if (!wp->w_p_diff) { wp->w_p_wrap_save = wp->w_p_wrap; @@ -1116,7 +1390,7 @@ void diff_win_options(win_T *wp, int addbuf) wp->w_p_fdl_save = wp->w_p_fdl; } wp->w_p_fdc = diff_foldcolumn; - wp->w_p_fen = TRUE; + wp->w_p_fen = true; wp->w_p_fdl = 0; foldUpdateAll(wp); @@ -1211,88 +1485,97 @@ void ex_diffoff(exarg_T *eap) /// /// @param idx_orig idx of original file /// @param idx_new idx of new file -/// @param fname name of diff output file -static void diff_read(int idx_orig, int idx_new, char_u *fname) +/// @dout diff output +static void diff_read(int idx_orig, int idx_new, diffout_T *dout) { - FILE *fd; + FILE *fd = NULL; + int line_idx = 0; diff_T *dprev = NULL; diff_T *dp = curtab->tp_first_diff; diff_T *dn, *dpl; - long f1, l1, f2, l2; - char_u linebuf[LBUFLEN]; // only need to hold the diff line - int difftype; - char_u *p; + char_u linebuf[LBUFLEN]; // only need to hold the diff line + char_u *line; long off; int i; linenr_T lnum_orig, lnum_new; long count_orig, count_new; - int notset = TRUE; // block "*dp" not set yet - - fd = mch_fopen((char *)fname, "r"); - - if (fd == NULL) { - EMSG(_("E98: Cannot read diff output")); - return; + int notset = true; // block "*dp" not set yet + enum { + DIFF_ED, + DIFF_UNIFIED, + DIFF_NONE + } diffstyle = DIFF_NONE; + + if (dout->dout_fname == NULL) { + diffstyle = DIFF_UNIFIED; + } else { + fd = mch_fopen((char *)dout->dout_fname, "r"); + if (fd == NULL) { + EMSG(_("E98: Cannot read diff output")); + return; + } } for (;;) { - if (vim_fgets(linebuf, LBUFLEN, fd)) { - // end of file - break; - } - - if (!isdigit(*linebuf)) { - // not the start of a diff block - continue; - } - - // This line must be one of three formats: - // {first}[,{last}]c{first}[,{last}] - // {first}a{first}[,{last}] - // {first}[,{last}]d{first} - p = linebuf; - f1 = getdigits_long(&p); - - if (*p == ',') { - ++p; - l1 = getdigits_long(&p); - } else { - l1 = f1; - } - - if ((*p != 'a') && (*p != 'c') && (*p != 'd')) { - // invalid diff format - continue; - } - difftype = *p++; - f2 = getdigits_long(&p); - - if (*p == ',') { - ++p; - l2 = getdigits_long(&p); - } else { - l2 = f2; - } - - if ((l1 < f1) || (l2 < f2)) { - // invalid line range - continue; - } - - if (difftype == 'a') { - lnum_orig = f1 + 1; - count_orig = 0; + if (fd == NULL) { + if (line_idx >= dout->dout_ga.ga_len) { + break; // did last line + } + line = ((char_u **)dout->dout_ga.ga_data)[line_idx++]; } else { - lnum_orig = f1; - count_orig = l1 - f1 + 1; + if (vim_fgets(linebuf, LBUFLEN, fd)) { + break; // end of file + } + line = linebuf; + } + + if (diffstyle == DIFF_NONE) { + // Determine diff style. + // ed like diff looks like this: + // {first}[,{last}]c{first}[,{last}] + // {first}a{first}[,{last}] + // {first}[,{last}]d{first} + // + // unified diff looks like this: + // --- file1 2018-03-20 13:23:35.783153140 +0100 + // +++ file2 2018-03-20 13:23:41.183156066 +0100 + // @@ -1,3 +1,5 @@ + if (isdigit(*line)) { + diffstyle = DIFF_ED; + } else if ((STRNCMP(line, "@@ ", 3) == 0)) { + diffstyle = DIFF_UNIFIED; + } else if ((STRNCMP(line, "--- ", 4) == 0) + && (vim_fgets(linebuf, LBUFLEN, fd) == 0) + && (STRNCMP(line, "+++ ", 4) == 0) + && (vim_fgets(linebuf, LBUFLEN, fd) == 0) + && (STRNCMP(line, "@@ ", 3) == 0)) { + diffstyle = DIFF_UNIFIED; + } else { + // Format not recognized yet, skip over this line. Cygwin diff + // may put a warning at the start of the file. + continue; + } } - if (difftype == 'd') { - lnum_new = f2 + 1; - count_new = 0; + if (diffstyle == DIFF_ED) { + if (!isdigit(*line)) { + continue; // not the start of a diff block + } + if (parse_diff_ed(line, &lnum_orig, &count_orig, + &lnum_new, &count_new) == FAIL) { + continue; + } + } else if (diffstyle == DIFF_UNIFIED) { + if (STRNCMP(line, "@@ ", 3) != 0) { + continue; // not the start of a diff block + } + if (parse_diff_unified(line, &lnum_orig, &count_orig, + &lnum_new, &count_new) == FAIL) { + continue; + } } else { - lnum_new = f2; - count_new = l2 - f2 + 1; + EMSG(_("E959: Invalid diff format.")); + break; } // Go over blocks before the change, for which orig and new are equal. @@ -1304,7 +1587,7 @@ static void diff_read(int idx_orig, int idx_new, char_u *fname) } dprev = dp; dp = dp->df_next; - notset = TRUE; + notset = true; } if ((dp != NULL) @@ -1391,7 +1674,7 @@ static void diff_read(int idx_orig, int idx_new, char_u *fname) } } } - notset = FALSE; // "*dp" has been set + notset = false; // "*dp" has been set } // for remaining diff blocks orig and new are equal @@ -1401,10 +1684,12 @@ static void diff_read(int idx_orig, int idx_new, char_u *fname) } dprev = dp; dp = dp->df_next; - notset = TRUE; + notset = true; } - fclose(fd); + if (fd != NULL) { + fclose(fd); + } } /// Copy an entry at "dp" from "idx_orig" to "idx_new". @@ -1503,23 +1788,23 @@ int diff_check(win_T *wp, linenr_T lnum) } if (lnum < dp->df_lnum[idx] + dp->df_count[idx]) { - int zero = FALSE; + int zero = false; // Changed or inserted line. If the other buffers have a count of // zero, the lines were inserted. If the other buffers have the same // count, check if the lines are identical. - cmp = FALSE; + cmp = false; for (i = 0; i < DB_COUNT; ++i) { if ((i != idx) && (curtab->tp_diffbuf[i] != NULL)) { if (dp->df_count[i] == 0) { - zero = TRUE; + zero = true; } else { if (dp->df_count[i] != dp->df_count[idx]) { // nr of lines changed. return -1; } - cmp = TRUE; + cmp = true; } } } @@ -1543,7 +1828,7 @@ int diff_check(win_T *wp, linenr_T lnum) // the difference. Can't remove the entry here, we might be halfway // through updating the window. Just report the text as unchanged. // Other windows might still show the change though. - if (zero == FALSE) { + if (zero == false) { return 0; } return -2; @@ -1635,20 +1920,28 @@ static bool diff_equal_char(const char_u *const p1, const char_u *const p2, /// @return on-zero if the two strings are different. static int diff_cmp(char_u *s1, char_u *s2) { - if ((diff_flags & (DIFF_ICASE | DIFF_IWHITE)) == 0) { + if ((diff_flags & DIFF_IBLANK) + && (*skipwhite(s1) == NUL || *skipwhite(s2) == NUL)) { + return 0; + } + + if ((diff_flags & (DIFF_ICASE | ALL_WHITE_DIFF)) == 0) { return STRCMP(s1, s2); } - if ((diff_flags & DIFF_ICASE) && !(diff_flags & DIFF_IWHITE)) { + if ((diff_flags & DIFF_ICASE) && !(diff_flags & ALL_WHITE_DIFF)) { return mb_stricmp((const char *)s1, (const char *)s2); } - // Ignore white space changes and possibly ignore case. char_u *p1 = s1; char_u *p2 = s2; + // Ignore white space changes and possibly ignore case. while (*p1 != NUL && *p2 != NUL) { - if (ascii_iswhite(*p1) && ascii_iswhite(*p2)) { + if (((diff_flags & DIFF_IWHITE) + && ascii_iswhite(*p1) && ascii_iswhite(*p2)) + || ((diff_flags & DIFF_IWHITEALL) + && (ascii_iswhite(*p1) || ascii_iswhite(*p2)))) { p1 = skipwhite(p1); p2 = skipwhite(p2); } else { @@ -1815,6 +2108,8 @@ int diffopt_changed(void) int diff_context_new = 6; int diff_flags_new = 0; int diff_foldcolumn_new = 2; + long diff_algorithm_new = 0; + long diff_indent_heuristic = 0; char_u *p = p_dip; while (*p != NUL) { @@ -1824,9 +2119,18 @@ int diffopt_changed(void) } else if ((STRNCMP(p, "context:", 8) == 0) && ascii_isdigit(p[8])) { p += 8; diff_context_new = getdigits_int(&p); + } else if (STRNCMP(p, "iblank", 6) == 0) { + p += 6; + diff_flags_new |= DIFF_IBLANK; } else if (STRNCMP(p, "icase", 5) == 0) { p += 5; diff_flags_new |= DIFF_ICASE; + } else if (STRNCMP(p, "iwhiteall", 9) == 0) { + p += 9; + diff_flags_new |= DIFF_IWHITEALL; + } else if (STRNCMP(p, "iwhiteeol", 9) == 0) { + p += 9; + diff_flags_new |= DIFF_IWHITEEOL; } else if (STRNCMP(p, "iwhite", 6) == 0) { p += 6; diff_flags_new |= DIFF_IWHITE; @@ -1842,6 +2146,29 @@ int diffopt_changed(void) } else if (STRNCMP(p, "hiddenoff", 9) == 0) { p += 9; diff_flags_new |= DIFF_HIDDEN_OFF; + } else if (STRNCMP(p, "indent-heuristic", 16) == 0) { + p += 16; + diff_indent_heuristic = XDF_INDENT_HEURISTIC; + } else if (STRNCMP(p, "internal", 8) == 0) { + p += 8; + diff_flags_new |= DIFF_INTERNAL; + } else if (STRNCMP(p, "algorithm:", 10) == 0) { + p += 10; + if (STRNCMP(p, "myers", 5) == 0) { + p += 5; + diff_algorithm_new = 0; + } else if (STRNCMP(p, "minimal", 7) == 0) { + p += 7; + diff_algorithm_new = XDF_NEED_MINIMAL; + } else if (STRNCMP(p, "patience", 8) == 0) { + p += 8; + diff_algorithm_new = XDF_PATIENCE_DIFF; + } else if (STRNCMP(p, "histogram", 9) == 0) { + p += 9; + diff_algorithm_new = XDF_HISTOGRAM_DIFF; + } else { + return FAIL; + } } if ((*p != ',') && (*p != NUL)) { @@ -1853,23 +2180,27 @@ int diffopt_changed(void) } } + diff_algorithm_new |= diff_indent_heuristic; + // Can't have both "horizontal" and "vertical". if ((diff_flags_new & DIFF_HORIZONTAL) && (diff_flags_new & DIFF_VERTICAL)) { return FAIL; } - // If "icase" or "iwhite" was added or removed, need to update the diff. - if (diff_flags != diff_flags_new) { + // If flags were added or removed, or the algorithm was changed, need to + // update the diff. + if (diff_flags != diff_flags_new || diff_algorithm != diff_algorithm_new) { FOR_ALL_TABS(tp) { - tp->tp_diff_invalid = TRUE; + tp->tp_diff_invalid = true; } } diff_flags = diff_flags_new; diff_context = diff_context_new; diff_foldcolumn = diff_foldcolumn_new; + diff_algorithm = diff_algorithm_new; - diff_redraw(TRUE); + diff_redraw(true); // recompute the scroll binding with the new option value, may // remove or add filler lines @@ -1910,7 +2241,7 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) int l; // Make a copy of the line, the next ml_get() will invalidate it. - char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, FALSE)); + char_u *line_org = vim_strsave(ml_get_buf(wp->w_buffer, lnum, false)); int idx = diff_buf_idx(wp->w_buffer); if (idx == DB_COUNT) { @@ -1942,15 +2273,18 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) } added = false; line_new = ml_get_buf(curtab->tp_diffbuf[i], - dp->df_lnum[i] + off, FALSE); + dp->df_lnum[i] + off, false); // Search for start of difference si_org = si_new = 0; while (line_org[si_org] != NUL) { - if ((diff_flags & DIFF_IWHITE) - && ascii_iswhite(line_org[si_org]) - && ascii_iswhite(line_new[si_new])) { + if (((diff_flags & DIFF_IWHITE) + && ascii_iswhite(line_org[si_org]) + && ascii_iswhite(line_new[si_new])) + || ((diff_flags & DIFF_IWHITEALL) + && (ascii_iswhite(line_org[si_org]) + || ascii_iswhite(line_new[si_new])))) { si_org = (int)(skipwhite(line_org + si_org) - line_org); si_new = (int)(skipwhite(line_new + si_new) - line_new); } else { @@ -1980,9 +2314,12 @@ bool diff_find_change(win_T *wp, linenr_T lnum, int *startp, int *endp) && ei_new >= si_new && ei_org >= 0 && ei_new >= 0) { - if ((diff_flags & DIFF_IWHITE) - && ascii_iswhite(line_org[ei_org]) - && ascii_iswhite(line_new[ei_new])) { + if (((diff_flags & DIFF_IWHITE) + && ascii_iswhite(line_org[ei_org]) + && ascii_iswhite(line_new[ei_new])) + || ((diff_flags & DIFF_IWHITEALL) + && (ascii_iswhite(line_org[ei_org]) + || ascii_iswhite(line_new[ei_new])))) { while (ei_org >= *startp && ascii_iswhite(line_org[ei_org])) { ei_org--; } @@ -2117,7 +2454,7 @@ void ex_diffgetput(exarg_T *eap) int start_skip, end_skip; int new_count; int buf_empty; - int found_not_ma = FALSE; + int found_not_ma = false; int idx_other; int idx_from; int idx_to; @@ -2138,7 +2475,7 @@ void ex_diffgetput(exarg_T *eap) || MODIFIABLE(curtab->tp_diffbuf[idx_other])) { break; } - found_not_ma = TRUE; + found_not_ma = true; } } @@ -2176,7 +2513,7 @@ void ex_diffgetput(exarg_T *eap) // digits only i = atol((char *)eap->arg); } else { - i = buflist_findpat(eap->arg, p, FALSE, TRUE, FALSE); + i = buflist_findpat(eap->arg, p, false, true, false); if (i < 0) { // error message already given @@ -2202,7 +2539,7 @@ void ex_diffgetput(exarg_T *eap) } } - diff_busy = TRUE; + diff_busy = true; // When no range given include the line above or below the cursor. if (eap->addr_count == 0) { @@ -2239,7 +2576,7 @@ void ex_diffgetput(exarg_T *eap) change_warning(0); if (diff_buf_idx(curbuf) != idx_to) { EMSG(_("E787: Buffer changed unexpectedly")); - return; + goto theend; } } @@ -2308,7 +2645,7 @@ void ex_diffgetput(exarg_T *eap) for (i = 0; i < count; ++i) { // remember deleting the last line of the buffer buf_empty = curbuf->b_ml.ml_line_count == 1; - ml_delete(lnum, FALSE); + ml_delete(lnum, false); added--; } @@ -2317,15 +2654,15 @@ void ex_diffgetput(exarg_T *eap) if (nr > curtab->tp_diffbuf[idx_from]->b_ml.ml_line_count) { break; } - p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, FALSE)); - ml_append(lnum + i - 1, p, 0, FALSE); + p = vim_strsave(ml_get_buf(curtab->tp_diffbuf[idx_from], nr, false)); + ml_append(lnum + i - 1, p, 0, false); xfree(p); added++; if (buf_empty && (curbuf->b_ml.ml_line_count == 2)) { // Added the first line into an empty buffer, need to // delete the dummy empty line. - buf_empty = FALSE; - ml_delete((linenr_T)2, FALSE); + buf_empty = false; + ml_delete((linenr_T)2, false); } } new_count = dp->df_count[idx_to] + added; @@ -2399,20 +2736,31 @@ void ex_diffgetput(exarg_T *eap) // another buffer. Sync undo if the command was typed. This isn't // 100% right when ":diffput" is used in a function or mapping. if (KeyTyped) { - u_sync(FALSE); + u_sync(false); } aucmd_restbuf(&aco); } - diff_busy = FALSE; +theend: + diff_busy = false; + if (diff_need_update) { + ex_diffupdate(NULL); + } - // Check that the cursor is on a valid character and update it's position. - // When there were filler lines the topline has become invalid. + // Check that the cursor is on a valid character and update its + // position. When there were filler lines the topline has become + // invalid. check_cursor(); changed_line_abv_curs(); - // Also need to redraw the other buffers. - diff_redraw(FALSE); + if (diff_need_update) { + // redraw already done by ex_diffupdate() + diff_need_update = false; + } else { + // Also need to redraw the other buffers. + diff_redraw(false); + apply_autocmds(EVENT_DIFFUPDATED, NULL, NULL, false, curbuf); + } } /// Update folds for all diff buffers for entry "dp". @@ -2637,3 +2985,145 @@ linenr_T diff_lnum_win(linenr_T lnum, win_T *wp) } return n; } + +/// +/// Handle an ED style diff line. +/// Return FAIL if the line does not contain diff info. +/// +static int parse_diff_ed(char_u *line, + linenr_T *lnum_orig, + long *count_orig, + linenr_T *lnum_new, + long *count_new) +{ + char_u *p; + long f1, l1, f2, l2; + int difftype; + + // The line must be one of three formats: + // change: {first}[,{last}]c{first}[,{last}] + // append: {first}a{first}[,{last}] + // delete: {first}[,{last}]d{first} + p = line; + f1 = getdigits(&p); + if (*p == ',') { + p++; + l1 = getdigits(&p); + } else { + l1 = f1; + } + if (*p != 'a' && *p != 'c' && *p != 'd') { + return FAIL; // invalid diff format + } + difftype = *p++; + f2 = getdigits(&p); + if (*p == ',') { + p++; + l2 = getdigits(&p); + } else { + l2 = f2; + } + if (l1 < f1 || l2 < f2) { + return FAIL; + } + + if (difftype == 'a') { + *lnum_orig = f1 + 1; + *count_orig = 0; + } else { + *lnum_orig = f1; + *count_orig = l1 - f1 + 1; + } + if (difftype == 'd') { + *lnum_new = f2 + 1; + *count_new = 0; + } else { + *lnum_new = f2; + *count_new = l2 - f2 + 1; + } + return OK; +} + +/// +/// Parses unified diff with zero(!) context lines. +/// Return FAIL if there is no diff information in "line". +/// +static int parse_diff_unified(char_u *line, + linenr_T *lnum_orig, + long *count_orig, + linenr_T *lnum_new, + long *count_new) +{ + char_u *p; + long oldline, oldcount, newline, newcount; + + // Parse unified diff hunk header: + // @@ -oldline,oldcount +newline,newcount @@ + p = line; + if (*p++ == '@' && *p++ == '@' && *p++ == ' ' && *p++ == '-') { + oldline = getdigits(&p); + if (*p == ',') { + p++; + oldcount = getdigits(&p); + } else { + oldcount = 1; + } + if (*p++ == ' ' && *p++ == '+') { + newline = getdigits(&p); + if (*p == ',') { + p++; + newcount = getdigits(&p); + } else { + newcount = 1; + } + } else { + return FAIL; // invalid diff format + } + + if (oldcount == 0) { + oldline += 1; + } + if (newcount == 0) { + newline += 1; + } + if (newline == 0) { + newline = 1; + } + + *lnum_orig = oldline; + *count_orig = oldcount; + *lnum_new = newline; + *count_new = newcount; + + return OK; + } + + return FAIL; +} + +/// +/// Callback function for the xdl_diff() function. +/// Stores the diff output in a grow array. +/// +static int xdiff_out(void *priv, mmbuffer_t *mb, int nbuf) +{ + diffout_T *dout = (diffout_T *)priv; + char_u *p; + + // The header line always comes by itself, text lines in at least two + // parts. We drop the text part. + if (nbuf > 1) { + return 0; + } + + // sanity check + if (STRNCMP(mb[0].ptr, "@@ ", 3) != 0) { + return 0; + } + + ga_grow(&dout->dout_ga, 1); + + p = vim_strnsave((char_u *)mb[0].ptr, mb[0].size); + ((char_u **)dout->dout_ga.ga_data)[dout->dout_ga.ga_len++] = p; + return 0; +} diff --git a/src/nvim/edit.c b/src/nvim/edit.c index d20660bfb9..c04190eaba 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -3127,10 +3127,16 @@ static void ins_compl_restart(void) */ static void ins_compl_set_original_text(char_u *str) { - /* Replace the original text entry. */ - if (compl_first_match->cp_flags & ORIGINAL_TEXT) { /* safety check */ + // Replace the original text entry. + // The ORIGINAL_TEXT flag is either at the first item or might possibly be + // at the last item for backward completion + if (compl_first_match->cp_flags & ORIGINAL_TEXT) { // safety check xfree(compl_first_match->cp_str); compl_first_match->cp_str = vim_strsave(str); + } else if (compl_first_match->cp_prev != NULL + && (compl_first_match->cp_prev->cp_flags & ORIGINAL_TEXT)) { + xfree(compl_first_match->cp_prev->cp_str); + compl_first_match->cp_prev->cp_str = vim_strsave(str); } } @@ -3189,7 +3195,8 @@ static bool ins_compl_prep(int c) /* Ignore end of Select mode mapping and mouse scroll buttons. */ if (c == K_SELECT || c == K_MOUSEDOWN || c == K_MOUSEUP - || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT) { + || c == K_MOUSELEFT || c == K_MOUSERIGHT || c == K_EVENT + || c == K_COMMAND) { return retval; } diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 4e0e3f6f1f..d67818aa81 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -2728,6 +2728,12 @@ void ex_call(exarg_T *eap) lnum = eap->line1; for (; lnum <= eap->line2; lnum++) { if (eap->addr_count > 0) { // -V560 + if (lnum > curbuf->b_ml.ml_line_count) { + // If the function deleted lines or switched to another buffer + // the line number may become invalid. + EMSG(_(e_invrange)); + break; + } curwin->w_cursor.lnum = lnum; curwin->w_cursor.col = 0; curwin->w_cursor.coladd = 0; @@ -8119,6 +8125,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) const int save_msg_silent = msg_silent; const int save_emsg_silent = emsg_silent; const bool save_emsg_noredir = emsg_noredir; + const bool save_redir_off = redir_off; garray_T *const save_capture_ga = capture_ga; if (check_secure()) { @@ -8146,6 +8153,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) garray_T capture_local; ga_init(&capture_local, (int)sizeof(char), 80); capture_ga = &capture_local; + redir_off = false; if (argvars[0].v_type != VAR_LIST) { do_cmdline_cmd(tv_get_string(&argvars[0])); @@ -8163,6 +8171,7 @@ static void f_execute(typval_T *argvars, typval_T *rettv, FunPtr fptr) msg_silent = save_msg_silent; emsg_silent = save_emsg_silent; emsg_noredir = save_emsg_noredir; + redir_off = save_redir_off; ga_append(capture_ga, NUL); rettv->v_type = VAR_STRING; @@ -10326,10 +10335,10 @@ static void f_getwininfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) tabnr++; int16_t winnr = 0; FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + winnr++; if (wparg != NULL && wp != wparg) { continue; } - winnr++; dict_T *const d = get_win_info(wp, tabnr, winnr); tv_list_append_dict(rettv->vval.v_list, d); if (wparg != NULL) { @@ -10793,17 +10802,6 @@ static void f_has(typval_T *argvars, typval_T *rettv, FunPtr fptr) n = true; } - if (STRICMP(name, "ruby") == 0 && n == true) { - char *rubyhost = call_func_retstr("provider#ruby#Detect", 0, NULL, true); - if (rubyhost) { - if (*rubyhost == NUL) { - // Invalid rubyhost executable. Gem is probably not installed. - n = false; - } - xfree(rubyhost); - } - } - rettv->vval.v_number = n; } @@ -11718,7 +11716,7 @@ static void f_jobstart(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (new_cwd && strlen(new_cwd) > 0) { cwd = new_cwd; // The new cwd must be a directory. - if (!os_isdir((char_u *)cwd)) { + if (!os_isdir_executable((const char *)cwd)) { EMSG2(_(e_invarg2), "expected valid directory"); shell_free_argv(argv); return; @@ -16763,7 +16761,7 @@ static void f_termopen(typval_T *argvars, typval_T *rettv, FunPtr fptr) if (new_cwd && *new_cwd != NUL) { cwd = new_cwd; // The new cwd must be a directory. - if (!os_isdir((const char_u *)cwd)) { + if (!os_isdir_executable((const char *)cwd)) { EMSG2(_(e_invarg2), "expected valid directory"); shell_free_argv(argv); return; @@ -19244,7 +19242,8 @@ static void set_var(const char *name, const size_t name_len, typval_T *const tv, } return; } else if (v->di_tv.v_type != tv->v_type) { - internal_error("set_var()"); + EMSG2(_("E963: setting %s to value with wrong type"), name); + return; } } @@ -19532,6 +19531,7 @@ void ex_echo(exarg_T *eap) typval_T rettv; bool needclr = true; bool atstart = true; + const int did_emsg_before = did_emsg; if (eap->skip) ++emsg_skip; @@ -19546,7 +19546,7 @@ void ex_echo(exarg_T *eap) // Report the invalid expression unless the expression evaluation // has been cancelled due to an aborting error, an interrupt, or an // exception. - if (!aborting()) { + if (!aborting() && did_emsg == did_emsg_before) { EMSG2(_(e_invexpr2), p); } need_clr_eos = false; @@ -19635,7 +19635,7 @@ void ex_execute(exarg_T *eap) int ret = OK; char_u *p; garray_T ga; - int save_did_emsg; + int save_did_emsg = did_emsg; ga_init(&ga, 1, 80); @@ -19649,8 +19649,9 @@ void ex_execute(exarg_T *eap) * has been cancelled due to an aborting error, an interrupt, or an * exception. */ - if (!aborting()) + if (!aborting() && did_emsg == save_did_emsg) { EMSG2(_(e_invexpr2), p); + } ret = FAIL; break; } @@ -22758,7 +22759,18 @@ bool eval_has_provider(const char *name) CHECK_PROVIDER(python); return has_python; } else if (strequal(name, "ruby")) { + bool need_check_ruby = (has_ruby == -1); CHECK_PROVIDER(ruby); + if (need_check_ruby && has_ruby == 1) { + char *rubyhost = call_func_retstr("provider#ruby#Detect", 0, NULL, true); + if (rubyhost) { + if (*rubyhost == NUL) { + // Invalid rubyhost executable. Gem is probably not installed. + has_ruby = 0; + } + xfree(rubyhost); + } + } return has_ruby; } diff --git a/src/nvim/eval/encode.c b/src/nvim/eval/encode.c index 2563e38258..e9ab6cd3e2 100644 --- a/src/nvim/eval/encode.c +++ b/src/nvim/eval/encode.c @@ -933,7 +933,7 @@ char *encode_tv2json(typval_T *tv, size_t *len) #define TYPVAL_ENCODE_CONV_BOOL(tv, num) \ do { \ - if ((num)) { \ + if (num) { \ msgpack_pack_true(packer); \ } else { \ msgpack_pack_false(packer); \ diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 6a93b20345..912aecafec 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -2850,7 +2850,7 @@ const char *tv_get_string_buf_chk(const typval_T *const tv, char *const buf) /// Get the string value of a "stringish" VimL object. /// /// @warning For number and special values it uses a single, static buffer. It -/// may be used only once, next call to get_tv_string may reuse it. Use +/// may be used only once, next call to tv_get_string may reuse it. Use /// tv_get_string_buf() if you need to use tv_get_string() output after /// calling it again. /// @@ -2869,7 +2869,7 @@ const char *tv_get_string_chk(const typval_T *const tv) /// Get the string value of a "stringish" VimL object. /// /// @warning For number and special values it uses a single, static buffer. It -/// may be used only once, next call to get_tv_string may reuse it. Use +/// may be used only once, next call to tv_get_string may reuse it. Use /// tv_get_string_buf() if you need to use tv_get_string() output after /// calling it again. /// diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index dc942eb0b3..36c4e333cf 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -816,10 +816,23 @@ int do_move(linenr_T line1, linenr_T line2, linenr_T dest) linenr_T last_line; // Last line in file after adding new text if (dest >= line1 && dest < line2) { - EMSG(_("E134: Move lines into themselves")); + EMSG(_("E134: Cannot move a range of lines into itself")); return FAIL; } + // Do nothing if we are not actually moving any lines. This will prevent + // the 'modified' flag from being set without cause. + if (dest == line1 - 1 || dest == line2) { + // Move the cursor as if lines were moved (see below) to be backwards + // compatible. + if (dest >= line1) { + curwin->w_cursor.lnum = dest; + } else { + curwin->w_cursor.lnum = dest + (line2 - line1) + 1; + } + return OK; + } + num_lines = line2 - line1 + 1; /* @@ -5672,6 +5685,14 @@ void ex_sign(exarg_T *eap) int len; arg += 5; + for (s = arg; s + 1 < p; s++) { + if (*s == '\\') { + // Remove a backslash, so that it is possible + // to use a space. + STRMOVE(s, s + 1); + p--; + } + } // Count cells and check for non-printable chars cells = 0; diff --git a/src/nvim/ex_cmds.lua b/src/nvim/ex_cmds.lua index df1aa938ed..79ca5363e0 100644 --- a/src/nvim/ex_cmds.lua +++ b/src/nvim/ex_cmds.lua @@ -28,15 +28,15 @@ local FILES = bit.bor(XFILE, EXTRA) local WORD1 = bit.bor(EXTRA, NOSPC) local FILE1 = bit.bor(FILES, NOSPC) -local ADDR_LINES = 0 -local ADDR_WINDOWS = 1 -local ADDR_ARGUMENTS = 2 -local ADDR_LOADED_BUFFERS = 3 -local ADDR_BUFFERS = 4 -local ADDR_TABS = 5 -local ADDR_TABS_RELATIVE = 6 -local ADDR_QUICKFIX = 7 -local ADDR_OTHER = 99 +local ADDR_LINES = 0 -- buffer line numbers +local ADDR_WINDOWS = 1 -- window number +local ADDR_ARGUMENTS = 2 -- argument number +local ADDR_LOADED_BUFFERS = 3 -- buffer number of loaded buffer +local ADDR_BUFFERS = 4 -- buffer number +local ADDR_TABS = 5 -- tab page number +local ADDR_TABS_RELATIVE = 6 -- Tab page that only relative +local ADDR_QUICKFIX = 7 -- quickfix list entry number +local ADDR_OTHER = 99 -- something else -- The following table is described in ex_cmds_defs.h file. return { @@ -1707,24 +1707,6 @@ return { func='ex_next', }, { - command='nbkey', - flags=bit.bor(EXTRA, NOTADR, NEEDARG), - addr_type=ADDR_LINES, - func='ex_ni', - }, - { - command='nbclose', - flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, - func='ex_ni', - }, - { - command='nbstart', - flags=bit.bor(WORD1, TRLBAR, CMDWIN), - addr_type=ADDR_LINES, - func='ex_ni', - }, - { command='new', flags=bit.bor(BANG, FILE1, RANGE, NOTADR, EDITCMD, ARGOPT, TRLBAR), addr_type=ADDR_LINES, @@ -1959,18 +1941,6 @@ return { func='ex_previous', }, { - command='promptfind', - flags=bit.bor(EXTRA, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, - func='ex_ni', - }, - { - command='promptrepl', - flags=bit.bor(EXTRA, NOTRLCOM, CMDWIN), - addr_type=ADDR_LINES, - func='ex_ni', - }, - { command='profile', flags=bit.bor(BANG, EXTRA, TRLBAR, CMDWIN), addr_type=ADDR_LINES, @@ -2326,8 +2296,8 @@ return { }, { command='scriptnames', - flags=bit.bor(TRLBAR, CMDWIN), - addr_type=ADDR_LINES, + flags=bit.bor(BANG, RANGE, NOTADR, COUNT, TRLBAR, CMDWIN), + addr_type=ADDR_OTHER, func='ex_scriptnames', }, { @@ -3117,12 +3087,6 @@ return { func='do_wqall', }, { - command='wsverb', - flags=bit.bor(EXTRA, NOTADR, NEEDARG), - addr_type=ADDR_LINES, - func='ex_ni', - }, - { command='wshada', flags=bit.bor(BANG, FILE1, TRLBAR, CMDWIN), addr_type=ADDR_LINES, diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 90fb7b8bc3..c9b6d19aaa 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -3069,6 +3069,17 @@ theend: /// ":scriptnames" void ex_scriptnames(exarg_T *eap) { + if (eap->addr_count > 0) { + // :script {scriptId}: edit the script + if (eap->line2 < 1 || eap->line2 > script_items.ga_len) { + EMSG(_(e_invarg)); + } else { + eap->arg = SCRIPT_ITEM(eap->line2).sn_name; + do_exedit(eap, NULL); + } + return; + } + for (int i = 1; i <= script_items.ga_len && !got_int; i++) { if (SCRIPT_ITEM(i).sn_name != NULL) { home_replace(NULL, SCRIPT_ITEM(i).sn_name, @@ -3506,7 +3517,12 @@ static char *get_locale_val(int what) } #endif - +// Return true when "lang" starts with a valid language name. +// Rejects NULL, empty string, "C", "C.UTF-8" and others. +static bool is_valid_mess_lang(char *lang) +{ + return lang != NULL && ASCII_ISALPHA(lang[0]) && ASCII_ISALPHA(lang[1]); +} /// Obtain the current messages language. Used to set the default for /// 'helplang'. May return NULL or an empty string. @@ -3526,14 +3542,14 @@ char *get_mess_lang(void) # endif # else p = os_getenv("LC_ALL"); - if (p == NULL) { + if (!is_valid_mess_lang(p)) { p = os_getenv("LC_MESSAGES"); - if (p == NULL) { + if (!is_valid_mess_lang(p)) { p = os_getenv("LANG"); } } # endif - return p; + return is_valid_mess_lang(p) ? p : NULL; } // Complicated #if; matches with where get_mess_env() is used below. @@ -3812,7 +3828,13 @@ static void script_host_execute(char *name, exarg_T *eap) // current range tv_list_append_number(args, (int)eap->line1); tv_list_append_number(args, (int)eap->line2); - (void)eval_call_provider(name, "execute", args); + + if (!eval_has_provider(name)) { + emsgf("E319: No \"%s\" provider found. Run \":checkhealth provider\"", + name); + } else { + (void)eval_call_provider(name, "execute", args); + } } } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 6ac7656a2f..4ef332186e 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -1804,15 +1804,19 @@ static char_u * do_one_cmd(char_u **cmdlinep, errormsg = (char_u *)_(get_text_locked_msg()); goto doend; } - /* Disallow editing another buffer when "curbuf_lock" is set. - * Do allow ":edit" (check for argument later). - * Do allow ":checktime" (it's postponed). */ + + // Disallow editing another buffer when "curbuf_lock" is set. + // Do allow ":checktime" (it is postponed). + // Do allow ":edit" (check for an argument later). + // Do allow ":file" with no arguments (check for an argument later). if (!(ea.argt & CMDWIN) - && ea.cmdidx != CMD_edit && ea.cmdidx != CMD_checktime + && ea.cmdidx != CMD_edit + && ea.cmdidx != CMD_file && !IS_USER_CMDIDX(ea.cmdidx) - && curbuf_locked()) + && curbuf_locked()) { goto doend; + } if (!ni && !(ea.argt & RANGE) && ea.addr_count > 0) { /* no range allowed */ @@ -1884,6 +1888,11 @@ static char_u * do_one_cmd(char_u **cmdlinep, else ea.arg = skipwhite(p); + // ":file" cannot be run with an argument when "curbuf_lock" is set + if (ea.cmdidx == CMD_file && *ea.arg != NUL && curbuf_locked()) { + goto doend; + } + /* * Check for "++opt=val" argument. * Must be first, allow ":w ++enc=utf8 !cmd" @@ -8645,7 +8654,10 @@ eval_vars ( break; case SPEC_AFILE: // file name for autocommand - if (autocmd_fname != NULL && !path_is_absolute(autocmd_fname)) { + if (autocmd_fname != NULL + && !path_is_absolute(autocmd_fname) + // For CmdlineEnter and related events, <afile> is not a path! #9348 + && !strequal("/", (char *)autocmd_fname)) { // Still need to turn the fname into a full path. It was // postponed to avoid a delay when <afile> is not used. result = (char_u *)FullName_save((char *)autocmd_fname, false); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 19a52c913a..bfc32887ca 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -1804,6 +1804,37 @@ static int empty_pattern(char_u *p) static int command_line_changed(CommandLineState *s) { + // Trigger CmdlineChanged autocommands. + if (has_event(EVENT_CMDLINECHANGED)) { + TryState tstate; + Error err = ERROR_INIT; + bool tl_ret = true; + dict_T *dict = get_vim_var_dict(VV_EVENT); + + char firstcbuf[2]; + firstcbuf[0] = s->firstc > 0 ? s->firstc : '-'; + firstcbuf[1] = 0; + + // set v:event to a dictionary with information about the commandline + tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf); + tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level); + tv_dict_set_keys_readonly(dict); + try_enter(&tstate); + + apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf, + (char_u *)firstcbuf, false, curbuf); + tv_dict_clear(dict); + + tl_ret = try_leave(&tstate, &err); + if (!tl_ret && ERROR_SET(&err)) { + msg_putchar('\n'); + msg_printf_attr(HL_ATTR(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg); + api_clear_error(&err); + redrawcmd(); + } + tl_ret = true; + } + // 'incsearch' highlighting. if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) { pos_T end_pos; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index d0e30ddbd3..fe12a69801 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -1739,13 +1739,13 @@ failed: close(0); #ifndef WIN32 // On Unix, use stderr for stdin, makes shell commands work. - ignored = dup(2); + vim_ignored = dup(2); #else // On Windows, use the console input handle for stdin. HANDLE conin = CreateFile("CONIN$", GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, 0, (HANDLE)NULL); - ignored = _open_osfhandle(conin, _O_RDONLY); + vim_ignored = _open_osfhandle(conin, _O_RDONLY); #endif } @@ -4531,48 +4531,83 @@ bool vim_fgets(char_u *buf, int size, FILE *fp) FUNC_ATTR_NONNULL_ALL } /// Read 2 bytes from "fd" and turn them into an int, MSB first. +/// Returns -1 when encountering EOF. int get2c(FILE *fd) { - int n; - - n = getc(fd); - n = (n << 8) + getc(fd); - return n; + const int n = getc(fd); + if (n == EOF) { + return -1; + } + const int c = getc(fd); + if (c == EOF) { + return -1; + } + return (n << 8) + c; } /// Read 3 bytes from "fd" and turn them into an int, MSB first. +/// Returns -1 when encountering EOF. int get3c(FILE *fd) { - int n; - - n = getc(fd); - n = (n << 8) + getc(fd); - n = (n << 8) + getc(fd); - return n; + int n = getc(fd); + if (n == EOF) { + return -1; + } + int c = getc(fd); + if (c == EOF) { + return -1; + } + n = (n << 8) + c; + c = getc(fd); + if (c == EOF) { + return -1; + } + return (n << 8) + c; } /// Read 4 bytes from "fd" and turn them into an int, MSB first. +/// Returns -1 when encountering EOF. int get4c(FILE *fd) { // Use unsigned rather than int otherwise result is undefined // when left-shift sets the MSB. unsigned n; - n = (unsigned)getc(fd); - n = (n << 8) + (unsigned)getc(fd); - n = (n << 8) + (unsigned)getc(fd); - n = (n << 8) + (unsigned)getc(fd); + int c = getc(fd); + if (c == EOF) { + return -1; + } + n = (unsigned)c; + c = getc(fd); + if (c == EOF) { + return -1; + } + n = (n << 8) + (unsigned)c; + c = getc(fd); + if (c == EOF) { + return -1; + } + n = (n << 8) + (unsigned)c; + c = getc(fd); + if (c == EOF) { + return -1; + } + n = (n << 8) + (unsigned)c; return (int)n; } /// Read 8 bytes from `fd` and turn them into a time_t, MSB first. +/// Returns -1 when encountering EOF. time_t get8ctime(FILE *fd) { time_t n = 0; - int i; - for (i = 0; i < 8; i++) { - n = (n << 8) + getc(fd); + for (int i = 0; i < 8; i++) { + const int c = getc(fd); + if (c == EOF) { + return -1; + } + n = (n << 8) + c; } return n; } @@ -5932,19 +5967,19 @@ void au_event_restore(char_u *old_ei) * will be automatically executed for <event> * when editing a file matching <pat>, in * the current group. - * :autocmd <event> <pat> Show the auto-commands associated with + * :autocmd <event> <pat> Show the autocommands associated with * <event> and <pat>. - * :autocmd <event> Show the auto-commands associated with + * :autocmd <event> Show the autocommands associated with * <event>. - * :autocmd Show all auto-commands. - * :autocmd! <event> <pat> <cmd> Remove all auto-commands associated with + * :autocmd Show all autocommands. + * :autocmd! <event> <pat> <cmd> Remove all autocommands associated with * <event> and <pat>, and add the command * <cmd>, for the current group. - * :autocmd! <event> <pat> Remove all auto-commands associated with + * :autocmd! <event> <pat> Remove all autocommands associated with * <event> and <pat> for the current group. - * :autocmd! <event> Remove all auto-commands associated with + * :autocmd! <event> Remove all autocommands associated with * <event> for the current group. - * :autocmd! Remove ALL auto-commands for the current + * :autocmd! Remove ALL autocommands for the current * group. * * Multiple events and patterns may be given separated by commas. Here are @@ -6037,8 +6072,8 @@ void do_autocmd(char_u *arg_in, int forceit) * Print header when showing autocommands. */ if (!forceit && *cmd == NUL) { - /* Highlight title */ - MSG_PUTS_TITLE(_("\n--- Auto-Commands ---")); + // Highlight title + MSG_PUTS_TITLE(_("\n--- Autocommands ---")); } /* @@ -6868,7 +6903,13 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, } else { sfname = vim_strsave(fname); // Don't try expanding the following events. - if (event == EVENT_COLORSCHEME + if (event == EVENT_CMDLINECHANGED + || event == EVENT_CMDLINEENTER + || event == EVENT_CMDLINELEAVE + || event == EVENT_CMDWINENTER + || event == EVENT_CMDWINLEAVE + || event == EVENT_CMDUNDEFINED + || event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE || event == EVENT_DIRCHANGED || event == EVENT_FILETYPE @@ -6907,8 +6948,8 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io, autocmd_match = fname; - /* Don't redraw while doing auto commands. */ - ++RedrawingDisabled; + // Don't redraw while doing autocommands. + RedrawingDisabled++; save_sourcing_name = sourcing_name; sourcing_name = NULL; /* don't free this one */ save_sourcing_lnum = sourcing_lnum; @@ -7119,7 +7160,7 @@ auto_next_pat ( apc->tail, ap->allow_dirs) : ap->buflocal_nr == apc->arg_bufnr) { const char *const name = event_nr2name(apc->event); - s = _("%s Auto commands for \"%s\""); + s = _("%s Autocommands for \"%s\""); const size_t sourcing_name_len = (STRLEN(s) + strlen(name) + ap->patlen + 1); sourcing_name = xmalloc(sourcing_name_len); diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 53a3218c51..39975308d7 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -171,9 +171,8 @@ bool hasFoldingWin( int low_level = 0; checkupdate(win); - /* - * Return quickly when there is no folding at all in this window. - */ + + // Return quickly when there is no folding at all in this window. if (!hasAnyFolding(win)) { if (infop != NULL) infop->fi_level = 0; @@ -2851,8 +2850,9 @@ static void foldlevelIndent(fline_T *flp) flp->lvl = 0; else flp->lvl = -1; - } else - flp->lvl = get_indent_buf(buf, lnum) / get_sw_value(curbuf); + } else { + flp->lvl = get_indent_buf(buf, lnum) / get_sw_value(buf); + } if (flp->lvl > flp->wp->w_p_fdn) { flp->lvl = (int) MAX(0, flp->wp->w_p_fdn); } diff --git a/src/nvim/globals.h b/src/nvim/globals.h index df74e8b2ff..04ff1320ce 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1125,8 +1125,7 @@ EXTERN FILE *time_fd INIT(= NULL); /* where to write startup timing */ * can't do anything useful with the value. Assign to this variable to avoid * the warning. */ -EXTERN int ignored; -EXTERN char *ignoredp; +EXTERN int vim_ignored; // Start a msgpack-rpc channel over stdin/stdout. EXTERN bool embedded_mode INIT(= false); diff --git a/src/nvim/highlight.c b/src/nvim/highlight.c index a104137d9e..5a9727f46e 100644 --- a/src/nvim/highlight.c +++ b/src/nvim/highlight.c @@ -177,8 +177,27 @@ void update_window_hl(win_T *wp, bool invalid) } } +/// Gets HL_UNDERLINE highlight. +int hl_get_underline(void) +{ + return get_attr_entry((HlEntry){ + .attr = (HlAttrs){ + .cterm_ae_attr = (int16_t)HL_UNDERLINE, + .cterm_fg_color = 0, + .cterm_bg_color = 0, + .rgb_ae_attr = (int16_t)HL_UNDERLINE, + .rgb_fg_color = -1, + .rgb_bg_color = -1, + .rgb_sp_color = -1, + }, + .kind = kHlUI, + .id1 = 0, + .id2 = 0, + }); +} + /// Get attribute code for forwarded :terminal highlights. -int get_term_attr_entry(HlAttrs *aep) +int hl_get_term_attr(HlAttrs *aep) { return get_attr_entry((HlEntry){ .attr= *aep, .kind = kHlTerminal, .id1 = 0, .id2 = 0 }); diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index 09d20c75ea..40025fcbbb 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -22,7 +22,7 @@ typedef enum { /// Stores a complete highlighting entry, including colors and attributes /// for both TUI and GUI. typedef struct attr_entry { - int16_t rgb_ae_attr, cterm_ae_attr; // HL_BOLD, etc. + int16_t rgb_ae_attr, cterm_ae_attr; ///< HlAttrFlags RgbValue rgb_fg_color, rgb_bg_color, rgb_sp_color; int cterm_fg_color, cterm_bg_color; } HlAttrs; @@ -37,17 +37,6 @@ typedef struct attr_entry { .cterm_bg_color = 0, \ } -// sentinel value that compares unequal to any valid highlight -#define HLATTRS_INVALID (HlAttrs) { \ - .rgb_ae_attr = -1, \ - .cterm_ae_attr = -1, \ - .rgb_fg_color = -1, \ - .rgb_bg_color = -1, \ - .rgb_sp_color = -1, \ - .cterm_fg_color = 0, \ - .cterm_bg_color = 0, \ -} - /// Values for index in highlight_attr[]. /// When making changes, also update hlf_names below! typedef enum { @@ -94,8 +83,8 @@ typedef enum { , HLF_TP // tabpage line , HLF_TPS // tabpage line selected , HLF_TPF // tabpage line filler - , HLF_CUC // 'cursurcolumn' - , HLF_CUL // 'cursurline' + , HLF_CUC // 'cursorcolumn' + , HLF_CUL // 'cursorline' , HLF_MC // 'colorcolumn' , HLF_QFL // selected quickfix line , HLF_0 // Whitespace diff --git a/src/nvim/main.c b/src/nvim/main.c index af54e62393..8a40577e8f 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -73,6 +73,7 @@ #ifndef WIN32 # include "nvim/os/pty_process_unix.h" #endif +#include "nvim/api/vim.h" // Maximum number of commands from + or -c arguments. #define MAX_ARG_CMDS 10 @@ -150,6 +151,8 @@ void event_init(void) signal_init(); // finish mspgack-rpc initialization channel_init(); + remote_ui_init(); + api_vim_init(); terminal_init(); } @@ -1719,6 +1722,48 @@ static void exe_commands(mparm_T *parmp) TIME_MSG("executing command arguments"); } +/// Source system-wide vimrc if built with one defined +/// +/// Does one of the following things, stops after whichever succeeds: +/// +/// 1. Source system vimrc file from $XDG_CONFIG_DIRS/nvim/sysinit.vim +/// 2. Source system vimrc file from $VIM +static void do_system_initialization(void) +{ + char *const config_dirs = stdpaths_get_xdg_var(kXDGConfigDirs); + if (config_dirs != NULL) { + const void *iter = NULL; + const char path_tail[] = { + 'n', 'v', 'i', 'm', PATHSEP, + 's', 'y', 's', 'i', 'n', 'i', 't', '.', 'v', 'i', 'm', NUL + }; + do { + const char *dir; + size_t dir_len; + iter = vim_env_iter(':', config_dirs, iter, &dir, &dir_len); + if (dir == NULL || dir_len == 0) { + break; + } + char *vimrc = xmalloc(dir_len + sizeof(path_tail) + 1); + memcpy(vimrc, dir, dir_len); + vimrc[dir_len] = PATHSEP; + memcpy(vimrc + dir_len + 1, path_tail, sizeof(path_tail)); + if (do_source((char_u *)vimrc, false, DOSO_NONE) != FAIL) { + xfree(vimrc); + xfree(config_dirs); + return; + } + xfree(vimrc); + } while (iter != NULL); + xfree(config_dirs); + } + +#ifdef SYS_VIMRC_FILE + // Get system wide defaults, if the file name is defined. + (void)do_source((char_u *)SYS_VIMRC_FILE, false, DOSO_NONE); +#endif +} + /// Source vimrc or do other user initialization /// /// Does one of the following things, stops after whichever succeeds: @@ -1801,10 +1846,7 @@ static void source_startup_scripts(const mparm_T *const parmp) } } } else if (!silent_mode) { -#ifdef SYS_VIMRC_FILE - // Get system wide defaults, if the file name is defined. - (void) do_source((char_u *)SYS_VIMRC_FILE, false, DOSO_NONE); -#endif + do_system_initialization(); if (do_user_initialization()) { // Read initialization commands from ".vimrc" or ".exrc" in current diff --git a/src/nvim/map.c b/src/nvim/map.c index cc264f3729..53ab734802 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -166,3 +166,4 @@ MAP_IMPL(handle_T, ptr_t, DEFAULT_INITIALIZER) MAP_IMPL(String, MsgpackRpcRequestHandler, MSGPACK_HANDLER_INITIALIZER) #define KVEC_INITIALIZER { .size = 0, .capacity = 0, .items = NULL } MAP_IMPL(HlEntry, int, DEFAULT_INITIALIZER) +MAP_IMPL(String, handle_T, 0) diff --git a/src/nvim/map.h b/src/nvim/map.h index 65204a798b..0e4308b953 100644 --- a/src/nvim/map.h +++ b/src/nvim/map.h @@ -37,6 +37,7 @@ MAP_DECLS(uint64_t, ptr_t) MAP_DECLS(handle_T, ptr_t) MAP_DECLS(String, MsgpackRpcRequestHandler) MAP_DECLS(HlEntry, int) +MAP_DECLS(String, handle_T) #define map_new(T, U) map_##T##_##U##_new #define map_free(T, U) map_##T##_##U##_free diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 8789075c44..6b96a3b070 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -16,6 +16,7 @@ #include "nvim/message.h" #include "nvim/misc1.h" #include "nvim/ui.h" +#include "nvim/api/vim.h" #ifdef HAVE_JEMALLOC // Force je_ prefix on jemalloc functions. @@ -681,6 +682,7 @@ void free_all_mem(void) break; eval_clear(); + api_vim_free_all_mem(); // Free all buffers. Reset 'autochdir' to avoid accessing things that // were freed already. diff --git a/src/nvim/message.c b/src/nvim/message.c index edce30e6fa..6de81a8aaf 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -505,7 +505,7 @@ int emsg(const char_u *s_) */ if (cause_errthrow((char_u *)s, severe, &ignore) == true) { if (!ignore) { - did_emsg = true; + did_emsg++; } return true; } @@ -554,7 +554,7 @@ int emsg(const char_u *s_) } else { flush_buffers(FLUSH_MINIMAL); // flush internal buffers } - did_emsg = true; // flag for DoOneCmd() + did_emsg++; // flag for DoOneCmd() } emsg_on_display = true; // remember there is an error message @@ -1896,6 +1896,9 @@ static void msg_scroll_up(void) } else { screen_del_lines(0, 1, (int)Rows, 0, Columns); } + // TODO(bfredl): when msgsep display is properly batched, this fill should be + // eliminated. + screen_fill(Rows-1, Rows, 0, (int)Columns, ' ', ' ', 0); } /* @@ -2311,6 +2314,7 @@ static int do_more_prompt(int typed_char) if (toscroll == -1 && screen_ins_lines(0, 1, (int)Rows, 0, (int)Columns) == OK) { + screen_fill(0, 1, 0, (int)Columns, ' ', ' ', 0); // display line at top (void)disp_sb_line(0, mp); } else { @@ -2821,7 +2825,6 @@ do_dialog ( Ex command */ ) { - int oldState; int retval = 0; char_u *hotkeys; int c; @@ -2834,7 +2837,10 @@ do_dialog ( } - oldState = State; + int save_msg_silent = msg_silent; + int oldState = State; + + msg_silent = 0; // If dialog prompts for input, user needs to see it! #8788 State = CONFIRM; setmouse(); @@ -2887,6 +2893,7 @@ do_dialog ( xfree(hotkeys); + msg_silent = save_msg_silent; State = oldState; setmouse(); --no_wait_return; diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 4032210213..ffe2d11f08 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -52,6 +52,7 @@ #include "nvim/window.h" #include "nvim/os/os.h" #include "nvim/os/shell.h" +#include "nvim/os/signal.h" #include "nvim/os/input.h" #include "nvim/os/time.h" #include "nvim/event/stream.h" @@ -1121,8 +1122,9 @@ int get_last_leader_offset(char_u *line, char_u **flags) if (ascii_iswhite(string[0])) { if (i == 0 || !ascii_iswhite(line[i - 1])) continue; - while (ascii_iswhite(string[0])) - ++string; + while (ascii_iswhite(*string)) { + string++; + } } for (j = 0; string[j] != NUL && string[j] == line[i + j]; ++j) /* do nothing */; @@ -1138,6 +1140,19 @@ int get_last_leader_offset(char_u *line, char_u **flags) continue; } + if (vim_strchr(part_buf, COM_MIDDLE) != NULL) { + // For a middlepart comment, only consider it to match if + // everything before the current position in the line is + // whitespace. Otherwise we would think we are inside a + // comment if the middle part appears somewhere in the middle + // of the line. E.g. for C the "*" appears often. + for (j = 0; ascii_iswhite(line[j]) && j <= i; j++) { + } + if (j < i) { + continue; + } + } + /* * We have found a match, stop searching. */ @@ -1936,10 +1951,10 @@ changed_lines( { changed_lines_buf(curbuf, lnum, lnume, xtra); - if (xtra == 0 && curwin->w_p_diff) { - /* When the number of lines doesn't change then mark_adjust() isn't - * called and other diff buffers still need to be marked for - * displaying. */ + if (xtra == 0 && curwin->w_p_diff && !diff_internal()) { + // When the number of lines doesn't change then mark_adjust() isn't + // called and other diff buffers still need to be marked for + // displaying. linenr_T wlnum; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { @@ -2008,6 +2023,10 @@ static void changed_common(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra /* mark the buffer as modified */ changed(); + if (curwin->w_p_diff && diff_internal()) { + curtab->tp_diff_update = true; + } + /* set the '. mark */ if (!cmdmod.keepjumps) { RESET_FMARK(&curbuf->b_last_change, ((pos_T) {lnum, col, 0}), 0); @@ -2653,6 +2672,8 @@ void preserve_exit(void) } really_exiting = true; + // Ignore SIGHUP while we are already exiting. #9274 + signal_reject_deadly(); mch_errmsg(IObuff); mch_errmsg("\n"); ui_flush(); diff --git a/src/nvim/move.c b/src/nvim/move.c index bddcefc8ec..3a29851ee6 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -918,9 +918,9 @@ void curs_columns( extra = ((int)prev_skipcol - (int)curwin->w_skipcol) / width; if (extra > 0) { - win_ins_lines(curwin, 0, extra, false); + win_ins_lines(curwin, 0, extra); } else if (extra < 0) { - win_del_lines(curwin, 0, -extra, false); + win_del_lines(curwin, 0, -extra); } } else { curwin->w_skipcol = 0; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 3356cdc61e..46a9e95b91 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -641,7 +641,16 @@ static WBuffer *serialize_response(uint64_t channel_id, { msgpack_packer pac; msgpack_packer_init(&pac, sbuffer, msgpack_sbuffer_write); - msgpack_rpc_serialize_response(response_id, err, arg, &pac); + if (ERROR_SET(err) && response_id == NO_RESPONSE) { + Array args = ARRAY_DICT_INIT; + ADD(args, INTEGER_OBJ(err->type)); + ADD(args, STRING_OBJ(cstr_to_string(err->msg))); + msgpack_rpc_serialize_request(0, cstr_as_string("nvim_error_event"), + args, &pac); + api_free_array(args); + } else { + msgpack_rpc_serialize_response(response_id, err, arg, &pac); + } log_server_msg(channel_id, sbuffer); WBuffer *rv = wstream_new_buffer(xmemdup(sbuffer->data, sbuffer->size), sbuffer->size, diff --git a/src/nvim/normal.c b/src/nvim/normal.c index f87de52a82..38ee0936aa 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -1325,6 +1325,14 @@ static int normal_check(VimState *state) normal_check_cursor_moved(s); normal_check_text_changed(s); + // Updating diffs from changed() does not always work properly, + // esp. updating folds. Do an update just before redrawing if + // needed. + if (curtab->tp_diff_update || curtab->tp_diff_invalid) { + ex_diffupdate(NULL); + curtab->tp_diff_update = false; + } + // Scroll-binding for diff mode may have been postponed until // here. Avoids doing it for every change. if (diff_need_scrollbind) { diff --git a/src/nvim/option.c b/src/nvim/option.c index a0fb2d9e36..11f3df7cfa 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -1068,6 +1068,10 @@ void set_helplang_default(const char *lang) if (STRNICMP(p_hlg, "zh_", 3) == 0 && STRLEN(p_hlg) >= 5) { p_hlg[0] = (char_u)TOLOWER_ASC(p_hlg[3]); p_hlg[1] = (char_u)TOLOWER_ASC(p_hlg[4]); + } else if (STRLEN(p_hlg) >= 1 && *p_hlg == 'C') { + // any C like setting, such as C.UTF-8, becomes "en" + p_hlg[0] = 'e'; + p_hlg[1] = 'n'; } p_hlg[2] = NUL; options[idx].flags |= P_ALLOCED; @@ -1769,14 +1773,13 @@ do_set ( // Set the new value. *(char_u **)(varp) = newval; - if (!starting && origval != NULL && newval != NULL) { - // origval may be freed by - // did_set_string_option(), make a copy. - saved_origval = xstrdup((char *)origval); - // newval (and varp) may become invalid if the - // buffer is closed by autocommands. - saved_newval = xstrdup((char *)newval); - } + // origval may be freed by + // did_set_string_option(), make a copy. + saved_origval = (origval != NULL) ? xstrdup((char *)origval) : 0; + + // newval (and varp) may become invalid if the + // buffer is closed by autocommands. + saved_newval = (newval != NULL) ? xstrdup((char *)newval) : 0; // Handle side effects, and set the global value for // ":set" on local options. Note: when setting 'syntax' @@ -1786,8 +1789,14 @@ do_set ( new_value_alloced, oldval, errbuf, opt_flags); if (errmsg == NULL) { - trigger_optionsset_string(opt_idx, opt_flags, saved_origval, - saved_newval); + if (!starting) { + trigger_optionsset_string(opt_idx, opt_flags, saved_origval, + saved_newval); + } + if (options[opt_idx].flags & P_UI_OPTION) { + ui_call_option_set(cstr_as_string(options[opt_idx].fullname), + STRING_OBJ(cstr_as_string(saved_newval))); + } } xfree(saved_origval); xfree(saved_newval); @@ -2378,8 +2387,8 @@ static char *set_string_option(const int opt_idx, const char *const value, char *const oldval = *varp; *varp = s; - char *const saved_oldval = (starting ? NULL : xstrdup(oldval)); - char *const saved_newval = (starting ? NULL : xstrdup(s)); + char *const saved_oldval = xstrdup(oldval); + char *const saved_newval = xstrdup(s); char *const r = (char *)did_set_string_option( opt_idx, (char_u **)varp, (int)true, (char_u *)oldval, NULL, opt_flags); @@ -2389,8 +2398,13 @@ static char *set_string_option(const int opt_idx, const char *const value, // call autocommand after handling side effects if (r == NULL) { - trigger_optionsset_string(opt_idx, opt_flags, - saved_oldval, saved_newval); + if (!starting) { + trigger_optionsset_string(opt_idx, opt_flags, saved_oldval, saved_newval); + } + if (options[opt_idx].flags & P_UI_OPTION) { + ui_call_option_set(cstr_as_string(options[opt_idx].fullname), + STRING_OBJ(cstr_as_string(saved_newval))); + } } xfree(saved_oldval); xfree(saved_newval); @@ -4056,10 +4070,11 @@ static char *set_bool_option(const int opt_idx, char_u *const varp, (char_u *) options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); - if (options[opt_idx].flags & P_UI_OPTION) { - ui_call_option_set(cstr_as_string(options[opt_idx].fullname), - BOOLEAN_OBJ(value)); - } + } + + if (options[opt_idx].flags & P_UI_OPTION) { + ui_call_option_set(cstr_as_string(options[opt_idx].fullname), + BOOLEAN_OBJ(value)); } comp_col(); /* in case 'ruler' or 'showcmd' changed */ @@ -4429,10 +4444,11 @@ static char *set_num_option(int opt_idx, char_u *varp, long value, (char_u *) options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); - if (options[opt_idx].flags & P_UI_OPTION) { - ui_call_option_set(cstr_as_string(options[opt_idx].fullname), - INTEGER_OBJ(value)); - } + } + + if (errmsg == NULL && options[opt_idx].flags & P_UI_OPTION) { + ui_call_option_set(cstr_as_string(options[opt_idx].fullname), + INTEGER_OBJ(value)); } comp_col(); /* in case 'columns' or 'ls' changed */ @@ -4461,10 +4477,6 @@ static void trigger_optionsset_string(int opt_idx, int opt_flags, apply_autocmds(EVENT_OPTIONSET, (char_u *)options[opt_idx].fullname, NULL, false, NULL); reset_v_option_vars(); - if (options[opt_idx].flags & P_UI_OPTION) { - ui_call_option_set(cstr_as_string(options[opt_idx].fullname), - STRING_OBJ(cstr_as_string(newval))); - } } } diff --git a/src/nvim/options.lua b/src/nvim/options.lua index bc7f1a2b0a..0cc6f58c5f 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -132,10 +132,10 @@ return { { full_name='background', abbreviation='bg', type='string', scope={'global'}, - vi_def=true, + vim=true, redraw={'all_windows'}, varname='p_bg', - defaults={if_true={vi="light"}} + defaults={if_true={vi="light",vim="dark"}} }, { full_name='backspace', abbreviation='bs', @@ -615,7 +615,7 @@ return { alloced=true, redraw={'current_window'}, varname='p_dip', - defaults={if_true={vi="filler"}} + defaults={if_true={vi="internal,filler"}} }, { full_name='digraph', abbreviation='dg', diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index cf00fd4f82..9a4391a0ae 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -110,7 +110,7 @@ bool os_isrealdir(const char *name) /// Check if the given path is a directory or not. /// -/// @return `true` if `fname` is a directory. +/// @return `true` if `name` is a directory. bool os_isdir(const char_u *name) FUNC_ATTR_NONNULL_ALL { @@ -126,6 +126,25 @@ bool os_isdir(const char_u *name) return true; } +/// Check if the given path is a directory and is executable. +/// Gives the same results as `os_isdir()` on Windows. +/// +/// @return `true` if `name` is a directory and executable. +bool os_isdir_executable(const char *name) + FUNC_ATTR_NONNULL_ALL +{ + int32_t mode = os_getperm((const char *)name); + if (mode < 0) { + return false; + } + +#ifdef WIN32 + return (S_ISDIR(mode)); +#else + return (S_ISDIR(mode) && (S_IXUSR & mode)); +#endif +} + /// Check what `name` is: /// @return NODE_NORMAL: file or directory (or doesn't exist) /// NODE_WRITABLE: writable device, socket, fifo, etc. diff --git a/src/nvim/os/lang.c b/src/nvim/os/lang.c index 6b2a54ddbe..108a9c6c39 100644 --- a/src/nvim/os/lang.c +++ b/src/nvim/os/lang.c @@ -17,14 +17,31 @@ void lang_init(void) { #ifdef __APPLE__ if (os_getenv("LANG") == NULL) { + const char *lang_region = NULL; + CFTypeRef cf_lang_region = NULL; + CFLocaleRef cf_locale = CFLocaleCopyCurrent(); - CFTypeRef cf_lang_region = CFLocaleGetValue(cf_locale, - kCFLocaleIdentifier); - CFRetain(cf_lang_region); - CFRelease(cf_locale); + if (cf_locale) { + cf_lang_region = CFLocaleGetValue(cf_locale, kCFLocaleIdentifier); + CFRetain(cf_lang_region); + lang_region = CFStringGetCStringPtr(cf_lang_region, + kCFStringEncodingUTF8); + CFRelease(cf_locale); + } else { + // Use the primary language defined in Preferences -> Language & Region + CFArrayRef cf_langs = CFLocaleCopyPreferredLanguages(); + if (cf_langs && CFArrayGetCount(cf_langs) > 0) { + cf_lang_region = CFArrayGetValueAtIndex(cf_langs, 0); + CFRetain(cf_lang_region); + CFRelease(cf_langs); + lang_region = CFStringGetCStringPtr(cf_lang_region, + kCFStringEncodingUTF8); + } else { + ELOG("$LANG is empty and your primary language cannot be inferred."); + return; + } + } - const char *lang_region = CFStringGetCStringPtr(cf_lang_region, - kCFStringEncodingUTF8); if (lang_region) { os_setenv("LANG", lang_region, true); } else { @@ -37,6 +54,11 @@ void lang_init(void) CFRelease(cf_lang_region); # ifdef HAVE_LOCALE_H setlocale(LC_ALL, ""); + +# ifdef LC_NUMERIC + // Make sure strtod() uses a decimal point, not a comma. + setlocale(LC_NUMERIC, "C"); +# endif # endif } #endif diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index bafbfe1e4b..bcf57e1b5b 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -12,7 +12,7 @@ #include <sys/ioctl.h> // forkpty is not in POSIX, so headers are platform-specific -#if defined(__FreeBSD__) || defined (__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) # include <libutil.h> #elif defined(__OpenBSD__) || defined(__NetBSD__) || defined(__APPLE__) # include <util.h> diff --git a/src/nvim/po/CMakeLists.txt b/src/nvim/po/CMakeLists.txt index 6811f99add..3a70264dd1 100644 --- a/src/nvim/po/CMakeLists.txt +++ b/src/nvim/po/CMakeLists.txt @@ -8,6 +8,7 @@ if(NOT LANGUAGES) af ca cs + da de en_GB eo diff --git a/src/nvim/po/da.po b/src/nvim/po/da.po new file mode 100644 index 0000000000..58cd19210b --- /dev/null +++ b/src/nvim/po/da.po @@ -0,0 +1,7088 @@ +# Danish translation for Vim +# Copyright (C) 2018 The Vim authors +# This file is distributed under the same license as the vim package. +# scootergrisen, 2018. +msgid "" +msgstr "" +"Project-Id-Version: Vim 8.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-06-08 22:09+0200\n" +"PO-Revision-Date: 2018-06-23 23:30+0200\n" +"Last-Translator: scootergrisen\n" +"Language-Team: Danish\n" +"Language: da\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "E831: bf_key_init() called with empty password" +msgstr "E831: bf_key_init() kaldt med tom adgangskode" + +msgid "E820: sizeof(uint32_t) != 4" +msgstr "E820: sizeof(uint32_t) != 4" + +msgid "E817: Blowfish big/little endian use wrong" +msgstr "E817: Forkert brug af stor/lille byterækkefølge for blowfish" + +msgid "E818: sha256 test failed" +msgstr "E818: sha256-test mislykkede" + +msgid "E819: Blowfish test failed" +msgstr "E819: Blowfish-test mislykkede" + +msgid "[Location List]" +msgstr "[Placeringsliste]" + +msgid "[Quickfix List]" +msgstr "[Quickfix-liste]" + +msgid "E855: Autocommands caused command to abort" +msgstr "E855: Autokommandoer forÃ¥rsagede afbrydelse af kommando" + +msgid "E82: Cannot allocate any buffer, exiting..." +msgstr "E82: Kan ikke allokere buffer, afslutter..." + +msgid "E83: Cannot allocate buffer, using other one..." +msgstr "E83: Kan ikke allokere buffer, bruger en anden..." + +msgid "E931: Buffer cannot be registered" +msgstr "E931: Buffer kan ikke registreres" + +msgid "E937: Attempt to delete a buffer that is in use" +msgstr "E937: Forsøg pÃ¥ at slette en buffer som er i brug" + +msgid "E515: No buffers were unloaded" +msgstr "E515: Ingen buffere blev udlæst" + +msgid "E516: No buffers were deleted" +msgstr "E516: Ingen brugere blev slettet" + +msgid "E517: No buffers were wiped out" +msgstr "E517: Ingen buffere blev ryddet" + +msgid "1 buffer unloaded" +msgstr "1 buffer udlæst" + +#, c-format +msgid "%d buffers unloaded" +msgstr "%d buffere udlæst" + +msgid "1 buffer deleted" +msgstr "1 buffer slettet" + +#, c-format +msgid "%d buffers deleted" +msgstr "%d buffere slettet" + +msgid "1 buffer wiped out" +msgstr "1 buffer ryddet" + +#, c-format +msgid "%d buffers wiped out" +msgstr "%d buffere ryddet" + +msgid "E90: Cannot unload last buffer" +msgstr "E90: Kan ikke udlæse sidste buffer" + +msgid "E84: No modified buffer found" +msgstr "E84: Fandt ingen ændret buffer" + +msgid "E85: There is no listed buffer" +msgstr "E85: Der er ingen oplistet buffer" + +msgid "E87: Cannot go beyond last buffer" +msgstr "E87: Kan ikke gÃ¥ over sidste buffer" + +msgid "E88: Cannot go before first buffer" +msgstr "E88: Kan ikke gÃ¥ før første buffer" + +#, c-format +msgid "E89: No write since last change for buffer %ld (add ! to override)" +msgstr "" +"E89: Ingen skrivning siden sidste ændring for bufferen %ld (tilføj ! for at " +"tilsidesætte)" + +msgid "E948: Job still running (add ! to end the job)" +msgstr "E948: Job kører stadig (tilføj ! for at afslutte jobbet)" + +msgid "E37: No write since last change (add ! to override)" +msgstr "" +"E37: Ingen skrivning siden sidste ændring (tilføj ! for at tilsidesætte)" + +msgid "E948: Job still running" +msgstr "E948: Job kører stadig" + +msgid "E37: No write since last change" +msgstr "E37: Ingen skrivning siden sidste ændring" + +msgid "W14: Warning: List of file names overflow" +msgstr "W14: Advarsel: Overløb i liste over filnavne" + +#, c-format +msgid "E92: Buffer %ld not found" +msgstr "E92: Bufferen %ld blev ikke fundet" + +#, c-format +msgid "E93: More than one match for %s" +msgstr "E93: Flere end ét match for %s" + +#, c-format +msgid "E94: No matching buffer for %s" +msgstr "E94: Ingen matchende buffer for %s" + +#, c-format +msgid "line %ld" +msgstr "linje %ld" + +msgid "E95: Buffer with this name already exists" +msgstr "E95: Buffer med dette navn findes allerede" + +msgid " [Modified]" +msgstr " [Ændret]" + +msgid "[Not edited]" +msgstr "[Ikke redigeret]" + +msgid "[New file]" +msgstr "[Ny fil]" + +msgid "[Read errors]" +msgstr "[Læsefejl]" + +msgid "[RO]" +msgstr "[SB]" + +msgid "[readonly]" +msgstr "[skrivebeskyttet]" + +#, c-format +msgid "1 line --%d%%--" +msgstr "1 linje --%d%%--" + +#, c-format +msgid "%ld lines --%d%%--" +msgstr "%ld linjer --%d%%--" + +#, c-format +msgid "line %ld of %ld --%d%%-- col " +msgstr "linje %ld af %ld --%d%%-- kol " + +msgid "[No Name]" +msgstr "[Intet navn]" + +msgid "help" +msgstr "hjælp" + +msgid "[Help]" +msgstr "[Hjælp]" + +msgid "[Preview]" +msgstr "[ForhÃ¥ndsvisning]" + +msgid "All" +msgstr "Alt" + +msgid "Bot" +msgstr "Ned" + +msgid "Top" +msgstr "Øve" + +msgid "" +"\n" +"# Buffer list:\n" +msgstr "" +"\n" +"# Bufferliste:\n" + +msgid "E382: Cannot write, 'buftype' option is set" +msgstr "E382: Kan ikke skrive, 'buftype'-tilvalget er sat" + +msgid "[Prompt]" +msgstr "[Prompt]" + +msgid "[Scratch]" +msgstr "[Kladdeblok]" + +msgid "" +"\n" +"--- Signs ---" +msgstr "" +"\n" +"--- Signs ---" + +#, c-format +msgid "Signs for %s:" +msgstr "Signs for %s:" + +#, c-format +msgid " line=%ld id=%d name=%s" +msgstr " linje=%ld id=%d navn=%s" + +msgid "E902: Cannot connect to port" +msgstr "E902: Kan ikke oprette forbindelse til port" + +msgid "E901: gethostbyname() in channel_open()" +msgstr "E901: gethostbyname() i channel_open()" + +msgid "E898: socket() in channel_open()" +msgstr "E898: socket() i channel_open()" + +msgid "E903: received command with non-string argument" +msgstr "E903: modtog kommando med argument som ikke er en streng" + +msgid "E904: last argument for expr/call must be a number" +msgstr "E904: sidste argument for udtryk/kald skal være et nummer" + +msgid "E904: third argument for call must be a list" +msgstr "E904: tredje argument for kald skal være en liste" + +#, c-format +msgid "E905: received unknown command: %s" +msgstr "E905: modtog ukendt kommando: %s" + +#, c-format +msgid "E630: %s(): write while not connected" +msgstr "E630: %s(): skrivning mens der ikke er forbindelse" + +#, c-format +msgid "E631: %s(): write failed" +msgstr "E631: %s(): skrivning mislykkedes" + +#, c-format +msgid "E917: Cannot use a callback with %s()" +msgstr "E917: Kan ikke bruge et callback med %s()" + +msgid "E912: cannot use ch_evalexpr()/ch_sendexpr() with a raw or nl channel" +msgstr "" +"E912: kan ikke bruge ch_evalexpr()/ch_sendexpr() med en rÃ¥- eller nl-kanal" + +msgid "E906: not an open channel" +msgstr "E906: ikke en Ã¥ben kanal" + +msgid "E920: _io file requires _name to be set" +msgstr "E920: _io-fil kræver at _name er sat" + +msgid "E915: in_io buffer requires in_buf or in_name to be set" +msgstr "E915: in_io-buffer kræver at in_buf eller in_name er sat" + +#, c-format +msgid "E918: buffer must be loaded: %s" +msgstr "E918: buffer skal være indlæst: %s" + +msgid "E821: File is encrypted with unknown method" +msgstr "E821: Filen er krypteret med ukendt metode" + +msgid "Warning: Using a weak encryption method; see :help 'cm'" +msgstr "Advarsel: Bruger en svag krypteringsmetode; se :help 'cm'" + +msgid "Enter encryption key: " +msgstr "Indtast krypteringsnøgle: " + +msgid "Enter same key again: " +msgstr "Indtast samme nøgle igen: " + +msgid "Keys don't match!" +msgstr "Nøglerne er ikke ens!" + +msgid "[crypted]" +msgstr "[crypted]" + +#, c-format +msgid "E720: Missing colon in Dictionary: %s" +msgstr "E720: Manglende kolon i ordbog: %s" + +#, c-format +msgid "E721: Duplicate key in Dictionary: \"%s\"" +msgstr "E721: Duplikeret nøgle i ordbog: \"%s\"" + +#, c-format +msgid "E722: Missing comma in Dictionary: %s" +msgstr "E722: Manglende komma i ordbog: %s" + +#, c-format +msgid "E723: Missing end of Dictionary '}': %s" +msgstr "E723: Manglende slutning pÃ¥ ordbog '}': %s" + +msgid "extend() argument" +msgstr "extend()-argument" + +#, c-format +msgid "E737: Key already exists: %s" +msgstr "E737: Nøgle findes allerede: %s" + +#, c-format +msgid "E96: Cannot diff more than %ld buffers" +msgstr "E96: Kan ikke diff'e flere end %ld buffere" + +msgid "E810: Cannot read or write temp files" +msgstr "E810: Kan ikke læse eller skrive midlertidige filer" + +msgid "E97: Cannot create diffs" +msgstr "E97: Kan ikke oprette diff'er" + +msgid "Patch file" +msgstr "Patch-fil" + +msgid "E816: Cannot read patch output" +msgstr "E816: Kan ikke læse patch-output" + +msgid "E98: Cannot read diff output" +msgstr "E98: Kan ikke læse diff-output" + +msgid "E99: Current buffer is not in diff mode" +msgstr "E99: Nuværende buffer er ikke i diff-tilstand" + +msgid "E793: No other buffer in diff mode is modifiable" +msgstr "E793: Ingen anden buffer i diff-tilstand kan ændres" + +msgid "E100: No other buffer in diff mode" +msgstr "E100: Ingen anden buffer i diff-tilstand" + +msgid "E101: More than two buffers in diff mode, don't know which one to use" +msgstr "" +"E101: Mere end to buffere i diff-tilstand, ved ikke hvilke der skal bruges" + +#, c-format +msgid "E102: Can't find buffer \"%s\"" +msgstr "E102: Kan ikke finde bufferen \"%s\"" + +#, c-format +msgid "E103: Buffer \"%s\" is not in diff mode" +msgstr "E103: Bufferen \"%s\" er ikke i diff-tilstand" + +msgid "E787: Buffer changed unexpectedly" +msgstr "E787: Buffer ændret uventet" + +msgid "E104: Escape not allowed in digraph" +msgstr "E104: Escape ikke tilladt i digraf" + +msgid "E544: Keymap file not found" +msgstr "E544: Keymap-fil ikke fundet" + +msgid "E105: Using :loadkeymap not in a sourced file" +msgstr "E105: Bruger :loadkeymap ikke i en sourced fil" + +msgid "E791: Empty keymap entry" +msgstr "E791: Tom keymap-post" + +msgid " Keyword completion (^N^P)" +msgstr " Fuldførelse af nøgleord (^N^P)" + +msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" +msgstr " ^X tilstand (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" + +msgid " Whole line completion (^L^N^P)" +msgstr " Fuldførelse af hel linje (^L^N^P)" + +msgid " File name completion (^F^N^P)" +msgstr " Fuldførelse af filnavn (^F^N^P)" + +msgid " Tag completion (^]^N^P)" +msgstr " Fuldførelse af tag (^]^N^P)" + +msgid " Path pattern completion (^N^P)" +msgstr " Fuldførelse af sti (^N^P)" + +msgid " Definition completion (^D^N^P)" +msgstr " Fuldførelse af definition (^D^N^P)" + +msgid " Dictionary completion (^K^N^P)" +msgstr " Fuldførelse af ordbog (^K^N^P)" + +msgid " Thesaurus completion (^T^N^P)" +msgstr " Fuldførelse af tesaurus (^T^N^P)" + +msgid " Command-line completion (^V^N^P)" +msgstr " Fuldførelse af kommandolinje (^V^N^P)" + +msgid " User defined completion (^U^N^P)" +msgstr " Fuldførelse af brugerdefineret (^U^N^P)" + +msgid " Omni completion (^O^N^P)" +msgstr " Fuldførelse af omni (^O^N^P)" + +msgid " Spelling suggestion (s^N^P)" +msgstr " Staveforslag (s^N^P)" + +msgid " Keyword Local completion (^N^P)" +msgstr " Fuldførelse af nøgleord local (^N^P)" + +msgid "Hit end of paragraph" +msgstr "Stødte pÃ¥ slutningen af afsnit" + +msgid "E839: Completion function changed window" +msgstr "E839: Fuldførelse-funktion ændrede vindue" + +msgid "E840: Completion function deleted text" +msgstr "E840: Fuldførelse-funktion slettede tekst" + +msgid "'dictionary' option is empty" +msgstr "'dictionary'-tilvalget er tomt" + +msgid "'thesaurus' option is empty" +msgstr "'thesaurus'-tilvalget er tomt" + +#, c-format +msgid "Scanning dictionary: %s" +msgstr "Skanner ordbog: %s" + +msgid " (insert) Scroll (^E/^Y)" +msgstr " (indsæt) Rul (^E/^Y)" + +msgid " (replace) Scroll (^E/^Y)" +msgstr " (erstat) Rul (^E/^Y)" + +#, c-format +msgid "Scanning: %s" +msgstr "Skanner: %s" + +msgid "Scanning tags." +msgstr "Skanner tags." + +msgid "match in file" +msgstr "match i fil" + +msgid " Adding" +msgstr " Tilføjer" + +msgid "-- Searching..." +msgstr "-- Søger..." + +msgid "Back at original" +msgstr "Tilbage ved original" + +msgid "Word from other line" +msgstr "Ord fra anden linje" + +msgid "The only match" +msgstr "Det eneste match" + +#, c-format +msgid "match %d of %d" +msgstr "match %d af %d" + +#, c-format +msgid "match %d" +msgstr "match %d" + +msgid "E18: Unexpected characters in :let" +msgstr "E18: Uventede tegn i :let" + +#, c-format +msgid "E121: Undefined variable: %s" +msgstr "E121: Udefineret variabel: %s" + +msgid "E111: Missing ']'" +msgstr "E111: Manglende ']'" + +msgid "E719: Cannot use [:] with a Dictionary" +msgstr "E719: Kan ikke bruge [:] med en ordbog" + +#, c-format +msgid "E734: Wrong variable type for %s=" +msgstr "E734: Forkert variabeltype for %s=" + +#, c-format +msgid "E461: Illegal variable name: %s" +msgstr "E461: Ulovligt variabelnavn: %s" + +msgid "E806: using Float as a String" +msgstr "E806: bruger flydende kommatal som en streng" + +msgid "E687: Less targets than List items" +msgstr "E687: Færre mÃ¥l end listepunkter" + +msgid "E688: More targets than List items" +msgstr "E688: Flere mÃ¥l end listepunkter" + +msgid "Double ; in list of variables" +msgstr "Dobbelt ; i liste over variabler" + +#, c-format +msgid "E738: Can't list variables for %s" +msgstr "E738: Kan ikke opliste variabler for %s" + +msgid "E689: Can only index a List or Dictionary" +msgstr "E689: Kan kun indeksere en liste eller ordbog" + +msgid "E708: [:] must come last" +msgstr "E708: [:] skal være sidst" + +msgid "E709: [:] requires a List value" +msgstr "E709: [:] kræver en listeværdi" + +msgid "E710: List value has more items than target" +msgstr "E710: Listeværdi har flere punkter end mÃ¥l" + +msgid "E711: List value has not enough items" +msgstr "E711: Listeværdi har ikke nok punkter" + +msgid "E690: Missing \"in\" after :for" +msgstr "E690: Manglende \"in\" efter :for" + +#, c-format +msgid "E108: No such variable: \"%s\"" +msgstr "E108: Ingen sÃ¥dan variabel: \"%s\"" + +#, c-format +msgid "E940: Cannot lock or unlock variable %s" +msgstr "E940: Kan ikke lÃ¥se eller lÃ¥se op for variablen %s" + +msgid "E743: variable nested too deep for (un)lock" +msgstr "E743: variabel indlejret for dybt til at blive lÃ¥st/lÃ¥st op" + +msgid "E109: Missing ':' after '?'" +msgstr "E109: Manglende ':' efter '?'" + +msgid "E804: Cannot use '%' with Float" +msgstr "E804: Kan ikke bruge '%' med flydende kommatal" + +msgid "E110: Missing ')'" +msgstr "E110: Manglende ')'" + +msgid "E695: Cannot index a Funcref" +msgstr "E695: Kan ikke indeksere en funcref" + +msgid "E909: Cannot index a special variable" +msgstr "E909: Kan ikke indeksere en speciel variabel" + +#, c-format +msgid "E112: Option name missing: %s" +msgstr "E112: Tilvalgsnavn mangler: %s" + +#, c-format +msgid "E113: Unknown option: %s" +msgstr "E113: Ukendt tilvalg: %s" + +#, c-format +msgid "E114: Missing quote: %s" +msgstr "E114: Manglende citationstegn: %s" + +#, c-format +msgid "E115: Missing quote: %s" +msgstr "E115: Manglende citationstegn: %s" + +msgid "Not enough memory to set references, garbage collection aborted!" +msgstr "Ikke nok hukommelse til at sætte referencer, affaldsindsamling afbrudt!" + +msgid "E724: variable nested too deep for displaying" +msgstr "E724: variabel indlejret for dybt til at blive vist" + +msgid "E805: Using a Float as a Number" +msgstr "E805: Bruger et flydende kommatal som et nummer" + +msgid "E703: Using a Funcref as a Number" +msgstr "E703: Bruger en funcref som et nummer" + +msgid "E745: Using a List as a Number" +msgstr "E745: Bruger en liste som et nummer" + +msgid "E728: Using a Dictionary as a Number" +msgstr "E728: Bruger en ordbog som et nummer" + +msgid "E910: Using a Job as a Number" +msgstr "E910: Bruger et job som et nummer" + +msgid "E913: Using a Channel as a Number" +msgstr "E913: Bruger en kanal som et nummer" + +msgid "E891: Using a Funcref as a Float" +msgstr "E891: Bruger en funcref som et fyldende kommatal" + +msgid "E892: Using a String as a Float" +msgstr "E892: Bruger en streng som et flydende kommatal" + +msgid "E893: Using a List as a Float" +msgstr "E893: Bruger en liste som et flydende kommatal" + +msgid "E894: Using a Dictionary as a Float" +msgstr "E894: Bruger en ordbog som et flydende kommatal" + +msgid "E907: Using a special value as a Float" +msgstr "E907: Bruger en speciel værdi som et flydende kommatal" + +msgid "E911: Using a Job as a Float" +msgstr "E911: Bruger et job som et flydende kommatal" + +msgid "E914: Using a Channel as a Float" +msgstr "E914: Bruger en kanal som et flydende kommatal" + +msgid "E729: using Funcref as a String" +msgstr "E729: bruger funcref som en streng" + +msgid "E730: using List as a String" +msgstr "E730: bruger liste som en streng" + +msgid "E731: using Dictionary as a String" +msgstr "E731: bruger ordbog som en streng" + +msgid "E908: using an invalid value as a String" +msgstr "E908: bruger en ugyldig værdi som en streng" + +#, c-format +msgid "E795: Cannot delete variable %s" +msgstr "E795: Kan ikke slette variablen %s" + +#, c-format +msgid "E704: Funcref variable name must start with a capital: %s" +msgstr "E704: Funcref-variabelnavn skal begynde med et stort bogstav: %s" + +#, c-format +msgid "E705: Variable name conflicts with existing function: %s" +msgstr "E705: Variabelnavn er i konflikt med eksisterende funktion: %s" + +#, c-format +msgid "E741: Value is locked: %s" +msgstr "E741: Værdien er lÃ¥st: %s" + +msgid "Unknown" +msgstr "Ukendt" + +#, c-format +msgid "E742: Cannot change value of %s" +msgstr "E742: Kan ikke ændre værdien af %s" + +msgid "E698: variable nested too deep for making a copy" +msgstr "E698: variabel indlejret for dybt til at lave en kopi" + +msgid "" +"\n" +"# global variables:\n" +msgstr "" +"\n" +"# globale variabler:\n" + +msgid "" +"\n" +"\tLast set from " +msgstr "" +"\n" +"\tSidst sat fra " + +msgid "E691: Can only compare List with List" +msgstr "E691: Kan kun sammenligne liste med liste" + +msgid "E692: Invalid operation for List" +msgstr "E692: Ugyldig handling for liste" + +msgid "E735: Can only compare Dictionary with Dictionary" +msgstr "E735: Kan kun sammenligne ordbog med ordbog" + +msgid "E736: Invalid operation for Dictionary" +msgstr "E736: Ugyldig handling for ordbog" + +msgid "E694: Invalid operation for Funcrefs" +msgstr "E694: Ugyldig handling for funcref'er" + +msgid "map() argument" +msgstr "map()-argument" + +msgid "filter() argument" +msgstr "filter()-argument" + +#, c-format +msgid "E686: Argument of %s must be a List" +msgstr "E686: Argument af %s skal være en liste" + +msgid "E928: String required" +msgstr "E928: Streng kræves" + +msgid "E808: Number or Float required" +msgstr "E808: Nummer eller flydende kommatal kræves" + +msgid "add() argument" +msgstr "add()-argument" + +msgid "E785: complete() can only be used in Insert mode" +msgstr "E785: complete() kan kun bruges i indsæt-tilstand" + +msgid "&Ok" +msgstr "&Ok" + +#, c-format +msgid "+-%s%3ld line: " +msgid_plural "+-%s%3ld lines: " +msgstr[0] "+-%s%3ld linje: " +msgstr[1] "+-%s%3ld linjer: " + +#, c-format +msgid "E700: Unknown function: %s" +msgstr "E700: Ukendt funktion: %s" + +msgid "E922: expected a dict" +msgstr "E922: ventede en ordbog" + +msgid "E923: Second argument of function() must be a list or a dict" +msgstr "E923: Andet argument af function() skal være en liste eller en ordbog" + +msgid "" +"&OK\n" +"&Cancel" +msgstr "" +"&OK\n" +"&Annuller" + +msgid "called inputrestore() more often than inputsave()" +msgstr "kaldte inputrestore() flere gange end inputsave()" + +msgid "insert() argument" +msgstr "insert()-argument" + +msgid "E786: Range not allowed" +msgstr "E786: OmrÃ¥de ikke tilladt" + +msgid "E916: not a valid job" +msgstr "E916: ikke et gyldigt job" + +msgid "E701: Invalid type for len()" +msgstr "E701: Ugyldig type for len()" + +#, c-format +msgid "E798: ID is reserved for \":match\": %ld" +msgstr "E798: ID er reserveret til \":match\": %ld" + +msgid "E726: Stride is zero" +msgstr "E726: Stride er nul" + +msgid "E727: Start past end" +msgstr "E727: Start efter slutningen" + +msgid "<empty>" +msgstr "<tom>" + +msgid "E240: No connection to the X server" +msgstr "E240: Ingen forbindelse til X-serveren" + +#, c-format +msgid "E241: Unable to send to %s" +msgstr "E241: Kan ikke sende til %s" + +msgid "E277: Unable to read a server reply" +msgstr "E277: Kan ikke læse et serversvar" + +msgid "E941: already started a server" +msgstr "E941: allerede startet en server" + +msgid "E942: +clientserver feature not available" +msgstr "E942: +clientserver-funktionalitet ikke tilgængelig" + +msgid "remove() argument" +msgstr "remove()-argument" + +msgid "E655: Too many symbolic links (cycle?)" +msgstr "E655: For mange symbolske links (cyklus?)" + +msgid "reverse() argument" +msgstr "reverse()-argument" + +msgid "E258: Unable to send to client" +msgstr "E258: Kan ikke sende til klient" + +#, c-format +msgid "E927: Invalid action: '%s'" +msgstr "E927: Ugyldig handling: '%s'" + +msgid "sort() argument" +msgstr "sort()-argument" + +msgid "uniq() argument" +msgstr "uniq()-argument" + +msgid "E702: Sort compare function failed" +msgstr "E702: Sort-sammenligningsfunktion mislykkedes" + +msgid "E882: Uniq compare function failed" +msgstr "E882: Uniq-sammenligningsfunktion mislykkedes" + +msgid "(Invalid)" +msgstr "(Ugyldig)" + +#, c-format +msgid "E935: invalid submatch number: %d" +msgstr "E935: ugyldigt undermatch-nummer: %d" + +msgid "E677: Error writing temp file" +msgstr "E677: Fejl ved skrivning af midlertidig fil" + +msgid "E921: Invalid callback argument" +msgstr "E921: Ugyldigt callback-argument" + +#, c-format +msgid "<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s" +msgstr "<%s>%s%s %d, hex %02x, oct %03o, digr %s" + +#, c-format +msgid "<%s>%s%s %d, Hex %02x, Octal %03o" +msgstr "<%s>%s%s %d, hex %02x, octal %03o" + +#, c-format +msgid "> %d, Hex %04x, Oct %o, Digr %s" +msgstr "> %d, hex %04x, oct %o, digr %s" + +#, c-format +msgid "> %d, Hex %08x, Oct %o, Digr %s" +msgstr "> %d, hex %08x, oct %o, digr %s" + +#, c-format +msgid "> %d, Hex %04x, Octal %o" +msgstr "> %d, hex %04x, octal %o" + +#, c-format +msgid "> %d, Hex %08x, Octal %o" +msgstr "> %d, hex %08x, octal %o" + +msgid "E134: Move lines into themselves" +msgstr "E134: flyt linjer ind i dem selv" + +msgid "1 line moved" +msgstr "1 linje flyttet" + +#, c-format +msgid "%ld lines moved" +msgstr "%ld linjer flyttet" + +#, c-format +msgid "%ld lines filtered" +msgstr "%ld linjer filtreret" + +msgid "E135: *Filter* Autocommands must not change current buffer" +msgstr "E135: *Filter*-autokommandoer mÃ¥ ikke ændre nuværende buffer" + +msgid "[No write since last change]\n" +msgstr "[Ingen skrivning siden sidste ændring]\n" + +#, c-format +msgid "%sviminfo: %s in line: " +msgstr "%sviminfo: %s pÃ¥ linje: " + +msgid "E136: viminfo: Too many errors, skipping rest of file" +msgstr "E136: viminfo: For mange fejl, springer resten af filen over" + +#, c-format +msgid "Reading viminfo file \"%s\"%s%s%s" +msgstr "Læser viminfo-filen \"%s\"%s%s%s" + +msgid " info" +msgstr " info" + +msgid " marks" +msgstr " mærker" + +msgid " oldfiles" +msgstr " gamle filer" + +msgid " FAILED" +msgstr " MISLYKKEDES" + +#, c-format +msgid "E137: Viminfo file is not writable: %s" +msgstr "E137: Viminfo-filen er skrivebeskyttet: %s" + +#, c-format +msgid "E929: Too many viminfo temp files, like %s!" +msgstr "E929: For mange midlertidige filer for viminfo, sÃ¥som %s!" + +#, c-format +msgid "E138: Can't write viminfo file %s!" +msgstr "E138: Kan ikke skrive viminfo-filen %s!" + +#, c-format +msgid "Writing viminfo file \"%s\"" +msgstr "Skriver viminfo-filen \"%s\"" + +#, c-format +msgid "E886: Can't rename viminfo file to %s!" +msgstr "E886: Kan ikke omdøbe viminfo-fil til %s!" + +#, c-format +msgid "# This viminfo file was generated by Vim %s.\n" +msgstr "# Denne viminfo-fil blev genereret af Vim %s.\n" + +msgid "" +"# You may edit it if you're careful!\n" +"\n" +msgstr "" +"# Du kan redigere den, hvis du er forsigtig!\n" +"\n" + +msgid "# Value of 'encoding' when this file was written\n" +msgstr "# Værdien af 'encoding' da filen blev skrevet\n" + +msgid "Illegal starting char" +msgstr "Ulovligt tegn i begyndelsen" + +msgid "" +"\n" +"# Bar lines, copied verbatim:\n" +msgstr "" +"\n" +"#-bjælkelinjer, kopieret ordret:\n" + +msgid "Save As" +msgstr "Gem som" + +msgid "Write partial file?" +msgstr "Skriv ufuldstændig fil?" + +msgid "E140: Use ! to write partial buffer" +msgstr "E140: Brug ! til at skrive ufuldstændig buffer" + +#, c-format +msgid "Overwrite existing file \"%s\"?" +msgstr "Overskriv eksisterende fil \"%s\"?" + +#, c-format +msgid "Swap file \"%s\" exists, overwrite anyway?" +msgstr "Swap-filen \"%s\" findes, overskriv alligevel?" + +#, c-format +msgid "E768: Swap file exists: %s (:silent! overrides)" +msgstr "E768: Swap-filen findes: %s (:silent! tilsidesætter)" + +#, c-format +msgid "E141: No file name for buffer %ld" +msgstr "E141: Intet filnavn for buffer %ld" + +msgid "E142: File not written: Writing is disabled by 'write' option" +msgstr "E142: Fil ikke skrevet: Skrivning er deaktiveret af 'write'-tilvalget" + +#, c-format +msgid "" +"'readonly' option is set for \"%s\".\n" +"Do you wish to write anyway?" +msgstr "" +"'readonly'-tilvalget er sat for \"%s\".\n" +"Vil du skrive alligevel?" + +#, c-format +msgid "" +"File permissions of \"%s\" are read-only.\n" +"It may still be possible to write it.\n" +"Do you wish to try?" +msgstr "" +"Filtilladelserne for \"%s\" er skrivebeskyttede.\n" +"Der kan stadig være mulighed for at skrive den.\n" +"Vil du prøve?" + +#, c-format +msgid "E505: \"%s\" is read-only (add ! to override)" +msgstr "E505: \"%s\" er skrivebeskyttet (tilføj ! for at tilsidesætte)" + +msgid "Edit File" +msgstr "Rediger fil" + +#, c-format +msgid "E143: Autocommands unexpectedly deleted new buffer %s" +msgstr "E143: Autokommandoer slettede uventede ny buffer %s" + +msgid "E144: non-numeric argument to :z" +msgstr "E144: ikke-numerisk argument til :z" + +msgid "E145: Shell commands not allowed in rvim" +msgstr "E145: Skalkommandoer er ikke tilladt i rvim" + +msgid "E146: Regular expressions can't be delimited by letters" +msgstr "E146: Regulære udtryk kan ikke afgrænses af bogstaver" + +#, c-format +msgid "replace with %s (y/n/a/q/l/^E/^Y)?" +msgstr "erstat med %s (y/n/a/q/l/^E/^Y)?" + +msgid "(Interrupted) " +msgstr "(Afbrudt) " + +msgid "1 match" +msgstr "1 match" + +msgid "1 substitution" +msgstr "1 erstatning" + +#, c-format +msgid "%ld matches" +msgstr "%ld match" + +#, c-format +msgid "%ld substitutions" +msgstr "%ld erstatninger" + +msgid " on 1 line" +msgstr " pÃ¥ 1 linje" + +#, c-format +msgid " on %ld lines" +msgstr " pÃ¥ %ld linjer" + +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: Kan ikke foretage :global rekursivt med et omrÃ¥de" + +msgid "E148: Regular expression missing from global" +msgstr "E148: Regulære udtryk mangler fra global" + +#, c-format +msgid "Pattern found in every line: %s" +msgstr "Mønster fundet pÃ¥ hver linje: %s" + +#, c-format +msgid "Pattern not found: %s" +msgstr "Mønster ikke fundet: %s" + +msgid "" +"\n" +"# Last Substitute String:\n" +"$" +msgstr "" +"\n" +"# Sidste erstatningsstreng:\n" +"$" + +msgid "E478: Don't panic!" +msgstr "E478: Tag det bare helt roligt!" + +#, c-format +msgid "E661: Sorry, no '%s' help for %s" +msgstr "E661: Beklager, ingen '%s' hjælp til %s" + +#, c-format +msgid "E149: Sorry, no help for %s" +msgstr "E149: Beklager, ingen hjælp til %s" + +#, c-format +msgid "Sorry, help file \"%s\" not found" +msgstr "Beklager, hjælpfilen \"%s\" ikke fundet" + +#, c-format +msgid "E151: No match: %s" +msgstr "E151: Intet match: %s" + +#, c-format +msgid "E152: Cannot open %s for writing" +msgstr "E152: Kan ikke Ã¥bne %s til skrivning" + +#, c-format +msgid "E153: Unable to open %s for reading" +msgstr "E153: Kan ikke Ã¥bne %s til læsning" + +#, c-format +msgid "E670: Mix of help file encodings within a language: %s" +msgstr "E670: Blanding af kodninger for hjælpfiler i samme sprog: %s" + +#, c-format +msgid "E154: Duplicate tag \"%s\" in file %s/%s" +msgstr "E154: Duplikeret tag \"%s\" i fil %s/%s" + +#, c-format +msgid "E150: Not a directory: %s" +msgstr "E150: Ikke en mappe: %s" + +#, c-format +msgid "E160: Unknown sign command: %s" +msgstr "E160: Ukendt sign-kommando: %s" + +msgid "E156: Missing sign name" +msgstr "E156: Manglende sign-navn" + +msgid "E612: Too many signs defined" +msgstr "E612: For mange signs defineret" + +#, c-format +msgid "E239: Invalid sign text: %s" +msgstr "E239: Ugyldig sign-tekst: %s" + +#, c-format +msgid "E155: Unknown sign: %s" +msgstr "E155: Ukendt sign: %s" + +msgid "E159: Missing sign number" +msgstr "E159: Manglende sign-nummer" + +#, c-format +msgid "E158: Invalid buffer name: %s" +msgstr "E158: Ugyldigt buffernavn: %s" + +msgid "E934: Cannot jump to a buffer that does not have a name" +msgstr "E934: Kan ikke hoppe til en buffer som ikke har et navn" + +#, c-format +msgid "E157: Invalid sign ID: %ld" +msgstr "E157: Ugyldigt sign-ID: %ld" + +#, c-format +msgid "E885: Not possible to change sign %s" +msgstr "E885: Det er ikke muligt at ændre sign %s" + +msgid " (NOT FOUND)" +msgstr " (IKKE FUNDET)" + +msgid " (not supported)" +msgstr " (understøttes ikke)" + +msgid "[Deleted]" +msgstr "[Slettet]" + +msgid "No old files" +msgstr "Ingen gamle filer" + +msgid "Entering Debug mode. Type \"cont\" to continue." +msgstr "GÃ¥r i fejlretningstilstand. Skriv \"cont\" for at fortsætte." + +#, c-format +msgid "Oldval = \"%s\"" +msgstr "Oldval = \"%s\"" + +#, c-format +msgid "Newval = \"%s\"" +msgstr "Newval = \"%s\"" + +#, c-format +msgid "line %ld: %s" +msgstr "linje %ld: %s" + +#, c-format +msgid "cmd: %s" +msgstr "cmd: %s" + +msgid "frame is zero" +msgstr "ramme er nul" + +#, c-format +msgid "frame at highest level: %d" +msgstr "ramme pÃ¥ højeste niveau: %d" + +#, c-format +msgid "Breakpoint in \"%s%s\" line %ld" +msgstr "Breakpoint i \"%s%s\" linje %ld" + +#, c-format +msgid "E161: Breakpoint not found: %s" +msgstr "E161: Breakpoint ikke fundet: %s" + +msgid "No breakpoints defined" +msgstr "Ingen breakpoints defineret" + +#, c-format +msgid "%3d %s %s line %ld" +msgstr "%3d %s %s linje %ld" + +#, c-format +msgid "%3d expr %s" +msgstr "%3d udtryk %s" + +msgid "E750: First use \":profile start {fname}\"" +msgstr "E750: Brug først \":profile start {fname}\"" + +#, c-format +msgid "Save changes to \"%s\"?" +msgstr "Gem ændringer til \"%s\"?" + +#, c-format +msgid "E947: Job still running in buffer \"%s\"" +msgstr "E947: Job kører stadig i bufferen \"%s\"" + +#, c-format +msgid "E162: No write since last change for buffer \"%s\"" +msgstr "E162: Ingen skrivning siden sidste ændring for bufferen \"%s\"" + +msgid "Warning: Entered other buffer unexpectedly (check autocommands)" +msgstr "Advarsel: Indtastede anden buffer uventede (tjek autokommandoer)" + +msgid "E163: There is only one file to edit" +msgstr "E163: Der er kun én fil at redigere" + +msgid "E164: Cannot go before first file" +msgstr "E164: Kan ikke gÃ¥ før første fil" + +msgid "E165: Cannot go beyond last file" +msgstr "E165: Kan ikke gÃ¥ over sidste fil" + +#, c-format +msgid "E666: compiler not supported: %s" +msgstr "E666: kompiler understøttes ikke: %s" + +#, c-format +msgid "Searching for \"%s\" in \"%s\"" +msgstr "Søger efter \"%s\" i \"%s\"" + +#, c-format +msgid "Searching for \"%s\"" +msgstr "Søger efter \"%s\"" + +#, c-format +msgid "not found in '%s': \"%s\"" +msgstr "ikke fundet i '%s': \"%s\"" + +#, c-format +msgid "W20: Required python version 2.x not supported, ignoring file: %s" +msgstr "W20: Krævede python-version 2.x understøttes ikke, ignorerer fil: %s" + +#, c-format +msgid "W21: Required python version 3.x not supported, ignoring file: %s" +msgstr "W21: Krævede python-version 3.x understøttes ikke, ignorerer fil: %s" + +msgid "Source Vim script" +msgstr "Source Vim-script" + +#, c-format +msgid "Cannot source a directory: \"%s\"" +msgstr "Kan ikke source en mappe: \"%s\"" + +#, c-format +msgid "could not source \"%s\"" +msgstr "kunne ikke source \"%s\"" + +#, c-format +msgid "line %ld: could not source \"%s\"" +msgstr "linje %ld: kunne ikke source \"%s\"" + +#, c-format +msgid "sourcing \"%s\"" +msgstr "sourcing \"%s\"" + +#, c-format +msgid "line %ld: sourcing \"%s\"" +msgstr "linje %ld: sourcing \"%s\"" + +#, c-format +msgid "finished sourcing %s" +msgstr "færdig med sourcing af %s" + +#, c-format +msgid "continuing in %s" +msgstr "fortsætter i %s" + +msgid "modeline" +msgstr "tilstandslinje" + +msgid "--cmd argument" +msgstr "--cmd-argument" + +msgid "-c argument" +msgstr "-c-argument" + +msgid "environment variable" +msgstr "miljøvariabel" + +msgid "error handler" +msgstr "fejlhÃ¥ndtering" + +msgid "W15: Warning: Wrong line separator, ^M may be missing" +msgstr "W15: Advarsel: Forkert linjeseparator, ^M mangler muligvis" + +msgid "E167: :scriptencoding used outside of a sourced file" +msgstr "E167: :scriptencoding brugt udenfor en sourced fil" + +msgid "E168: :finish used outside of a sourced file" +msgstr "E168: :finish udenfor en sourced fil" + +#, c-format +msgid "Current %slanguage: \"%s\"" +msgstr "Nuværende %ssprog: \"%s\"" + +#, c-format +msgid "E197: Cannot set language to \"%s\"" +msgstr "E197: Kan ikke sætte sprog til \"%s\"" + +msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." +msgstr "GÃ¥r i Ex-tilstand. Skriv \"visual\" for at gÃ¥ til normal tilstand." + +msgid "E501: At end-of-file" +msgstr "E501: Ved filens slutning" + +msgid "E169: Command too recursive" +msgstr "E169: Kommando for rekursiv" + +#, c-format +msgid "E605: Exception not caught: %s" +msgstr "E605: Undtagelse ikke fanget: %s" + +msgid "End of sourced file" +msgstr "Slut pÃ¥ sourced fil" + +msgid "End of function" +msgstr "Slutning af funktion" + +msgid "E464: Ambiguous use of user-defined command" +msgstr "E464: Flertydig brug af brugerdefineret kommando" + +msgid "E492: Not an editor command" +msgstr "E492: Ikke en editor-kommando" + +msgid "E493: Backwards range given" +msgstr "E493: Baglæns omrÃ¥de givet" + +msgid "Backwards range given, OK to swap" +msgstr "Baglæns omrÃ¥de givet, OK at bytte om" + +msgid "E494: Use w or w>>" +msgstr "E494: Brug w eller w>>" + +msgid "E943: Command table needs to be updated, run 'make cmdidxs'" +msgstr "E943: Kommandotabel skal opdateres, kør 'make cmdidxs'" + +msgid "E319: Sorry, the command is not available in this version" +msgstr "E319: Beklager, kommandoen er ikke tilgængelig i denne version" + +msgid "1 more file to edit. Quit anyway?" +msgstr "1 fil mere at redigere. Afslut alligevel?" + +#, c-format +msgid "%d more files to edit. Quit anyway?" +msgstr "%d filer mere at redigere. Afslut alligevel?" + +msgid "E173: 1 more file to edit" +msgstr "E173: 1 fil mere at redigere" + +#, c-format +msgid "E173: %ld more files to edit" +msgstr "E173: %ld filer mere at redigere" + +msgid "E174: Command already exists: add ! to replace it" +msgstr "E174: Kommandoen findes allerede: tilføj ! for at erstatte den" + +msgid "" +"\n" +" Name Args Address Complete Definition" +msgstr "" +"\n" +" Navn Argumenter Adresse Fuldført Definition" + +msgid "No user-defined commands found" +msgstr "Fandt ingen brugerdefinerede kommandoer" + +msgid "E175: No attribute specified" +msgstr "E175: Ingen attribut angivet" + +msgid "E176: Invalid number of arguments" +msgstr "E176: Ugyldigt antal argumenter" + +msgid "E177: Count cannot be specified twice" +msgstr "E177: Tælling mÃ¥ ikke angives to gange" + +msgid "E178: Invalid default value for count" +msgstr "E178: Ugyldig standardværdi for tælling" + +msgid "E179: argument required for -complete" +msgstr "E179: argument kræves til -complete" + +msgid "E179: argument required for -addr" +msgstr "E179: argument kræves til -addr" + +#, c-format +msgid "E181: Invalid attribute: %s" +msgstr "E181: Ugyldig attribut: %s" + +msgid "E182: Invalid command name" +msgstr "E182: Ugyldigt kommandonavn" + +msgid "E183: User defined commands must start with an uppercase letter" +msgstr "E183: Brugerdefinerede kommandoer skal begynde med et stort bogstav" + +msgid "E841: Reserved name, cannot be used for user defined command" +msgstr "E841: Reserveret navn, kan ikke bruges til brugerdefineret kommando" + +#, c-format +msgid "E184: No such user-defined command: %s" +msgstr "E184: Ingen sÃ¥dan brugerdefineret kommando: %s" + +#, c-format +msgid "E180: Invalid address type value: %s" +msgstr "E180: Ugyldig værdi for adressetype: %s" + +#, c-format +msgid "E180: Invalid complete value: %s" +msgstr "E180: Ugyldig complete-værdi: %s" + +msgid "E468: Completion argument only allowed for custom completion" +msgstr "E468: Fuldførelse-argument kun tilladt for tilpasset fuldførelse" + +msgid "E467: Custom completion requires a function argument" +msgstr "E467: Tilpasset fuldførelse kræver et funktion-argument" + +msgid "unknown" +msgstr "ukendt" + +#, c-format +msgid "E185: Cannot find color scheme '%s'" +msgstr "E185: Kan ikke finde farveskemaet '%s'" + +msgid "Greetings, Vim user!" +msgstr "Hejsa, Vim-bruger!" + +msgid "E784: Cannot close last tab page" +msgstr "E784: Kan ikke lukke sidste fanebladsside" + +msgid "Already only one tab page" +msgstr "Allerede kun én fanebladsside" + +msgid "Edit File in new window" +msgstr "Rediger fil i nyt vindue" + +#, c-format +msgid "Tab page %d" +msgstr "Fanebladsside %d" + +msgid "No swap file" +msgstr "Ingen swap-fil" + +msgid "Append File" +msgstr "Tilføj fil til slutningen" + +msgid "E747: Cannot change directory, buffer is modified (add ! to override)" +msgstr "" +"E747: Kan ikke skifte mappe, buffer er ændret (tilføj ! for at tilsidesætte)" + +msgid "E186: No previous directory" +msgstr "E186: Ingen tidligere ordbog" + +msgid "E187: Unknown" +msgstr "E187: Ukendt" + +msgid "E465: :winsize requires two number arguments" +msgstr "E465: :winsize kræver to nummer-argumenter" + +#, c-format +msgid "Window position: X %d, Y %d" +msgstr "Vinduesplacering: X %d, Y %d" + +msgid "E188: Obtaining window position not implemented for this platform" +msgstr "" +"E188: Indhentelse af vinduesplacering ikke implementeret pÃ¥ denne platform" + +msgid "E466: :winpos requires two number arguments" +msgstr "E466: :winpos kræver to nummer-argumenter" + +msgid "E930: Cannot use :redir inside execute()" +msgstr "E930: Kan ikke bruge :redir i execute()" + +msgid "Save Redirection" +msgstr "Gem omdirigering" + +msgid "Save View" +msgstr "Gem visning" + +msgid "Save Session" +msgstr "Gem session" + +msgid "Save Setup" +msgstr "Gem opsætning" + +#, c-format +msgid "E739: Cannot create directory: %s" +msgstr "E739: Kan ikke oprette mappe: %s" + +#, c-format +msgid "E189: \"%s\" exists (add ! to override)" +msgstr "E189: \"%s\" findes (tilføj ! for at tilsidesætte)" + +#, c-format +msgid "E190: Cannot open \"%s\" for writing" +msgstr "E190: Kan ikke Ã¥bne \"%s\" til skrivning" + +msgid "E191: Argument must be a letter or forward/backward quote" +msgstr "" +"E191: Argument skal være et bogstav eller retvendt/omvendt citationstegn" + +msgid "E192: Recursive use of :normal too deep" +msgstr "E192: Rekursiv brug af :normal for dyb" + +msgid "E809: #< is not available without the +eval feature" +msgstr "E809: #< er ikke tilgængelig uden +eval-funktionaliteten" + +msgid "E194: No alternate file name to substitute for '#'" +msgstr "E194: Intet alternate-filnavn til erstatning for '#'" + +msgid "E495: no autocommand file name to substitute for \"<afile>\"" +msgstr "E495: intet autokommando-filnavn til erstatning for \"<afile>\"" + +msgid "E496: no autocommand buffer number to substitute for \"<abuf>\"" +msgstr "E496: intet autokommando-buffernummer til erstatning for \"<abuf>\"" + +msgid "E497: no autocommand match name to substitute for \"<amatch>\"" +msgstr "E497: intet autokommando-matchnavn til erstatning for \"<amatch>\"" + +msgid "E498: no :source file name to substitute for \"<sfile>\"" +msgstr "E498: intet :source-filnavn til erstatning for \"<sfile>\"" + +msgid "E842: no line number to use for \"<slnum>\"" +msgstr "E842: intet linjenummer til brug for \"<slnum>\"" + +#, no-c-format +msgid "E499: Empty file name for '%' or '#', only works with \":p:h\"" +msgstr "E499: Tomt filnavn for '%' eller '#', virker kun med \":p:h\"" + +msgid "E500: Evaluates to an empty string" +msgstr "E500: Evaluerer til en tom streng" + +msgid "E195: Cannot open viminfo file for reading" +msgstr "E195: Kan ikke Ã¥bne viminfo-fil til læsning" + +msgid "Untitled" +msgstr "Unavngivet" + +msgid "E196: No digraphs in this version" +msgstr "E196: Ingen digraffer i denne version" + +msgid "E608: Cannot :throw exceptions with 'Vim' prefix" +msgstr "E608: Kan ikke :throw-undtagelser med 'Vim'-præfiks" + +#, c-format +msgid "Exception thrown: %s" +msgstr "Undtagelse kastet: %s" + +#, c-format +msgid "Exception finished: %s" +msgstr "Undtagelse færdig: %s" + +#, c-format +msgid "Exception discarded: %s" +msgstr "Undtagelse forkastet: %s" + +#, c-format +msgid "%s, line %ld" +msgstr "%s, linje %ld" + +#, c-format +msgid "Exception caught: %s" +msgstr "Undtagelse fanget: %s" + +#, c-format +msgid "%s made pending" +msgstr "%s gjort afventende" + +#, c-format +msgid "%s resumed" +msgstr "%s genoptaget" + +#, c-format +msgid "%s discarded" +msgstr "%s forkastet" + +msgid "Exception" +msgstr "Undtagelse" + +msgid "Error and interrupt" +msgstr "Fejl og afbryd" + +msgid "Error" +msgstr "Fejl" + +msgid "Interrupt" +msgstr "Afbryd" + +msgid "E579: :if nesting too deep" +msgstr "E579: :if-indlejring for dyb" + +msgid "E580: :endif without :if" +msgstr "E580: :endif uden :if" + +msgid "E581: :else without :if" +msgstr "E581: :else uden :if" + +msgid "E582: :elseif without :if" +msgstr "E582: :elseif uden :if" + +msgid "E583: multiple :else" +msgstr "E583: flere :else" + +msgid "E584: :elseif after :else" +msgstr "E584: :elseif efter :else" + +msgid "E585: :while/:for nesting too deep" +msgstr "E585: :while/:for-indlejring for dyb" + +msgid "E586: :continue without :while or :for" +msgstr "E586: :continue uden :while eller :for" + +msgid "E587: :break without :while or :for" +msgstr "E587: :break uden :while eller :for" + +msgid "E732: Using :endfor with :while" +msgstr "E732: Bruger :endfor med :while" + +msgid "E733: Using :endwhile with :for" +msgstr "E733: Bruger :endwhile med :for" + +msgid "E601: :try nesting too deep" +msgstr "E601: :try-indlejring for dyb" + +msgid "E603: :catch without :try" +msgstr "E603: :catch uden :try" + +msgid "E604: :catch after :finally" +msgstr "E604: :catch efter :finally" + +msgid "E606: :finally without :try" +msgstr "E606: :finally uden :try" + +msgid "E607: multiple :finally" +msgstr "E607: flere :finally" + +msgid "E602: :endtry without :try" +msgstr "E602: :endtry uden :try" + +msgid "E193: :endfunction not inside a function" +msgstr "E193: :endfunction ikke i en funktion" + +msgid "E788: Not allowed to edit another buffer now" +msgstr "E788: Ikke tilladt at redigere anden buffer nu" + +msgid "E811: Not allowed to change buffer information now" +msgstr "E811: Ikke tilladt at ændre bufferinformation nu" + +msgid "tagname" +msgstr "tagnavn" + +msgid " kind file\n" +msgstr " kind-fil\n" + +msgid "'history' option is zero" +msgstr "'history'-tilvalget er nul" + +#, c-format +msgid "" +"\n" +"# %s History (newest to oldest):\n" +msgstr "" +"\n" +"# %s Historik (nyeste til ældste):\n" + +msgid "Command Line" +msgstr "Kommandolinje" + +msgid "Search String" +msgstr "Søgestreng" + +msgid "Expression" +msgstr "Udtryk" + +msgid "Input Line" +msgstr "Inputlinje" + +msgid "Debug Line" +msgstr "Fejlretningslinje" + +msgid "E198: cmd_pchar beyond the command length" +msgstr "E198: cmd_pchar efter kommandolængden" + +msgid "E199: Active window or buffer deleted" +msgstr "E199: Aktivt vindue eller buffer slettet" + +msgid "E812: Autocommands changed buffer or buffer name" +msgstr "E812: Autokommandoer ændrede buffer eller buffernavn" + +msgid "Illegal file name" +msgstr "Ulovlig filnavn" + +msgid "is a directory" +msgstr "er en mappe" + +msgid "is not a file" +msgstr "er ikke en fil" + +msgid "is a device (disabled with 'opendevice' option)" +msgstr "er en enhed (deaktiveret med 'opendevice'-tilvalget)" + +msgid "[New File]" +msgstr "[Ny fil]" + +msgid "[New DIRECTORY]" +msgstr "[Ny MAPPE]" + +msgid "[File too big]" +msgstr "[Filen er for stor]" + +msgid "[Permission Denied]" +msgstr "[Tilladelse nægtet]" + +msgid "E200: *ReadPre autocommands made the file unreadable" +msgstr "E200: *ReadPre-autokommandoer gjorde filen ulæselig" + +msgid "E201: *ReadPre autocommands must not change current buffer" +msgstr "E201: *ReadPre-autokommandoer mÃ¥ ikke ændre nuværende buffer" + +msgid "Vim: Reading from stdin...\n" +msgstr "Vim: Læser fra stdin...\n" + +msgid "Reading from stdin..." +msgstr "Læser fra stdin..." + +msgid "E202: Conversion made file unreadable!" +msgstr "E202: Konvertering gjorde filen ulæselig!" + +msgid "[fifo/socket]" +msgstr "[fifo/sokkel]" + +msgid "[fifo]" +msgstr "[fifo]" + +msgid "[socket]" +msgstr "[sokkel]" + +msgid "[character special]" +msgstr "[character special]" + +msgid "[CR missing]" +msgstr "[CR mangler]" + +msgid "[long lines split]" +msgstr "[opdeling af lange linjer]" + +msgid "[NOT converted]" +msgstr "[IKKE konverteret]" + +msgid "[converted]" +msgstr "[konverteret]" + +#, c-format +msgid "[CONVERSION ERROR in line %ld]" +msgstr "[KONVERTERINGSFEJL pÃ¥ linje %ld]" + +#, c-format +msgid "[ILLEGAL BYTE in line %ld]" +msgstr "[ULOVLIG BYTE pÃ¥ linje %ld]" + +msgid "[READ ERRORS]" +msgstr "[LÆSEFEJL]" + +msgid "Can't find temp file for conversion" +msgstr "Kan ikke finde midlertidig fil til konvertering" + +msgid "Conversion with 'charconvert' failed" +msgstr "Konvertering med 'charconvert' mislykkedes" + +msgid "can't read output of 'charconvert'" +msgstr "kan ikke læse output af 'charconvert'" + +msgid "E676: No matching autocommands for acwrite buffer" +msgstr "E676: Ingen matchende autokommandoer for acwrite-buffer" + +msgid "E203: Autocommands deleted or unloaded buffer to be written" +msgstr "E203: Autokommandoer slettet eller udlæste buffer som skal skrives" + +msgid "E204: Autocommand changed number of lines in unexpected way" +msgstr "E204: Autokommando ændrede antal linjer pÃ¥ en uventede mÃ¥de" + +msgid "NetBeans disallows writes of unmodified buffers" +msgstr "NetBeans tillader ikke skrivninger af uændrede buffere" + +msgid "Partial writes disallowed for NetBeans buffers" +msgstr "Ufuldstændige skrivninger er ikke tilladt for NetBeans-buffere" + +msgid "is not a file or writable device" +msgstr "er ikke en fil eller enhed som der kan skrives til" + +msgid "writing to device disabled with 'opendevice' option" +msgstr "skrivning til enhed er deaktiveret med 'opendevice'-tilvalget" + +msgid "is read-only (add ! to override)" +msgstr "er skrivebeskyttet (tilføj ! for at tilsidesætte)" + +msgid "E506: Can't write to backup file (add ! to override)" +msgstr "" +"E506: Kan ikke skrive til sikkerhedskopieret fil (tilføj ! for at " +"tilsidesætte)" + +msgid "E507: Close error for backup file (add ! to override)" +msgstr "" +"E507: Fejl ved lukning af sikkerhedskopieret fil (tilføj ! for at " +"tilsidesætte)" + +msgid "E508: Can't read file for backup (add ! to override)" +msgstr "" +"E508: Kan ikke læse fil til sikkerhedskopiering (tilføj ! for at " +"tilsidesætte)" + +msgid "E509: Cannot create backup file (add ! to override)" +msgstr "" +"E509: Kan ikke oprette (create) sikkerhedskopieret fil (tilføj ! for at " +"tilsidesætte)" + +msgid "E510: Can't make backup file (add ! to override)" +msgstr "" +"E510: Kan ikke oprette (make) sikkerhedskopieret fil (tilføj ! for at " +"tilsidesætte)" + +msgid "E214: Can't find temp file for writing" +msgstr "E214: Kan ikke finde midlertidig fil til skrivning" + +msgid "E213: Cannot convert (add ! to write without conversion)" +msgstr "E213: Kan ikke konvertere (tilføj ! for at skrive uden konvertering)" + +msgid "E166: Can't open linked file for writing" +msgstr "E166: Kan ikke Ã¥bne linket fil til skrivning" + +msgid "E212: Can't open file for writing" +msgstr "E212: Kan ikke Ã¥bne filen til skrivning" + +msgid "E949: File changed while writing" +msgstr "E949: Filen blev ændret ved skrivning" + +msgid "E512: Close failed" +msgstr "E512: Lukning mislykkedes" + +msgid "E513: write error, conversion failed (make 'fenc' empty to override)" +msgstr "" +"E513: fejl ved skrivning, konvertering mislykkedes (gør 'fenc' tom for at " +"tilsidesætte)" + +#, c-format +msgid "" +"E513: write error, conversion failed in line %ld (make 'fenc' empty to " +"override)" +msgstr "" +"E513: fejl ved skrivning, konvertering mislykkedes pÃ¥ linje %ld (gør 'fenc' " +"tom for at tilsidesætte)" + +msgid "E514: write error (file system full?)" +msgstr "E514: skrivefejl (er filsystemet fuldt?)" + +msgid " CONVERSION ERROR" +msgstr " KONVERTERINGSFEJL" + +#, c-format +msgid " in line %ld;" +msgstr " pÃ¥ linje %ld;" + +msgid "[Device]" +msgstr "[Enhed]" + +msgid "[New]" +msgstr "[Ny]" + +msgid " [a]" +msgstr " [a]" + +msgid " appended" +msgstr " tilføjet i slutningen" + +msgid " [w]" +msgstr " [s]" + +msgid " written" +msgstr " skrevet" + +msgid "E205: Patchmode: can't save original file" +msgstr "E205: Patchmode: kan ikke gemme original fil" + +msgid "E206: patchmode: can't touch empty original file" +msgstr "E206: patchmode: kan ikke touch tom original fil" + +msgid "E207: Can't delete backup file" +msgstr "E207: Kan ikke slette sikkerhedskopieret fil" + +msgid "" +"\n" +"WARNING: Original file may be lost or damaged\n" +msgstr "" +"\n" +"ADVARSEL: Den originale fil kan man mistet eller beskadiget\n" + +msgid "don't quit the editor until the file is successfully written!" +msgstr "afslut ikke editoren inden filen er blevet skrevet!" + +msgid "[dos]" +msgstr "[dos]" + +msgid "[dos format]" +msgstr "[dos-format]" + +msgid "[mac]" +msgstr "[mac]" + +msgid "[mac format]" +msgstr "[mac-format]" + +msgid "[unix]" +msgstr "[unix]" + +msgid "[unix format]" +msgstr "[unix-format]" + +msgid "1 line, " +msgstr "1 linje, " + +#, c-format +msgid "%ld lines, " +msgstr "%ld linjer, " + +msgid "1 character" +msgstr "1 tegn" + +#, c-format +msgid "%lld characters" +msgstr "%lld tegn" + +msgid "[noeol]" +msgstr "[ingen eol]" + +msgid "[Incomplete last line]" +msgstr "[Ufuldstændig sidste linje]" + +msgid "WARNING: The file has been changed since reading it!!!" +msgstr "ADVARSEL: Filen er blevet ændret siden den blev læst!!!" + +msgid "Do you really want to write to it" +msgstr "Vil du virkelig skrive den" + +#, c-format +msgid "E208: Error writing to \"%s\"" +msgstr "E208: Fejl ved skrivning til \"%s\"" + +#, c-format +msgid "E209: Error closing \"%s\"" +msgstr "E209: Fejl ved lukning af \"%s\"" + +#, c-format +msgid "E210: Error reading \"%s\"" +msgstr "E210: Fejl ved læsning af \"%s\"" + +msgid "E246: FileChangedShell autocommand deleted buffer" +msgstr "E246: FileChangedShell-autokommando slettede buffer" + +#, c-format +msgid "E211: File \"%s\" no longer available" +msgstr "E211: Filen \"%s\" er ikke længere tilgængelig" + +#, c-format +msgid "" +"W12: Warning: File \"%s\" has changed and the buffer was changed in Vim as " +"well" +msgstr "" +"W12: Advarsel: Filen \"%s\" er blevet ændret og bufferen blev ogsÃ¥ ændret i " +"Vim" + +msgid "See \":help W12\" for more info." +msgstr "Se \":help W12\" for mere info." + +#, c-format +msgid "W11: Warning: File \"%s\" has changed since editing started" +msgstr "W11: Advarsel: Filen \"%s\" er blevet ændret siden redigeringen startede" + +msgid "See \":help W11\" for more info." +msgstr "Se \":help W11\" for mere info." + +#, c-format +msgid "W16: Warning: Mode of file \"%s\" has changed since editing started" +msgstr "" +"W16: Advarsel: Tilstanden af filen \"%s\" er blevet ændret siden redigeringen " +"startede" + +msgid "See \":help W16\" for more info." +msgstr "Se \":help W16\" for mere info." + +#, c-format +msgid "W13: Warning: File \"%s\" has been created after editing started" +msgstr "" +"W13: Advarsel: Filen \"%s\" er blevet oprettet efter redigeringen startede" + +msgid "Warning" +msgstr "Advarsel" + +msgid "" +"&OK\n" +"&Load File" +msgstr "" +"&OK\n" +"&Indlæs fil" + +#, c-format +msgid "E462: Could not prepare for reloading \"%s\"" +msgstr "E462: Kunne ikke forbedre til genindlæsning af \"%s\"" + +#, c-format +msgid "E321: Could not reload \"%s\"" +msgstr "E321: Kunne ikke genindlæse \"%s\"" + +msgid "--Deleted--" +msgstr "--Slettet--" + +#, c-format +msgid "auto-removing autocommand: %s <buffer=%d>" +msgstr "auto-removing-autokommando: %s <buffer=%d>" + +#, c-format +msgid "E367: No such group: \"%s\"" +msgstr "E367: Ingen sÃ¥dan gruppe: \"%s\"" + +msgid "E936: Cannot delete the current group" +msgstr "E936: Kan ikke slette den nuværende gruppe" + +msgid "W19: Deleting augroup that is still in use" +msgstr "W19: Sletter augroup som stadig er i brug" + +#, c-format +msgid "E215: Illegal character after *: %s" +msgstr "E215: Ulovligt tegn efter *: %s" + +#, c-format +msgid "E216: No such event: %s" +msgstr "E216: Ingen sÃ¥dan hændelse: %s" + +#, c-format +msgid "E216: No such group or event: %s" +msgstr "E216: Ingen sÃ¥dan gruppe eller hændelse: %s" + +msgid "" +"\n" +"--- Auto-Commands ---" +msgstr "" +"\n" +"--- Auto-kommandoer ---" + +#, c-format +msgid "E680: <buffer=%d>: invalid buffer number " +msgstr "E680: <buffer=%d>: ugyldigt buffernummer " + +msgid "E217: Can't execute autocommands for ALL events" +msgstr "E217: Kan ikke udføre autokommandoer for ALLE hændelser" + +msgid "No matching autocommands" +msgstr "Ingen matchende autokommandoer" + +msgid "E218: autocommand nesting too deep" +msgstr "E218: autokommando indlejret for dyb" + +#, c-format +msgid "%s Auto commands for \"%s\"" +msgstr "%s Auto-kommandoer for \"%s\"" + +#, c-format +msgid "Executing %s" +msgstr "Udfører %s" + +#, c-format +msgid "autocommand %s" +msgstr "autokommando %s" + +msgid "E219: Missing {." +msgstr "E219: Manglende {." + +msgid "E220: Missing }." +msgstr "E220: Manglende }." + +msgid "E490: No fold found" +msgstr "E490: Ingen sammenfoldning fundet" + +msgid "E350: Cannot create fold with current 'foldmethod'" +msgstr "E350: Kan ikke oprette sammenfoldning med nuværende 'foldmethod'" + +msgid "E351: Cannot delete fold with current 'foldmethod'" +msgstr "E351: Kan ikke slette sammenfoldning med nuværende 'foldmethod'" + +#, c-format +msgid "+--%3ld line folded " +msgid_plural "+--%3ld lines folded " +msgstr[0] "+--%3ld linje sammenfoldet " +msgstr[1] "+--%3ld linjer sammenfoldet " + +msgid "E222: Add to read buffer" +msgstr "E222: Tilføj til læsebuffer" + +msgid "E223: recursive mapping" +msgstr "E223: rekursiv mapping" + +#, c-format +msgid "E224: global abbreviation already exists for %s" +msgstr "E224: global forkortelse findes allerede for %s" + +#, c-format +msgid "E225: global mapping already exists for %s" +msgstr "E225: global mapping findes allerede for %s" + +#, c-format +msgid "E226: abbreviation already exists for %s" +msgstr "E226: forkortelse findes allerede for %s" + +#, c-format +msgid "E227: mapping already exists for %s" +msgstr "E227: mapping findes allerede for %s" + +msgid "No abbreviation found" +msgstr "Ingen forkortelse fundet" + +msgid "No mapping found" +msgstr "Ingen mapping fundet" + +msgid "E228: makemap: Illegal mode" +msgstr "E228: makemap: Ulovlig tilstand" + +msgid "E851: Failed to create a new process for the GUI" +msgstr "E851: Kunne ikke oprette en ny proces for GUI'en" + +msgid "E852: The child process failed to start the GUI" +msgstr "E852: Barneprocessen kunne ikke starte GUI'en" + +msgid "E229: Cannot start the GUI" +msgstr "E229: Kan ikke starte GUI'en" + +#, c-format +msgid "E230: Cannot read from \"%s\"" +msgstr "E230: Kan ikke læse fra \"%s\"" + +msgid "E665: Cannot start GUI, no valid font found" +msgstr "E665: Kan ikke starte GUI, ingen gyldig skrifttype fundet" + +msgid "E231: 'guifontwide' invalid" +msgstr "E231: 'guifontwide' ugyldig" + +msgid "E599: Value of 'imactivatekey' is invalid" +msgstr "E599: Værdien af 'imactivatekey' er ugyldig" + +#, c-format +msgid "E254: Cannot allocate color %s" +msgstr "E254: Kan ikke allokere farven %s" + +msgid "No match at cursor, finding next" +msgstr "Intet match ved markør, finder næste" + +msgid "<cannot open> " +msgstr "<kan ikke Ã¥bne> " + +#, c-format +msgid "E616: vim_SelFile: can't get font %s" +msgstr "E616: vim_SelFile: kan ikke hente skrifttypen %s" + +msgid "E614: vim_SelFile: can't return to current directory" +msgstr "E614: vim_SelFile: kan ikke vende tilbage til nuværende mappe" + +msgid "Pathname:" +msgstr "Stinavn:" + +msgid "E615: vim_SelFile: can't get current directory" +msgstr "E615: vim_SelFile: kan ikke hente nuværende mappe" + +msgid "OK" +msgstr "OK" + +msgid "Cancel" +msgstr "Annuller" + +msgid "Scrollbar Widget: Could not get geometry of thumb pixmap." +msgstr "" +"Rullebjælke-widget: Kunne ikke hente geometri eller pixelkort til miniature." + +msgid "Vim dialog" +msgstr "Vim-dialog" + +msgid "E232: Cannot create BalloonEval with both message and callback" +msgstr "E232: Kan ikke oprette BalloonEval med bÃ¥de meddelelse og callback" + +msgid "_Cancel" +msgstr "_Annuller" + +msgid "_Save" +msgstr "_Gem" + +msgid "_Open" +msgstr "_Ã…bn" + +msgid "_OK" +msgstr "_OK" + +msgid "" +"&Yes\n" +"&No\n" +"&Cancel" +msgstr "" +"&Ja\n" +"&Nej\n" +"&Annuller" + +msgid "Yes" +msgstr "Ja" + +msgid "No" +msgstr "Nej" + +msgid "Input _Methods" +msgstr "Input_metoder" + +msgid "VIM - Search and Replace..." +msgstr "VIM - Søg og erstat..." + +msgid "VIM - Search..." +msgstr "VIM - Søg..." + +msgid "Find what:" +msgstr "Find hvad:" + +msgid "Replace with:" +msgstr "Erstat med:" + +msgid "Match whole word only" +msgstr "Match kun hele ord" + +msgid "Match case" +msgstr "Der skelnes ikke mellem store og smÃ¥ bogstaver" + +msgid "Direction" +msgstr "Retning" + +msgid "Up" +msgstr "Op" + +msgid "Down" +msgstr "Ned" + +msgid "Find Next" +msgstr "Find næste" + +msgid "Replace" +msgstr "Erstat" + +msgid "Replace All" +msgstr "Erstat alle" + +msgid "_Close" +msgstr "_Luk" + +msgid "Vim: Received \"die\" request from session manager\n" +msgstr "Vim: Modtog \"die\"-anmodning fra sessionshÃ¥ndtering\n" + +msgid "Close tab" +msgstr "Luk faneblad" + +msgid "New tab" +msgstr "Nyt faneblad" + +msgid "Open Tab..." +msgstr "Ã…bn faneblad..." + +msgid "Vim: Main window unexpectedly destroyed\n" +msgstr "Vim: Hovedvindue uventet ødelagt\n" + +msgid "&Filter" +msgstr "&Filter" + +msgid "&Cancel" +msgstr "&Annuller" + +msgid "Directories" +msgstr "Mapper" + +msgid "Filter" +msgstr "Filter" + +msgid "&Help" +msgstr "&Hjælp" + +msgid "Files" +msgstr "Filer" + +msgid "&OK" +msgstr "&OK" + +msgid "Selection" +msgstr "Markering" + +msgid "Find &Next" +msgstr "Find &næste" + +msgid "&Replace" +msgstr "&Erstat" + +msgid "Replace &All" +msgstr "Erstat &alle" + +msgid "&Undo" +msgstr "&Fortryd" + +msgid "Open tab..." +msgstr "Ã…bn faneblad..." + +msgid "Find string (use '\\\\' to find a '\\')" +msgstr "Find streng (brug '\\\\' til at finde et '\\')" + +msgid "Find & Replace (use '\\\\' to find a '\\')" +msgstr "Find og erstat (brug '\\\\' til at finde et '\\')" + +msgid "Not Used" +msgstr "Ikke brugt" + +msgid "Directory\t*.nothing\n" +msgstr "Mappe\t\t*.nothing\n" + +#, c-format +msgid "E671: Cannot find window title \"%s\"" +msgstr "E671: Kan ikke finde vinduestitlen \"%s\"" + +#, c-format +msgid "E243: Argument not supported: \"-%s\"; Use the OLE version." +msgstr "E243: Argumentet understøttes ikke: \"-%s\"; Brug OLE-versionen." + +msgid "E672: Unable to open window inside MDI application" +msgstr "E672: Kan ikke Ã¥bne vindue i MDI-program" + +msgid "Vim E458: Cannot allocate colormap entry, some colors may be incorrect" +msgstr "" +"Vim E458: Kan ikke allokere colormap-post, nogle farver kan være forkerte" + +#, c-format +msgid "E250: Fonts for the following charsets are missing in fontset %s:" +msgstr "E250: Skrifttyper for følgende tegnsæt mangler i skrifttypesættet %s:" + +#, c-format +msgid "E252: Fontset name: %s" +msgstr "E252: Skrifttypesætnavn: %s" + +#, c-format +msgid "Font '%s' is not fixed-width" +msgstr "Skrifttypen '%s' er ikke med fast bredde" + +#, c-format +msgid "E253: Fontset name: %s" +msgstr "E253: Skrifttypesætnavn: %s" + +#, c-format +msgid "Font0: %s" +msgstr "Skrifttype0: %s" + +#, c-format +msgid "Font1: %s" +msgstr "Skrifttype1: %s" + +#, c-format +msgid "Font%ld width is not twice that of font0" +msgstr "Bredden pÃ¥ skrifttype%ld er ikke det dobbelte af skrifttype0" + +#, c-format +msgid "Font0 width: %ld" +msgstr "Bredden pÃ¥ skrifttype0: %ld" + +#, c-format +msgid "Font1 width: %ld" +msgstr "Bredden pÃ¥ skrifttype1: %ld" + +msgid "Invalid font specification" +msgstr "Ugyldig skrifttypespecifikation" + +msgid "&Dismiss" +msgstr "&Luk" + +msgid "no specific match" +msgstr "intet specifikt match" + +msgid "Vim - Font Selector" +msgstr "Vim - Skrifttypevælger" + +msgid "Name:" +msgstr "Navn:" + +msgid "Show size in Points" +msgstr "Vis størrelse i punkter" + +msgid "Encoding:" +msgstr "Kodning:" + +msgid "Font:" +msgstr "Skrifttype:" + +msgid "Style:" +msgstr "Stil:" + +msgid "Size:" +msgstr "Størrelse:" + +msgid "E256: Hangul automata ERROR" +msgstr "E256: FEJL ved Hangul automata" + +msgid "E550: Missing colon" +msgstr "E550: Manglende kolon" + +msgid "E551: Illegal component" +msgstr "E551: Ulovlig komponent" + +msgid "E552: digit expected" +msgstr "E552: ciffer ventet" + +#, c-format +msgid "Page %d" +msgstr "Side %d" + +msgid "No text to be printed" +msgstr "Ingen tekst at udskrive" + +#, c-format +msgid "Printing page %d (%d%%)" +msgstr "Udskriver side %d (%d%%)" + +#, c-format +msgid " Copy %d of %d" +msgstr " Kopi %d af %d" + +#, c-format +msgid "Printed: %s" +msgstr "Udskrev: %s" + +msgid "Printing aborted" +msgstr "Udskrivning afbrudt" + +msgid "E455: Error writing to PostScript output file" +msgstr "E455: Fejl ved skrivning til PostScript-output-fil" + +#, c-format +msgid "E624: Can't open file \"%s\"" +msgstr "E624: Kan ikke Ã¥bne filen \"%s\"" + +#, c-format +msgid "E457: Can't read PostScript resource file \"%s\"" +msgstr "E457: Kan ikke læse PostScript-ressourcefilen \"%s\"" + +#, c-format +msgid "E618: file \"%s\" is not a PostScript resource file" +msgstr "E618: filen \"%s\" er ikke en PostScript-ressourcefil" + +#, c-format +msgid "E619: file \"%s\" is not a supported PostScript resource file" +msgstr "E619: filen \"%s\" er ikke en understøttet PostScript-ressourcefil" + +#, c-format +msgid "E621: \"%s\" resource file has wrong version" +msgstr "E621: \"%s\"-ressourcefilen har forkert version" + +msgid "E673: Incompatible multi-byte encoding and character set." +msgstr "E673: Inkompatibel multibyte-kodning og -tegnsæt." + +msgid "E674: printmbcharset cannot be empty with multi-byte encoding." +msgstr "E674: printmbcharset mÃ¥ ikke være tom med multibyte-kodning." + +msgid "E675: No default font specified for multi-byte printing." +msgstr "E675: Ingen standardskrifttype angivet for multibyte-udskrivning." + +msgid "E324: Can't open PostScript output file" +msgstr "E324: Kan ikke Ã¥bne PostScript-output-fil" + +#, c-format +msgid "E456: Can't open file \"%s\"" +msgstr "E456: Kan ikke Ã¥bne filen \"%s\"" + +msgid "E456: Can't find PostScript resource file \"prolog.ps\"" +msgstr "E456: Kan ikke finde PostScript-ressourcefilen \"prolog.ps\"" + +msgid "E456: Can't find PostScript resource file \"cidfont.ps\"" +msgstr "E456: Kan ikke finde PostScript-ressourcefilen \"cidfont.ps\"" + +#, c-format +msgid "E456: Can't find PostScript resource file \"%s.ps\"" +msgstr "E456: Kan ikke finde PostScript-ressourcefilen \"%s.ps\"" + +#, c-format +msgid "E620: Unable to convert to print encoding \"%s\"" +msgstr "E620: Kan ikke konvertere til udskrivningskodningen \"%s\"" + +msgid "Sending to printer..." +msgstr "Sender til printer..." + +msgid "E365: Failed to print PostScript file" +msgstr "E365: Kunne ikke udskrive PostScript-fil" + +msgid "Print job sent." +msgstr "Udskrivningsjob sendt." + +msgid "Add a new database" +msgstr "Tilføj en ny database" + +msgid "Query for a pattern" +msgstr "Forespørgsel til et mønster" + +msgid "Show this message" +msgstr "Vis denne meddelelse" + +msgid "Kill a connection" +msgstr "Dræb en forbindelse" + +msgid "Reinit all connections" +msgstr "Geninitialisere alle forbindelser" + +msgid "Show connections" +msgstr "Vis forbindelser" + +#, c-format +msgid "E560: Usage: cs[cope] %s" +msgstr "E560: Anvendelse: cs[cope] %s" + +msgid "This cscope command does not support splitting the window.\n" +msgstr "Denne cscope-kommando understøtter ikke opdeling af vinduet.\n" + +msgid "E562: Usage: cstag <ident>" +msgstr "E562: Anvendelse: cstag <ident>" + +msgid "E257: cstag: tag not found" +msgstr "E257: cstag: tag ikke fundet" + +#, c-format +msgid "E563: stat(%s) error: %d" +msgstr "E563: fejl ved stat(%s): %d" + +msgid "E563: stat error" +msgstr "E563: fejl ved stat" + +#, c-format +msgid "E564: %s is not a directory or a valid cscope database" +msgstr "E564: %s er ikke en mappe eller en gyldig cscope-database" + +#, c-format +msgid "Added cscope database %s" +msgstr "Tilføjede cscope-databasen %s" + +#, c-format +msgid "E262: error reading cscope connection %ld" +msgstr "E262: fejl ved læsning af cscope-forbindelse %ld" + +msgid "E561: unknown cscope search type" +msgstr "E561: ukendt cscope-søgetype" + +msgid "E566: Could not create cscope pipes" +msgstr "E566: Kunne ikke oprette cscope-pipes" + +msgid "E622: Could not fork for cscope" +msgstr "E622: Kunne ikke fork for cscope" + +msgid "cs_create_connection setpgid failed" +msgstr "cs_create_connection setpgid mislykkedes" + +msgid "cs_create_connection exec failed" +msgstr "cs_create_connection exec mislykkedes" + +msgid "cs_create_connection: fdopen for to_fp failed" +msgstr "cs_create_connection: fdopen for to_fp mislykkedes" + +msgid "cs_create_connection: fdopen for fr_fp failed" +msgstr "cs_create_connection: fdopen for fr_fp mislykkedes" + +msgid "E623: Could not spawn cscope process" +msgstr "E623: Kunne ikke spawn cscope-proces" + +msgid "E567: no cscope connections" +msgstr "E567: ingen cscope-forbindelser" + +#, c-format +msgid "E469: invalid cscopequickfix flag %c for %c" +msgstr "E469: ugyldigt cscopequickfix-flag %c for %c" + +#, c-format +msgid "E259: no matches found for cscope query %s of %s" +msgstr "E259: ingen match fundet for cscope-forespørgsel %s af %s" + +msgid "cscope commands:\n" +msgstr "cscope-kommandoer:\n" + +#, c-format +msgid "%-5s: %s%*s (Usage: %s)" +msgstr "%-5s: %s%*s (Anvendelse: %s)" + +msgid "" +"\n" +" a: Find assignments to this symbol\n" +" c: Find functions calling this function\n" +" d: Find functions called by this function\n" +" e: Find this egrep pattern\n" +" f: Find this file\n" +" g: Find this definition\n" +" i: Find files #including this file\n" +" s: Find this C symbol\n" +" t: Find this text string\n" +msgstr "" +"\n" +" a: Find tildelinger til symbolet\n" +" c: Find funktioner som kalder funktionen\n" +" d: Find funktioner som kaldes af funktionen\n" +" e: Find dette egrep-mønster\n" +" f: Find filen\n" +" g: Find definitionen\n" +" i: Find filer som #inkludere filen\n" +" s: Find C-symbolet\n" +" t: Find tekststrengen\n" + +#, c-format +msgid "E625: cannot open cscope database: %s" +msgstr "E625: kan ikke Ã¥bne cscope-database: %s" + +msgid "E626: cannot get cscope database information" +msgstr "E626: kan ikke hente information for cscope-database" + +msgid "E568: duplicate cscope database not added" +msgstr "E568: duplikeret cscope-database ikke tilføjet" + +#, c-format +msgid "E261: cscope connection %s not found" +msgstr "E261: cscope-forbindelsen %s ikke fundet" + +#, c-format +msgid "cscope connection %s closed" +msgstr "cscope-forbindelsen %s lukket" + +msgid "E570: fatal error in cs_manage_matches" +msgstr "E570: fatal fejl i cs_manage_matches" + +#, c-format +msgid "Cscope tag: %s" +msgstr "Cscope-tag: %s" + +msgid "" +"\n" +" # line" +msgstr "" +"\n" +" # linje" + +msgid "filename / context / line\n" +msgstr "filnavn/kontekst/linje\n" + +#, c-format +msgid "E609: Cscope error: %s" +msgstr "E609: Fejl ved cscope: %s" + +msgid "All cscope databases reset" +msgstr "Alle cscope-databaser nulstillet" + +msgid "no cscope connections\n" +msgstr "ingen cscope-forbindelser\n" + +msgid " # pid database name prepend path\n" +msgstr " # pid databasenavn prepend-sti\n" + +msgid "Lua library cannot be loaded." +msgstr "Lua-bibliotek kan ikke indlæses." + +msgid "cannot save undo information" +msgstr "kan ikke gemme fortrydinformation" + +msgid "" +"E815: Sorry, this command is disabled, the MzScheme libraries could not be " +"loaded." +msgstr "" +"E815: Beklager, kommandoen er deaktiveret, MzScheme-bibliotekerne kunne ikke " +"indlæses." + +msgid "" +"E895: Sorry, this command is disabled, the MzScheme's racket/base module " +"could not be loaded." +msgstr "" +"E895: Beklager, kommandoen er deaktiveret, MzScheme's racket-/base-modul " +"kunne ikke indlæses." + +msgid "invalid expression" +msgstr "ugyldigt udtryk" + +msgid "expressions disabled at compile time" +msgstr "udtryk deaktiveret ved kompileringstid" + +msgid "hidden option" +msgstr "skjult tilvalg" + +msgid "unknown option" +msgstr "ukendt tilvalg" + +msgid "window index is out of range" +msgstr "vinduesindeks udenfor omrÃ¥de" + +msgid "couldn't open buffer" +msgstr "kan ikke Ã¥bne buffer" + +msgid "cannot delete line" +msgstr "kan ikke slette linje" + +msgid "cannot replace line" +msgstr "kan ikke erstatte linje" + +msgid "cannot insert line" +msgstr "kan ikke indsætte linje" + +msgid "string cannot contain newlines" +msgstr "streng mÃ¥ ikke indeholde linjeskift" + +msgid "error converting Scheme values to Vim" +msgstr "fejl ved konvertering af Scheme-værdier til Vim" + +msgid "Vim error: ~a" +msgstr "Fejl ved Vim: ~a" + +msgid "Vim error" +msgstr "Fejl ved Vim" + +msgid "buffer is invalid" +msgstr "buffer er ugyldig" + +msgid "window is invalid" +msgstr "vindue er ugyldigt" + +msgid "linenr out of range" +msgstr "linjenummer udenfor omrÃ¥de" + +msgid "not allowed in the Vim sandbox" +msgstr "ikke tilladt i Vim-sandboksen" + +msgid "E836: This Vim cannot execute :python after using :py3" +msgstr "E836: Denne Vim kan ikke udføre :python efter brug af :py3" + +msgid "" +"E263: Sorry, this command is disabled, the Python library could not be " +"loaded." +msgstr "" +"E263: Beklager, kommandoen er deaktiveret, Python-biblioteket kunne ikke " +"indlæses." + +msgid "" +"E887: Sorry, this command is disabled, the Python's site module could not be " +"loaded." +msgstr "" +"E887: Beklager, kommandoen er deaktiveret, Python's site-modul kunne ikke " +"indlæses." + +msgid "E659: Cannot invoke Python recursively" +msgstr "E659: Kan ikke starte Python rekursivt" + +msgid "E837: This Vim cannot execute :py3 after using :python" +msgstr "E837: Denne Vim kan ikke udføre :py3 efter brug af :python" + +msgid "E265: $_ must be an instance of String" +msgstr "E265: $_ skal være en instans af streng" + +msgid "" +"E266: Sorry, this command is disabled, the Ruby library could not be loaded." +msgstr "" +"E266: Beklager, kommandoen er deaktiveret, Ruby-biblioteket kunne ikke " +"indlæses." + +msgid "E267: unexpected return" +msgstr "E267: uventet return" + +msgid "E268: unexpected next" +msgstr "E268: uventet next" + +msgid "E269: unexpected break" +msgstr "E269: uventet break" + +msgid "E270: unexpected redo" +msgstr "E270: uventet redo" + +msgid "E271: retry outside of rescue clause" +msgstr "E271: prøv igen udenfor rescue clause" + +msgid "E272: unhandled exception" +msgstr "E272: uhÃ¥ndteret undtagelse" + +#, c-format +msgid "E273: unknown longjmp status %d" +msgstr "E273: ukendt longjmp-status %d" + +msgid "invalid buffer number" +msgstr "ugyldigt buffernummer" + +msgid "not implemented yet" +msgstr "endnu ikke implementeret" + +msgid "cannot set line(s)" +msgstr "kan ikke sætte linje(r)" + +msgid "invalid mark name" +msgstr "ugyldigt mærkenavn" + +msgid "mark not set" +msgstr "mærke ikke sat" + +#, c-format +msgid "row %d column %d" +msgstr "række %d kolonne %d" + +msgid "cannot insert/append line" +msgstr "kan ikke indsætte/tilføje linje" + +msgid "line number out of range" +msgstr "linjenummer udenfor omrÃ¥de" + +msgid "unknown flag: " +msgstr "ukendt flag: " + +msgid "unknown vimOption" +msgstr "ukendt vimOption" + +msgid "keyboard interrupt" +msgstr "tastaturafbryd" + +msgid "vim error" +msgstr "fejl ved vim" + +msgid "cannot create buffer/window command: object is being deleted" +msgstr "kan ikke oprette buffer-/vindue-kommando: objekt slettes" + +msgid "" +"cannot register callback command: buffer/window is already being deleted" +msgstr "" +"kan ikke registrere callback-kommando: buffer/vindue er allerede ved at " +"blive slettet" + +msgid "" +"E280: TCL FATAL ERROR: reflist corrupt!? Please report this to vim-dev@vim." +"org" +msgstr "" +"E280: FATAL FEJL VED TCL: reflist korrupt!? Rapportér det venligst til vim-" +"dev@vim.org" + +msgid "cannot register callback command: buffer/window reference not found" +msgstr "" +"kan ikke registrere callback-kommando: buffer-/vindue-reference ikke fundet" + +msgid "" +"E571: Sorry, this command is disabled: the Tcl library could not be loaded." +msgstr "" +"E571: Beklager, kommandoen er deaktiveret: Tcl-biblioteket kunne ikke " +"indlæses." + +#, c-format +msgid "E572: exit code %d" +msgstr "E572: afslutningskode %d" + +msgid "cannot get line" +msgstr "kan ikke hente linje" + +msgid "Unable to register a command server name" +msgstr "Kan ikke registrere et kommandoservernavn" + +msgid "E248: Failed to send command to the destination program" +msgstr "E248: Kunne ikke sende kommando til destinationsprogrammet" + +#, c-format +msgid "E573: Invalid server id used: %s" +msgstr "E573: Ugyldigt server-id brugt: %s" + +msgid "E251: VIM instance registry property is badly formed. Deleted!" +msgstr "" +"E251: Registreringsegenskab for VIM-instans er dÃ¥rligt udformet. Slettet!" + +#, c-format +msgid "E938: Duplicate key in JSON: \"%s\"" +msgstr "E938: Duplikeret nøgle i JSON: \"%s\"" + +#, c-format +msgid "E696: Missing comma in List: %s" +msgstr "E696: Manglende komma i liste: %s" + +#, c-format +msgid "E697: Missing end of List ']': %s" +msgstr "E697: Manglende slutning pÃ¥ List ']': %s" + +msgid "Unknown option argument" +msgstr "Ukendt tilvalgsargument" + +msgid "Too many edit arguments" +msgstr "For mange redigeringsargumenter" + +msgid "Argument missing after" +msgstr "Argument mangler efter" + +msgid "Garbage after option argument" +msgstr "Affald efter tilvalgsargument" + +msgid "Too many \"+command\", \"-c command\" or \"--cmd command\" arguments" +msgstr "" +"For mange \"+kommando\"-, \"-c kommando\"- eller \"--cmd kommando\"-argumenter" + +msgid "Invalid argument for" +msgstr "Ugyldigt argument for" + +#, c-format +msgid "%d files to edit\n" +msgstr "%d filer at redigere\n" + +msgid "netbeans is not supported with this GUI\n" +msgstr "netbeans understøttes ikke med denne GUI\n" + +msgid "'-nb' cannot be used: not enabled at compile time\n" +msgstr "'-nb' kan ikke bruges: ikke aktiveret ved kompileringstid\n" + +msgid "This Vim was not compiled with the diff feature." +msgstr "Denne Vim blev ikke kompileret med diff-funktionaliteten." + +msgid "Attempt to open script file again: \"" +msgstr "Forsøg pÃ¥ at Ã¥bne scriptfil igen: \"" + +msgid "Cannot open for reading: \"" +msgstr "Kan ikke Ã¥bne til læsning: \"" + +msgid "Cannot open for script output: \"" +msgstr "Kan ikke Ã¥bne for script-output: \"" + +msgid "Vim: Error: Failure to start gvim from NetBeans\n" +msgstr "Vim: Fejl: Kunne ikke starte gvim fra NetBeans\n" + +msgid "Vim: Error: This version of Vim does not run in a Cygwin terminal\n" +msgstr "Vim: Fejl: Denne version af Vim kører ikke i en Cygwin-terminal\n" + +msgid "Vim: Warning: Output is not to a terminal\n" +msgstr "Vim: Advarsel: Output er ikke til en terminal\n" + +msgid "Vim: Warning: Input is not from a terminal\n" +msgstr "Vim: Advarsel: Input er ikke fra en terminal\n" + +msgid "pre-vimrc command line" +msgstr "pre-vimrc-kommandolinje" + +#, c-format +msgid "E282: Cannot read from \"%s\"" +msgstr "E282: Kan ikke læse fra \"%s\"" + +msgid "" +"\n" +"More info with: \"vim -h\"\n" +msgstr "" +"\n" +"Mere info med: \"vim -h\"\n" + +msgid "[file ..] edit specified file(s)" +msgstr "[fil ..] rediger angivne fil(er)" + +msgid "- read text from stdin" +msgstr "- læs tekst fra stdin" + +msgid "-t tag edit file where tag is defined" +msgstr "-t tag rediger fil hvor tag er defineret" + +msgid "-q [errorfile] edit file with first error" +msgstr "-q [fejlfil] rediger fil med første fejl" + +msgid "" +"\n" +"\n" +"usage:" +msgstr "" +"\n" +"\n" +"anvendelse:" + +msgid " vim [arguments] " +msgstr " vim [argumenter] " + +msgid "" +"\n" +" or:" +msgstr "" +"\n" +" eller:" + +msgid "" +"\n" +"Where case is ignored prepend / to make flag upper case" +msgstr "" +"\n" +"NÃ¥r der ikke skelnes mellem store og smÃ¥ bogstaver, sÃ¥ tilføj / i " +"begyndelsen for at gøre flag til store bogstaver" + +msgid "" +"\n" +"\n" +"Arguments:\n" +msgstr "" +"\n" +"\n" +"Argumenter:\n" + +msgid "--\t\t\tOnly file names after this" +msgstr "--\t\t\tKun filnavne herefter" + +msgid "--literal\t\tDon't expand wildcards" +msgstr "--literal\t\tUdvid ikke jokertegn" + +msgid "-register\t\tRegister this gvim for OLE" +msgstr "-register\t\tRegistrer denne gvim til OLE" + +msgid "-unregister\t\tUnregister gvim for OLE" +msgstr "-unregister\t\tAfregistrer gvim for OLE" + +msgid "-g\t\t\tRun using GUI (like \"gvim\")" +msgstr "-g\t\t\tKør med GUI (ligesom \"gvim\")" + +msgid "-f or --nofork\tForeground: Don't fork when starting GUI" +msgstr "-f eller --nofork\tForgrund: Fork ikke nÃ¥r GUI startes" + +msgid "-v\t\t\tVi mode (like \"vi\")" +msgstr "-v\t\t\tVi-tilstand (ligesom \"vi\")" + +msgid "-e\t\t\tEx mode (like \"ex\")" +msgstr "-e\t\t\tEx-tilstand (ligesom \"ex\")" + +msgid "-E\t\t\tImproved Ex mode" +msgstr "-E\t\t\tForbedret Ex-tilstand" + +msgid "-s\t\t\tSilent (batch) mode (only for \"ex\")" +msgstr "-s\t\t\tStille (batch) tilstand (kun til \"ex\")" + +msgid "-d\t\t\tDiff mode (like \"vimdiff\")" +msgstr "-d\t\t\tDiff-tilstand (ligesom \"vimdiff\")" + +msgid "-y\t\t\tEasy mode (like \"evim\", modeless)" +msgstr "-y\t\t\tEasy-tilstand (ligesom \"evim\", tilstandsløs)" + +msgid "-R\t\t\tReadonly mode (like \"view\")" +msgstr "-R\t\t\tSkrivebeskyttet tilstand (ligesom \"view\")" + +msgid "-Z\t\t\tRestricted mode (like \"rvim\")" +msgstr "-Z\t\t\tRestriktiv tilstand (ligesom \"rvim\")" + +msgid "-m\t\t\tModifications (writing files) not allowed" +msgstr "-m\t\t\tÆndringer (skrivning af filer) ikke tilladt" + +msgid "-M\t\t\tModifications in text not allowed" +msgstr "-M\t\t\tÆndringer i tekst ikke tilladt" + +msgid "-b\t\t\tBinary mode" +msgstr "-b\t\t\tBinær tilstand" + +msgid "-l\t\t\tLisp mode" +msgstr "-l\t\t\tLisp-tilstand" + +msgid "-C\t\t\tCompatible with Vi: 'compatible'" +msgstr "-C\t\t\tKompatibel med Vi: 'compatible'" + +msgid "-N\t\t\tNot fully Vi compatible: 'nocompatible'" +msgstr "-N\t\t\tIkke fuldt ud Vi-kompatibel: 'nocompatible'" + +msgid "-V[N][fname]\t\tBe verbose [level N] [log messages to fname]" +msgstr "-V[N][fnavn]\t\tVær uddybende [niveau N] [log meddelelser til fnavn]" + +msgid "-D\t\t\tDebugging mode" +msgstr "-D\t\t\tFejlretningstilstand" + +msgid "-n\t\t\tNo swap file, use memory only" +msgstr "-n\t\t\tIngen swap-fil, brug kun hukommelse" + +msgid "-r\t\t\tList swap files and exit" +msgstr "-r\t\t\tOplist swap-filer og afslut" + +msgid "-r (with file name)\tRecover crashed session" +msgstr "-r (med filnavn)\tGendan session som holdt op med at virke" + +msgid "-L\t\t\tSame as -r" +msgstr "-L\t\t\tSamme som -r" + +msgid "-f\t\t\tDon't use newcli to open window" +msgstr "-f\t\t\tBrug ikke newcli til at Ã¥bne vindue" + +msgid "-dev <device>\t\tUse <device> for I/O" +msgstr "-dev <enhed>\t\tBrug <enhed> til I/O" + +msgid "-A\t\t\tstart in Arabic mode" +msgstr "-A\t\t\tstart i arabisk tilstand" + +msgid "-H\t\t\tStart in Hebrew mode" +msgstr "-H\t\t\tStart i hebraisk tilstand" + +msgid "-F\t\t\tStart in Farsi mode" +msgstr "-F\t\t\tStart i persisk tilstand" + +msgid "-T <terminal>\tSet terminal type to <terminal>" +msgstr "-T <terminal>\tSæt terminaltype til <terminal>" + +msgid "--not-a-term\t\tSkip warning for input/output not being a terminal" +msgstr "" +"--not-a-term\t\tSpring advarsel over for input/output som ikke er en terminal" + +msgid "--ttyfail\t\tExit if input or output is not a terminal" +msgstr "--ttyfail\t\tAfslut hvis input eller output ikke er en terminal" + +msgid "-u <vimrc>\t\tUse <vimrc> instead of any .vimrc" +msgstr "-u <vimrc>\t\tBrug <vimrc> i stedet for nogen .vimrc" + +msgid "-U <gvimrc>\t\tUse <gvimrc> instead of any .gvimrc" +msgstr "-U <gvimrc>\t\tBrug <gvimrc> i stedet for nogen .gvimrc" + +msgid "--noplugin\t\tDon't load plugin scripts" +msgstr "--noplugin\t\tIndlæs ikke plugin-scripts" + +msgid "-p[N]\t\tOpen N tab pages (default: one for each file)" +msgstr "-p[N]\t\tÃ…bn N fanebladssider (standard: én pr. fil)" + +msgid "-o[N]\t\tOpen N windows (default: one for each file)" +msgstr "-o[N]\t\tÃ…bn N vinduer (standard: én pr. fil)" + +msgid "-O[N]\t\tLike -o but split vertically" +msgstr "-O[N]\t\tLigesom -o men opdel lodret" + +msgid "+\t\t\tStart at end of file" +msgstr "+\t\t\tBegynd ved slutningen af filen" + +msgid "+<lnum>\t\tStart at line <lnum>" +msgstr "+<lnum>\t\tBegynd ved linje <lnum>" + +msgid "--cmd <command>\tExecute <command> before loading any vimrc file" +msgstr "--cmd <kommando>\tUdfør <kommando> inden indlæsning af vimrc-filer" + +msgid "-c <command>\t\tExecute <command> after loading the first file" +msgstr "-c <kommando>\tUdfør <kommando> efter indlæsning af den første fil" + +msgid "-S <session>\t\tSource file <session> after loading the first file" +msgstr "-S <session>\t\tSource filen <session> efter indlæsning af den første fil" + +msgid "-s <scriptin>\tRead Normal mode commands from file <scriptin>" +msgstr "-s <scriptind>\tLæs normal tilstand-kommandoer fra filen <scriptind>" + +msgid "-w <scriptout>\tAppend all typed commands to file <scriptout>" +msgstr "" +"-w <scriptud>\tTilføj alle indtastede kommandoer til slutningen af filen " +"<scriptud>" + +msgid "-W <scriptout>\tWrite all typed commands to file <scriptout>" +msgstr "-W <scriptud>\tSkriv alle indtastede kommandoer til filen <scriptud>" + +msgid "-x\t\t\tEdit encrypted files" +msgstr "-x\t\t\tRediger krypterede filer" + +msgid "-display <display>\tConnect vim to this particular X-server" +msgstr "-display <display>\tForbind vim til denne X-server" + +msgid "-X\t\t\tDo not connect to X server" +msgstr "-X\t\t\tOpret ikke forbindelse til X-server" + +msgid "--remote <files>\tEdit <files> in a Vim server if possible" +msgstr "--remote <filer>\tRediger <filer> i en Vim-server, hvis det er muligt" + +msgid "--remote-silent <files> Same, don't complain if there is no server" +msgstr "" +"--remote-silent <filer> Samme, men vær tavs hvis der ikke er nogen server" + +msgid "" +"--remote-wait <files> As --remote but wait for files to have been edited" +msgstr "" +"--remote-wait <filer> Som --remote men vent pÃ¥ filer som skal redigeres" + +msgid "" +"--remote-wait-silent <files> Same, don't complain if there is no server" +msgstr "" +"--remote-wait-silent <filer> Samme, men vær tavs hvis der ikke er nogen " +"server" + +msgid "" +"--remote-tab[-wait][-silent] <files> As --remote but use tab page per file" +msgstr "" +"--remote-tab[-wait][-silent] <filer> Som --remote men brug fanebladsside " +"pr. fil" + +msgid "--remote-send <keys>\tSend <keys> to a Vim server and exit" +msgstr "--remote-send <nøgler>\tSend <nøgler> til en Vim-server og afslut" + +msgid "--remote-expr <expr>\tEvaluate <expr> in a Vim server and print result" +msgstr "" +"--remote-expr <udtryk>\tEvaluér <udtryk> i en Vim-server og udskriv " +"resultatet" + +msgid "--serverlist\t\tList available Vim server names and exit" +msgstr "--serverlist\t\tOplist tilgængelige Vim-servernavne og afslut" + +msgid "--servername <name>\tSend to/become the Vim server <name>" +msgstr "--servername <navn>\tSend til/bliv Vim-serveren <navn>" + +msgid "--startuptime <file>\tWrite startup timing messages to <file>" +msgstr "--startuptime <fil>\tSkriv meddelelser om opstartstiming til <fil>" + +msgid "-i <viminfo>\t\tUse <viminfo> instead of .viminfo" +msgstr "-i <viminfo>\t\tBrug <viminfo> i stedet for .viminfo" + +msgid "--clean\t\t'nocompatible', Vim defaults, no plugins, no viminfo" +msgstr "--clean\t\t'nocompatible', Vim-standarder, ingen plugins, ingen viminfo" + +msgid "-h or --help\tPrint Help (this message) and exit" +msgstr "-h eller --help\tUdskriv hjælp (denne meddelelse) og afslut" + +msgid "--version\t\tPrint version information and exit" +msgstr "--version\t\tUdskriv versionsinformation og afslut" + +msgid "" +"\n" +"Arguments recognised by gvim (Motif version):\n" +msgstr "" +"\n" +"Argumenter som genkendes af gvim (Motif-version):\n" + +msgid "" +"\n" +"Arguments recognised by gvim (neXtaw version):\n" +msgstr "" +"\n" +"Argumenter som genkendes af gvim (neXtaw-version):\n" + +msgid "" +"\n" +"Arguments recognised by gvim (Athena version):\n" +msgstr "" +"\n" +"Argumenter som genkendes af gvim (Athena-version):\n" + +msgid "-display <display>\tRun vim on <display>" +msgstr "-display <display>\tKør vim pÃ¥ <display>" + +msgid "-iconic\t\tStart vim iconified" +msgstr "-iconic\t\tStart vim som ikon" + +msgid "-background <color>\tUse <color> for the background (also: -bg)" +msgstr "-background <farve>\tBrug <farve> til baggrunden (ogsÃ¥: -bg)" + +msgid "-foreground <color>\tUse <color> for normal text (also: -fg)" +msgstr "-foreground <farve>\tBrug <farve> til normal tekst (ogsÃ¥: -fg)" + +msgid "-font <font>\t\tUse <font> for normal text (also: -fn)" +msgstr "-font <skrifttype>\tBrug <skrifttype> til normal tekst (ogsÃ¥: -fn)" + +msgid "-boldfont <font>\tUse <font> for bold text" +msgstr "-boldfont <skrifttype>\tBrug <skrifttype> til fed tekst" + +msgid "-italicfont <font>\tUse <font> for italic text" +msgstr "-italicfont <skriftt.>\tBrug <skrifttype> til kursiv tekst" + +msgid "-geometry <geom>\tUse <geom> for initial geometry (also: -geom)" +msgstr "-geometry <geom>\tBrug <geom> for indledende geometri (ogsÃ¥: -geom)" + +msgid "-borderwidth <width>\tUse a border width of <width> (also: -bw)" +msgstr "-borderwidth <bredde>\tBrug en kantbredde pÃ¥ <bredde> (ogsÃ¥: -bw)" + +msgid "-scrollbarwidth <width> Use a scrollbar width of <width> (also: -sw)" +msgstr "" +"-scrollbarwidth <bredde> Brug en rullebjælkebredde pÃ¥ <bredde> (ogsÃ¥: -sw)" + +msgid "-menuheight <height>\tUse a menu bar height of <height> (also: -mh)" +msgstr "-menuheight <højde>\tBrug en menulinjehøjde pÃ¥ <højde> (ogsÃ¥: -mh)" + +msgid "-reverse\t\tUse reverse video (also: -rv)" +msgstr "-reverse\t\tBrug omvendt grafik (ogsÃ¥: -rv)" + +msgid "+reverse\t\tDon't use reverse video (also: +rv)" +msgstr "+reverse\t\tBrug ikke omvendt grafik (ogsÃ¥: +rv)" + +msgid "-xrm <resource>\tSet the specified resource" +msgstr "-xrm <ressource>\tSæt den angivne ressource" + +msgid "" +"\n" +"Arguments recognised by gvim (GTK+ version):\n" +msgstr "" +"\n" +"Argumenter genkendt af gvim (GTK+-version):\n" + +msgid "-display <display>\tRun vim on <display> (also: --display)" +msgstr "-display <display>\tKør vim pÃ¥ <display> (ogsÃ¥: --display)" + +msgid "--role <role>\tSet a unique role to identify the main window" +msgstr "--role <rolle>\tSæt en unik rolle til at identificere hovedvinduet" + +msgid "--socketid <xid>\tOpen Vim inside another GTK widget" +msgstr "--socketid <xid>\tÃ…bn Vim i en anden GTK-widget" + +msgid "--echo-wid\t\tMake gvim echo the Window ID on stdout" +msgstr "--echo-wid\t\tFÃ¥ gvim til at skrive vinduets ID til stdout" + +msgid "-P <parent title>\tOpen Vim inside parent application" +msgstr "-P <forældertitel>\tÃ…bn Vim i forælderprogram" + +msgid "--windowid <HWND>\tOpen Vim inside another win32 widget" +msgstr "--windowid <HWND>\tÃ…bn Vim i en anden win32-widget" + +msgid "No display" +msgstr "Intet display" + +msgid ": Send failed.\n" +msgstr ": Sending mislykkedes.\n" + +msgid ": Send failed. Trying to execute locally\n" +msgstr ": Sending mislykkedes. Prøver at udføre lokalt\n" + +#, c-format +msgid "%d of %d edited" +msgstr "%d af %d redigeret" + +msgid "No display: Send expression failed.\n" +msgstr "Intet display: Send-udtryk mislykkedes.\n" + +msgid ": Send expression failed.\n" +msgstr ": Send-udtryk mislykkedes.\n" + +msgid "No marks set" +msgstr "Ingen mærker sat" + +#, c-format +msgid "E283: No marks matching \"%s\"" +msgstr "E283: Ingen mærker matcher \"%s\"" + +msgid "" +"\n" +"mark line col file/text" +msgstr "" +"\n" +"mærke linje kol fil/tekst" + +msgid "" +"\n" +" jump line col file/text" +msgstr "" +"\n" +" hop linje kol fil/tekst" + +msgid "" +"\n" +"change line col text" +msgstr "" +"\n" +"skift linje kol tekst" + +msgid "" +"\n" +"# File marks:\n" +msgstr "" +"\n" +"# Filmærker:\n" + +msgid "" +"\n" +"# Jumplist (newest first):\n" +msgstr "" +"\n" +"# Hopliste (nyeste først):\n" + +msgid "" +"\n" +"# History of marks within files (newest to oldest):\n" +msgstr "" +"\n" +"# Historik over mærker i filer (nyeste til ældste):\n" + +msgid "Missing '>'" +msgstr "Manglende '>'" + +msgid "E543: Not a valid codepage" +msgstr "E543: Ikke en gyldig tegnkodningstabel" + +msgid "E284: Cannot set IC values" +msgstr "E284: Kan ikke sætte IC-værdier" + +msgid "E285: Failed to create input context" +msgstr "E285: Kunne ikke oprette inputkontekst" + +msgid "E286: Failed to open input method" +msgstr "E286: Kunne ikke Ã¥bne inputmetode" + +msgid "E287: Warning: Could not set destroy callback to IM" +msgstr "E287: Advarsel: Kunne ikke sætte destroy callback til IM" + +msgid "E288: input method doesn't support any style" +msgstr "E288: inputmetode understøtter ikke nogen stil" + +msgid "E289: input method doesn't support my preedit type" +msgstr "E289: inputmetode understøtter ikke min preedit-type" + +msgid "E293: block was not locked" +msgstr "E293: blok blev ikke lÃ¥st" + +msgid "E294: Seek error in swap file read" +msgstr "E294: Søgefejl ved læsning af swap-fil" + +msgid "E295: Read error in swap file" +msgstr "E295: Læsefejl i swap-fil" + +msgid "E296: Seek error in swap file write" +msgstr "E296: Søgefejl ved skrivning af swap-fil" + +msgid "E297: Write error in swap file" +msgstr "E297: Skrivefejl i swap-fil" + +msgid "E300: Swap file already exists (symlink attack?)" +msgstr "E300: Swap-filen findes allerede (symlink angreb?)" + +msgid "E298: Didn't get block nr 0?" +msgstr "E298: Blev blok nr. 0 ikke hentet?" + +msgid "E298: Didn't get block nr 1?" +msgstr "E298: Blev blok nr. 1 ikke hentet?" + +msgid "E298: Didn't get block nr 2?" +msgstr "E298: Blev blok nr. 2 ikke hentet?" + +msgid "E843: Error while updating swap file crypt" +msgstr "E843: Fejl ved opdatering af crypt for swap-fil" + +msgid "E301: Oops, lost the swap file!!!" +msgstr "E301: Ups, mistede swap-filen!!!" + +msgid "E302: Could not rename swap file" +msgstr "E302: Kunne ikke omdøbe swap-fil" + +#, c-format +msgid "E303: Unable to open swap file for \"%s\", recovery impossible" +msgstr "E303: Kan ikke Ã¥bne swap-filen for \"%s\", gendannelse er ikke muligt" + +msgid "E304: ml_upd_block0(): Didn't get block 0??" +msgstr "E304: ml_upd_block0(): Blev blok 0 ikke hentet??" + +#, c-format +msgid "E305: No swap file found for %s" +msgstr "E305: Fandt ingen swap-fil for %s" + +msgid "Enter number of swap file to use (0 to quit): " +msgstr "Indtast antal swap-filer som der skal bruges (0 for at afslutte): " + +#, c-format +msgid "E306: Cannot open %s" +msgstr "E306: Kan ikke Ã¥bne %s" + +msgid "Unable to read block 0 from " +msgstr "Kan ikke læse blok 0 fra " + +msgid "" +"\n" +"Maybe no changes were made or Vim did not update the swap file." +msgstr "" +"\n" +"MÃ¥ske er der ikke foretaget nogen ændringer eller Vim opdaterede ikke swap-" +"filen." + +msgid " cannot be used with this version of Vim.\n" +msgstr " kan ikke bruges med denne version af Vim.\n" + +msgid "Use Vim version 3.0.\n" +msgstr "Brug Vim version 3.0.\n" + +#, c-format +msgid "E307: %s does not look like a Vim swap file" +msgstr "E307: %s ligner ikke en Vim swap-fil" + +msgid " cannot be used on this computer.\n" +msgstr " kan ikke bruges pÃ¥ denne computer.\n" + +msgid "The file was created on " +msgstr "Filen blev oprettet pÃ¥ " + +msgid "" +",\n" +"or the file has been damaged." +msgstr "" +",\n" +"eller filen er beskadiget." + +#, c-format +msgid "" +"E833: %s is encrypted and this version of Vim does not support encryption" +msgstr "" +"E833: %s er krypteret og denne version af Vim understøtter ikke kryptering" + +msgid " has been damaged (page size is smaller than minimum value).\n" +msgstr " er beskadiget (sidestørrelsen er mindre end minimumsværdien).\n" + +#, c-format +msgid "Using swap file \"%s\"" +msgstr "Bruger swap-filen \"%s\"" + +#, c-format +msgid "Original file \"%s\"" +msgstr "Den originale fil \"%s\"" + +msgid "E308: Warning: Original file may have been changed" +msgstr "E308: Advarsel: Den originale fil kan være ændret" + +#, c-format +msgid "Swap file is encrypted: \"%s\"" +msgstr "Swap-filen er krypteret: \"%s\"" + +msgid "" +"\n" +"If you entered a new crypt key but did not write the text file," +msgstr "" +"\n" +"Hvis du indtastede en ny crypt-nøgle men ikke skrev tekstfilen," + +msgid "" +"\n" +"enter the new crypt key." +msgstr "" +"\n" +"sÃ¥ indtast den nye crypt-nøgle." + +msgid "" +"\n" +"If you wrote the text file after changing the crypt key press enter" +msgstr "" +"\n" +"Hvis du skrev tekstfilen efter crypt-nøglen blev ændret, sÃ¥ tryk pÃ¥ enter" + +msgid "" +"\n" +"to use the same key for text file and swap file" +msgstr "" +"\n" +"for at bruge den samme nøgle til tekstfilen og swap-filen" + +#, c-format +msgid "E309: Unable to read block 1 from %s" +msgstr "E309: Kan ikke læse blok 1 fra %s" + +msgid "???MANY LINES MISSING" +msgstr "???MANGE LINJER MANGLER" + +msgid "???LINE COUNT WRONG" +msgstr "???LINJEANTAL FORKERT" + +msgid "???EMPTY BLOCK" +msgstr "???TOM BLOK" + +msgid "???LINES MISSING" +msgstr "???LINJER MANGLER" + +#, c-format +msgid "E310: Block 1 ID wrong (%s not a .swp file?)" +msgstr "E310: Forkert ID for blok 1 (%s ikke en .swp-fil?)" + +msgid "???BLOCK MISSING" +msgstr "???BLOK MANGLER" + +msgid "??? from here until ???END lines may be messed up" +msgstr "??? herfra indtil ???SLUT kan linjer være rodet" + +msgid "??? from here until ???END lines may have been inserted/deleted" +msgstr "??? herfra indtil ???SLUT kan linjer være indsat/slettet" + +msgid "???END" +msgstr "???SLUT" + +msgid "E311: Recovery Interrupted" +msgstr "E311: Gendannelse afbrudt" + +msgid "" +"E312: Errors detected while recovering; look for lines starting with ???" +msgstr "" +"E312: Fejl registreret ved gendannelse; kig efter linjer som begynder med " +"???" + +msgid "See \":help E312\" for more information." +msgstr "Se \":help E312\" for mere information." + +msgid "Recovery completed. You should check if everything is OK." +msgstr "Gendannelse gennemført. Du bør tjekke om alt er OK." + +msgid "" +"\n" +"(You might want to write out this file under another name\n" +msgstr "" +"\n" +"(Det kan være du vil skrive filen under et andet navn\n" + +msgid "and run diff with the original file to check for changes)" +msgstr "og kør diff men den originale fil for at tjekke for ændringer)" + +msgid "Recovery completed. Buffer contents equals file contents." +msgstr "" +"Gendannelse gennemført. Bufferens indhold er det samme som filens indhold." + +msgid "" +"\n" +"You may want to delete the .swp file now.\n" +"\n" +msgstr "" +"\n" +"Det kan være du vil slette .swp-filen nu.\n" +"\n" + +msgid "Using crypt key from swap file for the text file.\n" +msgstr "Bruger crypt-nøglen fra swap-filen til tekstfilen.\n" + +msgid "Swap files found:" +msgstr "Swap-filer fundet:" + +msgid " In current directory:\n" +msgstr " I nuværende mappe:\n" + +msgid " Using specified name:\n" +msgstr " Bruger angivne navn:\n" + +msgid " In directory " +msgstr " I mappe " + +msgid " -- none --\n" +msgstr " -- ingen --\n" + +msgid " owned by: " +msgstr " ejet af: " + +msgid " dated: " +msgstr " dateret: " + +msgid " dated: " +msgstr " dateret: " + +msgid " [from Vim version 3.0]" +msgstr " [fra Vim version 3.0]" + +msgid " [does not look like a Vim swap file]" +msgstr " [ligner ikke en Vim swap-fil]" + +msgid " file name: " +msgstr " filnavn: " + +msgid "" +"\n" +" modified: " +msgstr "" +"\n" +" ændret: " + +msgid "YES" +msgstr "JA" + +msgid "no" +msgstr "nej" + +msgid "" +"\n" +" user name: " +msgstr "" +"\n" +" brugernavn: " + +msgid " host name: " +msgstr " værtsnavn: " + +msgid "" +"\n" +" host name: " +msgstr "" +"\n" +" værtsnavn: " + +msgid "" +"\n" +" process ID: " +msgstr "" +"\n" +" proces-ID: " + +msgid " (still running)" +msgstr " (kører stadig)" + +msgid "" +"\n" +" [not usable with this version of Vim]" +msgstr "" +"\n" +" [ikke anvendelig med denne version af Vim]" + +msgid "" +"\n" +" [not usable on this computer]" +msgstr "" +"\n" +" [ikke anvendelig pÃ¥ denne computer]" + +msgid " [cannot be read]" +msgstr " [kan ikke læses]" + +msgid " [cannot be opened]" +msgstr " [kan ikke Ã¥bnes]" + +msgid "E313: Cannot preserve, there is no swap file" +msgstr "E313: Kan ikke bevares, der er ikke nogen swap-fil" + +msgid "File preserved" +msgstr "Fil bevaret" + +msgid "E314: Preserve failed" +msgstr "E314: Bevaring mislykkedes" + +#, c-format +msgid "E315: ml_get: invalid lnum: %ld" +msgstr "E315: ml_get: ugyldig lnum: %ld" + +#, c-format +msgid "E316: ml_get: cannot find line %ld" +msgstr "E316: ml_get: kan ikke finde linje %ld" + +msgid "E317: pointer block id wrong 3" +msgstr "E317: forkert blok-id for pointer 3" + +msgid "stack_idx should be 0" +msgstr "stack_idx skal være 0" + +msgid "E318: Updated too many blocks?" +msgstr "E318: Opdaterede for mange blokke?" + +msgid "E317: pointer block id wrong 4" +msgstr "E317: forkert blok-id for pointer 4" + +msgid "deleted block 1?" +msgstr "slettede blok 1?" + +#, c-format +msgid "E320: Cannot find line %ld" +msgstr "E320: Kan ikke finde linje %ld" + +msgid "E317: pointer block id wrong" +msgstr "E317: forkert blok-id for pointer" + +msgid "pe_line_count is zero" +msgstr "pe_line_count er nul" + +#, c-format +msgid "E322: line number out of range: %ld past the end" +msgstr "E322: linjenummer udenfor omrÃ¥de: %ld efter slutningen" + +#, c-format +msgid "E323: line count wrong in block %ld" +msgstr "E323: linje antal forkert i blok %ld" + +msgid "Stack size increases" +msgstr "Stakstørrelse øges" + +msgid "E317: pointer block id wrong 2" +msgstr "E317: forkert blok-id for pointer 2" + +#, c-format +msgid "E773: Symlink loop for \"%s\"" +msgstr "E773: Symlink-løkke for \"%s\"" + +msgid "E325: ATTENTION" +msgstr "E325: OBS" + +msgid "" +"\n" +"Found a swap file by the name \"" +msgstr "" +"\n" +"Fandt en swap-fil ved navn \"" + +msgid "While opening file \"" +msgstr "Ved Ã¥bning af filen \"" + +msgid " NEWER than swap file!\n" +msgstr " NYERE end swap-fil!\n" + +msgid "" +"\n" +"(1) Another program may be editing the same file. If this is the case,\n" +" be careful not to end up with two different instances of the same\n" +" file when making changes. Quit, or continue with caution.\n" +msgstr "" +"\n" +"(1) Et andet program redigere muligvis den samme fil. Hvis det er tilfældet,\n" +" sÃ¥ pas pÃ¥ ikke at ende med to forskellige instanser af den samme\n" +" fil nÃ¥r der foretages ændringer. Afslut, eller fortsæt med forsigtighed.\n" + +msgid "(2) An edit session for this file crashed.\n" +msgstr "(2) En redigeringssession for filen holdt op med at virke.\n" + +msgid " If this is the case, use \":recover\" or \"vim -r " +msgstr " Hvis det er tilfældet, sÃ¥ brug \":recover\" eller \"vim -r " + +msgid "" +"\"\n" +" to recover the changes (see \":help recovery\").\n" +msgstr "" +"\"\n" +" for at gendanne ændringerne (se \":help recovery\").\n" + +msgid " If you did this already, delete the swap file \"" +msgstr " Hvis du allerede har gjort det, sÃ¥ slet swap-filen \"" + +msgid "" +"\"\n" +" to avoid this message.\n" +msgstr "" +"\"\n" +" for at undgÃ¥ denne meddelelse.\n" + +msgid "Swap file \"" +msgstr "Swap-filen \"" + +msgid "\" already exists!" +msgstr "\" findes allerede!" + +msgid "VIM - ATTENTION" +msgstr "VIM - OBS" + +msgid "Swap file already exists!" +msgstr "Swap-filen findes allerede!" + +msgid "" +"&Open Read-Only\n" +"&Edit anyway\n" +"&Recover\n" +"&Quit\n" +"&Abort" +msgstr "" +"&Ã…bn skrivebeskyttet\n" +"&Rediger alligevel\n" +"&Gendan\n" +"&Afslut\n" +"&Afbryd" + +msgid "" +"&Open Read-Only\n" +"&Edit anyway\n" +"&Recover\n" +"&Delete it\n" +"&Quit\n" +"&Abort" +msgstr "" +"&Ã…bn skrivebeskyttet\n" +"&Rediger alligevel\n" +"&Gendan\n" +"&Slet den\n" +"&Afslut\n" +"&Afbryd" + +msgid "E326: Too many swap files found" +msgstr "E326: For mange swap-filer fundet" + +msgid "E327: Part of menu-item path is not sub-menu" +msgstr "E327: Del af sti til menupunkt er ikke undermenu" + +msgid "E328: Menu only exists in another mode" +msgstr "E328: Menuen findes kun i en anden tilstand" + +#, c-format +msgid "E329: No menu \"%s\"" +msgstr "E329: Ingen menu \"%s\"" + +msgid "E792: Empty menu name" +msgstr "E792: Tomt menunavn" + +msgid "E330: Menu path must not lead to a sub-menu" +msgstr "E330: Menusti mÃ¥ ikke lede til en undermenu" + +msgid "E331: Must not add menu items directly to menu bar" +msgstr "E331: MÃ¥ ikke tilføje menupunkter direkte til menulinje" + +msgid "E332: Separator cannot be part of a menu path" +msgstr "E332: Separator mÃ¥ ikke være del af en menusti" + +msgid "" +"\n" +"--- Menus ---" +msgstr "" +"\n" +"--- Menuer ---" + +msgid "Tear off this menu" +msgstr "Løsriv menuen" + +#, c-format +msgid "E335: Menu not defined for %s mode" +msgstr "E335: Menu ikke defineret for %s-tilstand" + +msgid "E333: Menu path must lead to a menu item" +msgstr "E333: Menusti skal lede til et menupunkt" + +#, c-format +msgid "E334: Menu not found: %s" +msgstr "E334: Menu ikke fundet: %s" + +msgid "E336: Menu path must lead to a sub-menu" +msgstr "E336: Menusti skal lede til en undermenu" + +msgid "E337: Menu not found - check menu names" +msgstr "E337: Menu ikke fundet - tjek menunavne" + +#, c-format +msgid "Error detected while processing %s:" +msgstr "Fejl registreret ved behandling af %s:" + +#, c-format +msgid "line %4ld:" +msgstr "linje %4ld:" + +#, c-format +msgid "E354: Invalid register name: '%s'" +msgstr "E354: Ugyldigt registernavn: '%s'" + +msgid "Messages maintainer: Bram Moolenaar <Bram@vim.org>" +msgstr "Oversætter: scootergrisen" + +msgid "Interrupt: " +msgstr "Afbryd: " + +msgid "Press ENTER or type command to continue" +msgstr "Tryk pÃ¥ ENTER eller skriv kommando for at fortsætte" + +#, c-format +msgid "%s line %ld" +msgstr "%s linje %ld" + +msgid "-- More --" +msgstr "-- Mere --" + +msgid " SPACE/d/j: screen/page/line down, b/u/k: up, q: quit " +msgstr " MELLEMRUM/d/j: skærm/side/linje ned, b/u/k: op, q: afslut " + +msgid "Question" +msgstr "SpørgsmÃ¥l" + +msgid "" +"&Yes\n" +"&No" +msgstr "" +"&Ja\n" +"&Nej" + +msgid "" +"&Yes\n" +"&No\n" +"Save &All\n" +"&Discard All\n" +"&Cancel" +msgstr "" +"&Ja\n" +"&Nej\n" +"Gem &alle\n" +"&Forkast alle\n" +"&Annuller" + +msgid "Select Directory dialog" +msgstr "Vælg mappe-dialog" + +msgid "Save File dialog" +msgstr "Gem fil-dialog" + +msgid "Open File dialog" +msgstr "Ã…bn fil-dialog" + +msgid "E338: Sorry, no file browser in console mode" +msgstr "E338: Beklager, ingen filbrowser i konsol-tilstand" + +msgid "E766: Insufficient arguments for printf()" +msgstr "E766: Ikke nok argumenter for printf()" + +msgid "E807: Expected Float argument for printf()" +msgstr "E807: Ventede flydende kommatal-argument for printf()" + +msgid "E767: Too many arguments to printf()" +msgstr "E767: For mange argumenter til printf()" + +msgid "W10: Warning: Changing a readonly file" +msgstr "W10: Advarsel: Ændre en skrivebeskyttet fil" + +msgid "Type number and <Enter> or click with mouse (empty cancels): " +msgstr "Skriv nummer og <Enter> eller klik med musen (tom annullerer): " + +msgid "Type number and <Enter> (empty cancels): " +msgstr "Skriv nummer og <Enter> (tom annullerer): " + +msgid "1 more line" +msgstr "1 linje mere" + +msgid "1 line less" +msgstr "1 linje mindre" + +#, c-format +msgid "%ld more lines" +msgstr "%ld linjer mere" + +#, c-format +msgid "%ld fewer lines" +msgstr "%ld linjere mindre" + +msgid " (Interrupted)" +msgstr " (Afbrudt)" + +msgid "Beep!" +msgstr "Bip!" + +msgid "ERROR: " +msgstr "FEJL: " + +#, c-format +msgid "" +"\n" +"[bytes] total alloc-freed %lu-%lu, in use %lu, peak use %lu\n" +msgstr "" +"\n" +"[byte] samlet allok-frigivet %lu-%lu, i brug %lu, spidspunktsbrug %lu\n" + +#, c-format +msgid "" +"[calls] total re/malloc()'s %lu, total free()'s %lu\n" +"\n" +msgstr "" +"[kald] samlet re/malloc()'er %lu, samlet free()'er %lu\n" +"\n" + +msgid "E340: Line is becoming too long" +msgstr "E340: Linje er ved at blive for lang" + +#, c-format +msgid "E341: Internal error: lalloc(%ld, )" +msgstr "E341: Intern fejl: lalloc(%ld, )" + +#, c-format +msgid "E342: Out of memory! (allocating %lu bytes)" +msgstr "E342: Ikke mere ledig hukommelse! (allokerer %lu byte)" + +#, c-format +msgid "Calling shell to execute: \"%s\"" +msgstr "Kalder skal til udførelse af: \"%s\"" + +msgid "E545: Missing colon" +msgstr "E545: Manglende kolon" + +msgid "E546: Illegal mode" +msgstr "E546: Ulovlig tilstand" + +msgid "E547: Illegal mouseshape" +msgstr "E547: Ulovlig museform" + +msgid "E548: digit expected" +msgstr "E548: ciffer ventet" + +msgid "E549: Illegal percentage" +msgstr "E549: Ulovlig procent" + +msgid "E854: path too long for completion" +msgstr "E854: sti for lang til fuldførelse" + +#, c-format +msgid "" +"E343: Invalid path: '**[number]' must be at the end of the path or be " +"followed by '%s'." +msgstr "" +"E343: Ugyldig sti: '**[nummer]' skal være i slutningen af stien eller " +"efterfølges af '%s'." + +#, c-format +msgid "E344: Can't find directory \"%s\" in cdpath" +msgstr "E344: Kan ikke finde mappen \"%s\" i cdpath" + +#, c-format +msgid "E345: Can't find file \"%s\" in path" +msgstr "E345: Kan ikke finde filen \"%s\" i path" + +#, c-format +msgid "E346: No more directory \"%s\" found in cdpath" +msgstr "E346: Ikke flere mappe \"%s\" fundet i cdpath" + +#, c-format +msgid "E347: No more file \"%s\" found in path" +msgstr "E347: Ikke flere fil \"%s\" fundet i path" + +#, c-format +msgid "E668: Wrong access mode for NetBeans connection info file: \"%s\"" +msgstr "E668: Forkert adgangstilstand for NetBeans-forbindelsens info-fil: \"%s\"" + +#, c-format +msgid "E658: NetBeans connection lost for buffer %ld" +msgstr "E658: NetBeans-forbindelse mistet for buffer %ld" + +msgid "E838: netbeans is not supported with this GUI" +msgstr "E838: netbeans understøttes ikke med denne GUI" + +msgid "E511: netbeans already connected" +msgstr "E511: netbeans allerede forbundet" + +#, c-format +msgid "E505: %s is read-only (add ! to override)" +msgstr "E505: %s er skrivebeskyttet (tilføj ! for at tilsidesætte)" + +msgid "E349: No identifier under cursor" +msgstr "E349: Ingen identifikator under markør" + +msgid "E774: 'operatorfunc' is empty" +msgstr "E774: 'operatorfunc' er tom" + +msgid "E775: Eval feature not available" +msgstr "E775: Eval-funktionalitet ikke tilgængelig" + +msgid "Warning: terminal cannot highlight" +msgstr "Advarsel: terminal kan ikke fremhæve" + +msgid "E348: No string under cursor" +msgstr "E348: Ingen streng under markør" + +msgid "E352: Cannot erase folds with current 'foldmethod'" +msgstr "E352: Kan ikke slette sammenfoldninger med nuværende 'foldmethod'" + +msgid "E664: changelist is empty" +msgstr "E664: ændringsliste er tom" + +msgid "E662: At start of changelist" +msgstr "E662: Ved begyndelsen af ændringsliste" + +msgid "E663: At end of changelist" +msgstr "E663: Ved slutningen af ændringsliste" + +msgid "Type :qa! and press <Enter> to abandon all changes and exit Vim" +msgstr "" +"Skriv :qa! og tryk pÃ¥ <Enter> for at droppe alle ændringer og afslut Vim" + +#, c-format +msgid "1 line %sed 1 time" +msgstr "1 linje %sed 1 gang" + +#, c-format +msgid "1 line %sed %d times" +msgstr "1 linje %sed %d gange" + +#, c-format +msgid "%ld lines %sed 1 time" +msgstr "%ld linjer %sed 1 gang" + +#, c-format +msgid "%ld lines %sed %d times" +msgstr "%ld linjer %sed %d gange" + +#, c-format +msgid "%ld lines to indent... " +msgstr "%ld linjer at indrykke... " + +msgid "1 line indented " +msgstr "1 linje indrykket " + +#, c-format +msgid "%ld lines indented " +msgstr "%ld linjer indrykket " + +msgid "E748: No previously used register" +msgstr "E748: Intet tidligere brugt register" + +msgid "cannot yank; delete anyway" +msgstr "kan ikke rykke; slet alligevel" + +msgid "1 line changed" +msgstr "1 linje ændret" + +#, c-format +msgid "%ld lines changed" +msgstr "%ld linjer ændret" + +#, c-format +msgid "freeing %ld lines" +msgstr "frigør %ld linjer" + +#, c-format +msgid " into \"%c" +msgstr " i \"%c" + +#, c-format +msgid "block of 1 line yanked%s" +msgstr "blok pÃ¥ 1 linje rykket%s" + +#, c-format +msgid "1 line yanked%s" +msgstr "1 linje rykket%s" + +#, c-format +msgid "block of %ld lines yanked%s" +msgstr "blok pÃ¥ %ld linjer rykket%s" + +#, c-format +msgid "%ld lines yanked%s" +msgstr "%ld linjer rykket%s" + +#, c-format +msgid "E353: Nothing in register %s" +msgstr "E353: Intet i register %s" + +msgid "" +"\n" +"--- Registers ---" +msgstr "" +"\n" +"--- Registre ---" + +msgid "Illegal register name" +msgstr "Ulovligt registernavn" + +msgid "" +"\n" +"# Registers:\n" +msgstr "" +"\n" +"# Registre:\n" + +#, c-format +msgid "E574: Unknown register type %d" +msgstr "E574: Ukendt registertype %d" + +msgid "" +"E883: search pattern and expression register may not contain two or more " +"lines" +msgstr "" +"E883: søgemønster og udtryksregister mÃ¥ ikke indeholde to eller flere linjer" + +#, c-format +msgid "%ld Cols; " +msgstr "%ld kolonner; " + +#, c-format +msgid "Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Bytes" +msgstr "Markerede %s%ld af %ld linje; %lld af %lld ord; %lld af %lld byte" + +#, c-format +msgid "" +"Selected %s%ld of %ld Lines; %lld of %lld Words; %lld of %lld Chars; %lld of " +"%lld Bytes" +msgstr "" +"Markerede %s%ld af %ld linje; %lld af %lld ord; %lld af %lld tegn; %lld af %" +"lld byte" + +#, c-format +msgid "Col %s of %s; Line %ld of %ld; Word %lld of %lld; Byte %lld of %lld" +msgstr "Kol %s af %s; Linje %ld af %ld; Ord %lld af %lld; Byte %lld af %lld" + +#, c-format +msgid "" +"Col %s of %s; Line %ld of %ld; Word %lld of %lld; Char %lld of %lld; Byte " +"%lld of %lld" +msgstr "" +"Kol %s af %s; Linje %ld af %ld; Ord %lld af %lld; Tegn %lld af %lld; Byte %" +"lld af %lld" + +#, c-format +msgid "(+%lld for BOM)" +msgstr "(+%lld for BOM)" + +msgid "Thanks for flying Vim" +msgstr "Tak fordi du fløj med Vim" + +msgid "E518: Unknown option" +msgstr "E518: Ukendt tilvalg" + +msgid "E519: Option not supported" +msgstr "E519: Tilvalg understøttes ikke" + +msgid "E520: Not allowed in a modeline" +msgstr "E520: Ikke tilladt pÃ¥ en tilstandslinje" + +msgid "E846: Key code not set" +msgstr "E846: Tastekode ikke sat" + +msgid "E521: Number required after =" +msgstr "E521: Nummer kræves efter =" + +msgid "E522: Not found in termcap" +msgstr "E522: Ikke fundet i termcap" + +#, c-format +msgid "E539: Illegal character <%s>" +msgstr "E539: Ulovligt tegn <%s>" + +#, c-format +msgid "For option %s" +msgstr "For tilvalget %s" + +msgid "E529: Cannot set 'term' to empty string" +msgstr "E529: Kan ikke sætte 'term' til tom streng" + +msgid "E530: Cannot change term in GUI" +msgstr "E530: Kan ikke skifte term i GUI" + +msgid "E531: Use \":gui\" to start the GUI" +msgstr "E531: Brug \":gui\" til at starte GUI'en" + +msgid "E589: 'backupext' and 'patchmode' are equal" +msgstr "E589: 'backupext' og 'patchmode' er ens" + +msgid "E834: Conflicts with value of 'listchars'" +msgstr "E834: Er i konflikt med værdien af 'listchars'" + +msgid "E835: Conflicts with value of 'fillchars'" +msgstr "E835: Er i konflikt med værdien af 'fillchars'" + +msgid "E617: Cannot be changed in the GTK+ 2 GUI" +msgstr "E617: Kan ikke ændres i GTK+ 2 GUI'en" + +#, c-format +msgid "E950: Cannot convert between %s and %s" +msgstr "E950: Kan ikke konvertere mellem %s og %s" + +msgid "E524: Missing colon" +msgstr "E524: Manglende kolon" + +msgid "E525: Zero length string" +msgstr "E525: Streng uden længde" + +#, c-format +msgid "E526: Missing number after <%s>" +msgstr "E526: Manglende nummer efter <%s>" + +msgid "E527: Missing comma" +msgstr "E527: Manglende komma" + +msgid "E528: Must specify a ' value" +msgstr "E528: Skal angive en '-værdi" + +msgid "E595: contains unprintable or wide character" +msgstr "E595: indeholder tegn som ikke kan udskrives eller er bredt" + +msgid "E596: Invalid font(s)" +msgstr "E596: Ugyldig skrifttype(r)" + +msgid "E597: can't select fontset" +msgstr "E597: kan ikke vælge skrifttypesæt" + +msgid "E598: Invalid fontset" +msgstr "E598: Ugyldigt skrifttypesæt" + +msgid "E533: can't select wide font" +msgstr "E533: kan ikke vælge bred skrifttype" + +msgid "E534: Invalid wide font" +msgstr "E534: Ugyldig bred skrifttype" + +#, c-format +msgid "E535: Illegal character after <%c>" +msgstr "E535: Ulovligt tegn efter <%c>" + +msgid "E536: comma required" +msgstr "E536: komma kræves" + +#, c-format +msgid "E537: 'commentstring' must be empty or contain %s" +msgstr "E537: 'commentstring' skal være tom eller indeholde %s" + +msgid "E538: No mouse support" +msgstr "E538: Ingen understøttelse af mus" + +msgid "E540: Unclosed expression sequence" +msgstr "E540: Ulukket udtryk-sekvens" + +msgid "E541: too many items" +msgstr "E541: for mange punkter" + +msgid "E542: unbalanced groups" +msgstr "E542: ubalancerede grupper" + +msgid "E946: Cannot make a terminal with running job modifiable" +msgstr "E946: Kan ikke gøre en terminal med kørende job ændringsbar" + +msgid "E590: A preview window already exists" +msgstr "E590: Der findes allerede et forhÃ¥ndsvisningsvindue" + +msgid "W17: Arabic requires UTF-8, do ':set encoding=utf-8'" +msgstr "W17: Arabisk kræver UTF-8, brug ':set encoding=utf-8'" + +msgid "E954: 24-bit colors are not supported on this environment" +msgstr "E954: 24-bit farver understøttes ikke i dette miljø" + +#, c-format +msgid "E593: Need at least %d lines" +msgstr "E593: Skal være mindst %d linjer" + +#, c-format +msgid "E594: Need at least %d columns" +msgstr "E594: Skal være mindst %d kolonner" + +#, c-format +msgid "E355: Unknown option: %s" +msgstr "E355: Ukendt tilvalg: %s" + +#, c-format +msgid "E521: Number required: &%s = '%s'" +msgstr "E521: Nummer kræves: &%s = '%s'" + +msgid "" +"\n" +"--- Terminal codes ---" +msgstr "" +"\n" +"--- Terminal-koder ---" + +msgid "" +"\n" +"--- Global option values ---" +msgstr "" +"\n" +"--- Værdier for globale tilvalg ---" + +msgid "" +"\n" +"--- Local option values ---" +msgstr "" +"\n" +"--- Værdier for lokale tilvalg ---" + +msgid "" +"\n" +"--- Options ---" +msgstr "" +"\n" +"--- Tilvalg ---" + +msgid "E356: get_varp ERROR" +msgstr "E356: Fejl ved get_varp" + +#, c-format +msgid "E357: 'langmap': Matching character missing for %s" +msgstr "E357: 'langmap': Matchende tegn mangler for %s" + +#, c-format +msgid "E358: 'langmap': Extra characters after semicolon: %s" +msgstr "E358: 'langmap': Ekstra tegn efter semikolon: %s" + +msgid "cannot open " +msgstr "kan ikke Ã¥bne " + +msgid "VIM: Can't open window!\n" +msgstr "VIM: Kan ikke Ã¥bne vindue!\n" + +msgid "Need Amigados version 2.04 or later\n" +msgstr "Behøver Amigados version 2.04 eller senere\n" + +#, c-format +msgid "Need %s version %ld\n" +msgstr "Behøver %s version %ld\n" + +msgid "Cannot open NIL:\n" +msgstr "Kan ikke Ã¥bne NIL:\n" + +msgid "Cannot create " +msgstr "Kan ikke oprette " + +#, c-format +msgid "Vim exiting with %d\n" +msgstr "Vim afsluttede med %d\n" + +msgid "cannot change console mode ?!\n" +msgstr "kan ikke skifte konsoltilstand ?!\n" + +msgid "mch_get_shellsize: not a console??\n" +msgstr "mch_get_shellsize: ikke en konsol??\n" + +msgid "E360: Cannot execute shell with -f option" +msgstr "E360: Kan ikke udføre skal med -f-tilvalget" + +msgid "Cannot execute " +msgstr "Kan ikke udføre " + +msgid "shell " +msgstr "skal " + +msgid " returned\n" +msgstr " returnerede\n" + +msgid "ANCHOR_BUF_SIZE too small." +msgstr "ANCHOR_BUF_SIZE for lille." + +msgid "I/O ERROR" +msgstr "FEJL VED I/O" + +msgid "Message" +msgstr "Meddelelse" + +msgid "E237: Printer selection failed" +msgstr "E237: Valg af printer mislykkedes" + +#, c-format +msgid "to %s on %s" +msgstr "til %s pÃ¥ %s" + +#, c-format +msgid "E613: Unknown printer font: %s" +msgstr "E613: Ukendt skrifttype til printer: %s" + +#, c-format +msgid "E238: Print error: %s" +msgstr "E238: Fejl ved udskrivning: %s" + +#, c-format +msgid "Printing '%s'" +msgstr "Udskriver '%s'" + +#, c-format +msgid "E244: Illegal charset name \"%s\" in font name \"%s\"" +msgstr "E244: Ulovligt tegnsætnavn \"%s\" i skrifttypenavnet \"%s\"" + +#, c-format +msgid "E244: Illegal quality name \"%s\" in font name \"%s\"" +msgstr "E244: Ulovligt kvalitetsnavn \"%s\" i skrifttypenavnet \"%s\"" + +#, c-format +msgid "E245: Illegal char '%c' in font name \"%s\"" +msgstr "E245: Ulovligt tegn '%c' i skrifttypenavnet \"%s\"" + +#, c-format +msgid "Opening the X display took %ld msec" +msgstr "Ã…bningen af X-displayet tog %ld ms" + +msgid "" +"\n" +"Vim: Got X error\n" +msgstr "" +"\n" +"Vim: Fik fejl ved X\n" + +msgid "Testing the X display failed" +msgstr "Test af X-displayet mislykkedes" + +msgid "Opening the X display timed out" +msgstr "Ã…bningen af X-displayet fik timeout" + +msgid "" +"\n" +"Could not get security context for " +msgstr "" +"\n" +"Kunne ikke hente sikkerhedskontekst for " + +msgid "" +"\n" +"Could not set security context for " +msgstr "" +"\n" +"Kunne ikke sætte sikkerhedskontekst for " + +#, c-format +msgid "Could not set security context %s for %s" +msgstr "Kunne ikke sætte sikkerhedskonteksten %s for %s" + +#, c-format +msgid "Could not get security context %s for %s. Removing it!" +msgstr "Kunne ikke hente sikkerhedskonteksten %s for %s. Fjerner den!" + +msgid "" +"\n" +"Cannot execute shell sh\n" +msgstr "" +"\n" +"Kan ikke udføre skallen sh\n" + +msgid "" +"\n" +"shell returned " +msgstr "" +"\n" +"skal returnerede " + +msgid "" +"\n" +"Cannot create pipes\n" +msgstr "" +"\n" +"Kan ikke oprette pipes\n" + +msgid "" +"\n" +"Cannot fork\n" +msgstr "" +"\n" +"Kan ikke fork\n" + +msgid "" +"\n" +"Cannot execute shell " +msgstr "" +"\n" +"Kan ikke udføre skallen " + +msgid "" +"\n" +"Command terminated\n" +msgstr "" +"\n" +"Kommando termineret\n" + +msgid "XSMP lost ICE connection" +msgstr "XSMP mistede ICE-forbindelse" + +#, c-format +msgid "dlerror = \"%s\"" +msgstr "dlerror = \"%s\"" + +msgid "Opening the X display failed" +msgstr "Ã…bningen af X-displayet mislykkedes" + +msgid "XSMP handling save-yourself request" +msgstr "XSMP-hÃ¥ndtering save-yourself-anmodning" + +msgid "XSMP opening connection" +msgstr "XSMP Ã¥bner forbindelse" + +msgid "XSMP ICE connection watch failed" +msgstr "XSMP ICE-forbindelse watch mislykkedes" + +#, c-format +msgid "XSMP SmcOpenConnection failed: %s" +msgstr "XSMP SmcOpenConnection mislykkedes: %s" + +msgid "At line" +msgstr "PÃ¥ linje" + +msgid "Could not load vim32.dll!" +msgstr "Kunne ikke indlæse vim32.dll!" + +msgid "VIM Error" +msgstr "Fejl ved VIM" + +msgid "Could not fix up function pointers to the DLL!" +msgstr "Kunne ikke rette op pÃ¥ funktion-pointere til DLL'en!" + +#, c-format +msgid "Vim: Caught %s event\n" +msgstr "Vim: Fangede %s-hændelse\n" + +msgid "close" +msgstr "luk" + +msgid "logoff" +msgstr "log ud" + +msgid "shutdown" +msgstr "luk ned" + +msgid "E371: Command not found" +msgstr "E371: Kommando ikke fundet" + +msgid "" +"VIMRUN.EXE not found in your $PATH.\n" +"External commands will not pause after completion.\n" +"See :help win32-vimrun for more information." +msgstr "" +"VIMRUN.EXE ikke fundet i din $PATH.\n" +"Eksterne kommandoer sættes ikke pÃ¥ pause efter fuldførelse.\n" +"Se :help win32-vimrun for mere information." + +msgid "Vim Warning" +msgstr "Advarsel ved Vim" + +#, c-format +msgid "shell returned %d" +msgstr "skal returnerede %d" + +msgid "E926: Current location list was changed" +msgstr "E926: Nuværende placeringsliste blev ændret" + +#, c-format +msgid "E372: Too many %%%c in format string" +msgstr "E372: For mange %%%c i formatet streng" + +#, c-format +msgid "E373: Unexpected %%%c in format string" +msgstr "E373: Uventet %%%c i formatet streng" + +msgid "E374: Missing ] in format string" +msgstr "E374: Manglende ] i formatet streng" + +#, c-format +msgid "E375: Unsupported %%%c in format string" +msgstr "E375: Ikke-understøttet %%%c i formatet streng" + +#, c-format +msgid "E376: Invalid %%%c in format string prefix" +msgstr "E376: Ugyldig %%%c i præfiks for formatet streng" + +#, c-format +msgid "E377: Invalid %%%c in format string" +msgstr "E377: Ugyldig %%%c i formatet streng" + +msgid "E378: 'errorformat' contains no pattern" +msgstr "E378: 'errorformat' indeholder ikke noget mønter" + +msgid "E379: Missing or empty directory name" +msgstr "E379: Manglende eller tomt mappenavn" + +msgid "E553: No more items" +msgstr "E553: Ikke flere punkter" + +msgid "E924: Current window was closed" +msgstr "E924: Nuværende vindue blev lukket" + +msgid "E925: Current quickfix was changed" +msgstr "E925: Nuværende quickfix blev ændret" + +#, c-format +msgid "(%d of %d)%s%s: " +msgstr "(%d af %d)%s%s: " + +msgid " (line deleted)" +msgstr " (linje slettet)" + +#, c-format +msgid "%serror list %d of %d; %d errors " +msgstr "%sfejlliste %d af %d; %d fejl " + +msgid "E380: At bottom of quickfix stack" +msgstr "E380: Nederst i quickfix-stakken" + +msgid "E381: At top of quickfix stack" +msgstr "E381: Øverst i quickfix-stakken" + +msgid "No entries" +msgstr "Ingen poster" + +msgid "Error file" +msgstr "Fejlfil" + +msgid "E683: File name missing or invalid pattern" +msgstr "E683: Filnavn mangler eller ugyldigt mønster" + +#, c-format +msgid "Cannot open file \"%s\"" +msgstr "Kan ikke Ã¥bne filen \"%s\"" + +msgid "E681: Buffer is not loaded" +msgstr "E681: Buffer er ikke indlæst" + +msgid "E777: String or List expected" +msgstr "E777: Streng eller liste ventet" + +#, c-format +msgid "E369: invalid item in %s%%[]" +msgstr "E369: ugyldigt punkt i %s%%[]" + +#, c-format +msgid "E769: Missing ] after %s[" +msgstr "E769: Manglende ] efter %s[" + +msgid "E944: Reverse range in character class" +msgstr "E944: Baglæns omrÃ¥de i tegnklasse" + +msgid "E945: Range too large in character class" +msgstr "E945: OmrÃ¥de for stort i tegnklasse" + +#, c-format +msgid "E53: Unmatched %s%%(" +msgstr "E53: Ikke-matchet %s%%(" + +#, c-format +msgid "E54: Unmatched %s(" +msgstr "E54: Ikke-matchet %s(" + +#, c-format +msgid "E55: Unmatched %s)" +msgstr "E55: Ikke-matchet %s)" + +msgid "E66: \\z( not allowed here" +msgstr "E66: \\z( ikke tilladt her" + +msgid "E67: \\z1 et al. not allowed here" +msgstr "E67: \\z1 og andre ikke tilladt her" + +#, c-format +msgid "E69: Missing ] after %s%%[" +msgstr "E69: Manglende ] efter %s%%[" + +#, c-format +msgid "E70: Empty %s%%[]" +msgstr "E70: Tom %s%%[]" + +msgid "E65: Illegal back reference" +msgstr "E65: Ulovlig tilbage-reference" + +msgid "E339: Pattern too long" +msgstr "E339: Mønster for langt" + +msgid "E50: Too many \\z(" +msgstr "E50: For mange \\z(" + +#, c-format +msgid "E51: Too many %s(" +msgstr "E51: For mange %s(" + +msgid "E52: Unmatched \\z(" +msgstr "E52: Ikke-matchet \\z(" + +#, c-format +msgid "E59: invalid character after %s@" +msgstr "E59: ugyldigt tegn efter %s@" + +#, c-format +msgid "E60: Too many complex %s{...}s" +msgstr "E60: For mange komplekse %s{...}s" + +#, c-format +msgid "E61: Nested %s*" +msgstr "E61: Indlejret %s*" + +#, c-format +msgid "E62: Nested %s%c" +msgstr "E62: Indlejret %s%c" + +msgid "E63: invalid use of \\_" +msgstr "E63: ugyldig brug af \\_" + +#, c-format +msgid "E64: %s%c follows nothing" +msgstr "E64: %s%c efterfølger intet" + +msgid "E68: Invalid character after \\z" +msgstr "E68: Ugyldigt tegn efter \\z" + +#, c-format +msgid "E678: Invalid character after %s%%[dxouU]" +msgstr "E678: Ugyldigt tegn efter %s%%[dxouU]" + +#, c-format +msgid "E71: Invalid character after %s%%" +msgstr "E71: Ugyldigt tegn efter %s%%" + +#, c-format +msgid "E554: Syntax error in %s{...}" +msgstr "E554: Fejl ved syntaks i %s{...}" + +msgid "External submatches:\n" +msgstr "Eksterne undermatch:\n" + +#, c-format +msgid "E888: (NFA regexp) cannot repeat %s" +msgstr "E888: (NFA regexp) kan ikke gentage %s" + +msgid "" +"E864: \\%#= can only be followed by 0, 1, or 2. The automatic engine will be " +"used " +msgstr "" +"E864: \\%#= mÃ¥ kun efterfølges af 0, 1 eller 2. Bruger den automatiske motor " + +msgid "Switching to backtracking RE engine for pattern: " +msgstr "Skifter til backtracking RE-motor for mønster: " + +msgid "E865: (NFA) Regexp end encountered prematurely" +msgstr "E865: (NFA) Mødte slutningen pÃ¥ regulært udtryk for tidligt" + +#, c-format +msgid "E866: (NFA regexp) Misplaced %c" +msgstr "E866: (NFA regexp) Forkert placeret %c" + +#, c-format +msgid "E877: (NFA regexp) Invalid character class: %ld" +msgstr "E877: (NFA regexp) Ugyldig tegnklasse: %ld" + +#, c-format +msgid "E867: (NFA) Unknown operator '\\z%c'" +msgstr "E867: (NFA) Ukendt operator '\\z%c'" + +msgid "E951: \\% value too large" +msgstr "E951: \\%-værdi for stor" + +#, c-format +msgid "E867: (NFA) Unknown operator '\\%%%c'" +msgstr "E867: (NFA) Ukendt operator '\\%%%c'" + +msgid "E868: Error building NFA with equivalence class!" +msgstr "E868: Fejl ved bygning af NFA med ligestillet klasse!" + +#, c-format +msgid "E869: (NFA) Unknown operator '\\@%c'" +msgstr "E869: (NFA) Ukendt operator '\\@%c'" + +msgid "E870: (NFA regexp) Error reading repetition limits" +msgstr "E870: (NFA regexp) Fejl ved læsning af gentagelsesgrænser" + +msgid "E871: (NFA regexp) Can't have a multi follow a multi !" +msgstr "E871: (NFA regexp) En multi mÃ¥ ikke efterfølges af en multi !" + +msgid "E872: (NFA regexp) Too many '('" +msgstr "E872: (NFA regexp) For mange '('" + +msgid "E879: (NFA regexp) Too many \\z(" +msgstr "E879: (NFA regexp) For mange \\z(" + +msgid "E873: (NFA regexp) proper termination error" +msgstr "E873: (NFA regexp) fejl ved korrekt terminering" + +msgid "E874: (NFA) Could not pop the stack !" +msgstr "E874: (NFA) Kunne ikke pop'e stakken !" + +msgid "" +"E875: (NFA regexp) (While converting from postfix to NFA), too many states " +"left on stack" +msgstr "" +"E875: (NFA regexp) (Ved konvertering fra postfix til NFA), for mange " +"tilstande tilbage pÃ¥ stak" + +msgid "E876: (NFA regexp) Not enough space to store the whole NFA " +msgstr "E876: (NFA regexp) Ikke nok plads til at lagre hele NFA'en " + +msgid "E878: (NFA) Could not allocate memory for branch traversal!" +msgstr "E878: (NFA) Kunne ikke allokere hukommelse til gennemgang af gren!" + +msgid "" +"Could not open temporary log file for writing, displaying on stderr ... " +msgstr "Kunne ikke Ã¥bne midlertidig logfil til skrivning, viser pÃ¥ stderr ... " + +#, c-format +msgid "(NFA) COULD NOT OPEN %s !" +msgstr "(NFA) KUNNE IKKE Ã…BNE %s !" + +msgid "Could not open temporary log file for writing " +msgstr "Kunne ikke Ã¥bne midlertidig logfil til skrivning " + +msgid " VREPLACE" +msgstr " VERSTAT" + +msgid " REPLACE" +msgstr " ERSTAT" + +msgid " REVERSE" +msgstr " BAGLÆNS" + +msgid " INSERT" +msgstr " INDSÆT" + +msgid " (insert)" +msgstr " (indsæt)" + +msgid " (replace)" +msgstr " (erstat)" + +msgid " (vreplace)" +msgstr " (verstat)" + +msgid " Hebrew" +msgstr " Hebraisk" + +msgid " Arabic" +msgstr " Arabisk" + +msgid " (paste)" +msgstr " (indsæt)" + +msgid " VISUAL" +msgstr " VISUEL" + +msgid " VISUAL LINE" +msgstr " VISUEL LINJE" + +msgid " VISUAL BLOCK" +msgstr " VISUEL BLOK" + +msgid " SELECT" +msgstr " VÆLG" + +msgid " SELECT LINE" +msgstr " VÆLG LINJE" + +msgid " SELECT BLOCK" +msgstr " VÆLG BLOK" + +msgid "recording" +msgstr "optager" + +#, c-format +msgid "E383: Invalid search string: %s" +msgstr "E383: Ugyldig søgestreng: %s" + +#, c-format +msgid "E384: search hit TOP without match for: %s" +msgstr "E384: søgning ramte ØVERST uden match for: %s" + +#, c-format +msgid "E385: search hit BOTTOM without match for: %s" +msgstr "E385: søgning ramte NEDERST uden match for: %s" + +msgid "E386: Expected '?' or '/' after ';'" +msgstr "E386: Ventede '?' eller '/' efter ';'" + +msgid " (includes previously listed match)" +msgstr " (inkluderer tidligere oplistet match)" + +msgid "--- Included files " +msgstr "--- Inkluderede filer " + +msgid "not found " +msgstr "ikke fundet " + +msgid "in path ---\n" +msgstr "i sti ---\n" + +msgid " (Already listed)" +msgstr " (Allerede oplistet)" + +msgid " NOT FOUND" +msgstr " IKKE FUNDET" + +#, c-format +msgid "Scanning included file: %s" +msgstr "Skanner inkluderede filer: %s" + +#, c-format +msgid "Searching included file %s" +msgstr "Søger efter inkluderede fil %s" + +msgid "E387: Match is on current line" +msgstr "E387: Match er pÃ¥ nuværende linje" + +msgid "All included files were found" +msgstr "Alle inkluderede filer blev fundet" + +msgid "No included files" +msgstr "Ingen inkluderede filer" + +msgid "E388: Couldn't find definition" +msgstr "E388: Kunne ikke finde definition" + +msgid "E389: Couldn't find pattern" +msgstr "E389: Kunne ikke finde mønster" + +msgid "Substitute " +msgstr "Erstatning " + +#, c-format +msgid "" +"\n" +"# Last %sSearch Pattern:\n" +"~" +msgstr "" +"\n" +"# Sidste %sSøgemønster:\n" +"~" + +msgid "E756: Spell checking is not enabled" +msgstr "E756: Stavekontrol er ikke aktiveret" + +#, c-format +msgid "Warning: Cannot find word list \"%s_%s.spl\" or \"%s_ascii.spl\"" +msgstr "Advarsel: Kan ikke finde ordlisten \"%s_%s.spl\" eller \"%s_ascii.spl\"" + +#, c-format +msgid "Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\"" +msgstr "Advarsel: Kan ikke finde ordlisten \"%s.%s.spl\" eller \"%s.ascii.spl\"" + +msgid "E797: SpellFileMissing autocommand deleted buffer" +msgstr "E797: SpellFileMissing-autokommando slettede buffer" + +#, c-format +msgid "Warning: region %s not supported" +msgstr "Advarsel: regionen %s understøttes ikke" + +msgid "Sorry, no suggestions" +msgstr "Beklager, ingen forslag" + +#, c-format +msgid "Sorry, only %ld suggestions" +msgstr "Beklager, kun %ld forslag" + +#, c-format +msgid "Change \"%.*s\" to:" +msgstr "Ændr \"%.*s\" til:" + +#, c-format +msgid " < \"%.*s\"" +msgstr " < \"%.*s\"" + +msgid "E752: No previous spell replacement" +msgstr "E752: Ingen tidligere staveerstatning" + +#, c-format +msgid "E753: Not found: %s" +msgstr "E753: Ikke fundet: %s" + +msgid "E758: Truncated spell file" +msgstr "E758: Afkortet spell-fil" + +#, c-format +msgid "Trailing text in %s line %d: %s" +msgstr "Efterstillede tekst i %s linje %d: %s" + +#, c-format +msgid "Affix name too long in %s line %d: %s" +msgstr "Affix-navn for langt i %s linje %d: %s" + +msgid "E761: Format error in affix file FOL, LOW or UPP" +msgstr "E761: Fejl i format i affix-filens FOL, LOW eller UPP" + +msgid "E762: Character in FOL, LOW or UPP is out of range" +msgstr "E762: Tegn i FOL, LOW eller UPP er udenfor omrÃ¥de" + +msgid "Compressing word tree..." +msgstr "Komprimerer ordtræ..." + +#, c-format +msgid "Reading spell file \"%s\"" +msgstr "Læser spell-filen \"%s\"" + +msgid "E757: This does not look like a spell file" +msgstr "E757: Det ligner ikke en spell-fil" + +msgid "E771: Old spell file, needs to be updated" +msgstr "E771: Gammel spell-fil, som skal opdateres" + +msgid "E772: Spell file is for newer version of Vim" +msgstr "E772: Spell-filen er til en nyere version af Vim" + +msgid "E770: Unsupported section in spell file" +msgstr "E770: Ikke-understøttet sektion i spell-fil" + +#, c-format +msgid "E778: This does not look like a .sug file: %s" +msgstr "E778: ligner ikke en .sug-fil: %s" + +#, c-format +msgid "E779: Old .sug file, needs to be updated: %s" +msgstr "E779: Gammel .sug-fil, som skal opdateres: %s" + +#, c-format +msgid "E780: .sug file is for newer version of Vim: %s" +msgstr "E780: .sug-filen er til en nyere version af Vim: %s" + +#, c-format +msgid "E781: .sug file doesn't match .spl file: %s" +msgstr "E781: .sug-filen matcher ikke .spl-filen: %s" + +#, c-format +msgid "E782: error while reading .sug file: %s" +msgstr "E782: fejl ved læsning af .sug-fil: %s" + +#, c-format +msgid "Reading affix file %s ..." +msgstr "Læser affix-filen %s ..." + +#, c-format +msgid "Conversion failure for word in %s line %d: %s" +msgstr "Mislykkede konvertering for ordet %s linje %d: %s" + +#, c-format +msgid "Conversion in %s not supported: from %s to %s" +msgstr "Konvertering i %s understøttes ikke: fra %s til %s" + +#, c-format +msgid "Conversion in %s not supported" +msgstr "Konvertering i %s understøttes ikke" + +#, c-format +msgid "Invalid value for FLAG in %s line %d: %s" +msgstr "Ugyldig værdi for FLAG i %s linje %d: %s" + +#, c-format +msgid "FLAG after using flags in %s line %d: %s" +msgstr "FLAG efter brug af flag i %s linje %d: %s" + +#, c-format +msgid "" +"Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line " +"%d" +msgstr "" +"Definering af COMPOUNDFORBIDFLAG efter PFX-punkt kan give forkerte " +"resultater i %s linje %d" + +#, c-format +msgid "" +"Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line " +"%d" +msgstr "" +"Definering af COMPOUNDPERMITFLAG efter PFX-punkt kan give forkerte " +"resultater i %s linje %d" + +#, c-format +msgid "Wrong COMPOUNDRULES value in %s line %d: %s" +msgstr "Forkert COMPOUNDRULES-værdi i %s linje %d: %s" + +#, c-format +msgid "Wrong COMPOUNDWORDMAX value in %s line %d: %s" +msgstr "Forkert COMPOUNDWORDMAX-værdi i %s linje %d: %s" + +#, c-format +msgid "Wrong COMPOUNDMIN value in %s line %d: %s" +msgstr "Forkert COMPOUNDMIN-værdi i %s linje %d: %s" + +#, c-format +msgid "Wrong COMPOUNDSYLMAX value in %s line %d: %s" +msgstr "Forkert COMPOUNDSYLMAX-værdi i %s linje %d: %s" + +#, c-format +msgid "Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s" +msgstr "Forkert CHECKCOMPOUNDPATTERN-værdi i %s linje %d: %s" + +#, c-format +msgid "Different combining flag in continued affix block in %s line %d: %s" +msgstr "Forskellige kombineringsflag i fortsat affix-blok i %s linje %d: %s" + +#, c-format +msgid "Duplicate affix in %s line %d: %s" +msgstr "Duplikeret affix i %s linje %d: %s" + +#, c-format +msgid "" +"Affix also used for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST in %s " +"line %d: %s" +msgstr "" +"Affix ogsÃ¥ brugt for BAD/RARE/KEEPCASE/NEEDAFFIX/NEEDCOMPOUND/NOSUGGEST i %s " +"linje %d: %s" + +#, c-format +msgid "Expected Y or N in %s line %d: %s" +msgstr "Ventede Y eller N i %s linje %d: %s" + +#, c-format +msgid "Broken condition in %s line %d: %s" +msgstr "Ødelagt betingelse i %s linje %d: %s" + +#, c-format +msgid "Expected REP(SAL) count in %s line %d" +msgstr "Ventede REP(SAL)-tælling i %s linje %d" + +#, c-format +msgid "Expected MAP count in %s line %d" +msgstr "Ventede MAP-tælling i %s linje %d" + +#, c-format +msgid "Duplicate character in MAP in %s line %d" +msgstr "Duplikeret tegn i MAP i %s linje %d" + +#, c-format +msgid "Unrecognized or duplicate item in %s line %d: %s" +msgstr "Ikke-genkendt eller duplikeret punkt i %s linje %d: %s" + +#, c-format +msgid "Missing FOL/LOW/UPP line in %s" +msgstr "Manglende FOL-/LOW-/UPP-linje i %s" + +msgid "COMPOUNDSYLMAX used without SYLLABLE" +msgstr "COMPOUNDSYLMAX brugt uden SYLLABLE" + +msgid "Too many postponed prefixes" +msgstr "For mange udskudte præfikser" + +msgid "Too many compound flags" +msgstr "For mange compound-flag" + +msgid "Too many postponed prefixes and/or compound flags" +msgstr "For mange udskudte præfikser og/eller compound-flag" + +#, c-format +msgid "Missing SOFO%s line in %s" +msgstr "Manglende SOFO%s-linje i %s" + +#, c-format +msgid "Both SAL and SOFO lines in %s" +msgstr "BÃ¥de SAL- og SOFO-linjer i %s" + +#, c-format +msgid "Flag is not a number in %s line %d: %s" +msgstr "Flag er ikke et nummer i %s linje %d: %s" + +#, c-format +msgid "Illegal flag in %s line %d: %s" +msgstr "Ulovligt flag i %s linje %d: %s" + +#, c-format +msgid "%s value differs from what is used in another .aff file" +msgstr "%s-værdi er ikke den samme som bruges i en anden .aff-fil" + +#, c-format +msgid "Reading dictionary file %s ..." +msgstr "Læser ordbogsfilen %s ..." + +#, c-format +msgid "E760: No word count in %s" +msgstr "E760: Ingen ordtælling i %s" + +#, c-format +msgid "line %6d, word %6ld - %s" +msgstr "linje %6d, ord %6ld - %s" + +#, c-format +msgid "Duplicate word in %s line %d: %s" +msgstr "Duplikeret ord i %s linje %d: %s" + +#, c-format +msgid "First duplicate word in %s line %d: %s" +msgstr "Første duplikeret ord i %s linje %d: %s" + +#, c-format +msgid "%d duplicate word(s) in %s" +msgstr "%d duplikeret ord i %s" + +#, c-format +msgid "Ignored %d word(s) with non-ASCII characters in %s" +msgstr "Ignorerede %d ord med ikke-ASCII-tegn i %s" + +#, c-format +msgid "Reading word file %s ..." +msgstr "Læser ordfilen %s ..." + +#, c-format +msgid "Duplicate /encoding= line ignored in %s line %d: %s" +msgstr "Duplikeret /encoding=-linje ignoreret i %s linje %d: %s" + +#, c-format +msgid "/encoding= line after word ignored in %s line %d: %s" +msgstr "/encoding=-linje efter ord ignoreret i %s linje %d: %s" + +#, c-format +msgid "Duplicate /regions= line ignored in %s line %d: %s" +msgstr "Duplikerede /regions=-linjer ignoreret i %s linje %d: %s" + +#, c-format +msgid "Too many regions in %s line %d: %s" +msgstr "For mange regioner i %s linje %d: %s" + +#, c-format +msgid "/ line ignored in %s line %d: %s" +msgstr "/-linje ignoreret i %s linje %d: %s" + +#, c-format +msgid "Invalid region nr in %s line %d: %s" +msgstr "Ugyldigt regisionsnummer i %s linje %d: %s" + +#, c-format +msgid "Unrecognized flags in %s line %d: %s" +msgstr "Ugenkendte flag i %s linje %d: %s" + +#, c-format +msgid "Ignored %d words with non-ASCII characters" +msgstr "Ignorerer %d ord med ikke-ASCII-tegn" + +msgid "E845: Insufficient memory, word list will be incomplete" +msgstr "E845: Ikke nok hukommelse, ordlisten vil være ufuldstændig" + +#, c-format +msgid "Compressed %d of %d nodes; %d (%d%%) remaining" +msgstr "Komprimerede %d af %d punkter; %d (%d%%) tilbage" + +msgid "Reading back spell file..." +msgstr "Læser spell-fil tilbage..." + +msgid "Performing soundfolding..." +msgstr "Udfører lydsammenfoldning..." + +#, c-format +msgid "Number of words after soundfolding: %ld" +msgstr "Antal ord efter lydsammenfoldning: %ld" + +#, c-format +msgid "Total number of words: %d" +msgstr "Samlet antal ord: %d" + +#, c-format +msgid "Writing suggestion file %s ..." +msgstr "Skriver forslagsfilen %s ..." + +#, c-format +msgid "Estimated runtime memory use: %d bytes" +msgstr "AnslÃ¥et brug af afviklingshukommelse: %d byte" + +msgid "E751: Output file name must not have region name" +msgstr "E751: Outputfilnavn mÃ¥ ikke have regionsnavn" + +#, c-format +msgid "E754: Only up to %ld regions supported" +msgstr "E754: Kun op til %ld regioner understøttes" + +#, c-format +msgid "E755: Invalid region in %s" +msgstr "E755: Ugyldig region i %s" + +msgid "Warning: both compounding and NOBREAK specified" +msgstr "Advarsel: bÃ¥de compounding og NOBREAK angivet" + +#, c-format +msgid "Writing spell file %s ..." +msgstr "Skriver spell-filen %s ..." + +msgid "Done!" +msgstr "Færdig!" + +#, c-format +msgid "E765: 'spellfile' does not have %ld entries" +msgstr "E765: 'spellfile' har ingen %ld-poster" + +#, c-format +msgid "Word '%.*s' removed from %s" +msgstr "Ordet '%.*s' fjernet fra %s" + +#, c-format +msgid "Word '%.*s' added to %s" +msgstr "Ordet '%.*s' tilføjet til %s" + +msgid "E763: Word characters differ between spell files" +msgstr "E763: Ordtegn er ikke ens i spell-filer" + +msgid "E783: duplicate char in MAP entry" +msgstr "E783: duplikeret tegn i MAP-post" + +msgid "No Syntax items defined for this buffer" +msgstr "Ingen syntakspunkter defineret for denne buffer" + +msgid "syntax conceal on" +msgstr "syntax conceal on" + +msgid "syntax conceal off" +msgstr "syntax conceal off" + +#, c-format +msgid "E390: Illegal argument: %s" +msgstr "E390: Ulovligt argument: %s" + +msgid "syntax case ignore" +msgstr "syntax case ignore" + +msgid "syntax case match" +msgstr "syntax case match" + +msgid "syntax spell toplevel" +msgstr "syntax spell toplevel" + +msgid "syntax spell notoplevel" +msgstr "syntax spell notoplevel" + +msgid "syntax spell default" +msgstr "syntax spell default" + +msgid "syntax iskeyword " +msgstr "syntax iskeyword " + +#, c-format +msgid "E391: No such syntax cluster: %s" +msgstr "E391: Ingen sÃ¥dan syntaks-cluster: %s" + +msgid "syncing on C-style comments" +msgstr "synkronisering pÃ¥ C-style-kommentarer" + +msgid "no syncing" +msgstr "ingen synkronisering" + +msgid "syncing starts " +msgstr "synkronisering starter " + +msgid " lines before top line" +msgstr " linjer inden øverste linje" + +msgid "" +"\n" +"--- Syntax sync items ---" +msgstr "" +"\n" +"--- Syntaks-synkroniseringspunkter ---" + +msgid "" +"\n" +"syncing on items" +msgstr "" +"\n" +"synkroniserer pÃ¥ punkter" + +msgid "" +"\n" +"--- Syntax items ---" +msgstr "" +"\n" +"--- Syntakspunkter ---" + +#, c-format +msgid "E392: No such syntax cluster: %s" +msgstr "E392: Ingen sÃ¥dan syntaks-cluster: %s" + +msgid "minimal " +msgstr "minimal " + +msgid "maximal " +msgstr "maksimal " + +msgid "; match " +msgstr "; match " + +msgid " line breaks" +msgstr " linjeombrydninger" + +msgid "E395: contains argument not accepted here" +msgstr "E395: indeholder argument som ikke accepteres her" + +msgid "E844: invalid cchar value" +msgstr "E844: ugyldig cchar-værdi" + +msgid "E393: group[t]here not accepted here" +msgstr "E393: group[t]here accepteres ikke her" + +#, c-format +msgid "E394: Didn't find region item for %s" +msgstr "E394: Find ikke regionspunkter for %s" + +msgid "E397: Filename required" +msgstr "E397: Filnavn kræves" + +msgid "E847: Too many syntax includes" +msgstr "E847: For mange syntaks inkluderinger" + +#, c-format +msgid "E789: Missing ']': %s" +msgstr "E789: Manglende ']': %s" + +#, c-format +msgid "E890: trailing char after ']': %s]%s" +msgstr "E890: efterstillede tegn efter ']': %s]%s" + +#, c-format +msgid "E398: Missing '=': %s" +msgstr "E398: Manglende '=': %s" + +#, c-format +msgid "E399: Not enough arguments: syntax region %s" +msgstr "E399: For mange argumenter: syntaks region %s" + +msgid "E848: Too many syntax clusters" +msgstr "E848: For mange syntaks-clusters" + +msgid "E400: No cluster specified" +msgstr "E400: Ingen cluster angivet" + +#, c-format +msgid "E401: Pattern delimiter not found: %s" +msgstr "E401: Mønsterafgrænser ikke fundet: %s" + +#, c-format +msgid "E402: Garbage after pattern: %s" +msgstr "E402: Affald efter mønster: %s" + +msgid "E403: syntax sync: line continuations pattern specified twice" +msgstr "" +"E403: syntaks synkronisering: linjefortsættelsesmønster angivet to gange" + +#, c-format +msgid "E404: Illegal arguments: %s" +msgstr "E404: Ulovlige argumenter: %s" + +#, c-format +msgid "E405: Missing equal sign: %s" +msgstr "E405: Manglende lighedstegn: %s" + +#, c-format +msgid "E406: Empty argument: %s" +msgstr "E406: Tomt argument: %s" + +#, c-format +msgid "E407: %s not allowed here" +msgstr "E407: %s ikke tilladt her" + +#, c-format +msgid "E408: %s must be first in contains list" +msgstr "E408: %s skal være først i contains-liste" + +#, c-format +msgid "E409: Unknown group name: %s" +msgstr "E409: Ukendt gruppenavn: %s" + +#, c-format +msgid "E410: Invalid :syntax subcommand: %s" +msgstr "E410: Ugyldig :syntax-underkommando: %s" + +msgid "" +" TOTAL COUNT MATCH SLOWEST AVERAGE NAME PATTERN" +msgstr "" +" SAMLET ANTAL MATCH LANGSOMST GENNEMS. NAVN MØNSTER" + +msgid "E679: recursive loop loading syncolor.vim" +msgstr "E679: rekursiv løkke ved indlæsning af syncolor.vim" + +#, c-format +msgid "E411: highlight group not found: %s" +msgstr "E411: fremhævningsgruppe ikke fundet: %s" + +#, c-format +msgid "E412: Not enough arguments: \":highlight link %s\"" +msgstr "E412: Ikke nok argumenter: \":highlight link %s\"" + +#, c-format +msgid "E413: Too many arguments: \":highlight link %s\"" +msgstr "E413: For mange argumenter: \":highlight link %s\"" + +msgid "E414: group has settings, highlight link ignored" +msgstr "E414: gruppe har indstillinger, highlight link ignoreret" + +#, c-format +msgid "E415: unexpected equal sign: %s" +msgstr "E415: uventet lighedstegn: %s" + +#, c-format +msgid "E416: missing equal sign: %s" +msgstr "E416: manglende lighedstegn: %s" + +#, c-format +msgid "E417: missing argument: %s" +msgstr "E417: manglende argument: %s" + +#, c-format +msgid "E418: Illegal value: %s" +msgstr "E418: Ulovlig værdi: %s" + +msgid "E419: FG color unknown" +msgstr "E419: Forgrundsfarve ukendt" + +msgid "E420: BG color unknown" +msgstr "E420: Baggrundsfarve ukendt" + +#, c-format +msgid "E421: Color name or number not recognized: %s" +msgstr "E421: Farvenavn eller -nummer ikke genkendt: %s" + +#, c-format +msgid "E422: terminal code too long: %s" +msgstr "E422: terminalkode for lang: %s" + +#, c-format +msgid "E423: Illegal argument: %s" +msgstr "E423: Ulovligt argument: %s" + +msgid "E424: Too many different highlighting attributes in use" +msgstr "E424: For mange forskellige fremhævningsattributter i brug" + +msgid "E669: Unprintable character in group name" +msgstr "E669: Tegn som ikke kan udskrives i gruppenavn" + +msgid "W18: Invalid character in group name" +msgstr "W18: Ugyldige tegn i gruppenavn" + +msgid "E849: Too many highlight and syntax groups" +msgstr "E849: For mange fremhævnings- og syntaksgrupper" + +msgid "E555: at bottom of tag stack" +msgstr "E555: nederst i tag-stak" + +msgid "E556: at top of tag stack" +msgstr "E556: øverst i tag-stak" + +msgid "E425: Cannot go before first matching tag" +msgstr "E425: Kan ikke gÃ¥ efter første matchende tag" + +#, c-format +msgid "E426: tag not found: %s" +msgstr "E426: tag ikke fundet: %s" + +msgid " # pri kind tag" +msgstr " # pri kind tag" + +msgid "file\n" +msgstr "fil\n" + +msgid "E427: There is only one matching tag" +msgstr "E427: Der er kun ét matchende tag" + +msgid "E428: Cannot go beyond last matching tag" +msgstr "E428: Kan ikke gÃ¥ efter sidste matchende tag" + +#, c-format +msgid "File \"%s\" does not exist" +msgstr "Filen \"%s\" findes ikke" + +#, c-format +msgid "tag %d of %d%s" +msgstr "tag %d af %d%s" + +msgid " or more" +msgstr " eller flere" + +msgid " Using tag with different case!" +msgstr " Bruger tag med anden versaltype!" + +#, c-format +msgid "E429: File \"%s\" does not exist" +msgstr "E429: Filen \"%s\" findes ikke" + +msgid "" +"\n" +" # TO tag FROM line in file/text" +msgstr "" +"\n" +" # TIL tag FRA linje i fil/tekst" + +#, c-format +msgid "Searching tags file %s" +msgstr "Søger i tags-filen %s" + +#, c-format +msgid "E430: Tag file path truncated for %s\n" +msgstr "E430: Tag-filens sti afkortet for %s\n" + +msgid "Ignoring long line in tags file" +msgstr "Ignorerer lang linje i tags-fil" + +#, c-format +msgid "E431: Format error in tags file \"%s\"" +msgstr "E431: Fejl ved format i tags-filen \"%s\"" + +#, c-format +msgid "Before byte %ld" +msgstr "Inden byte %ld" + +#, c-format +msgid "E432: Tags file not sorted: %s" +msgstr "E432: Tags-fil ikke sorteret: %s" + +msgid "E433: No tags file" +msgstr "E433: Ingen tags-fil" + +msgid "E434: Can't find tag pattern" +msgstr "E434: Kan ikke finde tag-mønster" + +msgid "E435: Couldn't find tag, just guessing!" +msgstr "E435: Kunne ikke finde tag, gætter bare!" + +#, c-format +msgid "Duplicate field name: %s" +msgstr "Duplikeret feltnavn: %s" + +msgid "' not known. Available builtin terminals are:" +msgstr "' ikke kendt. Tilgængelige indbyggede terminaler:" + +msgid "defaulting to '" +msgstr "bruger standarden '" + +msgid "E557: Cannot open termcap file" +msgstr "E557: Kan ikke Ã¥bne termcap-fil" + +msgid "E558: Terminal entry not found in terminfo" +msgstr "E558: Terminal-post ikke fundet i terminfo" + +msgid "E559: Terminal entry not found in termcap" +msgstr "E559: Terminal-post ikke fundet i termcap" + +#, c-format +msgid "E436: No \"%s\" entry in termcap" +msgstr "E436: Ingen \"%s\"-post i termcap" + +msgid "E437: terminal capability \"cm\" required" +msgstr "E437: terminal-formÃ¥enheden \"cm\" kræves" + +msgid "" +"\n" +"--- Terminal keys ---" +msgstr "" +"\n" +"--- Terminal-taster ---" + +msgid "Cannot open $VIMRUNTIME/rgb.txt" +msgstr "Kan ikke Ã¥bne $VIMRUNTIME/rgb.txt" + +#, c-format +msgid "Kill job in \"%s\"?" +msgstr "Dræb job i \"%s\"?" + +msgid "Terminal" +msgstr "Terminal" + +msgid "Terminal-finished" +msgstr "Terminal-færdig" + +msgid "active" +msgstr "aktiv" + +msgid "running" +msgstr "køre" + +msgid "finished" +msgstr "færdig" + +#, c-format +msgid "E953: File exists: %s" +msgstr "E953: Filen findes: %s" + +msgid "E955: Not a terminal buffer" +msgstr "E955: Ikke en terminal-buffer" + +msgid "new shell started\n" +msgstr "ny skal startet\n" + +msgid "Vim: Error reading input, exiting...\n" +msgstr "Vim: Fejl ved læsning af input, afslutter...\n" + +msgid "Used CUT_BUFFER0 instead of empty selection" +msgstr "Brugte CUT_BUFFER0 i stedet for tom markering" + +msgid "E881: Line count changed unexpectedly" +msgstr "E881: Linjeantal ændret uventet" + +msgid "No undo possible; continue anyway" +msgstr "Ingen fortryd mulig; fortsætter alligevel" + +#, c-format +msgid "E828: Cannot open undo file for writing: %s" +msgstr "E828: Kan ikke Ã¥bne fortrydelsesfil til skrivning: %s" + +#, c-format +msgid "E825: Corrupted undo file (%s): %s" +msgstr "E825: Korrupt fortrydelsesfil (%s): %s" + +msgid "Cannot write undo file in any directory in 'undodir'" +msgstr "Kan ikke skrive fortrydelsesfil i nogen mappe i 'undodir'" + +#, c-format +msgid "Will not overwrite with undo file, cannot read: %s" +msgstr "Overskriver ikke med fortrydelsesfil, kan ikke læse: %s" + +#, c-format +msgid "Will not overwrite, this is not an undo file: %s" +msgstr "Overskriver ikke, det er ikke en fortrydelsesfil: %s" + +msgid "Skipping undo file write, nothing to undo" +msgstr "Springer skrivning af fortrydelsesfil over, intet at fortryde" + +#, c-format +msgid "Writing undo file: %s" +msgstr "Skriver fortrydelsesfil: %s" + +#, c-format +msgid "E829: write error in undo file: %s" +msgstr "E829: fejl ved skrivning i fortrydelsesfil: %s" + +#, c-format +msgid "Not reading undo file, owner differs: %s" +msgstr "Læser ikke fortrydelsesfil, ejer er ikke den samme: %s" + +#, c-format +msgid "Reading undo file: %s" +msgstr "Læser fortrydelsesfil: %s" + +#, c-format +msgid "E822: Cannot open undo file for reading: %s" +msgstr "E822: Kan ikke Ã¥bne fortrydelsesfil til læsning: %s" + +#, c-format +msgid "E823: Not an undo file: %s" +msgstr "E823: Ikke en fortrydelsesfil: %s" + +#, c-format +msgid "E832: Non-encrypted file has encrypted undo file: %s" +msgstr "E832: Ikke-krypteret fil har krypteret fortrydelsesfil: %s" + +#, c-format +msgid "E826: Undo file decryption failed: %s" +msgstr "E826: Dekryptering af fortrydelsesfil mislykkedes: %s" + +#, c-format +msgid "E827: Undo file is encrypted: %s" +msgstr "E827: Fortrydelsesfilen er krypteret: %s" + +#, c-format +msgid "E824: Incompatible undo file: %s" +msgstr "E824: Inkompatibel fortrydelsesfil: %s" + +msgid "File contents changed, cannot use undo info" +msgstr "Filindholdet ændret, kan ikke bruge fortrydelsesinfo" + +#, c-format +msgid "Finished reading undo file %s" +msgstr "Færdig med at læse fortrydelsesfilen %s" + +msgid "Already at oldest change" +msgstr "Allerede ved ældste ændring" + +msgid "Already at newest change" +msgstr "Allerede ved nyeste ændring" + +#, c-format +msgid "E830: Undo number %ld not found" +msgstr "E830: Fortrydelsesnummer %ld ikke fundet" + +msgid "E438: u_undo: line numbers wrong" +msgstr "E438: u_undo: linjenumre forkerte" + +msgid "more line" +msgstr "linje mere" + +msgid "more lines" +msgstr "linjer mere" + +msgid "line less" +msgstr "linje mindre" + +msgid "fewer lines" +msgstr "linjere mindre" + +msgid "change" +msgstr "ændring" + +msgid "changes" +msgstr "ændringer" + +#, c-format +msgid "%ld %s; %s #%ld %s" +msgstr "%ld %s; %s #%ld %s" + +msgid "before" +msgstr "inden" + +msgid "after" +msgstr "efter" + +msgid "Nothing to undo" +msgstr "Intet at fortryde" + +msgid "number changes when saved" +msgstr "nummer ændrin. hvornÃ¥r gemt" + +#, c-format +msgid "%ld seconds ago" +msgstr "%ld sekunder siden" + +msgid "E790: undojoin is not allowed after undo" +msgstr "E790: undojoin ikke tilladt efter undo" + +msgid "E439: undo list corrupt" +msgstr "E439: fortrydelsesliste korrupt" + +msgid "E440: undo line missing" +msgstr "E440: fortrydelseslinje mangler" + +#, c-format +msgid "E122: Function %s already exists, add ! to replace it" +msgstr "E122: Funktionen %s findes allerede, tilføj ! for at erstatte den" + +msgid "E717: Dictionary entry already exists" +msgstr "E717: Ordbog-post findes allerede" + +msgid "E718: Funcref required" +msgstr "E718: Funcref kræves" + +#, c-format +msgid "E130: Unknown function: %s" +msgstr "E130: Ukendt funktion: %s" + +#, c-format +msgid "E125: Illegal argument: %s" +msgstr "E125: Ulovligt argument: %s" + +#, c-format +msgid "E853: Duplicate argument name: %s" +msgstr "E853: Duplikeret argumentnavn: %s" + +#, c-format +msgid "E740: Too many arguments for function %s" +msgstr "E740: For mange argumenter til funktionen %s" + +#, c-format +msgid "E116: Invalid arguments for function %s" +msgstr "E116: Ugyldige argumenter til funktionen %s" + +msgid "E132: Function call depth is higher than 'maxfuncdepth'" +msgstr "E132: Dybden af funktionskald er større end 'maxfuncdepth'" + +#, c-format +msgid "calling %s" +msgstr "kalder %s" + +#, c-format +msgid "%s aborted" +msgstr "%s afbrudt" + +#, c-format +msgid "%s returning #%ld" +msgstr "%s returnerer #%ld" + +#, c-format +msgid "%s returning %s" +msgstr "%s returnerer %s" + +msgid "E699: Too many arguments" +msgstr "E699: For mange argumenter" + +#, c-format +msgid "E117: Unknown function: %s" +msgstr "E117: Ukendt funktion: %s" + +#, c-format +msgid "E933: Function was deleted: %s" +msgstr "E933: Funktion blev slettet: %s" + +#, c-format +msgid "E119: Not enough arguments for function: %s" +msgstr "E119: Ikke nok argumenter til funktionen: %s" + +#, c-format +msgid "E120: Using <SID> not in a script context: %s" +msgstr "E120: Bruger <SID> ikke i et script kontekst: %s" + +#, c-format +msgid "E725: Calling dict function without Dictionary: %s" +msgstr "E725: Kalder dict-funktion uden ordbog: %s" + +msgid "E129: Function name required" +msgstr "E129: Funktionsnavn kræves" + +#, c-format +msgid "E128: Function name must start with a capital or \"s:\": %s" +msgstr "E128: Funktionsnavnet skal begynde med et stort bogstav eller \"s:\": %s" + +#, c-format +msgid "E884: Function name cannot contain a colon: %s" +msgstr "E884: Funktionsnavnet mÃ¥ ikke indeholdet et kolon: %s" + +#, c-format +msgid "E123: Undefined function: %s" +msgstr "E123: Udefineret funktion: %s" + +#, c-format +msgid "E124: Missing '(': %s" +msgstr "E124: Manglende '(': %s" + +msgid "E862: Cannot use g: here" +msgstr "E862: Kan ikke bruge g: her" + +#, c-format +msgid "E932: Closure function should not be at top level: %s" +msgstr "E932: Closure-funktion skal ikke være pÃ¥ topniveau: %s" + +msgid "E126: Missing :endfunction" +msgstr "E126: Manglende :endfunction" + +#, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: Tekst fundet efter :endfunction: %s" + +#, c-format +msgid "E707: Function name conflicts with variable: %s" +msgstr "E707: Funktionsnavnet er i konflikt med variablen: %s" + +#, c-format +msgid "E127: Cannot redefine function %s: It is in use" +msgstr "E127: Kan ikke redefinere funktionen %s: Den er i brug" + +#, c-format +msgid "E746: Function name does not match script file name: %s" +msgstr "E746: Funktionsnavn matcher ikke scriptfilnavn: %s" + +#, c-format +msgid "E131: Cannot delete function %s: It is in use" +msgstr "E131: Kan ikke slette funktionen %s: Den er i brug" + +msgid "E133: :return not inside a function" +msgstr "E133: :return ikke i en funktion" + +#, c-format +msgid "E107: Missing parentheses: %s" +msgstr "E107: Manglende parenteser: %s" + +msgid "" +"\n" +"MS-Windows 64-bit GUI version" +msgstr "" +"\n" +"MS-Windows 64-bit GUI-version" + +msgid "" +"\n" +"MS-Windows 32-bit GUI version" +msgstr "" +"\n" +"MS-Windows 32-bit GUI-version" + +msgid " with OLE support" +msgstr " med understøttelse af OLE" + +msgid "" +"\n" +"MS-Windows 64-bit console version" +msgstr "" +"\n" +"MS-Windows 64-bit konsol-version" + +msgid "" +"\n" +"MS-Windows 32-bit console version" +msgstr "" +"\n" +"MS-Windows 32-bit konsol-version" + +msgid "" +"\n" +"macOS version" +msgstr "" +"\n" +"macOS-version" + +msgid "" +"\n" +"macOS version w/o darwin feat." +msgstr "" +"\n" +"macOS-version med/uden darwin-funktionalitet." + +msgid "" +"\n" +"OpenVMS version" +msgstr "" +"\n" +"OpenVMS-version" + +msgid "" +"\n" +"Included patches: " +msgstr "" +"\n" +"Rettelser som er med: " + +msgid "" +"\n" +"Extra patches: " +msgstr "" +"\n" +"Ekstra rettelser: " + +msgid "Modified by " +msgstr "Ændret af " + +msgid "" +"\n" +"Compiled " +msgstr "" +"\n" +"Kompileret " + +msgid "by " +msgstr "af " + +msgid "" +"\n" +"Huge version " +msgstr "" +"\n" +"Huge-version " + +msgid "" +"\n" +"Big version " +msgstr "" +"\n" +"Big-version " + +msgid "" +"\n" +"Normal version " +msgstr "" +"\n" +"Normal-version " + +msgid "" +"\n" +"Small version " +msgstr "" +"\n" +"Small-version " + +msgid "" +"\n" +"Tiny version " +msgstr "" +"\n" +"Tiny-version " + +msgid "without GUI." +msgstr "uden GUI." + +msgid "with GTK3 GUI." +msgstr "med GTK3-GUI." + +msgid "with GTK2-GNOME GUI." +msgstr "med GTK2-GNOME-GUI." + +msgid "with GTK2 GUI." +msgstr "med GTK2-GUI." + +msgid "with X11-Motif GUI." +msgstr "med X11-Motif-GUI." + +msgid "with X11-neXtaw GUI." +msgstr "med X11-neXtaw-GUI." + +msgid "with X11-Athena GUI." +msgstr "med X11-Athena-GUI." + +msgid "with Photon GUI." +msgstr "med Photon-GUI." + +msgid "with GUI." +msgstr "med GUI." + +msgid "with Carbon GUI." +msgstr "med Carbon-GUI." + +msgid "with Cocoa GUI." +msgstr "med Cocoa-GUI." + +msgid " Features included (+) or not (-):\n" +msgstr " Funktionaliteter som er med (+) eller ikke (-):\n" + +msgid " system vimrc file: \"" +msgstr " system vimrc-fil: \"" + +msgid " user vimrc file: \"" +msgstr " bruger vimrc-fil: \"" + +msgid " 2nd user vimrc file: \"" +msgstr " 2. bruger vimrc-fil: \"" + +msgid " 3rd user vimrc file: \"" +msgstr " 3. bruger vimrc-fil: \"" + +msgid " user exrc file: \"" +msgstr " bruger exrc-fil: \"" + +msgid " 2nd user exrc file: \"" +msgstr " 2. bruger exrc-fil: \"" + +msgid " system gvimrc file: \"" +msgstr " system gvimrc-fil: \"" + +msgid " user gvimrc file: \"" +msgstr " bruger gvimrc-fil: \"" + +msgid "2nd user gvimrc file: \"" +msgstr "2. bruger gvimrc-fil: \"" + +msgid "3rd user gvimrc file: \"" +msgstr "3. bruger gvimrc-fil: \"" + +msgid " defaults file: \"" +msgstr " defaults-fil: \"" + +msgid " system menu file: \"" +msgstr " system menu-fil: \"" + +msgid " fall-back for $VIM: \"" +msgstr " fall-back for $VIM: \"" + +msgid " f-b for $VIMRUNTIME: \"" +msgstr " f-b for $VIMRUNTIME: \"" + +msgid "Compilation: " +msgstr "Kompilering: " + +msgid "Compiler: " +msgstr "Kompiler: " + +msgid "Linking: " +msgstr "Linking: " + +msgid " DEBUG BUILD" +msgstr " FEJLRETNINGSBYG" + +msgid "VIM - Vi IMproved" +msgstr "VIM - Vi IMproved" + +msgid "version " +msgstr "version " + +msgid "by Bram Moolenaar et al." +msgstr "af Bram Moolenaar og andre" + +msgid "Vim is open source and freely distributable" +msgstr "Vim er open source og kan frit distribueres" + +msgid "Help poor children in Uganda!" +msgstr "Hjælp fattige børn i Uganda!" + +msgid "type :help iccf<Enter> for information " +msgstr "skriv :help iccf<Enter> for information " + +msgid "type :q<Enter> to exit " +msgstr "skriv :q<Enter> for at afslutte " + +msgid "type :help<Enter> or <F1> for on-line help" +msgstr "skriv :help<Enter> eller <F1> for onlinehjælp " + +msgid "type :help version8<Enter> for version info" +msgstr "skriv :help version8<Enter> for versionsinfo" + +msgid "Running in Vi compatible mode" +msgstr "Kører i Vi-kompatibel-tilstand" + +msgid "type :set nocp<Enter> for Vim defaults" +msgstr "skriv :set nocp<Enter> for Vim-standarder" + +msgid "type :help cp-default<Enter> for info on this" +msgstr "skriv :help cp-default<Enter> for info om det " + +msgid "menu Help->Orphans for information " +msgstr "menu Hjælp->Forældreløse børnfor information " + +msgid "Running modeless, typed text is inserted" +msgstr "Kører tilstandsløs, skrevet tekst indsættes" + +msgid "menu Edit->Global Settings->Toggle Insert Mode " +msgstr "menu Rediger->Globale indstillinger->Indsæt-tilstand til/fra " + +msgid " for two modes " +msgstr " for to-tilstande " + +msgid "menu Edit->Global Settings->Toggle Vi Compatible" +msgstr "menu Rediger->Globale indstillinger->Vi-kompatibel til/fra" + +msgid " for Vim defaults " +msgstr " for Vim-standarder " + +msgid "Sponsor Vim development!" +msgstr "Sponsorer udviklingen af Vim!" + +msgid "Become a registered Vim user!" +msgstr "Bliv en registreret Vim-bruger!" + +msgid "type :help sponsor<Enter> for information " +msgstr "skriv :help sponsor<Enter> for information " + +msgid "type :help register<Enter> for information " +msgstr "skriv :help register<Enter> for information " + +msgid "menu Help->Sponsor/Register for information " +msgstr "menu Hjælp->Sponsorer/registrer for information " + +msgid "Already only one window" +msgstr "Allerede kun ét vindue" + +msgid "E441: There is no preview window" +msgstr "E441: Der er ikke noget forhÃ¥ndsvisningsvindue" + +msgid "E442: Can't split topleft and botright at the same time" +msgstr "E442: Kan ikke opdele øverste venstre og nederste højre pÃ¥ samme tid" + +msgid "E443: Cannot rotate when another window is split" +msgstr "E443: Kan ikke rotere nÃ¥r et andet vindue er opdelt" + +msgid "E444: Cannot close last window" +msgstr "E444: Kan ikke lukke sidste vindue" + +msgid "E813: Cannot close autocmd window" +msgstr "E813: Kan ikke lukke autocmd-vindue" + +msgid "E814: Cannot close window, only autocmd window would remain" +msgstr "E814: Kan ikke lukke vindue, kun autocmd-vindue ville være tilbage" + +msgid "E445: Other window contains changes" +msgstr "E445: Et andet vindue indeholder ændringer" + +msgid "E446: No file name under cursor" +msgstr "E446: Intet filnavn under markør" + +#, c-format +msgid "E447: Can't find file \"%s\" in path" +msgstr "E447: Kan ikke finde filen \"%s\" i sti" + +#, c-format +msgid "E799: Invalid ID: %ld (must be greater than or equal to 1)" +msgstr "E799: Ugyldigt ID: %ld (skal være større end eller lig med 1)" + +#, c-format +msgid "E801: ID already taken: %ld" +msgstr "E801: ID allerede taget: %ld" + +msgid "List or number required" +msgstr "Liste eller nummer kræves" + +#, c-format +msgid "E802: Invalid ID: %ld (must be greater than or equal to 1)" +msgstr "E802: Ugyldigt ID: %ld (skal være større end eller lig med 1)" + +#, c-format +msgid "E803: ID not found: %ld" +msgstr "E803: ID ikke fundet: %ld" + +#, c-format +msgid "E370: Could not load library %s" +msgstr "E370: Kunne ikke indlæse biblioteket %s" + +msgid "Sorry, this command is disabled: the Perl library could not be loaded." +msgstr "" +"Beklager, kommandoen er deaktiveret: Perl-biblioteket kunne ikke indlæses." + +msgid "E299: Perl evaluation forbidden in sandbox without the Safe module" +msgstr "E299: Perl-evaluering forbudt i sandbox uden Safe-modulet" + +msgid "Edit with &multiple Vims" +msgstr "Rediger med &flere Vim'er" + +msgid "Edit with single &Vim" +msgstr "Rediger med én &Vim" + +msgid "Diff with Vim" +msgstr "Diff med Vim" + +msgid "Edit with &Vim" +msgstr "Rediger med &Vim" + +msgid "Edit with existing Vim - " +msgstr "Rediger med eksisterende Vim - " + +msgid "Edits the selected file(s) with Vim" +msgstr "Redigerer den valgt fil(er) med Vim" + +msgid "Error creating process: Check if gvim is in your path!" +msgstr "Fejl ved oprettelse af proces: Tjek om gvim er i din sti!" + +msgid "gvimext.dll error" +msgstr "fejl ved gvimext.dll" + +msgid "Path length too long!" +msgstr "Stiens længde er for lang!" + +msgid "--No lines in buffer--" +msgstr "--Ingen linjer i buffer--" + +msgid "E470: Command aborted" +msgstr "E470: Kommando afbrudt" + +msgid "E471: Argument required" +msgstr "E471: Argument kræves" + +msgid "E10: \\ should be followed by /, ? or &" +msgstr "E10: \\ skal efterføles af /, ? eller &" + +msgid "E11: Invalid in command-line window; <CR> executes, CTRL-C quits" +msgstr "E11: Ugyldig i kommandolinjevindue; <CR> udfører, CTRL-C afslutter" + +msgid "E12: Command not allowed from exrc/vimrc in current dir or tag search" +msgstr "" +"E12: Kommando ikke tilladt fra exrc/vimrc i nuværende mappe- eller " +"tagsøgning" + +msgid "E171: Missing :endif" +msgstr "E171: Manglende :endif" + +msgid "E600: Missing :endtry" +msgstr "E600: Manglende :endtry" + +msgid "E170: Missing :endwhile" +msgstr "E170: Manglende :endwhile" + +msgid "E170: Missing :endfor" +msgstr "E170: Manglende :endfor" + +msgid "E588: :endwhile without :while" +msgstr "E588: :endwhile uden :while" + +msgid "E588: :endfor without :for" +msgstr "E588: :endfor uden :for" + +msgid "E13: File exists (add ! to override)" +msgstr "E13: Filen findes (tilføj ! for at tilsidesætte)" + +msgid "E472: Command failed" +msgstr "E472: Kommando mislykkede" + +#, c-format +msgid "E234: Unknown fontset: %s" +msgstr "E234: Ukendt skrifttypesæt: %s" + +#, c-format +msgid "E235: Unknown font: %s" +msgstr "E235: Ukendt skrifttype: %s" + +#, c-format +msgid "E236: Font \"%s\" is not fixed-width" +msgstr "E236: Skrifttypen \"%s\" er ikke med fast bredde" + +msgid "E473: Internal error" +msgstr "E473: Intern fejl" + +#, c-format +msgid "E685: Internal error: %s" +msgstr "E685: Intern fejl: %s" + +msgid "Interrupted" +msgstr "Afbrudt" + +msgid "E14: Invalid address" +msgstr "E14: Ugyldig adresse" + +msgid "E474: Invalid argument" +msgstr "E474: Ugyldigt argument" + +#, c-format +msgid "E475: Invalid argument: %s" +msgstr "E475: Ugyldigt argument: %s" + +#, c-format +msgid "E475: Invalid value for argument %s" +msgstr "E475: Ugyldig værdi for argumentet %s" + +#, c-format +msgid "E475: Invalid value for argument %s: %s" +msgstr "E475: Ugyldig værdi for argumentet %s: %s" + +#, c-format +msgid "E15: Invalid expression: %s" +msgstr "E15: Ugyldigt udtryk: %s" + +msgid "E16: Invalid range" +msgstr "E16: Ugyldigt omrÃ¥de" + +msgid "E476: Invalid command" +msgstr "E476: Ugyldig kommando" + +#, c-format +msgid "E17: \"%s\" is a directory" +msgstr "E17: \"%s\" er en mappe" + +#, c-format +msgid "E364: Library call failed for \"%s()\"" +msgstr "E364: Kald af bibliotek mislykkedes for \"%s()\"" + +msgid "E667: Fsync failed" +msgstr "E667: Fsync mislykkedes" + +#, c-format +msgid "E448: Could not load library function %s" +msgstr "E448: Kunne ikke indlæse biblioteksfunktionen %s" + +msgid "E19: Mark has invalid line number" +msgstr "E19: Mærke har ugyldigt linjenummer" + +msgid "E20: Mark not set" +msgstr "E20: Mærke ikke sat" + +msgid "E21: Cannot make changes, 'modifiable' is off" +msgstr "E21: Kan ikke foretage ændringer, 'modifiable' er slÃ¥et fra" + +msgid "E22: Scripts nested too deep" +msgstr "E22: Scripts indlejret for dybt" + +msgid "E23: No alternate file" +msgstr "E23: Ingen alternate-fil" + +msgid "E24: No such abbreviation" +msgstr "E24: Ingen sÃ¥dan forkortelse" + +msgid "E477: No ! allowed" +msgstr "E477: Ingen ! tilladt" + +msgid "E25: GUI cannot be used: Not enabled at compile time" +msgstr "E25: GUI kan ikke bruges: Ikke aktiveret ved kompileringstid" + +msgid "E26: Hebrew cannot be used: Not enabled at compile time\n" +msgstr "E26: Hebraisk kan ikke bruges: Ikke aktiveret ved kompileringstid\n" + +msgid "E27: Farsi cannot be used: Not enabled at compile time\n" +msgstr "" +"E27: Persisk kan ikke bruges: Ikke aktiveret ved kompileringstid\n" +"\n" + +msgid "E800: Arabic cannot be used: Not enabled at compile time\n" +msgstr "E800: Arabisk kan ikke bruges: Ikke aktiveret ved kompileringstid\n" + +#, c-format +msgid "E28: No such highlight group name: %s" +msgstr "E28: Intet sÃ¥dan fremhævningsgruppenavn: %s" + +msgid "E29: No inserted text yet" +msgstr "E29: Endnu ingen indsat tekst" + +msgid "E30: No previous command line" +msgstr "E30: Ingen tidligere kommandolinje" + +msgid "E31: No such mapping" +msgstr "E31: Ingen sÃ¥dan mapping" + +msgid "E479: No match" +msgstr "E479: Intet match" + +#, c-format +msgid "E480: No match: %s" +msgstr "E480: Intet match: %s" + +msgid "E32: No file name" +msgstr "E32: Intet filnavn" + +msgid "E33: No previous substitute regular expression" +msgstr "E33: Ingen tidligere erstatnings regulært udtryk" + +msgid "E34: No previous command" +msgstr "E34: Ingen tidligere kommando" + +msgid "E35: No previous regular expression" +msgstr "E35: Ingen tidligere regulære udtryk" + +msgid "E481: No range allowed" +msgstr "E481: Intet omrÃ¥de tilladt" + +msgid "E36: Not enough room" +msgstr "E36: Ikke plads nok" + +#, c-format +msgid "E247: no registered server named \"%s\"" +msgstr "E247: ingen registreret server ved navn \"%s\"" + +#, c-format +msgid "E482: Can't create file %s" +msgstr "E482: Kan ikke oprette filen %s" + +msgid "E483: Can't get temp file name" +msgstr "E483: Kan ikke hente midlertidigt filnavn" + +#, c-format +msgid "E484: Can't open file %s" +msgstr "E484: Kan ikke Ã¥bne filen %s" + +#, c-format +msgid "E485: Can't read file %s" +msgstr "E485: Kan ikke læse filen %s" + +msgid "E38: Null argument" +msgstr "E38: Null-argument" + +msgid "E39: Number expected" +msgstr "E39: Nummer ventet" + +#, c-format +msgid "E40: Can't open errorfile %s" +msgstr "E40: Kan ikke Ã¥bne fejlfilen %s" + +msgid "E233: cannot open display" +msgstr "E233: kan ikke Ã¥bne display" + +msgid "E41: Out of memory!" +msgstr "E41: Ikke mere ledig hukommelse!" + +msgid "Pattern not found" +msgstr "Mønster ikke fundet" + +#, c-format +msgid "E486: Pattern not found: %s" +msgstr "E486: Mønster ikke fundet: %s" + +msgid "E487: Argument must be positive" +msgstr "E487: Argument skal være positivt" + +msgid "E459: Cannot go back to previous directory" +msgstr "E459: Kan ikke gÃ¥ tilbage til tidligere mappe" + +msgid "E42: No Errors" +msgstr "E42: Ingen fejl" + +msgid "E776: No location list" +msgstr "E776: Ingen placeringsliste" + +msgid "E43: Damaged match string" +msgstr "E43: Beskadiget matchstreng" + +msgid "E44: Corrupted regexp program" +msgstr "E44: Korrupt regexp-program" + +msgid "E45: 'readonly' option is set (add ! to override)" +msgstr "E45: 'readonly'-tilvalget er sat (tilføj ! for at tilsidesætte)" + +#, c-format +msgid "E46: Cannot change read-only variable \"%s\"" +msgstr "E46: Kan ikke ændre skrivebeskyttet variabel \"%s\"" + +#, c-format +msgid "E794: Cannot set variable in the sandbox: \"%s\"" +msgstr "E794: Kan ikke sætte variabel i sandboksen: \"%s\"" + +msgid "E713: Cannot use empty key for Dictionary" +msgstr "E713: Kan ikke bruge tom nøgle til ordbog" + +msgid "E715: Dictionary required" +msgstr "E715: Ordbog kræves" + +#, c-format +msgid "E684: list index out of range: %ld" +msgstr "E684: listeindeks udenfor omrÃ¥de: %ld" + +#, c-format +msgid "E118: Too many arguments for function: %s" +msgstr "E118: For mange argumenter til funktion: %s" + +#, c-format +msgid "E716: Key not present in Dictionary: %s" +msgstr "E716: Nøgle findes ikke i ordbog: %s" + +msgid "E714: List required" +msgstr "E714: Liste kræves" + +#, c-format +msgid "E712: Argument of %s must be a List or Dictionary" +msgstr "E712: Argument af %s skal være en liste eller ordbog" + +msgid "E47: Error while reading errorfile" +msgstr "E47: Fejl ved læsning af fejlfil" + +msgid "E48: Not allowed in sandbox" +msgstr "E48: Ikke tilladt i sandboks" + +msgid "E523: Not allowed here" +msgstr "E523: Ikke tilladt her" + +msgid "E359: Screen mode setting not supported" +msgstr "E359: Skærmtilstand-indstilling understøttes ikke" + +msgid "E49: Invalid scroll size" +msgstr "E49: Ugyldig rullestørrelse" + +msgid "E91: 'shell' option is empty" +msgstr "E91: 'shell'-tilvalget er tomt" + +msgid "E255: Couldn't read in sign data!" +msgstr "E255: Kunne ikke læse i sign-data!" + +msgid "E72: Close error on swap file" +msgstr "E72: Fejl ved lukning af swap-fil" + +msgid "E73: tag stack empty" +msgstr "E73: tag-stak tom" + +msgid "E74: Command too complex" +msgstr "E74: Kommando for kompleks" + +msgid "E75: Name too long" +msgstr "E75: Navn for langt" + +msgid "E76: Too many [" +msgstr "E76: For mange [" + +msgid "E77: Too many file names" +msgstr "E77: For mange filnavne" + +msgid "E488: Trailing characters" +msgstr "E488: Efterstillede tegn" + +msgid "E78: Unknown mark" +msgstr "E78: Ukendt mærke" + +msgid "E79: Cannot expand wildcards" +msgstr "E79: Kan ikke udvide jokertegn" + +msgid "E591: 'winheight' cannot be smaller than 'winminheight'" +msgstr "E591: 'winheight' mÃ¥ ikke være mindre end 'winminheight'" + +msgid "E592: 'winwidth' cannot be smaller than 'winminwidth'" +msgstr "E592: 'winwidth' mÃ¥ ikke være mindre end 'winminwidth'" + +msgid "E80: Error while writing" +msgstr "E80: Fejl ved skrivning" + +msgid "E939: Positive count required" +msgstr "E939: Positiv tælling kræves" + +msgid "E81: Using <SID> not in a script context" +msgstr "E81: Bruger <SID> ikke i et script kontekst" + +msgid "E449: Invalid expression received" +msgstr "E449: Ugyldigt udtryk modtaget" + +msgid "E463: Region is guarded, cannot modify" +msgstr "E463: Regionen er beskyttet, kan ikke ændre" + +msgid "E744: NetBeans does not allow changes in read-only files" +msgstr "E744: NetBeans tillader ikke ændringer i skrivebeskyttede filer" + +msgid "E363: pattern uses more memory than 'maxmempattern'" +msgstr "E363: mønster bruger mere hukommelse end 'maxmempattern'" + +msgid "E749: empty buffer" +msgstr "E749: tom buffer" + +#, c-format +msgid "E86: Buffer %ld does not exist" +msgstr "E86: Bufferen %ld findes ikke" + +msgid "E682: Invalid search pattern or delimiter" +msgstr "E682: Ugyldigt søgemønster eller -afgrænser" + +msgid "E139: File is loaded in another buffer" +msgstr "E139: Filen er indlæst i en anden buffer" + +#, c-format +msgid "E764: Option '%s' is not set" +msgstr "E764: Tilvalget '%s' er ikke sat" + +msgid "E850: Invalid register name" +msgstr "E850: Ugyldigt registernavn" + +#, c-format +msgid "E919: Directory not found in '%s': \"%s\"" +msgstr "E919: Mappe ikke fundet i '%s': \"%s\"" + +msgid "E952: Autocommand caused recursive behavior" +msgstr "E952: Autokommando forÃ¥rsagede rekursiv opførsel" + +msgid "search hit TOP, continuing at BOTTOM" +msgstr "søgning ramte ØVERST, fortsætter ved NEDERST" + +msgid "search hit BOTTOM, continuing at TOP" +msgstr "søgning ramte NEDERST, fortsætter ved ØVERST" + +#, c-format +msgid "Need encryption key for \"%s\"" +msgstr "Behøver krypteringsnøgle til \"%s\"" + +msgid "empty keys are not allowed" +msgstr "tomme nøgler er ikke tilladt" + +msgid "dictionary is locked" +msgstr "ordbog er lÃ¥st" + +msgid "list is locked" +msgstr "liste er lÃ¥st" + +#, c-format +msgid "failed to add key '%s' to dictionary" +msgstr "kunne ikke tilføje nøglen '%s' til ordbog" + +#, c-format +msgid "index must be int or slice, not %s" +msgstr "indeks skal være heltal eller slice, ikke %s" + +#, c-format +msgid "expected str() or unicode() instance, but got %s" +msgstr "ventede str()- eller unicode()-instans, men fik %s" + +#, c-format +msgid "expected bytes() or str() instance, but got %s" +msgstr "ventede bytes()- eller str()-instans, min fik %s" + +#, c-format +msgid "" +"expected int(), long() or something supporting coercing to long(), but got %s" +msgstr "" +"ventede int(), long() eller noget som understøtter coercing til long(), min " +"fik %s" + +#, c-format +msgid "expected int() or something supporting coercing to int(), but got %s" +msgstr "" +"ventede int() eller noget som understøtter coercing til int(), min fik %s" + +msgid "value is too large to fit into C int type" +msgstr "værdi er for stor til at passe i C-heltalstype" + +msgid "value is too small to fit into C int type" +msgstr "værdi er for lille til at passe i C-heltalstype" + +msgid "number must be greater than zero" +msgstr "nummer skal være større end nul" + +msgid "number must be greater or equal to zero" +msgstr "nummer skal være større end eller lig med nul" + +msgid "can't delete OutputObject attributes" +msgstr "kan ikke slette OutputObject-attributter" + +#, c-format +msgid "invalid attribute: %s" +msgstr "ugyldig attribut: %s" + +msgid "E264: Python: Error initialising I/O objects" +msgstr "E264: Python: Fejl ved initialisering af I/O-objekter" + +msgid "failed to change directory" +msgstr "kunne ikke skifte mappe" + +#, c-format +msgid "expected 3-tuple as imp.find_module() result, but got %s" +msgstr "ventede 3-tuple som imp.find_module() resultat, men fik %s" + +#, c-format +msgid "expected 3-tuple as imp.find_module() result, but got tuple of size %d" +msgstr "" +"ventede 3-tuple som imp.find_module() resultat, men fik tuple af størrelse %" +"d" + +msgid "internal error: imp.find_module returned tuple with NULL" +msgstr "intern fejl: imp.find_module returnerede tuple med NULL" + +msgid "cannot delete vim.Dictionary attributes" +msgstr "kan ikke slette vim.Dictionary-attributter" + +msgid "cannot modify fixed dictionary" +msgstr "kan ikke ændre fast ordbog" + +#, c-format +msgid "cannot set attribute %s" +msgstr "kan ikke sætte attributten %s" + +msgid "hashtab changed during iteration" +msgstr "hashtab ændret under gennemløb" + +#, c-format +msgid "expected sequence element of size 2, but got sequence of size %d" +msgstr "ventede sekvenselement af størrelse 2, men fik sekvens af størrelse %d" + +msgid "list constructor does not accept keyword arguments" +msgstr "liste-constructor accepterer ikke nøgleord-argumenter" + +msgid "list index out of range" +msgstr "listeindeks udenfor omrÃ¥de" + +#, c-format +msgid "internal error: failed to get vim list item %d" +msgstr "intern fejl: kunne ikke hente vim-listepunkt %d" + +msgid "slice step cannot be zero" +msgstr "slice-trin mÃ¥ ikke være nul" + +#, c-format +msgid "attempt to assign sequence of size greater than %d to extended slice" +msgstr "forsøg pÃ¥ at tildele sekvens som er større end %d til udvidet slice" + +#, c-format +msgid "internal error: no vim list item %d" +msgstr "intern fejl: intet vim-listepunkt %d" + +msgid "internal error: not enough list items" +msgstr "intern fejl: ikke nok listepunkter" + +msgid "internal error: failed to add item to list" +msgstr "intern fejl: kunne ikke tilføje punkt til liste" + +#, c-format +msgid "attempt to assign sequence of size %d to extended slice of size %d" +msgstr "" +"forsøg pÃ¥ at tildele sekvens af størrelsen %d til udvidet slice af " +"størrelsen %d" + +msgid "failed to add item to list" +msgstr "kunne ikke tilføje punkt til liste" + +msgid "cannot delete vim.List attributes" +msgstr "kan ikke slette vim.List-attributter" + +msgid "cannot modify fixed list" +msgstr "kan ikke ændre fast liste" + +#, c-format +msgid "unnamed function %s does not exist" +msgstr "unavngivet funktion %s findes ikke" + +#, c-format +msgid "function %s does not exist" +msgstr "funktionen %s findes ikke" + +#, c-format +msgid "failed to run function %s" +msgstr "kunne ikke køre funktionen %s" + +msgid "unable to get option value" +msgstr "kan ikke hente tilvalgsværdi" + +msgid "internal error: unknown option type" +msgstr "intern fejl: ukendt tilvalgstype" + +msgid "problem while switching windows" +msgstr "problem ved skift af vinduer" + +#, c-format +msgid "unable to unset global option %s" +msgstr "kan ikke fjerne det globale tilvalg %s" + +#, c-format +msgid "unable to unset option %s which does not have global value" +msgstr "kan ikke fjerne tilvalget %s som ikke har global værdi" + +msgid "attempt to refer to deleted tab page" +msgstr "forsøg pÃ¥ at referere til slettet fanebladsside" + +msgid "no such tab page" +msgstr "ingen sÃ¥dan fanebladsside" + +msgid "attempt to refer to deleted window" +msgstr "forsøg pÃ¥ at referere til slettet vindue" + +msgid "readonly attribute: buffer" +msgstr "skrivebeskyttet attribut: buffer" + +msgid "cursor position outside buffer" +msgstr "markørposition udenfor buffer" + +msgid "no such window" +msgstr "intet sÃ¥dan vindue" + +msgid "attempt to refer to deleted buffer" +msgstr "forsøg pÃ¥ at referere til slettet buffer" + +msgid "failed to rename buffer" +msgstr "kunne ikke omdøbe bufferen" + +msgid "mark name must be a single character" +msgstr "mærkenavn skal være ét tegn" + +#, c-format +msgid "expected vim.Buffer object, but got %s" +msgstr "ventede vim.Buffer-objekt, men fik %s" + +#, c-format +msgid "failed to switch to buffer %d" +msgstr "kunne ikke skifte til bufferen %d" + +#, c-format +msgid "expected vim.Window object, but got %s" +msgstr "ventede vim.Window-objekt, men fik %s" + +msgid "failed to find window in the current tab page" +msgstr "kunne ikke finde vindue i den nuværende fanebladsside" + +msgid "did not switch to the specified window" +msgstr "skiftede ikke til det angivne vindue" + +#, c-format +msgid "expected vim.TabPage object, but got %s" +msgstr "ventede vim.TabPage-objekt, men fik %s" + +msgid "did not switch to the specified tab page" +msgstr "skiftede ikke til den angivne fanebladsside" + +msgid "failed to run the code" +msgstr "kunne ikke køre koden" + +msgid "E858: Eval did not return a valid python object" +msgstr "E858: Eval returnerede ikke et gyldigt python-objekt" + +msgid "E859: Failed to convert returned python object to vim value" +msgstr "E859: Kunne ikke konvertere returnerede python-objekt til vim-værdi" + +#, c-format +msgid "unable to convert %s to vim dictionary" +msgstr "kan ikke konvertere %s til vim-ordbog" + +#, c-format +msgid "unable to convert %s to vim list" +msgstr "kan ikke konvertere %s til vim-liste" + +#, c-format +msgid "unable to convert %s to vim structure" +msgstr "kan ikke konvertere %s til vim-struktur" + +msgid "internal error: NULL reference passed" +msgstr "intern fejl: NULL-reference givet" + +msgid "internal error: invalid value type" +msgstr "intern fejl: ugyldig værditype" + +msgid "" +"Failed to set path hook: sys.path_hooks is not a list\n" +"You should now do the following:\n" +"- append vim.path_hook to sys.path_hooks\n" +"- append vim.VIM_SPECIAL_PATH to sys.path\n" +msgstr "" +"Kunne ikke sætte sti-hook: sys.path_hooks er ikke en liste\n" +"Du bør nu gøre følgende:\n" +"- tilføj vim.path_hook til slutningen af sys.path_hooks\n" +"- tilføj vim.VIM_SPECIAL_PATH til slutningen af sys.path\n" + +msgid "" +"Failed to set path: sys.path is not a list\n" +"You should now append vim.VIM_SPECIAL_PATH to sys.path" +msgstr "" +"Kunne ikke sætte sti: sys.path er ikke en liste\n" +"Du bør nu tilføje vim.VIM_SPECIAL_PATH til slutningen af sys.path" + +msgid "" +"Vim macro files (*.vim)\t*.vim\n" +"All Files (*.*)\t*.*\n" +msgstr "" +"Vim-makrofiler (*.vim)\t*.vim\n" +"Alle filer (*.*)\t*.*\n" + +msgid "All Files (*.*)\t*.*\n" +msgstr "Alle filer (*.*)\t*.*\n" + +msgid "" +"All Files (*.*)\t*.*\n" +"C source (*.c, *.h)\t*.c;*.h\n" +"C++ source (*.cpp, *.hpp)\t*.cpp;*.hpp\n" +"VB code (*.bas, *.frm)\t*.bas;*.frm\n" +"Vim files (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" +msgstr "" +"Alle filer (*.*)\t*.*\n" +"C-kildekode (*.c, *.h)\t*.c;*.h\n" +"C++-kildekode (*.cpp, *.hpp)\t*.cpp;*.hpp\n" +"VB-kode (*.bas, *.frm)\t*.bas;*.frm\n" +"Vim-filer (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" + +msgid "" +"Vim macro files (*.vim)\t*.vim\n" +"All Files (*)\t*\n" +msgstr "" +"Vim-makrofiler (*.vim)\t*.vim\n" +"Alle filer (*)\t*\n" + +msgid "All Files (*)\t*\n" +msgstr "Alle filer (*)\t*\n" + +msgid "" +"All Files (*)\t*\n" +"C source (*.c, *.h)\t*.c;*.h\n" +"C++ source (*.cpp, *.hpp)\t*.cpp;*.hpp\n" +"Vim files (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" +msgstr "" +"Alle filer (*)\t*\n" +"C-kildekode (*.c, *.h)\t*.c;*.h\n" +"C++-kildekode (*.cpp, *.hpp)\t*.cpp;*.hpp\n" +"Vim-filer (*.vim, _vimrc, _gvimrc)\t*.vim;_vimrc;_gvimrc\n" diff --git a/src/nvim/po/uk.po b/src/nvim/po/uk.po index 4c5d169ec9..211d38e53a 100644 --- a/src/nvim/po/uk.po +++ b/src/nvim/po/uk.po @@ -14,13 +14,13 @@ msgid "" msgstr "" "Project-Id-Version: vim 7.4\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-06-20 22:34+0300\n" -"PO-Revision-Date: 2010-06-18 21:53+0300\n" +"POT-Creation-Date: 2018-12-18 22:42+0200\n" +"PO-Revision-Date: 2018-12-18 22:42+0200\n" "Last-Translator: Ðнатолій Сахнік <sakhnik@gmail.com>\n" "Language-Team: Ukrainian\n" "Language: uk\n" "MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=utf-8\n" +"Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" msgid "[Location List]" @@ -77,7 +77,6 @@ msgstr "E90: Ðе можу вивантажити оÑтанній буфер" msgid "E84: No modified buffer found" msgstr "E84: Жоден буфер не змінено" -#. back where we started, didn't find anything. msgid "E85: There is no listed buffer" msgstr "E85: У ÑпиÑку немає буферів" @@ -96,7 +95,6 @@ msgid "" "E89: No write since last change for buffer %<PRId64> (add ! to override)" msgstr "E89: Буфер %<PRId64> має зміни (! щоб не зважати)" -#. wrap around (may cause duplicates) msgid "W14: Warning: List of file names overflow" msgstr "W14: Обережно: СпиÑок назв файлів переповнено" @@ -152,7 +150,6 @@ msgstr "Ñ€Ñдок %<PRId64> з %<PRId64> --%d%%-- колонка " msgid "[No Name]" msgstr "[Без назви]" -#. Must be a help buffer. msgid "help" msgstr "допомога" @@ -217,18 +214,28 @@ msgstr "E549: Ðеправильний відÑоток" msgid "E96: Cannot diff more than %<PRId64> buffers" msgstr "E96: Ðе можна порівнювати понад %<PRId64> буфери(ів)" +#, c-format +msgid "Not enough memory to use internal diff for buffer \"%s\"" +msgstr "ÐедоÑтатньо пам’Ñті Ð´Ð»Ñ Ð²Ð½ÑƒÑ‚Ñ€Ñ–ÑˆÐ½ÑŒÐ¾Ð³Ð¾ порівнÑÐ½Ð½Ñ Ñƒ буфері \"%s\"" + msgid "E810: Cannot read or write temp files" msgstr "E810: Ðе можна читати чи запиÑувати тимчаÑові файли" msgid "E97: Cannot create diffs" msgstr "E97: Ðе вдалоÑÑ Ñтворити порівнÑннÑ" +msgid "E960: Problem creating the internal diff" +msgstr "E960: Ðе вдалоÑÑ Ñтворити внутрішнє порівнÑннÑ" + msgid "E816: Cannot read patch output" msgstr "E816: Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ результат patch" msgid "E98: Cannot read diff output" msgstr "E98: Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ результат diff" +msgid "E959: Invalid diff format." +msgstr "E959: Ðеправильний формат порівнÑннÑ." + msgid "E99: Current buffer is not in diff mode" msgstr "E99: Цей буфер не в режимі порівнÑннÑ" @@ -269,7 +276,6 @@ msgstr "E791: Елемент розкладки порожній" msgid " Keyword completion (^N^P)" msgstr " Ð”Ð¾Ð¿Ð¾Ð²Ð½ÐµÐ½Ð½Ñ ÐºÐ»ÑŽÑ‡Ð¾Ð²Ð¸Ñ… Ñлів (^N^P)" -#. ctrl_x_mode == 0, ^P/^N compl. msgid " ^X mode (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" msgstr " Режим ^X (^]^D^E^F^I^K^L^N^O^Ps^U^V^Y)" @@ -329,7 +335,7 @@ msgid "Scanning dictionary: %s" msgstr "СкануєтьÑÑ Ñловник: %s" msgid " (insert) Scroll (^E/^Y)" -msgstr " (вÑтавка) Прогорнути (^E/^Y)" +msgstr " (вÑтавити) Прогорнути (^E/^Y)" msgid " (replace) Scroll (^E/^Y)" msgstr " (заміна) Прогорнути (^E/^Y)" @@ -347,10 +353,6 @@ msgstr "збіг у файлі" msgid " Adding" msgstr " ДодаєтьÑÑ" -#. showmode might reset the internal line pointers, so it must -#. * be called before line = ml_get(), or when this address is no -#. * longer needed. -- Acevedo. -#. msgid "-- Searching..." msgstr "-- Пошук..." @@ -427,7 +429,6 @@ msgstr "E461: ÐеприпуÑтима назва змінної: %s" msgid "E46: Cannot change read-only variable \"%.*s\"" msgstr "E46: Змінна тільки Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ: «%.*s»" -#. TODO(ZyX-I): move to eval/executor #, c-format msgid "E734: Wrong variable type for %s=" msgstr "E734: Ðеправильний тип змінної Ð´Ð»Ñ %s=" @@ -484,8 +485,6 @@ msgstr "E107: Пропущено дужки: %s" msgid "E108: No such variable: \"%s\"" msgstr "E108: Змінної немає: «%s»" -#. For historical reasons this error is not given for Lists and -#. Dictionaries. E.g. b: dictionary may be locked/unlocked. #, c-format msgid "E940: Cannot lock or unlock variable %s" msgstr "E940: Ðеможливо заблокувати чи розблокувати змінну %s" @@ -615,7 +614,7 @@ msgid "Invalid channel stream \"%s\"" msgstr "Ðекоректний потік Ð·Ð°Ð²Ð´Ð°Ð½Ð½Ñ Â«%s»" msgid "E785: complete() can only be used in Insert mode" -msgstr "E785: complete() можна вживати тільки в режимі вÑтавки" +msgstr "E785: complete() можна вживати тільки в режимі вÑтавлÑннÑ" msgid "&Ok" msgstr "&O:Гаразд" @@ -739,7 +738,6 @@ msgstr "аргумент uniq()" msgid "E702: Sort compare function failed" msgstr "E702: Помилка у функції порівнÑннÑ" -#. -V547 msgid "E882: Uniq compare function failed" msgstr "E882: Помилка у функції порівнÑÐ½Ð½Ñ uniq" @@ -754,6 +752,10 @@ msgstr "(Ðеможливо)" msgid "E935: invalid submatch number: %d" msgstr "E935: неправильний номер групи ÑпівпадіннÑ: %d" +#, c-format +msgid "Executing command: \"%s\"" +msgstr "ВиконуєтьÑÑ ÐºÐ¾Ð¼Ð°Ð½Ð´Ð°: «%s»" + msgid "Can only call this function in an unmodified buffer" msgstr "Цю функцію можна викликати тільки у незміненому буфері" @@ -764,7 +766,6 @@ msgstr "E921: Ðекоректний аргумент функції Ð·Ð²Ð¾Ñ€Ð¾Ñ msgid "E80: Error while writing: %s" msgstr "E80: Помилка під Ñ‡Ð°Ñ Ð·Ð°Ð¿Ð¸Ñу: %s" -#. Using %s, p and not %c, *p to preserve multibyte characters #, c-format msgid "E5060: Unknown flag: %s" msgstr "E5060: Ðевідомий прапорець: %s" @@ -781,6 +782,10 @@ msgid "E80: Error when closing file %s: %s" msgstr "E80: Помилка при закритті файлу %s: %s" #, c-format +msgid "E963: setting %s to value with wrong type" +msgstr "E963: вÑÑ‚Ð°Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ %s до Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ Ð· неправильним типом" + +#, c-format msgid "E794: Cannot set variable in the sandbox: \"%.*s\"" msgstr "E794: Ðе можна вÑтановити змінну у піÑочниці: «%.*s»" @@ -818,6 +823,10 @@ msgid "E126: Missing :endfunction" msgstr "E126: Бракує :endfunction" #, c-format +msgid "W22: Text found after :endfunction: %s" +msgstr "W22: ТрапивÑÑ Ñ‚ÐµÐºÑÑ‚ піÑÐ»Ñ :endfunction: %s" + +#, c-format msgid "E707: Function name conflicts with variable: %s" msgstr "E707: Ðазва функції Ñпівпадає зі змінною: %s" @@ -1091,8 +1100,6 @@ msgstr "чаÑтковий Ñловник self" msgid "itself" msgstr "Ñам Ñебе" -#. Only give this message once for a recursive call to avoid -#. flooding the user with errors. msgid "E724: unable to correctly dump variable with self-referencing container" msgstr "" "E724: не вдалоÑÑ ÐºÐ¾Ñ€ÐµÐºÑ‚Ð½Ð¾ злити змінну з контейнером, Ñкий Ñам на Ñебе " @@ -1244,10 +1251,22 @@ msgid "connection refused" msgstr "з'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ð¼Ð¾Ð²Ð»ÐµÐ½Ð¾" #, c-format +msgid "<%s>%s%s %d, Hex %02x, Oct %03o, Digr %s" +msgstr "<%s>%s%s %d, шіÑÑ‚ %02x, Ð²Ñ–Ñ %03o, дигр %s" + +#, c-format msgid "<%s>%s%s %d, Hex %02x, Octal %03o" msgstr "<%s>%s%s %d, шіÑÑ‚ %02x, Ð²Ñ–Ñ %03o" #, c-format +msgid "> %d, Hex %04x, Oct %o, Digr %s" +msgstr "> %d, шіÑÑ‚ %04x, Ð²Ñ–Ñ %o, дигр %s" + +#, c-format +msgid "> %d, Hex %08x, Oct %o, Digr %s" +msgstr "> %d, шіÑÑ‚ %08x, Ð²Ñ–Ñ %o, дигр %s" + +#, c-format msgid "> %d, Hex %04x, Octal %o" msgstr "> %d, шіÑÑ‚ %04x, Ð²Ñ–Ñ %o" @@ -1255,8 +1274,8 @@ msgstr "> %d, шіÑÑ‚ %04x, Ð²Ñ–Ñ %o" msgid "> %d, Hex %08x, Octal %o" msgstr "> %d, шіÑÑ‚ %08x, Ð²Ñ–Ñ %o" -msgid "E134: Move lines into themselves" -msgstr "E134: Ðеможливо переміÑтити Ñ€Ñдки Ñамі в Ñебе" +msgid "E134: Cannot move a range of lines into itself" +msgstr "E134: Ðеможливо переміÑтити діапазон Ñ€Ñдків Ñам у Ñебе" msgid "1 line moved" msgstr "Переміщено один Ñ€Ñдок" @@ -1367,8 +1386,8 @@ msgstr " в одному Ñ€Ñдку" msgid " on %<PRId64> lines" msgstr " в %<PRId64> Ñ€Ñдках" -msgid "E147: Cannot do :global recursive" -msgstr "E147: :global не можна рекурÑивно" +msgid "E147: Cannot do :global recursive with a range" +msgstr "E147: :global не можна рекурÑивно з діапазоном" msgid "E148: Regular expression missing from global" msgstr "E148: У global бракує зразка" @@ -1505,8 +1524,9 @@ msgstr "E750: Спочатку зробіть «:profile start {файл}»" msgid "Save changes to \"%s\"?" msgstr "Зберегти зміни в «%s»?" -msgid "Untitled" -msgstr "Ðеназваний" +#, c-format +msgid "Close \"%s\"?" +msgstr "Закрити «%s»?" #, c-format msgid "E162: No write since last change for buffer \"%s\"" @@ -1604,8 +1624,6 @@ msgstr "Мова (%s): «%s»" msgid "E197: Cannot set language to \"%s\"" msgstr "E197: Ðе вдалоÑÑ Ð²Ñтановити мову «%s»" -#. don't redisplay the window -#. don't wait for return msgid "Entering Ex mode. Type \"visual\" to go to Normal mode." msgstr "Режим Ex. Ð”Ð»Ñ Ð¿Ð¾Ð²ÐµÑ€Ð½ÐµÐ½Ð½Ñ Ð´Ð¾ нормального режиму виконайте «visual»" @@ -1640,8 +1658,6 @@ msgstr "E493: Інтервал задано навиворіт" msgid "Backwards range given, OK to swap" msgstr "Інтервал задано навиворіт, щоб помінÑти міÑцÑми — ГÐÐ ÐЗД" -#. append -#. typed wrong msgid "E494: Use w or w>>" msgstr "E494: Спробуйте w або w>>" @@ -1765,7 +1781,6 @@ msgstr "E189: Файл «%s» Ñ–Ñнує (! щоб не зважати)" msgid "E190: Cannot open \"%s\" for writing" msgstr "E190: Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ «%s» Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу" -#. set mark msgid "E191: Argument must be a letter or forward/backward quote" msgstr "E191: Ðргумент має бути літерою, ` або '" @@ -1797,15 +1812,16 @@ msgstr "E499: Ðазва файлу Ð´Ð»Ñ '%' чи '#' порожнÑ, прац msgid "E500: Evaluates to an empty string" msgstr "E500: Результат — порожній Ñ€Ñдок" +msgid "Untitled" +msgstr "Ðеназваний" + msgid "E608: Cannot :throw exceptions with 'Vim' prefix" msgstr "E608: Ðе можна викидати (:throw) винÑтки з префікÑом 'Vim'" -#. always scroll up, don't overwrite #, c-format msgid "Exception thrown: %s" msgstr "ВинÑткова ÑитуаціÑ: %s" -#. always scroll up, don't overwrite #, c-format msgid "Exception finished: %s" msgstr "ВинÑток закінчено: %s" @@ -1818,7 +1834,6 @@ msgstr "ВинÑток Ñкинуто: %s" msgid "%s, line %<PRId64>" msgstr "%s, Ñ€Ñдок %<PRId64>" -#. always scroll up, don't overwrite #, c-format msgid "Exception caught: %s" msgstr "Спіймано винÑткову Ñитуацію: %s" @@ -1844,7 +1859,6 @@ msgstr "Помилка, перервано" msgid "Error" msgstr "Помилка" -#. if (pending & CSTP_INTERRUPT) msgid "Interrupt" msgstr "Перервано" @@ -1887,15 +1901,12 @@ msgstr "E601: Забагато вкладених :try" msgid "E603: :catch without :try" msgstr "E603: :catch без :try" -#. Give up for a ":catch" after ":finally" and ignore it. -#. * Just parse. msgid "E604: :catch after :finally" msgstr "E604: :catch піÑÐ»Ñ :finally" msgid "E606: :finally without :try" msgstr "E606: :finally без :try" -#. Give up for a multiple ":finally" and ignore it. msgid "E607: multiple :finally" msgstr "E607: Ðе одне :finally" @@ -1927,8 +1938,8 @@ msgid "E5401: List item %i is not a List" msgstr "E5401: Елемент ÑпиÑку %i не List" #, c-format -msgid "E5402: List item %i has incorrect length: %li /= 3" -msgstr "E5402: Елемент ÑпиÑку %i має неправильну довжину: %li /= 3" +msgid "E5402: List item %i has incorrect length: %d /= 3" +msgstr "E5402: Елемент ÑпиÑку %i має неправильну довжину: %d /= 3" msgid "E5403: Chunk %i start %" msgstr "E5403: Початок шматка %i %" @@ -2002,8 +2013,6 @@ msgstr "[Ðовий файл]" msgid "[New DIRECTORY]" msgstr "[Ðовий каталог]" -#. libuv only returns -errno in Unix and in Windows open() does not -#. set EOVERFLOW msgid "[File too big]" msgstr "[Файл завеликий]" @@ -2016,23 +2025,18 @@ msgstr "E200: Ðвтокоманди *ReadPre унеможливили читаРmsgid "E201: *ReadPre autocommands must not change current buffer" msgstr "E201: Ðвтокоманди *ReadPre не повинні змінювати цей буфер" -#. Re-opening the original file failed! msgid "E202: Conversion made file unreadable!" msgstr "E202: ÐšÐ¾Ð½Ð²ÐµÑ€Ñ‚Ð°Ñ†Ñ–Ñ ÑƒÐ½ÐµÐ¼Ð¾Ð¶Ð»Ð¸Ð²Ð¸Ð»Ð° Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ!" -#. fifo or socket msgid "[fifo/socket]" msgstr "[канал/Ñокет]" -#. fifo msgid "[fifo]" msgstr "[канал]" -#. or socket msgid "[socket]" msgstr "[Ñокет]" -#. or character special msgid "[character special]" msgstr "[Ñпец. Ñимвольний]" @@ -2098,7 +2102,6 @@ msgstr "E509: Ðе вдалоÑÑ Ñтворити резервну копію ( msgid "E510: Can't make backup file (add ! to override)" msgstr "E510: Ðе вдалоÑÑ Ð·Ñ€Ð¾Ð±Ð¸Ñ‚Ð¸ резервну копію (! щоб не зважати)" -#. Can't write without a tempfile! msgid "E214: Can't find temp file for writing" msgstr "E214: Ðе вдалоÑÑ Ð¿Ñ–Ð´ÑˆÑƒÐºÐ°Ñ‚Ð¸ тимчаÑовий файл Ð´Ð»Ñ Ð·Ð°Ð¿Ð¸Ñу" @@ -2163,7 +2166,6 @@ msgstr "E206: ЛатаннÑ: не вдалоÑÑ Ñтворити оригінРmsgid "E207: Can't delete backup file" msgstr "E207: Ðе вдалоÑÑ Ð·Ð½Ð¸Ñ‰Ð¸Ñ‚Ð¸ резервний файл" -#. Set highlight for error messages. msgid "" "\n" "WARNING: Original file may be lost or damaged\n" @@ -2212,9 +2214,6 @@ msgstr "[Ðеповний оÑтанній Ñ€Ñдок]" msgid "[noeol]" msgstr "[noeol]" -#. Don't overwrite messages here. -#. Must give this prompt. -#. Don't use emsg() here, don't want to flush the buffers. msgid "WARNING: The file has been changed since reading it!!!" msgstr "ЗÐСТЕРЕЖЕÐÐЯ: Файл змінивÑÑ Ð· чаÑу оÑтаннього читаннÑ!!!" @@ -2292,7 +2291,6 @@ msgstr "--Знищено--" msgid "auto-removing autocommand: %s <buffer=%d>" msgstr "Ðвтоматичне Ð·Ð½Ð¸Ñ‰ÐµÐ½Ð½Ñ Ð°Ð²Ñ‚Ð¾ÐºÐ¾Ð¼Ð°Ð½Ð´Ð¸: %s <буфер=%d>" -#. the group doesn't exist #, c-format msgid "E367: No such group: \"%s\"" msgstr "E367: Ðемає такої групи: «%s»" @@ -2315,10 +2313,9 @@ msgstr "E216: Ðемає такої події: %s" msgid "E216: No such group or event: %s" msgstr "E216: Ðемає такої групи чи події: %s" -#. Highlight title msgid "" "\n" -"--- Auto-Commands ---" +"--- Autocommands ---" msgstr "" "\n" "--- Ðвтокоманди ---" @@ -2337,8 +2334,8 @@ msgid "E218: autocommand nesting too deep" msgstr "E218: Забагато вкладених автокоманд" #, c-format -msgid "%s Auto commands for \"%s\"" -msgstr "Ðвтокоманди %s Ð´Ð»Ñ Â«%s»" +msgid "%s Autocommands for \"%s\"" +msgstr "%s Ðвтокоманди Ð´Ð»Ñ Â«%s»" #, c-format msgid "Executing %s" @@ -2363,7 +2360,6 @@ msgstr "E350: Ðе вдалоÑÑ Ñтворити згортку методом msgid "E351: Cannot delete fold with current 'foldmethod'" msgstr "E351: Ðе вдалоÑÑ Ð·Ð½Ð¸Ñ‰Ð¸Ñ‚Ð¸ згортку методом 'foldmethod'" -#. buffer has already been read msgid "E222: Add to read buffer" msgstr "E222: Додати до буфера читаннÑ" @@ -2395,17 +2391,9 @@ msgstr "Заміни клавіш не знайдено" msgid "E228: makemap: Illegal mode" msgstr "E228: makemap: ÐеприпуÑтимий режим" -#. /< key value of 'cedit' option -#. /< type of cmdline window or 0 -#. /< result of cmdline window or 0 -#. /< cmdline recursion level msgid "--No lines in buffer--" msgstr "--Жодного Ñ€Ñдка--" -#. -#. * The error messages that can be shared are included here. -#. * Excluded are errors that are only used once and debugging messages. -#. msgid "E470: Command aborted" msgstr "E470: Команду перервано" @@ -2516,7 +2504,8 @@ msgstr "E906: Ðекоректний потік Ð´Ð»Ñ ÐºÐ°Ð½Ð°Ð»Ñƒ rpc, use 'rp msgid "" "E5210: dict key '%s' already set for buffered stream in channel %<PRIu64>" msgstr "" -"E5210: ключ Ñловника '%s' вже вÑтановлено Ð´Ð»Ñ Ð±ÑƒÑ„ÐµÑ€Ð¸Ð·Ð¾Ð²Ð°Ð½Ð¾Ð³Ð¾ потоку у каналі %<PRIu64>" +"E5210: ключ Ñловника '%s' вже вÑтановлено Ð´Ð»Ñ Ð±ÑƒÑ„ÐµÑ€Ð¸Ð·Ð¾Ð²Ð°Ð½Ð¾Ð³Ð¾ потоку у каналі " +"%<PRIu64>" #, c-format msgid "E364: Library call failed for \"%s()\"" @@ -2741,6 +2730,9 @@ msgstr "E850: Ðеправильна назва регіÑтру" msgid "E919: Directory not found in '%s': \"%s\"" msgstr "E919: Каталог не знайдено у '%s': «%s»" +msgid "E952: Autocommand caused recursive behavior" +msgstr "E952: Ðвтокоманди призвели до рекурÑÑ–Ñ—" + msgid "E519: Option not supported" msgstr "E519: ÐžÐ¿Ñ†Ñ–Ñ Ð½Ðµ підтримуєтьÑÑ" @@ -2864,6 +2856,9 @@ msgstr "E365: Ðе вдалоÑÑ Ð½Ð°Ð´Ñ€ÑƒÐºÑƒÐ²Ð°Ñ‚Ð¸ файл PostScript" msgid "Print job sent." msgstr "Ð—Ð°Ð²Ð´Ð°Ð½Ð½Ñ Ð´Ñ€ÑƒÐºÑƒ відіÑлано." +msgid "E424: Too many different highlighting attributes in use" +msgstr "E424: ВикориÑтано забагато різних атрибутів кольору" + msgid "Add a new database" msgstr "Додати нову базу даних" @@ -2988,7 +2983,6 @@ msgstr "E261: З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· cscope %s не знайдено" msgid "cscope connection %s closed" msgstr "З'Ñ”Ð´Ð½Ð°Ð½Ð½Ñ Ð· cscope %s закінчено" -#. should not reach here msgid "E570: fatal error in cs_manage_matches" msgstr "E570: Фатальна помилка в cs_manage_matches" @@ -2996,7 +2990,6 @@ msgstr "E570: Фатальна помилка в cs_manage_matches" msgid "Cscope tag: %s" msgstr "Мітка cscope: %s" -#. Column headers for match number, line number and filename. msgid "" "\n" " # line" @@ -3028,8 +3021,8 @@ msgid "" "E5100: Cannot convert given lua table: table should either have a sequence " "of positive integer keys or contain only string keys" msgstr "" -"E5100: Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€Ð¸Ñ‚Ð¸ таблицю lua: Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ Ð¿Ð¾Ð²Ð¸Ð½Ð½Ð° мати поÑлідовніÑть " -"додатних чиÑел Ñк ключі або текÑтові ключі" +"E5100: Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€Ð¸Ñ‚Ð¸ таблицю lua: Ñ‚Ð°Ð±Ð»Ð¸Ñ†Ñ Ð¿Ð¾Ð²Ð¸Ð½Ð½Ð° мати " +"поÑлідовніÑть додатних чиÑел Ñк ключі або текÑтові ключі" msgid "E5101: Cannot convert given lua type" msgstr "E5101: Ðе вдалоÑÑ Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€Ð¸Ñ‚Ð¸ тип lua" @@ -3045,7 +3038,6 @@ msgstr "E5106: Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ð¼Ð¾Ð´ÑƒÐ»Ñ vim: %.*s" msgid "E970: Failed to initialize lua interpreter" msgstr "E970: Ðе вдалоÑÑ Ñ–Ð½Ñ–Ñ†Ñ–Ð°Ð»Ñ–Ð·ÑƒÐ²Ð°Ñ‚Ð¸ інтерпретатор lua" -#. stack: vim, error #, c-format msgid "E5117: Error while updating package paths: %.*s" msgstr "E5117: Помилка Ð¾Ð½Ð¾Ð²Ð»ÐµÐ½Ð½Ñ ÑˆÐ»Ñхів пакунку: %.*s" @@ -3101,7 +3093,6 @@ msgstr "E5112: Помилка ÑÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ ÑˆÐ¼Ð°Ñ‚ÐºÑƒ lua: %.*s" msgid "E5113: Error while calling lua chunk: %.*s" msgstr "E5113: Помилка виклику шматку lua: %.*s" -#. Error messages msgid "Argument missing after" msgstr "Пропущено аргумент піÑлÑ" @@ -3132,7 +3123,6 @@ msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ: \"%s\": %s\n msgid "Cannot open for script output: \"" msgstr "Ðе вдалоÑÑ Ð²Ñ–Ð´ÐºÑ€Ð¸Ñ‚Ð¸ Ñк вихідний файл: \"" -#. just in case.. msgid "pre-vimrc command line" msgstr "команди перед vimrc" @@ -3147,7 +3137,6 @@ msgstr "" "\n" "ДізнайтеÑÑ Ð±Ñ–Ð»ÑŒÑˆÐµ: \"" -#. kill us with CTRL-C here, if you like msgid "Usage:\n" msgstr "Вжиток:\n" @@ -3155,8 +3144,7 @@ msgid " nvim [options] [file ...] Edit file(s)\n" msgstr " nvim [опції] [файл ...] Редагувати файли\n" msgid " nvim [options] -t <tag> Edit file where tag is defined\n" -msgstr "" -" nvim [опції] -t <мітка> Редагувати файл, де визначено мітку\n" +msgstr " nvim [опції] -t <мітка> Редагувати файл, де визначено мітку\n" msgid " nvim [options] -q [errorfile] Edit file with first error\n" msgstr " nvim [опції] -q [Ñ„.помилки] Редагувати файл з першою помилкою\n" @@ -3175,7 +3163,8 @@ msgid " + Start at end of file\n" msgstr " + Розпочати в кінці файлу\n" msgid " --cmd <cmd> Execute <cmd> before any config\n" -msgstr " --cmd <команда> Виконати <команду> перед будь-Ñкою конфігурацією\n" +msgstr "" +" --cmd <команда> Виконати <команду> перед будь-Ñкою конфігурацією\n" msgid " +<cmd>, -c <cmd> Execute <cmd> after config and first file\n" msgstr "" @@ -3211,13 +3200,13 @@ msgstr "" "пам'Ñті\n" msgid " -o[N] Open N windows (default: one per file)\n" -msgstr "" -" -o[N] Відкрити N вікон (Ñтандартно: одне на файл)\n" +msgstr " -o[N] Відкрити N вікон (Ñтандартно: одне на файл)\n" msgid "" " -O[N] Open N vertical windows (default: one per file)\n" msgstr "" -" -o[N] Відкрити N вертикальних вікон (Ñтандартно: одне на файл)\n" +" -o[N] Відкрити N вертикальних вікон (Ñтандартно: одне на " +"файл)\n" msgid " -p[N] Open N tab pages (default: one per file)\n" msgstr "" @@ -3244,19 +3233,18 @@ msgid " -u <config> Use this config file\n" msgstr " -u <config> Вжити цей файл конфігурації\n" msgid " -v, --version Print version information\n" -msgstr "" -" -v, --version Ðадрукувати інформацію про верÑÑ–ÑŽ програми\n" +msgstr " -v, --version Ðадрукувати інформацію про верÑÑ–ÑŽ програми\n" msgid " -V[N][file] Verbose [level][file]\n" -msgstr "" -" -V[N][файл] Більше повідомлень [рівень][файл]\n" +msgstr " -V[N][файл] Більше повідомлень [рівень][файл]\n" msgid " -Z Restricted mode\n" msgstr " -Z Обмежений режим\n" msgid " --api-info Write msgpack-encoded API metadata to stdout\n" msgstr "" -" --api-info ЗапиÑати метадані API, Ñеріалізовані у msgpack, у stdout\n" +" --api-info ЗапиÑати метадані API, Ñеріалізовані у msgpack, у " +"stdout\n" msgid " --embed Use stdin/stdout as a msgpack-rpc channel\n" msgstr "" @@ -3288,7 +3276,6 @@ msgstr "Ðе вÑтановлено жодної помітки" msgid "E283: No marks matching \"%s\"" msgstr "E283: Помітку «%s» не знайдено" -#. Highlight title msgid "" "\n" "mark line col file/text" @@ -3296,7 +3283,6 @@ msgstr "" "\n" "пом. Ñ€Ñд. кол. файл/текÑÑ‚" -#. Highlight title msgid "" "\n" " jump line col file/text" @@ -3304,7 +3290,6 @@ msgstr "" "\n" " точка Ñ€Ñд. Ñтовп. файл/текÑÑ‚" -#. Highlight title msgid "" "\n" "change line col text" @@ -3339,7 +3324,6 @@ msgstr "E298: Ðемає блоку 1?" msgid "E298: Didn't get block nr 2?" msgstr "E298: Ðемає блоку 2?" -#. could not (re)open the swap file, what can we do???? msgid "E301: Oops, lost the swap file!!!" msgstr "E301: Ой, втрачено файл обміну!!!" @@ -3353,7 +3337,6 @@ msgstr "E303: Ðе вдалоÑÑ Ð¿Ñ€Ð¾Ñ‡Ð¸Ñ‚Ð°Ñ‚Ð¸ файл обміну Ð´Ð»Ñ msgid "E304: ml_upd_block0(): Didn't get block 0??" msgstr "E304: ml_upd_block0(): Ðемає блоку 0??" -#. no swap files found #, c-format msgid "E305: No swap file found for %s" msgstr "E305: Ðе знайдено файлу обміну Ð´Ð»Ñ %s" @@ -3481,7 +3464,6 @@ msgstr "" "Можливо, тепер ви хочете знищити файл обміну .swp.\n" "\n" -#. use msg() to start the scrolling properly msgid "Swap files found:" msgstr "Знайдено файли обміну:" @@ -3555,8 +3537,8 @@ msgstr "" "\n" " ID процеÑу: " -msgid " (still running)" -msgstr " (виконуєтьÑÑ)" +msgid " (STILL RUNNING)" +msgstr " (ЩЕ ВИКОÐУЄТЬСЯ)" msgid "" "\n" @@ -3644,11 +3626,12 @@ msgstr "" msgid "While opening file \"" msgstr "При відкритті файлу \"" +msgid " CANNOT BE FOUND" +msgstr " ÐЕ ЗÐÐЙДЕÐО" + msgid " NEWER than swap file!\n" msgstr " ÐОВІШИЙ за файл обміну!\n" -#. Some of these messages are long to allow translation to -#. * other languages. msgid "" "\n" "(1) Another program may be editing the same file. If this is the case,\n" @@ -3720,14 +3703,6 @@ msgstr "" "&Q:Вийти\n" "&A:Перервати" -#. -#. * Change the ".swp" extension to find another file that can be used. -#. * First decrement the last char: ".swo", ".swn", etc. -#. * If that still isn't enough decrement the last but one char: ".svz" -#. * Can happen when editing many "No Name" buffers. -#. -#. ".s?a" -#. ".saa": tried enough, give up msgid "E326: Too many swap files found" msgstr "E326: Знайдено забагато файлів обміну" @@ -3756,7 +3731,6 @@ msgstr "E328: Меню може бути тільки в іншому режим msgid "E329: No menu \"%s\"" msgstr "E329: Ðемає меню «%s»" -#. Only a mnemonic or accelerator is not valid. msgid "E792: Empty menu name" msgstr "E792: ÐŸÐ¾Ñ€Ð¾Ð¶Ð½Ñ Ð½Ð°Ð·Ð²Ð° меню" @@ -3769,8 +3743,6 @@ msgstr "E331: Ðе можна додавати елементи меню Ð¿Ñ€Ð¾Ñ msgid "E332: Separator cannot be part of a menu path" msgstr "E332: Роздільник не може бути чаÑтиною шлÑху меню" -#. Now we have found the matching menu, and we list the mappings -#. Highlight title msgid "" "\n" "--- Menus ---" @@ -3878,15 +3850,6 @@ msgstr " (Перервано)" msgid "Beep!" msgstr "Дзень!" -#, c-format -msgid "Calling shell to execute: \"%s\"" -msgstr "ВикликаєтьÑÑ Ð¾Ð±Ð¾Ð»Ð¾Ð½ÐºÐ° щоб виконати: «%s»" - -#. -#. * nv_*(): functions called to handle Normal and Visual mode commands. -#. * n_*(): functions called to handle Normal mode commands. -#. * v_*(): functions called to handle Visual mode commands. -#. msgid "E349: No identifier under cursor" msgstr "E349: Ðемає ідентифікатора над курÑором" @@ -3912,7 +3875,8 @@ msgid "E663: At end of changelist" msgstr "E663: Кінець ÑпиÑку змін" msgid "Type :qa! and press <Enter> to abandon all changes and exit Nvim" -msgstr "Введіть :qa! Ñ– натиÑніÑть <Enter> щоб відкинути вÑÑ– зміни Ñ– вийти Nvim" +msgstr "" +"Введіть :qa! Ñ– натиÑніÑть <Enter> щоб відкинути вÑÑ– зміни Ñ– вийти Nvim" #, c-format msgid "1 line %sed 1 time" @@ -3951,25 +3915,30 @@ msgstr "Один Ñ€Ñдок змінено" msgid "%<PRId64> lines changed" msgstr "Змінено Ñ€Ñдків: %<PRId64>" -msgid "block of 1 line yanked" -msgstr "Запам'Ñтав блок з одного Ñ€Ñдка" +#, c-format +msgid " into \"%c" +msgstr " у \"%c" + +#, c-format +msgid "block of 1 line yanked%s" +msgstr "Запам'Ñтав блок з одного Ñ€Ñдка%s" -msgid "1 line yanked" -msgstr "Запам'Ñтав один Ñ€Ñдок" +#, c-format +msgid "1 line yanked%s" +msgstr "Запам'Ñтав один Ñ€Ñдок%s" #, c-format -msgid "block of %<PRId64> lines yanked" -msgstr "Запам'Ñтав блок із %<PRId64> Ñ€Ñдків" +msgid "block of %<PRId64> lines yanked%s" +msgstr "Запам'Ñтав блок із %<PRId64> Ñ€Ñдків%s" #, c-format -msgid "%<PRId64> lines yanked" -msgstr "Запам'Ñтав Ñ€Ñдків: %<PRId64>" +msgid "%<PRId64> lines yanked%s" +msgstr "Запам'Ñтав Ñ€Ñдків: %<PRId64>%s" #, c-format msgid "E353: Nothing in register %s" msgstr "E353: У регіÑтрі %s нічого немає" -#. Highlight title msgid "" "\n" "--- Registers ---" @@ -4023,7 +3992,6 @@ msgstr "" msgid "(+%<PRId64> for BOM)" msgstr "(+%<PRId64> Ð´Ð»Ñ BOM)" -#. found a mismatch: skip msgid "E518: Unknown option" msgstr "E518: Ðевідома опціÑ" @@ -4111,9 +4079,6 @@ msgstr "E594: Потрібно щонайменше %d Ñтовпців" msgid "E355: Unknown option: %s" msgstr "E355: Ðевідома опціÑ: %s" -#. There's another character after zeros or the string -#. is empty. In both cases, we are trying to set a -#. num option using a string. #, c-format msgid "E521: Number required: &%s = '%s'" msgstr "E521: Потрібно вказати Number: &%s = '%s'" @@ -4175,8 +4140,6 @@ msgstr "" "\n" "не вдалоÑÑ Ð·Ð°Ð¿ÑƒÑтити оболонку: " -#. Can happen if system() tries to send input to a shell command that was -#. backgrounded (:call system("cat - &", "foo")). #3529 #5241 #, c-format msgid "E5677: Error writing input to shell-command: %s" msgstr "E5677: Ðе вдалоÑÑ Ð·Ð°Ð¿Ð¸Ñати на вхід команди оболонки: %s" @@ -4222,7 +4185,6 @@ msgstr "E376: Помилковий `%%%c' у префікÑÑ– Ñ€Ñдку форРmsgid "E377: Invalid %%%c in format string" msgstr "E377: Помилковий `%%%c' у Ñ€Ñдку формату" -#. nothing found msgid "E378: 'errorformat' contains no pattern" msgstr "E378: 'errorformat' не міÑтить зразок" @@ -4306,8 +4268,8 @@ msgstr "E55: Ðемає пари %s)" msgid "E66: \\z( not allowed here" msgstr "E66: \\z( тут не дозволено" -msgid "E67: \\z1 et al. not allowed here" -msgstr "E67: \\z1 та ін. тут не дозволено" +msgid "E67: \\z1 - \\z9 not allowed here" +msgstr "E67: \\z1 - \\z9 тут не дозволено" #, c-format msgid "E69: Missing ] after %s%%[" @@ -4401,10 +4363,10 @@ msgid " REVERSE" msgstr " ÐÐВИВОРІТ" msgid " INSERT" -msgstr " ВСТÐВКÐ" +msgstr " ВСТÐВИТИ" msgid " (insert)" -msgstr " (вÑтавка)" +msgstr " (вÑтавити)" msgid " (replace)" msgstr " (заміна)" @@ -4460,7 +4422,6 @@ msgstr "E386: ПіÑÐ»Ñ `;' має бути `?' або `/'" msgid " (includes previously listed match)" msgstr " (разом з попередніми збігами)" -#. cursor at status line msgid "--- Included files " msgstr "--- Включені файли " @@ -4523,8 +4484,8 @@ msgid "System error while writing ShaDa file: %s" msgstr "СиÑтемна помилка при читанні з файлу ShaDa: %s" #, c-format -msgid "Reading ShaDa file \"%s\"%s%s%s" -msgstr "ЗчитуєтьÑÑ Ñ„Ð°Ð¹Ð» ShaDa: «%s»%s%s%s" +msgid "Reading ShaDa file \"%s\"%s%s%s%s" +msgstr "ЗчитуєтьÑÑ Ñ„Ð°Ð¹Ð» ShaDa: «%s»%s%s%s%s" msgid " info" msgstr " інформаціÑ" @@ -4582,8 +4543,6 @@ msgstr "" "СиÑтемна помилка при відкритті файлу ShaDa %s Ð´Ð»Ñ Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ñ‰Ð¾Ð± виконати " "Ð·Ð»Ð¸Ñ‚Ñ‚Ñ Ð¿ÐµÑ€ÐµÐ´ запиÑом: %s" -#. Tried names from .tmp.a to .tmp.z, all failed. Something must be -#. wrong then. #, c-format msgid "E138: All %s.tmp.X files exist, cannot write ShaDa file!" msgstr "E138: УÑÑ– файли %s.tmp.X зайнÑто, неможливо запиÑати файл ShaDa!" @@ -4657,12 +4616,8 @@ msgstr "" msgid "" "Error while reading ShaDa file: there is an item at position %<PRIu64> that " "is stated to be too long" -msgstr "" -"Помилка Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ ShaDa: Ñ” задовгий елемент у позиції %<PRIu64>" +msgstr "Помилка Ñ‡Ð¸Ñ‚Ð°Ð½Ð½Ñ Ñ„Ð°Ð¹Ð»Ñƒ ShaDa: Ñ” задовгий елемент у позиції %<PRIu64>" -#. kSDItemUnknown cannot possibly pass that far because it is -1 and that -#. will fail in msgpack_read_uint64. But kSDItemMissing may and it will -#. otherwise be skipped because (1 << 0) will never appear in flags. #, c-format msgid "" "Error while reading ShaDa file: there is an item at position %<PRIu64> that " @@ -4703,24 +4658,6 @@ msgstr "" "Помилка при читанні файлу ShaDa: ÑпиÑок буферів у позиції %<PRIu64> міÑтить " "поле, Ñке не має назву файлу" -#. values for ts_isdiff -#. no different byte (yet) -#. different byte found -#. inserting character -#. values for ts_flags -#. already checked that prefix is OK -#. tried split at this point -#. did a delete, "ts_delidx" has index -#. special values ts_prefixdepth -#. not using prefixes -#. walking through the prefix tree -#. highest value that's not special -#. mode values for find_word -#. find word case-folded -#. find keep-case word -#. find word after prefix -#. find case-folded compound word -#. find keep-case compound word msgid "E759: Format error in spell file" msgstr "E759: Помилка формату у файлі орфографії" @@ -4735,8 +4672,6 @@ msgstr "" msgid "E797: SpellFileMissing autocommand deleted buffer" msgstr "E797: Ðвтокоманда SpellFileMissing знищила буфер" -#. This is probably an error. Give a warning and -#. accept the words anyway. #, c-format msgid "Warning: region %s not supported" msgstr "ЗаÑтереженнÑ: регіон %s не підтримуєтьÑÑ" @@ -4748,8 +4683,6 @@ msgstr "Пробачте, немає пропозицій" msgid "Sorry, only %<PRId64> suggestions" msgstr "Пробачте, тільки %<PRId64> пропозицій" -#. for when 'cmdheight' > 1 -#. avoid more prompt #, c-format msgid "Change \"%.*s\" to:" msgstr "Замінити «%.*s» на:" @@ -4967,8 +4900,8 @@ msgid "E760: No word count in %s" msgstr "E760: Ðемає кількоÑті Ñлів у %s" #, c-format -msgid "line %6d, word %6d - %s" -msgstr "Ñ€Ñдок %6d, Ñлово %6d - %s" +msgid "line %6d, word %6ld - %s" +msgstr "Ñ€Ñдок %6d, Ñлово %6ld - %s" #, c-format msgid "Duplicate word in %s line %d: %s" @@ -4991,32 +4924,36 @@ msgid "Reading word file %s..." msgstr "ЧитаєтьÑÑ Ñ„Ð°Ð¹Ð» Ñлів %s..." #, c-format -msgid "Duplicate /encoding= line ignored in %s line %d: %s" -msgstr "ÐŸÐ¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ€Ñдка /encoding= проігноровано у %s у Ñ€Ñдку %d: %s" +msgid "Conversion failure for word in %s line %ld: %s" +msgstr "Помилка Ð¿ÐµÑ€ÐµÑ‚Ð²Ð¾Ñ€ÐµÐ½Ð½Ñ Ñлова у %s у Ñ€Ñдку %ld: %s" + +#, c-format +msgid "Duplicate /encoding= line ignored in %s line %ld: %s" +msgstr "ÐŸÐ¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ€Ñдка /encoding= проігноровано у %s у Ñ€Ñдку %ld: %s" #, c-format -msgid "/encoding= line after word ignored in %s line %d: %s" -msgstr "Ð Ñдок /encoding= піÑÐ»Ñ Ñлова проігноровано у %s у Ñ€Ñдку %d: %s" +msgid "/encoding= line after word ignored in %s line %ld: %s" +msgstr "Ð Ñдок /encoding= піÑÐ»Ñ Ñлова проігноровано у %s у Ñ€Ñдку %ld: %s" #, c-format -msgid "Duplicate /regions= line ignored in %s line %d: %s" -msgstr "ÐŸÐ¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ€Ñдка /regions= проігноровано у %s у Ñ€Ñдку %d: %s" +msgid "Duplicate /regions= line ignored in %s line %ld: %s" +msgstr "ÐŸÐ¾Ð²Ñ‚Ð¾Ñ€ÐµÐ½Ð½Ñ Ñ€Ñдка /regions= проігноровано у %s у Ñ€Ñдку %ld: %s" #, c-format -msgid "Too many regions in %s line %d: %s" -msgstr "Забагато регіонів у %s у Ñ€Ñдку %d: %s" +msgid "Too many regions in %s line %ld: %s" +msgstr "Забагато регіонів у %s у Ñ€Ñдку %ld: %s" #, c-format -msgid "/ line ignored in %s line %d: %s" -msgstr "Ð Ñдок / проігноровано у %s у Ñ€Ñдку %d: %s" +msgid "/ line ignored in %s line %ld: %s" +msgstr "Ð Ñдок / проігноровано у %s у Ñ€Ñдку %ld: %s" #, c-format -msgid "Invalid region nr in %s line %d: %s" -msgstr "Ðекоректний номер регіону у %s у Ñ€Ñдку %d: %s" +msgid "Invalid region nr in %s line %ld: %s" +msgstr "Ðекоректний номер регіону у %s у Ñ€Ñдку %ld: %s" #, c-format -msgid "Unrecognized flags in %s line %d: %s" -msgstr "Ðерозпізнані прапорці у %s у Ñ€Ñдку %d: %s" +msgid "Unrecognized flags in %s line %ld: %s" +msgstr "Ðерозпізнані прапорці у %s у Ñ€Ñдку %ld: %s" #, c-format msgid "Ignored %d words with non-ASCII characters" @@ -5029,8 +4966,6 @@ msgstr "СтиÑнено %d з %d вузлів; залишилоÑÑ %d (%d%%)" msgid "Reading back spell file..." msgstr "ПеречитуєтьÑÑ Ñ„Ð°Ð¹Ð» орфографії..." -#. Go through the trie of good words, soundfold each word and add it to -#. the soundfold trie. msgid "Performing soundfolding..." msgstr "ВиконуєтьÑÑ Ð·Ð³Ð¾Ñ€Ñ‚Ð°Ð½Ð½Ñ Ð·Ð²ÑƒÐºÑ–Ð²..." @@ -5053,8 +4988,9 @@ msgstr "Оцінка ÑÐ¿Ð¾Ð¶Ð¸Ð²Ð°Ð½Ð½Ñ Ð¿Ð°Ð¼'Ñті: %d байт" msgid "E751: Output file name must not have region name" msgstr "E751: Вихідний файл не повинен мати назву регіону" -msgid "E754: Only up to 8 regions supported" -msgstr "E754: ПідтримуєтьÑÑ Ñ‚Ñ–Ð»ÑŒÐºÐ¸ до воÑьми регіонів" +#, c-format +msgid "E754: Only up to %d regions supported" +msgstr "E754: ПідтримуєтьÑÑ Ñ‚Ñ–Ð»ÑŒÐºÐ¸ до %d регіонів" #, c-format msgid "E755: Invalid region in %s" @@ -5085,8 +5021,6 @@ msgstr "Слово '%.*s' додано до %s" msgid "E763: Word characters differ between spell files" msgstr "E763: Символи у Ñлові відрізнÑютьÑÑ Ñƒ файлах орфографії" -#. This should have been checked when generating the .spl -#. file. msgid "E783: duplicate char in MAP entry" msgstr "E783: Повторено Ñимвол у елементі MAP" @@ -5102,11 +5036,11 @@ msgstr "E767: Забагато аргументів Ð´Ð»Ñ printf()" msgid "No Syntax items defined for this buffer" msgstr "Ð”Ð»Ñ Ð±ÑƒÑ„ÐµÑ€Ð° не визначено елементів ÑинтакÑиÑу" -msgid "syn conceal on" -msgstr "Ð¿Ñ€Ð¸Ñ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÐ²Ñ–Ð¼Ðº" +msgid "syntax conceal on" +msgstr "ÑинтакÑичне Ð¿Ñ€Ð¸Ñ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ ÑƒÐ²Ñ–Ð¼Ðº" -msgid "syn conceal off" -msgstr "Ð¿Ñ€Ð¸Ñ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð¼Ðº" +msgid "syntax conceal off" +msgstr "ÑинтакÑичне Ð¿Ñ€Ð¸Ñ…Ð¾Ð²ÑƒÐ²Ð°Ð½Ð½Ñ Ð²Ð¸Ð¼Ðº" #, c-format msgid "E390: Illegal argument: %s" @@ -5224,7 +5158,6 @@ msgstr "E848: Забагато ÑинтакÑичних клаÑтерів" msgid "E400: No cluster specified" msgstr "E400: КлаÑтер не вказано" -#. end delimiter not found #, c-format msgid "E401: Pattern delimiter not found: %s" msgstr "E401: Кінець зразку не знайдено: %s" @@ -5318,9 +5251,6 @@ msgstr "E421: Ðерозпізнана назва або номер кольор msgid "E423: Illegal argument: %s" msgstr "E423: Ðеправильний аргумент: %s" -msgid "E424: Too many different highlighting attributes in use" -msgstr "E424: ВикориÑтано забагато різних атрибутів кольору" - msgid "E669: Unprintable character in group name" msgstr "E669: Ðедруковний Ñимвол у назві групи" @@ -5359,7 +5289,6 @@ msgstr "E428: Це вже оÑÑ‚Ð°Ð½Ð½Ñ Ð²Ñ–Ð´Ð¿Ð¾Ð²Ñ–Ð´Ð½Ð° мітка" msgid "File \"%s\" does not exist" msgstr "Файл «%s» не Ñ–Ñнує" -#. Give an indication of the number of matching tags #, c-format msgid "tag %d of %d%s" msgstr "мітка %d з %d%s" @@ -5374,7 +5303,6 @@ msgstr " ВикориÑтано мітку, не розрізнÑючи велРmsgid "E429: File \"%s\" does not exist" msgstr "E429: Файл «%s» не Ñ–Ñнує" -#. Highlight title msgid "" "\n" " # TO tag FROM line in file/text" @@ -5401,7 +5329,6 @@ msgstr "Перед байтом %<PRId64>" msgid "E432: Tags file not sorted: %s" msgstr "E432: Файл теґів не впорÑдкований: %s" -#. never opened any tags file msgid "E433: No tags file" msgstr "E433: Ðемає файлу теґів" @@ -5415,8 +5342,6 @@ msgstr "E435: Ðе вдалоÑÑ Ð·Ð½Ð°Ð¹Ñ‚Ð¸ мітку, тільки прип msgid "Duplicate field name: %s" msgstr "Ðазва Ð¿Ð¾Ð»Ñ Ð¿Ð¾Ð²Ñ‚Ð¾Ñ€ÑŽÑ”Ñ‚ÑŒÑÑ: %s" -#. This happens when the FileChangedRO autocommand changes the -#. * file in a way it becomes shorter. msgid "E881: Line count changed unexpectedly" msgstr "E881: КількіÑть Ñ€Ñдків неÑподівано змінилаÑÑ" @@ -5572,8 +5497,8 @@ msgstr " заміна Ð´Ð»Ñ $VIMRUNTIME: \"" msgid "Nvim is open source and freely distributable" msgstr "Nvim — це відкрита й вільно розповÑюджувана програма" -msgid "https://neovim.io/community" -msgstr "https://neovim.io/community" +msgid "https://neovim.io/#chat" +msgstr "https://neovim.io/#chat" msgid "type :help nvim<Enter> if you are new! " msgstr ":help nvim<Enter> Ñкщо ви вперше! " @@ -5628,17 +5553,6 @@ msgstr "E15: Ðерозпізнаний Ñимвол: %.*s" msgid "E15: Operator is not associative: %.*s" msgstr "E15: Оператор не аÑоціативний: %.*s" -#. / Record missing operator: for things like -#. / -#. / :echo @a @a -#. / -#. / (allowed) or -#. / -#. / :echo (@a @a) -#. / -#. / (parsed as OpMissing(@a, @a)). -#. Multiple expressions allowed, return without calling -#. viml_parser_advance(). #, c-format msgid "E15: Missing operator: %.*s" msgstr "E15: Бракує оператора: %.*s" @@ -5705,7 +5619,6 @@ msgstr "E15: ОчікуєтьÑÑ Ð·Ð½Ð°Ñ‡ÐµÐ½Ð½Ñ, отримано праву msgid "E15: Don't know what figure brace means: %.*s" msgstr "E15: Ðе знаю, що означає фігурна дужка: %.*s" -#. Only first branch is valid. #, c-format msgid "E15: Unexpected arrow: %.*s" msgstr "E15: Ðеочікувано Ñтрілка: %.*s" @@ -5744,7 +5657,8 @@ msgstr "E115: Бракує одинарних лапок: %.*s" #, c-format msgid "E475: Expected closing bracket to end list assignment lvalue: %.*s" -msgstr "E475: ОчікуєтьÑÑ Ð¿Ñ€Ð°Ð²Ð° квадратна дужка щоб закінчити приÑÐ²Ð¾Ñ”Ð½Ð½Ñ ÑпиÑку: %.*s" +msgstr "" +"E475: ОчікуєтьÑÑ Ð¿Ñ€Ð°Ð²Ð° квадратна дужка щоб закінчити приÑÐ²Ð¾Ñ”Ð½Ð½Ñ ÑпиÑку: %.*s" #, c-format msgid "E15: Misplaced assignment: %.*s" diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index 51a7dd670f..d9e307bb71 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -2661,6 +2661,8 @@ void ex_copen(exarg_T *eap) } } } else { + int flags = 0; + qf_buf = qf_find_buf(qi); /* The current window becomes the previous window afterwards. */ @@ -2668,11 +2670,17 @@ void ex_copen(exarg_T *eap) if ((eap->cmdidx == CMD_copen || eap->cmdidx == CMD_cwindow) && cmdmod.split == 0) - /* Create the new window at the very bottom, except when - * :belowright or :aboveleft is used. */ + // Create the new quickfix window at the very bottom, except when + // :belowright or :aboveleft is used. win_goto(lastwin); - if (win_split(height, WSP_BELOW | WSP_NEWLOC) == FAIL) - return; /* not enough room for window */ + // Default is to open the window below the current window + if (cmdmod.split == 0) { + flags = WSP_BELOW; + } + flags |= WSP_NEWLOC; + if (win_split(height, flags) == FAIL) { + return; // not enough room for window + } RESET_BINDING(curwin); if (eap->cmdidx == CMD_lopen || eap->cmdidx == CMD_lwindow) { diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index 15a701f022..d62a009fbc 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -485,7 +485,7 @@ static char_u e_unmatchedpp[] = N_("E53: Unmatched %s%%("); static char_u e_unmatchedp[] = N_("E54: Unmatched %s("); static char_u e_unmatchedpar[] = N_("E55: Unmatched %s)"); static char_u e_z_not_allowed[] = N_("E66: \\z( not allowed here"); -static char_u e_z1_not_allowed[] = N_("E67: \\z1 et al. not allowed here"); +static char_u e_z1_not_allowed[] = N_("E67: \\z1 - \\z9 not allowed here"); static char_u e_missing_sb[] = N_("E69: Missing ] after %s%%["); static char_u e_empty_sb[] = N_("E70: Empty %s%%[]"); #define NOT_MULTI 0 @@ -1952,7 +1952,7 @@ static char_u *regatom(int *flagp) { c = no_Magic(getchr()); switch (c) { - case '(': if (reg_do_extmatch != REX_SET) + case '(': if ((reg_do_extmatch & REX_SET) == 0) EMSG_RET_NULL(_(e_z_not_allowed)); if (one_exactly) EMSG_ONE_RET_NULL; @@ -1971,7 +1971,7 @@ static char_u *regatom(int *flagp) case '6': case '7': case '8': - case '9': if (reg_do_extmatch != REX_USE) + case '9': if ((reg_do_extmatch & REX_USE) == 0) EMSG_RET_NULL(_(e_z1_not_allowed)); ret = regnode(ZREF + c - '0'); re_has_z = REX_USE; @@ -7257,15 +7257,13 @@ int vim_regexec_nl(regmatch_T *rmp, char_u *line, colnr_T col) return vim_regexec_both(rmp, line, col, true); } -/* - * Match a regexp against multiple lines. - * "rmp->regprog" is a compiled regexp as returned by vim_regcomp(). - * Note: "rmp->regprog" may be freed and changed. - * Uses curbuf for line count and 'iskeyword'. - * - * Return zero if there is no match. Return number of lines contained in the - * match otherwise. - */ +/// Match a regexp against multiple lines. +/// "rmp->regprog" must be a compiled regexp as returned by vim_regcomp(). +/// Note: "rmp->regprog" may be freed and changed, even set to NULL. +/// Uses curbuf for line count and 'iskeyword'. +/// +/// Return zero if there is no match. Return number of lines contained in the +/// match otherwise. long vim_regexec_multi( regmmatch_T *rmp, win_T *win, /* window in which to search or NULL */ @@ -7297,7 +7295,12 @@ long vim_regexec_multi( p_re = BACKTRACKING_ENGINE; vim_regfree(rmp->regprog); report_re_switch(pat); + // checking for \z misuse was already done when compiling for NFA, + // allow all here + reg_do_extmatch = REX_ALL; rmp->regprog = vim_regcomp(pat, re_flags); + reg_do_extmatch = 0; + if (rmp->regprog != NULL) { result = rmp->regprog->engine->regexec_multi(rmp, win, buf, lnum, col, tm); diff --git a/src/nvim/regexp.h b/src/nvim/regexp.h index 97595c4d29..74ed34188c 100644 --- a/src/nvim/regexp.h +++ b/src/nvim/regexp.h @@ -5,19 +5,20 @@ #include "nvim/buffer_defs.h" #include "nvim/regexp_defs.h" -/* Second argument for vim_regcomp(). */ -#define RE_MAGIC 1 /* 'magic' option */ -#define RE_STRING 2 /* match in string instead of buffer text */ -#define RE_STRICT 4 /* don't allow [abc] without ] */ -#define RE_AUTO 8 /* automatic engine selection */ +// Second argument for vim_regcomp(). +#define RE_MAGIC 1 ///< 'magic' option +#define RE_STRING 2 ///< match in string instead of buffer text +#define RE_STRICT 4 ///< don't allow [abc] without ] +#define RE_AUTO 8 ///< automatic engine selection -/* values for reg_do_extmatch */ -#define REX_SET 1 /* to allow \z\(...\), */ -#define REX_USE 2 /* to allow \z\1 et al. */ +// values for reg_do_extmatch +#define REX_SET 1 ///< to allow \z\(...\), +#define REX_USE 2 ///< to allow \z\1 et al. +#define REX_ALL (REX_SET | REX_USE) -/* regexp.c */ +// regexp.c #ifdef INCLUDE_GENERATED_DECLARATIONS # include "regexp.h.generated.h" #endif -#endif /* NVIM_REGEXP_H */ +#endif // NVIM_REGEXP_H diff --git a/src/nvim/regexp_nfa.c b/src/nvim/regexp_nfa.c index fe18cb4389..08ef7da9c1 100644 --- a/src/nvim/regexp_nfa.c +++ b/src/nvim/regexp_nfa.c @@ -1367,20 +1367,23 @@ static int nfa_regatom(void) case '7': case '8': case '9': - /* \z1...\z9 */ - if (reg_do_extmatch != REX_USE) + // \z1...\z9 + if ((reg_do_extmatch & REX_USE) == 0) { EMSG_RET_FAIL(_(e_z1_not_allowed)); + } EMIT(NFA_ZREF1 + (no_Magic(c) - '1')); /* No need to set nfa_has_backref, the sub-matches don't * change when \z1 .. \z9 matches or not. */ re_has_z = REX_USE; break; case '(': - /* \z( */ - if (reg_do_extmatch != REX_SET) + // \z( + if (reg_do_extmatch != REX_SET) { EMSG_RET_FAIL(_(e_z_not_allowed)); - if (nfa_reg(REG_ZPAREN) == FAIL) - return FAIL; /* cascaded error */ + } + if (nfa_reg(REG_ZPAREN) == FAIL) { + return FAIL; // cascaded error + } re_has_z = REX_SET; break; default: @@ -5052,10 +5055,11 @@ static int nfa_regmatch(nfa_regprog_T *prog, nfa_state_T *start, /* swap lists */ thislist = &list[flag]; nextlist = &list[flag ^= 1]; - nextlist->n = 0; /* clear nextlist */ - nextlist->has_pim = FALSE; - ++nfa_listid; - if (prog->re_engine == AUTOMATIC_ENGINE && nfa_listid >= NFA_MAX_STATES) { + nextlist->n = 0; // clear nextlist + nextlist->has_pim = false; + nfa_listid++; + if (prog->re_engine == AUTOMATIC_ENGINE + && (nfa_listid >= NFA_MAX_STATES)) { // Too many states, retry with old engine. nfa_match = NFA_TOO_EXPENSIVE; goto theend; diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 46aa771a89..d095bdb7c8 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -669,8 +669,8 @@ static void win_update(win_T *wp) type = wp->w_redr_type; - if (type == NOT_VALID) { - wp->w_redr_status = TRUE; + if (type >= NOT_VALID) { + wp->w_redr_status = true; wp->w_lines_valid = 0; } @@ -888,10 +888,7 @@ static void win_update(win_T *wp) // Try to insert the correct number of lines. // If not the last window, delete the lines at the bottom. // win_ins_lines may fail when the terminal can't do it. - if (i > 0) { - check_for_delay(false); - } - if (win_ins_lines(wp, 0, i, false) == OK) { + if (win_ins_lines(wp, 0, i) == OK) { if (wp->w_lines_valid != 0) { /* Need to update rows that are new, stop at the * first one that scrolled down. */ @@ -949,8 +946,7 @@ static void win_update(win_T *wp) /* ... but don't delete new filler lines. */ row -= wp->w_topfill; if (row > 0) { - check_for_delay(false); - if (win_del_lines(wp, 0, row, false) == OK) { + if (win_del_lines(wp, 0, row) == OK) { bot_start = wp->w_height - row; } else { mid_start = 0; // redraw all lines @@ -1305,8 +1301,7 @@ static void win_update(win_T *wp) if (row - xtra_rows >= wp->w_height - 2) { mod_bot = MAXLNUM; } else { - check_for_delay(false); - if (win_del_lines(wp, row, -xtra_rows, false) == FAIL) { + if (win_del_lines(wp, row, -xtra_rows) == FAIL) { mod_bot = MAXLNUM; } else { bot_start = wp->w_height + xtra_rows; @@ -1319,8 +1314,7 @@ static void win_update(win_T *wp) if (row + xtra_rows >= wp->w_height - 2) { mod_bot = MAXLNUM; } else { - check_for_delay(false); - if (win_ins_lines(wp, row + old_rows, xtra_rows, false) == FAIL) { + if (win_ins_lines(wp, row + old_rows, xtra_rows) == FAIL) { mod_bot = MAXLNUM; } else if (top_end > row + old_rows) { // Scrolled the part at the top that requires @@ -1513,8 +1507,7 @@ static void win_update(win_T *wp) wp->w_botline = lnum; } } else { - draw_vsep_win(wp, row); - if (eof) { /* we hit the end of the file */ + if (eof) { // we hit the end of the file wp->w_botline = buf->b_ml.ml_line_count + 1; j = diff_check_fill(wp, wp->w_botline); if (j > 0 && !wp->w_botfill) { @@ -1538,6 +1531,10 @@ static void win_update(win_T *wp) win_draw_end(wp, fill_eob, ' ', row, wp->w_height, HLF_EOB); } + if (wp->w_redr_type >= REDRAW_TOP) { + draw_vsep_win(wp, 0); + } + /* Reset the type of redrawing required, the window has been updated. */ wp->w_redr_type = 0; wp->w_old_topfill = wp->w_topfill; @@ -2172,21 +2169,21 @@ win_line ( static linenr_T checked_lnum = 0; /* line number for "checked_col" */ static int checked_col = 0; /* column in "checked_lnum" up to which * there are no spell errors */ - static int cap_col = -1; /* column to check for Cap word */ - static linenr_T capcol_lnum = 0; /* line number where "cap_col" used */ - int cur_checked_col = 0; /* checked column for current line */ - int extra_check; /* has syntax or linebreak */ - int multi_attr = 0; /* attributes desired by multibyte */ - int mb_l = 1; /* multi-byte byte length */ - int mb_c = 0; /* decoded multi-byte character */ - int mb_utf8 = FALSE; /* screen char is UTF-8 char */ - int u8cc[MAX_MCO]; /* composing UTF-8 chars */ - int filler_lines; /* nr of filler lines to be drawn */ - int filler_todo; /* nr of filler lines still to do + 1 */ - hlf_T diff_hlf = (hlf_T)0; /* type of diff highlighting */ - int change_start = MAXCOL; /* first col of changed area */ - int change_end = -1; /* last col of changed area */ - colnr_T trailcol = MAXCOL; /* start of trailing spaces */ + static int cap_col = -1; // column to check for Cap word + static linenr_T capcol_lnum = 0; // line number where "cap_col" + int cur_checked_col = 0; // checked column for current line + int extra_check = 0; // has syntax or linebreak + int multi_attr = 0; // attributes desired by multibyte + int mb_l = 1; // multi-byte byte length + int mb_c = 0; // decoded multi-byte character + bool mb_utf8 = false; // screen char is UTF-8 char + int u8cc[MAX_MCO]; // composing UTF-8 chars + int filler_lines; // nr of filler lines to be drawn + int filler_todo; // nr of filler lines still to do + 1 + hlf_T diff_hlf = (hlf_T)0; // type of diff highlighting + int change_start = MAXCOL; // first col of changed area + int change_end = -1; // last col of changed area + colnr_T trailcol = MAXCOL; // start of trailing spaces int need_showbreak = false; // overlong line, skip first x chars int line_attr = 0; // attribute for the whole line int line_attr_lowprio = 0; // low-priority attribute for the line @@ -2829,7 +2826,7 @@ win_line ( draw_state = WL_BRI - 1; } - // draw 'breakindent': indent wrapped text accodringly + // draw 'breakindent': indent wrapped text accordingly if (draw_state == WL_BRI - 1 && n_extra == 0) { draw_state = WL_BRI; // if need_showbreak is set, breakindent also applies @@ -3055,8 +3052,13 @@ win_line ( diff_hlf = HLF_CHD; // changed line } line_attr = win_hl_attr(wp, diff_hlf); + // Overlay CursorLine onto diff-mode highlight. if (wp->w_p_cul && lnum == wp->w_cursor.lnum) { - line_attr = hl_combine_attr(line_attr, win_hl_attr(wp, HLF_CUL)); + line_attr = 0 != line_attr_lowprio // Low-priority CursorLine + ? hl_combine_attr(hl_combine_attr(win_hl_attr(wp, HLF_CUL), + line_attr), + hl_get_underline()) + : hl_combine_attr(line_attr, win_hl_attr(wp, HLF_CUL)); } } @@ -3103,8 +3105,9 @@ win_line ( mb_utf8 = true; u8cc[0] = 0; c = 0xc0; - } else - mb_utf8 = FALSE; + } else { + mb_utf8 = false; + } } else { c = *p_extra; if (has_mbyte) { @@ -3275,7 +3278,7 @@ win_line ( && (*mb_char2cells)(mb_c) == 2) { c = '>'; mb_c = c; - mb_utf8 = FALSE; + mb_utf8 = false; mb_l = 1; multi_attr = win_hl_attr(wp, HLF_AT); // Put pointer back so that the character will be @@ -3298,7 +3301,7 @@ win_line ( saved_attr2 = char_attr; // save current attr } mb_c = c; - mb_utf8 = FALSE; + mb_utf8 = false; mb_l = 1; } @@ -3576,7 +3579,7 @@ win_line ( } } - mb_utf8 = (int)false; // don't draw as UTF-8 + mb_utf8 = false; // don't draw as UTF-8 if (wp->w_p_list) { c = lcs_tab1; if (wp->w_p_lbr) { @@ -3639,8 +3642,9 @@ win_line ( mb_utf8 = true; u8cc[0] = 0; c = 0xc0; - } else - mb_utf8 = FALSE; /* don't draw as UTF-8 */ + } else { + mb_utf8 = false; // don't draw as UTF-8 + } } else if (c != NUL) { p_extra = transchar(c); if (n_extra == 0) { @@ -3729,8 +3733,9 @@ win_line ( mb_utf8 = true; u8cc[0] = 0; c = 0xc0; - } else - mb_utf8 = FALSE; /* don't draw as UTF-8 */ + } else { + mb_utf8 = false; // don't draw as UTF-8 + } } else { prev_syntax_id = 0; is_concealing = FALSE; @@ -4056,8 +4061,9 @@ win_line ( mb_utf8 = true; u8cc[0] = 0; c = 0xc0; - } else - mb_utf8 = FALSE; + } else { + mb_utf8 = false; + } } /* advance to the next 'colorcolumn' */ @@ -4262,7 +4268,6 @@ win_line ( && filler_todo <= 0 ) { win_draw_end(wp, '@', ' ', row, wp->w_height, HLF_AT); - draw_vsep_win(wp, row); row = endrow; } @@ -4348,7 +4353,6 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, unsigned max_off_from; unsigned max_off_to; int col = 0; - int hl; bool redraw_this; // Does character need redraw? bool redraw_next; // redraw_this for next character bool clear_next = false; @@ -4474,24 +4478,10 @@ static void screen_line(int row, int coloff, int endcol, int clear_width, } } - if (clear_width > 0) { - // For a window that's left of another, draw the separator char. - if (col + coloff < Columns && wp->w_vsep_width > 0) { - int c = fillchar_vsep(wp, &hl); - schar_T sc; - schar_from_char(sc, c); - - if (schar_cmp(ScreenLines[off_to], sc) - || ScreenAttrs[off_to] != hl) { - schar_copy(ScreenLines[off_to], sc); - ScreenAttrs[off_to] = hl; - if (start_dirty == -1) { - start_dirty = col; - } - end_dirty = col+1; - } - } else - LineWraps[row] = FALSE; + if (clear_width > 0 || wp->w_width != Columns) { + // If we cleared after the end of the line, it did not wrap. + // For vsplit, line wrapping is not possible. + LineWraps[row] = false; } if (clear_end < end_dirty) { @@ -5613,6 +5603,7 @@ next_search_hl ( linenr_T l; colnr_T matchcol; long nmatched = 0; + int save_called_emsg = called_emsg; if (shl->lnum != 0) { /* Check for three situations: @@ -5705,6 +5696,9 @@ next_search_hl ( shl->lnum += shl->rm.startpos[0].lnum; break; /* useful match found */ } + + // Restore called_emsg for assert_fails(). + called_emsg = save_called_emsg; } } @@ -6071,10 +6065,10 @@ static void screenclear2(void) return; } - /* blank out ScreenLines */ - for (i = 0; i < Rows; ++i) { - lineclear(LineOffset[i], (int)Columns); - LineWraps[i] = FALSE; + // blank out ScreenLines + for (i = 0; i < Rows; i++) { + lineclear(LineOffset[i], (int)Columns, true); + LineWraps[i] = false; } ui_call_grid_clear(1); // clear the display @@ -6098,12 +6092,13 @@ static void screenclear2(void) /* * Clear one line in ScreenLines. */ -static void lineclear(unsigned off, int width) +static void lineclear(unsigned off, int width, bool valid) { for (int col = 0; col < width; col++) { schar_from_ascii(ScreenLines[off + col], ' '); } - (void)memset(ScreenAttrs + off, 0, (size_t)width * sizeof(sattr_T)); + int fill = valid ? 0 : -1; + (void)memset(ScreenAttrs + off, fill, (size_t)width * sizeof(sattr_T)); } /// Copy part of a Screenline for vertically split window. @@ -6139,53 +6134,36 @@ void setcursor(void) } /// Insert 'line_count' lines at 'row' in window 'wp'. -/// If 'invalid' is TRUE the wp->w_lines[].wl_lnum is invalidated. -/// If 'mayclear' is TRUE the screen will be cleared if it is faster than -/// scrolling. /// Returns FAIL if the lines are not inserted, OK for success. -int win_ins_lines(win_T *wp, int row, int line_count, int invalid) +int win_ins_lines(win_T *wp, int row, int line_count) { - if (wp->w_height < 5) { - return FAIL; - } - - return win_do_lines(wp, row, line_count, invalid, false); + return win_do_lines(wp, row, line_count, false); } /// Delete "line_count" window lines at "row" in window "wp". -/// If "invalid" is TRUE curwin->w_lines[] is invalidated. -/// If "mayclear" is TRUE the screen will be cleared if it is faster than -/// scrolling /// Return OK for success, FAIL if the lines are not deleted. -int win_del_lines(win_T *wp, int row, int line_count, int invalid) +int win_del_lines(win_T *wp, int row, int line_count) { - return win_do_lines(wp, row, line_count, invalid, true); + return win_do_lines(wp, row, line_count, true); } // Common code for win_ins_lines() and win_del_lines(). // Returns OK or FAIL when the work has been done. -static int win_do_lines(win_T *wp, int row, int line_count, - int invalid, int del) +static int win_do_lines(win_T *wp, int row, int line_count, int del) { - if (invalid) { - wp->w_lines_valid = 0; - } - if (!redrawing() || line_count <= 0) { return FAIL; } - // Delete all remaining lines + // No lines are being moved, just draw over the entire area if (row + line_count >= wp->w_height) { - screen_fill(wp->w_winrow + row, wp->w_winrow + wp->w_height, - wp->w_wincol, W_ENDCOL(wp), - ' ', ' ', 0); return OK; } // when scrolling, the message on the command line should be cleared, // otherwise it will stay there forever. - clear_cmdline = TRUE; + check_for_delay(false); + clear_cmdline = true; int retval; if (del) { @@ -6237,7 +6215,7 @@ int screen_ins_lines(int row, int line_count, int end, int col, int width) linecopy(j + line_count, j, col, width); } j += line_count; - lineclear(LineOffset[j] + col, width); + lineclear(LineOffset[j] + col, width, false); LineWraps[j] = false; } else { j = end - 1 - i; @@ -6248,7 +6226,7 @@ int screen_ins_lines(int row, int line_count, int end, int col, int width) } LineOffset[j + line_count] = temp; LineWraps[j + line_count] = false; - lineclear(temp, (int)Columns); + lineclear(temp, (int)Columns, false); } } @@ -6283,7 +6261,7 @@ int screen_del_lines(int row, int line_count, int end, int col, int width) linecopy(j - line_count, j, col, width); } j -= line_count; - lineclear(LineOffset[j] + col, width); + lineclear(LineOffset[j] + col, width, false); LineWraps[j] = false; } else { // whole width, moving the line pointers is faster @@ -6295,7 +6273,7 @@ int screen_del_lines(int row, int line_count, int end, int col, int width) } LineOffset[j - line_count] = temp; LineWraps[j - line_count] = false; - lineclear(temp, (int)Columns); + lineclear(temp, (int)Columns, false); } } diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 3b0a950ff2..4921824316 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -999,10 +999,13 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } else if (fmt_spec == 'd') { // signed switch (length_modifier) { - case '\0': + case '\0': { + arg = (int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int)); + break; + } case 'h': { - // char and short arguments are passed as int - arg = (tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int)); + // char and short arguments are passed as int16_t + arg = (int16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int)); break; } case 'l': { @@ -1031,11 +1034,16 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap, } else { // unsigned switch (length_modifier) { - case '\0': + case '\0': { + uarg = (unsigned int)(tvs + ? tv_nr(tvs, &arg_idx) + : va_arg(ap, unsigned int)); + break; + } case 'h': { - uarg = (tvs - ? (unsigned)tv_nr(tvs, &arg_idx) - : va_arg(ap, unsigned)); + uarg = (uint16_t)(tvs + ? tv_nr(tvs, &arg_idx) + : va_arg(ap, unsigned int)); break; } case 'l': { diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 22eabc75c1..973d09d065 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -61,6 +61,7 @@ struct hl_group { scid_T sg_scriptID; ///< script in which the group was last set // for terminal UIs int sg_cterm; ///< "cterm=" highlighting attr + ///< (combination of \ref HlAttrFlags) int sg_cterm_fg; ///< terminal fg color number + 1 int sg_cterm_bg; ///< terminal bg color number + 1 bool sg_cterm_bold; ///< bold attr was set for light color @@ -2893,6 +2894,13 @@ static int syn_regexec(regmmatch_T *rmp, linenr_T lnum, colnr_T col, syn_time_T pt = profile_start(); } + if (rmp->regprog == NULL) { + // This can happen if a previous call to vim_regexec_multi() tried to + // use the NFA engine, which resulted in NFA_TOO_EXPENSIVE, and + // compiling the pattern with the other engine fails. + return false; + } + rmp->rmm_maxcol = syn_buf->b_p_smc; r = vim_regexec_multi(rmp, syn_win, syn_buf, lnum, col, NULL); diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index d831979022..f715344689 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -603,13 +603,14 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int attr_id = 0; if (hl_attrs || vt_fg != -1 || vt_bg != -1) { - attr_id = get_term_attr_entry(&(HlAttrs) { + attr_id = hl_get_term_attr(&(HlAttrs) { .cterm_ae_attr = (int16_t)hl_attrs, .cterm_fg_color = vt_fg_idx, .cterm_bg_color = vt_bg_idx, .rgb_ae_attr = (int16_t)hl_attrs, .rgb_fg_color = vt_fg, .rgb_bg_color = vt_bg, + .rgb_sp_color = -1, }); } diff --git a/src/nvim/testdir/runtest.vim b/src/nvim/testdir/runtest.vim index 99b2b940d7..4fe7db135b 100644 --- a/src/nvim/testdir/runtest.vim +++ b/src/nvim/testdir/runtest.vim @@ -245,6 +245,7 @@ let s:flaky = [ \ 'Test_oneshot()', \ 'Test_out_cb()', \ 'Test_paused()', + \ 'Test_popup_and_window_resize()', \ 'Test_quoteplus()', \ 'Test_quotestar()', \ 'Test_reltime()', diff --git a/src/nvim/testdir/shared.vim b/src/nvim/testdir/shared.vim index b1f1d8fe66..eb6798f353 100644 --- a/src/nvim/testdir/shared.vim +++ b/src/nvim/testdir/shared.vim @@ -136,29 +136,27 @@ endfunc " Wait for up to a second for "expr" to become true. " Return time slept in milliseconds. With the +reltime feature this can be " more than the actual waiting time. Without +reltime it can also be less. -func WaitFor(expr) +func WaitFor(expr, ...) + let timeout = get(a:000, 0, 1000) " using reltime() is more accurate, but not always available if has('reltime') let start = reltime() else let slept = 0 endif - for i in range(100) - try - if eval(a:expr) - if has('reltime') - return float2nr(reltimefloat(reltime(start)) * 1000) - endif - return slept + for i in range(timeout / 10) + if eval(a:expr) + if has('reltime') + return float2nr(reltimefloat(reltime(start)) * 1000) endif - catch - endtry + return slept + endif if !has('reltime') let slept += 10 endif sleep 10m endfor - return 1000 + return timeout endfunc " Wait for up to a given milliseconds. diff --git a/src/nvim/testdir/test_alot.vim b/src/nvim/testdir/test_alot.vim index 36dcdc3386..0602ff6a45 100644 --- a/src/nvim/testdir/test_alot.vim +++ b/src/nvim/testdir/test_alot.vim @@ -28,6 +28,7 @@ source test_lambda.vim source test_mapping.vim source test_menu.vim source test_messages.vim +source test_move.vim source test_partial.vim source test_popup.vim source test_put.vim diff --git a/src/nvim/testdir/test_arglist.vim b/src/nvim/testdir/test_arglist.vim index 19d0cee47a..20171bb599 100644 --- a/src/nvim/testdir/test_arglist.vim +++ b/src/nvim/testdir/test_arglist.vim @@ -297,6 +297,18 @@ func Test_argdelete() %argd endfunc +func Test_argdelete_completion() + args foo bar + + call feedkeys(":argdelete \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"argdelete bar foo', @:) + + call feedkeys(":argdelete x \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"argdelete x bar foo', @:) + + %argd +endfunc + " Tests for the :next, :prev, :first, :last, :rewind commands func Test_argpos() call Reset_arglist() diff --git a/src/nvim/testdir/test_autocmd.vim b/src/nvim/testdir/test_autocmd.vim index 9e8d2081c8..253d6750ed 100644 --- a/src/nvim/testdir/test_autocmd.vim +++ b/src/nvim/testdir/test_autocmd.vim @@ -818,6 +818,18 @@ func Test_QuitPre() endfunc func Test_Cmdline() + au! CmdlineChanged : let g:text = getcmdline() + let g:text = 0 + call feedkeys(":echom 'hello'\<CR>", 'xt') + call assert_equal("echom 'hello'", g:text) + au! CmdlineChanged + + au! CmdlineChanged : let g:entered = expand('<afile>') + let g:entered = 0 + call feedkeys(":echom 'hello'\<CR>", 'xt') + call assert_equal(':', g:entered) + au! CmdlineChanged + au! CmdlineEnter : let g:entered = expand('<afile>') au! CmdlineLeave : let g:left = expand('<afile>') let g:entered = 0 @@ -828,6 +840,8 @@ func Test_Cmdline() au! CmdlineEnter au! CmdlineLeave + let save_shellslash = &shellslash + set noshellslash au! CmdlineEnter / let g:entered = expand('<afile>') au! CmdlineLeave / let g:left = expand('<afile>') let g:entered = 0 @@ -840,6 +854,7 @@ func Test_Cmdline() bwipe! au! CmdlineEnter au! CmdlineLeave + let &shellslash = save_shellslash endfunc " Test for BufWritePre autocommand that deletes or unloads the buffer. diff --git a/src/nvim/testdir/test_compiler.vim b/src/nvim/testdir/test_compiler.vim index 84fba0f9a4..4600a28da5 100644 --- a/src/nvim/testdir/test_compiler.vim +++ b/src/nvim/testdir/test_compiler.vim @@ -5,6 +5,11 @@ func Test_compiler() return endif + " $LANG changes the output of Perl. + if $LANG != '' + unlet $LANG + endif + e Xfoo.pl compiler perl call assert_equal('perl', b:current_compiler) diff --git a/src/nvim/testdir/test_diffmode.vim b/src/nvim/testdir/test_diffmode.vim index afe289b262..ad3eec3274 100644 --- a/src/nvim/testdir/test_diffmode.vim +++ b/src/nvim/testdir/test_diffmode.vim @@ -2,6 +2,9 @@ func Test_diff_fold_sync() enew! + let g:update_count = 0 + au DiffUpdated * let g:update_count += 1 + let l = range(50) call setline(1, l) diffthis @@ -27,12 +30,27 @@ func Test_diff_fold_sync() call win_gotoid(winone) call assert_equal(23, getcurpos()[1]) + call assert_equal(1, g:update_count) + au! DiffUpdated + windo diffoff close! set nomodified endfunc func Test_vert_split() + set diffopt=filler + call Common_vert_split() + set diffopt& +endfunc + +func Test_vert_split_internal() + set diffopt=internal,filler + call Common_vert_split() + set diffopt& +endfunc + +func Common_vert_split() " Disable the title to avoid xterm keeping the wrong one. set notitle noicon new @@ -201,6 +219,26 @@ func Test_diffget_diffput() %bwipe! endfunc +" Test putting two changes from one buffer to another +func Test_diffput_two() + new a + let win_a = win_getid() + call setline(1, range(1, 10)) + diffthis + new b + let win_b = win_getid() + call setline(1, range(1, 10)) + 8del + 5del + diffthis + call win_gotoid(win_a) + %diffput + call win_gotoid(win_b) + call assert_equal(map(range(1, 10), 'string(v:val)'), getline(1, '$')) + bwipe! a + bwipe! b +endfunc + func Test_dp_do_buffer() e! one let bn1=bufnr('%') @@ -257,6 +295,28 @@ func Test_dp_do_buffer() %bwipe! endfunc +func Test_do_lastline() + e! one + call setline(1, ['1','2','3','4','5','6']) + diffthis + + new two + call setline(1, ['2','4','5']) + diffthis + + 1 + norm dp]c + norm dp]c + wincmd w + call assert_equal(4, line('$')) + norm G + norm do + call assert_equal(3, line('$')) + + windo diffoff + %bwipe! +endfunc + func Test_diffoff() enew! call setline(1, ['Two', 'Three']) @@ -275,10 +335,8 @@ func Test_diffoff() bwipe! endfunc -func Test_diffopt_icase() - set diffopt=icase,foldcolumn:0 - - e one +func Common_icase_test() + edit one call setline(1, ['One', 'Two', 'Three', 'Four', 'Fi#ve']) redraw let normattr = screenattr(1, 1) @@ -300,32 +358,54 @@ func Test_diffopt_icase() diffoff! %bwipe! +endfunc + +func Test_diffopt_icase() + set diffopt=icase,foldcolumn:0 + call Common_icase_test() set diffopt& endfunc -func Test_diffopt_iwhite() - set diffopt=iwhite,foldcolumn:0 +func Test_diffopt_icase_internal() + set diffopt=icase,foldcolumn:0,internal + call Common_icase_test() + set diffopt& +endfunc - e one - " Difference in trailing spaces should be ignored, +func Common_iwhite_test() + edit one + " Difference in trailing spaces and amount of spaces should be ignored, " but not other space differences. - call setline(1, ["One \t", 'Two', 'Three', 'Four']) + call setline(1, ["One \t", 'Two', 'Three', 'one two', 'one two', 'Four']) redraw let normattr = screenattr(1, 1) diffthis botright vert new two - call setline(1, ["One\t ", "Two\t ", 'Three', ' Four']) + call setline(1, ["One\t ", "Two\t ", 'Three', 'one two', 'onetwo', ' Four']) diffthis redraw call assert_equal(normattr, screenattr(1, 1)) call assert_equal(normattr, screenattr(2, 1)) call assert_equal(normattr, screenattr(3, 1)) - call assert_notequal(normattr, screenattr(4, 1)) + call assert_equal(normattr, screenattr(4, 1)) + call assert_notequal(normattr, screenattr(5, 1)) + call assert_notequal(normattr, screenattr(6, 1)) diffoff! %bwipe! +endfunc + +func Test_diffopt_iwhite() + set diffopt=iwhite,foldcolumn:0 + call Common_iwhite_test() + set diffopt& +endfunc + +func Test_diffopt_iwhite_internal() + set diffopt=internal,iwhite,foldcolumn:0 + call Common_iwhite_test() set diffopt& endfunc @@ -339,8 +419,13 @@ func Test_diffopt_context() set diffopt=context:2 call assert_equal('+-- 2 lines: 1', foldtextresult(1)) + set diffopt=internal,context:2 + call assert_equal('+-- 2 lines: 1', foldtextresult(1)) + set diffopt=context:1 call assert_equal('+-- 3 lines: 1', foldtextresult(1)) + set diffopt=internal,context:1 + call assert_equal('+-- 3 lines: 1', foldtextresult(1)) diffoff! %bwipe! @@ -348,7 +433,7 @@ func Test_diffopt_context() endfunc func Test_diffopt_horizontal() - set diffopt=horizontal + set diffopt=internal,horizontal diffsplit call assert_equal(&columns, winwidth(1)) @@ -362,7 +447,7 @@ func Test_diffopt_horizontal() endfunc func Test_diffopt_vertical() - set diffopt=vertical + set diffopt=internal,vertical diffsplit call assert_equal(&lines - 2, winheight(1)) @@ -376,7 +461,7 @@ func Test_diffopt_vertical() endfunc func Test_diffopt_hiddenoff() - set diffopt=filler,foldcolumn:0,hiddenoff + set diffopt=internal,filler,foldcolumn:0,hiddenoff e! one call setline(1, ['Two', 'Three']) redraw @@ -399,7 +484,7 @@ func Test_diffopt_hiddenoff() endfunc func Test_diffoff_hidden() - set diffopt=filler,foldcolumn:0 + set diffopt=internal,filler,foldcolumn:0 e! one call setline(1, ['Two', 'Three']) redraw @@ -480,7 +565,9 @@ func Test_diffexpr() endif func DiffExpr() - silent exe '!diff ' . v:fname_in . ' ' . v:fname_new . '>' . v:fname_out + " Prepend some text to check diff type detection + call writefile(['warning', ' message'], v:fname_out) + silent exe '!diff ' . v:fname_in . ' ' . v:fname_new . '>>' . v:fname_out endfunc set diffexpr=DiffExpr() set diffopt=foldcolumn:0 @@ -523,7 +610,7 @@ func Test_diffpatch() 3 + 4 . - saveas Xpatch + saveas! Xpatch bwipe! new call assert_fails('diffpatch Xpatch', 'E816:') @@ -569,6 +656,22 @@ func Test_diff_nomodifiable() %bwipe! endfunc +func Test_diff_filler() + new + call setline(1, [1, 2, 3, 'x', 4]) + diffthis + vnew + call setline(1, [1, 2, 'y', 'y', 3, 4]) + diffthis + redraw + + call assert_equal([0, 0, 0, 0, 0, 0, 0, 1, 0], map(range(-1, 7), 'diff_filler(v:val)')) + wincmd w + call assert_equal([0, 0, 0, 0, 2, 0, 0, 0], map(range(-1, 6), 'diff_filler(v:val)')) + + %bwipe! +endfunc + func Test_diff_lastline() enew! only! @@ -615,3 +718,23 @@ func Test_diff_with_cursorline() call StopVimInTerminal(buf) call delete('Xtest_diff_cursorline') endfunc + +func Test_diff_of_diff() + if !CanRunVimInTerminal() + return + endif + + call writefile([ + \ 'call setline(1, ["aa","bb","cc","@@ -3,2 +5,7 @@","dd","ee","ff"])', + \ 'vnew', + \ 'call setline(1, ["aa","bb","cc"])', + \ 'windo diffthis', + \ ], 'Xtest_diff_diff') + let buf = RunVimInTerminal('-S Xtest_diff_diff', {}) + + call VerifyScreenDump(buf, 'Test_diff_of_diff_01', {}) + + " clean up + call StopVimInTerminal(buf) + call delete('Xtest_diff_diff') +endfunc diff --git a/src/nvim/testdir/test_eval_stuff.vim b/src/nvim/testdir/test_eval_stuff.vim index 92e1ec5335..111c85bb95 100644 --- a/src/nvim/testdir/test_eval_stuff.vim +++ b/src/nvim/testdir/test_eval_stuff.vim @@ -11,3 +11,13 @@ endfunction func Test_catch_return_with_error() call assert_equal(1, s:foo()) endfunc + +func Test_E963() + " These commands used to cause an internal error prior to vim 8.1.0563 + let v_e = v:errors + let v_o = v:oldfiles + call assert_fails("let v:errors=''", 'E963:') + call assert_equal(v_e, v:errors) + call assert_fails("let v:oldfiles=''", 'E963:') + call assert_equal(v_o, v:oldfiles) +endfunc diff --git a/src/nvim/testdir/test_expr.vim b/src/nvim/testdir/test_expr.vim index aaf32dff04..4f99625e73 100644 --- a/src/nvim/testdir/test_expr.vim +++ b/src/nvim/testdir/test_expr.vim @@ -177,6 +177,22 @@ function Test_printf_misc() call assert_equal('173', printf('%O', 123)) call assert_equal('7b', printf('%x', 123)) call assert_equal('7B', printf('%X', 123)) + + call assert_equal('123', printf('%hd', 123)) + call assert_equal('-123', printf('%hd', -123)) + call assert_equal('-1', printf('%hd', 0xFFFF)) + call assert_equal('-1', printf('%hd', 0x1FFFFF)) + + call assert_equal('123', printf('%hu', 123)) + call assert_equal('65413', printf('%hu', -123)) + call assert_equal('65535', printf('%hu', 0xFFFF)) + call assert_equal('65535', printf('%hu', 0x1FFFFF)) + + call assert_equal('123', printf('%ld', 123)) + call assert_equal('-123', printf('%ld', -123)) + call assert_equal('65535', printf('%ld', 0xFFFF)) + call assert_equal('131071', printf('%ld', 0x1FFFF)) + call assert_equal('{', printf('%c', 123)) call assert_equal('abc', printf('%s', 'abc')) call assert_equal('abc', printf('%S', 'abc')) @@ -216,6 +232,11 @@ function Test_printf_misc() call assert_equal(' 123', printf('% *d', 5, 123)) call assert_equal(' +123', printf('%+ *d', 5, 123)) + call assert_equal('foobar', printf('%.*s', 9, 'foobar')) + call assert_equal('foo', printf('%.*s', 3, 'foobar')) + call assert_equal('', printf('%.*s', 0, 'foobar')) + call assert_equal('foobar', printf('%.*s', -1, 'foobar')) + " Simple quote (thousand grouping char) is ignored. call assert_equal('+00123456', printf("%+'09d", 123456)) @@ -238,6 +259,11 @@ function Test_printf_misc() call assert_equal(' 00123', printf('%6.5d', 123)) call assert_equal(' 0007b', printf('%6.5x', 123)) + call assert_equal('123', printf('%.2d', 123)) + call assert_equal('0123', printf('%.4d', 123)) + call assert_equal('0000000123', printf('%.10d', 123)) + call assert_equal('123', printf('%.0d', 123)) + call assert_equal('abc', printf('%2s', 'abc')) call assert_equal('abc', printf('%2S', 'abc')) call assert_equal('abc', printf('%.4s', 'abc')) @@ -335,6 +361,11 @@ function Test_printf_float() call assert_equal("str2float('inf')", printf('%s', 1.0/0.0)) call assert_equal("-str2float('inf')", printf('%s', -1.0/0.0)) + " Test special case where max precision is truncated at 340. + call assert_equal('1.000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', printf('%.330f', 1.0)) + call assert_equal('1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', printf('%.340f', 1.0)) + call assert_equal('1.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', printf('%.350f', 1.0)) + " Float nan (not a number) has no sign. call assert_equal('nan', printf('%f', sqrt(-1.0))) call assert_equal('nan', printf('%f', 0.0/0.0)) diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index b0a1ea0225..5d4a0ff3cb 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -596,3 +596,7 @@ func Test_script_detection() filetype off endfunc +func Test_setfiletype_completion() + call feedkeys(":setfiletype java\<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"setfiletype java javacc javascript', @:) +endfunc diff --git a/src/nvim/testdir/test_fold.vim b/src/nvim/testdir/test_fold.vim index b6a545f959..0b4b5d1922 100644 --- a/src/nvim/testdir/test_fold.vim +++ b/src/nvim/testdir/test_fold.vim @@ -514,6 +514,35 @@ func Test_fold_marker() enew! endfunc +" test create fold markers with C filetype +func Test_fold_create_marker_in_C() + enew! + set fdm=marker fdl=9 + set filetype=c + + let content = [ + \ '/*', + \ ' * comment', + \ ' * ', + \ ' *', + \ ' */', + \ 'int f(int* p) {', + \ ' *p = 3;', + \ ' return 0;', + \ '}' + \] + for c in range(len(content) - 1) + bw! + call append(0, content) + call cursor(c + 1, 1) + norm! zfG + call assert_equal(content[c] . (c < 4 ? '{{{' : '/*{{{*/'), getline(c + 1)) + endfor + + set fdm& fdl& + enew! +endfunc + " test folding with indent func Test_fold_indent() enew! @@ -674,3 +703,20 @@ func Test_fold_last_line_with_pagedown() set fdm& enew! endfunc + +func Test_folds_marker_in_comment2() + new + call setline(1, ['Lorem ipsum dolor sit', 'Lorem ipsum dolor sit', 'Lorem ipsum dolor sit']) + setl fen fdm=marker + setl commentstring=<!--%s--> + setl comments=s:<!--,m:\ \ \ \ ,e:--> + norm! zf2j + setl nofen + :1y + call assert_equal(['Lorem ipsum dolor sit<!--{{{-->'], getreg(0,1,1)) + :+2y + call assert_equal(['Lorem ipsum dolor sit<!--}}}-->'], getreg(0,1,1)) + + set foldmethod& + bwipe! +endfunc diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index b1138bfc96..7dc9f31ce7 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -1018,3 +1018,22 @@ func Test_trim() let chars = join(map(range(1, 0x20) + [0xa0], {n -> nr2char(n)}), '') call assert_equal("x", trim(chars . "x" . chars)) endfunc + +func EditAnotherFile() + let word = expand('<cword>') + edit Xfuncrange2 +endfunc + +func Test_func_range_with_edit() + " Define a function that edits another buffer, then call it with a range that + " is invalid in that buffer. + call writefile(['just one line'], 'Xfuncrange2') + new + call setline(1, range(10)) + write Xfuncrange1 + call assert_fails('5,8call EditAnotherFile()', 'E16:') + + call delete('Xfuncrange1') + call delete('Xfuncrange2') + bwipe! +endfunc diff --git a/src/nvim/testdir/test_history.vim b/src/nvim/testdir/test_history.vim index ca31e3f06c..16aad9889e 100644 --- a/src/nvim/testdir/test_history.vim +++ b/src/nvim/testdir/test_history.vim @@ -104,3 +104,8 @@ function Test_Search_history_window() call assert_equal('a', @/) bwipe! endfunc + +function Test_history_completion() + call feedkeys(":history \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"history / : = > ? @ all cmd debug expr input search', @:) +endfunc diff --git a/src/nvim/testdir/test_ins_complete.vim b/src/nvim/testdir/test_ins_complete.vim index 5ff63e58ba..d3429617d0 100644 --- a/src/nvim/testdir/test_ins_complete.vim +++ b/src/nvim/testdir/test_ins_complete.vim @@ -142,6 +142,19 @@ function Test_CompleteDoneDict() au! CompleteDone endfunc +func Test_CompleteDone_undo() + au CompleteDone * call append(0, "prepend1") + new + call setline(1, ["line1", "line2"]) + call feedkeys("Go\<C-X>\<C-N>\<CR>\<ESC>", "tx") + call assert_equal(["prepend1", "line1", "line2", "line1", ""], + \ getline(1, '$')) + undo + call assert_equal(["line1", "line2"], getline(1, '$')) + bwipe! + au! CompleteDone +endfunc + function! s:CompleteDone_CompleteFuncDictNoUserData( findstart, base ) if a:findstart return 0 diff --git a/src/nvim/testdir/test_lambda.vim b/src/nvim/testdir/test_lambda.vim index 311cc6e2cb..6e07c874b4 100644 --- a/src/nvim/testdir/test_lambda.vim +++ b/src/nvim/testdir/test_lambda.vim @@ -31,11 +31,11 @@ function! Test_lambda_with_timer() endfunction call s:Foo() - sleep 200ms + sleep 210ms " do not collect lambda - call garbagecollect() + call test_garbagecollect_now() let m = s:n - sleep 200ms + sleep 230ms call timer_stop(s:timer_id) call assert_true(m > 1) call assert_true(s:n > m + 1) diff --git a/src/nvim/testdir/test_messages.vim b/src/nvim/testdir/test_messages.vim index 188406e440..12101ec1f8 100644 --- a/src/nvim/testdir/test_messages.vim +++ b/src/nvim/testdir/test_messages.vim @@ -38,3 +38,8 @@ function Test_messages() let &more = oldmore endtry endfunction + +func Test_message_completion() + call feedkeys(":message \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"message clear', @:) +endfunc diff --git a/src/nvim/testdir/test_move.vim b/src/nvim/testdir/test_move.vim new file mode 100644 index 0000000000..d774c93dbd --- /dev/null +++ b/src/nvim/testdir/test_move.vim @@ -0,0 +1,40 @@ +" Test the ":move" command. + +func Test_move() + enew! + call append(0, ['line 1', 'line 2', 'line 3']) + g /^$/ delete _ + set nomodified + + move . + call assert_equal(['line 1', 'line 2', 'line 3'], getline(1, 3)) + call assert_false(&modified) + + 1,2move 0 + call assert_equal(['line 1', 'line 2', 'line 3'], getline(1, 3)) + call assert_false(&modified) + + 1,3move 3 + call assert_equal(['line 1', 'line 2', 'line 3'], getline(1, 3)) + call assert_false(&modified) + + 1move 2 + call assert_equal(['line 2', 'line 1', 'line 3'], getline(1, 3)) + call assert_true(&modified) + set nomodified + + 3move 0 + call assert_equal(['line 3', 'line 2', 'line 1'], getline(1, 3)) + call assert_true(&modified) + set nomodified + + 2,3move 0 + call assert_equal(['line 2', 'line 1', 'line 3'], getline(1, 3)) + call assert_true(&modified) + set nomodified + + call assert_fails('1,2move 1', 'E134') + call assert_fails('2,3move 2', 'E134') + + %bwipeout! +endfunc diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 6fd58a1483..6c43cbc1dc 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -246,6 +246,10 @@ func! Test_popup_completion_insertmode() iunmap <F5> endfunc +" TODO: Fix what breaks after this line. +" - Do not use "q!", it may exit Vim if there is an error +finish + func Test_noinsert_complete() function! s:complTest1() abort call complete(1, ['source', 'soundfold']) @@ -571,6 +575,15 @@ func Test_completion_clear_candidate_list() bw! endfunc +func Test_popup_complete_backwards() + new + call setline(1, ['Post', 'Port', 'Po']) + let expected=['Post', 'Port', 'Port'] + call cursor(3,2) + call feedkeys("A\<C-X>". repeat("\<C-P>", 3). "rt\<cr>", 'tx') + call assert_equal(expected, getline(1,'$')) + bwipe! +endfunc func Test_popup_and_preview_autocommand() " This used to crash Vim @@ -678,18 +691,24 @@ func Test_popup_and_window_resize() let g:buf = term_start([$NVIM_PRG, '--clean', '-c', 'set noswapfile'], {'term_rows': h / 3}) call term_sendkeys(g:buf, (h / 3 - 1)."o\<esc>G") call term_sendkeys(g:buf, "i\<c-x>") - call term_wait(g:buf, 100) + call term_wait(g:buf, 200) call term_sendkeys(g:buf, "\<c-v>") call term_wait(g:buf, 100) + " popup first entry "!" must be at the top + call WaitFor('term_getline(g:buf, 1) =~ "^!"') call assert_match('^!\s*$', term_getline(g:buf, 1)) exe 'resize +' . (h - 1) call term_wait(g:buf, 100) redraw! - call WaitFor('"" == term_getline(g:buf, 1)') + " popup shifted down, first line is now empty + call WaitFor('term_getline(g:buf, 1) == ""') call assert_equal('', term_getline(g:buf, 1)) sleep 100m - call WaitFor('"^!" =~ term_getline(g:buf, term_getcursor(g:buf)[0] + 1)') + " popup is below cursor line and shows first match "!" + call WaitFor('term_getline(g:buf, term_getcursor(g:buf)[0] + 1) =~ "^!"') call assert_match('^!\s*$', term_getline(g:buf, term_getcursor(g:buf)[0] + 1)) + " cursor line also shows ! + call assert_match('^!\s*$', term_getline(g:buf, term_getcursor(g:buf)[0])) bwipe! endfunc diff --git a/src/nvim/testdir/test_python2.vim b/src/nvim/testdir/test_python2.vim index 63c38cd5d1..5ba9fd68cf 100644 --- a/src/nvim/testdir/test_python2.vim +++ b/src/nvim/testdir/test_python2.vim @@ -25,3 +25,30 @@ func Test_pydo() bwipe! endif endfunc + +func Test_vim_function() + " Check creating vim.Function object + py import vim + + func s:foo() + return matchstr(expand('<sfile>'), '<SNR>\zs\d\+_foo$') + endfunc + let name = '<SNR>' . s:foo() + + try + py f = vim.bindeval('function("s:foo")') + call assert_equal(name, pyeval('f.name')) + catch + call assert_false(v:exception) + endtry + + try + py f = vim.Function('\x80\xfdR' + vim.eval('s:foo()')) + call assert_equal(name, pyeval('f.name')) + catch + call assert_false(v:exception) + endtry + + py del f + delfunc s:foo +endfunc diff --git a/src/nvim/testdir/test_python3.vim b/src/nvim/testdir/test_python3.vim index f5b2c89853..2e3fc93674 100644 --- a/src/nvim/testdir/test_python3.vim +++ b/src/nvim/testdir/test_python3.vim @@ -25,3 +25,30 @@ func Test_py3do() bwipe! endif endfunc + +func Test_vim_function() + " Check creating vim.Function object + py3 import vim + + func s:foo() + return matchstr(expand('<sfile>'), '<SNR>\zs\d\+_foo$') + endfunc + let name = '<SNR>' . s:foo() + + try + py3 f = vim.bindeval('function("s:foo")') + call assert_equal(name, py3eval('f.name')) + catch + call assert_false(v:exception) + endtry + + try + py3 f = vim.Function(b'\x80\xfdR' + vim.eval('s:foo()').encode()) + call assert_equal(name, py3eval('f.name')) + catch + call assert_false(v:exception) + endtry + + py3 del f + delfunc s:foo +endfunc diff --git a/src/nvim/testdir/test_quickfix.vim b/src/nvim/testdir/test_quickfix.vim index 624e642e7f..cb3e7ca8f6 100644 --- a/src/nvim/testdir/test_quickfix.vim +++ b/src/nvim/testdir/test_quickfix.vim @@ -2235,6 +2235,35 @@ func Test_cclose_in_autocmd() " call test_override('starting', 0) endfunc +" Check that ":file" without an argument is possible even when "curbuf_lock" +" is set. +func Test_file_from_copen() + " Works without argument. + augroup QF_Test + au! + au FileType qf file + augroup END + copen + + augroup QF_Test + au! + augroup END + cclose + + " Fails with argument. + augroup QF_Test + au! + au FileType qf call assert_fails(':file foo', 'E788') + augroup END + copen + augroup QF_Test + au! + augroup END + cclose + + augroup! QF_Test +endfunction + func Test_resize_from_copen() augroup QF_Test au! @@ -2608,3 +2637,30 @@ func Test_shorten_fname() silent! clist call assert_equal('test_quickfix.vim', bufname('test_quickfix.vim')) endfunc + +" Test for the position of the quickfix and location list window +func Test_qfwin_pos() + " Open two windows + new | only + new + cexpr ['F1:10:L10'] + copen + " Quickfix window should be the bottom most window + call assert_equal(3, winnr()) + close + " Open at the very top + wincmd t + topleft copen + call assert_equal(1, winnr()) + close + " open left of the current window + wincmd t + below new + leftabove copen + call assert_equal(2, winnr()) + close + " open right of the current window + rightbelow copen + call assert_equal(3, winnr()) + close +endfunc diff --git a/src/nvim/testdir/test_quotestar.vim b/src/nvim/testdir/test_quotestar.vim index 3a7edcbd7c..3ce1a84281 100644 --- a/src/nvim/testdir/test_quotestar.vim +++ b/src/nvim/testdir/test_quotestar.vim @@ -87,6 +87,18 @@ func Do_test_quotestar_for_x11() " Check that the *-register of this vim instance is changed as expected. call WaitFor('@* == "yes"', 3000) + " Handle the large selection over 262040 byte. + let length = 262044 + let sample = 'a' . repeat('b', length - 2) . 'c' + let @* = sample + call WaitFor('remote_expr("' . name . '", "len(@*) >= ' . length . '", "", 1)', 3000) + let res = remote_expr(name, "@*", "", 2) + call assert_equal(length, len(res)) + " Check length to prevent a large amount of output at assertion failure. + if length == len(res) + call assert_equal(sample, res) + endif + if has('unix') && has('gui') && !has('gui_running') let @* = '' diff --git a/src/nvim/testdir/test_scriptnames.vim b/src/nvim/testdir/test_scriptnames.vim new file mode 100644 index 0000000000..fc6c910bfa --- /dev/null +++ b/src/nvim/testdir/test_scriptnames.vim @@ -0,0 +1,26 @@ +" Test for :scriptnames + +func Test_scriptnames() + call writefile(['let did_load_script = 123'], 'Xscripting') + source Xscripting + call assert_equal(123, g:did_load_script) + + let scripts = split(execute('scriptnames'), "\n") + let last = scripts[-1] + call assert_match('\<Xscripting\>', last) + let lastnr = substitute(last, '\D*\(\d\+\):.*', '\1', '') + exe 'script ' . lastnr + call assert_equal('Xscripting', expand('%:t')) + + call assert_fails('script ' . (lastnr + 1), 'E474:') + call assert_fails('script 0', 'E939:') + + new + call setline(1, 'nothing') + call assert_fails('script ' . lastnr, 'E37:') + exe 'script! ' . lastnr + call assert_equal('Xscripting', expand('%:t')) + + bwipe + call delete('Xscripting') +endfunc diff --git a/src/nvim/testdir/test_signs.vim b/src/nvim/testdir/test_signs.vim index d3c6d05f4f..3960177acd 100644 --- a/src/nvim/testdir/test_signs.vim +++ b/src/nvim/testdir/test_signs.vim @@ -104,6 +104,33 @@ func Test_sign() exe 'sign jump 43 file=' . fn call assert_equal('B', getline('.')) + " can't define a sign with a non-printable character as text + call assert_fails("sign define Sign4 text=\e linehl=Comment", 'E239:') + call assert_fails("sign define Sign4 text=a\e linehl=Comment", 'E239:') + call assert_fails("sign define Sign4 text=\ea linehl=Comment", 'E239:') + + " Only 1 or 2 character text is allowed + call assert_fails("sign define Sign4 text=abc linehl=Comment", 'E239:') + call assert_fails("sign define Sign4 text= linehl=Comment", 'E239:') + call assert_fails("sign define Sign4 text=\ ab linehl=Comment", 'E239:') + + " define sign with whitespace + sign define Sign4 text=\ X linehl=Comment + sign undefine Sign4 + sign define Sign4 linehl=Comment text=\ X + sign undefine Sign4 + + sign define Sign5 text=X\ linehl=Comment + sign undefine Sign5 + sign define Sign5 linehl=Comment text=X\ + sign undefine Sign5 + + " define sign with backslash + sign define Sign4 text=\\\\ linehl=Comment + sign undefine Sign4 + sign define Sign4 text=\\ linehl=Comment + sign undefine Sign4 + " After undefining the sign, we should no longer be able to place it. sign undefine Sign1 sign undefine Sign2 diff --git a/src/nvim/testdir/test_spell.vim b/src/nvim/testdir/test_spell.vim index a2828b21d2..b3438cc649 100644 --- a/src/nvim/testdir/test_spell.vim +++ b/src/nvim/testdir/test_spell.vim @@ -85,6 +85,36 @@ func Test_spellreall() bwipe! endfunc +func Test_spellinfo() + throw 'skipped: Nvim does not support enc=latin1' + new + + set enc=latin1 spell spelllang=en + call assert_match("^\nfile: .*/runtime/spell/en.latin1.spl\n$", execute('spellinfo')) + + set enc=cp1250 spell spelllang=en + call assert_match("^\nfile: .*/runtime/spell/en.ascii.spl\n$", execute('spellinfo')) + + if has('multi_byte') + set enc=utf-8 spell spelllang=en + call assert_match("^\nfile: .*/runtime/spell/en.utf-8.spl\n$", execute('spellinfo')) + endif + + set enc=latin1 spell spelllang=en_us,en_nz + call assert_match("^\n" . + \ "file: .*/runtime/spell/en.latin1.spl\n" . + \ "file: .*/runtime/spell/en.latin1.spl\n$", execute('spellinfo')) + + set spell spelllang= + call assert_fails('spellinfo', 'E756:') + + set nospell spelllang=en + call assert_fails('spellinfo', 'E756:') + + set enc& spell& spelllang& + bwipe +endfunc + func Test_zz_basic() call LoadAffAndDic(g:test_data_aff1, g:test_data_dic1) call RunGoodBad("wrong OK puts. Test the end", diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index 2f4d857986..3bc9eaf756 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -150,6 +150,83 @@ func Test_compatible_args() call delete('Xtestout') endfunc +" Test the -o[N] and -O[N] arguments to open N windows split +" horizontally or vertically. +func Test_o_arg() + let after = [ + \ 'call writefile([winnr("$"), + \ winheight(1), winheight(2), &lines, + \ winwidth(1), winwidth(2), &columns, + \ bufname(winbufnr(1)), bufname(winbufnr(2))], + \ "Xtestout")', + \ 'qall', + \ ] + if RunVim([], after, '-o2') + " Open 2 windows split horizontally. Expect: + " - 2 windows + " - both windows should have the same or almost the same height + " - sum of both windows height (+ 3 for both statusline and Ex command) + " should be equal to the number of lines + " - both windows should have the same width which should be equal to the + " number of columns + " - buffer of both windows should have no name + let [wn, wh1, wh2, ln, ww1, ww2, cn, bn1, bn2] = readfile('Xtestout') + call assert_equal('2', wn) + call assert_inrange(0, 1, wh1 - wh2) + call assert_equal(string(wh1 + wh2 + 3), ln) + call assert_equal(ww1, ww2) + call assert_equal(ww1, cn) + call assert_equal('', bn1) + call assert_equal('', bn2) + endif + + if RunVim([], after, '-o foo bar') + " Same expectations as for -o2 but buffer names should be foo and bar + let [wn, wh1, wh2, ln, ww1, ww2, cn, bn1, bn2] = readfile('Xtestout') + call assert_equal('2', wn) + call assert_inrange(0, 1, wh1 - wh2) + call assert_equal(string(wh1 + wh2 + 3), ln) + call assert_equal(ww1, ww2) + call assert_equal(ww1, cn) + call assert_equal('foo', bn1) + call assert_equal('bar', bn2) + endif + + if RunVim([], after, '-O2') + " Open 2 windows split vertically. Expect: + " - 2 windows + " - both windows should have the same or almost the same width + " - sum of both windows width (+ 1 separator) should be equal to the + " number of columns + " - both windows should have the same height + " - window height (+ 2 for the statusline and Ex command) should be equal + " to the number of lines + " - buffer of both windowns should have no name + let [wn, wh1, wh2, ln, ww1, ww2, cn, bn1, bn2] = readfile('Xtestout') + call assert_equal('2', wn) + call assert_inrange(0, 1, ww1 - ww2) + call assert_equal(string(ww1 + ww2 + 1), cn) + call assert_equal(wh1, wh2) + call assert_equal(string(wh1 + 2), ln) + call assert_equal('', bn1) + call assert_equal('', bn2) + endif + + if RunVim([], after, '-O foo bar') + " Same expectations as for -O2 but buffer names should be foo and bar + let [wn, wh1, wh2, ln, ww1, ww2, cn, bn1, bn2] = readfile('Xtestout') + call assert_equal('2', wn) + call assert_inrange(0, 1, ww1 - ww2) + call assert_equal(string(ww1 + ww2 + 1), cn) + call assert_equal(wh1, wh2) + call assert_equal(string(wh1 + 2), ln) + call assert_equal('foo', bn1) + call assert_equal('bar', bn2) + endif + + call delete('Xtestout') +endfunc + func Test_file_args() let after = [ \ 'call writefile(argv(), "Xtestout")', diff --git a/src/nvim/testdir/test_stat.vim b/src/nvim/testdir/test_stat.vim index c276df0a92..253f74c2ad 100644 --- a/src/nvim/testdir/test_stat.vim +++ b/src/nvim/testdir/test_stat.vim @@ -1,31 +1,33 @@ " Tests for stat functions and checktime func CheckFileTime(doSleep) - let fname = 'Xtest.tmp' + let fnames = ['Xtest1.tmp', 'Xtest2.tmp', 'Xtest3.tmp'] + let times = [] let result = 0 - let ts = localtime() - if a:doSleep - sleep 1 - endif + " Use three files istead of localtim(), with a network filesystem the file + " times may differ at bit let fl = ['Hello World!'] - call writefile(fl, fname) - let tf = getftime(fname) - if a:doSleep - sleep 1 - endif - let te = localtime() + for fname in fnames + call writefile(fl, fname) + call add(times, getftime(fname)) + if a:doSleep + sleep 1 + endif + endfor - let time_correct = (ts <= tf && tf <= te) + let time_correct = (times[0] <= times[1] && times[1] <= times[2]) if a:doSleep || time_correct - call assert_true(time_correct) - call assert_equal(strlen(fl[0] . "\n"), getfsize(fname)) - call assert_equal('file', getftype(fname)) - call assert_equal('rw-', getfperm(fname)[0:2]) + call assert_true(time_correct, printf('Expected %s <= %s <= %s', times[0], times[1], times[2])) + call assert_equal(strlen(fl[0] . "\n"), getfsize(fnames[0])) + call assert_equal('file', getftype(fnames[0])) + call assert_equal('rw-', getfperm(fnames[0])[0:2]) let result = 1 endif - call delete(fname) + for fname in fnames + call delete(fname) + endfor return result endfunc @@ -141,17 +143,29 @@ func Test_getftype() endif for cdevfile in systemlist('find /dev -type c -maxdepth 2 2>/dev/null') - call assert_equal('cdev', getftype(cdevfile)) + let type = getftype(cdevfile) + " ignore empty result, can happen if the file disappeared + if type != '' + call assert_equal('cdev', type) + endif endfor for bdevfile in systemlist('find /dev -type b -maxdepth 2 2>/dev/null') - call assert_equal('bdev', getftype(bdevfile)) + let type = getftype(bdevfile) + " ignore empty result, can happen if the file disappeared + if type != '' + call assert_equal('bdev', type) + endif endfor " The /run/ directory typically contains socket files. " If it does not, test won't fail but will not test socket files. for socketfile in systemlist('find /run -type s -maxdepth 2 2>/dev/null') - call assert_equal('socket', getftype(socketfile)) + let type = getftype(socketfile) + " ignore empty result, can happen if the file disappeared + if type != '' + call assert_equal('socket', type) + endif endfor " TODO: file type 'other' is not tested. How can we test it? diff --git a/src/nvim/testdir/test_syntax.vim b/src/nvim/testdir/test_syntax.vim index e35c0f1105..6978faeb7b 100644 --- a/src/nvim/testdir/test_syntax.vim +++ b/src/nvim/testdir/test_syntax.vim @@ -114,6 +114,15 @@ func Test_syntime() bd endfunc +func Test_syntime_completion() + if !has('profile') + return + endif + + call feedkeys(":syntime \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"syntime clear off on report', @:) +endfunc + func Test_syntax_list() syntax on let a = execute('syntax list') @@ -482,3 +491,15 @@ fun Test_synstack_synIDtrans() syn clear bw! endfunc + +" Using \z() in a region with NFA failing should not crash. +func Test_syn_wrong_z_one() + new + call setline(1, ['just some text', 'with foo and bar to match with']) + syn region FooBar start="foo\z(.*\)bar" end="\z1" + " call test_override("nfa_fail", 1) + redraw! + redraw! + " call test_override("ALL", 0) + bwipe! +endfunc diff --git a/src/nvim/testdir/test_timers.vim b/src/nvim/testdir/test_timers.vim index da61751bf4..cdfdd473b9 100644 --- a/src/nvim/testdir/test_timers.vim +++ b/src/nvim/testdir/test_timers.vim @@ -44,7 +44,7 @@ func Test_repeat_many() let timer = timer_start(50, 'MyHandler', {'repeat': -1}) sleep 200m call timer_stop(timer) - call assert_inrange(2, 4, g:val) + call assert_inrange((has('mac') ? 1 : 2), 4, g:val) endfunc func Test_with_partial_callback() @@ -121,7 +121,7 @@ func Test_paused() let slept = WaitFor('g:val == 1') call assert_equal(1, g:val) if has('reltime') - call assert_inrange(0, 100, slept) + call assert_inrange(0, 140, slept) else call assert_inrange(0, 10, slept) endif @@ -167,6 +167,9 @@ func Test_stop_all_in_callback() let g:timer1 = timer_start(10, 'StopTimerAll') let info = timer_info() call assert_equal(1, len(info)) + if has('mac') + sleep 100m + endif sleep 40m let info = timer_info() call assert_equal(0, len(info)) diff --git a/src/nvim/testdir/test_undo.vim b/src/nvim/testdir/test_undo.vim index 83ede1dc37..9729ca9f57 100644 --- a/src/nvim/testdir/test_undo.vim +++ b/src/nvim/testdir/test_undo.vim @@ -373,7 +373,7 @@ funct Test_undofile() let cwd = getcwd() if has('win32') " Replace windows drive such as C:... into C%... - let cwd = substitute(cwd, '^\([A-Z]\):', '\1%', 'g') + let cwd = substitute(cwd, '^\([a-zA-Z]\):', '\1%', 'g') endif let pathsep = has('win32') ? '\' : '/' let cwd = substitute(cwd . pathsep . 'Xundofoo', pathsep, '%', 'g') diff --git a/src/nvim/testdir/test_usercommands.vim b/src/nvim/testdir/test_usercommands.vim index db603610da..1520c2f32a 100644 --- a/src/nvim/testdir/test_usercommands.vim +++ b/src/nvim/testdir/test_usercommands.vim @@ -206,3 +206,15 @@ func Test_CmdCompletion() com! -complete=customlist,CustomComp DoCmd : call assert_fails("call feedkeys(':DoCmd \<C-D>', 'tx')", 'E117:') endfunc + +func CallExecute(A, L, P) + " Drop first '\n' + return execute('echo "hi"')[1:] +endfunc + +func Test_use_execute_in_completion() + command! -nargs=* -complete=custom,CallExecute DoExec : + call feedkeys(":DoExec \<C-A>\<C-B>\"\<CR>", 'tx') + call assert_equal('"DoExec hi', @:) + delcommand DoExec +endfunc diff --git a/src/nvim/tui/terminfo.c b/src/nvim/tui/terminfo.c index 42237903ea..27792655c9 100644 --- a/src/nvim/tui/terminfo.c +++ b/src/nvim/tui/terminfo.c @@ -13,6 +13,7 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/option.h" +#include "nvim/os/os.h" #include "nvim/tui/terminfo.h" #include "nvim/tui/terminfo_defs.h" @@ -33,6 +34,24 @@ bool terminfo_is_term_family(const char *term, const char *family) && ('\0' == term[flen] || '-' == term[flen]); } +bool terminfo_is_bsd_console(const char *term) +{ +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) \ + || defined(__DragonFly__) + if (strequal(term, "vt220") // OpenBSD + || strequal(term, "vt100")) { // NetBSD + return true; + } +# if defined(__FreeBSD__) + // FreeBSD console sets TERM=xterm, but it does not support xterm features + // like cursor-shaping. Assume that TERM=xterm is degraded. #8644 + return strequal(term, "xterm") && !!os_getenv("XTERM_VERSION"); +# endif +#else + return false; +#endif +} + /// Loads a built-in terminfo db when we (unibilium) failed to load a terminfo /// record from the environment (termcap systems, unrecognized $TERM, …). /// We do not attempt to detect xterm pretenders here. diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index f444d1ec84..c73de1049c 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -99,6 +99,7 @@ typedef struct { bool can_set_lr_margin; bool can_set_left_right_margin; bool can_scroll; + bool can_erase_chars; bool immediate_wrap_after_last_column; bool bce; bool mouse_enabled; @@ -108,8 +109,9 @@ typedef struct { cursorentry_T cursor_shapes[SHAPE_IDX_COUNT]; HlAttrs clear_attrs; kvec_t(HlAttrs) attrs; - HlAttrs print_attrs; + int print_attr_id; bool default_attr; + bool can_clear_attr; ModeShape showing_mode; struct { int enable_mouse, disable_mouse; @@ -124,6 +126,7 @@ typedef struct { int set_cursor_style, reset_cursor_style; int enter_undercurl_mode, exit_undercurl_mode, set_underline_color; } unibi_ext; + char *space_buf; } TUIData; static bool volatile got_winch = false; @@ -189,6 +192,7 @@ static void terminfo_start(UI *ui) data->scroll_region_is_full_screen = true; data->bufpos = 0; data->default_attr = false; + data->can_clear_attr = false; data->is_invisible = true; data->busy = false; data->cork = false; @@ -281,14 +285,17 @@ static void terminfo_start(UI *ui) const char *colorterm = os_getenv("COLORTERM"); const char *termprg = os_getenv("TERM_PROGRAM"); const char *vte_version_env = os_getenv("VTE_VERSION"); - long vte_version = vte_version_env ? strtol(vte_version_env, NULL, 10) : 0; + long vtev = vte_version_env ? strtol(vte_version_env, NULL, 10) : 0; bool iterm_env = termprg && strstr(termprg, "iTerm.app"); bool konsole = terminfo_is_term_family(term, "konsole") || os_getenv("KONSOLE_PROFILE_NAME") || os_getenv("KONSOLE_DBUS_SESSION"); + const char *konsolev_env = os_getenv("KONSOLE_VERSION"); + long konsolev = konsolev_env ? strtol(konsolev_env, NULL, 10) + : (konsole ? 1 : 0); - patch_terminfo_bugs(data, term, colorterm, vte_version, konsole, iterm_env); - augment_terminfo(data, term, colorterm, vte_version, konsole, iterm_env); + patch_terminfo_bugs(data, term, colorterm, vtev, konsolev, iterm_env); + augment_terminfo(data, term, colorterm, vtev, konsolev, iterm_env); data->can_change_scroll_region = !!unibi_get_str(data->ut, unibi_change_scroll_region); data->can_set_lr_margin = @@ -301,6 +308,7 @@ static void terminfo_start(UI *ui) && !!unibi_get_str(data->ut, unibi_parm_delete_line) && !!unibi_get_str(data->ut, unibi_insert_line) && !!unibi_get_str(data->ut, unibi_parm_insert_line); + data->can_erase_chars = !!unibi_get_str(data->ut, unibi_erase_chars); data->immediate_wrap_after_last_column = conemu_ansi || terminfo_is_term_family(term, "cygwin") @@ -366,7 +374,7 @@ static void terminfo_stop(UI *ui) static void tui_terminal_start(UI *ui) { TUIData *data = ui->data; - data->print_attrs = HLATTRS_INVALID; + data->print_attr_id = -1; ugrid_init(&data->grid); terminfo_start(ui); update_size(ui); @@ -465,6 +473,7 @@ static void tui_main(UIBridgeData *bridge, UI *ui) loop_close(&tui_loop, false); kv_destroy(data->invalid_regions); kv_destroy(data->attrs); + xfree(data->space_buf); xfree(data); } @@ -499,8 +508,17 @@ static void sigwinch_cb(SignalWatcher *watcher, int signum, void *data) ui_schedule_refresh(); } -static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb) +static bool attrs_differ(UI *ui, int id1, int id2, bool rgb) { + TUIData *data = ui->data; + if (id1 == id2) { + return false; + } else if (id1 < 0 || id2 < 0) { + return true; + } + HlAttrs a1 = kv_A(data->attrs, (size_t)id1); + HlAttrs a2 = kv_A(data->attrs, (size_t)id2); + if (rgb) { return a1.rgb_fg_color != a2.rgb_fg_color || a1.rgb_bg_color != a2.rgb_bg_color @@ -515,21 +533,16 @@ static bool attrs_differ(HlAttrs a1, HlAttrs a2, bool rgb) } } -static bool no_bg(UI *ui, HlAttrs attrs) -{ - return ui->rgb ? attrs.rgb_bg_color == -1 - : attrs.cterm_bg_color == 0; -} - -static void update_attrs(UI *ui, HlAttrs attrs) +static void update_attrs(UI *ui, int attr_id) { TUIData *data = ui->data; - if (!attrs_differ(attrs, data->print_attrs, ui->rgb)) { + if (!attrs_differ(ui, attr_id, data->print_attr_id, ui->rgb)) { + data->print_attr_id = attr_id; return; } - - data->print_attrs = attrs; + data->print_attr_id = attr_id; + HlAttrs attrs = kv_A(data->attrs, (size_t)attr_id); int fg = ui->rgb ? attrs.rgb_fg_color : (attrs.cterm_fg_color - 1); if (fg == -1) { @@ -634,6 +647,12 @@ static void update_attrs(UI *ui, HlAttrs attrs) data->default_attr = fg == -1 && bg == -1 && !bold && !italic && !underline && !undercurl && !reverse && !standout; + + // Non-BCE terminals can't clear with non-default background color. Some BCE + // terminals don't support attributes either, so don't rely on it. But assume + // italic and bold has no effect if there is no text. + data->can_clear_attr = !reverse && !standout && !underline && !undercurl + && (data->bce || bg == -1); } static void final_column_wrap(UI *ui) @@ -659,7 +678,7 @@ static void print_cell(UI *ui, UCell *ptr) // Printing the next character finally advances the cursor. final_column_wrap(ui); } - update_attrs(ui, kv_A(data->attrs, ptr->attr)); + update_attrs(ui, ptr->attr); out(ui, ptr->data, strlen(ptr->data)); grid->col++; if (data->immediate_wrap_after_last_column) { @@ -675,8 +694,8 @@ static bool cheap_to_print(UI *ui, int row, int col, int next) UCell *cell = grid->cells[row] + col; while (next) { next--; - if (attrs_differ(kv_A(data->attrs, cell->attr), - data->print_attrs, ui->rgb)) { + if (attrs_differ(ui, cell->attr, + data->print_attr_id, ui->rgb)) { if (data->default_attr) { return false; } @@ -726,7 +745,7 @@ static void cursor_goto(UI *ui, int row, int col) int n = col - grid->col; if (n <= (row == grid->row ? 4 : 2) && cheap_to_print(ui, grid->row, grid->col, n)) { - UGRID_FOREACH_CELL(grid, grid->row, grid->row, grid->col, col - 1, { + UGRID_FOREACH_CELL(grid, grid->row, grid->col, col, { print_cell(ui, cell); }); } @@ -798,50 +817,45 @@ safe_move: } static void clear_region(UI *ui, int top, int bot, int left, int right, - HlAttrs attrs) + int attr_id) { TUIData *data = ui->data; UGrid *grid = &data->grid; - bool cleared = false; - - // non-BCE terminals can't clear with non-default background color - bool can_clear = data->bce || no_bg(ui, attrs); - - if (can_clear && right == ui->width -1) { - // Background is set to the default color and the right edge matches the - // screen end, try to use terminal codes for clearing the requested area. - update_attrs(ui, attrs); - if (left == 0) { - if (bot == ui->height - 1) { - if (top == 0) { - unibi_out(ui, unibi_clear_screen); - ugrid_goto(&data->grid, top, left); - } else { - cursor_goto(ui, top, 0); - unibi_out(ui, unibi_clr_eos); - } - cleared = true; - } + update_attrs(ui, attr_id); + + // Background is set to the default color and the right edge matches the + // screen end, try to use terminal codes for clearing the requested area. + if (data->can_clear_attr + && left == 0 && right == ui->width && bot == ui->height) { + if (top == 0) { + unibi_out(ui, unibi_clear_screen); + ugrid_goto(&data->grid, top, left); + } else { + cursor_goto(ui, top, 0); + unibi_out(ui, unibi_clr_eos); } + } else { + int width = right-left; - if (!cleared) { - // iterate through each line and clear with clr_eol - for (int row = top; row <= bot; row++) { - cursor_goto(ui, row, left); + // iterate through each line and clear + for (int row = top; row < bot; row++) { + cursor_goto(ui, row, left); + if (data->can_clear_attr && right == ui->width) { unibi_out(ui, unibi_clr_eol); + } else if (data->can_erase_chars && data->can_clear_attr && width >= 5) { + UNIBI_SET_NUM_VAR(data->params[0], width); + unibi_out(ui, unibi_erase_chars); + } else { + out(ui, data->space_buf, (size_t)width); + grid->col += width; + if (data->immediate_wrap_after_last_column) { + // Printing at the right margin immediately advances the cursor. + final_column_wrap(ui); + } } - cleared = true; } } - - if (!cleared) { - // could not clear using faster terminal codes, refresh the whole region - UGRID_FOREACH_CELL(grid, top, bot, left, right, { - cursor_goto(ui, row, col); - print_cell(ui, cell); - }); - } } static void set_scroll_region(UI *ui, int top, int bot, int left, int right) @@ -902,12 +916,16 @@ static void tui_grid_resize(UI *ui, Integer g, Integer width, Integer height) UGrid *grid = &data->grid; ugrid_resize(grid, (int)width, (int)height); + xfree(data->space_buf); + data->space_buf = xmalloc((size_t)width * sizeof(*data->space_buf)); + memset(data->space_buf, ' ', (size_t)width); + // resize might not always be followed by a clear before flush // so clip the invalid region for (size_t i = 0; i < kv_size(data->invalid_regions); i++) { Rect *r = &kv_A(data->invalid_regions, i); - r->bot = MIN(r->bot, grid->height-1); - r->right = MIN(r->right, grid->width-1); + r->bot = MIN(r->bot, grid->height); + r->right = MIN(r->right, grid->width); } if (!got_winch) { // Try to resize the terminal window. @@ -930,8 +948,7 @@ static void tui_grid_clear(UI *ui, Integer g) UGrid *grid = &data->grid; ugrid_clear(grid); kv_size(data->invalid_regions) = 0; - clear_region(ui, 0, grid->height-1, 0, grid->width-1, - data->clear_attrs); + clear_region(ui, 0, grid->height, 0, grid->width, 0); } static void tui_grid_cursor_goto(UI *ui, Integer grid, Integer row, Integer col) @@ -1089,9 +1106,7 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow, data->scroll_region_is_full_screen = fullwidth && top == 0 && bot == ui->height-1; - int clear_top, clear_bot; - ugrid_scroll(grid, top, bot, left, right, (int)rows, - &clear_top, &clear_bot); + ugrid_scroll(grid, top, bot, left, right, (int)rows); bool can_scroll = data->can_scroll && (data->scroll_region_is_full_screen @@ -1106,8 +1121,6 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow, set_scroll_region(ui, top, bot, left, right); } cursor_goto(ui, top, left); - // also set default color attributes or some terminals can become funny - update_attrs(ui, data->clear_attrs); if (rows > 0) { if (rows == 1) { @@ -1129,16 +1142,14 @@ static void tui_grid_scroll(UI *ui, Integer g, Integer startrow, Integer endrow, if (!data->scroll_region_is_full_screen) { reset_scroll_region(ui, fullwidth); } - - if (!(data->bce || no_bg(ui, data->clear_attrs))) { - // Scrolling will leave wrong background in the cleared area on non-BCE - // terminals. Update the cleared area. - clear_region(ui, clear_top, clear_bot, left, right, - data->clear_attrs); - } } else { - // Mark the entire scroll region as invalid for redrawing later - invalidate(ui, top, bot, left, right); + // Mark the moved region as invalid for redrawing later + if (rows > 0) { + endrow = endrow - rows; + } else { + startrow = startrow - rows; + } + invalidate(ui, (int)startrow, (int)endrow, (int)startcol, (int)endcol); } } @@ -1171,8 +1182,8 @@ static void tui_default_colors_set(UI *ui, Integer rgb_fg, Integer rgb_bg, data->clear_attrs.cterm_fg_color = (int)cterm_fg; data->clear_attrs.cterm_bg_color = (int)cterm_bg; - data->print_attrs = HLATTRS_INVALID; - invalidate(ui, 0, data->grid.height-1, 0, data->grid.width-1); + data->print_attr_id = -1; + invalidate(ui, 0, data->grid.height, 0, data->grid.width); } static void tui_flush(UI *ui) @@ -1194,11 +1205,27 @@ static void tui_flush(UI *ui) while (kv_size(data->invalid_regions)) { Rect r = kv_pop(data->invalid_regions); - assert(r.bot < grid->height && r.right < grid->width); - UGRID_FOREACH_CELL(grid, r.top, r.bot, r.left, r.right, { - cursor_goto(ui, row, col); - print_cell(ui, cell); - }); + assert(r.bot <= grid->height && r.right <= grid->width); + + for (int row = r.top; row < r.bot; row++) { + int clear_attr = grid->cells[row][r.right-1].attr; + int clear_col; + for (clear_col = r.right; clear_col > 0; clear_col--) { + UCell *cell = &grid->cells[row][clear_col-1]; + if (!(cell->data[0] == ' ' && cell->data[1] == NUL + && cell->attr == clear_attr)) { + break; + } + } + + UGRID_FOREACH_CELL(grid, row, r.left, clear_col, { + cursor_goto(ui, row, col); + print_cell(ui, cell); + }); + if (clear_col < r.right) { + clear_region(ui, row, row+1, clear_col, r.right, clear_attr); + } + } } cursor_goto(ui, data->row, data->col); @@ -1283,8 +1310,8 @@ static void tui_option_set(UI *ui, String name, Object value) if (strequal(name.data, "termguicolors")) { ui->rgb = value.data.boolean; - data->print_attrs = HLATTRS_INVALID; - invalidate(ui, 0, data->grid.height-1, 0, data->grid.width-1); + data->print_attr_id = -1; + invalidate(ui, 0, data->grid.height, 0, data->grid.width); } } @@ -1300,18 +1327,16 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, assert((size_t)attrs[c-startcol] < kv_size(data->attrs)); grid->cells[linerow][c].attr = attrs[c-startcol]; } - UGRID_FOREACH_CELL(grid, (int)linerow, (int)linerow, (int)startcol, - (int)endcol-1, { - cursor_goto(ui, row, col); + UGRID_FOREACH_CELL(grid, (int)linerow, (int)startcol, (int)endcol, { + cursor_goto(ui, (int)linerow, col); print_cell(ui, cell); }); if (clearcol > endcol) { - HlAttrs cl_attrs = kv_A(data->attrs, (size_t)clearattr); ugrid_clear_chunk(grid, (int)linerow, (int)endcol, (int)clearcol, (sattr_T)clearattr); - clear_region(ui, (int)linerow, (int)linerow, (int)endcol, (int)clearcol-1, - cl_attrs); + clear_region(ui, (int)linerow, (int)linerow+1, (int)endcol, (int)clearcol, + (int)clearattr); } if (wrap && ui->width == grid->width && linerow + 1 < grid->height) { @@ -1319,9 +1344,10 @@ static void tui_raw_line(UI *ui, Integer g, Integer linerow, Integer startcol, // width and the line continuation is within the grid. if (endcol != grid->width) { - // Print the last cell of the row, if we haven't already done so. - cursor_goto(ui, (int)linerow, grid->width - 1); - print_cell(ui, &grid->cells[linerow][grid->width - 1]); + // Print the last char of the row, if we haven't already done so. + int size = grid->cells[linerow][grid->width - 1].data[0] == NUL ? 2 : 1; + cursor_goto(ui, (int)linerow, grid->width - size); + print_cell(ui, &grid->cells[linerow][grid->width - size]); } // Wrap the cursor over to the next line. The next line will be @@ -1334,27 +1360,17 @@ static void invalidate(UI *ui, int top, int bot, int left, int right) { TUIData *data = ui->data; Rect *intersects = NULL; - // Increase dimensions before comparing to ensure adjacent regions are - // treated as intersecting - --top; - ++bot; - --left; - ++right; for (size_t i = 0; i < kv_size(data->invalid_regions); i++) { Rect *r = &kv_A(data->invalid_regions, i); - if (!(top > r->bot || bot < r->top - || left > r->right || right < r->left)) { + // adjacent regions are treated as overlapping + if (!(top > r->bot || bot < r->top) + && !(left > r->right || right < r->left)) { intersects = r; break; } } - ++top; - --bot; - ++left; - --right; - if (intersects) { // If top/bot/left/right intersects with a invalid rect, we replace it // by the union @@ -1514,16 +1530,19 @@ static int unibi_find_ext_bool(unibi_term *ut, const char *name) /// and several terminal emulators falsely announce incorrect terminal types. static void patch_terminfo_bugs(TUIData *data, const char *term, const char *colorterm, long vte_version, - bool konsole, bool iterm_env) + long konsolev, bool iterm_env) { unibi_term *ut = data->ut; - const char * xterm_version = os_getenv("XTERM_VERSION"); + const char *xterm_version = os_getenv("XTERM_VERSION"); #if 0 // We don't need to identify this specifically, for now. bool roxterm = !!os_getenv("ROXTERM_ID"); #endif - bool xterm = terminfo_is_term_family(term, "xterm"); + bool xterm = terminfo_is_term_family(term, "xterm") + // Treat Terminal.app as generic xterm-like, for now. + || terminfo_is_term_family(term, "nsterm"); bool kitty = terminfo_is_term_family(term, "xterm-kitty"); bool linuxvt = terminfo_is_term_family(term, "linux"); + bool bsdvt = terminfo_is_bsd_console(term); bool rxvt = terminfo_is_term_family(term, "rxvt"); bool teraterm = terminfo_is_term_family(term, "teraterm"); bool putty = terminfo_is_term_family(term, "putty"); @@ -1539,12 +1558,12 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, bool alacritty = terminfo_is_term_family(term, "alacritty"); // None of the following work over SSH; see :help TERM . bool iterm_pretending_xterm = xterm && iterm_env; - bool konsole_pretending_xterm = xterm && konsole; + bool konsole_pretending_xterm = xterm && konsolev; bool gnome_pretending_xterm = xterm && colorterm && strstr(colorterm, "gnome-terminal"); bool mate_pretending_xterm = xterm && colorterm && strstr(colorterm, "mate-terminal"); - bool true_xterm = xterm && !!xterm_version; + bool true_xterm = xterm && !!xterm_version && !bsdvt; bool cygwin = terminfo_is_term_family(term, "cygwin"); bool conemu = terminfo_is_term_family(term, "conemu"); @@ -1689,7 +1708,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, unibi_set_num(ut, unibi_max_colors, 256); unibi_set_str(ut, unibi_set_a_foreground, XTERM_SETAF_256_COLON); unibi_set_str(ut, unibi_set_a_background, XTERM_SETAB_256_COLON); - } else if (konsole || xterm || gnome || rxvt || st || putty + } else if (konsolev || xterm || gnome || rxvt || st || putty || linuxvt // Linux 4.8+ supports 256-colour SGR. || mate_pretending_xterm || gnome_pretending_xterm || tmux @@ -1710,7 +1729,8 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, } // Blacklist of terminals that cannot be trusted to report DECSCUSR support. - if (!(st || (vte_version != 0 && vte_version < 3900) || konsole)) { + if (!(st || (vte_version != 0 && vte_version < 3900) + || (konsolev > 0 && konsolev < 180770))) { data->unibi_ext.reset_cursor_style = unibi_find_ext_str(ut, "Se"); data->unibi_ext.set_cursor_style = unibi_find_ext_str(ut, "Ss"); } @@ -1719,15 +1739,15 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, // adding them to terminal types, that have such control sequences but lack // the correct terminfo entries, is a fixup, not an augmentation. if (-1 == data->unibi_ext.set_cursor_style) { - // DECSCUSR (cursor shape) sequence is widely supported by several terminal - // types. https://github.com/gnachman/iTerm2/pull/92 - // xterm extension: vertical bar - if (!konsole + // DECSCUSR (cursor shape) is widely supported. + // https://github.com/gnachman/iTerm2/pull/92 + if ((!bsdvt && (!konsolev || konsolev >= 180770)) && ((xterm && !vte_version) // anything claiming xterm compat // per MinTTY 0.4.3-1 release notes from 2009 || putty // per https://bugzilla.gnome.org/show_bug.cgi?id=720821 || (vte_version >= 3900) + || (konsolev >= 180770) // #9364 || tmux // per tmux manual page // https://lists.gnu.org/archive/html/screen-devel/2013-03/msg00000.html || screen @@ -1735,7 +1755,7 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, || rxvt // per command.C // per analysis of VT100Terminal.m || iterm || iterm_pretending_xterm - || teraterm // per TeraTerm "Supported Control Functions" doco + || teraterm // per TeraTerm "Supported Control Functions" doco || alacritty // https://github.com/jwilm/alacritty/pull/608 || cygwin // Some linux-type terminals implement the xterm extension. @@ -1776,12 +1796,10 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, ""); } unibi_set_ext_str(ut, (size_t)data->unibi_ext.reset_cursor_style, - "\x1b[?c"); - } else if (konsole) { - // Konsole uses an idiosyncratic escape code to set the cursor shape and - // does not support DECSCUSR. This makes Konsole set up and apply a - // nonce profile, which has side-effects on temporary font resizing. - // In an ideal world, Konsole would just support DECSCUSR. + "\x1b[?c"); + } else if (konsolev > 0 && konsolev < 180770) { + // Konsole before version 18.07.70: set up a nonce profile. This has + // side-effects on temporary font resizing. #6798 data->unibi_ext.set_cursor_style = (int)unibi_add_ext_str(ut, "Ss", TMUX_WRAP(tmux, "\x1b]50;CursorShape=%?" "%p1%{3}%<" "%t%{0}" // block @@ -1804,10 +1822,14 @@ static void patch_terminfo_bugs(TUIData *data, const char *term, /// This adds stuff that is not in standard terminfo as extended unibilium /// capabilities. static void augment_terminfo(TUIData *data, const char *term, - const char *colorterm, long vte_version, bool konsole, bool iterm_env) + const char *colorterm, long vte_version, + long konsolev, bool iterm_env) { unibi_term *ut = data->ut; - bool xterm = terminfo_is_term_family(term, "xterm"); + bool xterm = terminfo_is_term_family(term, "xterm") + // Treat Terminal.app as generic xterm-like, for now. + || terminfo_is_term_family(term, "nsterm"); + bool bsdvt = terminfo_is_bsd_console(term); bool dtterm = terminfo_is_term_family(term, "dtterm"); bool rxvt = terminfo_is_term_family(term, "rxvt"); bool teraterm = terminfo_is_term_family(term, "teraterm"); @@ -1818,16 +1840,17 @@ static void augment_terminfo(TUIData *data, const char *term, || terminfo_is_term_family(term, "iterm2") || terminfo_is_term_family(term, "iTerm.app") || terminfo_is_term_family(term, "iTerm2.app"); + bool alacritty = terminfo_is_term_family(term, "alacritty"); // None of the following work over SSH; see :help TERM . bool iterm_pretending_xterm = xterm && iterm_env; - const char * xterm_version = os_getenv("XTERM_VERSION"); - bool true_xterm = xterm && !!xterm_version; + const char *xterm_version = os_getenv("XTERM_VERSION"); + bool true_xterm = xterm && !!xterm_version && !bsdvt; // Only define this capability for terminal types that we know understand it. if (dtterm // originated this extension || xterm // per xterm ctlseqs doco - || konsole // per commentary in VT102Emulation.cpp + || konsolev // per commentary in VT102Emulation.cpp || teraterm // per TeraTerm "Supported Control Functions" doco || rxvt) { // per command.C data->unibi_ext.resize_screen = (int)unibi_add_ext_str(ut, @@ -1885,7 +1908,8 @@ static void augment_terminfo(TUIData *data, const char *term, // would use a tmux control sequence and an extra if(screen) test. data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str( ut, NULL, TMUX_WRAP(tmux, "\033]Pl%p1%06x\033\\")); - } else if ((xterm || rxvt) && (vte_version == 0 || vte_version >= 3900)) { + } else if ((xterm || rxvt || alacritty) + && (vte_version == 0 || vte_version >= 3900)) { // Supported in urxvt, newer VTE. data->unibi_ext.set_cursor_color = (int)unibi_add_ext_str( ut, "ext.set_cursor_color", "\033]12;#%p1%06x\007"); @@ -1925,13 +1949,9 @@ static void augment_terminfo(TUIData *data, const char *term, ut, "ext.enter_undercurl_mode", "\x1b[4:3m"); data->unibi_ext.exit_undercurl_mode = (int)unibi_add_ext_str( ut, "ext.exit_undercurl_mode", "\x1b[4:0m"); - if (has_colon_rgb) { - data->unibi_ext.set_underline_color = (int)unibi_add_ext_str( - ut, "ext.set_underline_color", "\x1b[58:2:%p1%d:%p2%d:%p3%dm"); - } else { - data->unibi_ext.set_underline_color = (int)unibi_add_ext_str( - ut, "ext.set_underline_color", "\x1b[58;2;%p1%d;%p2%d;%p3%dm"); - } + // Only support colon syntax. #9270 + data->unibi_ext.set_underline_color = (int)unibi_add_ext_str( + ut, "ext.set_underline_color", "\x1b[58:2::%p1%d:%p2%d:%p3%dm"); } } diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c index b741a61d8c..f5bd35a48e 100644 --- a/src/nvim/ugrid.c +++ b/src/nvim/ugrid.c @@ -52,8 +52,7 @@ void ugrid_goto(UGrid *grid, int row, int col) grid->col = col; } -void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, - int count, int *clear_top, int *clear_bot) +void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, int count) { // Compute start/stop/step for the loop below int start, stop, step; @@ -76,26 +75,18 @@ void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, memcpy(target_row, source_row, sizeof(UCell) * (size_t)(right - left + 1)); } - - // clear cells in the emptied region, - if (count > 0) { - *clear_top = stop; - *clear_bot = stop + count - 1; - } else { - *clear_bot = stop; - *clear_top = stop + count + 1; - } - clear_region(grid, *clear_top, *clear_bot, left, right, 0); } static void clear_region(UGrid *grid, int top, int bot, int left, int right, sattr_T attr) { - UGRID_FOREACH_CELL(grid, top, bot, left, right, { - cell->data[0] = ' '; - cell->data[1] = 0; - cell->attr = attr; - }); + for (int row = top; row <= bot; row++) { + UGRID_FOREACH_CELL(grid, row, left, right+1, { + cell->data[0] = ' '; + cell->data[1] = 0; + cell->attr = attr; + }); + } } static void destroy_cells(UGrid *grid) diff --git a/src/nvim/ugrid.h b/src/nvim/ugrid.h index af78fe91c5..33a706b8c0 100644 --- a/src/nvim/ugrid.h +++ b/src/nvim/ugrid.h @@ -22,15 +22,13 @@ struct ugrid { // -V:UGRID_FOREACH_CELL:625 -#define UGRID_FOREACH_CELL(grid, top, bot, left, right, code) \ +#define UGRID_FOREACH_CELL(grid, row, startcol, endcol, code) \ do { \ - for (int row = top; row <= bot; row++) { \ - UCell *row_cells = (grid)->cells[row]; \ - for (int col = left; col <= right; col++) { \ - UCell *cell = row_cells + col; \ - (void)(cell); \ - code; \ - } \ + UCell *row_cells = (grid)->cells[row]; \ + for (int col = startcol; col < endcol; col++) { \ + UCell *cell = row_cells + col; \ + (void)(cell); \ + code; \ } \ } while (0) diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index ebd4651f4d..bd5d37be73 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -104,7 +104,7 @@ static void ui_thread_run(void *data) static void ui_bridge_stop(UI *b) { - // Detach brigde first, so that "stop" is the last event the TUI loop + // Detach bridge first, so that "stop" is the last event the TUI loop // receives from the main thread. #8041 ui_detach_impl(b); UIBridgeData *bridge = (UIBridgeData *)b; @@ -117,6 +117,7 @@ static void ui_bridge_stop(UI *b) if (stopped) { // -V547 break; } + // TODO(justinmk): Remove this. Use a cond-wait above. #9274 loop_poll_events(&main_loop, 10); // Process one event. } uv_thread_join(&bridge->ui_thread); diff --git a/src/nvim/version.c b/src/nvim/version.c index beb65a8bfd..966ecfd719 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -159,7 +159,7 @@ static const int included_patches[] = { // 1769, // 1768, // 1767, - // 1766, + 1766, 1765, 1764, // 1763, @@ -167,7 +167,7 @@ static const int included_patches[] = { // 1761, 1760, // 1759, - // 1758, + 1758, 1757, // 1756, 1755, @@ -194,7 +194,7 @@ static const int included_patches[] = { // 1734, // 1733, // 1732, - // 1731, + 1731, 1730, // 1729, // 1728, @@ -305,7 +305,7 @@ static const int included_patches[] = { // 1623, 1622, // 1621, - // 1620, + 1620, // 1619, 1618, // 1617, @@ -372,7 +372,7 @@ static const int included_patches[] = { 1556, 1555, // 1554, - // 1553, + 1553, // 1552, // 1551, // 1550, @@ -460,15 +460,15 @@ static const int included_patches[] = { 1468, 1467, 1466, - // 1465, + 1465, 1464, // 1463, // 1462, // 1461, // 1460, // 1459, - // 1458, - // 1457, + 1458, + 1457, 1456, // 1455, // 1454, @@ -487,7 +487,7 @@ static const int included_patches[] = { 1441, // 1440, 1439, - // 1438, + 1438, 1437, // 1436, 1435, @@ -498,7 +498,7 @@ static const int included_patches[] = { // 1430, // 1429, 1428, - // 1427, + 1427, 1426, // 1425, 1424, @@ -573,7 +573,7 @@ static const int included_patches[] = { // 1355, // 1354, // 1353, - // 1352, + 1352, 1351, // 1350, // 1349, @@ -646,7 +646,7 @@ static const int included_patches[] = { 1282, 1281, // 1280, - // 1279, + 1279, // 1278, // 1277, // 1276, @@ -676,7 +676,7 @@ static const int included_patches[] = { 1252, 1251, 1250, - // 1249, + 1249, 1248, 1247, // 1246, @@ -754,15 +754,15 @@ static const int included_patches[] = { // 1174, // 1173, 1172, - // 1171, + 1171, // 1170, 1169, 1168, // 1167, 1166, - // 1165, + 1165, // 1164, - // 1163, + 1163, // 1162, 1161, 1160, @@ -1188,7 +1188,7 @@ static const int included_patches[] = { // 740, // 739, // 738, - // 737, + 737, 736, 735, 734, @@ -2173,7 +2173,7 @@ void intro_message(int colon) N_(NVIM_VERSION_LONG), "", N_("Nvim is open source and freely distributable"), - N_("https://neovim.io/community"), + N_("https://neovim.io/#chat"), "", N_("type :help nvim<Enter> if you are new! "), N_("type :checkhealth<Enter> to optimize Nvim"), diff --git a/src/nvim/xdiff/COPYING b/src/nvim/xdiff/COPYING new file mode 100644 index 0000000000..f3f1b3b65e --- /dev/null +++ b/src/nvim/xdiff/COPYING @@ -0,0 +1,504 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + <one line to give the library's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + <signature of Ty Coon>, 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/src/nvim/xdiff/README.txt b/src/nvim/xdiff/README.txt new file mode 100644 index 0000000000..1afe74095b --- /dev/null +++ b/src/nvim/xdiff/README.txt @@ -0,0 +1,16 @@ +The files in this directory come from the xdiff implementation in git. +You can find it here: https://github.com/git/git/tree/master/xdiff +The files were last updated 2018 September 10. + +This is originally based on libxdiff, which can be found here: +http://www.xmailserver.org/xdiff-lib.html + +The git version was used because it has been maintained and improved. +And since it's part of git it is expected to be reliable. + +The code is distributed under the GNU LGPL license. It is included in the +COPYING file. + +Changes in these files were made to avoid compiler warnings. + +The first work for including xdiff in Vim was done by Christian Brabandt. diff --git a/src/nvim/xdiff/xdiff.h b/src/nvim/xdiff/xdiff.h new file mode 100644 index 0000000000..bc26fb64fd --- /dev/null +++ b/src/nvim/xdiff/xdiff.h @@ -0,0 +1,143 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#if !defined(XDIFF_H) +#define XDIFF_H + +#ifdef __cplusplus +extern "C" { +#endif /* #ifdef __cplusplus */ + +/* xpparm_t.flags */ +#define XDF_NEED_MINIMAL (1 << 0) + +#define XDF_IGNORE_WHITESPACE (1 << 1) +#define XDF_IGNORE_WHITESPACE_CHANGE (1 << 2) +#define XDF_IGNORE_WHITESPACE_AT_EOL (1 << 3) +#define XDF_IGNORE_CR_AT_EOL (1 << 4) +#define XDF_WHITESPACE_FLAGS (XDF_IGNORE_WHITESPACE | \ + XDF_IGNORE_WHITESPACE_CHANGE | \ + XDF_IGNORE_WHITESPACE_AT_EOL | \ + XDF_IGNORE_CR_AT_EOL) + +#define XDF_IGNORE_BLANK_LINES (1 << 7) + +#define XDF_PATIENCE_DIFF (1 << 14) +#define XDF_HISTOGRAM_DIFF (1 << 15) +#define XDF_DIFF_ALGORITHM_MASK (XDF_PATIENCE_DIFF | XDF_HISTOGRAM_DIFF) +#define XDF_DIFF_ALG(x) ((x) & XDF_DIFF_ALGORITHM_MASK) + +#define XDF_INDENT_HEURISTIC (1 << 23) + +/* xdemitconf_t.flags */ +#define XDL_EMIT_FUNCNAMES (1 << 0) +#define XDL_EMIT_FUNCCONTEXT (1 << 2) + +/* merge simplification levels */ +#define XDL_MERGE_MINIMAL 0 +#define XDL_MERGE_EAGER 1 +#define XDL_MERGE_ZEALOUS 2 +#define XDL_MERGE_ZEALOUS_ALNUM 3 + +/* merge favor modes */ +#define XDL_MERGE_FAVOR_OURS 1 +#define XDL_MERGE_FAVOR_THEIRS 2 +#define XDL_MERGE_FAVOR_UNION 3 + +/* merge output styles */ +#define XDL_MERGE_DIFF3 1 + +typedef struct s_mmfile { + char *ptr; + long size; +} mmfile_t; + +typedef struct s_mmbuffer { + char *ptr; + long size; +} mmbuffer_t; + +typedef struct s_xpparam { + unsigned long flags; + + /* See Documentation/diff-options.txt. */ + char **anchors; + size_t anchors_nr; +} xpparam_t; + +typedef struct s_xdemitcb { + void *priv; + int (*outf)(void *, mmbuffer_t *, int); +} xdemitcb_t; + +typedef long (*find_func_t)(const char *line, long line_len, char *buffer, long buffer_size, void *priv); + +typedef int (*xdl_emit_hunk_consume_func_t)(long start_a, long count_a, + long start_b, long count_b, + void *cb_data); + +typedef struct s_xdemitconf { + long ctxlen; + long interhunkctxlen; + unsigned long flags; + find_func_t find_func; + void *find_func_priv; + xdl_emit_hunk_consume_func_t hunk_func; +} xdemitconf_t; + +typedef struct s_bdiffparam { + long bsize; +} bdiffparam_t; + +#include "../memory.h" + +#define xdl_malloc(x) xmalloc((x)) +#define xdl_free(ptr) xfree(ptr) +#define xdl_realloc(ptr,x) xrealloc((ptr),(x)) + +void *xdl_mmfile_first(mmfile_t *mmf, long *size); +long xdl_mmfile_size(mmfile_t *mmf); + +int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdemitconf_t const *xecfg, xdemitcb_t *ecb); + +typedef struct s_xmparam { + xpparam_t xpp; + int marker_size; + int level; + int favor; + int style; + const char *ancestor; /* label for orig */ + const char *file1; /* label for mf1 */ + const char *file2; /* label for mf2 */ +} xmparam_t; + +#define DEFAULT_CONFLICT_MARKER_SIZE 7 + +int xdl_merge(mmfile_t *orig, mmfile_t *mf1, mmfile_t *mf2, + xmparam_t const *xmp, mmbuffer_t *result); + +#ifdef __cplusplus +} +#endif /* #ifdef __cplusplus */ + +#endif /* #if !defined(XDIFF_H) */ diff --git a/src/nvim/xdiff/xdiffi.c b/src/nvim/xdiff/xdiffi.c new file mode 100644 index 0000000000..96d5277027 --- /dev/null +++ b/src/nvim/xdiff/xdiffi.c @@ -0,0 +1,1043 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#include "xinclude.h" + +#define XDL_MAX_COST_MIN 256 +#define XDL_HEUR_MIN_COST 256 +#define XDL_LINE_MAX (long)((1UL << (CHAR_BIT * sizeof(long) - 1)) - 1) +#define XDL_SNAKE_CNT 20 +#define XDL_K_HEUR 4 + +typedef struct s_xdpsplit { + long i1, i2; + int min_lo, min_hi; +} xdpsplit_t; + +/* + * See "An O(ND) Difference Algorithm and its Variations", by Eugene Myers. + * Basically considers a "box" (off1, off2, lim1, lim2) and scan from both + * the forward diagonal starting from (off1, off2) and the backward diagonal + * starting from (lim1, lim2). If the K values on the same diagonal crosses + * returns the furthest point of reach. We might end up having to expensive + * cases using this algorithm is full, so a little bit of heuristic is needed + * to cut the search and to return a suboptimal point. + */ +static long xdl_split(unsigned long const *ha1, long off1, long lim1, + unsigned long const *ha2, long off2, long lim2, + long *kvdf, long *kvdb, int need_min, xdpsplit_t *spl, + xdalgoenv_t *xenv) { + long dmin = off1 - lim2, dmax = lim1 - off2; + long fmid = off1 - off2, bmid = lim1 - lim2; + long odd = (fmid - bmid) & 1; + long fmin = fmid, fmax = fmid; + long bmin = bmid, bmax = bmid; + long ec, d, i1, i2, prev1, best, dd, v, k; + + /* + * Set initial diagonal values for both forward and backward path. + */ + kvdf[fmid] = off1; + kvdb[bmid] = lim1; + + for (ec = 1;; ec++) { + int got_snake = 0; + + /* + * We need to extent the diagonal "domain" by one. If the next + * values exits the box boundaries we need to change it in the + * opposite direction because (max - min) must be a power of two. + * Also we initialize the external K value to -1 so that we can + * avoid extra conditions check inside the core loop. + */ + if (fmin > dmin) + kvdf[--fmin - 1] = -1; + else + ++fmin; + if (fmax < dmax) + kvdf[++fmax + 1] = -1; + else + --fmax; + + for (d = fmax; d >= fmin; d -= 2) { + if (kvdf[d - 1] >= kvdf[d + 1]) + i1 = kvdf[d - 1] + 1; + else + i1 = kvdf[d + 1]; + prev1 = i1; + i2 = i1 - d; + for (; i1 < lim1 && i2 < lim2 && ha1[i1] == ha2[i2]; i1++, i2++); + if (i1 - prev1 > xenv->snake_cnt) + got_snake = 1; + kvdf[d] = i1; + if (odd && bmin <= d && d <= bmax && kvdb[d] <= i1) { + spl->i1 = i1; + spl->i2 = i2; + spl->min_lo = spl->min_hi = 1; + return ec; + } + } + + /* + * We need to extent the diagonal "domain" by one. If the next + * values exits the box boundaries we need to change it in the + * opposite direction because (max - min) must be a power of two. + * Also we initialize the external K value to -1 so that we can + * avoid extra conditions check inside the core loop. + */ + if (bmin > dmin) + kvdb[--bmin - 1] = XDL_LINE_MAX; + else + ++bmin; + if (bmax < dmax) + kvdb[++bmax + 1] = XDL_LINE_MAX; + else + --bmax; + + for (d = bmax; d >= bmin; d -= 2) { + if (kvdb[d - 1] < kvdb[d + 1]) + i1 = kvdb[d - 1]; + else + i1 = kvdb[d + 1] - 1; + prev1 = i1; + i2 = i1 - d; + for (; i1 > off1 && i2 > off2 && ha1[i1 - 1] == ha2[i2 - 1]; i1--, i2--); + if (prev1 - i1 > xenv->snake_cnt) + got_snake = 1; + kvdb[d] = i1; + if (!odd && fmin <= d && d <= fmax && i1 <= kvdf[d]) { + spl->i1 = i1; + spl->i2 = i2; + spl->min_lo = spl->min_hi = 1; + return ec; + } + } + + if (need_min) + continue; + + /* + * If the edit cost is above the heuristic trigger and if + * we got a good snake, we sample current diagonals to see + * if some of the, have reached an "interesting" path. Our + * measure is a function of the distance from the diagonal + * corner (i1 + i2) penalized with the distance from the + * mid diagonal itself. If this value is above the current + * edit cost times a magic factor (XDL_K_HEUR) we consider + * it interesting. + */ + if (got_snake && ec > xenv->heur_min) { + for (best = 0, d = fmax; d >= fmin; d -= 2) { + dd = d > fmid ? d - fmid: fmid - d; + i1 = kvdf[d]; + i2 = i1 - d; + v = (i1 - off1) + (i2 - off2) - dd; + + if (v > XDL_K_HEUR * ec && v > best && + off1 + xenv->snake_cnt <= i1 && i1 < lim1 && + off2 + xenv->snake_cnt <= i2 && i2 < lim2) { + for (k = 1; ha1[i1 - k] == ha2[i2 - k]; k++) + if (k == xenv->snake_cnt) { + best = v; + spl->i1 = i1; + spl->i2 = i2; + break; + } + } + } + if (best > 0) { + spl->min_lo = 1; + spl->min_hi = 0; + return ec; + } + + for (best = 0, d = bmax; d >= bmin; d -= 2) { + dd = d > bmid ? d - bmid: bmid - d; + i1 = kvdb[d]; + i2 = i1 - d; + v = (lim1 - i1) + (lim2 - i2) - dd; + + if (v > XDL_K_HEUR * ec && v > best && + off1 < i1 && i1 <= lim1 - xenv->snake_cnt && + off2 < i2 && i2 <= lim2 - xenv->snake_cnt) { + for (k = 0; ha1[i1 + k] == ha2[i2 + k]; k++) + if (k == xenv->snake_cnt - 1) { + best = v; + spl->i1 = i1; + spl->i2 = i2; + break; + } + } + } + if (best > 0) { + spl->min_lo = 0; + spl->min_hi = 1; + return ec; + } + } + + /* + * Enough is enough. We spent too much time here and now we collect + * the furthest reaching path using the (i1 + i2) measure. + */ + if (ec >= xenv->mxcost) { + long fbest, fbest1, bbest, bbest1; + + fbest = fbest1 = -1; + for (d = fmax; d >= fmin; d -= 2) { + i1 = XDL_MIN(kvdf[d], lim1); + i2 = i1 - d; + if (lim2 < i2) + i1 = lim2 + d, i2 = lim2; + if (fbest < i1 + i2) { + fbest = i1 + i2; + fbest1 = i1; + } + } + + bbest = bbest1 = XDL_LINE_MAX; + for (d = bmax; d >= bmin; d -= 2) { + i1 = XDL_MAX(off1, kvdb[d]); + i2 = i1 - d; + if (i2 < off2) + i1 = off2 + d, i2 = off2; + if (i1 + i2 < bbest) { + bbest = i1 + i2; + bbest1 = i1; + } + } + + if ((lim1 + lim2) - bbest < fbest - (off1 + off2)) { + spl->i1 = fbest1; + spl->i2 = fbest - fbest1; + spl->min_lo = 1; + spl->min_hi = 0; + } else { + spl->i1 = bbest1; + spl->i2 = bbest - bbest1; + spl->min_lo = 0; + spl->min_hi = 1; + } + return ec; + } + } +} + + +/* + * Rule: "Divide et Impera". Recursively split the box in sub-boxes by calling + * the box splitting function. Note that the real job (marking changed lines) + * is done in the two boundary reaching checks. + */ +int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, + diffdata_t *dd2, long off2, long lim2, + long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv) { + unsigned long const *ha1 = dd1->ha, *ha2 = dd2->ha; + + /* + * Shrink the box by walking through each diagonal snake (SW and NE). + */ + for (; off1 < lim1 && off2 < lim2 && ha1[off1] == ha2[off2]; off1++, off2++); + for (; off1 < lim1 && off2 < lim2 && ha1[lim1 - 1] == ha2[lim2 - 1]; lim1--, lim2--); + + /* + * If one dimension is empty, then all records on the other one must + * be obviously changed. + */ + if (off1 == lim1) { + char *rchg2 = dd2->rchg; + long *rindex2 = dd2->rindex; + + for (; off2 < lim2; off2++) + rchg2[rindex2[off2]] = 1; + } else if (off2 == lim2) { + char *rchg1 = dd1->rchg; + long *rindex1 = dd1->rindex; + + for (; off1 < lim1; off1++) + rchg1[rindex1[off1]] = 1; + } else { + xdpsplit_t spl; + spl.i1 = spl.i2 = 0; + + /* + * Divide ... + */ + if (xdl_split(ha1, off1, lim1, ha2, off2, lim2, kvdf, kvdb, + need_min, &spl, xenv) < 0) { + + return -1; + } + + /* + * ... et Impera. + */ + if (xdl_recs_cmp(dd1, off1, spl.i1, dd2, off2, spl.i2, + kvdf, kvdb, spl.min_lo, xenv) < 0 || + xdl_recs_cmp(dd1, spl.i1, lim1, dd2, spl.i2, lim2, + kvdf, kvdb, spl.min_hi, xenv) < 0) { + + return -1; + } + } + + return 0; +} + + +int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdfenv_t *xe) { + long ndiags; + long *kvd, *kvdf, *kvdb; + xdalgoenv_t xenv; + diffdata_t dd1, dd2; + + if (XDF_DIFF_ALG(xpp->flags) == XDF_PATIENCE_DIFF) + return xdl_do_patience_diff(mf1, mf2, xpp, xe); + + if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) + return xdl_do_histogram_diff(mf1, mf2, xpp, xe); + + if (xdl_prepare_env(mf1, mf2, xpp, xe) < 0) { + + return -1; + } + + /* + * Allocate and setup K vectors to be used by the differential algorithm. + * One is to store the forward path and one to store the backward path. + */ + ndiags = xe->xdf1.nreff + xe->xdf2.nreff + 3; + if (!(kvd = (long *) xdl_malloc((2 * ndiags + 2) * sizeof(long)))) { + + xdl_free_env(xe); + return -1; + } + kvdf = kvd; + kvdb = kvdf + ndiags; + kvdf += xe->xdf2.nreff + 1; + kvdb += xe->xdf2.nreff + 1; + + xenv.mxcost = xdl_bogosqrt(ndiags); + if (xenv.mxcost < XDL_MAX_COST_MIN) + xenv.mxcost = XDL_MAX_COST_MIN; + xenv.snake_cnt = XDL_SNAKE_CNT; + xenv.heur_min = XDL_HEUR_MIN_COST; + + dd1.nrec = xe->xdf1.nreff; + dd1.ha = xe->xdf1.ha; + dd1.rchg = xe->xdf1.rchg; + dd1.rindex = xe->xdf1.rindex; + dd2.nrec = xe->xdf2.nreff; + dd2.ha = xe->xdf2.ha; + dd2.rchg = xe->xdf2.rchg; + dd2.rindex = xe->xdf2.rindex; + + if (xdl_recs_cmp(&dd1, 0, dd1.nrec, &dd2, 0, dd2.nrec, + kvdf, kvdb, (xpp->flags & XDF_NEED_MINIMAL) != 0, &xenv) < 0) { + + xdl_free(kvd); + xdl_free_env(xe); + return -1; + } + + xdl_free(kvd); + + return 0; +} + + +static xdchange_t *xdl_add_change(xdchange_t *xscr, long i1, long i2, long chg1, long chg2) { + xdchange_t *xch; + + if (!(xch = (xdchange_t *) xdl_malloc(sizeof(xdchange_t)))) + return NULL; + + xch->next = xscr; + xch->i1 = i1; + xch->i2 = i2; + xch->chg1 = chg1; + xch->chg2 = chg2; + xch->ignore = 0; + + return xch; +} + + +static int recs_match(xrecord_t *rec1, xrecord_t *rec2, long flags) +{ + return (rec1->ha == rec2->ha && + xdl_recmatch(rec1->ptr, rec1->size, + rec2->ptr, rec2->size, + flags)); +} + +/* + * If a line is indented more than this, xget_indent() just returns this value. + * This avoids having to do absurd amounts of work for data that are not + * human-readable text, and also ensures that the output of xget_indent fits within + * an int. + */ +#define MAX_INDENT 200 + +/* + * Return the amount of indentation of the specified line, treating TAB as 8 + * columns. Return -1 if line is empty or contains only whitespace. Clamp the + * output value at MAX_INDENT. + */ +static int xget_indent(xrecord_t *rec) +{ + long i; + int ret = 0; + + for (i = 0; i < rec->size; i++) { + char c = rec->ptr[i]; + + if (!XDL_ISSPACE(c)) + return ret; + else if (c == ' ') + ret += 1; + else if (c == '\t') + ret += 8 - ret % 8; + /* ignore other whitespace characters */ + + if (ret >= MAX_INDENT) + return MAX_INDENT; + } + + /* The line contains only whitespace. */ + return -1; +} + +/* + * If more than this number of consecutive blank rows are found, just return this + * value. This avoids requiring O(N^2) work for pathological cases, and also + * ensures that the output of score_split fits in an int. + */ +#define MAX_BLANKS 20 + +/* Characteristics measured about a hypothetical split position. */ +struct split_measurement { + /* + * Is the split at the end of the file (aside from any blank lines)? + */ + int end_of_file; + + /* + * How much is the line immediately following the split indented (or -1 if + * the line is blank): + */ + int indent; + + /* + * How many consecutive lines above the split are blank? + */ + int pre_blank; + + /* + * How much is the nearest non-blank line above the split indented (or -1 + * if there is no such line)? + */ + int pre_indent; + + /* + * How many lines after the line following the split are blank? + */ + int post_blank; + + /* + * How much is the nearest non-blank line after the line following the + * split indented (or -1 if there is no such line)? + */ + int post_indent; +}; + +struct split_score { + /* The effective indent of this split (smaller is preferred). */ + int effective_indent; + + /* Penalty for this split (smaller is preferred). */ + int penalty; +}; + +/* + * Fill m with information about a hypothetical split of xdf above line split. + */ +static void measure_split(const xdfile_t *xdf, long split, + struct split_measurement *m) +{ + long i; + + if (split >= xdf->nrec) { + m->end_of_file = 1; + m->indent = -1; + } else { + m->end_of_file = 0; + m->indent = xget_indent(xdf->recs[split]); + } + + m->pre_blank = 0; + m->pre_indent = -1; + for (i = split - 1; i >= 0; i--) { + m->pre_indent = xget_indent(xdf->recs[i]); + if (m->pre_indent != -1) + break; + m->pre_blank += 1; + if (m->pre_blank == MAX_BLANKS) { + m->pre_indent = 0; + break; + } + } + + m->post_blank = 0; + m->post_indent = -1; + for (i = split + 1; i < xdf->nrec; i++) { + m->post_indent = xget_indent(xdf->recs[i]); + if (m->post_indent != -1) + break; + m->post_blank += 1; + if (m->post_blank == MAX_BLANKS) { + m->post_indent = 0; + break; + } + } +} + +/* + * The empirically-determined weight factors used by score_split() below. + * Larger values means that the position is a less favorable place to split. + * + * Note that scores are only ever compared against each other, so multiplying + * all of these weight/penalty values by the same factor wouldn't change the + * heuristic's behavior. Still, we need to set that arbitrary scale *somehow*. + * In practice, these numbers are chosen to be large enough that they can be + * adjusted relative to each other with sufficient precision despite using + * integer math. + */ + +/* Penalty if there are no non-blank lines before the split */ +#define START_OF_FILE_PENALTY 1 + +/* Penalty if there are no non-blank lines after the split */ +#define END_OF_FILE_PENALTY 21 + +/* Multiplier for the number of blank lines around the split */ +#define TOTAL_BLANK_WEIGHT (-30) + +/* Multiplier for the number of blank lines after the split */ +#define POST_BLANK_WEIGHT 6 + +/* + * Penalties applied if the line is indented more than its predecessor + */ +#define RELATIVE_INDENT_PENALTY (-4) +#define RELATIVE_INDENT_WITH_BLANK_PENALTY 10 + +/* + * Penalties applied if the line is indented less than both its predecessor and + * its successor + */ +#define RELATIVE_OUTDENT_PENALTY 24 +#define RELATIVE_OUTDENT_WITH_BLANK_PENALTY 17 + +/* + * Penalties applied if the line is indented less than its predecessor but not + * less than its successor + */ +#define RELATIVE_DEDENT_PENALTY 23 +#define RELATIVE_DEDENT_WITH_BLANK_PENALTY 17 + +/* + * We only consider whether the sum of the effective indents for splits are + * less than (-1), equal to (0), or greater than (+1) each other. The resulting + * value is multiplied by the following weight and combined with the penalty to + * determine the better of two scores. + */ +#define INDENT_WEIGHT 60 + +/* + * How far do we slide a hunk at most? + */ +#define INDENT_HEURISTIC_MAX_SLIDING 100 + +/* + * Compute a badness score for the hypothetical split whose measurements are + * stored in m. The weight factors were determined empirically using the tools and + * corpus described in + * + * https://github.com/mhagger/diff-slider-tools + * + * Also see that project if you want to improve the weights based on, for example, + * a larger or more diverse corpus. + */ +static void score_add_split(const struct split_measurement *m, struct split_score *s) +{ + /* + * A place to accumulate penalty factors (positive makes this index more + * favored): + */ + int post_blank, total_blank, indent, any_blanks; + + if (m->pre_indent == -1 && m->pre_blank == 0) + s->penalty += START_OF_FILE_PENALTY; + + if (m->end_of_file) + s->penalty += END_OF_FILE_PENALTY; + + /* + * Set post_blank to the number of blank lines following the split, + * including the line immediately after the split: + */ + post_blank = (m->indent == -1) ? 1 + m->post_blank : 0; + total_blank = m->pre_blank + post_blank; + + /* Penalties based on nearby blank lines: */ + s->penalty += TOTAL_BLANK_WEIGHT * total_blank; + s->penalty += POST_BLANK_WEIGHT * post_blank; + + if (m->indent != -1) + indent = m->indent; + else + indent = m->post_indent; + + any_blanks = (total_blank != 0); + + /* Note that the effective indent is -1 at the end of the file: */ + s->effective_indent += indent; + + if (indent == -1) { + /* No additional adjustments needed. */ + } else if (m->pre_indent == -1) { + /* No additional adjustments needed. */ + } else if (indent > m->pre_indent) { + /* + * The line is indented more than its predecessor. + */ + s->penalty += any_blanks ? + RELATIVE_INDENT_WITH_BLANK_PENALTY : + RELATIVE_INDENT_PENALTY; + } else if (indent == m->pre_indent) { + /* + * The line has the same indentation level as its predecessor. + * No additional adjustments needed. + */ + } else { + /* + * The line is indented less than its predecessor. It could be + * the block terminator of the previous block, but it could + * also be the start of a new block (e.g., an "else" block, or + * maybe the previous block didn't have a block terminator). + * Try to distinguish those cases based on what comes next: + */ + if (m->post_indent != -1 && m->post_indent > indent) { + /* + * The following line is indented more. So it is likely + * that this line is the start of a block. + */ + s->penalty += any_blanks ? + RELATIVE_OUTDENT_WITH_BLANK_PENALTY : + RELATIVE_OUTDENT_PENALTY; + } else { + /* + * That was probably the end of a block. + */ + s->penalty += any_blanks ? + RELATIVE_DEDENT_WITH_BLANK_PENALTY : + RELATIVE_DEDENT_PENALTY; + } + } +} + +static int score_cmp(struct split_score *s1, struct split_score *s2) +{ + /* -1 if s1.effective_indent < s2->effective_indent, etc. */ + int cmp_indents = ((s1->effective_indent > s2->effective_indent) - + (s1->effective_indent < s2->effective_indent)); + + return INDENT_WEIGHT * cmp_indents + (s1->penalty - s2->penalty); +} + +/* + * Represent a group of changed lines in an xdfile_t (i.e., a contiguous group + * of lines that was inserted or deleted from the corresponding version of the + * file). We consider there to be such a group at the beginning of the file, at + * the end of the file, and between any two unchanged lines, though most such + * groups will usually be empty. + * + * If the first line in a group is equal to the line following the group, then + * the group can be slid down. Similarly, if the last line in a group is equal + * to the line preceding the group, then the group can be slid up. See + * group_slide_down() and group_slide_up(). + * + * Note that loops that are testing for changed lines in xdf->rchg do not need + * index bounding since the array is prepared with a zero at position -1 and N. + */ +struct xdlgroup { + /* + * The index of the first changed line in the group, or the index of + * the unchanged line above which the (empty) group is located. + */ + long start; + + /* + * The index of the first unchanged line after the group. For an empty + * group, end is equal to start. + */ + long end; +}; + +/* + * Initialize g to point at the first group in xdf. + */ +static void group_init(xdfile_t *xdf, struct xdlgroup *g) +{ + g->start = g->end = 0; + while (xdf->rchg[g->end]) + g->end++; +} + +/* + * Move g to describe the next (possibly empty) group in xdf and return 0. If g + * is already at the end of the file, do nothing and return -1. + */ +static inline int group_next(xdfile_t *xdf, struct xdlgroup *g) +{ + if (g->end == xdf->nrec) + return -1; + + g->start = g->end + 1; + for (g->end = g->start; xdf->rchg[g->end]; g->end++) + ; + + return 0; +} + +/* + * Move g to describe the previous (possibly empty) group in xdf and return 0. + * If g is already at the beginning of the file, do nothing and return -1. + */ +static inline int group_previous(xdfile_t *xdf, struct xdlgroup *g) +{ + if (g->start == 0) + return -1; + + g->end = g->start - 1; + for (g->start = g->end; xdf->rchg[g->start - 1]; g->start--) + ; + + return 0; +} + +/* + * If g can be slid toward the end of the file, do so, and if it bumps into a + * following group, expand this group to include it. Return 0 on success or -1 + * if g cannot be slid down. + */ +static int group_slide_down(xdfile_t *xdf, struct xdlgroup *g, long flags) +{ + if (g->end < xdf->nrec && + recs_match(xdf->recs[g->start], xdf->recs[g->end], flags)) { + xdf->rchg[g->start++] = 0; + xdf->rchg[g->end++] = 1; + + while (xdf->rchg[g->end]) + g->end++; + + return 0; + } else { + return -1; + } +} + +/* + * If g can be slid toward the beginning of the file, do so, and if it bumps + * into a previous group, expand this group to include it. Return 0 on success + * or -1 if g cannot be slid up. + */ +static int group_slide_up(xdfile_t *xdf, struct xdlgroup *g, long flags) +{ + if (g->start > 0 && + recs_match(xdf->recs[g->start - 1], xdf->recs[g->end - 1], flags)) { + xdf->rchg[--g->start] = 1; + xdf->rchg[--g->end] = 0; + + while (xdf->rchg[g->start - 1]) + g->start--; + + return 0; + } else { + return -1; + } +} + +static void xdl_bug(const char *msg) +{ + fprintf(stderr, "BUG: %s\n", msg); + exit(1); +} + +/* + * Move back and forward change groups for a consistent and pretty diff output. + * This also helps in finding joinable change groups and reducing the diff + * size. + */ +int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags) { + struct xdlgroup g, go; + long earliest_end, end_matching_other; + long groupsize; + + group_init(xdf, &g); + group_init(xdfo, &go); + + while (1) { + /* If the group is empty in the to-be-compacted file, skip it: */ + if (g.end == g.start) + goto next; + + /* + * Now shift the change up and then down as far as possible in + * each direction. If it bumps into any other changes, merge them. + */ + do { + groupsize = g.end - g.start; + + /* + * Keep track of the last "end" index that causes this + * group to align with a group of changed lines in the + * other file. -1 indicates that we haven't found such + * a match yet: + */ + end_matching_other = -1; + + /* Shift the group backward as much as possible: */ + while (!group_slide_up(xdf, &g, flags)) + if (group_previous(xdfo, &go)) + xdl_bug("group sync broken sliding up"); + + /* + * This is this highest that this group can be shifted. + * Record its end index: + */ + earliest_end = g.end; + + if (go.end > go.start) + end_matching_other = g.end; + + /* Now shift the group forward as far as possible: */ + while (1) { + if (group_slide_down(xdf, &g, flags)) + break; + if (group_next(xdfo, &go)) + xdl_bug("group sync broken sliding down"); + + if (go.end > go.start) + end_matching_other = g.end; + } + } while (groupsize != g.end - g.start); + + /* + * If the group can be shifted, then we can possibly use this + * freedom to produce a more intuitive diff. + * + * The group is currently shifted as far down as possible, so the + * heuristics below only have to handle upwards shifts. + */ + + if (g.end == earliest_end) { + /* no shifting was possible */ + } else if (end_matching_other != -1) { + /* + * Move the possibly merged group of changes back to line + * up with the last group of changes from the other file + * that it can align with. + */ + while (go.end == go.start) { + if (group_slide_up(xdf, &g, flags)) + xdl_bug("match disappeared"); + if (group_previous(xdfo, &go)) + xdl_bug("group sync broken sliding to match"); + } + } else if (flags & XDF_INDENT_HEURISTIC) { + /* + * Indent heuristic: a group of pure add/delete lines + * implies two splits, one between the end of the "before" + * context and the start of the group, and another between + * the end of the group and the beginning of the "after" + * context. Some splits are aesthetically better and some + * are worse. We compute a badness "score" for each split, + * and add the scores for the two splits to define a + * "score" for each position that the group can be shifted + * to. Then we pick the shift with the lowest score. + */ + long shift, best_shift = -1; + struct split_score best_score; + + shift = earliest_end; + if (g.end - groupsize - 1 > shift) + shift = g.end - groupsize - 1; + if (g.end - INDENT_HEURISTIC_MAX_SLIDING > shift) + shift = g.end - INDENT_HEURISTIC_MAX_SLIDING; + for (; shift <= g.end; shift++) { + struct split_measurement m; + struct split_score score = {0, 0}; + + measure_split(xdf, shift, &m); + score_add_split(&m, &score); + measure_split(xdf, shift - groupsize, &m); + score_add_split(&m, &score); + if (best_shift == -1 || + score_cmp(&score, &best_score) <= 0) { + best_score.effective_indent = score.effective_indent; + best_score.penalty = score.penalty; + best_shift = shift; + } + } + + while (g.end > best_shift) { + if (group_slide_up(xdf, &g, flags)) + xdl_bug("best shift unreached"); + if (group_previous(xdfo, &go)) + xdl_bug("group sync broken sliding to blank line"); + } + } + + next: + /* Move past the just-processed group: */ + if (group_next(xdf, &g)) + break; + if (group_next(xdfo, &go)) + xdl_bug("group sync broken moving to next group"); + } + + if (!group_next(xdfo, &go)) + xdl_bug("group sync broken at end of file"); + + return 0; +} + + +int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr) { + xdchange_t *cscr = NULL, *xch; + char *rchg1 = xe->xdf1.rchg, *rchg2 = xe->xdf2.rchg; + long i1, i2, l1, l2; + + /* + * Trivial. Collects "groups" of changes and creates an edit script. + */ + for (i1 = xe->xdf1.nrec, i2 = xe->xdf2.nrec; i1 >= 0 || i2 >= 0; i1--, i2--) + if (rchg1[i1 - 1] || rchg2[i2 - 1]) { + for (l1 = i1; rchg1[i1 - 1]; i1--); + for (l2 = i2; rchg2[i2 - 1]; i2--); + + if (!(xch = xdl_add_change(cscr, i1, i2, l1 - i1, l2 - i2))) { + xdl_free_script(cscr); + return -1; + } + cscr = xch; + } + + *xscr = cscr; + + return 0; +} + + +void xdl_free_script(xdchange_t *xscr) { + xdchange_t *xch; + + while ((xch = xscr) != NULL) { + xscr = xscr->next; + xdl_free(xch); + } +} + +static int xdl_call_hunk_func(xdfenv_t *xe UNUSED, xdchange_t *xscr, xdemitcb_t *ecb, + xdemitconf_t const *xecfg) +{ + xdchange_t *xch, *xche; + + for (xch = xscr; xch; xch = xche->next) { + xche = xdl_get_hunk(&xch, xecfg); + if (!xch) + break; + if (xecfg->hunk_func(xch->i1, xche->i1 + xche->chg1 - xch->i1, + xch->i2, xche->i2 + xche->chg2 - xch->i2, + ecb->priv) < 0) + return -1; + } + return 0; +} + +static void xdl_mark_ignorable(xdchange_t *xscr, xdfenv_t *xe, long flags) +{ + xdchange_t *xch; + + for (xch = xscr; xch; xch = xch->next) { + int ignore = 1; + xrecord_t **rec; + long i; + + rec = &xe->xdf1.recs[xch->i1]; + for (i = 0; i < xch->chg1 && ignore; i++) + ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); + + rec = &xe->xdf2.recs[xch->i2]; + for (i = 0; i < xch->chg2 && ignore; i++) + ignore = xdl_blankline(rec[i]->ptr, rec[i]->size, flags); + + xch->ignore = ignore; + } +} + +int xdl_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdemitconf_t const *xecfg, xdemitcb_t *ecb) { + xdchange_t *xscr; + xdfenv_t xe; + emit_func_t ef = xecfg->hunk_func ? xdl_call_hunk_func : xdl_emit_diff; + + if (xdl_do_diff(mf1, mf2, xpp, &xe) < 0) { + + return -1; + } + if (xdl_change_compact(&xe.xdf1, &xe.xdf2, xpp->flags) < 0 || + xdl_change_compact(&xe.xdf2, &xe.xdf1, xpp->flags) < 0 || + xdl_build_script(&xe, &xscr) < 0) { + + xdl_free_env(&xe); + return -1; + } + if (xscr) { + if (xpp->flags & XDF_IGNORE_BLANK_LINES) + xdl_mark_ignorable(xscr, &xe, xpp->flags); + + if (ef(&xe, xscr, ecb, xecfg) < 0) { + + xdl_free_script(xscr); + xdl_free_env(&xe); + return -1; + } + xdl_free_script(xscr); + } + xdl_free_env(&xe); + + return 0; +} diff --git a/src/nvim/xdiff/xdiffi.h b/src/nvim/xdiff/xdiffi.h new file mode 100644 index 0000000000..8f1c7c8b04 --- /dev/null +++ b/src/nvim/xdiff/xdiffi.h @@ -0,0 +1,64 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#if !defined(XDIFFI_H) +#define XDIFFI_H + + +typedef struct s_diffdata { + long nrec; + unsigned long const *ha; + long *rindex; + char *rchg; +} diffdata_t; + +typedef struct s_xdalgoenv { + long mxcost; + long snake_cnt; + long heur_min; +} xdalgoenv_t; + +typedef struct s_xdchange { + struct s_xdchange *next; + long i1, i2; + long chg1, chg2; + int ignore; +} xdchange_t; + + + +int xdl_recs_cmp(diffdata_t *dd1, long off1, long lim1, + diffdata_t *dd2, long off2, long lim2, + long *kvdf, long *kvdb, int need_min, xdalgoenv_t *xenv); +int xdl_do_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdfenv_t *xe); +int xdl_change_compact(xdfile_t *xdf, xdfile_t *xdfo, long flags); +int xdl_build_script(xdfenv_t *xe, xdchange_t **xscr); +void xdl_free_script(xdchange_t *xscr); +int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, + xdemitconf_t const *xecfg); +int xdl_do_patience_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdfenv_t *env); +int xdl_do_histogram_diff(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdfenv_t *env); + +#endif /* #if !defined(XDIFFI_H) */ diff --git a/src/nvim/xdiff/xemit.c b/src/nvim/xdiff/xemit.c new file mode 100644 index 0000000000..d8a6f1ed38 --- /dev/null +++ b/src/nvim/xdiff/xemit.c @@ -0,0 +1,332 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#include "xinclude.h" + +static long xdl_get_rec(xdfile_t *xdf, long ri, char const **rec) { + + *rec = xdf->recs[ri]->ptr; + + return xdf->recs[ri]->size; +} + + +static int xdl_emit_record(xdfile_t *xdf, long ri, char const *pre, xdemitcb_t *ecb) { + long size, psize = (long)strlen(pre); + char const *rec; + + size = xdl_get_rec(xdf, ri, &rec); + if (xdl_emit_diffrec(rec, size, pre, psize, ecb) < 0) { + + return -1; + } + + return 0; +} + + +/* + * Starting at the passed change atom, find the latest change atom to be included + * inside the differential hunk according to the specified configuration. + * Also advance xscr if the first changes must be discarded. + */ +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg) +{ + xdchange_t *xch, *xchp, *lxch; + long max_common = 2 * xecfg->ctxlen + xecfg->interhunkctxlen; + long max_ignorable = xecfg->ctxlen; + unsigned long ignored = 0; /* number of ignored blank lines */ + + /* remove ignorable changes that are too far before other changes */ + for (xchp = *xscr; xchp && xchp->ignore; xchp = xchp->next) { + xch = xchp->next; + + if (xch == NULL || + xch->i1 - (xchp->i1 + xchp->chg1) >= max_ignorable) + *xscr = xch; + } + + if (*xscr == NULL) + return NULL; + + lxch = *xscr; + + for (xchp = *xscr, xch = xchp->next; xch; xchp = xch, xch = xch->next) { + long distance = xch->i1 - (xchp->i1 + xchp->chg1); + if (distance > max_common) + break; + + if (distance < max_ignorable && (!xch->ignore || lxch == xchp)) { + lxch = xch; + ignored = 0; + } else if (distance < max_ignorable && xch->ignore) { + ignored += xch->chg2; + } else if (lxch != xchp && + xch->i1 + (long)ignored - (lxch->i1 + lxch->chg1) > max_common) { + break; + } else if (!xch->ignore) { + lxch = xch; + ignored = 0; + } else { + ignored += xch->chg2; + } + } + + return lxch; +} + + +#if 0 +static long def_ff(const char *rec, long len, char *buf, long sz, void *priv UNUSED) +{ + if (len > 0 && + (isalpha((unsigned char)*rec) || /* identifier? */ + *rec == '_' || /* also identifier? */ + *rec == '$')) { /* identifiers from VMS and other esoterico */ + if (len > sz) + len = sz; + while (0 < len && isspace((unsigned char)rec[len - 1])) + len--; + memcpy(buf, rec, len); + return len; + } + return -1; +} +#endif + +#if 0 +static long match_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri, + char *buf, long sz) +{ + const char *rec; + long len = xdl_get_rec(xdf, ri, &rec); + if (!xecfg->find_func) + return def_ff(rec, len, buf, sz, xecfg->find_func_priv); + return xecfg->find_func(rec, len, buf, sz, xecfg->find_func_priv); +} +#endif + +#if 0 +static int is_func_rec(xdfile_t *xdf, xdemitconf_t const *xecfg, long ri) +{ + char dummy[1]; + return match_func_rec(xdf, xecfg, ri, dummy, sizeof(dummy)) >= 0; +} +#endif + +struct func_line { + long len; + char buf[80]; +}; + +#if 0 +static long get_func_line(xdfenv_t *xe, xdemitconf_t const *xecfg, + struct func_line *func_line, long start, long limit) +{ + long l, size, step = (start > limit) ? -1 : 1; + char *buf, dummy[1]; + + buf = func_line ? func_line->buf : dummy; + size = func_line ? sizeof(func_line->buf) : sizeof(dummy); + + for (l = start; l != limit && 0 <= l && l < xe->xdf1.nrec; l += step) { + long len = match_func_rec(&xe->xdf1, xecfg, l, buf, size); + if (len >= 0) { + if (func_line) + func_line->len = len; + return l; + } + } + return -1; +} +#endif + +#if 0 +static int is_empty_rec(xdfile_t *xdf, long ri) +{ + const char *rec; + long len = xdl_get_rec(xdf, ri, &rec); + + while (len > 0 && XDL_ISSPACE(*rec)) { + rec++; + len--; + } + return !len; +} +#endif + +int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, + xdemitconf_t const *xecfg) { + long s1, s2, e1, e2, lctx; + xdchange_t *xch, *xche; +#if 0 + long funclineprev = -1; +#endif + struct func_line func_line; + + func_line.len = 0; + + for (xch = xscr; xch; xch = xche->next) { + xche = xdl_get_hunk(&xch, xecfg); + if (!xch) + break; + + s1 = XDL_MAX(xch->i1 - xecfg->ctxlen, 0); + s2 = XDL_MAX(xch->i2 - xecfg->ctxlen, 0); + +#if 0 + if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) { + long fs1, i1 = xch->i1; + + /* Appended chunk? */ + if (i1 >= xe->xdf1.nrec) { + long i2 = xch->i2; + + /* + * We don't need additional context if + * a whole function was added. + */ + while (i2 < xe->xdf2.nrec) { + if (is_func_rec(&xe->xdf2, xecfg, i2)) + goto post_context_calculation; + i2++; + } + + /* + * Otherwise get more context from the + * pre-image. + */ + i1 = xe->xdf1.nrec - 1; + } + + fs1 = get_func_line(xe, xecfg, NULL, i1, -1); + while (fs1 > 0 && !is_empty_rec(&xe->xdf1, fs1 - 1) && + !is_func_rec(&xe->xdf1, xecfg, fs1 - 1)) + fs1--; + if (fs1 < 0) + fs1 = 0; + if (fs1 < s1) { + s2 -= s1 - fs1; + s1 = fs1; + } + } + + post_context_calculation: +#endif + lctx = xecfg->ctxlen; + lctx = XDL_MIN(lctx, xe->xdf1.nrec - (xche->i1 + xche->chg1)); + lctx = XDL_MIN(lctx, xe->xdf2.nrec - (xche->i2 + xche->chg2)); + + e1 = xche->i1 + xche->chg1 + lctx; + e2 = xche->i2 + xche->chg2 + lctx; + +#if 0 + if (xecfg->flags & XDL_EMIT_FUNCCONTEXT) { + long fe1 = get_func_line(xe, xecfg, NULL, + xche->i1 + xche->chg1, + xe->xdf1.nrec); + while (fe1 > 0 && is_empty_rec(&xe->xdf1, fe1 - 1)) + fe1--; + if (fe1 < 0) + fe1 = xe->xdf1.nrec; + if (fe1 > e1) { + e2 += fe1 - e1; + e1 = fe1; + } + + /* + * Overlap with next change? Then include it + * in the current hunk and start over to find + * its new end. + */ + if (xche->next) { + long l = XDL_MIN(xche->next->i1, + xe->xdf1.nrec - 1); + if (l - xecfg->ctxlen <= e1 || + get_func_line(xe, xecfg, NULL, l, e1) < 0) { + xche = xche->next; + goto post_context_calculation; + } + } + } +#endif + + /* + * Emit current hunk header. + */ + +#if 0 + if (xecfg->flags & XDL_EMIT_FUNCNAMES) { + get_func_line(xe, xecfg, &func_line, + s1 - 1, funclineprev); + funclineprev = s1 - 1; + } +#endif + if (xdl_emit_hunk_hdr(s1 + 1, e1 - s1, s2 + 1, e2 - s2, + func_line.buf, func_line.len, ecb) < 0) + return -1; + + /* + * Emit pre-context. + */ + for (; s2 < xch->i2; s2++) + if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0) + return -1; + + for (s1 = xch->i1, s2 = xch->i2;; xch = xch->next) { + /* + * Merge previous with current change atom. + */ + for (; s1 < xch->i1 && s2 < xch->i2; s1++, s2++) + if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0) + return -1; + + /* + * Removes lines from the first file. + */ + for (s1 = xch->i1; s1 < xch->i1 + xch->chg1; s1++) + if (xdl_emit_record(&xe->xdf1, s1, "-", ecb) < 0) + return -1; + + /* + * Adds lines from the second file. + */ + for (s2 = xch->i2; s2 < xch->i2 + xch->chg2; s2++) + if (xdl_emit_record(&xe->xdf2, s2, "+", ecb) < 0) + return -1; + + if (xch == xche) + break; + s1 = xch->i1 + xch->chg1; + s2 = xch->i2 + xch->chg2; + } + + /* + * Emit post-context. + */ + for (s2 = xche->i2 + xche->chg2; s2 < e2; s2++) + if (xdl_emit_record(&xe->xdf2, s2, " ", ecb) < 0) + return -1; + } + + return 0; +} diff --git a/src/nvim/xdiff/xemit.h b/src/nvim/xdiff/xemit.h new file mode 100644 index 0000000000..1b9887e670 --- /dev/null +++ b/src/nvim/xdiff/xemit.h @@ -0,0 +1,36 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#if !defined(XEMIT_H) +#define XEMIT_H + + +typedef int (*emit_func_t)(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, + xdemitconf_t const *xecfg); + +xdchange_t *xdl_get_hunk(xdchange_t **xscr, xdemitconf_t const *xecfg); +int xdl_emit_diff(xdfenv_t *xe, xdchange_t *xscr, xdemitcb_t *ecb, + xdemitconf_t const *xecfg); + + + +#endif /* #if !defined(XEMIT_H) */ diff --git a/src/nvim/xdiff/xhistogram.c b/src/nvim/xdiff/xhistogram.c new file mode 100644 index 0000000000..3fb8974dd4 --- /dev/null +++ b/src/nvim/xdiff/xhistogram.c @@ -0,0 +1,386 @@ +/* + * Copyright (C) 2010, Google Inc. + * and other copyright owners as documented in JGit's IP log. + * + * This program and the accompanying materials are made available + * under the terms of the Eclipse Distribution License v1.0 which + * accompanies this distribution, is reproduced below, and is + * available at http://www.eclipse.org/org/documents/edl-v10.php + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * - Neither the name of the Eclipse Foundation, Inc. nor the + * names of its contributors may be used to endorse or promote + * products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "xinclude.h" +#include "xtypes.h" +#include "xdiff.h" + +#define MAX_PTR INT_MAX +#define MAX_CNT INT_MAX + +#define LINE_END(n) (line##n + count##n - 1) +#define LINE_END_PTR(n) (*line##n + *count##n - 1) + +struct histindex { + struct record { + unsigned int ptr, cnt; + struct record *next; + } **records, /* an occurrence */ + **line_map; /* map of line to record chain */ + chastore_t rcha; + unsigned int *next_ptrs; + unsigned int table_bits, + records_size, + line_map_size; + + unsigned int max_chain_length, + key_shift, + ptr_shift; + + unsigned int cnt, + has_common; + + xdfenv_t *env; + xpparam_t const *xpp; +}; + +struct region { + unsigned int begin1, end1; + unsigned int begin2, end2; +}; + +#define LINE_MAP(i, a) (i->line_map[(a) - i->ptr_shift]) + +#define NEXT_PTR(index, ptr) \ + (index->next_ptrs[(ptr) - index->ptr_shift]) + +#define CNT(index, ptr) \ + ((LINE_MAP(index, ptr))->cnt) + +#define REC(env, s, l) \ + (env->xdf##s.recs[l - 1]) + +static int cmp_recs(xpparam_t const *xpp, + xrecord_t *r1, xrecord_t *r2) +{ + return r1->ha == r2->ha && + xdl_recmatch(r1->ptr, r1->size, r2->ptr, r2->size, + xpp->flags); +} + +#define CMP_ENV(xpp, env, s1, l1, s2, l2) \ + (cmp_recs(xpp, REC(env, s1, l1), REC(env, s2, l2))) + +#define CMP(i, s1, l1, s2, l2) \ + (cmp_recs(i->xpp, REC(i->env, s1, l1), REC(i->env, s2, l2))) + +#define TABLE_HASH(index, side, line) \ + XDL_HASHLONG((REC(index->env, side, line))->ha, index->table_bits) + +static int scanA(struct histindex *index, int line1, int count1) +{ + int ptr, tbl_idx; + unsigned int chain_len; + struct record **rec_chain, *rec; + + for (ptr = LINE_END(1); line1 <= ptr; ptr--) { + tbl_idx = TABLE_HASH(index, 1, ptr); + rec_chain = index->records + tbl_idx; + rec = *rec_chain; + + chain_len = 0; + while (rec) { + if (CMP(index, 1, rec->ptr, 1, ptr)) { + /* + * ptr is identical to another element. Insert + * it onto the front of the existing element + * chain. + */ + NEXT_PTR(index, ptr) = rec->ptr; + rec->ptr = ptr; + /* cap rec->cnt at MAX_CNT */ + rec->cnt = XDL_MIN(MAX_CNT, rec->cnt + 1); + LINE_MAP(index, ptr) = rec; + goto continue_scan; + } + + rec = rec->next; + chain_len++; + } + + if (chain_len == index->max_chain_length) + return -1; + + /* + * This is the first time we have ever seen this particular + * element in the sequence. Construct a new chain for it. + */ + if (!(rec = xdl_cha_alloc(&index->rcha))) + return -1; + rec->ptr = ptr; + rec->cnt = 1; + rec->next = *rec_chain; + *rec_chain = rec; + LINE_MAP(index, ptr) = rec; + +continue_scan: + ; /* no op */ + } + + return 0; +} + +static int try_lcs(struct histindex *index, struct region *lcs, int b_ptr, + int line1, int count1, int line2, int count2) +{ + unsigned int b_next = b_ptr + 1; + struct record *rec = index->records[TABLE_HASH(index, 2, b_ptr)]; + unsigned int as, ae, bs, be, np, rc; + int should_break; + + for (; rec; rec = rec->next) { + if (rec->cnt > index->cnt) { + if (!index->has_common) + index->has_common = CMP(index, 1, rec->ptr, 2, b_ptr); + continue; + } + + as = rec->ptr; + if (!CMP(index, 1, as, 2, b_ptr)) + continue; + + index->has_common = 1; + for (;;) { + should_break = 0; + np = NEXT_PTR(index, as); + bs = b_ptr; + ae = as; + be = bs; + rc = rec->cnt; + + while (line1 < (int)as && line2 < (int)bs + && CMP(index, 1, as - 1, 2, bs - 1)) { + as--; + bs--; + if (1 < rc) + rc = XDL_MIN(rc, CNT(index, as)); + } + while ((int)ae < LINE_END(1) && (int)be < LINE_END(2) + && CMP(index, 1, ae + 1, 2, be + 1)) { + ae++; + be++; + if (1 < rc) + rc = XDL_MIN(rc, CNT(index, ae)); + } + + if (b_next <= be) + b_next = be + 1; + if (lcs->end1 - lcs->begin1 < ae - as || rc < index->cnt) { + lcs->begin1 = as; + lcs->begin2 = bs; + lcs->end1 = ae; + lcs->end2 = be; + index->cnt = rc; + } + + if (np == 0) + break; + + while (np <= ae) { + np = NEXT_PTR(index, np); + if (np == 0) { + should_break = 1; + break; + } + } + + if (should_break) + break; + + as = np; + } + } + return b_next; +} + +static int fall_back_to_classic_diff(xpparam_t const *xpp, xdfenv_t *env, + int line1, int count1, int line2, int count2) +{ + xpparam_t xpparam; + xpparam.flags = xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; + + return xdl_fall_back_diff(env, &xpparam, + line1, count1, line2, count2); +} + +static inline void free_index(struct histindex *index) +{ + xdl_free(index->records); + xdl_free(index->line_map); + xdl_free(index->next_ptrs); + xdl_cha_free(&index->rcha); +} + +static int find_lcs(xpparam_t const *xpp, xdfenv_t *env, + struct region *lcs, + int line1, int count1, int line2, int count2) +{ + int b_ptr; + int sz, ret = -1; + struct histindex index; + + memset(&index, 0, sizeof(index)); + + index.env = env; + index.xpp = xpp; + + index.records = NULL; + index.line_map = NULL; + /* in case of early xdl_cha_free() */ + index.rcha.head = NULL; + + index.table_bits = xdl_hashbits(count1); + sz = index.records_size = 1 << index.table_bits; + sz *= sizeof(struct record *); + if (!(index.records = (struct record **) xdl_malloc(sz))) + goto cleanup; + memset(index.records, 0, sz); + + sz = index.line_map_size = count1; + sz *= sizeof(struct record *); + if (!(index.line_map = (struct record **) xdl_malloc(sz))) + goto cleanup; + memset(index.line_map, 0, sz); + + sz = index.line_map_size; + sz *= sizeof(unsigned int); + if (!(index.next_ptrs = (unsigned int *) xdl_malloc(sz))) + goto cleanup; + memset(index.next_ptrs, 0, sz); + + /* lines / 4 + 1 comes from xprepare.c:xdl_prepare_ctx() */ + if (xdl_cha_init(&index.rcha, sizeof(struct record), count1 / 4 + 1) < 0) + goto cleanup; + + index.ptr_shift = line1; + index.max_chain_length = 64; + + if (scanA(&index, line1, count1)) + goto cleanup; + + index.cnt = index.max_chain_length + 1; + + for (b_ptr = line2; b_ptr <= LINE_END(2); ) + b_ptr = try_lcs(&index, lcs, b_ptr, line1, count1, line2, count2); + + if (index.has_common && index.max_chain_length < index.cnt) + ret = 1; + else + ret = 0; + +cleanup: + free_index(&index); + return ret; +} + +static int histogram_diff(xpparam_t const *xpp, xdfenv_t *env, + int line1, int count1, int line2, int count2) +{ + struct region lcs; + int lcs_found; + int result; +redo: + result = -1; + + if (count1 <= 0 && count2 <= 0) + return 0; + + if (LINE_END(1) >= MAX_PTR) + return -1; + + if (!count1) { + while(count2--) + env->xdf2.rchg[line2++ - 1] = 1; + return 0; + } else if (!count2) { + while(count1--) + env->xdf1.rchg[line1++ - 1] = 1; + return 0; + } + + memset(&lcs, 0, sizeof(lcs)); + lcs_found = find_lcs(xpp, env, &lcs, line1, count1, line2, count2); + if (lcs_found < 0) + goto out; + else if (lcs_found) + result = fall_back_to_classic_diff(xpp, env, line1, count1, line2, count2); + else { + if (lcs.begin1 == 0 && lcs.begin2 == 0) { + while (count1--) + env->xdf1.rchg[line1++ - 1] = 1; + while (count2--) + env->xdf2.rchg[line2++ - 1] = 1; + result = 0; + } else { + result = histogram_diff(xpp, env, + line1, lcs.begin1 - line1, + line2, lcs.begin2 - line2); + if (result) + goto out; + /* + * result = histogram_diff(xpp, env, + * lcs.end1 + 1, LINE_END(1) - lcs.end1, + * lcs.end2 + 1, LINE_END(2) - lcs.end2); + * but let's optimize tail recursion ourself: + */ + count1 = LINE_END(1) - lcs.end1; + line1 = lcs.end1 + 1; + count2 = LINE_END(2) - lcs.end2; + line2 = lcs.end2 + 1; + goto redo; + } + } +out: + return result; +} + +int xdl_do_histogram_diff(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env) +{ + if (xdl_prepare_env(file1, file2, xpp, env) < 0) + return -1; + + return histogram_diff(xpp, env, + env->xdf1.dstart + 1, env->xdf1.dend - env->xdf1.dstart + 1, + env->xdf2.dstart + 1, env->xdf2.dend - env->xdf2.dstart + 1); +} diff --git a/src/nvim/xdiff/xinclude.h b/src/nvim/xdiff/xinclude.h new file mode 100644 index 0000000000..46b8608314 --- /dev/null +++ b/src/nvim/xdiff/xinclude.h @@ -0,0 +1,61 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +/* defines HAVE_ATTRIBUTE_UNUSED */ +#ifdef HAVE_CONFIG_H +# include "../auto/config.h" +#endif + +/* Mark unused function arguments with UNUSED, so that gcc -Wunused-parameter + * can be used to check for mistakes. */ +#ifdef HAVE_ATTRIBUTE_UNUSED +# define UNUSED __attribute__((unused)) +#else +# define UNUSED +#endif + +#if defined(_MSC_VER) +# define inline __inline +#endif + +#if !defined(XINCLUDE_H) +#define XINCLUDE_H + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#if !defined(_WIN32) +#include <unistd.h> +#endif +#include <string.h> +#include <limits.h> + +#include "xmacros.h" +#include "xdiff.h" +#include "xtypes.h" +#include "xutils.h" +#include "xprepare.h" +#include "xdiffi.h" +#include "xemit.h" + + +#endif /* #if !defined(XINCLUDE_H) */ diff --git a/src/nvim/xdiff/xmacros.h b/src/nvim/xdiff/xmacros.h new file mode 100644 index 0000000000..2809a28ca9 --- /dev/null +++ b/src/nvim/xdiff/xmacros.h @@ -0,0 +1,54 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#if !defined(XMACROS_H) +#define XMACROS_H + + + + +#define XDL_MIN(a, b) ((a) < (b) ? (a): (b)) +#define XDL_MAX(a, b) ((a) > (b) ? (a): (b)) +#define XDL_ABS(v) ((v) >= 0 ? (v): -(v)) +#define XDL_ISDIGIT(c) ((c) >= '0' && (c) <= '9') +#define XDL_ISSPACE(c) (isspace((unsigned char)(c))) +#define XDL_ADDBITS(v,b) ((v) + ((v) >> (b))) +#define XDL_MASKBITS(b) ((1UL << (b)) - 1) +#define XDL_HASHLONG(v,b) (XDL_ADDBITS((unsigned long)(v), b) & XDL_MASKBITS(b)) +#define XDL_PTRFREE(p) do { if (p) { xdl_free(p); (p) = NULL; } } while (0) +#define XDL_LE32_PUT(p, v) \ +do { \ + unsigned char *__p = (unsigned char *) (p); \ + *__p++ = (unsigned char) (v); \ + *__p++ = (unsigned char) ((v) >> 8); \ + *__p++ = (unsigned char) ((v) >> 16); \ + *__p = (unsigned char) ((v) >> 24); \ +} while (0) +#define XDL_LE32_GET(p, v) \ +do { \ + unsigned char const *__p = (unsigned char const *) (p); \ + (v) = (unsigned long) __p[0] | ((unsigned long) __p[1]) << 8 | \ + ((unsigned long) __p[2]) << 16 | ((unsigned long) __p[3]) << 24; \ +} while (0) + + +#endif /* #if !defined(XMACROS_H) */ diff --git a/src/nvim/xdiff/xpatience.c b/src/nvim/xdiff/xpatience.c new file mode 100644 index 0000000000..2c65aac386 --- /dev/null +++ b/src/nvim/xdiff/xpatience.c @@ -0,0 +1,393 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003-2016 Davide Libenzi, Johannes E. Schindelin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ +#include "xinclude.h" +#include "xtypes.h" +#include "xdiff.h" + +/* + * The basic idea of patience diff is to find lines that are unique in + * both files. These are intuitively the ones that we want to see as + * common lines. + * + * The maximal ordered sequence of such line pairs (where ordered means + * that the order in the sequence agrees with the order of the lines in + * both files) naturally defines an initial set of common lines. + * + * Now, the algorithm tries to extend the set of common lines by growing + * the line ranges where the files have identical lines. + * + * Between those common lines, the patience diff algorithm is applied + * recursively, until no unique line pairs can be found; these line ranges + * are handled by the well-known Myers algorithm. + */ + +#define NON_UNIQUE ULONG_MAX + +/* + * This is a hash mapping from line hash to line numbers in the first and + * second file. + */ +struct hashmap { + int nr, alloc; + struct entry { + unsigned long hash; + /* + * 0 = unused entry, 1 = first line, 2 = second, etc. + * line2 is NON_UNIQUE if the line is not unique + * in either the first or the second file. + */ + unsigned long line1, line2; + /* + * "next" & "previous" are used for the longest common + * sequence; + * initially, "next" reflects only the order in file1. + */ + struct entry *next, *previous; + + /* + * If 1, this entry can serve as an anchor. See + * Documentation/diff-options.txt for more information. + */ + unsigned anchor : 1; + } *entries, *first, *last; + /* were common records found? */ + unsigned long has_matches; + mmfile_t *file1, *file2; + xdfenv_t *env; + xpparam_t const *xpp; +}; + +static int is_anchor(xpparam_t const *xpp, const char *line) +{ + size_t i; + for (i = 0; i < xpp->anchors_nr; i++) { + if (!strncmp(line, xpp->anchors[i], strlen(xpp->anchors[i]))) + return 1; + } + return 0; +} + +/* The argument "pass" is 1 for the first file, 2 for the second. */ +static void insert_record(xpparam_t const *xpp, int line, struct hashmap *map, + int pass) +{ + xrecord_t **records = pass == 1 ? + map->env->xdf1.recs : map->env->xdf2.recs; + xrecord_t *record = records[line - 1], *other; + /* + * After xdl_prepare_env() (or more precisely, due to + * xdl_classify_record()), the "ha" member of the records (AKA lines) + * is _not_ the hash anymore, but a linearized version of it. In + * other words, the "ha" member is guaranteed to start with 0 and + * the second record's ha can only be 0 or 1, etc. + * + * So we multiply ha by 2 in the hope that the hashing was + * "unique enough". + */ + int index = (int)((record->ha << 1) % map->alloc); + + while (map->entries[index].line1) { + other = map->env->xdf1.recs[map->entries[index].line1 - 1]; + if (map->entries[index].hash != record->ha || + !xdl_recmatch(record->ptr, record->size, + other->ptr, other->size, + map->xpp->flags)) { + if (++index >= map->alloc) + index = 0; + continue; + } + if (pass == 2) + map->has_matches = 1; + if (pass == 1 || map->entries[index].line2) + map->entries[index].line2 = NON_UNIQUE; + else + map->entries[index].line2 = line; + return; + } + if (pass == 2) + return; + map->entries[index].line1 = line; + map->entries[index].hash = record->ha; + map->entries[index].anchor = is_anchor(xpp, map->env->xdf1.recs[line - 1]->ptr); + if (!map->first) + map->first = map->entries + index; + if (map->last) { + map->last->next = map->entries + index; + map->entries[index].previous = map->last; + } + map->last = map->entries + index; + map->nr++; +} + +/* + * This function has to be called for each recursion into the inter-hunk + * parts, as previously non-unique lines can become unique when being + * restricted to a smaller part of the files. + * + * It is assumed that env has been prepared using xdl_prepare(). + */ +static int fill_hashmap(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env, + struct hashmap *result, + int line1, int count1, int line2, int count2) +{ + result->file1 = file1; + result->file2 = file2; + result->xpp = xpp; + result->env = env; + + /* We know exactly how large we want the hash map */ + result->alloc = count1 * 2; + result->entries = (struct entry *) + xdl_malloc(result->alloc * sizeof(struct entry)); + if (!result->entries) + return -1; + memset(result->entries, 0, result->alloc * sizeof(struct entry)); + + /* First, fill with entries from the first file */ + while (count1--) + insert_record(xpp, line1++, result, 1); + + /* Then search for matches in the second file */ + while (count2--) + insert_record(xpp, line2++, result, 2); + + return 0; +} + +/* + * Find the longest sequence with a smaller last element (meaning a smaller + * line2, as we construct the sequence with entries ordered by line1). + */ +static int binary_search(struct entry **sequence, int longest, + struct entry *entry) +{ + int left = -1, right = longest; + + while (left + 1 < right) { + int middle = left + (right - left) / 2; + /* by construction, no two entries can be equal */ + if (sequence[middle]->line2 > entry->line2) + right = middle; + else + left = middle; + } + /* return the index in "sequence", _not_ the sequence length */ + return left; +} + +/* + * The idea is to start with the list of common unique lines sorted by + * the order in file1. For each of these pairs, the longest (partial) + * sequence whose last element's line2 is smaller is determined. + * + * For efficiency, the sequences are kept in a list containing exactly one + * item per sequence length: the sequence with the smallest last + * element (in terms of line2). + */ +static struct entry *find_longest_common_sequence(struct hashmap *map) +{ + struct entry **sequence = (struct entry **)xdl_malloc(map->nr * sizeof(struct entry *)); + int longest = 0, i; + struct entry *entry; + /* + * If not -1, this entry in sequence must never be overridden. + * Therefore, overriding entries before this has no effect, so + * do not do that either. + */ + int anchor_i = -1; + + /* Added to silence Coverity. */ + if (sequence == NULL) + return map->first; + + for (entry = map->first; entry; entry = entry->next) { + if (!entry->line2 || entry->line2 == NON_UNIQUE) + continue; + i = binary_search(sequence, longest, entry); + entry->previous = i < 0 ? NULL : sequence[i]; + ++i; + if (i <= anchor_i) + continue; + sequence[i] = entry; + if (entry->anchor) { + anchor_i = i; + longest = anchor_i + 1; + } else if (i == longest) { + longest++; + } + } + + /* No common unique lines were found */ + if (!longest) { + xdl_free(sequence); + return NULL; + } + + /* Iterate starting at the last element, adjusting the "next" members */ + entry = sequence[longest - 1]; + entry->next = NULL; + while (entry->previous) { + entry->previous->next = entry; + entry = entry->previous; + } + xdl_free(sequence); + return entry; +} + +static int match(struct hashmap *map, int line1, int line2) +{ + xrecord_t *record1 = map->env->xdf1.recs[line1 - 1]; + xrecord_t *record2 = map->env->xdf2.recs[line2 - 1]; + return xdl_recmatch(record1->ptr, record1->size, + record2->ptr, record2->size, map->xpp->flags); +} + +static int patience_diff(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env, + int line1, int count1, int line2, int count2); + +static int walk_common_sequence(struct hashmap *map, struct entry *first, + int line1, int count1, int line2, int count2) +{ + int end1 = line1 + count1, end2 = line2 + count2; + int next1, next2; + + for (;;) { + /* Try to grow the line ranges of common lines */ + if (first) { + next1 = first->line1; + next2 = first->line2; + while (next1 > line1 && next2 > line2 && + match(map, next1 - 1, next2 - 1)) { + next1--; + next2--; + } + } else { + next1 = end1; + next2 = end2; + } + while (line1 < next1 && line2 < next2 && + match(map, line1, line2)) { + line1++; + line2++; + } + + /* Recurse */ + if (next1 > line1 || next2 > line2) { + struct hashmap submap; + + memset(&submap, 0, sizeof(submap)); + if (patience_diff(map->file1, map->file2, + map->xpp, map->env, + line1, next1 - line1, + line2, next2 - line2)) + return -1; + } + + if (!first) + return 0; + + while (first->next && + first->next->line1 == first->line1 + 1 && + first->next->line2 == first->line2 + 1) + first = first->next; + + line1 = first->line1 + 1; + line2 = first->line2 + 1; + + first = first->next; + } +} + +static int fall_back_to_classic_diff(struct hashmap *map, + int line1, int count1, int line2, int count2) +{ + xpparam_t xpp; + xpp.flags = map->xpp->flags & ~XDF_DIFF_ALGORITHM_MASK; + + return xdl_fall_back_diff(map->env, &xpp, + line1, count1, line2, count2); +} + +/* + * Recursively find the longest common sequence of unique lines, + * and if none was found, ask xdl_do_diff() to do the job. + * + * This function assumes that env was prepared with xdl_prepare_env(). + */ +static int patience_diff(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env, + int line1, int count1, int line2, int count2) +{ + struct hashmap map; + struct entry *first; + int result = 0; + + /* trivial case: one side is empty */ + if (!count1) { + while(count2--) + env->xdf2.rchg[line2++ - 1] = 1; + return 0; + } else if (!count2) { + while(count1--) + env->xdf1.rchg[line1++ - 1] = 1; + return 0; + } + + memset(&map, 0, sizeof(map)); + if (fill_hashmap(file1, file2, xpp, env, &map, + line1, count1, line2, count2)) + return -1; + + /* are there any matching lines at all? */ + if (!map.has_matches) { + while(count1--) + env->xdf1.rchg[line1++ - 1] = 1; + while(count2--) + env->xdf2.rchg[line2++ - 1] = 1; + xdl_free(map.entries); + return 0; + } + + first = find_longest_common_sequence(&map); + if (first) + result = walk_common_sequence(&map, first, + line1, count1, line2, count2); + else + result = fall_back_to_classic_diff(&map, + line1, count1, line2, count2); + + xdl_free(map.entries); + return result; +} + +int xdl_do_patience_diff(mmfile_t *file1, mmfile_t *file2, + xpparam_t const *xpp, xdfenv_t *env) +{ + if (xdl_prepare_env(file1, file2, xpp, env) < 0) + return -1; + + /* environment is cleaned up in xdl_diff() */ + return patience_diff(file1, file2, xpp, env, + 1, env->xdf1.nrec, 1, env->xdf2.nrec); +} diff --git a/src/nvim/xdiff/xprepare.c b/src/nvim/xdiff/xprepare.c new file mode 100644 index 0000000000..abeb8fb84e --- /dev/null +++ b/src/nvim/xdiff/xprepare.c @@ -0,0 +1,483 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#include "xinclude.h" + + +#define XDL_KPDIS_RUN 4 +#define XDL_MAX_EQLIMIT 1024 +#define XDL_SIMSCAN_WINDOW 100 +#define XDL_GUESS_NLINES1 256 +#define XDL_GUESS_NLINES2 20 + + +typedef struct s_xdlclass { + struct s_xdlclass *next; + unsigned long ha; + char const *line; + long size; + long idx; + long len1, len2; +} xdlclass_t; + +typedef struct s_xdlclassifier { + unsigned int hbits; + long hsize; + xdlclass_t **rchash; + chastore_t ncha; + xdlclass_t **rcrecs; + long alloc; + long count; + long flags; +} xdlclassifier_t; + + + + +static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags); +static void xdl_free_classifier(xdlclassifier_t *cf); +static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash, + unsigned int hbits, xrecord_t *rec); +static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp, + xdlclassifier_t *cf, xdfile_t *xdf); +static void xdl_free_ctx(xdfile_t *xdf); +static int xdl_clean_mmatch(char const *dis, long i, long s, long e); +static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2); +static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2); +static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2); + + + + +static int xdl_init_classifier(xdlclassifier_t *cf, long size, long flags) { + cf->flags = flags; + + cf->hbits = xdl_hashbits((unsigned int) size); + cf->hsize = 1 << cf->hbits; + + if (xdl_cha_init(&cf->ncha, sizeof(xdlclass_t), size / 4 + 1) < 0) { + + return -1; + } + if (!(cf->rchash = (xdlclass_t **) xdl_malloc(cf->hsize * sizeof(xdlclass_t *)))) { + + xdl_cha_free(&cf->ncha); + return -1; + } + memset(cf->rchash, 0, cf->hsize * sizeof(xdlclass_t *)); + + cf->alloc = size; + if (!(cf->rcrecs = (xdlclass_t **) xdl_malloc(cf->alloc * sizeof(xdlclass_t *)))) { + + xdl_free(cf->rchash); + xdl_cha_free(&cf->ncha); + return -1; + } + + cf->count = 0; + + return 0; +} + + +static void xdl_free_classifier(xdlclassifier_t *cf) { + + xdl_free(cf->rcrecs); + xdl_free(cf->rchash); + xdl_cha_free(&cf->ncha); +} + + +static int xdl_classify_record(unsigned int pass, xdlclassifier_t *cf, xrecord_t **rhash, + unsigned int hbits, xrecord_t *rec) { + long hi; + char const *line; + xdlclass_t *rcrec; + xdlclass_t **rcrecs; + + line = rec->ptr; + hi = (long) XDL_HASHLONG(rec->ha, cf->hbits); + for (rcrec = cf->rchash[hi]; rcrec; rcrec = rcrec->next) + if (rcrec->ha == rec->ha && + xdl_recmatch(rcrec->line, rcrec->size, + rec->ptr, rec->size, cf->flags)) + break; + + if (!rcrec) { + if (!(rcrec = xdl_cha_alloc(&cf->ncha))) { + + return -1; + } + rcrec->idx = cf->count++; + if (cf->count > cf->alloc) { + cf->alloc *= 2; + if (!(rcrecs = (xdlclass_t **) xdl_realloc(cf->rcrecs, cf->alloc * sizeof(xdlclass_t *)))) { + + return -1; + } + cf->rcrecs = rcrecs; + } + cf->rcrecs[rcrec->idx] = rcrec; + rcrec->line = line; + rcrec->size = rec->size; + rcrec->ha = rec->ha; + rcrec->len1 = rcrec->len2 = 0; + rcrec->next = cf->rchash[hi]; + cf->rchash[hi] = rcrec; + } + + (pass == 1) ? rcrec->len1++ : rcrec->len2++; + + rec->ha = (unsigned long) rcrec->idx; + + hi = (long) XDL_HASHLONG(rec->ha, hbits); + rec->next = rhash[hi]; + rhash[hi] = rec; + + return 0; +} + + +static int xdl_prepare_ctx(unsigned int pass, mmfile_t *mf, long narec, xpparam_t const *xpp, + xdlclassifier_t *cf, xdfile_t *xdf) { + unsigned int hbits; + long nrec, hsize, bsize; + unsigned long hav; + char const *blk, *cur, *top, *prev; + xrecord_t *crec; + xrecord_t **recs, **rrecs; + xrecord_t **rhash; + unsigned long *ha; + char *rchg; + long *rindex; + + ha = NULL; + rindex = NULL; + rchg = NULL; + rhash = NULL; + recs = NULL; + + if (xdl_cha_init(&xdf->rcha, sizeof(xrecord_t), narec / 4 + 1) < 0) + goto abort; + if (!(recs = (xrecord_t **) xdl_malloc(narec * sizeof(xrecord_t *)))) + goto abort; + + if (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF) + hbits = hsize = 0; + else { + hbits = xdl_hashbits((unsigned int) narec); + hsize = 1 << hbits; + if (!(rhash = (xrecord_t **) xdl_malloc(hsize * sizeof(xrecord_t *)))) + goto abort; + memset(rhash, 0, hsize * sizeof(xrecord_t *)); + } + + nrec = 0; + if ((cur = blk = xdl_mmfile_first(mf, &bsize)) != NULL) { + for (top = blk + bsize; cur < top; ) { + prev = cur; + hav = xdl_hash_record(&cur, top, xpp->flags); + if (nrec >= narec) { + narec *= 2; + if (!(rrecs = (xrecord_t **) xdl_realloc(recs, narec * sizeof(xrecord_t *)))) + goto abort; + recs = rrecs; + } + if (!(crec = xdl_cha_alloc(&xdf->rcha))) + goto abort; + crec->ptr = prev; + crec->size = (long) (cur - prev); + crec->ha = hav; + recs[nrec++] = crec; + + if ((XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) && + xdl_classify_record(pass, cf, rhash, hbits, crec) < 0) + goto abort; + } + } + + if (!(rchg = (char *) xdl_malloc((nrec + 2) * sizeof(char)))) + goto abort; + memset(rchg, 0, (nrec + 2) * sizeof(char)); + + if (!(rindex = (long *) xdl_malloc((nrec + 1) * sizeof(long)))) + goto abort; + if (!(ha = (unsigned long *) xdl_malloc((nrec + 1) * sizeof(unsigned long)))) + goto abort; + + xdf->nrec = nrec; + xdf->recs = recs; + xdf->hbits = hbits; + xdf->rhash = rhash; + xdf->rchg = rchg + 1; + xdf->rindex = rindex; + xdf->nreff = 0; + xdf->ha = ha; + xdf->dstart = 0; + xdf->dend = nrec - 1; + + return 0; + +abort: + xdl_free(ha); + xdl_free(rindex); + xdl_free(rchg); + xdl_free(rhash); + xdl_free(recs); + xdl_cha_free(&xdf->rcha); + return -1; +} + + +static void xdl_free_ctx(xdfile_t *xdf) { + + xdl_free(xdf->rhash); + xdl_free(xdf->rindex); + xdl_free(xdf->rchg - 1); + xdl_free(xdf->ha); + xdl_free(xdf->recs); + xdl_cha_free(&xdf->rcha); +} + + +int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdfenv_t *xe) { + long enl1, enl2, sample; + xdlclassifier_t cf; + + memset(&cf, 0, sizeof(cf)); + + /* + * For histogram diff, we can afford a smaller sample size and + * thus a poorer estimate of the number of lines, as the hash + * table (rhash) won't be filled up/grown. The number of lines + * (nrecs) will be updated correctly anyway by + * xdl_prepare_ctx(). + */ + sample = (XDF_DIFF_ALG(xpp->flags) == XDF_HISTOGRAM_DIFF + ? XDL_GUESS_NLINES2 : XDL_GUESS_NLINES1); + + enl1 = xdl_guess_lines(mf1, sample) + 1; + enl2 = xdl_guess_lines(mf2, sample) + 1; + + if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF && + xdl_init_classifier(&cf, enl1 + enl2 + 1, xpp->flags) < 0) + return -1; + + if (xdl_prepare_ctx(1, mf1, enl1, xpp, &cf, &xe->xdf1) < 0) { + + xdl_free_classifier(&cf); + return -1; + } + if (xdl_prepare_ctx(2, mf2, enl2, xpp, &cf, &xe->xdf2) < 0) { + + xdl_free_ctx(&xe->xdf1); + xdl_free_classifier(&cf); + return -1; + } + + if ((XDF_DIFF_ALG(xpp->flags) != XDF_PATIENCE_DIFF) && + (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) && + xdl_optimize_ctxs(&cf, &xe->xdf1, &xe->xdf2) < 0) { + + xdl_free_ctx(&xe->xdf2); + xdl_free_ctx(&xe->xdf1); + xdl_free_classifier(&cf); + return -1; + } + + if (XDF_DIFF_ALG(xpp->flags) != XDF_HISTOGRAM_DIFF) + xdl_free_classifier(&cf); + + return 0; +} + + +void xdl_free_env(xdfenv_t *xe) { + + xdl_free_ctx(&xe->xdf2); + xdl_free_ctx(&xe->xdf1); +} + + +static int xdl_clean_mmatch(char const *dis, long i, long s, long e) { + long r, rdis0, rpdis0, rdis1, rpdis1; + + /* + * Limits the window the is examined during the similar-lines + * scan. The loops below stops when dis[i - r] == 1 (line that + * has no match), but there are corner cases where the loop + * proceed all the way to the extremities by causing huge + * performance penalties in case of big files. + */ + if (i - s > XDL_SIMSCAN_WINDOW) + s = i - XDL_SIMSCAN_WINDOW; + if (e - i > XDL_SIMSCAN_WINDOW) + e = i + XDL_SIMSCAN_WINDOW; + + /* + * Scans the lines before 'i' to find a run of lines that either + * have no match (dis[j] == 0) or have multiple matches (dis[j] > 1). + * Note that we always call this function with dis[i] > 1, so the + * current line (i) is already a multimatch line. + */ + for (r = 1, rdis0 = 0, rpdis0 = 1; (i - r) >= s; r++) { + if (!dis[i - r]) + rdis0++; + else if (dis[i - r] == 2) + rpdis0++; + else + break; + } + /* + * If the run before the line 'i' found only multimatch lines, we + * return 0 and hence we don't make the current line (i) discarded. + * We want to discard multimatch lines only when they appear in the + * middle of runs with nomatch lines (dis[j] == 0). + */ + if (rdis0 == 0) + return 0; + for (r = 1, rdis1 = 0, rpdis1 = 1; (i + r) <= e; r++) { + if (!dis[i + r]) + rdis1++; + else if (dis[i + r] == 2) + rpdis1++; + else + break; + } + /* + * If the run after the line 'i' found only multimatch lines, we + * return 0 and hence we don't make the current line (i) discarded. + */ + if (rdis1 == 0) + return 0; + rdis1 += rdis0; + rpdis1 += rpdis0; + + return rpdis1 * XDL_KPDIS_RUN < (rpdis1 + rdis1); +} + + +/* + * Try to reduce the problem complexity, discard records that have no + * matches on the other file. Also, lines that have multiple matches + * might be potentially discarded if they happear in a run of discardable. + */ +static int xdl_cleanup_records(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) { + long i, nm, nreff, mlim; + xrecord_t **recs; + xdlclass_t *rcrec; + char *dis, *dis1, *dis2; + + if (!(dis = (char *) xdl_malloc(xdf1->nrec + xdf2->nrec + 2))) { + + return -1; + } + memset(dis, 0, xdf1->nrec + xdf2->nrec + 2); + dis1 = dis; + dis2 = dis1 + xdf1->nrec + 1; + + if ((mlim = xdl_bogosqrt(xdf1->nrec)) > XDL_MAX_EQLIMIT) + mlim = XDL_MAX_EQLIMIT; + for (i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; i <= xdf1->dend; i++, recs++) { + rcrec = cf->rcrecs[(*recs)->ha]; + nm = rcrec ? rcrec->len2 : 0; + dis1[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1; + } + + if ((mlim = xdl_bogosqrt(xdf2->nrec)) > XDL_MAX_EQLIMIT) + mlim = XDL_MAX_EQLIMIT; + for (i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; i <= xdf2->dend; i++, recs++) { + rcrec = cf->rcrecs[(*recs)->ha]; + nm = rcrec ? rcrec->len1 : 0; + dis2[i] = (nm == 0) ? 0: (nm >= mlim) ? 2: 1; + } + + for (nreff = 0, i = xdf1->dstart, recs = &xdf1->recs[xdf1->dstart]; + i <= xdf1->dend; i++, recs++) { + if (dis1[i] == 1 || + (dis1[i] == 2 && !xdl_clean_mmatch(dis1, i, xdf1->dstart, xdf1->dend))) { + xdf1->rindex[nreff] = i; + xdf1->ha[nreff] = (*recs)->ha; + nreff++; + } else + xdf1->rchg[i] = 1; + } + xdf1->nreff = nreff; + + for (nreff = 0, i = xdf2->dstart, recs = &xdf2->recs[xdf2->dstart]; + i <= xdf2->dend; i++, recs++) { + if (dis2[i] == 1 || + (dis2[i] == 2 && !xdl_clean_mmatch(dis2, i, xdf2->dstart, xdf2->dend))) { + xdf2->rindex[nreff] = i; + xdf2->ha[nreff] = (*recs)->ha; + nreff++; + } else + xdf2->rchg[i] = 1; + } + xdf2->nreff = nreff; + + xdl_free(dis); + + return 0; +} + + +/* + * Early trim initial and terminal matching records. + */ +static int xdl_trim_ends(xdfile_t *xdf1, xdfile_t *xdf2) { + long i, lim; + xrecord_t **recs1, **recs2; + + recs1 = xdf1->recs; + recs2 = xdf2->recs; + for (i = 0, lim = XDL_MIN(xdf1->nrec, xdf2->nrec); i < lim; + i++, recs1++, recs2++) + if ((*recs1)->ha != (*recs2)->ha) + break; + + xdf1->dstart = xdf2->dstart = i; + + recs1 = xdf1->recs + xdf1->nrec - 1; + recs2 = xdf2->recs + xdf2->nrec - 1; + for (lim -= i, i = 0; i < lim; i++, recs1--, recs2--) + if ((*recs1)->ha != (*recs2)->ha) + break; + + xdf1->dend = xdf1->nrec - i - 1; + xdf2->dend = xdf2->nrec - i - 1; + + return 0; +} + + +static int xdl_optimize_ctxs(xdlclassifier_t *cf, xdfile_t *xdf1, xdfile_t *xdf2) { + + if (xdl_trim_ends(xdf1, xdf2) < 0 || + xdl_cleanup_records(cf, xdf1, xdf2) < 0) { + + return -1; + } + + return 0; +} diff --git a/src/nvim/xdiff/xprepare.h b/src/nvim/xdiff/xprepare.h new file mode 100644 index 0000000000..947d9fc1bb --- /dev/null +++ b/src/nvim/xdiff/xprepare.h @@ -0,0 +1,34 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#if !defined(XPREPARE_H) +#define XPREPARE_H + + + +int xdl_prepare_env(mmfile_t *mf1, mmfile_t *mf2, xpparam_t const *xpp, + xdfenv_t *xe); +void xdl_free_env(xdfenv_t *xe); + + + +#endif /* #if !defined(XPREPARE_H) */ diff --git a/src/nvim/xdiff/xtypes.h b/src/nvim/xdiff/xtypes.h new file mode 100644 index 0000000000..8442bd436e --- /dev/null +++ b/src/nvim/xdiff/xtypes.h @@ -0,0 +1,67 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#if !defined(XTYPES_H) +#define XTYPES_H + + + +typedef struct s_chanode { + struct s_chanode *next; + long icurr; +} chanode_t; + +typedef struct s_chastore { + chanode_t *head, *tail; + long isize, nsize; + chanode_t *ancur; + chanode_t *sncur; + long scurr; +} chastore_t; + +typedef struct s_xrecord { + struct s_xrecord *next; + char const *ptr; + long size; + unsigned long ha; +} xrecord_t; + +typedef struct s_xdfile { + chastore_t rcha; + long nrec; + unsigned int hbits; + xrecord_t **rhash; + long dstart, dend; + xrecord_t **recs; + char *rchg; + long *rindex; + long nreff; + unsigned long *ha; +} xdfile_t; + +typedef struct s_xdfenv { + xdfile_t xdf1, xdf2; +} xdfenv_t; + + + +#endif /* #if !defined(XTYPES_H) */ diff --git a/src/nvim/xdiff/xutils.c b/src/nvim/xdiff/xutils.c new file mode 100644 index 0000000000..25a090fb73 --- /dev/null +++ b/src/nvim/xdiff/xutils.c @@ -0,0 +1,425 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#include <limits.h> +#include <assert.h> +#include "xinclude.h" + + + + +long xdl_bogosqrt(long n) { + long i; + + /* + * Classical integer square root approximation using shifts. + */ + for (i = 1; n > 0; n >>= 2) + i <<= 1; + + return i; +} + + +int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, + xdemitcb_t *ecb) { + int i = 2; + mmbuffer_t mb[3]; + + mb[0].ptr = (char *) pre; + mb[0].size = psize; + mb[1].ptr = (char *) rec; + mb[1].size = size; + if (size > 0 && rec[size - 1] != '\n') { + mb[2].ptr = (char *) "\n\\ No newline at end of file\n"; + mb[2].size = (long)strlen(mb[2].ptr); + i++; + } + if (ecb->outf(ecb->priv, mb, i) < 0) { + + return -1; + } + + return 0; +} + +void *xdl_mmfile_first(mmfile_t *mmf, long *size) +{ + *size = mmf->size; + return mmf->ptr; +} + + +long xdl_mmfile_size(mmfile_t *mmf) +{ + return mmf->size; +} + + +int xdl_cha_init(chastore_t *cha, long isize, long icount) { + + cha->head = cha->tail = NULL; + cha->isize = isize; + cha->nsize = icount * isize; + cha->ancur = cha->sncur = NULL; + cha->scurr = 0; + + return 0; +} + + +void xdl_cha_free(chastore_t *cha) { + chanode_t *cur, *tmp; + + for (cur = cha->head; (tmp = cur) != NULL;) { + cur = cur->next; + xdl_free(tmp); + } +} + + +void *xdl_cha_alloc(chastore_t *cha) { + chanode_t *ancur; + void *data; + + if (!(ancur = cha->ancur) || ancur->icurr == cha->nsize) { + if (!(ancur = (chanode_t *) xdl_malloc(sizeof(chanode_t) + cha->nsize))) { + + return NULL; + } + ancur->icurr = 0; + ancur->next = NULL; + if (cha->tail) + cha->tail->next = ancur; + if (!cha->head) + cha->head = ancur; + cha->tail = ancur; + cha->ancur = ancur; + } + + data = (char *) ancur + sizeof(chanode_t) + ancur->icurr; + ancur->icurr += cha->isize; + + return data; +} + +long xdl_guess_lines(mmfile_t *mf, long sample) { + long nl = 0, size, tsize = 0; + char const *data, *cur, *top; + + if ((cur = data = xdl_mmfile_first(mf, &size)) != NULL) { + for (top = data + size; nl < sample && cur < top; ) { + nl++; + if (!(cur = memchr(cur, '\n', top - cur))) + cur = top; + else + cur++; + } + tsize += (long) (cur - data); + } + + if (nl && tsize) + nl = xdl_mmfile_size(mf) / (tsize / nl); + + return nl + 1; +} + +int xdl_blankline(const char *line, long size, long flags) +{ + long i; + + if (!(flags & XDF_WHITESPACE_FLAGS)) + return (size <= 1); + + for (i = 0; i < size && XDL_ISSPACE(line[i]); i++) + ; + + return (i == size); +} + +/* + * Have we eaten everything on the line, except for an optional + * CR at the very end? + */ +static int ends_with_optional_cr(const char *l, long s, long i) +{ + int complete = s && l[s-1] == '\n'; + + if (complete) + s--; + if (s == i) + return 1; + /* do not ignore CR at the end of an incomplete line */ + if (complete && s == i + 1 && l[i] == '\r') + return 1; + return 0; +} + +int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags) +{ + int i1, i2; + + if (s1 == s2 && !memcmp(l1, l2, s1)) + return 1; + if (!(flags & XDF_WHITESPACE_FLAGS)) + return 0; + + i1 = 0; + i2 = 0; + + /* + * -w matches everything that matches with -b, and -b in turn + * matches everything that matches with --ignore-space-at-eol, + * which in turn matches everything that matches with --ignore-cr-at-eol. + * + * Each flavor of ignoring needs different logic to skip whitespaces + * while we have both sides to compare. + */ + if (flags & XDF_IGNORE_WHITESPACE) { + goto skip_ws; + while (i1 < s1 && i2 < s2) { + if (l1[i1++] != l2[i2++]) + return 0; + skip_ws: + while (i1 < s1 && XDL_ISSPACE(l1[i1])) + i1++; + while (i2 < s2 && XDL_ISSPACE(l2[i2])) + i2++; + } + } else if (flags & XDF_IGNORE_WHITESPACE_CHANGE) { + while (i1 < s1 && i2 < s2) { + if (XDL_ISSPACE(l1[i1]) && XDL_ISSPACE(l2[i2])) { + /* Skip matching spaces and try again */ + while (i1 < s1 && XDL_ISSPACE(l1[i1])) + i1++; + while (i2 < s2 && XDL_ISSPACE(l2[i2])) + i2++; + continue; + } + if (l1[i1++] != l2[i2++]) + return 0; + } + } else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL) { + while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) { + i1++; + i2++; + } + } else if (flags & XDF_IGNORE_CR_AT_EOL) { + /* Find the first difference and see how the line ends */ + while (i1 < s1 && i2 < s2 && l1[i1] == l2[i2]) { + i1++; + i2++; + } + return (ends_with_optional_cr(l1, s1, i1) && + ends_with_optional_cr(l2, s2, i2)); + } + + /* + * After running out of one side, the remaining side must have + * nothing but whitespace for the lines to match. Note that + * ignore-whitespace-at-eol case may break out of the loop + * while there still are characters remaining on both lines. + */ + if (i1 < s1) { + while (i1 < s1 && XDL_ISSPACE(l1[i1])) + i1++; + if (s1 != i1) + return 0; + } + if (i2 < s2) { + while (i2 < s2 && XDL_ISSPACE(l2[i2])) + i2++; + return (s2 == i2); + } + return 1; +} + +static unsigned long xdl_hash_record_with_whitespace(char const **data, + char const *top, long flags) { + unsigned long ha = 5381; + char const *ptr = *data; + int cr_at_eol_only = (flags & XDF_WHITESPACE_FLAGS) == XDF_IGNORE_CR_AT_EOL; + + for (; ptr < top && *ptr != '\n'; ptr++) { + if (cr_at_eol_only) { + /* do not ignore CR at the end of an incomplete line */ + if (*ptr == '\r' && + (ptr + 1 < top && ptr[1] == '\n')) + continue; + } + else if (XDL_ISSPACE(*ptr)) { + const char *ptr2 = ptr; + int at_eol; + while (ptr + 1 < top && XDL_ISSPACE(ptr[1]) + && ptr[1] != '\n') + ptr++; + at_eol = (top <= ptr + 1 || ptr[1] == '\n'); + if (flags & XDF_IGNORE_WHITESPACE) + ; /* already handled */ + else if (flags & XDF_IGNORE_WHITESPACE_CHANGE + && !at_eol) { + ha += (ha << 5); + ha ^= (unsigned long) ' '; + } + else if (flags & XDF_IGNORE_WHITESPACE_AT_EOL + && !at_eol) { + while (ptr2 != ptr + 1) { + ha += (ha << 5); + ha ^= (unsigned long) *ptr2; + ptr2++; + } + } + continue; + } + ha += (ha << 5); + ha ^= (unsigned long) *ptr; + } + *data = ptr < top ? ptr + 1: ptr; + + return ha; +} + +unsigned long xdl_hash_record(char const **data, char const *top, long flags) { + unsigned long ha = 5381; + char const *ptr = *data; + + if (flags & XDF_WHITESPACE_FLAGS) + return xdl_hash_record_with_whitespace(data, top, flags); + + for (; ptr < top && *ptr != '\n'; ptr++) { + ha += (ha << 5); + ha ^= (unsigned long) *ptr; + } + *data = ptr < top ? ptr + 1: ptr; + + return ha; +} + +unsigned int xdl_hashbits(unsigned int size) { + unsigned int val = 1, bits = 0; + + for (; val < size && bits < CHAR_BIT * sizeof(unsigned int); val <<= 1, bits++); + return bits ? bits: 1; +} + + +int xdl_num_out(char *out, long val) { + char *ptr, *str = out; + char buf[32]; + + ptr = buf + sizeof(buf) - 1; + *ptr = '\0'; + if (val < 0) { + *--ptr = '-'; + val = -val; + } + for (; val && ptr > buf; val /= 10) + *--ptr = "0123456789"[val % 10]; + if (*ptr) + for (; *ptr; ptr++, str++) + *str = *ptr; + else + *str++ = '0'; + *str = '\0'; + + return str - out; +} + +int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, + const char *func, long funclen, xdemitcb_t *ecb) { + int nb = 0; + mmbuffer_t mb; + char buf[128]; + + memcpy(buf, "@@ -", 4); + nb += 4; + + nb += xdl_num_out(buf + nb, c1 ? s1: s1 - 1); + + if (c1 != 1) { + memcpy(buf + nb, ",", 1); + nb += 1; + + nb += xdl_num_out(buf + nb, c1); + } + + memcpy(buf + nb, " +", 2); + nb += 2; + + nb += xdl_num_out(buf + nb, c2 ? s2: s2 - 1); + + if (c2 != 1) { + memcpy(buf + nb, ",", 1); + nb += 1; + + nb += xdl_num_out(buf + nb, c2); + } + + memcpy(buf + nb, " @@", 3); + nb += 3; + if (func && funclen) { + buf[nb++] = ' '; + if (funclen > (long)sizeof(buf) - nb - 1) + funclen = sizeof(buf) - nb - 1; + memcpy(buf + nb, func, funclen); + nb += funclen; + } + buf[nb++] = '\n'; + + mb.ptr = buf; + mb.size = nb; + if (ecb->outf(ecb->priv, &mb, 1) < 0) + return -1; + + return 0; +} + +int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp, + int line1, int count1, int line2, int count2) +{ + /* + * This probably does not work outside Git, since + * we have a very simple mmfile structure. + * + * Note: ideally, we would reuse the prepared environment, but + * the libxdiff interface does not (yet) allow for diffing only + * ranges of lines instead of the whole files. + */ + mmfile_t subfile1, subfile2; + xdfenv_t env; + + subfile1.ptr = (char *)diff_env->xdf1.recs[line1 - 1]->ptr; + subfile1.size = diff_env->xdf1.recs[line1 + count1 - 2]->ptr + + diff_env->xdf1.recs[line1 + count1 - 2]->size - subfile1.ptr; + subfile2.ptr = (char *)diff_env->xdf2.recs[line2 - 1]->ptr; + subfile2.size = diff_env->xdf2.recs[line2 + count2 - 2]->ptr + + diff_env->xdf2.recs[line2 + count2 - 2]->size - subfile2.ptr; + if (xdl_do_diff(&subfile1, &subfile2, xpp, &env) < 0) + return -1; + + memcpy(diff_env->xdf1.rchg + line1 - 1, env.xdf1.rchg, count1); + memcpy(diff_env->xdf2.rchg + line2 - 1, env.xdf2.rchg, count2); + + xdl_free_env(&env); + + return 0; +} diff --git a/src/nvim/xdiff/xutils.h b/src/nvim/xdiff/xutils.h new file mode 100644 index 0000000000..fba7bae03c --- /dev/null +++ b/src/nvim/xdiff/xutils.h @@ -0,0 +1,47 @@ +/* + * LibXDiff by Davide Libenzi ( File Differential Library ) + * Copyright (C) 2003 Davide Libenzi + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, see + * <http://www.gnu.org/licenses/>. + * + * Davide Libenzi <davidel@xmailserver.org> + * + */ + +#if !defined(XUTILS_H) +#define XUTILS_H + + + +long xdl_bogosqrt(long n); +int xdl_emit_diffrec(char const *rec, long size, char const *pre, long psize, + xdemitcb_t *ecb); +int xdl_cha_init(chastore_t *cha, long isize, long icount); +void xdl_cha_free(chastore_t *cha); +void *xdl_cha_alloc(chastore_t *cha); +long xdl_guess_lines(mmfile_t *mf, long sample); +int xdl_blankline(const char *line, long size, long flags); +int xdl_recmatch(const char *l1, long s1, const char *l2, long s2, long flags); +unsigned long xdl_hash_record(char const **data, char const *top, long flags); +unsigned int xdl_hashbits(unsigned int size); +int xdl_num_out(char *out, long val); +int xdl_emit_hunk_hdr(long s1, long c1, long s2, long c2, + const char *func, long funclen, xdemitcb_t *ecb); +int xdl_fall_back_diff(xdfenv_t *diff_env, xpparam_t const *xpp, + int line1, int count1, int line2, int count2); + + + +#endif /* #if !defined(XUTILS_H) */ |