diff options
158 files changed, 12565 insertions, 7599 deletions
diff --git a/.gitattributes b/.gitattributes index 1deb4dea49..e09a918303 100755 --- a/.gitattributes +++ b/.gitattributes @@ -1,7 +1,6 @@ *.h linguist-language=C src/nvim/testdir/test42.in diff .github/ export-ignore -ci/ export-ignore .travis.yml export-ignore codecov.yml export-ignore .builds/ export-ignore diff --git a/.github/workflows/commitlint.config.js b/.github/workflows/commitlint.config.js deleted file mode 100644 index 5f10ffc6f4..0000000000 --- a/.github/workflows/commitlint.config.js +++ /dev/null @@ -1,35 +0,0 @@ -module.exports = { - rules: { - 'body-leading-blank': [1, 'always'], - 'body-max-line-length': [2, 'always', 100], - 'footer-leading-blank': [1, 'always'], - 'footer-max-line-length': [2, 'always', 100], - 'header-max-length': [2, 'always', 100], - 'scope-case': [2, 'always', 'lower-case'], - 'subject-case': [ - 2, - 'never', - ['sentence-case', 'start-case', 'pascal-case', 'upper-case'], - ], - 'subject-empty': [2, 'never'], - 'subject-full-stop': [2, 'never', '.'], - 'type-case': [2, 'always', 'lower-case'], - 'type-empty': [2, 'never'], - 'type-enum': [ - 2, - 'always', - [ - 'build', - 'chore', - 'ci', - 'docs', - 'feat', - 'fix', - 'perf', - 'refactor', - 'revert', - 'test', - ], - ], - }, -}; diff --git a/.github/workflows/commitlint.config_patch.js b/.github/workflows/commitlint.config_patch.js deleted file mode 100644 index ca398c45dc..0000000000 --- a/.github/workflows/commitlint.config_patch.js +++ /dev/null @@ -1,27 +0,0 @@ -module.exports = { - parserPreset: { - parserOpts: { headerPattern: /^([^\(\):]*)(?:\((.*)\))?!?:(.*)$/ } - }, - rules: { - 'body-leading-blank': [1, 'always'], - 'body-max-line-length': [2, 'always', 100], - 'footer-max-line-length': [2, 'always', 100], - 'scope-case': [2, 'always', 'lower-case'], - 'subject-case': [ - 2, - 'never', - ['sentence-case', 'start-case', 'pascal-case', 'upper-case'], - ], - 'subject-empty': [2, 'never'], - 'subject-full-stop': [2, 'never', '.'], - 'type-case': [2, 'always', 'lower-case'], - 'type-empty': [2, 'never'], - 'type-enum': [ - 2, - 'always', - [ - 'vim-patch', - ], - ], - }, -}; diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 9ae138fbd7..4c9c526946 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -9,10 +9,8 @@ jobs: - uses: actions/checkout@v2.3.1 with: fetch-depth: 0 - - run: npm install --save-dev @commitlint/cli - - run: | - if [[ "$(gh pr view ${{ github.event.pull_request.number }} --json commits --jq '.[][0].messageHeadline')" == vim-patch* ]];then - npx commitlint --from HEAD~1 --to HEAD --verbose --help-url https://github.com/neovim/neovim/blob/master/CONTRIBUTING.md#commit-messages --config .github/workflows/commitlint.config_patch.js - else - npx commitlint --from HEAD~1 --to HEAD --verbose --help-url https://github.com/neovim/neovim/blob/master/CONTRIBUTING.md#commit-messages --config .github/workflows/commitlint.config.js - fi + - uses: rhysd/action-setup-vim@v1 + with: + neovim: true + - run: gh pr checkout ${{ github.event.pull_request.number }} + - run: nvim --clean -es +"lua require('scripts.lintcommit').main({trace=true})" diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml index 67ad4c0552..c7a331c657 100644 --- a/.github/workflows/labeler.yml +++ b/.github/workflows/labeler.yml @@ -12,3 +12,19 @@ jobs: - uses: actions/labeler@main with: repo-token: "${{ secrets.GITHUB_TOKEN }}" + type-scope: + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + steps: + - uses: actions/checkout@v2.3.1 + - run: gh pr checkout ${{ github.event.pull_request.number }} + + # Extract type and try to add it as a label + - run: gh pr edit --add-label "$(echo "${{ github.event.pull_request.title }}" | sed -E 's|([[:alpha:]]+)(\(.*\))?:.*|\1|')" || true + + # Extract scope and try to add it as a label + - run: gh pr edit --add-label "$(echo "${{ github.event.pull_request.title }}" | sed -E 's|[[:alpha:]]+\((.+)\):.*|\1|')" || true diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2a565574fa..fe3540f1f6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -161,7 +161,7 @@ see potential bugs found by [PVS Studio](https://www.viva64.com/en/pvs-studio/). - Use this format for commit messages (where `{id}` is the PVS warning-id)): ``` - PVS/V{id}: {description} + fix(PVS/V{id}): {description} ``` - Search the Neovim commit history to find examples: ``` @@ -177,7 +177,7 @@ master build. To view the defects, just request access; you will be approved. - Use this format for commit messages (where `{id}` is the CID (Coverity ID); ([example](https://github.com/neovim/neovim/pull/804))): ``` - coverity/{id}: {description} + fix(coverity/{id}): {description} ``` - Search the Neovim commit history to find examples: ``` @@ -217,13 +217,21 @@ You can lint a single file (but this will _not_ exclude legacy errors): ### Style -The repo includes a `.clang-format` config file which (mostly) matches the -[style-guide]. You can use `clang-format` to format code with the `gq` -operator in Nvim: - - if !empty(findfile('.clang-format', ';')) - setlocal formatprg=clang-format\ -style=file - endif +- Style rules are (mostly) defined by `src/uncrustify.cfg` which tries to match + the [style-guide]. To use the Nvim `gq` command with `uncrustify`: + ``` + if !empty(findfile('src/uncrustify.cfg', ';')) + setlocal formatprg=uncrustify\ -q\ -l\ C\ -c\ src/uncrustify.cfg\ --no-backup + endif + ``` + The required version of `uncrustify` is specified in `uncrustify.cfg`. +- There is also `.clang-format` which has drifted from the [style-guide], but + is available for reference. To use the Nvim `gq` command with `clang-format`: + ``` + if !empty(findfile('.clang-format', ';')) + setlocal formatprg=clang-format\ -style=file + endif + ``` ### Navigate @@ -263,7 +271,7 @@ as context, use the `-W` argument as well. [1820]: https://github.com/neovim/neovim/pull/1820 [hub]: https://hub.github.com/ [conventional_commits]: https://www.conventionalcommits.org -[style-guide]: http://neovim.io/develop/style-guide.xml +[style-guide]: https://neovim.io/doc/user/dev_style.html#dev-style [ASan]: http://clang.llvm.org/docs/AddressSanitizer.html [run-tests]: https://github.com/neovim/neovim/blob/master/test/README.md#running-tests [wiki-faq]: https://github.com/neovim/neovim/wiki/FAQ @@ -278,3 +286,4 @@ as context, use the `-W` argument as well. [wiki-contribute-help]: https://github.com/neovim/neovim/wiki/contribute-%3Ahelp [pr-draft]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request [pr-ready]: https://docs.github.com/en/github/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/changing-the-stage-of-a-pull-request +[uncrustify]: https://formulae.brew.sh/formula/uncrustify @@ -199,6 +199,7 @@ The externally maintained libraries used by Neovim are: - lua-compat: MIT license - tree-sitter: MIT license - xdiff: LGPL license + - lua-cjson: MIT license ==== diff --git a/MAINTAIN.md b/MAINTAIN.md index 73578a8c5d..681ba91e3f 100644 --- a/MAINTAIN.md +++ b/MAINTAIN.md @@ -55,6 +55,28 @@ has a major bug: - The [nightly job](https://github.com/neovim/bot-ci/blob/master/ci/nightly.sh) will update the release assets based on the `stable` tag. +Third-party dependencies +-------------- + +These "bundled" dependencies can be updated by bumping their versions in `third-party/CMakeLists.txt`: + - [Lua](https://www.lua.org/download.html) + - [LuaJIT](https://github.com/LuaJIT/LuaJIT) + - [Luv](https://github.com/luvit/luv) + - [libtermkey](https://github.com/neovim/libtermkey) + - [libuv](https://github.com/libuv/libuv) + - [libvterm](http://www.leonerd.org.uk/code/libvterm/) + - [lua-compat](https://github.com/keplerproject/lua-compat-5.3) + - [tree-sitter](https://github.com/tree-sitter/tree-sitter) + +These dependencies are "vendored" (inlined), we need to update the sources manually: + - [libmpack](https://github.com/libmpack/libmpack) + - [xdiff](https://github.com/git/git/tree/master/xdiff) + - [lua-cjson](https://github.com/openresty/lua-cjson) + - [Klib](https://github.com/attractivechaos/klib) + +We also maintain some forks, particularly for Windows, if we are waiting on upstream changes: +https://github.com/neovim/neovim/wiki/Deps + See also -------- diff --git a/runtime/doc/diagnostic.txt b/runtime/doc/diagnostic.txt index 68e4353d5b..276571d042 100644 --- a/runtime/doc/diagnostic.txt +++ b/runtime/doc/diagnostic.txt @@ -187,6 +187,11 @@ can be customized using the following: > sign define DiagnosticSignInfo text=I texthl=DiagnosticSignInfo linehl= numhl= sign define DiagnosticSignHint text=H texthl=DiagnosticSignHint linehl= numhl= +When the "severity_sort" option is set (see |vim.diagnostic.config()|) the +priority of each sign depends on the severity of the associated diagnostic. +Otherwise, all signs have the same priority (the value of the "priority" +option in the "signs" table of |vim.diagnostic.config()| or 10 if unset). + ============================================================================== EVENTS *diagnostic-events* @@ -196,7 +201,52 @@ DiagnosticsChanged After diagnostics have changed. Example: > autocmd User DiagnosticsChanged lua vim.diagnostic.setqflist({open = false }) < - +============================================================================== +CUSTOMIZATION *diagnostic-config* + +If you need more customization over the way diagnostics are displayed than the +built-in configuration options provide, you can override the display handler +explicitly. For example, use the following to only show a sign for the highest +severity diagnostic on a given line: > + + -- Disable the default signs handler + vim.diagnostic.config({signs = false}) + + -- Create a namespace. This won't be used to add any diagnostics, + -- only to display them. + local ns = vim.api.nvim_create_namespace("my_namespace") + + -- Create a reference to the original function + local orig_show = vim.diagnostic.show + + local function set_signs(bufnr) + -- Get all diagnostics from the current buffer + local diagnostics = vim.diagnostic.get(bufnr) + + -- Find the "worst" diagnostic per line + local max_severity_per_line = {} + for _, d in pairs(diagnostics) do + local m = max_severity_per_line[d.lnum] + if not m or d.severity < m.severity then + max_severity_per_line[d.lnum] = d + end + end + + -- Show the filtered diagnostics using the custom namespace. Use the + -- reference to the original function to avoid a loop. + local filtered_diagnostics = vim.tbl_values(max_severity_per_line) + orig_show(ns, bufnr, filtered_diagnostics, { + virtual_text=false, + underline=false, + signs=true + }) + end + + function vim.diagnostic.show(namespace, bufnr, ...) + orig_show(namespace, bufnr, ...) + set_signs(bufnr) + end +< ============================================================================== Lua module: vim.diagnostic *diagnostic-api* @@ -248,6 +298,12 @@ config({opts}, {namespace}) *vim.diagnostic.config()* • severity: Only show signs for diagnostics matching the given severity |diagnostic-severity| + • priority: (number, default 10) Base + priority to use for signs. When + {severity_sort} is used, the priority of + a sign is adjusted based on its severity. + Otherwise, all signs use the same + priority. • update_in_insert: (default false) Update diagnostics in Insert mode (if false, diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index ac02bdae32..d9ecdb40de 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -2431,6 +2431,7 @@ getloclist({nr}) List list of location list items getloclist({nr}, {what}) Dict get specific location list properties getmarklist([{buf}]) List list of global/local marks getmatches([{win}]) List list of current matches +getmousepos() Dict last known mouse position getpid() Number process ID of Vim getpos({expr}) List position of cursor, mark, etc. getqflist() List list of quickfix items @@ -3418,6 +3419,8 @@ complete_info([{what}]) *complete_info()* "" Not in completion mode "keyword" Keyword completion |i_CTRL-X_CTRL-N| "ctrl_x" Just pressed CTRL-X |i_CTRL-X| + "scroll" Scrolling with |i_CTRL-X_CTRL-E| or + |i_CTRL-X_CTRL-Y| "whole_line" Whole lines |i_CTRL-X_CTRL-L| "files" File names |i_CTRL-X_CTRL-F| "tags" Tags |i_CTRL-X_CTRL-]| @@ -4709,7 +4712,8 @@ getchar([expr]) *getchar()* When the user clicks a mouse button, the mouse event will be returned. The position can then be found in |v:mouse_col|, |v:mouse_lnum|, |v:mouse_winid| and |v:mouse_win|. - Mouse move events will be ignored. + |getmousepos()| can also be used. Mouse move events will be + ignored. This example positions the mouse as it would normally happen: > let c = getchar() if c == "\<LeftMouse>" && v:mouse_win > 0 @@ -5099,6 +5103,35 @@ getmatches([{win}]) *getmatches()* 'pattern': 'FIXME', 'priority': 10, 'id': 2}] > :unlet m < +getmousepos() *getmousepos()* + Returns a Dictionary with the last known position of the + mouse. This can be used in a mapping for a mouse click. The + items are: + screenrow screen row + screencol screen column + winid Window ID of the click + winrow row inside "winid" + wincol column inside "winid" + line text line inside "winid" + column text column inside "winid" + All numbers are 1-based. + + If not over a window, e.g. when in the command line, then only + "screenrow" and "screencol" are valid, the others are zero. + + When on the status line below a window or the vertical + separater right of a window, the "line" and "column" values + are zero. + + When the position is after the text then "column" is the + length of the text in bytes plus one. + + If the mouse is over a focusable floating window then that + window is used. + + When using |getchar()| the Vim variables |v:mouse_lnum|, + |v:mouse_col| and |v:mouse_winid| also provide these values. + *getpid()* getpid() Return a Number which is the process ID of the Vim process. This is a unique number, until Vim exits. @@ -6832,29 +6865,31 @@ mode([expr]) Return a string that indicates the current mode. niR Normal using |i_CTRL-O| in |Replace-mode| niV Normal using |i_CTRL-O| in |Virtual-Replace-mode| v Visual by character + vs Visual by character using |v_CTRL-O| in Select mode V Visual by line + Vs Visual by line using |v_CTRL-O| in Select mode CTRL-V Visual blockwise + CTRL-Vs Visual blockwise using |v_CTRL-O| in Select mode s Select by character S Select by line CTRL-S Select blockwise - vs Visual by character using |v_CTRL-O| from - Select mode - Vs Visual by line using |v_CTRL-O| from Select mode - CTRL-Vs Visual blockwise using |v_CTRL-O| from Select mode i Insert ic Insert mode completion |compl-generic| ix Insert mode |i_CTRL-X| completion R Replace |R| Rc Replace mode completion |compl-generic| - Rv Virtual Replace |gR| Rx Replace mode |i_CTRL-X| completion + Rv Virtual Replace |gR| + Rvc Virtual Replace mode completion |compl-generic| + Rvx Virtual Replace mode |i_CTRL-X| completion c Command-line editing cv Vim Ex mode |Q| or |gQ| r Hit-enter prompt rm The -- more -- prompt - r? |:confirm| query of some sort + r? A |:confirm| query of some sort ! Shell or external command is executing t Terminal mode: keys go to the job + This is useful in the 'statusline' option or when used with |remote_expr()| In most other places it always returns "c" or "n". diff --git a/runtime/doc/helphelp.txt b/runtime/doc/helphelp.txt index 4a94701b2e..aaa2a35fe1 100644 --- a/runtime/doc/helphelp.txt +++ b/runtime/doc/helphelp.txt @@ -389,17 +389,5 @@ highlighting. So do these: You can find the details in $VIMRUNTIME/syntax/help.vim - *inclusion* -Vim is for everybody, no matter race, gender or anything. Some people make a -big deal about using "he" or "his" when referring to the user, thinking it -means we assume the user is male. That is not the case, it's just a habit of -writing help text, which quite often is many years old. Also, a lot of the -text is written by contributors for whom English is not their first language. -We do not make any assumptions about the gender of the user, no matter how the -text is phrased. Some people have suggested using "they", but that is not -regular English. We do not want to spend much time on this discussion. The -goal is that the reader understands how Vim works, the exact wording is -secondary. - vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt index c434f6db66..5b2a8d68b4 100644 --- a/runtime/doc/lsp.txt +++ b/runtime/doc/lsp.txt @@ -869,12 +869,17 @@ clear_references() *vim.lsp.buf.clear_references()* Removes document highlights from current buffer. code_action({context}) *vim.lsp.buf.code_action()* - Selects a code action from the input list that is available at - the current cursor position. + Selects a code action available at the current cursor + position. Parameters: ~ - {context} (table, optional) Valid `CodeActionContext` - object + {context} table|nil `CodeActionContext` of the LSP specification: + • diagnostics: (table|nil) LSP`Diagnostic[]` . Inferred from the current position if not + provided. + • only: (string|nil) LSP `CodeActionKind` used + to filter the code actions. Most language + servers support values like `refactor` or + `quickfix` . See also: ~ https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction @@ -1015,8 +1020,13 @@ range_code_action({context}, {start_pos}, {end_pos}) Performs |vim.lsp.buf.code_action()| for a given range. Parameters: ~ - {context} (table, optional) Valid `CodeActionContext` - object + {context} table|nil `CodeActionContext` of the LSP specification: + • diagnostics: (table|nil) LSP`Diagnostic[]` . Inferred from the current position if not + provided. + • only: (string|nil) LSP `CodeActionKind` + used to filter the code actions. Most + language servers support values like + `refactor` or `quickfix` . {start_pos} ({number, number}, optional) mark-indexed position. Defaults to the start of the last visual selection. diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt index 4ceb123ffc..f6daa70b4c 100644 --- a/runtime/doc/lua.txt +++ b/runtime/doc/lua.txt @@ -1650,4 +1650,25 @@ uri_to_fname({uri}) *vim.uri_to_fname()* Return: ~ Filename + +============================================================================== +Lua module: ui *lua-ui* + +select({items}, {opts}, {on_choice}) *vim.ui.select()* + Prompts the user to pick a single item from a collection of + entries + + Parameters: ~ + {items} table Arbitrary items + {opts} table Additional options + • prompt (string|nil) Text of the prompt. + Defaults to `Select one of:` + • format_item (function item -> text) + Function to format an individual item from + `items` . Defaults to `tostring` . + {on_choice} function ((item|nil, idx|nil) -> ()) Called + once the user made a choice. `idx` is the + 1-based index of `item` within `item` . `nil` + if the user aborted the dialog. + vim:tw=78:ts=8:ft=help:norl: diff --git a/runtime/filetype.vim b/runtime/filetype.vim index bc3965d4b6..322215e1de 100644 --- a/runtime/filetype.vim +++ b/runtime/filetype.vim @@ -872,6 +872,9 @@ au BufNewFile,BufRead *.json-patch setf json " Jupyter Notebook is also json au BufNewFile,BufRead *.ipynb setf json +" Other files that look like json +au BufNewFile,BufRead .babelrc,.eslintrc,.prettierrc,.firebaserc setf json + " JSONC au BufNewFile,BufRead *.jsonc setf jsonc @@ -1136,6 +1139,9 @@ au BufNewFile,BufRead Neomuttrc setf neomuttrc " Netrc au BufNewFile,BufRead .netrc setf netrc +" Nginx +au BufNewFile,BufRead *.nginx,nginx*.conf,*nginx.conf,*/etc/nginx/*,*/usr/local/nginx/conf/*,*/nginx/*.conf setf nginx + " Ninja file au BufNewFile,BufRead *.ninja setf ninja diff --git a/runtime/lua/vim/diagnostic.lua b/runtime/lua/vim/diagnostic.lua index 3f41ee5df8..c7c8c1878e 100644 --- a/runtime/lua/vim/diagnostic.lua +++ b/runtime/lua/vim/diagnostic.lua @@ -556,6 +556,9 @@ end --- - signs: (default true) Use signs for diagnostics. Options: --- * severity: Only show signs for diagnostics matching the given severity --- |diagnostic-severity| +--- * priority: (number, default 10) Base priority to use for signs. When +--- {severity_sort} is used, the priority of a sign is adjusted based on +--- its severity. Otherwise, all signs use the same priority. --- - update_in_insert: (default false) Update diagnostics in Insert mode (if false, --- diagnostics are updated on InsertLeave) --- - severity_sort: (default false) Sort diagnostics by severity. This affects the order in @@ -617,23 +620,22 @@ function M.set(namespace, bufnr, diagnostics, opts) } if vim.tbl_isempty(diagnostics) then - return M.reset(namespace, bufnr) - end - - if not diagnostic_cleanup[bufnr][namespace] then - diagnostic_cleanup[bufnr][namespace] = true - - -- Clean up our data when the buffer unloads. - vim.api.nvim_buf_attach(bufnr, false, { - on_detach = function(_, b) - clear_diagnostic_cache(b, namespace) - diagnostic_cleanup[b][namespace] = nil - end - }) + clear_diagnostic_cache(namespace, bufnr) + else + if not diagnostic_cleanup[bufnr][namespace] then + diagnostic_cleanup[bufnr][namespace] = true + + -- Clean up our data when the buffer unloads. + vim.api.nvim_buf_attach(bufnr, false, { + on_detach = function(_, b) + clear_diagnostic_cache(b, namespace) + diagnostic_cleanup[b][namespace] = nil + end + }) + end + set_diagnostic_cache(namespace, bufnr, diagnostics) end - set_diagnostic_cache(namespace, bufnr, diagnostics) - if vim.api.nvim_buf_is_loaded(bufnr) then M.show(namespace, bufnr, diagnostics, opts) elseif opts then @@ -643,6 +645,13 @@ function M.set(namespace, bufnr, diagnostics, opts) vim.api.nvim_command("doautocmd <nomodeline> User DiagnosticsChanged") end +--- Get current diagnostic namespaces. +--- +---@return table A list of active diagnostic namespaces |vim.diagnostic|. +function M.get_namespaces() + return vim.deepcopy(all_namespaces) +end + --- Get current diagnostics. --- ---@param bufnr number|nil Buffer number to get diagnostics from. Use 0 for @@ -806,16 +815,35 @@ function M._set_signs(namespace, bufnr, diagnostics, opts) } bufnr = get_bufnr(bufnr) - opts = get_resolved_options({ signs = opts }, namespace, bufnr).signs + opts = get_resolved_options({ signs = opts }, namespace, bufnr) - if opts and opts.severity then - diagnostics = filter_by_severity(opts.severity, diagnostics) + if opts.signs and opts.signs.severity then + diagnostics = filter_by_severity(opts.signs.severity, diagnostics) end local ns = get_namespace(namespace) define_default_signs() + -- 10 is the default sign priority when none is explicitly specified + local priority = opts.signs and opts.signs.priority or 10 + local get_priority + if opts.severity_sort then + if type(opts.severity_sort) == "table" and opts.severity_sort.reverse then + get_priority = function(severity) + return priority + (severity - vim.diagnostic.severity.ERROR) + end + else + get_priority = function(severity) + return priority + (vim.diagnostic.severity.HINT - severity) + end + end + else + get_priority = function() + return priority + end + end + for _, diagnostic in ipairs(diagnostics) do vim.fn.sign_place( 0, @@ -823,7 +851,7 @@ function M._set_signs(namespace, bufnr, diagnostics, opts) sign_highlight_map[diagnostic.severity], bufnr, { - priority = opts and opts.priority, + priority = get_priority(diagnostic.severity), lnum = diagnostic.lnum + 1 } ) diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua index ae9a7ab513..c7a88a0993 100644 --- a/runtime/lua/vim/lsp.lua +++ b/runtime/lua/vim/lsp.lua @@ -830,7 +830,7 @@ function lsp.start_client(config) rpc.request('initialize', initialize_params, function(init_err, result) assert(not init_err, tostring(init_err)) assert(result, "server sent empty result") - rpc.notify('initialized', {[vim.type_idx]=vim.types.dictionary}) + rpc.notify('initialized', vim.empty_dict()) client.initialized = true uninitialized_clients[client_id] = nil client.workspaceFolders = initialize_params.workspaceFolders diff --git a/runtime/lua/vim/lsp/buf.lua b/runtime/lua/vim/lsp/buf.lua index 054f7aee04..245f29943e 100644 --- a/runtime/lua/vim/lsp/buf.lua +++ b/runtime/lua/vim/lsp/buf.lua @@ -289,7 +289,6 @@ function M.references(context) params.context = context or { includeDeclaration = true; } - params[vim.type_idx] = vim.types.dictionary request('textDocument/references', params) end @@ -451,6 +450,93 @@ function M.clear_references() util.buf_clear_references() end + +---@private +-- +--- This is not public because the main extension point is +--- vim.ui.select which can be overridden independently. +--- +--- Can't call/use vim.lsp.handlers['textDocument/codeAction'] because it expects +--- `(err, CodeAction[] | Command[], ctx)`, but we want to aggregate the results +--- from multiple clients to have 1 single UI prompt for the user, yet we still +--- need to be able to link a `CodeAction|Command` to the right client for +--- `codeAction/resolve` +local function on_code_action_results(results, ctx) + local action_tuples = {} + for client_id, result in pairs(results) do + for _, action in pairs(result.result or {}) do + table.insert(action_tuples, { client_id, action }) + end + end + if #action_tuples == 0 then + vim.notify('No code actions available', vim.log.levels.INFO) + return + end + + ---@private + local function apply_action(action, client) + if action.edit then + util.apply_workspace_edit(action.edit) + end + if action.command then + local command = type(action.command) == 'table' and action.command or action + local fn = vim.lsp.commands[command.command] + if fn then + local enriched_ctx = vim.deepcopy(ctx) + enriched_ctx.client_id = client.id + fn(command, ctx) + else + M.execute_command(command) + end + end + end + + ---@private + local function on_user_choice(action_tuple) + if not action_tuple then + return + end + -- textDocument/codeAction can return either Command[] or CodeAction[] + -- + -- CodeAction + -- ... + -- edit?: WorkspaceEdit -- <- must be applied before command + -- command?: Command + -- + -- Command: + -- title: string + -- command: string + -- arguments?: any[] + -- + local client = vim.lsp.get_client_by_id(action_tuple[1]) + local action = action_tuple[2] + if not action.edit + and client + and type(client.resolved_capabilities.code_action) == 'table' + and client.resolved_capabilities.code_action.resolveProvider then + + client.request('codeAction/resolve', action, function(err, resolved_action) + if err then + vim.notify(err.code .. ': ' .. err.message, vim.log.levels.ERROR) + return + end + apply_action(resolved_action, client) + end) + else + apply_action(action, client) + end + end + + vim.ui.select(action_tuples, { + prompt = 'Code actions:', + format_item = function(action_tuple) + local title = action_tuple[2].title:gsub('\r\n', '\\r\\n') + return title:gsub('\n', '\\n') + end, + }, on_user_choice) +end + + --- Requests code actions from all clients and calls the handler exactly once --- with all aggregated results ---@private @@ -458,22 +544,28 @@ local function code_action_request(params) local bufnr = vim.api.nvim_get_current_buf() local method = 'textDocument/codeAction' vim.lsp.buf_request_all(bufnr, method, params, function(results) - local actions = {} - for _, r in pairs(results) do - vim.list_extend(actions, r.result or {}) - end - vim.lsp.handlers[method](nil, actions, {bufnr=bufnr, method=method}) + on_code_action_results(results, { bufnr = bufnr, method = method, params = params }) end) end ---- Selects a code action from the input list that is available at the current +--- Selects a code action available at the current --- cursor position. --- ----@param context: (table, optional) Valid `CodeActionContext` object +---@param context table|nil `CodeActionContext` of the LSP specification: +--- - diagnostics: (table|nil) +--- LSP `Diagnostic[]`. Inferred from the current +--- position if not provided. +--- - only: (string|nil) +--- LSP `CodeActionKind` used to filter the code actions. +--- Most language servers support values like `refactor` +--- or `quickfix`. ---@see https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction function M.code_action(context) validate { context = { context, 't', true } } - context = context or { diagnostics = vim.lsp.diagnostic.get_line_diagnostics() } + context = context or {} + if not context.diagnostics then + context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() + end local params = util.make_range_params() params.context = context code_action_request(params) @@ -481,14 +573,25 @@ end --- Performs |vim.lsp.buf.code_action()| for a given range. --- ----@param context: (table, optional) Valid `CodeActionContext` object +--- +---@param context table|nil `CodeActionContext` of the LSP specification: +--- - diagnostics: (table|nil) +--- LSP `Diagnostic[]`. Inferred from the current +--- position if not provided. +--- - only: (string|nil) +--- LSP `CodeActionKind` used to filter the code actions. +--- Most language servers support values like `refactor` +--- or `quickfix`. ---@param start_pos ({number, number}, optional) mark-indexed position. ---Defaults to the start of the last visual selection. ---@param end_pos ({number, number}, optional) mark-indexed position. ---Defaults to the end of the last visual selection. function M.range_code_action(context, start_pos, end_pos) validate { context = { context, 't', true } } - context = context or { diagnostics = vim.lsp.diagnostic.get_line_diagnostics() } + context = context or {} + if not context.diagnostics then + context.diagnostics = vim.lsp.diagnostic.get_line_diagnostics() + end local params = util.make_given_range_params(start_pos, end_pos) params.context = context code_action_request(params) diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua index 9cedb2f1db..20b203fe99 100644 --- a/runtime/lua/vim/lsp/codelens.lua +++ b/runtime/lua/vim/lsp/codelens.lua @@ -31,10 +31,24 @@ local function execute_lens(lens, bufnr, client_id) local line = lens.range.start.line api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1) + local command = lens.command + local fn = vim.lsp.commands[command.command] + if fn then + fn(command, { bufnr = bufnr, client_id = client_id }) + return + end -- Need to use the client that returned the lens → must not use buf_request local client = vim.lsp.get_client_by_id(client_id) assert(client, 'Client is required to execute lens, client_id=' .. client_id) - client.request('workspace/executeCommand', lens.command, function(...) + local command_provider = client.server_capabilities.executeCommandProvider + local commands = type(command_provider) == 'table' and command_provider.commands or {} + if not vim.tbl_contains(commands, command.command) then + vim.notify(string.format( + "Language server does not support command `%s`. This command may require a client extension.", command.command), + vim.log.levels.WARN) + return + end + client.request('workspace/executeCommand', command, function(...) local result = vim.lsp.handlers['workspace/executeCommand'](...) M.refresh() return result diff --git a/runtime/lua/vim/lsp/handlers.lua b/runtime/lua/vim/lsp/handlers.lua index 624f8b5462..eff27807be 100644 --- a/runtime/lua/vim/lsp/handlers.lua +++ b/runtime/lua/vim/lsp/handlers.lua @@ -3,7 +3,6 @@ local protocol = require 'vim.lsp.protocol' local util = require 'vim.lsp.util' local vim = vim local api = vim.api -local buf = require 'vim.lsp.buf' local M = {} @@ -109,51 +108,6 @@ M['client/registerCapability'] = function(_, _, ctx) return vim.NIL end ---see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_codeAction -M['textDocument/codeAction'] = function(_, result, ctx) - if result == nil or vim.tbl_isempty(result) then - print("No code actions available") - return - end - - local option_strings = {"Code actions:"} - for i, action in ipairs(result) do - local title = action.title:gsub('\r\n', '\\r\\n') - title = title:gsub('\n', '\\n') - table.insert(option_strings, string.format("%d. %s", i, title)) - end - - local choice = vim.fn.inputlist(option_strings) - if choice < 1 or choice > #result then - return - end - local action = result[choice] - -- textDocument/codeAction can return either Command[] or CodeAction[] - -- - -- CodeAction - -- ... - -- edit?: WorkspaceEdit -- <- must be applied before command - -- command?: Command - -- - -- Command: - -- title: string - -- command: string - -- arguments?: any[] - -- - if action.edit then - util.apply_workspace_edit(action.edit) - end - if action.command then - local command = type(action.command) == 'table' and action.command or action - local fn = vim.lsp.commands[command.command] - if fn then - fn(command, ctx) - else - buf.execute_command(command) - end - end -end - --see: https://microsoft.github.io/language-server-protocol/specifications/specification-current/#workspace_applyEdit M['workspace/applyEdit'] = function(_, workspace_edit) if not workspace_edit then return end diff --git a/runtime/lua/vim/lsp/protocol.lua b/runtime/lua/vim/lsp/protocol.lua index 27703b4503..b3aa8b934f 100644 --- a/runtime/lua/vim/lsp/protocol.lua +++ b/runtime/lua/vim/lsp/protocol.lua @@ -645,6 +645,10 @@ function protocol.make_client_capabilities() end)(); }; }; + dataSupport = true; + resolveSupport = { + properties = { 'edit', } + }; }; completion = { dynamicRegistration = false; diff --git a/runtime/lua/vim/lsp/rpc.lua b/runtime/lua/vim/lsp/rpc.lua index 7f31bbdf75..255eb65dfe 100644 --- a/runtime/lua/vim/lsp/rpc.lua +++ b/runtime/lua/vim/lsp/rpc.lua @@ -4,34 +4,6 @@ local log = require('vim.lsp.log') local protocol = require('vim.lsp.protocol') local validate, schedule, schedule_wrap = vim.validate, vim.schedule, vim.schedule_wrap --- TODO replace with a better implementation. ----@private ---- Encodes to JSON. ---- ----@param data (table) Data to encode ----@returns (string) Encoded object -local function json_encode(data) - local status, result = pcall(vim.fn.json_encode, data) - if status then - return result - else - return nil, result - end -end ----@private ---- Decodes from JSON. ---- ----@param data (string) Data to decode ----@returns (table) Decoded JSON object -local function json_decode(data) - local status, result = pcall(vim.fn.json_decode, data) - if status then - return result - else - return nil, result - end -end - ---@private --- Checks whether a given path exists and is a directory. ---@param filename (string) path to check @@ -389,16 +361,13 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) --- Encodes {payload} into a JSON-RPC message and sends it to the remote --- process. --- - ---@param payload (table) Converted into a JSON string, see |json_encode()| + ---@param payload table ---@returns true if the payload could be scheduled, false if the main event-loop is in the process of closing. local function encode_and_send(payload) local _ = log.debug() and log.debug("rpc.send", payload) if handle == nil or handle:is_closing() then return false end - -- TODO(ashkan) remove this once we have a Lua json_encode - schedule(function() - local encoded = assert(json_encode(payload)) - stdin:write(format_message_with_content_length(encoded)) - end) + local encoded = vim.json.encode(payload) + stdin:write(format_message_with_content_length(encoded)) return true end @@ -488,14 +457,15 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) ---@private local function handle_body(body) - local decoded, err = json_decode(body) - if not decoded then - -- on_error(client_errors.INVALID_SERVER_JSON, err) + local ok, decoded = pcall(vim.json.decode, body) + if not ok then + on_error(client_errors.INVALID_SERVER_JSON, decoded) return end local _ = log.debug() and log.debug("rpc.receive", decoded) if type(decoded.method) == 'string' and decoded.id then + local err -- Server Request decoded.params = convert_NIL(decoded.params) -- Schedule here so that the users functions don't trigger an error and @@ -582,8 +552,6 @@ local function start(cmd, cmd_args, dispatchers, extra_spawn_params) on_error(client_errors.INVALID_SERVER_MESSAGE, decoded) end end - -- TODO(ashkan) remove this once we have a Lua json_decode - handle_body = schedule_wrap(handle_body) local request_parser = coroutine.wrap(request_parser_loop) request_parser() diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua index e95f170427..fca956fb57 100644 --- a/runtime/lua/vim/lsp/util.lua +++ b/runtime/lua/vim/lsp/util.lua @@ -951,6 +951,11 @@ end ---@param width (number) window width (in character cells) ---@param height (number) window height (in character cells) ---@param opts (table, optional) +--- - offset_x (number) offset to add to `col` +--- - offset_y (number) offset to add to `row` +--- - border (string or table) override `border` +--- - focusable (string or table) override `focusable` +--- - zindex (string or table) override `zindex`, defaults to 50 ---@returns (table) Options function M.make_floating_popup_options(width, height, opts) validate { @@ -975,7 +980,7 @@ function M.make_floating_popup_options(width, height, opts) else anchor = anchor..'S' height = math.min(lines_above, height) - row = -get_border_size(opts).height + row = 0 end if vim.fn.wincol() + width + (opts.offset_x or 0) <= api.nvim_get_option('columns') then @@ -1124,8 +1129,6 @@ end --- - wrap_at character to wrap at for computing height --- - max_width maximal width of floating window --- - max_height maximal height of floating window ---- - pad_left number of columns to pad contents at left ---- - pad_right number of columns to pad contents at right --- - pad_top number of lines to pad contents at top --- - pad_bottom number of lines to pad contents at bottom --- - separator insert separator after code block @@ -1376,8 +1379,6 @@ end --- - wrap_at character to wrap at for computing height when wrap is enabled --- - max_width maximal width of floating window --- - max_height maximal height of floating window ---- - pad_left number of columns to pad contents at left ---- - pad_right number of columns to pad contents at right --- - pad_top number of lines to pad contents at top --- - pad_bottom number of lines to pad contents at bottom --- - focus_id if a popup with this id is opened, then focus it diff --git a/runtime/lua/vim/ui.lua b/runtime/lua/vim/ui.lua new file mode 100644 index 0000000000..5eab20fc54 --- /dev/null +++ b/runtime/lua/vim/ui.lua @@ -0,0 +1,36 @@ +local M = {} + +--- Prompts the user to pick a single item from a collection of entries +--- +---@param items table Arbitrary items +---@param opts table Additional options +--- - prompt (string|nil) +--- Text of the prompt. Defaults to `Select one of:` +--- - format_item (function item -> text) +--- Function to format an +--- individual item from `items`. Defaults to `tostring`. +---@param on_choice function ((item|nil, idx|nil) -> ()) +--- Called once the user made a choice. +--- `idx` is the 1-based index of `item` within `item`. +--- `nil` if the user aborted the dialog. +function M.select(items, opts, on_choice) + vim.validate { + items = { items, 'table', false }, + on_choice = { on_choice, 'function', false }, + } + opts = opts or {} + local choices = {opts.prompt or 'Select one of:'} + local format_item = opts.format_item or tostring + for i, item in pairs(items) do + table.insert(choices, string.format('%d: %s', i, format_item(item))) + end + local choice = vim.fn.inputlist(choices) + if choice < 1 or choice > #items then + on_choice(nil, nil) + else + on_choice(items[choice], choice) + end +end + + +return M diff --git a/scripts/finddeclarations.pl b/scripts/finddeclarations.pl deleted file mode 100755 index 1b1a57b9b7..0000000000 --- a/scripts/finddeclarations.pl +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/perl - -use strict; -use warnings; - -if ($ARGV[0] eq '--help') { - print << "EOF"; -Usage: - - $0 definitions.c -EOF - exit; -} - -my ($cfname, $sfname, $gfname, $cpp) = @ARGV; - -my $F; - -open $F, "<", $cfname; - -my $text = join "", <$F>; - -close $F; - -my $s = qr/(?>\s*)/aso; -my $w = qr/(?>\w+)/aso; -my $argname = qr/$w(?:\[(?>\w+)\])?/aso; -my $type_regex = qr/(?:$w$s\**$s)+/aso; -my $arg_regex = qr/(?:$type_regex$s$argname)/aso; - -while ($text =~ / - (?<=\n) # Definition starts at the start of line - $type_regex # Return type - $s$w # Function name - $s\($s - (?: - $arg_regex(?:$s,$s$arg_regex)*+ - ($s,$s\.\.\.)? # varargs function - |void - )? - $s\) - (?:$s FUNC_ATTR_$w(?:\((?>[^)]*)\))?)*+ # Optional attributes - (?=$s;) # Ending semicolon - /axsogp) { - my $match = "${^MATCH}"; - my $s = "${^PREMATCH}"; - $s =~ s/[^\n]++//g; - my $line = 1 + length $s; - print "${cfname}:${line}: $match\n"; -} diff --git a/scripts/gen_vimdoc.py b/scripts/gen_vimdoc.py index 64ed8d61f6..36e01153f1 100755 --- a/scripts/gen_vimdoc.py +++ b/scripts/gen_vimdoc.py @@ -123,11 +123,13 @@ CONFIG = { 'vim.lua', 'shared.lua', 'uri.lua', + 'ui.lua', ], 'files': ' '.join([ os.path.join(base_dir, 'src/nvim/lua/vim.lua'), os.path.join(base_dir, 'runtime/lua/vim/shared.lua'), os.path.join(base_dir, 'runtime/lua/vim/uri.lua'), + os.path.join(base_dir, 'runtime/lua/vim/ui.lua'), ]), 'file_patterns': '*.lua', 'fn_name_prefix': '', @@ -141,6 +143,7 @@ CONFIG = { # `shared` functions are exposed on the `vim` module. 'shared': 'vim', 'uri': 'vim', + 'ui': 'vim.ui', }, 'append_only': [ 'shared.lua', diff --git a/scripts/lintcommit.lua b/scripts/lintcommit.lua index 11ad8eb9ef..98f9da246c 100644 --- a/scripts/lintcommit.lua +++ b/scripts/lintcommit.lua @@ -91,7 +91,7 @@ local function validate_commit(commit_message) -- Check that description doesn't end with a period if vim.endswith(after_colon, ".") then - return [[Description ends with a period (\".\").]] + return [[Description ends with a period (".").]] end -- Check that description has exactly one whitespace after colon, followed by diff --git a/scripts/release.sh b/scripts/release.sh index 4ec959d697..380503662d 100755 --- a/scripts/release.sh +++ b/scripts/release.sh @@ -80,8 +80,8 @@ _do_release_commit() { _do_bump_commit() { $__sed -i.bk 's/(NVIM_VERSION_PRERELEASE) ""/\1 "-dev"/' CMakeLists.txt $__sed -i.bk 's/set\((NVIM_VERSION_PATCH) [[:digit:]]/set(\1 ?/' CMakeLists.txt - rm CMakeLists.txt.bk - rm runtime/nvim.appdata.xml.bk + rm -f CMakeLists.txt.bk + rm -f runtime/nvim.appdata.xml.bk nvim +'/NVIM_VERSION' +1new +'exe "norm! iUpdate version numbers!!!"' \ -O CMakeLists.txt diff --git a/scripts/vim-patch.sh b/scripts/vim-patch.sh index f4b817dfff..d92480abb9 100755 --- a/scripts/vim-patch.sh +++ b/scripts/vim-patch.sh @@ -125,8 +125,12 @@ commit_message() { } find_git_remote() { - git_remote=$(git remote -v \ - | awk '$2 ~ /github.com[:\/]neovim\/neovim/ && $3 == "(fetch)" {print $1; exit}') + local git_remote + if [[ "${1-}" == fork ]]; then + git_remote=$(git remote -v | awk '$2 !~ /github.com[:\/]neovim\/neovim/ && $3 == "(fetch)" {print $1; exit}') + else + git_remote=$(git remote -v | awk '$2 ~ /github.com[:\/]neovim\/neovim/ && $3 == "(fetch)" {print $1; exit}') + fi if [[ -z "$git_remote" ]]; then git_remote="origin" fi @@ -268,8 +272,8 @@ stage_patch() { get_vimpatch "$1" local try_apply="${2:-}" - local git_remote - git_remote="$(find_git_remote)" + local nvim_remote + nvim_remote="$(find_git_remote)" local checked_out_branch checked_out_branch="$(git rev-parse --abbrev-ref HEAD)" @@ -277,16 +281,16 @@ stage_patch() { msg_ok "Current branch '${checked_out_branch}' seems to be a vim-patch" echo " branch; not creating a new branch." else - printf '\nFetching "%s/master".\n' "${git_remote}" - output="$(git fetch "${git_remote}" master 2>&1)" && + printf '\nFetching "%s/master".\n' "${nvim_remote}" + output="$(git fetch "${nvim_remote}" master 2>&1)" && msg_ok "${output}" || (msg_err "${output}"; false) local nvim_branch="${BRANCH_PREFIX}${vim_version}" echo - echo "Creating new branch '${nvim_branch}' based on '${git_remote}/master'." + echo "Creating new branch '${nvim_branch}' based on '${nvim_remote}/master'." cd "${NVIM_SOURCE_DIR}" - output="$(git checkout -b "${nvim_branch}" "${git_remote}/master" 2>&1)" && + output="$(git checkout -b "${nvim_branch}" "${nvim_remote}/master" 2>&1)" && msg_ok "${output}" || (msg_err "${output}"; false) fi @@ -362,13 +366,13 @@ submit_pr() { exit 1 fi - local git_remote - git_remote="$(find_git_remote)" + local nvim_remote + nvim_remote="$(find_git_remote)" local pr_body - pr_body="$(git log --grep=vim-patch --reverse --format='#### %s%n%n%b%n' "${git_remote}"/master..HEAD)" + pr_body="$(git log --grep=vim-patch --reverse --format='#### %s%n%n%b%n' "${nvim_remote}"/master..HEAD)" local patches # Extract just the "vim-patch:X.Y.ZZZZ" or "vim-patch:sha" portion of each log - patches=("$(git log --grep=vim-patch --reverse --format='%s' "${git_remote}"/master..HEAD | sed 's/: .*//')") + patches=("$(git log --grep=vim-patch --reverse --format='%s' "${nvim_remote}"/master..HEAD | sed 's/: .*//')") # shellcheck disable=SC2206 patches=(${patches[@]//vim-patch:}) # Remove 'vim-patch:' prefix for each item in array. local pr_title="${patches[*]}" # Create space-separated string from array. @@ -376,8 +380,19 @@ submit_pr() { pr_title="$(printf 'vim-patch:%s' "${pr_title#,}")" if [[ $push_first -ne 0 ]]; then - echo "Pushing to 'origin/${checked_out_branch}'." - output="$(git push origin "${checked_out_branch}" 2>&1)" && + local push_remote + push_remote="$(git config --get branch."${checked_out_branch}".pushRemote || true)" + if [[ -z "$push_remote" ]]; then + push_remote="$(git config --get remote.pushDefault || true)" + if [[ -z "$push_remote" ]]; then + push_remote="$(git config --get branch."${checked_out_branch}".remote || true)" + if [[ -z "$push_remote" ]] || [[ "$push_remote" == "$nvim_remote" ]]; then + push_remote="$(find_git_remote fork)" + fi + fi + fi + echo "Pushing to '${push_remote}/${checked_out_branch}'." + output="$(git push "${push_remote}" "${checked_out_branch}" 2>&1)" && msg_ok "${output}" || (msg_err "${output}"; false) diff --git a/src/cjson/fpconv.c b/src/cjson/fpconv.c new file mode 100644 index 0000000000..b2f7a214c2 --- /dev/null +++ b/src/cjson/fpconv.c @@ -0,0 +1,211 @@ +/* fpconv - Floating point conversion routines + * + * Copyright (c) 2011-2012 Mark Pulford <mark@kyne.com.au> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* JSON uses a '.' decimal separator. strtod() / sprintf() under C libraries + * with locale support will break when the decimal separator is a comma. + * + * fpconv_* will around these issues with a translation buffer if required. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <string.h> + +#include "fpconv.h" + +/* Workaround for MSVC */ +#ifdef _MSC_VER +#define inline __inline +#define snprintf sprintf_s +#endif + +/* Lua CJSON assumes the locale is the same for all threads within a + * process and doesn't change after initialisation. + * + * This avoids the need for per thread storage or expensive checks + * for call. */ +static char locale_decimal_point = '.'; + +/* In theory multibyte decimal_points are possible, but + * Lua CJSON only supports UTF-8 and known locales only have + * single byte decimal points ([.,]). + * + * localconv() may not be thread safe (=>crash), and nl_langinfo() is + * not supported on some platforms. Use sprintf() instead - if the + * locale does change, at least Lua CJSON won't crash. */ +static void fpconv_update_locale(void) +{ + char buf[8]; + + snprintf(buf, sizeof(buf), "%g", 0.5); + + /* Failing this test might imply the platform has a buggy dtoa + * implementation or wide characters */ + if (buf[0] != '0' || buf[2] != '5' || buf[3] != 0) { + fprintf(stderr, "Error: wide characters found or printf() bug."); + abort(); + } + + locale_decimal_point = buf[1]; +} + +/* Check for a valid number character: [-+0-9a-yA-Y.] + * Eg: -0.6e+5, infinity, 0xF0.F0pF0 + * + * Used to find the probable end of a number. It doesn't matter if + * invalid characters are counted - strtod() will find the valid + * number if it exists. The risk is that slightly more memory might + * be allocated before a parse error occurs. */ +static inline int valid_number_character(char ch) +{ + char lower_ch; + + if ('0' <= ch && ch <= '9') + return 1; + if (ch == '-' || ch == '+' || ch == '.') + return 1; + + /* Hex digits, exponent (e), base (p), "infinity",.. */ + lower_ch = ch | 0x20; + if ('a' <= lower_ch && lower_ch <= 'y') + return 1; + + return 0; +} + +/* Calculate the size of the buffer required for a strtod locale + * conversion. */ +static int strtod_buffer_size(const char *s) +{ + const char *p = s; + + while (valid_number_character(*p)) + p++; + + return p - s; +} + +/* Similar to strtod(), but must be passed the current locale's decimal point + * character. Guaranteed to be called at the start of any valid number in a string */ +double fpconv_strtod(const char *nptr, char **endptr) +{ + char localbuf[FPCONV_G_FMT_BUFSIZE]; + char *buf, *endbuf, *dp; + int buflen; + double value; + + /* System strtod() is fine when decimal point is '.' */ + if (locale_decimal_point == '.') + return strtod(nptr, endptr); + + buflen = strtod_buffer_size(nptr); + if (!buflen) { + /* No valid characters found, standard strtod() return */ + *endptr = (char *)nptr; + return 0; + } + + /* Duplicate number into buffer */ + if (buflen >= FPCONV_G_FMT_BUFSIZE) { + /* Handle unusually large numbers */ + buf = malloc(buflen + 1); + if (!buf) { + fprintf(stderr, "Out of memory"); + abort(); + } + } else { + /* This is the common case.. */ + buf = localbuf; + } + memcpy(buf, nptr, buflen); + buf[buflen] = 0; + + /* Update decimal point character if found */ + dp = strchr(buf, '.'); + if (dp) + *dp = locale_decimal_point; + + value = strtod(buf, &endbuf); + *endptr = (char *)&nptr[endbuf - buf]; + if (buflen >= FPCONV_G_FMT_BUFSIZE) + free(buf); + + return value; +} + +/* "fmt" must point to a buffer of at least 6 characters */ +static void set_number_format(char *fmt, int precision) +{ + int d1, d2, i; + + assert(1 <= precision && precision <= 16); + + /* Create printf format (%.14g) from precision */ + d1 = precision / 10; + d2 = precision % 10; + fmt[0] = '%'; + fmt[1] = '.'; + i = 2; + if (d1) { + fmt[i++] = '0' + d1; + } + fmt[i++] = '0' + d2; + fmt[i++] = 'g'; + fmt[i] = 0; +} + +/* Assumes there is always at least 32 characters available in the target buffer */ +int fpconv_g_fmt(char *str, double num, int precision) +{ + char buf[FPCONV_G_FMT_BUFSIZE]; + char fmt[6]; + int len; + char *b; + + set_number_format(fmt, precision); + + /* Pass through when decimal point character is dot. */ + if (locale_decimal_point == '.') + return snprintf(str, FPCONV_G_FMT_BUFSIZE, fmt, num); + + /* snprintf() to a buffer then translate for other decimal point characters */ + len = snprintf(buf, FPCONV_G_FMT_BUFSIZE, fmt, num); + + /* Copy into target location. Translate decimal point if required */ + b = buf; + do { + *str++ = (*b == locale_decimal_point ? '.' : *b); + } while(*b++); + + return len; +} + +void fpconv_init() +{ + fpconv_update_locale(); +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/src/cjson/fpconv.h b/src/cjson/fpconv.h new file mode 100644 index 0000000000..6ac97808b6 --- /dev/null +++ b/src/cjson/fpconv.h @@ -0,0 +1,22 @@ +/* Lua CJSON floating point conversion routines */ + +/* Buffer required to store the largest string representation of a double. + * + * Longest double printed with %.14g is 21 characters long: + * -1.7976931348623e+308 */ +# define FPCONV_G_FMT_BUFSIZE 32 + +#ifdef USE_INTERNAL_FPCONV +static inline void fpconv_init() +{ + /* Do nothing - not required */ +} +#else +extern void fpconv_init(void); +#endif + +extern int fpconv_g_fmt(char*, double, int); +extern double fpconv_strtod(const char*, char**); + +/* vi:ai et sw=4 ts=4: + */ diff --git a/src/cjson/lua_cjson.c b/src/cjson/lua_cjson.c new file mode 100644 index 0000000000..92d07963bd --- /dev/null +++ b/src/cjson/lua_cjson.c @@ -0,0 +1,1609 @@ +/* Lua CJSON - JSON support for Lua + * + * Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +/* Caveats: + * - JSON "null" values are represented as lightuserdata since Lua + * tables cannot contain "nil". Compare with cjson.null. + * - Invalid UTF-8 characters are not detected and will be passed + * untouched. If required, UTF-8 error checking should be done + * outside this library. + * - Javascript comments are not part of the JSON spec, and are not + * currently supported. + * + * Note: Decoding is slower than encoding. Lua spends significant + * time (30%) managing tables when parsing JSON since it is + * difficult to know object/array sizes ahead of time. + */ + +#include <assert.h> +#include <stdint.h> +#include <string.h> +#include <math.h> +#include <limits.h> +#include <lua.h> +#include <lauxlib.h> + +#include "nvim/lua/executor.h" + +#include "lua_cjson.h" +#include "strbuf.h" +#include "fpconv.h" + +#ifndef CJSON_MODNAME +#define CJSON_MODNAME "cjson" +#endif + +#ifndef CJSON_VERSION +#define CJSON_VERSION "2.1.0.9" +#endif + +#ifdef _MSC_VER +#define snprintf sprintf_s + +#ifndef isnan +#include <float.h> +#define isnan(x) _isnan(x) +#endif + +#endif + +/* Workaround for Solaris platforms missing isinf() */ +#if !defined(isinf) && (defined(USE_INTERNAL_ISINF) || defined(MISSING_ISINF)) +#define isinf(x) (!isnan(x) && isnan((x) - (x))) +#endif + +#define DEFAULT_SPARSE_CONVERT 0 +#define DEFAULT_SPARSE_RATIO 2 +#define DEFAULT_SPARSE_SAFE 10 +#define DEFAULT_ENCODE_MAX_DEPTH 1000 +#define DEFAULT_DECODE_MAX_DEPTH 1000 +#define DEFAULT_ENCODE_INVALID_NUMBERS 0 +#define DEFAULT_DECODE_INVALID_NUMBERS 1 +#define DEFAULT_ENCODE_KEEP_BUFFER 1 +#define DEFAULT_ENCODE_NUMBER_PRECISION 14 +#define DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT 0 +#define DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT 0 +#define DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH 1 + +#ifdef DISABLE_INVALID_NUMBERS +#undef DEFAULT_DECODE_INVALID_NUMBERS +#define DEFAULT_DECODE_INVALID_NUMBERS 0 +#endif + +#ifdef _MSC_VER +/* Microsoft C compiler lacks strncasecmp and strcasecmp. */ +#define strncasecmp _strnicmp +#define strcasecmp _stricmp +#endif + +#if LONG_MAX > ((1UL << 31) - 1) +#define json_lightudata_mask(ludata) \ + ((void *) ((uintptr_t) (ludata) & ((1UL << 47) - 1))) + +#else +#define json_lightudata_mask(ludata) (ludata) +#endif + +#if LUA_VERSION_NUM > 501 +#define lua_objlen(L,i) lua_rawlen(L, (i)) +#endif + +static const char * const *json_empty_array; +static const char * const *json_array; + +typedef enum { + T_OBJ_BEGIN, + T_OBJ_END, + T_ARR_BEGIN, + T_ARR_END, + T_STRING, + T_NUMBER, + T_BOOLEAN, + T_NULL, + T_COLON, + T_COMMA, + T_END, + T_WHITESPACE, + T_ERROR, + T_UNKNOWN +} json_token_type_t; + +static const char *json_token_type_name[] = { + "T_OBJ_BEGIN", + "T_OBJ_END", + "T_ARR_BEGIN", + "T_ARR_END", + "T_STRING", + "T_NUMBER", + "T_BOOLEAN", + "T_NULL", + "T_COLON", + "T_COMMA", + "T_END", + "T_WHITESPACE", + "T_ERROR", + "T_UNKNOWN", + NULL +}; + +typedef struct { + json_token_type_t ch2token[256]; + char escape2char[256]; /* Decoding */ + + /* encode_buf is only allocated and used when + * encode_keep_buffer is set */ + strbuf_t encode_buf; + + int encode_sparse_convert; + int encode_sparse_ratio; + int encode_sparse_safe; + int encode_max_depth; + int encode_invalid_numbers; /* 2 => Encode as "null" */ + int encode_number_precision; + int encode_keep_buffer; + int encode_empty_table_as_object; + int encode_escape_forward_slash; + + int decode_invalid_numbers; + int decode_max_depth; + int decode_array_with_array_mt; +} json_config_t; + +typedef struct { + const char *data; + const char *ptr; + strbuf_t *tmp; /* Temporary storage for strings */ + json_config_t *cfg; + int current_depth; +} json_parse_t; + +typedef struct { + json_token_type_t type; + int index; + union { + const char *string; + double number; + int boolean; + } value; + int string_len; +} json_token_t; + +static const char *char2escape[256] = { + "\\u0000", "\\u0001", "\\u0002", "\\u0003", + "\\u0004", "\\u0005", "\\u0006", "\\u0007", + "\\b", "\\t", "\\n", "\\u000b", + "\\f", "\\r", "\\u000e", "\\u000f", + "\\u0010", "\\u0011", "\\u0012", "\\u0013", + "\\u0014", "\\u0015", "\\u0016", "\\u0017", + "\\u0018", "\\u0019", "\\u001a", "\\u001b", + "\\u001c", "\\u001d", "\\u001e", "\\u001f", + NULL, NULL, "\\\"", NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\/", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, "\\\\", NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, "\\u007f", + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + +/* ===== CONFIGURATION ===== */ + +static json_config_t *json_fetch_config(lua_State *l) +{ + json_config_t *cfg; + + cfg = lua_touserdata(l, lua_upvalueindex(1)); + if (!cfg) + luaL_error(l, "BUG: Unable to fetch CJSON configuration"); + + return cfg; +} + +/* Ensure the correct number of arguments have been provided. + * Pad with nil to allow other functions to simply check arg[i] + * to find whether an argument was provided */ +static json_config_t *json_arg_init(lua_State *l, int args) +{ + luaL_argcheck(l, lua_gettop(l) <= args, args + 1, + "found too many arguments"); + + while (lua_gettop(l) < args) + lua_pushnil(l); + + return json_fetch_config(l); +} + +/* Process integer options for configuration functions */ +static int json_integer_option(lua_State *l, int optindex, int *setting, + int min, int max) +{ + char errmsg[64]; + int value; + + if (!lua_isnil(l, optindex)) { + value = luaL_checkinteger(l, optindex); + snprintf(errmsg, sizeof(errmsg), "expected integer between %d and %d", min, max); + luaL_argcheck(l, min <= value && value <= max, 1, errmsg); + *setting = value; + } + + lua_pushinteger(l, *setting); + + return 1; +} + +/* Process enumerated arguments for a configuration function */ +static int json_enum_option(lua_State *l, int optindex, int *setting, + const char **options, int bool_true) +{ + static const char *bool_options[] = { "off", "on", NULL }; + + if (!options) { + options = bool_options; + bool_true = 1; + } + + if (!lua_isnil(l, optindex)) { + if (bool_true && lua_isboolean(l, optindex)) + *setting = lua_toboolean(l, optindex) * bool_true; + else + *setting = luaL_checkoption(l, optindex, NULL, options); + } + + if (bool_true && (*setting == 0 || *setting == bool_true)) + lua_pushboolean(l, *setting); + else + lua_pushstring(l, options[*setting]); + + return 1; +} + +/* Configures handling of extremely sparse arrays: + * convert: Convert extremely sparse arrays into objects? Otherwise error. + * ratio: 0: always allow sparse; 1: never allow sparse; >1: use ratio + * safe: Always use an array when the max index <= safe */ +static int json_cfg_encode_sparse_array(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 3); + + json_enum_option(l, 1, &cfg->encode_sparse_convert, NULL, 1); + json_integer_option(l, 2, &cfg->encode_sparse_ratio, 0, INT_MAX); + json_integer_option(l, 3, &cfg->encode_sparse_safe, 0, INT_MAX); + + return 3; +} + +/* Configures the maximum number of nested arrays/objects allowed when + * encoding */ +static int json_cfg_encode_max_depth(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->encode_max_depth, 1, INT_MAX); +} + +/* Configures the maximum number of nested arrays/objects allowed when + * encoding */ +static int json_cfg_decode_max_depth(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->decode_max_depth, 1, INT_MAX); +} + +/* Configures number precision when converting doubles to text */ +static int json_cfg_encode_number_precision(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_integer_option(l, 1, &cfg->encode_number_precision, 1, 16); +} + +/* Configures how to treat empty table when encode lua table */ +static int json_cfg_encode_empty_table_as_object(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + return json_enum_option(l, 1, &cfg->encode_empty_table_as_object, NULL, 1); +} + +/* Configures how to decode arrays */ +static int json_cfg_decode_array_with_array_mt(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + json_enum_option(l, 1, &cfg->decode_array_with_array_mt, NULL, 1); + + return 1; +} + +/* Configures JSON encoding buffer persistence */ +static int json_cfg_encode_keep_buffer(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + int old_value; + + old_value = cfg->encode_keep_buffer; + + json_enum_option(l, 1, &cfg->encode_keep_buffer, NULL, 1); + + /* Init / free the buffer if the setting has changed */ + if (old_value ^ cfg->encode_keep_buffer) { + if (cfg->encode_keep_buffer) + strbuf_init(&cfg->encode_buf, 0); + else + strbuf_free(&cfg->encode_buf); + } + + return 1; +} + +#if defined(DISABLE_INVALID_NUMBERS) && !defined(USE_INTERNAL_FPCONV) +void json_verify_invalid_number_setting(lua_State *l, int *setting) +{ + if (*setting == 1) { + *setting = 0; + luaL_error(l, "Infinity, NaN, and/or hexadecimal numbers are not supported."); + } +} +#else +#define json_verify_invalid_number_setting(l, s) do { } while(0) +#endif + +static int json_cfg_encode_invalid_numbers(lua_State *l) +{ + static const char *options[] = { "off", "on", "null", NULL }; + json_config_t *cfg = json_arg_init(l, 1); + + json_enum_option(l, 1, &cfg->encode_invalid_numbers, options, 1); + + json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); + + return 1; +} + +static int json_cfg_decode_invalid_numbers(lua_State *l) +{ + json_config_t *cfg = json_arg_init(l, 1); + + json_enum_option(l, 1, &cfg->decode_invalid_numbers, NULL, 1); + + json_verify_invalid_number_setting(l, &cfg->encode_invalid_numbers); + + return 1; +} + +static int json_cfg_encode_escape_forward_slash(lua_State *l) +{ + int ret; + json_config_t *cfg = json_arg_init(l, 1); + + ret = json_enum_option(l, 1, &cfg->encode_escape_forward_slash, NULL, 1); + if (cfg->encode_escape_forward_slash) { + char2escape['/'] = "\\/"; + } else { + char2escape['/'] = NULL; + } + return ret; +} + +static int json_destroy_config(lua_State *l) +{ + json_config_t *cfg; + + cfg = lua_touserdata(l, 1); + if (cfg) + strbuf_free(&cfg->encode_buf); + cfg = NULL; + + return 0; +} + +static void json_create_config(lua_State *l) +{ + json_config_t *cfg; + int i; + + cfg = lua_newuserdata(l, sizeof(*cfg)); + + /* Create GC method to clean up strbuf */ + lua_newtable(l); + lua_pushcfunction(l, json_destroy_config); + lua_setfield(l, -2, "__gc"); + lua_setmetatable(l, -2); + + cfg->encode_sparse_convert = DEFAULT_SPARSE_CONVERT; + cfg->encode_sparse_ratio = DEFAULT_SPARSE_RATIO; + cfg->encode_sparse_safe = DEFAULT_SPARSE_SAFE; + cfg->encode_max_depth = DEFAULT_ENCODE_MAX_DEPTH; + cfg->decode_max_depth = DEFAULT_DECODE_MAX_DEPTH; + cfg->encode_invalid_numbers = DEFAULT_ENCODE_INVALID_NUMBERS; + cfg->decode_invalid_numbers = DEFAULT_DECODE_INVALID_NUMBERS; + cfg->encode_keep_buffer = DEFAULT_ENCODE_KEEP_BUFFER; + cfg->encode_number_precision = DEFAULT_ENCODE_NUMBER_PRECISION; + cfg->encode_empty_table_as_object = DEFAULT_ENCODE_EMPTY_TABLE_AS_OBJECT; + cfg->decode_array_with_array_mt = DEFAULT_DECODE_ARRAY_WITH_ARRAY_MT; + cfg->encode_escape_forward_slash = DEFAULT_ENCODE_ESCAPE_FORWARD_SLASH; + +#if DEFAULT_ENCODE_KEEP_BUFFER > 0 + strbuf_init(&cfg->encode_buf, 0); +#endif + + /* Decoding init */ + + /* Tag all characters as an error */ + for (i = 0; i < 256; i++) + cfg->ch2token[i] = T_ERROR; + + /* Set tokens that require no further processing */ + cfg->ch2token['{'] = T_OBJ_BEGIN; + cfg->ch2token['}'] = T_OBJ_END; + cfg->ch2token['['] = T_ARR_BEGIN; + cfg->ch2token[']'] = T_ARR_END; + cfg->ch2token[','] = T_COMMA; + cfg->ch2token[':'] = T_COLON; + cfg->ch2token['\0'] = T_END; + cfg->ch2token[' '] = T_WHITESPACE; + cfg->ch2token['\t'] = T_WHITESPACE; + cfg->ch2token['\n'] = T_WHITESPACE; + cfg->ch2token['\r'] = T_WHITESPACE; + + /* Update characters that require further processing */ + cfg->ch2token['f'] = T_UNKNOWN; /* false? */ + cfg->ch2token['i'] = T_UNKNOWN; /* inf, ininity? */ + cfg->ch2token['I'] = T_UNKNOWN; + cfg->ch2token['n'] = T_UNKNOWN; /* null, nan? */ + cfg->ch2token['N'] = T_UNKNOWN; + cfg->ch2token['t'] = T_UNKNOWN; /* true? */ + cfg->ch2token['"'] = T_UNKNOWN; /* string? */ + cfg->ch2token['+'] = T_UNKNOWN; /* number? */ + cfg->ch2token['-'] = T_UNKNOWN; + for (i = 0; i < 10; i++) + cfg->ch2token['0' + i] = T_UNKNOWN; + + /* Lookup table for parsing escape characters */ + for (i = 0; i < 256; i++) + cfg->escape2char[i] = 0; /* String error */ + cfg->escape2char['"'] = '"'; + cfg->escape2char['\\'] = '\\'; + cfg->escape2char['/'] = '/'; + cfg->escape2char['b'] = '\b'; + cfg->escape2char['t'] = '\t'; + cfg->escape2char['n'] = '\n'; + cfg->escape2char['f'] = '\f'; + cfg->escape2char['r'] = '\r'; + cfg->escape2char['u'] = 'u'; /* Unicode parsing required */ +} + +/* ===== ENCODING ===== */ + +static void json_encode_exception(lua_State *l, json_config_t *cfg, strbuf_t *json, int lindex, + const char *reason) +{ + if (!cfg->encode_keep_buffer) + strbuf_free(json); + luaL_error(l, "Cannot serialise %s: %s", + lua_typename(l, lua_type(l, lindex)), reason); +} + +/* json_append_string args: + * - lua_State + * - JSON strbuf + * - String (Lua stack index) + * + * Returns nothing. Doesn't remove string from Lua stack */ +static void json_append_string(lua_State *l, strbuf_t *json, int lindex) +{ + const char *escstr; + unsigned i; + const char *str; + size_t len; + + str = lua_tolstring(l, lindex, &len); + + /* Worst case is len * 6 (all unicode escapes). + * This buffer is reused constantly for small strings + * If there are any excess pages, they won't be hit anyway. + * This gains ~5% speedup. */ + strbuf_ensure_empty_length(json, len * 6 + 2); + + strbuf_append_char_unsafe(json, '\"'); + for (i = 0; i < len; i++) { + escstr = char2escape[(unsigned char)str[i]]; + if (escstr) + strbuf_append_string(json, escstr); + else + strbuf_append_char_unsafe(json, str[i]); + } + strbuf_append_char_unsafe(json, '\"'); +} + +/* Find the size of the array on the top of the Lua stack + * -1 object (not a pure array) + * >=0 elements in array + */ +static int lua_array_length(lua_State *l, json_config_t *cfg, strbuf_t *json) +{ + double k; + int max; + int items; + + max = 0; + items = 0; + + lua_pushnil(l); + /* table, startkey */ + while (lua_next(l, -2) != 0) { + /* table, key, value */ + if (lua_type(l, -2) == LUA_TNUMBER && + (k = lua_tonumber(l, -2))) { + /* Integer >= 1 ? */ + if (floor(k) == k && k >= 1) { + if (k > max) + max = k; + items++; + lua_pop(l, 1); + continue; + } + } + + /* Must not be an array (non integer key) */ + lua_pop(l, 2); + return -1; + } + + /* Encode excessively sparse arrays as objects (if enabled) */ + if (cfg->encode_sparse_ratio > 0 && + max > items * cfg->encode_sparse_ratio && + max > cfg->encode_sparse_safe) { + if (!cfg->encode_sparse_convert) + json_encode_exception(l, cfg, json, -1, "excessively sparse array"); + + return -1; + } + + return max; +} + +static void json_check_encode_depth(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + /* Ensure there are enough slots free to traverse a table (key, + * value) and push a string for a potential error message. + * + * Unlike "decode", the key and value are still on the stack when + * lua_checkstack() is called. Hence an extra slot for luaL_error() + * below is required just in case the next check to lua_checkstack() + * fails. + * + * While this won't cause a crash due to the EXTRA_STACK reserve + * slots, it would still be an improper use of the API. */ + if (current_depth <= cfg->encode_max_depth && lua_checkstack(l, 3)) + return; + + if (!cfg->encode_keep_buffer) + strbuf_free(json); + + luaL_error(l, "Cannot serialise, excessive nesting (%d)", + current_depth); +} + +static void json_append_data(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json); + +/* json_append_array args: + * - lua_State + * - JSON strbuf + * - Size of passwd Lua array (top of stack) */ +static void json_append_array(lua_State *l, json_config_t *cfg, int current_depth, + strbuf_t *json, int array_length) +{ + int comma, i; + + strbuf_append_char(json, '['); + + comma = 0; + for (i = 1; i <= array_length; i++) { + if (comma) + strbuf_append_char(json, ','); + else + comma = 1; + + lua_rawgeti(l, -1, i); + json_append_data(l, cfg, current_depth, json); + lua_pop(l, 1); + } + + strbuf_append_char(json, ']'); +} + +static void json_append_number(lua_State *l, json_config_t *cfg, + strbuf_t *json, int lindex) +{ + double num = lua_tonumber(l, lindex); + int len; + + if (cfg->encode_invalid_numbers == 0) { + /* Prevent encoding invalid numbers */ + if (isinf(num) || isnan(num)) + json_encode_exception(l, cfg, json, lindex, + "must not be NaN or Infinity"); + } else if (cfg->encode_invalid_numbers == 1) { + /* Encode NaN/Infinity separately to ensure Javascript compatible + * values are used. */ + if (isnan(num)) { + strbuf_append_mem(json, "NaN", 3); + return; + } + if (isinf(num)) { + if (num < 0) + strbuf_append_mem(json, "-Infinity", 9); + else + strbuf_append_mem(json, "Infinity", 8); + return; + } + } else { + /* Encode invalid numbers as "null" */ + if (isinf(num) || isnan(num)) { + strbuf_append_mem(json, "null", 4); + return; + } + } + + strbuf_ensure_empty_length(json, FPCONV_G_FMT_BUFSIZE); + len = fpconv_g_fmt(strbuf_empty_ptr(json), num, cfg->encode_number_precision); + strbuf_extend_length(json, len); +} + +static void json_append_object(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + int comma, keytype; + + /* Object */ + strbuf_append_char(json, '{'); + + lua_pushnil(l); + /* table, startkey */ + comma = 0; + while (lua_next(l, -2) != 0) { + if (comma) + strbuf_append_char(json, ','); + else + comma = 1; + + /* table, key, value */ + keytype = lua_type(l, -2); + if (keytype == LUA_TNUMBER) { + strbuf_append_char(json, '"'); + json_append_number(l, cfg, json, -2); + strbuf_append_mem(json, "\":", 2); + } else if (keytype == LUA_TSTRING) { + json_append_string(l, json, -2); + strbuf_append_char(json, ':'); + } else { + json_encode_exception(l, cfg, json, -2, + "table key must be a number or string"); + /* never returns */ + } + + /* table, key, value */ + json_append_data(l, cfg, current_depth, json); + lua_pop(l, 1); + /* table, key */ + } + + strbuf_append_char(json, '}'); +} + +/* Serialise Lua data into JSON string. */ +static void json_append_data(lua_State *l, json_config_t *cfg, + int current_depth, strbuf_t *json) +{ + int len; + int as_array = 0; + int as_empty_dict = 0; + int has_metatable; + + switch (lua_type(l, -1)) { + case LUA_TSTRING: + json_append_string(l, json, -1); + break; + case LUA_TNUMBER: + json_append_number(l, cfg, json, -1); + break; + case LUA_TBOOLEAN: + if (lua_toboolean(l, -1)) + strbuf_append_mem(json, "true", 4); + else + strbuf_append_mem(json, "false", 5); + break; + case LUA_TTABLE: + current_depth++; + json_check_encode_depth(l, cfg, current_depth, json); + + has_metatable = lua_getmetatable(l, -1); + + if (has_metatable) { + + nlua_pushref(l, nlua_empty_dict_ref); + if (lua_rawequal(l, -2, -1)) { + as_empty_dict = true; + } else { + lua_pop(l, 1); + lua_pushlightuserdata(l, json_lightudata_mask(&json_array)); + lua_rawget(l, LUA_REGISTRYINDEX); + as_array = lua_rawequal(l, -1, -2); + } + lua_pop(l, 2); + } + + if (as_array) { + len = lua_objlen(l, -1); + json_append_array(l, cfg, current_depth, json, len); + } else { + len = lua_array_length(l, cfg, json); + + if (len > 0 || (len == 0 && !cfg->encode_empty_table_as_object && !as_empty_dict)) { + json_append_array(l, cfg, current_depth, json, len); + } else { + if (has_metatable) { + lua_getmetatable(l, -1); + lua_pushlightuserdata(l, json_lightudata_mask( + &json_empty_array)); + lua_rawget(l, LUA_REGISTRYINDEX); + as_array = lua_rawequal(l, -1, -2); + lua_pop(l, 2); /* pop pointer + metatable */ + if (as_array) { + json_append_array(l, cfg, current_depth, json, 0); + break; + } + } + json_append_object(l, cfg, current_depth, json); + } + } + break; + case LUA_TNIL: + strbuf_append_mem(json, "null", 4); + break; + case LUA_TLIGHTUSERDATA: + if (lua_touserdata(l, -1) == &json_array) { + json_append_array(l, cfg, current_depth, json, 0); + } + break; + case LUA_TUSERDATA: + nlua_pushref(l, nlua_nil_ref); + bool is_nil = lua_rawequal(l, -2, -1); + lua_pop(l, 1); + if (is_nil) { + strbuf_append_mem(json, "null", 4); + break; + } else { + FALLTHROUGH; + } + default: + /* Remaining types (LUA_TFUNCTION, LUA_TUSERDATA, LUA_TTHREAD, + * and LUA_TLIGHTUSERDATA) cannot be serialised */ + json_encode_exception(l, cfg, json, -1, "type not supported"); + /* never returns */ + } +} + +static int json_encode(lua_State *l) +{ + json_config_t *cfg = json_fetch_config(l); + strbuf_t local_encode_buf; + strbuf_t *encode_buf; + char *json; + int len; + + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + if (!cfg->encode_keep_buffer) { + /* Use private buffer */ + encode_buf = &local_encode_buf; + strbuf_init(encode_buf, 0); + } else { + /* Reuse existing buffer */ + encode_buf = &cfg->encode_buf; + strbuf_reset(encode_buf); + } + + json_append_data(l, cfg, 0, encode_buf); + json = strbuf_string(encode_buf, &len); + + lua_pushlstring(l, json, len); + + if (!cfg->encode_keep_buffer) + strbuf_free(encode_buf); + + return 1; +} + +/* ===== DECODING ===== */ + +static void json_process_value(lua_State *l, json_parse_t *json, + json_token_t *token); + +static int hexdigit2int(char hex) +{ + if ('0' <= hex && hex <= '9') + return hex - '0'; + + /* Force lowercase */ + hex |= 0x20; + if ('a' <= hex && hex <= 'f') + return 10 + hex - 'a'; + + return -1; +} + +static int decode_hex4(const char *hex) +{ + int digit[4]; + int i; + + /* Convert ASCII hex digit to numeric digit + * Note: this returns an error for invalid hex digits, including + * NULL */ + for (i = 0; i < 4; i++) { + digit[i] = hexdigit2int(hex[i]); + if (digit[i] < 0) { + return -1; + } + } + + return (digit[0] << 12) + + (digit[1] << 8) + + (digit[2] << 4) + + digit[3]; +} + +/* Converts a Unicode codepoint to UTF-8. + * Returns UTF-8 string length, and up to 4 bytes in *utf8 */ +static int codepoint_to_utf8(char *utf8, int codepoint) +{ + /* 0xxxxxxx */ + if (codepoint <= 0x7F) { + utf8[0] = codepoint; + return 1; + } + + /* 110xxxxx 10xxxxxx */ + if (codepoint <= 0x7FF) { + utf8[0] = (codepoint >> 6) | 0xC0; + utf8[1] = (codepoint & 0x3F) | 0x80; + return 2; + } + + /* 1110xxxx 10xxxxxx 10xxxxxx */ + if (codepoint <= 0xFFFF) { + utf8[0] = (codepoint >> 12) | 0xE0; + utf8[1] = ((codepoint >> 6) & 0x3F) | 0x80; + utf8[2] = (codepoint & 0x3F) | 0x80; + return 3; + } + + /* 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint <= 0x1FFFFF) { + utf8[0] = (codepoint >> 18) | 0xF0; + utf8[1] = ((codepoint >> 12) & 0x3F) | 0x80; + utf8[2] = ((codepoint >> 6) & 0x3F) | 0x80; + utf8[3] = (codepoint & 0x3F) | 0x80; + return 4; + } + + return 0; +} + + +/* Called when index pointing to beginning of UTF-16 code escape: \uXXXX + * \u is guaranteed to exist, but the remaining hex characters may be + * missing. + * Translate to UTF-8 and append to temporary token string. + * Must advance index to the next character to be processed. + * Returns: 0 success + * -1 error + */ +static int json_append_unicode_escape(json_parse_t *json) +{ + char utf8[4]; /* Surrogate pairs require 4 UTF-8 bytes */ + int codepoint; + int surrogate_low; + int len; + int escape_len = 6; + + /* Fetch UTF-16 code unit */ + codepoint = decode_hex4(json->ptr + 2); + if (codepoint < 0) + return -1; + + /* UTF-16 surrogate pairs take the following 2 byte form: + * 11011 x yyyyyyyyyy + * When x = 0: y is the high 10 bits of the codepoint + * x = 1: y is the low 10 bits of the codepoint + * + * Check for a surrogate pair (high or low) */ + if ((codepoint & 0xF800) == 0xD800) { + /* Error if the 1st surrogate is not high */ + if (codepoint & 0x400) + return -1; + + /* Ensure the next code is a unicode escape */ + if (*(json->ptr + escape_len) != '\\' || + *(json->ptr + escape_len + 1) != 'u') { + return -1; + } + + /* Fetch the next codepoint */ + surrogate_low = decode_hex4(json->ptr + 2 + escape_len); + if (surrogate_low < 0) + return -1; + + /* Error if the 2nd code is not a low surrogate */ + if ((surrogate_low & 0xFC00) != 0xDC00) + return -1; + + /* Calculate Unicode codepoint */ + codepoint = (codepoint & 0x3FF) << 10; + surrogate_low &= 0x3FF; + codepoint = (codepoint | surrogate_low) + 0x10000; + escape_len = 12; + } + + /* Convert codepoint to UTF-8 */ + len = codepoint_to_utf8(utf8, codepoint); + if (!len) + return -1; + + /* Append bytes and advance parse index */ + strbuf_append_mem_unsafe(json->tmp, utf8, len); + json->ptr += escape_len; + + return 0; +} + +static void json_set_token_error(json_token_t *token, json_parse_t *json, + const char *errtype) +{ + token->type = T_ERROR; + token->index = json->ptr - json->data; + token->value.string = errtype; +} + +static void json_next_string_token(json_parse_t *json, json_token_t *token) +{ + char *escape2char = json->cfg->escape2char; + char ch; + + /* Caller must ensure a string is next */ + assert(*json->ptr == '"'); + + /* Skip " */ + json->ptr++; + + /* json->tmp is the temporary strbuf used to accumulate the + * decoded string value. + * json->tmp is sized to handle JSON containing only a string value. + */ + strbuf_reset(json->tmp); + + while ((ch = *json->ptr) != '"') { + if (!ch) { + /* Premature end of the string */ + json_set_token_error(token, json, "unexpected end of string"); + return; + } + + /* Handle escapes */ + if (ch == '\\') { + /* Fetch escape character */ + ch = *(json->ptr + 1); + + /* Translate escape code and append to tmp string */ + ch = escape2char[(unsigned char)ch]; + if (ch == 'u') { + if (json_append_unicode_escape(json) == 0) + continue; + + json_set_token_error(token, json, + "invalid unicode escape code"); + return; + } + if (!ch) { + json_set_token_error(token, json, "invalid escape code"); + return; + } + + /* Skip '\' */ + json->ptr++; + } + /* Append normal character or translated single character + * Unicode escapes are handled above */ + strbuf_append_char_unsafe(json->tmp, ch); + json->ptr++; + } + json->ptr++; /* Eat final quote (") */ + + strbuf_ensure_null(json->tmp); + + token->type = T_STRING; + token->value.string = strbuf_string(json->tmp, &token->string_len); +} + +/* JSON numbers should take the following form: + * -?(0|[1-9]|[1-9][0-9]+)(.[0-9]+)?([eE][-+]?[0-9]+)? + * + * json_next_number_token() uses strtod() which allows other forms: + * - numbers starting with '+' + * - NaN, -NaN, infinity, -infinity + * - hexadecimal numbers + * - numbers with leading zeros + * + * json_is_invalid_number() detects "numbers" which may pass strtod()'s + * error checking, but should not be allowed with strict JSON. + * + * json_is_invalid_number() may pass numbers which cause strtod() + * to generate an error. + */ +static int json_is_invalid_number(json_parse_t *json) +{ + const char *p = json->ptr; + + /* Reject numbers starting with + */ + if (*p == '+') + return 1; + + /* Skip minus sign if it exists */ + if (*p == '-') + p++; + + /* Reject numbers starting with 0x, or leading zeros */ + if (*p == '0') { + int ch2 = *(p + 1); + + if ((ch2 | 0x20) == 'x' || /* Hex */ + ('0' <= ch2 && ch2 <= '9')) /* Leading zero */ + return 1; + + return 0; + } else if (*p <= '9') { + return 0; /* Ordinary number */ + } + + /* Reject inf/nan */ + if (!strncasecmp(p, "inf", 3)) + return 1; + if (!strncasecmp(p, "nan", 3)) + return 1; + + /* Pass all other numbers which may still be invalid, but + * strtod() will catch them. */ + return 0; +} + +static void json_next_number_token(json_parse_t *json, json_token_t *token) +{ + char *endptr; + + token->type = T_NUMBER; + token->value.number = fpconv_strtod(json->ptr, &endptr); + if (json->ptr == endptr) + json_set_token_error(token, json, "invalid number"); + else + json->ptr = endptr; /* Skip the processed number */ + + return; +} + +/* Fills in the token struct. + * T_STRING will return a pointer to the json_parse_t temporary string + * T_ERROR will leave the json->ptr pointer at the error. + */ +static void json_next_token(json_parse_t *json, json_token_t *token) +{ + const json_token_type_t *ch2token = json->cfg->ch2token; + int ch; + + /* Eat whitespace. */ + while (1) { + ch = (unsigned char)*(json->ptr); + token->type = ch2token[ch]; + if (token->type != T_WHITESPACE) + break; + json->ptr++; + } + + /* Store location of new token. Required when throwing errors + * for unexpected tokens (syntax errors). */ + token->index = json->ptr - json->data; + + /* Don't advance the pointer for an error or the end */ + if (token->type == T_ERROR) { + json_set_token_error(token, json, "invalid token"); + return; + } + + if (token->type == T_END) { + return; + } + + /* Found a known single character token, advance index and return */ + if (token->type != T_UNKNOWN) { + json->ptr++; + return; + } + + /* Process characters which triggered T_UNKNOWN + * + * Must use strncmp() to match the front of the JSON string. + * JSON identifier must be lowercase. + * When strict_numbers if disabled, either case is allowed for + * Infinity/NaN (since we are no longer following the spec..) */ + if (ch == '"') { + json_next_string_token(json, token); + return; + } else if (ch == '-' || ('0' <= ch && ch <= '9')) { + if (!json->cfg->decode_invalid_numbers && json_is_invalid_number(json)) { + json_set_token_error(token, json, "invalid number"); + return; + } + json_next_number_token(json, token); + return; + } else if (!strncmp(json->ptr, "true", 4)) { + token->type = T_BOOLEAN; + token->value.boolean = 1; + json->ptr += 4; + return; + } else if (!strncmp(json->ptr, "false", 5)) { + token->type = T_BOOLEAN; + token->value.boolean = 0; + json->ptr += 5; + return; + } else if (!strncmp(json->ptr, "null", 4)) { + token->type = T_NULL; + json->ptr += 4; + return; + } else if (json->cfg->decode_invalid_numbers && + json_is_invalid_number(json)) { + /* When decode_invalid_numbers is enabled, only attempt to process + * numbers we know are invalid JSON (Inf, NaN, hex) + * This is required to generate an appropriate token error, + * otherwise all bad tokens will register as "invalid number" + */ + json_next_number_token(json, token); + return; + } + + /* Token starts with t/f/n but isn't recognised above. */ + json_set_token_error(token, json, "invalid token"); +} + +/* This function does not return. + * DO NOT CALL WITH DYNAMIC MEMORY ALLOCATED. + * The only supported exception is the temporary parser string + * json->tmp struct. + * json and token should exist on the stack somewhere. + * luaL_error() will long_jmp and release the stack */ +static void json_throw_parse_error(lua_State *l, json_parse_t *json, + const char *exp, json_token_t *token) +{ + const char *found; + + strbuf_free(json->tmp); + + if (token->type == T_ERROR) + found = token->value.string; + else + found = json_token_type_name[token->type]; + + /* Note: token->index is 0 based, display starting from 1 */ + luaL_error(l, "Expected %s but found %s at character %d", + exp, found, token->index + 1); +} + +static inline void json_decode_ascend(json_parse_t *json) +{ + json->current_depth--; +} + +static void json_decode_descend(lua_State *l, json_parse_t *json, int slots) +{ + json->current_depth++; + + if (json->current_depth <= json->cfg->decode_max_depth && + lua_checkstack(l, slots)) { + return; + } + + strbuf_free(json->tmp); + luaL_error(l, "Found too many nested data structures (%d) at character %d", + json->current_depth, json->ptr - json->data); +} + +static void json_parse_object_context(lua_State *l, json_parse_t *json) +{ + json_token_t token; + + /* 3 slots required: + * .., table, key, value */ + json_decode_descend(l, json, 3); + + lua_newtable(l); + + json_next_token(json, &token); + + /* Handle empty objects */ + if (token.type == T_OBJ_END) { + nlua_pushref(l, nlua_empty_dict_ref); \ + lua_setmetatable(l, -2); \ + json_decode_ascend(json); + return; + } + + while (1) { + if (token.type != T_STRING) + json_throw_parse_error(l, json, "object key string", &token); + + /* Push key */ + lua_pushlstring(l, token.value.string, token.string_len); + + json_next_token(json, &token); + if (token.type != T_COLON) + json_throw_parse_error(l, json, "colon", &token); + + /* Fetch value */ + json_next_token(json, &token); + json_process_value(l, json, &token); + + /* Set key = value */ + lua_rawset(l, -3); + + json_next_token(json, &token); + + if (token.type == T_OBJ_END) { + json_decode_ascend(json); + return; + } + + if (token.type != T_COMMA) + json_throw_parse_error(l, json, "comma or object end", &token); + + json_next_token(json, &token); + } +} + +/* Handle the array context */ +static void json_parse_array_context(lua_State *l, json_parse_t *json) +{ + json_token_t token; + int i; + + /* 2 slots required: + * .., table, value */ + json_decode_descend(l, json, 2); + + lua_newtable(l); + + /* set array_mt on the table at the top of the stack */ + if (json->cfg->decode_array_with_array_mt) { + lua_pushlightuserdata(l, json_lightudata_mask(&json_array)); + lua_rawget(l, LUA_REGISTRYINDEX); + lua_setmetatable(l, -2); + } + + json_next_token(json, &token); + + /* Handle empty arrays */ + if (token.type == T_ARR_END) { + json_decode_ascend(json); + return; + } + + for (i = 1; ; i++) { + json_process_value(l, json, &token); + lua_rawseti(l, -2, i); /* arr[i] = value */ + + json_next_token(json, &token); + + if (token.type == T_ARR_END) { + json_decode_ascend(json); + return; + } + + if (token.type != T_COMMA) + json_throw_parse_error(l, json, "comma or array end", &token); + + json_next_token(json, &token); + } +} + +/* Handle the "value" context */ +static void json_process_value(lua_State *l, json_parse_t *json, + json_token_t *token) +{ + switch (token->type) { + case T_STRING: + lua_pushlstring(l, token->value.string, token->string_len); + break;; + case T_NUMBER: + lua_pushnumber(l, token->value.number); + break;; + case T_BOOLEAN: + lua_pushboolean(l, token->value.boolean); + break;; + case T_OBJ_BEGIN: + json_parse_object_context(l, json); + break;; + case T_ARR_BEGIN: + json_parse_array_context(l, json); + break;; + case T_NULL: + nlua_pushref(l, nlua_nil_ref); + break;; + default: + json_throw_parse_error(l, json, "value", token); + } +} + +static int json_decode(lua_State *l) +{ + json_parse_t json; + json_token_t token; + size_t json_len; + + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + json.cfg = json_fetch_config(l); + json.data = luaL_checklstring(l, 1, &json_len); + json.current_depth = 0; + json.ptr = json.data; + + /* Detect Unicode other than UTF-8 (see RFC 4627, Sec 3) + * + * CJSON can support any simple data type, hence only the first + * character is guaranteed to be ASCII (at worst: '"'). This is + * still enough to detect whether the wrong encoding is in use. */ + if (json_len >= 2 && (!json.data[0] || !json.data[1])) + luaL_error(l, "JSON parser does not support UTF-16 or UTF-32"); + + /* Ensure the temporary buffer can hold the entire string. + * This means we no longer need to do length checks since the decoded + * string must be smaller than the entire json string */ + json.tmp = strbuf_new(json_len); + + json_next_token(&json, &token); + json_process_value(l, &json, &token); + + /* Ensure there is no more input left */ + json_next_token(&json, &token); + + if (token.type != T_END) + json_throw_parse_error(l, &json, "the end", &token); + + strbuf_free(json.tmp); + + return 1; +} + +/* ===== INITIALISATION ===== */ + +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 502 +/* Compatibility for Lua 5.1 and older LuaJIT. + * + * compat_luaL_setfuncs() is used to create a module table where the functions + * have json_config_t as their first upvalue. Code borrowed from Lua 5.2 + * source's luaL_setfuncs(). + */ +static void compat_luaL_setfuncs(lua_State *l, const luaL_Reg *reg, int nup) +{ + int i; + + luaL_checkstack(l, nup, "too many upvalues"); + for (; reg->name != NULL; reg++) { /* fill the table with given functions */ + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(l, -nup); + lua_pushcclosure(l, reg->func, nup); /* closure with those upvalues */ + lua_setfield(l, -(nup + 2), reg->name); + } + lua_pop(l, nup); /* remove upvalues */ +} +#else +#define compat_luaL_setfuncs(L, reg, nup) luaL_setfuncs(L, reg, nup) +#endif + +/* Call target function in protected mode with all supplied args. + * Assumes target function only returns a single non-nil value. + * Convert and return thrown errors as: nil, "error message" */ +static int json_protect_conversion(lua_State *l) +{ + int err; + + /* Deliberately throw an error for invalid arguments */ + luaL_argcheck(l, lua_gettop(l) == 1, 1, "expected 1 argument"); + + /* pcall() the function stored as upvalue(1) */ + lua_pushvalue(l, lua_upvalueindex(1)); + lua_insert(l, 1); + err = lua_pcall(l, 1, 1, 0); + if (!err) + return 1; + + if (err == LUA_ERRRUN) { + lua_pushnil(l); + lua_insert(l, -2); + return 2; + } + + /* Since we are not using a custom error handler, the only remaining + * errors are memory related */ + return luaL_error(l, "Memory allocation error in CJSON protected call"); +} + +/* Return cjson module table */ +int lua_cjson_new(lua_State *l) +{ + luaL_Reg reg[] = { + { "encode", json_encode }, + { "decode", json_decode }, + { "encode_empty_table_as_object", json_cfg_encode_empty_table_as_object }, + { "decode_array_with_array_mt", json_cfg_decode_array_with_array_mt }, + { "encode_sparse_array", json_cfg_encode_sparse_array }, + { "encode_max_depth", json_cfg_encode_max_depth }, + { "decode_max_depth", json_cfg_decode_max_depth }, + { "encode_number_precision", json_cfg_encode_number_precision }, + { "encode_keep_buffer", json_cfg_encode_keep_buffer }, + { "encode_invalid_numbers", json_cfg_encode_invalid_numbers }, + { "decode_invalid_numbers", json_cfg_decode_invalid_numbers }, + { "encode_escape_forward_slash", json_cfg_encode_escape_forward_slash }, + { "new", lua_cjson_new }, + { NULL, NULL } + }; + + /* Initialise number conversions */ + fpconv_init(); + + /* Test if array metatables are in registry */ + lua_pushlightuserdata(l, json_lightudata_mask(&json_empty_array)); + lua_rawget(l, LUA_REGISTRYINDEX); + if (lua_isnil(l, -1)) { + /* Create array metatables. + * + * If multiple calls to lua_cjson_new() are made, + * this prevents overriding the tables at the given + * registry's index with a new one. + */ + lua_pop(l, 1); + + /* empty_array_mt */ + lua_pushlightuserdata(l, json_lightudata_mask(&json_empty_array)); + lua_newtable(l); + lua_rawset(l, LUA_REGISTRYINDEX); + + /* array_mt */ + lua_pushlightuserdata(l, json_lightudata_mask(&json_array)); + lua_newtable(l); + lua_rawset(l, LUA_REGISTRYINDEX); + } + + /* cjson module table */ + lua_newtable(l); + + /* Register functions with config data as upvalue */ + json_create_config(l); + compat_luaL_setfuncs(l, reg, 1); + + /* Set cjson.null */ + nlua_pushref(l, nlua_nil_ref); + lua_setfield(l, -2, "null"); + + /* Set cjson.empty_array_mt */ + lua_pushlightuserdata(l, json_lightudata_mask(&json_empty_array)); + lua_rawget(l, LUA_REGISTRYINDEX); + lua_setfield(l, -2, "empty_array_mt"); + + /* Set cjson.array_mt */ + lua_pushlightuserdata(l, json_lightudata_mask(&json_array)); + lua_rawget(l, LUA_REGISTRYINDEX); + lua_setfield(l, -2, "array_mt"); + + /* Set cjson.empty_array */ + lua_pushlightuserdata(l, json_lightudata_mask(&json_array)); + lua_setfield(l, -2, "empty_array"); + + /* Set module name / version fields */ + lua_pushliteral(l, CJSON_MODNAME); + lua_setfield(l, -2, "_NAME"); + lua_pushliteral(l, CJSON_VERSION); + lua_setfield(l, -2, "_VERSION"); + + return 1; +} + +/* Return cjson.safe module table */ +static int lua_cjson_safe_new(lua_State *l) +{ + const char *func[] = { "decode", "encode", NULL }; + int i; + + lua_cjson_new(l); + + /* Fix new() method */ + lua_pushcfunction(l, lua_cjson_safe_new); + lua_setfield(l, -2, "new"); + + for (i = 0; func[i]; i++) { + lua_getfield(l, -1, func[i]); + lua_pushcclosure(l, json_protect_conversion, 1); + lua_setfield(l, -2, func[i]); + } + + return 1; +} + +int luaopen_cjson(lua_State *l) +{ + lua_cjson_new(l); + +#ifdef ENABLE_CJSON_GLOBAL + /* Register a global "cjson" table. */ + lua_pushvalue(l, -1); + lua_setglobal(l, CJSON_MODNAME); +#endif + + /* Return cjson table */ + return 1; +} + +int luaopen_cjson_safe(lua_State *l) +{ + lua_cjson_safe_new(l); + + /* Return cjson.safe table */ + return 1; +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/src/cjson/lua_cjson.h b/src/cjson/lua_cjson.h new file mode 100644 index 0000000000..3f70b679be --- /dev/null +++ b/src/cjson/lua_cjson.h @@ -0,0 +1,10 @@ +#ifndef CJSON_LUACJSON_H +#define CJSON_LUACJSON_H + +#include "lua.h" + +int lua_cjson_new(lua_State *l); +int luaopen_cjson(lua_State *l); +int luaopen_cjson_safe(lua_State *l); + +#endif // CJSON_LUACJSON_H diff --git a/src/cjson/strbuf.c b/src/cjson/strbuf.c new file mode 100644 index 0000000000..f0f7f4b9a3 --- /dev/null +++ b/src/cjson/strbuf.c @@ -0,0 +1,251 @@ +/* strbuf - String buffer routines + * + * Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> + +#include "strbuf.h" + +static void die(const char *fmt, ...) +{ + va_list arg; + + va_start(arg, fmt); + vfprintf(stderr, fmt, arg); + va_end(arg); + fprintf(stderr, "\n"); + + exit(-1); +} + +void strbuf_init(strbuf_t *s, int len) +{ + int size; + + if (len <= 0) + size = STRBUF_DEFAULT_SIZE; + else + size = len + 1; /* \0 terminator */ + + s->buf = NULL; + s->size = size; + s->length = 0; + s->increment = STRBUF_DEFAULT_INCREMENT; + s->dynamic = 0; + s->reallocs = 0; + s->debug = 0; + + s->buf = malloc(size); + if (!s->buf) + die("Out of memory"); + + strbuf_ensure_null(s); +} + +strbuf_t *strbuf_new(int len) +{ + strbuf_t *s; + + s = malloc(sizeof(strbuf_t)); + if (!s) + die("Out of memory"); + + strbuf_init(s, len); + + /* Dynamic strbuf allocation / deallocation */ + s->dynamic = 1; + + return s; +} + +void strbuf_set_increment(strbuf_t *s, int increment) +{ + /* Increment > 0: Linear buffer growth rate + * Increment < -1: Exponential buffer growth rate */ + if (increment == 0 || increment == -1) + die("BUG: Invalid string increment"); + + s->increment = increment; +} + +static inline void debug_stats(strbuf_t *s) +{ + if (s->debug) { + fprintf(stderr, "strbuf(%lx) reallocs: %d, length: %d, size: %d\n", + (long)s, s->reallocs, s->length, s->size); + } +} + +/* If strbuf_t has not been dynamically allocated, strbuf_free() can + * be called any number of times strbuf_init() */ +void strbuf_free(strbuf_t *s) +{ + debug_stats(s); + + if (s->buf) { + free(s->buf); + s->buf = NULL; + } + if (s->dynamic) + free(s); +} + +char *strbuf_free_to_string(strbuf_t *s, int *len) +{ + char *buf; + + debug_stats(s); + + strbuf_ensure_null(s); + + buf = s->buf; + if (len) + *len = s->length; + + if (s->dynamic) + free(s); + + return buf; +} + +static int calculate_new_size(strbuf_t *s, int len) +{ + int reqsize, newsize; + + if (len <= 0) + die("BUG: Invalid strbuf length requested"); + + /* Ensure there is room for optional NULL termination */ + reqsize = len + 1; + + /* If the user has requested to shrink the buffer, do it exactly */ + if (s->size > reqsize) + return reqsize; + + newsize = s->size; + if (s->increment < 0) { + /* Exponential sizing */ + while (newsize < reqsize) + newsize *= -s->increment; + } else { + /* Linear sizing */ + newsize = ((newsize + s->increment - 1) / s->increment) * s->increment; + } + + return newsize; +} + + +/* Ensure strbuf can handle a string length bytes long (ignoring NULL + * optional termination). */ +void strbuf_resize(strbuf_t *s, int len) +{ + int newsize; + + newsize = calculate_new_size(s, len); + + if (s->debug > 1) { + fprintf(stderr, "strbuf(%lx) resize: %d => %d\n", + (long)s, s->size, newsize); + } + + s->size = newsize; + s->buf = realloc(s->buf, s->size); + if (!s->buf) + die("Out of memory"); + s->reallocs++; +} + +void strbuf_append_string(strbuf_t *s, const char *str) +{ + int space, i; + + space = strbuf_empty_length(s); + + for (i = 0; str[i]; i++) { + if (space < 1) { + strbuf_resize(s, s->length + 1); + space = strbuf_empty_length(s); + } + + s->buf[s->length] = str[i]; + s->length++; + space--; + } +} + +/* strbuf_append_fmt() should only be used when an upper bound + * is known for the output string. */ +void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...) +{ + va_list arg; + int fmt_len; + + strbuf_ensure_empty_length(s, len); + + va_start(arg, fmt); + fmt_len = vsnprintf(s->buf + s->length, len, fmt, arg); + va_end(arg); + + if (fmt_len < 0) + die("BUG: Unable to convert number"); /* This should never happen.. */ + + s->length += fmt_len; +} + +/* strbuf_append_fmt_retry() can be used when the there is no known + * upper bound for the output string. */ +void strbuf_append_fmt_retry(strbuf_t *s, const char *fmt, ...) +{ + va_list arg; + int fmt_len, try; + int empty_len; + + /* If the first attempt to append fails, resize the buffer appropriately + * and try again */ + for (try = 0; ; try++) { + va_start(arg, fmt); + /* Append the new formatted string */ + /* fmt_len is the length of the string required, excluding the + * trailing NULL */ + empty_len = strbuf_empty_length(s); + /* Add 1 since there is also space to store the terminating NULL. */ + fmt_len = vsnprintf(s->buf + s->length, empty_len + 1, fmt, arg); + va_end(arg); + + if (fmt_len <= empty_len) + break; /* SUCCESS */ + if (try > 0) + die("BUG: length of formatted string changed"); + + strbuf_resize(s, s->length + fmt_len); + } + + s->length += fmt_len; +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/src/cjson/strbuf.h b/src/cjson/strbuf.h new file mode 100644 index 0000000000..5df0b7bea3 --- /dev/null +++ b/src/cjson/strbuf.h @@ -0,0 +1,159 @@ +/* strbuf - String buffer routines + * + * Copyright (c) 2010-2012 Mark Pulford <mark@kyne.com.au> + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#include <stdlib.h> +#include <stdarg.h> + +/* Workaround for MSVC */ +#ifdef _MSC_VER +#define inline __inline +#endif + +/* Size: Total bytes allocated to *buf + * Length: String length, excluding optional NULL terminator. + * Increment: Allocation increments when resizing the string buffer. + * Dynamic: True if created via strbuf_new() + */ + +typedef struct { + char *buf; + int size; + int length; + int increment; + int dynamic; + int reallocs; + int debug; +} strbuf_t; + +#ifndef STRBUF_DEFAULT_SIZE +#define STRBUF_DEFAULT_SIZE 1023 +#endif +#ifndef STRBUF_DEFAULT_INCREMENT +#define STRBUF_DEFAULT_INCREMENT -2 +#endif + +/* Initialise */ +extern strbuf_t *strbuf_new(int len); +extern void strbuf_init(strbuf_t *s, int len); +extern void strbuf_set_increment(strbuf_t *s, int increment); + +/* Release */ +extern void strbuf_free(strbuf_t *s); +extern char *strbuf_free_to_string(strbuf_t *s, int *len); + +/* Management */ +extern void strbuf_resize(strbuf_t *s, int len); +static int strbuf_empty_length(strbuf_t *s); +static int strbuf_length(strbuf_t *s); +static char *strbuf_string(strbuf_t *s, int *len); +static void strbuf_ensure_empty_length(strbuf_t *s, int len); +static char *strbuf_empty_ptr(strbuf_t *s); +static void strbuf_extend_length(strbuf_t *s, int len); + +/* Update */ +extern void strbuf_append_fmt(strbuf_t *s, int len, const char *fmt, ...); +extern void strbuf_append_fmt_retry(strbuf_t *s, const char *format, ...); +static void strbuf_append_mem(strbuf_t *s, const char *c, int len); +extern void strbuf_append_string(strbuf_t *s, const char *str); +static void strbuf_append_char(strbuf_t *s, const char c); +static void strbuf_ensure_null(strbuf_t *s); + +/* Reset string for before use */ +static inline void strbuf_reset(strbuf_t *s) +{ + s->length = 0; +} + +static inline int strbuf_allocated(strbuf_t *s) +{ + return s->buf != NULL; +} + +/* Return bytes remaining in the string buffer + * Ensure there is space for a NULL terminator. */ +static inline int strbuf_empty_length(strbuf_t *s) +{ + return s->size - s->length - 1; +} + +static inline void strbuf_ensure_empty_length(strbuf_t *s, int len) +{ + if (len > strbuf_empty_length(s)) + strbuf_resize(s, s->length + len); +} + +static inline char *strbuf_empty_ptr(strbuf_t *s) +{ + return s->buf + s->length; +} + +static inline void strbuf_extend_length(strbuf_t *s, int len) +{ + s->length += len; +} + +static inline int strbuf_length(strbuf_t *s) +{ + return s->length; +} + +static inline void strbuf_append_char(strbuf_t *s, const char c) +{ + strbuf_ensure_empty_length(s, 1); + s->buf[s->length++] = c; +} + +static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c) +{ + s->buf[s->length++] = c; +} + +static inline void strbuf_append_mem(strbuf_t *s, const char *c, int len) +{ + strbuf_ensure_empty_length(s, len); + memcpy(s->buf + s->length, c, len); + s->length += len; +} + +static inline void strbuf_append_mem_unsafe(strbuf_t *s, const char *c, int len) +{ + memcpy(s->buf + s->length, c, len); + s->length += len; +} + +static inline void strbuf_ensure_null(strbuf_t *s) +{ + s->buf[s->length] = 0; +} + +static inline char *strbuf_string(strbuf_t *s, int *len) +{ + if (len) + *len = s->length; + + return s->buf; +} + +/* vi:ai et sw=4 ts=4: + */ diff --git a/src/clint.py b/src/clint.py index e7d76366b0..4b7bf002e6 100755 --- a/src/clint.py +++ b/src/clint.py @@ -2544,6 +2544,7 @@ def CheckSpacing(filename, clean_lines, linenum, nesting_state, error): r'(?<!\bPMap)' r'(?<!\bArrayOf)' r'(?<!\bDictionaryOf)' + r'(?<!\bDict)' r'\((?:const )?(?:struct )?[a-zA-Z_]\w*(?: *\*(?:const)?)*\)' r' +' r'-?(?:\*+|&)?(?:\w+|\+\+|--|\()', cast_line) diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 331ab16dd7..9c6eafa2df 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -27,6 +27,7 @@ set(BINARY_LIB_DIR ${PROJECT_BINARY_DIR}/lib/nvim/) set(API_DISPATCH_GENERATOR ${GENERATOR_DIR}/gen_api_dispatch.lua) set(API_UI_EVENTS_GENERATOR ${GENERATOR_DIR}/gen_api_ui_events.lua) set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua) +set(GENERATOR_HASHY ${GENERATOR_DIR}/hashy.lua) set(API_METADATA ${PROJECT_BINARY_DIR}/api_metadata.mpack) set(FUNCS_DATA ${PROJECT_BINARY_DIR}/funcs_data.mpack) set(LUA_API_C_BINDINGS ${GENERATED_DIR}/lua_api_c_bindings.generated.c) @@ -42,12 +43,15 @@ set(GENERATED_UI_EVENTS_METADATA ${GENERATED_DIR}/api/private/ui_events_metadata set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h) set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h) set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h) +set(GENERATED_KEYSETS ${GENERATED_DIR}/keysets.generated.h) +set(GENERATED_KEYSETS_DEFS ${GENERATED_DIR}/keysets_defs.generated.h) set(GENERATED_EVENTS_ENUM ${GENERATED_INCLUDES_DIR}/auevents_enum.generated.h) set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h) set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h) set(EX_CMDS_GENERATOR ${GENERATOR_DIR}/gen_ex_cmds.lua) set(FUNCS_GENERATOR ${GENERATOR_DIR}/gen_eval.lua) set(EVENTS_GENERATOR ${GENERATOR_DIR}/gen_events.lua) +set(KEYSETS_GENERATOR ${GENERATOR_DIR}/gen_keysets.lua) set(OPTIONS_GENERATOR ${GENERATOR_DIR}/gen_options.lua) set(UNICODE_TABLES_GENERATOR ${GENERATOR_DIR}/gen_unicode_tables.lua) set(UNICODE_DIR ${PROJECT_SOURCE_DIR}/unicode) @@ -87,8 +91,8 @@ file(MAKE_DIRECTORY ${LINT_SUPPRESSES_ROOT}/src) file(GLOB NVIM_SOURCES *.c) file(GLOB NVIM_HEADERS *.h) -file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c) -file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h) +file(GLOB EXTERNAL_SOURCES ../xdiff/*.c ../mpack/*.c ../cjson/*.c) +file(GLOB EXTERNAL_HEADERS ../xdiff/*.h ../mpack/*.h ../cjson/*.h) foreach(subdir os @@ -171,7 +175,7 @@ foreach(sfile ${CONV_SOURCES}) message(FATAL_ERROR "${sfile} doesn't exist (it was added to CONV_SOURCES)") endif() endforeach() -# xdiff, mpack: inlined external project, we don't maintain it. #9306 +# xdiff, mpack, lua-cjson: inlined external project, we don't maintain it. #9306 list(APPEND CONV_SOURCES ${EXTERNAL_SOURCES}) if(NOT MSVC) @@ -261,6 +265,7 @@ foreach(sfile ${NVIM_SOURCES} "${GENERATED_UI_EVENTS_CALL}" "${GENERATED_UI_EVENTS_REMOTE}" "${GENERATED_UI_EVENTS_BRIDGE}" + "${GENERATED_KEYSETS}" ) get_filename_component(full_d ${sfile} PATH) file(RELATIVE_PATH d "${CMAKE_CURRENT_LIST_DIR}" "${full_d}") @@ -364,12 +369,14 @@ add_custom_command( list(APPEND NVIM_GENERATED_FOR_HEADERS "${GENERATED_EX_CMDS_ENUM}" "${GENERATED_EVENTS_ENUM}" + "${GENERATED_KEYSETS_DEFS}" ) list(APPEND NVIM_GENERATED_FOR_SOURCES "${GENERATED_API_DISPATCH}" "${GENERATED_EX_CMDS_DEFS}" "${GENERATED_EVENTS_NAMES_MAP}" + "${GENERATED_KEYSETS}" "${GENERATED_OPTIONS}" "${GENERATED_UNICODE_TABLES}" "${VIM_MODULE_FILE}" @@ -404,6 +411,12 @@ add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP} DEPENDS ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua ) +add_custom_command(OUTPUT ${GENERATED_KEYSETS} ${GENERATED_KEYSETS_DEFS} + COMMAND ${LUA_PRG} ${KEYSETS_GENERATOR} + ${CMAKE_CURRENT_LIST_DIR} ${LUA_SHARED_MODULE_SOURCE} ${GENERATED_KEYSETS} ${GENERATED_KEYSETS_DEFS} + DEPENDS ${KEYSETS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/api/keysets.lua ${GENERATOR_HASHY} +) + add_custom_command(OUTPUT ${GENERATED_OPTIONS} COMMAND ${LUA_PRG} ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR} ${GENERATED_OPTIONS} diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 8973f8fef6..34d7f6e32d 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -858,7 +858,7 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err) /// @see |nvim_set_keymap()| /// /// @param buffer Buffer handle, or 0 for current buffer -void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dictionary opts, +void nvim_buf_set_keymap(Buffer buffer, String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err) FUNC_API_SINCE(6) { @@ -874,8 +874,7 @@ void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err) FUNC_API_SINCE(6) { String rhs = { .data = "", .size = 0 }; - Dictionary opts = ARRAY_DICT_INIT; - modify_keymap(buffer, true, mode, lhs, rhs, opts, err); + modify_keymap(buffer, true, mode, lhs, rhs, NULL, err); } /// Gets a map of buffer-local |user-commands|. @@ -885,22 +884,13 @@ void nvim_buf_del_keymap(Buffer buffer, String mode, String lhs, Error *err) /// @param[out] err Error details, if any. /// /// @returns Map of maps describing commands. -Dictionary nvim_buf_get_commands(Buffer buffer, Dictionary opts, Error *err) +Dictionary nvim_buf_get_commands(Buffer buffer, Dict(get_commands) *opts, Error *err) FUNC_API_SINCE(4) { bool global = (buffer == -1); - bool builtin = false; - - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object v = opts.items[i].value; - if (!strequal("builtin", k.data)) { - api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); - return (Dictionary)ARRAY_DICT_INIT; - } - if (strequal("builtin", k.data)) { - builtin = v.data.boolean; - } + bool builtin = api_object_to_bool(opts->builtin, "builtin", false, err); + if (ERROR_SET(err)) { + return (Dictionary)ARRAY_DICT_INIT; } if (global) { @@ -1415,6 +1405,10 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// - end_col : ending col of the mark, 0-based exclusive. /// - hl_group : name of the highlight group used to highlight /// this mark. +/// - hl_eol : when true, for a multiline highlight covering the +/// EOL of a line, continue the highlight for the rest +/// of the screen line (just like for diff and +/// cursorline highlight). /// - virt_text : virtual text to link to this mark. /// A list of [text, highlight] tuples, each representing a /// text chunk with specified highlight. `highlight` element @@ -1442,10 +1436,28 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// default /// - "combine": combine with background text color /// - "blend": blend with background text color. -/// - hl_eol : when true, for a multiline highlight covering the -/// EOL of a line, continue the highlight for the rest -/// of the screen line (just like for diff and -/// cursorline highlight). +/// +/// - virt_lines : virtual lines to add next to this mark +/// This should be an array over lines, where each line in +/// turn is an array over [text, highlight] tuples. In +/// general, buffer and window options do not affect the +/// display of the text. In particular 'wrap' +/// and 'linebreak' options do not take effect, so +/// the number of extra screen lines will always match +/// the size of the array. However the 'tabstop' buffer +/// option is still used for hard tabs. By default lines are +/// placed below the buffer line containing the mark. +/// +/// Note: currently virtual lines are limited to one block +/// per buffer. Thus setting a new mark disables any previous +/// `virt_lines` decoration. However plugins should not rely +/// on this behaviour, as this limitation is planned to be +/// removed. +/// +/// - virt_lines_above: place virtual lines above instead. +/// - virt_lines_leftcol: Place extmarks in the leftmost +/// column of the window, bypassing +/// sign and number columns. /// /// - ephemeral : for use with |nvim_set_decoration_provider| /// callbacks. The mark will only be used for the current @@ -1463,7 +1475,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id, Object start, Object e /// @param[out] err Error details, if any /// @return Id of the created/updated extmark Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col, - Dictionary opts, Error *err) + Dict(set_extmark) *opts, Error *err) FUNC_API_SINCE(7) { buf_T *buf = find_buffer_by_handle(buffer, err); @@ -1476,177 +1488,174 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer return 0; } - bool ephemeral = false; uint64_t id = 0; + if (opts->id.type == kObjectTypeInteger && opts->id.data.integer > 0) { + id = (uint64_t)opts->id.data.integer; + } else if (HAS_KEY(opts->id)) { + api_set_error(err, kErrorTypeValidation, "id is not a positive integer"); + goto error; + } + int line2 = -1; - Decoration decor = DECORATION_INIT; + if (opts->end_line.type == kObjectTypeInteger) { + Integer val = opts->end_line.data.integer; + if (val < 0 || val > buf->b_ml.ml_line_count) { + api_set_error(err, kErrorTypeValidation, "end_line value outside range"); + goto error; + } else { + line2 = (int)val; + } + } else if (HAS_KEY(opts->end_line)) { + api_set_error(err, kErrorTypeValidation, "end_line is not an integer"); + goto error; + } + colnr_T col2 = -1; + if (opts->end_col.type == kObjectTypeInteger) { + Integer val = opts->end_col.data.integer; + if (val < 0 || val > MAXCOL) { + api_set_error(err, kErrorTypeValidation, "end_col value outside range"); + goto error; + } else { + col2 = (int)val; + } + } else if (HAS_KEY(opts->end_col)) { + api_set_error(err, kErrorTypeValidation, "end_col is not an integer"); + goto error; + } - bool right_gravity = true; - bool end_right_gravity = false; - bool end_gravity_set = false; + Decoration decor = DECORATION_INIT; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object *v = &opts.items[i].value; - if (strequal("id", k.data)) { - if (v->type != kObjectTypeInteger || v->data.integer <= 0) { - api_set_error(err, kErrorTypeValidation, - "id is not a positive integer"); - goto error; - } + if (HAS_KEY(opts->hl_group)) { + decor.hl_id = object_to_hl_id(opts->hl_group, "hl_group", err); + if (ERROR_SET(err)) { + goto error; + } + } - id = (uint64_t)v->data.integer; - } else if (strequal("end_line", k.data)) { - if (v->type != kObjectTypeInteger) { - api_set_error(err, kErrorTypeValidation, - "end_line is not an integer"); - goto error; - } - if (v->data.integer < 0 || v->data.integer > buf->b_ml.ml_line_count) { - api_set_error(err, kErrorTypeValidation, - "end_line value outside range"); - goto error; - } + if (opts->virt_text.type == kObjectTypeArray) { + decor.virt_text = parse_virt_text(opts->virt_text.data.array, err, + &decor.virt_text_width); + if (ERROR_SET(err)) { + goto error; + } + } else if (HAS_KEY(opts->virt_text)) { + api_set_error(err, kErrorTypeValidation, "virt_text is not an Array"); + goto error; + } + + if (opts->virt_text_pos.type == kObjectTypeString) { + String str = opts->virt_text_pos.data.string; + if (strequal("eol", str.data)) { + decor.virt_text_pos = kVTEndOfLine; + } else if (strequal("overlay", str.data)) { + decor.virt_text_pos = kVTOverlay; + } else if (strequal("right_align", str.data)) { + decor.virt_text_pos = kVTRightAlign; + } else { + api_set_error(err, kErrorTypeValidation, "virt_text_pos: invalid value"); + goto error; + } + } else if (HAS_KEY(opts->virt_text_pos)) { + api_set_error(err, kErrorTypeValidation, "virt_text_pos is not a String"); + goto error; + } - line2 = (int)v->data.integer; - } else if (strequal("end_col", k.data)) { - if (v->type != kObjectTypeInteger) { - api_set_error(err, kErrorTypeValidation, - "end_col is not an integer"); - goto error; - } - if (v->data.integer < 0 || v->data.integer > MAXCOL) { - api_set_error(err, kErrorTypeValidation, - "end_col value outside range"); - goto error; - } + if (opts->virt_text_win_col.type == kObjectTypeInteger) { + decor.col = (int)opts->virt_text_win_col.data.integer; + decor.virt_text_pos = kVTWinCol; + } else if (HAS_KEY(opts->virt_text_win_col)) { + api_set_error(err, kErrorTypeValidation, + "virt_text_win_col is not a Number of the correct size"); + goto error; + } - col2 = (colnr_T)v->data.integer; - } else if (strequal("hl_group", k.data)) { - String hl_group; - switch (v->type) { - case kObjectTypeString: - hl_group = v->data.string; - decor.hl_id = syn_check_group((char_u *)(hl_group.data), - (int)hl_group.size); - break; - case kObjectTypeInteger: - decor.hl_id = (int)v->data.integer; - break; - default: - api_set_error(err, kErrorTypeValidation, - "hl_group is not valid."); - goto error; - } - } else if (strequal("virt_text", k.data)) { - if (v->type != kObjectTypeArray) { - api_set_error(err, kErrorTypeValidation, - "virt_text is not an Array"); - goto error; - } - decor.virt_text = parse_virt_text(v->data.array, err, - &decor.virt_text_width); - if (ERROR_SET(err)) { - goto error; - } - } else if (strequal("virt_text_pos", k.data)) { - if (v->type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, - "virt_text_pos is not a String"); - goto error; - } - String str = v->data.string; - if (strequal("eol", str.data)) { - decor.virt_text_pos = kVTEndOfLine; - } else if (strequal("overlay", str.data)) { - decor.virt_text_pos = kVTOverlay; - } else if (strequal("right_align", str.data)) { - decor.virt_text_pos = kVTRightAlign; - } else { - api_set_error(err, kErrorTypeValidation, - "virt_text_pos: invalid value"); - goto error; - } - } else if (strequal("virt_text_win_col", k.data)) { - if (v->type != kObjectTypeInteger) { - api_set_error(err, kErrorTypeValidation, - "virt_text_win_col is not a Number of the correct size"); - goto error; - } +#define OPTION_TO_BOOL(target, name, val) \ + target = api_object_to_bool(opts-> name, #name, val, err); \ + if (ERROR_SET(err)) { \ + goto error; \ + } - decor.col = (int)v->data.integer; - decor.virt_text_pos = kVTWinCol; - } else if (strequal("virt_text_hide", k.data)) { - decor.virt_text_hide = api_object_to_bool(*v, - "virt_text_hide", false, err); - if (ERROR_SET(err)) { - goto error; - } - } else if (strequal("hl_eol", k.data)) { - decor.hl_eol = api_object_to_bool(*v, "hl_eol", false, err); - if (ERROR_SET(err)) { - goto error; - } - } else if (strequal("hl_mode", k.data)) { - if (v->type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, - "hl_mode is not a String"); - goto error; - } - String str = v->data.string; - if (strequal("replace", str.data)) { - decor.hl_mode = kHlModeReplace; - } else if (strequal("combine", str.data)) { - decor.hl_mode = kHlModeCombine; - } else if (strequal("blend", str.data)) { - decor.hl_mode = kHlModeBlend; - } else { - api_set_error(err, kErrorTypeValidation, - "virt_text_pos: invalid value"); + OPTION_TO_BOOL(decor.virt_text_hide, virt_text_hide, false); + OPTION_TO_BOOL(decor.hl_eol, hl_eol, false); + + if (opts->hl_mode.type == kObjectTypeString) { + String str = opts->hl_mode.data.string; + if (strequal("replace", str.data)) { + decor.hl_mode = kHlModeReplace; + } else if (strequal("combine", str.data)) { + decor.hl_mode = kHlModeCombine; + } else if (strequal("blend", str.data)) { + decor.hl_mode = kHlModeBlend; + } else { + api_set_error(err, kErrorTypeValidation, + "virt_text_pos: invalid value"); + goto error; + } + } else if (HAS_KEY(opts->hl_mode)) { + api_set_error(err, kErrorTypeValidation, "hl_mode is not a String"); + goto error; + } + + VirtLines virt_lines = KV_INITIAL_VALUE; + bool virt_lines_above = false; + bool virt_lines_leftcol = false; + + if (opts->virt_lines.type == kObjectTypeArray) { + Array a = opts->virt_lines.data.array; + for (size_t j = 0; j < a.size; j++) { + if (a.items[j].type != kObjectTypeArray) { + api_set_error(err, kErrorTypeValidation, "virt_text_line item is not an Array"); goto error; } - } else if (strequal("ephemeral", k.data)) { - ephemeral = api_object_to_bool(*v, "ephemeral", false, err); + int dummig; + VirtText jtem = parse_virt_text(a.items[j].data.array, err, &dummig); + kv_push(virt_lines, jtem); if (ERROR_SET(err)) { goto error; } - } else if (strequal("priority", k.data)) { - if (v->type != kObjectTypeInteger) { - api_set_error(err, kErrorTypeValidation, - "priority is not a Number of the correct size"); - goto error; - } + } + } else if (HAS_KEY(opts->virt_lines)) { + api_set_error(err, kErrorTypeValidation, "virt_lines is not an Array"); + goto error; + } - if (v->data.integer < 0 || v->data.integer > UINT16_MAX) { - api_set_error(err, kErrorTypeValidation, - "priority is not a valid value"); - goto error; - } - decor.priority = (DecorPriority)v->data.integer; - } else if (strequal("right_gravity", k.data)) { - if (v->type != kObjectTypeBoolean) { - api_set_error(err, kErrorTypeValidation, - "right_gravity must be a boolean"); - goto error; - } - right_gravity = v->data.boolean; - } else if (strequal("end_right_gravity", k.data)) { - if (v->type != kObjectTypeBoolean) { - api_set_error(err, kErrorTypeValidation, - "end_right_gravity must be a boolean"); - goto error; - } - end_right_gravity = v->data.boolean; - end_gravity_set = true; - } else { - api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); + OPTION_TO_BOOL(virt_lines_above, virt_lines_above, false); + OPTION_TO_BOOL(virt_lines_leftcol, virt_lines_leftcol, false); + + if (opts->priority.type == kObjectTypeInteger) { + Integer val = opts->priority.data.integer; + + if (val < 0 || val > UINT16_MAX) { + api_set_error(err, kErrorTypeValidation, "priority is not a valid value"); goto error; } + decor.priority = (DecorPriority)val; + } else if (HAS_KEY(opts->priority)) { + api_set_error(err, kErrorTypeValidation, "priority is not a Number of the correct size"); + goto error; + } + + bool right_gravity = true; + OPTION_TO_BOOL(right_gravity, right_gravity, true); + + // Only error out if they try to set end_right_gravity without + // setting end_col or end_line + if (line2 == -1 && col2 == -1 && HAS_KEY(opts->end_right_gravity)) { + api_set_error(err, kErrorTypeValidation, + "cannot set end_right_gravity without setting end_line or end_col"); + goto error; } + bool end_right_gravity = false; + OPTION_TO_BOOL(end_right_gravity, end_right_gravity, false); + size_t len = 0; + + bool ephemeral = false; + OPTION_TO_BOOL(ephemeral, ephemeral, false); + if (line < 0 || line > buf->b_ml.ml_line_count) { api_set_error(err, kErrorTypeValidation, "line value outside range"); return 0; @@ -1661,15 +1670,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer return 0; } - - // Only error out if they try to set end_right_gravity without - // setting end_col or end_line - if (line2 == -1 && col2 == -1 && end_gravity_set) { - api_set_error(err, kErrorTypeValidation, - "cannot set end_right_gravity " - "without setting end_line or end_col"); - } - if (col2 >= 0) { if (line2 >= 0 && line2 < buf->b_ml.ml_line_count) { len = ephemeral ? MAXCOL : STRLEN(ml_get_buf(buf, (linenr_T)line2 + 1, false)); @@ -1688,15 +1688,6 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer col2 = 0; } - if (decor.virt_text_pos == kVTRightAlign) { - decor.col = 0; - for (size_t i = 0; i < kv_size(decor.virt_text); i++) { - decor.col - += (int)mb_string2cells((char_u *)kv_A(decor.virt_text, i).text); - } - } - - Decoration *d = NULL; if (ephemeral) { @@ -1721,9 +1712,23 @@ Integer nvim_buf_set_extmark(Buffer buffer, Integer ns_id, Integer line, Integer goto error; } - id = extmark_set(buf, (uint64_t)ns_id, id, (int)line, (colnr_T)col, - line2, col2, d, right_gravity, - end_right_gravity, kExtmarkNoUndo); + if (kv_size(virt_lines) && buf->b_virt_line_mark) { + mtpos_t pos = marktree_lookup(buf->b_marktree, buf->b_virt_line_mark, NULL); + clear_virt_lines(buf, pos.row); // handles pos.row == -1 + } + + uint64_t mark = extmark_set(buf, (uint64_t)ns_id, &id, (int)line, (colnr_T)col, + line2, col2, d, right_gravity, + end_right_gravity, kExtmarkNoUndo); + + if (kv_size(virt_lines)) { + buf->b_virt_lines = virt_lines; + buf->b_virt_line_mark = mark; + buf->b_virt_line_pos = -1; + buf->b_virt_line_above = virt_lines_above; + buf->b_virt_line_leftcol = virt_lines_leftcol; + redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, line+1+(virt_lines_above?0:1))); + } } return (Integer)id; @@ -1827,7 +1832,7 @@ Integer nvim_buf_add_highlight(Buffer buffer, Integer ns_id, String hl_group, In end_line++; } - extmark_set(buf, ns, 0, + extmark_set(buf, ns, NULL, (int)line, (colnr_T)col_start, end_line, (colnr_T)col_end, decor_hl(hl_id), true, false, kExtmarkNoUndo); diff --git a/src/nvim/api/deprecated.c b/src/nvim/api/deprecated.c index 21b9db85c0..332fc0ba96 100644 --- a/src/nvim/api/deprecated.c +++ b/src/nvim/api/deprecated.c @@ -150,7 +150,7 @@ Integer nvim_buf_set_virtual_text(Buffer buffer, Integer src_id, Integer line, A decor->virt_text = virt_text; decor->virt_text_width = width; - extmark_set(buf, ns_id, 0, (int)line, 0, -1, -1, decor, true, + extmark_set(buf, ns_id, NULL, (int)line, 0, -1, -1, decor, true, false, kExtmarkNoUndo); return src_id; } diff --git a/src/nvim/api/keysets.lua b/src/nvim/api/keysets.lua new file mode 100644 index 0000000000..76ce9e15ea --- /dev/null +++ b/src/nvim/api/keysets.lua @@ -0,0 +1,52 @@ +return { + context = { + "types"; + }; + set_extmark = { + "id"; + "end_line"; + "end_col"; + "hl_group"; + "virt_text"; + "virt_text_pos"; + "virt_text_win_col"; + "virt_text_hide"; + "hl_eol"; + "hl_mode"; + "ephemeral"; + "priority"; + "right_gravity"; + "end_right_gravity"; + "virt_lines"; + "virt_lines_above"; + "virt_lines_leftcol"; + }; + keymap = { + "noremap"; + "nowait"; + "silent"; + "script"; + "expr"; + "unique"; + }; + get_commands = { + "builtin"; + }; + float_config = { + "row"; + "col"; + "width"; + "height"; + "anchor"; + "relative"; + "win"; + "bufpos"; + "external"; + "focusable"; + "zindex"; + "border"; + "style"; + "noautocmd"; + }; +} + diff --git a/src/nvim/api/private/defs.h b/src/nvim/api/private/defs.h index f0d48bf145..8346e01558 100644 --- a/src/nvim/api/private/defs.h +++ b/src/nvim/api/private/defs.h @@ -19,6 +19,7 @@ #ifdef INCLUDE_GENERATED_DECLARATIONS # define ArrayOf(...) Array # define DictionaryOf(...) Dictionary +# define Dict(name) KeyDict_##name #endif // Basic types @@ -129,5 +130,14 @@ struct key_value_pair { Object value; }; +typedef Object *(*field_hash)(void *retval, const char *str, size_t len); +typedef struct { + char *str; + size_t ptr_off; +} KeySetLink; + +#ifdef INCLUDE_GENERATED_DECLARATIONS +# include "keysets_defs.generated.h" +#endif #endif // NVIM_API_PRIVATE_DEFS_H diff --git a/src/nvim/api/private/dispatch.c b/src/nvim/api/private/dispatch.c index 38ce7ca78c..5e93ccce17 100644 --- a/src/nvim/api/private/dispatch.c +++ b/src/nvim/api/private/dispatch.c @@ -45,5 +45,5 @@ MsgpackRpcRequestHandler msgpack_rpc_get_handler_for(const char *name, size_t na } #ifdef INCLUDE_GENERATED_DECLARATIONS -#include "api/private/dispatch_wrappers.generated.h" +# include "api/private/dispatch_wrappers.generated.h" #endif diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 541793e528..24fac0a916 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -6,6 +6,7 @@ #include <stdbool.h> #include <stdlib.h> #include <string.h> +#include <stddef.h> #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" @@ -814,7 +815,7 @@ Array string_to_array(const String input, bool crlf) /// buffer, or -1 to signify global behavior ("all buffers") /// @param is_unmap When true, removes the mapping that matches {lhs}. void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String rhs, - Dictionary opts, Error *err) + Dict(keymap) *opts, Error *err) { char *err_msg = NULL; // the error message to report, if any char *err_arg = NULL; // argument for the error message format string @@ -833,10 +834,21 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String return; } - MapArguments parsed_args; - memset(&parsed_args, 0, sizeof(parsed_args)); - if (parse_keymap_opts(opts, &parsed_args, err)) { - goto fail_and_free; + MapArguments parsed_args = MAP_ARGUMENTS_INIT; + if (opts) { +#define KEY_TO_BOOL(name) \ + parsed_args. name = api_object_to_bool(opts-> name, #name, false, err); \ + if (ERROR_SET(err)) { \ + goto fail_and_free; \ + } + + KEY_TO_BOOL(nowait); + KEY_TO_BOOL(noremap); + KEY_TO_BOOL(silent); + KEY_TO_BOOL(script); + KEY_TO_BOOL(expr); + KEY_TO_BOOL(unique); +#undef KEY_TO_BOOL } parsed_args.buffer = !global; @@ -947,95 +959,6 @@ fail_and_free: return; } -/// Read in the given opts, setting corresponding flags in `out`. -/// -/// @param opts A dictionary passed to @ref nvim_set_keymap or -/// @ref nvim_buf_set_keymap. -/// @param[out] out MapArguments object in which to set parsed -/// |:map-arguments| flags. -/// @param[out] err Error details, if any. -/// -/// @returns Zero on success, nonzero on failure. -Integer parse_keymap_opts(Dictionary opts, MapArguments *out, Error *err) -{ - char *err_msg = NULL; // the error message to report, if any - char *err_arg = NULL; // argument for the error message format string - ErrorType err_type = kErrorTypeNone; - - out->buffer = false; - out->nowait = false; - out->silent = false; - out->script = false; - out->expr = false; - out->unique = false; - - for (size_t i = 0; i < opts.size; i++) { - KeyValuePair *key_and_val = &opts.items[i]; - char *optname = key_and_val->key.data; - - if (key_and_val->value.type != kObjectTypeBoolean) { - err_msg = "Gave non-boolean value for an opt: %s"; - err_arg = optname; - err_type = kErrorTypeValidation; - goto fail_with_message; - } - - bool was_valid_opt = false; - switch (optname[0]) { - // note: strncmp up to and including the null terminator, so that - // "nowaitFoobar" won't match against "nowait" - - // don't recognize 'buffer' as a key; user shouldn't provide <buffer> - // when calling nvim_set_keymap or nvim_buf_set_keymap, since it can be - // inferred from which function they called - case 'n': - if (STRNCMP(optname, "noremap", 8) == 0) { - was_valid_opt = true; - out->noremap = key_and_val->value.data.boolean; - } else if (STRNCMP(optname, "nowait", 7) == 0) { - was_valid_opt = true; - out->nowait = key_and_val->value.data.boolean; - } - break; - case 's': - if (STRNCMP(optname, "silent", 7) == 0) { - was_valid_opt = true; - out->silent = key_and_val->value.data.boolean; - } else if (STRNCMP(optname, "script", 7) == 0) { - was_valid_opt = true; - out->script = key_and_val->value.data.boolean; - } - break; - case 'e': - if (STRNCMP(optname, "expr", 5) == 0) { - was_valid_opt = true; - out->expr = key_and_val->value.data.boolean; - } - break; - case 'u': - if (STRNCMP(optname, "unique", 7) == 0) { - was_valid_opt = true; - out->unique = key_and_val->value.data.boolean; - } - break; - default: - break; - } // switch - if (!was_valid_opt) { - err_msg = "Invalid key: %s"; - err_arg = optname; - err_type = kErrorTypeValidation; - goto fail_with_message; - } - } // for - - return 0; - -fail_with_message: - api_set_error(err, err_type, err_msg, err_arg); - return 1; -} - /// Collects `n` buffer lines into array `l`, optionally replacing newlines /// with NUL. /// @@ -1413,8 +1336,10 @@ static void set_option_value_for(char *key, int numval, char *stringval, int opt switch (opt_type) { case SREQ_WIN: - if (switch_win(&save_curwin, &save_curtab, (win_T *)from, - win_find_tabpage((win_T *)from), false) == FAIL) { + if (switch_win_noblock(&save_curwin, &save_curtab, (win_T *)from, + win_find_tabpage((win_T *)from), true) + == FAIL) { + restore_win_noblock(save_curwin, save_curtab, true); if (try_end(err)) { return; } @@ -1424,7 +1349,7 @@ static void set_option_value_for(char *key, int numval, char *stringval, int opt return; } set_option_value_err(key, numval, stringval, opt_flags, err); - restore_win(save_curwin, save_curtab, true); + restore_win_noblock(save_curwin, save_curtab, true); break; case SREQ_BUF: aucmd_prepbuf(&aco, (buf_T *)from); @@ -1625,7 +1550,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width) } } - char *text = transstr(str.size > 0 ? str.data : ""); // allocates + char *text = transstr(str.size > 0 ? str.data : "", false); // allocates w += (int)mb_string2cells((char_u *)text); kv_push(virt_text, ((VirtTextChunk){ .text = text, .hl_id = hl_id })); @@ -1877,213 +1802,227 @@ static void parse_border_style(Object style, FloatConfig *fconfig, Error *err) } } -bool parse_float_config(Dictionary config, FloatConfig *fconfig, bool reconf, bool new_win, +bool parse_float_config(Dict(float_config) *config, FloatConfig *fconfig, bool reconf, bool new_win, Error *err) { - // TODO(bfredl): use a get/has_key interface instead and get rid of extra - // flags - bool has_row = false, has_col = false, has_relative = false; - bool has_external = false, has_window = false; - bool has_width = false, has_height = false; - bool has_bufpos = false; - - for (size_t i = 0; i < config.size; i++) { - char *key = config.items[i].key.data; - Object val = config.items[i].value; - if (!strcmp(key, "row")) { - has_row = true; - if (val.type == kObjectTypeInteger) { - fconfig->row = (double)val.data.integer; - } else if (val.type == kObjectTypeFloat) { - fconfig->row = val.data.floating; - } else { - api_set_error(err, kErrorTypeValidation, - "'row' key must be Integer or Float"); - return false; - } - } else if (!strcmp(key, "col")) { - has_col = true; - if (val.type == kObjectTypeInteger) { - fconfig->col = (double)val.data.integer; - } else if (val.type == kObjectTypeFloat) { - fconfig->col = val.data.floating; - } else { - api_set_error(err, kErrorTypeValidation, - "'col' key must be Integer or Float"); - return false; - } - } else if (strequal(key, "width")) { - has_width = true; - if (val.type == kObjectTypeInteger && val.data.integer > 0) { - fconfig->width = (int)val.data.integer; - } else { - api_set_error(err, kErrorTypeValidation, - "'width' key must be a positive Integer"); + bool has_relative = false, relative_is_win = false; + if (config->relative.type == kObjectTypeString) { + // ignore empty string, to match nvim_win_get_config + if (config->relative.data.string.size > 0) { + if (!parse_float_relative(config->relative.data.string, &fconfig->relative)) { + api_set_error(err, kErrorTypeValidation, "Invalid value of 'relative' key"); return false; } - } else if (strequal(key, "height")) { - has_height = true; - if (val.type == kObjectTypeInteger && val.data.integer > 0) { - fconfig->height = (int)val.data.integer; - } else { - api_set_error(err, kErrorTypeValidation, - "'height' key must be a positive Integer"); - return false; - } - } else if (!strcmp(key, "anchor")) { - if (val.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, - "'anchor' key must be String"); - return false; - } - if (!parse_float_anchor(val.data.string, &fconfig->anchor)) { - api_set_error(err, kErrorTypeValidation, - "Invalid value of 'anchor' key"); - return false; - } - } else if (!strcmp(key, "relative")) { - if (val.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, - "'relative' key must be String"); - return false; - } - // ignore empty string, to match nvim_win_get_config - if (val.data.string.size > 0) { - has_relative = true; - if (!parse_float_relative(val.data.string, &fconfig->relative)) { - api_set_error(err, kErrorTypeValidation, - "Invalid value of 'relative' key"); - return false; - } - } - } else if (!strcmp(key, "win")) { - has_window = true; - if (val.type != kObjectTypeInteger - && val.type != kObjectTypeWindow) { - api_set_error(err, kErrorTypeValidation, - "'win' key must be Integer or Window"); - return false; - } - fconfig->window = (Window)val.data.integer; - } else if (!strcmp(key, "bufpos")) { - if (val.type != kObjectTypeArray) { - api_set_error(err, kErrorTypeValidation, - "'bufpos' key must be Array"); - return false; - } - if (!parse_float_bufpos(val.data.array, &fconfig->bufpos)) { - api_set_error(err, kErrorTypeValidation, - "Invalid value of 'bufpos' key"); - return false; - } - has_bufpos = true; - } else if (!strcmp(key, "external")) { - has_external = fconfig->external - = api_object_to_bool(val, "'external' key", false, err); - if (ERROR_SET(err)) { - return false; - } - } else if (!strcmp(key, "focusable")) { - fconfig->focusable - = api_object_to_bool(val, "'focusable' key", true, err); - if (ERROR_SET(err)) { - return false; - } - } else if (strequal(key, "zindex")) { - if (val.type == kObjectTypeInteger && val.data.integer > 0) { - fconfig->zindex = (int)val.data.integer; - } else { + + if (!(HAS_KEY(config->row) && HAS_KEY(config->col)) && !HAS_KEY(config->bufpos)) { api_set_error(err, kErrorTypeValidation, - "'zindex' key must be a positive Integer"); + "'relative' requires 'row'/'col' or 'bufpos'"); return false; } - } else if (!strcmp(key, "border")) { - parse_border_style(val, fconfig, err); - if (ERROR_SET(err)) { - return false; + + has_relative = true; + fconfig->external = false; + if (fconfig->relative == kFloatRelativeWindow) { + relative_is_win = true; + fconfig->bufpos.lnum = -1; } - } else if (!strcmp(key, "style")) { - if (val.type != kObjectTypeString) { - api_set_error(err, kErrorTypeValidation, - "'style' key must be String"); + } + } else if (HAS_KEY(config->relative)) { + api_set_error(err, kErrorTypeValidation, "'relative' key must be String"); + return false; + } + + if (config->anchor.type == kObjectTypeString) { + if (!parse_float_anchor(config->anchor.data.string, &fconfig->anchor)) { + api_set_error(err, kErrorTypeValidation, "Invalid value of 'anchor' key"); + return false; + } + } else if (HAS_KEY(config->anchor)) { + api_set_error(err, kErrorTypeValidation, "'anchor' key must be String"); + return false; + } + + if (HAS_KEY(config->row)) { + if (!has_relative) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'row'"); + return false; + } else if (config->row.type == kObjectTypeInteger) { + fconfig->row = (double)config->row.data.integer; + } else if (config->row.type == kObjectTypeFloat) { + fconfig->row = config->row.data.floating; + } else { + api_set_error(err, kErrorTypeValidation, + "'row' key must be Integer or Float"); + return false; + } + } + + if (HAS_KEY(config->col)) { + if (!has_relative) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'col'"); + return false; + } else if (config->col.type == kObjectTypeInteger) { + fconfig->col = (double)config->col.data.integer; + } else if (config->col.type == kObjectTypeFloat) { + fconfig->col = config->col.data.floating; + } else { + api_set_error(err, kErrorTypeValidation, + "'col' key must be Integer or Float"); + return false; + } + } + + if (HAS_KEY(config->bufpos)) { + if (!has_relative) { + api_set_error(err, kErrorTypeValidation, "non-float cannot have 'bufpos'"); + return false; + } else if (config->bufpos.type == kObjectTypeArray) { + if (!parse_float_bufpos(config->bufpos.data.array, &fconfig->bufpos)) { + api_set_error(err, kErrorTypeValidation, "Invalid value of 'bufpos' key"); return false; } - if (val.data.string.data[0] == NUL) { - fconfig->style = kWinStyleUnused; - } else if (striequal(val.data.string.data, "minimal")) { - fconfig->style = kWinStyleMinimal; - } else { - api_set_error(err, kErrorTypeValidation, - "Invalid value of 'style' key"); + + if (!HAS_KEY(config->row)) { + fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1; } - } else if (strequal(key, "noautocmd") && new_win) { - fconfig->noautocmd - = api_object_to_bool(val, "'noautocmd' key", false, err); - if (ERROR_SET(err)) { - return false; + if (!HAS_KEY(config->col)) { + fconfig->col = 0; } } else { - api_set_error(err, kErrorTypeValidation, - "Invalid key '%s'", key); + api_set_error(err, kErrorTypeValidation, "'bufpos' key must be Array"); return false; } } - if (has_window && !(has_relative - && fconfig->relative == kFloatRelativeWindow)) { - api_set_error(err, kErrorTypeValidation, - "'win' key is only valid with relative='win'"); + if (config->width.type == kObjectTypeInteger && config->width.data.integer > 0) { + fconfig->width = (int)config->width.data.integer; + } else if (HAS_KEY(config->width)) { + api_set_error(err, kErrorTypeValidation, "'width' key must be a positive Integer"); + return false; + } else if (!reconf) { + api_set_error(err, kErrorTypeValidation, "Must specify 'width'"); return false; } - if ((has_relative && fconfig->relative == kFloatRelativeWindow) - && (!has_window || fconfig->window == 0)) { - fconfig->window = curwin->handle; + if (config->height.type == kObjectTypeInteger && config->height.data.integer > 0) { + fconfig->height = (int)config->height.data.integer; + } else if (HAS_KEY(config->height)) { + api_set_error(err, kErrorTypeValidation, "'height' key must be a positive Integer"); + return false; + } else if (!reconf) { + api_set_error(err, kErrorTypeValidation, "Must specify 'height'"); + return false; } - if (has_window && !has_bufpos) { - fconfig->bufpos.lnum = -1; + if (relative_is_win) { + fconfig->window = curwin->handle; + if (config->win.type == kObjectTypeInteger || config->win.type == kObjectTypeWindow) { + if (config->win.data.integer > 0) { + fconfig->window = (Window)config->win.data.integer; + } + } else if (HAS_KEY(config->win)) { + api_set_error(err, kErrorTypeValidation, "'win' key must be Integer or Window"); + return false; + } + } else { + if (HAS_KEY(config->win)) { + api_set_error(err, kErrorTypeValidation, "'win' key is only valid with relative='win'"); + return false; + } } - if (has_bufpos) { - if (!has_row) { - fconfig->row = (fconfig->anchor & kFloatAnchorSouth) ? 0 : 1; - has_row = true; + if (HAS_KEY(config->external)) { + fconfig->external = api_object_to_bool(config->external, "'external' key", false, err); + if (ERROR_SET(err)) { + return false; } - if (!has_col) { - fconfig->col = 0; - has_col = true; + if (has_relative && fconfig->external) { + api_set_error(err, kErrorTypeValidation, + "Only one of 'relative' and 'external' must be used"); + return false; + } + if (fconfig->external && !ui_has(kUIMultigrid)) { + api_set_error(err, kErrorTypeValidation, + "UI doesn't support external windows"); + return false; } } - if (has_relative && has_external) { - api_set_error(err, kErrorTypeValidation, - "Only one of 'relative' and 'external' must be used"); - return false; - } else if (!reconf && !has_relative && !has_external) { + if (!reconf && (!has_relative && !fconfig->external)) { api_set_error(err, kErrorTypeValidation, "One of 'relative' and 'external' must be used"); return false; - } else if (has_relative) { - fconfig->external = false; } - if (!reconf && !(has_height && has_width)) { - api_set_error(err, kErrorTypeValidation, - "Must specify 'width' and 'height'"); - return false; + + if (HAS_KEY(config->focusable)) { + fconfig->focusable = api_object_to_bool(config->focusable, "'focusable' key", false, err); + if (ERROR_SET(err)) { + return false; + } } - if (fconfig->external && !ui_has(kUIMultigrid)) { - api_set_error(err, kErrorTypeValidation, - "UI doesn't support external windows"); + if (config->zindex.type == kObjectTypeInteger && config->zindex.data.integer > 0) { + fconfig->zindex = (int)config->zindex.data.integer; + } else if (HAS_KEY(config->zindex)) { + api_set_error(err, kErrorTypeValidation, "'zindex' key must be a positive Integer"); return false; } - if (has_relative != has_row || has_row != has_col) { - api_set_error(err, kErrorTypeValidation, - "'relative' requires 'row'/'col' or 'bufpos'"); + if (HAS_KEY(config->border)) { + parse_border_style(config->border, fconfig, err); + if (ERROR_SET(err)) { + return false; + } + } + + if (config->style.type == kObjectTypeString) { + if (config->style.data.string.data[0] == NUL) { + fconfig->style = kWinStyleUnused; + } else if (striequal(config->style.data.string.data, "minimal")) { + fconfig->style = kWinStyleMinimal; + } else { + api_set_error(err, kErrorTypeValidation, "Invalid value of 'style' key"); + } + } else if (HAS_KEY(config->style)) { + api_set_error(err, kErrorTypeValidation, "'style' key must be String"); return false; } + + if (HAS_KEY(config->noautocmd)) { + if (!new_win) { + api_set_error(err, kErrorTypeValidation, "Invalid key: 'noautocmd'"); + return false; + } + fconfig->noautocmd = api_object_to_bool(config->noautocmd, "'noautocmd' key", false, err); + if (ERROR_SET(err)) { + return false; + } + } + return true; } + +bool api_dict_to_keydict(void *rv, field_hash hashy, Dictionary dict, Error *err) +{ + for (size_t i = 0; i < dict.size; i++) { + String k = dict.items[i].key; + Object *field = hashy(rv, k.data, k.size); + if (!field) { + api_set_error(err, kErrorTypeValidation, "Invalid key: '%.*s'", (int)k.size, k.data); + return false; + } + + *field = dict.items[i].value; + } + + return true; +} + +void api_free_keydict(void *dict, KeySetLink *table) +{ + for (size_t i = 0; table[i].str; i++) { + api_free_object(*(Object *)((char *)dict + table[i].ptr_off)); + } +} + diff --git a/src/nvim/api/private/helpers.h b/src/nvim/api/private/helpers.h index ecce6afa26..2cdd80bffe 100644 --- a/src/nvim/api/private/helpers.h +++ b/src/nvim/api/private/helpers.h @@ -59,6 +59,9 @@ #define NIL ((Object)OBJECT_INIT) #define NULL_STRING ((String)STRING_INIT) +// currently treat key=vim.NIL as if the key was missing +#define HAS_KEY(o) ((o).type != kObjectTypeNil) + #define PUT(dict, k, v) \ kv_push(dict, ((KeyValuePair) { .key = cstr_to_string(k), .value = v })) @@ -138,6 +141,9 @@ typedef struct { } while (0) #ifdef INCLUDE_GENERATED_DECLARATIONS +# include "keysets.h.generated.h" # include "api/private/helpers.h.generated.h" #endif + + #endif // NVIM_API_PRIVATE_HELPERS_H diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 3be45d0cf7..f80d605d9a 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -1360,7 +1360,9 @@ void nvim_chan_send(Integer chan, String data, Error *err) /// - `bufpos`: Places float relative to buffer text (only when /// relative="win"). Takes a tuple of zero-indexed [line, column]. /// `row` and `col` if given are applied relative to this -/// position, else they default to `row=1` and `col=0` +/// position, else they default to: +/// - `row=1` and `col=0` if `anchor` is "NW" or "NE" +/// - `row=0` and `col=0` if `anchor` is "SW" or "SE" /// (thus like a tooltip near the buffer text). /// - `row`: Row position in units of "screen cell height", may be fractional. /// - `col`: Column position in units of "screen cell width", may be @@ -1422,7 +1424,7 @@ void nvim_chan_send(Integer chan, String data, Error *err) /// @param[out] err Error details, if any /// /// @return Window handle, or 0 on error -Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config, Error *err) +Window nvim_open_win(Buffer buffer, Boolean enter, Dict(float_config) *config, Error *err) FUNC_API_SINCE(6) FUNC_API_CHECK_TEXTLOCK { @@ -1437,13 +1439,14 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dictionary config, Error *err if (enter) { win_enter(wp, false); } + // autocmds in win_enter or win_set_buf below may close the window + if (win_valid(wp) && buffer > 0) { + win_set_buf(wp->handle, buffer, fconfig.noautocmd, err); + } if (!win_valid(wp)) { api_set_error(err, kErrorTypeException, "Window was closed immediately"); return 0; } - if (buffer > 0) { - win_set_buf(wp->handle, buffer, fconfig.noautocmd, err); - } if (fconfig.style == kWinStyleMinimal) { win_set_minimal_style(wp); @@ -1758,24 +1761,15 @@ Dictionary nvim_get_color_map(void) /// @param[out] err Error details, if any /// /// @return map of global |context|. -Dictionary nvim_get_context(Dictionary opts, Error *err) +Dictionary nvim_get_context(Dict(context) *opts, Error *err) FUNC_API_SINCE(6) { Array types = ARRAY_DICT_INIT; - for (size_t i = 0; i < opts.size; i++) { - String k = opts.items[i].key; - Object v = opts.items[i].value; - if (strequal("types", k.data)) { - if (v.type != kObjectTypeArray) { - api_set_error(err, kErrorTypeValidation, "invalid value for key: %s", - k.data); - return (Dictionary)ARRAY_DICT_INIT; - } - types = v.data.array; - } else { - api_set_error(err, kErrorTypeValidation, "unexpected key: %s", k.data); - return (Dictionary)ARRAY_DICT_INIT; - } + if (opts->types.type == kObjectTypeArray) { + types = opts->types.data.array; + } else if (opts->types.type != kObjectTypeNil) { + api_set_error(err, kErrorTypeValidation, "invalid value for key: types"); + return (Dictionary)ARRAY_DICT_INIT; } int int_types = types.size > 0 ? 0 : kCtxAll; @@ -1885,7 +1879,7 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode) /// as keys excluding |<buffer>| but including |noremap|. /// Values are Booleans. Unknown key is an error. /// @param[out] err Error details, if any. -void nvim_set_keymap(String mode, String lhs, String rhs, Dictionary opts, Error *err) +void nvim_set_keymap(String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err) FUNC_API_SINCE(6) { modify_keymap(-1, false, mode, lhs, rhs, opts, err); @@ -1911,7 +1905,7 @@ void nvim_del_keymap(String mode, String lhs, Error *err) /// @param[out] err Error details, if any. /// /// @returns Map of maps describing commands. -Dictionary nvim_get_commands(Dictionary opts, Error *err) +Dictionary nvim_get_commands(Dict(get_commands) *opts, Error *err) FUNC_API_SINCE(4) { return nvim_buf_get_commands(-1, opts, err); diff --git a/src/nvim/api/window.c b/src/nvim/api/window.c index 99ba297111..95eae1af2d 100644 --- a/src/nvim/api/window.c +++ b/src/nvim/api/window.c @@ -385,7 +385,7 @@ Boolean nvim_win_is_valid(Window window) /// @param config Map defining the window configuration, /// see |nvim_open_win()| /// @param[out] err Error details, if any -void nvim_win_set_config(Window window, Dictionary config, Error *err) +void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err) FUNC_API_SINCE(6) { win_T *win = find_window_by_handle(window, err); diff --git a/src/nvim/autocmd.c b/src/nvim/autocmd.c index 4c502b53d4..d991b88131 100644 --- a/src/nvim/autocmd.c +++ b/src/nvim/autocmd.c @@ -24,8 +24,8 @@ #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS -#include "auevents_name_map.generated.h" -#include "autocmd.c.generated.h" +# include "auevents_name_map.generated.h" +# include "autocmd.c.generated.h" #endif // @@ -199,7 +199,7 @@ static void au_cleanup(void) } // Loop over all events. - for (event = (event_T)0; (int)event < (int)NUM_EVENTS; + for (event = (event_T)0; (int)event < NUM_EVENTS; event = (event_T)((int)event + 1)) { // Loop over all autocommand patterns. prev_ap = &(first_autopat[(int)event]); @@ -266,7 +266,7 @@ void aubuflocal_remove(buf_T *buf) } // invalidate buflocals looping through events - for (event = (event_T)0; (int)event < (int)NUM_EVENTS; + for (event = (event_T)0; (int)event < NUM_EVENTS; event = (event_T)((int)event + 1)) { // loop over all autocommand patterns for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) { @@ -321,7 +321,7 @@ static void au_del_group(char_u *name) AutoPat *ap; int in_use = false; - for (event = (event_T)0; (int)event < (int)NUM_EVENTS; + for (event = (event_T)0; (int)event < NUM_EVENTS; event = (event_T)((int)event + 1)) { for (ap = first_autopat[(int)event]; ap != NULL; ap = ap->next) { if (ap->group == i && ap->pat != NULL) { @@ -475,7 +475,7 @@ static char_u *find_end_event(char_u *arg, int have_group) pat = arg + 1; } else { for (pat = arg; *pat && *pat != '|' && !ascii_iswhite(*pat); pat = p) { - if ((int)event_name2nr(pat, &p) >= (int)NUM_EVENTS) { + if ((int)event_name2nr(pat, &p) >= NUM_EVENTS) { if (have_group) { EMSG2(_("E216: No such event: %s"), pat); } else { @@ -701,7 +701,7 @@ void do_autocmd(char_u *arg_in, int forceit) if (!forceit && *cmd != NUL) { EMSG(_(e_cannot_define_autocommands_for_all_events)); } else { - for (event_T event = (event_T)0; event < (int)NUM_EVENTS; + for (event_T event = (event_T)0; event < NUM_EVENTS; event = (event_T)(event + 1)) { if (do_autocmd_event(event, pat, once, nested, cmd, forceit, group) == FAIL) { @@ -956,10 +956,11 @@ static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, c return OK; } -// Implementation of ":doautocmd [group] event [fname]". -// Return OK for success, FAIL for failure; -int do_doautocmd(char_u *arg, bool do_msg, // give message for no matching autocmds? - bool *did_something) +/// Implementation of ":doautocmd [group] event [fname]". +/// Return OK for success, FAIL for failure; +/// +/// @param do_msg give message for no matching autocmds? +int do_doautocmd(char_u *arg, bool do_msg, bool *did_something) { char_u *fname; int nothing_done = true; @@ -1916,8 +1917,8 @@ char_u *get_augroup_name(expand_T *xp, int idx) return (char_u *)AUGROUP_NAME(idx); } -char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd // true for :doauto*, false for :autocmd - ) +/// @param doautocmd true for :doauto*, false for :autocmd +char_u *set_context_in_autocmd(expand_T *xp, char_u *arg, int doautocmd) { char_u *p; int group; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index c58eb1610e..20dd94622f 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -65,6 +65,7 @@ #include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/plines.h" #include "nvim/quickfix.h" #include "nvim/regexp.h" #include "nvim/screen.h" @@ -98,13 +99,15 @@ typedef enum { kBffInitChangedtick = 2, } BufFreeFlags; -// Read data from buffer for retrying. -static int read_buffer(int read_stdin, // read file from stdin, otherwise fifo - exarg_T *eap, // for forced 'ff' and 'fenc' or NULL - int flags) // extra flags for readfile() +/// Read data from buffer for retrying. +/// +/// @param read_stdin read file from stdin, otherwise fifo +/// @param eap for forced 'ff' and 'fenc' or NULL +/// @param flags extra flags for readfile() +static int read_buffer(int read_stdin, exarg_T *eap, int flags) { - int retval = OK; - linenr_T line_count; + int retval = OK; + linenr_T line_count; // // Read from the buffer which the text is already filled in and append at @@ -114,7 +117,7 @@ static int read_buffer(int read_stdin, // read file from stdin, otherwis line_count = curbuf->b_ml.ml_line_count; retval = readfile(read_stdin ? NULL : curbuf->b_ffname, read_stdin ? NULL : curbuf->b_fname, - (linenr_T)line_count, (linenr_T)0, (linenr_T)MAXLNUM, eap, + line_count, (linenr_T)0, (linenr_T)MAXLNUM, eap, flags | READ_BUFFER); if (retval == OK) { // Delete the binary lines. @@ -146,16 +149,18 @@ static int read_buffer(int read_stdin, // read file from stdin, otherwis return retval; } -// Open current buffer, that is: open the memfile and read the file into -// memory. -// Return FAIL for failure, OK otherwise. -int open_buffer(int read_stdin, // read file from stdin - exarg_T *eap, // for forced 'ff' and 'fenc' or NULL - int flags // extra flags for readfile() - ) +/// Open current buffer, that is: open the memfile and read the file into +/// memory. +/// +/// @param read_stdin read file from stdin +/// @param eap for forced 'ff' and 'fenc' or NULL +/// @param flags extra flags for readfile() +/// +/// @return FAIL for failure, OK otherwise. +int open_buffer(int read_stdin, exarg_T *eap, int flags) { int retval = OK; - bufref_T old_curbuf; + bufref_T old_curbuf; long old_tw = curbuf->b_p_tw; int read_fifo = false; @@ -816,6 +821,7 @@ static void free_buffer_stuff(buf_T *buf, int free_flags) uc_clear(&buf->b_ucmds); // clear local user commands buf_delete_signs(buf, (char_u *)"*"); // delete any signs extmark_free_all(buf); // delete any extmarks + clear_virt_lines(buf, -1); map_clear_int(buf, MAP_ALL_MODES, true, false); // clear local mappings map_clear_int(buf, MAP_ALL_MODES, true, true); // clear local abbrevs XFREE_CLEAR(buf->b_start_fenc); @@ -939,23 +945,22 @@ void handle_swap_exists(bufref_T *old_curbuf) swap_exists_action = SEA_NONE; // -V519 } -/* - * do_bufdel() - delete or unload buffer(s) - * - * addr_count == 0: ":bdel" - delete current buffer - * addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete - * buffer "end_bnr", then any other arguments. - * addr_count == 2: ":N,N bdel" - delete buffers in range - * - * command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or - * DOBUF_DEL (":bdel") - * - * Returns error message or NULL - */ -char_u *do_bufdel(int command, char_u *arg, // pointer to extra arguments - int addr_count, int start_bnr, // first buffer number in a range - int end_bnr, // buffer nr or last buffer nr in a range - int forceit) +/// do_bufdel() - delete or unload buffer(s) +/// +/// addr_count == 0: ":bdel" - delete current buffer +/// addr_count == 1: ":N bdel" or ":bdel N [N ..]" - first delete +/// buffer "end_bnr", then any other arguments. +/// addr_count == 2: ":N,N bdel" - delete buffers in range +/// +/// command can be DOBUF_UNLOAD (":bunload"), DOBUF_WIPE (":bwipeout") or +/// DOBUF_DEL (":bdel") +/// +/// @param arg pointer to extra arguments +/// @param start_bnr first buffer number in a range +/// @param end_bnr buffer nr or last buffer nr in a range +/// +/// @return error message or NULL +char_u *do_bufdel(int command, char_u *arg, int addr_count, int start_bnr, int end_bnr, int forceit) { int do_current = 0; // delete current buffer? int deleted = 0; // number of buffers deleted @@ -1097,26 +1102,26 @@ static int empty_curbuf(int close_others, int forceit, int action) return retval; } -/* - * Implementation of the commands for the buffer list. - * - * action == DOBUF_GOTO go to specified buffer - * action == DOBUF_SPLIT split window and go to specified buffer - * action == DOBUF_UNLOAD unload specified buffer(s) - * action == DOBUF_DEL delete specified buffer(s) from buffer list - * action == DOBUF_WIPE delete specified buffer(s) really - * - * start == DOBUF_CURRENT go to "count" buffer from current buffer - * start == DOBUF_FIRST go to "count" buffer from first buffer - * start == DOBUF_LAST go to "count" buffer from last buffer - * start == DOBUF_MOD go to "count" modified buffer from current buffer - * - * Return FAIL or OK. - */ -int do_buffer(int action, int start, int dir, // FORWARD or BACKWARD - int count, // buffer number or number of buffers - int forceit // true for :...! - ) + +/// Implementation of the commands for the buffer list. +/// +/// action == DOBUF_GOTO go to specified buffer +/// action == DOBUF_SPLIT split window and go to specified buffer +/// action == DOBUF_UNLOAD unload specified buffer(s) +/// action == DOBUF_DEL delete specified buffer(s) from buffer list +/// action == DOBUF_WIPE delete specified buffer(s) really +/// +/// start == DOBUF_CURRENT go to "count" buffer from current buffer +/// start == DOBUF_FIRST go to "count" buffer from first buffer +/// start == DOBUF_LAST go to "count" buffer from last buffer +/// start == DOBUF_MOD go to "count" modified buffer from current buffer +/// +/// @param dir FORWARD or BACKWARD +/// @param count buffer number or number of buffers +/// @param forceit true for :...! +/// +/// @return FAIL or OK. +int do_buffer(int action, int start, int dir, int count, int forceit) { buf_T *buf; buf_T *bp; @@ -2154,11 +2159,13 @@ static buf_T *buflist_findname_file_id(char_u *ffname, FileID *file_id, bool fil /// Find file in buffer list by a regexp pattern. /// Return fnum of the found buffer. /// Return < 0 for error. -int buflist_findpat(const char_u *pattern, const char_u *pattern_end, // pointer to first char after pattern - bool unlisted, // find unlisted buffers - bool diffmode, // find diff-mode buffers only - bool curtab_only // find buffers in current tab only - ) +/// +/// @param pattern_end pointer to first char after pattern +/// @param unlisted find unlisted buffers +/// @param diffmode find diff-mode buffers only +/// @param curtab_only find buffers in current tab only +int buflist_findpat(const char_u *pattern, const char_u *pattern_end, bool unlisted, bool diffmode, + bool curtab_only) FUNC_ATTR_NONNULL_ARG(1) { int match = -1; @@ -2466,14 +2473,14 @@ buf_T *buflist_findnr(int nr) return handle_get_buffer((handle_T)nr); } -/* - * Get name of file 'n' in the buffer list. - * When the file has no name an empty string is returned. - * home_replace() is used to shorten the file name (used for marks). - * Returns a pointer to allocated memory, of NULL when failed. - */ -char_u *buflist_nr2name(int n, int fullname, int helptail // for help buffers return tail only - ) +/// Get name of file 'n' in the buffer list. +/// When the file has no name an empty string is returned. +/// home_replace() is used to shorten the file name (used for marks). +/// +/// @param helptail for help buffers return tail only +/// +/// @return a pointer to allocated memory, of NULL when failed. +char_u *buflist_nr2name(int n, int fullname, int helptail) { buf_T *buf; @@ -2800,13 +2807,14 @@ int buflist_name_nr(int fnum, char_u **fname, linenr_T *lnum) return OK; } -// Set the file name for "buf" to "ffname_arg", short file name to -// "sfname_arg". -// The file name with the full path is also remembered, for when :cd is used. -// Returns FAIL for failure (file name already in use by other buffer) -// OK otherwise. -int setfname(buf_T *buf, char_u *ffname_arg, char_u *sfname_arg, bool message // give message when buffer already exists - ) +/// Set the file name for "buf" to "ffname_arg", short file name to +/// "sfname_arg". +/// The file name with the full path is also remembered, for when :cd is used. +/// +/// @param message give message when buffer already exists +/// +/// @return FAIL for failure (file name already in use by other buffer) OK otherwise. +int setfname(buf_T *buf, char_u *ffname_arg, char_u *sfname_arg, bool message) { char_u *ffname = ffname_arg; char_u *sfname = sfname_arg; @@ -2934,12 +2942,11 @@ buf_T *setaltfname(char_u *ffname, char_u *sfname, linenr_T lnum) return buf; } -/* - * Get alternate file name for current window. - * Return NULL if there isn't any, and give error message if requested. - */ -char_u *getaltfname(bool errmsg // give error message - ) +/// Get alternate file name for current window. +/// Return NULL if there isn't any, and give error message if requested. +/// +/// @param errmsg give error message +char_u *getaltfname(bool errmsg) { char_u *fname; linenr_T dummy; @@ -3078,11 +3085,10 @@ static bool buf_same_file_id(buf_T *buf, FileID *file_id) return buf->file_id_valid && os_fileid_equal(&(buf->file_id), file_id); } -/* - * Print info about the current buffer. - */ -void fileinfo(int fullname, // when non-zero print full path - int shorthelp, int dont_truncate) +/// Print info about the current buffer. +/// +/// @param fullname when non-zero print full path +void fileinfo(int fullname, int shorthelp, int dont_truncate) { char_u *name; int n; @@ -3258,7 +3264,7 @@ void maketitle(void) buf_p += MIN(size, SPACE_FOR_FNAME); } else { buf_p += transstr_buf((const char *)path_tail(curbuf->b_fname), - buf_p, SPACE_FOR_FNAME + 1); + buf_p, SPACE_FOR_FNAME + 1, true); } switch (bufIsChanged(curbuf) @@ -3307,7 +3313,7 @@ void maketitle(void) // room for the server name. When there is no room (very long // file name) use (...). if ((size_t)(buf_p - buf) < SPACE_FOR_DIR) { - char *const tbuf = transstr(buf_p); + char *const tbuf = transstr(buf_p, true); const size_t free_space = SPACE_FOR_DIR - (size_t)(buf_p - buf) + 1; const size_t dir_len = xstrlcpy(buf_p, tbuf, free_space); buf_p += MIN(dir_len, free_space - 1); @@ -3423,14 +3429,14 @@ void resettitle(void) ui_flush(); } -# if defined(EXITFREE) +#if defined(EXITFREE) void free_titles(void) { xfree(lasttitle); xfree(lasticon); } -# endif +#endif /// Enumeration specifying the valid numeric bases that can /// be used when printing numbers in the status line. @@ -4653,7 +4659,7 @@ void get_rel_pos(win_T *wp, char_u *buf, int buflen) long below; // number of lines below window above = wp->w_topline - 1; - above += diff_check_fill(wp, wp->w_topline) - wp->w_topfill; + above += win_get_fill(wp, wp->w_topline) - wp->w_topfill; if (wp->w_topline == 1 && wp->w_topfill >= 1) { // All buffer lines are displayed and there is an indication // of filler lines, that can be considered seeing all lines. @@ -4751,12 +4757,11 @@ char_u *alist_name(aentry_T *aep) return bp->b_fname; } -/* - * do_arg_all(): Open up to 'count' windows, one for each argument. - */ -void do_arg_all(int count, int forceit, // hide buffers in current windows - int keep_tabs // keep current tabs, for ":tab drop file" - ) +/// do_arg_all(): Open up to 'count' windows, one for each argument. +/// +/// @param forceit hide buffers in current windows +/// @param keep_tabs keep current tabs, for ":tab drop file" +void do_arg_all(int count, int forceit, int keep_tabs) { char_u *opened; // Array of weight for which args are open: // 0: not opened @@ -5254,12 +5259,11 @@ void do_modelines(int flags) entered--; } -/* - * chk_modeline() - check a single line for a mode string - * Return FAIL if an error encountered. - */ -static int chk_modeline(linenr_T lnum, int flags // Same as for do_modelines(). - ) +/// chk_modeline() - check a single line for a mode string +/// Return FAIL if an error encountered. +/// +/// @param flags Same as for do_modelines(). +static int chk_modeline(linenr_T lnum, int flags) { char_u *s; char_u *e; @@ -5631,13 +5635,12 @@ bool buf_contents_changed(buf_T *buf) return differ; } -/* - * Wipe out a buffer and decrement the last buffer number if it was used for - * this buffer. Call this to wipe out a temp buffer that does not contain any - * marks. - */ -void wipe_buffer(buf_T *buf, bool aucmd // When true trigger autocommands. - ) +/// Wipe out a buffer and decrement the last buffer number if it was used for +/// this buffer. Call this to wipe out a temp buffer that does not contain any +/// marks. +/// +/// @param aucmd When true trigger autocommands. +void wipe_buffer(buf_T *buf, bool aucmd) { if (!aucmd) { // Don't trigger BufDelete autocommands here. diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index ba2bcd7223..0264a60117 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -868,6 +868,12 @@ struct file_buffer { Map(uint64_t, ExtmarkItem) b_extmark_index[1]; Map(uint64_t, ExtmarkNs) b_extmark_ns[1]; // extmark namespaces + VirtLines b_virt_lines; + uint64_t b_virt_line_mark; + int b_virt_line_pos; + bool b_virt_line_above; + bool b_virt_line_leftcol; + // array of channel_id:s which have asked to receive updates for this // buffer. kvec_t(uint64_t) update_channels; diff --git a/src/nvim/change.c b/src/nvim/change.c index ccceb0e320..4ac5edeaa9 100644 --- a/src/nvim/change.c +++ b/src/nvim/change.c @@ -461,15 +461,15 @@ void changed_lines_buf(buf_T *buf, linenr_T lnum, linenr_T lnume, long xtra) /// When only inserting lines, "lnum" and "lnume" are equal. /// Takes care of calling changed() and updating b_mod_*. /// Careful: may trigger autocommands that reload the buffer. -void changed_lines(linenr_T lnum, // first line with change - colnr_T col, // column in first line with change - linenr_T lnume, // line below last changed line - long xtra, // number of extra lines (negative when deleting) - bool do_buf_event // some callers like undo/redo call changed_lines() - // and then increment changedtick *again*. This flag - // allows these callers to send the nvim_buf_lines_event - // events after they're done modifying changedtick. - ) +/// +/// @param lnum first line with change +/// @param col column in first line with change +/// @param lnume line below last changed line +/// @param xtra number of extra lines (negative when deleting) +/// @param do_buf_event some callers like undo/redo call changed_lines() and +/// then increment changedtick *again*. This flag allows these callers to send +/// the nvim_buf_lines_event events after they're done modifying changedtick. +void changed_lines(linenr_T lnum, colnr_T col, linenr_T lnume, long xtra, bool do_buf_event) { changed_lines_buf(curbuf, lnum, lnume, xtra); @@ -950,9 +950,10 @@ int copy_indent(int size, char_u *src) /// "second_line_indent": indent for after ^^D in Insert mode or if flag /// OPENLINE_COM_LIST /// +/// @param dir FORWARD or BACKWARD +/// /// @return true on success, false on failure -int open_line(int dir, // FORWARD or BACKWARD - int flags, int second_line_indent) +int open_line(int dir, int flags, int second_line_indent) { char_u *next_line = NULL; // copy of the next line char_u *p_extra = NULL; // what goes to next line diff --git a/src/nvim/charset.c b/src/nvim/charset.c index 0ad7dddd33..f899ebf57c 100644 --- a/src/nvim/charset.c +++ b/src/nvim/charset.c @@ -310,7 +310,7 @@ void trans_characters(char_u *buf, int bufsize) /// /// @return number of bytes needed to hold a translation of `s`, NUL byte not /// included. -size_t transstr_len(const char *const s) +size_t transstr_len(const char *const s, bool untab) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE { const char *p = s; @@ -331,6 +331,9 @@ size_t transstr_len(const char *const s) } } p += l; + } else if (*p == TAB && !untab) { + len += 1; + p++; } else { const int b2c_l = byte2cells((uint8_t)(*p++)); // Illegal byte sequence may occupy up to 4 characters. @@ -346,9 +349,10 @@ size_t transstr_len(const char *const s) /// @param[out] buf Buffer to which result should be saved. /// @param[in] len Buffer length. Resulting string may not occupy more then /// len - 1 bytes (one for trailing NUL byte). +/// @param[in] untab remove tab characters /// /// @return length of the resulting string, without the NUL byte. -size_t transstr_buf(const char *const s, char *const buf, const size_t len) +size_t transstr_buf(const char *const s, char *const buf, const size_t len, bool untab) FUNC_ATTR_NONNULL_ALL { const char *p = s; @@ -379,6 +383,8 @@ size_t transstr_buf(const char *const s, char *const buf, const size_t len) } } p += l; + } else if (*p == TAB && !untab) { + *buf_p++ = *p++; } else { const char *const tb = (const char *)transchar_byte((uint8_t)(*p++)); const size_t tb_len = strlen(tb); @@ -401,14 +407,14 @@ size_t transstr_buf(const char *const s, char *const buf, const size_t len) /// @param[in] s String to replace characters from. /// /// @return [allocated] translated string -char *transstr(const char *const s) +char *transstr(const char *const s, bool untab) FUNC_ATTR_NONNULL_RET { // Compute the length of the result, taking account of unprintable // multi-byte characters. - const size_t len = transstr_len((const char *)s) + 1; + const size_t len = transstr_len((const char *)s, untab) + 1; char *const buf = xmalloc(len); - transstr_buf(s, buf, len); + transstr_buf(s, buf, len, untab); return buf; } diff --git a/src/nvim/cursor.c b/src/nvim/cursor.c index 2eced03c03..e334fd166e 100644 --- a/src/nvim/cursor.c +++ b/src/nvim/cursor.c @@ -93,10 +93,10 @@ int coladvance(colnr_T wcol) return rc; } -static int coladvance2(pos_T *pos, bool addspaces, // change the text to achieve our goal? - bool finetune, // change char offset for the exact column - colnr_T wcol_arg // column to move to (can be negative) - ) +/// @param addspaces change the text to achieve our goal? +/// @param finetune change char offset for the exact column +/// @param wcol_arg column to move to (can be negative) +static int coladvance2(pos_T *pos, bool addspaces, bool finetune, colnr_T wcol_arg) { colnr_T wcol = wcol_arg; int idx; diff --git a/src/nvim/debugger.c b/src/nvim/debugger.c index a5dd4cd485..58b1d9ce7f 100644 --- a/src/nvim/debugger.c +++ b/src/nvim/debugger.c @@ -398,7 +398,7 @@ void dbg_check_breakpoint(exarg_T *eap) // replace K_SNR with "<SNR>" if (debug_breakpoint_name[0] == K_SPECIAL && debug_breakpoint_name[1] == KS_EXTRA - && debug_breakpoint_name[2] == (int)KE_SNR) { + && debug_breakpoint_name[2] == KE_SNR) { p = (char_u *)"<SNR>"; } else { p = (char_u *)""; @@ -718,10 +718,11 @@ void ex_breaklist(exarg_T *eap) /// Find a breakpoint for a function or sourced file. /// Returns line number at which to break; zero when no matching breakpoint. -linenr_T dbg_find_breakpoint(bool file, // true for a file, false for a function - char_u *fname, // file or function name - linenr_T after // after this line number - ) +/// +/// @param file true for a file, false for a function +/// @param fname file or function name +/// @param after after this line number +linenr_T dbg_find_breakpoint(bool file, char_u *fname, linenr_T after) { return debuggy_find(file, fname, after, &dbg_breakp, NULL); } @@ -738,12 +739,13 @@ bool has_profiling(bool file, char_u *fname, bool *fp) } /// Common code for dbg_find_breakpoint() and has_profiling(). -static linenr_T debuggy_find(bool file, // true for a file, false for a function - char_u *fname, // file or function name - linenr_T after, // after this line number - garray_T *gap, // either &dbg_breakp or &prof_ga - bool *fp // if not NULL: return forceit - ) +/// +/// @param file true for a file, false for a function +/// @param fname file or function name +/// @param after after this line number +/// @param gap either &dbg_breakp or &prof_ga +/// @param fp if not NULL: return forceit +static linenr_T debuggy_find(bool file, char_u *fname, linenr_T after, garray_T *gap, bool *fp) { struct debuggy *bp; linenr_T lnum = 0; diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index 0f21b47261..4e80528c74 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -37,7 +37,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start decor->priority = DECOR_PRIORITY_BASE; // TODO(bfredl): if decoration had blocky mode, we could avoid this loop - for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum ++) { + for (linenr_T lnum = pos_start.lnum; lnum <= pos_end.lnum; lnum++) { int end_off = 0; if (pos_start.lnum < lnum && lnum < pos_end.lnum) { // TODO(bfredl): This is quite ad-hoc, but the space between |num| and @@ -59,7 +59,7 @@ void bufhl_add_hl_pos_offset(buf_T *buf, int src_id, int hl_id, lpos_T pos_start hl_start = pos_start.col + offset; hl_end = pos_end.col + offset; } - (void)extmark_set(buf, (uint64_t)src_id, 0, + (void)extmark_set(buf, (uint64_t)src_id, NULL, (int)lnum-1, hl_start, (int)lnum-1+end_off, hl_end, decor, true, false, kExtmarkNoUndo); } @@ -412,3 +412,35 @@ void decor_free_all_mem(void) } kv_destroy(decor_providers); } + + +int decor_virtual_lines(win_T *wp, linenr_T lnum) +{ + buf_T *buf = wp->w_buffer; + if (!buf->b_virt_line_mark) { + return 0; + } + if (buf->b_virt_line_pos < 0) { + mtpos_t pos = marktree_lookup(buf->b_marktree, buf->b_virt_line_mark, NULL); + if (pos.row < 0) { + buf->b_virt_line_mark = 0; + } + buf->b_virt_line_pos = pos.row + (buf->b_virt_line_above ? 0 : 1); + } + + return (lnum-1 == buf->b_virt_line_pos) ? (int)kv_size(buf->b_virt_lines) : 0; +} + +void clear_virt_lines(buf_T *buf, int row) +{ + if (row > -1) { + redraw_buf_line_later(buf, MIN(buf->b_ml.ml_line_count, + row+1+(buf->b_virt_line_above?0:1))); + } + for (size_t i = 0; i < kv_size(buf->b_virt_lines); i++) { + clear_virttext(&kv_A(buf->b_virt_lines, i)); + } + kv_destroy(buf->b_virt_lines); // re-initializes + buf->b_virt_line_pos = -1; + buf->b_virt_line_mark = 0; +} diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 28dabeeada..35f5af87ed 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -7,14 +7,6 @@ // actual Decoration data is in extmark_defs.h -typedef struct { - char *text; - int hl_id; -} VirtTextChunk; - -typedef kvec_t(VirtTextChunk) VirtText; -#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) - typedef uint16_t DecorPriority; #define DECOR_PRIORITY_BASE 0x1000 diff --git a/src/nvim/diff.c b/src/nvim/diff.c index 1d70145209..5c43b2498e 100644 --- a/src/nvim/diff.c +++ b/src/nvim/diff.c @@ -73,21 +73,21 @@ 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 + 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 + 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 + 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 @@ -702,9 +702,9 @@ static void clear_diffout(diffout_T *dout) /// @return FAIL for failure. static int diff_write_buffer(buf_T *buf, diffin_T *din) { - linenr_T lnum; + linenr_T lnum; char_u *s; - long len = 0; + long len = 0; char_u *ptr; // xdiff requires one big block of memory with all the text. @@ -732,7 +732,7 @@ static int diff_write_buffer(buf_T *buf, diffin_T *din) 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) { - char_u cbuf[MB_MAXBYTES + 1]; + char_u cbuf[MB_MAXBYTES + 1]; // xdiff doesn't support ignoring case, fold-case the text. int c = PTR2CHAR(s); @@ -787,10 +787,10 @@ static int diff_write(buf_T *buf, diffin_T *din) /// @param dio /// @param idx_orig /// @param eap can be NULL -static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap) +static void diff_try_update(diffio_T *dio, int idx_orig, exarg_T *eap) { buf_T *buf; - int idx_new; + int idx_new; if (dio->dio_internal) { ga_init(&dio->dio_diff.dout_ga, sizeof(char *), 1000); @@ -928,7 +928,7 @@ void ex_diffupdate(exarg_T *eap) } // Only use the internal method if it did not fail for one of the buffers. - diffio_T diffio; + diffio_T diffio; memset(&diffio, 0, sizeof(diffio)); diffio.dio_internal = diff_internal() && !diff_internal_failed(); @@ -1044,9 +1044,9 @@ static int check_external_diff(diffio_T *diffio) /// static int diff_file_internal(diffio_T *diffio) { - xpparam_t param; - xdemitconf_t emit_cfg; - xdemitcb_t emit_cb; + xpparam_t param; + xdemitconf_t emit_cfg; + xdemitcb_t emit_cb; memset(¶m, 0, sizeof(param)); memset(&emit_cfg, 0, sizeof(emit_cfg)); @@ -1985,26 +1985,6 @@ static int diff_cmp(char_u *s1, char_u *s2) return 0; } -/// Return the number of filler lines above "lnum". -/// -/// @param wp -/// @param lnum -/// -/// @return Number of filler lines above lnum -int diff_check_fill(win_T *wp, linenr_T lnum) -{ - // be quick when there are no filler lines - if (!(diff_flags & DIFF_FILLER)) { - return 0; - } - int n = diff_check(wp, lnum); - - if (n <= 0) { - return 0; - } - return n; -} - /// Set the topline of "towin" to match the position in "fromwin", so that they /// show the same diff'ed lines. /// @@ -2030,6 +2010,7 @@ void diff_set_topline(win_T *fromwin, win_T *towin) } towin->w_topfill = 0; + // search for a change that includes "lnum" in the list of diffblocks. for (dp = curtab->tp_first_diff; dp != NULL; dp = dp->df_next) { if (lnum <= dp->df_lnum[fromidx] + dp->df_count[fromidx]) { @@ -2255,6 +2236,13 @@ bool diffopt_closeoff(void) return (diff_flags & DIFF_CLOSE_OFF) != 0; } +// Return true if 'diffopt' contains "filler". +bool diffopt_filler(void) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + return (diff_flags & DIFF_FILLER) != 0; +} + /// Find the difference within a changed line. /// /// @param wp window whose current buffer to check @@ -3033,8 +3021,8 @@ static int parse_diff_ed(char_u *line, linenr_T *lnum_orig, long *count_orig, li long *count_new) { char_u *p; - long f1, l1, f2, l2; - int difftype; + long f1, l1, f2, l2; + int difftype; // The line must be one of three formats: // change: {first}[,{last}]c{first}[,{last}] @@ -3088,7 +3076,7 @@ static int parse_diff_unified(char_u *line, linenr_T *lnum_orig, long *count_ori linenr_T *lnum_new, long *count_new) { char_u *p; - long oldline, oldcount, newline, newcount; + long oldline, oldcount, newline, newcount; // Parse unified diff hunk header: // @@ -oldline,oldcount +newline,newcount @@ diff --git a/src/nvim/edit.c b/src/nvim/edit.c index 20dc3cd06b..5ac733285d 100644 --- a/src/nvim/edit.c +++ b/src/nvim/edit.c @@ -116,7 +116,7 @@ static char *ctrl_x_msgs[] = static char *ctrl_x_mode_names[] = { "keyword", "ctrl_x", - "unknown", // CTRL_X_SCROLL + "scroll", "whole_line", "files", "tags", @@ -147,7 +147,7 @@ struct compl_S { compl_T *cp_prev; char_u *cp_str; // matched text char_u *(cp_text[CPT_COUNT]); // text for the menu - typval_T cp_user_data; + typval_T cp_user_data; char_u *cp_fname; // file containing the match, allocated when // cp_flags has CP_FREE_FNAME int cp_flags; // CP_ values @@ -3329,7 +3329,7 @@ void get_complete_info(list_T *what_list, dict_T *retdict) // Return Insert completion mode name string static char_u *ins_compl_mode(void) { - if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || compl_started) { + if (ctrl_x_mode == CTRL_X_NOT_DEFINED_YET || ctrl_x_mode == CTRL_X_SCROLL || compl_started) { return (char_u *)ctrl_x_mode_names[ctrl_x_mode & ~CTRL_X_WANT_IDENT]; } return (char_u *)""; @@ -3344,12 +3344,10 @@ static char_u *ins_compl_mode(void) */ static int ins_compl_bs(void) { - char_u *line; - char_u *p; - - line = get_cursor_line_ptr(); - p = line + curwin->w_cursor.col; + char_u *line = get_cursor_line_ptr(); + char_u *p = line + curwin->w_cursor.col; MB_PTR_BACK(line, p); + ptrdiff_t p_off = p - line; // Stop completion when the whole word was deleted. For Omni completion // allow the word to be deleted, we won't match everything. @@ -3369,8 +3367,12 @@ static int ins_compl_bs(void) ins_compl_restart(); } + // ins_compl_restart() calls update_screen(0) which may invalidate the pointer + // TODO(bfredl): get rid of random update_screen() calls deep inside completion logic + line = get_cursor_line_ptr(); + xfree(compl_leader); - compl_leader = vim_strnsave(line + compl_col, (int)(p - line) - compl_col); + compl_leader = vim_strnsave(line + compl_col, (int)p_off - compl_col); ins_compl_new_leader(); if (compl_shown_match != NULL) { // Make sure current match is not a hidden item. @@ -5679,7 +5681,7 @@ static void insert_special(int c, int allow_modmask, int ctrlv) * stop and defer processing to the "normal" mechanism. * '0' and '^' are special, because they can be followed by CTRL-D. */ -# define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^') +#define ISSPECIAL(c) ((c) < ' ' || (c) >= DEL || (c) == '0' || (c) == '^') #define WHITECHAR(cc) ( \ ascii_iswhite(cc) \ diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 0b73d6b37f..768b82b464 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -5848,8 +5848,8 @@ static int get_env_tv(char_u **arg, typval_T *rettv, int evaluate) { char_u *name; char_u *string = NULL; - int len; - int cc; + int len; + int cc; ++*arg; name = *arg; @@ -6299,9 +6299,9 @@ int assert_fails(typval_T *argvars) FUNC_ATTR_NONNULL_ALL { const char *const cmd = tv_get_string_chk(&argvars[0]); - garray_T ga; + garray_T ga; int ret = 0; - int save_trylevel = trylevel; + int save_trylevel = trylevel; // trylevel must be zero for a ":throw" command to be considered failed trylevel = 0; @@ -7345,7 +7345,7 @@ void set_buffer_lines(buf_T *buf, linenr_T lnum_arg, bool append, const typval_T const char *line = NULL; list_T *l = NULL; listitem_T *li = NULL; - long added = 0; + long added = 0; linenr_T append_lnum; buf_T *curbuf_save = NULL; win_T *curwin_save = NULL; @@ -8807,9 +8807,9 @@ char_u *set_cmdarg(exarg_T *eap, char_u *oldarg) char_u *newval = xmalloc(newval_len); if (eap->force_bin == FORCE_BIN) { - sprintf((char *)newval, " ++bin"); + snprintf((char *)newval, newval_len, " ++bin"); } else if (eap->force_bin == FORCE_NOBIN) { - sprintf((char *)newval, " ++nobin"); + snprintf((char *)newval, newval_len, " ++nobin"); } else { *newval = NUL; } diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 762d741fb7..c6ac27b269 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -162,6 +162,7 @@ return { getloclist={args={1, 2}}, getmarklist={args={0, 1}}, getmatches={args={0, 1}}, + getmousepos={}, getpid={}, getpos={args=1}, getqflist={args={0, 1}}, diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index ff91f13444..5569d74413 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -80,16 +80,16 @@ KHASH_MAP_INIT_STR(functions, VimLFuncDef) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "eval/funcs.c.generated.h" -#ifdef _MSC_VER +# ifdef _MSC_VER // This prevents MSVC from replacing the functions with intrinsics, // and causing errors when trying to get their addresses in funcs.generated.h -#pragma function(ceil) -#pragma function(floor) -#endif +# pragma function(ceil) +# pragma function(floor) +# endif PRAGMA_DIAG_PUSH_IGNORE_MISSING_PROTOTYPES PRAGMA_DIAG_PUSH_IGNORE_IMPLICIT_FALLTHROUGH -#include "funcs.generated.h" +# include "funcs.generated.h" PRAGMA_DIAG_POP PRAGMA_DIAG_POP #endif @@ -248,7 +248,7 @@ static int non_zero_arg(typval_T *argvars) static void float_op_wrapper(typval_T *argvars, typval_T *rettv, FunPtr fptr) { float_T f; - float_T (*function)(float_T) = (float_T (*)(float_T))fptr; + float_T (*function)(float_T) = (float_T (*)(float_T)) fptr; rettv->v_type = VAR_FLOAT; if (tv_get_float_chk(argvars, &f)) { @@ -1802,7 +1802,7 @@ static void f_did_filetype(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_diff_filler(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - rettv->vval.v_number = diff_check_fill(curwin, tv_get_lnum(argvars)); + rettv->vval.v_number = MAX(0, diff_check(curwin, tv_get_lnum(argvars))); } /* @@ -2695,13 +2695,13 @@ static void f_foldlevel(typval_T *argvars, typval_T *rettv, FunPtr fptr) */ static void f_foldtext(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - linenr_T foldstart; - linenr_T foldend; + linenr_T foldstart; + linenr_T foldend; char_u *dashes; - linenr_T lnum; + linenr_T lnum; char_u *s; char_u *r; - int len; + int len; char *txt; rettv->v_type = VAR_STRING; @@ -3310,10 +3310,10 @@ static void f_getcmdwintype(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_getcompletion(typval_T *argvars, typval_T *rettv, FunPtr fptr) { char_u *pat; - expand_T xpc; - bool filtered = false; - int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH - | WILD_NO_BEEP; + expand_T xpc; + bool filtered = false; + int options = WILD_SILENT | WILD_USE_NL | WILD_ADD_SLASH + | WILD_NO_BEEP; if (argvars[1].v_type != VAR_STRING) { EMSG2(_(e_invarg2), "type must be a string"); @@ -3750,6 +3750,59 @@ static void f_getmatches(typval_T *argvars, typval_T *rettv, FunPtr fptr) } } +// "getmousepos()" function +void f_getmousepos(typval_T *argvars, typval_T *rettv, FunPtr fptr) +{ + dict_T *d; + win_T *wp; + int row = mouse_row; + int col = mouse_col; + int grid = mouse_grid; + varnumber_T winid = 0; + varnumber_T winrow = 0; + varnumber_T wincol = 0; + linenr_T line = 0; + varnumber_T column = 0; + + tv_dict_alloc_ret(rettv); + d = rettv->vval.v_dict; + + tv_dict_add_nr(d, S_LEN("screenrow"), (varnumber_T)mouse_row + 1); + tv_dict_add_nr(d, S_LEN("screencol"), (varnumber_T)mouse_col + 1); + + wp = mouse_find_win(&grid, &row, &col); + if (wp != NULL) { + int height = wp->w_height + wp->w_status_height; + // The height is adjusted by 1 when there is a bottom border. This is not + // necessary for a top border since `row` starts at -1 in that case. + if (row < height + wp->w_border_adj[2]) { + winid = wp->handle; + winrow = row + 1 + wp->w_border_adj[0]; // Adjust by 1 for top border + wincol = col + 1 + wp->w_border_adj[3]; // Adjust by 1 for left border + if (row >= 0 && row < wp->w_height && col >= 0 && col < wp->w_width) { + char_u *p; + int count; + + mouse_comp_pos(wp, &row, &col, &line); + + // limit to text length plus one + p = ml_get_buf(wp->w_buffer, line, false); + count = (int)STRLEN(p); + if (col > count) { + col = count; + } + + column = col + 1; + } + } + } + tv_dict_add_nr(d, S_LEN("winid"), winid); + tv_dict_add_nr(d, S_LEN("winrow"), winrow); + tv_dict_add_nr(d, S_LEN("wincol"), wincol); + tv_dict_add_nr(d, S_LEN("line"), (varnumber_T)line); + tv_dict_add_nr(d, S_LEN("column"), column); +} + /* * "getpid()" function */ @@ -4113,8 +4166,8 @@ static void f_win_screenpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) // static void win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags) { - int dir; - int height = wp->w_height; + int dir; + int height = wp->w_height; win_T *oldwin = curwin; if (wp == targetwin) { @@ -4153,7 +4206,7 @@ static void f_win_splitmove(typval_T *argvars, typval_T *rettv, FunPtr fptr) { win_T *wp; win_T *targetwin; - int flags = 0, size = 0; + int flags = 0, size = 0; wp = find_win_by_nr_or_id(&argvars[0]); targetwin = find_win_by_nr_or_id(&argvars[1]); @@ -5968,7 +6021,7 @@ static void find_some_match(typval_T *const argvars, typval_T *const rettv, const SomeMatchType type) { char_u *str = NULL; - long len = 0; + long len = 0; char_u *expr = NULL; regmatch_T regmatch; char_u *save_cpo; @@ -7250,17 +7303,17 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, FunPtr fptr) buf[1] = NUL; colnr_T reglen = 0; switch (get_reg_type(regname, ®len)) { - case kMTLineWise: - buf[0] = 'V'; - break; - case kMTCharWise: - buf[0] = 'v'; - break; - case kMTBlockWise: - vim_snprintf(buf, sizeof(buf), "%c%d", Ctrl_V, reglen + 1); - break; - case kMTUnknown: - abort(); + case kMTLineWise: + buf[0] = 'V'; + break; + case kMTCharWise: + buf[0] = 'v'; + break; + case kMTBlockWise: + vim_snprintf(buf, sizeof(buf), "%c%d", Ctrl_V, reglen + 1); + break; + case kMTUnknown: + abort(); } tv_dict_add_str(dict, S_LEN("regtype"), buf); @@ -9056,7 +9109,7 @@ static void f_setpos(typval_T *argvars, typval_T *rettv, FunPtr fptr) { pos_T pos; int fnum; - colnr_T curswant = -1; + colnr_T curswant = -1; rettv->vval.v_number = -1; const char *const name = tv_get_string_chk(argvars); @@ -9100,25 +9153,25 @@ static int get_yank_type(char_u **const pp, MotionType *const yank_type, long *c { char_u *stropt = *pp; switch (*stropt) { - case 'v': - case 'c': // character-wise selection - *yank_type = kMTCharWise; - break; - case 'V': - case 'l': // line-wise selection - *yank_type = kMTLineWise; - break; - case 'b': - case Ctrl_V: // block-wise selection - *yank_type = kMTBlockWise; - if (ascii_isdigit(stropt[1])) { - stropt++; - *block_len = getdigits_long(&stropt, false, 0) - 1; - stropt--; - } - break; - default: - return FAIL; + case 'v': + case 'c': // character-wise selection + *yank_type = kMTCharWise; + break; + case 'V': + case 'l': // line-wise selection + *yank_type = kMTLineWise; + break; + case 'b': + case Ctrl_V: // block-wise selection + *yank_type = kMTBlockWise; + if (ascii_isdigit(stropt[1])) { + stropt++; + *block_len = getdigits_long(&stropt, false, 0) - 1; + stropt--; + } + break; + default: + return FAIL; } *pp = stropt; return OK; @@ -9319,7 +9372,7 @@ static void f_settagstack(typval_T *argvars, typval_T *rettv, FunPtr fptr) static char *e_invact2 = N_("E962: Invalid action: '%s'"); win_T *wp; dict_T *d; - int action = 'r'; + int action = 'r'; rettv->vval.v_number = -1; @@ -10801,7 +10854,7 @@ static void f_strridx(typval_T *argvars, typval_T *rettv, FunPtr fptr) static void f_strtrans(typval_T *argvars, typval_T *rettv, FunPtr fptr) { rettv->v_type = VAR_STRING; - rettv->vval.v_string = (char_u *)transstr(tv_get_string(&argvars[0])); + rettv->vval.v_string = (char_u *)transstr(tv_get_string(&argvars[0]), true); } /* diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 3bc4ec9381..075b50a366 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -100,7 +100,7 @@ void list_write_log(const char *const fname) } } -#ifdef EXITFREE +# ifdef EXITFREE /// Free list log void list_free_log(void) { @@ -110,7 +110,7 @@ void list_free_log(void) chunk = list_log_first; } } -#endif +# endif #endif //{{{2 List item diff --git a/src/nvim/eval/userfunc.c b/src/nvim/eval/userfunc.c index 77297f9ffa..657777d7db 100644 --- a/src/nvim/eval/userfunc.c +++ b/src/nvim/eval/userfunc.c @@ -39,7 +39,7 @@ #define FC_CFUNC 0x800 // C function extension #ifdef INCLUDE_GENERATED_DECLARATIONS -#include "eval/userfunc.c.generated.h" +# include "eval/userfunc.c.generated.h" #endif hashtab_T func_hashtab; @@ -68,11 +68,11 @@ void func_init(void) static int get_function_args(char_u **argp, char_u endchar, garray_T *newargs, int *varargs, garray_T *default_args, bool skip) { - bool mustend = false; + bool mustend = false; char_u *arg = *argp; char_u *p = arg; - int c; - int i; + int c; + int i; if (newargs != NULL) { ga_init(newargs, (int)sizeof(char_u *), 3); @@ -205,8 +205,8 @@ static void register_closure(ufunc_T *fp) /// Get a name for a lambda. Returned in static memory. char_u *get_lambda_name(void) { - static char_u name[30]; - static int lambda_no = 0; + static char_u name[30]; + static int lambda_no = 0; snprintf((char *)name, sizeof(name), "<lambda>%d", ++lambda_no); return name; @@ -217,16 +217,16 @@ char_u *get_lambda_name(void) /// @return OK or FAIL. Returns NOTDONE for dict or {expr}. int get_lambda_tv(char_u **arg, typval_T *rettv, bool evaluate) { - garray_T newargs = GA_EMPTY_INIT_VALUE; + garray_T newargs = GA_EMPTY_INIT_VALUE; garray_T *pnewargs; ufunc_T *fp = NULL; partial_T *pt = NULL; - int varargs; - int ret; + int varargs; + int ret; char_u *start = skipwhite(*arg + 1); char_u *s, *e; bool *old_eval_lavars = eval_lavars_used; - bool eval_lavars = false; + bool eval_lavars = false; // First, check if this is a lambda expression. "->" must exists. ret = get_function_args(&start, '-', NULL, NULL, NULL, true); diff --git a/src/nvim/event/stream.c b/src/nvim/event/stream.c index 8569b92d56..b34fd73d52 100644 --- a/src/nvim/event/stream.c +++ b/src/nvim/event/stream.c @@ -20,7 +20,7 @@ // For compatibility with libuv < 1.19.0 (tested on 1.18.0) #if UV_VERSION_MINOR < 19 -#define uv_stream_get_write_queue_size(stream) stream->write_queue_size +# define uv_stream_get_write_queue_size(stream) stream->write_queue_size #endif /// Sets the stream associated with `fd` to "blocking" mode. diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index f8186c000e..bbc1dd9717 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -1800,7 +1800,7 @@ int do_write(exarg_T *eap) int retval = FAIL; char_u *free_fname = NULL; buf_T *alt_buf = NULL; - int name_was_missing; + int name_was_missing; if (not_writing()) { // check 'write' option return FAIL; @@ -2294,8 +2294,8 @@ int do_ecmd(int fnum, char_u *ffname, char_u *sfname, exarg_T *eap, linenr_T new char_u *new_name = NULL; bool did_set_swapcommand = false; buf_T *buf; - bufref_T bufref; - bufref_T old_curbuf; + bufref_T bufref; + bufref_T old_curbuf; char_u *free_fname = NULL; int retval = FAIL; long n; @@ -3078,7 +3078,7 @@ void ex_change(exarg_T *eap) void ex_z(exarg_T *eap) { char_u *x; - int64_t bigness; + int64_t bigness; char_u *kind; int minus = 0; linenr_T start, end, curs, i; @@ -5549,8 +5549,9 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, if (add_help_tags || path_full_compare((char_u *)"$VIMRUNTIME/doc", dir, false, true) == kEqualFiles) { - s = xmalloc(18 + STRLEN(tagfname)); - sprintf((char *)s, "help-tags\t%s\t1\n", tagfname); + size_t s_len = 18 + STRLEN(tagfname); + s = xmalloc(s_len); + snprintf((char *)s, s_len, "help-tags\t%s\t1\n", tagfname); GA_APPEND(char_u *, &ga, s); } @@ -5611,10 +5612,11 @@ static void helptags_one(char_u *dir, const char_u *ext, const char_u *tagfname, && (vim_strchr((char_u *)" \t\n\r", s[1]) != NULL || s[1] == '\0')) { *p2 = '\0'; - ++p1; - s = xmalloc((p2 - p1) + STRLEN(fname) + 2); + p1++; + size_t s_len= (p2 - p1) + STRLEN(fname) + 2; + s = xmalloc(s_len); GA_APPEND(char_u *, &ga, s); - sprintf((char *)s, "%s\t%s", p1, fname); + snprintf((char *)s, s_len, "%s\t%s", p1, fname); // find next '*' p2 = vim_strchr(p2 + 1, '*'); diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 1a576bd891..46b86fbc84 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -109,7 +109,7 @@ struct source_cookie { vimconv_T conv; ///< type of conversion }; -# define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) +#define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds2.c.generated.h" @@ -317,7 +317,7 @@ static void profile_reset(void) } // Reset functions. - size_t n = func_hashtab.ht_used; + size_t n = func_hashtab.ht_used; hashitem_T *hi = func_hashtab.ht_array; for (; n > (size_t)0; hi++) { @@ -1703,8 +1703,8 @@ void init_pyxversion(void) static int requires_py_version(char_u *filename) { FILE *file; - int requires_py_version = 0; - int i, lines; + int requires_py_version = 0; + int i, lines; lines = (int)p_mls; if (lines < 0) { @@ -2340,7 +2340,7 @@ void ex_scriptnames(exarg_T *eap) } } -# if defined(BACKSLASH_IN_FILENAME) +#if defined(BACKSLASH_IN_FILENAME) /// Fix slashes in the list of script names for 'shellslash'. void scriptnames_slash_adjust(void) { @@ -2351,7 +2351,7 @@ void scriptnames_slash_adjust(void) } } -# endif +#endif /// Get a pointer to a script name. Used for ":verbose set". /// Message appended to "Last set from " @@ -2388,7 +2388,7 @@ char_u *get_scriptname(LastSet last_set, bool *should_free) } } -# if defined(EXITFREE) +#if defined(EXITFREE) void free_scriptnames(void) { profile_reset(); @@ -2396,7 +2396,7 @@ void free_scriptnames(void) # define FREE_SCRIPTNAME(item) xfree((item)->sn_name) GA_DEEP_CLEAR(&script_items, scriptitem_T, FREE_SCRIPTNAME); } -# endif +#endif linenr_T get_sourced_lnum(LineGetter fgetline, void *cookie) { @@ -2786,17 +2786,17 @@ char *get_mess_lang(void) { char *p; -# ifdef HAVE_GET_LOCALE_VAL -# if defined(LC_MESSAGES) +#ifdef HAVE_GET_LOCALE_VAL +# if defined(LC_MESSAGES) p = get_locale_val(LC_MESSAGES); -# else +# else // This is necessary for Win32, where LC_MESSAGES is not defined and $LANG // may be set to the LCID number. LC_COLLATE is the best guess, LC_TIME // and LC_MONETARY may be set differently for a Japanese working in the // US. p = get_locale_val(LC_COLLATE); -# endif -# else +# endif +#else p = os_getenv("LC_ALL"); if (!is_valid_mess_lang(p)) { p = os_getenv("LC_MESSAGES"); @@ -2804,7 +2804,7 @@ char *get_mess_lang(void) p = os_getenv("LANG"); } } -# endif +#endif return is_valid_mess_lang(p) ? p : NULL; } @@ -2842,37 +2842,37 @@ void set_lang_var(void) { const char *loc; -# ifdef HAVE_GET_LOCALE_VAL +#ifdef HAVE_GET_LOCALE_VAL loc = get_locale_val(LC_CTYPE); -# else +#else // setlocale() not supported: use the default value loc = "C"; -# endif +#endif set_vim_var_string(VV_CTYPE, loc, -1); // When LC_MESSAGES isn't defined use the value from $LC_MESSAGES, fall // back to LC_CTYPE if it's empty. -# ifdef HAVE_WORKING_LIBINTL +#ifdef HAVE_WORKING_LIBINTL loc = (char *)get_mess_env(); -# elif defined(LC_MESSAGES) +#elif defined(LC_MESSAGES) loc = get_locale_val(LC_MESSAGES); -# else +#else // In Windows LC_MESSAGES is not defined fallback to LC_CTYPE loc = get_locale_val(LC_CTYPE); -# endif +#endif set_vim_var_string(VV_LANG, loc, -1); -# ifdef HAVE_GET_LOCALE_VAL +#ifdef HAVE_GET_LOCALE_VAL loc = get_locale_val(LC_TIME); -# endif +#endif set_vim_var_string(VV_LC_TIME, loc, -1); -# ifdef HAVE_GET_LOCALE_VAL +#ifdef HAVE_GET_LOCALE_VAL loc = get_locale_val(LC_COLLATE); -# else +#else // setlocale() not supported: use the default value loc = "C"; -# endif +#endif set_vim_var_string(VV_COLLATE, loc, -1); } @@ -2889,11 +2889,11 @@ void ex_language(exarg_T *eap) char_u *name; int what = LC_ALL; char *whatstr = ""; -#ifdef LC_MESSAGES -# define VIM_LC_MESSAGES LC_MESSAGES -#else -# define VIM_LC_MESSAGES 6789 -#endif +# ifdef LC_MESSAGES +# define VIM_LC_MESSAGES LC_MESSAGES +# else +# define VIM_LC_MESSAGES 6789 +# endif name = eap->arg; @@ -2922,43 +2922,43 @@ void ex_language(exarg_T *eap) } if (*name == NUL) { -#ifdef HAVE_WORKING_LIBINTL +# ifdef HAVE_WORKING_LIBINTL if (what == VIM_LC_MESSAGES) { p = get_mess_env(); } else { -#endif +# endif p = (char_u *)setlocale(what, NULL); -#ifdef HAVE_WORKING_LIBINTL +# ifdef HAVE_WORKING_LIBINTL } -#endif +# endif if (p == NULL || *p == NUL) { p = (char_u *)"Unknown"; } smsg(_("Current %slanguage: \"%s\""), whatstr, p); } else { -#ifndef LC_MESSAGES +# ifndef LC_MESSAGES if (what == VIM_LC_MESSAGES) { loc = ""; } else { -#endif +# endif loc = setlocale(what, (char *)name); -#ifdef LC_NUMERIC +# ifdef LC_NUMERIC // Make sure strtod() uses a decimal point, not a comma. setlocale(LC_NUMERIC, "C"); -#endif -#ifndef LC_MESSAGES +# endif +# ifndef LC_MESSAGES } -#endif +# endif if (loc == NULL) { EMSG2(_("E197: Cannot set language to \"%s\""), name); } else { -#ifdef HAVE_NL_MSG_CAT_CNTR +# ifdef HAVE_NL_MSG_CAT_CNTR // Need to do this for GNU gettext, otherwise cached translations // will be used again. extern int _nl_msg_cat_cntr; _nl_msg_cat_cntr++; -#endif +# endif // Reset $LC_ALL, otherwise it would overrule everything. os_setenv("LC_ALL", "", 1); @@ -2987,7 +2987,7 @@ void ex_language(exarg_T *eap) static char_u **locales = NULL; // Array of all available locales -#ifndef WIN32 +# ifndef WIN32 static bool did_init_locales = false; /// Return an array of strings for all available locales + NULL for the @@ -3022,20 +3022,20 @@ static char_u **find_locales(void) ((char_u **)locales_ga.ga_data)[locales_ga.ga_len] = NULL; return (char_u **)locales_ga.ga_data; } -#endif +# endif /// Lazy initialization of all available locales. static void init_locales(void) { -#ifndef WIN32 +# ifndef WIN32 if (!did_init_locales) { did_init_locales = true; locales = find_locales(); } -#endif +# endif } -# if defined(EXITFREE) +# if defined(EXITFREE) void free_locales(void) { int i; @@ -3047,7 +3047,7 @@ void free_locales(void) } } -# endif +# endif /// Function given to ExpandGeneric() to obtain the possible arguments of the /// ":language" command. diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 68dd039278..dff3b4223b 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -99,7 +99,7 @@ static garray_T ucmds = { 0, 0, sizeof(ucmd_T), 4, NULL }; #define USER_CMD_GA(gap, i) (&((ucmd_T *)((gap)->ga_data))[i]) // Whether a command index indicates a user command. -# define IS_USER_CMDIDX(idx) ((int)(idx) < 0) +#define IS_USER_CMDIDX(idx) ((int)(idx) < 0) // Struct for storing a line inside a while/for loop typedef struct { @@ -5637,7 +5637,7 @@ static void ex_command(exarg_T *eap) uint32_t argt = 0; long def = -1; int flags = 0; - int compl = EXPAND_NOTHING; + int compl = EXPAND_NOTHING; char_u *compl_arg = NULL; cmd_addr_T addr_type_arg = ADDR_NONE; int has_attr = (eap->arg[0] == '-'); @@ -6949,7 +6949,7 @@ static void ex_goto(exarg_T *eap) */ void alist_clear(alist_T *al) { -# define FREE_AENTRY_FNAME(arg) xfree(arg->ae_fname) +#define FREE_AENTRY_FNAME(arg) xfree(arg->ae_fname) GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME); } diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index ddf60eac18..a9990df58f 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -140,25 +140,25 @@ static unsigned last_prompt_id = 0; // Struct to store the viewstate during 'incsearch' highlighting. typedef struct { - colnr_T vs_curswant; - colnr_T vs_leftcol; - linenr_T vs_topline; - int vs_topfill; - linenr_T vs_botline; - int vs_empty_rows; + colnr_T vs_curswant; + colnr_T vs_leftcol; + linenr_T vs_topline; + int vs_topfill; + linenr_T vs_botline; + int vs_empty_rows; } viewstate_T; // Struct to store the state of 'incsearch' highlighting. typedef struct { - pos_T search_start; // where 'incsearch' starts searching - pos_T save_cursor; + pos_T search_start; // where 'incsearch' starts searching + pos_T save_cursor; viewstate_T init_viewstate; viewstate_T old_viewstate; - pos_T match_start; - pos_T match_end; - bool did_incsearch; - bool incsearch_postponed; - int magic_save; + pos_T match_start; + pos_T match_end; + bool did_incsearch; + bool incsearch_postponed; + int magic_save; } incsearch_state_T; typedef struct command_line_state { @@ -178,8 +178,8 @@ typedef struct command_line_state { int did_wild_list; // did wild_list() recently int wim_index; // index in wim_flags[] int res; - int save_msg_scroll; - int save_State; // remember State when called + int save_msg_scroll; + int save_State; // remember State when called char_u *save_p_icm; int some_key_typed; // one of the keys was typed // mouse drag and release events are ignored, unless they are @@ -383,7 +383,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s // Don't do 'hlsearch' highlighting if the pattern matches everything. if (!use_last_pat) { char_u c = *end; - int empty; + int empty; *end = NUL; empty = empty_pattern(p); @@ -1502,7 +1502,7 @@ static int may_do_command_line_next_incsearch(int firstc, long count, incsearch_ ui_busy_start(); ui_flush(); - pos_T t; + pos_T t; char_u *pat; int search_flags = SEARCH_NOOF; char_u save; @@ -2570,13 +2570,13 @@ static void realloc_cmdbuff(int len) static char_u *arshape_buf = NULL; -# if defined(EXITFREE) +#if defined(EXITFREE) void free_arshape_buf(void) { xfree(arshape_buf); } -# endif +#endif enum { MAX_CB_ERRORS = 1 }; @@ -4111,7 +4111,7 @@ char *vim_strsave_fnameescape(const char *const fname, const bool shell FUNC_ATT FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { #ifdef BACKSLASH_IN_FILENAME -#define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<" +# define PATH_ESC_CHARS " \t\n*?[{`%#'\"|!<" char_u buf[sizeof(PATH_ESC_CHARS)]; int j = 0; @@ -4125,8 +4125,8 @@ char *vim_strsave_fnameescape(const char *const fname, const bool shell FUNC_ATT char *p = (char *)vim_strsave_escaped((const char_u *)fname, (const char_u *)buf); #else -#define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<") -#define SHELL_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<>();&") +# define PATH_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<") +# define SHELL_ESC_CHARS ((char_u *)" \t\n*?[{`$\\%#'\"|!<>();&") char *p = (char *)vim_strsave_escaped((const char_u *)fname, (shell ? SHELL_ESC_CHARS : PATH_ESC_CHARS)); if (shell && csh_like_shell()) { @@ -5272,7 +5272,7 @@ static void *call_user_expand_func(user_expand_func_T user_expand_func, expand_T static int ExpandUserDefined(expand_T *xp, regmatch_T *regmatch, int *num_file, char_u ***file) { char_u *e; - garray_T ga; + garray_T ga; char_u *const retstr = call_user_expand_func((user_expand_func_T)call_func_retstr, xp, num_file, file); @@ -6250,8 +6250,8 @@ int hist_type2char(int type) static int open_cmdwin(void) { struct cmdline_info save_ccline; - bufref_T old_curbuf; - bufref_T bufref; + bufref_T old_curbuf; + bufref_T bufref; win_T *old_curwin = curwin; win_T *wp; int i; diff --git a/src/nvim/extmark.c b/src/nvim/extmark.c index 819b8ad3dc..cf01c305d7 100644 --- a/src/nvim/extmark.c +++ b/src/nvim/extmark.c @@ -56,8 +56,8 @@ static ExtmarkNs *buf_ns_ref(buf_T *buf, uint64_t ns_id, bool put) { /// Create or update an extmark /// /// must not be used during iteration! -/// @returns the mark id -uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, int row, colnr_T col, int end_row, +/// @returns the internal mark id +uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t *idp, int row, colnr_T col, int end_row, colnr_T end_col, Decoration *decor, bool right_gravity, bool end_right_gravity, ExtmarkOp op) { @@ -65,6 +65,7 @@ uint64_t extmark_set(buf_T *buf, uint64_t ns_id, uint64_t id, int row, colnr_T c assert(ns != NULL); mtpos_t old_pos; uint64_t mark = 0; + uint64_t id = idp ? *idp : 0; if (id == 0) { id = ns->free_id++; @@ -118,7 +119,11 @@ revised: if (decor) { decor_redraw(buf, row, end_row > -1 ? end_row : row, decor); } - return id; + + if (idp) { + *idp = id; + } + return mark; } static bool extmark_setraw(buf_T *buf, uint64_t mark, int row, colnr_T col) @@ -169,6 +174,10 @@ bool extmark_del(buf_T *buf, uint64_t ns_id, uint64_t id) decor_free(item.decor); } + if (mark == buf->b_virt_line_mark) { + clear_virt_lines(buf, pos.row); + } + map_del(uint64_t, uint64_t)(ns->map, id); map_del(uint64_t, ExtmarkItem)(buf->b_extmark_index, mark); @@ -227,6 +236,9 @@ bool extmark_clear(buf_T *buf, uint64_t ns_id, int l_row, colnr_T l_col, int u_r } uint64_t start_id = mark.id & ~MARKTREE_END_FLAG; + if (start_id == buf->b_virt_line_mark) { + clear_virt_lines(buf, mark.row); + } ExtmarkItem item = map_get(uint64_t, ExtmarkItem)(buf->b_extmark_index, start_id); @@ -496,6 +508,7 @@ void extmark_apply_undo(ExtmarkUndoObject undo_info, bool undo) kExtmarkNoUndo); } } + curbuf->b_virt_line_pos = -1; } @@ -574,7 +587,8 @@ void extmark_splice_impl(buf_T *buf, int start_row, colnr_T start_col, bcount_t int old_row, colnr_T old_col, bcount_t old_byte, int new_row, colnr_T new_col, bcount_t new_byte, ExtmarkOp undo) { - curbuf->deleted_bytes2 = 0; + buf->deleted_bytes2 = 0; + buf->b_virt_line_pos = -1; buf_updates_send_splice(buf, start_row, start_col, start_byte, old_row, old_col, old_byte, new_row, new_col, new_byte); @@ -665,7 +679,8 @@ void extmark_move_region(buf_T *buf, int start_row, colnr_T start_col, bcount_t int extent_row, colnr_T extent_col, bcount_t extent_byte, int new_row, colnr_T new_col, bcount_t new_byte, ExtmarkOp undo) { - curbuf->deleted_bytes2 = 0; + buf->deleted_bytes2 = 0; + buf->b_virt_line_pos = -1; // TODO(bfredl): this is not synced to the buffer state inside the callback. // But unless we make the undo implementation smarter, this is not ensured // anyway. diff --git a/src/nvim/extmark_defs.h b/src/nvim/extmark_defs.h index b5d91382ec..2da4f3dc00 100644 --- a/src/nvim/extmark_defs.h +++ b/src/nvim/extmark_defs.h @@ -6,6 +6,16 @@ typedef struct Decoration Decoration; +typedef struct { + char *text; + int hl_id; +} VirtTextChunk; + +typedef kvec_t(VirtTextChunk) VirtText; +#define VIRTTEXT_EMPTY ((VirtText)KV_INITIAL_VALUE) +typedef kvec_t(VirtText) VirtLines; + + typedef struct { uint64_t ns_id; diff --git a/src/nvim/fileio.c b/src/nvim/fileio.c index 31af47999b..f5a4efc371 100644 --- a/src/nvim/fileio.c +++ b/src/nvim/fileio.c @@ -65,7 +65,7 @@ // For compatibility with libuv < 1.20.0 (tested on 1.18.0) #ifndef UV_FS_COPYFILE_FICLONE -#define UV_FS_COPYFILE_FICLONE 0 +# define UV_FS_COPYFILE_FICLONE 0 #endif #define HAS_BW_FLAGS @@ -105,9 +105,9 @@ struct bw_info { int bw_conv_error; // set for conversion error linenr_T bw_conv_error_lnum; // first line with error or zero linenr_T bw_start_lnum; // line number at start of buffer -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV iconv_t bw_iconv_fd; // descriptor for iconv() or -1 -# endif +#endif }; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -233,11 +233,11 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski char_u *fenc_next = NULL; // next item in 'fencs' or NULL bool advance_fenc = false; long real_size = 0; -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV iconv_t iconv_fd = (iconv_t)-1; // descriptor for iconv() or -1 bool did_iconv = false; // true when iconv() failed and trying // 'charconvert' next -# endif +#endif bool converted = false; // true if conversion done bool notconverted = false; // true if conversion wanted but it wasn't possible char_u conv_rest[CONV_RESTLEN]; @@ -373,10 +373,10 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski if (perm >= 0 && !S_ISREG(perm) // not a regular file ... && !S_ISFIFO(perm) // ... or fifo && !S_ISSOCK(perm) // ... or socket -# ifdef OPEN_CHR_FILES +#ifdef OPEN_CHR_FILES && !(S_ISCHR(perm) && is_dev_fd_file(fname)) // ... or a character special file named /dev/fd/<n> -# endif +#endif ) { if (S_ISDIR(perm)) { filemess(curbuf, fname, (char_u *)_(msg_is_a_directory), 0); @@ -493,11 +493,11 @@ int readfile(char_u *fname, char_u *sfname, linenr_T from, linenr_T lines_to_ski } else { filemess(curbuf, sfname, (char_u *)( (fd == UV_EFBIG) ? _("[File too big]") : -# if defined(UNIX) && defined(EOVERFLOW) +#if defined(UNIX) && defined(EOVERFLOW) // libuv only returns -errno in Unix and in Windows open() does not // set EOVERFLOW (fd == -EOVERFLOW) ? _("[File too big]") : -# endif +#endif _("[Permission Denied]")), 0); curbuf->b_p_ro = TRUE; // must use "w!" now } @@ -768,13 +768,13 @@ retry: } } -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV if (iconv_fd != (iconv_t)-1) { // aborted conversion with iconv(), close the descriptor iconv_close(iconv_fd); iconv_fd = (iconv_t)-1; } -# endif +#endif if (advance_fenc) { /* @@ -833,13 +833,13 @@ retry: -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV // Try using iconv() if we can't convert internally. if (fio_flags == 0 && !did_iconv) { iconv_fd = (iconv_t)my_iconv_open((char_u *)"utf-8", fenc); } -# endif +#endif /* * Use the 'charconvert' expression when conversion is required @@ -847,13 +847,13 @@ retry: */ if (fio_flags == 0 && !read_stdin && !read_buffer && *p_ccv != NUL && !read_fifo -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV && iconv_fd == (iconv_t)-1 -# endif +#endif ) { -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV did_iconv = false; -# endif +#endif /* Skip conversion when it's already done (retry for wrong * "fileformat"). */ if (tmpname == NULL) { @@ -872,9 +872,9 @@ retry: } } else { if (fio_flags == 0 -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV && iconv_fd == (iconv_t)-1 -# endif +#endif ) { /* Conversion wanted but we can't. * Try the next conversion in 'fileencodings' */ @@ -960,11 +960,11 @@ retry: * ucs-4 to utf-8: 4 bytes become up to 6 bytes, size must be * multiple of 4 */ real_size = (int)size; -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV if (iconv_fd != (iconv_t)-1) { size = size / ICONV_MULT; } else { -# endif +#endif if (fio_flags & FIO_LATIN1) { size = size / 2; } else if (fio_flags & (FIO_UCS2 | FIO_UTF16)) { @@ -974,9 +974,9 @@ retry: } else if (fio_flags == FIO_UCSBOM) { size = size / ICONV_MULT; // worst case } -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV } -# endif +#endif if (conv_restlen > 0) { // Insert unconverted bytes from previous line. memmove(ptr, conv_rest, conv_restlen); // -V614 @@ -1055,9 +1055,9 @@ retry: // When we did a conversion report an error. if (fio_flags != 0 -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV || iconv_fd != (iconv_t)-1 -# endif +#endif ) { if (can_retry) { goto rewind_retry; @@ -1081,9 +1081,9 @@ retry: * leave the UTF8 checking code to do it, as it * works slightly differently. */ if (bad_char_behavior != BAD_KEEP && (fio_flags != 0 -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV || iconv_fd != (iconv_t)-1 -# endif +#endif )) { while (conv_restlen > 0) { *(--ptr) = bad_char_behavior; @@ -1091,12 +1091,12 @@ retry: } } fio_flags = 0; // don't convert this -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV if (iconv_fd != (iconv_t)-1) { iconv_close(iconv_fd); iconv_fd = (iconv_t)-1; } -# endif +#endif } } } @@ -1165,7 +1165,7 @@ retry: break; } -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV if (iconv_fd != (iconv_t)-1) { /* * Attempt conversion of the read bytes to 'encoding' using @@ -1223,7 +1223,7 @@ retry: memmove(line_start, buffer, (size_t)linerest); size = ((char_u *)top - ptr); } -# endif +#endif if (fio_flags != 0) { unsigned int u8c; @@ -1441,12 +1441,12 @@ retry: if (can_retry && !incomplete_tail) { break; } -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV // When we did a conversion report an error. if (iconv_fd != (iconv_t)-1 && conv_error == 0) { conv_error = readfile_linenr(linecnt, ptr, p); } -# endif +#endif // Remember the first linenr with an illegal byte if (conv_error == 0 && illegal_byte == 0) { illegal_byte = readfile_linenr(linecnt, ptr, p); @@ -1469,17 +1469,17 @@ retry: // Detected a UTF-8 error. rewind_retry: // Retry reading with another conversion. -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV if (*p_ccv != NUL && iconv_fd != (iconv_t)-1) { // iconv() failed, try 'charconvert' did_iconv = true; } else { -# endif +#endif // use next item from 'fileencodings' advance_fenc = true; -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV } -# endif +#endif file_rewind = true; goto retry; } @@ -1700,11 +1700,11 @@ failed: if (fenc_alloced) { xfree(fenc); } -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV if (iconv_fd != (iconv_t)-1) { iconv_close(iconv_fd); } -# endif +#endif if (!read_buffer && !read_stdin) { close(fd); // errors are ignored @@ -2280,9 +2280,9 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_ write_info.bw_conv_error = FALSE; write_info.bw_conv_error_lnum = 0; write_info.bw_restlen = 0; -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV write_info.bw_iconv_fd = (iconv_t)-1; -# endif +#endif /* After writing a file changedtick changes but we don't want to display * the line. */ @@ -2690,7 +2690,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_ if (fd < 0) { // can't write in directory backup_copy = TRUE; } else { -# ifdef UNIX +#ifdef UNIX os_fchown(fd, file_info_old.stat.st_uid, file_info_old.stat.st_gid); if (!os_fileinfo((char *)IObuff, &file_info) || file_info.stat.st_uid != file_info_old.stat.st_uid @@ -2698,7 +2698,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_ || (long)file_info.stat.st_mode != perm) { backup_copy = TRUE; } -# endif +#endif /* Close the file before removing it, on MS-Windows we * can't delete an open file. */ close(fd); @@ -2711,7 +2711,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_ * Break symlinks and/or hardlinks if we've been asked to. */ if ((bkc & BKC_BREAKSYMLINK) || (bkc & BKC_BREAKHARDLINK)) { -# ifdef UNIX +#ifdef UNIX bool file_info_link_ok = os_fileinfo_link((char *)fname, &file_info); // Symlinks. @@ -2728,7 +2728,7 @@ int buf_write(buf_T *buf, char_u *fname, char_u *sfname, linenr_T start, linenr_ || os_fileinfo_id_equal(&file_info, &file_info_old))) { backup_copy = FALSE; } -# endif +#endif } // make sure we have a valid backup extension to use @@ -3085,7 +3085,7 @@ nobackup: if (converted && wb_flags == 0) { -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV // Use iconv() conversion when conversion is needed and it's not done // internally. write_info.bw_iconv_fd = (iconv_t)my_iconv_open(fenc, (char_u *)"utf-8"); @@ -3098,7 +3098,7 @@ nobackup: } write_info.bw_first = TRUE; } else -# endif +#endif /* * When the file needs to be converted with 'charconvert' after @@ -3114,9 +3114,9 @@ nobackup: } } if (converted && wb_flags == 0 -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV && write_info.bw_iconv_fd == (iconv_t)-1 -# endif +#endif && wfname == fname) { if (!forceit) { SET_ERRMSG(_("E213: Cannot convert (add ! to write without conversion)")); @@ -3644,12 +3644,12 @@ nofail: } xfree(fenc_tofree); xfree(write_info.bw_conv_buf); -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV if (write_info.bw_iconv_fd != (iconv_t)-1) { iconv_close(write_info.bw_iconv_fd); write_info.bw_iconv_fd = (iconv_t)-1; } -# endif +#endif #ifdef HAVE_ACL mch_free_acl(acl); #endif @@ -4034,7 +4034,7 @@ static int buf_write_bytes(struct bw_info *ip) } } -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV if (ip->bw_iconv_fd != (iconv_t)-1) { const char *from; size_t fromlen; @@ -4096,7 +4096,7 @@ static int buf_write_bytes(struct bw_info *ip) buf = ip->bw_conv_buf; len = (int)((char_u *)to - ip->bw_conv_buf); } -# endif +#endif } if (ip->bw_fd < 0) { diff --git a/src/nvim/fold.c b/src/nvim/fold.c index 7a017702ee..06b8049176 100644 --- a/src/nvim/fold.c +++ b/src/nvim/fold.c @@ -1414,7 +1414,7 @@ static void deleteFoldEntry(win_T *const wp, garray_T *const gap, const int idx, */ void deleteFoldRecurse(buf_T *bp, garray_T *gap) { -# define DELETE_FOLD_NESTED(fd) deleteFoldRecurse(bp, &((fd)->fd_nested)) +#define DELETE_FOLD_NESTED(fd) deleteFoldRecurse(bp, &((fd)->fd_nested)) GA_DEEP_CLEAR(gap, fold_T, DELETE_FOLD_NESTED); } @@ -1875,7 +1875,7 @@ char_u *get_foldtext(win_T *wp, linenr_T lnum, linenr_T lnume, foldinfo_T foldin } } if (*p != NUL) { - p = (char_u *)transstr((const char *)text); + p = (char_u *)transstr((const char *)text, true); xfree(text); text = p; } diff --git a/src/nvim/generators/c_grammar.lua b/src/nvim/generators/c_grammar.lua index c9ab0cf709..a5f76e1c6a 100644 --- a/src/nvim/generators/c_grammar.lua +++ b/src/nvim/generators/c_grammar.lua @@ -17,7 +17,7 @@ local fill = ws ^ 0 local c_comment = P('//') * (not_nl ^ 0) local c_preproc = P('#') * (not_nl ^ 0) local typed_container = - (P('ArrayOf(') + P('DictionaryOf(')) * ((any - P(')')) ^ 1) * P(')') + (P('ArrayOf(') + P('DictionaryOf(') + P('Dict(')) * ((any - P(')')) ^ 1) * P(')') local c_id = ( typed_container + (letter * (alpha ^ 0)) diff --git a/src/nvim/generators/gen_api_dispatch.lua b/src/nvim/generators/gen_api_dispatch.lua index 99d80cdebc..6ed2e5dab8 100644 --- a/src/nvim/generators/gen_api_dispatch.lua +++ b/src/nvim/generators/gen_api_dispatch.lua @@ -152,6 +152,8 @@ for _,f in ipairs(functions) do for i,param in ipairs(f.parameters) do if param[1] == "DictionaryOf(LuaRef)" then param = {"Dictionary", param[2]} + elseif startswith(param[1], "Dict(") then + param = {"Dictionary", param[2]} end f_exported.parameters[i] = param end @@ -173,7 +175,10 @@ local output = io.open(dispatch_outputf, 'wb') local function real_type(type) local rv = type - if c_grammar.typed_container:match(rv) then + local rmatch = string.match(type, "Dict%(([_%w]+)%)") + if rmatch then + return "KeyDict_"..rmatch + elseif c_grammar.typed_container:match(rv) then if rv:match('Array') then rv = 'Array' else @@ -209,8 +214,9 @@ for i = 1, #functions do -- Declare/initialize variables that will hold converted arguments for j = 1, #fn.parameters do local param = fn.parameters[j] + local rt = real_type(param[1]) local converted = 'arg_'..j - output:write('\n '..param[1]..' '..converted..';') + output:write('\n '..rt..' '..converted..';') end output:write('\n') output:write('\n if (args.size != '..#fn.parameters..') {') @@ -225,7 +231,24 @@ for i = 1, #functions do param = fn.parameters[j] converted = 'arg_'..j local rt = real_type(param[1]) - if rt ~= 'Object' then + if rt == 'Object' then + output:write('\n '..converted..' = args.items['..(j - 1)..'];\n') + elseif rt:match('^KeyDict_') then + converted = '&' .. converted + output:write('\n if (args.items['..(j - 1)..'].type == kObjectTypeDictionary) {') --luacheck: ignore 631 + output:write('\n memset('..converted..', 0, sizeof(*'..converted..'));') -- TODO: neeeee + output:write('\n if (!api_dict_to_keydict('..converted..', '..rt..'_get_field, args.items['..(j - 1)..'].data.dictionary, error)) {') + output:write('\n goto cleanup;') + output:write('\n }') + output:write('\n } else if (args.items['..(j - 1)..'].type == kObjectTypeArray && args.items['..(j - 1)..'].data.array.size == 0) {') --luacheck: ignore 631 + output:write('\n memset('..converted..', 0, sizeof(*'..converted..'));') + + output:write('\n } else {') + output:write('\n api_set_error(error, kErrorTypeException, \ + "Wrong type for argument '..j..' when calling '..fn.name..', expecting '..param[1]..'");') + output:write('\n goto cleanup;') + output:write('\n }\n') + else if rt:match('^Buffer$') or rt:match('^Window$') or rt:match('^Tabpage$') then -- Buffer, Window, and Tabpage have a specific type, but are stored in integer output:write('\n if (args.items['.. @@ -257,10 +280,7 @@ for i = 1, #functions do "Wrong type for argument '..j..' when calling '..fn.name..', expecting '..param[1]..'");') output:write('\n goto cleanup;') output:write('\n }\n') - else - output:write('\n '..converted..' = args.items['..(j - 1)..'];\n') end - args[#args + 1] = converted end @@ -423,13 +443,24 @@ local function process_function(fn) if param[1] == "DictionaryOf(LuaRef)" then extra = "true, " end + local errshift = 0 + if string.match(param_type, '^KeyDict_') then + write_shifted_output(output, string.format([[ + %s %s = { 0 }; nlua_pop_keydict(lstate, &%s, %s_get_field, %s&err);]], param_type, cparam, cparam, param_type, extra)) + cparam = '&'..cparam + errshift = 1 -- free incomplete dict on error + else + write_shifted_output(output, string.format([[ + const %s %s = nlua_pop_%s(lstate, %s&err);]], param[1], cparam, param_type, extra)) + end + write_shifted_output(output, string.format([[ - const %s %s = nlua_pop_%s(lstate, %s&err); if (ERROR_SET(&err)) { goto exit_%u; } - ]], param[1], cparam, param_type, extra, #fn.parameters - j)) + + ]], #fn.parameters - j + errshift)) free_code[#free_code + 1] = ('api_free_%s(%s);'):format( lc_param_type, cparam) cparams = cparam .. ', ' .. cparams @@ -446,7 +477,7 @@ local function process_function(fn) for i = 1, #free_code do local rev_i = #free_code - i + 1 local code = free_code[rev_i] - if i == 1 then + if i == 1 and not string.match(real_type(fn.parameters[1][1]), '^KeyDict_') then free_at_exit_code = free_at_exit_code .. ('\n %s'):format(code) else free_at_exit_code = free_at_exit_code .. ('\n exit_%u:\n %s'):format( diff --git a/src/nvim/generators/gen_declarations.lua b/src/nvim/generators/gen_declarations.lua index 0782c8115d..97491679a4 100755 --- a/src/nvim/generators/gen_declarations.lua +++ b/src/nvim/generators/gen_declarations.lua @@ -60,7 +60,7 @@ local right_word = concat( ) local word = branch( concat( - branch(lit('ArrayOf('), lit('DictionaryOf(')), -- typed container macro + branch(lit('ArrayOf('), lit('DictionaryOf('), lit('Dict(')), -- typed container macro one_or_more(any_character - lit(')')), lit(')') ), diff --git a/src/nvim/generators/gen_keysets.lua b/src/nvim/generators/gen_keysets.lua new file mode 100644 index 0000000000..63ef202fe1 --- /dev/null +++ b/src/nvim/generators/gen_keysets.lua @@ -0,0 +1,67 @@ + +local nvimsrcdir = arg[1] +local shared_file = arg[2] +local funcs_file = arg[3] +local defs_file = arg[4] + +_G.vim = loadfile(shared_file)() + +if nvimsrcdir == '--help' then + print([[ +Usage: + lua gen_keyset.lua TODOFIXUPDATETHIS + +Will generate build/src/nvim/auto/keyset.generated.h with definition of functions +static const array. +]]) + os.exit(0) +end + + +package.path = nvimsrcdir .. '/?.lua;' .. package.path +local hashy = require'generators.hashy' + +local funcspipe = io.open(funcs_file, 'wb') +local defspipe = io.open(defs_file, 'wb') + +local keysets = require'api.keysets' + +for name, keys in pairs(keysets) do + local neworder, hashfun = hashy.hashy_hash(name, keys, function (idx) + return name.."_table["..idx.."].str" + end) + + defspipe:write("typedef struct {\n") + for _, key in ipairs(neworder) do + defspipe:write(" Object "..key..";\n") + end + defspipe:write("} KeyDict_"..name..";\n\n") + + defspipe:write("extern KeySetLink "..name.."_table[];\n") + + funcspipe:write("KeySetLink "..name.."_table[] = {\n") + for _, key in ipairs(neworder) do + funcspipe:write(' {"'..key..'", offsetof(KeyDict_'..name..", "..key..")},\n") + end + funcspipe:write(' {NULL, 0},\n') + funcspipe:write("};\n\n") + + funcspipe:write(hashfun) + + funcspipe:write([[ +Object *KeyDict_]]..name..[[_get_field(void *retval, const char *str, size_t len) +{ + int hash = ]]..name..[[_hash(str, len); + if (hash == -1) { + return NULL; + } + + return (Object *)((char *)retval + ]]..name..[[_table[hash].ptr_off); +} + +]]) + defspipe:write("#define api_free_keydict_"..name.."(x) api_free_keydict(x, "..name.."_table)\n") +end + +funcspipe:close() +defspipe:close() diff --git a/src/nvim/generators/hashy.lua b/src/nvim/generators/hashy.lua new file mode 100644 index 0000000000..fac24c810a --- /dev/null +++ b/src/nvim/generators/hashy.lua @@ -0,0 +1,122 @@ +-- HASHY McHASHFACE + +local M = {} +_G.d = M + + +local function setdefault(table, key) + local val = table[key] + if val == nil then + val = {} + table[key] = val + end + return val +end + +function M.build_pos_hash(strings) + local len_buckets = {} + local maxlen = 0 + for _,s in ipairs(strings) do + table.insert(setdefault(len_buckets, #s),s) + if #s > maxlen then maxlen = #s end + end + + local len_pos_buckets = {} + local worst_buck_size = 0 + + for len = 1,maxlen do + local strs = len_buckets[len] + if strs then + -- the best position so far generates `best_bucket` + -- with `minsize` worst case collisions + local bestpos, minsize, best_bucket = nil, #strs*2, nil + for pos = 1,len do + local try_bucket = {} + for _,str in ipairs(strs) do + local poschar = string.sub(str, pos, pos) + table.insert(setdefault(try_bucket, poschar), str) + end + local maxsize = 1 + for _,pos_strs in pairs(try_bucket) do + maxsize = math.max(maxsize, #pos_strs) + end + if maxsize < minsize then + bestpos = pos + minsize = maxsize + best_bucket = try_bucket + end + end + len_pos_buckets[len] = {bestpos, best_bucket} + worst_buck_size = math.max(worst_buck_size, minsize) + end + end + return len_pos_buckets, maxlen, worst_buck_size +end + +function M.switcher(put, tab, maxlen, worst_buck_size) + local neworder = {} + put " switch (len) {\n" + local bucky = worst_buck_size > 1 + for len = 1,maxlen do + local vals = tab[len] + if vals then + put(" case "..len..": ") + local pos, posbuck = unpack(vals) + local keys = vim.tbl_keys(posbuck) + if #keys > 1 then + table.sort(keys) + put("switch (str["..(pos-1).."]) {\n") + for _,c in ipairs(keys) do + local buck = posbuck[c] + local startidx = #neworder + vim.list_extend(neworder, buck) + local endidx = #neworder + put(" case '"..c.."': ") + put("low = "..startidx.."; ") + if bucky then put("high = "..endidx.."; ") end + put "break;\n" + end + put " default: break;\n" + put " }\n " + else + local startidx = #neworder + table.insert(neworder, posbuck[keys[1]][1]) + local endidx = #neworder + put("low = "..startidx.."; ") + if bucky then put("high = "..endidx.."; ") end + end + put " break;\n" + end + end + put " default: break;\n" + put " }\n" + return neworder +end + +function M.hashy_hash(name, strings, access) + local stats = {} + local put = function(str) table.insert(stats, str) end + local len_pos_buckets, maxlen, worst_buck_size = M.build_pos_hash(strings) + put("int "..name.."_hash(const char *str, size_t len)\n{\n") + if worst_buck_size > 1 then + put(" int low = 0, high = 0;\n") + else + put(" int low = -1;\n") + end + local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size) + if worst_buck_size > 1 then + error [[ not implemented yet ]] -- TODO(bfredl) + else + put [[ + if (low < 0) { + return -1; + } + ]] + put("if(memcmp(str, "..access("low")..", len)) {\n return -1;\n }\n") + put " return low;\n" + put "}\n\n" + end + return neworder, table.concat(stats) +end + +return M diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c index beb4ff4da6..15acd73aa5 100644 --- a/src/nvim/getchar.c +++ b/src/nvim/getchar.c @@ -1000,6 +1000,18 @@ void ins_char_typebuf(int c) buf[3] = NUL; } else { buf[utf_char2bytes(c, buf)] = NUL; + char_u *p = buf; + while (*p) { + if ((uint8_t)(*p) == CSI || (uint8_t)(*p) == K_SPECIAL) { + bool is_csi = (uint8_t)(*p) == CSI; + memmove(p + 3, p + 1, STRLEN(p + 1) + 1); + *p++ = K_SPECIAL; + *p++ = is_csi ? KS_EXTRA : KS_SPECIAL; + *p++ = is_csi ? KE_CSI : KE_FILLER; + } else { + p++; + } + } } (void)ins_typebuf(buf, KeyNoremap, 0, !KeyTyped, cmd_silent); } @@ -1558,7 +1570,7 @@ int vgetc(void) // a CSI (0x9B), // of a K_SPECIAL - KS_EXTRA - KE_CSI, which is CSI too. c = vgetorpeek(true); - if (vgetorpeek(true) == (int)KE_CSI && c == KS_EXTRA) { + if (vgetorpeek(true) == KE_CSI && c == KS_EXTRA) { buf[i] = CSI; } } @@ -1573,9 +1585,9 @@ int vgetc(void) if (!no_mapping && KeyTyped && (mod_mask == MOD_MASK_ALT || mod_mask == MOD_MASK_META)) { mod_mask = 0; - stuffcharReadbuff(c); - u_sync(false); - c = ESC; + ins_char_typebuf(c); + ins_char_typebuf(ESC); + continue; } break; @@ -1934,7 +1946,7 @@ static int vgetorpeek(bool advance) && (mp->m_keys[0] != K_SPECIAL || mp->m_keys[1] != KS_EXTRA || mp->m_keys[2] - != (int)KE_SNR)) { + != KE_SNR)) { continue; } /* @@ -2216,7 +2228,7 @@ static int vgetorpeek(bool advance) if (!ascii_iswhite(ptr[col])) { curwin->w_wcol = vcol; } - vcol += lbr_chartabsize(ptr, ptr + col, (colnr_T)vcol); + vcol += lbr_chartabsize(ptr, ptr + col, vcol); col += utfc_ptr2len(ptr + col); } curwin->w_wrow = curwin->w_cline_row diff --git a/src/nvim/getchar.h b/src/nvim/getchar.h index f0b52079aa..83fa00977f 100644 --- a/src/nvim/getchar.h +++ b/src/nvim/getchar.h @@ -56,6 +56,8 @@ struct map_arguments { size_t orig_rhs_len; }; typedef struct map_arguments MapArguments; +#define MAP_ARGUMENTS_INIT { false, false, false, false, false, false, false, \ + { 0 }, 0, NULL, 0, false, NULL, 0 } #define KEYLEN_PART_KEY -1 // keylen value for incomplete key-code #define KEYLEN_PART_MAP -2 // keylen value for incomplete mapping diff --git a/src/nvim/hardcopy.c b/src/nvim/hardcopy.c index 125108ee78..9d9ffa550a 100644 --- a/src/nvim/hardcopy.c +++ b/src/nvim/hardcopy.c @@ -140,13 +140,13 @@ static uint32_t curr_bg; static uint32_t curr_fg; static int page_count; -# define OPT_MBFONT_USECOURIER 0 -# define OPT_MBFONT_ASCII 1 -# define OPT_MBFONT_REGULAR 2 -# define OPT_MBFONT_BOLD 3 -# define OPT_MBFONT_OBLIQUE 4 -# define OPT_MBFONT_BOLDOBLIQUE 5 -# define OPT_MBFONT_NUM_OPTIONS 6 +#define OPT_MBFONT_USECOURIER 0 +#define OPT_MBFONT_ASCII 1 +#define OPT_MBFONT_REGULAR 2 +#define OPT_MBFONT_BOLD 3 +#define OPT_MBFONT_OBLIQUE 4 +#define OPT_MBFONT_BOLDOBLIQUE 5 +#define OPT_MBFONT_NUM_OPTIONS 6 static option_table_T mbfont_opts[OPT_MBFONT_NUM_OPTIONS] = { @@ -643,12 +643,8 @@ void ex_hardcopy(exarg_T *eap) * PS.) */ if (mch_print_init(&settings, - curbuf->b_fname == NULL - ? (char_u *)buf_spname(curbuf) - : curbuf->b_sfname == NULL - ? curbuf->b_fname - : curbuf->b_sfname, - eap->forceit) == FAIL) { + curbuf->b_fname == NULL ? buf_spname(curbuf) : curbuf->b_sfname == + NULL ? curbuf->b_fname : curbuf->b_sfname, eap->forceit) == FAIL) { return; } diff --git a/src/nvim/if_cscope.c b/src/nvim/if_cscope.c index 31615e744a..76dcb58236 100644 --- a/src/nvim/if_cscope.c +++ b/src/nvim/if_cscope.c @@ -9,31 +9,29 @@ * might be a few lines of code that look similar to what Nvi has. */ -#include <stdbool.h> - #include <assert.h> #include <errno.h> -#include <inttypes.h> #include <fcntl.h> +#include <inttypes.h> +#include <stdbool.h> +#include <sys/stat.h> +#include <sys/types.h> -#include "nvim/buffer.h" #include "nvim/ascii.h" -#include "nvim/if_cscope.h" +#include "nvim/buffer.h" #include "nvim/charset.h" +#include "nvim/event/stream.h" #include "nvim/fileio.h" -#include "nvim/message.h" +#include "nvim/if_cscope.h" #include "nvim/memory.h" +#include "nvim/message.h" +#include "nvim/os/input.h" +#include "nvim/os/os.h" #include "nvim/os/time.h" #include "nvim/path.h" #include "nvim/quickfix.h" #include "nvim/strings.h" #include "nvim/tag.h" -#include "nvim/os/os.h" -#include "nvim/os/input.h" -#include "nvim/event/stream.h" - -#include <sys/types.h> -#include <sys/stat.h> #if defined(UNIX) # include <sys/wait.h> #endif @@ -90,19 +88,20 @@ char_u *get_cscope_name(expand_T *xp, int idx) // Complete with sub-commands of ":cscope": // add, find, help, kill, reset, show return (char_u *)cs_cmds[idx].name; - case EXP_SCSCOPE_SUBCMD: - { + case EXP_SCSCOPE_SUBCMD: { // Complete with sub-commands of ":scscope": same sub-commands as // ":cscope" but skip commands which don't support split windows int i; - for (i = 0, current_idx = 0; cs_cmds[i].name != NULL; i++) - if (cs_cmds[i].cansplit) - if (current_idx++ == idx) + for (i = 0, current_idx = 0; cs_cmds[i].name != NULL; i++) { + if (cs_cmds[i].cansplit) { + if (current_idx++ == idx) { break; + } + } + } return (char_u *)cs_cmds[i].name; } - case EXP_CSCOPE_FIND: - { + case EXP_CSCOPE_FIND: { const char *query_type[] = { "a", "c", "d", "e", "f", "g", "i", "s", "t", NULL @@ -114,8 +113,7 @@ char_u *get_cscope_name(expand_T *xp, int idx) // redundant. return (char_u *)query_type[idx]; } - case EXP_CSCOPE_KILL: - { + case EXP_CSCOPE_KILL: { static char connection[5]; // ":cscope kill" accepts connection numbers or partial names of @@ -124,8 +122,9 @@ char_u *get_cscope_name(expand_T *xp, int idx) // connections. size_t i; for (i = 0, current_idx = 0; i < csinfo_size; i++) { - if (csinfo[i].fname == NULL) + if (csinfo[i].fname == NULL) { continue; + } if (current_idx++ == idx) { vim_snprintf(connection, sizeof(connection), "%zu", i); return (char_u *)connection; @@ -172,11 +171,9 @@ void set_context_in_cscope_cmd(expand_T *xp, const char *arg, cmdidx_T cmdidx) /// Find the command, print help if invalid, and then call the corresponding /// command function. -static void -do_cscope_general( - exarg_T *eap, - int make_split // whether to split window -) +/// +/// @param make_split whether to split window +static void do_cscope_general(exarg_T *eap, int make_split) { cscmd_T *cmdp; @@ -187,8 +184,7 @@ do_cscope_general( if (make_split) { if (!cmdp->cansplit) { - (void)MSG_PUTS(_( - "This cscope command does not support splitting the window.\n")); + (void)MSG_PUTS(_("This cscope command does not support splitting the window.\n")); return; } postponed_split = -1; @@ -228,14 +224,16 @@ void ex_cstag(exarg_T *eap) case 0: if (cs_check_for_connections()) { ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE, - FALSE, *eap->cmdlinep); + FALSE, *eap->cmdlinep); if (ret == FALSE) { cs_free_tags(); - if (msg_col) + if (msg_col) { msg_putchar('\n'); + } - if (cs_check_for_tags()) + if (cs_check_for_tags()) { ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE); + } } } else if (cs_check_for_tags()) { ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE); @@ -245,21 +243,24 @@ void ex_cstag(exarg_T *eap) if (cs_check_for_tags()) { ret = do_tag(eap->arg, DT_JUMP, 0, eap->forceit, FALSE); if (ret == FALSE) { - if (msg_col) + if (msg_col) { msg_putchar('\n'); + } if (cs_check_for_connections()) { ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, - FALSE, FALSE, *eap->cmdlinep); - if (ret == FALSE) + FALSE, FALSE, *eap->cmdlinep); + if (ret == FALSE) { cs_free_tags(); + } } } } else if (cs_check_for_connections()) { ret = cs_find_common("g", (char *)(eap->arg), eap->forceit, FALSE, - FALSE, *eap->cmdlinep); - if (ret == FALSE) + FALSE, *eap->cmdlinep); + if (ret == FALSE) { cs_free_tags(); + } } break; default: @@ -306,29 +307,29 @@ void cs_print_tags(void) /* * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function * - * Checks for the existence of a |cscope| connection. If no - * parameters are specified, then the function returns: + * Checks for the existence of a |cscope| connection. If no + * parameters are specified, then the function returns: * - * 0, if cscope was not available (not compiled in), or if there - * are no cscope connections; or - * 1, if there is at least one cscope connection. + * 0, if cscope was not available (not compiled in), or if there + * are no cscope connections; or + * 1, if there is at least one cscope connection. * - * If parameters are specified, then the value of {num} - * determines how existence of a cscope connection is checked: + * If parameters are specified, then the value of {num} + * determines how existence of a cscope connection is checked: * - * {num} Description of existence check - * ----- ------------------------------ - * 0 Same as no parameters (e.g., "cscope_connection()"). - * 1 Ignore {prepend}, and use partial string matches for - * {dbpath}. - * 2 Ignore {prepend}, and use exact string matches for - * {dbpath}. - * 3 Use {prepend}, use partial string matches for both - * {dbpath} and {prepend}. - * 4 Use {prepend}, use exact string matches for both - * {dbpath} and {prepend}. + * {num} Description of existence check + * ----- ------------------------------ + * 0 Same as no parameters (e.g., "cscope_connection()"). + * 1 Ignore {prepend}, and use partial string matches for + * {dbpath}. + * 2 Ignore {prepend}, and use exact string matches for + * {dbpath}. + * 3 Use {prepend}, use partial string matches for both + * {dbpath} and {prepend}. + * 4 Use {prepend}, use exact string matches for both + * {dbpath} and {prepend}. * - * Note: All string comparisons are case sensitive! + * Note: All string comparisons are case sensitive! */ bool cs_connection(int num, char_u *dbpath, char_u *ppath) { @@ -393,8 +394,9 @@ static int cs_add(exarg_T *eap) cs_usage_msg(Add); return CSCOPE_FAILURE; } - if ((ppath = strtok((char *)NULL, (const char *)" ")) != NULL) + if ((ppath = strtok((char *)NULL, (const char *)" ")) != NULL) { flags = strtok((char *)NULL, (const char *)" "); + } return cs_add_common(fname, ppath, flags); } @@ -413,18 +415,16 @@ static void cs_stat_emsg(char *fname) /// The common routine to add a new cscope connection. Called by /// cs_add() and cs_reset(). I really don't like to do this, but this /// routine uses a number of goto statements. -static int -cs_add_common( - char *arg1, // filename - may contain environment variables - char *arg2, // prepend path - may contain environment variables - char *flags -) +/// +/// @param arg1 filename - may contain environment variables +/// @param arg2 prepend path - may contain environment variables +static int cs_add_common(char *arg1, char *arg2, char *flags) { - char *fname = NULL; - char *fname2 = NULL; - char *ppath = NULL; + char *fname = NULL; + char *fname2 = NULL; + char *ppath = NULL; size_t usedlen = 0; - char_u *fbuf = NULL; + char_u *fbuf = NULL; // get the filename (arg1), expand it, and try to stat it fname = xmalloc(MAXPATHL + 1); @@ -443,8 +443,9 @@ cs_add_common( bool file_info_ok = os_fileinfo(fname, &file_info); if (!file_info_ok) { staterr: - if (p_csverbose) + if (p_csverbose) { cs_stat_emsg(fname); + } goto add_err; } @@ -465,31 +466,32 @@ staterr: while (fname[strlen(fname)-1] == '/' ) { fname[strlen(fname)-1] = '\0'; - if (fname[0] == '\0') + if (fname[0] == '\0') { break; + } } - if (fname[0] == '\0') + if (fname[0] == '\0') { (void)sprintf(fname2, "/%s", CSCOPE_DBFILE); - else + } else { (void)sprintf(fname2, "%s/%s", fname, CSCOPE_DBFILE); + } file_info_ok = os_fileinfo(fname2, &file_info); if (!file_info_ok) { - if (p_csverbose) + if (p_csverbose) { cs_stat_emsg(fname2); + } goto add_err; } i = cs_insert_filelist(fname2, ppath, flags, &file_info); - } - else if (S_ISREG(file_info.stat.st_mode) || S_ISLNK(file_info.stat.st_mode)) - { + } else if (S_ISREG(file_info.stat.st_mode) || S_ISLNK(file_info.stat.st_mode)) { i = cs_insert_filelist(fname, ppath, flags, &file_info); } else { - if (p_csverbose) - (void)EMSG2( - _("E564: %s is not a directory or a valid cscope database"), - fname); + if (p_csverbose) { + (void)EMSG2(_("E564: %s is not a directory or a valid cscope database"), + fname); + } goto add_err; } @@ -538,15 +540,15 @@ static size_t cs_cnt_connections(void) size_t cnt = 0; for (size_t i = 0; i < csinfo_size; i++) { - if (csinfo[i].fname != NULL) + if (csinfo[i].fname != NULL) { cnt++; + } } return cnt; } -static void cs_reading_emsg( - size_t idx // connection index -) +/// @param idx connection index +static void cs_reading_emsg(size_t idx) { EMSGU(_("E262: error reading cscope connection %" PRIu64), idx); } @@ -582,7 +584,7 @@ static int cs_cnt_matches(size_t idx) // Accept "\S*cscope: X lines", also matches "mlcscope". // Bail out for the "Unable to search" error. if (strstr((const char *)buf, "Unable to search database") != NULL) { - break; + break; } if ((stok = strtok(buf, (const char *)" ")) == NULL) { continue; @@ -591,18 +593,21 @@ static int cs_cnt_matches(size_t idx) continue; } - if ((stok = strtok(NULL, (const char *)" ")) == NULL) + if ((stok = strtok(NULL, (const char *)" ")) == NULL) { continue; + } nlines = atoi(stok); if (nlines < 0) { nlines = 0; break; } - if ((stok = strtok(NULL, (const char *)" ")) == NULL) + if ((stok = strtok(NULL, (const char *)" ")) == NULL) { continue; - if (strncmp((const char *)stok, "lines", 5)) + } + if (strncmp((const char *)stok, "lines", 5)) { continue; + } break; } @@ -620,31 +625,40 @@ static char *cs_create_cmd(char *csoption, char *pattern) char *pat; switch (csoption[0]) { - case '0': case 's': + case '0': + case 's': search = 0; break; - case '1': case 'g': + case '1': + case 'g': search = 1; break; - case '2': case 'd': + case '2': + case 'd': search = 2; break; - case '3': case 'c': + case '3': + case 'c': search = 3; break; - case '4': case 't': + case '4': + case 't': search = 4; break; - case '6': case 'e': + case '6': + case 'e': search = 6; break; - case '7': case 'f': + case '7': + case 'f': search = 7; break; - case '8': case 'i': + case '8': + case 'i': search = 8; break; - case '9': case 'a': + case '9': + case 'a': search = 9; break; default: @@ -656,9 +670,11 @@ static char *cs_create_cmd(char *csoption, char *pattern) // Skip white space before the patter, except for text and pattern search, // they may want to use the leading white space. pat = pattern; - if (search != 4 && search != 6) - while (ascii_iswhite(*pat)) + if (search != 4 && search != 6) { + while (ascii_iswhite(*pat)) { ++pat; + } + } cmd = xmalloc(strlen(pat) + 2); @@ -675,7 +691,7 @@ static int cs_create_connection(size_t i) #ifdef UNIX int to_cs[2], from_cs[2]; #endif - char *prog, *cmd, *ppath = NULL; + char *prog, *cmd, *ppath = NULL; #if defined(UNIX) /* @@ -686,14 +702,18 @@ static int cs_create_connection(size_t i) if (pipe(to_cs) < 0 || pipe(from_cs) < 0) { (void)EMSG(_("E566: Could not create cscope pipes")); err_closing: - if (to_cs[0] != -1) + if (to_cs[0] != -1) { (void)close(to_cs[0]); - if (to_cs[1] != -1) + } + if (to_cs[1] != -1) { (void)close(to_cs[1]); - if (from_cs[0] != -1) + } + if (from_cs[0] != -1) { (void)close(from_cs[0]); - if (from_cs[1] != -1) + } + if (from_cs[1] != -1) { (void)close(from_cs[1]); + } return CSCOPE_FAILURE; } @@ -759,8 +779,9 @@ err_closing: len += strlen(ppath); } - if (csinfo[i].flags) + if (csinfo[i].flags) { len += strlen(csinfo[i].flags); + } cmd = xmalloc(len); @@ -779,10 +800,10 @@ err_closing: (void)strcat(cmd, " "); (void)strcat(cmd, csinfo[i].flags); } -# ifdef UNIX +#ifdef UNIX // on Win32 we still need prog xfree(prog); -# endif +#endif xfree(ppath); #if defined(UNIX) @@ -791,12 +812,14 @@ err_closing: # if defined(HAVE_SETSID) (void)setsid(); # else - if (setpgid(0, 0) == -1) + if (setpgid(0, 0) == -1) { PERROR(_("cs_create_connection setpgid failed")); + } # endif # endif - if (execl("/bin/sh", "sh", "-c", cmd, (char *)NULL) == -1) + if (execl("/bin/sh", "sh", "-c", cmd, (char *)NULL) == -1) { PERROR(_("cs_create_connection exec failed")); + } exit(127); // NOTREACHED @@ -827,7 +850,7 @@ err_closing: si.hStdError = stdout_wr; si.hStdInput = stdin_rd; created = CreateProcess(NULL, cmd, NULL, NULL, TRUE, CREATE_NEW_CONSOLE, - NULL, NULL, &si, &pi); + NULL, NULL, &si, &pi); xfree(prog); xfree(cmd); @@ -888,9 +911,11 @@ static int cs_find(exarg_T *eap) * Let's replace the NULs written by strtok() with spaces - we need the * spaces to correctly display the quickfix/location list window's title. */ - for (int i = 0; i < eap_arg_len; ++i) - if (NUL == eap->arg[i]) + for (int i = 0; i < eap_arg_len; ++i) { + if (NUL == eap->arg[i]) { eap->arg[i] = ' '; + } + } return cs_find_common(opt, pat, eap->forceit, true, eap->cmdidx == CMD_lcscope, *eap->cmdlinep); @@ -898,8 +923,8 @@ static int cs_find(exarg_T *eap) /// Common code for cscope find, shared by cs_find() and ex_cstag(). -static int cs_find_common(char *opt, char *pat, int forceit, int verbose, - int use_ll, char_u *cmdline) +static int cs_find_common(char *opt, char *pat, int forceit, int verbose, int use_ll, + char_u *cmdline) { char *cmd; int *nummatches; @@ -966,8 +991,9 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, // create the actual command to send to cscope cmd = cs_create_cmd(opt, pat); - if (cmd == NULL) + if (cmd == NULL) { return FALSE; + } nummatches = xmalloc(sizeof(int) * csinfo_size); @@ -978,8 +1004,9 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, } totmatches = 0; for (size_t i = 0; i < csinfo_size; i++) { - if (csinfo[i].fname == NULL || csinfo[i].to_fp == NULL) + if (csinfo[i].fname == NULL || csinfo[i].to_fp == NULL) { continue; + } // send cmd to cscope (void)fprintf(csinfo[i].to_fp, "%s\n", cmd); @@ -987,11 +1014,13 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, nummatches[i] = cs_cnt_matches(i); - if (nummatches[i] > -1) + if (nummatches[i] > -1) { totmatches += (size_t)nummatches[i]; + } - if (nummatches[i] == 0) + if (nummatches[i] == 0) { (void)cs_read_prompt(i); + } } xfree(cmd); @@ -1014,10 +1043,10 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, if (qfpos != NULL && *qfpos != '0') { // Fill error list. - FILE *f; - char_u *tmp = vim_tempname(); - qf_info_T *qi = NULL; - win_T *wp = NULL; + FILE *f; + char_u *tmp = vim_tempname(); + qf_info_T *qi = NULL; + win_T *wp = NULL; f = os_fopen((char *)tmp, "w"); if (f == NULL) { @@ -1039,14 +1068,15 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, } apply_autocmds(EVENT_QUICKFIXCMDPOST, (char_u *)"cscope", - curbuf->b_fname, TRUE, curbuf); - if (use_ll) + curbuf->b_fname, TRUE, curbuf); + if (use_ll) { /* * In the location list window, use the displayed location * list. Otherwise, use the location list for the window. */ qi = (bt_quickfix(wp->w_buffer) && wp->w_llist_ref != NULL) ? wp->w_llist_ref : wp->w_llist; + } qf_jump(qi, 0, 0, forceit); } } @@ -1059,11 +1089,11 @@ static int cs_find_common(char *opt, char *pat, int forceit, int verbose, size_t matched = 0; // read output - cs_fill_results((char *)pat, totmatches, nummatches, &matches, - &contexts, &matched); + cs_fill_results(pat, totmatches, nummatches, &matches, &contexts, &matched); xfree(nummatches); - if (matches == NULL) + if (matches == NULL) { return FALSE; + } (void)cs_manage_matches(matches, contexts, matched, Store); @@ -1086,10 +1116,10 @@ static int cs_help(exarg_T *eap) space_cnt = 0; } (void)smsg(_("%-5s: %s%*s (Usage: %s)"), - cmdp->name, - help, space_cnt, " ", - cmdp->usage); - if (strcmp(cmdp->name, "find") == 0) + cmdp->name, + help, space_cnt, " ", + cmdp->usage); + if (strcmp(cmdp->name, "find") == 0) { MSG_PUTS(_("\n" " a: Find assignments to this symbol\n" " c: Find functions calling this function\n" @@ -1100,6 +1130,7 @@ static int cs_help(exarg_T *eap) " i: Find files #including this file\n" " s: Find this C symbol\n" " t: Find this text string\n")); + } cmdp++; } @@ -1121,8 +1152,7 @@ static void clear_csinfo(size_t i) } /// Insert a new cscope database filename into the filelist. -static int cs_insert_filelist(char *fname, char *ppath, char *flags, - FileInfo *file_info) +static int cs_insert_filelist(char *fname, char *ppath, char *flags, FileInfo *file_info) { size_t i = 0; bool empty_found = false; @@ -1130,8 +1160,9 @@ static int cs_insert_filelist(char *fname, char *ppath, char *flags, for (size_t j = 0; j < csinfo_size; j++) { if (csinfo[j].fname != NULL && os_fileid_equal_fileinfo(&(csinfo[j].file_id), file_info)) { - if (p_csverbose) + if (p_csverbose) { (void)EMSG(_("E568: duplicate cscope database not added")); + } return CSCOPE_FAILURE; } @@ -1154,8 +1185,9 @@ static int cs_insert_filelist(char *fname, char *ppath, char *flags, csinfo_size *= 2; csinfo = xrealloc(csinfo, sizeof(csinfo_T)*csinfo_size); } - for (size_t j = csinfo_size/2; j < csinfo_size; j++) + for (size_t j = csinfo_size/2; j < csinfo_size; j++) { clear_csinfo(j); + } } csinfo[i].fname = xmalloc(strlen(fname) + 1); @@ -1165,14 +1197,16 @@ static int cs_insert_filelist(char *fname, char *ppath, char *flags, if (ppath != NULL) { csinfo[i].ppath = xmalloc(strlen(ppath) + 1); (void)strcpy(csinfo[i].ppath, (const char *)ppath); - } else + } else { csinfo[i].ppath = NULL; + } if (flags != NULL) { csinfo[i].flags = xmalloc(strlen(flags) + 1); (void)strcpy(csinfo[i].flags, (const char *)flags); - } else + } else { csinfo[i].flags = NULL; + } os_fileinfo_id(file_info, &(csinfo[i].file_id)); assert(i <= INT_MAX); @@ -1181,25 +1215,28 @@ static int cs_insert_filelist(char *fname, char *ppath, char *flags, /// Find cscope command in command table. -static cscmd_T * cs_lookup_cmd(exarg_T *eap) +static cscmd_T *cs_lookup_cmd(exarg_T *eap) { cscmd_T *cmdp; char *stok; size_t len; - if (eap->arg == NULL) + if (eap->arg == NULL) { return NULL; + } // Store length of eap->arg before it gets modified by strtok(). eap_arg_len = (int)STRLEN(eap->arg); - if ((stok = strtok((char *)(eap->arg), (const char *)" ")) == NULL) + if ((stok = strtok((char *)(eap->arg), (const char *)" ")) == NULL) { return NULL; + } len = strlen(stok); for (cmdp = cs_cmds; cmdp->name != NULL; ++cmdp) { - if (strncmp((const char *)(stok), cmdp->name, len) == 0) + if (strncmp((const char *)(stok), cmdp->name, len) == 0) { return cmdp; + } } return NULL; } @@ -1224,21 +1261,23 @@ static int cs_kill(exarg_T *eap) || (strlen(stok) < 3 && stok[0] == '-' && ascii_isdigit((int)(stok[1])))) { num = atoi(stok); - if (num == -1) + if (num == -1) { killall = true; - else if (num >= 0) { + } else if (num >= 0) { i = (size_t)num; } else { // All negative values besides -1 are invalid. - if (p_csverbose) + if (p_csverbose) { (void)EMSG2(_("E261: cscope connection %s not found"), stok); + } return CSCOPE_FAILURE; } } else { // Else it must be part of a name. We will try to find a match // within all the names in the csinfo data structure for (i = 0; i < csinfo_size; i++) { - if (csinfo[i].fname != NULL && strstr(csinfo[i].fname, stok)) + if (csinfo[i].fname != NULL && strstr(csinfo[i].fname, stok)) { break; + } } } @@ -1250,8 +1289,9 @@ static int cs_kill(exarg_T *eap) } else { if (killall) { for (i = 0; i < csinfo_size; i++) { - if (csinfo[i].fname) + if (csinfo[i].fname) { cs_kill_execute(i, csinfo[i].fname); + } } } else { cs_kill_execute((size_t)i, stok); @@ -1263,10 +1303,10 @@ static int cs_kill(exarg_T *eap) /// Actually kills a specific cscope connection. -static void cs_kill_execute( - size_t i, // cscope table index - char *cname // cscope database name -) +/// +/// @param i cscope table index +/// @param cname cscope database name +static void cs_kill_execute(size_t i, char *cname) { if (p_csverbose) { msg_clr_eos(); @@ -1293,8 +1333,7 @@ static void cs_kill_execute( /// Besides, even if this particular case didn't happen, the search pattern /// would still have to be modified to escape all the special regular expression /// characters to comply with ctags formatting. -static char *cs_make_vim_style_matches(char *fname, char *slno, char *search, - char *tagstr) +static char *cs_make_vim_style_matches(char *fname, char *slno, char *search, char *tagstr) { // vim style is ctags: // @@ -1339,8 +1378,7 @@ static char *cs_make_vim_style_matches(char *fname, char *slno, char *search, /// Free: frees up everything and resets /// /// Print: prints the tags -static char *cs_manage_matches(char **matches, char **contexts, - size_t totmatches, mcmd_e cmd) +static char *cs_manage_matches(char **matches, char **contexts, size_t totmatches, mcmd_e cmd) { static char **mp = NULL; static char **cp = NULL; @@ -1352,16 +1390,18 @@ static char *cs_manage_matches(char **matches, char **contexts, case Store: assert(matches != NULL); assert(totmatches > 0); - if (mp != NULL || cp != NULL) + if (mp != NULL || cp != NULL) { (void)cs_manage_matches(NULL, NULL, 0, Free); + } mp = matches; cp = contexts; cnt = totmatches; next = 0; break; case Get: - if (next >= cnt) + if (next >= cnt) { return NULL; + } p = mp[next]; next++; @@ -1370,8 +1410,9 @@ static char *cs_manage_matches(char **matches, char **contexts, if (mp != NULL) { while (cnt--) { xfree(mp[cnt]); - if (cp != NULL) + if (cp != NULL) { xfree(cp[cnt]); + } } xfree(mp); xfree(cp); @@ -1396,8 +1437,8 @@ static char *cs_manage_matches(char **matches, char **contexts, /// Parse cscope output. -static char *cs_parse_results(size_t cnumber, char *buf, int bufsize, - char **context, char **linenumber, char **search) +static char *cs_parse_results(size_t cnumber, char *buf, int bufsize, char **context, + char **linenumber, char **search) { int ch; char *p; @@ -1421,8 +1462,9 @@ retry: // If the line's too long for the buffer, discard it. if ((p = strchr(buf, '\n')) == NULL) { - while ((ch = getc(csinfo[cnumber].fr_fp)) != EOF && ch != '\n') + while ((ch = getc(csinfo[cnumber].fr_fp)) != EOF && ch != '\n') { ; + } return NULL; } *p = '\0'; @@ -1430,14 +1472,18 @@ retry: /* * cscope output is in the following format: * - * <filename> <context> <line number> <pattern> + * <filename> <context> <line number> <pattern> */ - if ((name = strtok((char *)buf, (const char *)" ")) == NULL) + char *saveptr = NULL; + if ((name = os_strtok(buf, (const char *)" ", &saveptr)) == NULL) { return NULL; - if ((*context = strtok(NULL, (const char *)" ")) == NULL) + } + if ((*context = os_strtok(NULL, (const char *)" ", &saveptr)) == NULL) { return NULL; - if ((*linenumber = strtok(NULL, (const char *)" ")) == NULL) + } + if ((*linenumber = os_strtok(NULL, (const char *)" ", &saveptr)) == NULL) { return NULL; + } *search = *linenumber + strlen(*linenumber) + 1; // +1 to skip \0 // --- nvi --- @@ -1463,25 +1509,29 @@ static void cs_file_results(FILE *f, int *nummatches_a) char *buf = xmalloc(CSREAD_BUFSIZE); for (size_t i = 0; i < csinfo_size; i++) { - if (nummatches_a[i] < 1) + if (nummatches_a[i] < 1) { continue; + } for (int j = 0; j < nummatches_a[i]; j++) { if ((fullname = cs_parse_results(i, buf, CSREAD_BUFSIZE, &cntx, - &slno, &search)) == NULL) + &slno, &search)) == NULL) { continue; + } context = xmalloc(strlen(cntx) + 5); - if (strcmp(cntx, "<global>")==0) + if (strcmp(cntx, "<global>")==0) { strcpy(context, "<<global>>"); - else + } else { sprintf(context, "<<%s>>", cntx); + } - if (search == NULL) + if (search == NULL) { fprintf(f, "%s\t%s\t%s\n", fullname, slno, context); - else + } else { fprintf(f, "%s\t%s\t%s %s\n", fullname, slno, context, search); + } xfree(context); xfree(fullname); @@ -1495,9 +1545,8 @@ static void cs_file_results(FILE *f, int *nummatches_a) /// Get parsed cscope output and calls cs_make_vim_style_matches to convert /// into ctags format. /// When there are no matches sets "*matches_p" to NULL. -static void cs_fill_results(char *tagstr, size_t totmatches, int *nummatches_a, - char ***matches_p, char ***cntxts_p, - size_t *matched) +static void cs_fill_results(char *tagstr, size_t totmatches, int *nummatches_a, char ***matches_p, + char ***cntxts_p, size_t *matched) { char *buf; char *search, *slno; @@ -1514,22 +1563,24 @@ static void cs_fill_results(char *tagstr, size_t totmatches, int *nummatches_a, cntxts = xmalloc(sizeof(char *) * (size_t)totmatches); for (size_t i = 0; i < csinfo_size; i++) { - if (nummatches_a[i] < 1) + if (nummatches_a[i] < 1) { continue; + } for (int j = 0; j < nummatches_a[i]; j++) { if ((fullname = cs_parse_results(i, buf, CSREAD_BUFSIZE, &cntx, - &slno, &search)) == NULL) + &slno, &search)) == NULL) { continue; + } matches[totsofar] = cs_make_vim_style_matches(fullname, slno, search, tagstr); xfree(fullname); - if (strcmp(cntx, "<global>") == 0) + if (strcmp(cntx, "<global>") == 0) { cntxts[totsofar] = NULL; - else { + } else { cntxts[totsofar] = xstrdup(cntx); } @@ -1691,9 +1742,9 @@ static void cs_print_tags_priv(char **matches, char **cntxts, static int cs_read_prompt(size_t i) { int ch; - char *buf = NULL; // buffer for possible error message from cscope + char *buf = NULL; // buffer for possible error message from cscope size_t bufpos = 0; - char *cs_emsg = _("E609: Cscope error: %s"); + char *cs_emsg = _("E609: Cscope error: %s"); size_t cs_emsg_len = strlen(cs_emsg); static char *eprompt = "Press the RETURN key to continue:"; size_t epromptlen = strlen(eprompt); @@ -1886,10 +1937,12 @@ static void cs_release_csp(size_t i, bool freefnpp) } #endif - if (csinfo[i].fr_fp != NULL) + if (csinfo[i].fr_fp != NULL) { (void)fclose(csinfo[i].fr_fp); - if (csinfo[i].to_fp != NULL) + } + if (csinfo[i].to_fp != NULL) { (void)fclose(csinfo[i].to_fp); + } if (freefnpp) { xfree(csinfo[i].fname); @@ -1904,11 +1957,12 @@ static void cs_release_csp(size_t i, bool freefnpp) /// Calls cs_kill on all cscope connections then reinits. static int cs_reset(exarg_T *eap) { - char **dblist = NULL, **pplist = NULL, **fllist = NULL; + char **dblist = NULL, **pplist = NULL, **fllist = NULL; char buf[25]; // for snprintf " (#%zu)" - if (csinfo_size == 0) + if (csinfo_size == 0) { return CSCOPE_SUCCESS; + } // malloc our db and ppath list dblist = xmalloc(csinfo_size * sizeof(char *)); @@ -1919,8 +1973,9 @@ static int cs_reset(exarg_T *eap) dblist[i] = csinfo[i].fname; pplist[i] = csinfo[i].ppath; fllist[i] = csinfo[i].flags; - if (csinfo[i].fname != NULL) + if (csinfo[i].fname != NULL) { cs_release_csp(i, FALSE); + } } // rebuild the cscope connection list @@ -1959,8 +2014,8 @@ static int cs_reset(exarg_T *eap) /// Contrast this with my development system (Digital Unix), which does. static char *cs_resolve_file(size_t i, char *name) { - char *fullname; - char_u *csdir = NULL; + char *fullname; + char_u *csdir = NULL; /* * Ppath is freed when we destroy the cscope connection. @@ -1975,8 +2030,8 @@ static char *cs_resolve_file(size_t i, char *name) // path in path resolution. csdir = xmalloc(MAXPATHL); STRLCPY(csdir, csinfo[i].fname, - path_tail((char_u *)csinfo[i].fname) - - (char_u *)csinfo[i].fname + 1); + path_tail((char_u *)csinfo[i].fname) + - (char_u *)csinfo[i].fname + 1); len += STRLEN(csdir); } @@ -1985,8 +2040,7 @@ static char *cs_resolve_file(size_t i, char *name) // happens, you are screwed up and need to fix how you're using cscope. if (csinfo[i].ppath != NULL && (strncmp(name, csinfo[i].ppath, strlen(csinfo[i].ppath)) != 0) - && (name[0] != '/') - ) { + && (name[0] != '/')) { fullname = xmalloc(len); (void)sprintf(fullname, "%s/%s", csinfo[i].ppath, name); } else if (csdir != NULL && csinfo[i].fname != NULL && *csdir != NUL) { @@ -2005,15 +2059,15 @@ static char *cs_resolve_file(size_t i, char *name) /// Show all cscope connections. static int cs_show(exarg_T *eap) { - if (cs_cnt_connections() == 0) + if (cs_cnt_connections() == 0) { MSG_PUTS(_("no cscope connections\n")); - else { - MSG_PUTS_ATTR( - _(" # pid database name prepend path\n"), - HL_ATTR(HLF_T)); + } else { + MSG_PUTS_ATTR(_(" # pid database name prepend path\n"), + HL_ATTR(HLF_T)); for (size_t i = 0; i < csinfo_size; i++) { - if (csinfo[i].fname == NULL) + if (csinfo[i].fname == NULL) { continue; + } if (csinfo[i].ppath != NULL) { (void)smsg("%2zu %-5" PRId64 " %-34s %-32s", i, @@ -2033,8 +2087,9 @@ static int cs_show(exarg_T *eap) /// Only called when VIM exits to quit any cscope sessions. void cs_end(void) { - for (size_t i = 0; i < csinfo_size; i++) + for (size_t i = 0; i < csinfo_size; i++) { cs_release_csp(i, true); + } xfree(csinfo); csinfo_size = 0; } diff --git a/src/nvim/lua/converter.c b/src/nvim/lua/converter.c index 07c53299fa..fd4cfc4c31 100644 --- a/src/nvim/lua/converter.c +++ b/src/nvim/lua/converter.c @@ -755,7 +755,7 @@ void nlua_push_Array(lua_State *lstate, const Array array, bool special) FUNC_ATTR_NONNULL_ALL \ { \ lua_pushnumber(lstate, (lua_Number)(item)); \ -} + } GENERATE_INDEX_FUNCTION(Buffer) GENERATE_INDEX_FUNCTION(Window) @@ -1126,9 +1126,9 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) size_t len; const char *s = lua_tolstring(lstate, -1, &len); *cur.obj = STRING_OBJ(((String) { - .data = xmemdupz(s, len), - .size = len, - })); + .data = xmemdupz(s, len), + .size = len, + })); break; } case LUA_TNUMBER: { @@ -1147,10 +1147,10 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) switch (table_props.type) { case kObjectTypeArray: *cur.obj = ARRAY_OBJ(((Array) { - .items = NULL, - .size = 0, - .capacity = 0, - })); + .items = NULL, + .size = 0, + .capacity = 0, + })); if (table_props.maxidx != 0) { cur.obj->data.array.items = xcalloc(table_props.maxidx, @@ -1162,10 +1162,10 @@ Object nlua_pop_Object(lua_State *const lstate, bool ref, Error *const err) break; case kObjectTypeDictionary: *cur.obj = DICTIONARY_OBJ(((Dictionary) { - .items = NULL, - .size = 0, - .capacity = 0, - })); + .items = NULL, + .size = 0, + .capacity = 0, + })); if (table_props.string_keys_num != 0) { cur.obj->data.dictionary.items = xcalloc(table_props.string_keys_num, @@ -1299,3 +1299,25 @@ void nlua_init_types(lua_State *const lstate) lua_rawset(lstate, -3); } + + +void nlua_pop_keydict(lua_State *L, void *retval, field_hash hashy, Error *err) +{ + lua_pushnil(L); // [dict, nil] + while (lua_next(L, -2)) { + // [dict, key, value] + size_t len; + const char *s = lua_tolstring(L, -2, &len); + Object *field = hashy(retval, s, len); + if (!field) { + api_set_error(err, kErrorTypeValidation, "invalid key: %.*s", (int)len, s); + lua_pop(L, 3); // [] + return; + } + + *field = nlua_pop_Object(L, true, err); + } + // [dict] + lua_pop(L, 1); + // [] +} diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c index 8c7dc90111..3f93bb9a09 100644 --- a/src/nvim/lua/executor.c +++ b/src/nvim/lua/executor.c @@ -5,6 +5,7 @@ #include <lua.h> #include <lualib.h> +#include "cjson/lua_cjson.h" #include "luv/luv.h" #include "mpack/lmpack.h" #include "nvim/api/private/defs.h" @@ -531,6 +532,9 @@ static int nlua_state_init(lua_State *const lstate) FUNC_ATTR_NONNULL_ALL lua_pushcfunction(lstate, &nlua_xdl_diff); lua_setfield(lstate, -2, "diff"); + lua_cjson_new(lstate); + lua_setfield(lstate, -2, "json"); + lua_setglobal(lstate, "vim"); { diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua index ba124c41ad..7a209f2d79 100644 --- a/src/nvim/lua/vim.lua +++ b/src/nvim/lua/vim.lua @@ -108,6 +108,9 @@ setmetatable(vim, { elseif key == 'diagnostic' then t.diagnostic = require('vim.diagnostic') return t.diagnostic + elseif key == 'ui' then + t.ui = require('vim.ui') + return t.ui end end }) diff --git a/src/nvim/lua/xdiff.c b/src/nvim/lua/xdiff.c index b89807b9f1..3955fbe72c 100644 --- a/src/nvim/lua/xdiff.c +++ b/src/nvim/lua/xdiff.c @@ -53,8 +53,8 @@ static int write_string(void *priv, mmbuffer_t *mb, int nbuf) static int hunk_locations_cb(long start_a, long count_a, long start_b, long count_b, void *cb_data) { // Mimic extra offsets done by xdiff, see: - // src/nvim/xdiff/xemit.c:284 - // src/nvim/xdiff/xutils.c:(356,368) + // src/xdiff/xemit.c:284 + // src/xdiff/xutils.c:(356,368) if (count_a > 0) { start_a += 1; } @@ -83,8 +83,8 @@ static int hunk_locations_cb(long start_a, long count_a, long start_b, long coun static int call_on_hunk_cb(long start_a, long count_a, long start_b, long count_b, void *cb_data) { // Mimic extra offsets done by xdiff, see: - // src/nvim/xdiff/xemit.c:284 - // src/nvim/xdiff/xutils.c:(356,368) + // src/xdiff/xemit.c:284 + // src/xdiff/xutils.c:(356,368) if (count_a > 0) { start_a += 1; } @@ -265,8 +265,8 @@ int nlua_xdl_diff(lua_State *lstate) Error err = ERROR_INIT; xdemitconf_t cfg; - xpparam_t params; - xdemitcb_t ecb; + xpparam_t params; + xdemitcb_t ecb; memset(&cfg, 0, sizeof(cfg)); memset(¶ms, 0, sizeof(params)); diff --git a/src/nvim/main.c b/src/nvim/main.c index d977589ad7..f801351d2d 100644 --- a/src/nvim/main.c +++ b/src/nvim/main.c @@ -1306,35 +1306,6 @@ static void set_window_layout(mparm_T *paramp) } } -/* - * Read all the plugin files. - * Only when compiled with +eval, since most plugins need it. - */ -static void load_plugins(void) -{ - if (p_lpl) { - char_u *rtp_copy = NULL; - char_u *const plugin_pattern_vim = (char_u *)"plugin/**/*.vim"; // NOLINT - char_u *const plugin_pattern_lua = (char_u *)"plugin/**/*.lua"; // NOLINT - - // don't use source_runtime() yet so we can check for :packloadall below - source_in_path(p_rtp, plugin_pattern_vim, DIP_ALL | DIP_NOAFTER); - source_in_path(p_rtp, plugin_pattern_lua, DIP_ALL | DIP_NOAFTER); - TIME_MSG("loading rtp plugins"); - xfree(rtp_copy); - - // Only source "start" packages if not done already with a :packloadall - // command. - if (!did_source_packages) { - load_start_packages(); - } - TIME_MSG("loading packages"); - - source_runtime(plugin_pattern_vim, DIP_ALL | DIP_AFTER); - source_runtime(plugin_pattern_lua, DIP_ALL | DIP_AFTER); - TIME_MSG("loading after plugins"); - } -} /* * "-q errorfile": Load the error file now. diff --git a/src/nvim/map.c b/src/nvim/map.c index 1c986a4fa4..20d5570e8c 100644 --- a/src/nvim/map.c +++ b/src/nvim/map.c @@ -36,11 +36,11 @@ #if defined(ARCH_64) -#define ptr_t_hash(key) uint64_t_hash((uint64_t)key) -#define ptr_t_eq(a, b) uint64_t_eq((uint64_t)a, (uint64_t)b) +# define ptr_t_hash(key) uint64_t_hash((uint64_t)key) +# define ptr_t_eq(a, b) uint64_t_eq((uint64_t)a, (uint64_t)b) #elif defined(ARCH_32) -#define ptr_t_hash(key) uint32_t_hash((uint32_t)key) -#define ptr_t_eq(a, b) uint32_t_eq((uint32_t)a, (uint32_t)b) +# define ptr_t_hash(key) uint32_t_hash((uint32_t)key) +# define ptr_t_eq(a, b) uint32_t_eq((uint32_t)a, (uint32_t)b) #endif #define INITIALIZER(T, U) T##_##U##_initializer diff --git a/src/nvim/mbyte.c b/src/nvim/mbyte.c index fe4ec9f96c..253ddfc253 100644 --- a/src/nvim/mbyte.c +++ b/src/nvim/mbyte.c @@ -1395,9 +1395,9 @@ static int utf_strnicmp(const char_u *s1, const char_u *s2, size_t n1, size_t n2 } #ifdef WIN32 -#ifndef CP_UTF8 -# define CP_UTF8 65001 // magic number from winnls.h -#endif +# ifndef CP_UTF8 +# define CP_UTF8 65001 // magic number from winnls.h +# endif /// Converts string from UTF-8 to UTF-16. /// @@ -2208,13 +2208,13 @@ char_u *enc_locale(void) char buf[50]; const char *s; -# ifdef HAVE_NL_LANGINFO_CODESET +#ifdef HAVE_NL_LANGINFO_CODESET if (!(s = nl_langinfo(CODESET)) || *s == NUL) -# endif +#endif { -# if defined(HAVE_LOCALE_H) +#if defined(HAVE_LOCALE_H) if (!(s = setlocale(LC_CTYPE, NULL)) || *s == NUL) -# endif +#endif { if ((s = os_getenv("LC_ALL"))) { if ((s = os_getenv("LC_CTYPE"))) { @@ -2265,7 +2265,7 @@ enc_locale_copy_enc: return enc_canonize((char_u *)buf); } -# if defined(HAVE_ICONV) +#if defined(HAVE_ICONV) /* @@ -2277,7 +2277,7 @@ enc_locale_copy_enc: void *my_iconv_open(char_u *to, char_u *from) { iconv_t fd; -#define ICONV_TESTLEN 400 +# define ICONV_TESTLEN 400 char_u tobuf[ICONV_TESTLEN]; char *p; size_t tolen; @@ -2395,7 +2395,7 @@ static char_u *iconv_string(const vimconv_T *const vcp, char_u *str, size_t slen return result; } -# endif // HAVE_ICONV +#endif // HAVE_ICONV @@ -2425,11 +2425,11 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, c int to_is_utf8; // Reset to no conversion. -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV if (vcp->vc_type == CONV_ICONV && vcp->vc_fd != (iconv_t)-1) { iconv_close(vcp->vc_fd); } -# endif +#endif *vcp = (vimconv_T)MBYTE_NONE_CONV; // No conversion when one of the names is empty or they are equal. @@ -2466,7 +2466,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, c // Internal utf-8 -> latin9 conversion. vcp->vc_type = CONV_TO_LATIN9; } -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV else { // NOLINT(readability/braces) // Use iconv() for conversion. vcp->vc_fd = (iconv_t)my_iconv_open(to_is_utf8 ? (char_u *)"utf-8" : to, @@ -2476,7 +2476,7 @@ int convert_setup_ext(vimconv_T *vcp, char_u *from, bool from_unicode_is_utf8, c vcp->vc_factor = 4; // could be longer too... } } -# endif +#endif if (vcp->vc_type == CONV_NONE) { return FAIL; } @@ -2644,11 +2644,11 @@ char_u *string_convert_ext(const vimconv_T *const vcp, char_u *ptr, size_t *lenp } break; -# ifdef HAVE_ICONV +#ifdef HAVE_ICONV case CONV_ICONV: // conversion with vcp->vc_fd retval = iconv_string(vcp, ptr, len, unconvlenp, lenp); break; -# endif +#endif } return retval; diff --git a/src/nvim/memory.c b/src/nvim/memory.c index 0f5f4c1e40..5e6c6a8189 100644 --- a/src/nvim/memory.c +++ b/src/nvim/memory.c @@ -526,35 +526,35 @@ void time_to_bytes(time_t time_, uint8_t buf[8]) #if defined(EXITFREE) -#include "nvim/buffer.h" -#include "nvim/charset.h" -#include "nvim/diff.h" -#include "nvim/edit.h" -#include "nvim/eval/typval.h" -#include "nvim/ex_cmds.h" -#include "nvim/ex_docmd.h" -#include "nvim/ex_getln.h" -#include "nvim/file_search.h" -#include "nvim/fileio.h" -#include "nvim/fold.h" -#include "nvim/getchar.h" -#include "nvim/mark.h" -#include "nvim/mbyte.h" -#include "nvim/memline.h" -#include "nvim/move.h" -#include "nvim/ops.h" -#include "nvim/option.h" -#include "nvim/os/os.h" -#include "nvim/os_unix.h" -#include "nvim/path.h" -#include "nvim/quickfix.h" -#include "nvim/regexp.h" -#include "nvim/screen.h" -#include "nvim/search.h" -#include "nvim/spell.h" -#include "nvim/syntax.h" -#include "nvim/tag.h" -#include "nvim/window.h" +# include "nvim/buffer.h" +# include "nvim/charset.h" +# include "nvim/diff.h" +# include "nvim/edit.h" +# include "nvim/eval/typval.h" +# include "nvim/ex_cmds.h" +# include "nvim/ex_docmd.h" +# include "nvim/ex_getln.h" +# include "nvim/file_search.h" +# include "nvim/fileio.h" +# include "nvim/fold.h" +# include "nvim/getchar.h" +# include "nvim/mark.h" +# include "nvim/mbyte.h" +# include "nvim/memline.h" +# include "nvim/move.h" +# include "nvim/ops.h" +# include "nvim/option.h" +# include "nvim/os/os.h" +# include "nvim/os_unix.h" +# include "nvim/path.h" +# include "nvim/quickfix.h" +# include "nvim/regexp.h" +# include "nvim/screen.h" +# include "nvim/search.h" +# include "nvim/spell.h" +# include "nvim/syntax.h" +# include "nvim/tag.h" +# include "nvim/window.h" /* * Free everything that we allocated. diff --git a/src/nvim/menu.c b/src/nvim/menu.c index 2b1a250604..de8503f9d0 100644 --- a/src/nvim/menu.c +++ b/src/nvim/menu.c @@ -64,7 +64,7 @@ static vimmenu_T **get_root_menu(const char_u *const name) /// @param eap Ex command arguments void ex_menu(exarg_T *eap) { - char_u *menu_path; + char *menu_path; int modes; char_u *map_to; // command mapped to the menu entry int noremap; @@ -166,7 +166,7 @@ void ex_menu(exarg_T *eap) } - menu_path = arg; + menu_path = (char *)arg; if (*menu_path == '.') { EMSG2(_(e_invarg2), menu_path); goto theend; @@ -178,25 +178,25 @@ void ex_menu(exarg_T *eap) * If there is only a menu name, display menus with that name. */ if (*map_to == NUL && !unmenu && enable == kNone) { - show_menus(menu_path, modes); + show_menus((char_u *)menu_path, modes); goto theend; } else if (*map_to != NUL && (unmenu || enable != kNone)) { EMSG(_(e_trailing)); goto theend; } - vimmenu_T **root_menu_ptr = get_root_menu(menu_path); + vimmenu_T **root_menu_ptr = get_root_menu((char_u *)menu_path); if (enable != kNone) { // Change sensitivity of the menu. // For the PopUp menu, remove a menu for each mode separately. // Careful: menu_enable_recurse() changes menu_path. if (STRCMP(menu_path, "*") == 0) { // meaning: do all menus - menu_path = (char_u *)""; + menu_path = ""; } if (menu_is_popup(menu_path)) { - for (i = 0; i < MENU_INDEX_TIP; ++i) { + for (i = 0; i < MENU_INDEX_TIP; i++) { if (modes & (1 << i)) { p = popup_mode_name(menu_path, i); menu_enable_recurse(*root_menu_ptr, p, MENU_ALL_MODES, enable); @@ -204,20 +204,20 @@ void ex_menu(exarg_T *eap) } } } - menu_enable_recurse(*root_menu_ptr, menu_path, modes, enable); + menu_enable_recurse(*root_menu_ptr, (char_u *)menu_path, modes, enable); } else if (unmenu) { /* * Delete menu(s). */ if (STRCMP(menu_path, "*") == 0) { // meaning: remove all menus - menu_path = (char_u *)""; + menu_path = ""; } /* * For the PopUp menu, remove a menu for each mode separately. */ if (menu_is_popup(menu_path)) { - for (i = 0; i < MENU_INDEX_TIP; ++i) { + for (i = 0; i < MENU_INDEX_TIP; i++) { if (modes & (1 << i)) { p = popup_mode_name(menu_path, i); remove_menu(root_menu_ptr, p, MENU_ALL_MODES, true); @@ -227,7 +227,7 @@ void ex_menu(exarg_T *eap) } // Careful: remove_menu() changes menu_path - remove_menu(root_menu_ptr, menu_path, modes, false); + remove_menu(root_menu_ptr, (char_u *)menu_path, modes, false); } else { /* * Add menu(s). @@ -245,13 +245,13 @@ void ex_menu(exarg_T *eap) menuarg.modes = modes; menuarg.noremap[0] = noremap; menuarg.silent[0] = silent; - add_menu_path(menu_path, &menuarg, pri_tab, map_to); + add_menu_path((char_u *)menu_path, &menuarg, pri_tab, map_to); /* * For the PopUp menu, add a menu for each mode separately. */ if (menu_is_popup(menu_path)) { - for (i = 0; i < MENU_INDEX_TIP; ++i) { + for (i = 0; i < MENU_INDEX_TIP; i++) { if (modes & (1 << i)) { p = popup_mode_name(menu_path, i); // Include all modes, to make ":amenu" work @@ -1273,12 +1273,12 @@ int get_menu_cmd_modes(const char *cmd, bool forceit, int *noremap, int *unmenu) * Modify a menu name starting with "PopUp" to include the mode character. * Returns the name in allocated memory. */ -static char_u *popup_mode_name(char_u *name, int idx) +static char_u *popup_mode_name(char *name, int idx) { size_t len = STRLEN(name); assert(len >= 4); - char_u *p = vim_strnsave(name, len + 1); + char_u *p = vim_strnsave((char_u *)name, len + 1); memmove(p + 6, p + 5, len - 4); p[5] = menu_mode_chars[idx]; @@ -1337,14 +1337,14 @@ static char_u *menu_text(const char_u *str, int *mnemonic, char_u **actext) bool menu_is_menubar(const char_u *const name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { - return !menu_is_popup(name) + return !menu_is_popup((char *)name) && !menu_is_toolbar(name) && !menu_is_winbar(name) && *name != MNU_HIDDEN_CHAR; } // Return true if "name" is a popup menu name. -bool menu_is_popup(const char_u *const name) +bool menu_is_popup(const char *const name) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { return STRNCMP(name, "PopUp", 5) == 0; @@ -1374,7 +1374,7 @@ int menu_is_separator(char_u *name) static int menu_is_hidden(char_u *name) { return (name[0] == MNU_HIDDEN_CHAR) - || (menu_is_popup(name) && name[5] != NUL); + || (menu_is_popup((char *)name) && name[5] != NUL); } // Execute "menu". Use by ":emenu" and the window toolbar. @@ -1383,25 +1383,25 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu) FUNC_ATTR_NONNULL_ARG(2) { int idx = -1; - char_u *mode; + char *mode; // Use the Insert mode entry when returning to Insert mode. if (((State & INSERT) || restart_edit) && !current_sctx.sc_sid) { - mode = (char_u *)"Insert"; + mode = "Insert"; idx = MENU_INDEX_INSERT; } else if (State & CMDLINE) { - mode = (char_u *)"Command"; + mode = "Command"; idx = MENU_INDEX_CMDLINE; } else if (get_real_state() & VISUAL) { /* Detect real visual mode -- if we are really in visual mode we * don't need to do any guesswork to figure out what the selection * is. Just execute the visual binding for the menu. */ - mode = (char_u *)"Visual"; + mode = "Visual"; idx = MENU_INDEX_VISUAL; } else if (eap != NULL && eap->addr_count) { pos_T tpos; - mode = (char_u *)"Visual"; + mode = "Visual"; idx = MENU_INDEX_VISUAL; /* GEDDES: This is not perfect - but it is a @@ -1442,7 +1442,7 @@ static void execute_menu(const exarg_T *eap, vimmenu_T *menu) } if (idx == -1 || eap == NULL) { - mode = (char_u *)"Normal"; + mode = "Normal"; idx = MENU_INDEX_NORMAL; } diff --git a/src/nvim/message.c b/src/nvim/message.c index f9fe7774f6..ed673b52d3 100644 --- a/src/nvim/message.c +++ b/src/nvim/message.c @@ -762,7 +762,7 @@ bool emsgf_multiline(const char *const fmt, ...) va_list ap; - static char errbuf[MULTILINE_BUFSIZE]; + static char errbuf[MULTILINE_BUFSIZE]; if (emsg_not_now()) { return true; } diff --git a/src/nvim/misc1.c b/src/nvim/misc1.c index 2166631767..2dfe5df325 100644 --- a/src/nvim/misc1.c +++ b/src/nvim/misc1.c @@ -874,7 +874,7 @@ void preserve_exit(void) */ #ifndef BREAKCHECK_SKIP -# define BREAKCHECK_SKIP 1000 +# define BREAKCHECK_SKIP 1000 #endif static int breakcheck_count = 0; diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index b65d87e617..cf463fd40a 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -241,7 +241,7 @@ retnomove: if (row < 0) { count = 0; for (first = true; curwin->w_topline > 1; ) { - if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) { + if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { count++; } else { count += plines_win(curwin, curwin->w_topline - 1, true); @@ -251,8 +251,8 @@ retnomove: } first = false; (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); - if (curwin->w_topfill < diff_check(curwin, curwin->w_topline)) { - ++curwin->w_topfill; + if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { + curwin->w_topfill++; } else { --curwin->w_topline; curwin->w_topfill = 0; @@ -283,11 +283,10 @@ retnomove: } if (curwin->w_topfill > 0) { - --curwin->w_topfill; + curwin->w_topfill--; } else { - ++curwin->w_topline; - curwin->w_topfill = - diff_check_fill(curwin, curwin->w_topline); + curwin->w_topline++; + curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); } } check_topfill(curwin, false); @@ -373,12 +372,12 @@ bool mouse_comp_pos(win_T *win, int *rowp, int *colp, linenr_T *lnump) while (row > 0) { // Don't include filler lines in "count" - if (win->w_p_diff + if (win_may_fill(win) && !hasFoldingWin(win, lnum, NULL, NULL, true, NULL)) { if (lnum == win->w_topline) { row -= win->w_topfill; } else { - row -= diff_check_fill(win, lnum); + row -= win_get_fill(win, lnum); } count = plines_win_nofill(win, lnum, true); } else { diff --git a/src/nvim/move.c b/src/nvim/move.c index 3a5b2fb211..c4f8e81fa3 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -197,7 +197,7 @@ void update_topline(win_T *wp) } } // Check if there are more filler lines than allowed. - if (!check_topline && wp->w_topfill > diff_check_fill(wp, wp->w_topline)) { + if (!check_topline && wp->w_topfill > win_get_fill(wp, wp->w_topline)) { check_topline = true; } @@ -582,8 +582,7 @@ static void curs_rows(win_T *wp) --i; // hold at inserted lines } } - if (valid - && (lnum != wp->w_topline || !wp->w_p_diff)) { + if (valid && (lnum != wp->w_topline || !win_may_fill(wp))) { lnum = wp->w_lines[i].wl_lastlnum + 1; // Cursor inside folded lines, don't count this row if (lnum > wp->w_cursor.lnum) { @@ -795,7 +794,7 @@ void curs_columns(win_T *wp, int may_scroll) // column char_u *const sbr = get_showbreak_value(wp); if (*sbr && *get_cursor_pos_ptr() == NUL - && wp->w_wcol == (int)vim_strsize(sbr)) { + && wp->w_wcol == vim_strsize(sbr)) { wp->w_wcol = 0; } } @@ -854,7 +853,7 @@ void curs_columns(win_T *wp, int may_scroll) if (wp->w_cursor.lnum == wp->w_topline) { wp->w_wrow += wp->w_topfill; } else { - wp->w_wrow += diff_check_fill(wp, wp->w_cursor.lnum); + wp->w_wrow += win_get_fill(wp, wp->w_cursor.lnum); } prev_skipcol = wp->w_skipcol; @@ -992,7 +991,7 @@ void textpos2screenpos(win_T *wp, pos_T *pos, int *rowp, int *scolp, int *ccolp, if ((local && existing_row) || visible_row) { colnr_T off; colnr_T col; - int width; + int width; getvcol(wp, pos, &scol, &ccol, &ecol); @@ -1041,7 +1040,7 @@ bool scrolldown(long line_count, int byfold) (void)hasFolding(curwin->w_topline, &curwin->w_topline, NULL); validate_cursor(); // w_wrow needs to be valid while (line_count-- > 0) { - if (curwin->w_topfill < diff_check(curwin, curwin->w_topline) + if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline) && curwin->w_topfill < curwin->w_height_inner - 1) { curwin->w_topfill++; done++; @@ -1122,7 +1121,7 @@ bool scrollup(long line_count, int byfold) linenr_T botline = curwin->w_botline; if ((byfold && hasAnyFolding(curwin)) - || curwin->w_p_diff) { + || win_may_fill(curwin)) { // count each sequence of folded lines as one logical line linenr_T lnum = curwin->w_topline; while (line_count--) { @@ -1135,8 +1134,8 @@ bool scrollup(long line_count, int byfold) if (lnum >= curbuf->b_ml.ml_line_count) { break; } - ++lnum; - curwin->w_topfill = diff_check_fill(curwin, lnum); + lnum++; + curwin->w_topfill = win_get_fill(curwin, lnum); } } // approximate w_botline @@ -1207,7 +1206,7 @@ static void max_topfill(void) if (n >= curwin->w_height_inner) { curwin->w_topfill = 0; } else { - curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); + curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); if (curwin->w_topfill + n > curwin->w_height_inner) { curwin->w_topfill = curwin->w_height_inner - n; } @@ -1220,8 +1219,7 @@ static void max_topfill(void) */ void scrolldown_clamp(void) { - int can_fill = (curwin->w_topfill - < diff_check_fill(curwin, curwin->w_topline)); + int can_fill = (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)); if (curwin->w_topline <= 1 && !can_fill) { @@ -1302,7 +1300,7 @@ void scrollup_clamp(void) */ static void topline_back(win_T *wp, lineoff_T *lp) { - if (lp->fill < diff_check_fill(wp, lp->lnum)) { + if (lp->fill < win_get_fill(wp, lp->lnum)) { // Add a filler line lp->fill++; lp->height = 1; @@ -1328,7 +1326,7 @@ static void topline_back(win_T *wp, lineoff_T *lp) */ static void botline_forw(win_T *wp, lineoff_T *lp) { - if (lp->fill < diff_check_fill(wp, lp->lnum + 1)) { + if (lp->fill < win_get_fill(wp, lp->lnum + 1)) { // Add a filler line. lp->fill++; lp->height = 1; @@ -1355,8 +1353,8 @@ static void botline_forw(win_T *wp, lineoff_T *lp) static void botline_topline(lineoff_T *lp) { if (lp->fill > 0) { - ++lp->lnum; - lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1; + lp->lnum++; + lp->fill = win_get_fill(curwin, lp->lnum) - lp->fill + 1; } } @@ -1368,8 +1366,8 @@ static void botline_topline(lineoff_T *lp) static void topline_botline(lineoff_T *lp) { if (lp->fill > 0) { - lp->fill = diff_check_fill(curwin, lp->lnum) - lp->fill + 1; - --lp->lnum; + lp->fill = win_get_fill(curwin, lp->lnum) - lp->fill + 1; + lp->lnum--; } } @@ -1417,7 +1415,7 @@ void scroll_cursor_top(int min_scroll, int always) // "used" already contains the number of filler lines above, don't add it // again. // Hide filler lines above cursor line by adding them to "extra". - int extra = diff_check_fill(curwin, curwin->w_cursor.lnum); + int extra = win_get_fill(curwin, curwin->w_cursor.lnum); /* * Check if the lines from "top" to "bot" fit in the window. If they do, @@ -1475,7 +1473,7 @@ void scroll_cursor_top(int min_scroll, int always) if (curwin->w_topline > curwin->w_cursor.lnum) { curwin->w_topline = curwin->w_cursor.lnum; } - curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); + curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); if (curwin->w_topfill > 0 && extra > off) { curwin->w_topfill -= extra - off; if (curwin->w_topfill < 0) { @@ -1505,7 +1503,7 @@ void set_empty_rows(win_T *wp, int used) } else { wp->w_empty_rows = wp->w_height_inner - used; if (wp->w_botline <= wp->w_buffer->b_ml.ml_line_count) { - wp->w_filler_rows = diff_check_fill(wp, wp->w_botline); + wp->w_filler_rows = win_get_fill(wp, wp->w_botline); if (wp->w_empty_rows > wp->w_filler_rows) { wp->w_empty_rows -= wp->w_filler_rows; } else { @@ -1531,10 +1529,10 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) lineoff_T boff; int fill_below_window; linenr_T old_topline = curwin->w_topline; - int old_topfill = curwin->w_topfill; + int old_topfill = curwin->w_topfill; linenr_T old_botline = curwin->w_botline; - int old_valid = curwin->w_valid; - int old_empty_rows = curwin->w_empty_rows; + int old_valid = curwin->w_valid; + int old_empty_rows = curwin->w_empty_rows; linenr_T cln = curwin->w_cursor.lnum; // Cursor Line Number long so = get_scrolloff_value(curwin); @@ -1590,7 +1588,7 @@ void scroll_cursor_bot(int min_scroll, int set_topbot) } loff.fill = 0; boff.fill = 0; - fill_below_window = diff_check_fill(curwin, curwin->w_botline) + fill_below_window = win_get_fill(curwin, curwin->w_botline) - curwin->w_filler_rows; while (loff.lnum > 1) { @@ -1835,7 +1833,7 @@ void cursor_correct(void) // Count filler lines below this line as context. if (topline < botline) { - above += diff_check_fill(curwin, topline + 1); + above += win_get_fill(curwin, topline + 1); } ++topline; } @@ -1889,9 +1887,7 @@ int onepage(Direction dir, long count) ? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so) && curwin->w_botline > curbuf->b_ml.ml_line_count) : (curwin->w_topline == 1 - && curwin->w_topfill == - diff_check_fill(curwin, curwin->w_topline) - )) { + && curwin->w_topfill == win_get_fill(curwin, curwin->w_topline))) { beep_flush(); retval = FAIL; break; @@ -1919,7 +1915,7 @@ int onepage(Direction dir, long count) /* For the overlap, start with the line just below the window * and go upwards. */ loff.lnum = curwin->w_botline; - loff.fill = diff_check_fill(curwin, loff.lnum) + loff.fill = win_get_fill(curwin, loff.lnum) - curwin->w_filler_rows; get_scroll_overlap(&loff, -1); curwin->w_topline = loff.lnum; @@ -1956,8 +1952,7 @@ int onepage(Direction dir, long count) * line at the bottom of the window. Make sure this results in * the same line as before doing CTRL-F. */ loff.lnum = curwin->w_topline - 1; - loff.fill = diff_check_fill(curwin, loff.lnum + 1) - - curwin->w_topfill; + loff.fill = win_get_fill(curwin, loff.lnum + 1) - curwin->w_topfill; get_scroll_overlap(&loff, 1); if (loff.lnum >= curbuf->b_ml.ml_line_count) { @@ -2000,8 +1995,7 @@ int onepage(Direction dir, long count) /* First try using the maximum number of filler lines. If * that's not enough, backup one line. */ loff.fill = curwin->w_topfill; - if (curwin->w_topfill < diff_check_fill(curwin, - curwin->w_topline)) { + if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { max_topfill(); } if (curwin->w_topfill == loff.fill) { @@ -2146,8 +2140,8 @@ void halfpage(bool flag, linenr_T Prenum) break; } (void)hasFolding(curwin->w_topline, NULL, &curwin->w_topline); - ++curwin->w_topline; - curwin->w_topfill = diff_check_fill(curwin, curwin->w_topline); + curwin->w_topline++; + curwin->w_topfill = win_get_fill(curwin, curwin->w_topline); if (curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) { ++curwin->w_cursor.lnum; @@ -2158,11 +2152,9 @@ void halfpage(bool flag, linenr_T Prenum) curwin->w_valid &= ~(VALID_CROW|VALID_WROW); scrolled += i; - /* - * Correct w_botline for changed w_topline. - * Won't work when there are filler lines. - */ - if (curwin->w_p_diff) { + // Correct w_botline for changed w_topline. + // Won't work when there are filler lines. + if (win_may_fill(curwin)) { curwin->w_valid &= ~(VALID_BOTLINE|VALID_BOTLINE_AP); } else { room += i; @@ -2197,7 +2189,7 @@ void halfpage(bool flag, linenr_T Prenum) * scroll the text down */ while (n > 0 && curwin->w_topline > 1) { - if (curwin->w_topfill < diff_check_fill(curwin, curwin->w_topline)) { + if (curwin->w_topfill < win_get_fill(curwin, curwin->w_topline)) { i = 1; n--; curwin->w_topfill++; diff --git a/src/nvim/msgpack_rpc/channel.c b/src/nvim/msgpack_rpc/channel.c index 813f21407c..a1a1f0f8c0 100644 --- a/src/nvim/msgpack_rpc/channel.c +++ b/src/nvim/msgpack_rpc/channel.c @@ -33,8 +33,8 @@ #include "nvim/vim.h" #if MIN_LOG_LEVEL > DEBUG_LOG_LEVEL -#define log_client_msg(...) -#define log_server_msg(...) +# define log_client_msg(...) +# define log_server_msg(...) #endif static PMap(cstr_t) event_strings = MAP_INIT; @@ -699,14 +699,14 @@ const char *rpc_client_name(Channel *chan) } #if MIN_LOG_LEVEL <= DEBUG_LOG_LEVEL -#define REQ "[request] " -#define RES "[response] " -#define NOT "[notify] " -#define ERR "[error] " +# define REQ "[request] " +# define RES "[response] " +# define NOT "[notify] " +# define ERR "[error] " // Cannot define array with negative offsets, so this one is needed to be added // to MSGPACK_UNPACK_\* values. -#define MUR_OFF 2 +# define MUR_OFF 2 static const char *const msgpack_error_messages[] = { [MSGPACK_UNPACK_EXTRA_BYTES + MUR_OFF] = "extra bytes found", diff --git a/src/nvim/msgpack_rpc/helpers.c b/src/nvim/msgpack_rpc/helpers.c index 0bc58fffba..549016e751 100644 --- a/src/nvim/msgpack_rpc/helpers.c +++ b/src/nvim/msgpack_rpc/helpers.c @@ -15,6 +15,7 @@ #include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS +# include "keysets.generated.h" # include "msgpack_rpc/helpers.c.generated.h" #endif @@ -47,17 +48,15 @@ static msgpack_sbuffer sbuffer; } \ \ static void msgpack_rpc_from_##lt(Integer o, msgpack_packer *res) \ -/* uncrustify:indent-off */ \ FUNC_ATTR_NONNULL_ARG(2) \ -/* uncrustify:indent-on */ \ { \ - msgpack_packer pac; \ - msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ - msgpack_pack_int64(&pac, (handle_T)o); \ - msgpack_pack_ext(res, sbuffer.size, \ - kObjectType##t - EXT_OBJECT_TYPE_SHIFT); \ - msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); \ - msgpack_sbuffer_clear(&sbuffer); \ + msgpack_packer pac; \ + msgpack_packer_init(&pac, &sbuffer, msgpack_sbuffer_write); \ + msgpack_pack_int64(&pac, (handle_T)o); \ + msgpack_pack_ext(res, sbuffer.size, \ + kObjectType##t - EXT_OBJECT_TYPE_SHIFT); \ + msgpack_pack_ext_body(res, sbuffer.data, sbuffer.size); \ + msgpack_sbuffer_clear(&sbuffer); \ } void msgpack_rpc_helpers_init(void) @@ -90,11 +89,11 @@ bool msgpack_rpc_to_object(const msgpack_object *const obj, Object *const arg) kvec_withinit_t(MPToAPIObjectStackItem, 2) stack = KV_INITIAL_VALUE; kvi_init(stack); kvi_push(stack, ((MPToAPIObjectStackItem) { - .mobj = obj, - .aobj = arg, - .container = false, - .idx = 0, - })); + .mobj = obj, + .aobj = arg, + .container = false, + .idx = 0, + })); while (ret && kv_size(stack)) { MPToAPIObjectStackItem cur = kv_last(stack); if (!cur.container) { @@ -154,19 +153,19 @@ case type: { \ cur.idx++; kv_last(stack) = cur; kvi_push(stack, ((MPToAPIObjectStackItem) { - .mobj = &cur.mobj->via.array.ptr[idx], - .aobj = &cur.aobj->data.array.items[idx], - .container = false, - })); + .mobj = &cur.mobj->via.array.ptr[idx], + .aobj = &cur.aobj->data.array.items[idx], + .container = false, + })); } } else { *cur.aobj = ARRAY_OBJ(((Array) { - .size = size, - .capacity = size, - .items = (size > 0 + .size = size, + .capacity = size, + .items = (size > 0 ? xcalloc(size, sizeof(*cur.aobj->data.array.items)) : NULL), - })); + })); cur.container = true; kv_last(stack) = cur; } @@ -207,20 +206,20 @@ case type: { \ } if (ret) { kvi_push(stack, ((MPToAPIObjectStackItem) { - .mobj = &cur.mobj->via.map.ptr[idx].val, - .aobj = &cur.aobj->data.dictionary.items[idx].value, - .container = false, - })); + .mobj = &cur.mobj->via.map.ptr[idx].val, + .aobj = &cur.aobj->data.dictionary.items[idx].value, + .container = false, + })); } } } else { *cur.aobj = DICTIONARY_OBJ(((Dictionary) { - .size = size, - .capacity = size, - .items = (size > 0 + .size = size, + .capacity = size, + .items = (size > 0 ? xcalloc(size, sizeof(*cur.aobj->data.dictionary.items)) : NULL), - })); + })); cur.container = true; kv_last(stack) = cur; } @@ -412,9 +411,9 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) cur.idx++; kv_last(stack) = cur; kvi_push(stack, ((APIToMPObjectStackItem) { - .aobj = &cur.aobj->data.array.items[idx], - .container = false, - })); + .aobj = &cur.aobj->data.array.items[idx], + .container = false, + })); } } else { msgpack_pack_array(res, size); @@ -435,9 +434,9 @@ void msgpack_rpc_from_object(const Object result, msgpack_packer *const res) msgpack_rpc_from_string(cur.aobj->data.dictionary.items[idx].key, res); kvi_push(stack, ((APIToMPObjectStackItem) { - .aobj = &cur.aobj->data.dictionary.items[idx].value, - .container = false, - })); + .aobj = &cur.aobj->data.dictionary.items[idx].value, + .container = false, + })); } } else { msgpack_pack_map(res, size); diff --git a/src/nvim/normal.c b/src/nvim/normal.c index b8a62a8fea..493c704f42 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3372,7 +3372,7 @@ static void may_clear_cmdline(void) } // Routines for displaying a partly typed command -# define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30 +#define SHOWCMD_BUFLEN SHOWCMD_COLS + 1 + 30 static char_u showcmd_buf[SHOWCMD_BUFLEN]; static char_u old_showcmd_buf[SHOWCMD_BUFLEN]; // For push_showcmd() static bool showcmd_is_clear = true; @@ -5261,16 +5261,15 @@ static void nv_scroll(cmdarg_T *cap) } else { if (cap->cmdchar == 'M') { // Don't count filler lines above the window. - used -= diff_check_fill(curwin, curwin->w_topline) + used -= win_get_fill(curwin, curwin->w_topline) - curwin->w_topfill; validate_botline(curwin); // make sure w_empty_rows is valid half = (curwin->w_height_inner - curwin->w_empty_rows + 1) / 2; for (n = 0; curwin->w_topline + n < curbuf->b_ml.ml_line_count; n++) { // Count half he number of filler lines to be "below this // line" and half to be "above the next line". - if (n > 0 && used + diff_check_fill(curwin, curwin->w_topline - + n) / 2 >= half) { - --n; + if (n > 0 && used + win_get_fill(curwin, curwin->w_topline + n) / 2 >= half) { + n--; break; } used += plines_win(curwin, curwin->w_topline + n, true); diff --git a/src/nvim/option.c b/src/nvim/option.c index 310f382db7..5646a62cd4 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -374,11 +374,11 @@ void set_init_1(bool clean_arg) * temp files. */ { -# ifdef UNIX +#ifdef UNIX static char *(names[4]) = { "", "TMPDIR", "TEMP", "TMP" }; -# else +#else static char *(names[3]) = { "TMPDIR", "TEMP", "TMP" }; -# endif +#endif garray_T ga; opt_idx = findoption("backupskip"); @@ -386,16 +386,16 @@ void set_init_1(bool clean_arg) for (size_t n = 0; n < ARRAY_SIZE(names); n++) { bool mustfree = true; char *p; -# ifdef UNIX +#ifdef UNIX if (*names[n] == NUL) { -# ifdef __APPLE__ +# ifdef __APPLE__ p = "/private/tmp"; -# else +# else p = "/tmp"; -# endif +# endif mustfree = false; } else -# endif +#endif { p = vim_getenv(names[n]); } @@ -2397,6 +2397,8 @@ static char_u *did_set_string_option(int opt_idx, char_u **varp, bool new_value_ os_setenv("VIMRUNTIME", "", 1); didset_vimruntime = false; } + } else if (varp == &p_rtp || varp == &p_pp) { // 'runtimepath' 'packpath' + runtime_search_path_invalidate(); } else if (varp == &curwin->w_p_culopt || gvarp == &curwin->w_allbuf_opt.wo_culopt) { // 'cursorlineopt' if (**varp == NUL || fill_culopt_flags(*varp, curwin) != OK) { @@ -3439,7 +3441,7 @@ static char_u *set_chars_option(win_T *wp, char_u **varp, bool set) struct chars_tab { int *cp; ///< char value char *name; ///< char id - int def; ///< default value + int def; ///< default value }; struct chars_tab *tab; @@ -5829,10 +5831,10 @@ static char_u *get_varp(vimoption_T *p) return (char_u *)&(curbuf->b_p_cms); case PV_CPT: return (char_u *)&(curbuf->b_p_cpt); -# ifdef BACKSLASH_IN_FILENAME +#ifdef BACKSLASH_IN_FILENAME case PV_CSL: return (char_u *)&(curbuf->b_p_csl); -# endif +#endif case PV_CFU: return (char_u *)&(curbuf->b_p_cfu); case PV_OFU: @@ -6177,9 +6179,9 @@ void buf_copy_options(buf_T *buf, int flags) buf->b_p_inf = p_inf; buf->b_p_swf = cmdmod.noswapfile ? false : p_swf; buf->b_p_cpt = vim_strsave(p_cpt); -# ifdef BACKSLASH_IN_FILENAME +#ifdef BACKSLASH_IN_FILENAME buf->b_p_csl = vim_strsave(p_csl); -# endif +#endif buf->b_p_cfu = vim_strsave(p_cfu); buf->b_p_ofu = vim_strsave(p_ofu); buf->b_p_tfu = vim_strsave(p_tfu); diff --git a/src/nvim/os/env.c b/src/nvim/os/env.c index f239c9e1ec..0f363ecfc3 100644 --- a/src/nvim/os/env.c +++ b/src/nvim/os/env.c @@ -22,15 +22,15 @@ #include "nvim/vim.h" #ifdef WIN32 -#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 +# include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 #endif #ifdef HAVE__NSGETENVIRON -#include <crt_externs.h> +# include <crt_externs.h> #endif #ifdef HAVE_SYS_UTSNAME_H -#include <sys/utsname.h> +# include <sys/utsname.h> #endif // Because `uv_os_getenv` requires allocating, we must manage a map to maintain @@ -1161,7 +1161,7 @@ char_u *home_replace_save(buf_T *buf, char_u *src) FUNC_ATTR_NONNULL_RET /// Function given to ExpandGeneric() to obtain an environment variable name. char_u *get_env_name(expand_T *xp, int idx) { -# define ENVNAMELEN 100 +#define ENVNAMELEN 100 // this static buffer is needed to avoid a memory leak in ExpandGeneric static char_u name[ENVNAMELEN]; assert(idx >= 0); diff --git a/src/nvim/os/fs.c b/src/nvim/os/fs.c index d50d68c99e..bbf70a4830 100644 --- a/src/nvim/os/fs.c +++ b/src/nvim/os/fs.c @@ -29,7 +29,7 @@ #include "nvim/strings.h" #ifdef WIN32 -#include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 +# include "nvim/mbyte.h" // for utf8_to_utf16, utf16_to_utf8 #endif #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -1226,12 +1226,12 @@ char *os_resolve_shortcut(const char *fname) goto shortcut_errorw; } -# if 0 // This makes Vim wait a long time if the target does not exist. +# if 0 // This makes Vim wait a long time if the target does not exist. hr = pslw->lpVtbl->Resolve(pslw, NULL, SLR_NO_UI); if (hr != S_OK) { goto shortcut_errorw; } -# endif +# endif // Get the path to the link target. ZeroMemory(wsz, MAX_PATH * sizeof(wchar_t)); @@ -1262,7 +1262,7 @@ shortcut_end: return rfname; } -#define is_path_sep(c) ((c) == L'\\' || (c) == L'/') +# define is_path_sep(c) ((c) == L'\\' || (c) == L'/') /// Returns true if the path contains a reparse point (junction or symbolic /// link). Otherwise false in returned. bool os_is_reparse_point_include(const char *path) diff --git a/src/nvim/os/pty_process_unix.c b/src/nvim/os/pty_process_unix.c index d94de2e397..24ecf5c24f 100644 --- a/src/nvim/os/pty_process_unix.c +++ b/src/nvim/os/pty_process_unix.c @@ -157,7 +157,7 @@ static void init_child(PtyProcess *ptyproc) FUNC_ATTR_NONNULL_ALL { #if defined(HAVE__NSGETENVIRON) -#define environ (*_NSGetEnviron()) +# define environ (*_NSGetEnviron()) #else extern char **environ; #endif diff --git a/src/nvim/os/shell.c b/src/nvim/os/shell.c index f0d446b4c5..2bff65b241 100644 --- a/src/nvim/os/shell.c +++ b/src/nvim/os/shell.c @@ -873,10 +873,10 @@ static void system_data_cb(Stream *stream, RBuffer *buf, size_t count, void *dat /// Returns the previous decision if size=0. static bool out_data_decide_throttle(size_t size) { - static uint64_t started = 0; // Start time of the current throttle. - static size_t received = 0; // Bytes observed since last throttle. - static size_t visit = 0; // "Pulse" count of the current throttle. - static char pulse_msg[] = { ' ', ' ', ' ', '\0' }; + static uint64_t started = 0; // Start time of the current throttle. + static size_t received = 0; // Bytes observed since last throttle. + static size_t visit = 0; // "Pulse" count of the current throttle. + static char pulse_msg[] = { ' ', ' ', ' ', '\0' }; if (!size) { bool previous_decision = (visit > 0); @@ -933,8 +933,8 @@ static bool out_data_decide_throttle(size_t size) static void out_data_ring(char *output, size_t size) { #define MAX_CHUNK_SIZE (OUT_DATA_THRESHOLD / 2) - static char last_skipped[MAX_CHUNK_SIZE]; // Saved output. - static size_t last_skipped_len = 0; + static char last_skipped[MAX_CHUNK_SIZE]; // Saved output. + static size_t last_skipped_len = 0; assert(output != NULL || (size == 0 || size == SIZE_MAX)); diff --git a/src/nvim/os/tty.c b/src/nvim/os/tty.c index c80ef99084..126b1b0044 100644 --- a/src/nvim/os/tty.c +++ b/src/nvim/os/tty.c @@ -23,15 +23,6 @@ /// @param out_fd stdout file descriptor void os_tty_guess_term(const char **term, int out_fd) { - bool winpty = (os_getenv("NVIM") != NULL); - - if (winpty) { - // Force TERM=win32con when running in winpty. - *term = "win32con"; - uv_tty_set_vterm_state(UV_TTY_UNSUPPORTED); - return; - } - bool conemu_ansi = strequal(os_getenv("ConEmuANSI"), "ON"); bool vtp = false; diff --git a/src/nvim/os/users.c b/src/nvim/os/users.c index 2687c66f24..fd7ead68da 100644 --- a/src/nvim/os/users.c +++ b/src/nvim/os/users.c @@ -43,7 +43,7 @@ int os_get_usernames(garray_T *users) } ga_init(users, sizeof(char *), 20); -# if defined(HAVE_GETPWENT) && defined(HAVE_PWD_H) +#if defined(HAVE_GETPWENT) && defined(HAVE_PWD_H) { struct passwd *pw; @@ -53,7 +53,7 @@ int os_get_usernames(garray_T *users) } endpwent(); } -# elif defined(WIN32) +#elif defined(WIN32) { DWORD nusers = 0, ntotal = 0, i; PUSER_INFO_0 uinfo; @@ -73,8 +73,8 @@ int os_get_usernames(garray_T *users) NetApiBufferFree(uinfo); } } -# endif -# if defined(HAVE_GETPWNAM) +#endif +#if defined(HAVE_GETPWNAM) { const char *user_env = os_getenv("USER"); @@ -104,7 +104,7 @@ int os_get_usernames(garray_T *users) } } } -# endif +#endif return OK; } diff --git a/src/nvim/plines.c b/src/nvim/plines.c index 28138af13c..5b0418ed92 100644 --- a/src/nvim/plines.c +++ b/src/nvim/plines.c @@ -13,6 +13,7 @@ #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/decoration.h" #include "nvim/diff.h" #include "nvim/fold.h" #include "nvim/func_attr.h" @@ -41,7 +42,34 @@ int plines_win(win_T *wp, linenr_T lnum, bool winheight) { // Check for filler lines above this buffer line. When folded the result // is one line anyway. - return plines_win_nofill(wp, lnum, winheight) + diff_check_fill(wp, lnum); + return plines_win_nofill(wp, lnum, winheight) + win_get_fill(wp, lnum); +} + + +/// Return the number of filler lines above "lnum". +/// +/// @param wp +/// @param lnum +/// +/// @return Number of filler lines above lnum +int win_get_fill(win_T *wp, linenr_T lnum) +{ + int virt_lines = decor_virtual_lines(wp, lnum); + + // be quick when there are no filler lines + if (diffopt_filler()) { + int n = diff_check(wp, lnum); + + if (n > 0) { + return virt_lines+n; + } + } + return virt_lines; +} + +bool win_may_fill(win_T *wp) +{ + return (wp->w_p_diff && diffopt_filler()) || wp->w_buffer->b_virt_line_mark; } /// @param winheight when true limit to window height @@ -107,7 +135,7 @@ int plines_win_col(win_T *wp, linenr_T lnum, long column) { // Check for filler lines above this buffer line. When folded the result // is one line anyway. - int lines = diff_check_fill(wp, lnum); + int lines = win_get_fill(wp, lnum); if (!wp->w_p_wrap) { return lines + 1; diff --git a/src/nvim/popupmnu.c b/src/nvim/popupmnu.c index aeb2c8c44a..606c03f838 100644 --- a/src/nvim/popupmnu.c +++ b/src/nvim/popupmnu.c @@ -514,7 +514,7 @@ void pum_redraw(void) char_u saved = *p; *p = NUL; - st = (char_u *)transstr((const char *)s); + st = (char_u *)transstr((const char *)s, true); *p = saved; if (pum_rl) { diff --git a/src/nvim/quickfix.c b/src/nvim/quickfix.c index d0bd63e6ca..eb0ba874f4 100644 --- a/src/nvim/quickfix.c +++ b/src/nvim/quickfix.c @@ -93,7 +93,7 @@ typedef enum /// information and entries can be added later using setqflist()/setloclist(). typedef struct qf_list_S { unsigned qf_id; ///< Unique identifier for this list - qfltype_T qfl_type; + qfltype_T qfl_type; qfline_T *qf_start; ///< pointer to the first error qfline_T *qf_last; ///< pointer to the last error qfline_T *qf_ptr; ///< pointer to the current error @@ -103,7 +103,7 @@ typedef struct qf_list_S { char_u *qf_title; ///< title derived from the command that created ///< the error list or set by setqflist typval_T *qf_ctx; ///< context set by setqflist/setloclist - Callback qftf_cb; ///< 'quickfixtextfunc' callback function + Callback qftf_cb; ///< 'quickfixtextfunc' callback function struct dir_stack_T *qf_dir_stack; char_u *qf_directory; @@ -201,13 +201,13 @@ typedef struct { size_t errmsglen; long lnum; long end_lnum; - int col; + int col; int end_col; bool use_viscol; char_u *pattern; - int enr; + int enr; char_u type; - bool valid; + bool valid; } qffields_T; #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -244,7 +244,7 @@ static char_u *e_no_more_items = (char_u *)N_("E553: No more items"); // Looking up a buffer can be slow if there are many. Remember the last one // to make this a lot faster if there are multiple matches in the same file. static char_u *qf_last_bufname = NULL; -static bufref_T qf_last_bufref = { NULL, 0, 0 }; +static bufref_T qf_last_bufref = { NULL, 0, 0 }; static char *e_current_quickfix_list_was_changed = N_("E925: Current quickfix list was changed"); @@ -913,7 +913,7 @@ static int qf_parse_line(qf_list_T *qfl, char_u *linebuf, size_t linelen, efm_T qffields_T *fields) { efm_T *fmt_ptr; - int idx = 0; + int idx = 0; char_u *tail = NULL; int status; @@ -3115,8 +3115,8 @@ void qf_list(exarg_T *eap) int idx1 = 1; int idx2 = -1; char_u *arg = eap->arg; - int all = eap->forceit; // if not :cl!, only show - // recognised errors + int all = eap->forceit; // if not :cl!, only show + // recognised errors qf_info_T *qi; if ((qi = qf_cmd_get_stack(eap, true)) == NULL) { @@ -6448,7 +6448,7 @@ static int qf_setprop_items_from_lines(qf_info_T *qi, int qf_idx, const dict_T * { char_u *errorformat = p_efm; dictitem_T *efm_di; - int retval = FAIL; + int retval = FAIL; // Use the user supplied errorformat settings (if present) if ((efm_di = tv_dict_find(what, S_LEN("efm"))) != NULL) { @@ -6491,7 +6491,7 @@ static int qf_setprop_context(qf_list_T *qfl, dictitem_T *di) static int qf_setprop_curidx(qf_info_T *qi, qf_list_T *qfl, const dictitem_T *di) FUNC_ATTR_NONNULL_ALL { - int newidx; + int newidx; // If the specified index is '$', then use the last entry if (di->di_tv.v_type == VAR_STRING @@ -6537,7 +6537,7 @@ static int qf_set_properties(qf_info_T *qi, const dict_T *what, int action, char { qf_list_T *qfl; dictitem_T *di; - int retval = FAIL; + int retval = FAIL; bool newlist = action == ' ' || qf_stack_empty(qi); int qf_idx = qf_setprop_get_qfidx(qi, what, action, &newlist); if (qf_idx == INVALID_QFIDX) { // List not found diff --git a/src/nvim/runtime.c b/src/nvim/runtime.c index cbd1e081b3..e1669a8c19 100644 --- a/src/nvim/runtime.c +++ b/src/nvim/runtime.c @@ -5,6 +5,7 @@ /// /// Management of runtime files (including packages) +#include "nvim/api/private/helpers.h" #include "nvim/ascii.h" #include "nvim/charset.h" #include "nvim/eval.h" @@ -20,6 +21,9 @@ # include "runtime.c.generated.h" #endif +static bool runtime_search_path_valid = false; +static int *runtime_search_path_ref = NULL; +static RuntimeSearchPath runtime_search_path; /// ":runtime [what] {name}" void ex_runtime(exarg_T *eap) @@ -89,8 +93,7 @@ int do_in_path(char_u *path, char_u *name, int flags, DoInRuntimepathCB callback // Skip after or non-after directories. if (flags & (DIP_NOAFTER | DIP_AFTER)) { - bool is_after = buflen >= 5 - && STRCMP(buf + buflen - 5, "after") == 0; + bool is_after = path_is_after(buf, buflen); if ((is_after && (flags & DIP_NOAFTER)) || (!is_after && (flags & DIP_AFTER))) { @@ -155,6 +158,115 @@ int do_in_path(char_u *path, char_u *name, int flags, DoInRuntimepathCB callback return did_one ? OK : FAIL; } +/// Find the file "name" in all directories in "path" and invoke +/// "callback(fname, cookie)". +/// "name" can contain wildcards. +/// When "flags" has DIP_ALL: source all files, otherwise only the first one. +/// When "flags" has DIP_DIR: find directories instead of files. +/// When "flags" has DIP_ERR: give an error message if there is no match. +/// +/// return FAIL when no file could be sourced, OK otherwise. +int do_in_cached_path(char_u *name, int flags, DoInRuntimepathCB callback, void *cookie) +{ + runtime_search_path_validate(); + char_u *tail; + int num_files; + char_u **files; + int i; + bool did_one = false; + + char_u buf[MAXPATHL]; + + if (p_verbose > 10 && name != NULL) { + verbose_enter(); + smsg(_("Searching for \"%s\" in runtime path"), (char *)name); + verbose_leave(); + } + + RuntimeSearchPath path = runtime_search_path; + int ref = 0; + if (runtime_search_path_ref == NULL) { + // cached path was unreferenced. keep a ref to + // prevent runtime_search_path() to freeing it too early + ref++; + runtime_search_path_ref = &ref; + } + + // Loop over all entries in cached path + for (size_t j = 0; j < kv_size(path); j++) { + SearchPathItem item = kv_A(path, j); + size_t buflen = strlen(item.path); + + // Skip after or non-after directories. + if (flags & (DIP_NOAFTER | DIP_AFTER)) { + if ((item.after && (flags & DIP_NOAFTER)) + || (!item.after && (flags & DIP_AFTER))) { + continue; + } + } + + if (name == NULL) { + (*callback)((char_u *)item.path, cookie); + did_one = true; + } else if (buflen + STRLEN(name) + 2 < MAXPATHL) { + STRCPY(buf, item.path); + add_pathsep((char *)buf); + tail = buf + STRLEN(buf); + + // Loop over all patterns in "name" + char_u *np = name; + while (*np != NUL && ((flags & DIP_ALL) || !did_one)) { + // Append the pattern from "name" to buf[]. + assert(MAXPATHL >= (tail - buf)); + copy_option_part(&np, tail, (size_t)(MAXPATHL - (tail - buf)), + "\t "); + + if (p_verbose > 10) { + verbose_enter(); + smsg(_("Searching for \"%s\""), buf); + verbose_leave(); + } + + int ew_flags = ((flags & DIP_DIR) ? EW_DIR : EW_FILE) + | (flags & DIP_DIRFILE) ? (EW_DIR|EW_FILE) : 0; + + // Expand wildcards, invoke the callback for each match. + char_u *(pat[]) = { buf }; + if (gen_expand_wildcards(1, pat, &num_files, &files, ew_flags) == OK) { + for (i = 0; i < num_files; i++) { + (*callback)(files[i], cookie); + did_one = true; + if (!(flags & DIP_ALL)) { + break; + } + } + FreeWild(num_files, files); + } + } + } + } + + if (!did_one && name != NULL) { + if (flags & DIP_ERR) { + EMSG3(_(e_dirnotf), "runtime path", name); + } else if (p_verbose > 0) { + verbose_enter(); + smsg(_("not found in runtime path: \"%s\""), name); + verbose_leave(); + } + } + + if (ref) { + if (runtime_search_path_ref == &ref) { + runtime_search_path_ref = NULL; + } else { + runtime_search_path_free(path); + } + } + + + return did_one ? OK : FAIL; +} /// Find "name" in "path". When found, invoke the callback function for /// it: callback(fname, "cookie") /// When "flags" has DIP_ALL repeat for all matches, otherwise only the first @@ -167,13 +279,6 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags, DoInRuntimepathCB c void *cookie) { int done = FAIL; - if (!(flags & (DIP_NOAFTER | DIP_AFTER))) { - done = do_in_path_and_pp(path, name, flags | DIP_NOAFTER, callback, cookie); - if (done == OK && !(flags & DIP_ALL)) { - return done; - } - flags |= DIP_AFTER; - } if ((flags & DIP_NORTP) == 0) { done |= do_in_path(path, (name && !*name) ? NULL : name, flags, callback, cookie); @@ -227,10 +332,182 @@ int do_in_path_and_pp(char_u *path, char_u *name, int flags, DoInRuntimepathCB c return done; } +static void push_path(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, + char *entry, bool after) +{ + handle_T h = map_get(String, handle_T)(rtp_used, cstr_as_string((char *)entry)); + if (h == 0) { + char *allocated = xstrdup(entry); + map_put(String, handle_T)(rtp_used, cstr_as_string(allocated), 1); + kv_push(*search_path, ((SearchPathItem){ allocated, after })); + } +} + +static void expand_rtp_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, + char *entry, bool after) +{ + if (map_get(String, handle_T)(rtp_used, cstr_as_string(entry))) { + return; + } + + if (!*entry) { + push_path(search_path, rtp_used, entry, after); + } + + int num_files; + char_u **files; + char_u *(pat[]) = { (char_u *)entry }; + if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) { + for (int i = 0; i < num_files; i++) { + push_path(search_path, rtp_used, (char *)files[i], after); + } + FreeWild(num_files, files); + } +} + +static void expand_pack_entry(RuntimeSearchPath *search_path, Map(String, handle_T) *rtp_used, + CharVec *after_path, char_u *pack_entry) +{ + static char buf[MAXPATHL]; + char *(start_pat[]) = { "/pack/*/start/*", "/start/*" }; // NOLINT + for (int i = 0; i < 2; i++) { + if (STRLEN(pack_entry) + STRLEN(start_pat[i]) + 1 > MAXPATHL) { + continue; + } + xstrlcpy(buf, (char *)pack_entry, MAXPATHL); + xstrlcat(buf, start_pat[i], sizeof buf); + expand_rtp_entry(search_path, rtp_used, buf, false); + size_t after_size = STRLEN(buf)+7; + char *after = xmallocz(after_size); + xstrlcpy(after, buf, after_size); + xstrlcat(after, "/after", after_size); + kv_push(*after_path, after); + } +} + +static bool path_is_after(char_u *buf, size_t buflen) +{ + // NOTE: we only consider dirs exactly matching "after" to be an AFTER dir. + // vim8 considers all dirs like "foo/bar_after", "Xafter" etc, as an + // "after" dir in SOME codepaths not not in ALL codepaths. + return buflen >= 5 + && (!(buflen >= 6) || vim_ispathsep(buf[buflen-6])) + && STRCMP(buf + buflen - 5, "after") == 0; +} + +RuntimeSearchPath runtime_search_path_build(void) +{ + kvec_t(String) pack_entries = KV_INITIAL_VALUE; + // TODO(bfredl): these should just be sets, when Set(String) is do merge to + // master. + Map(String, handle_T) pack_used = MAP_INIT; + Map(String, handle_T) rtp_used = MAP_INIT; + RuntimeSearchPath search_path = KV_INITIAL_VALUE; + CharVec after_path = KV_INITIAL_VALUE; + + static char_u buf[MAXPATHL]; + for (char *entry = (char *)p_pp; *entry != NUL; ) { + char *cur_entry = entry; + copy_option_part((char_u **)&entry, buf, MAXPATHL, ","); + + String the_entry = { .data = cur_entry, .size = STRLEN(buf) }; + + kv_push(pack_entries, the_entry); + map_put(String, handle_T)(&pack_used, the_entry, 0); + } + + + char *rtp_entry; + for (rtp_entry = (char *)p_rtp; *rtp_entry != NUL; ) { + char *cur_entry = rtp_entry; + copy_option_part((char_u **)&rtp_entry, buf, MAXPATHL, ","); + size_t buflen = STRLEN(buf); + + if (path_is_after(buf, buflen)) { + rtp_entry = cur_entry; + break; + } + + // fact: &rtp entries can contain wild chars + expand_rtp_entry(&search_path, &rtp_used, (char *)buf, false); + + handle_T *h = map_ref(String, handle_T)(&pack_used, cstr_as_string((char *)buf), false); + if (h) { + (*h)++; + expand_pack_entry(&search_path, &rtp_used, &after_path, buf); + } + } + + for (size_t i = 0; i < kv_size(pack_entries); i++) { + handle_T h = map_get(String, handle_T)(&pack_used, kv_A(pack_entries, i)); + if (h == 0) { + expand_pack_entry(&search_path, &rtp_used, &after_path, (char_u *)kv_A(pack_entries, i).data); + } + } + + // "after" packages + for (size_t i = 0; i < kv_size(after_path); i++) { + expand_rtp_entry(&search_path, &rtp_used, kv_A(after_path, i), true); + xfree(kv_A(after_path, i)); + } + + // "after" dirs in rtp + for (; *rtp_entry != NUL;) { + copy_option_part((char_u **)&rtp_entry, buf, MAXPATHL, ","); + expand_rtp_entry(&search_path, &rtp_used, (char *)buf, path_is_after(buf, STRLEN(buf))); + } + + // strings are not owned + kv_destroy(pack_entries); + kv_destroy(after_path); + map_destroy(String, handle_T)(&pack_used); + map_destroy(String, handle_T)(&rtp_used); + + return search_path; +} + +void runtime_search_path_invalidate(void) +{ + runtime_search_path_valid = false; +} + +void runtime_search_path_free(RuntimeSearchPath path) +{ + for (size_t j = 0; j < kv_size(path); j++) { + SearchPathItem item = kv_A(path, j); + xfree(item.path); + } + kv_destroy(path); +} + +void runtime_search_path_validate(void) +{ + if (!runtime_search_path_valid) { + if (!runtime_search_path_ref) { + runtime_search_path_free(runtime_search_path); + } + runtime_search_path = runtime_search_path_build(); + runtime_search_path_valid = true; + runtime_search_path_ref = NULL; // initially unowned + } +} + + + /// Just like do_in_path_and_pp(), using 'runtimepath' for "path". int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, void *cookie) { - return do_in_path_and_pp(p_rtp, name, flags | DIP_START, callback, cookie); + int success = FAIL; + if (!(flags & DIP_NORTP)) { + success |= do_in_cached_path((name && !*name) ? NULL : name, flags, callback, cookie); + flags = (flags & ~DIP_START) | DIP_NORTP; + } + // TODO(bfredl): we could integrate disabled OPT dirs into the cached path + // which would effectivize ":packadd myoptpack" as well + if ((flags & (DIP_START|DIP_OPT)) && (success == FAIL || (flags & DIP_ALL))) { + success |= do_in_path_and_pp(p_rtp, name, flags, callback, cookie); + } + return success; } /// Source the file "name" from all directories in 'runtimepath'. @@ -240,8 +517,7 @@ int do_in_runtimepath(char_u *name, int flags, DoInRuntimepathCB callback, void /// return FAIL when no file could be sourced, OK otherwise. int source_runtime(char_u *name, int flags) { - flags |= (flags & DIP_NORTP) ? 0 : DIP_START; - return source_in_path(p_rtp, name, flags); + return do_in_runtimepath(name, flags, source_callback, NULL); } /// Just like source_runtime(), but use "path" instead of 'runtimepath'. @@ -266,7 +542,10 @@ static void source_all_matches(char_u *pat) } /// Add the package directory to 'runtimepath' -static int add_pack_dir_to_rtp(char_u *fname) +/// +/// @param fname the package path +/// @param is_pack whether the added dir is a "pack/*/start/*/" style package +static int add_pack_dir_to_rtp(char_u *fname, bool is_pack) { char_u *p4, *p3, *p2, *p1, *p; char_u *buf = NULL; @@ -344,7 +623,7 @@ static int add_pack_dir_to_rtp(char_u *fname) // check if rtp/pack/name/start/name/after exists afterdir = concat_fnames((char *)fname, "after", true); size_t afterlen = 0; - if (os_isdir((char_u *)afterdir)) { + if (is_pack ? pack_has_entries((char_u *)afterdir) : os_isdir((char_u *)afterdir)) { afterlen = strlen(afterdir) + 1; // add one for comma } @@ -465,7 +744,7 @@ static void add_pack_plugin(bool opt, char_u *fname, void *cookie) xfree(buf); if (!found) { // directory is not yet in 'runtimepath', add it - if (add_pack_dir_to_rtp(fname) == FAIL) { + if (add_pack_dir_to_rtp(fname, false) == FAIL) { return; } } @@ -486,6 +765,41 @@ static void add_opt_pack_plugin(char_u *fname, void *cookie) add_pack_plugin(true, fname, cookie); } + +/// Add all packages in the "start" directory to 'runtimepath'. +void add_pack_start_dirs(void) +{ + do_in_path(p_pp, NULL, DIP_ALL + DIP_DIR, add_pack_start_dir, NULL); +} + +static bool pack_has_entries(char_u *buf) +{ + int num_files; + char_u **files; + char_u *(pat[]) = { (char_u *)buf }; + if (gen_expand_wildcards(1, pat, &num_files, &files, EW_DIR) == OK) { + FreeWild(num_files, files); + } + return num_files > 0; +} + +static void add_pack_start_dir(char_u *fname, void *cookie) +{ + static char_u buf[MAXPATHL]; + char *(start_pat[]) = { "/start/*", "/pack/*/start/*" }; // NOLINT + for (int i = 0; i < 2; i++) { + if (STRLEN(fname) + STRLEN(start_pat[i]) + 1 > MAXPATHL) { + continue; + } + xstrlcpy((char *)buf, (char *)fname, MAXPATHL); + xstrlcat((char *)buf, start_pat[i], sizeof buf); + if (pack_has_entries(buf)) { + add_pack_dir_to_rtp(buf, true); + } + } +} + + /// Load plugins from all packages in the "start" directory. void load_start_packages(void) { @@ -504,10 +818,43 @@ void ex_packloadall(exarg_T *eap) // First do a round to add all directories to 'runtimepath', then load // the plugins. This allows for plugins to use an autoload directory // of another plugin. + add_pack_start_dirs(); load_start_packages(); } } +/// Read all the plugin files at startup +void load_plugins(void) +{ + if (p_lpl) { + char_u *rtp_copy = p_rtp; + char_u *const plugin_pattern_vim = (char_u *)"plugin/**/*.vim"; // NOLINT + char_u *const plugin_pattern_lua = (char_u *)"plugin/**/*.lua"; // NOLINT + + if (!did_source_packages) { + rtp_copy = vim_strsave(p_rtp); + add_pack_start_dirs(); + } + + // don't use source_runtime() yet so we can check for :packloadall below + source_in_path(rtp_copy, plugin_pattern_vim, DIP_ALL | DIP_NOAFTER); + source_in_path(rtp_copy, plugin_pattern_lua, DIP_ALL | DIP_NOAFTER); + TIME_MSG("loading rtp plugins"); + + // Only source "start" packages if not done already with a :packloadall + // command. + if (!did_source_packages) { + xfree(rtp_copy); + load_start_packages(); + } + TIME_MSG("loading packages"); + + source_runtime(plugin_pattern_vim, DIP_ALL | DIP_AFTER); + source_runtime(plugin_pattern_lua, DIP_ALL | DIP_AFTER); + TIME_MSG("loading after plugins"); + } +} + /// ":packadd[!] {name}" void ex_packadd(exarg_T *eap) { diff --git a/src/nvim/runtime.h b/src/nvim/runtime.h index b40c2b670e..db31ae4e1e 100644 --- a/src/nvim/runtime.h +++ b/src/nvim/runtime.h @@ -7,10 +7,30 @@ typedef void (*DoInRuntimepathCB)(char_u *, void *); +typedef struct { + char *path; + bool after; +} SearchPathItem; + +typedef kvec_t(SearchPathItem) RuntimeSearchPath; +typedef kvec_t(char *) CharVec; + // last argument for do_source() #define DOSO_NONE 0 #define DOSO_VIMRC 1 // loading vimrc file +// Used for flags in do_in_path() +#define DIP_ALL 0x01 // all matches, not just the first one +#define DIP_DIR 0x02 // find directories instead of files +#define DIP_ERR 0x04 // give an error message when none found +#define DIP_START 0x08 // also use "start" directory in 'packpath' +#define DIP_OPT 0x10 // also use "opt" directory in 'packpath' +#define DIP_NORTP 0x20 // do not use 'runtimepath' +#define DIP_NOAFTER 0x40 // skip "after" directories +#define DIP_AFTER 0x80 // only use "after" directories +#define DIP_LUA 0x100 // also use ".lua" files +#define DIP_DIRFILE 0x200 // find both files and directories + #ifdef INCLUDE_GENERATED_DECLARATIONS # include "runtime.h.generated.h" diff --git a/src/nvim/screen.c b/src/nvim/screen.c index 79e960fe8b..fcd8fb1118 100644 --- a/src/nvim/screen.c +++ b/src/nvim/screen.c @@ -1003,8 +1003,7 @@ static void win_update(win_T *wp, Providers *providers) i = plines_m_win(wp, wp->w_topline, wp->w_lines[0].wl_lnum - 1); // insert extra lines for previously invisible filler lines if (wp->w_lines[0].wl_lnum != wp->w_topline) { - i += diff_check_fill(wp, wp->w_lines[0].wl_lnum) - - wp->w_old_topfill; + i += win_get_fill(wp, wp->w_lines[0].wl_lnum) - wp->w_old_topfill; } if (i != 0 && i < wp->w_grid.Rows - 2) { // less than a screen off // Try to insert the correct number of lines. @@ -1067,7 +1066,7 @@ static void win_update(win_T *wp, Providers *providers) if (wp->w_lines[0].wl_lnum == wp->w_topline) { row += wp->w_old_topfill; } else { - row += diff_check_fill(wp, wp->w_topline); + row += win_get_fill(wp, wp->w_topline); } // ... but don't delete new filler lines. row -= wp->w_topfill; @@ -1101,12 +1100,12 @@ static void win_update(win_T *wp, Providers *providers) break; } } - /* Correct the first entry for filler lines at the top - * when it won't get updated below. */ - if (wp->w_p_diff && bot_start > 0) { - wp->w_lines[0].wl_size = - plines_win_nofill(wp, wp->w_topline, true) - + wp->w_topfill; + + // Correct the first entry for filler lines at the top + // when it won't get updated below. + if (win_may_fill(wp) && bot_start > 0) { + wp->w_lines[0].wl_size = (plines_win_nofill(wp, wp->w_topline, true) + + wp->w_topfill); } } } @@ -1564,7 +1563,7 @@ static void win_update(win_T *wp, Providers *providers) && lnum > wp->w_topline && !(dy_flags & (DY_LASTLINE | DY_TRUNCATE)) && srow + wp->w_lines[idx].wl_size > wp->w_grid.Rows - && diff_check_fill(wp, lnum) == 0) { + && win_get_fill(wp, lnum) == 0) { // This line is not going to fit. Don't draw anything here, // will draw "@ " lines below. row = wp->w_grid.Rows + 1; @@ -1664,7 +1663,7 @@ static void win_update(win_T *wp, Providers *providers) * Don't overwrite it, it can be edited. */ wp->w_botline = lnum + 1; - } else if (diff_check_fill(wp, lnum) >= wp->w_grid.Rows - srow) { + } else if (win_get_fill(wp, lnum) >= wp->w_grid.Rows - srow) { // Window ends in filler lines. wp->w_botline = lnum; wp->w_filler_rows = wp->w_grid.Rows - srow; @@ -1691,7 +1690,7 @@ static void win_update(win_T *wp, Providers *providers) } else { 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); + j = win_get_fill(wp, wp->w_botline); if (j > 0 && !wp->w_botfill) { // Display filler text below last line. win_line() will check // for ml_line_count+1 and only draw filler lines @@ -1866,7 +1865,7 @@ static int compute_foldcolumn(win_T *wp, int col) /// Put a single char from an UTF-8 buffer into a line buffer. /// /// Handles composing chars and arabic shaping state. -static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) +static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, bool rl, int vcol) { const char_u *p = (char_u *)s->p; int cells = utf_ptr2cells(p); @@ -1876,7 +1875,13 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) return -1; } u8c = utfc_ptr2char(p, u8cc); - if (*p < 0x80 && u8cc[0] == 0) { + if (*p == TAB) { + cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells); + for (int c = 0; c < cells; c++) { + schar_from_ascii(dest[c], ' '); + } + goto done; + } else if (*p < 0x80 && u8cc[0] == 0) { schar_from_ascii(dest[0], *p); s->prev_c = u8c; } else { @@ -1909,6 +1914,7 @@ static int line_putchar(LineState *s, schar_T *dest, int maxcells, bool rl) if (cells > 1) { dest[1][0] = 0; } +done: s->p += c_len; return cells; } @@ -2055,7 +2061,7 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc bool draw_color_col = false; // highlight colorcolumn int *color_cols = NULL; // pointer to according columns array bool has_spell = false; // this buffer has spell checking -# define SPWORDLEN 150 +#define SPWORDLEN 150 char_u nextline[SPWORDLEN * 2]; // text with start of the next line int nextlinecol = 0; // column where nextline[] starts int nextline_idx = 0; /* index in nextline[] where next line @@ -2118,12 +2124,12 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc // draw_state: items that are drawn in sequence: #define WL_START 0 // nothing done yet -# define WL_CMDLINE WL_START + 1 // cmdline window column -# define WL_FOLD WL_CMDLINE + 1 // 'foldcolumn' -# define WL_SIGN WL_FOLD + 1 // column for signs +#define WL_CMDLINE WL_START + 1 // cmdline window column +#define WL_FOLD WL_CMDLINE + 1 // 'foldcolumn' +#define WL_SIGN WL_FOLD + 1 // column for signs #define WL_NR WL_SIGN + 1 // line number -# define WL_BRI WL_NR + 1 // 'breakindent' -# define WL_SBR WL_BRI + 1 // 'showbreak' or 'diff' +#define WL_BRI WL_NR + 1 // 'breakindent' +#define WL_SBR WL_BRI + 1 // 'showbreak' or 'diff' #define WL_LINE WL_SBR + 1 // text in the line int draw_state = WL_START; // what to draw next @@ -2138,8 +2144,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc int did_wcol = false; int match_conc = 0; ///< cchar for match functions int old_boguscols = 0; -# define VCOL_HLC (vcol - vcol_off) -# define FIX_FOR_BOGUSCOLS \ +#define VCOL_HLC (vcol - vcol_off) +#define FIX_FOR_BOGUSCOLS \ { \ n_extra += vcol_off; \ vcol -= vcol_off; \ @@ -2366,8 +2372,11 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc filler_lines = 0; area_highlighting = true; } + int virtual_lines = decor_virtual_lines(wp, lnum); + filler_lines += virtual_lines; if (lnum == wp->w_topline) { filler_lines = wp->w_topfill; + virtual_lines = MIN(virtual_lines, filler_lines); } filler_todo = filler_lines; @@ -2895,7 +2904,18 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc if (draw_state == WL_SBR - 1 && n_extra == 0) { draw_state = WL_SBR; - if (filler_todo > 0) { + if (filler_todo > filler_lines - virtual_lines) { + // TODO(bfredl): check this doesn't inhibit TUI-style + // clear-to-end-of-line. + c_extra = ' '; + c_final = NUL; + if (wp->w_p_rl) { + n_extra = col + 1; + } else { + n_extra = grid->Columns - col; + } + char_attr = 0; + } else if (filler_todo > 0) { // draw "deleted" diff line(s) if (char2cells(wp->w_p_fcs_chars.diff) > 1) { c_extra = '-'; @@ -3667,8 +3687,8 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc n_extra = tab_len; } else { char_u *p; - int i; - int saved_nextra = n_extra; + int i; + int saved_nextra = n_extra; if (vcol_off > 0) { // there are characters to conceal @@ -4402,7 +4422,19 @@ static int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool noc && !wp->w_p_rl; // Not right-to-left. int draw_col = col - boguscols; - draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns); + if (filler_todo > 0) { + int index = filler_todo - (filler_lines - virtual_lines); + if (index > 0) { + int fpos = kv_size(buf->b_virt_lines) - index; + assert(fpos >= 0); + int offset = buf->b_virt_line_leftcol ? 0 : win_col_offset; + draw_virt_text_item(buf, offset, kv_A(buf->b_virt_lines, fpos), + kHlModeReplace, grid->Columns, offset); + } + } else { + draw_virt_text(buf, win_col_offset, &draw_col, grid->Columns); + } + grid_put_linebuf(grid, row, 0, draw_col, grid->Columns, wp->w_p_rl, wp, wp->w_hl_attr_normal, wrap); if (wrap) { @@ -4485,67 +4517,80 @@ void draw_virt_text(buf_T *buf, int col_off, int *end_col, int max_col) bool do_eol = state->eol_col > -1; for (size_t i = 0; i < kv_size(state->active); i++) { DecorRange *item = &kv_A(state->active, i); - if (item->start_row == state->row && kv_size(item->decor.virt_text)) { - if (item->win_col == -1) { - if (item->decor.virt_text_pos == kVTRightAlign) { - right_pos -= item->decor.virt_text_width; - item->win_col = right_pos; - } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { - item->win_col = state->eol_col; - state->eol_col += item->decor.virt_text_width; - } else if (item->decor.virt_text_pos == kVTWinCol) { - item->win_col = MAX(item->decor.col+col_off, 0); - } - } - if (item->win_col < 0) { - continue; - } - VirtText vt = item->decor.virt_text; - HlMode hl_mode = item->decor.hl_mode; - LineState s = LINE_STATE(""); - int virt_attr = 0; - int col = item->win_col; - size_t virt_pos = 0; - item->win_col = -2; // deactivate - - while (col < max_col) { - if (!*s.p) { - if (virt_pos >= kv_size(vt)) { - break; - } - virt_attr = 0; - do { - s.p = kv_A(vt, virt_pos).text; - int hl_id = kv_A(vt, virt_pos).hl_id; - virt_attr = hl_combine_attr(virt_attr, - hl_id > 0 ? syn_id2attr(hl_id) : 0); - virt_pos++; - } while (!s.p && virt_pos < kv_size(vt)); - if (!s.p) { - break; - } - } - int attr; - bool through = false; - if (hl_mode == kHlModeCombine) { - attr = hl_combine_attr(linebuf_attr[col], virt_attr); - } else if (hl_mode == kHlModeBlend) { - through = (*s.p == ' '); - attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through); - } else { - attr = virt_attr; - } - schar_T dummy[2]; - int cells = line_putchar(&s, through ? dummy : &linebuf_char[col], - max_col-col, false); - linebuf_attr[col++] = attr; - if (cells > 1) { - linebuf_attr[col++] = attr; - } + if (!(item->start_row == state->row && kv_size(item->decor.virt_text))) { + continue; + } + if (item->win_col == -1) { + if (item->decor.virt_text_pos == kVTRightAlign) { + right_pos -= item->decor.virt_text_width; + item->win_col = right_pos; + } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { + item->win_col = state->eol_col; + } else if (item->decor.virt_text_pos == kVTWinCol) { + item->win_col = MAX(item->decor.col+col_off, 0); + } + } + if (item->win_col < 0) { + continue; + } + + int col = draw_virt_text_item(buf, item->win_col, item->decor.virt_text, + item->decor.hl_mode, max_col, item->win_col-col_off); + item->win_col = -2; // deactivate + if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { + state->eol_col = col+1; + } + + *end_col = MAX(*end_col, col); + } +} + +static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col, + int vcol) +{ + LineState s = LINE_STATE(""); + int virt_attr = 0; + size_t virt_pos = 0; + + while (col < max_col) { + if (!*s.p) { + if (virt_pos >= kv_size(vt)) { + break; + } + virt_attr = 0; + do { + s.p = kv_A(vt, virt_pos).text; + int hl_id = kv_A(vt, virt_pos).hl_id; + virt_attr = hl_combine_attr(virt_attr, + hl_id > 0 ? syn_id2attr(hl_id) : 0); + virt_pos++; + } while (!s.p && virt_pos < kv_size(vt)); + if (!s.p) { + break; } - *end_col = MAX(*end_col, col); } + int attr; + bool through = false; + if (hl_mode == kHlModeCombine) { + attr = hl_combine_attr(linebuf_attr[col], virt_attr); + } else if (hl_mode == kHlModeBlend) { + through = (*s.p == ' '); + attr = hl_blend_attrs(linebuf_attr[col], virt_attr, &through); + } else { + attr = virt_attr; + } + schar_T dummy[2]; + int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col], + max_col-col, false, vcol); + // if we failed to emit a char, we still need to advance + cells = MAX(cells, 1); + + for (int c = 0; c < cells; c++) { + linebuf_attr[col++] = attr; + } + vcol += cells; } + return col; } /// Determine if dedicated window grid should be used or the default_grid @@ -5489,7 +5534,7 @@ static void win_redr_custom(win_T *wp, bool draw_ruler) ewp->w_p_crb = p_crb_save; // Make all characters printable. - p = (char_u *)transstr((const char *)buf); + p = (char_u *)transstr((const char *)buf, true); len = STRLCPY(buf, p, sizeof(buf)); len = (size_t)len < sizeof(buf) ? len : (int)sizeof(buf) - 1; xfree(p); diff --git a/src/nvim/search.c b/src/nvim/search.c index 4be2402f1d..3d30932d69 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -1046,8 +1046,8 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, char_u *strcopy = NULL; char_u *ps; char_u *msgbuf = NULL; - size_t len; - bool has_offset = false; + size_t len; + bool has_offset = false; /* * A line offset is not remembered, this is vi compatible. @@ -1182,8 +1182,8 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char_u *pat, long count, if ((options & SEARCH_ECHO) && messaging() && !msg_silent && (!cmd_silent || !shortmess(SHM_SEARCHCOUNT))) { char_u *trunc; - char_u off_buf[40]; - size_t off_len = 0; + char_u off_buf[40]; + size_t off_len = 0; // Compute msg_row early. msg_start(); @@ -2459,7 +2459,7 @@ int findsent(Direction dir, long count) { pos_T pos, tpos; int c; - int (*func)(pos_T *); + int (*func)(pos_T *); bool noskip = false; // do not skip blanks pos = curwin->w_cursor; @@ -4474,7 +4474,7 @@ static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool sh update_search_stat(dirc, pos, cursor_pos, &stat, recompute, maxcount, timeout); if (stat.cur > 0) { - char t[SEARCH_STAT_BUF_LEN]; + char t[SEARCH_STAT_BUF_LEN]; if (curwin->w_p_rl && *curwin->w_p_rlc == 's') { if (stat.incomplete == 1) { @@ -4534,19 +4534,19 @@ static void cmdline_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, bool sh static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchstat_T *stat, bool recompute, int maxcount, long timeout) { - int save_ws = p_ws; - bool wraparound = false; - pos_T p = (*pos); - static pos_T lastpos = { 0, 0, 0 }; - static int cur = 0; - static int cnt = 0; - static bool exact_match = false; - static int incomplete = 0; - static int last_maxcount = SEARCH_STAT_DEF_MAX_COUNT; - static int chgtick = 0; + int save_ws = p_ws; + bool wraparound = false; + pos_T p = (*pos); + static pos_T lastpos = { 0, 0, 0 }; + static int cur = 0; + static int cnt = 0; + static bool exact_match = false; + static int incomplete = 0; + static int last_maxcount = SEARCH_STAT_DEF_MAX_COUNT; + static int chgtick = 0; static char_u *lastpat = NULL; static buf_T *lbuf = NULL; - proftime_T start; + proftime_T start; memset(stat, 0, sizeof(searchstat_T)); @@ -4636,12 +4636,12 @@ static void update_search_stat(int dirc, pos_T *pos, pos_T *cursor_pos, searchst // "searchcount()" function void f_searchcount(typval_T *argvars, typval_T *rettv, FunPtr fptr) { - pos_T pos = curwin->w_cursor; + pos_T pos = curwin->w_cursor; char_u *pattern = NULL; - int maxcount = SEARCH_STAT_DEF_MAX_COUNT; - long timeout = SEARCH_STAT_DEF_TIMEOUT; - bool recompute = true; - searchstat_T stat; + int maxcount = SEARCH_STAT_DEF_MAX_COUNT; + long timeout = SEARCH_STAT_DEF_TIMEOUT; + bool recompute = true; + searchstat_T stat; tv_dict_alloc_ret(rettv); diff --git a/src/nvim/sha256.c b/src/nvim/sha256.c index a2378cc202..9d6a2f2c41 100644 --- a/src/nvim/sha256.c +++ b/src/nvim/sha256.c @@ -51,8 +51,7 @@ void sha256_start(context_sha256_T *ctx) ctx->state[7] = 0x5BE0CD19; } -static void sha256_process(context_sha256_T *ctx, - const char_u data[SHA256_BUFFER_SIZE]) +static void sha256_process(context_sha256_T *ctx, const char_u data[SHA256_BUFFER_SIZE]) { uint32_t temp1, temp2, W[SHA256_BUFFER_SIZE]; uint32_t A, B, C, D, E, F, G, H; @@ -88,7 +87,7 @@ static void sha256_process(context_sha256_T *ctx, #define R(t) \ (W[t] = S1(W[t - 2]) + W[t - 7] + \ - S0(W[t - 15]) + W[t - 16]) + S0(W[t - 15]) + W[t - 16]) #define P(a, b, c, d, e, f, g, h, x, K) { \ temp1 = h + S3(e) + F1(e, f, g) + K + x; \ @@ -188,7 +187,7 @@ void sha256_update(context_sha256_T *ctx, const char_u *input, size_t length) uint32_t left = ctx->total[0] & (SHA256_BUFFER_SIZE-1); // left < buf size - ctx->total[0] += (uint32_t) length; + ctx->total[0] += (uint32_t)length; ctx->total[0] &= 0xFFFFFFFF; if (ctx->total[0] < length) { @@ -262,8 +261,8 @@ void sha256_finish(context_sha256_T *ctx, char_u digest[SHA256_SUM_SIZE]) /// /// @returns hex digest of "buf[buf_len]" in a static array. /// if "salt" is not NULL also do "salt[salt_len]". -const char *sha256_bytes(const uint8_t *restrict buf, size_t buf_len, - const uint8_t *restrict salt, size_t salt_len) +const char *sha256_bytes(const uint8_t *restrict buf, size_t buf_len, const uint8_t *restrict salt, + size_t salt_len) { char_u sha256sum[SHA256_SUM_SIZE]; static char hexit[SHA256_BUFFER_SIZE + 1]; // buf size + NULL diff --git a/src/nvim/shada.c b/src/nvim/shada.c index 7d277fe5c8..bf245bec51 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1,51 +1,50 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com -#include <stdlib.h> -#include <stddef.h> -#include <stdbool.h> -#include <string.h> -#include <inttypes.h> -#include <errno.h> #include <assert.h> - +#include <errno.h> +#include <inttypes.h> #include <msgpack.h> +#include <stdbool.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> #include <uv.h> -#include "nvim/os/os.h" -#include "nvim/os/time.h" -#include "nvim/vim.h" -#include "nvim/pos.h" -#include "nvim/ascii.h" -#include "nvim/shada.h" -#include "nvim/message.h" -#include "nvim/globals.h" -#include "nvim/memory.h" -#include "nvim/mark.h" -#include "nvim/macros.h" -#include "nvim/ops.h" -#include "nvim/garray.h" -#include "nvim/option.h" -#include "nvim/msgpack_rpc/helpers.h" #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" +#include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" +#include "nvim/eval/decode.h" +#include "nvim/eval/encode.h" +#include "nvim/eval/typval.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" -#include "nvim/search.h" -#include "nvim/regexp.h" -#include "nvim/eval/typval.h" -#include "nvim/version.h" -#include "nvim/path.h" #include "nvim/fileio.h" -#include "nvim/os/fileio.h" -#include "nvim/strings.h" -#include "nvim/quickfix.h" -#include "nvim/eval/encode.h" -#include "nvim/eval/decode.h" +#include "nvim/garray.h" +#include "nvim/globals.h" #include "nvim/lib/khash.h" #include "nvim/lib/kvec.h" +#include "nvim/macros.h" +#include "nvim/mark.h" +#include "nvim/memory.h" +#include "nvim/message.h" +#include "nvim/msgpack_rpc/helpers.h" +#include "nvim/ops.h" +#include "nvim/option.h" +#include "nvim/os/fileio.h" +#include "nvim/os/os.h" +#include "nvim/os/time.h" +#include "nvim/path.h" +#include "nvim/pos.h" +#include "nvim/quickfix.h" +#include "nvim/regexp.h" +#include "nvim/search.h" +#include "nvim/shada.h" +#include "nvim/strings.h" +#include "nvim/version.h" +#include "nvim/vim.h" #ifdef HAVE_BE64TOH # define _BSD_SOURCE 1 @@ -67,24 +66,24 @@ KHASH_MAP_INIT_STR(fnamebufs, buf_T *) KHASH_SET_INIT_STR(strset) #define copy_option_part(src, dest, ...) \ - ((char *) copy_option_part((char_u **) src, (char_u *) dest, __VA_ARGS__)) + ((char *)copy_option_part((char_u **)src, (char_u *)dest, __VA_ARGS__)) #define find_shada_parameter(...) \ - ((const char *) find_shada_parameter(__VA_ARGS__)) + ((const char *)find_shada_parameter(__VA_ARGS__)) #define home_replace_save(a, b) \ - ((char *)home_replace_save(a, (char_u *)b)) + ((char *)home_replace_save(a, (char_u *)b)) #define home_replace(a, b, c, d, e) \ - home_replace(a, (char_u *)b, (char_u *)c, d, e) + home_replace(a, (char_u *)b, (char_u *)c, d, e) #define vim_rename(a, b) \ - (vim_rename((char_u *)a, (char_u *)b)) + (vim_rename((char_u *)a, (char_u *)b)) #define mb_strnicmp(a, b, c) \ - (mb_strnicmp((char_u *)a, (char_u *)b, c)) + (mb_strnicmp((char_u *)a, (char_u *)b, c)) #define path_try_shorten_fname(b) \ - ((char *)path_try_shorten_fname((char_u *)b)) + ((char *)path_try_shorten_fname((char_u *)b)) #define buflist_new(ffname, sfname, ...) \ - (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) -#define os_isdir(f) (os_isdir((char_u *) f)) -#define regtilde(s, m) ((char *) regtilde((char_u *) s, m)) -#define path_tail_with_sep(f) ((char *) path_tail_with_sep((char_u *)f)) + (buflist_new((char_u *)ffname, (char_u *)sfname, __VA_ARGS__)) +#define os_isdir(f) (os_isdir((char_u *)f)) +#define regtilde(s, m) ((char *)regtilde((char_u *)s, m)) +#define path_tail_with_sep(f) ((char *)path_tail_with_sep((char_u *)f)) #define SEARCH_KEY_MAGIC "sm" #define SEARCH_KEY_SMARTCASE "sc" @@ -172,7 +171,7 @@ typedef enum { kSDItemBufferList = 9, ///< Buffer list. kSDItemLocalMark = 10, ///< Buffer-local mark. kSDItemChange = 11, ///< Item from buffer change list. -#define SHADA_LAST_ENTRY ((uint64_t) kSDItemChange) +#define SHADA_LAST_ENTRY ((uint64_t)kSDItemChange) } ShadaEntryType; /// Possible results when reading ShaDa file @@ -202,11 +201,11 @@ enum SRNIFlags { kSDReadHeader = (1 << kSDItemHeader), ///< Determines whether header should ///< be read (it is usually ignored). kSDReadUndisableableData = ( - (1 << kSDItemSearchPattern) - | (1 << kSDItemSubString) - | (1 << kSDItemJump)), ///< Data reading which cannot be disabled by - ///< &shada or other options except for disabling - ///< reading ShaDa as a whole. + (1 << kSDItemSearchPattern) + | (1 << kSDItemSubString) + | (1 << kSDItemJump)), ///< Data reading which cannot be disabled by + ///< &shada or other options except for disabling + ///< reading ShaDa as a whole. kSDReadRegisters = (1 << kSDItemRegister), ///< Determines whether registers ///< should be read (may only be ///< disabled when writing, but @@ -434,13 +433,13 @@ typedef struct sd_write_def { #endif #define DEF_SDE(name, attr, ...) \ - [kSDItem##name] = { \ - .timestamp = 0, \ - .type = kSDItem##name, \ - .data = { \ - .attr = { __VA_ARGS__ } \ - } \ - } + [kSDItem##name] = { \ + .timestamp = 0, \ + .type = kSDItem##name, \ + .data = { \ + .attr = { __VA_ARGS__ } \ + } \ + } #define DEFAULT_POS { 1, 0, 0 } static const pos_T default_pos = DEFAULT_POS; static const ShadaEntry sd_default_values[] = { @@ -475,9 +474,9 @@ static const ShadaEntry sd_default_values[] = { DEF_SDE(Variable, global_var, .name = NULL, .value = { - .v_type = VAR_UNKNOWN, - .vval = { .v_string = NULL } - }, + .v_type = VAR_UNKNOWN, + .vval = { .v_string = NULL } + }, .additional_elements = NULL), DEF_SDE(GlobalMark, filemark, .name = '"', @@ -533,17 +532,16 @@ static inline void hmll_init(HMLList *const hmll, const size_t size) /// /// @return `for` cycle header (use `HMLL_FORALL(hmll, cur_entry) {body}`). #define HMLL_FORALL(hmll, cur_entry, code) \ - for (HMLListEntry *cur_entry = (hmll)->first; cur_entry != NULL; \ - cur_entry = cur_entry->next) { \ - code \ - } \ + for (HMLListEntry *cur_entry = (hmll)->first; cur_entry != NULL; \ + cur_entry = cur_entry->next) { \ + code \ + } \ /// Remove entry from the linked list /// /// @param hmll List to remove from. /// @param hmll_entry Entry to remove. -static inline void hmll_remove(HMLList *const hmll, - HMLListEntry *const hmll_entry) +static inline void hmll_remove(HMLList *const hmll, HMLListEntry *const hmll_entry) FUNC_ATTR_NONNULL_ALL { if (hmll_entry == hmll->last_free_entry - 1) { @@ -580,9 +578,7 @@ static inline void hmll_remove(HMLList *const hmll, /// to insert at the first entry. /// @param[in] data Data to insert. /// @param[in] can_free_entry True if data can be freed. -static inline void hmll_insert(HMLList *const hmll, - HMLListEntry *hmll_entry, - const ShadaEntry data, +static inline void hmll_insert(HMLList *const hmll, HMLListEntry *hmll_entry, const ShadaEntry data, const bool can_free_entry) FUNC_ATTR_NONNULL_ARG(1) { @@ -595,11 +591,11 @@ static inline void hmll_insert(HMLList *const hmll, } HMLListEntry *target_entry; if (hmll->free_entry == NULL) { - assert((size_t) (hmll->last_free_entry - hmll->entries) + assert((size_t)(hmll->last_free_entry - hmll->entries) == hmll->num_entries); target_entry = hmll->last_free_entry++; } else { - assert((size_t) (hmll->last_free_entry - hmll->entries) - 1 + assert((size_t)(hmll->last_free_entry - hmll->entries) - 1 == hmll->num_entries); target_entry = hmll->free_entry; hmll->free_entry = NULL; @@ -637,10 +633,10 @@ static inline void hmll_insert(HMLList *const hmll, /// /// @return `for` cycle header (use `HMLL_FORALL(hmll, cur_entry) {body}`). #define HMLL_ITER_BACK(hmll, cur_entry, code) \ - for (cur_entry = (hmll)->last; cur_entry != NULL; \ - cur_entry = cur_entry->prev) { \ - code \ - } + for (cur_entry = (hmll)->last; cur_entry != NULL; \ + cur_entry = cur_entry->prev) { \ + code \ + } /// Free linked list /// @@ -655,8 +651,7 @@ static inline void hmll_dealloc(HMLList *const hmll) /// Wrapper for reading from file descriptors /// /// @return -1 or number of bytes read. -static ptrdiff_t read_file(ShaDaReadDef *const sd_reader, void *const dest, - const size_t size) +static ptrdiff_t read_file(ShaDaReadDef *const sd_reader, void *const dest, const size_t size) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { const ptrdiff_t ret = file_read(sd_reader->cookie, dest, size); @@ -678,14 +673,13 @@ static int read_char(ShaDaReadDef *const sd_reader) if (read_bytes != 1) { return EOF; } - return (int) ret; + return (int)ret; } /// Wrapper for writing to file descriptors /// /// @return -1 or number of bytes written. -static ptrdiff_t write_file(ShaDaWriteDef *const sd_writer, - const void *const dest, +static ptrdiff_t write_file(ShaDaWriteDef *const sd_writer, const void *const dest, const size_t size) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { @@ -720,8 +714,7 @@ static void close_sd_writer(ShaDaWriteDef *const sd_writer) /// /// @return FAIL in case of failure, OK in case of success. May set /// sd_reader->eof or sd_reader->error. -static int sd_reader_skip_read(ShaDaReadDef *const sd_reader, - const size_t offset) +static int sd_reader_skip_read(ShaDaReadDef *const sd_reader, const size_t offset) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { const ptrdiff_t skip_bytes = file_skip(sd_reader->cookie, offset); @@ -749,8 +742,7 @@ static int sd_reader_skip_read(ShaDaReadDef *const sd_reader, /// /// @return kSDReadStatusReadError, kSDReadStatusNotShaDa or /// kSDReadStatusSuccess. -static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, - const size_t offset) +static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, const size_t offset) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { if (sd_reader->skip(sd_reader, offset) != OK) { @@ -762,7 +754,7 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, emsgf(_(RCERR "Error while reading ShaDa file: " "last entry specified that it occupies %" PRIu64 " bytes, " "but file ended earlier"), - (uint64_t) offset); + (uint64_t)offset); return kSDReadStatusNotShaDa; } abort(); @@ -776,8 +768,7 @@ static ShaDaReadResult sd_reader_skip(ShaDaReadDef *const sd_reader, /// @param[out] sd_reader Location where reader structure will be saved. /// /// @return libuv error in case of error, 0 otherwise. -static int open_shada_file_for_reading(const char *const fname, - ShaDaReadDef *sd_reader) +static int open_shada_file_for_reading(const char *const fname, ShaDaReadDef *sd_reader) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { int error; @@ -819,7 +810,7 @@ static void close_file(void *cookie) static inline bool in_bufset(const khash_t(bufset) *const set, const buf_T *buf) FUNC_ATTR_PURE { - return kh_get(bufset, set, (uintptr_t) buf) != kh_end(set); + return kh_get(bufset, set, (uintptr_t)buf) != kh_end(set); } /// Check whether string is in the given set @@ -837,7 +828,7 @@ static inline bool in_strset(const khash_t(strset) *const set, char *str) /// Msgpack callback for writing to ShaDaWriteDef* static int msgpack_sd_writer_write(void *data, const char *buf, size_t len) { - ShaDaWriteDef *const sd_writer = (ShaDaWriteDef *) data; + ShaDaWriteDef *const sd_writer = (ShaDaWriteDef *)data; ptrdiff_t written_bytes = sd_writer->write(sd_writer, buf, len); if (written_bytes == -1) { emsgf(_(SERR "System error while writing ShaDa file: %s"), @@ -910,10 +901,8 @@ static int shada_read_file(const char *const file, const int flags) /// @param[out] hist Location where iteration results should be saved. /// /// @return Next iteration state. -static const void *shada_hist_iter(const void *const iter, - const uint8_t history_type, - const bool zero, - ShadaEntry *const hist) +static const void *shada_hist_iter(const void *const iter, const uint8_t history_type, + const bool zero, ShadaEntry *const hist) FUNC_ATTR_NONNULL_ARG(4) FUNC_ATTR_WARN_UNUSED_RESULT { histentry_T hist_he; @@ -927,9 +916,9 @@ static const void *shada_hist_iter(const void *const iter, .data = { .history_item = { .histtype = history_type, - .string = (char *) hist_he.hisstr, - .sep = (char) (history_type == HIST_SEARCH - ? (char) hist_he.hisstr[STRLEN(hist_he.hisstr) + 1] + .string = (char *)hist_he.hisstr, + .sep = (char)(history_type == HIST_SEARCH + ? (char)hist_he.hisstr[STRLEN(hist_he.hisstr) + 1] : 0), .additional_elements = hist_he.additional_elements, } @@ -954,8 +943,8 @@ static const void *shada_hist_iter(const void *const iter, /// be used. Must be true only if inserting /// entry from current Neovim history. /// @param[in] can_free_entry True if entry can be freed. -static void hms_insert(HistoryMergerState *const hms_p, const ShadaEntry entry, - const bool do_iter, const bool can_free_entry) +static void hms_insert(HistoryMergerState *const hms_p, const ShadaEntry entry, const bool do_iter, + const bool can_free_entry) FUNC_ATTR_NONNULL_ALL { if (do_iter) { @@ -1008,11 +997,8 @@ static void hms_insert(HistoryMergerState *const hms_p, const ShadaEntry entry, /// @param[in] do_merge Prepare structure for merging elements. /// @param[in] reading If true, then merger is reading history for use /// in Neovim. -static inline void hms_init(HistoryMergerState *const hms_p, - const uint8_t history_type, - const size_t num_elements, - const bool do_merge, - const bool reading) +static inline void hms_init(HistoryMergerState *const hms_p, const uint8_t history_type, + const size_t num_elements, const bool do_merge, const bool reading) FUNC_ATTR_NONNULL_ALL { hmll_init(&hms_p->hmll, num_elements); @@ -1027,8 +1013,7 @@ static inline void hms_init(HistoryMergerState *const hms_p, /// /// @param[in,out] hms_p Merger structure into which history should be /// inserted. -static inline void hms_insert_whole_neovim_history( - HistoryMergerState *const hms_p) +static inline void hms_insert_whole_neovim_history(HistoryMergerState *const hms_p) FUNC_ATTR_NONNULL_ALL { while (hms_p->last_hist_entry.type != kSDItemMissing) { @@ -1048,21 +1033,20 @@ static inline void hms_insert_whole_neovim_history( /// @param[out] new_hisidx New last history entry index. /// @param[out] new_hisnum Amount of history items in merger structure. static inline void hms_to_he_array(const HistoryMergerState *const hms_p, - histentry_T *const hist_array, - int *const new_hisidx, + histentry_T *const hist_array, int *const new_hisidx, int *const new_hisnum) FUNC_ATTR_NONNULL_ALL { histentry_T *hist = hist_array; HMLL_FORALL(&hms_p->hmll, cur_entry, { hist->timestamp = cur_entry->data.timestamp; - hist->hisnum = (int) (hist - hist_array) + 1; - hist->hisstr = (char_u *) cur_entry->data.data.history_item.string; + hist->hisnum = (int)(hist - hist_array) + 1; + hist->hisstr = (char_u *)cur_entry->data.data.history_item.string; hist->additional_elements = - cur_entry->data.data.history_item.additional_elements; + cur_entry->data.data.history_item.additional_elements; hist++; }) - *new_hisnum = (int) (hist - hist_array); + *new_hisnum = (int)(hist - hist_array); *new_hisidx = *new_hisnum - 1; } @@ -1083,7 +1067,7 @@ static inline void hms_dealloc(HistoryMergerState *const hms_p) /// /// @return for cycle header. Use `HMS_ITER(hms_p, cur_entry) {body}`. #define HMS_ITER(hms_p, cur_entry, code) \ - HMLL_FORALL(&((hms_p)->hmll), cur_entry, code) + HMLL_FORALL(&((hms_p)->hmll), cur_entry, code) /// Find buffer for given buffer name (cached) /// @@ -1091,8 +1075,7 @@ static inline void hms_dealloc(HistoryMergerState *const hms_p) /// @param[in] fname File name to find. /// /// @return Pointer to the buffer or NULL. -static buf_T *find_buffer(khash_t(fnamebufs) *const fname_bufs, - const char *const fname) +static buf_T *find_buffer(khash_t(fnamebufs) *const fname_bufs, const char *const fname) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL { int kh_ret; @@ -1123,7 +1106,7 @@ static inline bool marks_equal(const pos_T a, const pos_T b) entry, fname_cond, free_func, fin_func, \ idxadj_func, afterfree_func) \ do { \ - const int jl_len = (int) jumps_size; \ + const int jl_len = (int)jumps_size; \ int i; \ for (i = jl_len; i > 0; i--) { \ const jumps_type jl_entry = jumps[i - 1]; \ @@ -1140,17 +1123,17 @@ static inline bool marks_equal(const pos_T a, const pos_T b) free_func(jumps[0]); \ i--; \ if (i > 0) { \ - memmove(&jumps[0], &jumps[1], sizeof(jumps[1]) * (size_t) i); \ + memmove(&jumps[0], &jumps[1], sizeof(jumps[1]) * (size_t)i); \ } \ } else if (i != jl_len) { \ memmove(&jumps[i + 1], &jumps[i], \ - sizeof(jumps[0]) * (size_t) (jl_len - i)); \ + sizeof(jumps[0]) * (size_t)(jl_len - i)); \ } \ } else if (i == 0) { \ if (jl_len == JUMPLISTSIZE) { \ i = -1; \ } else if (jl_len > 0) { \ - memmove(&jumps[1], &jumps[0], sizeof(jumps[0]) * (size_t) jl_len); \ + memmove(&jumps[1], &jumps[0], sizeof(jumps[0]) * (size_t)jl_len); \ } \ } \ if (i != -1) { \ @@ -1177,8 +1160,8 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) const bool get_old_files = (flags & (kShaDaGetOldfiles | kShaDaForceit) && (force || tv_list_len(oldfiles_list) == 0)); const bool want_marks = flags & kShaDaWantMarks; - const unsigned srni_flags = (unsigned) ( - (flags & kShaDaWantInfo + const unsigned srni_flags = (unsigned)( + (flags & kShaDaWantInfo ? (kSDReadUndisableableData | kSDReadRegisters | kSDReadGlobalMarks @@ -1190,11 +1173,11 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) && ARGCOUNT == 0 ? kSDReadBufferList : 0)) - : 0) - | (want_marks && get_shada_parameter('\'') > 0 + : 0) + | (want_marks && get_shada_parameter('\'') > 0 ? kSDReadLocalMarks | kSDReadChanges : 0) - | (get_old_files + | (get_old_files ? kSDReadLocalMarks : 0)); if (srni_flags == 0) { @@ -1204,7 +1187,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) HistoryMergerState hms[HIST_COUNT]; if (srni_flags & kSDReadHistory) { for (uint8_t i = 0; i < HIST_COUNT; i++) { - hms_init(&hms[i], i, (size_t) p_hi, true, true); + hms_init(&hms[i], i, (size_t)p_hi, true, true); } } ShadaEntry cur_entry; @@ -1219,253 +1202,237 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) while ((srni_ret = shada_read_next_item(sd_reader, &cur_entry, srni_flags, 0)) != kSDReadStatusFinished) { switch (srni_ret) { - case kSDReadStatusSuccess: { - break; - } - case kSDReadStatusFinished: { - // Should be handled by the while condition. - abort(); - } - case kSDReadStatusNotShaDa: - case kSDReadStatusReadError: { - goto shada_read_main_cycle_end; - } - case kSDReadStatusMalformed: { - continue; - } + case kSDReadStatusSuccess: + break; + case kSDReadStatusFinished: + // Should be handled by the while condition. + abort(); + case kSDReadStatusNotShaDa: + case kSDReadStatusReadError: + goto shada_read_main_cycle_end; + case kSDReadStatusMalformed: + continue; } switch (cur_entry.type) { - case kSDItemMissing: { - abort(); - } - case kSDItemUnknown: { - break; - } - case kSDItemHeader: { - shada_free_shada_entry(&cur_entry); - break; - } - case kSDItemSearchPattern: { - if (!force) { - SearchPattern pat; - (cur_entry.data.search_pattern.is_substitute_pattern + case kSDItemMissing: + abort(); + case kSDItemUnknown: + break; + case kSDItemHeader: + shada_free_shada_entry(&cur_entry); + break; + case kSDItemSearchPattern: + if (!force) { + SearchPattern pat; + (cur_entry.data.search_pattern.is_substitute_pattern ? &get_substitute_pattern : &get_search_pattern)(&pat); - if (pat.pat != NULL && pat.timestamp >= cur_entry.timestamp) { - shada_free_shada_entry(&cur_entry); - break; - } + if (pat.pat != NULL && pat.timestamp >= cur_entry.timestamp) { + shada_free_shada_entry(&cur_entry); + break; } - (cur_entry.data.search_pattern.is_substitute_pattern + } + (cur_entry.data.search_pattern.is_substitute_pattern ? &set_substitute_pattern : &set_search_pattern)((SearchPattern) { - .magic = cur_entry.data.search_pattern.magic, - .no_scs = !cur_entry.data.search_pattern.smartcase, - .off = { - .dir = cur_entry.data.search_pattern.search_backward ? '?' : '/', - .line = cur_entry.data.search_pattern.has_line_offset, - .end = cur_entry.data.search_pattern.place_cursor_at_end, - .off = cur_entry.data.search_pattern.offset, - }, - .pat = (char_u *) cur_entry.data.search_pattern.pat, - .additional_data = cur_entry.data.search_pattern.additional_data, - .timestamp = cur_entry.timestamp, - }); - if (cur_entry.data.search_pattern.is_last_used) { - set_last_used_pattern( - cur_entry.data.search_pattern.is_substitute_pattern); - set_no_hlsearch(!cur_entry.data.search_pattern.highlighted); - } - // Do not free shada entry: its allocated memory was saved above. - break; - } - case kSDItemSubString: { - if (!force) { - SubReplacementString sub; - sub_get_replacement(&sub); - if (sub.sub != NULL && sub.timestamp >= cur_entry.timestamp) { - shada_free_shada_entry(&cur_entry); - break; - } - } - sub_set_replacement((SubReplacementString) { - .sub = cur_entry.data.sub_string.sub, - .timestamp = cur_entry.timestamp, - .additional_elements = cur_entry.data.sub_string.additional_elements, - }); - // Without using regtilde and without / &cpo flag previous substitute - // string is close to useless: you can only use it with :& or :~ and - // that’s all because s//~ is not available until the first call to - // regtilde. Vim was not calling this for some reason. - (void) regtilde(cur_entry.data.sub_string.sub, p_magic); - // Do not free shada entry: its allocated memory was saved above. - break; + .magic = cur_entry.data.search_pattern.magic, + .no_scs = !cur_entry.data.search_pattern.smartcase, + .off = { + .dir = cur_entry.data.search_pattern.search_backward ? '?' : '/', + .line = cur_entry.data.search_pattern.has_line_offset, + .end = cur_entry.data.search_pattern.place_cursor_at_end, + .off = cur_entry.data.search_pattern.offset, + }, + .pat = (char_u *)cur_entry.data.search_pattern.pat, + .additional_data = cur_entry.data.search_pattern.additional_data, + .timestamp = cur_entry.timestamp, + }); + if (cur_entry.data.search_pattern.is_last_used) { + set_last_used_pattern(cur_entry.data.search_pattern.is_substitute_pattern); + set_no_hlsearch(!cur_entry.data.search_pattern.highlighted); } - case kSDItemHistoryEntry: { - if (cur_entry.data.history_item.histtype >= HIST_COUNT) { + // Do not free shada entry: its allocated memory was saved above. + break; + case kSDItemSubString: + if (!force) { + SubReplacementString sub; + sub_get_replacement(&sub); + if (sub.sub != NULL && sub.timestamp >= cur_entry.timestamp) { shada_free_shada_entry(&cur_entry); break; } - hms_insert(hms + cur_entry.data.history_item.histtype, cur_entry, true, - true); - // Do not free shada entry: its allocated memory was saved above. + } + sub_set_replacement((SubReplacementString) { + .sub = cur_entry.data.sub_string.sub, + .timestamp = cur_entry.timestamp, + .additional_elements = cur_entry.data.sub_string.additional_elements, + }); + // Without using regtilde and without / &cpo flag previous substitute + // string is close to useless: you can only use it with :& or :~ and + // that’s all because s//~ is not available until the first call to + // regtilde. Vim was not calling this for some reason. + (void)regtilde(cur_entry.data.sub_string.sub, p_magic); + // Do not free shada entry: its allocated memory was saved above. + break; + case kSDItemHistoryEntry: + if (cur_entry.data.history_item.histtype >= HIST_COUNT) { + shada_free_shada_entry(&cur_entry); + break; + } + hms_insert(hms + cur_entry.data.history_item.histtype, cur_entry, true, + true); + // Do not free shada entry: its allocated memory was saved above. + break; + case kSDItemRegister: + if (cur_entry.data.reg.type != kMTCharWise + && cur_entry.data.reg.type != kMTLineWise + && cur_entry.data.reg.type != kMTBlockWise) { + shada_free_shada_entry(&cur_entry); break; } - case kSDItemRegister: { - if (cur_entry.data.reg.type != kMTCharWise - && cur_entry.data.reg.type != kMTLineWise - && cur_entry.data.reg.type != kMTBlockWise) { + if (!force) { + const yankreg_T *const reg = op_reg_get(cur_entry.data.reg.name); + if (reg == NULL || reg->timestamp >= cur_entry.timestamp) { shada_free_shada_entry(&cur_entry); break; } - if (!force) { - const yankreg_T *const reg = op_reg_get(cur_entry.data.reg.name); - if (reg == NULL || reg->timestamp >= cur_entry.timestamp) { - shada_free_shada_entry(&cur_entry); - break; - } - } - if (!op_reg_set(cur_entry.data.reg.name, (yankreg_T) { - .y_array = (char_u **)cur_entry.data.reg.contents, - .y_size = cur_entry.data.reg.contents_size, - .y_type = cur_entry.data.reg.type, - .y_width = (colnr_T) cur_entry.data.reg.width, - .timestamp = cur_entry.timestamp, - .additional_data = cur_entry.data.reg.additional_data, - }, cur_entry.data.reg.is_unnamed)) { - shada_free_shada_entry(&cur_entry); - } - // Do not free shada entry: its allocated memory was saved above. - break; } - case kSDItemVariable: { - var_set_global(cur_entry.data.global_var.name, - cur_entry.data.global_var.value); - cur_entry.data.global_var.value.v_type = VAR_UNKNOWN; + if (!op_reg_set(cur_entry.data.reg.name, (yankreg_T) { + .y_array = (char_u **)cur_entry.data.reg.contents, + .y_size = cur_entry.data.reg.contents_size, + .y_type = cur_entry.data.reg.type, + .y_width = (colnr_T)cur_entry.data.reg.width, + .timestamp = cur_entry.timestamp, + .additional_data = cur_entry.data.reg.additional_data, + }, cur_entry.data.reg.is_unnamed)) { shada_free_shada_entry(&cur_entry); - break; } - case kSDItemJump: - case kSDItemGlobalMark: { - buf_T *buf = find_buffer(&fname_bufs, cur_entry.data.filemark.fname); - if (buf != NULL) { - XFREE_CLEAR(cur_entry.data.filemark.fname); - } - xfmark_T fm = (xfmark_T) { - .fname = (char_u *) (buf == NULL + // Do not free shada entry: its allocated memory was saved above. + break; + case kSDItemVariable: + var_set_global(cur_entry.data.global_var.name, + cur_entry.data.global_var.value); + cur_entry.data.global_var.value.v_type = VAR_UNKNOWN; + shada_free_shada_entry(&cur_entry); + break; + case kSDItemJump: + case kSDItemGlobalMark: { + buf_T *buf = find_buffer(&fname_bufs, cur_entry.data.filemark.fname); + if (buf != NULL) { + XFREE_CLEAR(cur_entry.data.filemark.fname); + } + xfmark_T fm = (xfmark_T) { + .fname = (char_u *)(buf == NULL ? cur_entry.data.filemark.fname : NULL), - .fmark = { - .mark = cur_entry.data.filemark.mark, - .fnum = (buf == NULL ? 0 : buf->b_fnum), - .timestamp = cur_entry.timestamp, - .additional_data = cur_entry.data.filemark.additional_data, - }, - }; - if (cur_entry.type == kSDItemGlobalMark) { - if (!mark_set_global(cur_entry.data.filemark.name, fm, !force)) { - shada_free_shada_entry(&cur_entry); - break; - } - } else { + .fmark = { + .mark = cur_entry.data.filemark.mark, + .fnum = (buf == NULL ? 0 : buf->b_fnum), + .timestamp = cur_entry.timestamp, + .additional_data = cur_entry.data.filemark.additional_data, + }, + }; + if (cur_entry.type == kSDItemGlobalMark) { + if (!mark_set_global(cur_entry.data.filemark.name, fm, !force)) { + shada_free_shada_entry(&cur_entry); + break; + } + } else { #define SDE_TO_XFMARK(entry) fm #define ADJUST_IDX(i) \ - if (curwin->w_jumplistidx >= i \ - && curwin->w_jumplistidx + 1 <= curwin->w_jumplistlen) { \ - curwin->w_jumplistidx++; \ - } + if (curwin->w_jumplistidx >= i \ + && curwin->w_jumplistidx + 1 <= curwin->w_jumplistlen) { \ + curwin->w_jumplistidx++; \ + } #define DUMMY_AFTERFREE(entry) - MERGE_JUMPS(curwin->w_jumplistlen, curwin->w_jumplist, xfmark_T, - fmark.timestamp, fmark.mark, cur_entry, - (buf == NULL + MERGE_JUMPS(curwin->w_jumplistlen, curwin->w_jumplist, xfmark_T, + fmark.timestamp, fmark.mark, cur_entry, + (buf == NULL ? (jl_entry.fname != NULL && STRCMP(fm.fname, jl_entry.fname) == 0) : fm.fmark.fnum == jl_entry.fmark.fnum), - free_xfmark, SDE_TO_XFMARK, ADJUST_IDX, DUMMY_AFTERFREE); + free_xfmark, SDE_TO_XFMARK, ADJUST_IDX, DUMMY_AFTERFREE); #undef SDE_TO_XFMARK #undef ADJUST_IDX #undef DUMMY_AFTERFREE - } - // Do not free shada entry: its allocated memory was saved above. - break; } - case kSDItemBufferList: { - for (size_t i = 0; i < cur_entry.data.buffer_list.size; i++) { - char *const sfname = path_try_shorten_fname( - cur_entry.data.buffer_list.buffers[i].fname); - buf_T *const buf = buflist_new( - cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, - BLN_LISTED); - if (buf != NULL) { - RESET_FMARK(&buf->b_last_cursor, - cur_entry.data.buffer_list.buffers[i].pos, 0); - buflist_setfpos(buf, curwin, buf->b_last_cursor.mark.lnum, - buf->b_last_cursor.mark.col, false); - buf->additional_data = - cur_entry.data.buffer_list.buffers[i].additional_data; - cur_entry.data.buffer_list.buffers[i].additional_data = NULL; - } + // Do not free shada entry: its allocated memory was saved above. + break; + } + case kSDItemBufferList: + for (size_t i = 0; i < cur_entry.data.buffer_list.size; i++) { + char *const sfname = path_try_shorten_fname(cur_entry.data.buffer_list.buffers[i].fname); + buf_T *const buf = buflist_new(cur_entry.data.buffer_list.buffers[i].fname, sfname, 0, + BLN_LISTED); + if (buf != NULL) { + RESET_FMARK(&buf->b_last_cursor, + cur_entry.data.buffer_list.buffers[i].pos, 0); + buflist_setfpos(buf, curwin, buf->b_last_cursor.mark.lnum, + buf->b_last_cursor.mark.col, false); + buf->additional_data = + cur_entry.data.buffer_list.buffers[i].additional_data; + cur_entry.data.buffer_list.buffers[i].additional_data = NULL; } - shada_free_shada_entry(&cur_entry); - break; } - case kSDItemChange: - case kSDItemLocalMark: { - if (get_old_files && !in_strset(&oldfiles_set, - cur_entry.data.filemark.fname)) { - char *fname = cur_entry.data.filemark.fname; - if (want_marks) { - // Do not bother with allocating memory for the string if already - // allocated string from cur_entry can be used. It cannot be used if - // want_marks is set because this way it may be used for a mark. - fname = xstrdup(fname); - } - int kh_ret; - (void)kh_put(strset, &oldfiles_set, fname, &kh_ret); - tv_list_append_allocated_string(oldfiles_list, fname); - if (!want_marks) { - // Avoid free because this string was already used. - cur_entry.data.filemark.fname = NULL; - } + shada_free_shada_entry(&cur_entry); + break; + case kSDItemChange: + case kSDItemLocalMark: { + if (get_old_files && !in_strset(&oldfiles_set, + cur_entry.data.filemark.fname)) { + char *fname = cur_entry.data.filemark.fname; + if (want_marks) { + // Do not bother with allocating memory for the string if already + // allocated string from cur_entry can be used. It cannot be used if + // want_marks is set because this way it may be used for a mark. + fname = xstrdup(fname); } + int kh_ret; + (void)kh_put(strset, &oldfiles_set, fname, &kh_ret); + tv_list_append_allocated_string(oldfiles_list, fname); if (!want_marks) { - shada_free_shada_entry(&cur_entry); - break; + // Avoid free because this string was already used. + cur_entry.data.filemark.fname = NULL; } - buf_T *buf = find_buffer(&fname_bufs, cur_entry.data.filemark.fname); - if (buf == NULL) { + } + if (!want_marks) { + shada_free_shada_entry(&cur_entry); + break; + } + buf_T *buf = find_buffer(&fname_bufs, cur_entry.data.filemark.fname); + if (buf == NULL) { + shada_free_shada_entry(&cur_entry); + break; + } + const fmark_T fm = (fmark_T) { + .mark = cur_entry.data.filemark.mark, + .fnum = 0, + .timestamp = cur_entry.timestamp, + .additional_data = cur_entry.data.filemark.additional_data, + }; + if (cur_entry.type == kSDItemLocalMark) { + if (!mark_set_local(cur_entry.data.filemark.name, buf, fm, !force)) { shada_free_shada_entry(&cur_entry); break; } - const fmark_T fm = (fmark_T) { - .mark = cur_entry.data.filemark.mark, - .fnum = 0, - .timestamp = cur_entry.timestamp, - .additional_data = cur_entry.data.filemark.additional_data, - }; - if (cur_entry.type == kSDItemLocalMark) { - if (!mark_set_local(cur_entry.data.filemark.name, buf, fm, !force)) { - shada_free_shada_entry(&cur_entry); - break; - } - } else { - int kh_ret; - (void) kh_put(bufset, &cl_bufs, (uintptr_t) buf, &kh_ret); + } else { + int kh_ret; + (void)kh_put(bufset, &cl_bufs, (uintptr_t)buf, &kh_ret); #define SDE_TO_FMARK(entry) fm #define AFTERFREE(entry) (entry).data.filemark.fname = NULL #define DUMMY_IDX_ADJ(i) - MERGE_JUMPS(buf->b_changelistlen, buf->b_changelist, fmark_T, - timestamp, mark, cur_entry, true, - free_fmark, SDE_TO_FMARK, DUMMY_IDX_ADJ, AFTERFREE); + MERGE_JUMPS(buf->b_changelistlen, buf->b_changelist, fmark_T, + timestamp, mark, cur_entry, true, + free_fmark, SDE_TO_FMARK, DUMMY_IDX_ADJ, AFTERFREE); #undef SDE_TO_FMARK #undef AFTERFREE #undef DUMMY_IDX_ADJ - } - // Do not free shada entry: except for fname, its allocated memory (i.e. - // additional_data attribute contents if non-NULL) was saved above. - xfree(cur_entry.data.filemark.fname); - break; } + // Do not free shada entry: except for fname, its allocated memory (i.e. + // additional_data attribute contents if non-NULL) was saved above. + xfree(cur_entry.data.filemark.fname); + break; + } } } shada_read_main_cycle_end: @@ -1490,7 +1457,7 @@ shada_read_main_cycle_end: } if (cl_bufs.n_occupied) { FOR_ALL_TAB_WINDOWS(tp, wp) { - (void) tp; + (void)tp; if (in_bufset(&cl_bufs, wp->w_buffer)) { wp->w_changelistidx = wp->w_buffer->b_changelistlen; } @@ -1499,7 +1466,7 @@ shada_read_main_cycle_end: kh_dealloc(bufset, &cl_bufs); const char *key; kh_foreach_key(&fname_bufs, key, { - xfree((void *) key); + xfree((void *)key); }) kh_dealloc(fnamebufs, &fname_bufs); kh_dealloc(strset, &oldfiles_set); @@ -1544,33 +1511,33 @@ static char *shada_filename(const char *file) // If shell is not performing them then they should be done in main.c // where arguments are parsed, *not here*. expand_env((char_u *)file, &(NameBuff[0]), MAXPATHL); - file = (const char *) &(NameBuff[0]); + file = (const char *)&(NameBuff[0]); } } return xstrdup(file); } #define PACK_STATIC_STR(s) \ - do { \ - msgpack_pack_str(spacker, sizeof(s) - 1); \ - msgpack_pack_str_body(spacker, s, sizeof(s) - 1); \ - } while (0) + do { \ + msgpack_pack_str(spacker, sizeof(s) - 1); \ + msgpack_pack_str_body(spacker, s, sizeof(s) - 1); \ + } while (0) #define PACK_STRING(s) \ - do { \ - const String s_ = (s); \ - msgpack_pack_str(spacker, s_.size); \ - if (s_.size) { \ - msgpack_pack_str_body(spacker, s_.data, s_.size); \ - } \ - } while (0) + do { \ + const String s_ = (s); \ + msgpack_pack_str(spacker, s_.size); \ + if (s_.size) { \ + msgpack_pack_str_body(spacker, s_.data, s_.size); \ + } \ + } while (0) #define PACK_BIN(s) \ - do { \ - const String s_ = (s); \ - msgpack_pack_bin(spacker, s_.size); \ - if (s_.size > 0) { \ - msgpack_pack_bin_body(spacker, s_.data, s_.size); \ - } \ - } while (0) + do { \ + const String s_ = (s); \ + msgpack_pack_bin(spacker, s_.size); \ + if (s_.size > 0) { \ + msgpack_pack_bin_body(spacker, s_.data, s_.size); \ + } \ + } while (0) /// Write single ShaDa entry /// @@ -1580,8 +1547,7 @@ static char *shada_filename(const char *file) /// restrictions. /// /// @return kSDWriteSuccessfull, kSDWriteFailed or kSDWriteIgnError. -static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, - ShadaEntry entry, +static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, ShadaEntry entry, const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL { @@ -1625,244 +1591,239 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, #define CHECK_DEFAULT(entry, attr) \ (sd_default_values[entry.type].data.attr == entry.data.attr) #define ONE_IF_NOT_DEFAULT(entry, attr) \ - ((size_t) (!CHECK_DEFAULT(entry, attr))) + ((size_t)(!CHECK_DEFAULT(entry, attr))) switch (entry.type) { - case kSDItemMissing: { - abort(); + case kSDItemMissing: + abort(); + case kSDItemUnknown: + if (spacker->callback(spacker->data, entry.data.unknown_item.contents, + (unsigned)entry.data.unknown_item.size) == -1) { + goto shada_pack_entry_error; } - case kSDItemUnknown: { - if (spacker->callback(spacker->data, entry.data.unknown_item.contents, - (unsigned) entry.data.unknown_item.size) == -1) { - goto shada_pack_entry_error; - } - break; + break; + case kSDItemHistoryEntry: { + const bool is_hist_search = + entry.data.history_item.histtype == HIST_SEARCH; + const size_t arr_size = 2 + (size_t)is_hist_search + (size_t)( + tv_list_len(entry.data. + history_item. + additional_elements)); + msgpack_pack_array(spacker, arr_size); + msgpack_pack_uint8(spacker, entry.data.history_item.histtype); + PACK_BIN(cstr_as_string(entry.data.history_item.string)); + if (is_hist_search) { + msgpack_pack_uint8(spacker, (uint8_t)entry.data.history_item.sep); } - case kSDItemHistoryEntry: { - const bool is_hist_search = - entry.data.history_item.histtype == HIST_SEARCH; - const size_t arr_size = 2 + (size_t)is_hist_search + (size_t)( - tv_list_len(entry.data.history_item.additional_elements)); - msgpack_pack_array(spacker, arr_size); - msgpack_pack_uint8(spacker, entry.data.history_item.histtype); - PACK_BIN(cstr_as_string(entry.data.history_item.string)); - if (is_hist_search) { - msgpack_pack_uint8(spacker, (uint8_t)entry.data.history_item.sep); - } - DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements, - "history entry item"); - break; - } - case kSDItemVariable: { - if (entry.data.global_var.value.v_type == VAR_TYPE_BLOB) { - // Strings and Blobs both pack as msgpack BINs; differentiate them by - // storing an additional VAR_TYPE_BLOB element alongside Blobs - list_T *const list = tv_list_alloc(1); - tv_list_append_number(list, VAR_TYPE_BLOB); - entry.data.global_var.additional_elements = list; - } - const size_t arr_size = 2 + (size_t)( - tv_list_len(entry.data.global_var.additional_elements)); - msgpack_pack_array(spacker, arr_size); - const String varname = cstr_as_string(entry.data.global_var.name); - PACK_BIN(varname); - char vardesc[256] = "variable g:"; - memcpy(&vardesc[sizeof("variable g:") - 1], varname.data, - varname.size + 1); - if (encode_vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc) - == FAIL) { - ret = kSDWriteIgnError; - EMSG2(_(WERR "Failed to write variable %s"), - entry.data.global_var.name); - goto shada_pack_entry_error; - } - DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements, - "variable item"); - break; + DUMP_ADDITIONAL_ELEMENTS(entry.data.history_item.additional_elements, + "history entry item"); + break; + } + case kSDItemVariable: { + if (entry.data.global_var.value.v_type == VAR_TYPE_BLOB) { + // Strings and Blobs both pack as msgpack BINs; differentiate them by + // storing an additional VAR_TYPE_BLOB element alongside Blobs + list_T *const list = tv_list_alloc(1); + tv_list_append_number(list, VAR_TYPE_BLOB); + entry.data.global_var.additional_elements = list; } - case kSDItemSubString: { - const size_t arr_size = 1 + (size_t)( - tv_list_len(entry.data.sub_string.additional_elements)); - msgpack_pack_array(spacker, arr_size); - PACK_BIN(cstr_as_string(entry.data.sub_string.sub)); - DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements, - "sub string item"); - break; + const size_t arr_size = 2 + (size_t)( + tv_list_len(entry.data.global_var.additional_elements)); + msgpack_pack_array(spacker, arr_size); + const String varname = cstr_as_string(entry.data.global_var.name); + PACK_BIN(varname); + char vardesc[256] = "variable g:"; + memcpy(&vardesc[sizeof("variable g:") - 1], varname.data, + varname.size + 1); + if (encode_vim_to_msgpack(spacker, &entry.data.global_var.value, vardesc) + == FAIL) { + ret = kSDWriteIgnError; + EMSG2(_(WERR "Failed to write variable %s"), + entry.data.global_var.name); + goto shada_pack_entry_error; } - case kSDItemSearchPattern: { - const size_t map_size = (size_t) ( - 1 // Search pattern is always present - + ONE_IF_NOT_DEFAULT(entry, search_pattern.magic) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.is_last_used) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.smartcase) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.has_line_offset) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.place_cursor_at_end) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.is_substitute_pattern) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.highlighted) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.offset) - + ONE_IF_NOT_DEFAULT(entry, search_pattern.search_backward) - // finally, additional data: - + (size_t) ( - entry.data.search_pattern.additional_data + DUMP_ADDITIONAL_ELEMENTS(entry.data.global_var.additional_elements, + "variable item"); + break; + } + case kSDItemSubString: { + const size_t arr_size = 1 + (size_t)( + tv_list_len(entry.data.sub_string.additional_elements)); + msgpack_pack_array(spacker, arr_size); + PACK_BIN(cstr_as_string(entry.data.sub_string.sub)); + DUMP_ADDITIONAL_ELEMENTS(entry.data.sub_string.additional_elements, + "sub string item"); + break; + } + case kSDItemSearchPattern: { + const size_t map_size = (size_t)( + 1 // Search pattern is always present + + ONE_IF_NOT_DEFAULT(entry, search_pattern.magic) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.is_last_used) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.smartcase) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.has_line_offset) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.place_cursor_at_end) + + ONE_IF_NOT_DEFAULT(entry, + search_pattern.is_substitute_pattern) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.highlighted) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.offset) + + ONE_IF_NOT_DEFAULT(entry, search_pattern.search_backward) + // finally, additional data: + + (size_t)( + entry.data.search_pattern.additional_data ? entry.data.search_pattern.additional_data->dv_hashtab.ht_used : 0)); - msgpack_pack_map(spacker, map_size); - PACK_STATIC_STR(SEARCH_KEY_PAT); - PACK_BIN(cstr_as_string(entry.data.search_pattern.pat)); + msgpack_pack_map(spacker, map_size); + PACK_STATIC_STR(SEARCH_KEY_PAT); + PACK_BIN(cstr_as_string(entry.data.search_pattern.pat)); #define PACK_BOOL(entry, name, attr) \ - do { \ - if (!CHECK_DEFAULT(entry, search_pattern.attr)) { \ - PACK_STATIC_STR(name); \ - if (sd_default_values[entry.type].data.search_pattern.attr) { \ - msgpack_pack_false(spacker); \ - } else { \ - msgpack_pack_true(spacker); \ - } \ - } \ - } while (0) - PACK_BOOL(entry, SEARCH_KEY_MAGIC, magic); - PACK_BOOL(entry, SEARCH_KEY_IS_LAST_USED, is_last_used); - PACK_BOOL(entry, SEARCH_KEY_SMARTCASE, smartcase); - PACK_BOOL(entry, SEARCH_KEY_HAS_LINE_OFFSET, has_line_offset); - PACK_BOOL(entry, SEARCH_KEY_PLACE_CURSOR_AT_END, place_cursor_at_end); - PACK_BOOL(entry, SEARCH_KEY_IS_SUBSTITUTE_PATTERN, is_substitute_pattern); - PACK_BOOL(entry, SEARCH_KEY_HIGHLIGHTED, highlighted); - PACK_BOOL(entry, SEARCH_KEY_BACKWARD, search_backward); - if (!CHECK_DEFAULT(entry, search_pattern.offset)) { - PACK_STATIC_STR(SEARCH_KEY_OFFSET); - msgpack_pack_int64(spacker, entry.data.search_pattern.offset); - } -#undef PACK_BOOL - DUMP_ADDITIONAL_DATA(entry.data.search_pattern.additional_data, - "search pattern item"); - break; + do { \ + if (!CHECK_DEFAULT(entry, search_pattern.attr)) { \ + PACK_STATIC_STR(name); \ + if (sd_default_values[entry.type].data.search_pattern.attr) { \ + msgpack_pack_false(spacker); \ + } else { \ + msgpack_pack_true(spacker); \ + } \ + } \ + } while (0) + PACK_BOOL(entry, SEARCH_KEY_MAGIC, magic); + PACK_BOOL(entry, SEARCH_KEY_IS_LAST_USED, is_last_used); + PACK_BOOL(entry, SEARCH_KEY_SMARTCASE, smartcase); + PACK_BOOL(entry, SEARCH_KEY_HAS_LINE_OFFSET, has_line_offset); + PACK_BOOL(entry, SEARCH_KEY_PLACE_CURSOR_AT_END, place_cursor_at_end); + PACK_BOOL(entry, SEARCH_KEY_IS_SUBSTITUTE_PATTERN, is_substitute_pattern); + PACK_BOOL(entry, SEARCH_KEY_HIGHLIGHTED, highlighted); + PACK_BOOL(entry, SEARCH_KEY_BACKWARD, search_backward); + if (!CHECK_DEFAULT(entry, search_pattern.offset)) { + PACK_STATIC_STR(SEARCH_KEY_OFFSET); + msgpack_pack_int64(spacker, entry.data.search_pattern.offset); } - case kSDItemChange: - case kSDItemGlobalMark: - case kSDItemLocalMark: - case kSDItemJump: { - const size_t map_size = (size_t) ( - 1 // File name - + ONE_IF_NOT_DEFAULT(entry, filemark.mark.lnum) - + ONE_IF_NOT_DEFAULT(entry, filemark.mark.col) - + ONE_IF_NOT_DEFAULT(entry, filemark.name) - // Additional entries, if any: - + (size_t) ( - entry.data.filemark.additional_data == NULL +#undef PACK_BOOL + DUMP_ADDITIONAL_DATA(entry.data.search_pattern.additional_data, + "search pattern item"); + break; + } + case kSDItemChange: + case kSDItemGlobalMark: + case kSDItemLocalMark: + case kSDItemJump: { + const size_t map_size = (size_t)( + 1 // File name + + ONE_IF_NOT_DEFAULT(entry, filemark.mark.lnum) + + ONE_IF_NOT_DEFAULT(entry, filemark.mark.col) + + ONE_IF_NOT_DEFAULT(entry, filemark.name) + // Additional entries, if any: + + (size_t)( + entry.data.filemark.additional_data == NULL ? 0 : entry.data.filemark.additional_data->dv_hashtab.ht_used)); - msgpack_pack_map(spacker, map_size); - PACK_STATIC_STR(KEY_FILE); - PACK_BIN(cstr_as_string(entry.data.filemark.fname)); - if (!CHECK_DEFAULT(entry, filemark.mark.lnum)) { - PACK_STATIC_STR(KEY_LNUM); - msgpack_pack_long(spacker, entry.data.filemark.mark.lnum); - } - if (!CHECK_DEFAULT(entry, filemark.mark.col)) { - PACK_STATIC_STR(KEY_COL); - msgpack_pack_long(spacker, entry.data.filemark.mark.col); - } - assert(entry.type == kSDItemJump || entry.type == kSDItemChange + msgpack_pack_map(spacker, map_size); + PACK_STATIC_STR(KEY_FILE); + PACK_BIN(cstr_as_string(entry.data.filemark.fname)); + if (!CHECK_DEFAULT(entry, filemark.mark.lnum)) { + PACK_STATIC_STR(KEY_LNUM); + msgpack_pack_long(spacker, entry.data.filemark.mark.lnum); + } + if (!CHECK_DEFAULT(entry, filemark.mark.col)) { + PACK_STATIC_STR(KEY_COL); + msgpack_pack_long(spacker, entry.data.filemark.mark.col); + } + assert(entry.type == kSDItemJump || entry.type == kSDItemChange ? CHECK_DEFAULT(entry, filemark.name) : true); - if (!CHECK_DEFAULT(entry, filemark.name)) { - PACK_STATIC_STR(KEY_NAME_CHAR); - msgpack_pack_uint8(spacker, (uint8_t) entry.data.filemark.name); - } - DUMP_ADDITIONAL_DATA(entry.data.filemark.additional_data, - "mark (change, jump, global or local) item"); - break; + if (!CHECK_DEFAULT(entry, filemark.name)) { + PACK_STATIC_STR(KEY_NAME_CHAR); + msgpack_pack_uint8(spacker, (uint8_t)entry.data.filemark.name); } - case kSDItemRegister: { - const size_t map_size = (size_t) ( - 2 // Register contents and name - + ONE_IF_NOT_DEFAULT(entry, reg.type) - + ONE_IF_NOT_DEFAULT(entry, reg.width) - + ONE_IF_NOT_DEFAULT(entry, reg.is_unnamed) - // Additional entries, if any: - + (size_t) (entry.data.reg.additional_data == NULL + DUMP_ADDITIONAL_DATA(entry.data.filemark.additional_data, + "mark (change, jump, global or local) item"); + break; + } + case kSDItemRegister: { + const size_t map_size = (size_t)( + 2 // Register contents and name + + ONE_IF_NOT_DEFAULT(entry, reg.type) + + ONE_IF_NOT_DEFAULT(entry, reg.width) + + ONE_IF_NOT_DEFAULT(entry, reg.is_unnamed) + // Additional entries, if any: + + (size_t)(entry.data.reg.additional_data == NULL ? 0 : entry.data.reg.additional_data->dv_hashtab.ht_used)); - msgpack_pack_map(spacker, map_size); - PACK_STATIC_STR(REG_KEY_CONTENTS); - msgpack_pack_array(spacker, entry.data.reg.contents_size); - for (size_t i = 0; i < entry.data.reg.contents_size; i++) { - PACK_BIN(cstr_as_string(entry.data.reg.contents[i])); - } - PACK_STATIC_STR(KEY_NAME_CHAR); - msgpack_pack_char(spacker, entry.data.reg.name); - if (!CHECK_DEFAULT(entry, reg.type)) { - PACK_STATIC_STR(REG_KEY_TYPE); - msgpack_pack_uint8(spacker, (uint8_t)entry.data.reg.type); - } - if (!CHECK_DEFAULT(entry, reg.width)) { - PACK_STATIC_STR(REG_KEY_WIDTH); - msgpack_pack_uint64(spacker, (uint64_t) entry.data.reg.width); - } - if (!CHECK_DEFAULT(entry, reg.is_unnamed)) { - PACK_STATIC_STR(REG_KEY_UNNAMED); - if (entry.data.reg.is_unnamed) { - msgpack_pack_true(spacker); - } else { - msgpack_pack_false(spacker); - } + msgpack_pack_map(spacker, map_size); + PACK_STATIC_STR(REG_KEY_CONTENTS); + msgpack_pack_array(spacker, entry.data.reg.contents_size); + for (size_t i = 0; i < entry.data.reg.contents_size; i++) { + PACK_BIN(cstr_as_string(entry.data.reg.contents[i])); + } + PACK_STATIC_STR(KEY_NAME_CHAR); + msgpack_pack_char(spacker, entry.data.reg.name); + if (!CHECK_DEFAULT(entry, reg.type)) { + PACK_STATIC_STR(REG_KEY_TYPE); + msgpack_pack_uint8(spacker, (uint8_t)entry.data.reg.type); + } + if (!CHECK_DEFAULT(entry, reg.width)) { + PACK_STATIC_STR(REG_KEY_WIDTH); + msgpack_pack_uint64(spacker, (uint64_t)entry.data.reg.width); + } + if (!CHECK_DEFAULT(entry, reg.is_unnamed)) { + PACK_STATIC_STR(REG_KEY_UNNAMED); + if (entry.data.reg.is_unnamed) { + msgpack_pack_true(spacker); + } else { + msgpack_pack_false(spacker); } - DUMP_ADDITIONAL_DATA(entry.data.reg.additional_data, "register item"); - break; } - case kSDItemBufferList: { - msgpack_pack_array(spacker, entry.data.buffer_list.size); - for (size_t i = 0; i < entry.data.buffer_list.size; i++) { - const size_t map_size = (size_t) ( - 1 // Buffer name - + (size_t) (entry.data.buffer_list.buffers[i].pos.lnum - != default_pos.lnum) - + (size_t) (entry.data.buffer_list.buffers[i].pos.col - != default_pos.col) - // Additional entries, if any: - + (size_t) ( - entry.data.buffer_list.buffers[i].additional_data == NULL + DUMP_ADDITIONAL_DATA(entry.data.reg.additional_data, "register item"); + break; + } + case kSDItemBufferList: + msgpack_pack_array(spacker, entry.data.buffer_list.size); + for (size_t i = 0; i < entry.data.buffer_list.size; i++) { + const size_t map_size = (size_t)( + 1 // Buffer name + + (size_t)(entry.data.buffer_list.buffers[i].pos.lnum + != default_pos.lnum) + + (size_t)(entry.data.buffer_list.buffers[i].pos.col + != default_pos.col) + // Additional entries, if any: + + (size_t)( + entry.data.buffer_list.buffers[i].additional_data + == NULL ? 0 : (entry.data.buffer_list.buffers[i].additional_data ->dv_hashtab.ht_used))); - msgpack_pack_map(spacker, map_size); - PACK_STATIC_STR(KEY_FILE); - PACK_BIN(cstr_as_string(entry.data.buffer_list.buffers[i].fname)); - if (entry.data.buffer_list.buffers[i].pos.lnum != 1) { - PACK_STATIC_STR(KEY_LNUM); - msgpack_pack_uint64( - spacker, (uint64_t) entry.data.buffer_list.buffers[i].pos.lnum); - } - if (entry.data.buffer_list.buffers[i].pos.col != 0) { - PACK_STATIC_STR(KEY_COL); - msgpack_pack_uint64( - spacker, (uint64_t) entry.data.buffer_list.buffers[i].pos.col); - } - DUMP_ADDITIONAL_DATA(entry.data.buffer_list.buffers[i].additional_data, - "buffer list subitem"); + msgpack_pack_map(spacker, map_size); + PACK_STATIC_STR(KEY_FILE); + PACK_BIN(cstr_as_string(entry.data.buffer_list.buffers[i].fname)); + if (entry.data.buffer_list.buffers[i].pos.lnum != 1) { + PACK_STATIC_STR(KEY_LNUM); + msgpack_pack_uint64(spacker, (uint64_t)entry.data.buffer_list.buffers[i].pos.lnum); } - break; + if (entry.data.buffer_list.buffers[i].pos.col != 0) { + PACK_STATIC_STR(KEY_COL); + msgpack_pack_uint64(spacker, (uint64_t)entry.data.buffer_list.buffers[i].pos.col); + } + DUMP_ADDITIONAL_DATA(entry.data.buffer_list.buffers[i].additional_data, + "buffer list subitem"); } - case kSDItemHeader: { - msgpack_pack_map(spacker, entry.data.header.size); - for (size_t i = 0; i < entry.data.header.size; i++) { - PACK_STRING(entry.data.header.items[i].key); - const Object obj = entry.data.header.items[i].value; - switch (obj.type) { - case kObjectTypeString: { - PACK_BIN(obj.data.string); - break; - } - case kObjectTypeInteger: { - msgpack_pack_int64(spacker, (int64_t) obj.data.integer); - break; - } - default: { - abort(); - } - } + break; + case kSDItemHeader: + msgpack_pack_map(spacker, entry.data.header.size); + for (size_t i = 0; i < entry.data.header.size; i++) { + PACK_STRING(entry.data.header.items[i].key); + const Object obj = entry.data.header.items[i].value; + switch (obj.type) { + case kObjectTypeString: + PACK_BIN(obj.data.string); + break; + case kObjectTypeInteger: + msgpack_pack_int64(spacker, (int64_t)obj.data.integer); + break; + default: + abort(); } - break; } + break; } #undef CHECK_DEFAULT #undef ONE_IF_NOT_DEFAULT @@ -1872,17 +1833,17 @@ static ShaDaWriteResult shada_pack_entry(msgpack_packer *const packer, goto shada_pack_entry_error; } } else { - if (msgpack_pack_uint64(packer, (uint64_t) entry.type) == -1) { + if (msgpack_pack_uint64(packer, (uint64_t)entry.type) == -1) { goto shada_pack_entry_error; } } - if (msgpack_pack_uint64(packer, (uint64_t) entry.timestamp) == -1) { + if (msgpack_pack_uint64(packer, (uint64_t)entry.timestamp) == -1) { goto shada_pack_entry_error; } if (sbuf.size > 0) { - if ((msgpack_pack_uint64(packer, (uint64_t) sbuf.size) == -1) + if ((msgpack_pack_uint64(packer, (uint64_t)sbuf.size) == -1) || (packer->callback(packer->data, sbuf.data, - (unsigned) sbuf.size) == -1)) { + (unsigned)sbuf.size) == -1)) { goto shada_pack_entry_error; } } @@ -1905,9 +1866,9 @@ shada_pack_entry_error: /// @param[in] entry Entry written. /// @param[in] max_kbyte Maximum size of an item in KiB. Zero means no /// restrictions. -static inline ShaDaWriteResult shada_pack_pfreed_entry( - msgpack_packer *const packer, PossiblyFreedShadaEntry entry, - const size_t max_kbyte) +static inline ShaDaWriteResult shada_pack_pfreed_entry(msgpack_packer *const packer, + PossiblyFreedShadaEntry entry, + const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE { ShaDaWriteResult ret = kSDWriteSuccessfull; @@ -1945,9 +1906,10 @@ static int compare_file_marks(const void *a, const void *b) /// /// @return kSDReadStatusNotShaDa, kSDReadStatusReadError or /// kSDReadStatusSuccess. -static inline ShaDaReadResult shada_parse_msgpack( - ShaDaReadDef *const sd_reader, const size_t length, - msgpack_unpacked *ret_unpacked, char **const ret_buf) +static inline ShaDaReadResult shada_parse_msgpack(ShaDaReadDef *const sd_reader, + const size_t length, + msgpack_unpacked *ret_unpacked, + char **const ret_buf) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ARG(1) { const uintmax_t initial_fpos = sd_reader->fpos; @@ -1964,47 +1926,42 @@ shada_parse_msgpack_read_next: {} msgpack_unpacked unpacked; msgpack_unpacked_init(&unpacked); const msgpack_unpack_return result = - msgpack_unpack_next(&unpacked, buf, length, &off); + msgpack_unpack_next(&unpacked, buf, length, &off); ShaDaReadResult ret = kSDReadStatusSuccess; switch (result) { - case MSGPACK_UNPACK_SUCCESS: { - if (off < length) { - goto shada_parse_msgpack_extra_bytes; - } - break; - } - case MSGPACK_UNPACK_PARSE_ERROR: { - emsgf(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error " - "at position %" PRIu64), - (uint64_t) initial_fpos); - ret = kSDReadStatusNotShaDa; - break; + case MSGPACK_UNPACK_SUCCESS: + if (off < length) { + goto shada_parse_msgpack_extra_bytes; } - case MSGPACK_UNPACK_NOMEM_ERROR: { - if (!did_try_to_free) { - did_try_to_free = true; - try_to_free_memory(); - goto shada_parse_msgpack_read_next; - } - EMSG(_(e_outofmem)); - ret = kSDReadStatusReadError; - break; - } - case MSGPACK_UNPACK_CONTINUE: { - emsgf(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string " - "at position %" PRIu64), - (uint64_t) initial_fpos); - ret = kSDReadStatusNotShaDa; - break; + break; + case MSGPACK_UNPACK_PARSE_ERROR: + emsgf(_(RCERR "Failed to parse ShaDa file due to a msgpack parser error " + "at position %" PRIu64), + (uint64_t)initial_fpos); + ret = kSDReadStatusNotShaDa; + break; + case MSGPACK_UNPACK_NOMEM_ERROR: + if (!did_try_to_free) { + did_try_to_free = true; + try_to_free_memory(); + goto shada_parse_msgpack_read_next; } - case MSGPACK_UNPACK_EXTRA_BYTES: { + EMSG(_(e_outofmem)); + ret = kSDReadStatusReadError; + break; + case MSGPACK_UNPACK_CONTINUE: + emsgf(_(RCERR "Failed to parse ShaDa file: incomplete msgpack string " + "at position %" PRIu64), + (uint64_t)initial_fpos); + ret = kSDReadStatusNotShaDa; + break; + case MSGPACK_UNPACK_EXTRA_BYTES: shada_parse_msgpack_extra_bytes: - emsgf(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string " - "at position %" PRIu64), - (uint64_t) initial_fpos); - ret = kSDReadStatusNotShaDa; - break; - } + emsgf(_(RCERR "Failed to parse ShaDa file: extra bytes in msgpack string " + "at position %" PRIu64), + (uint64_t)initial_fpos); + ret = kSDReadStatusNotShaDa; + break; } if (ret_buf != NULL && ret == kSDReadStatusSuccess) { if (ret_unpacked == NULL) { @@ -2034,81 +1991,67 @@ static const char *shada_format_entry(const ShadaEntry entry) vim_snprintf(S_LEN(ret), "%s", "[ ] ts=%" PRIu64 " "); // ^ Space for `can_free_entry` switch (entry.type) { - case kSDItemMissing: { - vim_snprintf_add(S_LEN(ret), "Missing"); - break; - } - case kSDItemHeader: { - vim_snprintf_add(S_LEN(ret), "Header { TODO }"); - break; - } - case kSDItemBufferList: { - vim_snprintf_add(S_LEN(ret), "BufferList { TODO }"); - break; - } - case kSDItemUnknown: { - vim_snprintf_add(S_LEN(ret), "Unknown { TODO }"); - break; - } - case kSDItemSearchPattern: { - vim_snprintf_add(S_LEN(ret), "SearchPattern { TODO }"); - break; - } - case kSDItemSubString: { - vim_snprintf_add(S_LEN(ret), "SubString { TODO }"); - break; - } - case kSDItemHistoryEntry: { - vim_snprintf_add(S_LEN(ret), "HistoryEntry { TODO }"); - break; - } - case kSDItemRegister: { - vim_snprintf_add(S_LEN(ret), "Register { TODO }"); - break; - } - case kSDItemVariable: { - vim_snprintf_add(S_LEN(ret), "Variable { TODO }"); - break; - } + case kSDItemMissing: + vim_snprintf_add(S_LEN(ret), "Missing"); + break; + case kSDItemHeader: + vim_snprintf_add(S_LEN(ret), "Header { TODO }"); + break; + case kSDItemBufferList: + vim_snprintf_add(S_LEN(ret), "BufferList { TODO }"); + break; + case kSDItemUnknown: + vim_snprintf_add(S_LEN(ret), "Unknown { TODO }"); + break; + case kSDItemSearchPattern: + vim_snprintf_add(S_LEN(ret), "SearchPattern { TODO }"); + break; + case kSDItemSubString: + vim_snprintf_add(S_LEN(ret), "SubString { TODO }"); + break; + case kSDItemHistoryEntry: + vim_snprintf_add(S_LEN(ret), "HistoryEntry { TODO }"); + break; + case kSDItemRegister: + vim_snprintf_add(S_LEN(ret), "Register { TODO }"); + break; + case kSDItemVariable: + vim_snprintf_add(S_LEN(ret), "Variable { TODO }"); + break; #define FORMAT_MARK_ENTRY(entry_name, name_fmt, name_fmt_arg) \ - do { \ - typval_T ad_tv = { \ - .v_type = VAR_DICT, \ - .vval.v_dict = entry.data.filemark.additional_data \ - }; \ - size_t ad_len; \ - char *const ad = encode_tv2string(&ad_tv, &ad_len); \ - vim_snprintf_add( \ - S_LEN(ret), \ - entry_name " {" name_fmt " file=[%zu]\"%.512s\", " \ - "pos={l=%" PRIdLINENR ",c=%" PRIdCOLNR ",a=%" PRIdCOLNR "}, " \ - "ad={%p:[%zu]%.64s} }", \ - name_fmt_arg, \ - strlen(entry.data.filemark.fname), \ - entry.data.filemark.fname, \ - entry.data.filemark.mark.lnum, \ - entry.data.filemark.mark.col, \ - entry.data.filemark.mark.coladd, \ - (void *)entry.data.filemark.additional_data, \ - ad_len, \ - ad); \ - } while (0) - case kSDItemGlobalMark: { - FORMAT_MARK_ENTRY("GlobalMark", " name='%c',", entry.data.filemark.name); - break; - } - case kSDItemChange: { - FORMAT_MARK_ENTRY("Change", "%s", ""); - break; - } - case kSDItemLocalMark: { - FORMAT_MARK_ENTRY("LocalMark", " name='%c',", entry.data.filemark.name); - break; - } - case kSDItemJump: { - FORMAT_MARK_ENTRY("Jump", "%s", ""); - break; - } + do { \ + typval_T ad_tv = { \ + .v_type = VAR_DICT, \ + .vval.v_dict = entry.data.filemark.additional_data \ + }; \ + size_t ad_len; \ + char *const ad = encode_tv2string(&ad_tv, &ad_len); \ + vim_snprintf_add(S_LEN(ret), \ + entry_name " {" name_fmt " file=[%zu]\"%.512s\", " \ + "pos={l=%" PRIdLINENR ",c=%" PRIdCOLNR ",a=%" PRIdCOLNR "}, " \ + "ad={%p:[%zu]%.64s} }", \ + name_fmt_arg, \ + strlen(entry.data.filemark.fname), \ + entry.data.filemark.fname, \ + entry.data.filemark.mark.lnum, \ + entry.data.filemark.mark.col, \ + entry.data.filemark.mark.coladd, \ + (void *)entry.data.filemark.additional_data, \ + ad_len, \ + ad); \ + } while (0) + case kSDItemGlobalMark: + FORMAT_MARK_ENTRY("GlobalMark", " name='%c',", entry.data.filemark.name); + break; + case kSDItemChange: + FORMAT_MARK_ENTRY("Change", "%s", ""); + break; + case kSDItemLocalMark: + FORMAT_MARK_ENTRY("LocalMark", " name='%c',", entry.data.filemark.name); + break; + case kSDItemJump: + FORMAT_MARK_ENTRY("Jump", "%s", ""); + break; #undef FORMAT_MARK_ENTRY } return ret; @@ -2119,8 +2062,7 @@ static const char *shada_format_entry(const ShadaEntry entry) /// @param[in] entry ShaDa entry to format. /// /// @return string representing ShaDa entry in a static buffer. -static const char *shada_format_pfreed_entry( - const PossiblyFreedShadaEntry pfs_entry) +static const char *shada_format_pfreed_entry(const PossiblyFreedShadaEntry pfs_entry) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_UNUSED FUNC_ATTR_NONNULL_RET { char *ret = (char *)shada_format_entry(pfs_entry.data); @@ -2136,10 +2078,11 @@ static const char *shada_format_pfreed_entry( /// @param[in,out] ret_wms Location where results are saved. /// @param[out] packer MessagePack packer for entries which are not /// merged. -static inline ShaDaWriteResult shada_read_when_writing( - ShaDaReadDef *const sd_reader, const unsigned srni_flags, - const size_t max_kbyte, WriteMergerState *const wms, - msgpack_packer *const packer) +static inline ShaDaWriteResult shada_read_when_writing(ShaDaReadDef *const sd_reader, + const unsigned srni_flags, + const size_t max_kbyte, + WriteMergerState *const wms, + msgpack_packer *const packer) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { ShaDaWriteResult ret = kSDWriteSuccessfull; @@ -2149,217 +2092,202 @@ static inline ShaDaWriteResult shada_read_when_writing( max_kbyte)) != kSDReadStatusFinished) { switch (srni_ret) { - case kSDReadStatusSuccess: { - break; - } - case kSDReadStatusFinished: { - // Should be handled by the while condition. - abort(); - } - case kSDReadStatusNotShaDa: { - ret = kSDWriteReadNotShada; - FALLTHROUGH; - } - case kSDReadStatusReadError: { - return ret; - } - case kSDReadStatusMalformed: { - continue; - } + case kSDReadStatusSuccess: + break; + case kSDReadStatusFinished: + // Should be handled by the while condition. + abort(); + case kSDReadStatusNotShaDa: + ret = kSDWriteReadNotShada; + FALLTHROUGH; + case kSDReadStatusReadError: + return ret; + case kSDReadStatusMalformed: + continue; } #define COMPARE_WITH_ENTRY(wms_entry_, entry) \ - do { \ - PossiblyFreedShadaEntry *const wms_entry = (wms_entry_); \ - if (wms_entry->data.type != kSDItemMissing) { \ - if (wms_entry->data.timestamp >= (entry).timestamp) { \ - shada_free_shada_entry(&(entry)); \ - break; \ - } \ - if (wms_entry->can_free_entry) { \ - shada_free_shada_entry(&wms_entry->data); \ - } \ + do { \ + PossiblyFreedShadaEntry *const wms_entry = (wms_entry_); \ + if (wms_entry->data.type != kSDItemMissing) { \ + if (wms_entry->data.timestamp >= (entry).timestamp) { \ + shada_free_shada_entry(&(entry)); \ + break; \ + } \ + if (wms_entry->can_free_entry) { \ + shada_free_shada_entry(&wms_entry->data); \ } \ - *wms_entry = pfs_entry; \ - } while (0) + } \ + *wms_entry = pfs_entry; \ + } while (0) const PossiblyFreedShadaEntry pfs_entry = { .can_free_entry = true, .data = entry, }; switch (entry.type) { - case kSDItemMissing: { + case kSDItemMissing: + break; + case kSDItemHeader: + case kSDItemBufferList: + abort(); + case kSDItemUnknown: + ret = shada_pack_entry(packer, entry, 0); + shada_free_shada_entry(&entry); + break; + case kSDItemSearchPattern: + COMPARE_WITH_ENTRY((entry.data.search_pattern.is_substitute_pattern + ? &wms->sub_search_pattern + : &wms->search_pattern), entry); + break; + case kSDItemSubString: + COMPARE_WITH_ENTRY(&wms->replacement, entry); + break; + case kSDItemHistoryEntry: + if (entry.data.history_item.histtype >= HIST_COUNT) { + ret = shada_pack_entry(packer, entry, 0); + shada_free_shada_entry(&entry); break; } - case kSDItemHeader: - case kSDItemBufferList: { - abort(); + if (wms->hms[entry.data.history_item.histtype].hmll.size != 0) { + hms_insert(&wms->hms[entry.data.history_item.histtype], entry, true, + true); + } else { + shada_free_shada_entry(&entry); } - case kSDItemUnknown: { + break; + case kSDItemRegister: { + const int idx = op_reg_index(entry.data.reg.name); + if (idx < 0) { ret = shada_pack_entry(packer, entry, 0); shada_free_shada_entry(&entry); break; } - case kSDItemSearchPattern: { - COMPARE_WITH_ENTRY((entry.data.search_pattern.is_substitute_pattern - ? &wms->sub_search_pattern - : &wms->search_pattern), entry); - break; - } - case kSDItemSubString: { - COMPARE_WITH_ENTRY(&wms->replacement, entry); - break; + COMPARE_WITH_ENTRY(&wms->registers[idx], entry); + break; + } + case kSDItemVariable: + if (!in_strset(&wms->dumped_variables, entry.data.global_var.name)) { + ret = shada_pack_entry(packer, entry, 0); } - case kSDItemHistoryEntry: { - if (entry.data.history_item.histtype >= HIST_COUNT) { - ret = shada_pack_entry(packer, entry, 0); - shada_free_shada_entry(&entry); - break; + shada_free_shada_entry(&entry); + break; + case kSDItemGlobalMark: + if (ascii_isdigit(entry.data.filemark.name)) { + bool processed_mark = false; + // Completely ignore numbered mark names, make a list sorted by + // timestamp. + for (size_t i = ARRAY_SIZE(wms->numbered_marks); i > 0; i--) { + ShadaEntry wms_entry = wms->numbered_marks[i - 1].data; + if (wms_entry.type != kSDItemGlobalMark) { + continue; + } + // Ignore duplicates. + if (wms_entry.timestamp == entry.timestamp + && (wms_entry.data.filemark.additional_data == NULL + && entry.data.filemark.additional_data == NULL) + && marks_equal(wms_entry.data.filemark.mark, + entry.data.filemark.mark) + && strcmp(wms_entry.data.filemark.fname, + entry.data.filemark.fname) == 0) { + shada_free_shada_entry(&entry); + processed_mark = true; + break; + } + if (wms_entry.timestamp >= entry.timestamp) { + processed_mark = true; + if (i < ARRAY_SIZE(wms->numbered_marks)) { + replace_numbered_mark(wms, i, pfs_entry); + } else { + shada_free_shada_entry(&entry); + } + break; + } } - if (wms->hms[entry.data.history_item.histtype].hmll.size != 0) { - hms_insert(&wms->hms[entry.data.history_item.histtype], entry, true, - true); - } else { - shada_free_shada_entry(&entry); + if (!processed_mark) { + replace_numbered_mark(wms, 0, pfs_entry); } - break; - } - case kSDItemRegister: { - const int idx = op_reg_index(entry.data.reg.name); + } else { + const int idx = mark_global_index(entry.data.filemark.name); if (idx < 0) { ret = shada_pack_entry(packer, entry, 0); shada_free_shada_entry(&entry); break; } - COMPARE_WITH_ENTRY(&wms->registers[idx], entry); - break; + COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry); } - case kSDItemVariable: { - if (!in_strset(&wms->dumped_variables, entry.data.global_var.name)) { - ret = shada_pack_entry(packer, entry, 0); - } + break; + case kSDItemChange: + case kSDItemLocalMark: { + if (shada_removable(entry.data.filemark.fname)) { shada_free_shada_entry(&entry); break; } - case kSDItemGlobalMark: { - if (ascii_isdigit(entry.data.filemark.name)) { - bool processed_mark = false; - // Completely ignore numbered mark names, make a list sorted by - // timestamp. - for (size_t i = ARRAY_SIZE(wms->numbered_marks); i > 0; i--) { - ShadaEntry wms_entry = wms->numbered_marks[i - 1].data; - if (wms_entry.type != kSDItemGlobalMark) { - continue; - } - // Ignore duplicates. - if (wms_entry.timestamp == entry.timestamp - && (wms_entry.data.filemark.additional_data == NULL - && entry.data.filemark.additional_data == NULL) - && marks_equal(wms_entry.data.filemark.mark, - entry.data.filemark.mark) - && strcmp(wms_entry.data.filemark.fname, - entry.data.filemark.fname) == 0) { + const char *const fname = (const char *)entry.data.filemark.fname; + khiter_t k; + int kh_ret; + k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret); + FileMarks *const filemarks = &kh_val(&wms->file_marks, k); + if (kh_ret > 0) { + memset(filemarks, 0, sizeof(*filemarks)); + } + if (entry.timestamp > filemarks->greatest_timestamp) { + filemarks->greatest_timestamp = entry.timestamp; + } + if (entry.type == kSDItemLocalMark) { + const int idx = mark_local_index(entry.data.filemark.name); + if (idx < 0) { + filemarks->additional_marks = xrealloc(filemarks->additional_marks, + (++filemarks->additional_marks_size + * sizeof(filemarks->additional_marks[0]))); + filemarks->additional_marks[filemarks->additional_marks_size - 1] = + entry; + } else { + PossiblyFreedShadaEntry *const wms_entry = &filemarks->marks[idx]; + if (wms_entry->data.type != kSDItemMissing) { + if (wms_entry->data.timestamp >= entry.timestamp) { shada_free_shada_entry(&entry); - processed_mark = true; break; } - if (wms_entry.timestamp >= entry.timestamp) { - processed_mark = true; - if (i < ARRAY_SIZE(wms->numbered_marks)) { - replace_numbered_mark(wms, i, pfs_entry); - } else { - shada_free_shada_entry(&entry); + if (wms_entry->can_free_entry) { + if (kh_key(&wms->file_marks, k) + == wms_entry->data.data.filemark.fname) { + kh_key(&wms->file_marks, k) = entry.data.filemark.fname; } - break; + shada_free_shada_entry(&wms_entry->data); } } - if (!processed_mark) { - replace_numbered_mark(wms, 0, pfs_entry); - } - } else { - const int idx = mark_global_index(entry.data.filemark.name); - if (idx < 0) { - ret = shada_pack_entry(packer, entry, 0); - shada_free_shada_entry(&entry); - break; - } - COMPARE_WITH_ENTRY(&wms->global_marks[idx], entry); - } - break; - } - case kSDItemChange: - case kSDItemLocalMark: { - if (shada_removable(entry.data.filemark.fname)) { - shada_free_shada_entry(&entry); - break; - } - const char *const fname = (const char *) entry.data.filemark.fname; - khiter_t k; - int kh_ret; - k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret); - FileMarks *const filemarks = &kh_val(&wms->file_marks, k); - if (kh_ret > 0) { - memset(filemarks, 0, sizeof(*filemarks)); + *wms_entry = pfs_entry; } - if (entry.timestamp > filemarks->greatest_timestamp) { - filemarks->greatest_timestamp = entry.timestamp; - } - if (entry.type == kSDItemLocalMark) { - const int idx = mark_local_index(entry.data.filemark.name); - if (idx < 0) { - filemarks->additional_marks = xrealloc( - filemarks->additional_marks, - (++filemarks->additional_marks_size - * sizeof(filemarks->additional_marks[0]))); - filemarks->additional_marks[filemarks->additional_marks_size - 1] = - entry; - } else { - PossiblyFreedShadaEntry *const wms_entry = &filemarks->marks[idx]; - if (wms_entry->data.type != kSDItemMissing) { - if (wms_entry->data.timestamp >= entry.timestamp) { - shada_free_shada_entry(&entry); - break; - } - if (wms_entry->can_free_entry) { - if (kh_key(&wms->file_marks, k) - == wms_entry->data.data.filemark.fname) { - kh_key(&wms->file_marks, k) = entry.data.filemark.fname; - } - shada_free_shada_entry(&wms_entry->data); - } - } - *wms_entry = pfs_entry; - } - } else { + } else { #define FREE_POSSIBLY_FREED_SHADA_ENTRY(entry) \ - do { \ - if (entry.can_free_entry) { \ - shada_free_shada_entry(&entry.data); \ - } \ - } while (0) + do { \ + if (entry.can_free_entry) { \ + shada_free_shada_entry(&entry.data); \ + } \ + } while (0) #define SDE_TO_PFSDE(entry) \ - ((PossiblyFreedShadaEntry) { .can_free_entry = true, .data = entry }) + ((PossiblyFreedShadaEntry) { .can_free_entry = true, .data = entry }) #define AFTERFREE_DUMMY(entry) #define DUMMY_IDX_ADJ(i) - MERGE_JUMPS(filemarks->changes_size, filemarks->changes, - PossiblyFreedShadaEntry, data.timestamp, - data.data.filemark.mark, entry, true, - FREE_POSSIBLY_FREED_SHADA_ENTRY, SDE_TO_PFSDE, - DUMMY_IDX_ADJ, AFTERFREE_DUMMY); - } - break; - } - case kSDItemJump: { - MERGE_JUMPS(wms->jumps_size, wms->jumps, PossiblyFreedShadaEntry, - data.timestamp, data.data.filemark.mark, entry, - strcmp(jl_entry.data.data.filemark.fname, - entry.data.filemark.fname) == 0, + MERGE_JUMPS(filemarks->changes_size, filemarks->changes, + PossiblyFreedShadaEntry, data.timestamp, + data.data.filemark.mark, entry, true, FREE_POSSIBLY_FREED_SHADA_ENTRY, SDE_TO_PFSDE, DUMMY_IDX_ADJ, AFTERFREE_DUMMY); + } + break; + } + case kSDItemJump: + MERGE_JUMPS(wms->jumps_size, wms->jumps, PossiblyFreedShadaEntry, + data.timestamp, data.data.filemark.mark, entry, + strcmp(jl_entry.data.data.filemark.fname, + entry.data.filemark.fname) == 0, + FREE_POSSIBLY_FREED_SHADA_ENTRY, SDE_TO_PFSDE, + DUMMY_IDX_ADJ, AFTERFREE_DUMMY); #undef FREE_POSSIBLY_FREED_SHADA_ENTRY #undef SDE_TO_PFSDE #undef DUMMY_IDX_ADJ #undef AFTERFREE_DUMMY - break; - } + break; } } #undef COMPARE_WITH_ENTRY @@ -2372,8 +2300,7 @@ static inline ShaDaWriteResult shada_read_when_writing( /// @param[in] removable_bufs Cache of buffers ignored due to their location. /// /// @return true or false. -static inline bool ignore_buf(const buf_T *const buf, - khash_t(bufset) *const removable_bufs) +static inline bool ignore_buf(const buf_T *const buf, khash_t(bufset) *const removable_bufs) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE { return (buf->b_ffname == NULL || !buf->b_p_bl || bt_quickfix(buf) \ @@ -2385,8 +2312,7 @@ static inline bool ignore_buf(const buf_T *const buf, /// @param[in] removable_bufs Buffers which are ignored /// /// @return ShadaEntry List of buffers to save, kSDItemBufferList entry. -static inline ShadaEntry shada_get_buflist( - khash_t(bufset) *const removable_bufs) +static inline ShadaEntry shada_get_buflist(khash_t(bufset) *const removable_bufs) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_ALWAYS_INLINE { int max_bufs = get_shada_parameter('%'); @@ -2400,14 +2326,14 @@ static inline ShadaEntry shada_get_buflist( ShadaEntry buflist_entry = (ShadaEntry) { .type = kSDItemBufferList, - .timestamp = os_time(), - .data = { - .buffer_list = { - .size = buf_count, - .buffers = xmalloc(buf_count - * sizeof(*buflist_entry.data.buffer_list.buffers)), - }, + .timestamp = os_time(), + .data = { + .buffer_list = { + .size = buf_count, + .buffers = xmalloc(buf_count + * sizeof(*buflist_entry.data.buffer_list.buffers)), }, + }, }; size_t i = 0; FOR_ALL_BUFFERS(buf) { @@ -2419,8 +2345,8 @@ static inline ShadaEntry shada_get_buflist( } buflist_entry.data.buffer_list.buffers[i] = (struct buffer_list_buffer) { .pos = buf->b_last_cursor.mark, - .fname = (char *)buf->b_ffname, - .additional_data = buf->additional_data, + .fname = (char *)buf->b_ffname, + .additional_data = buf->additional_data, }; i++; } @@ -2442,8 +2368,7 @@ static inline ShadaEntry shada_get_buflist( /// saved. static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse, const SearchPatternGetter get_pattern, - const bool is_substitute_pattern, - const bool search_last_used, + const bool is_substitute_pattern, const bool search_last_used, const bool search_highlighted) FUNC_ATTR_ALWAYS_INLINE { @@ -2464,7 +2389,7 @@ static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse, ? defaults.data.search_pattern.has_line_offset : pat.off.line), .place_cursor_at_end = ( - is_substitute_pattern + is_substitute_pattern ? defaults.data.search_pattern.place_cursor_at_end : pat.off.end), .offset = (is_substitute_pattern @@ -2488,8 +2413,7 @@ static inline void add_search_pattern(PossiblyFreedShadaEntry *const ret_pse, /// /// @param[in] wms The WriteMergerState used when writing. /// @param[in] max_reg_lines The maximum number of register lines. -static inline void shada_initialize_registers(WriteMergerState *const wms, - int max_reg_lines) +static inline void shada_initialize_registers(WriteMergerState *const wms, int max_reg_lines) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE { const void *reg_iter = NULL; @@ -2534,8 +2458,7 @@ static inline void shada_initialize_registers(WriteMergerState *const wms, /// @param[out] wms Merger state to adjust. /// @param[in] idx Index at which new mark should be placed. /// @param[in] entry New mark. -static inline void replace_numbered_mark(WriteMergerState *const wms, - const size_t idx, +static inline void replace_numbered_mark(WriteMergerState *const wms, const size_t idx, const PossiblyFreedShadaEntry entry) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_ALWAYS_INLINE { @@ -2573,8 +2496,7 @@ static inline void find_removable_bufs(khash_t(bufset) *removable_bufs) /// @param[in] sd_reader Structure containing file reader definition. If it is /// not NULL then contents of this file will be merged /// with current Neovim runtime. -static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, - ShaDaReadDef *const sd_reader) +static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, ShaDaReadDef *const sd_reader) FUNC_ATTR_NONNULL_ARG(1) { ShaDaWriteResult ret = kSDWriteSuccessfull; @@ -2595,8 +2517,8 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, } const bool dump_registers = (max_reg_lines != 0); khash_t(bufset) removable_bufs = KHASH_EMPTY_TABLE(bufset); - const size_t max_kbyte = (size_t) max_kbyte_i; - const size_t num_marked_files = (size_t) get_shada_parameter('\''); + const size_t max_kbyte = (size_t)max_kbyte_i; + const size_t num_marked_files = (size_t)get_shada_parameter('\''); const bool dump_global_marks = get_shada_parameter('f') != 0; bool dump_history = false; @@ -2609,20 +2531,21 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (num_saved > 0) { dump_history = true; dump_one_history[i] = true; - hms_init(&wms->hms[i], i, (size_t) num_saved, sd_reader != NULL, false); + hms_init(&wms->hms[i], i, (size_t)num_saved, sd_reader != NULL, false); } else { dump_one_history[i] = false; } } - const unsigned srni_flags = (unsigned) ( - kSDReadUndisableableData - | kSDReadUnknown - | (dump_history ? kSDReadHistory : 0) - | (dump_registers ? kSDReadRegisters : 0) - | (dump_global_vars ? kSDReadVariables : 0) - | (dump_global_marks ? kSDReadGlobalMarks : 0) - | (num_marked_files ? kSDReadLocalMarks | kSDReadChanges : 0)); + const unsigned srni_flags = (unsigned)( + kSDReadUndisableableData + | kSDReadUnknown + | (dump_history ? kSDReadHistory : 0) + | (dump_registers ? kSDReadRegisters : 0) + | (dump_global_vars ? kSDReadVariables : 0) + | (dump_global_marks ? kSDReadGlobalMarks : 0) + | (num_marked_files ? kSDReadLocalMarks | + kSDReadChanges : 0)); msgpack_packer *const packer = msgpack_packer_new(sd_writer, &msgpack_sd_writer_write); @@ -2652,11 +2575,11 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, { STATIC_CSTR_AS_STRING("version"), STRING_OBJ(cstr_as_string(longVersion)) }, { STATIC_CSTR_AS_STRING("max_kbyte"), - INTEGER_OBJ((Integer) max_kbyte) }, + INTEGER_OBJ((Integer)max_kbyte) }, { STATIC_CSTR_AS_STRING("pid"), - INTEGER_OBJ((Integer) os_get_pid()) }, + INTEGER_OBJ((Integer)os_get_pid()) }, { STATIC_CSTR_AS_STRING("encoding"), - STRING_OBJ(cstr_as_string((char *) p_enc)) }, + STRING_OBJ(cstr_as_string((char *)p_enc)) }, }), } } @@ -2688,34 +2611,32 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, break; } switch (vartv.v_type) { - case VAR_FUNC: - case VAR_PARTIAL: + case VAR_FUNC: + case VAR_PARTIAL: + tv_clear(&vartv); + continue; + case VAR_DICT: { + dict_T *di = vartv.vval.v_dict; + int copyID = get_copyID(); + if (!set_ref_in_ht(&di->dv_hashtab, copyID, NULL) + && copyID == di->dv_copyID) { tv_clear(&vartv); continue; - case VAR_DICT: - { - dict_T *di = vartv.vval.v_dict; - int copyID = get_copyID(); - if (!set_ref_in_ht(&di->dv_hashtab, copyID, NULL) - && copyID == di->dv_copyID) { - tv_clear(&vartv); - continue; - } - break; - } - case VAR_LIST: - { - list_T *l = vartv.vval.v_list; - int copyID = get_copyID(); - if (!set_ref_in_list(l, copyID, NULL) - && copyID == l->lv_copyID) { - tv_clear(&vartv); - continue; - } - break; - } - default: - break; + } + break; + } + case VAR_LIST: { + list_T *l = vartv.vval.v_list; + int copyID = get_copyID(); + if (!set_ref_in_list(l, copyID, NULL) + && copyID == l->lv_copyID) { + tv_clear(&vartv); + continue; + } + break; + } + default: + break; } typval_T tgttv; tv_copy(&vartv, &tgttv); @@ -2725,7 +2646,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, .timestamp = cur_timestamp, .data = { .global_var = { - .name = (char *) name, + .name = (char *)name, .value = tgttv, .additional_elements = NULL, } @@ -2740,7 +2661,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, tv_clear(&tgttv); if (spe_ret == kSDWriteSuccessfull) { int kh_ret; - (void) kh_put(strset, &wms->dumped_variables, name, &kh_ret); + (void)kh_put(strset, &wms->dumped_variables, name, &kh_ret); } } while (var_iter != NULL); } @@ -2773,7 +2694,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, .timestamp = sub.timestamp, .data = { .sub_string = { - .sub = (char *) sub.sub, + .sub = (char *)sub.sub, .additional_elements = sub.additional_elements, } } @@ -2795,17 +2716,17 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, const char *fname; if (fm.fmark.fnum == 0) { assert(fm.fname != NULL); - if (shada_removable((const char *) fm.fname)) { + if (shada_removable((const char *)fm.fname)) { continue; } - fname = (const char *) fm.fname; + fname = (const char *)fm.fname; } else { const buf_T *const buf = buflist_findnr(fm.fmark.fnum); if (buf == NULL || buf->b_ffname == NULL || in_bufset(&removable_bufs, buf)) { continue; } - fname = (const char *) buf->b_ffname; + fname = (const char *)buf->b_ffname; } const PossiblyFreedShadaEntry pf_entry = { .can_free_entry = false, @@ -2842,7 +2763,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, continue; } const void *local_marks_iter = NULL; - const char *const fname = (const char *) buf->b_ffname; + const char *const fname = (const char *)buf->b_ffname; khiter_t k; int kh_ret; k = kh_put(file_marks, &wms->file_marks, fname, &kh_ret); @@ -2866,7 +2787,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, .filemark = { .mark = fm.mark, .name = name, - .fname = (char *) fname, + .fname = (char *)fname, .additional_data = fm.additional_data, } } @@ -2886,7 +2807,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, .data = { .filemark = { .mark = fm.mark, - .fname = (char *) fname, + .fname = (char *)fname, .additional_data = fm.additional_data, } } @@ -2896,13 +2817,13 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, filemarks->greatest_timestamp = fm.timestamp; } } - filemarks->changes_size = (size_t) buf->b_changelistlen; + filemarks->changes_size = (size_t)buf->b_changelistlen; } } if (sd_reader != NULL) { - const ShaDaWriteResult srww_ret = shada_read_when_writing( - sd_reader, srni_flags, max_kbyte, wms, packer); + const ShaDaWriteResult srww_ret = shada_read_when_writing(sd_reader, srni_flags, max_kbyte, wms, + packer); if (srww_ret != kSDWriteSuccessfull) { ret = srww_ret; } @@ -2968,7 +2889,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, const size_t file_markss_size = kh_size(&wms->file_marks); FileMarks **const all_file_markss = - xmalloc(file_markss_size * sizeof(*all_file_markss)); + xmalloc(file_markss_size * sizeof(*all_file_markss)); FileMarks **cur_file_marks = all_file_markss; for (khint_t i = kh_begin(&wms->file_marks); i != kh_end(&wms->file_marks); i++) { @@ -2976,7 +2897,7 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, *cur_file_marks++ = &kh_val(&wms->file_marks, i); } } - qsort((void *) all_file_markss, file_markss_size, sizeof(*all_file_markss), + qsort((void *)all_file_markss, file_markss_size, sizeof(*all_file_markss), &compare_file_marks); const size_t file_markss_to_dump = MIN(num_marked_files, file_markss_size); for (size_t i = 0; i < file_markss_to_dump; i++) { @@ -3007,11 +2928,10 @@ static ShaDaWriteResult shada_write(ShaDaWriteDef *const sd_writer, if (dump_one_history[i]) { hms_insert_whole_neovim_history(&wms->hms[i]); HMS_ITER(&wms->hms[i], cur_entry, { - if (shada_pack_pfreed_entry( - packer, (PossiblyFreedShadaEntry) { - .data = cur_entry->data, - .can_free_entry = cur_entry->can_free_entry, - }, max_kbyte) == kSDWriteFailed) { + if (shada_pack_pfreed_entry(packer, (PossiblyFreedShadaEntry) { + .data = cur_entry->data, + .can_free_entry = cur_entry->can_free_entry, + }, max_kbyte) == kSDWriteFailed) { ret = kSDWriteFailed; break; } @@ -3082,7 +3002,7 @@ int shada_write_file(const char *const file, bool nomerge) } // Save permissions from the original file, with modifications: - int perm = (int) os_getperm(fname); + int perm = (int)os_getperm(fname); perm = (perm >= 0) ? ((perm & 0777) | 0600) : 0600; // ^3 ^1 ^2 ^2,3 // 1: Strip SUID bit if any. @@ -3090,8 +3010,7 @@ int shada_write_file(const char *const file, bool nomerge) // 3: If somebody happened to delete the file after it was opened for // reading use u=rw permissions. shada_write_file_open: {} - sd_writer.cookie = file_open_new( - &error, tempname, kFileCreateOnly|kFileNoSymlink, perm); + sd_writer.cookie = file_open_new(&error, tempname, kFileCreateOnly|kFileNoSymlink, perm); if (sd_writer.cookie == NULL) { if (error == UV_EEXIST || error == UV_ELOOP) { // File already exists, try another name @@ -3244,8 +3163,7 @@ int shada_read_marks(void) /// @param[in] missing_ok If true, do not error out when file is missing. /// /// @return OK in case of success, FAIL otherwise. -int shada_read_everything(const char *const fname, const bool forceit, - const bool missing_ok) +int shada_read_everything(const char *const fname, const bool forceit, const bool missing_ok) { return shada_read_file(fname, kShaDaWantInfo|kShaDaWantMarks|kShaDaGetOldfiles @@ -3259,81 +3177,71 @@ static void shada_free_shada_entry(ShadaEntry *const entry) return; } switch (entry->type) { - case kSDItemMissing: { - break; - } - case kSDItemUnknown: { - xfree(entry->data.unknown_item.contents); - break; - } - case kSDItemHeader: { - api_free_dictionary(entry->data.header); - break; - } - case kSDItemChange: - case kSDItemJump: - case kSDItemGlobalMark: - case kSDItemLocalMark: { - tv_dict_unref(entry->data.filemark.additional_data); - xfree(entry->data.filemark.fname); - break; + case kSDItemMissing: + break; + case kSDItemUnknown: + xfree(entry->data.unknown_item.contents); + break; + case kSDItemHeader: + api_free_dictionary(entry->data.header); + break; + case kSDItemChange: + case kSDItemJump: + case kSDItemGlobalMark: + case kSDItemLocalMark: + tv_dict_unref(entry->data.filemark.additional_data); + xfree(entry->data.filemark.fname); + break; + case kSDItemSearchPattern: + tv_dict_unref(entry->data.search_pattern.additional_data); + xfree(entry->data.search_pattern.pat); + break; + case kSDItemRegister: + tv_dict_unref(entry->data.reg.additional_data); + for (size_t i = 0; i < entry->data.reg.contents_size; i++) { + xfree(entry->data.reg.contents[i]); } - case kSDItemSearchPattern: { - tv_dict_unref(entry->data.search_pattern.additional_data); - xfree(entry->data.search_pattern.pat); - break; - } - case kSDItemRegister: { - tv_dict_unref(entry->data.reg.additional_data); - for (size_t i = 0; i < entry->data.reg.contents_size; i++) { - xfree(entry->data.reg.contents[i]); - } - xfree(entry->data.reg.contents); - break; - } - case kSDItemHistoryEntry: { - tv_list_unref(entry->data.history_item.additional_elements); - xfree(entry->data.history_item.string); - break; - } - case kSDItemVariable: { - tv_list_unref(entry->data.global_var.additional_elements); - xfree(entry->data.global_var.name); - tv_clear(&entry->data.global_var.value); - break; - } - case kSDItemSubString: { - tv_list_unref(entry->data.sub_string.additional_elements); - xfree(entry->data.sub_string.sub); - break; - } - case kSDItemBufferList: { - for (size_t i = 0; i < entry->data.buffer_list.size; i++) { - xfree(entry->data.buffer_list.buffers[i].fname); - tv_dict_unref(entry->data.buffer_list.buffers[i].additional_data); - } - xfree(entry->data.buffer_list.buffers); - break; + xfree(entry->data.reg.contents); + break; + case kSDItemHistoryEntry: + tv_list_unref(entry->data.history_item.additional_elements); + xfree(entry->data.history_item.string); + break; + case kSDItemVariable: + tv_list_unref(entry->data.global_var.additional_elements); + xfree(entry->data.global_var.name); + tv_clear(&entry->data.global_var.value); + break; + case kSDItemSubString: + tv_list_unref(entry->data.sub_string.additional_elements); + xfree(entry->data.sub_string.sub); + break; + case kSDItemBufferList: + for (size_t i = 0; i < entry->data.buffer_list.size; i++) { + xfree(entry->data.buffer_list.buffers[i].fname); + tv_dict_unref(entry->data.buffer_list.buffers[i].additional_data); } + xfree(entry->data.buffer_list.buffers); + break; } } #ifndef HAVE_BE64TOH static inline uint64_t be64toh(uint64_t big_endian_64_bits) { -#ifdef ORDER_BIG_ENDIAN +# ifdef ORDER_BIG_ENDIAN return big_endian_64_bits; -#else +# else // It may appear that when !defined(ORDER_BIG_ENDIAN) actual order is big // endian. This variant is suboptimal, but it works regardless of actual // order. - uint8_t *buf = (uint8_t *) &big_endian_64_bits; + uint8_t *buf = (uint8_t *)&big_endian_64_bits; uint64_t ret = 0; for (size_t i = 8; i; i--) { - ret |= ((uint64_t) buf[i - 1]) << ((8 - i) * 8); + ret |= ((uint64_t)buf[i - 1]) << ((8 - i) * 8); } return ret; -#endif +# endif } #endif @@ -3346,8 +3254,7 @@ static inline uint64_t be64toh(uint64_t big_endian_64_bits) /// @return kSDReadStatusSuccess if everything was OK, kSDReadStatusNotShaDa if /// there were not enough bytes to read or kSDReadStatusReadError if /// there was some error while reading. -static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader, - char *const buffer, +static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader, char *const buffer, const size_t length) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { @@ -3385,8 +3292,7 @@ static ShaDaReadResult fread_len(ShaDaReadDef *const sd_reader, /// @return kSDReadStatusSuccess if reading was successful, /// kSDReadStatusNotShaDa if there were not enough bytes to read or /// kSDReadStatusReadError if reading failed for whatever reason. -static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, - const int first_char, +static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, const int first_char, uint64_t *const result) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { @@ -3401,42 +3307,37 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, emsgf(_(RCERR "Error while reading ShaDa file: " "expected positive integer at position %" PRIu64 ", but got nothing"), - (uint64_t) fpos); + (uint64_t)fpos); return kSDReadStatusNotShaDa; } } if (~first_char & 0x80) { // Positive fixnum - *result = (uint64_t) ((uint8_t) first_char); + *result = (uint64_t)((uint8_t)first_char); } else { size_t length = 0; switch (first_char) { - case 0xCC: { // uint8 - length = 1; - break; - } - case 0xCD: { // uint16 - length = 2; - break; - } - case 0xCE: { // uint32 - length = 4; - break; - } - case 0xCF: { // uint64 - length = 8; - break; - } - default: { - emsgf(_(RCERR "Error while reading ShaDa file: " - "expected positive integer at position %" PRIu64), - (uint64_t) fpos); - return kSDReadStatusNotShaDa; - } + case 0xCC: // uint8 + length = 1; + break; + case 0xCD: // uint16 + length = 2; + break; + case 0xCE: // uint32 + length = 4; + break; + case 0xCF: // uint64 + length = 8; + break; + default: + emsgf(_(RCERR "Error while reading ShaDa file: " + "expected positive integer at position %" PRIu64), + (uint64_t)fpos); + return kSDReadStatusNotShaDa; } uint64_t buf = 0; - char *buf_u8 = (char *) &buf; + char *buf_u8 = (char *)&buf; ShaDaReadResult fl_ret; if ((fl_ret = fread_len(sd_reader, &(buf_u8[sizeof(buf)-length]), length)) != kSDReadStatusSuccess) { @@ -3448,24 +3349,24 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, } #define READERR(entry_name, error_desc) \ - RERR "Error while reading ShaDa file: " \ - entry_name " entry at position %" PRIu64 " " \ - error_desc + RERR "Error while reading ShaDa file: " \ + entry_name " entry at position %" PRIu64 " " \ + error_desc #define CHECK_KEY(key, expected) ( \ - key.via.str.size == sizeof(expected) - 1 \ - && STRNCMP(key.via.str.ptr, expected, sizeof(expected) - 1) == 0) + key.via.str.size == sizeof(expected) - 1 \ + && STRNCMP(key.via.str.ptr, expected, sizeof(expected) - 1) == 0) #define CLEAR_GA_AND_ERROR_OUT(ga) \ - do { \ - ga_clear(&ga); \ - goto shada_read_next_item_error; \ - } while (0) + do { \ + ga_clear(&ga); \ + goto shada_read_next_item_error; \ + } while (0) #define ID(s) s #define BINDUP(b) xmemdupz(b.ptr, b.size) -#define TOINT(s) ((int) (s)) -#define TOLONG(s) ((long) (s)) -#define TOCHAR(s) ((char) (s)) -#define TOU8(s) ((uint8_t) (s)) -#define TOSIZE(s) ((size_t) (s)) +#define TOINT(s) ((int)(s)) +#define TOLONG(s) ((long)(s)) +#define TOCHAR(s) ((char)(s)) +#define TOU8(s) ((uint8_t)(s)) +#define TOSIZE(s) ((size_t)(s)) #define CHECKED_ENTRY(condition, error_desc, entry_name, obj, tgt, attr, \ proc) \ do { \ @@ -3486,18 +3387,16 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, } #define CHECKED_KEY(un, entry_name, name, error_desc, tgt, condition, attr, \ proc) \ - else if (CHECK_KEY( /* NOLINT(readability/braces) */ \ - un.data.via.map.ptr[i].key, name)) { \ - CHECKED_ENTRY( \ - condition, "has " name " key value " error_desc, \ - entry_name, un.data.via.map.ptr[i].val, \ - tgt, attr, proc); \ + else if (CHECK_KEY( /* NOLINT(readability/braces) */ \ + un.data.via.map.ptr[i].key, name)) { \ + CHECKED_ENTRY(condition, "has " name " key value " error_desc, \ + entry_name, un.data.via.map.ptr[i].val, \ + tgt, attr, proc); \ } #define TYPED_KEY(un, entry_name, name, type_name, tgt, objtype, attr, proc) \ - CHECKED_KEY( \ - un, entry_name, name, "which is not " type_name, tgt, \ - un.data.via.map.ptr[i].val.type == MSGPACK_OBJECT_##objtype, \ - attr, proc) + CHECKED_KEY(un, entry_name, name, "which is not " type_name, tgt, \ + un.data.via.map.ptr[i].val.type == MSGPACK_OBJECT_##objtype, \ + attr, proc) #define BOOLEAN_KEY(un, entry_name, name, tgt) \ TYPED_KEY(un, entry_name, name, "a boolean", tgt, BOOLEAN, boolean, ID) #define STRING_KEY(un, entry_name, name, tgt) \ @@ -3506,19 +3405,18 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, TYPED_KEY(un, entry_name, name, "a binary", tgt, BIN, bin, \ BIN_CONVERTED) #define INT_KEY(un, entry_name, name, tgt, proc) \ - CHECKED_KEY( \ - un, entry_name, name, "which is not an integer", tgt, \ - ((un.data.via.map.ptr[i].val.type \ - == MSGPACK_OBJECT_POSITIVE_INTEGER) \ - || (un.data.via.map.ptr[i].val.type \ - == MSGPACK_OBJECT_NEGATIVE_INTEGER)), \ - i64, proc) + CHECKED_KEY(un, entry_name, name, "which is not an integer", tgt, \ + ((un.data.via.map.ptr[i].val.type \ + == MSGPACK_OBJECT_POSITIVE_INTEGER) \ + || (un.data.via.map.ptr[i].val.type \ + == MSGPACK_OBJECT_NEGATIVE_INTEGER)), \ + i64, proc) #define INTEGER_KEY(un, entry_name, name, tgt) \ INT_KEY(un, entry_name, name, tgt, TOINT) #define LONG_KEY(un, entry_name, name, tgt) \ INT_KEY(un, entry_name, name, tgt, TOLONG) #define ADDITIONAL_KEY(un) \ - else { /* NOLINT(readability/braces) */ \ + else { /* NOLINT(readability/braces) */ \ ga_grow(&ad_ga, 1); \ memcpy(((char *)ad_ga.ga_data) + ((size_t)ad_ga.ga_len \ * sizeof(*un.data.via.map.ptr)), \ @@ -3529,54 +3427,54 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, #define CONVERTED(str, len) (xmemdupz((str), (len))) #define BIN_CONVERTED(b) CONVERTED(b.ptr, b.size) #define SET_ADDITIONAL_DATA(tgt, name) \ - do { \ - if (ad_ga.ga_len) { \ - msgpack_object obj = { \ - .type = MSGPACK_OBJECT_MAP, \ - .via = { \ - .map = { \ - .size = (uint32_t) ad_ga.ga_len, \ - .ptr = ad_ga.ga_data, \ - } \ - } \ - }; \ - typval_T adtv; \ - if (msgpack_to_vim(obj, &adtv) == FAIL \ - || adtv.v_type != VAR_DICT) { \ - emsgf(_(READERR(name, \ - "cannot be converted to a VimL dictionary")), \ - initial_fpos); \ - ga_clear(&ad_ga); \ - tv_clear(&adtv); \ - goto shada_read_next_item_error; \ + do { \ + if (ad_ga.ga_len) { \ + msgpack_object obj = { \ + .type = MSGPACK_OBJECT_MAP, \ + .via = { \ + .map = { \ + .size = (uint32_t)ad_ga.ga_len, \ + .ptr = ad_ga.ga_data, \ } \ - tgt = adtv.vval.v_dict; \ } \ + }; \ + typval_T adtv; \ + if (msgpack_to_vim(obj, &adtv) == FAIL \ + || adtv.v_type != VAR_DICT) { \ + emsgf(_(READERR(name, \ + "cannot be converted to a VimL dictionary")), \ + initial_fpos); \ ga_clear(&ad_ga); \ - } while (0) + tv_clear(&adtv); \ + goto shada_read_next_item_error; \ + } \ + tgt = adtv.vval.v_dict; \ + } \ + ga_clear(&ad_ga); \ + } while (0) #define SET_ADDITIONAL_ELEMENTS(src, src_maxsize, tgt, name) \ - do { \ - if ((src).size > (size_t) (src_maxsize)) { \ - msgpack_object obj = { \ - .type = MSGPACK_OBJECT_ARRAY, \ - .via = { \ - .array = { \ - .size = ((src).size - (uint32_t) (src_maxsize)), \ - .ptr = (src).ptr + (src_maxsize), \ - } \ - } \ - }; \ - typval_T aetv; \ - if (msgpack_to_vim(obj, &aetv) == FAIL) { \ - emsgf(_(READERR(name, "cannot be converted to a VimL list")), \ - initial_fpos); \ - tv_clear(&aetv); \ - goto shada_read_next_item_error; \ + do { \ + if ((src).size > (size_t)(src_maxsize)) { \ + msgpack_object obj = { \ + .type = MSGPACK_OBJECT_ARRAY, \ + .via = { \ + .array = { \ + .size = ((src).size - (uint32_t)(src_maxsize)), \ + .ptr = (src).ptr + (src_maxsize), \ } \ - assert(aetv.v_type == VAR_LIST); \ - (tgt) = aetv.vval.v_list; \ } \ - } while (0) + }; \ + typval_T aetv; \ + if (msgpack_to_vim(obj, &aetv) == FAIL) { \ + emsgf(_(READERR(name, "cannot be converted to a VimL list")), \ + initial_fpos); \ + tv_clear(&aetv); \ + goto shada_read_next_item_error; \ + } \ + assert(aetv.v_type == VAR_LIST); \ + (tgt) = aetv.vval.v_list; \ + } \ + } while (0) /// Iterate over shada file contents /// @@ -3588,10 +3486,8 @@ static ShaDaReadResult msgpack_read_uint64(ShaDaReadDef *const sd_reader, /// greater then given. /// /// @return Any value from ShaDaReadResult enum. -static ShaDaReadResult shada_read_next_item(ShaDaReadDef *const sd_reader, - ShadaEntry *const entry, - const unsigned flags, - const size_t max_kbyte) +static ShaDaReadResult shada_read_next_item(ShaDaReadDef *const sd_reader, ShadaEntry *const entry, + const unsigned flags, const size_t max_kbyte) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { ShaDaReadResult ret = kSDReadStatusMalformed; @@ -3607,11 +3503,11 @@ shada_read_next_item_start: // First: manually unpack type, timestamp and length. // This is needed to avoid both seeking and having to maintain a buffer. - uint64_t type_u64 = (uint64_t) kSDItemMissing; + uint64_t type_u64 = (uint64_t)kSDItemMissing; uint64_t timestamp_u64; uint64_t length_u64; - const uint64_t initial_fpos = (uint64_t) sd_reader->fpos; + const uint64_t initial_fpos = (uint64_t)sd_reader->fpos; const int first_char = read_char(sd_reader); if (first_char == EOF && sd_reader->eof) { return kSDReadStatusFinished; @@ -3654,7 +3550,7 @@ shada_read_next_item_start: if ((type_u64 > SHADA_LAST_ENTRY ? !(flags & kSDReadUnknown) - : !((unsigned) (1 << type_u64) & flags)) + : !((unsigned)(1 << type_u64) & flags)) || (max_kbyte && length > max_kbyte * 1024)) { // First entry is unknown or equal to "\n" (10)? Most likely this means that // current file is not a ShaDa file because first item should normally be @@ -3682,16 +3578,16 @@ shada_read_next_item_start: entry->data.unknown_item.size = length; entry->data.unknown_item.type = type_u64; if (initial_fpos == 0) { - const ShaDaReadResult spm_ret = shada_parse_msgpack( - sd_reader, length, NULL, &entry->data.unknown_item.contents); + const ShaDaReadResult spm_ret = shada_parse_msgpack(sd_reader, length, NULL, + &entry->data.unknown_item.contents); if (spm_ret != kSDReadStatusSuccess) { entry->type = kSDItemMissing; } return spm_ret; } else { entry->data.unknown_item.contents = xmalloc(length); - const ShaDaReadResult fl_ret = fread_len( - sd_reader, entry->data.unknown_item.contents, length); + const ShaDaReadResult fl_ret = + fread_len(sd_reader, entry->data.unknown_item.contents, length); if (fl_ret != kSDReadStatusSuccess) { shada_free_shada_entry(entry); entry->type = kSDItemMissing; @@ -3711,373 +3607,367 @@ shada_read_next_item_start: } ret = kSDReadStatusMalformed; entry->data = sd_default_values[type_u64].data; - switch ((ShadaEntryType) type_u64) { - case kSDItemHeader: { - if (!msgpack_rpc_to_dictionary(&(unpacked.data), &(entry->data.header))) { - emsgf(_(READERR("header", "is not a dictionary")), initial_fpos); - goto shada_read_next_item_error; - } - break; + switch ((ShadaEntryType)type_u64) { + case kSDItemHeader: + if (!msgpack_rpc_to_dictionary(&(unpacked.data), &(entry->data.header))) { + emsgf(_(READERR("header", "is not a dictionary")), initial_fpos); + goto shada_read_next_item_error; } - case kSDItemSearchPattern: { - if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgf(_(READERR("search pattern", "is not a dictionary")), - initial_fpos); - goto shada_read_next_item_error; - } - garray_T ad_ga; - ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); - for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR(unpacked, "search pattern") - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_MAGIC, - entry->data.search_pattern.magic) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_SMARTCASE, - entry->data.search_pattern.smartcase) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_HAS_LINE_OFFSET, - entry->data.search_pattern.has_line_offset) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_PLACE_CURSOR_AT_END, - entry->data.search_pattern.place_cursor_at_end) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_IS_LAST_USED, - entry->data.search_pattern.is_last_used) - BOOLEAN_KEY(unpacked, "search pattern", - SEARCH_KEY_IS_SUBSTITUTE_PATTERN, - entry->data.search_pattern.is_substitute_pattern) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_HIGHLIGHTED, - entry->data.search_pattern.highlighted) - BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_BACKWARD, - entry->data.search_pattern.search_backward) - INTEGER_KEY(unpacked, "search pattern", SEARCH_KEY_OFFSET, - entry->data.search_pattern.offset) - CONVERTED_STRING_KEY(unpacked, "search pattern", SEARCH_KEY_PAT, - entry->data.search_pattern.pat) - ADDITIONAL_KEY(unpacked) - } - if (entry->data.search_pattern.pat == NULL) { - emsgf(_(READERR("search pattern", "has no pattern")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - SET_ADDITIONAL_DATA(entry->data.search_pattern.additional_data, - "search pattern"); - break; + break; + case kSDItemSearchPattern: { + if (unpacked.data.type != MSGPACK_OBJECT_MAP) { + emsgf(_(READERR("search pattern", "is not a dictionary")), + initial_fpos); + goto shada_read_next_item_error; } - case kSDItemChange: - case kSDItemJump: - case kSDItemGlobalMark: - case kSDItemLocalMark: { - if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgf(_(READERR("mark", "is not a dictionary")), initial_fpos); - goto shada_read_next_item_error; - } - garray_T ad_ga; - ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); - for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR(unpacked, "mark") - if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, KEY_NAME_CHAR)) { - if (type_u64 == kSDItemJump || type_u64 == kSDItemChange) { - emsgf(_(READERR("mark", "has n key which is only valid for " - "local and global mark entries")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - CHECKED_ENTRY( - (unpacked.data.via.map.ptr[i].val.type - == MSGPACK_OBJECT_POSITIVE_INTEGER), - "has n key value which is not an unsigned integer", - "mark", unpacked.data.via.map.ptr[i].val, - entry->data.filemark.name, u64, TOCHAR); + garray_T ad_ga; + ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); + for (size_t i = 0; i < unpacked.data.via.map.size; i++) { + CHECK_KEY_IS_STR(unpacked, "search pattern") + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_MAGIC, + entry->data.search_pattern.magic) + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_SMARTCASE, + entry->data.search_pattern.smartcase) + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_HAS_LINE_OFFSET, + entry->data.search_pattern.has_line_offset) + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_PLACE_CURSOR_AT_END, + entry->data.search_pattern.place_cursor_at_end) + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_IS_LAST_USED, + entry->data.search_pattern.is_last_used) + BOOLEAN_KEY(unpacked, "search pattern", + SEARCH_KEY_IS_SUBSTITUTE_PATTERN, + entry->data.search_pattern.is_substitute_pattern) + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_HIGHLIGHTED, + entry->data.search_pattern.highlighted) + BOOLEAN_KEY(unpacked, "search pattern", SEARCH_KEY_BACKWARD, + entry->data.search_pattern.search_backward) + INTEGER_KEY(unpacked, "search pattern", SEARCH_KEY_OFFSET, + entry->data.search_pattern.offset) + CONVERTED_STRING_KEY(unpacked, "search pattern", SEARCH_KEY_PAT, + entry->data.search_pattern.pat) + ADDITIONAL_KEY(unpacked) + } + if (entry->data.search_pattern.pat == NULL) { + emsgf(_(READERR("search pattern", "has no pattern")), initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); + } + SET_ADDITIONAL_DATA(entry->data.search_pattern.additional_data, + "search pattern"); + break; + } + case kSDItemChange: + case kSDItemJump: + case kSDItemGlobalMark: + case kSDItemLocalMark: { + if (unpacked.data.type != MSGPACK_OBJECT_MAP) { + emsgf(_(READERR("mark", "is not a dictionary")), initial_fpos); + goto shada_read_next_item_error; + } + garray_T ad_ga; + ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); + for (size_t i = 0; i < unpacked.data.via.map.size; i++) { + CHECK_KEY_IS_STR(unpacked, "mark") + if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, KEY_NAME_CHAR)) { + if (type_u64 == kSDItemJump || type_u64 == kSDItemChange) { + emsgf(_(READERR("mark", "has n key which is only valid for " + "local and global mark entries")), initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); } - LONG_KEY(unpacked, "mark", KEY_LNUM, entry->data.filemark.mark.lnum) - INTEGER_KEY(unpacked, "mark", KEY_COL, entry->data.filemark.mark.col) - STRING_KEY(unpacked, "mark", KEY_FILE, entry->data.filemark.fname) - ADDITIONAL_KEY(unpacked) + CHECKED_ENTRY((unpacked.data.via.map.ptr[i].val.type + == MSGPACK_OBJECT_POSITIVE_INTEGER), + "has n key value which is not an unsigned integer", + "mark", unpacked.data.via.map.ptr[i].val, + entry->data.filemark.name, u64, TOCHAR); } - if (entry->data.filemark.fname == NULL) { - emsgf(_(READERR("mark", "is missing file name")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - if (entry->data.filemark.mark.lnum <= 0) { - emsgf(_(READERR("mark", "has invalid line number")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - if (entry->data.filemark.mark.col < 0) { - emsgf(_(READERR("mark", "has invalid column number")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - SET_ADDITIONAL_DATA(entry->data.filemark.additional_data, "mark"); - break; + LONG_KEY(unpacked, "mark", KEY_LNUM, entry->data.filemark.mark.lnum) + INTEGER_KEY(unpacked, "mark", KEY_COL, entry->data.filemark.mark.col) + STRING_KEY(unpacked, "mark", KEY_FILE, entry->data.filemark.fname) + ADDITIONAL_KEY(unpacked) } - case kSDItemRegister: { - if (unpacked.data.type != MSGPACK_OBJECT_MAP) { - emsgf(_(READERR("register", "is not a dictionary")), initial_fpos); - goto shada_read_next_item_error; - } - garray_T ad_ga; - ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); - for (size_t i = 0; i < unpacked.data.via.map.size; i++) { - CHECK_KEY_IS_STR(unpacked, "register") - if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, - REG_KEY_CONTENTS)) { - if (unpacked.data.via.map.ptr[i].val.type != MSGPACK_OBJECT_ARRAY) { - emsgf(_(READERR("register", - "has " REG_KEY_CONTENTS - " key with non-array value")), - initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - if (unpacked.data.via.map.ptr[i].val.via.array.size == 0) { - emsgf(_(READERR("register", - "has " REG_KEY_CONTENTS " key with empty array")), - initial_fpos); + if (entry->data.filemark.fname == NULL) { + emsgf(_(READERR("mark", "is missing file name")), initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); + } + if (entry->data.filemark.mark.lnum <= 0) { + emsgf(_(READERR("mark", "has invalid line number")), initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); + } + if (entry->data.filemark.mark.col < 0) { + emsgf(_(READERR("mark", "has invalid column number")), initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); + } + SET_ADDITIONAL_DATA(entry->data.filemark.additional_data, "mark"); + break; + } + case kSDItemRegister: { + if (unpacked.data.type != MSGPACK_OBJECT_MAP) { + emsgf(_(READERR("register", "is not a dictionary")), initial_fpos); + goto shada_read_next_item_error; + } + garray_T ad_ga; + ga_init(&ad_ga, sizeof(*(unpacked.data.via.map.ptr)), 1); + for (size_t i = 0; i < unpacked.data.via.map.size; i++) { + CHECK_KEY_IS_STR(unpacked, "register") + if (CHECK_KEY(unpacked.data.via.map.ptr[i].key, + REG_KEY_CONTENTS)) { + if (unpacked.data.via.map.ptr[i].val.type != MSGPACK_OBJECT_ARRAY) { + emsgf(_(READERR("register", + "has " REG_KEY_CONTENTS + " key with non-array value")), + initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); + } + if (unpacked.data.via.map.ptr[i].val.via.array.size == 0) { + emsgf(_(READERR("register", + "has " REG_KEY_CONTENTS " key with empty array")), + initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); + } + const msgpack_object_array arr = + unpacked.data.via.map.ptr[i].val.via.array; + for (size_t j = 0; j < arr.size; j++) { + if (arr.ptr[j].type != MSGPACK_OBJECT_BIN) { + emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " array " + "with non-binary value")), initial_fpos); CLEAR_GA_AND_ERROR_OUT(ad_ga); } - const msgpack_object_array arr = - unpacked.data.via.map.ptr[i].val.via.array; - for (size_t j = 0; j < arr.size; j++) { - if (arr.ptr[j].type != MSGPACK_OBJECT_BIN) { - emsgf(_(READERR("register", "has " REG_KEY_CONTENTS " array " - "with non-binary value")), initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - } - entry->data.reg.contents_size = arr.size; - entry->data.reg.contents = xmalloc(arr.size * sizeof(char *)); - for (size_t j = 0; j < arr.size; j++) { - entry->data.reg.contents[j] = BIN_CONVERTED(arr.ptr[j].via.bin); - } } - BOOLEAN_KEY(unpacked, "register", REG_KEY_UNNAMED, - entry->data.reg.is_unnamed) - TYPED_KEY(unpacked, "register", REG_KEY_TYPE, "an unsigned integer", - entry->data.reg.type, POSITIVE_INTEGER, u64, TOU8) - TYPED_KEY(unpacked, "register", KEY_NAME_CHAR, "an unsigned integer", - entry->data.reg.name, POSITIVE_INTEGER, u64, TOCHAR) - TYPED_KEY(unpacked, "register", REG_KEY_WIDTH, "an unsigned integer", - entry->data.reg.width, POSITIVE_INTEGER, u64, TOSIZE) - ADDITIONAL_KEY(unpacked) - } - if (entry->data.reg.contents == NULL) { - emsgf(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")), - initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); + entry->data.reg.contents_size = arr.size; + entry->data.reg.contents = xmalloc(arr.size * sizeof(char *)); + for (size_t j = 0; j < arr.size; j++) { + entry->data.reg.contents[j] = BIN_CONVERTED(arr.ptr[j].via.bin); + } } - SET_ADDITIONAL_DATA(entry->data.reg.additional_data, "register"); - break; + BOOLEAN_KEY(unpacked, "register", REG_KEY_UNNAMED, + entry->data.reg.is_unnamed) + TYPED_KEY(unpacked, "register", REG_KEY_TYPE, "an unsigned integer", + entry->data.reg.type, POSITIVE_INTEGER, u64, TOU8) + TYPED_KEY(unpacked, "register", KEY_NAME_CHAR, "an unsigned integer", + entry->data.reg.name, POSITIVE_INTEGER, u64, TOCHAR) + TYPED_KEY(unpacked, "register", REG_KEY_WIDTH, "an unsigned integer", + entry->data.reg.width, POSITIVE_INTEGER, u64, TOSIZE) + ADDITIONAL_KEY(unpacked) } - case kSDItemHistoryEntry: { - if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgf(_(READERR("history", "is not an array")), initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.size < 2) { - emsgf(_(READERR("history", "does not have enough elements")), - initial_fpos); + if (entry->data.reg.contents == NULL) { + emsgf(_(READERR("register", "has missing " REG_KEY_CONTENTS " array")), + initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); + } + SET_ADDITIONAL_DATA(entry->data.reg.additional_data, "register"); + break; + } + case kSDItemHistoryEntry: { + if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { + emsgf(_(READERR("history", "is not an array")), initial_fpos); + goto shada_read_next_item_error; + } + if (unpacked.data.via.array.size < 2) { + emsgf(_(READERR("history", "does not have enough elements")), + initial_fpos); + goto shada_read_next_item_error; + } + if (unpacked.data.via.array.ptr[0].type + != MSGPACK_OBJECT_POSITIVE_INTEGER) { + emsgf(_(READERR("history", "has wrong history type type")), + initial_fpos); + goto shada_read_next_item_error; + } + if (unpacked.data.via.array.ptr[1].type + != MSGPACK_OBJECT_BIN) { + emsgf(_(READERR("history", "has wrong history string type")), + initial_fpos); + goto shada_read_next_item_error; + } + if (memchr(unpacked.data.via.array.ptr[1].via.bin.ptr, 0, + unpacked.data.via.array.ptr[1].via.bin.size) != NULL) { + emsgf(_(READERR("history", "contains string with zero byte inside")), + initial_fpos); + goto shada_read_next_item_error; + } + entry->data.history_item.histtype = + (uint8_t)unpacked.data.via.array.ptr[0].via.u64; + const bool is_hist_search = + entry->data.history_item.histtype == HIST_SEARCH; + if (is_hist_search) { + if (unpacked.data.via.array.size < 3) { + emsgf(_(READERR("search history", + "does not have separator character")), initial_fpos); goto shada_read_next_item_error; } - if (unpacked.data.via.array.ptr[0].type + if (unpacked.data.via.array.ptr[2].type != MSGPACK_OBJECT_POSITIVE_INTEGER) { - emsgf(_(READERR("history", "has wrong history type type")), - initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.ptr[1].type - != MSGPACK_OBJECT_BIN) { - emsgf(_(READERR("history", "has wrong history string type")), - initial_fpos); + emsgf(_(READERR("search history", + "has wrong history separator type")), initial_fpos); goto shada_read_next_item_error; } - if (memchr(unpacked.data.via.array.ptr[1].via.bin.ptr, 0, - unpacked.data.via.array.ptr[1].via.bin.size) != NULL) { - emsgf(_(READERR("history", "contains string with zero byte inside")), - initial_fpos); - goto shada_read_next_item_error; - } - entry->data.history_item.histtype = - (uint8_t) unpacked.data.via.array.ptr[0].via.u64; - const bool is_hist_search = - entry->data.history_item.histtype == HIST_SEARCH; - if (is_hist_search) { - if (unpacked.data.via.array.size < 3) { - emsgf(_(READERR("search history", - "does not have separator character")), initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.ptr[2].type - != MSGPACK_OBJECT_POSITIVE_INTEGER) { - emsgf(_(READERR("search history", - "has wrong history separator type")), initial_fpos); + entry->data.history_item.sep = + (char)unpacked.data.via.array.ptr[2].via.u64; + } + size_t strsize; + strsize = ( + unpacked.data.via.array.ptr[1].via.bin.size + + 1 // Zero byte + + 1); // Separator character + entry->data.history_item.string = xmalloc(strsize); + memcpy(entry->data.history_item.string, + unpacked.data.via.array.ptr[1].via.bin.ptr, + unpacked.data.via.array.ptr[1].via.bin.size); + entry->data.history_item.string[strsize - 2] = 0; + entry->data.history_item.string[strsize - 1] = + entry->data.history_item.sep; + SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, (2 + is_hist_search), + entry->data.history_item.additional_elements, + "history"); + break; + } + case kSDItemVariable: { + if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { + emsgf(_(READERR("variable", "is not an array")), initial_fpos); + goto shada_read_next_item_error; + } + if (unpacked.data.via.array.size < 2) { + emsgf(_(READERR("variable", "does not have enough elements")), + initial_fpos); + goto shada_read_next_item_error; + } + if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) { + emsgf(_(READERR("variable", "has wrong variable name type")), + initial_fpos); + goto shada_read_next_item_error; + } + entry->data.global_var.name = + xmemdupz(unpacked.data.via.array.ptr[0].via.bin.ptr, + unpacked.data.via.array.ptr[0].via.bin.size); + SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2, + entry->data.global_var.additional_elements, + "variable"); + bool is_blob = false; + // A msgpack BIN could be a String or Blob; an additional VAR_TYPE_BLOB + // element is stored with Blobs which can be used to differentiate them + if (unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_BIN) { + const listitem_T *type_item + = tv_list_first(entry->data.global_var.additional_elements); + if (type_item != NULL) { + const typval_T *type_tv = TV_LIST_ITEM_TV(type_item); + if (type_tv->v_type != VAR_NUMBER + || type_tv->vval.v_number != VAR_TYPE_BLOB) { + emsgf(_(READERR("variable", "has wrong variable type")), + initial_fpos); goto shada_read_next_item_error; } - entry->data.history_item.sep = - (char) unpacked.data.via.array.ptr[2].via.u64; + is_blob = true; } - size_t strsize; - strsize = ( - unpacked.data.via.array.ptr[1].via.bin.size - + 1 // Zero byte - + 1); // Separator character - entry->data.history_item.string = xmalloc(strsize); - memcpy(entry->data.history_item.string, - unpacked.data.via.array.ptr[1].via.bin.ptr, - unpacked.data.via.array.ptr[1].via.bin.size); - entry->data.history_item.string[strsize - 2] = 0; - entry->data.history_item.string[strsize - 1] = - entry->data.history_item.sep; - SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, (2 + is_hist_search), - entry->data.history_item.additional_elements, - "history"); - break; } - case kSDItemVariable: { - if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgf(_(READERR("variable", "is not an array")), initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.size < 2) { - emsgf(_(READERR("variable", "does not have enough elements")), - initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) { - emsgf(_(READERR("variable", "has wrong variable name type")), - initial_fpos); - goto shada_read_next_item_error; - } - entry->data.global_var.name = - xmemdupz(unpacked.data.via.array.ptr[0].via.bin.ptr, - unpacked.data.via.array.ptr[0].via.bin.size); - SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 2, - entry->data.global_var.additional_elements, - "variable"); - bool is_blob = false; - // A msgpack BIN could be a String or Blob; an additional VAR_TYPE_BLOB - // element is stored with Blobs which can be used to differentiate them - if (unpacked.data.via.array.ptr[1].type == MSGPACK_OBJECT_BIN) { - const listitem_T *type_item - = tv_list_first(entry->data.global_var.additional_elements); - if (type_item != NULL) { - const typval_T *type_tv = TV_LIST_ITEM_TV(type_item); - if (type_tv->v_type != VAR_NUMBER - || type_tv->vval.v_number != VAR_TYPE_BLOB) { - emsgf(_(READERR("variable", "has wrong variable type")), - initial_fpos); - goto shada_read_next_item_error; - } - is_blob = true; - } - } - if (is_blob) { - const msgpack_object_bin *const bin - = &unpacked.data.via.array.ptr[1].via.bin; - blob_T *const blob = tv_blob_alloc(); - ga_concat_len(&blob->bv_ga, bin->ptr, (size_t)bin->size); - tv_blob_set_ret(&entry->data.global_var.value, blob); - } else if (msgpack_to_vim(unpacked.data.via.array.ptr[1], - &(entry->data.global_var.value)) == FAIL) { - emsgf(_(READERR("variable", "has value that cannot " - "be converted to the VimL value")), initial_fpos); - goto shada_read_next_item_error; - } - break; + if (is_blob) { + const msgpack_object_bin *const bin + = &unpacked.data.via.array.ptr[1].via.bin; + blob_T *const blob = tv_blob_alloc(); + ga_concat_len(&blob->bv_ga, bin->ptr, (size_t)bin->size); + tv_blob_set_ret(&entry->data.global_var.value, blob); + } else if (msgpack_to_vim(unpacked.data.via.array.ptr[1], + &(entry->data.global_var.value)) == FAIL) { + emsgf(_(READERR("variable", "has value that cannot " + "be converted to the VimL value")), initial_fpos); + goto shada_read_next_item_error; } - case kSDItemSubString: { - if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgf(_(READERR("sub string", "is not an array")), initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.size < 1) { - emsgf(_(READERR("sub string", "does not have enough elements")), - initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) { - emsgf(_(READERR("sub string", "has wrong sub string type")), - initial_fpos); - goto shada_read_next_item_error; - } - entry->data.sub_string.sub = - BIN_CONVERTED(unpacked.data.via.array.ptr[0].via.bin); - SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 1, - entry->data.sub_string.additional_elements, - "sub string"); + break; + } + case kSDItemSubString: + if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { + emsgf(_(READERR("sub string", "is not an array")), initial_fpos); + goto shada_read_next_item_error; + } + if (unpacked.data.via.array.size < 1) { + emsgf(_(READERR("sub string", "does not have enough elements")), + initial_fpos); + goto shada_read_next_item_error; + } + if (unpacked.data.via.array.ptr[0].type != MSGPACK_OBJECT_BIN) { + emsgf(_(READERR("sub string", "has wrong sub string type")), + initial_fpos); + goto shada_read_next_item_error; + } + entry->data.sub_string.sub = + BIN_CONVERTED(unpacked.data.via.array.ptr[0].via.bin); + SET_ADDITIONAL_ELEMENTS(unpacked.data.via.array, 1, + entry->data.sub_string.additional_elements, + "sub string"); + break; + case kSDItemBufferList: + if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { + emsgf(_(READERR("buffer list", "is not an array")), initial_fpos); + goto shada_read_next_item_error; + } + if (unpacked.data.via.array.size == 0) { break; } - case kSDItemBufferList: { - if (unpacked.data.type != MSGPACK_OBJECT_ARRAY) { - emsgf(_(READERR("buffer list", "is not an array")), initial_fpos); - goto shada_read_next_item_error; - } - if (unpacked.data.via.array.size == 0) { - break; - } - entry->data.buffer_list.buffers = - xcalloc(unpacked.data.via.array.size, - sizeof(*entry->data.buffer_list.buffers)); - for (size_t i = 0; i < unpacked.data.via.array.size; i++) { - entry->data.buffer_list.size++; - msgpack_unpacked unpacked_2 = (msgpack_unpacked) { - .data = unpacked.data.via.array.ptr[i], - }; + entry->data.buffer_list.buffers = + xcalloc(unpacked.data.via.array.size, + sizeof(*entry->data.buffer_list.buffers)); + for (size_t i = 0; i < unpacked.data.via.array.size; i++) { + entry->data.buffer_list.size++; + msgpack_unpacked unpacked_2 = (msgpack_unpacked) { + .data = unpacked.data.via.array.ptr[i], + }; + { + if (unpacked_2.data.type != MSGPACK_OBJECT_MAP) { + emsgf(_(RERR "Error while reading ShaDa file: " + "buffer list at position %" PRIu64 " " + "contains entry that is not a dictionary"), + initial_fpos); + goto shada_read_next_item_error; + } + entry->data.buffer_list.buffers[i].pos = default_pos; + garray_T ad_ga; + ga_init(&ad_ga, sizeof(*(unpacked_2.data.via.map.ptr)), 1); { - if (unpacked_2.data.type != MSGPACK_OBJECT_MAP) { - emsgf(_(RERR "Error while reading ShaDa file: " - "buffer list at position %" PRIu64 " " - "contains entry that is not a dictionary"), - initial_fpos); - goto shada_read_next_item_error; - } - entry->data.buffer_list.buffers[i].pos = default_pos; - garray_T ad_ga; - ga_init(&ad_ga, sizeof(*(unpacked_2.data.via.map.ptr)), 1); + // XXX: Temporarily reassign `i` because the macros depend on it. + const size_t j = i; { - // XXX: Temporarily reassign `i` because the macros depend on it. - const size_t j = i; - { - for (i = 0; i < unpacked_2.data.via.map.size; i++) { // -V535 - CHECK_KEY_IS_STR(unpacked_2, "buffer list entry") - LONG_KEY(unpacked_2, "buffer list entry", KEY_LNUM, - entry->data.buffer_list.buffers[j].pos.lnum) - INTEGER_KEY(unpacked_2, "buffer list entry", KEY_COL, - entry->data.buffer_list.buffers[j].pos.col) - STRING_KEY(unpacked_2, "buffer list entry", KEY_FILE, - entry->data.buffer_list.buffers[j].fname) - ADDITIONAL_KEY(unpacked_2) - } + for (i = 0; i < unpacked_2.data.via.map.size; i++) { // -V535 + CHECK_KEY_IS_STR(unpacked_2, "buffer list entry") + LONG_KEY(unpacked_2, "buffer list entry", KEY_LNUM, + entry->data.buffer_list.buffers[j].pos.lnum) + INTEGER_KEY(unpacked_2, "buffer list entry", KEY_COL, + entry->data.buffer_list.buffers[j].pos.col) + STRING_KEY(unpacked_2, "buffer list entry", KEY_FILE, + entry->data.buffer_list.buffers[j].fname) + ADDITIONAL_KEY(unpacked_2) } - i = j; // XXX: Restore `i`. } - if (entry->data.buffer_list.buffers[i].pos.lnum <= 0) { - emsgf(_(RERR "Error while reading ShaDa file: " - "buffer list at position %" PRIu64 " " - "contains entry with invalid line number"), - initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - if (entry->data.buffer_list.buffers[i].pos.col < 0) { - emsgf(_(RERR "Error while reading ShaDa file: " - "buffer list at position %" PRIu64 " " - "contains entry with invalid column number"), - initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - if (entry->data.buffer_list.buffers[i].fname == NULL) { - emsgf(_(RERR "Error while reading ShaDa file: " - "buffer list at position %" PRIu64 " " - "contains entry that does not have a file name"), - initial_fpos); - CLEAR_GA_AND_ERROR_OUT(ad_ga); - } - SET_ADDITIONAL_DATA( - entry->data.buffer_list.buffers[i].additional_data, - "buffer list entry"); + i = j; // XXX: Restore `i`. + } + if (entry->data.buffer_list.buffers[i].pos.lnum <= 0) { + emsgf(_(RERR "Error while reading ShaDa file: " + "buffer list at position %" PRIu64 " " + "contains entry with invalid line number"), + initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); + } + if (entry->data.buffer_list.buffers[i].pos.col < 0) { + emsgf(_(RERR "Error while reading ShaDa file: " + "buffer list at position %" PRIu64 " " + "contains entry with invalid column number"), + initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); } + if (entry->data.buffer_list.buffers[i].fname == NULL) { + emsgf(_(RERR "Error while reading ShaDa file: " + "buffer list at position %" PRIu64 " " + "contains entry that does not have a file name"), + initial_fpos); + CLEAR_GA_AND_ERROR_OUT(ad_ga); + } + SET_ADDITIONAL_DATA(entry->data.buffer_list.buffers[i].additional_data, + "buffer list entry"); } - break; - } - case kSDItemMissing: - case kSDItemUnknown: { - abort(); } + break; + case kSDItemMissing: + case kSDItemUnknown: + abort(); } - entry->type = (ShadaEntryType) type_u64; + entry->type = (ShadaEntryType)type_u64; ret = kSDReadStatusSuccess; shada_read_next_item_end: if (buf != NULL) { @@ -4086,7 +3976,7 @@ shada_read_next_item_end: } return ret; shada_read_next_item_error: - entry->type = (ShadaEntryType) type_u64; + entry->type = (ShadaEntryType)type_u64; shada_free_shada_entry(entry); entry->type = kSDItemMissing; goto shada_read_next_item_end; @@ -4121,13 +4011,13 @@ shada_read_next_item_error: static bool shada_removable(const char *name) FUNC_ATTR_WARN_UNUSED_RESULT { - char *p; + char *p; char part[MAXPATHL + 1]; bool retval = false; char *new_name = home_replace_save(NULL, name); - for (p = (char *) p_shada; *p; ) { - (void) copy_option_part(&p, part, ARRAY_SIZE(part), ", "); + for (p = (char *)p_shada; *p; ) { + (void)copy_option_part(&p, part, ARRAY_SIZE(part), ", "); if (part[0] == 'r') { home_replace(NULL, part + 1, NameBuff, MAXPATHL, true); size_t n = STRLEN(NameBuff); @@ -4148,8 +4038,8 @@ static bool shada_removable(const char *name) /// location. /// /// @return number of jumplist entries -static inline size_t shada_init_jumps( - PossiblyFreedShadaEntry *jumps, khash_t(bufset) *const removable_bufs) +static inline size_t shada_init_jumps(PossiblyFreedShadaEntry *jumps, + khash_t(bufset) *const removable_bufs) { if (!curwin->w_jumplistlen) { return 0; @@ -4176,9 +4066,9 @@ static inline size_t shada_init_jumps( : fm.fmark.fnum != 0) { continue; } - const char *const fname = (char *) (fm.fmark.fnum == 0 + const char *const fname = (char *)(fm.fmark.fnum == 0 ? (fm.fname == NULL ? NULL : fm.fname) - : buf ? buf->b_ffname : NULL); + : buf ? buf->b_ffname : NULL); if (fname == NULL) { continue; } @@ -4191,7 +4081,7 @@ static inline size_t shada_init_jumps( .filemark = { .name = NUL, .mark = fm.fmark.mark, - .fname = (char *) fname, + .fname = (char *)fname, .additional_data = fm.fmark.additional_data, } } @@ -4272,9 +4162,8 @@ void shada_encode_gvars(msgpack_sbuffer *const sbuf) do { typval_T vartv; const char *name = NULL; - var_iter = var_shada_iter( - var_iter, &name, &vartv, - VAR_FLAVOUR_DEFAULT | VAR_FLAVOUR_SESSION | VAR_FLAVOUR_SHADA); + var_iter = var_shada_iter(var_iter, &name, &vartv, + VAR_FLAVOUR_DEFAULT | VAR_FLAVOUR_SESSION | VAR_FLAVOUR_SHADA); if (name == NULL) { break; } @@ -4304,8 +4193,7 @@ void shada_encode_gvars(msgpack_sbuffer *const sbuf) /// Wrapper for reading from msgpack_sbuffer. /// /// @return number of bytes read. -static ptrdiff_t read_sbuf(ShaDaReadDef *const sd_reader, void *const dest, - const size_t size) +static ptrdiff_t read_sbuf(ShaDaReadDef *const sd_reader, void *const dest, const size_t size) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { msgpack_sbuffer *sbuf = (msgpack_sbuffer *)sd_reader->cookie; @@ -4327,8 +4215,7 @@ static ptrdiff_t read_sbuf(ShaDaReadDef *const sd_reader, void *const dest, /// /// @return FAIL in case of failure, OK in case of success. May set /// sd_reader->eof. -static int sd_sbuf_reader_skip_read(ShaDaReadDef *const sd_reader, - const size_t offset) +static int sd_sbuf_reader_skip_read(ShaDaReadDef *const sd_reader, const size_t offset) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { msgpack_sbuffer *sbuf = (msgpack_sbuffer *)sd_reader->cookie; @@ -4346,8 +4233,7 @@ static int sd_sbuf_reader_skip_read(ShaDaReadDef *const sd_reader, /// /// @param[in] sbuf msgpack_sbuffer to read from. /// @param[out] sd_reader Location where reader structure will be saved. -static void open_shada_sbuf_for_reading(const msgpack_sbuffer *const sbuf, - ShaDaReadDef *sd_reader) +static void open_shada_sbuf_for_reading(const msgpack_sbuffer *const sbuf, ShaDaReadDef *sd_reader) FUNC_ATTR_NONNULL_ALL { *sd_reader = (ShaDaReadDef) { diff --git a/src/nvim/sign.c b/src/nvim/sign.c index c6f59b42b8..25427de6bf 100644 --- a/src/nvim/sign.c +++ b/src/nvim/sign.c @@ -6,33 +6,33 @@ // -#include "nvim/vim.h" -#include "nvim/sign.h" #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" -#include "nvim/ex_docmd.h" #include "nvim/edit.h" +#include "nvim/ex_docmd.h" #include "nvim/fold.h" #include "nvim/move.h" +#include "nvim/option.h" #include "nvim/screen.h" +#include "nvim/sign.h" #include "nvim/syntax.h" -#include "nvim/option.h" +#include "nvim/vim.h" /// Struct to hold the sign properties. typedef struct sign sign_T; struct sign { - sign_T *sn_next; // next sign in list - int sn_typenr; // type number of sign - char_u *sn_name; // name of sign - char_u *sn_icon; // name of pixmap - char_u *sn_text; // text used instead of pixmap - int sn_line_hl; // highlight ID for line - int sn_text_hl; // highlight ID for text - int sn_num_hl; // highlight ID for line number + sign_T *sn_next; // next sign in list + int sn_typenr; // type number of sign + char_u *sn_name; // name of sign + char_u *sn_icon; // name of pixmap + char_u *sn_text; // text used instead of pixmap + int sn_line_hl; // highlight ID for line + int sn_text_hl; // highlight ID for text + int sn_num_hl; // highlight ID for line number }; static sign_T *first_sign = NULL; @@ -42,19 +42,19 @@ static void sign_list_defined(sign_T *sp); static void sign_undefine(sign_T *sp, sign_T *sp_prev); static char *cmds[] = { - "define", + "define", #define SIGNCMD_DEFINE 0 - "undefine", + "undefine", #define SIGNCMD_UNDEFINE 1 - "list", + "list", #define SIGNCMD_LIST 2 - "place", + "place", #define SIGNCMD_PLACE 3 - "unplace", + "unplace", #define SIGNCMD_UNPLACE 4 - "jump", + "jump", #define SIGNCMD_JUMP 5 - NULL + NULL #define SIGNCMD_LAST 6 }; @@ -65,17 +65,17 @@ static int next_sign_id = 1; // next sign id in the global group /// Initialize data needed for managing signs void init_signs(void) { - hash_init(&sg_table); // sign group hash table + hash_init(&sg_table); // sign group hash table } /// A new sign in group 'groupname' is added. If the group is not present, /// create it. Otherwise reference the group. /// -static signgroup_T * sign_group_ref(const char_u *groupname) +static signgroup_T *sign_group_ref(const char_u *groupname) { - hash_T hash; - hashitem_T *hi; - signgroup_T *group; + hash_T hash; + hashitem_T *hi; + signgroup_T *group; hash = hash_hash(groupname); hi = hash_lookup(&sg_table, (char *)groupname, STRLEN(groupname), hash); @@ -100,7 +100,7 @@ static signgroup_T * sign_group_ref(const char_u *groupname) /// removed, then remove the group. static void sign_group_unref(char_u *groupname) { - hashitem_T *hi; + hashitem_T *hi; signgroup_T *group; hi = hash_find(&sg_table, groupname); @@ -120,10 +120,10 @@ static void sign_group_unref(char_u *groupname) /// or in a named group. If 'group' is '*', then the sign is part of the group. bool sign_in_group(sign_entry_T *sign, const char_u *group) { - return ((group != NULL && STRCMP(group, "*") == 0) - || (group == NULL && sign->se_group == NULL) - || (group != NULL && sign->se_group != NULL - && STRCMP(group, sign->se_group->sg_name) == 0)); + return ((group != NULL && STRCMP(group, "*") == 0) + || (group == NULL && sign->se_group == NULL) + || (group != NULL && sign->se_group != NULL + && STRCMP(group, sign->se_group->sg_name) == 0)); } /// Get the next free sign identifier in the specified group @@ -166,17 +166,19 @@ int sign_group_get_next_signid(buf_T *buf, const char_u *groupname) /// Insert a new sign into the signlist for buffer 'buf' between the 'prev' and /// 'next' signs. -static void insert_sign( - buf_T *buf, // buffer to store sign in - sign_entry_T *prev, // previous sign entry - sign_entry_T *next, // next sign entry - int id, // sign ID - const char_u *group, // sign group; NULL for global group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr, // typenr of sign we are adding - bool has_text_or_icon // sign has text or icon -) +/// +/// @param buf buffer to store sign in +/// @param prev previous sign entry +/// @param next next sign entry +/// @param id sign ID +/// @param group sign group; NULL for global group +/// @param prio sign priority +/// @param lnum line number which gets the mark +/// @param typenr typenr of sign we are adding +/// @param has_text_or_icon sign has text or icon +static void insert_sign(buf_T *buf, sign_entry_T *prev, sign_entry_T *next, int id, + const char_u *group, int prio, linenr_T lnum, int typenr, + bool has_text_or_icon) { sign_entry_T *newsign = xmalloc(sizeof(sign_entry_T)); newsign->se_id = id; @@ -212,18 +214,19 @@ static void insert_sign( } /// Insert a new sign sorted by line number and sign priority. -static void insert_sign_by_lnum_prio( - buf_T *buf, // buffer to store sign in - sign_entry_T *prev, // previous sign entry - int id, // sign ID - const char_u *group, // sign group; NULL for global group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr, // typenr of sign we are adding - bool has_text_or_icon // sign has text or icon -) +/// +/// @param buf buffer to store sign in +/// @param prev previous sign entry +/// @param id sign ID +/// @param group sign group; NULL for global group +/// @param prio sign priority +/// @param lnum line number which gets the mark +/// @param typenr typenr of sign we are adding +/// @param has_text_or_icon sign has text or icon +static void insert_sign_by_lnum_prio(buf_T *buf, sign_entry_T *prev, int id, const char_u *group, + int prio, linenr_T lnum, int typenr, bool has_text_or_icon) { - sign_entry_T *sign; + sign_entry_T *sign; // keep signs sorted by lnum, priority and id: insert new sign at // the proper position in the list for this lnum. @@ -242,9 +245,9 @@ static void insert_sign_by_lnum_prio( } /// Get the name of a sign by its typenr. -char_u * sign_typenr2name(int typenr) +char_u *sign_typenr2name(int typenr) { - sign_T *sp; + sign_T *sp; for (sp = first_sign; sp != NULL; sp = sp->sn_next) { if (sp->sn_typenr == typenr) { @@ -255,9 +258,9 @@ char_u * sign_typenr2name(int typenr) } /// Return information about a sign in a Dict -dict_T * sign_get_info(sign_entry_T *sign) +dict_T *sign_get_info(sign_entry_T *sign) { - dict_T *d = tv_dict_alloc(); + dict_T *d = tv_dict_alloc(); tv_dict_add_nr(d, S_LEN("id"), sign->se_id); tv_dict_add_str(d, S_LEN("group"), ((sign->se_group == NULL) ? (char *)"" @@ -345,15 +348,16 @@ static void sign_sort_by_prio_on_line(buf_T *buf, sign_entry_T *sign) /// Add the sign into the signlist. Find the right spot to do it though. -void buf_addsign( - buf_T *buf, // buffer to store sign in - int id, // sign ID - const char_u *groupname, // sign group - int prio, // sign priority - linenr_T lnum, // line number which gets the mark - int typenr, // typenr of sign we are adding - bool has_text_or_icon // sign has text or icon -) +/// +/// @param buf buffer to store sign in +/// @param id sign ID +/// @param groupname sign group +/// @param prio sign priority +/// @param lnum line number which gets the mark +/// @param typenr typenr of sign we are adding +/// @param has_text_or_icon sign has text or icon +void buf_addsign(buf_T *buf, int id, const char_u *groupname, int prio, linenr_T lnum, int typenr, + bool has_text_or_icon) { sign_entry_T *sign; // a sign in the signlist sign_entry_T *prev; // the previous sign @@ -368,53 +372,51 @@ void buf_addsign( sign_sort_by_prio_on_line(buf, sign); return; } else if (lnum < sign->se_lnum) { - insert_sign_by_lnum_prio( - buf, - prev, - id, - groupname, - prio, - lnum, - typenr, - has_text_or_icon); + insert_sign_by_lnum_prio(buf, + prev, + id, + groupname, + prio, + lnum, + typenr, + has_text_or_icon); return; } prev = sign; } - insert_sign_by_lnum_prio( - buf, - prev, - id, - groupname, - prio, - lnum, - typenr, - has_text_or_icon); + insert_sign_by_lnum_prio(buf, + prev, + id, + groupname, + prio, + lnum, + typenr, + has_text_or_icon); } -// For an existing, placed sign "markId" change the type to "typenr". -// Returns the line number of the sign, or zero if the sign is not found. -linenr_T buf_change_sign_type( - buf_T *buf, // buffer to store sign in - int markId, // sign ID - const char_u *group, // sign group - int typenr, // typenr of sign we are adding - int prio // sign priority -) +/// For an existing, placed sign "markId" change the type to "typenr". +/// Returns the line number of the sign, or zero if the sign is not found. +/// +/// @param buf buffer to store sign in +/// @param markId sign ID +/// @param group sign group +/// @param typenr typenr of sign we are adding +/// @param prio sign priority +linenr_T buf_change_sign_type(buf_T *buf, int markId, const char_u *group, int typenr, int prio) { - sign_entry_T *sign; // a sign in the signlist + sign_entry_T *sign; // a sign in the signlist - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->se_id == markId && sign_in_group(sign, group)) { - sign->se_typenr = typenr; - sign->se_priority = prio; - sign_sort_by_prio_on_line(buf, sign); - return sign->se_lnum; - } + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (sign->se_id == markId && sign_in_group(sign, group)) { + sign->se_typenr = typenr; + sign->se_priority = prio; + sign_sort_by_prio_on_line(buf, sign); + return sign->se_lnum; } + } - return (linenr_T)0; + return (linenr_T)0; } /// Return the sign attrs which has the attribute specified by 'type'. Returns @@ -426,44 +428,43 @@ linenr_T buf_change_sign_type( /// @param max_signs the number of signs, with priority for the ones /// with the highest Ids. /// @return Attrs of the matching sign, or NULL -sign_attrs_T * sign_get_attr(SignType type, sign_attrs_T sattrs[], - int idx, int max_signs) +sign_attrs_T *sign_get_attr(SignType type, sign_attrs_T sattrs[], int idx, int max_signs) { - sign_attrs_T *matches[SIGN_SHOW_MAX]; - int nr_matches = 0; - - for (int i = 0; i < SIGN_SHOW_MAX; i++) { - if ( (type == SIGN_TEXT && sattrs[i].sat_text != NULL) - || (type == SIGN_LINEHL && sattrs[i].sat_linehl != 0) - || (type == SIGN_NUMHL && sattrs[i].sat_numhl != 0)) { - matches[nr_matches] = &sattrs[i]; - nr_matches++; - // attr list is sorted with most important (priority, id), thus we - // may stop as soon as we have max_signs matches - if (nr_matches >= max_signs) { - break; - } - } + sign_attrs_T *matches[SIGN_SHOW_MAX]; + int nr_matches = 0; + + for (int i = 0; i < SIGN_SHOW_MAX; i++) { + if ( (type == SIGN_TEXT && sattrs[i].sat_text != NULL) + || (type == SIGN_LINEHL && sattrs[i].sat_linehl != 0) + || (type == SIGN_NUMHL && sattrs[i].sat_numhl != 0)) { + matches[nr_matches] = &sattrs[i]; + nr_matches++; + // attr list is sorted with most important (priority, id), thus we + // may stop as soon as we have max_signs matches + if (nr_matches >= max_signs) { + break; + } } + } - if (nr_matches > idx) { - return matches[nr_matches - idx - 1]; - } + if (nr_matches > idx) { + return matches[nr_matches - idx - 1]; + } - return NULL; + return NULL; } /// Lookup a sign by typenr. Returns NULL if sign is not found. -static sign_T * find_sign_by_typenr(int typenr) +static sign_T *find_sign_by_typenr(int typenr) { - sign_T *sp; + sign_T *sp; - for (sp = first_sign; sp != NULL; sp = sp->sn_next) { - if (sp->sn_typenr == typenr) { - return sp; - } + for (sp = first_sign; sp != NULL; sp = sp->sn_next) { + if (sp->sn_typenr == typenr) { + return sp; } - return NULL; + } + return NULL; } /// Return the attributes of all the signs placed on line 'lnum' in buffer @@ -474,44 +475,44 @@ static sign_T * find_sign_by_typenr(int typenr) /// @return Number of signs of which attrs were found int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[]) { - sign_entry_T *sign; - sign_T *sp; + sign_entry_T *sign; + sign_T *sp; - int nr_matches = 0; + int nr_matches = 0; - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->se_lnum > lnum) { - // Signs are sorted by line number in the buffer. No need to check - // for signs after the specified line number 'lnum'. - break; - } + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (sign->se_lnum > lnum) { + // Signs are sorted by line number in the buffer. No need to check + // for signs after the specified line number 'lnum'. + break; + } - if (sign->se_lnum == lnum) { - sign_attrs_T sattr; - memset(&sattr, 0, sizeof(sattr)); - sattr.sat_typenr = sign->se_typenr; - sp = find_sign_by_typenr(sign->se_typenr); - if (sp != NULL) { - sattr.sat_text = sp->sn_text; - if (sattr.sat_text != NULL && sp->sn_text_hl != 0) { - sattr.sat_texthl = syn_id2attr(sp->sn_text_hl); - } - if (sp->sn_line_hl != 0) { - sattr.sat_linehl = syn_id2attr(sp->sn_line_hl); - } - if (sp->sn_num_hl != 0) { - sattr.sat_numhl = syn_id2attr(sp->sn_num_hl); - } - } - - sattrs[nr_matches] = sattr; - nr_matches++; - if (nr_matches == SIGN_SHOW_MAX) { - break; - } + if (sign->se_lnum == lnum) { + sign_attrs_T sattr; + memset(&sattr, 0, sizeof(sattr)); + sattr.sat_typenr = sign->se_typenr; + sp = find_sign_by_typenr(sign->se_typenr); + if (sp != NULL) { + sattr.sat_text = sp->sn_text; + if (sattr.sat_text != NULL && sp->sn_text_hl != 0) { + sattr.sat_texthl = syn_id2attr(sp->sn_text_hl); + } + if (sp->sn_line_hl != 0) { + sattr.sat_linehl = syn_id2attr(sp->sn_line_hl); + } + if (sp->sn_num_hl != 0) { + sattr.sat_numhl = syn_id2attr(sp->sn_num_hl); } + } + + sattrs[nr_matches] = sattr; + nr_matches++; + if (nr_matches == SIGN_SHOW_MAX) { + break; + } } - return nr_matches; + } + return nr_matches; } /// Delete sign 'id' in group 'group' from buffer 'buf'. @@ -520,14 +521,15 @@ int buf_get_signattrs(buf_T *buf, linenr_T lnum, sign_attrs_T sattrs[]) /// If 'group' is '*', then delete the sign in all the groups. If 'group' is /// NULL, then delete the sign in the global group. Otherwise delete the sign in /// the specified group. -/// Returns the line number of the deleted sign. If multiple signs are deleted, +/// +/// @param buf buffer sign is stored in +/// @param atlnum sign at this line, 0 - at any line +/// @param id sign id +/// @param group sign group +/// +/// @return the line number of the deleted sign. If multiple signs are deleted, /// then returns the line number of the last sign deleted. -linenr_T buf_delsign( - buf_T *buf, // buffer sign is stored in - linenr_T atlnum, // sign at this line, 0 - at any line - int id, // sign id - char_u *group // sign group -) +linenr_T buf_delsign(buf_T *buf, linenr_T atlnum, int id, char_u *group) { sign_entry_T **lastp; // pointer to pointer to current sign sign_entry_T *sign; // a sign in a b_signlist @@ -580,30 +582,30 @@ linenr_T buf_delsign( /// Find the line number of the sign with the requested id in group 'group'. If /// the sign does not exist, return 0 as the line number. This will still let /// the correct file get loaded. -int buf_findsign( - buf_T *buf, // buffer to store sign in - int id, // sign ID - char_u *group // sign group -) +/// +/// @param buf buffer to store sign in +/// @param id sign ID +/// @param group sign group +int buf_findsign(buf_T *buf, int id, char_u *group) { - sign_entry_T *sign; // a sign in the signlist + sign_entry_T *sign; // a sign in the signlist - FOR_ALL_SIGNS_IN_BUF(buf, sign) { - if (sign->se_id == id && sign_in_group(sign, group)) { - return (int)sign->se_lnum; - } + FOR_ALL_SIGNS_IN_BUF(buf, sign) { + if (sign->se_id == id && sign_in_group(sign, group)) { + return (int)sign->se_lnum; } + } - return 0; + return 0; } /// Return the sign at line 'lnum' in buffer 'buf'. Returns NULL if a sign is /// not found at the line. If 'groupname' is NULL, searches in the global group. -static sign_entry_T * buf_getsign_at_line( - buf_T *buf, // buffer whose sign we are searching for - linenr_T lnum, // line number of sign - char_u *groupname // sign group name -) +/// +/// @param buf buffer whose sign we are searching for +/// @param lnum line number of sign +/// @param groupname sign group name +static sign_entry_T *buf_getsign_at_line(buf_T *buf, linenr_T lnum, char_u *groupname) { sign_entry_T *sign; // a sign in the signlist @@ -623,61 +625,61 @@ static sign_entry_T * buf_getsign_at_line( } /// Return the identifier of the sign at line number 'lnum' in buffer 'buf'. -int buf_findsign_id( - buf_T *buf, // buffer whose sign we are searching for - linenr_T lnum, // line number of sign - char_u *groupname // sign group name -) +/// +/// @param buf buffer whose sign we are searching for +/// @param lnum line number of sign +/// @param groupname sign group name +int buf_findsign_id(buf_T *buf, linenr_T lnum, char_u *groupname) { - sign_entry_T *sign; // a sign in the signlist + sign_entry_T *sign; // a sign in the signlist - sign = buf_getsign_at_line(buf, lnum, groupname); - if (sign != NULL) { - return sign->se_id; - } + sign = buf_getsign_at_line(buf, lnum, groupname); + if (sign != NULL) { + return sign->se_id; + } - return 0; + return 0; } /// Delete signs in buffer "buf". void buf_delete_signs(buf_T *buf, char_u *group) { - sign_entry_T *sign; - sign_entry_T **lastp; // pointer to pointer to current sign - sign_entry_T *next; + sign_entry_T *sign; + sign_entry_T **lastp; // pointer to pointer to current sign + sign_entry_T *next; - // When deleting the last sign need to redraw the windows to remove the - // sign column. Not when curwin is NULL (this means we're exiting). - if (buf->b_signlist != NULL && curwin != NULL) { - changed_line_abv_curs(); - } + // When deleting the last sign need to redraw the windows to remove the + // sign column. Not when curwin is NULL (this means we're exiting). + if (buf->b_signlist != NULL && curwin != NULL) { + changed_line_abv_curs(); + } - lastp = &buf->b_signlist; - for (sign = buf->b_signlist; sign != NULL; sign = next) { - next = sign->se_next; - if (sign_in_group(sign, group)) { - *lastp = next; - if (next != NULL) { - next->se_prev = sign->se_prev; - } - if (sign->se_group != NULL) { - sign_group_unref(sign->se_group->sg_name); - } - xfree(sign); - } else { - lastp = &sign->se_next; + lastp = &buf->b_signlist; + for (sign = buf->b_signlist; sign != NULL; sign = next) { + next = sign->se_next; + if (sign_in_group(sign, group)) { + *lastp = next; + if (next != NULL) { + next->se_prev = sign->se_prev; + } + if (sign->se_group != NULL) { + sign_group_unref(sign->se_group->sg_name); } + xfree(sign); + } else { + lastp = &sign->se_next; } - buf->b_signcols_valid = false; + } + buf->b_signcols_valid = false; } /// List placed signs for "rbuf". If "rbuf" is NULL do it for all buffers. void sign_list_placed(buf_T *rbuf, char_u *sign_group) { buf_T *buf; - sign_entry_T *sign; - char lbuf[MSG_BUF_LEN]; - char group[MSG_BUF_LEN]; + sign_entry_T *sign; + char lbuf[MSG_BUF_LEN]; + char group[MSG_BUF_LEN]; MSG_PUTS_TITLE(_("\n--- Signs ---")); msg_putchar('\n'); @@ -720,12 +722,7 @@ void sign_list_placed(buf_T *rbuf, char_u *sign_group) } /// Adjust a placed sign for inserted/deleted lines. -void sign_mark_adjust( - linenr_T line1, - linenr_T line2, - long amount, - long amount_after -) +void sign_mark_adjust(linenr_T line1, linenr_T line2, long amount, long amount_after) { sign_entry_T *sign; // a sign in a b_signlist sign_entry_T *next; // the next sign in a b_signlist @@ -769,26 +766,26 @@ void sign_mark_adjust( /// Find index of a ":sign" subcmd from its name. /// "*end_cmd" must be writable. -static int sign_cmd_idx( - char_u *begin_cmd, // begin of sign subcmd - char_u *end_cmd // just after sign subcmd -) +/// +/// @param begin_cmd begin of sign subcmd +/// @param end_cmd just after sign subcmd +static int sign_cmd_idx(char_u *begin_cmd, char_u *end_cmd) { - int idx; - char_u save = *end_cmd; + int idx; + char_u save = *end_cmd; - *end_cmd = (char_u)NUL; - for (idx = 0; ; idx++) { - if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) { - break; - } + *end_cmd = (char_u)NUL; + for (idx = 0; ; idx++) { + if (cmds[idx] == NULL || STRCMP(begin_cmd, cmds[idx]) == 0) { + break; } - *end_cmd = save; - return idx; + } + *end_cmd = save; + return idx; } /// Find a sign by name. Also returns pointer to the previous sign. -static sign_T * sign_find(const char_u *name, sign_T **sp_prev) +static sign_T *sign_find(const char_u *name, sign_T **sp_prev) { sign_T *sp; @@ -808,10 +805,10 @@ static sign_T * sign_find(const char_u *name, sign_T **sp_prev) } /// Allocate a new sign -static sign_T * alloc_new_sign(char_u *name) +static sign_T *alloc_new_sign(char_u *name) { - sign_T *sp; - sign_T *lp; + sign_T *sp; + sign_T *lp; int start = next_sign_typenr; // Allocate a new sign. @@ -858,8 +855,8 @@ static void sign_define_init_icon(sign_T *sp, char_u *icon) /// Initialize the text for a new sign static int sign_define_init_text(sign_T *sp, char_u *text) { - char_u *s; - char_u *endp; + char_u *s; + char_u *endp; int cells; size_t len; @@ -904,17 +901,11 @@ static int sign_define_init_text(sign_T *sp, char_u *text) } /// Define a new sign or update an existing sign -int sign_define_by_name( - char_u *name, - char_u *icon, - char_u *linehl, - char_u *text, - char_u *texthl, - char_u *numhl -) +int sign_define_by_name(char_u *name, char_u *icon, char_u *linehl, char_u *text, char_u *texthl, + char_u *numhl) { - sign_T *sp_prev; - sign_T *sp; + sign_T *sp_prev; + sign_T *sp; sp = sign_find(name, &sp_prev); if (sp == NULL) { @@ -966,8 +957,8 @@ int sign_define_by_name( /// Free the sign specified by 'name'. int sign_undefine_by_name(const char_u *name) { - sign_T *sp_prev; - sign_T *sp; + sign_T *sp_prev; + sign_T *sp; sp = sign_find(name, &sp_prev); if (sp == NULL) { @@ -982,18 +973,18 @@ int sign_undefine_by_name(const char_u *name) static void may_force_numberwidth_recompute(buf_T *buf, int unplace) { FOR_ALL_TAB_WINDOWS(tp, wp) - if (wp->w_buffer == buf - && (wp->w_p_nu || wp->w_p_rnu) - && (unplace || wp->w_nrwidth_width < 2) - && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')) { - wp->w_nrwidth_line_count = 0; - } + if (wp->w_buffer == buf + && (wp->w_p_nu || wp->w_p_rnu) + && (unplace || wp->w_nrwidth_width < 2) + && (*wp->w_p_scl == 'n' && *(wp->w_p_scl + 1) == 'u')) { + wp->w_nrwidth_line_count = 0; + } } /// List the signs matching 'name' static void sign_list_by_name(char_u *name) { - sign_T *sp; + sign_T *sp; sp = sign_find(name, NULL); if (sp != NULL) { @@ -1005,14 +996,8 @@ static void sign_list_by_name(char_u *name) /// Place a sign at the specified file location or update a sign. -int sign_place( - int *sign_id, - const char_u *sign_group, - const char_u *sign_name, - buf_T *buf, - linenr_T lnum, - int prio -) +int sign_place(int *sign_id, const char_u *sign_group, const char_u *sign_name, buf_T *buf, + linenr_T lnum, int prio) { sign_T *sp; @@ -1038,14 +1023,13 @@ int sign_place( // ":sign place {id} line={lnum} name={name} file={fname}": // place a sign bool has_text_or_icon = sp->sn_text != NULL || sp->sn_icon != NULL; - buf_addsign( - buf, - *sign_id, - sign_group, - prio, - lnum, - sp->sn_typenr, - has_text_or_icon); + buf_addsign(buf, + *sign_id, + sign_group, + prio, + lnum, + sp->sn_typenr, + has_text_or_icon); } else { // ":sign place {id} file={fname}": change sign type and/or priority lnum = buf_change_sign_type(buf, *sign_id, sign_group, sp->sn_typenr, prio); @@ -1075,7 +1059,7 @@ int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) redraw_buf_later(buf, NOT_VALID); buf_delete_signs(buf, sign_group); } else { - linenr_T lnum; + linenr_T lnum; // Delete only the specified signs lnum = buf_delsign(buf, atlnum, sign_id, sign_group); @@ -1098,7 +1082,7 @@ int sign_unplace(int sign_id, char_u *sign_group, buf_T *buf, linenr_T atlnum) /// Unplace the sign at the current cursor line. static void sign_unplace_at_cursor(char_u *groupname) { - int id = -1; + int id = -1; id = buf_findsign_id(curwin->w_buffer, curwin->w_cursor.lnum, groupname); if (id > 0) { @@ -1144,13 +1128,13 @@ linenr_T sign_jump(int sign_id, char_u *sign_group, buf_T *buf) /// ":sign define {name} ..." command static void sign_define_cmd(char_u *sign_name, char_u *cmdline) { - char_u *arg; - char_u *p = cmdline; - char_u *icon = NULL; - char_u *text = NULL; - char_u *linehl = NULL; - char_u *texthl = NULL; - char_u *numhl = NULL; + char_u *arg; + char_u *p = cmdline; + char_u *icon = NULL; + char_u *text = NULL; + char_u *linehl = NULL; + char_u *texthl = NULL; + char_u *numhl = NULL; int failed = false; // set values for a defined sign. @@ -1194,14 +1178,8 @@ static void sign_define_cmd(char_u *sign_name, char_u *cmdline) } /// ":sign place" command -static void sign_place_cmd( - buf_T *buf, - linenr_T lnum, - char_u *sign_name, - int id, - char_u *group, - int prio -) +static void sign_place_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int id, char_u *group, + int prio) { if (id <= 0) { // List signs placed in a file/buffer @@ -1233,62 +1211,56 @@ static void sign_place_cmd( } /// ":sign unplace" command -static void sign_unplace_cmd( - buf_T *buf, - linenr_T lnum, - char_u *sign_name, - int id, - char_u *group -) +static void sign_unplace_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int id, char_u *group) { - if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0')) { - EMSG(_(e_invarg)); - return; - } + if (lnum >= 0 || sign_name != NULL || (group != NULL && *group == '\0')) { + EMSG(_(e_invarg)); + return; + } - if (id == -2) { - if (buf != NULL) { - // :sign unplace * file={fname} - // :sign unplace * group={group} file={fname} - // :sign unplace * group=* file={fname} - // :sign unplace * buffer={nr} - // :sign unplace * group={group} buffer={nr} - // :sign unplace * group=* buffer={nr} - sign_unplace(0, group, buf, 0); - } else { - // :sign unplace * - // :sign unplace * group={group} - // :sign unplace * group=* - FOR_ALL_BUFFERS(cbuf) { - if (cbuf->b_signlist != NULL) { - buf_delete_signs(cbuf, group); - } + if (id == -2) { + if (buf != NULL) { + // :sign unplace * file={fname} + // :sign unplace * group={group} file={fname} + // :sign unplace * group=* file={fname} + // :sign unplace * buffer={nr} + // :sign unplace * group={group} buffer={nr} + // :sign unplace * group=* buffer={nr} + sign_unplace(0, group, buf, 0); + } else { + // :sign unplace * + // :sign unplace * group={group} + // :sign unplace * group=* + FOR_ALL_BUFFERS(cbuf) { + if (cbuf->b_signlist != NULL) { + buf_delete_signs(cbuf, group); } } + } + } else { + if (buf != NULL) { + // :sign unplace {id} file={fname} + // :sign unplace {id} group={group} file={fname} + // :sign unplace {id} group=* file={fname} + // :sign unplace {id} buffer={nr} + // :sign unplace {id} group={group} buffer={nr} + // :sign unplace {id} group=* buffer={nr} + sign_unplace(id, group, buf, 0); } else { - if (buf != NULL) { - // :sign unplace {id} file={fname} - // :sign unplace {id} group={group} file={fname} - // :sign unplace {id} group=* file={fname} - // :sign unplace {id} buffer={nr} - // :sign unplace {id} group={group} buffer={nr} - // :sign unplace {id} group=* buffer={nr} - sign_unplace(id, group, buf, 0); + if (id == -1) { + // :sign unplace group={group} + // :sign unplace group=* + sign_unplace_at_cursor(group); } else { - if (id == -1) { - // :sign unplace group={group} - // :sign unplace group=* - sign_unplace_at_cursor(group); - } else { - // :sign unplace {id} - // :sign unplace {id} group={group} - // :sign unplace {id} group=* - FOR_ALL_BUFFERS(cbuf) { - sign_unplace(id, group, cbuf, 0); - } + // :sign unplace {id} + // :sign unplace {id} group={group} + // :sign unplace {id} group=* + FOR_ALL_BUFFERS(cbuf) { + sign_unplace(id, group, cbuf, 0); } } } + } } /// Jump to a placed sign commands: @@ -1296,13 +1268,7 @@ static void sign_unplace_cmd( /// :sign jump {id} buffer={nr} /// :sign jump {id} group={group} file={fname} /// :sign jump {id} group={group} buffer={nr} -static void sign_jump_cmd( - buf_T *buf, - linenr_T lnum, - char_u *sign_name, - int id, - char_u *group -) +static void sign_jump_cmd(buf_T *buf, linenr_T lnum, char_u *sign_name, int id, char_u *group) { if (sign_name == NULL && group == NULL && id == -1) { EMSG(_(e_argreq)); @@ -1324,21 +1290,13 @@ static void sign_jump_cmd( /// ":sign jump" commands. /// The supported arguments are: line={lnum} name={name} group={group} /// priority={prio} and file={fname} or buffer={nr}. -static int parse_sign_cmd_args( - int cmd, - char_u *arg, - char_u **sign_name, - int *signid, - char_u **group, - int *prio, - buf_T **buf, - linenr_T *lnum -) +static int parse_sign_cmd_args(int cmd, char_u *arg, char_u **sign_name, int *signid, + char_u **group, int *prio, buf_T **buf, linenr_T *lnum) { - char_u *arg1; - char_u *name; - char_u *filename = NULL; - int lnum_arg = false; + char_u *arg1; + char_u *name; + char_u *filename = NULL; + int lnum_arg = false; // first arg could be placed sign id arg1 = arg; @@ -1448,7 +1406,7 @@ void ex_sign(exarg_T *eap) } else if (*arg == NUL) { EMSG(_("E156: Missing sign name")); } else { - char_u *name; + char_u *name; // Isolate the sign name. If it's a number skip leading zeroes, // so that "099" and "99" are the same sign. But keep "0". @@ -1574,12 +1532,8 @@ list_T *get_buffer_signs(buf_T *buf) } /// Return information about all the signs placed in a buffer -static void sign_get_placed_in_buf( - buf_T *buf, - linenr_T lnum, - int sign_id, - const char_u *sign_group, - list_T *retlist) +static void sign_get_placed_in_buf(buf_T *buf, linenr_T lnum, int sign_id, const char_u *sign_group, + list_T *retlist) { dict_T *d; list_T *l; @@ -1609,13 +1563,8 @@ static void sign_get_placed_in_buf( /// Get a list of signs placed in buffer 'buf'. If 'num' is non-zero, return the /// sign placed at the line number. If 'lnum' is zero, return all the signs /// placed in 'buf'. If 'buf' is NULL, return signs placed in all the buffers. -void sign_get_placed( - buf_T *buf, - linenr_T lnum, - int sign_id, - const char_u *sign_group, - list_T *retlist -) +void sign_get_placed(buf_T *buf, linenr_T lnum, int sign_id, const char_u *sign_group, + list_T *retlist) { if (buf != NULL) { sign_get_placed_in_buf(buf, lnum, sign_id, sign_group, retlist); @@ -1697,13 +1646,13 @@ void free_signs(void) static enum { - EXP_SUBCMD, // expand :sign sub-commands - EXP_DEFINE, // expand :sign define {name} args - EXP_PLACE, // expand :sign place {id} args - EXP_LIST, // expand :sign place args - EXP_UNPLACE, // expand :sign unplace" - EXP_SIGN_NAMES, // expand with name of placed signs - EXP_SIGN_GROUPS, // expand with name of placed sign groups + EXP_SUBCMD, // expand :sign sub-commands + EXP_DEFINE, // expand :sign define {name} args + EXP_PLACE, // expand :sign place {id} args + EXP_LIST, // expand :sign place args + EXP_UNPLACE, // expand :sign unplace" + EXP_SIGN_NAMES, // expand with name of placed signs + EXP_SIGN_GROUPS, // expand with name of placed sign groups } expand_what; // Return the n'th sign name (used for command line completion) @@ -1740,45 +1689,45 @@ static char_u *get_nth_sign_group_name(int idx) /// Function given to ExpandGeneric() to obtain the sign command /// expansion. -char_u * get_sign_name(expand_T *xp, int idx) +char_u *get_sign_name(expand_T *xp, int idx) { switch (expand_what) { - case EXP_SUBCMD: - return (char_u *)cmds[idx]; - case EXP_DEFINE: { - char *define_arg[] = { "icon=", "linehl=", "text=", "texthl=", "numhl=", - NULL }; - return (char_u *)define_arg[idx]; - } - case EXP_PLACE: { - char *place_arg[] = { "line=", "name=", "group=", "priority=", "file=", - "buffer=", NULL }; - return (char_u *)place_arg[idx]; - } - case EXP_LIST: { - char *list_arg[] = { "group=", "file=", "buffer=", NULL }; - return (char_u *)list_arg[idx]; - } - case EXP_UNPLACE: { - char *unplace_arg[] = { "group=", "file=", "buffer=", NULL }; - return (char_u *)unplace_arg[idx]; - } - case EXP_SIGN_NAMES: - return get_nth_sign_name(idx); - case EXP_SIGN_GROUPS: - return get_nth_sign_group_name(idx); - default: - return NULL; + case EXP_SUBCMD: + return (char_u *)cmds[idx]; + case EXP_DEFINE: { + char *define_arg[] = { "icon=", "linehl=", "text=", "texthl=", "numhl=", + NULL }; + return (char_u *)define_arg[idx]; + } + case EXP_PLACE: { + char *place_arg[] = { "line=", "name=", "group=", "priority=", "file=", + "buffer=", NULL }; + return (char_u *)place_arg[idx]; + } + case EXP_LIST: { + char *list_arg[] = { "group=", "file=", "buffer=", NULL }; + return (char_u *)list_arg[idx]; + } + case EXP_UNPLACE: { + char *unplace_arg[] = { "group=", "file=", "buffer=", NULL }; + return (char_u *)unplace_arg[idx]; + } + case EXP_SIGN_NAMES: + return get_nth_sign_name(idx); + case EXP_SIGN_GROUPS: + return get_nth_sign_group_name(idx); + default: + return NULL; } } /// Handle command line completion for :sign command. void set_context_in_sign_cmd(expand_T *xp, char_u *arg) { - char_u *end_subcmd; - char_u *last; - int cmd_idx; - char_u *begin_subcmd_args; + char_u *end_subcmd; + char_u *last; + int cmd_idx; + char_u *begin_subcmd_args; // Default: expand subcommands. xp->xp_context = EXPAND_SIGN; @@ -1822,70 +1771,70 @@ void set_context_in_sign_cmd(expand_T *xp, char_u *arg) // Expand last argument name (before equal sign). xp->xp_pattern = last; switch (cmd_idx) { - case SIGNCMD_DEFINE: - expand_what = EXP_DEFINE; - break; - case SIGNCMD_PLACE: - // List placed signs - if (ascii_isdigit(*begin_subcmd_args)) { - // :sign place {id} {args}... - expand_what = EXP_PLACE; - } else { - // :sign place {args}... - expand_what = EXP_LIST; - } - break; - case SIGNCMD_LIST: - case SIGNCMD_UNDEFINE: - // :sign list <CTRL-D> - // :sign undefine <CTRL-D> - expand_what = EXP_SIGN_NAMES; - break; - case SIGNCMD_JUMP: - case SIGNCMD_UNPLACE: - expand_what = EXP_UNPLACE; - break; - default: - xp->xp_context = EXPAND_NOTHING; + case SIGNCMD_DEFINE: + expand_what = EXP_DEFINE; + break; + case SIGNCMD_PLACE: + // List placed signs + if (ascii_isdigit(*begin_subcmd_args)) { + // :sign place {id} {args}... + expand_what = EXP_PLACE; + } else { + // :sign place {args}... + expand_what = EXP_LIST; + } + break; + case SIGNCMD_LIST: + case SIGNCMD_UNDEFINE: + // :sign list <CTRL-D> + // :sign undefine <CTRL-D> + expand_what = EXP_SIGN_NAMES; + break; + case SIGNCMD_JUMP: + case SIGNCMD_UNPLACE: + expand_what = EXP_UNPLACE; + break; + default: + xp->xp_context = EXPAND_NOTHING; } } else { - // Expand last argument value (after equal sign). + // Expand last argument value (after equal sign). xp->xp_pattern = p + 1; switch (cmd_idx) { - case SIGNCMD_DEFINE: - if (STRNCMP(last, "texthl", 6) == 0 - || STRNCMP(last, "linehl", 6) == 0 - || STRNCMP(last, "numhl", 5) == 0) { - xp->xp_context = EXPAND_HIGHLIGHT; - } else if (STRNCMP(last, "icon", 4) == 0) { - xp->xp_context = EXPAND_FILES; - } else { - xp->xp_context = EXPAND_NOTHING; - } - break; - case SIGNCMD_PLACE: - if (STRNCMP(last, "name", 4) == 0) { - expand_what = EXP_SIGN_NAMES; - } else if (STRNCMP(last, "group", 5) == 0) { - expand_what = EXP_SIGN_GROUPS; - } else if (STRNCMP(last, "file", 4) == 0) { - xp->xp_context = EXPAND_BUFFERS; - } else { - xp->xp_context = EXPAND_NOTHING; - } - break; - case SIGNCMD_UNPLACE: - case SIGNCMD_JUMP: - if (STRNCMP(last, "group", 5) == 0) { - expand_what = EXP_SIGN_GROUPS; - } else if (STRNCMP(last, "file", 4) == 0) { - xp->xp_context = EXPAND_BUFFERS; - } else { - xp->xp_context = EXPAND_NOTHING; - } - break; - default: + case SIGNCMD_DEFINE: + if (STRNCMP(last, "texthl", 6) == 0 + || STRNCMP(last, "linehl", 6) == 0 + || STRNCMP(last, "numhl", 5) == 0) { + xp->xp_context = EXPAND_HIGHLIGHT; + } else if (STRNCMP(last, "icon", 4) == 0) { + xp->xp_context = EXPAND_FILES; + } else { xp->xp_context = EXPAND_NOTHING; + } + break; + case SIGNCMD_PLACE: + if (STRNCMP(last, "name", 4) == 0) { + expand_what = EXP_SIGN_NAMES; + } else if (STRNCMP(last, "group", 5) == 0) { + expand_what = EXP_SIGN_GROUPS; + } else if (STRNCMP(last, "file", 4) == 0) { + xp->xp_context = EXPAND_BUFFERS; + } else { + xp->xp_context = EXPAND_NOTHING; + } + break; + case SIGNCMD_UNPLACE: + case SIGNCMD_JUMP: + if (STRNCMP(last, "group", 5) == 0) { + expand_what = EXP_SIGN_GROUPS; + } else if (STRNCMP(last, "file", 4) == 0) { + xp->xp_context = EXPAND_BUFFERS; + } else { + xp->xp_context = EXPAND_NOTHING; + } + break; + default: + xp->xp_context = EXPAND_NOTHING; } } } @@ -1914,11 +1863,11 @@ int sign_define_from_dict(const char *name_arg, dict_T *dict) goto cleanup; } if (dict != NULL) { - icon = tv_dict_get_string(dict, "icon" , true); + icon = tv_dict_get_string(dict, "icon", true); linehl = tv_dict_get_string(dict, "linehl", true); - text = tv_dict_get_string(dict, "text" , true); + text = tv_dict_get_string(dict, "text", true); texthl = tv_dict_get_string(dict, "texthl", true); - numhl = tv_dict_get_string(dict, "numhl" , true); + numhl = tv_dict_get_string(dict, "numhl", true); } if (sign_define_by_name((char_u *)name, (char_u *)icon, (char_u *)linehl, @@ -1942,27 +1891,23 @@ cleanup: /// values in 'retlist'. void sign_define_multiple(list_T *l, list_T *retlist) { - int retval; + int retval; - TV_LIST_ITER_CONST(l, li, { - retval = -1; - if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) { - retval = sign_define_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict); - } else { - EMSG(_(e_dictreq)); - } - tv_list_append_number(retlist, retval); - }); + TV_LIST_ITER_CONST(l, li, { + retval = -1; + if (TV_LIST_ITEM_TV(li)->v_type == VAR_DICT) { + retval = sign_define_from_dict(NULL, TV_LIST_ITEM_TV(li)->vval.v_dict); + } else { + EMSG(_(e_dictreq)); + } + tv_list_append_number(retlist, retval); + }); } /// Place a new sign using the values specified in dict 'dict'. Returns the sign /// identifier if successfully placed, otherwise returns 0. -int sign_place_from_dict( - typval_T *id_tv, - typval_T *group_tv, - typval_T *name_tv, - typval_T *buf_tv, - dict_T *dict) +int sign_place_from_dict(typval_T *id_tv, typval_T *group_tv, typval_T *name_tv, typval_T *buf_tv, + dict_T *dict) { int sign_id = 0; char_u *group = NULL; diff --git a/src/nvim/spell.c b/src/nvim/spell.c index 610a359141..3e56ad561b 100644 --- a/src/nvim/spell.c +++ b/src/nvim/spell.c @@ -72,15 +72,14 @@ #include <inttypes.h> #include <limits.h> #include <stdbool.h> -#include <string.h> #include <stdlib.h> +#include <string.h> #include <wctype.h> -/* for offsetof() */ +// for offsetof() #include <stddef.h> #include "nvim/ascii.h" -#include "nvim/spell.h" #include "nvim/buffer.h" #include "nvim/change.h" #include "nvim/charset.h" @@ -92,6 +91,7 @@ #include "nvim/ex_docmd.h" #include "nvim/fileio.h" #include "nvim/func_attr.h" +#include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/hashtab.h" #include "nvim/mark.h" @@ -100,21 +100,21 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/garray.h" #include "nvim/normal.h" #include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/os.h" #include "nvim/os_unix.h" #include "nvim/path.h" #include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/search.h" +#include "nvim/spell.h" #include "nvim/spellfile.h" #include "nvim/strings.h" #include "nvim/syntax.h" -#include "nvim/undo.h" #include "nvim/ui.h" -#include "nvim/os/os.h" -#include "nvim/os/input.h" +#include "nvim/undo.h" // only used for su_badflags #define WF_MIXCAP 0x20 // mix of upper and lower case: macaRONI @@ -151,26 +151,26 @@ typedef struct suginfo_S { int su_maxscore; // maximum score for adding to su_ga int su_sfmaxscore; // idem, for when doing soundfold words garray_T su_sga; // like su_ga, sound-folded scoring - char_u *su_badptr; // start of bad word in line + char_u *su_badptr; // start of bad word in line int su_badlen; // length of detected bad word in line int su_badflags; // caps flags for bad word char_u su_badword[MAXWLEN]; // bad word truncated at su_badlen char_u su_fbadword[MAXWLEN]; // su_badword case-folded char_u su_sal_badword[MAXWLEN]; // su_badword soundfolded hashtab_T su_banned; // table with banned words - slang_T *su_sallang; // default language for sound folding + slang_T *su_sallang; // default language for sound folding } suginfo_T; // One word suggestion. Used in "si_ga". typedef struct { - char_u *st_word; // suggested word, allocated string + char_u *st_word; // suggested word, allocated string int st_wordlen; // STRLEN(st_word) int st_orglen; // length of replaced text int st_score; // lower is better int st_altscore; // used when st_score compares equal bool st_salscore; // st_score is for soundalike bool st_had_bonus; // bonus already included in score - slang_T *st_slang; // language used for sound folding + slang_T *st_slang; // language used for sound folding } suggest_T; #define SUG(ga, i) (((suggest_T *)(ga).ga_data)[i]) @@ -235,14 +235,14 @@ typedef struct { // Structure to store info for word matching. typedef struct matchinf_S { - langp_T *mi_lp; // info for language and region + langp_T *mi_lp; // info for language and region // pointers to original text to be checked - char_u *mi_word; // start of word being checked - char_u *mi_end; // end of matching word so far - char_u *mi_fend; // next char to be added to mi_fword - char_u *mi_cend; // char after what was used for - // mi_capflags + char_u *mi_word; // start of word being checked + char_u *mi_end; // end of matching word so far + char_u *mi_fend; // next char to be added to mi_fword + char_u *mi_cend; // char after what was used for + // mi_capflags // case-folded text char_u mi_fword[MAXWLEN + 1]; // mi_word case-folded @@ -265,11 +265,11 @@ typedef struct matchinf_S { // others int mi_result; // result so far: SP_BAD, SP_OK, etc. int mi_capflags; // WF_ONECAP WF_ALLCAP WF_KEEPCAP - win_T *mi_win; // buffer being checked + win_T *mi_win; // buffer being checked // for NOBREAK int mi_result2; // "mi_resul" without following word - char_u *mi_end2; // "mi_end" without following word + char_u *mi_end2; // "mi_end" without following word } matchinf_T; // Structure used for the cookie argument of do_in_runtimepath(). @@ -334,26 +334,24 @@ char *e_format = N_("E759: Format error in spell file"); static char_u *repl_from = NULL; static char_u *repl_to = NULL; -// Main spell-checking function. -// "ptr" points to a character that could be the start of a word. -// "*attrp" is set to the highlight index for a badly spelled word. For a -// non-word or when it's OK it remains unchanged. -// This must only be called when 'spelllang' is not empty. -// -// "capcol" is used to check for a Capitalised word after the end of a -// sentence. If it's zero then perform the check. Return the column where to -// check next, or -1 when no sentence end was found. If it's NULL then don't -// worry. -// -// Returns the length of the word in bytes, also when it's OK, so that the -// caller can skip over the word. -size_t spell_check( - win_T *wp, // current window - char_u *ptr, - hlf_T *attrp, - int *capcol, // column to check for Capital - bool docount // count good words -) +/// Main spell-checking function. +/// "ptr" points to a character that could be the start of a word. +/// "*attrp" is set to the highlight index for a badly spelled word. For a +/// non-word or when it's OK it remains unchanged. +/// This must only be called when 'spelllang' is not empty. +/// +/// "capcol" is used to check for a Capitalised word after the end of a +/// sentence. If it's zero then perform the check. Return the column where to +/// check next, or -1 when no sentence end was found. If it's NULL then don't +/// worry. +/// +/// @param wp current window +/// @param capcol column to check for Capital +/// @param docount count good words +/// +/// @return the length of the word in bytes, also when it's OK, so that the +/// caller can skip over the word. +size_t spell_check(win_T *wp, char_u *ptr, hlf_T *attrp, int *capcol, bool docount) { matchinf_T mi; // Most things are put in "mi" so that it can // be passed to functions quickly. @@ -383,7 +381,7 @@ size_t spell_check( // julifeest". if (*ptr >= '0' && *ptr <= '9') { if (*ptr == '0' && (ptr[1] == 'b' || ptr[1] == 'B')) { - mi.mi_end = (char_u*) skipbin((char*) ptr + 2); + mi.mi_end = (char_u *)skipbin((char *)ptr + 2); } else if (*ptr == '0' && (ptr[1] == 'x' || ptr[1] == 'X')) { mi.mi_end = skiphex(ptr + 2); } else { @@ -486,7 +484,7 @@ size_t spell_check( // Count the word in the first language where it's found to be OK. if (count_word && mi.mi_result == SP_OK) { count_common_word(mi.mi_lp->lp_slang, ptr, - (int)(mi.mi_end - ptr), 1); + (int)(mi.mi_end - ptr), 1); count_word = false; } } @@ -499,8 +497,8 @@ size_t spell_check( return nrlen; } } else if (!spell_iswordp_nmw(ptr, wp)) { - // When we are at a non-word character there is no error, just - // skip over the character (try looking for a word after it). + // When we are at a non-word character there is no error, just + // skip over the character (try looking for a word after it). if (capcol != NULL && wp->w_s->b_cap_prog != NULL) { regmatch_T regmatch; @@ -521,7 +519,7 @@ size_t spell_check( MB_PTR_ADV(mi.mi_end); } else if (mi.mi_result == SP_BAD && LANGP_ENTRY(wp->w_s->b_langp, 0)->lp_slang->sl_nobreak) { - char_u *p, *fp; + char_u *p, *fp; int save_result = mi.mi_result; // First language in 'spelllang' is NOBREAK. Find first position @@ -576,10 +574,10 @@ static void find_word(matchinf_T *mip, int mode) { int wlen = 0; int flen; - char_u *ptr; - slang_T *slang = mip->mi_lp->lp_slang; - char_u *byts; - idx_T *idxs; + char_u *ptr; + slang_T *slang = mip->mi_lp->lp_slang; + char_u *byts; + idx_T *idxs; if (mode == FIND_KEEPWORD || mode == FIND_KEEPCOMPOUND) { // Check for word with matching case in keep-case tree. @@ -588,9 +586,10 @@ static void find_word(matchinf_T *mip, int mode) byts = slang->sl_kbyts; idxs = slang->sl_kidxs; - if (mode == FIND_KEEPCOMPOUND) + if (mode == FIND_KEEPCOMPOUND) { // Skip over the previously found word(s). wlen += mip->mi_compoff; + } } else { // Check for case-folded in case-folded tree. ptr = mip->mi_fword; @@ -607,12 +606,11 @@ static void find_word(matchinf_T *mip, int mode) wlen = mip->mi_compoff; flen -= mip->mi_compoff; } - } - if (byts == NULL) + if (byts == NULL) { return; // array is empty - + } idx_T arridx = 0; int endlen[MAXWLEN]; // length at possible word endings idx_T endidx[MAXWLEN]; // possible word endings @@ -625,8 +623,9 @@ static void find_word(matchinf_T *mip, int mode) // - we reach the end of the tree, // - or we reach the end of the line. for (;; ) { - if (flen <= 0 && *mip->mi_fend != NUL) + if (flen <= 0 && *mip->mi_fend != NUL) { flen = fold_more(mip); + } len = byts[arridx++]; @@ -648,35 +647,39 @@ static void find_word(matchinf_T *mip, int mode) ++arridx; --len; } - if (len == 0) + if (len == 0) { break; // no children, word must end here + } } // Stop looking at end of the line. - if (ptr[wlen] == NUL) + if (ptr[wlen] == NUL) { break; + } // Perform a binary search in the list of accepted bytes. c = ptr[wlen]; - if (c == TAB) // <Tab> is handled like <Space> + if (c == TAB) { // <Tab> is handled like <Space> c = ' '; + } idx_T lo = arridx; idx_T hi = arridx + len - 1; while (lo < hi) { idx_T m = (lo + hi) / 2; - if (byts[m] > c) + if (byts[m] > c) { hi = m - 1; - else if (byts[m] < c) + } else if (byts[m] < c) { lo = m + 1; - else { + } else { lo = hi = m; break; } } // Stop if there is no matching byte. - if (hi < lo || byts[lo] != c) + if (hi < lo || byts[lo] != c) { break; + } // Continue at the child (if there is one). arridx = idxs[lo]; @@ -687,10 +690,12 @@ static void find_word(matchinf_T *mip, int mode) // checked word. if (c == ' ') { for (;; ) { - if (flen <= 0 && *mip->mi_fend != NUL) + if (flen <= 0 && *mip->mi_fend != NUL) { flen = fold_more(mip); - if (ptr[wlen] != ' ' && ptr[wlen] != TAB) + } + if (ptr[wlen] != ' ' && ptr[wlen] != TAB) { break; + } ++wlen; --flen; } @@ -711,11 +716,13 @@ static void find_word(matchinf_T *mip, int mode) continue; // not at first byte of character } if (spell_iswordp(ptr + wlen, mip->mi_win)) { - if (slang->sl_compprog == NULL && !slang->sl_nobreak) + if (slang->sl_compprog == NULL && !slang->sl_nobreak) { continue; // next char is a word character + } word_ends = false; - } else + } else { word_ends = true; + } // The prefix flag is before compound flags. Once a valid prefix flag // has been found we try compound flags. bool prefix_found = false; @@ -754,23 +761,26 @@ static void find_word(matchinf_T *mip, int mode) } if (mip->mi_capflags == WF_KEEPCAP - || !spell_valid_case(mip->mi_capflags, flags)) + || !spell_valid_case(mip->mi_capflags, flags)) { continue; + } } // When mode is FIND_PREFIX the word must support the prefix: // check the prefix ID and the condition. Do that for the list at // mip->mi_prefarridx that find_prefix() filled. else if (mode == FIND_PREFIX && !prefix_found) { c = valid_word_prefix(mip->mi_prefcnt, mip->mi_prefarridx, - flags, - mip->mi_word + mip->mi_cprefixlen, slang, - false); - if (c == 0) + flags, + mip->mi_word + mip->mi_cprefixlen, slang, + false); + if (c == 0) { continue; + } // Use the WF_RARE flag for a rare prefix. - if (c & WF_RAREPFX) + if (c & WF_RAREPFX) { flags |= WF_RARE; + } prefix_found = true; } @@ -790,8 +800,9 @@ static void find_word(matchinf_T *mip, int mode) // that's too short... Myspell compatibility requires this // anyway. if (((unsigned)flags >> 24) == 0 - || wlen - mip->mi_compoff < slang->sl_compminlen) + || wlen - mip->mi_compoff < slang->sl_compminlen) { continue; + } // For multi-byte chars check character length against // COMPOUNDMIN. if (slang->sl_compminlen > 0 @@ -804,27 +815,32 @@ static void find_word(matchinf_T *mip, int mode) // maximum for syllables is specified. if (!word_ends && mip->mi_complen + mip->mi_compextra + 2 > slang->sl_compmax - && slang->sl_compsylmax == MAXWLEN) + && slang->sl_compsylmax == MAXWLEN) { continue; + } // Don't allow compounding on a side where an affix was added, // unless COMPOUNDPERMITFLAG was used. - if (mip->mi_complen > 0 && (flags & WF_NOCOMPBEF)) + if (mip->mi_complen > 0 && (flags & WF_NOCOMPBEF)) { continue; - if (!word_ends && (flags & WF_NOCOMPAFT)) + } + if (!word_ends && (flags & WF_NOCOMPAFT)) { continue; + } // Quickly check if compounding is possible with this flag. if (!byte_in_str(mip->mi_complen == 0 ? slang->sl_compstartflags : slang->sl_compallflags, - ((unsigned)flags >> 24))) + ((unsigned)flags >> 24))) { continue; + } // If there is a match with a CHECKCOMPOUNDPATTERN rule // discard the compound word. - if (match_checkcompoundpattern(ptr, wlen, &slang->sl_comppat)) + if (match_checkcompoundpattern(ptr, wlen, &slang->sl_comppat)) { continue; + } if (mode == FIND_COMPOUND) { int capflags; @@ -842,8 +858,9 @@ static void find_word(matchinf_T *mip, int mode) } capflags = captype(p, mip->mi_word + wlen); if (capflags == WF_KEEPCAP || (capflags == WF_ALLCAP - && (flags & WF_FIXCAP) != 0)) + && (flags & WF_FIXCAP) != 0)) { continue; + } if (capflags != WF_ALLCAP) { // When the character before the word is a word @@ -876,23 +893,26 @@ static void find_word(matchinf_T *mip, int mode) STRLCPY(fword, ptr, endlen[endidxcnt] + 1); } } - if (!can_compound(slang, fword, mip->mi_compflags)) + if (!can_compound(slang, fword, mip->mi_compflags)) { continue; + } } else if (slang->sl_comprules != NULL - && !match_compoundrule(slang, mip->mi_compflags)) + && !match_compoundrule(slang, mip->mi_compflags)) { // The compound flags collected so far do not match any // COMPOUNDRULE, discard the compounded word. continue; + } } // Check NEEDCOMPOUND: can't use word without compounding. - else if (flags & WF_NEEDCOMP) + else if (flags & WF_NEEDCOMP) { continue; + } int nobreak_result = SP_OK; if (!word_ends) { int save_result = mip->mi_result; - char_u *save_end = mip->mi_end; + char_u *save_end = mip->mi_end; langp_T *save_lp = mip->mi_lp; // Check that a valid word follows. If there is one and we @@ -900,8 +920,9 @@ static void find_word(matchinf_T *mip, int mode) // always finished here. For NOBREAK we only check that a // valid word follows. // Recursive! - if (slang->sl_nobreak) + if (slang->sl_nobreak) { mip->mi_result = SP_BAD; + } // Find following word in case-folded tree. mip->mi_compoff = endlen[endidxcnt]; @@ -922,8 +943,9 @@ static void find_word(matchinf_T *mip, int mode) c = mip->mi_compoff; #endif ++mip->mi_complen; - if (flags & WF_COMPROOT) + if (flags & WF_COMPROOT) { ++mip->mi_compextra; + } // For NOBREAK we need to try all NOBREAK languages, at least // to find the ".add" file(s). @@ -931,8 +953,9 @@ static void find_word(matchinf_T *mip, int mode) if (slang->sl_nobreak) { mip->mi_lp = LANGP_ENTRY(mip->mi_win->w_s->b_langp, lpi); if (mip->mi_lp->lp_slang->sl_fidxs == NULL - || !mip->mi_lp->lp_slang->sl_nobreak) + || !mip->mi_lp->lp_slang->sl_nobreak) { continue; + } } find_word(mip, FIND_COMPOUND); @@ -956,12 +979,14 @@ static void find_word(matchinf_T *mip, int mode) #endif } - if (!slang->sl_nobreak) + if (!slang->sl_nobreak) { break; + } } --mip->mi_complen; - if (flags & WF_COMPROOT) + if (flags & WF_COMPROOT) { --mip->mi_compextra; + } mip->mi_lp = save_lp; if (slang->sl_nobreak) { @@ -969,25 +994,28 @@ static void find_word(matchinf_T *mip, int mode) mip->mi_result = save_result; mip->mi_end = save_end; } else { - if (mip->mi_result == SP_OK) + if (mip->mi_result == SP_OK) { break; + } continue; } } int res = SP_BAD; - if (flags & WF_BANNED) + if (flags & WF_BANNED) { res = SP_BANNED; - else if (flags & WF_REGION) { + } else if (flags & WF_REGION) { // Check region. - if ((mip->mi_lp->lp_region & (flags >> 16)) != 0) + if ((mip->mi_lp->lp_region & (flags >> 16)) != 0) { res = SP_OK; - else + } else { res = SP_LOCAL; - } else if (flags & WF_RARE) + } + } else if (flags & WF_RARE) { res = SP_RARE; - else + } else { res = SP_OK; + } // Always use the longest match and the best result. For NOBREAK // we separately keep the longest match without a following good @@ -997,36 +1025,37 @@ static void find_word(matchinf_T *mip, int mode) mip->mi_result2 = res; mip->mi_end2 = mip->mi_word + wlen; } else if (mip->mi_result2 == res - && mip->mi_end2 < mip->mi_word + wlen) + && mip->mi_end2 < mip->mi_word + wlen) { mip->mi_end2 = mip->mi_word + wlen; + } } else if (mip->mi_result > res) { mip->mi_result = res; mip->mi_end = mip->mi_word + wlen; - } else if (mip->mi_result == res && mip->mi_end < mip->mi_word + wlen) + } else if (mip->mi_result == res && mip->mi_end < mip->mi_word + wlen) { mip->mi_end = mip->mi_word + wlen; + } - if (mip->mi_result == SP_OK) + if (mip->mi_result == SP_OK) { break; + } } - if (mip->mi_result == SP_OK) + if (mip->mi_result == SP_OK) { break; + } } } -// Returns true if there is a match between the word ptr[wlen] and -// CHECKCOMPOUNDPATTERN rules, assuming that we will concatenate with another -// word. -// A match means that the first part of CHECKCOMPOUNDPATTERN matches at the -// end of ptr[wlen] and the second part matches after it. -static bool -match_checkcompoundpattern ( - char_u *ptr, - int wlen, - garray_T *gap // &sl_comppat -) +/// Returns true if there is a match between the word ptr[wlen] and +/// CHECKCOMPOUNDPATTERN rules, assuming that we will concatenate with another +/// word. +/// A match means that the first part of CHECKCOMPOUNDPATTERN matches at the +/// end of ptr[wlen] and the second part matches after it. +/// +/// @param gap &sl_comppat +static bool match_checkcompoundpattern(char_u *ptr, int wlen, garray_T *gap) { - char_u *p; + char_u *p; int len; for (int i = 0; i + 1 < gap->ga_len; i += 2) { @@ -1036,8 +1065,9 @@ match_checkcompoundpattern ( // check if first part matches at end of previous word. p = ((char_u **)gap->ga_data)[i]; len = (int)STRLEN(p); - if (len <= wlen && STRNCMP(ptr + wlen - len, p, len) == 0) + if (len <= wlen && STRNCMP(ptr + wlen - len, p, len) == 0) { return true; + } } } return false; @@ -1045,8 +1075,7 @@ match_checkcompoundpattern ( // Returns true if "flags" is a valid sequence of compound flags and "word" // does not have too many syllables. -static bool can_compound(slang_T *slang, const char_u *word, - const char_u *flags) +static bool can_compound(slang_T *slang, const char_u *word, const char_u *flags) FUNC_ATTR_NONNULL_ALL { char_u uflags[MAXWLEN * 2] = { 0 }; @@ -1069,8 +1098,9 @@ static bool can_compound(slang_T *slang, const char_u *word, // are too many syllables AND the number of compound words is above // COMPOUNDWORDMAX then compounding is not allowed. if (slang->sl_compsylmax < MAXWLEN - && count_syllables(slang, word) > slang->sl_compsylmax) + && count_syllables(slang, word) > slang->sl_compsylmax) { return (int)STRLEN(flags) < slang->sl_compmax; + } return true; } @@ -1082,8 +1112,9 @@ static bool can_be_compound(trystate_T *sp, slang_T *slang, char_u *compflags, i // If the flag doesn't appear in sl_compstartflags or sl_compallflags // then it can't possibly compound. if (!byte_in_str(sp->ts_complen == sp->ts_compsplit - ? slang->sl_compstartflags : slang->sl_compallflags, flag)) + ? slang->sl_compstartflags : slang->sl_compallflags, flag)) { return false; + } // If there are no wildcards, we can check if the flags collected so far // possibly can form a match with COMPOUNDRULE patterns. This only @@ -1105,7 +1136,7 @@ static bool can_be_compound(trystate_T *sp, slang_T *slang, char_u *compflags, i // Caller must check that slang->sl_comprules is not NULL. static bool match_compoundrule(slang_T *slang, char_u *compflags) { - char_u *p; + char_u *p; int i; int c; @@ -1115,30 +1146,37 @@ static bool match_compoundrule(slang_T *slang, char_u *compflags) // them against the current rule entry for (i = 0;; ++i) { c = compflags[i]; - if (c == NUL) + if (c == NUL) { // found a rule that matches for the flags we have so far return true; - if (*p == '/' || *p == NUL) + } + if (*p == '/' || *p == NUL) { break; // end of rule, it's too short + } if (*p == '[') { bool match = false; // compare against all the flags in [] ++p; - while (*p != ']' && *p != NUL) - if (*p++ == c) + while (*p != ']' && *p != NUL) { + if (*p++ == c) { match = true; - if (!match) + } + } + if (!match) { break; // none matches - } else if (*p != c) + } + } else if (*p != c) { break; // flag of word doesn't match flag in pattern + } ++p; } // Skip to the next "/", where the next pattern starts. p = vim_strchr(p, '/'); - if (p == NULL) + if (p == NULL) { break; + } } // Checked all the rules and none of them match the flags, so there @@ -1146,18 +1184,15 @@ static bool match_compoundrule(slang_T *slang, char_u *compflags) return false; } -// Return non-zero if the prefix indicated by "arridx" matches with the prefix -// ID in "flags" for the word "word". -// The WF_RAREPFX flag is included in the return value for a rare prefix. -static int -valid_word_prefix ( - int totprefcnt, // nr of prefix IDs - int arridx, // idx in sl_pidxs[] - int flags, - char_u *word, - slang_T *slang, - bool cond_req // only use prefixes with a condition -) +/// Return non-zero if the prefix indicated by "arridx" matches with the prefix +/// ID in "flags" for the word "word". +/// The WF_RAREPFX flag is included in the return value for a rare prefix. +/// +/// @param totprefcnt nr of prefix IDs +/// @param arridx idx in sl_pidxs[] +/// @param cond_req only use prefixes with a condition +static int valid_word_prefix(int totprefcnt, int arridx, int flags, char_u *word, slang_T *slang, + bool cond_req) { int prefcnt; int pidx; @@ -1168,13 +1203,15 @@ valid_word_prefix ( pidx = slang->sl_pidxs[arridx + prefcnt]; // Check the prefix ID. - if (prefid != (pidx & 0xff)) + if (prefid != (pidx & 0xff)) { continue; + } // Check if the prefix doesn't combine and the word already has a // suffix. - if ((flags & WF_HAS_AFF) && (pidx & WF_PFX_NC)) + if ((flags & WF_HAS_AFF) && (pidx & WF_PFX_NC)) { continue; + } // Check the condition, if there is one. The condition index is // stored in the two bytes above the prefix ID byte. @@ -1183,8 +1220,9 @@ valid_word_prefix ( if (!vim_regexec_prog(rp, false, word, 0)) { continue; } - } else if (cond_req) + } else if (cond_req) { continue; + } // It's a match! Return the WF_ flags. return pidx; @@ -1206,16 +1244,16 @@ static void find_prefix(matchinf_T *mip, int mode) int wlen = 0; int flen; int c; - char_u *ptr; + char_u *ptr; idx_T lo, hi, m; - slang_T *slang = mip->mi_lp->lp_slang; - char_u *byts; - idx_T *idxs; + slang_T *slang = mip->mi_lp->lp_slang; + char_u *byts; + idx_T *idxs; byts = slang->sl_pbyts; - if (byts == NULL) + if (byts == NULL) { return; // array is empty - + } // We use the case-folded word here, since prefixes are always // case-folded. ptr = mip->mi_fword; @@ -1232,8 +1270,9 @@ static void find_prefix(matchinf_T *mip, int mode) // - we reach the end of the tree, // - or we reach the end of the line. for (;; ) { - if (flen == 0 && *mip->mi_fend != NUL) + if (flen == 0 && *mip->mi_fend != NUL) { flen = fold_more(mip); + } len = byts[arridx++]; @@ -1254,9 +1293,10 @@ static void find_prefix(matchinf_T *mip, int mode) // Find the word that comes after the prefix. mip->mi_prefixlen = wlen; - if (mode == FIND_COMPOUND) + if (mode == FIND_COMPOUND) { // Skip over the previously found word(s). mip->mi_prefixlen += mip->mi_compoff; + } // Case-folded length may differ from original length. mip->mi_cprefixlen = nofold_len(mip->mi_fword, mip->mi_prefixlen, @@ -1264,13 +1304,15 @@ static void find_prefix(matchinf_T *mip, int mode) find_word(mip, FIND_PREFIX); - if (len == 0) + if (len == 0) { break; // no children, word must end here + } } // Stop looking at end of the line. - if (ptr[wlen] == NUL) + if (ptr[wlen] == NUL) { break; + } // Perform a binary search in the list of accepted bytes. c = ptr[wlen]; @@ -1278,19 +1320,20 @@ static void find_prefix(matchinf_T *mip, int mode) hi = arridx + len - 1; while (lo < hi) { m = (lo + hi) / 2; - if (byts[m] > c) + if (byts[m] > c) { hi = m - 1; - else if (byts[m] < c) + } else if (byts[m] < c) { lo = m + 1; - else { + } else { lo = hi = m; break; } } // Stop if there is no matching byte. - if (hi < lo || byts[lo] != c) + if (hi < lo || byts[lo] != c) { break; + } // Continue at the child (if there is one). arridx = idxs[lo]; @@ -1305,7 +1348,7 @@ static void find_prefix(matchinf_T *mip, int mode) static int fold_more(matchinf_T *mip) { int flen; - char_u *p; + char_u *p; p = mip->mi_fend; do { @@ -1349,42 +1392,40 @@ static bool no_spell_checking(win_T *wp) return false; } -// Moves to the next spell error. -// "curline" is false for "[s", "]s", "[S" and "]S". -// "curline" is true to find word under/after cursor in the same line. -// For Insert mode completion "dir" is BACKWARD and "curline" is true: move -// to after badly spelled word before the cursor. -// Return 0 if not found, length of the badly spelled word otherwise. -size_t -spell_move_to ( - win_T *wp, - int dir, // FORWARD or BACKWARD - bool allwords, // true for "[s"/"]s", false for "[S"/"]S" - bool curline, - hlf_T *attrp // return: attributes of bad word or NULL - // (only when "dir" is FORWARD) -) +/// Moves to the next spell error. +/// "curline" is false for "[s", "]s", "[S" and "]S". +/// "curline" is true to find word under/after cursor in the same line. +/// For Insert mode completion "dir" is BACKWARD and "curline" is true: move +/// to after badly spelled word before the cursor. +/// +/// @param dir FORWARD or BACKWARD +/// @param allwords true for "[s"/"]s", false for "[S"/"]S" +/// @param attrp return: attributes of bad word or NULL (only when "dir" is FORWARD) +/// +/// @return 0 if not found, length of the badly spelled word otherwise. +size_t spell_move_to(win_T *wp, int dir, bool allwords, bool curline, hlf_T *attrp) { linenr_T lnum; pos_T found_pos; size_t found_len = 0; - char_u *line; - char_u *p; - char_u *endp; + char_u *line; + char_u *p; + char_u *endp; hlf_T attr = HLF_COUNT; size_t len; int has_syntax = syntax_present(wp); int col; bool can_spell; - char_u *buf = NULL; + char_u *buf = NULL; size_t buflen = 0; int skip = 0; int capcol = -1; bool found_one = false; bool wrapped = false; - if (no_spell_checking(wp)) + if (no_spell_checking(wp)) { return 0; + } // Start looking for bad word at the start of the line, because we can't // start halfway through a word, we don't know where it starts or ends. @@ -1410,8 +1451,9 @@ spell_move_to ( assert(buf && buflen >= len + MAXWLEN + 2); // In first line check first word for Capital. - if (lnum == 1) + if (lnum == 1) { capcol = 0; + } // For checking first word with a capital skip white space. if (capcol == 0) { @@ -1431,10 +1473,11 @@ spell_move_to ( // Copy the line into "buf" and append the start of the next line if // possible. STRCPY(buf, line); - if (lnum < wp->w_buffer->b_ml.ml_line_count) + if (lnum < wp->w_buffer->b_ml.ml_line_count) { spell_cat_line(buf + STRLEN(buf), ml_get_buf(wp->w_buffer, lnum + 1, false), MAXWLEN); + } p = buf + skip; endp = buf + len; while (p < endp) { @@ -1443,8 +1486,9 @@ spell_move_to ( if (dir == BACKWARD && lnum == wp->w_cursor.lnum && !wrapped - && (colnr_T)(p - buf) >= wp->w_cursor.col) + && (colnr_T)(p - buf) >= wp->w_cursor.col) { break; + } // start of word attr = HLF_COUNT; @@ -1464,11 +1508,13 @@ spell_move_to ( if (has_syntax) { col = (int)(p - buf); (void)syn_get_id(wp, lnum, (colnr_T)col, - FALSE, &can_spell, FALSE); - if (!can_spell) + FALSE, &can_spell, FALSE); + if (!can_spell) { attr = HLF_COUNT; - } else + } + } else { can_spell = true; + } if (can_spell) { found_one = true; @@ -1479,8 +1525,9 @@ spell_move_to ( // No need to search further. wp->w_cursor = found_pos; xfree(buf); - if (attrp != NULL) + if (attrp != NULL) { *attrp = attr; + } return len; } else if (curline) { // Insert mode completion: put cursor after @@ -1490,8 +1537,9 @@ spell_move_to ( } found_len = len; } - } else + } else { found_one = true; + } } } @@ -1529,22 +1577,24 @@ spell_move_to ( // starting line again and accept the last match. lnum = wp->w_buffer->b_ml.ml_line_count; wrapped = true; - if (!shortmess(SHM_SEARCH)) + if (!shortmess(SHM_SEARCH)) { give_warning((char_u *)_(top_bot_msg), true); + } } capcol = -1; } else { - if (lnum < wp->w_buffer->b_ml.ml_line_count) + if (lnum < wp->w_buffer->b_ml.ml_line_count) { ++lnum; - else if (!p_ws) + } else if (!p_ws) { break; // at first line and 'nowrapscan' - else { + } else { // Wrap around to the start of the buffer. May search the // starting line again and accept the first match. lnum = 1; wrapped = true; - if (!shortmess(SHM_SEARCH)) + if (!shortmess(SHM_SEARCH)) { give_warning((char_u *)_(bot_top_msg), true); + } } // If we are back at the starting line and there is no match then @@ -1555,17 +1605,19 @@ spell_move_to ( // Skip the characters at the start of the next line that were // included in a match crossing line boundaries. - if (attr == HLF_COUNT) + if (attr == HLF_COUNT) { skip = (int)(p - endp); - else + } else { skip = 0; + } // Capcol skips over the inserted space. --capcol; // But after empty line check first word in next line - if (*skipwhite(line) == NUL) + if (*skipwhite(line) == NUL) { capcol = 0; + } } line_breakcheck(); @@ -1581,12 +1633,13 @@ spell_move_to ( // to skip those bytes if the word was OK. void spell_cat_line(char_u *buf, char_u *line, int maxlen) { - char_u *p; + char_u *p; int n; p = skipwhite(line); - while (vim_strchr((char_u *)"*#/\"\t", *p) != NULL) + while (vim_strchr((char_u *)"*#/\"\t", *p) != NULL) { p = skipwhite(p + 1); + } if (*p != NUL) { // Only worth concatenating if there is something else than spaces to @@ -1630,8 +1683,9 @@ static void spell_load_lang(char_u *lang) if (r == FAIL && *sl.sl_lang != NUL && round == 1 && apply_autocmds(EVENT_SPELLFILEMISSING, lang, - curbuf->b_fname, FALSE, curbuf)) + curbuf->b_fname, FALSE, curbuf)) { continue; + } break; } break; @@ -1647,9 +1701,8 @@ static void spell_load_lang(char_u *lang) lang); do_cmdline_cmd(autocmd_buf); } else { - smsg( - _("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""), - lang, spell_enc(), lang); + smsg(_("Warning: Cannot find word list \"%s.%s.spl\" or \"%s.ascii.spl\""), + lang, spell_enc(), lang); } } else if (sl.sl_slang != NULL) { // At least one file was loaded, now load ALL the additions. @@ -1662,9 +1715,9 @@ static void spell_load_lang(char_u *lang) // use "latin1" for "latin9". And limit to 60 characters (just in case). char_u *spell_enc(void) { - - if (STRLEN(p_enc) < 60 && STRCMP(p_enc, "iso-8859-15") != 0) + if (STRLEN(p_enc) < 60 && STRCMP(p_enc, "iso-8859-15") != 0) { return p_enc; + } return (char_u *)"latin1"; } @@ -1673,7 +1726,7 @@ char_u *spell_enc(void) static void int_wordlist_spl(char_u *fname) { vim_snprintf((char *)fname, MAXPATHL, SPL_FNAME_TMPL, - int_wordlist, spell_enc()); + int_wordlist, spell_enc()); } // Allocate a new slang_T for language "lang". "lang" can be NULL. @@ -1683,8 +1736,9 @@ slang_T *slang_alloc(char_u *lang) { slang_T *lp = xcalloc(1, sizeof(slang_T)); - if (lang != NULL) + if (lang != NULL) { lp->sl_name = vim_strsave(lang); + } ga_init(&lp->sl_rep, sizeof(fromto_T), 10); ga_init(&lp->sl_repsal, sizeof(fromto_T), 10); lp->sl_compmax = MAXWLEN; @@ -1722,7 +1776,7 @@ static void free_fromto(fromto_T *ftp) { // Clear an slang_T so that the file can be reloaded. void slang_clear(slang_T *lp) { - garray_T *gap; + garray_T *gap; XFREE_CLEAR(lp->sl_fbyts); XFREE_CLEAR(lp->sl_kbyts); @@ -1792,17 +1846,18 @@ void slang_clear_sug(slang_T *lp) // Invoked through do_in_runtimepath(). static void spell_load_cb(char_u *fname, void *cookie) { - spelload_T *slp = (spelload_T *)cookie; - slang_T *slang; + spelload_T *slp = (spelload_T *)cookie; + slang_T *slang; slang = spell_load_file(fname, slp->sl_lang, NULL, false); if (slang != NULL) { // When a previously loaded file has NOBREAK also use it for the // ".add" files. - if (slp->sl_nobreak && slang->sl_add) + if (slp->sl_nobreak && slang->sl_add) { slang->sl_nobreak = true; - else if (slang->sl_nobreak) + } else if (slang->sl_nobreak) { slp->sl_nobreak = true; + } slp->sl_slang = slang; } @@ -1818,10 +1873,10 @@ static void spell_load_cb(char_u *fname, void *cookie) void count_common_word(slang_T *lp, char_u *word, int len, int count) { hash_T hash; - hashitem_T *hi; + hashitem_T *hi; wordcount_T *wc; char_u buf[MAXWLEN]; - char_u *p; + char_u *p; if (len == -1) { p = word; @@ -1842,21 +1897,18 @@ void count_common_word(slang_T *lp, char_u *word, int len, int count) hash_add_item(&lp->sl_wordcount, hi, wc->wc_word, hash); } else { wc = HI2WC(hi); - if ((wc->wc_count += count) < (unsigned)count) // check for overflow + if ((wc->wc_count += count) < (unsigned)count) { // check for overflow wc->wc_count = MAXWORDCOUNT; + } } } -// Adjust the score of common words. -static int -score_wordcount_adj ( - slang_T *slang, - int score, - char_u *word, - bool split // word was split, less bonus -) +/// Adjust the score of common words. +/// +/// @param split word was split, less bonus +static int score_wordcount_adj(slang_T *slang, int score, char_u *word, bool split) { - hashitem_T *hi; + hashitem_T *hi; wordcount_T *wc; int bonus; int newscore; @@ -1864,18 +1916,21 @@ score_wordcount_adj ( hi = hash_find(&slang->sl_wordcount, word); if (!HASHITEM_EMPTY(hi)) { wc = HI2WC(hi); - if (wc->wc_count < SCORE_THRES2) + if (wc->wc_count < SCORE_THRES2) { bonus = SCORE_COMMON1; - else if (wc->wc_count < SCORE_THRES3) + } else if (wc->wc_count < SCORE_THRES3) { bonus = SCORE_COMMON2; - else + } else { bonus = SCORE_COMMON3; - if (split) + } + if (split) { newscore = score - bonus / 2; - else + } else { newscore = score - bonus; - if (newscore < 0) + } + if (newscore < 0) { return 0; + } return newscore; } return score; @@ -1885,11 +1940,13 @@ score_wordcount_adj ( // Like strchr() but independent of locale. bool byte_in_str(char_u *str, int n) { - char_u *p; + char_u *p; - for (p = str; *p != NUL; ++p) - if (*p == n) + for (p = str; *p != NUL; ++p) { + if (*p == n) { return true; + } + } return false; } @@ -1897,24 +1954,27 @@ bool byte_in_str(char_u *str, int n) // in "slang->sl_syl_items". int init_syl_tab(slang_T *slang) { - char_u *p; - char_u *s; + char_u *p; + char_u *s; int l; ga_init(&slang->sl_syl_items, sizeof(syl_item_T), 4); p = vim_strchr(slang->sl_syllable, '/'); while (p != NULL) { *p++ = NUL; - if (*p == NUL) // trailing slash + if (*p == NUL) { // trailing slash break; + } s = p; p = vim_strchr(p, '/'); - if (p == NULL) + if (p == NULL) { l = (int)STRLEN(s); - else + } else { l = (int)(p - s); - if (l >= SY_MAXLEN) + } + if (l >= SY_MAXLEN) { return SP_FORMERROR; + } syl_item_T *syl = GA_APPEND_VIA_PTR(syl_item_T, &slang->sl_syl_items); STRLCPY(syl->sy_chars, s, l + 1); @@ -1932,11 +1992,12 @@ static int count_syllables(slang_T *slang, const char_u *word) int cnt = 0; bool skip = false; int len; - syl_item_T *syl; + syl_item_T *syl; int c; - if (slang->sl_syllable == NULL) + if (slang->sl_syllable == NULL) { return 0; + } for (const char_u *p = word; *p != NUL; p += len) { // When running into a space reset counter. @@ -1951,8 +2012,9 @@ static int count_syllables(slang_T *slang, const char_u *word) for (int i = 0; i < slang->sl_syl_items.ga_len; ++i) { syl = ((syl_item_T *)slang->sl_syl_items.ga_data) + i; if (syl->sy_len > len - && STRNCMP(p, syl->sy_chars, syl->sy_len) == 0) + && STRNCMP(p, syl->sy_chars, syl->sy_len) == 0) { len = syl->sy_len; + } } if (len != 0) { // found a match, count syllable ++cnt; @@ -1961,9 +2023,9 @@ static int count_syllables(slang_T *slang, const char_u *word) // No recognized syllable item, at least a syllable char then? c = utf_ptr2char(p); len = (*mb_ptr2len)(p); - if (vim_strchr(slang->sl_syllable, c) == NULL) + if (vim_strchr(slang->sl_syllable, c) == NULL) { skip = false; // No, search for next syllable - else if (!skip) { + } else if (!skip) { ++cnt; // Yes, count it skip = true; // don't count following syllable chars } @@ -1977,26 +2039,26 @@ static int count_syllables(slang_T *slang, const char_u *word) char_u *did_set_spelllang(win_T *wp) { garray_T ga; - char_u *splp; - char_u *region; + char_u *splp; + char_u *region; char_u region_cp[3]; bool filename; int region_mask; - slang_T *slang; + slang_T *slang; int c; char_u lang[MAXWLEN + 1]; char_u spf_name[MAXPATHL]; int len; - char_u *p; + char_u *p; int round; - char_u *spf; - char_u *use_region = NULL; + char_u *spf; + char_u *use_region = NULL; bool dont_use_region = false; bool nobreak = false; - langp_T *lp, *lp2; + langp_T *lp, *lp2; static bool recursive = false; - char_u *ret_msg = NULL; - char_u *spl_copy; + char_u *ret_msg = NULL; + char_u *spl_copy; bufref_T bufref; set_bufref(&bufref, wp->w_buffer); @@ -2004,8 +2066,9 @@ char_u *did_set_spelllang(win_T *wp) // We don't want to do this recursively. May happen when a language is // not available and the SpellFileMissing autocommand opens a new buffer // in which 'spell' is set. - if (recursive) + if (recursive) { return NULL; + } recursive = true; ga_init(&ga, sizeof(langp_T), 2); @@ -2046,8 +2109,9 @@ char_u *did_set_spelllang(win_T *wp) STRLCPY(region_cp, p + 1, 3); memmove(p, p + 3, len - (p - lang) - 2); region = region_cp; - } else + } else { dont_use_region = true; + } // Check if we loaded this language before. for (slang = first_lang; slang != NULL; slang = slang->sl_next) { @@ -2061,28 +2125,32 @@ char_u *did_set_spelllang(win_T *wp) if (len > 3 && lang[len - 3] == '_') { region = lang + len - 2; lang[len - 3] = NUL; - } else + } else { dont_use_region = true; + } // Check if we loaded this language before. - for (slang = first_lang; slang != NULL; slang = slang->sl_next) - if (STRICMP(lang, slang->sl_name) == 0) + for (slang = first_lang; slang != NULL; slang = slang->sl_next) { + if (STRICMP(lang, slang->sl_name) == 0) { break; + } + } } if (region != NULL) { // If the region differs from what was used before then don't // use it for 'spellfile'. - if (use_region != NULL && STRCMP(region, use_region) != 0) + if (use_region != NULL && STRCMP(region, use_region) != 0) { dont_use_region = true; + } use_region = region; } // If not found try loading the language now. if (slang == NULL) { - if (filename) + if (filename) { (void)spell_load_file(lang, lang, NULL, false); - else { + } else { spell_load_lang(lang); // SpellFileMissing autocommands may do anything, including // destroying the buffer we are using... @@ -2105,16 +2173,19 @@ char_u *did_set_spelllang(win_T *wp) c = find_region(slang->sl_regions, region); if (c == REGION_ALL) { if (slang->sl_add) { - if (*slang->sl_regions != NUL) + if (*slang->sl_regions != NUL) { // This addition file is for other regions. region_mask = 0; - } else + } + } else { // This is probably an error. Give a warning and // accept the words anyway. smsg(_("Warning: region %s not supported"), region); - } else + } + } else { region_mask = 1 << c; + } } if (region_mask != 0) { @@ -2123,8 +2194,9 @@ char_u *did_set_spelllang(win_T *wp) p_->lp_region = region_mask; use_midword(slang, wp); - if (slang->sl_nobreak) + if (slang->sl_nobreak) { nobreak = true; + } } } } @@ -2138,8 +2210,9 @@ char_u *did_set_spelllang(win_T *wp) for (round = 0; round == 0 || *spf != NUL; ++round) { if (round == 0) { // Internal wordlist, if there is one. - if (int_wordlist == NULL) + if (int_wordlist == NULL) { continue; + } int_wordlist_spl(spf_name); } else { // One entry in 'spellfile'. @@ -2154,8 +2227,9 @@ char_u *did_set_spelllang(win_T *wp) break; } } - if (c < ga.ga_len) + if (c < ga.ga_len) { continue; + } } // Check if it was loaded already. @@ -2169,31 +2243,34 @@ char_u *did_set_spelllang(win_T *wp) // Not loaded, try loading it now. The language name includes the // region name, the region is ignored otherwise. for int_wordlist // use an arbitrary name. - if (round == 0) + if (round == 0) { STRCPY(lang, "internal wordlist"); - else { + } else { STRLCPY(lang, path_tail(spf_name), MAXWLEN + 1); p = vim_strchr(lang, '.'); - if (p != NULL) + if (p != NULL) { *p = NUL; // truncate at ".encoding.add" + } } slang = spell_load_file(spf_name, lang, NULL, true); // If one of the languages has NOBREAK we assume the addition // files also have this. - if (slang != NULL && nobreak) + if (slang != NULL && nobreak) { slang->sl_nobreak = true; + } } if (slang != NULL) { region_mask = REGION_ALL; if (use_region != NULL && !dont_use_region) { // find region in sl_regions c = find_region(slang->sl_regions, use_region); - if (c != REGION_ALL) + if (c != REGION_ALL) { region_mask = 1 << c; - else if (*slang->sl_regions != NUL) + } else if (*slang->sl_regions != NUL) { // This spell file is for other regions. region_mask = 0; + } } if (region_mask != 0) { @@ -2219,36 +2296,38 @@ char_u *did_set_spelllang(win_T *wp) lp = LANGP_ENTRY(ga, i); // sound folding - if (!GA_EMPTY(&lp->lp_slang->sl_sal)) + if (!GA_EMPTY(&lp->lp_slang->sl_sal)) { // language does sound folding itself lp->lp_sallang = lp->lp_slang; - else + } else { // find first similar language that does sound folding for (int j = 0; j < ga.ga_len; ++j) { lp2 = LANGP_ENTRY(ga, j); if (!GA_EMPTY(&lp2->lp_slang->sl_sal) && STRNCMP(lp->lp_slang->sl_name, - lp2->lp_slang->sl_name, 2) == 0) { + lp2->lp_slang->sl_name, 2) == 0) { lp->lp_sallang = lp2->lp_slang; break; } } + } // REP items - if (!GA_EMPTY(&lp->lp_slang->sl_rep)) + if (!GA_EMPTY(&lp->lp_slang->sl_rep)) { // language has REP items itself lp->lp_replang = lp->lp_slang; - else + } else { // find first similar language that has REP items for (int j = 0; j < ga.ga_len; ++j) { lp2 = LANGP_ENTRY(ga, j); if (!GA_EMPTY(&lp2->lp_slang->sl_rep) && STRNCMP(lp->lp_slang->sl_name, - lp2->lp_slang->sl_name, 2) == 0) { + lp2->lp_slang->sl_name, 2) == 0) { lp->lp_replang = lp2->lp_slang; break; } } + } } theend: @@ -2302,10 +2381,12 @@ static int find_region(char_u *rp, char_u *region) int i; for (i = 0;; i += 2) { - if (rp[i] == NUL) + if (rp[i] == NUL) { return REGION_ALL; - if (rp[i] == region[0] && rp[i + 1] == region[1]) + } + if (rp[i] == region[0] && rp[i + 1] == region[1]) { break; + } } return i / 2; } @@ -2323,7 +2404,7 @@ static int find_region(char_u *rp, char_u *region) int captype(char_u *word, char_u *end) FUNC_ATTR_NONNULL_ARG(1) { - char_u *p; + char_u *p; int firstcap; bool allcap; bool past_second = false; // past second word char @@ -2356,10 +2437,12 @@ int captype(char_u *word, char_u *end) } } - if (allcap) + if (allcap) { return WF_ALLCAP; - if (firstcap) + } + if (firstcap) { return WF_ONECAP; + } return 0; } @@ -2373,7 +2456,7 @@ static int badword_captype(char_u *word, char_u *end) int c; int l, u; bool first; - char_u *p; + char_u *p; if (flags & WF_KEEPCAP) { // Count the number of UPPER and lower case letters. @@ -2383,23 +2466,27 @@ static int badword_captype(char_u *word, char_u *end) c = PTR2CHAR(p); if (SPELL_ISUPPER(c)) { ++u; - if (p == word) + if (p == word) { first = true; - } else + } + } else { ++l; + } } // If there are more UPPER than lower case letters suggest an // ALLCAP word. Otherwise, if the first letter is UPPER then // suggest ONECAP. Exception: "ALl" most likely should be "All", // require three upper case letters. - if (u > l && u > 2) + if (u > l && u > 2) { flags |= WF_ALLCAP; - else if (first) + } else if (first) { flags |= WF_ONECAP; + } - if (u >= 2 && l >= 2) // maCARONI maCAroni + if (u >= 2 && l >= 2) { // maCARONI maCAroni flags |= WF_MIXCAP; + } } return flags; } @@ -2407,7 +2494,7 @@ static int badword_captype(char_u *word, char_u *end) // Delete the internal wordlist and its .spl file. void spell_delete_wordlist(void) { - char_u fname[MAXPATHL] = {0}; + char_u fname[MAXPATHL] = { 0 }; if (int_wordlist != NULL) { os_remove((char *)int_wordlist); @@ -2420,7 +2507,7 @@ void spell_delete_wordlist(void) // Free all languages. void spell_free_all(void) { - slang_T *slang; + slang_T *slang; // Go through all buffers and handle 'spelllang'. <VN> FOR_ALL_BUFFERS(buf) { @@ -2475,10 +2562,10 @@ static int bytes2offset(char_u **pp) c = *p++; if ((c & 0x80) == 0x00) { // 1 byte nr = c - 1; - } else if ((c & 0xc0) == 0x80) { // 2 bytes + } else if ((c & 0xc0) == 0x80) { // 2 bytes nr = (c & 0x3f) - 1; nr = nr * 255 + (*p++ - 1); - } else if ((c & 0xe0) == 0xc0) { // 3 bytes + } else if ((c & 0xe0) == 0xc0) { // 3 bytes nr = (c & 0x1f) - 1; nr = nr * 255 + (*p++ - 1); nr = nr * 255 + (*p++ - 1); @@ -2537,8 +2624,9 @@ void clear_spell_chartab(spelltab_T *sp) // We include digits. A word shouldn't start with a digit, but handling // that is done separately. - for (i = '0'; i <= '9'; ++i) + for (i = '0'; i <= '9'; ++i) { sp->st_isw[i] = true; + } for (i = 'A'; i <= 'Z'; ++i) { sp->st_isw[i] = true; sp->st_isu[i] = true; @@ -2627,9 +2715,10 @@ bool spell_iswordp_nmw(const char_u *p, win_T *wp) static bool spell_mb_isword_class(int cl, const win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { - if (wp->w_s->b_cjk) + if (wp->w_s->b_cjk) { // East Asian characters are not considered word characters. return cl == 2 || cl == 0x2800; + } return cl >= 2 && cl != 0x2070 && cl != 0x2080 && cl != 3; } @@ -2641,11 +2730,12 @@ static bool spell_iswordp_w(const int *p, const win_T *wp) const int *s; if (*p < 256 ? wp->w_s->b_spell_ismw[*p] - : (wp->w_s->b_spell_ismw_mb != NULL - && vim_strchr(wp->w_s->b_spell_ismw_mb, *p) != NULL)) + : (wp->w_s->b_spell_ismw_mb != NULL + && vim_strchr(wp->w_s->b_spell_ismw_mb, *p) != NULL)) { s = p + 1; - else + } else { s = p; + } if (*s > 255) { return spell_mb_isword_class(utf_class(*s), wp); @@ -2657,8 +2747,7 @@ static bool spell_iswordp_w(const int *p, const win_T *wp) // Uses the character definitions from the .spl file. // When using a multi-byte 'encoding' the length may change! // Returns FAIL when something wrong. -int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf, - int buflen) +int spell_casefold(const win_T *wp, char_u *str, int len, char_u *buf, int buflen) FUNC_ATTR_NONNULL_ALL { if (len >= buflen) { @@ -2708,8 +2797,8 @@ static int sps_limit = 9999; // max nr of suggestions given // Sets "sps_flags" and "sps_limit". int spell_check_sps(void) { - char_u *p; - char_u *s; + char_u *p; + char_u *s; char_u buf[MAXPATHL]; int f; @@ -2742,12 +2831,14 @@ int spell_check_sps(void) sps_limit = 9999; return FAIL; } - if (f != 0) + if (f != 0) { sps_flags = f; + } } - if (sps_flags == 0) + if (sps_flags == 0) { sps_flags = SPS_BEST; + } return OK; } @@ -2758,13 +2849,13 @@ int spell_check_sps(void) // When "count" is non-zero use that suggestion. void spell_suggest(int count) { - char_u *line; + char_u *line; pos_T prev_cursor = curwin->w_cursor; char_u wcopy[MAXWLEN + 2]; - char_u *p; + char_u *p; int c; suginfo_T sug; - suggest_T *stp; + suggest_T *stp; int mouse_used; int need_cap; int limit; @@ -2833,36 +2924,39 @@ void spell_suggest(int count) // Get the list of suggestions. Limit to 'lines' - 2 or the number in // 'spellsuggest', whatever is smaller. - if (sps_limit > (int)Rows - 2) + if (sps_limit > (int)Rows - 2) { limit = (int)Rows - 2; - else + } else { limit = sps_limit; + } spell_find_suggest(line + curwin->w_cursor.col, badlen, &sug, limit, - true, need_cap, true); + true, need_cap, true); - if (GA_EMPTY(&sug.su_ga)) + if (GA_EMPTY(&sug.su_ga)) { MSG(_("Sorry, no suggestions")); - else if (count > 0) { - if (count > sug.su_ga.ga_len) + } else if (count > 0) { + if (count > sug.su_ga.ga_len) { smsg(_("Sorry, only %" PRId64 " suggestions"), (int64_t)sug.su_ga.ga_len); + } } else { // When 'rightleft' is set the list is drawn right-left. cmdmsg_rl = curwin->w_p_rl; - if (cmdmsg_rl) + if (cmdmsg_rl) { msg_col = Columns - 1; + } // List the suggestions. msg_start(); msg_row = Rows - 1; // for when 'cmdheight' > 1 lines_left = Rows; // avoid more prompt vim_snprintf((char *)IObuff, IOSIZE, _("Change \"%.*s\" to:"), - sug.su_badlen, sug.su_badptr); + sug.su_badlen, sug.su_badptr); if (cmdmsg_rl && STRNCMP(IObuff, "Change", 6) == 0) { // And now the rabbit from the high hat: Avoid showing the // untranslated message rightleft. vim_snprintf((char *)IObuff, IOSIZE, ":ot \"%.*s\" egnahC", - sug.su_badlen, sug.su_badptr); + sug.su_badlen, sug.su_badptr); } msg_puts((const char *)IObuff); msg_clr_eos(); @@ -2875,10 +2969,11 @@ void spell_suggest(int count) // The suggested word may replace only part of the bad word, add // the not replaced part. STRLCPY(wcopy, stp->st_word, MAXWLEN + 1); - if (sug.su_badlen > stp->st_orglen) + if (sug.su_badlen > stp->st_orglen) { STRLCPY(wcopy + stp->st_wordlen, - sug.su_badptr + stp->st_orglen, - sug.su_badlen - stp->st_orglen + 1); + sug.su_badptr + stp->st_orglen, + sug.su_badlen - stp->st_orglen + 1); + } vim_snprintf((char *)IObuff, IOSIZE, "%2d", i + 1); if (cmdmsg_rl) { rl_mirror(IObuff); @@ -2897,16 +2992,18 @@ void spell_suggest(int count) if (p_verbose > 0) { // Add the score. - if (sps_flags & (SPS_DOUBLE | SPS_BEST)) + if (sps_flags & (SPS_DOUBLE | SPS_BEST)) { vim_snprintf((char *)IObuff, IOSIZE, " (%s%d - %d)", - stp->st_salscore ? "s " : "", - stp->st_score, stp->st_altscore); - else + stp->st_salscore ? "s " : "", + stp->st_score, stp->st_altscore); + } else { vim_snprintf((char *)IObuff, IOSIZE, " (%d)", - stp->st_score); - if (cmdmsg_rl) + stp->st_score); + } + if (cmdmsg_rl) { // Mirror the numbers, but keep the leading space. rl_mirror(IObuff + 1); + } msg_advance(30); msg_puts((const char *)IObuff); } @@ -2941,8 +3038,8 @@ void spell_suggest(int count) // repl_to. repl_from = vim_strnsave(sug.su_badptr, sug.su_badlen); vim_snprintf((char *)IObuff, IOSIZE, "%s%.*s", stp->st_word, - sug.su_badlen - stp->st_orglen, - sug.su_badptr + stp->st_orglen); + sug.su_badlen - stp->st_orglen, + sug.su_badptr + stp->st_orglen); repl_to = vim_strsave(IObuff); } else { // Replacing su_badlen or more, use the whole word. @@ -2961,7 +3058,7 @@ void spell_suggest(int count) ResetRedobuff(); AppendToRedobuff("ciw"); AppendToRedobuffLit(p + c, - stp->st_wordlen + sug.su_badlen - stp->st_orglen); + stp->st_wordlen + sug.su_badlen - stp->st_orglen); AppendCharToRedobuff(ESC); // "p" may be freed here @@ -2969,8 +3066,9 @@ void spell_suggest(int count) curwin->w_cursor.col = c; changed_bytes(curwin->w_cursor.lnum, c); - } else + } else { curwin->w_cursor = prev_cursor; + } spell_find_cleanup(&sug); xfree(line); @@ -2982,27 +3080,28 @@ void spell_suggest(int count) static bool check_need_cap(linenr_T lnum, colnr_T col) { bool need_cap = false; - char_u *line; - char_u *line_copy = NULL; - char_u *p; + char_u *line; + char_u *line_copy = NULL; + char_u *p; colnr_T endcol; regmatch_T regmatch; - if (curwin->w_s->b_cap_prog == NULL) + if (curwin->w_s->b_cap_prog == NULL) { return false; + } line = get_cursor_line_ptr(); endcol = 0; if (getwhitecols(line) >= (int)col) { // At start of line, check if previous line is empty or sentence // ends there. - if (lnum == 1) + if (lnum == 1) { need_cap = true; - else { + } else { line = ml_get(lnum - 1); - if (*skipwhite(line) == NUL) + if (*skipwhite(line) == NUL) { need_cap = true; - else { + } else { // Append a space in place of the line break. line_copy = concat_str(line, (char_u *)" "); line = line_copy; @@ -3042,10 +3141,10 @@ static bool check_need_cap(linenr_T lnum, colnr_T col) void ex_spellrepall(exarg_T *eap) { pos_T pos = curwin->w_cursor; - char_u *frompat; + char_u *frompat; int addlen; - char_u *line; - char_u *p; + char_u *line; + char_u *p; bool save_ws = p_ws; linenr_T prev_lnum = 0; @@ -3072,7 +3171,7 @@ void ex_spellrepall(exarg_T *eap) // when changing "etc" to "etc.". line = get_cursor_line_ptr(); if (addlen <= 0 || STRNCMP(line + curwin->w_cursor.col, - repl_to, STRLEN(repl_to)) != 0) { + repl_to, STRLEN(repl_to)) != 0) { p = xmalloc(STRLEN(line) + addlen + 1); memmove(p, line, curwin->w_cursor.col); STRCPY(p + curwin->w_cursor.col, repl_to); @@ -3093,26 +3192,23 @@ void ex_spellrepall(exarg_T *eap) curwin->w_cursor = pos; xfree(frompat); - if (sub_nsubs == 0) + if (sub_nsubs == 0) { EMSG2(_("E753: Not found: %s"), repl_from); - else + } else { do_sub_msg(false); + } } -// Find spell suggestions for "word". Return them in the growarray "*gap" as -// a list of allocated strings. -void -spell_suggest_list ( - garray_T *gap, - char_u *word, - int maxcount, // maximum nr of suggestions - bool need_cap, // 'spellcapcheck' matched - bool interactive -) +/// Find spell suggestions for "word". Return them in the growarray "*gap" as +/// a list of allocated strings. +/// +/// @param maxcount maximum nr of suggestions +/// @param need_cap 'spellcapcheck' matched +void spell_suggest_list(garray_T *gap, char_u *word, int maxcount, bool need_cap, bool interactive) { suginfo_T sug; - suggest_T *stp; - char_u *wcopy; + suggest_T *stp; + char_u *wcopy; spell_find_suggest(word, 0, &sug, maxcount, false, need_cap, interactive); @@ -3134,44 +3230,41 @@ spell_suggest_list ( spell_find_cleanup(&sug); } -// Find spell suggestions for the word at the start of "badptr". -// Return the suggestions in "su->su_ga". -// The maximum number of suggestions is "maxcount". -// Note: does use info for the current window. -// This is based on the mechanisms of Aspell, but completely reimplemented. -static void -spell_find_suggest ( - char_u *badptr, - int badlen, // length of bad word or 0 if unknown - suginfo_T *su, - int maxcount, - bool banbadword, // don't include badword in suggestions - bool need_cap, // word should start with capital - bool interactive -) +/// Find spell suggestions for the word at the start of "badptr". +/// Return the suggestions in "su->su_ga". +/// The maximum number of suggestions is "maxcount". +/// Note: does use info for the current window. +/// This is based on the mechanisms of Aspell, but completely reimplemented. +/// +/// @param badlen length of bad word or 0 if unknown +/// @param banbadword don't include badword in suggestions +/// @param need_cap word should start with capital +static void spell_find_suggest(char_u *badptr, int badlen, suginfo_T *su, int maxcount, + bool banbadword, bool need_cap, bool interactive) { hlf_T attr = HLF_COUNT; char_u buf[MAXPATHL]; - char_u *p; + char_u *p; bool do_combine = false; - char_u *sps_copy; + char_u *sps_copy; static bool expr_busy = false; int c; - langp_T *lp; + langp_T *lp; bool did_intern = false; // Set the info in "*su". memset(su, 0, sizeof(suginfo_T)); ga_init(&su->su_ga, (int)sizeof(suggest_T), 10); ga_init(&su->su_sga, (int)sizeof(suggest_T), 10); - if (*badptr == NUL) + if (*badptr == NUL) { return; + } hash_init(&su->su_banned); su->su_badptr = badptr; - if (badlen != 0) + if (badlen != 0) { su->su_badlen = badlen; - else { + } else { size_t tmplen = spell_check(curwin, su->su_badptr, &attr, NULL, false); assert(tmplen <= INT_MAX); su->su_badlen = (int)tmplen; @@ -3179,8 +3272,9 @@ spell_find_suggest ( su->su_maxcount = maxcount; su->su_maxscore = SCORE_MAXINIT; - if (su->su_badlen >= MAXWLEN) + if (su->su_badlen >= MAXWLEN) { su->su_badlen = MAXWLEN - 1; // just in case + } STRLCPY(su->su_badword, su->su_badptr, su->su_badlen + 1); (void)spell_casefold(curwin, su->su_badptr, su->su_badlen, su->su_fbadword, MAXWLEN); @@ -3192,9 +3286,10 @@ spell_find_suggest ( // get caps flags for bad word su->su_badflags = badword_captype(su->su_badptr, - su->su_badptr + su->su_badlen); - if (need_cap) + su->su_badptr + su->su_badlen); + if (need_cap) { su->su_badflags |= WF_ONECAP; + } // Find the default language for sound folding. We simply use the first // one in 'spelllang' that supports sound folding. That's good for when @@ -3210,9 +3305,10 @@ spell_find_suggest ( // Soundfold the bad word with the default sound folding, so that we don't // have to do this many times. - if (su->su_sallang != NULL) + if (su->su_sallang != NULL) { spell_soundfold(su->su_sallang, su->su_fbadword, true, - su->su_sal_badword); + su->su_sal_badword); + } // If the word is not capitalised and spell_check() doesn't consider the // word to be bad then it might need to be capitalised. Add a suggestion @@ -3221,12 +3317,13 @@ spell_find_suggest ( if (!SPELL_ISUPPER(c) && attr == HLF_COUNT) { make_case_word(su->su_badword, buf, WF_ONECAP); add_suggestion(su, &su->su_ga, buf, su->su_badlen, SCORE_ICASE, - 0, true, su->su_sallang, false); + 0, true, su->su_sallang, false); } // Ban the bad word itself. It may appear in another region. - if (banbadword) + if (banbadword) { add_banned(su, su->su_badword); + } // Make a copy of 'spellsuggest', because the expression may change it. sps_copy = vim_strsave(p_sps); @@ -3258,10 +3355,11 @@ spell_find_suggest ( xfree(sps_copy); - if (do_combine) + if (do_combine) { // Combine the two list of suggestions. This must be done last, // because sorting changes the order again. score_combine(su); + } } // Find suggestions by evaluating expression "expr". @@ -3297,9 +3395,9 @@ static void spell_suggest_expr(suginfo_T *su, char_u *expr) // Find suggestions in file "fname". Used for "file:" in 'spellsuggest'. static void spell_suggest_file(suginfo_T *su, char_u *fname) { - FILE *fd; + FILE *fd; char_u line[MAXWLEN * 2]; - char_u *p; + char_u *p; int len; char_u cword[MAXWLEN]; @@ -3315,13 +3413,15 @@ static void spell_suggest_file(suginfo_T *su, char_u *fname) line_breakcheck(); p = vim_strchr(line, '/'); - if (p == NULL) + if (p == NULL) { continue; // No Tab found, just skip the line. + } *p++ = NUL; if (STRICMP(su->su_badword, line) == 0) { // Match! Isolate the good word, until CR or NL. - for (len = 0; p[len] >= ' '; ++len) + for (len = 0; p[len] >= ' '; ++len) { ; + } p[len] = NUL; // If the suggestion doesn't have specific case duplicate the case @@ -3332,7 +3432,7 @@ static void spell_suggest_file(suginfo_T *su, char_u *fname) } add_suggestion(su, &su->su_ga, p, su->su_badlen, - SCORE_FILE, 0, true, su->su_sallang, false); + SCORE_FILE, 0, true, su->su_sallang, false); } } @@ -3360,15 +3460,17 @@ static void spell_suggest_intern(suginfo_T *su, bool interactive) suggest_try_change(su); // For the resulting top-scorers compute the sound-a-like score. - if (sps_flags & SPS_DOUBLE) + if (sps_flags & SPS_DOUBLE) { score_comp_sal(su); + } // 3. Try finding sound-a-like words. if ((sps_flags & SPS_FAST) == 0) { - if (sps_flags & SPS_BEST) + if (sps_flags & SPS_BEST) { // Adjust the word score for the suggestions found so far for how // they sounds like. rescore_suggestions(su); + } // While going through the soundfold tree "su_maxscore" is the score // for the soundfold word, limits the changes that are being tried, @@ -3407,9 +3509,10 @@ static void spell_suggest_intern(suginfo_T *su, bool interactive) } if ((sps_flags & SPS_DOUBLE) == 0 && su->su_ga.ga_len != 0) { - if (sps_flags & SPS_BEST) + if (sps_flags & SPS_BEST) { // Adjust the word score for how it sounds like. rescore_suggestions(su); + } // Remove bogus suggestions, sort and truncate at "maxcount". check_suggestions(su, &su->su_ga); @@ -3420,7 +3523,7 @@ static void spell_suggest_intern(suginfo_T *su, bool interactive) // Free the info put in "*su" by spell_find_suggest(). static void spell_find_cleanup(suginfo_T *su) { -# define FREE_SUG_WORD(sug) xfree(sug->st_word) +#define FREE_SUG_WORD(sug) xfree(sug->st_word) // Free the suggestions. GA_DEEP_CLEAR(&su->su_ga, suggest_T, FREE_SUG_WORD); GA_DEEP_CLEAR(&su->su_sga, suggest_T, FREE_SUG_WORD); @@ -3459,11 +3562,13 @@ static void allcap_copy(char_u *word, char_u *wcopy) if (c == 0xdf) { c = 'S'; - if (d - wcopy >= MAXWLEN - 1) + if (d - wcopy >= MAXWLEN - 1) { break; + } *d++ = c; - } else + } else { c = SPELL_TOUPPER(c); + } if (d - wcopy >= MAXWLEN - MB_MAXBYTES) { break; @@ -3476,7 +3581,7 @@ static void allcap_copy(char_u *word, char_u *wcopy) // Try finding suggestions by recognizing specific situations. static void suggest_try_special(suginfo_T *su) { - char_u *p; + char_u *p; size_t len; int c; char_u word[MAXWLEN]; @@ -3496,7 +3601,7 @@ static void suggest_try_special(suginfo_T *su) // Give a soundalike score of 0, compute the score as if deleting one // character. add_suggestion(su, &su->su_ga, word, su->su_badlen, - RESCORE(SCORE_REP, 0), 0, true, su->su_sallang, false); + RESCORE(SCORE_REP, 0), 0, true, su->su_sallang, false); } } @@ -3509,8 +3614,7 @@ proftime_T total; proftime_T times[STATE_FINAL + 1]; long counts[STATE_FINAL + 1]; - static void -prof_init(void) +static void prof_init(void) { for (int i = 0; i <= STATE_FINAL; i++) { profile_zero(×[i]); @@ -3521,8 +3625,7 @@ prof_init(void) } // call before changing state - static void -prof_store(state_T state) +static void prof_store(state_T state) { profile_end(¤t); profile_add(×[state], ¤t); @@ -3531,8 +3634,7 @@ prof_store(state_T state) } # define PROF_STORE(state) prof_store(state); - static void -prof_report(char *name) +static void prof_report(char *name) { FILE *fd = fopen("suggestprof", "a"); @@ -3554,8 +3656,8 @@ static void suggest_try_change(suginfo_T *su) { char_u fword[MAXWLEN]; // copy of the bad word, case-folded int n; - char_u *p; - langp_T *lp; + char_u *p; + langp_T *lp; // We make a copy of the case-folded bad word, so that we can modify it // to find matches (esp. REP items). Append some more text, changing @@ -3570,8 +3672,9 @@ static void suggest_try_change(suginfo_T *su) // If reloading a spell file fails it's still in the list but // everything has been cleared. - if (lp->lp_slang->sl_fbyts == NULL) + if (lp->lp_slang->sl_fbyts == NULL) { continue; + } // Try it for this language. Will add possible suggestions. // @@ -3623,28 +3726,28 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so char_u tword[MAXWLEN]; // good word collected so far trystate_T stack[MAXWLEN]; char_u preword[MAXWLEN * 3] = { 0 }; // word found with proper case; - // concatenation of prefix compound - // words and split word. NUL terminated - // when going deeper but not when coming - // back. + // concatenation of prefix compound + // words and split word. NUL terminated + // when going deeper but not when coming + // back. char_u compflags[MAXWLEN]; // compound flags, one for each word - trystate_T *sp; + trystate_T *sp; int newscore; int score; - char_u *byts, *fbyts, *pbyts; - idx_T *idxs, *fidxs, *pidxs; + char_u *byts, *fbyts, *pbyts; + idx_T *idxs, *fidxs, *pidxs; int depth; int c, c2, c3; int n = 0; int flags; - garray_T *gap; + garray_T *gap; idx_T arridx; int len; - char_u *p; - fromto_T *ftp; + char_u *p; + fromto_T *ftp; int fl = 0, tl; int repextra = 0; // extra bytes in fword[] from REP item - slang_T *slang = lp->lp_slang; + slang_T *slang = lp->lp_slang; int fword_ends; bool goodword_ends; #ifdef DEBUG_TRIEWALK @@ -3709,8 +3812,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (sp->ts_prefixdepth == PFD_PREFIXTREE) { // Skip over the NUL bytes, we use them later. - for (n = 0; n < len && byts[arridx + n] == 0; ++n) + for (n = 0; n < len && byts[arridx + n] == 0; ++n) { ; + } sp->ts_curi += n; // Always past NUL bytes now. @@ -3727,7 +3831,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so n = nofold_len(fword, sp->ts_fidx, su->su_badptr); flags = badword_captype(su->su_badptr, su->su_badptr + n); su->su_badflags = badword_captype(su->su_badptr + n, - su->su_badptr + su->su_badlen); + su->su_badptr + su->su_badlen); #ifdef DEBUG_TRIEWALK sprintf(changename[depth], "prefix"); #endif @@ -3743,7 +3847,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // and make find_keepcap_word() works. tword[sp->ts_twordlen] = NUL; make_case_word(tword + sp->ts_splitoff, - preword + sp->ts_prewordlen, flags); + preword + sp->ts_prewordlen, flags); sp->ts_prewordlen = (char_u)STRLEN(preword); sp->ts_splitoff = sp->ts_twordlen; } @@ -3764,8 +3868,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so flags = (int)idxs[arridx]; // Skip words with the NOSUGGEST flag. - if (flags & WF_NOSUGGEST) + if (flags & WF_NOSUGGEST) { break; + } fword_ends = (fword[sp->ts_fidx] == NUL || (soundfold @@ -3782,17 +3887,20 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // none this must be the first try without a prefix. n = stack[sp->ts_prefixdepth].ts_arridx; len = pbyts[n++]; - for (c = 0; c < len && pbyts[n + c] == 0; ++c) + for (c = 0; c < len && pbyts[n + c] == 0; ++c) { ; + } if (c > 0) { c = valid_word_prefix(c, n, flags, - tword + sp->ts_splitoff, slang, false); - if (c == 0) + tword + sp->ts_splitoff, slang, false); + if (c == 0) { break; + } // Use the WF_RARE flag for a rare prefix. - if (c & WF_RAREPFX) + if (c & WF_RAREPFX) { flags |= WF_RARE; + } // Tricky: when checking for both prefix and compounding // we run into the prefix flag first. @@ -3805,10 +3913,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Check NEEDCOMPOUND: can't use word without compounding. Do try // appending another compound word below. if (sp->ts_complen == sp->ts_compsplit && fword_ends - && (flags & WF_NEEDCOMP)) + && (flags & WF_NEEDCOMP)) { goodword_ends = false; - else + } else { goodword_ends = true; + } p = NULL; compound_ok = true; @@ -3821,18 +3930,19 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (sp->ts_fidx - sp->ts_splitfidx == sp->ts_twordlen - sp->ts_splitoff && STRNCMP(fword + sp->ts_splitfidx, - tword + sp->ts_splitoff, - sp->ts_fidx - sp->ts_splitfidx) == 0) { + tword + sp->ts_splitoff, + sp->ts_fidx - sp->ts_splitfidx) == 0) { preword[sp->ts_prewordlen] = NUL; newscore = score_wordcount_adj(slang, sp->ts_score, - preword + sp->ts_prewordlen, - sp->ts_prewordlen > 0); + preword + sp->ts_prewordlen, + sp->ts_prewordlen > 0); // Add the suggestion if the score isn't too bad. - if (newscore <= su->su_maxscore) + if (newscore <= su->su_maxscore) { add_suggestion(su, &su->su_ga, preword, - sp->ts_splitfidx - repextra, - newscore, 0, false, - lp->lp_sallang, false); + sp->ts_splitfidx - repextra, + newscore, 0, false, + lp->lp_sallang, false); + } break; } } else { @@ -3856,23 +3966,26 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so compflags[sp->ts_complen] = ((unsigned)flags >> 24); compflags[sp->ts_complen + 1] = NUL; STRLCPY(preword + sp->ts_prewordlen, - tword + sp->ts_splitoff, - sp->ts_twordlen - sp->ts_splitoff + 1); + tword + sp->ts_splitoff, + sp->ts_twordlen - sp->ts_splitoff + 1); // Verify CHECKCOMPOUNDPATTERN rules. if (match_checkcompoundpattern(preword, sp->ts_prewordlen, - &slang->sl_comppat)) + &slang->sl_comppat)) { compound_ok = false; + } if (compound_ok) { p = preword; - while (*skiptowhite(p) != NUL) + while (*skiptowhite(p) != NUL) { p = skipwhite(skiptowhite(p)); + } if (fword_ends && !can_compound(slang, p, - compflags + sp->ts_compsplit)) + compflags + sp->ts_compsplit)) { // Compound is not allowed. But it may still be // possible if we add another (short) word. compound_ok = false; + } } // Get pointer to last char of previous word. @@ -3884,29 +3997,31 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Form the word with proper case in preword. // If there is a word from a previous split, append. // For the soundfold tree don't change the case, simply append. - if (soundfold) + if (soundfold) { STRCPY(preword + sp->ts_prewordlen, tword + sp->ts_splitoff); - else if (flags & WF_KEEPCAP) + } else if (flags & WF_KEEPCAP) { // Must find the word in the keep-case tree. find_keepcap_word(slang, tword + sp->ts_splitoff, - preword + sp->ts_prewordlen); - else { + preword + sp->ts_prewordlen); + } else { // Include badflags: If the badword is onecap or allcap // use that for the goodword too. But if the badword is // allcap and it's only one char long use onecap. c = su->su_badflags; if ((c & WF_ALLCAP) - && su->su_badlen == (*mb_ptr2len)(su->su_badptr) - ) + && su->su_badlen == + (*mb_ptr2len)(su->su_badptr)) { c = WF_ONECAP; + } c |= flags; // When appending a compound word after a word character don't // use Onecap. - if (p != NULL && spell_iswordp_nmw(p, curwin)) + if (p != NULL && spell_iswordp_nmw(p, curwin)) { c &= ~WF_ONECAP; + } make_case_word(tword + sp->ts_splitoff, - preword + sp->ts_prewordlen, c); + preword + sp->ts_prewordlen, c); } if (!soundfold) { @@ -3919,8 +4034,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if ((sp->ts_complen == sp->ts_compsplit && WAS_BANNED(su, preword + sp->ts_prewordlen)) || WAS_BANNED(su, preword)) { - if (slang->sl_compprog == NULL) + if (slang->sl_compprog == NULL) { break; + } // the word so far was banned but we may try compounding goodword_ends = false; } @@ -3929,14 +4045,17 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so newscore = 0; if (!soundfold) { // soundfold words don't have flags if ((flags & WF_REGION) - && (((unsigned)flags >> 16) & lp->lp_region) == 0) + && (((unsigned)flags >> 16) & lp->lp_region) == 0) { newscore += SCORE_REGION; - if (flags & WF_RARE) + } + if (flags & WF_RARE) { newscore += SCORE_RARE; + } if (!spell_valid_case(su->su_badflags, - captype(preword + sp->ts_prewordlen, NULL))) + captype(preword + sp->ts_prewordlen, NULL))) { newscore += SCORE_ICASE; + } } // TODO: how about splitting in the soundfold tree? @@ -3951,15 +4070,16 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // print the stack of changes that brought us here smsg("------ %s -------", fword); - for (j = 0; j < depth; ++j) + for (j = 0; j < depth; ++j) { smsg("%s", changename[j]); + } } #endif if (soundfold) { // For soundfolded words we need to find the original // words, the edit distance and then add them. add_sound_suggest(su, preword, sp->ts_score, lp); - } else if (sp->ts_fidx > 0) { + } else if (sp->ts_fidx > 0) { // Give a penalty when changing non-word char to word // char, e.g., "thes," -> "these". p = fword + sp->ts_fidx; @@ -3974,15 +4094,15 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Give a bonus to words seen before. score = score_wordcount_adj(slang, - sp->ts_score + newscore, - preword + sp->ts_prewordlen, - sp->ts_prewordlen > 0); + sp->ts_score + newscore, + preword + sp->ts_prewordlen, + sp->ts_prewordlen > 0); // Add the suggestion if the score isn't too bad. if (score <= su->su_maxscore) { add_suggestion(su, &su->su_ga, preword, - sp->ts_fidx - repextra, - score, 0, false, lp->lp_sallang, false); + sp->ts_fidx - repextra, + score, 0, false, lp->lp_sallang, false); if (su->su_badflags & WF_MIXCAP) { // We really don't know if the word should be @@ -3990,13 +4110,13 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so c = captype(preword, NULL); if (c == 0 || c == WF_ALLCAP) { make_case_word(tword + sp->ts_splitoff, - preword + sp->ts_prewordlen, - c == 0 ? WF_ALLCAP : 0); + preword + sp->ts_prewordlen, + c == 0 ? WF_ALLCAP : 0); add_suggestion(su, &su->su_ga, preword, - sp->ts_fidx - repextra, - score + SCORE_ICASE, 0, false, - lp->lp_sallang, false); + sp->ts_fidx - repextra, + score + SCORE_ICASE, 0, false, + lp->lp_sallang, false); } } } @@ -4006,8 +4126,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Try word split and/or compounding. if ((sp->ts_fidx >= sp->ts_fidxtry || fword_ends) // Don't split in the middle of a character - && (sp->ts_tcharlen == 0) - ) { + && (sp->ts_tcharlen == 0)) { bool try_compound; int try_split; @@ -4045,7 +4164,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so || sp->ts_complen + 1 - sp->ts_compsplit < slang->sl_compmax) && (can_be_compound(sp, slang, - compflags, ((unsigned)flags >> 24)))) { + compflags, ((unsigned)flags >> 24)))) { try_compound = true; compflags[sp->ts_complen] = ((unsigned)flags >> 24); compflags[sp->ts_complen + 1] = NUL; @@ -4076,35 +4195,40 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // is only one word it must not have the NEEDCOMPOUND // flag. if (sp->ts_complen == sp->ts_compsplit - && (flags & WF_NEEDCOMP)) + && (flags & WF_NEEDCOMP)) { break; + } p = preword; - while (*skiptowhite(p) != NUL) + while (*skiptowhite(p) != NUL) { p = skipwhite(skiptowhite(p)); + } if (sp->ts_complen > sp->ts_compsplit && !can_compound(slang, p, - compflags + sp->ts_compsplit)) + compflags + sp->ts_compsplit)) { break; + } - if (slang->sl_nosplitsugs) + if (slang->sl_nosplitsugs) { newscore += SCORE_SPLIT_NO; - else + } else { newscore += SCORE_SPLIT; + } // Give a bonus to words seen before. newscore = score_wordcount_adj(slang, newscore, - preword + sp->ts_prewordlen, true); + preword + sp->ts_prewordlen, true); } if (TRY_DEEPER(su, stack, depth, newscore)) { go_deeper(stack, depth, newscore); #ifdef DEBUG_TRIEWALK - if (!try_compound && !fword_ends) + if (!try_compound && !fword_ends) { sprintf(changename[depth], "%.*s-%s: split", - sp->ts_twordlen, tword, fword + sp->ts_fidx); - else + sp->ts_twordlen, tword, fword + sp->ts_fidx); + } else { sprintf(changename[depth], "%.*s-%s: compound", - sp->ts_twordlen, tword, fword + sp->ts_fidx); + sp->ts_twordlen, tword, fword + sp->ts_fidx); + } #endif // Save things to be restored at STATE_SPLITUNDO. sp->ts_save_badflags = su->su_badflags; @@ -4115,8 +4239,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so sp = &stack[depth]; // Append a space to preword when splitting. - if (!try_compound && !fword_ends) + if (!try_compound && !fword_ends) { STRCAT(preword, " "); + } sp->ts_prewordlen = (char_u)STRLEN(preword); sp->ts_splitoff = sp->ts_twordlen; sp->ts_splitfidx = sp->ts_fidx; @@ -4127,8 +4252,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // character when the word ends. But only when the // good word can end. if (((!try_compound && !spell_iswordp_nmw(fword - + sp->ts_fidx, - curwin)) + + sp->ts_fidx, + curwin)) || fword_ends) && fword[sp->ts_fidx] != NUL && goodword_ends) { @@ -4138,28 +4263,30 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so if (fword_ends) { // Copy the skipped character to preword. memmove(preword + sp->ts_prewordlen, - fword + sp->ts_fidx, l); + fword + sp->ts_fidx, l); sp->ts_prewordlen += l; preword[sp->ts_prewordlen] = NUL; - } else + } else { sp->ts_score -= SCORE_SPLIT - SCORE_SUBST; + } sp->ts_fidx += l; } // When compounding include compound flag in // compflags[] (already set above). When splitting we // may start compounding over again. - if (try_compound) + if (try_compound) { ++sp->ts_complen; - else + } else { sp->ts_compsplit = sp->ts_complen; + } sp->ts_prefixdepth = PFD_NOPREFIX; // set su->su_badflags to the caps type at this // position n = nofold_len(fword, sp->ts_fidx, su->su_badptr); su->su_badflags = badword_captype(su->su_badptr + n, - su->su_badptr + su->su_badlen); + su->su_badptr + su->su_badlen); // Restart at top of the tree. sp->ts_arridx = 0; @@ -4194,8 +4321,7 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Past the NUL bytes in the node. su->su_badflags = sp->ts_save_badflags; if (fword[sp->ts_fidx] == NUL - && sp->ts_tcharlen == 0 - ) { + && sp->ts_tcharlen == 0) { // The badword ends, can't use STATE_PLAIN. PROF_STORE(sp->ts_state) sp->ts_state = STATE_DEL; @@ -4228,11 +4354,12 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // just deleted this byte, accepting it is always cheaper than // delete + substitute. if (c == fword[sp->ts_fidx] - || (sp->ts_tcharlen > 0 && sp->ts_isdiff != DIFF_NONE) - ) + || (sp->ts_tcharlen > 0 && + sp->ts_isdiff != DIFF_NONE)) { newscore = 0; - else + } else { newscore = SCORE_SUBST; + } if ((newscore == 0 || (sp->ts_fidx >= sp->ts_fidxtry && ((sp->ts_flags & TSF_DIDDEL) == 0 @@ -4240,14 +4367,15 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so && TRY_DEEPER(su, stack, depth, newscore)) { go_deeper(stack, depth, newscore); #ifdef DEBUG_TRIEWALK - if (newscore > 0) + if (newscore > 0) { sprintf(changename[depth], "%.*s-%s: subst %c to %c", - sp->ts_twordlen, tword, fword + sp->ts_fidx, - fword[sp->ts_fidx], c); - else + sp->ts_twordlen, tword, fword + sp->ts_fidx, + fword[sp->ts_fidx], c); + } else { sprintf(changename[depth], "%.*s-%s: accept %c", - sp->ts_twordlen, tword, fword + sp->ts_fidx, - fword[sp->ts_fidx]); + sp->ts_twordlen, tword, fword + sp->ts_fidx, + fword[sp->ts_fidx]); + } #endif ++depth; sp = &stack[depth]; @@ -4289,12 +4417,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so + sp->ts_fcharstart))) { sp->ts_score -= SCORE_SUBST - SCORE_SUBCOMP; } else if ( - !soundfold - && slang->sl_has_map - && similar_chars( - slang, - utf_ptr2char(tword + sp->ts_twordlen - sp->ts_tcharlen), - utf_ptr2char(fword + sp->ts_fcharstart))) { + !soundfold + && slang->sl_has_map + && similar_chars(slang, + utf_ptr2char(tword + sp->ts_twordlen - sp->ts_tcharlen), + utf_ptr2char(fword + sp->ts_fcharstart))) { // For a similar character adjust score from // SCORE_SUBST to SCORE_SIMILAR. sp->ts_score -= SCORE_SUBST - SCORE_SIMILAR; @@ -4339,19 +4466,20 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so PROF_STORE(sp->ts_state) sp->ts_state = STATE_INS_PREP; sp->ts_curi = 1; - if (soundfold && sp->ts_fidx == 0 && fword[sp->ts_fidx] == '*') + if (soundfold && sp->ts_fidx == 0 && fword[sp->ts_fidx] == '*') { // Deleting a vowel at the start of a word counts less, see // soundalike_score(). newscore = 2 * SCORE_DEL / 3; - else + } else { newscore = SCORE_DEL; + } if (fword[sp->ts_fidx] != NUL && TRY_DEEPER(su, stack, depth, newscore)) { go_deeper(stack, depth, newscore); #ifdef DEBUG_TRIEWALK sprintf(changename[depth], "%.*s-%s: delete %c", - sp->ts_twordlen, tword, fword + sp->ts_fidx, - fword[sp->ts_fidx]); + sp->ts_twordlen, tword, fword + sp->ts_fidx, + fword[sp->ts_fidx]); #endif ++depth; @@ -4421,19 +4549,20 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // accepting that byte is always better. n += sp->ts_curi++; c = byts[n]; - if (soundfold && sp->ts_twordlen == 0 && c == '*') + if (soundfold && sp->ts_twordlen == 0 && c == '*') { // Inserting a vowel at the start of a word counts less, // see soundalike_score(). newscore = 2 * SCORE_INS / 3; - else + } else { newscore = SCORE_INS; + } if (c != fword[sp->ts_fidx] && TRY_DEEPER(su, stack, depth, newscore)) { go_deeper(stack, depth, newscore); #ifdef DEBUG_TRIEWALK sprintf(changename[depth], "%.*s-%s: insert %c", - sp->ts_twordlen, tword, fword + sp->ts_fidx, - c); + sp->ts_twordlen, tword, fword + sp->ts_fidx, + c); #endif ++depth; sp = &stack[depth]; @@ -4454,8 +4583,9 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // soundfold words (illogical but does give a better // score). if (sp->ts_twordlen >= 2 - && tword[sp->ts_twordlen - 2] == c) + && tword[sp->ts_twordlen - 2] == c) { sp->ts_score -= SCORE_INS - SCORE_INSDUP; + } } } break; @@ -4566,8 +4696,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so go_deeper(stack, depth, SCORE_SWAP3); #ifdef DEBUG_TRIEWALK sprintf(changename[depth], "%.*s-%s: swap3 %c and %c", - sp->ts_twordlen, tword, fword + sp->ts_fidx, - c, c3); + sp->ts_twordlen, tword, fword + sp->ts_fidx, + c, c3); #endif PROF_STORE(sp->ts_state) sp->ts_state = STATE_UNSWAP3; @@ -4611,8 +4741,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so #ifdef DEBUG_TRIEWALK p = fword + sp->ts_fidx; sprintf(changename[depth], "%.*s-%s: rotate left %c%c%c", - sp->ts_twordlen, tword, fword + sp->ts_fidx, - p[0], p[1], p[2]); + sp->ts_twordlen, tword, fword + sp->ts_fidx, + p[0], p[1], p[2]); #endif PROF_STORE(sp->ts_state) sp->ts_state = STATE_UNROT3L; @@ -4648,8 +4778,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so #ifdef DEBUG_TRIEWALK p = fword + sp->ts_fidx; sprintf(changename[depth], "%.*s-%s: rotate right %c%c%c", - sp->ts_twordlen, tword, fword + sp->ts_fidx, - p[0], p[1], p[2]); + sp->ts_twordlen, tword, fword + sp->ts_fidx, + p[0], p[1], p[2]); #endif PROF_STORE(sp->ts_state) sp->ts_state = STATE_UNROT3R; @@ -4696,10 +4826,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // Use the first byte to quickly find the first entry that may // match. If the index is -1 there is none. - if (soundfold) + if (soundfold) { sp->ts_curi = slang->sl_repsal_first[fword[sp->ts_fidx]]; - else + } else { sp->ts_curi = lp->lp_replang->sl_rep_first[fword[sp->ts_fidx]]; + } if (sp->ts_curi < 0) { PROF_STORE(sp->ts_state) @@ -4717,10 +4848,11 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so // valid. p = fword + sp->ts_fidx; - if (soundfold) + if (soundfold) { gap = &slang->sl_repsal; - else + } else { gap = &lp->lp_replang->sl_rep; + } while (sp->ts_curi < gap->ga_len) { ftp = (fromto_T *)gap->ga_data + sp->ts_curi++; if (*ftp->ft_from != *p) { @@ -4733,8 +4865,8 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so go_deeper(stack, depth, SCORE_REP); #ifdef DEBUG_TRIEWALK sprintf(changename[depth], "%.*s-%s: replace %s with %s", - sp->ts_twordlen, tword, fword + sp->ts_fidx, - ftp->ft_from, ftp->ft_to); + sp->ts_twordlen, tword, fword + sp->ts_fidx, + ftp->ft_from, ftp->ft_to); #endif // Need to undo this afterwards. PROF_STORE(sp->ts_state) @@ -4755,19 +4887,21 @@ static void suggest_trie_walk(suginfo_T *su, langp_T *lp, char_u *fword, bool so } } - if (sp->ts_curi >= gap->ga_len && sp->ts_state == STATE_REP) + if (sp->ts_curi >= gap->ga_len && sp->ts_state == STATE_REP) { // No (more) matches. PROF_STORE(sp->ts_state) sp->ts_state = STATE_FINAL; + } break; case STATE_REP_UNDO: // Undo a REP replacement and continue with the next one. - if (soundfold) + if (soundfold) { gap = &slang->sl_repsal; - else + } else { gap = &lp->lp_replang->sl_rep; + } ftp = (fromto_T *)gap->ga_data + sp->ts_curi - 1; fl = (int)STRLEN(ftp->ft_from); tl = (int)STRLEN(ftp->ft_to); @@ -4815,7 +4949,7 @@ static void go_deeper(trystate_T *stack, int depth, int score_add) // fword[flen] and return the byte length of that many chars in "word". static int nofold_len(char_u *fword, int flen, char_u *word) { - char_u *p; + char_u *p; int i = 0; for (p = fword; p < fword + flen; MB_PTR_ADV(p)) { @@ -4849,9 +4983,9 @@ static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword) int len; int c; idx_T lo, hi, m; - char_u *p; - char_u *byts = slang->sl_kbyts; // array with bytes of the words - idx_T *idxs = slang->sl_kidxs; // array with indexes + char_u *p; + char_u *byts = slang->sl_kbyts; // array with bytes of the words + idx_T *idxs = slang->sl_kidxs; // array with indexes if (byts == NULL) { // array is empty: "cannot happen" @@ -4882,7 +5016,7 @@ static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword) // kword is getting too long, continue one level up --depth; - } else if (++round[depth] > 2) { + } else if (++round[depth] > 2) { // tried both fold-case and upper-case character, continue one // level up --depth; @@ -4907,19 +5041,20 @@ static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword) hi = tryidx + len - 1; while (lo < hi) { m = (lo + hi) / 2; - if (byts[m] > c) + if (byts[m] > c) { hi = m - 1; - else if (byts[m] < c) + } else if (byts[m] < c) { lo = m + 1; - else { + } else { lo = hi = m; break; } } // Stop if there is no matching byte. - if (hi < lo || byts[lo] != c) + if (hi < lo || byts[lo] != c) { break; + } // Continue at the child (if there is one). tryidx = idxs[lo]; @@ -4930,11 +5065,11 @@ static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword) // level deeper. if (round[depth] == 1) { STRNCPY(kword + kwordlen[depth], fword + fwordidx[depth], - flen); + flen); kwordlen[depth + 1] = kwordlen[depth] + flen; } else { STRNCPY(kword + kwordlen[depth], uword + uwordidx[depth], - ulen); + ulen); kwordlen[depth + 1] = kwordlen[depth] + ulen; } fwordidx[depth + 1] = fwordidx[depth] + flen; @@ -4955,11 +5090,11 @@ static void find_keepcap_word(slang_T *slang, char_u *fword, char_u *kword) // su->su_sga. static void score_comp_sal(suginfo_T *su) { - langp_T *lp; + langp_T *lp; char_u badsound[MAXWLEN]; int i; - suggest_T *stp; - suggest_T *sstp; + suggest_T *stp; + suggest_T *sstp; int score; ga_grow(&su->su_sga, su->su_ga.ga_len); @@ -4998,13 +5133,13 @@ static void score_comp_sal(suginfo_T *su) static void score_combine(suginfo_T *su) { garray_T ga; - garray_T *gap; - langp_T *lp; - suggest_T *stp; - char_u *p; + garray_T *gap; + langp_T *lp; + suggest_T *stp; + char_u *p; char_u badsound[MAXWLEN]; int round; - slang_T *slang = NULL; + slang_T *slang = NULL; // Add the alternate score to su_ga. for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) { @@ -5017,11 +5152,12 @@ static void score_combine(suginfo_T *su) for (int i = 0; i < su->su_ga.ga_len; ++i) { stp = &SUG(su->su_ga, i); stp->st_altscore = stp_sal_score(stp, su, slang, badsound); - if (stp->st_altscore == SCORE_MAXMAX) + if (stp->st_altscore == SCORE_MAXMAX) { stp->st_score = (stp->st_score * 3 + SCORE_BIG) / 4; - else + } else { stp->st_score = (stp->st_score * 3 + stp->st_altscore) / 4; + } stp->st_salscore = false; } break; @@ -5030,7 +5166,7 @@ static void score_combine(suginfo_T *su) if (slang == NULL) { // Using "double" without sound folding. (void)cleanup_suggestions(&su->su_ga, su->su_maxscore, - su->su_maxcount); + su->su_maxcount); return; } @@ -5038,11 +5174,12 @@ static void score_combine(suginfo_T *su) for (int i = 0; i < su->su_sga.ga_len; ++i) { stp = &SUG(su->su_sga, i); stp->st_altscore = spell_edit_score(slang, - su->su_badword, stp->st_word); - if (stp->st_score == SCORE_MAXMAX) + su->su_badword, stp->st_word); + if (stp->st_score == SCORE_MAXMAX) { stp->st_score = (SCORE_BIG * 7 + stp->st_altscore) / 8; - else + } else { stp->st_score = (stp->st_score * 7 + stp->st_altscore) / 8; + } stp->st_salscore = true; } @@ -5066,13 +5203,16 @@ static void score_combine(suginfo_T *su) // Don't add a word if it's already there. p = SUG(*gap, i).st_word; int j; - for (j = 0; j < ga.ga_len; ++j) - if (STRCMP(stp[j].st_word, p) == 0) + for (j = 0; j < ga.ga_len; ++j) { + if (STRCMP(stp[j].st_word, p) == 0) { break; - if (j == ga.ga_len) + } + } + if (j == ga.ga_len) { stp[ga.ga_len++] = SUG(*gap, i); - else + } else { xfree(p); + } } } } @@ -5091,19 +5231,15 @@ static void score_combine(suginfo_T *su) su->su_ga = ga; } -// For the goodword in "stp" compute the soundalike score compared to the -// badword. -static int -stp_sal_score ( - suggest_T *stp, - suginfo_T *su, - slang_T *slang, - char_u *badsound // sound-folded badword -) +/// For the goodword in "stp" compute the soundalike score compared to the +/// badword. +/// +/// @param badsound sound-folded badword +static int stp_sal_score(suggest_T *stp, suginfo_T *su, slang_T *slang, char_u *badsound) { - char_u *p; - char_u *pbad; - char_u *pgood; + char_u *p; + char_u *pbad; + char_u *pgood; char_u badsound2[MAXWLEN]; char_u fword[MAXWLEN]; char_u goodsound[MAXWLEN]; @@ -5111,9 +5247,9 @@ stp_sal_score ( int lendiff; lendiff = su->su_badlen - stp->st_orglen; - if (lendiff >= 0) + if (lendiff >= 0) { pbad = badsound; - else { + } else { // soundfold the bad word with more characters following (void)spell_casefold(curwin, su->su_badptr, stp->st_orglen, fword, MAXWLEN); @@ -5122,9 +5258,11 @@ stp_sal_score ( // removing the space. Don't do it when the good word also contains a // space. if (ascii_iswhite(su->su_badptr[su->su_badlen]) - && *skiptowhite(stp->st_word) == NUL) - for (p = fword; *(p = skiptowhite(p)) != NUL; ) + && *skiptowhite(stp->st_word) == NUL) { + for (p = fword; *(p = skiptowhite(p)) != NUL; ) { STRMOVE(p, p + 1); + } + } spell_soundfold(slang, fword, true, badsound2); pbad = badsound2; @@ -5135,10 +5273,11 @@ stp_sal_score ( // what replaces the bad word. STRCPY(goodword, stp->st_word); STRLCPY(goodword + stp->st_wordlen, - su->su_badptr + su->su_badlen - lendiff, lendiff + 1); + su->su_badptr + su->su_badlen - lendiff, lendiff + 1); pgood = goodword; - } else + } else { pgood = stp->st_word; + } // Sound-fold the word and compute the score for the difference. spell_soundfold(slang, pgood, false, goodsound); @@ -5153,17 +5292,18 @@ static sftword_T dumsft; // Prepare for calling suggest_try_soundalike(). static void suggest_try_soundalike_prep(void) { - langp_T *lp; - slang_T *slang; + langp_T *lp; + slang_T *slang; // Do this for all languages that support sound folding and for which a // .sug file has been loaded. for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) { lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); slang = lp->lp_slang; - if (!GA_EMPTY(&slang->sl_sal) && slang->sl_sbyts != NULL) + if (!GA_EMPTY(&slang->sl_sal) && slang->sl_sbyts != NULL) { // prepare the hashtable used by add_sound_suggest() hash_init(&slang->sl_sounddone); + } } } @@ -5172,8 +5312,8 @@ static void suggest_try_soundalike_prep(void) static void suggest_try_soundalike(suginfo_T *su) { char_u salword[MAXWLEN]; - langp_T *lp; - slang_T *slang; + langp_T *lp; + slang_T *slang; // Do this for all languages that support sound folding and for which a // .sug file has been loaded. @@ -5201,10 +5341,10 @@ static void suggest_try_soundalike(suginfo_T *su) // Finish up after calling suggest_try_soundalike(). static void suggest_try_soundalike_finish(void) { - langp_T *lp; - slang_T *slang; + langp_T *lp; + slang_T *slang; int todo; - hashitem_T *hi; + hashitem_T *hi; // Do this for all languages that support sound folding and for which a // .sug file has been loaded. @@ -5214,11 +5354,12 @@ static void suggest_try_soundalike_finish(void) if (!GA_EMPTY(&slang->sl_sal) && slang->sl_sbyts != NULL) { // Free the info about handled words. todo = (int)slang->sl_sounddone.ht_used; - for (hi = slang->sl_sounddone.ht_array; todo > 0; ++hi) + for (hi = slang->sl_sounddone.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { xfree(HI2SFT(hi)); --todo; } + } // Clear the hashtable, it may also be used by another region. hash_clear(&slang->sl_sounddone); @@ -5227,32 +5368,28 @@ static void suggest_try_soundalike_finish(void) } } -// A match with a soundfolded word is found. Add the good word(s) that -// produce this soundfolded word. -static void -add_sound_suggest ( - suginfo_T *su, - char_u *goodword, - int score, // soundfold score - langp_T *lp -) +/// A match with a soundfolded word is found. Add the good word(s) that +/// produce this soundfolded word. +/// +/// @param score soundfold score +static void add_sound_suggest(suginfo_T *su, char_u *goodword, int score, langp_T *lp) { - slang_T *slang = lp->lp_slang; // language for sound folding + slang_T *slang = lp->lp_slang; // language for sound folding int sfwordnr; - char_u *nrline; + char_u *nrline; int orgnr; char_u theword[MAXWLEN]; int i; int wlen; - char_u *byts; - idx_T *idxs; + char_u *byts; + idx_T *idxs; int n; int wordcount; int wc; int goodscore; hash_T hash; - hashitem_T *hi; - sftword_T *sft; + hashitem_T *hi; + sftword_T *sft; int bc, gc; int limit; @@ -5271,8 +5408,9 @@ add_sound_suggest ( hash_add_item(&slang->sl_sounddone, hi, sft->sft_word, hash); } else { sft = HI2SFT(hi); - if (score >= sft->sft_score) + if (score >= sft->sft_score) { return; + } sft->sft_score = score; } @@ -5299,25 +5437,28 @@ add_sound_suggest ( wordcount = 0; for (wlen = 0; wlen < MAXWLEN - 3; ++wlen) { i = 1; - if (wordcount == orgnr && byts[n + 1] == NUL) + if (wordcount == orgnr && byts[n + 1] == NUL) { break; // found end of word - - if (byts[n + 1] == NUL) + } + if (byts[n + 1] == NUL) { ++wordcount; + } // skip over the NUL bytes - for (; byts[n + i] == NUL; ++i) + for (; byts[n + i] == NUL; ++i) { if (i > byts[n]) { // safety check STRCPY(theword + wlen, "BAD"); wlen += 3; goto badword; } + } // One of the siblings must have the word. for (; i < byts[n]; ++i) { wc = idxs[idxs[n + i]]; // nr of words under this byte - if (wordcount + wc > orgnr) + if (wordcount + wc > orgnr) { break; + } wordcount += wc; } @@ -5330,12 +5471,13 @@ badword: // Go over the possible flags and regions. for (; i <= byts[n] && byts[n + i] == NUL; ++i) { char_u cword[MAXWLEN]; - char_u *p; + char_u *p; int flags = (int)idxs[n + i]; // Skip words with the NOSUGGEST flag - if (flags & WF_NOSUGGEST) + if (flags & WF_NOSUGGEST) { continue; + } if (flags & WF_KEEPCAP) { // Must find the word in the keep-case tree. @@ -5347,23 +5489,26 @@ badword: // Need to fix case according to "flags". make_case_word(theword, cword, flags); p = cword; - } else + } else { p = theword; + } } // Add the suggestion. if (sps_flags & SPS_DOUBLE) { // Add the suggestion if the score isn't too bad. - if (score <= su->su_maxscore) + if (score <= su->su_maxscore) { add_suggestion(su, &su->su_sga, p, su->su_badlen, - score, 0, false, slang, false); + score, 0, false, slang, false); + } } else { // Add a penalty for words in another region. if ((flags & WF_REGION) - && (((unsigned)flags >> 16) & lp->lp_region) == 0) + && (((unsigned)flags >> 16) & lp->lp_region) == 0) { goodscore = SCORE_REGION; - else + } else { goodscore = 0; + } // Add a small penalty for changing the first letter from // lower to upper case. Helps for "tath" -> "Kath", which is @@ -5373,8 +5518,9 @@ badword: if (SPELL_ISUPPER(gc)) { bc = PTR2CHAR(su->su_badword); if (!SPELL_ISUPPER(bc) - && SPELL_TOFOLD(bc) != SPELL_TOFOLD(gc)) + && SPELL_TOFOLD(bc) != SPELL_TOFOLD(gc)) { goodscore += SCORE_ICASE / 2; + } } // Compute the score for the good word. This only does letter @@ -5385,11 +5531,12 @@ badword: // If the limit is very high then the iterative method is // inefficient, using an array is quicker. limit = MAXSCORE(su->su_sfmaxscore - goodscore, score); - if (limit > SCORE_LIMITMAX) + if (limit > SCORE_LIMITMAX) { goodscore += spell_edit_score(slang, su->su_badword, p); - else + } else { goodscore += spell_edit_score_limit(slang, su->su_badword, - p, limit); + p, limit); + } // When going over the limit don't bother to do the rest. if (goodscore < SCORE_MAXMAX) { @@ -5398,9 +5545,10 @@ badword: // Add the suggestion if the score isn't too bad. goodscore = RESCORE(goodscore, score); - if (goodscore <= su->su_sfmaxscore) + if (goodscore <= su->su_sfmaxscore) { add_suggestion(su, &su->su_ga, p, su->su_badlen, - goodscore, score, true, slang, true); + goodscore, score, true, slang, true); + } } } } @@ -5414,9 +5562,9 @@ static int soundfold_find(slang_T *slang, char_u *word) int len; int wlen = 0; int c; - char_u *ptr = word; - char_u *byts; - idx_T *idxs; + char_u *ptr = word; + char_u *byts; + idx_T *idxs; int wordnr = 0; byts = slang->sl_sbyts; @@ -5430,35 +5578,41 @@ static int soundfold_find(slang_T *slang, char_u *word) // If the word ends we found the word. If not skip the NUL bytes. c = ptr[wlen]; if (byts[arridx] == NUL) { - if (c == NUL) + if (c == NUL) { break; + } // Skip over the zeros, there can be several. while (len > 0 && byts[arridx] == NUL) { ++arridx; --len; } - if (len == 0) + if (len == 0) { return -1; // no children, word should have ended here + } ++wordnr; } // If the word ends we didn't find it. - if (c == NUL) + if (c == NUL) { return -1; + } // Perform a binary search in the list of accepted bytes. - if (c == TAB) // <Tab> is handled like <Space> + if (c == TAB) { // <Tab> is handled like <Space> c = ' '; + } while (byts[arridx] < c) { // The word count is in the first idxs[] entry of the child. wordnr += idxs[idxs[arridx]]; ++arridx; - if (--len == 0) // end of the bytes, didn't find it + if (--len == 0) { // end of the bytes, didn't find it return -1; + } } - if (byts[arridx] != c) // didn't find the byte + if (byts[arridx] != c) { // didn't find the byte return -1; + } // Continue at the child (if there is one). arridx = idxs[arridx]; @@ -5466,9 +5620,11 @@ static int soundfold_find(slang_T *slang, char_u *word) // One space in the good word may stand for several spaces in the // checked word. - if (c == ' ') - while (ptr[wlen] == ' ' || ptr[wlen] == TAB) + if (c == ' ') { + while (ptr[wlen] == ' ' || ptr[wlen] == TAB) { ++wlen; + } + } } return wordnr; @@ -5477,15 +5633,16 @@ static int soundfold_find(slang_T *slang, char_u *word) // Copy "fword" to "cword", fixing case according to "flags". static void make_case_word(char_u *fword, char_u *cword, int flags) { - if (flags & WF_ALLCAP) + if (flags & WF_ALLCAP) { // Make it all upper-case allcap_copy(fword, cword); - else if (flags & WF_ONECAP) + } else if (flags & WF_ONECAP) { // Make the first letter upper-case onecap_copy(fword, cword, true); - else + } else { // Use goodword as-is. STRCPY(cword, fword); + } } // Returns true if "c1" and "c2" are similar characters according to the MAP @@ -5494,7 +5651,7 @@ static bool similar_chars(slang_T *slang, int c1, int c2) { int m1, m2; char_u buf[MB_MAXBYTES + 1]; - hashitem_T *hi; + hashitem_T *hi; if (c1 >= 256) { buf[utf_char2bytes(c1, buf)] = 0; @@ -5526,25 +5683,20 @@ static bool similar_chars(slang_T *slang, int c1, int c2) return m1 == m2; } -// Adds a suggestion to the list of suggestions. -// For a suggestion that is already in the list the lowest score is remembered. -static void -add_suggestion ( - suginfo_T *su, - garray_T *gap, // either su_ga or su_sga - const char_u *goodword, - int badlenarg, // len of bad word replaced with "goodword" - int score, - int altscore, - bool had_bonus, // value for st_had_bonus - slang_T *slang, // language for sound folding - bool maxsf // su_maxscore applies to soundfold score, - // su_sfmaxscore to the total score. -) +/// Adds a suggestion to the list of suggestions. +/// For a suggestion that is already in the list the lowest score is remembered. +/// +/// @param gap either su_ga or su_sga +/// @param badlenarg len of bad word replaced with "goodword" +/// @param had_bonus value for st_had_bonus +/// @param slang language for sound folding +/// @param maxsf su_maxscore applies to soundfold score, su_sfmaxscore to the total score. +static void add_suggestion(suginfo_T *su, garray_T *gap, const char_u *goodword, int badlenarg, + int score, int altscore, bool had_bonus, slang_T *slang, bool maxsf) { int goodlen; // len of goodword changed int badlen; // len of bad word changed - suggest_T *stp; + suggest_T *stp; suggest_T new_sug; // Minimize "badlen" for consistency. Avoids that changing "the the" to @@ -5554,8 +5706,9 @@ add_suggestion ( for (;; ) { goodlen = (int)(pgood - goodword); badlen = (int)(pbad - su->su_badptr); - if (goodlen <= 0 || badlen <= 0) + if (goodlen <= 0 || badlen <= 0) { break; + } MB_PTR_BACK(goodword, pgood); MB_PTR_BACK(su->su_badptr, pbad); if (utf_ptr2char(pgood) != utf_ptr2char(pbad)) { @@ -5563,10 +5716,11 @@ add_suggestion ( } } - if (badlen == 0 && goodlen == 0) + if (badlen == 0 && goodlen == 0) { // goodword doesn't change anything; may happen for "the the" changing // the first "the" to itself. return; + } int i; if (GA_EMPTY(gap)) { @@ -5581,8 +5735,9 @@ add_suggestion ( && stp->st_orglen == badlen && STRNCMP(stp->st_word, goodword, goodlen) == 0) { // Found it. Remember the word with the lowest score. - if (stp->st_slang == NULL) + if (stp->st_slang == NULL) { stp->st_slang = slang; + } new_sug.st_score = score; new_sug.st_altscore = altscore; @@ -5595,9 +5750,9 @@ add_suggestion ( // suggest_try_change() doesn't compute the soundalike // word to keep it fast, while some special methods set // the soundalike score to zero. - if (had_bonus) + if (had_bonus) { rescore_one(su, stp); - else { + } else { new_sug.st_word = stp->st_word; new_sug.st_wordlen = stp->st_wordlen; new_sug.st_slang = stp->st_slang; @@ -5630,25 +5785,24 @@ add_suggestion ( // If we have too many suggestions now, sort the list and keep // the best suggestions. if (gap->ga_len > SUG_MAX_COUNT(su)) { - if (maxsf) + if (maxsf) { su->su_sfmaxscore = cleanup_suggestions(gap, - su->su_sfmaxscore, SUG_CLEAN_COUNT(su)); - else + su->su_sfmaxscore, SUG_CLEAN_COUNT(su)); + } else { su->su_maxscore = cleanup_suggestions(gap, - su->su_maxscore, SUG_CLEAN_COUNT(su)); + su->su_maxscore, SUG_CLEAN_COUNT(su)); + } } } } -// Suggestions may in fact be flagged as errors. Esp. for banned words and -// for split words, such as "the the". Remove these from the list here. -static void -check_suggestions ( - suginfo_T *su, - garray_T *gap // either su_ga or su_sga -) +/// Suggestions may in fact be flagged as errors. Esp. for banned words and +/// for split words, such as "the the". Remove these from the list here. +/// +/// @param gap either su_ga or su_sga +static void check_suggestions(suginfo_T *su, garray_T *gap) { - suggest_T *stp; + suggest_T *stp; char_u longword[MAXWLEN + 1]; int len; hlf_T attr; @@ -5662,16 +5816,17 @@ check_suggestions ( STRLCPY(longword, stp[i].st_word, MAXWLEN + 1); len = stp[i].st_wordlen; STRLCPY(longword + len, su->su_badptr + stp[i].st_orglen, - MAXWLEN - len + 1); + MAXWLEN - len + 1); attr = HLF_COUNT; (void)spell_check(curwin, longword, &attr, NULL, false); if (attr != HLF_COUNT) { // Remove this entry. xfree(stp[i].st_word); --gap->ga_len; - if (i < gap->ga_len) + if (i < gap->ga_len) { memmove(stp + i, stp + i + 1, - sizeof(suggest_T) * (gap->ga_len - i)); + sizeof(suggest_T) * (gap->ga_len - i)); + } } } } @@ -5680,9 +5835,9 @@ check_suggestions ( // Add a word to be banned. static void add_banned(suginfo_T *su, char_u *word) { - char_u *s; + char_u *s; hash_T hash; - hashitem_T *hi; + hashitem_T *hi; hash = hash_hash(word); const size_t word_len = STRLEN(word); @@ -5707,23 +5862,24 @@ static void rescore_suggestions(suginfo_T *su) // Recompute the score for one suggestion if sound-folding is possible. static void rescore_one(suginfo_T *su, suggest_T *stp) { - slang_T *slang = stp->st_slang; + slang_T *slang = stp->st_slang; char_u sal_badword[MAXWLEN]; - char_u *p; + char_u *p; // Only rescore suggestions that have no sal score yet and do have a // language. if (slang != NULL && !GA_EMPTY(&slang->sl_sal) && !stp->st_had_bonus) { - if (slang == su->su_sallang) + if (slang == su->su_sallang) { p = su->su_sal_badword; - else { + } else { spell_soundfold(slang, su->su_fbadword, true, sal_badword); p = sal_badword; } stp->st_altscore = stp_sal_score(stp, su, slang, p); - if (stp->st_altscore == SCORE_MAXMAX) + if (stp->st_altscore == SCORE_MAXMAX) { stp->st_altscore = SCORE_BIG; + } stp->st_score = RESCORE(stp->st_score, stp->st_altscore); stp->st_had_bonus = true; } @@ -5734,28 +5890,27 @@ static void rescore_one(suginfo_T *su, suggest_T *stp) // First on "st_score", then "st_altscore" then alphabetically. static int sug_compare(const void *s1, const void *s2) { - suggest_T *p1 = (suggest_T *)s1; - suggest_T *p2 = (suggest_T *)s2; + suggest_T *p1 = (suggest_T *)s1; + suggest_T *p2 = (suggest_T *)s2; int n = p1->st_score - p2->st_score; if (n == 0) { n = p1->st_altscore - p2->st_altscore; - if (n == 0) + if (n == 0) { n = STRICMP(p1->st_word, p2->st_word); + } } return n; } -// Cleanup the suggestions: -// - Sort on score. -// - Remove words that won't be displayed. -// Returns the maximum score in the list or "maxscore" unmodified. -static int -cleanup_suggestions ( - garray_T *gap, - int maxscore, - int keep // nr of suggestions to keep -) +/// Cleanup the suggestions: +/// - Sort on score. +/// - Remove words that won't be displayed. +/// +/// @param keep nr of suggestions to keep +/// +/// @return the maximum score in the list or "maxscore" unmodified. +static int cleanup_suggestions(garray_T *gap, int maxscore, int keep) FUNC_ATTR_NONNULL_ALL { if (gap->ga_len > 0) { @@ -5823,12 +5978,12 @@ char *eval_soundfold(const char *const word) void spell_soundfold(slang_T *slang, char_u *inword, bool folded, char_u *res) { char_u fword[MAXWLEN]; - char_u *word; + char_u *word; - if (slang->sl_sofo) + if (slang->sl_sofo) { // SOFOFROM and SOFOTO used spell_soundfold_sofo(slang, inword, res); - else { + } else { // SAL items used. Requires the word to be case-folded. if (folded) { word = inword; @@ -5892,12 +6047,12 @@ static void spell_soundfold_sofo(slang_T *slang, char_u *inword, char_u *res) // Multi-byte version of spell_soundfold(). static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) { - salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data; + salitem_T *smp = (salitem_T *)slang->sl_sal.ga_data; int word[MAXWLEN] = { 0 }; int wres[MAXWLEN] = { 0 }; int l; - int *ws; - int *pf; + int *ws; + int *pf; int i, j, z; int reslen; int n, k = 0; @@ -5954,27 +6109,34 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) && ws[0] != NUL; ++n) { // Quickly skip entries that don't match the word. Most // entries are less then three chars, optimize for that. - if (c != ws[0]) + if (c != ws[0]) { continue; + } k = smp[n].sm_leadlen; if (k > 1) { - if (word[i + 1] != ws[1]) + if (word[i + 1] != ws[1]) { continue; + } if (k > 2) { - for (j = 2; j < k; ++j) - if (word[i + j] != ws[j]) + for (j = 2; j < k; ++j) { + if (word[i + j] != ws[j]) { break; - if (j < k) + } + } + if (j < k) { continue; + } } } if ((pf = smp[n].sm_oneof_w) != NULL) { // Check for match with one of the chars in "sm_oneof". - while (*pf != NUL && *pf != word[i + k]) + while (*pf != NUL && *pf != word[i + k]) { ++pf; - if (*pf == NUL) + } + if (*pf == NUL) { continue; + } ++k; } char_u *s = smp[n].sm_rules; @@ -5986,15 +6148,17 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) k--; s++; } - if (*s == '<') + if (*s == '<') { s++; + } if (ascii_isdigit(*s)) { // determine priority pri = *s - '0'; s++; } - if (*s == '^' && *(s + 1) == '^') + if (*s == '^' && *(s + 1) == '^') { s++; + } if (*s == NUL || (*s == '^' @@ -6017,19 +6181,24 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) for (; ((ws = smp[n0].sm_lead_w)[0] & 0xff) == (c0 & 0xff); ++n0) { // Quickly skip entries that don't match the word. - if (c0 != ws[0]) + if (c0 != ws[0]) { continue; + } k0 = smp[n0].sm_leadlen; if (k0 > 1) { - if (word[i + k] != ws[1]) + if (word[i + k] != ws[1]) { continue; + } if (k0 > 2) { pf = word + i + k + 1; - for (j = 2; j < k0; ++j) - if (*pf++ != ws[j]) + for (j = 2; j < k0; ++j) { + if (*pf++ != ws[j]) { break; - if (j < k0) + } + } + if (j < k0) { continue; + } } } k0 += k - 1; @@ -6037,10 +6206,12 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) if ((pf = smp[n0].sm_oneof_w) != NULL) { // Check for match with one of the chars in // "sm_oneof". - while (*pf != NUL && *pf != word[i + k0]) + while (*pf != NUL && *pf != word[i + k0]) { ++pf; - if (*pf == NUL) + } + if (*pf == NUL) { continue; + } ++k0; } @@ -6051,8 +6222,9 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) // "if (k0 == k)" s++; } - if (*s == '<') + if (*s == '<') { s++; + } if (ascii_isdigit(*s)) { p0 = *s - '0'; s++; @@ -6062,22 +6234,25 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) // *s == '^' cuts || (*s == '$' && !spell_iswordp_w(word + i + k0, - curwin))) { - if (k0 == k) + curwin))) { + if (k0 == k) { // this is just a piece of the string continue; + } - if (p0 < pri) + if (p0 < pri) { // priority too low continue; + } // rule fits; stop search break; } } if (p0 >= pri && (smp[n0].sm_lead_w[0] & 0xff) - == (c0 & 0xff)) + == (c0 & 0xff)) { continue; + } } // replace string @@ -6088,20 +6263,23 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) // rule with '<' is used if (reslen > 0 && ws != NULL && *ws != NUL && (wres[reslen - 1] == c - || wres[reslen - 1] == *ws)) + || wres[reslen - 1] == *ws)) { reslen--; + } z0 = 1; z = 1; k0 = 0; - if (ws != NULL) + if (ws != NULL) { while (*ws != NUL && word[i + k0] != NUL) { word[i + k0] = *ws; k0++; ws++; } - if (k > k0) + } + if (k > k0) { memmove(word + i + k0, word + i + k, - sizeof(int) * (wordlen - (i + k) + 1)); + sizeof(int) * (wordlen - (i + k) + 1)); + } // new "actual letter" c = word[i]; @@ -6109,23 +6287,27 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) // no '<' rule used i += k - 1; z = 0; - if (ws != NULL) + if (ws != NULL) { while (*ws != NUL && ws[1] != NUL && reslen < MAXWLEN) { - if (reslen == 0 || wres[reslen - 1] != *ws) + if (reslen == 0 || wres[reslen - 1] != *ws) { wres[reslen++] = *ws; + } ws++; } + } // new "actual letter" - if (ws == NULL) + if (ws == NULL) { c = NUL; - else + } else { c = *ws; + } if (strstr((char *)s, "^^") != NULL) { - if (c != NUL) + if (c != NUL) { wres[reslen++] = c; + } memmove(word, word + i + 1, - sizeof(int) * (wordlen - (i + 1) + 1)); + sizeof(int) * (wordlen - (i + 1) + 1)); i = 0; z0 = 1; } @@ -6133,7 +6315,7 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) break; } } - } else if (ascii_iswhite(c)) { + } else if (ascii_iswhite(c)) { c = ' '; k = 1; } @@ -6141,9 +6323,10 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) if (z0 == 0) { if (k && !p0 && reslen < MAXWLEN && c != NUL && (!slang->sl_collapse || reslen == 0 - || wres[reslen - 1] != c)) + || wres[reslen - 1] != c)) { // condense only double letters wres[reslen++] = c; + } i++; z = 0; @@ -6162,35 +6345,36 @@ static void spell_soundfold_wsal(slang_T *slang, char_u *inword, char_u *res) res[l] = NUL; } -// Compute a score for two sound-a-like words. -// This permits up to two inserts/deletes/swaps/etc. to keep things fast. -// Instead of a generic loop we write out the code. That keeps it fast by -// avoiding checks that will not be possible. -static int -soundalike_score ( - char_u *goodstart, // sound-folded good word - char_u *badstart // sound-folded bad word -) +/// Compute a score for two sound-a-like words. +/// This permits up to two inserts/deletes/swaps/etc. to keep things fast. +/// Instead of a generic loop we write out the code. That keeps it fast by +/// avoiding checks that will not be possible. +/// +/// @param goodstart sound-folded good word +/// @param badstart sound-folded bad word +static int soundalike_score(char_u *goodstart, char_u *badstart) { - char_u *goodsound = goodstart; - char_u *badsound = badstart; + char_u *goodsound = goodstart; + char_u *badsound = badstart; int goodlen; int badlen; int n; - char_u *pl, *ps; - char_u *pl2, *ps2; + char_u *pl, *ps; + char_u *pl2, *ps2; int score = 0; // Adding/inserting "*" at the start (word starts with vowel) shouldn't be // counted so much, vowels in the middle of the word aren't counted at all. if ((*badsound == '*' || *goodsound == '*') && *badsound != *goodsound) { if ((badsound[0] == NUL && goodsound[1] == NUL) - || (goodsound[0] == NUL && badsound[1] == NUL)) + || (goodsound[0] == NUL && badsound[1] == NUL)) { // changing word with vowel to word without a sound return SCORE_DEL; - if (badsound[0] == NUL || goodsound[0] == NUL) + } + if (badsound[0] == NUL || goodsound[0] == NUL) { // more than two changes return SCORE_MAXMAX; + } if (badsound[1] == goodsound[1] || (badsound[1] != NUL @@ -6199,10 +6383,11 @@ soundalike_score ( // handle like a substitute } else { score = 2 * SCORE_DEL / 3; - if (*badsound == '*') + if (*badsound == '*') { ++badsound; - else + } else { ++goodsound; + } } } @@ -6212,8 +6397,9 @@ soundalike_score ( // Return quickly if the lengths are too different to be fixed by two // changes. n = goodlen - badlen; - if (n < -2 || n > 2) + if (n < -2 || n > 2) { return SCORE_MAXMAX; + } if (n > 0) { pl = goodsound; // goodsound is longest @@ -6239,8 +6425,9 @@ soundalike_score ( ++ps; } // strings must be equal after second delete - if (STRCMP(pl + 1, ps) == 0) + if (STRCMP(pl + 1, ps) == 0) { return score + SCORE_DEL * 2; + } // Failed to compare. break; @@ -6253,20 +6440,23 @@ soundalike_score ( pl2 = pl + 1; ps2 = ps; while (*pl2 == *ps2) { - if (*pl2 == NUL) // reached the end + if (*pl2 == NUL) { // reached the end return score + SCORE_DEL; + } ++pl2; ++ps2; } // 2: delete then swap, then rest must be equal if (pl2[0] == ps2[1] && pl2[1] == ps2[0] - && STRCMP(pl2 + 2, ps2 + 2) == 0) + && STRCMP(pl2 + 2, ps2 + 2) == 0) { return score + SCORE_DEL + SCORE_SWAP; + } // 3: delete then substitute, then the rest must be equal - if (STRCMP(pl2 + 1, ps2 + 1) == 0) + if (STRCMP(pl2 + 1, ps2 + 1) == 0) { return score + SCORE_DEL + SCORE_SUBST; + } // 4: first swap then delete if (pl[0] == ps[1] && pl[1] == ps[0]) { @@ -6277,8 +6467,9 @@ soundalike_score ( ++ps2; } // delete a char and then strings must be equal - if (STRCMP(pl2 + 1, ps2) == 0) + if (STRCMP(pl2 + 1, ps2) == 0) { return score + SCORE_SWAP + SCORE_DEL; + } } // 5: first substitute then delete @@ -6289,8 +6480,9 @@ soundalike_score ( ++ps2; } // delete a char and then strings must be equal - if (STRCMP(pl2 + 1, ps2) == 0) + if (STRCMP(pl2 + 1, ps2) == 0) { return score + SCORE_SUBST + SCORE_DEL; + } // Failed to compare. break; @@ -6299,47 +6491,54 @@ soundalike_score ( // Lengths are equal, thus changes must result in same length: An // insert is only possible in combination with a delete. // 1: check if for identical strings - if (*pl == NUL) + if (*pl == NUL) { return score; + } // 2: swap if (pl[0] == ps[1] && pl[1] == ps[0]) { pl2 = pl + 2; // swap, skip two chars ps2 = ps + 2; while (*pl2 == *ps2) { - if (*pl2 == NUL) // reached the end + if (*pl2 == NUL) { // reached the end return score + SCORE_SWAP; + } ++pl2; ++ps2; } // 3: swap and swap again if (pl2[0] == ps2[1] && pl2[1] == ps2[0] - && STRCMP(pl2 + 2, ps2 + 2) == 0) + && STRCMP(pl2 + 2, ps2 + 2) == 0) { return score + SCORE_SWAP + SCORE_SWAP; + } // 4: swap and substitute - if (STRCMP(pl2 + 1, ps2 + 1) == 0) + if (STRCMP(pl2 + 1, ps2 + 1) == 0) { return score + SCORE_SWAP + SCORE_SUBST; + } } // 5: substitute pl2 = pl + 1; ps2 = ps + 1; while (*pl2 == *ps2) { - if (*pl2 == NUL) // reached the end + if (*pl2 == NUL) { // reached the end return score + SCORE_SUBST; + } ++pl2; ++ps2; } // 6: substitute and swap if (pl2[0] == ps2[1] && pl2[1] == ps2[0] - && STRCMP(pl2 + 2, ps2 + 2) == 0) + && STRCMP(pl2 + 2, ps2 + 2) == 0) { return score + SCORE_SUBST + SCORE_SWAP; + } // 7: substitute and substitute - if (STRCMP(pl2 + 1, ps2 + 1) == 0) + if (STRCMP(pl2 + 1, ps2 + 1) == 0) { return score + SCORE_SUBST + SCORE_SUBST; + } // 8: insert then delete pl2 = pl; @@ -6348,8 +6547,9 @@ soundalike_score ( ++pl2; ++ps2; } - if (STRCMP(pl2 + 1, ps2) == 0) + if (STRCMP(pl2 + 1, ps2) == 0) { return score + SCORE_INS + SCORE_DEL; + } // 9: delete then insert pl2 = pl + 1; @@ -6358,8 +6558,9 @@ soundalike_score ( ++pl2; ++ps2; } - if (STRCMP(pl2, ps2 + 1) == 0) + if (STRCMP(pl2, ps2 + 1) == 0) { return score + SCORE_INS + SCORE_DEL; + } // Failed to compare. break; @@ -6408,8 +6609,9 @@ static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) cnt = xmalloc(sizeof(int) * (badlen + 1) * (goodlen + 1)); CNT(0, 0) = 0; - for (j = 1; j <= goodlen; ++j) + for (j = 1; j <= goodlen; ++j) { CNT(0, j) = CNT(0, j - 1) + SCORE_INS; + } for (i = 1; i <= badlen; ++i) { CNT(i, 0) = CNT(i - 1, 0) + SCORE_DEL; @@ -6420,16 +6622,17 @@ static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) CNT(i, j) = CNT(i - 1, j - 1); } else { // Use a better score when there is only a case difference. - if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) + if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) { CNT(i, j) = SCORE_ICASE + CNT(i - 1, j - 1); - else { + } else { // For a similar character use SCORE_SIMILAR. if (slang != NULL && slang->sl_has_map - && similar_chars(slang, gc, bc)) + && similar_chars(slang, gc, bc)) { CNT(i, j) = SCORE_SIMILAR + CNT(i - 1, j - 1); - else + } else { CNT(i, j) = SCORE_SUBST + CNT(i - 1, j - 1); + } } if (i > 1 && j > 1) { @@ -6437,16 +6640,19 @@ static int spell_edit_score(slang_T *slang, char_u *badword, char_u *goodword) pgc = wgoodword[j - 2]; if (bc == pgc && pbc == gc) { t = SCORE_SWAP + CNT(i - 2, j - 2); - if (t < CNT(i, j)) + if (t < CNT(i, j)) { CNT(i, j) = t; + } } } t = SCORE_DEL + CNT(i - 1, j); - if (t < CNT(i, j)) + if (t < CNT(i, j)) { CNT(i, j) = t; + } t = SCORE_INS + CNT(i, j - 1); - if (t < CNT(i, j)) + if (t < CNT(i, j)) { CNT(i, j) = t; + } } } } @@ -6515,11 +6721,13 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo bc = wbadword[bi]; gc = wgoodword[gi]; - if (bc != gc) // stop at a char that's different + if (bc != gc) { // stop at a char that's different break; + } if (bc == NUL) { // both words end - if (score < minscore) + if (score < minscore) { minscore = score; + } goto pop; // do next alternative } ++bi; @@ -6528,14 +6736,16 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo if (gc == NUL) { // goodword ends, delete badword chars do { - if ((score += SCORE_DEL) >= minscore) + if ((score += SCORE_DEL) >= minscore) { goto pop; // do next alternative + } } while (wbadword[++bi] != NUL); minscore = score; - } else if (bc == NUL) { // badword ends, insert badword chars + } else if (bc == NUL) { // badword ends, insert badword chars do { - if ((score += SCORE_INS) >= minscore) + if ((score += SCORE_INS) >= minscore) { goto pop; // do next alternative + } } while (wgoodword[++gi] != NUL); minscore = score; } else { // both words continue @@ -6586,16 +6796,17 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo // Substitute one character for another which is the same // thing as deleting a character from both goodword and badword. // Use a better score when there is only a case difference. - if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) + if (SPELL_TOFOLD(bc) == SPELL_TOFOLD(gc)) { score += SCORE_ICASE; - else { + } else { // For a similar character use SCORE_SIMILAR. if (slang != NULL && slang->sl_has_map - && similar_chars(slang, gc, bc)) + && similar_chars(slang, gc, bc)) { score += SCORE_SIMILAR; - else + } else { score += SCORE_SUBST; + } } if (score < minscore) { @@ -6607,8 +6818,9 @@ static int spell_edit_score_limit_w(slang_T *slang, char_u *badword, char_u *goo } pop: // Get here to try the next alternative, pop it from the stack. - if (stackidx == 0) // stack is empty, finished + if (stackidx == 0) { // stack is empty, finished break; + } // pop an item from the stack --stackidx; @@ -6620,8 +6832,9 @@ pop: // When the score goes over "limit" it may actually be much higher. // Return a very large number to avoid going below the limit when giving a // bonus. - if (minscore > limit) + if (minscore > limit) { return SCORE_MAXMAX; + } return minscore; } @@ -6656,7 +6869,7 @@ void ex_spellinfo(exarg_T *eap) // ":spelldump" void ex_spelldump(exarg_T *eap) { - char_u *spl; + char_u *spl; long dummy; if (no_spell_checking(curwin)) { @@ -6685,50 +6898,49 @@ void ex_spelldump(exarg_T *eap) redraw_later(curwin, NOT_VALID); } -// Go through all possible words and: -// 1. When "pat" is NULL: dump a list of all words in the current buffer. -// "ic" and "dir" are not used. -// 2. When "pat" is not NULL: add matching words to insert mode completion. -void -spell_dump_compl ( - char_u *pat, // leading part of the word - int ic, // ignore case - Direction *dir, // direction for adding matches - int dumpflags_arg // DUMPFLAG_* -) +/// Go through all possible words and: +/// 1. When "pat" is NULL: dump a list of all words in the current buffer. +/// "ic" and "dir" are not used. +/// 2. When "pat" is not NULL: add matching words to insert mode completion. +/// +/// @param pat leading part of the word +/// @param ic ignore case +/// @param dir direction for adding matches +/// @param dumpflags_arg DUMPFLAG_* +void spell_dump_compl(char_u *pat, int ic, Direction *dir, int dumpflags_arg) { - langp_T *lp; - slang_T *slang; + langp_T *lp; + slang_T *slang; idx_T arridx[MAXWLEN]; int curi[MAXWLEN]; char_u word[MAXWLEN]; int c; - char_u *byts; - idx_T *idxs; + char_u *byts; + idx_T *idxs; linenr_T lnum = 0; int round; int depth; int n; int flags; - char_u *region_names = NULL; // region names being used + char_u *region_names = NULL; // region names being used bool do_region = true; // dump region names and numbers - char_u *p; + char_u *p; int dumpflags = dumpflags_arg; int patlen; // When ignoring case or when the pattern starts with capital pass this on // to dump_word(). if (pat != NULL) { - if (ic) + if (ic) { dumpflags |= DUMPFLAG_ICASE; - else { + } else { n = captype(pat, NULL); - if (n == WF_ONECAP) + if (n == WF_ONECAP) { dumpflags |= DUMPFLAG_ONECAP; - else if (n == WF_ALLCAP - && (int)STRLEN(pat) > mb_ptr2len(pat) - ) + } else if (n == WF_ALLCAP + && (int)STRLEN(pat) > mb_ptr2len(pat)) { dumpflags |= DUMPFLAG_ALLCAP; + } } } @@ -6738,9 +6950,9 @@ spell_dump_compl ( lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); p = lp->lp_slang->sl_regions; if (p[0] != 0) { - if (region_names == NULL) // first language with regions + if (region_names == NULL) { // first language with regions region_names = p; - else if (STRCMP(region_names, p) != 0) { + } else if (STRCMP(region_names, p) != 0) { do_region = false; // region names are different break; } @@ -6752,15 +6964,17 @@ spell_dump_compl ( vim_snprintf((char *)IObuff, IOSIZE, "/regions=%s", region_names); ml_append(lnum++, IObuff, (colnr_T)0, false); } - } else + } else { do_region = false; + } // Loop over all files loaded for the entries in 'spelllang'. for (int lpi = 0; lpi < curwin->w_s->b_langp.ga_len; ++lpi) { lp = LANGP_ENTRY(curwin->w_s->b_langp, lpi); slang = lp->lp_slang; - if (slang->sl_fbyts == NULL) // reloading failed + if (slang->sl_fbyts == NULL) { // reloading failed continue; + } if (pat == NULL) { vim_snprintf((char *)IObuff, IOSIZE, "# file: %s", slang->sl_fname); @@ -6769,10 +6983,11 @@ spell_dump_compl ( // When matching with a pattern and there are no prefixes only use // parts of the tree that match "pat". - if (pat != NULL && slang->sl_pbyts == NULL) + if (pat != NULL && slang->sl_pbyts == NULL) { patlen = (int)STRLEN(pat); - else + } else { patlen = -1; + } // round 1: case-folded tree // round 2: keep-case tree @@ -6786,9 +7001,9 @@ spell_dump_compl ( byts = slang->sl_kbyts; idxs = slang->sl_kidxs; } - if (byts == NULL) + if (byts == NULL) { continue; // array is empty - + } depth = 0; arridx[0] = 0; curi[0] = 1; @@ -6817,23 +7032,26 @@ spell_dump_compl ( || (((unsigned)flags >> 16) & lp->lp_region) != 0)) { word[depth] = NUL; - if (!do_region) + if (!do_region) { flags &= ~WF_REGION; + } // Dump the basic word if there is no prefix or // when it's the first one. c = (unsigned)flags >> 24; if (c == 0 || curi[depth] == 2) { dump_word(slang, word, pat, dir, - dumpflags, flags, lnum); - if (pat == NULL) + dumpflags, flags, lnum); + if (pat == NULL) { ++lnum; + } } // Apply the prefix, if there is one. - if (c != 0) + if (c != 0) { lnum = dump_prefixes(slang, word, pat, dir, - dumpflags, flags, lnum); + dumpflags, flags, lnum); + } } } else { // Normal char, go one level deeper. @@ -6849,8 +7067,9 @@ spell_dump_compl ( // ignore case... assert(depth >= 0); if (depth <= patlen - && mb_strnicmp(word, pat, (size_t)depth) != 0) + && mb_strnicmp(word, pat, (size_t)depth) != 0) { --depth; + } } } } @@ -6860,22 +7079,23 @@ spell_dump_compl ( // Dumps one word: apply case modifications and append a line to the buffer. // When "lnum" is zero add insert mode completion. -static void dump_word(slang_T *slang, char_u *word, char_u *pat, - Direction *dir, int dumpflags, int wordflags, - linenr_T lnum) +static void dump_word(slang_T *slang, char_u *word, char_u *pat, Direction *dir, int dumpflags, + int wordflags, linenr_T lnum) { bool keepcap = false; - char_u *p; - char_u *tw; + char_u *p; + char_u *tw; char_u cword[MAXWLEN]; char_u badword[MAXWLEN + 10]; int i; int flags = wordflags; - if (dumpflags & DUMPFLAG_ONECAP) + if (dumpflags & DUMPFLAG_ONECAP) { flags |= WF_ONECAP; - if (dumpflags & DUMPFLAG_ALLCAP) + } + if (dumpflags & DUMPFLAG_ALLCAP) { flags |= WF_ALLCAP; + } if ((dumpflags & DUMPFLAG_KEEPCASE) == 0 && (flags & WF_CAPMASK) != 0) { // Need to fix case according to "flags". @@ -6885,8 +7105,9 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, p = word; if ((dumpflags & DUMPFLAG_KEEPCASE) && ((captype(word, NULL) & WF_KEEPCAP) == 0 - || (flags & WF_FIXCAP) != 0)) + || (flags & WF_FIXCAP) != 0)) { keepcap = true; + } } tw = p; @@ -6917,13 +7138,13 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, } if (dumpflags & DUMPFLAG_COUNT) { - hashitem_T *hi; + hashitem_T *hi; // Include the word count for ":spelldump!". hi = hash_find(&slang->sl_wordcount, tw); if (!HASHITEM_EMPTY(hi)) { vim_snprintf((char *)IObuff, IOSIZE, "%s\t%d", - tw, HI2WC(hi)->wc_count); + tw, HI2WC(hi)->wc_count); p = IObuff; } } @@ -6939,20 +7160,16 @@ static void dump_word(slang_T *slang, char_u *word, char_u *pat, } } -// For ":spelldump": Find matching prefixes for "word". Prepend each to -// "word" and append a line to the buffer. -// When "lnum" is zero add insert mode completion. -// Return the updated line number. -static linenr_T -dump_prefixes ( - slang_T *slang, - char_u *word, // case-folded word - char_u *pat, - Direction *dir, - int dumpflags, - int flags, // flags with prefix ID - linenr_T startlnum -) +/// For ":spelldump": Find matching prefixes for "word". Prepend each to +/// "word" and append a line to the buffer. +/// When "lnum" is zero add insert mode completion. +/// +/// @param word case-folded word +/// @param flags flags with prefix ID +/// +/// @return the updated line number. +static linenr_T dump_prefixes(slang_T *slang, char_u *word, char_u *pat, Direction *dir, + int dumpflags, int flags, linenr_T startlnum) { idx_T arridx[MAXWLEN]; int curi[MAXWLEN]; @@ -6960,8 +7177,8 @@ dump_prefixes ( char_u word_up[MAXWLEN]; bool has_word_up = false; int c; - char_u *byts; - idx_T *idxs; + char_u *byts; + idx_T *idxs; linenr_T lnum = startlnum; int depth; int n; @@ -6998,19 +7215,22 @@ dump_prefixes ( c = byts[n]; if (c == 0) { // End of prefix, find out how many IDs there are. - for (i = 1; i < len; ++i) - if (byts[n + i] != 0) + for (i = 1; i < len; ++i) { + if (byts[n + i] != 0) { break; + } + } curi[depth] += i - 1; c = valid_word_prefix(i, n, flags, word, slang, false); if (c != 0) { STRLCPY(prefix + depth, word, MAXWLEN - depth); dump_word(slang, prefix, pat, dir, dumpflags, - (c & WF_RAREPFX) ? (flags | WF_RARE) - : flags, lnum); - if (lnum != 0) + (c & WF_RAREPFX) ? (flags | WF_RARE) + : flags, lnum); + if (lnum != 0) { ++lnum; + } } // Check for prefix that matches the word when the @@ -7018,14 +7238,15 @@ dump_prefixes ( // a condition. if (has_word_up) { c = valid_word_prefix(i, n, flags, word_up, slang, - true); + true); if (c != 0) { STRLCPY(prefix + depth, word_up, MAXWLEN - depth); dump_word(slang, prefix, pat, dir, dumpflags, - (c & WF_RAREPFX) ? (flags | WF_RARE) - : flags, lnum); - if (lnum != 0) + (c & WF_RAREPFX) ? (flags | WF_RARE) + : flags, lnum); + if (lnum != 0) { ++lnum; + } } } } else { @@ -7045,7 +7266,7 @@ dump_prefixes ( // Uses the spell-checking word characters. char_u *spell_to_word_end(char_u *start, win_T *win) { - char_u *p = start; + char_u *p = start; while (*p != NUL && spell_iswordp(p, win)) { MB_PTR_ADV(p); @@ -7060,8 +7281,8 @@ char_u *spell_to_word_end(char_u *start, win_T *win) // Returns the column number of the word. int spell_word_start(int startcol) { - char_u *line; - char_u *p; + char_u *line; + char_u *p; int col = 0; if (no_spell_checking(curwin)) { diff --git a/src/nvim/spellfile.c b/src/nvim/spellfile.c index 843ecec1b1..772275df84 100644 --- a/src/nvim/spellfile.c +++ b/src/nvim/spellfile.c @@ -226,19 +226,17 @@ // stored as an offset to the previous number in as // few bytes as possible, see offset2bytes()) -#include <stdio.h> #include <stdint.h> +#include <stdio.h> #include <wctype.h> -#include "nvim/vim.h" -#include "nvim/spell_defs.h" #include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/ex_cmds2.h" #include "nvim/fileio.h" -#include "nvim/memory.h" #include "nvim/memline.h" +#include "nvim/memory.h" #include "nvim/misc1.h" #include "nvim/option.h" #include "nvim/os/os.h" @@ -246,9 +244,11 @@ #include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/spell.h" +#include "nvim/spell_defs.h" #include "nvim/spellfile.h" #include "nvim/ui.h" #include "nvim/undo.h" +#include "nvim/vim.h" #ifndef UNIX // it's in os/unix_defs.h for Unix # include <time.h> // for time_t @@ -310,7 +310,7 @@ static char *msg_compressing = N_("Compressing word tree..."); // and .dic file. // Main structure to store the contents of a ".aff" file. typedef struct afffile_S { - char_u *af_enc; // "SET", normalized, alloc'ed string or NULL + char_u *af_enc; // "SET", normalized, alloc'ed string or NULL int af_flagtype; // AFT_CHAR, AFT_LONG, AFT_NUM or AFT_CAPLONG unsigned af_rare; // RARE ID for rare word unsigned af_keepcase; // KEEPCASE ID for keep-case word @@ -338,17 +338,17 @@ typedef struct afffile_S { typedef struct affentry_S affentry_T; // Affix entry from ".aff" file. Used for prefixes and suffixes. struct affentry_S { - affentry_T *ae_next; // next affix with same name/number - char_u *ae_chop; // text to chop off basic word (can be NULL) - char_u *ae_add; // text to add to basic word (can be NULL) - char_u *ae_flags; // flags on the affix (can be NULL) - char_u *ae_cond; // condition (NULL for ".") - regprog_T *ae_prog; // regexp program for ae_cond or NULL + affentry_T *ae_next; // next affix with same name/number + char_u *ae_chop; // text to chop off basic word (can be NULL) + char_u *ae_add; // text to add to basic word (can be NULL) + char_u *ae_flags; // flags on the affix (can be NULL) + char_u *ae_cond; // condition (NULL for ".") + regprog_T *ae_prog; // regexp program for ae_cond or NULL char ae_compforbid; // COMPOUNDFORBIDFLAG found char ae_comppermit; // COMPOUNDPERMITFLAG found }; -# define AH_KEY_LEN 17 // 2 x 8 bytes + NUL +#define AH_KEY_LEN 17 // 2 x 8 bytes + NUL // Affix header from ".aff" file. Used for af_pref and af_suff. typedef struct affheader_S { @@ -357,7 +357,7 @@ typedef struct affheader_S { int ah_newID; // prefix ID after renumbering; 0 if not used int ah_combine; // suffix may combine with prefix int ah_follows; // another affix block should be following - affentry_T *ah_first; // first affix entry + affentry_T *ah_first; // first affix entry } affheader_T; #define HI2AH(hi) ((affheader_T *)(hi)->hi_key) @@ -381,7 +381,7 @@ typedef struct compitem_S { typedef struct sblock_S sblock_T; struct sblock_S { int sb_used; // nr of bytes already in use - sblock_T *sb_next; // next block in list + sblock_T *sb_next; // next block in list char_u sb_data[1]; // data, actually longer }; @@ -397,9 +397,9 @@ struct wordnode_S { wordnode_T *next; // next node with same hash key wordnode_T *wnode; // parent node that will write this node } wn_u2; - wordnode_T *wn_child; // child (next byte in word) - wordnode_T *wn_sibling; // next sibling (alternate byte in word, - // always sorted) + wordnode_T *wn_child; // child (next byte in word) + wordnode_T *wn_sibling; // next sibling (alternate byte in word, + // always sorted) int wn_refs; // Nr. of references to this node. Only // relevant for first node in a list of // siblings, in following siblings it is @@ -425,29 +425,29 @@ struct wordnode_S { // Info used while reading the spell files. typedef struct spellinfo_S { - wordnode_T *si_foldroot; // tree with case-folded words + wordnode_T *si_foldroot; // tree with case-folded words long si_foldwcount; // nr of words in si_foldroot - wordnode_T *si_keeproot; // tree with keep-case words + wordnode_T *si_keeproot; // tree with keep-case words long si_keepwcount; // nr of words in si_keeproot - wordnode_T *si_prefroot; // tree with postponed prefixes + wordnode_T *si_prefroot; // tree with postponed prefixes long si_sugtree; // creating the soundfolding trie - sblock_T *si_blocks; // memory blocks used + sblock_T *si_blocks; // memory blocks used long si_blocks_cnt; // memory blocks allocated int si_did_emsg; // TRUE when ran out of memory long si_compress_cnt; // words to add before lowering // compression limit - wordnode_T *si_first_free; // List of nodes that have been freed during - // compression, linked by "wn_child" field. + wordnode_T *si_first_free; // List of nodes that have been freed during + // compression, linked by "wn_child" field. long si_free_count; // number of nodes in si_first_free #ifdef SPELL_PRINTTREE int si_wordnode_nr; // sequence nr for nodes #endif - buf_T *si_spellbuf; // buffer used to store soundfold word table + buf_T *si_spellbuf; // buffer used to store soundfold word table int si_ascii; // handling only ASCII words int si_add; // addition file @@ -457,18 +457,18 @@ typedef struct spellinfo_S { int si_memtot; // runtime memory used int si_verbose; // verbose messages int si_msg_count; // number of words added since last message - char_u *si_info; // info text chars or NULL + char_u *si_info; // info text chars or NULL int si_region_count; // number of regions supported (1 when there // are no regions) char_u si_region_name[MAXREGIONS * 2 + 1]; - // region names; used only if - // si_region_count > 1) + // region names; used only if + // si_region_count > 1) garray_T si_rep; // list of fromto_T entries from REP lines garray_T si_repsal; // list of fromto_T entries from REPSAL lines garray_T si_sal; // list of fromto_T entries from SAL lines - char_u *si_sofofr; // SOFOFROM text - char_u *si_sofoto; // SOFOTO text + char_u *si_sofofr; // SOFOFROM text + char_u *si_sofoto; // SOFOTO text int si_nosugfile; // NOSUGFILE item found int si_nosplitsugs; // NOSPLITSUGS item found int si_nocompoundsugs; // NOCOMPOUNDSUGS item found @@ -478,16 +478,16 @@ typedef struct spellinfo_S { time_t si_sugtime; // timestamp for .sug file int si_rem_accents; // soundsalike: remove accents garray_T si_map; // MAP info concatenated - char_u *si_midword; // MIDWORD chars or NULL + char_u *si_midword; // MIDWORD chars or NULL int si_compmax; // max nr of words for compounding int si_compminlen; // minimal length for compounding int si_compsylmax; // max nr of syllables for compounding int si_compoptions; // COMP_ flags garray_T si_comppat; // CHECKCOMPOUNDPATTERN items, each stored as // a string - char_u *si_compflags; // flags used for compounding + char_u *si_compflags; // flags used for compounding char_u si_nobreak; // NOBREAK - char_u *si_syllable; // syllable string + char_u *si_syllable; // syllable string garray_T si_prefcond; // table with conditions for postponed // prefixes, each stored as a string int si_newprefID; // current value for ah_newID @@ -508,16 +508,16 @@ typedef struct spellinfo_S { /// @return Allows to proceed if everything is OK, returns SP_TRUNCERROR if /// there are not enough bytes, returns SP_OTHERERROR if reading failed. #define SPELL_READ_BYTES(buf, n, fd, exit_code) \ - do { \ - const size_t n__SPRB = (n); \ - FILE *const fd__SPRB = (fd); \ - char *const buf__SPRB = (buf); \ - const size_t read_bytes__SPRB = fread(buf__SPRB, 1, n__SPRB, fd__SPRB); \ - if (read_bytes__SPRB != n__SPRB) { \ - exit_code; \ - return feof(fd__SPRB) ? SP_TRUNCERROR : SP_OTHERERROR; \ - } \ - } while (0) + do { \ + const size_t n__SPRB = (n); \ + FILE *const fd__SPRB = (fd); \ + char *const buf__SPRB = (buf); \ + const size_t read_bytes__SPRB = fread(buf__SPRB, 1, n__SPRB, fd__SPRB); \ + if (read_bytes__SPRB != n__SPRB) { \ + exit_code; \ + return feof(fd__SPRB) ? SP_TRUNCERROR : SP_OTHERERROR; \ + } \ + } while (0) /// Like #SPELL_READ_BYTES, but also error out if NUL byte was read /// @@ -525,16 +525,16 @@ typedef struct spellinfo_S { /// there are not enough bytes, returns SP_OTHERERROR if reading failed, /// returns SP_FORMERROR if read out a NUL byte. #define SPELL_READ_NONNUL_BYTES(buf, n, fd, exit_code) \ - do { \ - const size_t n__SPRNB = (n); \ - FILE *const fd__SPRNB = (fd); \ - char *const buf__SPRNB = (buf); \ - SPELL_READ_BYTES(buf__SPRNB, n__SPRNB, fd__SPRNB, exit_code); \ - if (memchr(buf__SPRNB, NUL, (size_t)n__SPRNB)) { \ - exit_code; \ - return SP_FORMERROR; \ - } \ - } while (0) + do { \ + const size_t n__SPRNB = (n); \ + FILE *const fd__SPRNB = (fd); \ + char *const buf__SPRNB = (buf); \ + SPELL_READ_BYTES(buf__SPRNB, n__SPRNB, fd__SPRNB, exit_code); \ + if (memchr(buf__SPRNB, NUL, (size_t)n__SPRNB)) { \ + exit_code; \ + return SP_FORMERROR; \ + } \ + } while (0) /// Check that spell file starts with a magic string /// @@ -556,40 +556,36 @@ static inline int spell_check_magic_string(FILE *const fd) return 0; } -// Load one spell file and store the info into a slang_T. -// -// This is invoked in three ways: -// - From spell_load_cb() to load a spell file for the first time. "lang" is -// the language name, "old_lp" is NULL. Will allocate an slang_T. -// - To reload a spell file that was changed. "lang" is NULL and "old_lp" -// points to the existing slang_T. -// - Just after writing a .spl file; it's read back to produce the .sug file. -// "old_lp" is NULL and "lang" is NULL. Will allocate an slang_T. -// -// Returns the slang_T the spell file was loaded into. NULL for error. -slang_T * -spell_load_file ( - char_u *fname, - char_u *lang, - slang_T *old_lp, - bool silent // no error if file doesn't exist -) +/// Load one spell file and store the info into a slang_T. +/// +/// This is invoked in three ways: +/// - From spell_load_cb() to load a spell file for the first time. "lang" is +/// the language name, "old_lp" is NULL. Will allocate an slang_T. +/// - To reload a spell file that was changed. "lang" is NULL and "old_lp" +/// points to the existing slang_T. +/// - Just after writing a .spl file; it's read back to produce the .sug file. +/// "old_lp" is NULL and "lang" is NULL. Will allocate an slang_T. +/// +/// @param silent no error if file doesn't exist +/// +/// @return the slang_T the spell file was loaded into. NULL for error. +slang_T *spell_load_file(char_u *fname, char_u *lang, slang_T *old_lp, bool silent) { - FILE *fd; - char_u *p; + FILE *fd; + char_u *p; int n; int len; - char_u *save_sourcing_name = sourcing_name; + char_u *save_sourcing_name = sourcing_name; linenr_T save_sourcing_lnum = sourcing_lnum; - slang_T *lp = NULL; + slang_T *lp = NULL; int c = 0; int res; fd = os_fopen((char *)fname, "r"); if (fd == NULL) { - if (!silent) + if (!silent) { EMSG2(_(e_notopen), fname); - else if (p_verbose > 2) { + } else if (p_verbose > 2) { verbose_enter(); smsg((char *)e_notopen, fname); verbose_leave(); @@ -610,8 +606,9 @@ spell_load_file ( // Check for .add.spl. lp->sl_add = strstr((char *)path_tail(fname), SPL_FNAME_ADD) != NULL; - } else + } else { lp = old_lp; + } // Set sourcing_name, so that error messages mention the file name. sourcing_name = fname; @@ -620,19 +617,16 @@ spell_load_file ( // <HEADER>: <fileID> const int scms_ret = spell_check_magic_string(fd); switch (scms_ret) { - case SP_FORMERROR: - case SP_TRUNCERROR: { - emsgf("%s", _("E757: This does not look like a spell file")); - goto endFAIL; - } - case SP_OTHERERROR: { - emsgf(_("E5042: Failed to read spell file %s: %s"), - fname, strerror(ferror(fd))); - goto endFAIL; - } - case 0: { - break; - } + case SP_FORMERROR: + case SP_TRUNCERROR: + emsgf("%s", _("E757: This does not look like a spell file")); + goto endFAIL; + case SP_OTHERERROR: + emsgf(_("E5042: Failed to read spell file %s: %s"), + fname, strerror(ferror(fd))); + goto endFAIL; + case 0: + break; } c = getc(fd); // <versionnr> if (c < VIMSPELLVERSION) { @@ -648,19 +642,22 @@ spell_load_file ( // <section>: <sectionID> <sectionflags> <sectionlen> (section contents) for (;; ) { n = getc(fd); // <sectionID> or <sectionend> - if (n == SN_END) + if (n == SN_END) { break; + } c = getc(fd); // <sectionflags> len = get4c(fd); // <sectionlen> - if (len < 0) + if (len < 0) { goto truncerr; + } res = 0; switch (n) { case SN_INFO: lp->sl_info = READ_STRING(fd, len); // <infotext> - if (lp->sl_info == NULL) + if (lp->sl_info == NULL) { goto endFAIL; + } break; case SN_REGION: @@ -673,8 +670,9 @@ spell_load_file ( case SN_MIDWORD: lp->sl_midword = READ_STRING(fd, len); // <midword> - if (lp->sl_midword == NULL) + if (lp->sl_midword == NULL) { goto endFAIL; + } break; case SN_PREFCOND: @@ -699,8 +697,9 @@ spell_load_file ( case SN_MAP: p = READ_STRING(fd, len); // <mapstr> - if (p == NULL) + if (p == NULL) { goto endFAIL; + } set_map_str(lp, p); xfree(p); break; @@ -731,10 +730,12 @@ spell_load_file ( case SN_SYLLABLE: lp->sl_syllable = READ_STRING(fd, len); // <syllable> - if (lp->sl_syllable == NULL) + if (lp->sl_syllable == NULL) { goto endFAIL; - if (init_syl_tab(lp) == FAIL) + } + if (init_syl_tab(lp) == FAIL) { goto endFAIL; + } break; default: @@ -744,9 +745,11 @@ spell_load_file ( EMSG(_("E770: Unsupported section in spell file")); goto endFAIL; } - while (--len >= 0) - if (getc(fd) < 0) + while (--len >= 0) { + if (getc(fd) < 0) { goto truncerr; + } + } break; } someerror: @@ -759,8 +762,9 @@ truncerr: EMSG(_(e_spell_trunc)); goto endFAIL; } - if (res == SP_OTHERERROR) + if (res == SP_OTHERERROR) { goto endFAIL; + } } // <LWORDTREE> @@ -792,16 +796,19 @@ truncerr: goto endOK; endFAIL: - if (lang != NULL) + if (lang != NULL) { // truncating the name signals the error to spell_load_lang() *lang = NUL; - if (lp != NULL && old_lp == NULL) + } + if (lp != NULL && old_lp == NULL) { slang_free(lp); + } lp = NULL; endOK: - if (fd != NULL) + if (fd != NULL) { fclose(fd); + } sourcing_name = save_sourcing_name; sourcing_lnum = save_sourcing_lnum; @@ -827,8 +834,9 @@ static void tree_count_words(char_u *byts, idx_T *idxs) if (curi[depth] > byts[arridx[depth]]) { // Done all bytes at this node, go up one level. idxs[arridx[depth]] = wordcount[depth]; - if (depth > 0) + if (depth > 0) { wordcount[depth - 1] += wordcount[depth]; + } --depth; fast_breakcheck(); @@ -862,10 +870,10 @@ static void tree_count_words(char_u *byts, idx_T *idxs) // Load the .sug files for languages that have one and weren't loaded yet. void suggest_load_files(void) { - langp_T *lp; - slang_T *slang; - char_u *dotp; - FILE *fd; + langp_T *lp; + slang_T *slang; + char_u *dotp; + FILE *fd; char_u buf[MAXWLEN]; int i; time_t timestamp; @@ -895,21 +903,22 @@ void suggest_load_files(void) } // <SUGHEADER>: <fileID> <versionnr> <timestamp> - for (i = 0; i < VIMSUGMAGICL; ++i) + for (i = 0; i < VIMSUGMAGICL; ++i) { buf[i] = getc(fd); // <fileID> + } if (STRNCMP(buf, VIMSUGMAGIC, VIMSUGMAGICL) != 0) { EMSG2(_("E778: This does not look like a .sug file: %s"), - slang->sl_fname); + slang->sl_fname); goto nextone; } c = getc(fd); // <versionnr> if (c < VIMSUGVERSION) { EMSG2(_("E779: Old .sug file, needs to be updated: %s"), - slang->sl_fname); + slang->sl_fname); goto nextone; - } else if (c > VIMSUGVERSION) { + } else if (c > VIMSUGVERSION) { EMSG2(_("E780: .sug file is for newer version of Vim: %s"), - slang->sl_fname); + slang->sl_fname); goto nextone; } @@ -918,7 +927,7 @@ void suggest_load_files(void) timestamp = get8ctime(fd); // <timestamp> if (timestamp != slang->sl_sugtime) { EMSG2(_("E781: .sug file doesn't match .spl file: %s"), - slang->sl_fname); + slang->sl_fname); goto nextone; } @@ -928,7 +937,7 @@ void suggest_load_files(void) false, 0) != 0) { someerror: EMSG2(_("E782: error while reading .sug file: %s"), - slang->sl_fname); + slang->sl_fname); slang_clear_sug(slang); goto nextone; } @@ -942,8 +951,9 @@ someerror: // <sugwcount> wcount = get4c(fd); - if (wcount < 0) + if (wcount < 0) { goto someerror; + } // Read all the wordnr lists into the buffer, one NUL terminated // list per line. @@ -956,8 +966,9 @@ someerror: goto someerror; } GA_APPEND(char_u, &ga, c); - if (c == NUL) + if (c == NUL) { break; + } } if (ml_append_buf(slang->sl_sugbuf, (linenr_T)wordnr, ga.ga_data, ga.ga_len, true) == FAIL) { @@ -972,8 +983,9 @@ someerror: tree_count_words(slang->sl_sbyts, slang->sl_sidxs); nextone: - if (fd != NULL) + if (fd != NULL) { fclose(fd); + } STRCPY(dotp, ".spl"); } } @@ -988,7 +1000,7 @@ nextone: static char_u *read_cnt_string(FILE *fd, int cnt_bytes, int *cntp) { int cnt = 0; - char_u *str; + char_u *str; // read the length bytes, MSB first for (int i = 0; i < cnt_bytes; i++) { @@ -1001,12 +1013,13 @@ static char_u *read_cnt_string(FILE *fd, int cnt_bytes, int *cntp) cnt = (cnt << 8) + (unsigned)c; } *cntp = cnt; - if (cnt == 0) + if (cnt == 0) { return NULL; // nothing to read, return NULL - + } str = READ_STRING(fd, cnt); - if (str == NULL) + if (str == NULL) { *cntp = SP_OTHERERROR; + } return str; } @@ -1027,14 +1040,15 @@ static int read_region_section(FILE *fd, slang_T *lp, int len) // Return SP_*ERROR flags. static int read_charflags_section(FILE *fd) { - char_u *flags; - char_u *fol; + char_u *flags; + char_u *fol; int flagslen, follen; // <charflagslen> <charflags> flags = read_cnt_string(fd, 1, &flagslen); - if (flagslen < 0) + if (flagslen < 0) { return flagslen; + } // <folcharslen> <folchars> fol = read_cnt_string(fd, 2, &follen); @@ -1044,15 +1058,17 @@ static int read_charflags_section(FILE *fd) } // Set the word-char flags and fill SPELL_ISUPPER() table. - if (flags != NULL && fol != NULL) + if (flags != NULL && fol != NULL) { set_spell_charflags(flags, flagslen, fol); + } xfree(flags); xfree(fol); // When <charflagslen> is zero then <fcharlen> must also be zero. - if ((flags == NULL) != (fol == NULL)) + if ((flags == NULL) != (fol == NULL)) { return SP_FORMERROR; + } return 0; } @@ -1094,11 +1110,12 @@ static int read_prefcond_section(FILE *fd, slang_T *lp) static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first) { int cnt; - fromto_T *ftp; + fromto_T *ftp; cnt = get2c(fd); // <repcount> - if (cnt < 0) + if (cnt < 0) { return SP_TRUNCERROR; + } ga_grow(gap, cnt); @@ -1107,15 +1124,18 @@ static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first) int c; ftp = &((fromto_T *)gap->ga_data)[gap->ga_len]; ftp->ft_from = read_cnt_string(fd, 1, &c); - if (c < 0) + if (c < 0) { return c; - if (c == 0) + } + if (c == 0) { return SP_FORMERROR; + } ftp->ft_to = read_cnt_string(fd, 1, &c); if (c <= 0) { xfree(ftp->ft_from); - if (c < 0) + if (c < 0) { return c; + } return SP_FORMERROR; } } @@ -1126,8 +1146,9 @@ static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first) } for (int i = 0; i < gap->ga_len; ++i) { ftp = &((fromto_T *)gap->ga_data)[i]; - if (first[*ftp->ft_from] == -1) + if (first[*ftp->ft_from] == -1) { first[*ftp->ft_from] = i; + } } return 0; } @@ -1137,10 +1158,10 @@ static int read_rep_section(FILE *fd, garray_T *gap, int16_t *first) static int read_sal_section(FILE *fd, slang_T *slang) { int cnt; - garray_T *gap; - salitem_T *smp; + garray_T *gap; + salitem_T *smp; int ccnt; - char_u *p; + char_u *p; slang->sl_sofo = false; @@ -1156,8 +1177,9 @@ static int read_sal_section(FILE *fd, slang_T *slang) } cnt = get2c(fd); // <salcount> - if (cnt < 0) + if (cnt < 0) { return SP_TRUNCERROR; + } gap = &slang->sl_sal; ga_init(gap, sizeof(salitem_T), 10); @@ -1169,8 +1191,9 @@ static int read_sal_section(FILE *fd, slang_T *slang) smp = &((salitem_T *)gap->ga_data)[gap->ga_len]; ccnt = getc(fd); // <salfromlen> - if (ccnt < 0) + if (ccnt < 0) { return SP_TRUNCERROR; + } p = xmalloc(ccnt + 2); smp->sm_lead = p; @@ -1178,8 +1201,9 @@ static int read_sal_section(FILE *fd, slang_T *slang) int i = 0; for (; i < ccnt; ++i) { c = getc(fd); // <salfrom> - if (vim_strchr((char_u *)"0123456789(-<^$", c) != NULL) + if (vim_strchr((char_u *)"0123456789(-<^$", c) != NULL) { break; + } *p++ = c; } smp->sm_leadlen = (int)(p - smp->sm_lead); @@ -1190,15 +1214,18 @@ static int read_sal_section(FILE *fd, slang_T *slang) smp->sm_oneof = p; for (++i; i < ccnt; ++i) { c = getc(fd); // <salfrom> - if (c == ')') + if (c == ')') { break; + } *p++ = c; } *p++ = NUL; - if (++i < ccnt) + if (++i < ccnt) { c = getc(fd); - } else + } + } else { smp->sm_oneof = NULL; + } // Any following chars go in sm_rules. smp->sm_rules = p; @@ -1209,7 +1236,8 @@ static int read_sal_section(FILE *fd, slang_T *slang) i++; if (i < ccnt) { SPELL_READ_NONNUL_BYTES( // <salfrom> - (char *)p, (size_t)(ccnt - i), fd, xfree(smp->sm_lead)); + (char *)p, (size_t)(ccnt - i), fd, + xfree(smp->sm_lead)); p += (ccnt - i); } *p++ = NUL; @@ -1272,13 +1300,16 @@ static int read_words_section(FILE *fd, slang_T *lp, int len) // Read one word at a time. for (i = 0;; ++i) { c = getc(fd); - if (c == EOF) + if (c == EOF) { return SP_TRUNCERROR; + } word[i] = c; - if (word[i] == NUL) + if (word[i] == NUL) { break; - if (i == MAXWLEN - 1) + } + if (i == MAXWLEN - 1) { return SP_FORMERROR; + } } // Init the count to 10. @@ -1293,15 +1324,16 @@ static int read_words_section(FILE *fd, slang_T *lp, int len) static int read_sofo_section(FILE *fd, slang_T *slang) { int cnt; - char_u *from, *to; + char_u *from, *to; int res; slang->sl_sofo = true; // <sofofromlen> <sofofrom> from = read_cnt_string(fd, 2, &cnt); - if (cnt < 0) + if (cnt < 0) { return cnt; + } // <sofotolen> <sofoto> to = read_cnt_string(fd, 2, &cnt); @@ -1311,12 +1343,13 @@ static int read_sofo_section(FILE *fd, slang_T *slang) } // Store the info in slang->sl_sal and/or slang->sl_sal_first. - if (from != NULL && to != NULL) + if (from != NULL && to != NULL) { res = set_sofo(slang, from, to); - else if (from != NULL || to != NULL) + } else if (from != NULL || to != NULL) { res = SP_FORMERROR; // only one of two strings is an error - else + } else { res = 0; + } xfree(from); xfree(to); @@ -1331,39 +1364,42 @@ static int read_compound(FILE *fd, slang_T *slang, int len) int todo = len; int c; int atstart; - char_u *pat; - char_u *pp; - char_u *cp; - char_u *ap; - char_u *crp; + char_u *pat; + char_u *pp; + char_u *cp; + char_u *ap; + char_u *crp; int cnt; - garray_T *gap; + garray_T *gap; - if (todo < 2) + if (todo < 2) { return SP_FORMERROR; // need at least two bytes - + } --todo; c = getc(fd); // <compmax> - if (c < 2) + if (c < 2) { c = MAXWLEN; + } slang->sl_compmax = c; --todo; c = getc(fd); // <compminlen> - if (c < 1) + if (c < 1) { c = 0; + } slang->sl_compminlen = c; --todo; c = getc(fd); // <compsylmax> - if (c < 1) + if (c < 1) { c = MAXWLEN; + } slang->sl_compsylmax = c; c = getc(fd); // <compoptions> - if (c != 0) + if (c != 0) { ungetc(c, fd); // be backwards compatible with Vim 7.0b - else { + } else { --todo; c = getc(fd); // only use the lower byte for now --todo; @@ -1381,13 +1417,15 @@ static int read_compound(FILE *fd, slang_T *slang, int len) ((char_u **)(gap->ga_data))[gap->ga_len++] = read_cnt_string(fd, 1, &cnt); // <comppatlen> <comppattext> - if (cnt < 0) + if (cnt < 0) { return cnt; + } todo -= cnt + 1; } } - if (todo < 0) + if (todo < 0) { return SP_FORMERROR; + } // Turn the COMPOUNDRULE items into a regexp pattern: // "a[bc]/a*b+" -> "^\(a[bc]\|a*b\+\)$". @@ -1436,17 +1474,18 @@ static int read_compound(FILE *fd, slang_T *slang, int len) if (atstart != 0) { // At start of item: copy flags to "sl_compstartflags". For a // [abc] item set "atstart" to 2 and copy up to the ']'. - if (c == '[') + if (c == '[') { atstart = 2; - else if (c == ']') + } else if (c == ']') { atstart = 0; - else { + } else { if (!byte_in_str(slang->sl_compstartflags, c)) { *cp++ = c; *cp = NUL; } - if (atstart == 1) + if (atstart == 1) { atstart = 0; + } } } @@ -1455,8 +1494,9 @@ static int read_compound(FILE *fd, slang_T *slang, int len) if (c == '?' || c == '+' || c == '*') { XFREE_CLEAR(slang->sl_comprules); crp = NULL; - } else + } else { *crp++ = c; + } } if (c == '/') { // slash separates two items @@ -1476,13 +1516,15 @@ static int read_compound(FILE *fd, slang_T *slang, int len) *pp++ = '$'; *pp = NUL; - if (crp != NULL) + if (crp != NULL) { *crp = NUL; + } slang->sl_compprog = vim_regcomp(pat, RE_MAGIC + RE_STRING + RE_STRICT); xfree(pat); - if (slang->sl_compprog == NULL) + if (slang->sl_compprog == NULL) { return SP_FORMERROR; + } return 0; } @@ -1491,8 +1533,8 @@ static int read_compound(FILE *fd, slang_T *slang, int len) // Returns SP_*ERROR flags when there is something wrong. static int set_sofo(slang_T *lp, char_u *from, char_u *to) { - char_u *s; - char_u *p; + char_u *s; + char_u *p; // Use "sl_sal" as an array with 256 pointers to a list of wide // characters. The index is the low byte of the character. @@ -1554,10 +1596,10 @@ static int set_sofo(slang_T *lp, char_u *from, char_u *to) // Fill the first-index table for "lp". static void set_sal_first(slang_T *lp) { - salfirst_T *sfirst; - salitem_T *smp; + salfirst_T *sfirst; + salitem_T *smp; int c; - garray_T *gap = &lp->sl_sal; + garray_T *gap = &lp->sl_sal; sfirst = lp->sl_sal_first; for (int i = 0; i < 256; ++i) { @@ -1613,24 +1655,21 @@ static int *mb_str2wide(char_u *s) return res; } -// Reads a tree from the .spl or .sug file. -// Allocates the memory and stores pointers in "bytsp" and "idxsp". -// This is skipped when the tree has zero length. -// Returns zero when OK, SP_ value for an error. -static int -spell_read_tree ( - FILE *fd, - char_u **bytsp, - long *bytsp_len, - idx_T **idxsp, - bool prefixtree, // true for the prefix tree - int prefixcnt // when "prefixtree" is true: prefix count -) +/// Reads a tree from the .spl or .sug file. +/// Allocates the memory and stores pointers in "bytsp" and "idxsp". +/// This is skipped when the tree has zero length. +/// +/// @param prefixtree true for the prefix tree +/// @param prefixcnt when "prefixtree" is true: prefix count +/// +/// @return zero when OK, SP_ value for an error. +static int spell_read_tree(FILE *fd, char_u **bytsp, long *bytsp_len, idx_T **idxsp, + bool prefixtree, int prefixcnt) FUNC_ATTR_NONNULL_ARG(1, 2, 4) { int idx; - char_u *bp; - idx_T *ip; + char_u *bp; + idx_T *ip; // The tree size was computed when writing the file, so that we can // allocate it as one long block. <nodecount> @@ -1656,30 +1695,28 @@ spell_read_tree ( // Recursively read the tree and store it in the array. idx = read_tree_node(fd, bp, ip, len, 0, prefixtree, prefixcnt); - if (idx < 0) + if (idx < 0) { return idx; + } } return 0; } -// Read one row of siblings from the spell file and store it in the byte array -// "byts" and index array "idxs". Recursively read the children. -// -// NOTE: The code here must match put_node()! -// -// Returns the index (>= 0) following the siblings. -// Returns SP_TRUNCERROR if the file is shorter than expected. -// Returns SP_FORMERROR if there is a format error. -static idx_T -read_tree_node ( - FILE *fd, - char_u *byts, - idx_T *idxs, - int maxidx, // size of arrays - idx_T startidx, // current index in "byts" and "idxs" - bool prefixtree, // true for reading PREFIXTREE - int maxprefcondnr // maximum for <prefcondnr> -) +/// Read one row of siblings from the spell file and store it in the byte array +/// "byts" and index array "idxs". Recursively read the children. +/// +/// NOTE: The code here must match put_node()! +/// +/// Returns the index (>= 0) following the siblings. +/// Returns SP_TRUNCERROR if the file is shorter than expected. +/// Returns SP_FORMERROR if there is a format error. +/// +/// @param maxidx size of arrays +/// @param startidx current index in "byts" and "idxs" +/// @param prefixtree true for reading PREFIXTREE +/// @param maxprefcondnr maximum for <prefcondnr> +static idx_T read_tree_node(FILE *fd, char_u *byts, idx_T *idxs, int maxidx, idx_T startidx, + bool prefixtree, int maxprefcondnr) { int len; int i; @@ -1690,18 +1727,21 @@ read_tree_node ( #define SHARED_MASK 0x8000000 len = getc(fd); // <siblingcount> - if (len <= 0) + if (len <= 0) { return SP_TRUNCERROR; + } - if (startidx + len >= maxidx) + if (startidx + len >= maxidx) { return SP_FORMERROR; + } byts[idx++] = len; // Read the byte values, flag/region bytes and shared indexes. for (i = 1; i <= len; ++i) { c = getc(fd); // <byte> - if (c < 0) + if (c < 0) { return SP_TRUNCERROR; + } if (c <= BY_SPECIAL) { if (c == BY_NOFLAGS && !prefixtree) { // No flags, all regions. @@ -1712,16 +1752,18 @@ read_tree_node ( // condition nr. In idxs[] store the prefix ID in the low // byte, the condition index shifted up 8 bits, the flags // shifted up 24 bits. - if (c == BY_FLAGS) + if (c == BY_FLAGS) { c = getc(fd) << 24; // <pflags> - else + } else { c = 0; + } c |= getc(fd); // <affixID> n = get2c(fd); // <prefcondnr> - if (n >= maxprefcondnr) + if (n >= maxprefcondnr) { return SP_FORMERROR; + } c |= (n << 8); } else { // c must be BY_FLAGS or BY_FLAGS2 // Read flags and optional region and prefix ID. In @@ -1729,21 +1771,25 @@ read_tree_node ( // that and prefix ID above the region. c2 = c; c = getc(fd); // <flags> - if (c2 == BY_FLAGS2) + if (c2 == BY_FLAGS2) { c = (getc(fd) << 8) + c; // <flags2> - if (c & WF_REGION) + } + if (c & WF_REGION) { c = (getc(fd) << 16) + c; // <region> - if (c & WF_AFX) + } + if (c & WF_AFX) { c = (getc(fd) << 24) + c; // <affixID> + } } idxs[idx] = c; c = 0; - } else { // c == BY_INDEX + } else { // c == BY_INDEX // <nodeidx> n = get3c(fd); - if (n < 0 || n >= maxidx) + if (n < 0 || n >= maxidx) { return SP_FORMERROR; + } idxs[idx] = n + SHARED_MASK; c = getc(fd); // <xbyte> } @@ -1754,38 +1800,39 @@ read_tree_node ( // Recursively read the children for non-shared siblings. // Skip the end-of-word ones (zero byte value) and the shared ones (and // remove SHARED_MASK) - for (i = 1; i <= len; ++i) + for (i = 1; i <= len; ++i) { if (byts[startidx + i] != 0) { - if (idxs[startidx + i] & SHARED_MASK) + if (idxs[startidx + i] & SHARED_MASK) { idxs[startidx + i] &= ~SHARED_MASK; - else { + } else { idxs[startidx + i] = idx; idx = read_tree_node(fd, byts, idxs, maxidx, idx, - prefixtree, maxprefcondnr); - if (idx < 0) + prefixtree, maxprefcondnr); + if (idx < 0) { break; + } } } + } return idx; } -// Reload the spell file "fname" if it's loaded. -static void -spell_reload_one ( - char_u *fname, - bool added_word // invoked through "zg" -) +/// Reload the spell file "fname" if it's loaded. +/// +/// @param added_word invoked through "zg" +static void spell_reload_one(char_u *fname, bool added_word) { - slang_T *slang; + slang_T *slang; bool didit = false; for (slang = first_lang; slang != NULL; slang = slang->sl_next) { if (path_full_compare(fname, slang->sl_fname, false, true) == kEqualFiles) { slang_clear(slang); - if (spell_load_file(fname, NULL, slang, false) == NULL) + if (spell_load_file(fname, NULL, slang, false) == NULL) { // reloading failed, clear the language slang_clear(slang); + } redraw_all_later(SOME_VALID); didit = true; } @@ -1793,8 +1840,9 @@ spell_reload_one ( // When "zg" was used and the file wasn't loaded yet, should redo // 'spelllang' to load it now. - if (added_word && !didit) + if (added_word && !didit) { did_set_spelllang(curwin); + } } // Functions for ":mkspell". @@ -1820,13 +1868,14 @@ static long compress_added = 500000; // word count // Sets "sps_flags". int spell_check_msm(void) { - char_u *p = p_msm; + char_u *p = p_msm; long start = 0; long incr = 0; long added = 0; - if (!ascii_isdigit(*p)) + if (!ascii_isdigit(*p)) { return FAIL; + } // block count = (value * 1024) / SBLOCKSIZE (but avoid overflow) start = (getdigits_long(&p, true, 0) * 10) / (SBLOCKSIZE / 102); if (*p != ',') { @@ -1864,11 +1913,12 @@ int spell_check_msm(void) // readable format, so that we can see what happens when adding a word and/or // compressing the tree. // Based on code from Olaf Seibert. -#define PRINTLINESIZE 1000 -#define PRINTWIDTH 6 +# define PRINTLINESIZE 1000 +# define PRINTWIDTH 6 -#define PRINTSOME(l, depth, fmt, a1, a2) vim_snprintf(l + depth * PRINTWIDTH, \ - PRINTLINESIZE - PRINTWIDTH * depth, fmt, a1, a2) +# define PRINTSOME(l, depth, fmt, a1, a2) vim_snprintf(l + depth * PRINTWIDTH, \ + PRINTLINESIZE - PRINTWIDTH * depth, fmt, a1, \ + a2) static char line1[PRINTLINESIZE]; static char line2[PRINTLINESIZE]; @@ -1876,7 +1926,7 @@ static char line3[PRINTLINESIZE]; static void spell_clear_flags(wordnode_T *node) { - wordnode_T *np; + wordnode_T *np; for (np = node; np != NULL; np = np->wn_sibling) { np->wn_u1.index = FALSE; @@ -1898,20 +1948,23 @@ static void spell_print_node(wordnode_T *node, int depth) node->wn_u1.index = TRUE; if (node->wn_byte != NUL) { - if (node->wn_child != NULL) + if (node->wn_child != NULL) { PRINTSOME(line1, depth, " %c -> ", node->wn_byte, 0); - else + } else { // Cannot happen? PRINTSOME(line1, depth, " %c ???", node->wn_byte, 0); - } else + } + } else { PRINTSOME(line1, depth, " $ ", 0, 0); + } PRINTSOME(line2, depth, "%d/%d ", node->wn_nr, node->wn_refs); - if (node->wn_sibling != NULL) + if (node->wn_sibling != NULL) { PRINTSOME(line3, depth, " | ", 0, 0); - else + } else { PRINTSOME(line3, depth, " ", 0, 0); + } if (node->wn_byte == NUL) { msg((char_u *)line1); @@ -1920,8 +1973,9 @@ static void spell_print_node(wordnode_T *node, int depth) } // do the children - if (node->wn_byte != NUL && node->wn_child != NULL) + if (node->wn_byte != NUL && node->wn_child != NULL) { spell_print_node(node->wn_child, depth + 1); + } // do the siblings if (node->wn_sibling != NULL) { @@ -1951,39 +2005,39 @@ static void spell_print_tree(wordnode_T *root) // Returns an afffile_T, NULL for complete failure. static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) { - FILE *fd; + FILE *fd; char_u rline[MAXLINELEN]; - char_u *line; - char_u *pc = NULL; + char_u *line; + char_u *pc = NULL; #define MAXITEMCNT 30 - char_u *(items[MAXITEMCNT]); + char_u *(items[MAXITEMCNT]); int itemcnt; - char_u *p; + char_u *p; int lnum = 0; affheader_T *cur_aff = NULL; bool did_postpone_prefix = false; int aff_todo = 0; - hashtab_T *tp; - char_u *low = NULL; - char_u *fol = NULL; - char_u *upp = NULL; + hashtab_T *tp; + char_u *low = NULL; + char_u *fol = NULL; + char_u *upp = NULL; int do_rep; int do_repsal; int do_sal; int do_mapline; bool found_map = false; - hashitem_T *hi; + hashitem_T *hi; int l; int compminlen = 0; // COMPOUNDMIN value int compsylmax = 0; // COMPOUNDSYLMAX value int compoptions = 0; // COMP_ flags int compmax = 0; // COMPOUNDWORDMAX value - char_u *compflags = NULL; // COMPOUNDFLAG and COMPOUNDRULE - // concatenated - char_u *midword = NULL; // MIDWORD value - char_u *syllable = NULL; // SYLLABLE value - char_u *sofofrom = NULL; // SOFOFROM value - char_u *sofoto = NULL; // SOFOTO value + char_u *compflags = NULL; // COMPOUNDFLAG and COMPOUNDRULE + // concatenated + char_u *midword = NULL; // MIDWORD value + char_u *syllable = NULL; // SYLLABLE value + char_u *sofofrom = NULL; // SOFOFROM value + char_u *sofoto = NULL; // SOFOTO value // Open the file. fd = os_fopen((char *)fname, "r"); @@ -2019,8 +2073,9 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) ++lnum; // Skip comment lines. - if (*rline == '#') + if (*rline == '#') { continue; + } // Convert from "SET" to 'encoding' when needed. xfree(pc); @@ -2041,22 +2096,29 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) // item. itemcnt = 0; for (p = line;; ) { - while (*p != NUL && *p <= ' ') // skip white space and CR/NL + while (*p != NUL && *p <= ' ') { // skip white space and CR/NL ++p; - if (*p == NUL) + } + if (*p == NUL) { break; - if (itemcnt == MAXITEMCNT) // too many items + } + if (itemcnt == MAXITEMCNT) { // too many items break; + } items[itemcnt++] = p; // A few items have arbitrary text argument, don't split them. - if (itemcnt == 2 && spell_info_item(items[0])) - while (*p >= ' ' || *p == TAB) // skip until CR/NL + if (itemcnt == 2 && spell_info_item(items[0])) { + while (*p >= ' ' || *p == TAB) { // skip until CR/NL ++p; - else - while (*p > ' ') // skip until white space or CR/NL + } + } else { + while (*p > ' ') { // skip until white space or CR/NL ++p; - if (*p == NUL) + } + } + if (*p == NUL) { break; + } *p++ = NUL; } @@ -2067,21 +2129,23 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) aff->af_enc = enc_canonize(items[1]); if (!spin->si_ascii && convert_setup(&spin->si_conv, aff->af_enc, - p_enc) == FAIL) + p_enc) == FAIL) { smsg(_("Conversion in %s not supported: from %s to %s"), fname, aff->af_enc, p_enc); + } spin->si_conv.vc_fail = true; } else if (is_aff_rule(items, itemcnt, "FLAG", 2) && aff->af_flagtype == AFT_CHAR) { - if (STRCMP(items[1], "long") == 0) + if (STRCMP(items[1], "long") == 0) { aff->af_flagtype = AFT_LONG; - else if (STRCMP(items[1], "num") == 0) + } else if (STRCMP(items[1], "num") == 0) { aff->af_flagtype = AFT_NUM; - else if (STRCMP(items[1], "caplong") == 0) + } else if (STRCMP(items[1], "caplong") == 0) { aff->af_flagtype = AFT_CAPLONG; - else + } else { smsg(_("Invalid value for FLAG in %s line %d: %s"), fname, lnum, items[1]); + } if (aff->af_rare != 0 || aff->af_keepcase != 0 || aff->af_bad != 0 @@ -2092,10 +2156,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) || aff->af_nosuggest != 0 || compflags != NULL || aff->af_suff.ht_used > 0 - || aff->af_pref.ht_used > 0) + || aff->af_pref.ht_used > 0) { smsg(_("FLAG after using flags in %s line %d: %s"), fname, lnum, items[1]); - } else if (spell_info_item(items[0]) && itemcnt > 1) { + } + } else if (spell_info_item(items[0]) && itemcnt > 1) { p = getroom(spin, (spin->si_info == NULL ? 0 : STRLEN(spin->si_info)) + STRLEN(items[0]) @@ -2111,7 +2176,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) } else if (is_aff_rule(items, itemcnt, "MIDWORD", 2) && midword == NULL) { midword = getroom_save(spin, items[1]); - } else if (is_aff_rule(items, itemcnt, "TRY", 2)) { + } else if (is_aff_rule(items, itemcnt, "TRY", 2)) { // ignored, we look in the tree for what chars may appear } // TODO: remove "RAR" later @@ -2119,54 +2184,56 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) || is_aff_rule(items, itemcnt, "RARE", 2)) && aff->af_rare == 0) { aff->af_rare = affitem2flag(aff->af_flagtype, items[1], - fname, lnum); + fname, lnum); } // TODO: remove "KEP" later else if ((is_aff_rule(items, itemcnt, "KEP", 2) || is_aff_rule(items, itemcnt, "KEEPCASE", 2)) && aff->af_keepcase == 0) { aff->af_keepcase = affitem2flag(aff->af_flagtype, items[1], - fname, lnum); + fname, lnum); } else if ((is_aff_rule(items, itemcnt, "BAD", 2) || is_aff_rule(items, itemcnt, "FORBIDDENWORD", 2)) && aff->af_bad == 0) { aff->af_bad = affitem2flag(aff->af_flagtype, items[1], - fname, lnum); + fname, lnum); } else if (is_aff_rule(items, itemcnt, "NEEDAFFIX", 2) && aff->af_needaffix == 0) { aff->af_needaffix = affitem2flag(aff->af_flagtype, items[1], - fname, lnum); + fname, lnum); } else if (is_aff_rule(items, itemcnt, "CIRCUMFIX", 2) && aff->af_circumfix == 0) { aff->af_circumfix = affitem2flag(aff->af_flagtype, items[1], - fname, lnum); + fname, lnum); } else if (is_aff_rule(items, itemcnt, "NOSUGGEST", 2) && aff->af_nosuggest == 0) { aff->af_nosuggest = affitem2flag(aff->af_flagtype, items[1], - fname, lnum); + fname, lnum); } else if ((is_aff_rule(items, itemcnt, "NEEDCOMPOUND", 2) || is_aff_rule(items, itemcnt, "ONLYINCOMPOUND", 2)) && aff->af_needcomp == 0) { aff->af_needcomp = affitem2flag(aff->af_flagtype, items[1], - fname, lnum); + fname, lnum); } else if (is_aff_rule(items, itemcnt, "COMPOUNDROOT", 2) && aff->af_comproot == 0) { aff->af_comproot = affitem2flag(aff->af_flagtype, items[1], - fname, lnum); + fname, lnum); } else if (is_aff_rule(items, itemcnt, "COMPOUNDFORBIDFLAG", 2) && aff->af_compforbid == 0) { aff->af_compforbid = affitem2flag(aff->af_flagtype, items[1], - fname, lnum); - if (aff->af_pref.ht_used > 0) + fname, lnum); + if (aff->af_pref.ht_used > 0) { smsg(_("Defining COMPOUNDFORBIDFLAG after PFX item may give wrong results in %s line %d"), fname, lnum); + } } else if (is_aff_rule(items, itemcnt, "COMPOUNDPERMITFLAG", 2) && aff->af_comppermit == 0) { aff->af_comppermit = affitem2flag(aff->af_flagtype, items[1], - fname, lnum); - if (aff->af_pref.ht_used > 0) + fname, lnum); + if (aff->af_pref.ht_used > 0) { smsg(_("Defining COMPOUNDPERMITFLAG after PFX item may give wrong results in %s line %d"), fname, lnum); + } } else if (is_aff_rule(items, itemcnt, "COMPOUNDFLAG", 2) && compflags == NULL) { // Turn flag "c" into COMPOUNDRULE compatible string "c+", @@ -2175,20 +2242,22 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) STRCPY(p, items[1]); STRCAT(p, "+"); compflags = p; - } else if (is_aff_rule(items, itemcnt, "COMPOUNDRULES", 2)) { + } else if (is_aff_rule(items, itemcnt, "COMPOUNDRULES", 2)) { // We don't use the count, but do check that it's a number and // not COMPOUNDRULE mistyped. - if (atoi((char *)items[1]) == 0) + if (atoi((char *)items[1]) == 0) { smsg(_("Wrong COMPOUNDRULES value in %s line %d: %s"), fname, lnum, items[1]); - } else if (is_aff_rule(items, itemcnt, "COMPOUNDRULE", 2)) { + } + } else if (is_aff_rule(items, itemcnt, "COMPOUNDRULE", 2)) { // Don't use the first rule if it is a number. if (compflags != NULL || *skipdigits(items[1]) != NUL) { // Concatenate this string to previously defined ones, // using a slash to separate them. l = (int)STRLEN(items[1]) + 1; - if (compflags != NULL) + if (compflags != NULL) { l += (int)STRLEN(compflags) + 1; + } p = getroom(spin, l, false); if (compflags != NULL) { STRCPY(p, compflags); @@ -2200,43 +2269,49 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) } else if (is_aff_rule(items, itemcnt, "COMPOUNDWORDMAX", 2) && compmax == 0) { compmax = atoi((char *)items[1]); - if (compmax == 0) + if (compmax == 0) { smsg(_("Wrong COMPOUNDWORDMAX value in %s line %d: %s"), fname, lnum, items[1]); + } } else if (is_aff_rule(items, itemcnt, "COMPOUNDMIN", 2) && compminlen == 0) { compminlen = atoi((char *)items[1]); - if (compminlen == 0) + if (compminlen == 0) { smsg(_("Wrong COMPOUNDMIN value in %s line %d: %s"), fname, lnum, items[1]); + } } else if (is_aff_rule(items, itemcnt, "COMPOUNDSYLMAX", 2) && compsylmax == 0) { compsylmax = atoi((char *)items[1]); - if (compsylmax == 0) + if (compsylmax == 0) { smsg(_("Wrong COMPOUNDSYLMAX value in %s line %d: %s"), fname, lnum, items[1]); - } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDDUP", 1)) { + } + } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDDUP", 1)) { compoptions |= COMP_CHECKDUP; - } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDREP", 1)) { + } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDREP", 1)) { compoptions |= COMP_CHECKREP; - } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDCASE", 1)) { + } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDCASE", 1)) { compoptions |= COMP_CHECKCASE; - } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDTRIPLE", 1)) { + } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDTRIPLE", 1)) { compoptions |= COMP_CHECKTRIPLE; - } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDPATTERN", 2)) { - if (atoi((char *)items[1]) == 0) + } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDPATTERN", 2)) { + if (atoi((char *)items[1]) == 0) { smsg(_("Wrong CHECKCOMPOUNDPATTERN value in %s line %d: %s"), fname, lnum, items[1]); - } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDPATTERN", 3)) { - garray_T *gap = &spin->si_comppat; + } + } else if (is_aff_rule(items, itemcnt, "CHECKCOMPOUNDPATTERN", 3)) { + garray_T *gap = &spin->si_comppat; int i; // Only add the couple if it isn't already there. - for (i = 0; i < gap->ga_len - 1; i += 2) + for (i = 0; i < gap->ga_len - 1; i += 2) { if (STRCMP(((char_u **)(gap->ga_data))[i], items[1]) == 0 && STRCMP(((char_u **)(gap->ga_data))[i + 1], - items[2]) == 0) + items[2]) == 0) { break; + } + } if (i >= gap->ga_len) { ga_grow(gap, 2); ((char_u **)(gap->ga_data))[gap->ga_len++] @@ -2247,15 +2322,15 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) } else if (is_aff_rule(items, itemcnt, "SYLLABLE", 2) && syllable == NULL) { syllable = getroom_save(spin, items[1]); - } else if (is_aff_rule(items, itemcnt, "NOBREAK", 1)) { + } else if (is_aff_rule(items, itemcnt, "NOBREAK", 1)) { spin->si_nobreak = true; - } else if (is_aff_rule(items, itemcnt, "NOSPLITSUGS", 1)) { + } else if (is_aff_rule(items, itemcnt, "NOSPLITSUGS", 1)) { spin->si_nosplitsugs = true; } else if (is_aff_rule(items, itemcnt, "NOCOMPOUNDSUGS", 1)) { spin->si_nocompoundsugs = true; - } else if (is_aff_rule(items, itemcnt, "NOSUGFILE", 1)) { + } else if (is_aff_rule(items, itemcnt, "NOSUGFILE", 1)) { spin->si_nosugfile = true; - } else if (is_aff_rule(items, itemcnt, "PFXPOSTPONE", 1)) { + } else if (is_aff_rule(items, itemcnt, "PFXPOSTPONE", 1)) { aff->af_pfxpostpone = true; } else if (is_aff_rule(items, itemcnt, "IGNOREEXTRA", 1)) { aff->af_ignoreextra = true; @@ -2266,10 +2341,11 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) int lasti = 4; char_u key[AH_KEY_LEN]; - if (*items[0] == 'P') + if (*items[0] == 'P') { tp = &aff->af_pref; - else + } else { tp = &aff->af_suff; + } // Myspell allows the same affix name to be used multiple // times. The affix files that do this have an undocumented @@ -2279,12 +2355,14 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) hi = hash_find(tp, key); if (!HASHITEM_EMPTY(hi)) { cur_aff = HI2AH(hi); - if (cur_aff->ah_combine != (*items[2] == 'Y')) + if (cur_aff->ah_combine != (*items[2] == 'Y')) { smsg(_("Different combining flag in continued affix block in %s line %d: %s"), fname, lnum, items[1]); - if (!cur_aff->ah_follows) + } + if (!cur_aff->ah_follows) { smsg(_("Duplicate affix in %s line %d: %s"), fname, lnum, items[1]); + } } else { // New affix letter. cur_aff = getroom(spin, sizeof(*cur_aff), true); @@ -2317,20 +2395,23 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) if (itemcnt > lasti && STRCMP(items[lasti], "S") == 0) { ++lasti; cur_aff->ah_follows = true; - } else + } else { cur_aff->ah_follows = false; + } // Myspell allows extra text after the item, but that might // mean mistakes go unnoticed. Require a comment-starter, // unless IGNOREEXTRA is used. Hunspell uses a "-" item. if (itemcnt > lasti && !aff->af_ignoreextra - && *items[lasti] != '#') + && *items[lasti] != '#') { smsg(_(e_afftrailing), fname, lnum, items[lasti]); + } - if (STRCMP(items[2], "Y") != 0 && STRCMP(items[2], "N") != 0) + if (STRCMP(items[2], "Y") != 0 && STRCMP(items[2], "N") != 0) { smsg(_("Expected Y or N in %s line %d: %s"), - fname, lnum, items[2]); + fname, lnum, items[2]); + } if (*items[0] == 'P' && aff->af_pfxpostpone) { if (cur_aff->ah_newID == 0) { @@ -2343,9 +2424,10 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) // postponed. We know that only after handling all // the items. did_postpone_prefix = false; - } else + } else { // Did use the ID in a previous block. did_postpone_prefix = true; + } } aff_todo = atoi((char *)items[3]); @@ -2354,7 +2436,7 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) && aff_todo > 0 && STRCMP(cur_aff->ah_key, items[1]) == 0 && itemcnt >= 5) { - affentry_T *aff_entry; + affentry_T *aff_entry; bool upper = false; int lasti = 5; @@ -2363,15 +2445,17 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) // Hunspell uses a "-" item. if (itemcnt > lasti && *items[lasti] != '#' && (STRCMP(items[lasti], "-") != 0 - || itemcnt != lasti + 1)) + || itemcnt != lasti + 1)) { smsg(_(e_afftrailing), fname, lnum, items[lasti]); + } // New item for an affix letter. aff_todo--; aff_entry = getroom(spin, sizeof(*aff_entry), true); - if (STRCMP(items[2], "0") != 0) + if (STRCMP(items[2], "0") != 0) { aff_entry->ae_chop = getroom_save(spin, items[2]); + } if (STRCMP(items[3], "0") != 0) { aff_entry->ae_add = getroom_save(spin, items[3]); @@ -2394,15 +2478,17 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) char_u buf[MAXLINELEN]; aff_entry->ae_cond = getroom_save(spin, items[4]); - if (*items[0] == 'P') + if (*items[0] == 'P') { sprintf((char *)buf, "^%s", items[4]); - else + } else { sprintf((char *)buf, "%s$", items[4]); + } aff_entry->ae_prog = vim_regcomp(buf, - RE_MAGIC + RE_STRING + RE_STRICT); - if (aff_entry->ae_prog == NULL) + RE_MAGIC + RE_STRING + RE_STRICT); + if (aff_entry->ae_prog == NULL) { smsg(_("Broken condition in %s line %d: %s"), fname, lnum, items[4]); + } } // For postponed prefixes we need an entry in si_prefcond @@ -2418,9 +2504,8 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) // be empty or start with the same letter. if (aff_entry->ae_chop != NULL && aff_entry->ae_add != NULL - && aff_entry->ae_chop[(*mb_ptr2len)( - aff_entry->ae_chop)] == NUL - ) { + && aff_entry->ae_chop[(*mb_ptr2len)(aff_entry->ae_chop)] == + NUL) { int c, c_up; c = PTR2CHAR(aff_entry->ae_chop); @@ -2445,10 +2530,9 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) aff_entry->ae_cond = getroom_save(spin, buf); if (aff_entry->ae_cond != NULL) { sprintf((char *)buf, "^%s", - aff_entry->ae_cond); + aff_entry->ae_cond); vim_regfree(aff_entry->ae_prog); - aff_entry->ae_prog = vim_regcomp( - buf, RE_MAGIC + RE_STRING); + aff_entry->ae_prog = vim_regcomp(buf, RE_MAGIC + RE_STRING); } } } @@ -2457,43 +2541,49 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) if (aff_entry->ae_chop == NULL) { int idx; - char_u **pp; + char_u **pp; int n; // Find a previously used condition. for (idx = spin->si_prefcond.ga_len - 1; idx >= 0; --idx) { p = ((char_u **)spin->si_prefcond.ga_data)[idx]; - if (str_equal(p, aff_entry->ae_cond)) + if (str_equal(p, aff_entry->ae_cond)) { break; + } } if (idx < 0) { // Not found, add a new condition. idx = spin->si_prefcond.ga_len; pp = GA_APPEND_VIA_PTR(char_u *, &spin->si_prefcond); *pp = (aff_entry->ae_cond == NULL) ? - NULL : getroom_save(spin, aff_entry->ae_cond); + NULL : getroom_save(spin, aff_entry->ae_cond); } // Add the prefix to the prefix tree. - if (aff_entry->ae_add == NULL) + if (aff_entry->ae_add == NULL) { p = (char_u *)""; - else + } else { p = aff_entry->ae_add; + } // PFX_FLAGS is a negative number, so that // tree_add_word() knows this is the prefix tree. n = PFX_FLAGS; - if (!cur_aff->ah_combine) + if (!cur_aff->ah_combine) { n |= WFP_NC; - if (upper) + } + if (upper) { n |= WFP_UP; - if (aff_entry->ae_comppermit) + } + if (aff_entry->ae_comppermit) { n |= WFP_COMPPERMIT; - if (aff_entry->ae_compforbid) + } + if (aff_entry->ae_compforbid) { n |= WFP_COMPFORBID; + } tree_add_word(spin, p, spin->si_prefroot, n, - idx, cur_aff->ah_newID); + idx, cur_aff->ah_newID); did_postpone_prefix = true; } @@ -2504,26 +2594,28 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) } } } - } else if (is_aff_rule(items, itemcnt, "FOL", 2) && fol == NULL) { + } else if (is_aff_rule(items, itemcnt, "FOL", 2) && fol == NULL) { fol = vim_strsave(items[1]); - } else if (is_aff_rule(items, itemcnt, "LOW", 2) && low == NULL) { + } else if (is_aff_rule(items, itemcnt, "LOW", 2) && low == NULL) { low = vim_strsave(items[1]); - } else if (is_aff_rule(items, itemcnt, "UPP", 2) && upp == NULL) { + } else if (is_aff_rule(items, itemcnt, "UPP", 2) && upp == NULL) { upp = vim_strsave(items[1]); } else if (is_aff_rule(items, itemcnt, "REP", 2) || is_aff_rule(items, itemcnt, "REPSAL", 2)) { /* Ignore REP/REPSAL count */; - if (!isdigit(*items[1])) + if (!isdigit(*items[1])) { smsg(_("Expected REP(SAL) count in %s line %d"), fname, lnum); + } } else if ((STRCMP(items[0], "REP") == 0 || STRCMP(items[0], "REPSAL") == 0) && itemcnt >= 3) { // REP/REPSAL item // Myspell ignores extra arguments, we require it starts with // # to detect mistakes. - if (itemcnt > 3 && items[3][0] != '#') + if (itemcnt > 3 && items[3][0] != '#') { smsg(_(e_afftrailing), fname, lnum, items[3]); + } if (items[0][3] == 'S' ? do_repsal : do_rep) { // Replace underscore with space (can't include a space // directly). @@ -2541,15 +2633,16 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) ? &spin->si_repsal : &spin->si_rep, items[1], items[2]); } - } else if (is_aff_rule(items, itemcnt, "MAP", 2)) { + } else if (is_aff_rule(items, itemcnt, "MAP", 2)) { // MAP item or count if (!found_map) { // First line contains the count. found_map = true; - if (!isdigit(*items[1])) + if (!isdigit(*items[1])) { smsg(_("Expected MAP count in %s line %d"), fname, lnum); - } else if (do_mapline) { + } + } else if (do_mapline) { int c; // Check that every character appears only once. @@ -2575,17 +2668,18 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) if (do_sal) { // SAL item (sounds-a-like) // Either one of the known keys or a from-to pair. - if (STRCMP(items[1], "followup") == 0) + if (STRCMP(items[1], "followup") == 0) { spin->si_followup = sal_to_bool(items[2]); - else if (STRCMP(items[1], "collapse_result") == 0) + } else if (STRCMP(items[1], "collapse_result") == 0) { spin->si_collapse = sal_to_bool(items[2]); - else if (STRCMP(items[1], "remove_accents") == 0) + } else if (STRCMP(items[1], "remove_accents") == 0) { spin->si_rem_accents = sal_to_bool(items[2]); - else + } else { // when "to" is "_" it means empty add_fromto(spin, &spin->si_sal, items[1], - STRCMP(items[2], "_") == 0 ? (char_u *)"" - : items[2]); + STRCMP(items[2], "_") == 0 ? (char_u *)"" + : items[2]); + } } } else if (is_aff_rule(items, itemcnt, "SOFOFROM", 2) && sofofrom == NULL) { @@ -2593,19 +2687,20 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) } else if (is_aff_rule(items, itemcnt, "SOFOTO", 2) && sofoto == NULL) { sofoto = getroom_save(spin, items[1]); - } else if (STRCMP(items[0], "COMMON") == 0) { + } else if (STRCMP(items[0], "COMMON") == 0) { int i; for (i = 1; i < itemcnt; ++i) { if (HASHITEM_EMPTY(hash_find(&spin->si_commonwords, - items[i]))) { + items[i]))) { p = vim_strsave(items[i]); hash_add(&spin->si_commonwords, p); } } - } else + } else { smsg(_("Unrecognized or duplicate item in %s line %d: %s"), fname, lnum, items[0]); + } } } @@ -2646,17 +2741,19 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) spin->si_compoptions |= compoptions; } - if (compflags != NULL) + if (compflags != NULL) { process_compflags(spin, aff, compflags); + } // Check that we didn't use too many renumbered flags. if (spin->si_newcompID < spin->si_newprefID) { - if (spin->si_newcompID == 127 || spin->si_newcompID == 255) + if (spin->si_newcompID == 127 || spin->si_newcompID == 255) { MSG(_("Too many postponed prefixes")); - else if (spin->si_newprefID == 0 || spin->si_newprefID == 127) + } else if (spin->si_newprefID == 0 || spin->si_newprefID == 127) { MSG(_("Too many compound flags")); - else + } else { MSG(_("Too many postponed prefixes and/or compound flags")); + } } if (syllable != NULL) { @@ -2665,12 +2762,12 @@ static afffile_T *spell_read_aff(spellinfo_T *spin, char_u *fname) } if (sofofrom != NULL || sofoto != NULL) { - if (sofofrom == NULL || sofoto == NULL) + if (sofofrom == NULL || sofoto == NULL) { smsg(_("Missing SOFO%s line in %s"), sofofrom == NULL ? "FROM" : "TO", fname); - else if (!GA_EMPTY(&spin->si_sal)) + } else if (!GA_EMPTY(&spin->si_sal)) { smsg(_("Both SAL and SOFO lines in %s"), fname); - else { + } else { aff_check_string(spin->si_sofofr, sofofrom, "SOFOFROM"); aff_check_string(spin->si_sofoto, sofoto, "SOFOTO"); spin->si_sofofr = sofofrom; @@ -2701,8 +2798,8 @@ static bool is_aff_rule(char_u **items, int itemcnt, char *rulename, int mincoun // ae_flags to ae_comppermit and ae_compforbid. static void aff_process_flags(afffile_T *affile, affentry_T *entry) { - char_u *p; - char_u *prevp; + char_u *p; + char_u *prevp; unsigned flag; if (entry->ae_flags != NULL @@ -2713,16 +2810,19 @@ static void aff_process_flags(afffile_T *affile, affentry_T *entry) if (flag == affile->af_comppermit || flag == affile->af_compforbid) { STRMOVE(prevp, p); p = prevp; - if (flag == affile->af_comppermit) + if (flag == affile->af_comppermit) { entry->ae_comppermit = true; - else + } else { entry->ae_compforbid = true; + } } - if (affile->af_flagtype == AFT_NUM && *p == ',') + if (affile->af_flagtype == AFT_NUM && *p == ',') { ++p; + } } - if (*entry->ae_flags == NUL) + if (*entry->ae_flags == NUL) { entry->ae_flags = NULL; // nothing left + } } } @@ -2742,16 +2842,17 @@ static bool spell_info_item(char_u *s) static unsigned affitem2flag(int flagtype, char_u *item, char_u *fname, int lnum) { unsigned res; - char_u *p = item; + char_u *p = item; res = get_affitem(flagtype, &p); if (res == 0) { - if (flagtype == AFT_NUM) + if (flagtype == AFT_NUM) { smsg(_("Flag is not a number in %s line %d: %s"), fname, lnum, item); - else + } else { smsg(_("Illegal flag in %s line %d: %s"), fname, lnum, item); + } } if (*p != NUL) { smsg(_(e_affname), fname, lnum, item); @@ -2781,8 +2882,9 @@ static unsigned get_affitem(int flagtype, char_u **pp) res = mb_ptr2char_adv((const char_u **)pp); if (flagtype == AFT_LONG || (flagtype == AFT_CAPLONG && res >= 'A' && res <= 'Z')) { - if (**pp == NUL) + if (**pp == NUL) { return 0; + } res = mb_ptr2char_adv((const char_u **)pp) + (res << 16); } } @@ -2795,22 +2897,23 @@ static unsigned get_affitem(int flagtype, char_u **pp) // they fit in one byte. static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compflags) { - char_u *p; - char_u *prevp; + char_u *p; + char_u *prevp; unsigned flag; - compitem_T *ci; + compitem_T *ci; int id; int len; - char_u *tp; + char_u *tp; char_u key[AH_KEY_LEN]; - hashitem_T *hi; + hashitem_T *hi; // Make room for the old and the new compflags, concatenated with a / in // between. Processing it makes it shorter, but we don't know by how // much, thus allocate the maximum. len = (int)STRLEN(compflags) + 1; - if (spin->si_compflags != NULL) + if (spin->si_compflags != NULL) { len += (int)STRLEN(spin->si_compflags) + 1; + } p = getroom(spin, len, false); if (spin->si_compflags != NULL) { STRCPY(p, spin->si_compflags); @@ -2820,10 +2923,10 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla tp = p + STRLEN(p); for (p = compflags; *p != NUL; ) { - if (vim_strchr((char_u *)"/?*+[]", *p) != NULL) + if (vim_strchr((char_u *)"/?*+[]", *p) != NULL) { // Copy non-flag characters directly. *tp++ = *p++; - else { + } else { // First get the flag number, also checks validity. prevp = p; flag = get_affitem(aff->af_flagtype, &p); @@ -2849,8 +2952,9 @@ static void process_compflags(spellinfo_T *spin, afffile_T *aff, char_u *compfla } *tp++ = id; } - if (aff->af_flagtype == AFT_NUM && *p == ',') + if (aff->af_flagtype == AFT_NUM && *p == ',') { ++p; + } } } @@ -2872,7 +2976,7 @@ static void check_renumber(spellinfo_T *spin) // Returns true if flag "flag" appears in affix list "afflist". static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag) { - char_u *p; + char_u *p; unsigned n; switch (flagtype) { @@ -2916,25 +3020,28 @@ static bool flag_in_afflist(int flagtype, char_u *afflist, unsigned flag) // Give a warning when "spinval" and "affval" numbers are set and not the same. static void aff_check_number(int spinval, int affval, char *name) { - if (spinval != 0 && spinval != affval) + if (spinval != 0 && spinval != affval) { smsg(_("%s value differs from what is used in another .aff file"), name); + } } // Give a warning when "spinval" and "affval" strings are set and not the same. static void aff_check_string(char_u *spinval, char_u *affval, char *name) { - if (spinval != NULL && STRCMP(spinval, affval) != 0) + if (spinval != NULL && STRCMP(spinval, affval) != 0) { smsg(_("%s value differs from what is used in another .aff file"), name); + } } // Returns true if strings "s1" and "s2" are equal. Also consider both being // NULL as equal. static bool str_equal(char_u *s1, char_u *s2) { - if (s1 == NULL || s2 == NULL) + if (s1 == NULL || s2 == NULL) { return s1 == s2; + } return STRCMP(s1, s2) == 0; } @@ -2960,11 +3067,11 @@ static bool sal_to_bool(char_u *s) // Free the structure filled by spell_read_aff(). static void spell_free_aff(afffile_T *aff) { - hashtab_T *ht; - hashitem_T *hi; + hashtab_T *ht; + hashitem_T *hi; int todo; affheader_T *ah; - affentry_T *ae; + affentry_T *ae; xfree(aff->af_enc); @@ -2975,12 +3082,14 @@ static void spell_free_aff(afffile_T *aff) if (!HASHITEM_EMPTY(hi)) { --todo; ah = HI2AH(hi); - for (ae = ah->ah_first; ae != NULL; ae = ae->ae_next) + for (ae = ah->ah_first; ae != NULL; ae = ae->ae_next) { vim_regfree(ae->ae_prog); + } } } - if (ht == &aff->af_suff) + if (ht == &aff->af_suff) { break; + } } hash_clear(&aff->af_pref); @@ -2994,18 +3103,18 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) { hashtab_T ht; char_u line[MAXLINELEN]; - char_u *p; - char_u *afflist; + char_u *p; + char_u *afflist; char_u store_afflist[MAXWLEN]; int pfxlen; bool need_affix; - char_u *dw; - char_u *pc; - char_u *w; + char_u *dw; + char_u *pc; + char_u *w; int l; hash_T hash; - hashitem_T *hi; - FILE *fd; + hashitem_T *hi; + FILE *fd; int lnum = 1; int non_ascii = 0; int retval = OK; @@ -3042,16 +3151,18 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) while (!vim_fgets(line, MAXLINELEN, fd) && !got_int) { line_breakcheck(); ++lnum; - if (line[0] == '#' || line[0] == '/') + if (line[0] == '#' || line[0] == '/') { continue; // comment line - + } // Remove CR, LF and white space from the end. White space halfway through // the word is kept to allow multi-word terms like "et al.". l = (int)STRLEN(line); - while (l > 0 && line[l - 1] <= ' ') + while (l > 0 && line[l - 1] <= ' ') { --l; - if (l == 0) + } + if (l == 0) { continue; // empty line + } line[l] = NUL; // Convert from "SET" to 'encoding' when needed. @@ -3117,15 +3228,17 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) hash = hash_hash(dw); hi = hash_lookup(&ht, (const char *)dw, STRLEN(dw), hash); if (!HASHITEM_EMPTY(hi)) { - if (p_verbose > 0) + if (p_verbose > 0) { smsg(_("Duplicate word in %s line %d: %s"), fname, lnum, dw); - else if (duplicate == 0) + } else if (duplicate == 0) { smsg(_("First duplicate word in %s line %d: %s"), fname, lnum, dw); + } ++duplicate; - } else + } else { hash_add_item(&ht, hi, dw, hash); + } flags = 0; store_afflist[0] = NUL; @@ -3135,48 +3248,57 @@ static int spell_read_dic(spellinfo_T *spin, char_u *fname, afffile_T *affile) // Extract flags from the affix list. flags |= get_affix_flags(affile, afflist); - if (affile->af_needaffix != 0 && flag_in_afflist( - affile->af_flagtype, afflist, affile->af_needaffix)) + if (affile->af_needaffix != 0 && + flag_in_afflist(affile->af_flagtype, afflist, + affile->af_needaffix)) { need_affix = true; + } - if (affile->af_pfxpostpone) + if (affile->af_pfxpostpone) { // Need to store the list of prefix IDs with the word. pfxlen = get_pfxlist(affile, afflist, store_afflist); + } - if (spin->si_compflags != NULL) + if (spin->si_compflags != NULL) { // Need to store the list of compound flags with the word. // Concatenate them to the list of prefix IDs. get_compflags(affile, afflist, store_afflist + pfxlen); + } } // Add the word to the word tree(s). if (store_word(spin, dw, flags, spin->si_region, - store_afflist, need_affix) == FAIL) + store_afflist, need_affix) == FAIL) { retval = FAIL; + } if (afflist != NULL) { // Find all matching suffixes and add the resulting words. // Additionally do matching prefixes that combine. if (store_aff_word(spin, dw, afflist, affile, - &affile->af_suff, &affile->af_pref, - CONDIT_SUF, flags, store_afflist, pfxlen) == FAIL) + &affile->af_suff, &affile->af_pref, + CONDIT_SUF, flags, store_afflist, pfxlen) == FAIL) { retval = FAIL; + } // Find all matching prefixes and add the resulting words. if (store_aff_word(spin, dw, afflist, affile, - &affile->af_pref, NULL, - CONDIT_SUF, flags, store_afflist, pfxlen) == FAIL) + &affile->af_pref, NULL, + CONDIT_SUF, flags, store_afflist, pfxlen) == FAIL) { retval = FAIL; + } } xfree(pc); } - if (duplicate > 0) + if (duplicate > 0) { smsg(_("%d duplicate word(s) in %s"), duplicate, fname); - if (spin->si_ascii && non_ascii > 0) + } + if (spin->si_ascii && non_ascii > 0) { smsg(_("Ignored %d word(s) with non-ASCII characters in %s"), non_ascii, fname); + } hash_clear(&ht); fclose(fd); @@ -3189,24 +3311,34 @@ static int get_affix_flags(afffile_T *affile, char_u *afflist) { int flags = 0; - if (affile->af_keepcase != 0 && flag_in_afflist( - affile->af_flagtype, afflist, affile->af_keepcase)) + if (affile->af_keepcase != 0 && + flag_in_afflist(affile->af_flagtype, afflist, + affile->af_keepcase)) { flags |= WF_KEEPCAP | WF_FIXCAP; - if (affile->af_rare != 0 && flag_in_afflist( - affile->af_flagtype, afflist, affile->af_rare)) + } + if (affile->af_rare != 0 && + flag_in_afflist(affile->af_flagtype, afflist, affile->af_rare)) { flags |= WF_RARE; - if (affile->af_bad != 0 && flag_in_afflist( - affile->af_flagtype, afflist, affile->af_bad)) + } + if (affile->af_bad != 0 && + flag_in_afflist(affile->af_flagtype, afflist, affile->af_bad)) { flags |= WF_BANNED; - if (affile->af_needcomp != 0 && flag_in_afflist( - affile->af_flagtype, afflist, affile->af_needcomp)) + } + if (affile->af_needcomp != 0 && + flag_in_afflist(affile->af_flagtype, afflist, + affile->af_needcomp)) { flags |= WF_NEEDCOMP; - if (affile->af_comproot != 0 && flag_in_afflist( - affile->af_flagtype, afflist, affile->af_comproot)) + } + if (affile->af_comproot != 0 && + flag_in_afflist(affile->af_flagtype, afflist, + affile->af_comproot)) { flags |= WF_COMPROOT; - if (affile->af_nosuggest != 0 && flag_in_afflist( - affile->af_flagtype, afflist, affile->af_nosuggest)) + } + if (affile->af_nosuggest != 0 && + flag_in_afflist(affile->af_flagtype, afflist, + affile->af_nosuggest)) { flags |= WF_NOSUGGEST; + } return flags; } @@ -3216,12 +3348,12 @@ static int get_affix_flags(afffile_T *affile, char_u *afflist) // and return the number of affixes. static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist) { - char_u *p; - char_u *prevp; + char_u *p; + char_u *prevp; int cnt = 0; int id; char_u key[AH_KEY_LEN]; - hashitem_T *hi; + hashitem_T *hi; for (p = afflist; *p != NUL; ) { prevp = p; @@ -3232,12 +3364,14 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist hi = hash_find(&affile->af_pref, key); if (!HASHITEM_EMPTY(hi)) { id = HI2AH(hi)->ah_newID; - if (id != 0) + if (id != 0) { store_afflist[cnt++] = id; + } } } - if (affile->af_flagtype == AFT_NUM && *p == ',') + if (affile->af_flagtype == AFT_NUM && *p == ',') { ++p; + } } store_afflist[cnt] = NUL; @@ -3249,11 +3383,11 @@ static int get_pfxlist(afffile_T *affile, char_u *afflist, char_u *store_afflist // Puts the flags in "store_afflist[]". static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_afflist) { - char_u *p; - char_u *prevp; + char_u *p; + char_u *prevp; int cnt = 0; char_u key[AH_KEY_LEN]; - hashitem_T *hi; + hashitem_T *hi; for (p = afflist; *p != NUL; ) { prevp = p; @@ -3261,48 +3395,47 @@ static void get_compflags(afffile_T *affile, char_u *afflist, char_u *store_affl // A flag is a compound flag if it appears in "af_comp". STRLCPY(key, prevp, p - prevp + 1); hi = hash_find(&affile->af_comp, key); - if (!HASHITEM_EMPTY(hi)) + if (!HASHITEM_EMPTY(hi)) { store_afflist[cnt++] = HI2CI(hi)->ci_newID; + } } - if (affile->af_flagtype == AFT_NUM && *p == ',') + if (affile->af_flagtype == AFT_NUM && *p == ',') { ++p; + } } store_afflist[cnt] = NUL; } -// Apply affixes to a word and store the resulting words. -// "ht" is the hashtable with affentry_T that need to be applied, either -// prefixes or suffixes. -// "xht", when not NULL, is the prefix hashtable, to be used additionally on -// the resulting words for combining affixes. -// -// Returns FAIL when out of memory. -static int -store_aff_word ( - spellinfo_T *spin, // spell info - char_u *word, // basic word start - char_u *afflist, // list of names of supported affixes - afffile_T *affile, - hashtab_T *ht, - hashtab_T *xht, - int condit, // CONDIT_SUF et al. - int flags, // flags for the word - char_u *pfxlist, // list of prefix IDs - int pfxlen // nr of flags in "pfxlist" for prefixes, rest - // is compound flags -) +/// Apply affixes to a word and store the resulting words. +/// "ht" is the hashtable with affentry_T that need to be applied, either +/// prefixes or suffixes. +/// "xht", when not NULL, is the prefix hashtable, to be used additionally on +/// the resulting words for combining affixes. +/// +/// @param spin spell info +/// @param word basic word start +/// @param afflist list of names of supported affixes +/// @param condit CONDIT_SUF et al. +/// @param flags flags for the word +/// @param pfxlist list of prefix IDs +/// @param pfxlen nr of flags in "pfxlist" for prefixes, rest is compound flags +/// +/// @return FAIL when out of memory. +static int store_aff_word(spellinfo_T *spin, char_u *word, char_u *afflist, afffile_T *affile, + hashtab_T *ht, hashtab_T *xht, int condit, int flags, char_u *pfxlist, + int pfxlen) { int todo; - hashitem_T *hi; + hashitem_T *hi; affheader_T *ah; - affentry_T *ae; + affentry_T *ae; char_u newword[MAXWLEN]; int retval = OK; int i, j; - char_u *p; + char_u *p; int use_flags; - char_u *use_pfxlist; + char_u *use_pfxlist; int use_pfxlen; bool need_affix; char_u store_afflist[MAXWLEN]; @@ -3320,7 +3453,7 @@ store_aff_word ( // supports this affix. if (((condit & CONDIT_COMB) == 0 || ah->ah_combine) && flag_in_afflist(affile->af_flagtype, afflist, - ah->ah_flag)) { + ah->ah_flag)) { // Loop over all affix entries with this name. for (ae = ah->ah_first; ae != NULL; ae = ae->ae_next) { // Check the condition. It's not logical to match case @@ -3344,7 +3477,7 @@ store_aff_word ( == ((condit & CONDIT_AFF) == 0 || ae->ae_flags == NULL || !flag_in_afflist(affile->af_flagtype, - ae->ae_flags, affile->af_circumfix)))) { + ae->ae_flags, affile->af_circumfix)))) { // Match. Remove the chop and add the affix. if (xht == NULL) { // prefix: chop/add at the start of the word @@ -3374,8 +3507,9 @@ store_aff_word ( } *p = NUL; } - if (ae->ae_add != NULL) + if (ae->ae_add != NULL) { STRCAT(newword, ae->ae_add); + } } use_flags = flags; @@ -3387,57 +3521,64 @@ store_aff_word ( // Extract flags from the affix list. use_flags |= get_affix_flags(affile, ae->ae_flags); - if (affile->af_needaffix != 0 && flag_in_afflist( - affile->af_flagtype, ae->ae_flags, - affile->af_needaffix)) + if (affile->af_needaffix != 0 && flag_in_afflist(affile->af_flagtype, ae->ae_flags, + affile->af_needaffix)) { need_affix = true; + } // When there is a CIRCUMFIX flag the other affix // must also have it and we don't add the word // with one affix. - if (affile->af_circumfix != 0 && flag_in_afflist( - affile->af_flagtype, ae->ae_flags, - affile->af_circumfix)) { + if (affile->af_circumfix != 0 && flag_in_afflist(affile->af_flagtype, ae->ae_flags, + affile->af_circumfix)) { use_condit |= CONDIT_CFIX; - if ((condit & CONDIT_CFIX) == 0) + if ((condit & CONDIT_CFIX) == 0) { need_affix = true; + } } if (affile->af_pfxpostpone || spin->si_compflags != NULL) { - if (affile->af_pfxpostpone) + if (affile->af_pfxpostpone) { // Get prefix IDS from the affix list. use_pfxlen = get_pfxlist(affile, - ae->ae_flags, store_afflist); - else + ae->ae_flags, store_afflist); + } else { use_pfxlen = 0; + } use_pfxlist = store_afflist; // Combine the prefix IDs. Avoid adding the // same ID twice. for (i = 0; i < pfxlen; ++i) { - for (j = 0; j < use_pfxlen; ++j) - if (pfxlist[i] == use_pfxlist[j]) + for (j = 0; j < use_pfxlen; ++j) { + if (pfxlist[i] == use_pfxlist[j]) { break; - if (j == use_pfxlen) + } + } + if (j == use_pfxlen) { use_pfxlist[use_pfxlen++] = pfxlist[i]; + } } - if (spin->si_compflags != NULL) + if (spin->si_compflags != NULL) { // Get compound IDS from the affix list. get_compflags(affile, ae->ae_flags, - use_pfxlist + use_pfxlen); - else + use_pfxlist + use_pfxlen); + } else { use_pfxlist[use_pfxlen] = NUL; + } // Combine the list of compound flags. // Concatenate them to the prefix IDs list. // Avoid adding the same ID twice. for (i = pfxlen; pfxlist[i] != NUL; ++i) { for (j = use_pfxlen; - use_pfxlist[j] != NUL; ++j) - if (pfxlist[i] == use_pfxlist[j]) + use_pfxlist[j] != NUL; ++j) { + if (pfxlist[i] == use_pfxlist[j]) { break; + } + } if (use_pfxlist[j] == NUL) { use_pfxlist[j++] = pfxlist[i]; use_pfxlist[j] = NUL; @@ -3462,52 +3603,58 @@ store_aff_word ( // ... don't use a prefix list if combining // affixes is not allowed. But do use the // compound flags after them. - if (!ah->ah_combine && use_pfxlist != NULL) + if (!ah->ah_combine && use_pfxlist != NULL) { use_pfxlist += use_pfxlen; + } } // When compounding is supported and there is no // "COMPOUNDPERMITFLAG" then forbid compounding on the // side where the affix is applied. if (spin->si_compflags != NULL && !ae->ae_comppermit) { - if (xht != NULL) + if (xht != NULL) { use_flags |= WF_NOCOMPAFT; - else + } else { use_flags |= WF_NOCOMPBEF; + } } // Store the modified word. if (store_word(spin, newword, use_flags, - spin->si_region, use_pfxlist, - need_affix) == FAIL) + spin->si_region, use_pfxlist, + need_affix) == FAIL) { retval = FAIL; + } // When added a prefix or a first suffix and the affix // has flags may add a(nother) suffix. RECURSIVE! - if ((condit & CONDIT_SUF) && ae->ae_flags != NULL) + if ((condit & CONDIT_SUF) && ae->ae_flags != NULL) { if (store_aff_word(spin, newword, ae->ae_flags, - affile, &affile->af_suff, xht, - use_condit & (xht == NULL + affile, &affile->af_suff, xht, + use_condit & (xht == NULL ? ~0 : ~CONDIT_SUF), - use_flags, use_pfxlist, pfxlen) == FAIL) + use_flags, use_pfxlist, pfxlen) == FAIL) { retval = FAIL; + } + } // When added a suffix and combining is allowed also // try adding a prefix additionally. Both for the // word flags and for the affix flags. RECURSIVE! if (xht != NULL && ah->ah_combine) { if (store_aff_word(spin, newword, - afflist, affile, - xht, NULL, use_condit, - use_flags, use_pfxlist, - pfxlen) == FAIL + afflist, affile, + xht, NULL, use_condit, + use_flags, use_pfxlist, + pfxlen) == FAIL || (ae->ae_flags != NULL && store_aff_word(spin, newword, - ae->ae_flags, affile, - xht, NULL, use_condit, - use_flags, use_pfxlist, - pfxlen) == FAIL)) + ae->ae_flags, affile, + xht, NULL, use_condit, + use_flags, use_pfxlist, + pfxlen) == FAIL)) { retval = FAIL; + } } } } @@ -3521,12 +3668,12 @@ store_aff_word ( // Read a file with a list of words. static int spell_read_wordfile(spellinfo_T *spin, char_u *fname) { - FILE *fd; + FILE *fd; long lnum = 0; char_u rline[MAXLINELEN]; - char_u *line; - char_u *pc = NULL; - char_u *p; + char_u *line; + char_u *pc = NULL; + char_u *p; int l; int retval = OK; bool did_word = false; @@ -3550,15 +3697,18 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname) ++lnum; // Skip comment lines. - if (*rline == '#') + if (*rline == '#') { continue; + } // Remove CR, LF and white space from the end. l = (int)STRLEN(rline); - while (l > 0 && rline[l - 1] <= ' ') + while (l > 0 && rline[l - 1] <= ' ') { --l; - if (l == 0) + } + if (l == 0) { continue; // empty or blank line + } rline[l] = NUL; // Convert from "/encoding={encoding}" to 'encoding' when needed. @@ -3586,16 +3736,17 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname) smsg(_("/encoding= line after word ignored in %s line %ld: %s"), fname, lnum, line - 1); } else { - char_u *enc; + char_u *enc; // Setup for conversion to 'encoding'. line += 9; enc = enc_canonize(line); if (!spin->si_ascii && convert_setup(&spin->si_conv, enc, - p_enc) == FAIL) + p_enc) == FAIL) { smsg(_("Conversion in %s not supported: from %s to %s"), fname, line, p_enc); + } xfree(enc); spin->si_conv.vc_fail = true; } @@ -3635,15 +3786,16 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname) if (p != NULL) { *p++ = NUL; while (*p != NUL) { - if (*p == '=') // keep-case word + if (*p == '=') { // keep-case word flags |= WF_KEEPCAP | WF_FIXCAP; - else if (*p == '!') // Bad, bad, wicked word. + } else if (*p == '!') { // Bad, bad, wicked word. flags |= WF_BANNED; - else if (*p == '?') // Rare word. + } else if (*p == '?') { // Rare word. flags |= WF_RARE; - else if (ascii_isdigit(*p)) { // region number(s) - if ((flags & WF_REGION) == 0) // first one + } else if (ascii_isdigit(*p)) { // region number(s) + if ((flags & WF_REGION) == 0) { // first one regionmask = 0; + } flags |= WF_REGION; l = *p - '0'; @@ -3681,7 +3833,7 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname) if (spin->si_ascii && non_ascii > 0) { vim_snprintf((char *)IObuff, IOSIZE, - _("Ignored %d words with non-ASCII characters"), non_ascii); + _("Ignored %d words with non-ASCII characters"), non_ascii); spell_message(spin, IObuff); } @@ -3699,16 +3851,17 @@ static int spell_read_wordfile(spellinfo_T *spin, char_u *fname) static void *getroom(spellinfo_T *spin, size_t len, bool align) FUNC_ATTR_NONNULL_RET { - char_u *p; - sblock_T *bl = spin->si_blocks; + char_u *p; + sblock_T *bl = spin->si_blocks; assert(len <= SBLOCKSIZE); - - if (align && bl != NULL) + + if (align && bl != NULL) { // Round size up for alignment. On some systems structures need to be // aligned to the size of a pointer (e.g., SPARC). bl->sb_used = (bl->sb_used + sizeof(char *) - 1) & ~(sizeof(char *) - 1); + } if (bl == NULL || bl->sb_used + len > SBLOCKSIZE) { // Allocate a block of memory. It is not freed until much later. @@ -3737,7 +3890,7 @@ static char_u *getroom_save(spellinfo_T *spin, char_u *s) // Free the list of allocated sblock_T. static void free_blocks(sblock_T *bl) { - sblock_T *next; + sblock_T *next; while (bl != NULL) { next = bl->sb_next; @@ -3754,22 +3907,20 @@ static wordnode_T *wordtree_alloc(spellinfo_T *spin) return (wordnode_T *)getroom(spin, sizeof(wordnode_T), true); } -// Store a word in the tree(s). -// Always store it in the case-folded tree. For a keep-case word this is -// useful when the word can also be used with all caps (no WF_FIXCAP flag) and -// used to find suggestions. -// For a keep-case word also store it in the keep-case tree. -// When "pfxlist" is not NULL store the word for each postponed prefix ID and -// compound flag. -static int -store_word ( - spellinfo_T *spin, - char_u *word, - int flags, // extra flags, WF_BANNED - int region, // supported region(s) - const char_u *pfxlist, // list of prefix IDs or NULL - bool need_affix // only store word with affix ID -) +/// Store a word in the tree(s). +/// Always store it in the case-folded tree. For a keep-case word this is +/// useful when the word can also be used with all caps (no WF_FIXCAP flag) and +/// used to find suggestions. +/// For a keep-case word also store it in the keep-case tree. +/// When "pfxlist" is not NULL store the word for each postponed prefix ID and +/// compound flag. +/// +/// @param flags extra flags, wf_banned +/// @param region supported region(s) +/// @param pfxlist list of prefix ids or null +/// @param need_affix only store word with affix id +static int store_word(spellinfo_T *spin, char_u *word, int flags, int region, const char_u *pfxlist, + bool need_affix) { int len = (int)STRLEN(word); int ct = captype(word, word + len); @@ -3807,12 +3958,13 @@ store_word ( // When "flags" < 0 we are adding to the prefix tree where "flags" is used for // "rare" and "region" is the condition nr. // Returns FAIL when out of memory. -static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int flags, int region, int affixID) +static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int flags, int region, + int affixID) { - wordnode_T *node = root; - wordnode_T *np; - wordnode_T *copyp, **copyprev; - wordnode_T **prev = NULL; + wordnode_T *node = root; + wordnode_T *np; + wordnode_T *copyp, **copyprev; + wordnode_T **prev = NULL; int i; // Add each byte of the word to the tree, including the NUL at the end. @@ -3826,11 +3978,13 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int for (copyp = node; copyp != NULL; copyp = copyp->wn_sibling) { // Allocate a new node and copy the info. np = get_wordnode(spin); - if (np == NULL) + if (np == NULL) { return FAIL; + } np->wn_child = copyp->wn_child; - if (np->wn_child != NULL) + if (np->wn_child != NULL) { ++np->wn_child->wn_refs; // child gets extra ref + } np->wn_byte = copyp->wn_byte; if (np->wn_byte == NUL) { np->wn_flags = copyp->wn_flags; @@ -3840,13 +3994,15 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int // Link the new node in the list, there will be one ref. np->wn_refs = 1; - if (copyprev != NULL) + if (copyprev != NULL) { *copyprev = np; + } copyprev = &np->wn_sibling; // Let "node" point to the head of the copied list. - if (copyp == node) + if (copyp == node) { node = np; + } } } @@ -3877,22 +4033,24 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int || node->wn_affixID != affixID))) { // Allocate a new node. np = get_wordnode(spin); - if (np == NULL) + if (np == NULL) { return FAIL; + } np->wn_byte = word[i]; // If "node" is NULL this is a new child or the end of the sibling // list: ref count is one. Otherwise use ref count of sibling and // make ref count of sibling one (matters when inserting in front // of the list of siblings). - if (node == NULL) + if (node == NULL) { np->wn_refs = 1; - else { + } else { np->wn_refs = node->wn_refs; node->wn_refs = 1; } - if (prev != NULL) + if (prev != NULL) { *prev = np; + } np->wn_sibling = node; node = np; } @@ -3934,7 +4092,7 @@ static int tree_add_word(spellinfo_T *spin, char_u *word, wordnode_T *root, int #ifndef SPELL_COMPRESS_ALLWAYS if (spin->si_compress_cnt == 1 // NOLINT(readability/braces) ? spin->si_free_count < MAXWLEN - : spin->si_blocks_cnt >= compress_start) + : spin->si_blocks_cnt >= compress_start) #endif { // Decrement the block counter. The effect is that we compress again @@ -3973,17 +4131,18 @@ static wordnode_T *get_wordnode(spellinfo_T *spin) { wordnode_T *n; - if (spin->si_first_free == NULL) + if (spin->si_first_free == NULL) { n = (wordnode_T *)getroom(spin, sizeof(wordnode_T), true); - else { + } else { n = spin->si_first_free; spin->si_first_free = n->wn_child; memset(n, 0, sizeof(wordnode_T)); --spin->si_free_count; } #ifdef SPELL_PRINTTREE - if (n != NULL) + if (n != NULL) { n->wn_nr = ++spin->si_wordnode_nr; + } #endif return n; } @@ -3995,13 +4154,14 @@ static wordnode_T *get_wordnode(spellinfo_T *spin) static int deref_wordnode(spellinfo_T *spin, wordnode_T *node) FUNC_ATTR_NONNULL_ALL { - wordnode_T *np; + wordnode_T *np; int cnt = 0; if (--node->wn_refs == 0) { for (np = node; np != NULL; np = np->wn_sibling) { - if (np->wn_child != NULL) + if (np->wn_child != NULL) { cnt += deref_wordnode(spin, np->wn_child); + } free_wordnode(spin, np); ++cnt; } @@ -4021,8 +4181,7 @@ static void free_wordnode(spellinfo_T *spin, wordnode_T *n) } // Compress a tree: find tails that are identical and can be shared. -static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, - const char *name) +static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, const char *name) FUNC_ATTR_NONNULL_ALL { hashtab_T ht; @@ -4039,12 +4198,13 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, if (spin->si_verbose || p_verbose > 2) #endif { - if (tot > 1000000) + if (tot > 1000000) { perc = (tot - n) / (tot / 100); - else if (tot == 0) + } else if (tot == 0) { perc = 0; - else + } else { perc = (tot - n) * 100 / tot; + } vim_snprintf((char *)IObuff, IOSIZE, _("Compressed %s of %ld nodes; %ld (%ld%%) remaining"), name, tot, tot - n, perc); @@ -4057,22 +4217,18 @@ static void wordtree_compress(spellinfo_T *spin, wordnode_T *root, } } -// Compress a node, its siblings and its children, depth first. -// Returns the number of compressed nodes. -static long node_compress( - spellinfo_T *spin, - wordnode_T *node, - hashtab_T *ht, - long *tot // total count of nodes before compressing, - // incremented while going through the tree -) +/// Compress a node, its siblings and its children, depth first. +/// Returns the number of compressed nodes. +/// +/// @param tot total count of nodes before compressing, incremented while going through the tree +static long node_compress(spellinfo_T *spin, wordnode_T *node, hashtab_T *ht, long *tot) FUNC_ATTR_NONNULL_ALL { - wordnode_T *np; - wordnode_T *tp; - wordnode_T *child; + wordnode_T *np; + wordnode_T *tp; + wordnode_T *child; hash_T hash; - hashitem_T *hi; + hashitem_T *hi; long len = 0; unsigned nr, n; long compressed = 0; @@ -4095,7 +4251,7 @@ static long node_compress( // There are children we encountered before with a hash value // identical to the current child. Now check if there is one // that is really identical. - for (tp = HI2WN(hi); tp != NULL; tp = tp->wn_u2.next) + for (tp = HI2WN(hi); tp != NULL; tp = tp->wn_u2.next) { if (node_equal(child, tp)) { // Found one! Now use that child in place of the // current one. This means the current child and all @@ -4105,6 +4261,7 @@ static long node_compress( np->wn_child = tp; break; } + } if (tp == NULL) { // No other child with this hash value equals the child of // the node, add it to the linked list after the first @@ -4113,10 +4270,11 @@ static long node_compress( child->wn_u2.next = tp->wn_u2.next; tp->wn_u2.next = child; } - } else + } else { // No other child has this hash value, add it to the // hashtable. hash_add_item(ht, hi, child->wn_u1.hashkey, hash); + } } } *tot += len + 1; // add one for the node that stores the length @@ -4127,12 +4285,13 @@ static long node_compress( node->wn_u1.hashkey[0] = len; nr = 0; for (np = node; np != NULL; np = np->wn_sibling) { - if (np->wn_byte == NUL) + if (np->wn_byte == NUL) { // end node: use wn_flags, wn_region and wn_affixID n = np->wn_flags + (np->wn_region << 8) + (np->wn_affixID << 16); - else + } else { // byte node: use the byte value and the child pointer n = (unsigned)(np->wn_byte + ((uintptr_t)np->wn_child << 8)); + } nr = nr * 101 + n; } @@ -4156,18 +4315,20 @@ static long node_compress( // Returns true when two nodes have identical siblings and children. static bool node_equal(wordnode_T *n1, wordnode_T *n2) { - wordnode_T *p1; - wordnode_T *p2; + wordnode_T *p1; + wordnode_T *p2; for (p1 = n1, p2 = n2; p1 != NULL && p2 != NULL; - p1 = p1->wn_sibling, p2 = p2->wn_sibling) + p1 = p1->wn_sibling, p2 = p2->wn_sibling) { if (p1->wn_byte != p2->wn_byte || (p1->wn_byte == NUL ? (p1->wn_flags != p2->wn_flags || p1->wn_region != p2->wn_region || p1->wn_affixID != p2->wn_affixID) - : (p1->wn_child != p2->wn_child))) + : (p1->wn_child != p2->wn_child))) { break; + } + } return p1 == NULL && p2 == NULL; } @@ -4176,8 +4337,8 @@ static bool node_equal(wordnode_T *n1, wordnode_T *n2) // Function given to qsort() to sort the REP items on "from" string. static int rep_compare(const void *s1, const void *s2) { - fromto_T *p1 = (fromto_T *)s1; - fromto_T *p2 = (fromto_T *)s2; + fromto_T *p1 = (fromto_T *)s1; + fromto_T *p2 = (fromto_T *)s2; return STRCMP(p1->ft_from, p2->ft_from); } @@ -4198,9 +4359,10 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) // <HEADER>: <fileID> <versionnr> // <fileID> size_t fwv = fwrite(VIMSPELLMAGIC, VIMSPELLMAGICL, 1, fd); - if (fwv != (size_t)1) + if (fwv != (size_t)1) { // Catch first write error, don't try writing more. goto theend; + } putc(VIMSPELLVERSION, fd); // <versionnr> @@ -4225,8 +4387,9 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) fwv &= fwrite(spin->si_region_name, l, 1, fd); // <regionname> ... regionmask = (1 << spin->si_region_count) - 1; - } else + } else { regionmask = 0; + } // SN_CHARFLAGS: <charflagslen> <charflags> <folcharslen> <folchars> // @@ -4254,10 +4417,12 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) fputc(128, fd); // <charflagslen> for (size_t i = 128; i < 256; ++i) { flags = 0; - if (spelltab.st_isw[i]) + if (spelltab.st_isw[i]) { flags |= CF_WORD; - if (spelltab.st_isu[i]) + } + if (spelltab.st_isu[i]) { flags |= CF_UPPER; + } fputc(flags, fd); // <charflags> } @@ -4296,24 +4461,28 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) // round 3: SN_REPSAL section for (unsigned int round = 1; round <= 3; ++round) { garray_T *gap; - if (round == 1) + if (round == 1) { gap = &spin->si_rep; - else if (round == 2) { + } else if (round == 2) { // Don't write SN_SAL when using a SN_SOFO section - if (spin->si_sofofr != NULL && spin->si_sofoto != NULL) + if (spin->si_sofofr != NULL && spin->si_sofoto != NULL) { continue; + } gap = &spin->si_sal; - } else + } else { gap = &spin->si_repsal; + } // Don't write the section if there are no items. - if (GA_EMPTY(gap)) + if (GA_EMPTY(gap)) { continue; + } // Sort the REP/REPSAL items. - if (round != 2) + if (round != 2) { qsort(gap->ga_data, (size_t)gap->ga_len, - sizeof(fromto_T), rep_compare); + sizeof(fromto_T), rep_compare); + } int sect_id = round == 1 ? SN_REP : (round == 2 ? SN_SAL : SN_REPSAL); putc(sect_id, fd); // <sectionID> @@ -4329,18 +4498,22 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) l += 1 + STRLEN(ftp->ft_from); // count <*fromlen> and <*from> l += 1 + STRLEN(ftp->ft_to); // count <*tolen> and <*to> } - if (round == 2) + if (round == 2) { ++l; // count <salflags> + } put_bytes(fd, l, 4); // <sectionlen> if (round == 2) { int i = 0; - if (spin->si_followup) + if (spin->si_followup) { i |= SAL_F0LLOWUP; - if (spin->si_collapse) + } + if (spin->si_collapse) { i |= SAL_COLLAPSE; - if (spin->si_rem_accents) + } + if (spin->si_rem_accents) { i |= SAL_REM_ACCENTS; + } putc(i, fd); // <salflags> } @@ -4354,11 +4527,11 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) l = STRLEN(p); assert(l < INT_MAX); putc((int)l, fd); - if (l > 0) + if (l > 0) { fwv &= fwrite(p, l, 1, fd); + } } } - } // SN_SOFO: <sofofromlen> <sofofrom> <sofotolen> <sofoto> @@ -4389,19 +4562,22 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) for (unsigned int round = 1; round <= 2; ++round) { size_t todo; size_t len = 0; - hashitem_T *hi; + hashitem_T *hi; todo = spin->si_commonwords.ht_used; - for (hi = spin->si_commonwords.ht_array; todo > 0; ++hi) + for (hi = spin->si_commonwords.ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { size_t l = STRLEN(hi->hi_key) + 1; len += l; - if (round == 2) // <word> + if (round == 2) { // <word> fwv &= fwrite(hi->hi_key, l, 1, fd); + } --todo; } - if (round == 1) + } + if (round == 1) { put_bytes(fd, len, 4); // <sectionlen> + } } } @@ -4509,12 +4685,13 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) spin->si_memtot = 0; for (unsigned int round = 1; round <= 3; ++round) { wordnode_T *tree; - if (round == 1) + if (round == 1) { tree = spin->si_foldroot->wn_sibling; - else if (round == 2) + } else if (round == 2) { tree = spin->si_keeproot->wn_sibling; - else + } else { tree = spin->si_prefroot->wn_sibling; + } // Clear the index and wnode fields in the tree. clear_node(tree); @@ -4534,16 +4711,20 @@ static int write_vim_spell(spellinfo_T *spin, char_u *fname) } // Write another byte to check for errors (file system full). - if (putc(0, fd) == EOF) + if (putc(0, fd) == EOF) { retval = FAIL; + } theend: - if (fclose(fd) == EOF) + if (fclose(fd) == EOF) { retval = FAIL; + } - if (fwv != (size_t)1) + if (fwv != (size_t)1) { retval = FAIL; - if (retval == FAIL) + } + if (retval == FAIL) { EMSG(_(e_write)); + } return retval; } @@ -4553,54 +4734,54 @@ theend: // space. static void clear_node(wordnode_T *node) { - wordnode_T *np; + wordnode_T *np; - if (node != NULL) + if (node != NULL) { for (np = node; np != NULL; np = np->wn_sibling) { np->wn_u1.index = 0; np->wn_u2.wnode = NULL; - if (np->wn_byte != NUL) + if (np->wn_byte != NUL) { clear_node(np->wn_child); + } } + } } -// Dump a word tree at node "node". -// -// This first writes the list of possible bytes (siblings). Then for each -// byte recursively write the children. -// -// NOTE: The code here must match the code in read_tree_node(), since -// assumptions are made about the indexes (so that we don't have to write them -// in the file). -// -// Returns the number of nodes used. -static int -put_node ( - FILE *fd, // NULL when only counting - wordnode_T *node, - int idx, - int regionmask, - bool prefixtree // true for PREFIXTREE -) +/// Dump a word tree at node "node". +/// +/// This first writes the list of possible bytes (siblings). Then for each +/// byte recursively write the children. +/// +/// NOTE: The code here must match the code in read_tree_node(), since +/// assumptions are made about the indexes (so that we don't have to write them +/// in the file). +/// +/// @param fd NULL when only counting +/// @param prefixtree true for PREFIXTREE +/// +/// @return the number of nodes used. +static int put_node(FILE *fd, wordnode_T *node, int idx, int regionmask, bool prefixtree) { // If "node" is zero the tree is empty. - if (node == NULL) + if (node == NULL) { return 0; + } // Store the index where this node is written. node->wn_u1.index = idx; // Count the number of siblings. int siblingcount = 0; - for (wordnode_T *np = node; np != NULL; np = np->wn_sibling) + for (wordnode_T *np = node; np != NULL; np = np->wn_sibling) { ++siblingcount; + } // Write the sibling count. - if (fd != NULL) + if (fd != NULL) { putc(siblingcount, fd); // <siblingcount> - + } // Write each sibling byte and optionally extra info. for (wordnode_T *np = node; np != NULL; np = np->wn_sibling) { if (np->wn_byte == 0) { @@ -4611,9 +4792,9 @@ put_node ( // associated condition nr (stored in wn_region). The // byte value is misused to store the "rare" and "not // combining" flags - if (np->wn_flags == (uint16_t)PFX_FLAGS) + if (np->wn_flags == (uint16_t)PFX_FLAGS) { putc(BY_NOFLAGS, fd); // <byte> - else { + } else { putc(BY_FLAGS, fd); // <byte> putc(np->wn_flags, fd); // <pflags> } @@ -4622,10 +4803,12 @@ put_node ( } else { // For word trees we write the flag/region items. int flags = np->wn_flags; - if (regionmask != 0 && np->wn_region != regionmask) + if (regionmask != 0 && np->wn_region != regionmask) { flags |= WF_REGION; - if (np->wn_affixID != 0) + } + if (np->wn_affixID != 0) { flags |= WF_AFX; + } if (flags == 0) { // word without flags or region putc(BY_NOFLAGS, fd); // <byte> @@ -4638,10 +4821,12 @@ put_node ( putc(BY_FLAGS, fd); // <byte> putc(flags, fd); // <flags> } - if (flags & WF_REGION) + if (flags & WF_REGION) { putc(np->wn_region, fd); // <region> - if (flags & WF_AFX) + } + if (flags & WF_AFX) { putc(np->wn_affixID, fd); // <affixID> + } } } } @@ -4653,15 +4838,17 @@ put_node ( putc(BY_INDEX, fd); // <byte> put_bytes(fd, (uintmax_t)np->wn_child->wn_u1.index, 3); // <nodeidx> } - } else if (np->wn_child->wn_u2.wnode == NULL) + } else if (np->wn_child->wn_u2.wnode == NULL) { // We will write the child below and give it an index. np->wn_child->wn_u2.wnode = node; + } - if (fd != NULL) + if (fd != NULL) { if (putc(np->wn_byte, fd) == EOF) { // <byte> or <xbyte> EMSG(_(e_write)); return 0; } + } } } @@ -4670,10 +4857,12 @@ put_node ( int newindex = idx + siblingcount + 1; // Recursively dump the children of each sibling. - for (wordnode_T *np = node; np != NULL; np = np->wn_sibling) - if (np->wn_byte != 0 && np->wn_child->wn_u2.wnode == node) + for (wordnode_T *np = node; np != NULL; np = np->wn_sibling) { + if (np->wn_byte != 0 && np->wn_child->wn_u2.wnode == node) { newindex = put_node(fd, np->wn_child, newindex, regionmask, - prefixtree); + prefixtree); + } + } return newindex; } @@ -4684,8 +4873,8 @@ put_node ( void ex_mkspell(exarg_T *eap) { int fcount; - char_u **fnames; - char_u *arg = eap->arg; + char_u **fnames; + char_u *arg = eap->arg; bool ascii = false; if (STRNCMP(arg, "-ascii", 6) == 0) { @@ -4705,9 +4894,9 @@ void ex_mkspell(exarg_T *eap) // Writes the file with the name "wfname", with ".spl" changed to ".sug". static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname) { - char_u *fname = NULL; + char_u *fname = NULL; int len; - slang_T *slang; + slang_T *slang; bool free_slang = false; // Read back the .spl file that was written. This fills the required @@ -4724,8 +4913,9 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname) if (slang == NULL) { spell_message(spin, (char_u *)_("Reading back spell file...")); slang = spell_load_file(wfname, NULL, NULL, false); - if (slang == NULL) + if (slang == NULL) { return; + } free_slang = true; } @@ -4740,15 +4930,17 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname) // Go through the trie of good words, soundfold each word and add it to // the soundfold trie. spell_message(spin, (char_u *)_("Performing soundfolding...")); - if (sug_filltree(spin, slang) == FAIL) + if (sug_filltree(spin, slang) == FAIL) { goto theend; + } // Create the table which links each soundfold word with a list of the // good words it may come from. Creates buffer "spin->si_spellbuf". // This also removes the wordnr from the NUL byte entries to make // compression possible. - if (sug_maketable(spin) == FAIL) + if (sug_maketable(spin) == FAIL) { goto theend; + } smsg(_("Number of words after soundfolding: %" PRId64), (int64_t)spin->si_spellbuf->b_ml.ml_line_count); @@ -4768,8 +4960,9 @@ static void spell_make_sugfile(spellinfo_T *spin, char_u *wfname) theend: xfree(fname); - if (free_slang) + if (free_slang) { slang_free(slang); + } free_blocks(spin->si_blocks); close_spellbuf(spin->si_spellbuf); } @@ -4777,8 +4970,8 @@ theend: // Build the soundfold trie for language "slang". static int sug_filltree(spellinfo_T *spin, slang_T *slang) { - char_u *byts; - idx_T *idxs; + char_u *byts; + idx_T *idxs; int depth; idx_T arridx[MAXWLEN]; int curi[MAXWLEN]; @@ -4809,13 +5002,13 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang) if (curi[depth] > byts[arridx[depth]]) { // Done all bytes at this node, go up one level. idxs[arridx[depth]] = wordcount[depth]; - if (depth > 0) + if (depth > 0) { wordcount[depth - 1] += wordcount[depth]; + } --depth; line_breakcheck(); } else { - // Do one more byte at this node. n = arridx[depth] + curi[depth]; ++curi[depth]; @@ -4829,9 +5022,10 @@ static int sug_filltree(spellinfo_T *spin, slang_T *slang) // We use the "flags" field for the MSB of the wordnr, // "region" for the LSB of the wordnr. if (tree_add_word(spin, tsalword, spin->si_foldroot, - words_done >> 16, words_done & 0xffff, - 0) == FAIL) + words_done >> 16, words_done & 0xffff, + 0) == FAIL) { return FAIL; + } ++words_done; ++wordcount[depth]; @@ -4880,25 +5074,22 @@ static int sug_maketable(spellinfo_T *spin) ga_init(&ga, 1, 100); // recursively go through the tree - if (sug_filltable(spin, spin->si_foldroot->wn_sibling, 0, &ga) == -1) + if (sug_filltable(spin, spin->si_foldroot->wn_sibling, 0, &ga) == -1) { res = FAIL; + } ga_clear(&ga); return res; } -// Fill the table for one node and its children. -// Returns the wordnr at the start of the node. -// Returns -1 when out of memory. -static int -sug_filltable ( - spellinfo_T *spin, - wordnode_T *node, - int startwordnr, - garray_T *gap // place to store line of numbers -) +/// Fill the table for one node and its children. +/// Returns the wordnr at the start of the node. +/// Returns -1 when out of memory. +/// +/// @param gap place to store line of numbers +static int sug_filltable(spellinfo_T *spin, wordnode_T *node, int startwordnr, garray_T *gap) { - wordnode_T *p, *np; + wordnode_T *p, *np; int wordnr = startwordnr; int nr; int prev_nr; @@ -4918,7 +5109,7 @@ sug_filltable ( nr -= prev_nr; prev_nr += nr; gap->ga_len += offset2bytes(nr, - (char_u *)gap->ga_data + gap->ga_len); + (char_u *)gap->ga_data + gap->ga_len); } // add the NUL byte @@ -4932,8 +5123,9 @@ sug_filltable ( // Remove extra NUL entries, we no longer need them. We don't // bother freeing the nodes, the won't be reused anyway. - while (p->wn_sibling != NULL && p->wn_sibling->wn_byte == NUL) + while (p->wn_sibling != NULL && p->wn_sibling->wn_byte == NUL) { p->wn_sibling = p->wn_sibling->wn_sibling; + } // Clear the flags on the remaining NUL node, so that compression // works a lot better. @@ -4941,8 +5133,9 @@ sug_filltable ( p->wn_region = 0; } else { wordnr = sug_filltable(spin, p->wn_child, wordnr, gap); - if (wordnr == -1) + if (wordnr == -1) { return -1; + } } } return wordnr; @@ -5002,7 +5195,7 @@ static void sug_write(spellinfo_T *spin, char_u *fname) spell_message(spin, IObuff); // <SUGHEADER>: <fileID> <versionnr> <timestamp> - if (fwrite(VIMSUGMAGIC, VIMSUGMAGICL, (size_t)1, fd) != 1) { // <fileID> + if (fwrite(VIMSUGMAGIC, VIMSUGMAGICL, (size_t)1, fd) != 1) { // <fileID> EMSG(_(e_write)); goto theend; } @@ -5049,11 +5242,12 @@ static void sug_write(spellinfo_T *spin, char_u *fname) } // Write another byte to check for errors. - if (putc(0, fd) == EOF) + if (putc(0, fd) == EOF) { EMSG(_(e_write)); + } vim_snprintf((char *)IObuff, IOSIZE, - _("Estimated runtime memory use: %d bytes"), spin->si_memtot); + _("Estimated runtime memory use: %d bytes"), spin->si_memtot); spell_message(spin, IObuff); theend: @@ -5062,23 +5256,20 @@ theend: } -// Create a Vim spell file from one or more word lists. -// "fnames[0]" is the output file name. -// "fnames[fcount - 1]" is the last input file name. -// Exception: when "fnames[0]" ends in ".add" it's used as the input file name -// and ".spl" is appended to make the output file name. -static void -mkspell ( - int fcount, - char_u **fnames, - bool ascii, // -ascii argument given - bool over_write, // overwrite existing output file - bool added_word // invoked through "zg" -) +/// Create a Vim spell file from one or more word lists. +/// "fnames[0]" is the output file name. +/// "fnames[fcount - 1]" is the last input file name. +/// Exception: when "fnames[0]" ends in ".add" it's used as the input file name +/// and ".spl" is appended to make the output file name. +/// +/// @param ascii -ascii argument given +/// @param over_write overwrite existing output file +/// @param added_word invoked through "zg" +static void mkspell(int fcount, char_u **fnames, bool ascii, bool over_write, bool added_word) { - char_u *fname = NULL; - char_u *wfname; - char_u **innames; + char_u *fname = NULL; + char_u *wfname; + char_u **innames; int incount; afffile_T *(afile[MAXREGIONS]); int i; @@ -5114,26 +5305,29 @@ mkspell ( // "path/en.latin1.add.spl". incount = 1; vim_snprintf((char *)wfname, MAXPATHL, "%s.spl", fnames[0]); - } else if (fcount == 1) { + } else if (fcount == 1) { // For ":mkspell path/vim" output file is "path/vim.latin1.spl". incount = 1; vim_snprintf((char *)wfname, MAXPATHL, SPL_FNAME_TMPL, - fnames[0], spin.si_ascii ? (char_u *)"ascii" : spell_enc()); - } else if (len > 4 && STRCMP(fnames[0] + len - 4, ".spl") == 0) { + fnames[0], spin.si_ascii ? (char_u *)"ascii" : spell_enc()); + } else if (len > 4 && STRCMP(fnames[0] + len - 4, ".spl") == 0) { // Name ends in ".spl", use as the file name. STRLCPY(wfname, fnames[0], MAXPATHL); - } else + } else { // Name should be language, make the file name from it. vim_snprintf((char *)wfname, MAXPATHL, SPL_FNAME_TMPL, - fnames[0], spin.si_ascii ? (char_u *)"ascii" : spell_enc()); + fnames[0], spin.si_ascii ? (char_u *)"ascii" : spell_enc()); + } // Check for .ascii.spl. - if (strstr((char *)path_tail(wfname), SPL_FNAME_ASCII) != NULL) + if (strstr((char *)path_tail(wfname), SPL_FNAME_ASCII) != NULL) { spin.si_ascii = true; + } // Check for .add.spl. - if (strstr((char *)path_tail(wfname), SPL_FNAME_ADD) != NULL) + if (strstr((char *)path_tail(wfname), SPL_FNAME_ADD) != NULL) { spin.si_add = true; + } } if (incount <= 0) { @@ -5184,8 +5378,9 @@ mkspell ( // one in the .spl file if the .aff file doesn't define one. That's // better than guessing the contents, the table will match a // previously loaded spell file. - if (!spin.si_add) + if (!spin.si_add) { spin.si_clear_chartab = true; + } // Read all the .aff and .dic files. // Text is converted to 'encoding'. @@ -5199,28 +5394,31 @@ mkspell ( // Read the .aff file. Will init "spin->si_conv" based on the // "SET" line. afile[i] = spell_read_aff(&spin, fname); - if (afile[i] == NULL) + if (afile[i] == NULL) { error = true; - else { + } else { // Read the .dic file and store the words in the trees. vim_snprintf((char *)fname, MAXPATHL, "%s.dic", - innames[i]); - if (spell_read_dic(&spin, fname, afile[i]) == FAIL) + innames[i]); + if (spell_read_dic(&spin, fname, afile[i]) == FAIL) { error = true; + } } } else { // No .aff file, try reading the file as a word list. Store // the words in the trees. - if (spell_read_wordfile(&spin, innames[i]) == FAIL) + if (spell_read_wordfile(&spin, innames[i]) == FAIL) { error = true; + } } // Free any conversion stuff. convert_setup(&spin.si_conv, NULL, NULL); } - if (spin.si_compflags != NULL && spin.si_nobreak) + if (spin.si_compflags != NULL && spin.si_nobreak) { MSG(_("Warning: both compounding and NOBREAK specified")); + } if (!error && !got_int) { // Combine tails in the tree. @@ -5240,12 +5438,13 @@ mkspell ( spell_message(&spin, (char_u *)_("Done!")); vim_snprintf((char *)IObuff, IOSIZE, - _("Estimated runtime memory use: %d bytes"), spin.si_memtot); + _("Estimated runtime memory use: %d bytes"), spin.si_memtot); spell_message(&spin, IObuff); // If the file is loaded need to reload it. - if (!error) + if (!error) { spell_reload_one(wfname, added_word); + } } // Free the allocated memory. @@ -5258,18 +5457,20 @@ mkspell ( hash_clear_all(&spin.si_commonwords, 0); // Free the .aff file structures. - for (i = 0; i < incount; ++i) - if (afile[i] != NULL) + for (i = 0; i < incount; ++i) { + if (afile[i] != NULL) { spell_free_aff(afile[i]); + } + } // Free all the bits and pieces at once. free_blocks(spin.si_blocks); // If there is soundfolding info and no NOSUGFILE item create the // .sug file with the soundfolded word trie. - if (spin.si_sugtime != 0 && !error && !got_int) + if (spin.si_sugtime != 0 && !error && !got_int) { spell_make_sugfile(&spin, wfname); - + } } theend: @@ -5283,12 +5484,14 @@ static void spell_message(const spellinfo_T *spin, char_u *str) FUNC_ATTR_NONNULL_ALL { if (spin->si_verbose || p_verbose > 2) { - if (!spin->si_verbose) + if (!spin->si_verbose) { verbose_enter(); + } MSG(str); ui_flush(); - if (!spin->si_verbose) + if (!spin->si_verbose) { verbose_leave(); + } } } @@ -5305,32 +5508,29 @@ void ex_spell(exarg_T *eap) eap->cmdidx == CMD_spellundo); } -// Add "word[len]" to 'spellfile' as a good or bad word. -void -spell_add_word ( - char_u *word, - int len, - SpellAddType what, // SPELL_ADD_ values - int idx, // "zG" and "zW": zero, otherwise index in - // 'spellfile' - bool undo // true for "zug", "zuG", "zuw" and "zuW" -) +/// Add "word[len]" to 'spellfile' as a good or bad word. +/// +/// @param what SPELL_ADD_ values +/// @param idx "zG" and "zW": zero, otherwise index in 'spellfile' +/// @param bool // true for "zug", "zuG", "zuw" and "zuW" +void spell_add_word(char_u *word, int len, SpellAddType what, int idx, bool undo) { - FILE *fd = NULL; - buf_T *buf = NULL; + FILE *fd = NULL; + buf_T *buf = NULL; bool new_spf = false; - char_u *fname; - char_u *fnamebuf = NULL; + char_u *fname; + char_u *fnamebuf = NULL; char_u line[MAXWLEN * 2]; long fpos, fpos_next = 0; int i; - char_u *spf; + char_u *spf; if (idx == 0) { // use internal wordlist if (int_wordlist == NULL) { int_wordlist = vim_tempname(); - if (int_wordlist == NULL) + if (int_wordlist == NULL) { return; + } } fname = int_wordlist; } else { @@ -5348,8 +5548,9 @@ spell_add_word ( for (spf = curwin->w_s->b_p_spf, i = 1; *spf != NUL; ++i) { copy_option_part(&spf, fnamebuf, MAXPATHL, ","); - if (i == idx) + if (i == idx) { break; + } if (*spf == NUL) { EMSGN(_("E765: 'spellfile' does not have %" PRId64 " entries"), idx); xfree(fnamebuf); @@ -5359,8 +5560,9 @@ spell_add_word ( // Check that the user isn't editing the .add file somewhere. buf = buflist_findname_exp(fnamebuf); - if (buf != NULL && buf->b_ml.ml_mfp == NULL) + if (buf != NULL && buf->b_ml.ml_mfp == NULL) { buf = NULL; + } if (buf != NULL && bufIsChanged(buf)) { EMSG(_(e_bufloaded)); xfree(fnamebuf); @@ -5402,8 +5604,9 @@ spell_add_word ( } } } - if (fd != NULL) + if (fd != NULL) { fclose(fd); + } } } @@ -5450,8 +5653,9 @@ spell_add_word ( mkspell(1, &fname, false, true, true); // If the .add file is edited somewhere, reload it. - if (buf != NULL) + if (buf != NULL) { buf_reload(buf, buf->b_orig_mode); + } redraw_all_later(SOME_VALID); } @@ -5461,13 +5665,13 @@ spell_add_word ( // Initialize 'spellfile' for the current buffer. static void init_spellfile(void) { - char_u *buf; + char_u *buf; int l; - char_u *fname; - char_u *rtp; - char_u *lend; + char_u *fname; + char_u *rtp; + char_u *lend; bool aspath = false; - char_u *lstart = curbuf->b_s.b_p_spl; + char_u *lstart = curbuf->b_s.b_p_spl; if (*curwin->w_s->b_p_spl != NUL && !GA_EMPTY(&curwin->w_s->b_langp)) { buf = xmalloc(MAXPATHL); @@ -5475,31 +5679,33 @@ static void init_spellfile(void) // Find the end of the language name. Exclude the region. If there // is a path separator remember the start of the tail. for (lend = curwin->w_s->b_p_spl; *lend != NUL - && vim_strchr((char_u *)",._", *lend) == NULL; ++lend) + && vim_strchr((char_u *)",._", *lend) == NULL; ++lend) { if (vim_ispathsep(*lend)) { aspath = true; lstart = lend + 1; } + } // Loop over all entries in 'runtimepath'. Use the first one where we // are allowed to write. rtp = p_rtp; while (*rtp != NUL) { - if (aspath) + if (aspath) { // Use directory of an entry with path, e.g., for // "/dir/lg.utf-8.spl" use "/dir". STRLCPY(buf, curbuf->b_s.b_p_spl, - lstart - curbuf->b_s.b_p_spl); - else + lstart - curbuf->b_s.b_p_spl); + } else { // Copy the path from 'runtimepath' to buf[]. copy_option_part(&rtp, buf, MAXPATHL, ","); + } if (os_file_is_writable((char *)buf) == 2) { // Use the first language name from 'spelllang' and the // encoding used in the first loaded .spl file. - if (aspath) + if (aspath) { STRLCPY(buf, curbuf->b_s.b_p_spl, - lend - curbuf->b_s.b_p_spl + 1); - else { + lend - curbuf->b_s.b_p_spl + 1); + } else { // Create the "spell" directory if it doesn't exist yet. l = (int)STRLEN(buf); vim_snprintf((char *)buf + l, MAXPATHL - l, "/spell"); @@ -5509,7 +5715,7 @@ static void init_spellfile(void) l = (int)STRLEN(buf); vim_snprintf((char *)buf + l, MAXPATHL - l, - "/%.*s", (int)(lend - lstart), lstart); + "/%.*s", (int)(lend - lstart), lstart); } l = (int)STRLEN(buf); fname = LANGP_ENTRY(curwin->w_s->b_langp, 0) @@ -5529,19 +5735,16 @@ static void init_spellfile(void) } } -// Set the spell character tables from strings in the .spl file. -static void -set_spell_charflags ( - char_u *flags, - int cnt, // length of "flags" - char_u *fol -) +/// Set the spell character tables from strings in the .spl file. +/// +/// @param cnt length of "flags" +static void set_spell_charflags(char_u *flags, int cnt, char_u *fol) { // We build the new tables here first, so that we can compare with the // previous one. spelltab_T new_st; int i; - char_u *p = fol; + char_u *p = fol; int c; clear_spell_chartab(&new_st); @@ -5555,8 +5758,9 @@ set_spell_charflags ( if (*p != NUL) { c = mb_ptr2char_adv((const char_u **)&p); new_st.st_fold[i + 128] = c; - if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) + if (i + 128 != c && new_st.st_isu[i + 128] && c < 256) { new_st.st_upper[c] = i + 128; + } } } @@ -5593,9 +5797,9 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap) { assert(gap->ga_len >= 0); - if (fd != NULL) + if (fd != NULL) { put_bytes(fd, (uintmax_t)gap->ga_len, 2); // <prefcondcnt> - + } size_t totlen = 2 + (size_t)gap->ga_len; // <prefcondcnt> and <condlen> bytes size_t x = 1; // collect return value of fwrite() for (int i = 0; i < gap->ga_len; ++i) { @@ -5609,8 +5813,9 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap) x &= fwrite(p, len, 1, fd); } totlen += len; - } else if (fd != NULL) + } else if (fd != NULL) { fputc(0, fd); + } } assert(totlen <= INT_MAX); @@ -5620,7 +5825,7 @@ static int write_spell_prefcond(FILE *fd, garray_T *gap) // Use map string "map" for languages "lp". static void set_map_str(slang_T *lp, char_u *map) { - char_u *p; + char_u *p; int headc = 0; int c; int i; @@ -5632,8 +5837,9 @@ static void set_map_str(slang_T *lp, char_u *map) lp->sl_has_map = true; // Init the array and hash tables empty. - for (i = 0; i < 256; ++i) + for (i = 0; i < 256; ++i) { lp->sl_map_array[i] = 0; + } hash_init(&lp->sl_map_hash); // The similar characters are stored separated with slashes: @@ -5654,9 +5860,9 @@ static void set_map_str(slang_T *lp, char_u *map) if (c >= 256) { int cl = mb_char2len(c); int headcl = mb_char2len(headc); - char_u *b; + char_u *b; hash_T hash; - hashitem_T *hi; + hashitem_T *hi; b = xmalloc(cl + headcl + 2); utf_char2bytes(c, b); @@ -5673,8 +5879,9 @@ static void set_map_str(slang_T *lp, char_u *map) EMSG(_("E783: duplicate char in MAP entry")); xfree(b); } - } else + } else { lp->sl_map_array[c] = headc; + } } } } diff --git a/src/nvim/state.c b/src/nvim/state.c index 02d63d8ab1..04271d750c 100644 --- a/src/nvim/state.c +++ b/src/nvim/state.c @@ -3,19 +3,18 @@ #include <assert.h> -#include "nvim/lib/kvec.h" - #include "nvim/ascii.h" +#include "nvim/edit.h" +#include "nvim/ex_docmd.h" +#include "nvim/getchar.h" +#include "nvim/lib/kvec.h" #include "nvim/log.h" -#include "nvim/state.h" -#include "nvim/vim.h" #include "nvim/main.h" -#include "nvim/getchar.h" #include "nvim/option_defs.h" -#include "nvim/ui.h" #include "nvim/os/input.h" -#include "nvim/ex_docmd.h" -#include "nvim/edit.h" +#include "nvim/state.h" +#include "nvim/ui.h" +#include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "state.c.generated.h" @@ -162,6 +161,11 @@ char *get_mode(void) if (State & VREPLACE_FLAG) { buf[0] = 'R'; buf[1] = 'v'; + if (ins_compl_active()) { + buf[2] = 'c'; + } else if (ctrl_x_mode_not_defined_yet()) { + buf[2] = 'x'; + } } else { if (State & REPLACE_FLAG) { buf[0] = 'R'; diff --git a/src/nvim/strings.c b/src/nvim/strings.c index 79a3db4843..11c1aa1760 100644 --- a/src/nvim/strings.c +++ b/src/nvim/strings.c @@ -1,28 +1,26 @@ // This is an open source non-commercial project. Dear PVS-Studio, please check // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com +#include <assert.h> #include <inttypes.h> +#include <math.h> #include <stdarg.h> #include <stdbool.h> #include <string.h> -#include <math.h> -#include <assert.h> -#include "nvim/assert.h" -#include "nvim/vim.h" #include "nvim/ascii.h" -#include "nvim/strings.h" -#include "nvim/file_search.h" +#include "nvim/assert.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/diff.h" #include "nvim/edit.h" #include "nvim/eval.h" +#include "nvim/eval/encode.h" #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" +#include "nvim/file_search.h" #include "nvim/fileio.h" -#include "nvim/func_attr.h" #include "nvim/fold.h" #include "nvim/func_attr.h" #include "nvim/getchar.h" @@ -35,8 +33,10 @@ #include "nvim/message.h" #include "nvim/misc1.h" #include "nvim/move.h" -#include "nvim/option.h" #include "nvim/ops.h" +#include "nvim/option.h" +#include "nvim/os/os.h" +#include "nvim/os/shell.h" #include "nvim/os_unix.h" #include "nvim/path.h" #include "nvim/quickfix.h" @@ -44,12 +44,11 @@ #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/spell.h" +#include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/tag.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/os/os.h" -#include "nvim/os/shell.h" -#include "nvim/eval/encode.h" /// Copy "string" into newly allocated memory. char_u *vim_strsave(const char_u *string) @@ -84,8 +83,7 @@ char_u *vim_strsave_escaped(const char_u *string, const char_u *esc_chars) * characters where rem_backslash() would remove the backslash. * Escape the characters with "cc". */ -char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, - char_u cc, bool bsl) +char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, char_u cc, bool bsl) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { /* @@ -100,9 +98,10 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, p += l - 1; continue; } - if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p))) - ++length; /* count a backslash */ - ++length; /* count an ordinary char */ + if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p))) { + ++length; // count a backslash + } + ++length; // count an ordinary char } char_u *escaped_string = xmalloc(length); @@ -112,11 +111,12 @@ char_u *vim_strsave_escaped_ext(const char_u *string, const char_u *esc_chars, if (l > 1) { memcpy(p2, p, l); p2 += l; - p += l - 1; /* skip multibyte char */ + p += l - 1; // skip multibyte char continue; } - if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p))) + if (vim_strchr(esc_chars, *p) != NULL || (bsl && rem_backslash(p))) { *p2++ = cc; + } *p2++ = *p; } *p2 = NUL; @@ -182,12 +182,11 @@ char *vim_strnsave_unquoted(const char *const string, const size_t length) * When "do_newline" is false do not escape newline unless it is csh shell. * Returns the result in allocated memory. */ -char_u *vim_strsave_shellescape(const char_u *string, - bool do_special, bool do_newline) +char_u *vim_strsave_shellescape(const char_u *string, bool do_special, bool do_newline) FUNC_ATTR_NONNULL_RET FUNC_ATTR_MALLOC FUNC_ATTR_NONNULL_ALL { - char_u *d; - char_u *escaped_string; + char_u *d; + char_u *escaped_string; size_t l; int csh_like; bool fish_like; @@ -202,7 +201,7 @@ char_u *vim_strsave_shellescape(const char_u *string, // itself must be escaped to get a literal '\'. fish_like = fish_like_shell(); - /* First count the number of extra bytes required. */ + // First count the number of extra bytes required. size_t length = STRLEN(string) + 3; // two quotes and a trailing NUL for (const char_u *p = string; *p != NUL; MB_PTR_ADV(p)) { #ifdef WIN32 @@ -217,12 +216,13 @@ char_u *vim_strsave_shellescape(const char_u *string, } if ((*p == '\n' && (csh_like || do_newline)) || (*p == '!' && (csh_like || do_special))) { - ++length; /* insert backslash */ - if (csh_like && do_special) - ++length; /* insert backslash */ + ++length; // insert backslash + if (csh_like && do_special) { + ++length; // insert backslash + } } if (do_special && find_cmdline_var(p, &l) >= 0) { - ++length; /* insert backslash */ + ++length; // insert backslash p += l - 1; } if (*p == '\\' && fish_like) { @@ -230,7 +230,7 @@ char_u *vim_strsave_shellescape(const char_u *string, } } - /* Allocate memory for the result and fill it. */ + // Allocate memory for the result and fill it. escaped_string = xmalloc(length); d = escaped_string; @@ -264,15 +264,17 @@ char_u *vim_strsave_shellescape(const char_u *string, if ((*p == '\n' && (csh_like || do_newline)) || (*p == '!' && (csh_like || do_special))) { *d++ = '\\'; - if (csh_like && do_special) + if (csh_like && do_special) { *d++ = '\\'; + } *d++ = *p++; continue; } if (do_special && find_cmdline_var(p, &l) >= 0) { - *d++ = '\\'; /* insert backslash */ - while (--l != SIZE_MAX) /* copy the var */ + *d++ = '\\'; // insert backslash + while (--l != SIZE_MAX) { // copy the var *d++ = *p++; + } continue; } if (*p == '\\' && fish_like) { @@ -285,11 +287,11 @@ char_u *vim_strsave_shellescape(const char_u *string, } // add terminating quote and finish with a NUL -# ifdef WIN32 +#ifdef WIN32 if (!p_ssl) { *d++ = '"'; } else -# endif +#endif *d++ = '\''; *d = NUL; @@ -384,11 +386,12 @@ char *strcase_save(const char *const orig, bool upper) void del_trailing_spaces(char_u *ptr) FUNC_ATTR_NONNULL_ALL { - char_u *q; + char_u *q; q = ptr + STRLEN(ptr); - while (--q > ptr && ascii_iswhite(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V) + while (--q > ptr && ascii_iswhite(q[0]) && q[-1] != '\\' && q[-1] != Ctrl_V) { *q = NUL; + } } #if (!defined(HAVE_STRCASECMP) && !defined(HAVE_STRICMP)) @@ -404,14 +407,16 @@ int vim_stricmp(const char *s1, const char *s2) for (;; ) { i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2); - if (i != 0) - return i; /* this character different */ - if (*s1 == NUL) - break; /* strings match until NUL */ + if (i != 0) { + return i; // this character different + } + if (*s1 == NUL) { + break; // strings match until NUL + } ++s1; ++s2; } - return 0; /* strings match */ + return 0; // strings match } #endif @@ -428,15 +433,17 @@ int vim_strnicmp(const char *s1, const char *s2, size_t len) while (len > 0) { i = (int)TOLOWER_LOC(*s1) - (int)TOLOWER_LOC(*s2); - if (i != 0) - return i; /* this character different */ - if (*s1 == NUL) - break; /* strings match until NUL */ + if (i != 0) { + return i; // this character different + } + if (*s1 == NUL) { + break; // strings match until NUL + } ++s1; ++s2; --len; } - return 0; /* strings match */ + return 0; // strings match } #endif @@ -490,10 +497,13 @@ bool has_non_ascii(const char_u *s) { const char_u *p; - if (s != NULL) - for (p = s; *p != NUL; ++p) - if (*p >= 128) + if (s != NULL) { + for (p = s; *p != NUL; ++p) { + if (*p >= 128) { return true; + } + } + } return false; } @@ -504,7 +514,7 @@ bool has_non_ascii_len(const char *const s, const size_t len) { if (s != NULL) { for (size_t i = 0; i < len; i++) { - if ((uint8_t) s[i] >= 128) { + if ((uint8_t)s[i] >= 128) { return true; } } @@ -527,7 +537,7 @@ char_u *concat_str(const char_u *restrict str1, const char_u *restrict str2) static const char *const e_printf = - N_("E766: Insufficient arguments for printf()"); + N_("E766: Insufficient arguments for printf()"); /// Get number argument from idxp entry in tvs /// @@ -606,15 +616,14 @@ static const void *tv_ptr(const typval_T *const tvs, int *const idxp) FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT { #define OFF(attr) offsetof(union typval_vval_union, attr) - STATIC_ASSERT( - OFF(v_string) == OFF(v_list) - && OFF(v_string) == OFF(v_dict) - && OFF(v_string) == OFF(v_partial) - && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_list) - && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_dict) - && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_partial), - "Strings, dictionaries, lists and partials are expected to be pointers, " - "so that all three of them can be accessed via v_string"); + STATIC_ASSERT(OFF(v_string) == OFF(v_list) + && OFF(v_string) == OFF(v_dict) + && OFF(v_string) == OFF(v_partial) + && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_list) + && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_dict) + && sizeof(tvs[0].vval.v_string) == sizeof(tvs[0].vval.v_partial), + "Strings, dictionaries, lists and partials are expected to be pointers, " + "so that all three of them can be accessed via v_string"); #undef OFF const int idx = *idxp - 1; if (tvs[idx].v_type == VAR_UNKNOWN) { @@ -735,8 +744,8 @@ int vim_snprintf(char *str, size_t str_m, const char *fmt, ...) // Return the representation of infinity for printf() function: // "-inf", "inf", "+inf", " inf", "-INF", "INF", "+INF" or " INF". -static const char *infinity_str(bool positive, char fmt_spec, - int force_sign, int space_for_positive) +static const char *infinity_str(bool positive, char fmt_spec, int force_sign, + int space_for_positive) { static const char *table[] = { "-inf", "inf", "+inf", " inf", @@ -765,8 +774,7 @@ int vim_vsnprintf(char *str, size_t str_m, const char *fmt, va_list ap) /// /// @return Number of bytes excluding NUL byte that would be written to the /// string if str_m was greater or equal to the return value. -int vim_vsnprintf_typval( - char *str, size_t str_m, const char *fmt, va_list ap, typval_T *const tvs) +int vim_vsnprintf_typval(char *str, size_t str_m, const char *fmt, va_list ap, typval_T *const tvs) { size_t str_l = 0; bool str_avail = str_l < str_m; @@ -800,7 +808,7 @@ int vim_vsnprintf_typval( char length_modifier = '\0'; // temporary buffer for simple numeric->string conversion -# define TMP_LEN 350 // 1e308 seems reasonable as the maximum printable +#define TMP_LEN 350 // 1e308 seems reasonable as the maximum printable char tmp[TMP_LEN]; // string address in case of string argument @@ -832,15 +840,22 @@ int vim_vsnprintf_typval( // parse flags while (true) { switch (*p) { - case '0': zero_padding = 1; p++; continue; - case '-': justify_left = 1; p++; continue; - // if both '0' and '-' flags appear, '0' should be ignored - case '+': force_sign = 1; space_for_positive = 0; p++; continue; - case ' ': force_sign = 1; p++; continue; - // if both ' ' and '+' flags appear, ' ' should be ignored - case '#': alternate_form = 1; p++; continue; - case '\'': p++; continue; - default: break; + case '0': + zero_padding = 1; p++; continue; + case '-': + justify_left = 1; p++; continue; + // if both '0' and '-' flags appear, '0' should be ignored + case '+': + force_sign = 1; space_for_positive = 0; p++; continue; + case ' ': + force_sign = 1; p++; continue; + // if both ' ' and '+' flags appear, ' ' should be ignored + case '#': + alternate_form = 1; p++; continue; + case '\'': + p++; continue; + default: + break; } break; } @@ -905,444 +920,447 @@ int vim_vsnprintf_typval( // common synonyms switch (fmt_spec) { - case 'i': fmt_spec = 'd'; break; - case 'D': fmt_spec = 'd'; length_modifier = 'l'; break; - case 'U': fmt_spec = 'u'; length_modifier = 'l'; break; - case 'O': fmt_spec = 'o'; length_modifier = 'l'; break; - default: break; + case 'i': + fmt_spec = 'd'; break; + case 'D': + fmt_spec = 'd'; length_modifier = 'l'; break; + case 'U': + fmt_spec = 'u'; length_modifier = 'l'; break; + case 'O': + fmt_spec = 'o'; length_modifier = 'l'; break; + default: + break; } switch (fmt_spec) { - case 'b': case 'B': - case 'd': case 'u': case 'o': case 'x': case 'X': - if (tvs && length_modifier == '\0') { - length_modifier = '2'; - } + case 'b': + case 'B': + case 'd': + case 'u': + case 'o': + case 'x': + case 'X': + if (tvs && length_modifier == '\0') { + length_modifier = '2'; + } } // get parameter value, do initial processing switch (fmt_spec) { - // '%' and 'c' behave similar to 's' regarding flags and field widths - case '%': case 'c': case 's': case 'S': - str_arg_l = 1; - switch (fmt_spec) { - case '%': - str_arg = p; - break; - - case 'c': { - const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int); - // standard demands unsigned char - uchar_arg = (unsigned char)j; - str_arg = (char *)&uchar_arg; - break; - } + // '%' and 'c' behave similar to 's' regarding flags and field widths + case '%': + case 'c': + case 's': + case 'S': + str_arg_l = 1; + switch (fmt_spec) { + case '%': + str_arg = p; + break; - case 's': - case 'S': - str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree) - : va_arg(ap, const char *); - if (!str_arg) { - str_arg = "[NULL]"; - str_arg_l = 6; - } else if (!precision_specified) { - // make sure not to address string beyond the specified - // precision - str_arg_l = strlen(str_arg); - } else if (precision == 0) { - // truncate string if necessary as requested by precision - str_arg_l = 0; - } else { - // memchr on HP does not like n > 2^31 - // TODO(elmart): check if this still holds / is relevant - str_arg_l = (size_t)((char *)xmemscan(str_arg, - NUL, - MIN(precision, - 0x7fffffff)) - - str_arg); - } - if (fmt_spec == 'S') { - if (min_field_width != 0) { - min_field_width += (strlen(str_arg) - - mb_string2cells((char_u *)str_arg)); - } - if (precision) { - char_u *p1; - size_t i = 0; - - for (p1 = (char_u *)str_arg; *p1; - p1 += mb_ptr2len(p1)) { - i += (size_t)utf_ptr2cells(p1); - if (i > precision) { - break; - } - } - str_arg_l = (size_t)(p1 - (char_u *)str_arg); + case 'c': { + const int j = tvs ? (int)tv_nr(tvs, &arg_idx) : va_arg(ap, int); + // standard demands unsigned char + uchar_arg = (unsigned char)j; + str_arg = (char *)&uchar_arg; + break; + } + + case 's': + case 'S': + str_arg = tvs ? tv_str(tvs, &arg_idx, &tofree) + : va_arg(ap, const char *); + if (!str_arg) { + str_arg = "[NULL]"; + str_arg_l = 6; + } else if (!precision_specified) { + // make sure not to address string beyond the specified + // precision + str_arg_l = strlen(str_arg); + } else if (precision == 0) { + // truncate string if necessary as requested by precision + str_arg_l = 0; + } else { + // memchr on HP does not like n > 2^31 + // TODO(elmart): check if this still holds / is relevant + str_arg_l = (size_t)((char *)xmemscan(str_arg, + NUL, + MIN(precision, + 0x7fffffff)) + - str_arg); + } + if (fmt_spec == 'S') { + if (min_field_width != 0) { + min_field_width += (strlen(str_arg) + - mb_string2cells((char_u *)str_arg)); + } + if (precision) { + char_u *p1; + size_t i = 0; + + for (p1 = (char_u *)str_arg; *p1; + p1 += mb_ptr2len(p1)) { + i += (size_t)utf_ptr2cells(p1); + if (i > precision) { + break; } } - break; - - default: - break; + str_arg_l = (size_t)(p1 - (char_u *)str_arg); + } } break; - case 'd': - case 'u': - case 'b': case 'B': - case 'o': - case 'x': case 'X': - case 'p': { - // u, b, B, o, x, X and p conversion specifiers imply - // the value is unsigned; d implies a signed value - - // 0 if numeric argument is zero (or if pointer is NULL for 'p'), - // +1 if greater than zero (or non NULL for 'p'), - // -1 if negative (unsigned argument is never negative) - int arg_sign = 0; - - intmax_t arg = 0; - uintmax_t uarg = 0; - - // only defined for p conversion - const void *ptr_arg = NULL; - - if (fmt_spec == 'p') { - ptr_arg = tvs ? tv_ptr(tvs, &arg_idx) : va_arg(ap, void *); - if (ptr_arg) { - arg_sign = 1; - } - } else if (fmt_spec == 'd') { - // signed - switch (length_modifier) { - case '\0': { - arg = (int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int)); - break; - } - case 'h': { - // 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': { - arg = (tvs ? (long)tv_nr(tvs, &arg_idx) : va_arg(ap, long)); - break; - } - case '2': { - arg = ( - tvs + default: + break; + } + break; + + case 'd': + case 'u': + case 'b': + case 'B': + case 'o': + case 'x': + case 'X': + case 'p': { + // u, b, B, o, x, X and p conversion specifiers imply + // the value is unsigned; d implies a signed value + + // 0 if numeric argument is zero (or if pointer is NULL for 'p'), + // +1 if greater than zero (or non NULL for 'p'), + // -1 if negative (unsigned argument is never negative) + int arg_sign = 0; + + intmax_t arg = 0; + uintmax_t uarg = 0; + + // only defined for p conversion + const void *ptr_arg = NULL; + + if (fmt_spec == 'p') { + ptr_arg = tvs ? tv_ptr(tvs, &arg_idx) : va_arg(ap, void *); + if (ptr_arg) { + arg_sign = 1; + } + } else if (fmt_spec == 'd') { + // signed + switch (length_modifier) { + case '\0': + arg = (int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, int)); + break; + case 'h': + // 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': + arg = (tvs ? (long)tv_nr(tvs, &arg_idx) : va_arg(ap, long)); + break; + case '2': + arg = ( + tvs ? (long long)tv_nr(tvs, &arg_idx) // NOLINT (runtime/int) : va_arg(ap, long long)); // NOLINT (runtime/int) - break; - } - case 'z': { - arg = (tvs + break; + case 'z': + arg = (tvs ? (ptrdiff_t)tv_nr(tvs, &arg_idx) : va_arg(ap, ptrdiff_t)); - break; - } - } - if (arg > 0) { - arg_sign = 1; - } else if (arg < 0) { - arg_sign = -1; - } - } else { - // unsigned - switch (length_modifier) { - case '\0': { - uarg = (unsigned int)(tvs + break; + } + if (arg > 0) { + arg_sign = 1; + } else if (arg < 0) { + arg_sign = -1; + } + } else { + // unsigned + switch (length_modifier) { + case '\0': + uarg = (unsigned int)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int)); - break; - } - case 'h': { - uarg = (uint16_t)(tvs + break; + case 'h': + uarg = (uint16_t)(tvs ? tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned int)); - break; - } - case 'l': { - uarg = (tvs + break; + case 'l': + uarg = (tvs ? (unsigned long)tv_nr(tvs, &arg_idx) : va_arg(ap, unsigned long)); - break; - } - case '2': { - uarg = (uintmax_t)(unsigned long long)( // NOLINT (runtime/int) - tvs + break; + case '2': + uarg = (uintmax_t)(unsigned long long)( // NOLINT (runtime/int) + tvs ? ((unsigned long long) // NOLINT (runtime/int) tv_nr(tvs, &arg_idx)) : va_arg(ap, unsigned long long)); // NOLINT (runtime/int) - break; - } - case 'z': { - uarg = (tvs + break; + case 'z': + uarg = (tvs ? (size_t)tv_nr(tvs, &arg_idx) : va_arg(ap, size_t)); - break; - } - } - arg_sign = (uarg != 0); + break; } + arg_sign = (uarg != 0); + } - str_arg = tmp; - str_arg_l = 0; + str_arg = tmp; + str_arg_l = 0; - // For d, i, u, o, x, and X conversions, if precision is specified, - // '0' flag should be ignored. This is so with Solaris 2.6, Digital - // UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. - if (precision_specified) { - zero_padding = 0; - } + // For d, i, u, o, x, and X conversions, if precision is specified, + // '0' flag should be ignored. This is so with Solaris 2.6, Digital + // UNIX 4.0, HPUX 10, Linux, FreeBSD, NetBSD; but not with Perl. + if (precision_specified) { + zero_padding = 0; + } - if (fmt_spec == 'd') { - if (force_sign && arg_sign >= 0) { - tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; - } - // leave negative numbers for snprintf to handle, to - // avoid handling tricky cases like (short int)-32768 - } else if (alternate_form) { - if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' - || fmt_spec == 'b' || fmt_spec == 'B')) { - tmp[str_arg_l++] = '0'; - tmp[str_arg_l++] = fmt_spec; - } - // alternate form should have no effect for p * conversion, but ... + if (fmt_spec == 'd') { + if (force_sign && arg_sign >= 0) { + tmp[str_arg_l++] = space_for_positive ? ' ' : '+'; } - - zero_padding_insertion_ind = str_arg_l; - if (!precision_specified) { - precision = 1; // default precision is 1 + // leave negative numbers for snprintf to handle, to + // avoid handling tricky cases like (short int)-32768 + } else if (alternate_form) { + if (arg_sign != 0 && (fmt_spec == 'x' || fmt_spec == 'X' + || fmt_spec == 'b' || fmt_spec == 'B')) { + tmp[str_arg_l++] = '0'; + tmp[str_arg_l++] = fmt_spec; } - if (precision == 0 && arg_sign == 0) { - // when zero value is formatted with an explicit precision 0, - // resulting formatted string is empty (d, i, u, b, B, o, x, X, p) - } else { - switch (fmt_spec) { - case 'p': { // pointer - str_arg_l += (size_t)snprintf(tmp + str_arg_l, - sizeof(tmp) - str_arg_l, - "%p", ptr_arg); - break; - } - case 'd': { // signed - str_arg_l += (size_t)snprintf(tmp + str_arg_l, - sizeof(tmp) - str_arg_l, - "%" PRIdMAX, arg); - break; - } - case 'b': case 'B': { // binary - size_t bits = 0; - for (bits = sizeof(uintmax_t) * 8; bits > 0; bits--) { - if ((uarg >> (bits - 1)) & 0x1) { - break; - } - } + // alternate form should have no effect for p * conversion, but ... + } - while (bits > 0) { - tmp[str_arg_l++] = ((uarg >> --bits) & 0x1) ? '1' : '0'; - } - break; - } - default: { // unsigned - // construct a simple format string for snprintf - char f[] = "%" PRIuMAX; - f[sizeof("%" PRIuMAX) - 1 - 1] = fmt_spec; - assert(PRIuMAX[sizeof(PRIuMAX) - 1 - 1] == 'u'); - str_arg_l += (size_t)snprintf(tmp + str_arg_l, - sizeof(tmp) - str_arg_l, - f, uarg); + zero_padding_insertion_ind = str_arg_l; + if (!precision_specified) { + precision = 1; // default precision is 1 + } + if (precision == 0 && arg_sign == 0) { + // when zero value is formatted with an explicit precision 0, + // resulting formatted string is empty (d, i, u, b, B, o, x, X, p) + } else { + switch (fmt_spec) { + case 'p': // pointer + str_arg_l += (size_t)snprintf(tmp + str_arg_l, + sizeof(tmp) - str_arg_l, + "%p", ptr_arg); + break; + case 'd': // signed + str_arg_l += (size_t)snprintf(tmp + str_arg_l, + sizeof(tmp) - str_arg_l, + "%" PRIdMAX, arg); + break; + case 'b': + case 'B': { // binary + size_t bits = 0; + for (bits = sizeof(uintmax_t) * 8; bits > 0; bits--) { + if ((uarg >> (bits - 1)) & 0x1) { break; } } - assert(str_arg_l < sizeof(tmp)); - // include the optional minus sign and possible "0x" in the region - // before the zero padding insertion point - if (zero_padding_insertion_ind < str_arg_l - && tmp[zero_padding_insertion_ind] == '-') { - zero_padding_insertion_ind++; - } - if (zero_padding_insertion_ind + 1 < str_arg_l - && tmp[zero_padding_insertion_ind] == '0' - && (tmp[zero_padding_insertion_ind + 1] == 'x' - || tmp[zero_padding_insertion_ind + 1] == 'X' - || tmp[zero_padding_insertion_ind + 1] == 'b' - || tmp[zero_padding_insertion_ind + 1] == 'B')) { - zero_padding_insertion_ind += 2; + while (bits > 0) { + tmp[str_arg_l++] = ((uarg >> --bits) & 0x1) ? '1' : '0'; } + break; + } + default: { // unsigned + // construct a simple format string for snprintf + char f[] = "%" PRIuMAX; + f[sizeof("%" PRIuMAX) - 1 - 1] = fmt_spec; + assert(PRIuMAX[sizeof(PRIuMAX) - 1 - 1] == 'u'); + str_arg_l += (size_t)snprintf(tmp + str_arg_l, + sizeof(tmp) - str_arg_l, + f, uarg); + break; + } } + assert(str_arg_l < sizeof(tmp)); - { - size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; - - if (alternate_form && fmt_spec == 'o' - // unless zero is already the first character - && !(zero_padding_insertion_ind < str_arg_l - && tmp[zero_padding_insertion_ind] == '0')) { - // assure leading zero for alternate-form octal numbers - if (!precision_specified - || precision < num_of_digits + 1) { - // precision is increased to force the first character to be - // zero, except if a zero value is formatted with an explicit - // precision of zero - precision = num_of_digits + 1; - } - } - // zero padding to specified precision? - if (num_of_digits < precision) { - number_of_zeros_to_pad = precision - num_of_digits; - } + // include the optional minus sign and possible "0x" in the region + // before the zero padding insertion point + if (zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '-') { + zero_padding_insertion_ind++; } - // zero padding to specified minimal field width? - if (!justify_left && zero_padding) { - const int n = (int)(min_field_width - (str_arg_l - + number_of_zeros_to_pad)); - if (n > 0) { - number_of_zeros_to_pad += (size_t)n; - } + if (zero_padding_insertion_ind + 1 < str_arg_l + && tmp[zero_padding_insertion_ind] == '0' + && (tmp[zero_padding_insertion_ind + 1] == 'x' + || tmp[zero_padding_insertion_ind + 1] == 'X' + || tmp[zero_padding_insertion_ind + 1] == 'b' + || tmp[zero_padding_insertion_ind + 1] == 'B')) { + zero_padding_insertion_ind += 2; } - break; } - case 'f': - case 'F': - case 'e': - case 'E': - case 'g': - case 'G': - { - // floating point - char format[40]; - int remove_trailing_zeroes = false; - - double f = tvs ? tv_float(tvs, &arg_idx) : va_arg(ap, double); - double abs_f = f < 0 ? -f : f; - - if (fmt_spec == 'g' || fmt_spec == 'G') { - // can't use %g directly, cause it prints "1.0" as "1" - if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0) { - fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f'; - } else { - fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; - } - remove_trailing_zeroes = true; + { + size_t num_of_digits = str_arg_l - zero_padding_insertion_ind; + + if (alternate_form && fmt_spec == 'o' + // unless zero is already the first character + && !(zero_padding_insertion_ind < str_arg_l + && tmp[zero_padding_insertion_ind] == '0')) { + // assure leading zero for alternate-form octal numbers + if (!precision_specified + || precision < num_of_digits + 1) { + // precision is increased to force the first character to be + // zero, except if a zero value is formatted with an explicit + // precision of zero + precision = num_of_digits + 1; } + } + // zero padding to specified precision? + if (num_of_digits < precision) { + number_of_zeros_to_pad = precision - num_of_digits; + } + } + // zero padding to specified minimal field width? + if (!justify_left && zero_padding) { + const int n = (int)(min_field_width - (str_arg_l + + number_of_zeros_to_pad)); + if (n > 0) { + number_of_zeros_to_pad += (size_t)n; + } + } + break; + } - if (xisinf(f) - || (strchr("fF", fmt_spec) != NULL && abs_f > 1.0e307)) { - xstrlcpy(tmp, infinity_str(f > 0.0, fmt_spec, - force_sign, space_for_positive), - sizeof(tmp)); - str_arg_l = strlen(tmp); - zero_padding = 0; - } else if (xisnan(f)) { - // Not a number: nan or NAN - memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4); - str_arg_l = 3; - zero_padding = 0; - } else { - // Regular float number - format[0] = '%'; - size_t l = 1; - if (force_sign) { - format[l++] = space_for_positive ? ' ' : '+'; - } - if (precision_specified) { - size_t max_prec = TMP_LEN - 10; + case 'f': + case 'F': + case 'e': + case 'E': + case 'g': + case 'G': { + // floating point + char format[40]; + int remove_trailing_zeroes = false; + + double f = tvs ? tv_float(tvs, &arg_idx) : va_arg(ap, double); + double abs_f = f < 0 ? -f : f; + + if (fmt_spec == 'g' || fmt_spec == 'G') { + // can't use %g directly, cause it prints "1.0" as "1" + if ((abs_f >= 0.001 && abs_f < 10000000.0) || abs_f == 0.0) { + fmt_spec = ASCII_ISUPPER(fmt_spec) ? 'F' : 'f'; + } else { + fmt_spec = fmt_spec == 'g' ? 'e' : 'E'; + } + remove_trailing_zeroes = true; + } - // make sure we don't get more digits than we have room for - if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { - max_prec -= (size_t)log10(abs_f); - } - if (precision > max_prec) { - precision = max_prec; - } - l += (size_t)snprintf(format + l, sizeof(format) - l, ".%d", - (int)precision); - } + if (xisinf(f) + || (strchr("fF", fmt_spec) != NULL && abs_f > 1.0e307)) { + xstrlcpy(tmp, infinity_str(f > 0.0, fmt_spec, + force_sign, space_for_positive), + sizeof(tmp)); + str_arg_l = strlen(tmp); + zero_padding = 0; + } else if (xisnan(f)) { + // Not a number: nan or NAN + memmove(tmp, ASCII_ISUPPER(fmt_spec) ? "NAN" : "nan", 4); + str_arg_l = 3; + zero_padding = 0; + } else { + // Regular float number + format[0] = '%'; + size_t l = 1; + if (force_sign) { + format[l++] = space_for_positive ? ' ' : '+'; + } + if (precision_specified) { + size_t max_prec = TMP_LEN - 10; - // Cast to char to avoid a conversion warning on Ubuntu 12.04. - assert(l + 1 < sizeof(format)); - format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); - format[l + 1] = NUL; + // make sure we don't get more digits than we have room for + if ((fmt_spec == 'f' || fmt_spec == 'F') && abs_f > 1.0) { + max_prec -= (size_t)log10(abs_f); + } + if (precision > max_prec) { + precision = max_prec; + } + l += (size_t)snprintf(format + l, sizeof(format) - l, ".%d", + (int)precision); + } - str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); - assert(str_arg_l < sizeof(tmp)); + // Cast to char to avoid a conversion warning on Ubuntu 12.04. + assert(l + 1 < sizeof(format)); + format[l] = (char)(fmt_spec == 'F' ? 'f' : fmt_spec); + format[l + 1] = NUL; - if (remove_trailing_zeroes) { - int i; - char *tp; + str_arg_l = (size_t)snprintf(tmp, sizeof(tmp), format, f); + assert(str_arg_l < sizeof(tmp)); - // using %g or %G: remove superfluous zeroes - if (fmt_spec == 'f' || fmt_spec == 'F') { - tp = tmp + str_arg_l - 1; - } else { - tp = (char *)vim_strchr((char_u *)tmp, - fmt_spec == 'e' ? 'e' : 'E'); - if (tp) { - // remove superfluous '+' and leading zeroes from exponent - if (tp[1] == '+') { - // change "1.0e+07" to "1.0e07" - STRMOVE(tp + 1, tp + 2); - str_arg_l--; - } - i = (tp[1] == '-') ? 2 : 1; - while (tp[i] == '0') { - // change "1.0e07" to "1.0e7" - STRMOVE(tp + i, tp + i + 1); - str_arg_l--; - } - tp--; - } - } + if (remove_trailing_zeroes) { + int i; + char *tp; - if (tp != NULL && !precision_specified) { - // remove trailing zeroes, but keep the one just after a dot - while (tp > tmp + 2 && *tp == '0' && tp[-1] != '.') { - STRMOVE(tp, tp + 1); - tp--; - str_arg_l--; - } + // using %g or %G: remove superfluous zeroes + if (fmt_spec == 'f' || fmt_spec == 'F') { + tp = tmp + str_arg_l - 1; + } else { + tp = (char *)vim_strchr((char_u *)tmp, + fmt_spec == 'e' ? 'e' : 'E'); + if (tp) { + // remove superfluous '+' and leading zeroes from exponent + if (tp[1] == '+') { + // change "1.0e+07" to "1.0e07" + STRMOVE(tp + 1, tp + 2); + str_arg_l--; } - } else { - // Be consistent: some printf("%e") use 1.0e+12 and some - // 1.0e+012; remove one zero in the last case. - char *tp = (char *)vim_strchr((char_u *)tmp, - fmt_spec == 'e' ? 'e' : 'E'); - if (tp && (tp[1] == '+' || tp[1] == '-') && tp[2] == '0' - && ascii_isdigit(tp[3]) && ascii_isdigit(tp[4])) { - STRMOVE(tp + 2, tp + 3); + i = (tp[1] == '-') ? 2 : 1; + while (tp[i] == '0') { + // change "1.0e07" to "1.0e7" + STRMOVE(tp + i, tp + i + 1); str_arg_l--; } + tp--; } } - if (zero_padding && min_field_width > str_arg_l - && (tmp[0] == '-' || force_sign)) { - // Padding 0's should be inserted after the sign. - number_of_zeros_to_pad = min_field_width - str_arg_l; - zero_padding_insertion_ind = 1; + + if (tp != NULL && !precision_specified) { + // remove trailing zeroes, but keep the one just after a dot + while (tp > tmp + 2 && *tp == '0' && tp[-1] != '.') { + STRMOVE(tp, tp + 1); + tp--; + str_arg_l--; + } + } + } else { + // Be consistent: some printf("%e") use 1.0e+12 and some + // 1.0e+012; remove one zero in the last case. + char *tp = (char *)vim_strchr((char_u *)tmp, + fmt_spec == 'e' ? 'e' : 'E'); + if (tp && (tp[1] == '+' || tp[1] == '-') && tp[2] == '0' + && ascii_isdigit(tp[3]) && ascii_isdigit(tp[4])) { + STRMOVE(tp + 2, tp + 3); + str_arg_l--; } - str_arg = tmp; - break; } + } + if (zero_padding && min_field_width > str_arg_l + && (tmp[0] == '-' || force_sign)) { + // Padding 0's should be inserted after the sign. + number_of_zeros_to_pad = min_field_width - str_arg_l; + zero_padding_insertion_ind = 1; + } + str_arg = tmp; + break; + } - default: - // unrecognized conversion specifier, keep format string as-is - zero_padding = 0; // turn zero padding off for non-numeric conversion - justify_left = 1; - min_field_width = 0; // reset flags - - // discard the unrecognized conversion, just keep - // the unrecognized conversion character - str_arg = p; - str_arg_l = 0; - if (*p) { - str_arg_l++; // include invalid conversion specifier - } - // unchanged if not at end-of-string - break; + default: + // unrecognized conversion specifier, keep format string as-is + zero_padding = 0; // turn zero padding off for non-numeric conversion + justify_left = 1; + min_field_width = 0; // reset flags + + // discard the unrecognized conversion, just keep + // the unrecognized conversion character + str_arg = p; + str_arg_l = 0; + if (*p) { + str_arg_l++; // include invalid conversion specifier + } + // unchanged if not at end-of-string + break; } if (*p) { diff --git a/src/nvim/syntax.c b/src/nvim/syntax.c index 559a7380eb..d2c94d9fe8 100644 --- a/src/nvim/syntax.c +++ b/src/nvim/syntax.c @@ -383,7 +383,7 @@ static int current_line_id = 0; // unique number for current line #define CUR_STATE(idx) ((stateitem_T *)(current_state.ga_data))[idx] static bool syn_time_on = false; -# define IF_SYN_TIME(p) (p) +#define IF_SYN_TIME(p) (p) // Set the timeout used for syntax highlighting. // Use NULL to reset, no timeout. @@ -578,7 +578,7 @@ void syntax_start(win_T *wp, linenr_T lnum) static void clear_syn_state(synstate_T *p) { if (p->sst_stacksize > SST_FIX_STATES) { -# define UNREF_BUFSTATE_EXTMATCH(bs) unref_extmatch((bs)->bs_extmatch) +#define UNREF_BUFSTATE_EXTMATCH(bs) unref_extmatch((bs)->bs_extmatch) GA_DEEP_CLEAR(&(p->sst_union.sst_ga), bufstate_T, UNREF_BUFSTATE_EXTMATCH); } else { for (int i = 0; i < p->sst_stacksize; i++) { @@ -592,7 +592,7 @@ static void clear_syn_state(synstate_T *p) */ static void clear_current_state(void) { -# define UNREF_STATEITEM_EXTMATCH(si) unref_extmatch((si)->si_extmatch) +#define UNREF_STATEITEM_EXTMATCH(si) unref_extmatch((si)->si_extmatch) GA_DEEP_CLEAR(¤t_state, stateitem_T, UNREF_STATEITEM_EXTMATCH); } @@ -1703,7 +1703,7 @@ static int syn_current_attr(const bool syncing, const bool displaying, bool *con regmmatch_T regmatch; lpos_T pos; reg_extmatch_T *cur_extmatch = NULL; - char_u buf_chartab[32]; // chartab array for syn iskeyword + char_u buf_chartab[32]; // chartab array for syn iskeyword char_u *line; // current line. NOTE: becomes invalid after // looking for a pattern match! @@ -5654,7 +5654,7 @@ static int in_id_list(stateitem_T *cur_si, int16_t *list, struct sp_syn *ssp, in struct subcommand { char *name; // subcommand name - void (*func)(exarg_T *, int); // function to call + void (*func)(exarg_T *, int); // function to call }; static struct subcommand subcommands[] = diff --git a/src/nvim/tag.c b/src/nvim/tag.c index c63cdad098..768cca284b 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -10,9 +10,7 @@ #include <stdbool.h> #include <string.h> -#include "nvim/vim.h" #include "nvim/ascii.h" -#include "nvim/tag.h" #include "nvim/buffer.h" #include "nvim/charset.h" #include "nvim/cursor.h" @@ -22,18 +20,21 @@ #include "nvim/ex_cmds2.h" #include "nvim/ex_docmd.h" #include "nvim/ex_getln.h" +#include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/garray.h" #include "nvim/if_cscope.h" #include "nvim/mark.h" #include "nvim/mbyte.h" +#include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/file_search.h" -#include "nvim/garray.h" -#include "nvim/memory.h" #include "nvim/move.h" #include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/os/os.h" +#include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" #include "nvim/quickfix.h" @@ -41,30 +42,29 @@ #include "nvim/screen.h" #include "nvim/search.h" #include "nvim/strings.h" +#include "nvim/tag.h" #include "nvim/ui.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/os/os.h" -#include "nvim/os/time.h" -#include "nvim/os/input.h" /* * Structure to hold pointers to various items in a tag line. */ typedef struct tag_pointers { // filled in by parse_tag_line(): - char_u *tagname; // start of tag name (skip "file:") - char_u *tagname_end; // char after tag name - char_u *fname; // first char of file name - char_u *fname_end; // char after file name - char_u *command; // first char of command + char_u *tagname; // start of tag name (skip "file:") + char_u *tagname_end; // char after tag name + char_u *fname; // first char of file name + char_u *fname_end; // char after file name + char_u *command; // first char of command // filled in by parse_match(): - char_u *command_end; // first char after command - char_u *tag_fname; // file name of the tags file. This is used + char_u *command_end; // first char after command + char_u *tag_fname; // file name of the tags file. This is used // when 'tr' is set. - char_u *tagkind; // "kind:" value - char_u *tagkind_end; // end of tagkind - char_u *user_data; // user_data string - char_u *user_data_end; // end of user_data + char_u *tagkind; // "kind:" value + char_u *tagkind_end; // end of tagkind + char_u *user_data; // user_data string + char_u *user_data_end; // end of user_data linenr_T tagline; // "line:" value } tagptrs_T; @@ -72,11 +72,11 @@ typedef struct tag_pointers { * Structure to hold info about the tag pattern being used. */ typedef struct { - char_u *pat; /* the pattern */ - int len; /* length of pat[] */ - char_u *head; /* start of pattern head */ - int headlen; /* length of head[] */ - regmatch_T regmatch; /* regexp program, may be NULL */ + char_u *pat; // the pattern + int len; // length of pat[] + char_u *head; // start of pattern head + int headlen; // length of head[] + regmatch_T regmatch; // regexp program, may be NULL } pat_T; // The matching tags are first stored in one of the hash tables. In @@ -93,11 +93,11 @@ typedef struct { #define MT_MASK 7 // mask for printing priority #define MT_COUNT 16 -static char *mt_names[MT_COUNT/2] = -{"FSC", "F C", "F ", "FS ", " SC", " C", " ", " S "}; +static char *mt_names[MT_COUNT/2] = +{ "FSC", "F C", "F ", "FS ", " SC", " C", " ", " S " }; -#define NOTAGFILE 99 /* return value for jumpto_tag */ -static char_u *nofile_fname = NULL; /* fname for NOTAGFILE error */ +#define NOTAGFILE 99 // return value for jumpto_tag +static char_u *nofile_fname = NULL; // fname for NOTAGFILE error #ifdef INCLUDE_GENERATED_DECLARATIONS @@ -111,7 +111,7 @@ static char_u *recurmsg static char_u *tfu_inv_ret_msg = (char_u *)N_("E987: invalid return value from tagfunc"); -static char_u *tagmatchname = NULL; /* name of last used tag */ +static char_u *tagmatchname = NULL; // name of last used tag /* * Tag for preview window is remembered separately, to avoid messing up the @@ -124,36 +124,31 @@ static int tfu_in_use = false; // disallow recursive call of tagfunc // Used instead of NUL to separate tag fields in the growarrays. #define TAG_SEP 0x02 -/* - * Jump to tag; handling of tag commands and tag stack - * - * *tag != NUL: ":tag {tag}", jump to new tag, add to tag stack - * - * type == DT_TAG: ":tag [tag]", jump to newer position or same tag again - * type == DT_HELP: like DT_TAG, but don't use regexp. - * type == DT_POP: ":pop" or CTRL-T, jump to old position - * type == DT_NEXT: jump to next match of same tag - * type == DT_PREV: jump to previous match of same tag - * type == DT_FIRST: jump to first match of same tag - * type == DT_LAST: jump to last match of same tag - * type == DT_SELECT: ":tselect [tag]", select tag from a list of all matches - * type == DT_JUMP: ":tjump [tag]", jump to tag or select tag from a list - * type == DT_CSCOPE: use cscope to find the tag - * type == DT_LTAG: use location list for displaying tag matches - * type == DT_FREE: free cached matches - * - * for cscope, returns TRUE if we jumped to tag or aborted, FALSE otherwise - */ -int -do_tag( - char_u *tag, // tag (pattern) to jump to - int type, - int count, - int forceit, // :ta with ! - int verbose // print "tag not found" message -) +/// Jump to tag; handling of tag commands and tag stack +/// +/// *tag != NUL: ":tag {tag}", jump to new tag, add to tag stack +/// +/// type == DT_TAG: ":tag [tag]", jump to newer position or same tag again +/// type == DT_HELP: like DT_TAG, but don't use regexp. +/// type == DT_POP: ":pop" or CTRL-T, jump to old position +/// type == DT_NEXT: jump to next match of same tag +/// type == DT_PREV: jump to previous match of same tag +/// type == DT_FIRST: jump to first match of same tag +/// type == DT_LAST: jump to last match of same tag +/// type == DT_SELECT: ":tselect [tag]", select tag from a list of all matches +/// type == DT_JUMP: ":tjump [tag]", jump to tag or select tag from a list +/// type == DT_CSCOPE: use cscope to find the tag +/// type == DT_LTAG: use location list for displaying tag matches +/// type == DT_FREE: free cached matches +/// +/// for cscope, returns TRUE if we jumped to tag or aborted, FALSE otherwise +/// +/// @param tag tag (pattern) to jump to +/// @param forceit :ta with ! +/// @param verbose print "tag not found" message +int do_tag(char_u *tag, int type, int count, int forceit, int verbose) { - taggy_T *tagstack = curwin->w_tagstack; + taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; int tagstacklen = curwin->w_tagstacklen; int cur_match = 0; @@ -170,16 +165,16 @@ do_tag( fmark_T saved_fmark; int jumped_to_tag = false; int new_num_matches; - char_u **new_matches; + char_u **new_matches; int use_tagstack; int skip_msg = false; char_u *buf_ffname = curbuf->b_ffname; // name for priority computation int use_tfu = 1; - /* remember the matches for the last used tag */ + // remember the matches for the last used tag static int num_matches = 0; - static int max_num_matches = 0; /* limit used for match search */ - static char_u **matches = NULL; + static int max_num_matches = 0; // limit used for match search + static char_u **matches = NULL; static int flags; if (tfu_in_use) { @@ -189,7 +184,7 @@ do_tag( #ifdef EXITFREE if (type == DT_FREE) { - /* remove the list of matches */ + // remove the list of matches FreeWild(num_matches, matches); cs_free_tags(); num_matches = 0; @@ -207,7 +202,7 @@ do_tag( free_string_option(nofile_fname); nofile_fname = NULL; - clearpos(&saved_fmark.mark); /* shutup gcc 4.0 */ + clearpos(&saved_fmark.mark); // shutup gcc 4.0 saved_fmark.fnum = 0; // Don't add a tag to the tagstack if 'tagstack' has been reset. @@ -226,7 +221,7 @@ do_tag( use_tagstack = true; } - /* new pattern, add to the tag stack */ + // new pattern, add to the tag stack if (*tag != NUL && (type == DT_TAG || type == DT_SELECT || type == DT_JUMP || type == DT_LTAG @@ -252,7 +247,7 @@ do_tag( tagstack_clear_entry(&tagstack[--tagstacklen]); } - /* if the tagstack is full: remove oldest entry */ + // if the tagstack is full: remove oldest entry if (++tagstacklen > TAGSTACKSIZE) { tagstacklen = TAGSTACKSIZE; tagstack_clear_entry(&tagstack[0]); @@ -293,7 +288,7 @@ do_tag( * way to the bottom now. */ tagstackidx = 0; - } else if (tagstackidx >= tagstacklen) { // count == 0? + } else if (tagstackidx >= tagstacklen) { // count == 0? EMSG(_(topmsg)); goto end_do_tag; } @@ -321,8 +316,9 @@ do_tag( curwin->w_cursor.col = saved_fmark.mark.col; curwin->w_set_curswant = true; check_cursor(); - if ((fdo_flags & FDO_TAG) && old_KeyTyped) + if ((fdo_flags & FDO_TAG) && old_KeyTyped) { foldOpenCursor(); + } // remove the old list of matches FreeWild(num_matches, matches); @@ -333,8 +329,7 @@ do_tag( } if (type == DT_TAG - || type == DT_LTAG - ) { + || type == DT_LTAG) { if (g_do_tagpreview != 0) { cur_match = ptag_entry.cur_match; cur_fnum = ptag_entry.cur_fnum; @@ -350,7 +345,7 @@ do_tag( tagstackidx = tagstacklen - 1; EMSG(_(topmsg)); save_pos = false; - } else if (tagstackidx < 0) { // must have been count == 0 + } else if (tagstackidx < 0) { // must have been count == 0 EMSG(_(bottommsg)); tagstackidx = 0; goto end_do_tag; @@ -367,23 +362,28 @@ do_tag( cur_match = ptag_entry.cur_match; cur_fnum = ptag_entry.cur_fnum; } else { - if (--tagstackidx < 0) + if (--tagstackidx < 0) { tagstackidx = 0; + } cur_match = tagstack[tagstackidx].cur_match; cur_fnum = tagstack[tagstackidx].cur_fnum; } switch (type) { - case DT_FIRST: cur_match = count - 1; break; + case DT_FIRST: + cur_match = count - 1; break; case DT_SELECT: case DT_JUMP: case DT_CSCOPE: - case DT_LAST: cur_match = MAXCOL - 1; break; - case DT_NEXT: cur_match += count; break; - case DT_PREV: cur_match -= count; break; + case DT_LAST: + cur_match = MAXCOL - 1; break; + case DT_NEXT: + cur_match += count; break; + case DT_PREV: + cur_match -= count; break; } - if (cur_match >= MAXCOL) + if (cur_match >= MAXCOL) { cur_match = MAXCOL - 1; - else if (cur_match < 0) { + } else if (cur_match < 0) { EMSG(_("E425: Cannot go before first matching tag")); skip_msg = true; cur_match = 0; @@ -424,8 +424,9 @@ do_tag( if (cur_fnum != curbuf->b_fnum) { buf_T *buf = buflist_findnr(cur_fnum); - if (buf != NULL) + if (buf != NULL) { buf_ffname = buf->b_ffname; + } } /* @@ -433,7 +434,7 @@ do_tag( */ for (;; ) { int other_name; - char_u *name; + char_u *name; // When desired match not found yet, try to find it (and others). if (use_tagstack) { @@ -466,8 +467,9 @@ do_tag( if (!no_regexp && *name == '/') { flags = TAG_REGEXP; ++name; - } else + } else { flags = TAG_NOIC; + } if (type == DT_CSCOPE) { flags = TAG_CSCOPE; @@ -490,9 +492,9 @@ do_tag( * to the start. Avoids that the order changes when using * ":tnext" and jumping to another file. */ if (!new_tag && !other_name) { - int j, k; - int idx = 0; - tagptrs_T tagp, tagp2; + int j, k; + int idx = 0; + tagptrs_T tagp, tagp2; // Find the position of each old match in the new list. Need // to use parse_match() to find the tag line. @@ -517,8 +519,9 @@ do_tag( } if (num_matches <= 0) { - if (verbose) + if (verbose) { EMSG2(_("E426: tag not found: %s"), name); + } g_do_tagpreview = 0; } else { bool ask_for_selection = false; @@ -533,7 +536,7 @@ do_tag( } else if (type == DT_SELECT || (type == DT_JUMP && num_matches > 1)) { print_tag_list(new_tag, use_tagstack, num_matches, matches); ask_for_selection = true; - } else if (type == DT_LTAG) { + } else if (type == DT_LTAG) { if (add_llist_tags(tag, num_matches, matches) == FAIL) { goto end_do_tag; } @@ -563,16 +566,17 @@ do_tag( * There will be an EMSG("file doesn't exist") below then. */ if ((type == DT_NEXT || type == DT_FIRST) && nofile_fname == NULL) { - if (num_matches == 1) + if (num_matches == 1) { EMSG(_("E427: There is only one matching tag")); - else + } else { EMSG(_("E428: Cannot go beyond last matching tag")); + } skip_msg = true; } cur_match = num_matches - 1; } if (use_tagstack) { - tagptrs_T tagp2; + tagptrs_T tagp2; tagstack[tagstackidx].cur_match = cur_match; tagstack[tagstackidx].cur_fnum = cur_fnum; @@ -581,12 +585,12 @@ do_tag( if (use_tfu && parse_match(matches[cur_match], &tagp2) == OK && tagp2.user_data) { XFREE_CLEAR(tagstack[tagstackidx].user_data); - tagstack[tagstackidx].user_data = vim_strnsave( - tagp2.user_data, tagp2.user_data_end - tagp2.user_data); + tagstack[tagstackidx].user_data = vim_strnsave(tagp2.user_data, + tagp2.user_data_end - tagp2.user_data); } tagstackidx++; - } else if (g_do_tagpreview != 0) { + } else if (g_do_tagpreview != 0) { ptag_entry.cur_match = cur_match; ptag_entry.cur_fnum = cur_fnum; } @@ -595,8 +599,9 @@ do_tag( * Only when going to try the next match, report that the previous * file didn't exist. Otherwise an EMSG() is given below. */ - if (nofile_fname != NULL && error_cur_match != cur_match) + if (nofile_fname != NULL && error_cur_match != cur_match) { smsg(_("File \"%s\" does not exist"), nofile_fname); + } ic = (matches[cur_match][0] & MT_IC_OFF); @@ -631,7 +636,7 @@ do_tag( // Let the SwapExists event know what tag we are jumping to. vim_snprintf((char *)IObuff, IOSIZE, ":ta %s\r", name); - set_vim_var_string(VV_SWAPCOMMAND, (char *) IObuff, -1); + set_vim_var_string(VV_SWAPCOMMAND, (char *)IObuff, -1); /* * Jump to the desired match. @@ -648,11 +653,12 @@ do_tag( && (max_num_matches != MAXCOL || cur_match < num_matches - 1))) { error_cur_match = cur_match; - if (use_tagstack) + if (use_tagstack) { --tagstackidx; - if (type == DT_PREV) + } + if (type == DT_PREV) { --cur_match; - else { + } else { type = DT_NEXT; ++cur_match; } @@ -662,8 +668,9 @@ do_tag( } else { /* We may have jumped to another window, check that * tagstackidx is still valid. */ - if (use_tagstack && tagstackidx > curwin->w_tagstacklen) + if (use_tagstack && tagstackidx > curwin->w_tagstacklen) { tagstackidx = curwin->w_tagstackidx; + } jumped_to_tag = true; } } @@ -684,331 +691,322 @@ end_do_tag: // // List all the matching tags. // -static void -print_tag_list( - int new_tag, - int use_tagstack, - int num_matches, - char_u **matches) +static void print_tag_list(int new_tag, int use_tagstack, int num_matches, char_u **matches) { - taggy_T *tagstack = curwin->w_tagstack; - int tagstackidx = curwin->w_tagstackidx; - int i; - char_u *p; - char_u *command_end; - tagptrs_T tagp; - int taglen; - int attr; - - // Assume that the first match indicates how long the tags can - // be, and align the file names to that. - parse_match(matches[0], &tagp); - taglen = (int)(tagp.tagname_end - tagp.tagname + 2); - if (taglen < 18) { - taglen = 18; - } - if (taglen > Columns - 25) { - taglen = MAXCOL; - } - if (msg_col == 0) { - msg_didout = false; // overwrite previous message - } - msg_start(); - msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T)); - msg_clr_eos(); + taggy_T *tagstack = curwin->w_tagstack; + int tagstackidx = curwin->w_tagstackidx; + int i; + char_u *p; + char_u *command_end; + tagptrs_T tagp; + int taglen; + int attr; + + // Assume that the first match indicates how long the tags can + // be, and align the file names to that. + parse_match(matches[0], &tagp); + taglen = (int)(tagp.tagname_end - tagp.tagname + 2); + if (taglen < 18) { + taglen = 18; + } + if (taglen > Columns - 25) { + taglen = MAXCOL; + } + if (msg_col == 0) { + msg_didout = false; // overwrite previous message + } + msg_start(); + msg_puts_attr(_(" # pri kind tag"), HL_ATTR(HLF_T)); + msg_clr_eos(); + taglen_advance(taglen); + msg_puts_attr(_("file\n"), HL_ATTR(HLF_T)); + + for (i = 0; i < num_matches && !got_int; i++) { + parse_match(matches[i], &tagp); + if (!new_tag && ( + (g_do_tagpreview != 0 + && i == ptag_entry.cur_match) + || (use_tagstack + && i == tagstack[tagstackidx].cur_match))) { + *IObuff = '>'; + } else { + *IObuff = ' '; + } + vim_snprintf((char *)IObuff + 1, IOSIZE - 1, + "%2d %s ", i + 1, + mt_names[matches[i][0] & MT_MASK]); + msg_puts((char *)IObuff); + if (tagp.tagkind != NULL) { + msg_outtrans_len(tagp.tagkind, + (int)(tagp.tagkind_end - tagp.tagkind)); + } + msg_advance(13); + msg_outtrans_len_attr(tagp.tagname, + (int)(tagp.tagname_end - tagp.tagname), + HL_ATTR(HLF_T)); + msg_putchar(' '); taglen_advance(taglen); - msg_puts_attr(_("file\n"), HL_ATTR(HLF_T)); - - for (i = 0; i < num_matches && !got_int; i++) { - parse_match(matches[i], &tagp); - if (!new_tag && ( - (g_do_tagpreview != 0 - && i == ptag_entry.cur_match) - || (use_tagstack - && i == tagstack[tagstackidx].cur_match))) { - *IObuff = '>'; - } else { - *IObuff = ' '; + + // Find out the actual file name. If it is long, truncate + // it and put "..." in the middle + p = tag_full_fname(&tagp); + if (p != NULL) { + msg_outtrans_attr(p, HL_ATTR(HLF_D)); + XFREE_CLEAR(p); + } + if (msg_col > 0) { + msg_putchar('\n'); + } + if (got_int) { + break; + } + msg_advance(15); + + // print any extra fields + command_end = tagp.command_end; + if (command_end != NULL) { + p = command_end + 3; + while (*p && *p != '\r' && *p != '\n') { + while (*p == TAB) { + p++; } - vim_snprintf((char *)IObuff + 1, IOSIZE - 1, - "%2d %s ", i + 1, - mt_names[matches[i][0] & MT_MASK]); - msg_puts((char *)IObuff); - if (tagp.tagkind != NULL) { - msg_outtrans_len(tagp.tagkind, - (int)(tagp.tagkind_end - tagp.tagkind)); + + // skip "file:" without a value (static tag) + if (STRNCMP(p, "file:", 5) == 0 && ascii_isspace(p[5])) { + p += 5; + continue; } - msg_advance(13); - msg_outtrans_len_attr(tagp.tagname, - (int)(tagp.tagname_end - tagp.tagname), - HL_ATTR(HLF_T)); - msg_putchar(' '); - taglen_advance(taglen); - - // Find out the actual file name. If it is long, truncate - // it and put "..." in the middle - p = tag_full_fname(&tagp); - if (p != NULL) { - msg_outtrans_attr(p, HL_ATTR(HLF_D)); - XFREE_CLEAR(p); + // skip "kind:<kind>" and "<kind>" + if (p == tagp.tagkind + || (p + 5 == tagp.tagkind + && STRNCMP(p, "kind:", 5) == 0)) { + p = tagp.tagkind_end; + continue; } - if (msg_col > 0) { + // print all other extra fields + attr = HL_ATTR(HLF_CM); + while (*p && *p != '\r' && *p != '\n') { + if (msg_col + ptr2cells(p) >= Columns) { msg_putchar('\n'); + if (got_int) { + break; + } + msg_advance(15); + } + p = msg_outtrans_one(p, attr); + if (*p == TAB) { + msg_puts_attr(" ", attr); + break; + } + if (*p == ':') { + attr = 0; + } } + } + if (msg_col > 15) { + msg_putchar('\n'); if (got_int) { - break; + break; } msg_advance(15); + } + } else { + for (p = tagp.command; + *p && *p != '\r' && *p != '\n'; + p++) { + } + command_end = p; + } - // print any extra fields - command_end = tagp.command_end; - if (command_end != NULL) { - p = command_end + 3; - while (*p && *p != '\r' && *p != '\n') { - while (*p == TAB) { - p++; - } - - // skip "file:" without a value (static tag) - if (STRNCMP(p, "file:", 5) == 0 && ascii_isspace(p[5])) { - p += 5; - continue; - } - // skip "kind:<kind>" and "<kind>" - if (p == tagp.tagkind - || (p + 5 == tagp.tagkind - && STRNCMP(p, "kind:", 5) == 0)) { - p = tagp.tagkind_end; - continue; - } - // print all other extra fields - attr = HL_ATTR(HLF_CM); - while (*p && *p != '\r' && *p != '\n') { - if (msg_col + ptr2cells(p) >= Columns) { - msg_putchar('\n'); - if (got_int) { - break; - } - msg_advance(15); - } - p = msg_outtrans_one(p, attr); - if (*p == TAB) { - msg_puts_attr(" ", attr); - break; - } - if (*p == ':') { - attr = 0; - } - } - } - if (msg_col > 15) { - msg_putchar('\n'); - if (got_int) { - break; - } - msg_advance(15); - } - } else { - for (p = tagp.command; - *p && *p != '\r' && *p != '\n'; - p++) { - } - command_end = p; - } - - // Put the info (in several lines) at column 15. - // Don't display "/^" and "?^". - p = tagp.command; - if (*p == '/' || *p == '?') { - p++; - if (*p == '^') { - p++; - } - } - // Remove leading whitespace from pattern - while (p != command_end && ascii_isspace(*p)) { - p++; - } + // Put the info (in several lines) at column 15. + // Don't display "/^" and "?^". + p = tagp.command; + if (*p == '/' || *p == '?') { + p++; + if (*p == '^') { + p++; + } + } + // Remove leading whitespace from pattern + while (p != command_end && ascii_isspace(*p)) { + p++; + } - while (p != command_end) { - if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) { - msg_putchar('\n'); - } - if (got_int) { - break; - } - msg_advance(15); + while (p != command_end) { + if (msg_col + (*p == TAB ? 1 : ptr2cells(p)) > Columns) { + msg_putchar('\n'); + } + if (got_int) { + break; + } + msg_advance(15); - // skip backslash used for escaping a command char or - // a backslash - if (*p == '\\' && (*(p + 1) == *tagp.command - || *(p + 1) == '\\')) { - p++; - } + // skip backslash used for escaping a command char or + // a backslash + if (*p == '\\' && (*(p + 1) == *tagp.command + || *(p + 1) == '\\')) { + p++; + } - if (*p == TAB) { - msg_putchar(' '); - p++; - } else { - p = msg_outtrans_one(p, 0); - } + if (*p == TAB) { + msg_putchar(' '); + p++; + } else { + p = msg_outtrans_one(p, 0); + } - // don't display the "$/;\"" and "$?;\"" - if (p == command_end - 2 && *p == '$' - && *(p + 1) == *tagp.command) { - break; - } - // don't display matching '/' or '?' - if (p == command_end - 1 && *p == *tagp.command - && (*p == '/' || *p == '?')) { - break; - } - } - if (msg_col) { - msg_putchar('\n'); - } - os_breakcheck(); + // don't display the "$/;\"" and "$?;\"" + if (p == command_end - 2 && *p == '$' + && *(p + 1) == *tagp.command) { + break; + } + // don't display matching '/' or '?' + if (p == command_end - 1 && *p == *tagp.command + && (*p == '/' || *p == '?')) { + break; + } } - if (got_int) { - got_int = false; // only stop the listing + if (msg_col) { + msg_putchar('\n'); } + os_breakcheck(); + } + if (got_int) { + got_int = false; // only stop the listing + } } // // Add the matching tags to the location list for the current // window. // -static int -add_llist_tags( - char_u *tag, - int num_matches, - char_u **matches) +static int add_llist_tags(char_u *tag, int num_matches, char_u **matches) { - list_T *list; - char_u tag_name[128 + 1]; - char_u *fname; - char_u *cmd; - int i; - char_u *p; - tagptrs_T tagp; - - fname = xmalloc(MAXPATHL + 1); - cmd = xmalloc(CMDBUFFSIZE + 1); - list = tv_list_alloc(0); - - for (i = 0; i < num_matches; i++) { - int len, cmd_len; - long lnum; - dict_T *dict; - - parse_match(matches[i], &tagp); - - // Save the tag name - len = (int)(tagp.tagname_end - tagp.tagname); - if (len > 128) { - len = 128; - } - xstrlcpy((char *)tag_name, (const char *)tagp.tagname, len + 1); - tag_name[len] = NUL; + list_T *list; + char_u tag_name[128 + 1]; + char_u *fname; + char_u *cmd; + int i; + char_u *p; + tagptrs_T tagp; - // Save the tag file name - p = tag_full_fname(&tagp); - if (p == NULL) { - continue; - } - xstrlcpy((char *)fname, (const char *)p, MAXPATHL); - XFREE_CLEAR(p); - - // Get the line number or the search pattern used to locate - // the tag. - lnum = 0; - if (isdigit(*tagp.command)) { - // Line number is used to locate the tag - lnum = atol((char *)tagp.command); - } else { - char_u *cmd_start, *cmd_end; + fname = xmalloc(MAXPATHL + 1); + cmd = xmalloc(CMDBUFFSIZE + 1); + list = tv_list_alloc(0); - // Search pattern is used to locate the tag + for (i = 0; i < num_matches; i++) { + int len, cmd_len; + long lnum; + dict_T *dict; - // Locate the end of the command - cmd_start = tagp.command; - cmd_end = tagp.command_end; - if (cmd_end == NULL) { - for (p = tagp.command; - *p && *p != '\r' && *p != '\n'; p++) { - } - cmd_end = p; - } + parse_match(matches[i], &tagp); - // Now, cmd_end points to the character after the - // command. Adjust it to point to the last - // character of the command. - cmd_end--; + // Save the tag name + len = (int)(tagp.tagname_end - tagp.tagname); + if (len > 128) { + len = 128; + } + xstrlcpy((char *)tag_name, (const char *)tagp.tagname, len + 1); + tag_name[len] = NUL; - // Skip the '/' and '?' characters at the - // beginning and end of the search pattern. - if (*cmd_start == '/' || *cmd_start == '?') { - cmd_start++; - } + // Save the tag file name + p = tag_full_fname(&tagp); + if (p == NULL) { + continue; + } + xstrlcpy((char *)fname, (const char *)p, MAXPATHL); + XFREE_CLEAR(p); + + // Get the line number or the search pattern used to locate + // the tag. + lnum = 0; + if (isdigit(*tagp.command)) { + // Line number is used to locate the tag + lnum = atol((char *)tagp.command); + } else { + char_u *cmd_start, *cmd_end; - if (*cmd_end == '/' || *cmd_end == '?') { - cmd_end--; - } + // Search pattern is used to locate the tag - len = 0; - cmd[0] = NUL; + // Locate the end of the command + cmd_start = tagp.command; + cmd_end = tagp.command_end; + if (cmd_end == NULL) { + for (p = tagp.command; + *p && *p != '\r' && *p != '\n'; p++) { + } + cmd_end = p; + } - // If "^" is present in the tag search pattern, then - // copy it first. - if (*cmd_start == '^') { - STRCPY(cmd, "^"); - cmd_start++; - len++; - } + // Now, cmd_end points to the character after the + // command. Adjust it to point to the last + // character of the command. + cmd_end--; - // Precede the tag pattern with \V to make it very - // nomagic. - STRCAT(cmd, "\\V"); - len += 2; + // Skip the '/' and '?' characters at the + // beginning and end of the search pattern. + if (*cmd_start == '/' || *cmd_start == '?') { + cmd_start++; + } - cmd_len = (int)(cmd_end - cmd_start + 1); - if (cmd_len > (CMDBUFFSIZE - 5)) { - cmd_len = CMDBUFFSIZE - 5; - } - snprintf((char *)cmd + len, CMDBUFFSIZE + 1 - len, - "%.*s", cmd_len, cmd_start); - len += cmd_len; - - if (cmd[len - 1] == '$') { - // Replace '$' at the end of the search pattern - // with '\$' - cmd[len - 1] = '\\'; - cmd[len] = '$'; - len++; - } + if (*cmd_end == '/' || *cmd_end == '?') { + cmd_end--; + } - cmd[len] = NUL; - } + len = 0; + cmd[0] = NUL; - dict = tv_dict_alloc(); - tv_list_append_dict(list, dict); + // If "^" is present in the tag search pattern, then + // copy it first. + if (*cmd_start == '^') { + STRCPY(cmd, "^"); + cmd_start++; + len++; + } - tv_dict_add_str(dict, S_LEN("text"), (const char *)tag_name); - tv_dict_add_str(dict, S_LEN("filename"), (const char *)fname); - tv_dict_add_nr(dict, S_LEN("lnum"), lnum); - if (lnum == 0) { - tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cmd); - } + // Precede the tag pattern with \V to make it very + // nomagic. + STRCAT(cmd, "\\V"); + len += 2; + + cmd_len = (int)(cmd_end - cmd_start + 1); + if (cmd_len > (CMDBUFFSIZE - 5)) { + cmd_len = CMDBUFFSIZE - 5; + } + snprintf((char *)cmd + len, CMDBUFFSIZE + 1 - len, + "%.*s", cmd_len, cmd_start); + len += cmd_len; + + if (cmd[len - 1] == '$') { + // Replace '$' at the end of the search pattern + // with '\$' + cmd[len - 1] = '\\'; + cmd[len] = '$'; + len++; + } + + cmd[len] = NUL; } - vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); - set_errorlist(curwin, list, ' ', IObuff, NULL); + dict = tv_dict_alloc(); + tv_list_append_dict(list, dict); - tv_list_free(list); - XFREE_CLEAR(fname); - XFREE_CLEAR(cmd); + tv_dict_add_str(dict, S_LEN("text"), (const char *)tag_name); + tv_dict_add_str(dict, S_LEN("filename"), (const char *)fname); + tv_dict_add_nr(dict, S_LEN("lnum"), lnum); + if (lnum == 0) { + tv_dict_add_str(dict, S_LEN("pattern"), (const char *)cmd); + } + } - return OK; + vim_snprintf((char *)IObuff, IOSIZE, "ltag %s", tag); + set_errorlist(curwin, list, ' ', IObuff, NULL); + + tv_list_free(list); + XFREE_CLEAR(fname); + XFREE_CLEAR(cmd); + + return OK; } /* @@ -1024,8 +1022,9 @@ static void taglen_advance(int l) if (l == MAXCOL) { msg_putchar('\n'); msg_advance(24); - } else + } else { msg_advance(13 + l); + } } /* @@ -1034,18 +1033,19 @@ static void taglen_advance(int l) void do_tags(exarg_T *eap) { int i; - char_u *name; - taggy_T *tagstack = curwin->w_tagstack; + char_u *name; + taggy_T *tagstack = curwin->w_tagstack; int tagstackidx = curwin->w_tagstackidx; int tagstacklen = curwin->w_tagstacklen; - /* Highlight title */ + // Highlight title MSG_PUTS_TITLE(_("\n # TO tag FROM line in file/text")); for (i = 0; i < tagstacklen; ++i) { if (tagstack[i].tagname != NULL) { name = fm_getname(&(tagstack[i].fmark), 30); - if (name == NULL) /* file name not available */ + if (name == NULL) { // file name not available continue; + } msg_putchar('\n'); vim_snprintf((char *)IObuff, IOSIZE, "%c%2d %2d %-15s %5ld ", @@ -1059,10 +1059,11 @@ void do_tags(exarg_T *eap) ? HL_ATTR(HLF_D) : 0); xfree(name); } - ui_flush(); /* show one line at a time */ + ui_flush(); // show one line at a time } - if (tagstackidx == tagstacklen) /* idx at top of stack */ + if (tagstackidx == tagstacklen) { // idx at top of stack MSG_PUTS("\n>"); + } } @@ -1078,15 +1079,17 @@ static int tag_strnicmp(char_u *s1, char_u *s2, size_t len) while (len > 0) { i = TOUPPER_ASC(*s1) - TOUPPER_ASC(*s2); - if (i != 0) - return i; /* this character different */ - if (*s1 == NUL) - break; /* strings match until NUL */ + if (i != 0) { + return i; // this character different + } + if (*s1 == NUL) { + break; // strings match until NUL + } ++s1; ++s2; --len; } - return 0; /* strings match */ + return 0; // strings match } @@ -1100,48 +1103,54 @@ static void prepare_pats(pat_T *pats, int has_re) if (has_re) { /* When the pattern starts with '^' or "\\<", binary searching can be * used (much faster). */ - if (pats->pat[0] == '^') + if (pats->pat[0] == '^') { pats->head = pats->pat + 1; - else if (pats->pat[0] == '\\' && pats->pat[1] == '<') + } else if (pats->pat[0] == '\\' && pats->pat[1] == '<') { pats->head = pats->pat + 2; - if (pats->head == pats->pat) + } + if (pats->head == pats->pat) { pats->headlen = 0; - else + } else { for (pats->headlen = 0; pats->head[pats->headlen] != NUL; - ++pats->headlen) + ++pats->headlen) { if (vim_strchr((char_u *)(p_magic ? ".[~*\\$" : "\\$"), - pats->head[pats->headlen]) != NULL) + pats->head[pats->headlen]) != NULL) { break; - if (p_tl != 0 && pats->headlen > p_tl) /* adjust for 'taglength' */ + } + } + } + if (p_tl != 0 && pats->headlen > p_tl) { // adjust for 'taglength' pats->headlen = p_tl; + } } - if (has_re) + if (has_re) { pats->regmatch.regprog = vim_regcomp(pats->pat, p_magic ? RE_MAGIC : 0); - else + } else { pats->regmatch.regprog = NULL; + } } -// -// Call the user-defined function to generate a list of tags used by -// find_tags(). -// -// Return OK if at least 1 tag has been successfully found, -// NOTDONE if the function returns v:null, and FAIL otherwise. -// -static int find_tagfunc_tags( - char_u *pat, // pattern supplied to the user-defined function - garray_T *ga, // the tags will be placed here - int *match_count, // here the number of tags found will be placed - int flags, // flags from find_tags (TAG_*) - char_u *buf_ffname) // name of buffer for priority +/// Call the user-defined function to generate a list of tags used by +/// find_tags(). +/// +/// Return OK if at least 1 tag has been successfully found, +/// NOTDONE if the function returns v:null, and FAIL otherwise. +/// +/// @param pat pattern supplied to the user-defined function +/// @param ga the tags will be placed here +/// @param match_count here the number of tags found will be placed +/// @param flags flags from find_tags (TAG_*) +/// @param buf_ffname name of buffer for priority +static int find_tagfunc_tags(char_u *pat, garray_T *ga, int *match_count, int flags, + char_u *buf_ffname) { - pos_T save_pos; - list_T *taglist; - int ntags = 0; - int result = FAIL; - typval_T args[4]; - typval_T rettv; + pos_T save_pos; + list_T *taglist; + int ntags = 0; + int result = FAIL; + typval_T args[4]; + typval_T rettv; char_u flagString[4]; taggy_T *tag = &curwin->w_tagstack[curwin->w_tagstackidx]; @@ -1195,12 +1204,12 @@ static int find_tagfunc_tags( taglist = rettv.vval.v_list; TV_LIST_ITER_CONST(taglist, li, { - char_u *res_name; - char_u *res_fname; - char_u *res_cmd; - char_u *res_kind; - int has_extra = 0; - int name_only = flags & TAG_NAMES; + char_u *res_name; + char_u *res_fname; + char_u *res_cmd; + char_u *res_kind; + int has_extra = 0; + int name_only = flags & TAG_NAMES; if (TV_LIST_ITEM_TV(li)->v_type != VAR_DICT) { EMSG(_(tfu_inv_ret_msg)); @@ -1326,50 +1335,46 @@ static int find_tagfunc_tags( return result; } -/* - * find_tags() - search for tags in tags files - * - * Return FAIL if search completely failed (*num_matches will be 0, *matchesp - * will be NULL), OK otherwise. - * - * There is a priority in which type of tag is recognized. - * - * 6. A static or global tag with a full matching tag for the current file. - * 5. A global tag with a full matching tag for another file. - * 4. A static tag with a full matching tag for another file. - * 3. A static or global tag with an ignore-case matching tag for the - * current file. - * 2. A global tag with an ignore-case matching tag for another file. - * 1. A static tag with an ignore-case matching tag for another file. - * - * Tags in an emacs-style tags file are always global. - * - * flags: - * TAG_HELP only search for help tags - * TAG_NAMES only return name of tag - * TAG_REGEXP use "pat" as a regexp - * TAG_NOIC don't always ignore case - * TAG_KEEP_LANG keep language - * TAG_CSCOPE use cscope results for tags - * TAG_NO_TAGFUNC do not call the 'tagfunc' function - */ -int -find_tags( - char_u *pat, // pattern to search for - int *num_matches, // return: number of matches found - char_u ***matchesp, // return: array of matches found - int flags, - int mincount, /* MAXCOL: find all matches - other: minimal number of matches */ - char_u *buf_ffname /* name of buffer for priority */ -) +/// find_tags() - search for tags in tags files +/// +/// Return FAIL if search completely failed (*num_matches will be 0, *matchesp +/// will be NULL), OK otherwise. +/// +/// There is a priority in which type of tag is recognized. +/// +/// 6. A static or global tag with a full matching tag for the current file. +/// 5. A global tag with a full matching tag for another file. +/// 4. A static tag with a full matching tag for another file. +/// 3. A static or global tag with an ignore-case matching tag for the +/// current file. +/// 2. A global tag with an ignore-case matching tag for another file. +/// 1. A static tag with an ignore-case matching tag for another file. +/// +/// Tags in an emacs-style tags file are always global. +/// +/// flags: +/// TAG_HELP only search for help tags +/// TAG_NAMES only return name of tag +/// TAG_REGEXP use "pat" as a regexp +/// TAG_NOIC don't always ignore case +/// TAG_KEEP_LANG keep language +/// TAG_CSCOPE use cscope results for tags +/// TAG_NO_TAGFUNC do not call the 'tagfunc' function +/// +/// @param pat pattern to search for +/// @param num_matches return: number of matches found +/// @param matchesp return: array of matches found +/// @param mincount MAXCOL: find all matches other: minimal number of matches */ +/// @param buf_ffname name of buffer for priority +int find_tags(char_u *pat, int *num_matches, char_u ***matchesp, int flags, int mincount, + char_u *buf_ffname) { - FILE *fp; - char_u *lbuf; /* line buffer */ - int lbuf_size = LSIZE; /* length of lbuf */ - char_u *tag_fname; /* name of tag file */ - tagname_T tn; /* info for get_tagfname() */ - int first_file; /* trying first tag file */ + FILE *fp; + char_u *lbuf; // line buffer + int lbuf_size = LSIZE; // length of lbuf + char_u *tag_fname; // name of tag file + tagname_T tn; // info for get_tagfname() + int first_file; // trying first tag file tagptrs_T tagp; bool did_open = false; // did open a tag file bool stop_searching = false; // stop when match found or error @@ -1377,8 +1382,8 @@ find_tags( int is_static; // current tag line is static int is_current; // file name matches bool eof = false; // found end-of-file - char_u *p; - char_u *s; + char_u *p; + char_u *s; int i; int tag_file_sorted = NUL; // !_TAG_FILE_SORTED value struct tag_search_info { // Binary search file offsets @@ -1396,17 +1401,17 @@ find_tags( off_T offset; int round; enum { - TS_START, /* at start of file */ - TS_LINEAR /* linear searching forward, till EOF */ - , TS_BINARY, /* binary searching */ - TS_SKIP_BACK, /* skipping backwards */ - TS_STEP_FORWARD /* stepping forwards */ - } state; /* Current search state */ + TS_START, // at start of file + TS_LINEAR // linear searching forward, till EOF + , TS_BINARY, // binary searching + TS_SKIP_BACK, // skipping backwards + TS_STEP_FORWARD // stepping forwards + } state; // Current search state int cmplen; - int match; /* matches */ - int match_no_ic = 0; /* matches with rm_ic == FALSE */ - int match_re; /* match with regexp */ + int match; // matches + int match_no_ic = 0; // matches with rm_ic == FALSE + int match_re; // match with regexp int matchoff = 0; int save_emsg_off; @@ -1416,16 +1421,16 @@ find_tags( hashtab_T ht_match[MT_COUNT]; // stores matches by key hash_T hash = 0; int match_count = 0; // number of matches found - char_u **matches; + char_u **matches; int mtt; int help_save; int help_pri = 0; - char_u *help_lang_find = NULL; // lang to be found + char_u *help_lang_find = NULL; // lang to be found char_u help_lang[3]; // lang of current tags file - char_u *saved_pat = NULL; // copy of pat[] + char_u *saved_pat = NULL; // copy of pat[] bool is_txt = false; - pat_T orgpat; /* holds unconverted pattern info */ + pat_T orgpat; // holds unconverted pattern info vimconv_T vimconv; int findall = (mincount == MAXCOL || mincount == TAG_MANY); @@ -1447,22 +1452,22 @@ find_tags( // Change the value of 'ignorecase' according to 'tagcase' for the // duration of this function. switch (curbuf->b_tc_flags ? curbuf->b_tc_flags : tc_flags) { - case TC_FOLLOWIC: - break; - case TC_IGNORE: - p_ic = true; - break; - case TC_MATCH: - p_ic = false; - break; - case TC_FOLLOWSCS: - p_ic = ignorecase(pat); - break; - case TC_SMART: - p_ic = ignorecase_opt(pat, true, true); - break; - default: - abort(); + case TC_FOLLOWIC: + break; + case TC_IGNORE: + p_ic = true; + break; + case TC_MATCH: + p_ic = false; + break; + case TC_FOLLOWSCS: + p_ic = ignorecase(pat); + break; + case TC_SMART: + p_ic = ignorecase_opt(pat, true, true); + break; + default: + abort(); } help_save = curbuf->b_help; @@ -1480,7 +1485,7 @@ find_tags( hash_init(&ht_match[mtt]); } - STRCPY(tag_fname, "from cscope"); /* for error messages */ + STRCPY(tag_fname, "from cscope"); // for error messages /* * Initialize a few variables @@ -1506,15 +1511,17 @@ find_tags( orgpat.len -= 3; } } - if (p_tl != 0 && orgpat.len > p_tl) /* adjust for 'taglength' */ + if (p_tl != 0 && orgpat.len > p_tl) { // adjust for 'taglength' orgpat.len = p_tl; + } save_emsg_off = emsg_off; - emsg_off = TRUE; /* don't want error for invalid RE here */ + emsg_off = TRUE; // don't want error for invalid RE here prepare_pats(&orgpat, has_re); emsg_off = save_emsg_off; - if (has_re && orgpat.regmatch.regprog == NULL) + if (has_re && orgpat.regmatch.regprog == NULL) { goto findtag_end; + } // This is only to avoid a compiler warning for using search_info // uninitialised. @@ -1580,8 +1587,9 @@ find_tags( /* When searching for a specific language skip tags files * for other languages. */ if (help_lang_find != NULL - && STRICMP(help_lang, help_lang_find) != 0) + && STRICMP(help_lang, help_lang_find) != 0) { continue; + } /* For CTRL-] in a help file prefer a match with the same * language. */ @@ -1591,23 +1599,26 @@ find_tags( && (i = (int)STRLEN(curbuf->b_fname)) > 4 && curbuf->b_fname[i - 1] == 'x' && curbuf->b_fname[i - 4] == '.' - && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0) + && STRNICMP(curbuf->b_fname + i - 3, help_lang, 2) == 0) { help_pri = 0; - else { + } else { help_pri = 1; for (s = p_hlg; *s != NUL; ++s) { - if (STRNICMP(s, help_lang, 2) == 0) + if (STRNICMP(s, help_lang, 2) == 0) { break; + } ++help_pri; - if ((s = vim_strchr(s, ',')) == NULL) + if ((s = vim_strchr(s, ',')) == NULL) { break; + } } if (s == NULL || *s == NUL) { /* Language not in 'helplang': use last, prefer English, * unless found already. */ ++help_pri; - if (STRICMP(help_lang, "en") != 0) + if (STRICMP(help_lang, "en") != 0) { ++help_pri; + } } } } @@ -1624,7 +1635,7 @@ find_tags( } did_open = true; // remember that we found at least one file - state = TS_START; /* we're at the start of the file */ + state = TS_START; // we're at the start of the file /* * Read and parse the lines in the file one by one @@ -1636,8 +1647,9 @@ find_tags( } else { fast_breakcheck(); } - if ((flags & TAG_INS_COMP)) /* Double brackets for gcc */ + if ((flags & TAG_INS_COMP)) { // Double brackets for gcc ins_compl_check_keys(30, false); + } if (got_int || compl_interrupted) { stop_searching = true; break; @@ -1649,18 +1661,20 @@ find_tags( retval = OK; break; } - if (get_it_again) + if (get_it_again) { goto line_read_in; + } /* * For binary search: compute the next offset to use. */ if (state == TS_BINARY) { offset = search_info.low_offset + ((search_info.high_offset - search_info.low_offset) / 2); - if (offset == search_info.curr_offset) - break; /* End the binary search without a match. */ - else + if (offset == search_info.curr_offset) { + break; // End the binary search without a match. + } else { search_info.curr_offset = offset; + } } else if (state == TS_SKIP_BACK) { // Skipping back (after a match during binary search). search_info.curr_offset -= lbuf_size * 2; @@ -1676,7 +1690,7 @@ find_tags( * start of the next line. */ if (state == TS_BINARY || state == TS_SKIP_BACK) { - /* Adjust the search file offset to the correct position */ + // Adjust the search file offset to the correct position search_info.curr_offset_used = search_info.curr_offset; vim_fseek(fp, search_info.curr_offset, SEEK_SET); eof = vim_fgets(lbuf, lbuf_size, fp); @@ -1691,13 +1705,13 @@ find_tags( } eof = vim_fgets(lbuf, lbuf_size, fp); } - /* skip empty and blank lines */ + // skip empty and blank lines while (!eof && vim_isblankline(lbuf)) { search_info.curr_offset = vim_ftell(fp); eof = vim_fgets(lbuf, lbuf_size, fp); } if (eof) { - /* Hit end of file. Skip backwards. */ + // Hit end of file. Skip backwards. state = TS_SKIP_BACK; search_info.match_offset = vim_ftell(fp); search_info.curr_offset = search_info.curr_offset_used; @@ -1708,7 +1722,7 @@ find_tags( * Not jumping around in the file: Read the next line. */ else { - /* skip empty and blank lines */ + // skip empty and blank lines do { eof = use_cscope ? cs_fgets(lbuf, lbuf_size) @@ -1716,13 +1730,13 @@ find_tags( } while (!eof && vim_isblankline(lbuf)); if (eof) { - break; /* end of file */ + break; // end of file } } line_read_in: if (vimconv.vc_type != CONV_NONE) { - char_u *conv_line; + char_u *conv_line; int len; /* Convert every line. Converting the pattern from 'enc' to @@ -1730,7 +1744,7 @@ line_read_in: * not recognized. */ conv_line = string_convert(&vimconv, lbuf, NULL); if (conv_line != NULL) { - /* Copy or swap lbuf and conv_line. */ + // Copy or swap lbuf and conv_line. len = (int)STRLEN(conv_line) + 1; if (len > lbuf_size) { xfree(lbuf); @@ -1754,30 +1768,33 @@ line_read_in: * case is folded lower case letters sort before "_". */ if (STRNCMP(lbuf, "!_TAG_", 6) <= 0 || (lbuf[0] == '!' && ASCII_ISLOWER(lbuf[1]))) { - if (STRNCMP(lbuf, "!_TAG_", 6) != 0) + if (STRNCMP(lbuf, "!_TAG_", 6) != 0) { /* Non-header item before the header, e.g. "!" itself. */ goto parse_line; + } /* * Read header line. */ - if (STRNCMP(lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) + if (STRNCMP(lbuf, "!_TAG_FILE_SORTED\t", 18) == 0) { tag_file_sorted = lbuf[18]; + } if (STRNCMP(lbuf, "!_TAG_FILE_ENCODING\t", 20) == 0) { /* Prepare to convert every line from the specified * encoding to 'encoding'. */ - for (p = lbuf + 20; *p > ' ' && *p < 127; ++p) + for (p = lbuf + 20; *p > ' ' && *p < 127; ++p) { ; + } *p = NUL; convert_setup(&vimconv, lbuf + 20, p_enc); } - /* Read the next line. Unrecognized flags are ignored. */ + // Read the next line. Unrecognized flags are ignored. continue; } - /* Headers ends. */ + // Headers ends. /* * When there is no tag head, or ignoring case, need to do a @@ -1788,18 +1805,19 @@ line_read_in: * flag set. * For cscope, it's always linear. */ - if (linear || use_cscope) + if (linear || use_cscope) { state = TS_LINEAR; - else if (tag_file_sorted == NUL) + } else if (tag_file_sorted == NUL) { state = TS_BINARY; - else if (tag_file_sorted == '1') + } else if (tag_file_sorted == '1') { state = TS_BINARY; - else if (tag_file_sorted == '2') { + } else if (tag_file_sorted == '2') { state = TS_BINARY; sortic = true; orgpat.regmatch.rm_ic = (p_ic || !noic); - } else + } else { state = TS_LINEAR; + } if (state == TS_BINARY && orgpat.regmatch.rm_ic && !sortic) { /* Binary search won't work for ignoring case, use linear @@ -1865,20 +1883,23 @@ parse_line: * there is no regexp, or the tag is too short. */ cmplen = (int)(tagp.tagname_end - tagp.tagname); - if (p_tl != 0 && cmplen > p_tl) /* adjust for 'taglength' */ + if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength' cmplen = p_tl; - if (has_re && orgpat.headlen < cmplen) + } + if (has_re && orgpat.headlen < cmplen) { cmplen = orgpat.headlen; - else if (state == TS_LINEAR && orgpat.headlen != cmplen) + } else if (state == TS_LINEAR && orgpat.headlen != cmplen) { continue; + } if (state == TS_BINARY) { /* * Simplistic check for unsorted tags file. */ i = (int)tagp.tagname[0]; - if (sortic) + if (sortic) { i = TOUPPER_ASC(tagp.tagname[0]); + } if (i < search_info.low_char || i > search_info.high_char) { sort_error = true; } @@ -1886,21 +1907,23 @@ parse_line: /* * Compare the current tag with the searched tag. */ - if (sortic) + if (sortic) { tagcmp = tag_strnicmp(tagp.tagname, orgpat.head, - (size_t)cmplen); - else + (size_t)cmplen); + } else { tagcmp = STRNCMP(tagp.tagname, orgpat.head, cmplen); + } /* * A match with a shorter tag means to search forward. * A match with a longer tag means to search backward. */ if (tagcmp == 0) { - if (cmplen < orgpat.headlen) + if (cmplen < orgpat.headlen) { tagcmp = -1; - else if (cmplen > orgpat.headlen) + } else if (cmplen > orgpat.headlen) { tagcmp = 1; + } } if (tagcmp == 0) { @@ -1915,37 +1938,40 @@ parse_line: search_info.curr_offset = vim_ftell(fp); if (search_info.curr_offset < search_info.high_offset) { search_info.low_offset = search_info.curr_offset; - if (sortic) + if (sortic) { search_info.low_char = TOUPPER_ASC(tagp.tagname[0]); - else + } else { search_info.low_char = tagp.tagname[0]; + } continue; } } if (tagcmp > 0 && search_info.curr_offset != search_info.high_offset) { search_info.high_offset = search_info.curr_offset; - if (sortic) + if (sortic) { search_info.high_char = TOUPPER_ASC(tagp.tagname[0]); - else + } else { search_info.high_char = tagp.tagname[0]; + } continue; } - /* No match yet and are at the end of the binary search. */ + // No match yet and are at the end of the binary search. break; - } else if (state == TS_SKIP_BACK) { + } else if (state == TS_SKIP_BACK) { assert(cmplen >= 0); - if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) + if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) { state = TS_STEP_FORWARD; - else + } else { /* Have to skip back more. Restore the curr_offset * used, otherwise we get stuck at a long line. */ search_info.curr_offset = search_info.curr_offset_used; + } continue; - } else if (state == TS_STEP_FORWARD) { + } else if (state == TS_STEP_FORWARD) { assert(cmplen >= 0); if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) { if ((off_T)vim_ftell(fp) > search_info.match_offset) { @@ -1954,23 +1980,27 @@ parse_line: continue; // before first match } } - } else - /* skip this match if it can't match */ - assert(cmplen >= 0); - if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) + } else { + // skip this match if it can't match + assert(cmplen >= 0); + } + if (mb_strnicmp(tagp.tagname, orgpat.head, (size_t)cmplen) != 0) { continue; + } // Can be a matching tag, isolate the file name and command. tagp.fname = tagp.tagname_end + 1; tagp.fname_end = vim_strchr(tagp.fname, TAB); tagp.command = tagp.fname_end + 1; - if (tagp.fname_end == NULL) + if (tagp.fname_end == NULL) { i = FAIL; - else + } else { i = OK; - } else + } + } else { i = parse_tag_line(lbuf, - &tagp); + &tagp); + } if (i == FAIL) { line_error = true; break; @@ -1981,20 +2011,23 @@ parse_line: * a regexp). */ cmplen = (int)(tagp.tagname_end - tagp.tagname); - if (p_tl != 0 && cmplen > p_tl) /* adjust for 'taglength' */ + if (p_tl != 0 && cmplen > p_tl) { // adjust for 'taglength' cmplen = p_tl; - /* if tag length does not match, don't try comparing */ - if (orgpat.len != cmplen) + } + // if tag length does not match, don't try comparing + if (orgpat.len != cmplen) { match = FALSE; - else { + } else { if (orgpat.regmatch.rm_ic) { assert(cmplen >= 0); match = mb_strnicmp(tagp.tagname, orgpat.pat, (size_t)cmplen) == 0; - if (match) + if (match) { match_no_ic = (STRNCMP(tagp.tagname, orgpat.pat, - cmplen) == 0); - } else + cmplen) == 0); + } + } else { match = (STRNCMP(tagp.tagname, orgpat.pat, cmplen) == 0); + } } /* @@ -2012,7 +2045,7 @@ parse_line: if (orgpat.regmatch.rm_ic) { orgpat.regmatch.rm_ic = FALSE; match_no_ic = vim_regexec(&orgpat.regmatch, tagp.tagname, - (colnr_T)0); + (colnr_T)0); orgpat.regmatch.rm_ic = TRUE; } } @@ -2025,7 +2058,7 @@ parse_line: int len = 0; if (use_cscope) { - /* Don't change the ordering, always use the same table. */ + // Don't change the ordering, always use the same table. mtt = MT_GL_OTH; } else { // Decide in which array to store this match. @@ -2035,27 +2068,31 @@ parse_line: // Decide in which of the sixteen tables to store this match. if (is_static) { - if (is_current) + if (is_current) { mtt = MT_ST_CUR; - else + } else { mtt = MT_ST_OTH; + } } else { - if (is_current) + if (is_current) { mtt = MT_GL_CUR; - else + } else { mtt = MT_GL_OTH; + } } - if (orgpat.regmatch.rm_ic && !match_no_ic) + if (orgpat.regmatch.rm_ic && !match_no_ic) { mtt += MT_IC_OFF; - if (match_re) + } + if (match_re) { mtt += MT_RE_OFF; + } } // Add the found match in ht_match[mtt] and ga_match[mtt]. // Store the info we need later, which depends on the kind of // tags we are dealing with. if (help_only) { -# define ML_EXTRA 3 +#define ML_EXTRA 3 // Append the help-heuristic number after the tagname, for // sorting it later. The heuristic is ignored for // detecting duplicates. @@ -2074,7 +2111,7 @@ parse_line: + help_pri); *tagp.tagname_end = TAB; - } else if (name_only) { + } else if (name_only) { if (get_it_again) { char_u *temp_end = tagp.command; @@ -2148,7 +2185,7 @@ parse_line: hash_add_item(&ht_match[mtt], hi, mfp, hash); ga_grow(&ga_match[mtt], 1); ((char_u **)(ga_match[mtt].ga_data)) - [ga_match[mtt].ga_len++] = mfp; + [ga_match[mtt].ga_len++] = mfp; match_count++; } else { // duplicate tag, drop it @@ -2156,9 +2193,10 @@ parse_line: } } } - if (use_cscope && eof) + if (use_cscope && eof) { break; - } /* forever */ + } + } // forever if (line_error) { EMSG2(_("E431: Format error in tags file \"%s\""), tag_fname); @@ -2169,10 +2207,12 @@ parse_line: line_error = false; } - if (!use_cscope) + if (!use_cscope) { fclose(fp); - if (vimconv.vc_type != CONV_NONE) + } + if (vimconv.vc_type != CONV_NONE) { convert_setup(&vimconv, NULL, NULL); + } tag_file_sorted = NUL; if (sort_error) { @@ -2188,27 +2228,31 @@ parse_line: stop_searching = true; } - if (stop_searching || use_cscope) + if (stop_searching || use_cscope) { break; + } + } // end of for-each-file loop - } /* end of for-each-file loop */ - - if (!use_cscope) + if (!use_cscope) { tagname_free(&tn); + } /* stop searching when already did a linear search, or when TAG_NOIC * used, and 'ignorecase' not set or already did case-ignore search */ - if (stop_searching || linear || (!p_ic && noic) || orgpat.regmatch.rm_ic) + if (stop_searching || linear || (!p_ic && noic) || orgpat.regmatch.rm_ic) { break; - if (use_cscope) + } + if (use_cscope) { break; - orgpat.regmatch.rm_ic = TRUE; /* try another time while ignoring case */ + } + orgpat.regmatch.rm_ic = TRUE; // try another time while ignoring case } if (!stop_searching) { - if (!did_open && verbose) /* never opened any tags file */ + if (!did_open && verbose) { // never opened any tags file EMSG(_("E433: No tags file")); - retval = OK; /* It's OK even when no tag found */ + } + retval = OK; // It's OK even when no tag found } findtag_end: @@ -2220,13 +2264,15 @@ findtag_end: * Move the matches from the ga_match[] arrays into one list of * matches. When retval == FAIL, free the matches. */ - if (retval == FAIL) + if (retval == FAIL) { match_count = 0; + } - if (match_count > 0) + if (match_count > 0) { matches = xmalloc(match_count * sizeof(char_u *)); - else + } else { matches = NULL; + } match_count = 0; for (mtt = 0; mtt < MT_COUNT; mtt++) { for (i = 0; i < ga_match[mtt].ga_len; i++) { @@ -2245,7 +2291,7 @@ findtag_end: } } } - matches[match_count++] = (char_u *)mfp; + matches[match_count++] = mfp; } } @@ -2275,7 +2321,7 @@ static void found_tagfile_cb(char_u *fname, void *cookie) char_u *const tag_fname = vim_strsave(fname); #ifdef BACKSLASH_IN_FILENAME - slash_adjust(tag_fname); + slash_adjust(tag_fname); #endif simplify_filename(tag_fname); GA_APPEND(char_u *, &tag_fnames, tag_fname); @@ -2293,24 +2339,22 @@ void free_tag_stuff(void) #endif -/* - * Get the next name of a tag file from the tag file list. - * For help files, use "tags" file only. - * - * Return FAIL if no more tag file names, OK otherwise. - */ -int -get_tagfname( - tagname_T *tnp, // holds status info - int first, // TRUE when first file name is wanted - char_u *buf // pointer to buffer of MAXPATHL chars -) +/// Get the next name of a tag file from the tag file list. +/// For help files, use "tags" file only. +/// +/// @param tnp holds status info +/// @param first TRUE when first file name is wanted +/// @param buf pointer to buffer of MAXPATHL chars +/// +/// @return FAIL if no more tag file names, OK otherwise. +int get_tagfname(tagname_T *tnp, int first, char_u *buf) { - char_u *fname = NULL; - char_u *r_ptr; + char_u *fname = NULL; + char_u *r_ptr; - if (first) + if (first) { memset(tnp, 0, sizeof(tagname_T)); + } if (curbuf->b_help) { /* @@ -2328,8 +2372,9 @@ get_tagfname( if (tnp->tn_hf_idx >= tag_fnames.ga_len) { /* Not found in 'runtimepath', use 'helpfile', if it exists and * wasn't used yet, replacing "help.txt" with "tags". */ - if (tnp->tn_hf_idx > tag_fnames.ga_len || *p_hf == NUL) + if (tnp->tn_hf_idx > tag_fnames.ga_len || *p_hf == NUL) { return FAIL; + } ++tnp->tn_hf_idx; STRCPY(buf, p_hf); STRCPY(path_tail(buf), "tags"); @@ -2367,14 +2412,15 @@ get_tagfname( for (;; ) { if (tnp->tn_did_filefind_init) { fname = vim_findfile(tnp->tn_search_ctx); - if (fname != NULL) + if (fname != NULL) { break; + } tnp->tn_did_filefind_init = FALSE; } else { - char_u *filename = NULL; + char_u *filename = NULL; - /* Stop when used all parts of 'tags'. */ + // Stop when used all parts of 'tags'. if (*tnp->tn_np == NUL) { vim_findfile_cleanup(tnp->tn_search_ctx); tnp->tn_search_ctx = NULL; @@ -2395,12 +2441,13 @@ get_tagfname( *filename++ = NUL; tnp->tn_search_ctx = vim_findfile_init(buf, filename, - r_ptr, 100, - FALSE, /* don't free visited list */ - FINDFILE_FILE, /* we search for a file */ - tnp->tn_search_ctx, TRUE, curbuf->b_ffname); - if (tnp->tn_search_ctx != NULL) + r_ptr, 100, + FALSE, // don't free visited list + FINDFILE_FILE, // we search for a file + tnp->tn_search_ctx, TRUE, curbuf->b_ffname); + if (tnp->tn_search_ctx != NULL) { tnp->tn_did_filefind_init = TRUE; + } } } @@ -2420,43 +2467,44 @@ void tagname_free(tagname_T *tnp) ga_clear_strings(&tag_fnames); } -/* - * Parse one line from the tags file. Find start/end of tag name, start/end of - * file name and start of search pattern. - * - * If is_etag is TRUE, tagp->fname and tagp->fname_end are not set. - * - * Return FAIL if there is a format error in this line, OK otherwise. - */ -static int -parse_tag_line( - char_u *lbuf, // line to be parsed - tagptrs_T *tagp -) +/// Parse one line from the tags file. Find start/end of tag name, start/end of +/// file name and start of search pattern. +/// +/// If is_etag is TRUE, tagp->fname and tagp->fname_end are not set. +/// +/// @param lbuf line to be parsed +/// +/// @return FAIL if there is a format error in this line, OK otherwise. +static int parse_tag_line(char_u *lbuf, tagptrs_T *tagp) { - char_u *p; + char_u *p; - /* Isolate the tagname, from lbuf up to the first white */ + // Isolate the tagname, from lbuf up to the first white tagp->tagname = lbuf; p = vim_strchr(lbuf, TAB); - if (p == NULL) + if (p == NULL) { return FAIL; + } tagp->tagname_end = p; - /* Isolate file name, from first to second white space */ - if (*p != NUL) + // Isolate file name, from first to second white space + if (*p != NUL) { ++p; + } tagp->fname = p; p = vim_strchr(p, TAB); - if (p == NULL) + if (p == NULL) { return FAIL; + } tagp->fname_end = p; - /* find start of search command, after second white space */ - if (*p != NUL) + // find start of search command, after second white space + if (*p != NUL) { ++p; - if (*p == NUL) + } + if (*p == NUL) { return FAIL; + } tagp->command = p; return OK; @@ -2466,12 +2514,12 @@ parse_tag_line( * Check if tagname is a static tag * * Static tags produced by the older ctags program have the format: - * 'file:tag file /pattern'. + * 'file:tag file /pattern'. * This is only recognized when both occurrence of 'file' are the same, to * avoid recognizing "string::string" or ":exit". * * Static tags produced by the new ctags program have the format: - * 'tag file /pattern/;"<Tab>file:' " + * 'tag file /pattern/;"<Tab>file:' " * * Return TRUE if it is a static tag and adjust *tagname to the real tag. * Return FALSE if it is not a static tag. @@ -2484,8 +2532,9 @@ static bool test_for_static(tagptrs_T *tagp) p = tagp->command; while ((p = vim_strchr(p, '\t')) != NULL) { ++p; - if (STRNCMP(p, "file:", 5) == 0) + if (STRNCMP(p, "file:", 5) == 0) { return TRUE; + } } return FALSE; @@ -2501,32 +2550,29 @@ static size_t matching_line_len(const char_u *const lbuf) return (p - lbuf) + STRLEN(p); } -/* - * Parse a line from a matching tag. Does not change the line itself. - * - * The line that we get looks like this: - * Emacs tag: <mtt><tag_fname><NUL><ebuf><NUL><lbuf> - * other tag: <mtt><tag_fname><NUL><NUL><lbuf> - * without Emacs tags: <mtt><tag_fname><NUL><lbuf> - * - * Return OK or FAIL. - */ -static int -parse_match( - char_u *lbuf, // input: matching line - tagptrs_T *tagp // output: pointers into the line -) +/// Parse a line from a matching tag. Does not change the line itself. +/// +/// The line that we get looks like this: +/// Emacs tag: <mtt><tag_fname><NUL><ebuf><NUL><lbuf> +/// other tag: <mtt><tag_fname><NUL><NUL><lbuf> +/// without Emacs tags: <mtt><tag_fname><NUL><lbuf> +/// +/// @param lbuf input: matching line +/// @param tagp output: pointers into the line +/// +/// @return OK or FAIL. +static int parse_match(char_u *lbuf, tagptrs_T *tagp) { int retval; - char_u *p; - char_u *pc, *pt; + char_u *p; + char_u *pc, *pt; tagp->tag_fname = lbuf + 1; lbuf += STRLEN(tagp->tag_fname) + 2; - /* Find search pattern and the file name for non-etags. */ + // Find search pattern and the file name for non-etags. retval = parse_tag_line(lbuf, - tagp); + tagp); tagp->tagkind = NULL; tagp->user_data = NULL; @@ -2534,7 +2580,7 @@ parse_match( tagp->command_end = NULL; if (retval == OK) { - /* Try to find a kind field: "kind:<kind>" or just "<kind>"*/ + // Try to find a kind field: "kind:<kind>" or just "<kind>" p = tagp->command; if (find_extra(&p) == OK) { tagp->command_end = p; @@ -2562,8 +2608,9 @@ parse_match( if (pc == NULL || (pt != NULL && pc > pt)) { tagp->tagkind = p; } - if (pt == NULL) + if (pt == NULL) { break; + } p = pt; MB_PTR_ADV(p); } @@ -2602,32 +2649,30 @@ static char_u *tag_full_fname(tagptrs_T *tagp) return fullname; } -/* - * Jump to a tag that has been found in one of the tag files - * - * returns OK for success, NOTAGFILE when file not found, FAIL otherwise. - */ -static int jumpto_tag( - const char_u *lbuf_arg, // line from the tags file for this tag - int forceit, // :ta with ! - int keep_help // keep help flag (FALSE for cscope) -) +/// Jump to a tag that has been found in one of the tag files +/// +/// @param lbuf_arg line from the tags file for this tag +/// @param forceit :ta with ! +/// @param keep_help keep help flag (FALSE for cscope) +/// +/// @return OK for success, NOTAGFILE when file not found, FAIL otherwise. +static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help) { int save_magic; bool save_p_ws; int save_p_scs, save_p_ic; linenr_T save_lnum; - char_u *str; - char_u *pbuf; /* search pattern buffer */ - char_u *pbuf_end; - char_u *tofree_fname = NULL; - char_u *fname; + char_u *str; + char_u *pbuf; // search pattern buffer + char_u *pbuf_end; + char_u *tofree_fname = NULL; + char_u *fname; tagptrs_T tagp; int retval = FAIL; int getfile_result = GETFILE_UNUSED; int search_options; - win_T *curwin_save = NULL; - char_u *full_fname = NULL; + win_T *curwin_save = NULL; + char_u *full_fname = NULL; const bool old_KeyTyped = KeyTyped; // getting the file may reset it const int l_g_do_tagpreview = g_do_tagpreview; const size_t len = matching_line_len(lbuf_arg) + 1; @@ -2636,7 +2681,7 @@ static int jumpto_tag( pbuf = xmalloc(LSIZE); - /* parse the match line into the tagp structure */ + // parse the match line into the tagp structure if (parse_match(lbuf, &tagp) == FAIL) { tagp.fname_end = NULL; goto erret; @@ -2646,7 +2691,7 @@ static int jumpto_tag( *tagp.fname_end = NUL; fname = tagp.fname; - /* copy the command to pbuf[], remove trailing CR/NL */ + // copy the command to pbuf[], remove trailing CR/NL str = tagp.command; for (pbuf_end = pbuf; *str && *str != '\n' && *str != '\r'; ) { *pbuf_end++ = *str++; @@ -2680,8 +2725,8 @@ static int jumpto_tag( * autocommand event (e.g., http://sys/file). */ if (!os_path_exists(fname) - && !has_autocmd(EVENT_BUFREADCMD, fname, NULL) - ) { + && !has_autocmd(EVENT_BUFREADCMD, fname, + NULL)) { retval = NOTAGFILE; xfree(nofile_fname); nofile_fname = vim_strsave(fname); @@ -2692,8 +2737,8 @@ static int jumpto_tag( if (l_g_do_tagpreview != 0) { - postponed_split = 0; /* don't split again below */ - curwin_save = curwin; /* Save current window */ + postponed_split = 0; // don't split again below + curwin_save = curwin; // Save current window /* * If we are reusing a window, we may change dir when @@ -2750,10 +2795,11 @@ static int jumpto_tag( if (keep_help) { /* A :ta from a help file will keep the b_help flag set. For ":ptag" * we need to use the flag from the window where we came from. */ - if (l_g_do_tagpreview != 0) + if (l_g_do_tagpreview != 0) { keep_help_flag = curwin_save->w_buffer->b_help; - else + } else { keep_help_flag = curbuf->b_help; + } } if (getfile_result == GETFILE_UNUSED) { @@ -2777,10 +2823,11 @@ static int jumpto_tag( * command. If 'cpoptions' does not contain 't', the search pattern * is not stored. */ - if (vim_strchr(p_cpo, CPO_TAGPAT) != NULL) + if (vim_strchr(p_cpo, CPO_TAGPAT) != NULL) { search_options = 0; - else + } else { search_options = SEARCH_KEEP; + } /* * If the command is a search, try here. @@ -2791,14 +2838,15 @@ static int jumpto_tag( * anything following. */ str = pbuf; - if (pbuf[0] == '/' || pbuf[0] == '?') + if (pbuf[0] == '/' || pbuf[0] == '?') { str = skip_regexp(pbuf + 1, pbuf[0], FALSE, NULL) + 1; - if (str > pbuf_end - 1) { /* search command with nothing following */ + } + if (str > pbuf_end - 1) { // search command with nothing following save_p_ws = p_ws; save_p_ic = p_ic; save_p_scs = p_scs; - p_ws = true; /* need 'wrapscan' for backward searches */ - p_ic = FALSE; /* don't ignore case now */ + p_ws = true; // need 'wrapscan' for backward searches + p_ic = FALSE; // don't ignore case now p_scs = FALSE; save_lnum = curwin->w_cursor.lnum; if (tagp.tagline > 0) { @@ -2898,15 +2946,17 @@ static int jumpto_tag( * For a help buffer: Put the cursor line at the top of the window, * the help subject will be below it. */ - if (curbuf->b_help) + if (curbuf->b_help) { set_topline(curwin, curwin->w_cursor.lnum); - if ((fdo_flags & FDO_TAG) && old_KeyTyped) + } + if ((fdo_flags & FDO_TAG) && old_KeyTyped) { foldOpenCursor(); + } } if (l_g_do_tagpreview != 0 && curwin != curwin_save && win_valid(curwin_save)) { - /* Return cursor to where we were */ + // Return cursor to where we were validate_cursor(); redraw_later(curwin, VALID); win_enter(curwin_save, true); @@ -2935,11 +2985,10 @@ erret: // If 'tagrelative' option set, change fname (name of file containing tag) // according to tag_fname (name of tag file containing fname). // Returns a pointer to allocated memory. -static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, - const bool expand) +static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, const bool expand) { - char_u *p; - char_u *expanded_fname = NULL; + char_u *p; + char_u *expanded_fname = NULL; expand_T xpc; /* @@ -2949,9 +2998,10 @@ static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, ExpandInit(&xpc); xpc.xp_context = EXPAND_FILES; expanded_fname = ExpandOne(&xpc, fname, NULL, - WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE); - if (expanded_fname != NULL) + WILD_LIST_NOTFOUND|WILD_SILENT, WILD_EXPAND_FREE); + if (expanded_fname != NULL) { fname = expanded_fname; + } } char_u *retval; @@ -2961,13 +3011,14 @@ static char_u *expand_tag_fname(char_u *fname, char_u *const tag_fname, retval = xmalloc(MAXPATHL); STRCPY(retval, tag_fname); STRLCPY(retval + (p - tag_fname), fname, - MAXPATHL - (p - tag_fname)); + MAXPATHL - (p - tag_fname)); /* * Translate names like "src/a/../b/file.c" into "src/b/file.c". */ simplify_filename(retval); - } else + } else { retval = vim_strsave(fname); + } xfree(expanded_fname); @@ -2984,9 +3035,9 @@ static int test_for_current(char_u *fname, char_u *fname_end, char_u *tag_fname, { int c; int retval = FALSE; - char_u *fullname; + char_u *fullname; - if (buf_ffname != NULL) { /* if the buffer has a name */ + if (buf_ffname != NULL) { // if the buffer has a name { c = *fname_end; *fname_end = NUL; @@ -3053,13 +3104,8 @@ static void tagstack_clear_entry(taggy_T *item) XFREE_CLEAR(item->user_data); } -int -expand_tags ( - int tagnames, /* expand tag names */ - char_u *pat, - int *num_file, - char_u ***file -) +/// @param tagnames expand tag names +int expand_tags(int tagnames, char_u *pat, int *num_file, char_u ***file) { int i; int extra_flag; @@ -3116,17 +3162,13 @@ expand_tags ( } -/* - * Add a tag field to the dictionary "dict". - * Return OK or FAIL. - */ -static int -add_tag_field( - dict_T *dict, - const char *field_name, - const char_u *start, // start of the value - const char_u *end // after the value; can be NULL -) +/// Add a tag field to the dictionary "dict". +/// Return OK or FAIL. +/// +/// @param start start of the value +/// @param end after the value; can be NULL +static int add_tag_field(dict_T *dict, const char *field_name, const char_u *start, + const char_u *end) FUNC_ATTR_NONNULL_ARG(1, 2) { int len = 0; @@ -3145,12 +3187,14 @@ add_tag_field( if (start != NULL) { if (end == NULL) { end = start + STRLEN(start); - while (end > start && (end[-1] == '\r' || end[-1] == '\n')) + while (end > start && (end[-1] == '\r' || end[-1] == '\n')) { --end; + } } len = (int)(end - start); - if (len > MAXPATHL - 1) + if (len > MAXPATHL - 1) { len = MAXPATHL - 1; + } STRLCPY(buf, start, len + 1); } buf[len] = NUL; @@ -3165,9 +3209,9 @@ add_tag_field( int get_tags(list_T *list, char_u *pat, char_u *buf_fname) { int num_matches, i, ret; - char_u **matches; - char_u *full_fname; - dict_T *dict; + char_u **matches; + char_u *full_fname; + dict_T *dict; tagptrs_T tp; bool is_static; @@ -3178,7 +3222,7 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname) int parse_result = parse_match(matches[i], &tp); // Avoid an unused variable warning in release builds. - (void) parse_result; + (void)parse_result; assert(parse_result == OK); is_static = test_for_static(&tp); @@ -3216,29 +3260,35 @@ int get_tags(list_T *list, char_u *pat, char_u *buf_fname) // skip "file:" (static tag) p += 4; } else if (!ascii_iswhite(*p)) { - char_u *s, *n; + char_u *s, *n; int len; /* Add extra field as a dict entry. Fields are * separated by Tabs. */ n = p; - while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') + while (*p != NUL && *p >= ' ' && *p < 127 && *p != ':') { ++p; + } len = (int)(p - n); if (*p == ':' && len > 0) { s = ++p; - while (*p != NUL && *p >= ' ') + while (*p != NUL && *p >= ' ') { ++p; + } n[len] = NUL; - if (add_tag_field(dict, (char *)n, s, p) == FAIL) + if (add_tag_field(dict, (char *)n, s, p) == FAIL) { ret = FAIL; + } n[len] = ':'; - } else - /* Skip field without colon. */ - while (*p != NUL && *p >= ' ') + } else { + // Skip field without colon. + while (*p != NUL && *p >= ' ') { ++p; - if (*p == NUL) + } + } + if (*p == NUL) { break; + } } } } @@ -3319,14 +3369,8 @@ static void tagstack_shift(win_T *wp) } // Push a new item to the tag stack -static void tagstack_push_item( - win_T *wp, - char_u *tagname, - int cur_fnum, - int cur_match, - pos_T mark, - int fnum, - char_u *user_data) +static void tagstack_push_item(win_T *wp, char_u *tagname, int cur_fnum, int cur_match, pos_T mark, + int fnum, char_u *user_data) { taggy_T *tagstack = wp->w_tagstack; int idx = wp->w_tagstacklen; // top of the stack @@ -3382,13 +3426,12 @@ static void tagstack_push_items(win_T *wp, list_T *l) if (mark.col > 0) { mark.col--; } - tagstack_push_item( - wp, - tagname, - (int)tv_dict_get_number(itemdict, "bufnr"), - (int)tv_dict_get_number(itemdict, "matchnr") - 1, - mark, fnum, - (char_u *)tv_dict_get_string(itemdict, "user_data", true)); + tagstack_push_item(wp, + tagname, + (int)tv_dict_get_number(itemdict, "bufnr"), + (int)tv_dict_get_number(itemdict, "matchnr") - 1, + mark, fnum, + (char_u *)tv_dict_get_string(itemdict, "user_data", true)); } } diff --git a/src/nvim/terminal.c b/src/nvim/terminal.c index 3335fa500a..6f19a9209e 100644 --- a/src/nvim/terminal.c +++ b/src/nvim/terminal.c @@ -37,45 +37,44 @@ // Some code from pangoterm http://www.leonerd.org.uk/code/pangoterm #include <assert.h> -#include <stdio.h> -#include <stdint.h> #include <stdbool.h> - +#include <stdint.h> +#include <stdio.h> #include <vterm.h> -#include "nvim/log.h" -#include "nvim/vim.h" -#include "nvim/terminal.h" -#include "nvim/message.h" -#include "nvim/memory.h" -#include "nvim/option.h" -#include "nvim/highlight.h" -#include "nvim/macros.h" -#include "nvim/mbyte.h" +#include "nvim/api/private/helpers.h" +#include "nvim/ascii.h" #include "nvim/buffer.h" #include "nvim/change.h" -#include "nvim/ascii.h" +#include "nvim/edit.h" +#include "nvim/event/loop.h" +#include "nvim/event/time.h" +#include "nvim/ex_cmds.h" +#include "nvim/ex_docmd.h" +#include "nvim/fileio.h" #include "nvim/getchar.h" -#include "nvim/ui.h" -#include "nvim/syntax.h" -#include "nvim/screen.h" +#include "nvim/highlight.h" #include "nvim/keymap.h" -#include "nvim/edit.h" -#include "nvim/mouse.h" -#include "nvim/memline.h" +#include "nvim/log.h" +#include "nvim/macros.h" +#include "nvim/main.h" #include "nvim/map.h" +#include "nvim/mbyte.h" +#include "nvim/memline.h" +#include "nvim/memory.h" +#include "nvim/message.h" #include "nvim/misc1.h" +#include "nvim/mouse.h" #include "nvim/move.h" -#include "nvim/main.h" +#include "nvim/option.h" +#include "nvim/os/input.h" +#include "nvim/screen.h" #include "nvim/state.h" -#include "nvim/ex_docmd.h" -#include "nvim/ex_cmds.h" +#include "nvim/syntax.h" +#include "nvim/terminal.h" +#include "nvim/ui.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/fileio.h" -#include "nvim/event/loop.h" -#include "nvim/event/time.h" -#include "nvim/os/input.h" -#include "nvim/api/private/helpers.h" typedef struct terminal_state { VimState state; @@ -210,7 +209,7 @@ Terminal *terminal_open(buf_T *buf, TerminalOptions opts) buf->b_p_ma = false; // 'nomodifiable' buf->b_p_ul = -1; // 'undolevels' buf->b_p_scbk = // 'scrollback' (initialize local from global) - (p_scbk < 0) ? 10000 : MAX(1, p_scbk); + (p_scbk < 0) ? 10000 : MAX(1, p_scbk); buf->b_p_tw = 0; // 'textwidth' set_option_value("wrap", false, NULL, OPT_LOCAL); set_option_value("list", false, NULL, OPT_LOCAL); @@ -452,56 +451,56 @@ static int terminal_execute(VimState *state, int key) TerminalState *s = (TerminalState *)state; switch (key) { - case K_LEFTMOUSE: - case K_LEFTDRAG: - case K_LEFTRELEASE: - case K_MOUSEMOVE: - case K_MIDDLEMOUSE: - case K_MIDDLEDRAG: - case K_MIDDLERELEASE: - case K_RIGHTMOUSE: - case K_RIGHTDRAG: - case K_RIGHTRELEASE: - case K_MOUSEDOWN: - case K_MOUSEUP: - if (send_mouse_event(s->term, key)) { - return 0; - } - break; - - case K_EVENT: - // We cannot let an event free the terminal yet. It is still needed. - s->term->refcount++; - state_handle_k_event(); - s->term->refcount--; - if (s->term->buf_handle == 0) { - s->close = true; - return 0; - } - break; + case K_LEFTMOUSE: + case K_LEFTDRAG: + case K_LEFTRELEASE: + case K_MOUSEMOVE: + case K_MIDDLEMOUSE: + case K_MIDDLEDRAG: + case K_MIDDLERELEASE: + case K_RIGHTMOUSE: + case K_RIGHTDRAG: + case K_RIGHTRELEASE: + case K_MOUSEDOWN: + case K_MOUSEUP: + if (send_mouse_event(s->term, key)) { + return 0; + } + break; + + case K_EVENT: + // We cannot let an event free the terminal yet. It is still needed. + s->term->refcount++; + state_handle_k_event(); + s->term->refcount--; + if (s->term->buf_handle == 0) { + s->close = true; + return 0; + } + break; - case K_COMMAND: - do_cmdline(NULL, getcmdkeycmd, NULL, 0); - break; + case K_COMMAND: + do_cmdline(NULL, getcmdkeycmd, NULL, 0); + break; - case Ctrl_N: - if (s->got_bsl) { - return 0; - } - FALLTHROUGH; + case Ctrl_N: + if (s->got_bsl) { + return 0; + } + FALLTHROUGH; - default: - if (key == Ctrl_BSL && !s->got_bsl) { - s->got_bsl = true; - break; - } - if (s->term->closed) { - s->close = true; - return 0; - } + default: + if (key == Ctrl_BSL && !s->got_bsl) { + s->got_bsl = true; + break; + } + if (s->term->closed) { + s->close = true; + return 0; + } - s->got_bsl = false; - terminal_send_key(s->term, key); + s->got_bsl = false; + terminal_send_key(s->term, key); } if (curbuf->terminal == NULL) { @@ -554,30 +553,30 @@ static bool is_filter_char(int c) { unsigned int flag = 0; switch (c) { - case 0x08: - flag = TPF_BS; - break; - case 0x09: - flag = TPF_HT; - break; - case 0x0A: - case 0x0D: - break; - case 0x0C: - flag = TPF_FF; - break; - case 0x1b: - flag = TPF_ESC; - break; - case 0x7F: - flag = TPF_DEL; - break; - default: - if (c < ' ') { - flag = TPF_C0; - } else if (c >= 0x80 && c <= 0x9F) { - flag = TPF_C1; - } + case 0x08: + flag = TPF_BS; + break; + case 0x09: + flag = TPF_HT; + break; + case 0x0A: + case 0x0D: + break; + case 0x0C: + flag = TPF_FF; + break; + case 0x1b: + flag = TPF_ESC; + break; + case 0x7F: + flag = TPF_DEL; + break; + default: + if (c < ' ') { + flag = TPF_C0; + } else if (c >= 0x80 && c <= 0x9F) { + flag = TPF_C1; + } } return !!(tpf_flags & flag); } @@ -666,8 +665,7 @@ static int get_rgb(VTermState *state, VTermColor color) } -void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, - int *term_attrs) +void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, int *term_attrs) { int height, width; vterm_get_size(term->vt, &height, &width); @@ -701,12 +699,12 @@ void terminal_get_line_attributes(Terminal *term, win_T *wp, int linenr, bool bg_set = vt_bg_idx && vt_bg_idx <= 16 && term->color_set[vt_bg_idx-1]; int hl_attrs = (cell.attrs.bold ? HL_BOLD : 0) - | (cell.attrs.italic ? HL_ITALIC : 0) - | (cell.attrs.reverse ? HL_INVERSE : 0) - | (cell.attrs.underline ? HL_UNDERLINE : 0) - | (cell.attrs.strike ? HL_STRIKETHROUGH: 0) - | ((fg_indexed && !fg_set) ? HL_FG_INDEXED : 0) - | ((bg_indexed && !bg_set) ? HL_BG_INDEXED : 0); + | (cell.attrs.italic ? HL_ITALIC : 0) + | (cell.attrs.reverse ? HL_INVERSE : 0) + | (cell.attrs.underline ? HL_UNDERLINE : 0) + | (cell.attrs.strike ? HL_STRIKETHROUGH: 0) + | ((fg_indexed && !fg_set) ? HL_FG_INDEXED : 0) + | ((bg_indexed && !bg_set) ? HL_BG_INDEXED : 0); int attr_id = 0; @@ -757,12 +755,11 @@ static int term_damage(VTermRect rect, void *data) static int term_moverect(VTermRect dest, VTermRect src, void *data) { invalidate_terminal(data, MIN(dest.start_row, src.start_row), - MAX(dest.end_row, src.end_row)); + MAX(dest.end_row, src.end_row)); return 1; } -static int term_movecursor(VTermPos new, VTermPos old, int visible, - void *data) +static int term_movecursor(VTermPos new, VTermPos old, int visible, void *data) { Terminal *term = data; term->cursor.row = new.row; @@ -791,26 +788,26 @@ static int term_settermprop(VTermProp prop, VTermValue *val, void *data) Terminal *term = data; switch (prop) { - case VTERM_PROP_ALTSCREEN: - break; - - case VTERM_PROP_CURSORVISIBLE: - term->cursor.visible = val->boolean; - invalidate_terminal(term, term->cursor.row, term->cursor.row + 1); - break; + case VTERM_PROP_ALTSCREEN: + break; + + case VTERM_PROP_CURSORVISIBLE: + term->cursor.visible = val->boolean; + invalidate_terminal(term, term->cursor.row, term->cursor.row + 1); + break; + + case VTERM_PROP_TITLE: { + buf_T *buf = handle_get_buffer(term->buf_handle); + buf_set_term_title(buf, val->string); + break; + } - case VTERM_PROP_TITLE: { - buf_T *buf = handle_get_buffer(term->buf_handle); - buf_set_term_title(buf, val->string); - break; - } + case VTERM_PROP_MOUSE: + term->forward_mouse = (bool)val->number; + break; - case VTERM_PROP_MOUSE: - term->forward_mouse = (bool)val->number; - break; - - default: - return 0; + default: + return 0; } return 1; @@ -844,12 +841,11 @@ static int term_sb_push(int cols, const VTermScreenCell *cells, void *data) // Make room at the start by shifting to the right. memmove(term->sb_buffer + 1, term->sb_buffer, - sizeof(term->sb_buffer[0]) * (term->sb_current - 1)); - + sizeof(term->sb_buffer[0]) * (term->sb_current - 1)); } else if (term->sb_current > 0) { // Make room at the start by shifting to the right. memmove(term->sb_buffer + 1, term->sb_buffer, - sizeof(term->sb_buffer[0]) * term->sb_current); + sizeof(term->sb_buffer[0]) * term->sb_current); } if (!sbrow) { @@ -894,7 +890,7 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) term->sb_current--; // Forget the "popped" row by shifting the rest onto it. memmove(term->sb_buffer, term->sb_buffer + 1, - sizeof(term->sb_buffer[0]) * (term->sb_current)); + sizeof(term->sb_buffer[0]) * (term->sb_current)); size_t cols_to_copy = (size_t)cols; if (cols_to_copy > sbrow->cols) { @@ -919,35 +915,41 @@ static int term_sb_pop(int cols, VTermScreenCell *cells, void *data) static void convert_modifiers(int key, VTermModifier *statep) { - if (mod_mask & MOD_MASK_SHIFT) { *statep |= VTERM_MOD_SHIFT; } - if (mod_mask & MOD_MASK_CTRL) { *statep |= VTERM_MOD_CTRL; } - if (mod_mask & MOD_MASK_ALT) { *statep |= VTERM_MOD_ALT; } + if (mod_mask & MOD_MASK_SHIFT) { + *statep |= VTERM_MOD_SHIFT; + } + if (mod_mask & MOD_MASK_CTRL) { + *statep |= VTERM_MOD_CTRL; + } + if (mod_mask & MOD_MASK_ALT) { + *statep |= VTERM_MOD_ALT; + } switch (key) { - case K_S_TAB: - case K_S_UP: - case K_S_DOWN: - case K_S_LEFT: - case K_S_RIGHT: - case K_S_F1: - case K_S_F2: - case K_S_F3: - case K_S_F4: - case K_S_F5: - case K_S_F6: - case K_S_F7: - case K_S_F8: - case K_S_F9: - case K_S_F10: - case K_S_F11: - case K_S_F12: - *statep |= VTERM_MOD_SHIFT; - break; - - case K_C_LEFT: - case K_C_RIGHT: - *statep |= VTERM_MOD_CTRL; - break; + case K_S_TAB: + case K_S_UP: + case K_S_DOWN: + case K_S_LEFT: + case K_S_RIGHT: + case K_S_F1: + case K_S_F2: + case K_S_F3: + case K_S_F4: + case K_S_F5: + case K_S_F6: + case K_S_F7: + case K_S_F8: + case K_S_F9: + case K_S_F10: + case K_S_F11: + case K_S_F12: + *statep |= VTERM_MOD_SHIFT; + break; + + case K_C_LEFT: + case K_C_RIGHT: + *statep |= VTERM_MOD_CTRL; + break; } } @@ -956,115 +958,212 @@ static VTermKey convert_key(int key, VTermModifier *statep) convert_modifiers(key, statep); switch (key) { - case K_BS: return VTERM_KEY_BACKSPACE; - case K_S_TAB: FALLTHROUGH; - case TAB: return VTERM_KEY_TAB; - case Ctrl_M: return VTERM_KEY_ENTER; - case ESC: return VTERM_KEY_ESCAPE; - - case K_S_UP: FALLTHROUGH; - case K_UP: return VTERM_KEY_UP; - case K_S_DOWN: FALLTHROUGH; - case K_DOWN: return VTERM_KEY_DOWN; - case K_S_LEFT: FALLTHROUGH; - case K_C_LEFT: FALLTHROUGH; - case K_LEFT: return VTERM_KEY_LEFT; - case K_S_RIGHT: FALLTHROUGH; - case K_C_RIGHT: FALLTHROUGH; - case K_RIGHT: return VTERM_KEY_RIGHT; - - case K_INS: return VTERM_KEY_INS; - case K_DEL: return VTERM_KEY_DEL; - case K_HOME: return VTERM_KEY_HOME; - case K_END: return VTERM_KEY_END; - case K_PAGEUP: return VTERM_KEY_PAGEUP; - case K_PAGEDOWN: return VTERM_KEY_PAGEDOWN; - - case K_K0: FALLTHROUGH; - case K_KINS: return VTERM_KEY_KP_0; - case K_K1: FALLTHROUGH; - case K_KEND: return VTERM_KEY_KP_1; - case K_K2: FALLTHROUGH; - case K_KDOWN: return VTERM_KEY_KP_2; - case K_K3: FALLTHROUGH; - case K_KPAGEDOWN: return VTERM_KEY_KP_3; - case K_K4: FALLTHROUGH; - case K_KLEFT: return VTERM_KEY_KP_4; - case K_K5: FALLTHROUGH; - case K_KORIGIN: return VTERM_KEY_KP_5; - case K_K6: FALLTHROUGH; - case K_KRIGHT: return VTERM_KEY_KP_6; - case K_K7: FALLTHROUGH; - case K_KHOME: return VTERM_KEY_KP_7; - case K_K8: FALLTHROUGH; - case K_KUP: return VTERM_KEY_KP_8; - case K_K9: FALLTHROUGH; - case K_KPAGEUP: return VTERM_KEY_KP_9; - case K_KDEL: FALLTHROUGH; - case K_KPOINT: return VTERM_KEY_KP_PERIOD; - case K_KENTER: return VTERM_KEY_KP_ENTER; - case K_KPLUS: return VTERM_KEY_KP_PLUS; - case K_KMINUS: return VTERM_KEY_KP_MINUS; - case K_KMULTIPLY: return VTERM_KEY_KP_MULT; - case K_KDIVIDE: return VTERM_KEY_KP_DIVIDE; - - case K_S_F1: FALLTHROUGH; - case K_F1: return VTERM_KEY_FUNCTION(1); - case K_S_F2: FALLTHROUGH; - case K_F2: return VTERM_KEY_FUNCTION(2); - case K_S_F3: FALLTHROUGH; - case K_F3: return VTERM_KEY_FUNCTION(3); - case K_S_F4: FALLTHROUGH; - case K_F4: return VTERM_KEY_FUNCTION(4); - case K_S_F5: FALLTHROUGH; - case K_F5: return VTERM_KEY_FUNCTION(5); - case K_S_F6: FALLTHROUGH; - case K_F6: return VTERM_KEY_FUNCTION(6); - case K_S_F7: FALLTHROUGH; - case K_F7: return VTERM_KEY_FUNCTION(7); - case K_S_F8: FALLTHROUGH; - case K_F8: return VTERM_KEY_FUNCTION(8); - case K_S_F9: FALLTHROUGH; - case K_F9: return VTERM_KEY_FUNCTION(9); - case K_S_F10: FALLTHROUGH; - case K_F10: return VTERM_KEY_FUNCTION(10); - case K_S_F11: FALLTHROUGH; - case K_F11: return VTERM_KEY_FUNCTION(11); - case K_S_F12: FALLTHROUGH; - case K_F12: return VTERM_KEY_FUNCTION(12); - - case K_F13: return VTERM_KEY_FUNCTION(13); - case K_F14: return VTERM_KEY_FUNCTION(14); - case K_F15: return VTERM_KEY_FUNCTION(15); - case K_F16: return VTERM_KEY_FUNCTION(16); - case K_F17: return VTERM_KEY_FUNCTION(17); - case K_F18: return VTERM_KEY_FUNCTION(18); - case K_F19: return VTERM_KEY_FUNCTION(19); - case K_F20: return VTERM_KEY_FUNCTION(20); - case K_F21: return VTERM_KEY_FUNCTION(21); - case K_F22: return VTERM_KEY_FUNCTION(22); - case K_F23: return VTERM_KEY_FUNCTION(23); - case K_F24: return VTERM_KEY_FUNCTION(24); - case K_F25: return VTERM_KEY_FUNCTION(25); - case K_F26: return VTERM_KEY_FUNCTION(26); - case K_F27: return VTERM_KEY_FUNCTION(27); - case K_F28: return VTERM_KEY_FUNCTION(28); - case K_F29: return VTERM_KEY_FUNCTION(29); - case K_F30: return VTERM_KEY_FUNCTION(30); - case K_F31: return VTERM_KEY_FUNCTION(31); - case K_F32: return VTERM_KEY_FUNCTION(32); - case K_F33: return VTERM_KEY_FUNCTION(33); - case K_F34: return VTERM_KEY_FUNCTION(34); - case K_F35: return VTERM_KEY_FUNCTION(35); - case K_F36: return VTERM_KEY_FUNCTION(36); - case K_F37: return VTERM_KEY_FUNCTION(37); - - default: return VTERM_KEY_NONE; + case K_BS: + return VTERM_KEY_BACKSPACE; + case K_S_TAB: + FALLTHROUGH; + case TAB: + return VTERM_KEY_TAB; + case Ctrl_M: + return VTERM_KEY_ENTER; + case ESC: + return VTERM_KEY_ESCAPE; + + case K_S_UP: + FALLTHROUGH; + case K_UP: + return VTERM_KEY_UP; + case K_S_DOWN: + FALLTHROUGH; + case K_DOWN: + return VTERM_KEY_DOWN; + case K_S_LEFT: + FALLTHROUGH; + case K_C_LEFT: + FALLTHROUGH; + case K_LEFT: + return VTERM_KEY_LEFT; + case K_S_RIGHT: + FALLTHROUGH; + case K_C_RIGHT: + FALLTHROUGH; + case K_RIGHT: + return VTERM_KEY_RIGHT; + + case K_INS: + return VTERM_KEY_INS; + case K_DEL: + return VTERM_KEY_DEL; + case K_HOME: + return VTERM_KEY_HOME; + case K_END: + return VTERM_KEY_END; + case K_PAGEUP: + return VTERM_KEY_PAGEUP; + case K_PAGEDOWN: + return VTERM_KEY_PAGEDOWN; + + case K_K0: + FALLTHROUGH; + case K_KINS: + return VTERM_KEY_KP_0; + case K_K1: + FALLTHROUGH; + case K_KEND: + return VTERM_KEY_KP_1; + case K_K2: + FALLTHROUGH; + case K_KDOWN: + return VTERM_KEY_KP_2; + case K_K3: + FALLTHROUGH; + case K_KPAGEDOWN: + return VTERM_KEY_KP_3; + case K_K4: + FALLTHROUGH; + case K_KLEFT: + return VTERM_KEY_KP_4; + case K_K5: + FALLTHROUGH; + case K_KORIGIN: + return VTERM_KEY_KP_5; + case K_K6: + FALLTHROUGH; + case K_KRIGHT: + return VTERM_KEY_KP_6; + case K_K7: + FALLTHROUGH; + case K_KHOME: + return VTERM_KEY_KP_7; + case K_K8: + FALLTHROUGH; + case K_KUP: + return VTERM_KEY_KP_8; + case K_K9: + FALLTHROUGH; + case K_KPAGEUP: + return VTERM_KEY_KP_9; + case K_KDEL: + FALLTHROUGH; + case K_KPOINT: + return VTERM_KEY_KP_PERIOD; + case K_KENTER: + return VTERM_KEY_KP_ENTER; + case K_KPLUS: + return VTERM_KEY_KP_PLUS; + case K_KMINUS: + return VTERM_KEY_KP_MINUS; + case K_KMULTIPLY: + return VTERM_KEY_KP_MULT; + case K_KDIVIDE: + return VTERM_KEY_KP_DIVIDE; + + case K_S_F1: + FALLTHROUGH; + case K_F1: + return VTERM_KEY_FUNCTION(1); + case K_S_F2: + FALLTHROUGH; + case K_F2: + return VTERM_KEY_FUNCTION(2); + case K_S_F3: + FALLTHROUGH; + case K_F3: + return VTERM_KEY_FUNCTION(3); + case K_S_F4: + FALLTHROUGH; + case K_F4: + return VTERM_KEY_FUNCTION(4); + case K_S_F5: + FALLTHROUGH; + case K_F5: + return VTERM_KEY_FUNCTION(5); + case K_S_F6: + FALLTHROUGH; + case K_F6: + return VTERM_KEY_FUNCTION(6); + case K_S_F7: + FALLTHROUGH; + case K_F7: + return VTERM_KEY_FUNCTION(7); + case K_S_F8: + FALLTHROUGH; + case K_F8: + return VTERM_KEY_FUNCTION(8); + case K_S_F9: + FALLTHROUGH; + case K_F9: + return VTERM_KEY_FUNCTION(9); + case K_S_F10: + FALLTHROUGH; + case K_F10: + return VTERM_KEY_FUNCTION(10); + case K_S_F11: + FALLTHROUGH; + case K_F11: + return VTERM_KEY_FUNCTION(11); + case K_S_F12: + FALLTHROUGH; + case K_F12: + return VTERM_KEY_FUNCTION(12); + + case K_F13: + return VTERM_KEY_FUNCTION(13); + case K_F14: + return VTERM_KEY_FUNCTION(14); + case K_F15: + return VTERM_KEY_FUNCTION(15); + case K_F16: + return VTERM_KEY_FUNCTION(16); + case K_F17: + return VTERM_KEY_FUNCTION(17); + case K_F18: + return VTERM_KEY_FUNCTION(18); + case K_F19: + return VTERM_KEY_FUNCTION(19); + case K_F20: + return VTERM_KEY_FUNCTION(20); + case K_F21: + return VTERM_KEY_FUNCTION(21); + case K_F22: + return VTERM_KEY_FUNCTION(22); + case K_F23: + return VTERM_KEY_FUNCTION(23); + case K_F24: + return VTERM_KEY_FUNCTION(24); + case K_F25: + return VTERM_KEY_FUNCTION(25); + case K_F26: + return VTERM_KEY_FUNCTION(26); + case K_F27: + return VTERM_KEY_FUNCTION(27); + case K_F28: + return VTERM_KEY_FUNCTION(28); + case K_F29: + return VTERM_KEY_FUNCTION(29); + case K_F30: + return VTERM_KEY_FUNCTION(30); + case K_F31: + return VTERM_KEY_FUNCTION(31); + case K_F32: + return VTERM_KEY_FUNCTION(32); + case K_F33: + return VTERM_KEY_FUNCTION(33); + case K_F34: + return VTERM_KEY_FUNCTION(34); + case K_F35: + return VTERM_KEY_FUNCTION(35); + case K_F36: + return VTERM_KEY_FUNCTION(36); + case K_F37: + return VTERM_KEY_FUNCTION(37); + + default: + return VTERM_KEY_NONE; } } -static void mouse_action(Terminal *term, int button, int row, int col, - bool drag, VTermModifier mod) +static void mouse_action(Terminal *term, int button, int row, int col, bool drag, VTermModifier mod) { if (term->pressed_button && (term->pressed_button != button || !drag)) { // release the previous button @@ -1100,16 +1199,26 @@ static bool send_mouse_event(Terminal *term, int c) bool drag = false; switch (c) { - case K_LEFTDRAG: drag = true; FALLTHROUGH; - case K_LEFTMOUSE: button = 1; break; - case K_MOUSEMOVE: drag = true; button = 0; break; - case K_MIDDLEDRAG: drag = true; FALLTHROUGH; - case K_MIDDLEMOUSE: button = 2; break; - case K_RIGHTDRAG: drag = true; FALLTHROUGH; - case K_RIGHTMOUSE: button = 3; break; - case K_MOUSEDOWN: button = 4; break; - case K_MOUSEUP: button = 5; break; - default: return false; + case K_LEFTDRAG: + drag = true; FALLTHROUGH; + case K_LEFTMOUSE: + button = 1; break; + case K_MOUSEMOVE: + drag = true; button = 0; break; + case K_MIDDLEDRAG: + drag = true; FALLTHROUGH; + case K_MIDDLEMOUSE: + button = 2; break; + case K_RIGHTDRAG: + drag = true; FALLTHROUGH; + case K_RIGHTMOUSE: + button = 3; break; + case K_MOUSEDOWN: + button = 4; break; + case K_MOUSEUP: + button = 5; break; + default: + return false; } mouse_action(term, button, row, col - offset, drag, 0); @@ -1162,7 +1271,7 @@ static void fetch_row(Terminal *term, int row, int end_col) if (cell.chars[0]) { for (int i = 0; cell.chars[i]; i++) { cell_len += utf_char2bytes((int)cell.chars[i], - (uint8_t *)ptr + cell_len); + (uint8_t *)ptr + cell_len); } } else { *ptr = ' '; @@ -1181,8 +1290,7 @@ static void fetch_row(Terminal *term, int row, int end_col) term->textbuf[line_len] = 0; } -static bool fetch_cell(Terminal *term, int row, int col, - VTermScreenCell *cell) +static bool fetch_cell(Terminal *term, int row, int col, VTermScreenCell *cell) { if (row < 0) { ScrollbackLine *sbrow = term->sb_buffer[-row - 1]; @@ -1197,8 +1305,8 @@ static bool fetch_cell(Terminal *term, int row, int col, return false; } } else { - vterm_screen_get_cell(term->vts, (VTermPos){.row = row, .col = col}, - cell); + vterm_screen_get_cell(term->vts, (VTermPos){ .row = row, .col = col }, + cell); } return true; } diff --git a/src/nvim/testdir/test_filetype.vim b/src/nvim/testdir/test_filetype.vim index cc789cb6bd..4a9cb4a8d8 100644 --- a/src/nvim/testdir/test_filetype.vim +++ b/src/nvim/testdir/test_filetype.vim @@ -259,7 +259,7 @@ let s:filename_checks = { \ 'jgraph': ['file.jgr'], \ 'jovial': ['file.jov', 'file.j73', 'file.jovial'], \ 'jproperties': ['file.properties', 'file.properties_xx', 'file.properties_xx_xx', 'some.properties_xx_xx_file'], - \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb'], + \ 'json': ['file.json', 'file.jsonp', 'file.json-patch', 'file.webmanifest', 'Pipfile.lock', 'file.ipynb', '.babelrc', '.eslintrc', '.prettierrc', '.firebaserc'], \ 'jsonc': ['file.jsonc'], \ 'jsp': ['file.jsp'], \ 'julia': ['file.jl'], @@ -344,6 +344,7 @@ let s:filename_checks = { \ 'nanorc': ['/etc/nanorc', 'file.nanorc', 'any/etc/nanorc'], \ 'ncf': ['file.ncf'], \ 'netrc': ['.netrc'], + \ 'nginx': ['file.nginx', 'nginxfile.conf', 'filenginx.conf', 'any/etc/nginx/file', 'any/usr/local/nginx/conf/file', 'any/nginx/file.conf'], \ 'ninja': ['file.ninja'], \ 'nqc': ['file.nqc'], \ 'nroff': ['file.tr', 'file.nr', 'file.roff', 'file.tmac', 'file.mom', 'tmac.file'], diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim index e82fefc7fc..366615821c 100644 --- a/src/nvim/testdir/test_functions.vim +++ b/src/nvim/testdir/test_functions.vim @@ -585,6 +585,8 @@ func Test_mode() exe "normal iabc\<C-X>\<C-L>\<F2>\<Esc>u" call assert_equal('i-ic', g:current_modes) + exe "normal R\<F2>\<Esc>" + call assert_equal('R-R', g:current_modes) " R_CTRL-P: Multiple matches exe "normal RBa\<C-P>\<F2>\<Esc>u" call assert_equal('R-Rc', g:current_modes) @@ -619,6 +621,42 @@ func Test_mode() exe "normal Rabc\<C-X>\<C-L>\<F2>\<Esc>u" call assert_equal('R-Rc', g:current_modes) + exe "normal gR\<F2>\<Esc>" + call assert_equal('R-Rv', g:current_modes) + " gR_CTRL-P: Multiple matches + exe "normal gRBa\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-P: Single match + exe "normal gRBro\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X + exe "normal gRBa\<C-X>\<F2>\<Esc>u" + call assert_equal('R-Rvx', g:current_modes) + " gR_CTRL-X CTRL-P: Multiple matches + exe "normal gRBa\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-P: Single match + exe "normal gRBro\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-P + CTRL-P: Single match + exe "normal gRBro\<C-X>\<C-P>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-L: Multiple matches + exe "normal gR\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-L: Single match + exe "normal gRBlu\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-P: No match + exe "normal gRCom\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-P: No match + exe "normal gRCom\<C-X>\<C-P>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + " gR_CTRL-X CTRL-L: No match + exe "normal gRabc\<C-X>\<C-L>\<F2>\<Esc>u" + call assert_equal('R-Rvc', g:current_modes) + call assert_equal('n', mode(0)) call assert_equal('n', mode(1)) diff --git a/src/nvim/testdir/test_popup.vim b/src/nvim/testdir/test_popup.vim index 710450293c..eb367cfe5c 100644 --- a/src/nvim/testdir/test_popup.vim +++ b/src/nvim/testdir/test_popup.vim @@ -950,6 +950,10 @@ func Test_popup_complete_info_01() \ ["\<C-X>", 'ctrl_x'], \ ["\<C-X>\<C-N>", 'keyword'], \ ["\<C-X>\<C-P>", 'keyword'], + \ ["\<C-X>\<C-E>", 'scroll'], + \ ["\<C-X>\<C-Y>", 'scroll'], + \ ["\<C-X>\<C-E>\<C-E>\<C-Y>", 'scroll'], + \ ["\<C-X>\<C-Y>\<C-E>\<C-Y>", 'scroll'], \ ["\<C-X>\<C-L>", 'whole_line'], \ ["\<C-X>\<C-F>", 'files'], \ ["\<C-X>\<C-]>", 'tags'], diff --git a/src/nvim/testdir/test_startup.vim b/src/nvim/testdir/test_startup.vim index daebe25466..b140077111 100644 --- a/src/nvim/testdir/test_startup.vim +++ b/src/nvim/testdir/test_startup.vim @@ -27,8 +27,8 @@ func Test_after_comes_later() set guioptions+=M let $HOME = "/does/not/exist" set loadplugins - set rtp=Xhere,Xafter,Xanother - set packpath=Xhere,Xafter + set rtp=Xhere,Xdir/after,Xanother + set packpath=Xhere,Xdir/after set nomore let g:sequence = "" [CODE] @@ -50,8 +50,8 @@ func Test_after_comes_later() call mkdir('Xhere/pack/foo/start/foobar/plugin', 'p') call writefile(['let g:sequence .= "pack "'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim') - call mkdir('Xafter/plugin', 'p') - call writefile(['let g:sequence .= "after "'], 'Xafter/plugin/later.vim') + call mkdir('Xdir/after/plugin', 'p') + call writefile(['let g:sequence .= "after "'], 'Xdir/after/plugin/later.vim') if RunVim(before, after, '') @@ -74,7 +74,7 @@ func Test_after_comes_later() call delete('Xsequence') call delete('Xhere', 'rf') call delete('Xanother', 'rf') - call delete('Xafter', 'rf') + call delete('Xdir', 'rf') endfunc func Test_pack_in_rtp_when_plugins_run() diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c index fb5e12c20e..803ff23cea 100644 --- a/src/nvim/tui/tui.c +++ b/src/nvim/tui/tui.c @@ -59,12 +59,12 @@ #define LINUXSET1C "\x1b[?1c" #ifdef NVIM_UNIBI_HAS_VAR_FROM -#define UNIBI_SET_NUM_VAR(var, num) \ +# define UNIBI_SET_NUM_VAR(var, num) \ do { \ (var) = unibi_var_from_num((num)); \ } while (0) #else -#define UNIBI_SET_NUM_VAR(var, num) (var).i = (num); +# define UNIBI_SET_NUM_VAR(var, num) (var).i = (num); #endif typedef struct { @@ -1483,7 +1483,7 @@ static void tui_guess_size(UI *ui) height = unibi_get_num(data->ut, unibi_lines); width = unibi_get_num(data->ut, unibi_columns); -end: + end: if (width <= 0 || height <= 0) { // use the defaults width = DFLT_COLS; @@ -2111,14 +2111,14 @@ static void flush_buf(UI *ui) static const char *tui_get_stty_erase(void) { static char stty_erase[2] = { 0 }; -#if defined(HAVE_TERMIOS_H) +# if defined(HAVE_TERMIOS_H) struct termios t; if (tcgetattr(input_global_fd(), &t) != -1) { stty_erase[0] = (char)t.c_cc[VERASE]; stty_erase[1] = '\0'; DLOG("stty/termios:erase=%s", stty_erase); } -#endif +# endif return stty_erase; } diff --git a/src/nvim/ugrid.c b/src/nvim/ugrid.c index 9e4aaff878..ef84cdf334 100644 --- a/src/nvim/ugrid.c +++ b/src/nvim/ugrid.c @@ -2,14 +2,14 @@ // it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com #include <assert.h> +#include <limits.h> #include <stdbool.h> #include <stdio.h> -#include <limits.h> #include "nvim/assert.h" -#include "nvim/vim.h" -#include "nvim/ui.h" #include "nvim/ugrid.h" +#include "nvim/ui.h" +#include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ugrid.c.generated.h" @@ -79,8 +79,7 @@ void ugrid_scroll(UGrid *grid, int top, int bot, int left, int right, int count) } } -static void clear_region(UGrid *grid, int top, int bot, int left, int right, - sattr_T attr) +static void clear_region(UGrid *grid, int top, int bot, int left, int right, sattr_T attr) { for (int row = top; row <= bot; row++) { UGRID_FOREACH_CELL(grid, row, left, right+1, { diff --git a/src/nvim/ui.c b/src/nvim/ui.c index 09709d0f43..aad72af025 100644 --- a/src/nvim/ui.c +++ b/src/nvim/ui.c @@ -3,40 +3,40 @@ #include <assert.h> #include <inttypes.h> +#include <limits.h> #include <stdbool.h> #include <string.h> -#include <limits.h> -#include "nvim/vim.h" -#include "nvim/log.h" +#include "nvim/ascii.h" #include "nvim/aucmd.h" -#include "nvim/ui.h" #include "nvim/charset.h" #include "nvim/cursor.h" +#include "nvim/cursor_shape.h" #include "nvim/diff.h" +#include "nvim/event/loop.h" #include "nvim/ex_cmds2.h" #include "nvim/ex_getln.h" #include "nvim/fold.h" +#include "nvim/garray.h" +#include "nvim/highlight.h" +#include "nvim/log.h" #include "nvim/main.h" -#include "nvim/ascii.h" -#include "nvim/misc1.h" #include "nvim/mbyte.h" -#include "nvim/garray.h" #include "nvim/memory.h" +#include "nvim/misc1.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" -#include "nvim/os_unix.h" -#include "nvim/event/loop.h" -#include "nvim/os/time.h" #include "nvim/os/input.h" #include "nvim/os/signal.h" +#include "nvim/os/time.h" +#include "nvim/os_unix.h" #include "nvim/popupmnu.h" #include "nvim/screen.h" -#include "nvim/highlight.h" +#include "nvim/ui.h" #include "nvim/ui_compositor.h" +#include "nvim/vim.h" #include "nvim/window.h" -#include "nvim/cursor_shape.h" #ifdef FEAT_TUI # include "nvim/tui/tui.h" #else @@ -70,9 +70,9 @@ static int pending_has_mouse = -1; static size_t uilog_seen = 0; static char uilog_last_event[1024] = { 0 }; -#ifndef EXITFREE -#define entered_free_all_mem false -#endif +# ifndef EXITFREE +# define entered_free_all_mem false +# endif # define UI_LOG(funname) \ do { \ @@ -95,7 +95,7 @@ static char uilog_last_event[1024] = { 0 }; // UI_CALL invokes a function on all registered UI instances. // This is called by code generated by generators/gen_api_ui_events.lua // C code should use ui_call_{funname} instead. -# define UI_CALL(cond, funname, ...) \ +#define UI_CALL(cond, funname, ...) \ do { \ bool any_call = false; \ for (size_t i = 0; i < ui_count; i++) { \ @@ -115,7 +115,7 @@ static char uilog_last_event[1024] = { 0 }; #endif #ifndef EXITFREE -#undef entered_free_all_mem +# undef entered_free_all_mem #endif void ui_init(void) @@ -382,8 +382,8 @@ void ui_set_ext_option(UI *ui, UIExtension ext, bool active) } } -void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol, - int clearattr, bool wrap) +void ui_line(ScreenGrid *grid, int row, int startcol, int endcol, int clearcol, int clearattr, + bool wrap) { assert(0 <= row && row < grid->Rows); LineFlags flags = wrap ? kLineFlagWrap : 0; @@ -515,23 +515,23 @@ void ui_check_mouse(void) // normal editing mode (not at hit-return message). for (char_u *p = p_mouse; *p; p++) { switch (*p) { - case 'a': - if (vim_strchr((char_u *)MOUSE_A, checkfor) != NULL) { - has_mouse = true; - return; - } - break; - case MOUSE_HELP: - if (checkfor != MOUSE_RETURN && curbuf->b_help) { - has_mouse = true; - return; - } - break; - default: - if (checkfor == *p) { - has_mouse = true; - return; - } + case 'a': + if (vim_strchr((char_u *)MOUSE_A, checkfor) != NULL) { + has_mouse = true; + return; + } + break; + case MOUSE_HELP: + if (checkfor != MOUSE_RETURN && curbuf->b_help) { + has_mouse = true; + return; + } + break; + default: + if (checkfor == *p) { + has_mouse = true; + return; + } } } } diff --git a/src/nvim/ui_bridge.c b/src/nvim/ui_bridge.c index 25f45b8fe6..3402df817a 100644 --- a/src/nvim/ui_bridge.c +++ b/src/nvim/ui_bridge.c @@ -5,18 +5,18 @@ // Used by the built-in TUI and libnvim-based UIs. #include <assert.h> +#include <limits.h> #include <stdbool.h> #include <stdio.h> -#include <limits.h> +#include "nvim/api/private/helpers.h" #include "nvim/log.h" #include "nvim/main.h" -#include "nvim/vim.h" -#include "nvim/ui.h" #include "nvim/memory.h" -#include "nvim/ui_bridge.h" #include "nvim/ugrid.h" -#include "nvim/api/private/helpers.h" +#include "nvim/ui.h" +#include "nvim/ui_bridge.h" +#include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_bridge.c.generated.h" @@ -26,8 +26,7 @@ // Schedule a function call on the UI bridge thread. #define UI_BRIDGE_CALL(ui, name, argc, ...) \ - ((UIBridgeData *)ui)->scheduler( \ - event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)) + ((UIBridgeData *)ui)->scheduler(event_create(ui_bridge_##name##_event, argc, __VA_ARGS__), UI(ui)) #define INT2PTR(i) ((void *)(intptr_t)i) #define PTR2INT(p) ((Integer)(intptr_t)p) @@ -41,6 +40,8 @@ UI *ui_bridge_attach(UI *ui, ui_main_fn ui_main, event_scheduler scheduler) UIBridgeData *rv = xcalloc(1, sizeof(UIBridgeData)); rv->ui = ui; rv->bridge.rgb = ui->rgb; + rv->bridge.width = ui->width; + rv->bridge.height = ui->height; rv->bridge.stop = ui_bridge_stop; rv->bridge.grid_resize = ui_bridge_grid_resize; rv->bridge.grid_clear = ui_bridge_grid_clear; @@ -136,8 +137,8 @@ static void ui_bridge_stop_event(void **argv) ui->stop(ui); } -static void ui_bridge_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, - HlAttrs cterm_attrs, Array info) +static void ui_bridge_hl_attr_define(UI *ui, Integer id, HlAttrs attrs, HlAttrs cterm_attrs, + Array info) { HlAttrs *a = xmalloc(sizeof(HlAttrs)); *a = attrs; @@ -161,11 +162,9 @@ static void ui_bridge_raw_line_event(void **argv) xfree(argv[8]); xfree(argv[9]); } -static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row, - Integer startcol, Integer endcol, - Integer clearcol, Integer clearattr, - LineFlags flags, const schar_T *chunk, - const sattr_T *attrs) +static void ui_bridge_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, + Integer clearcol, Integer clearattr, LineFlags flags, + const schar_T *chunk, const sattr_T *attrs) { size_t ncol = (size_t)(endcol-startcol); schar_T *c = xmemdup(chunk, ncol * sizeof(schar_T)); diff --git a/src/nvim/ui_compositor.c b/src/nvim/ui_compositor.c index 9c9aec1cf5..7a0f68cfeb 100644 --- a/src/nvim/ui_compositor.c +++ b/src/nvim/ui_compositor.c @@ -7,27 +7,27 @@ // Layer-based compositing: https://en.wikipedia.org/wiki/Digital_compositing #include <assert.h> +#include <limits.h> #include <stdbool.h> #include <stdio.h> -#include <limits.h> +#include "nvim/api/private/helpers.h" +#include "nvim/ascii.h" +#include "nvim/highlight.h" #include "nvim/lib/kvec.h" #include "nvim/log.h" +#include "nvim/lua/executor.h" #include "nvim/main.h" -#include "nvim/ascii.h" -#include "nvim/vim.h" -#include "nvim/ui.h" -#include "nvim/highlight.h" #include "nvim/memory.h" #include "nvim/message.h" +#include "nvim/os/os.h" #include "nvim/popupmnu.h" -#include "nvim/ui_compositor.h" -#include "nvim/ugrid.h" #include "nvim/screen.h" #include "nvim/syntax.h" -#include "nvim/api/private/helpers.h" -#include "nvim/lua/executor.h" -#include "nvim/os/os.h" +#include "nvim/ugrid.h" +#include "nvim/ui.h" +#include "nvim/ui_compositor.h" +#include "nvim/vim.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ui_compositor.c.generated.h" @@ -123,8 +123,8 @@ bool ui_comp_should_draw(void) /// TODO(bfredl): later on the compositor should just use win_float_pos events, /// though that will require slight event order adjustment: emit the win_pos /// events in the beginning of update_screen(0), rather than in ui_flush() -bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, - bool valid, bool on_top) +bool ui_comp_put_grid(ScreenGrid *grid, int row, int col, int height, int width, bool valid, + bool on_top) { bool moved; @@ -257,8 +257,7 @@ static void ui_comp_raise_grid(ScreenGrid *grid, size_t new_index) } } -static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, - Integer r, Integer c) +static void ui_comp_grid_cursor_goto(UI *ui, Integer grid_handle, Integer r, Integer c) { if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid_handle)) { return; @@ -304,8 +303,7 @@ ScreenGrid *ui_comp_mouse_focus(int row, int col) /// Baseline implementation. This is always correct, but we can sometimes /// do something more efficient (where efficiency means smaller deltas to /// the downstream UI.) -static void compose_line(Integer row, Integer startcol, Integer endcol, - LineFlags flags) +static void compose_line(Integer row, Integer startcol, Integer endcol, LineFlags flags) { // If rightleft is set, startcol may be -1. In such cases, the assertions // will fail because no overlap is found. Adjust startcol to prevent it. @@ -447,8 +445,8 @@ static void compose_line(Integer row, Integer startcol, Integer endcol, (const sattr_T *)attrbuf+skipstart); } -static void compose_debug(Integer startrow, Integer endrow, Integer startcol, - Integer endcol, int syn_id, bool delay) +static void compose_debug(Integer startrow, Integer endrow, Integer startcol, Integer endcol, + int syn_id, bool delay) { if (!(rdb_flags & RDB_COMPOSITOR)) { return; @@ -479,8 +477,7 @@ static void debug_delay(Integer lines) } -static void compose_area(Integer startrow, Integer endrow, - Integer startcol, Integer endcol) +static void compose_area(Integer startrow, Integer endrow, Integer startcol, Integer endcol) { compose_debug(startrow, endrow, startcol, endcol, dbghl_recompose, true); endrow = MIN(endrow, default_grid.Rows); @@ -505,11 +502,9 @@ void ui_comp_compose_grid(ScreenGrid *grid) } } -static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, - Integer startcol, Integer endcol, - Integer clearcol, Integer clearattr, - LineFlags flags, const schar_T *chunk, - const sattr_T *attrs) +static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, Integer startcol, Integer endcol, + Integer clearcol, Integer clearattr, LineFlags flags, + const schar_T *chunk, const sattr_T *attrs) { if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid)) { return; @@ -529,11 +524,11 @@ static void ui_comp_raw_line(UI *ui, Integer grid, Integer row, // when resizing nvim, a window will be attempted to be drawn on the older // and possibly larger global screen size. if (row >= default_grid.Rows) { - DLOG("compositor: invalid row %"PRId64" on grid %"PRId64, row, grid); + DLOG("compositor: invalid row %" PRId64 " on grid %" PRId64, row, grid); return; } if (clearcol > default_grid.Columns) { - DLOG("compositor: invalid last column %"PRId64" on grid %"PRId64, + DLOG("compositor: invalid last column %" PRId64 " on grid %" PRId64, clearcol, grid); if (startcol >= default_grid.Columns) { return; @@ -572,8 +567,8 @@ void ui_comp_set_screen_valid(bool valid) } } -static void ui_comp_msg_set_pos(UI *ui, Integer grid, Integer row, - Boolean scrolled, String sep_char) +static void ui_comp_msg_set_pos(UI *ui, Integer grid, Integer row, Boolean scrolled, + String sep_char) { msg_grid.comp_row = (int)row; if (scrolled && row > 0) { @@ -617,9 +612,8 @@ static bool curgrid_covered_above(int row) return kv_size(layers)-(above_msg?1:0) > curgrid->comp_index+1; } -static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top, - Integer bot, Integer left, Integer right, - Integer rows, Integer cols) +static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top, Integer bot, Integer left, + Integer right, Integer rows, Integer cols) { if (!ui_comp_should_draw() || !ui_comp_set_grid((int)grid)) { return; @@ -653,8 +647,7 @@ static void ui_comp_grid_scroll(UI *ui, Integer grid, Integer top, } } -static void ui_comp_grid_resize(UI *ui, Integer grid, - Integer width, Integer height) +static void ui_comp_grid_resize(UI *ui, Integer grid, Integer width, Integer height) { if (grid == 1) { ui_composed_call_grid_resize(1, width, height); diff --git a/src/nvim/undo.c b/src/nvim/undo.c index fb96d7e6ff..af214815f8 100644 --- a/src/nvim/undo.c +++ b/src/nvim/undo.c @@ -71,52 +71,51 @@ /* Uncomment the next line for including the u_check() function. This warns * for errors in the debug information. */ -/* #define U_DEBUG 1 */ -#define UH_MAGIC 0x18dade /* value for uh_magic when in use */ -#define UE_MAGIC 0xabc123 /* value for ue_magic when in use */ +// #define U_DEBUG 1 +#define UH_MAGIC 0x18dade // value for uh_magic when in use +#define UE_MAGIC 0xabc123 // value for ue_magic when in use #include <assert.h> +#include <fcntl.h> #include <inttypes.h> #include <limits.h> #include <stdbool.h> #include <string.h> -#include <fcntl.h> #include "auto/config.h" - -#include "nvim/buffer.h" #include "nvim/ascii.h" +#include "nvim/buffer.h" +#include "nvim/buffer_updates.h" #include "nvim/change.h" -#include "nvim/undo.h" #include "nvim/cursor.h" #include "nvim/edit.h" +#include "nvim/extmark.h" #include "nvim/fileio.h" #include "nvim/fold.h" -#include "nvim/buffer_updates.h" -#include "nvim/pos.h" // MAXLNUM +#include "nvim/garray.h" +#include "nvim/lib/kvec.h" #include "nvim/mark.h" -#include "nvim/extmark.h" #include "nvim/memline.h" +#include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/memory.h" -#include "nvim/garray.h" #include "nvim/option.h" +#include "nvim/os/os.h" +#include "nvim/os/time.h" #include "nvim/os_unix.h" #include "nvim/path.h" +#include "nvim/pos.h" // MAXLNUM #include "nvim/sha256.h" #include "nvim/state.h" #include "nvim/strings.h" #include "nvim/types.h" -#include "nvim/os/os.h" -#include "nvim/os/time.h" -#include "nvim/lib/kvec.h" +#include "nvim/undo.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "undo.c.generated.h" #endif -/* used in undo_end() to report number of added and deleted lines */ +// used in undo_end() to report number of added and deleted lines static long u_newcount, u_oldcount; /* @@ -136,13 +135,12 @@ static int seen_b_u_curhead; static int seen_b_u_newhead; static int header_count; -static void u_check_tree(u_header_T *uhp, - u_header_T *exp_uh_next, - u_header_T *exp_uh_alt_prev) { +static void u_check_tree(u_header_T *uhp, u_header_T *exp_uh_next, u_header_T *exp_uh_alt_prev) { u_entry_T *uep; - if (uhp == NULL) + if (uhp == NULL) { return; + } ++header_count; if (uhp == curbuf->b_u_curhead && ++seen_b_u_curhead > 1) { EMSG("b_u_curhead found twice (looping?)"); @@ -153,22 +151,22 @@ static void u_check_tree(u_header_T *uhp, return; } - if (uhp->uh_magic != UH_MAGIC) + if (uhp->uh_magic != UH_MAGIC) { EMSG("uh_magic wrong (may be using freed memory)"); - else { - /* Check pointers back are correct. */ + } else { + // Check pointers back are correct. if (uhp->uh_next.ptr != exp_uh_next) { EMSG("uh_next wrong"); smsg("expected: 0x%x, actual: 0x%x", - exp_uh_next, uhp->uh_next.ptr); + exp_uh_next, uhp->uh_next.ptr); } if (uhp->uh_alt_prev.ptr != exp_uh_alt_prev) { EMSG("uh_alt_prev wrong"); smsg("expected: 0x%x, actual: 0x%x", - exp_uh_alt_prev, uhp->uh_alt_prev.ptr); + exp_uh_alt_prev, uhp->uh_alt_prev.ptr); } - /* Check the undo tree at this header. */ + // Check the undo tree at this header. for (uep = uhp->uh_entry; uep != NULL; uep = uep->ue_next) { if (uep->ue_magic != UE_MAGIC) { EMSG("ue_magic wrong (may be using freed memory)"); @@ -176,10 +174,10 @@ static void u_check_tree(u_header_T *uhp, } } - /* Check the next alt tree. */ + // Check the next alt tree. u_check_tree(uhp->uh_alt_next.ptr, uhp->uh_next.ptr, uhp); - /* Check the next header in this branch. */ + // Check the next header in this branch. u_check_tree(uhp->uh_prev.ptr, uhp, NULL); } } @@ -192,14 +190,16 @@ static void u_check(int newhead_may_be_NULL) { u_check_tree(curbuf->b_u_oldhead, NULL, NULL); if (seen_b_u_newhead == 0 && curbuf->b_u_oldhead != NULL - && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL)) + && !(newhead_may_be_NULL && curbuf->b_u_newhead == NULL)) { EMSGN("b_u_newhead invalid: 0x%x", curbuf->b_u_newhead); - if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0) + } + if (curbuf->b_u_curhead != NULL && seen_b_u_curhead == 0) { EMSGN("b_u_curhead invalid: 0x%x", curbuf->b_u_curhead); + } if (header_count != curbuf->b_u_numhead) { EMSG("b_u_numhead invalid"); smsg("expected: %" PRId64 ", actual: %" PRId64, - (int64_t)header_count, (int64_t)curbuf->b_u_numhead); + (int64_t)header_count, (int64_t)curbuf->b_u_numhead); } } @@ -228,11 +228,12 @@ int u_save_cursor(void) int u_save(linenr_T top, linenr_T bot) { if (top >= bot || bot > (curbuf->b_ml.ml_line_count + 1)) { - return FAIL; /* rely on caller to do error messages */ + return FAIL; // rely on caller to do error messages } - if (top + 2 == bot) + if (top + 2 == bot) { u_saveline((linenr_T)(top + 1)); + } return u_savecommon(curbuf, top, bot, (linenr_T)0, false); } @@ -268,9 +269,8 @@ int u_inssub(linenr_T lnum) */ int u_savedel(linenr_T lnum, long nlines) { - return u_savecommon( - curbuf, lnum - 1, lnum + nlines, - nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, false); + return u_savecommon(curbuf, lnum - 1, lnum + nlines, + nlines == curbuf->b_ml.ml_line_count ? 2 : lnum, false); } /// Return true when undo is allowed. Otherwise print an error message and @@ -327,16 +327,14 @@ static inline void zero_fmark_additional_data(fmark_T *fmarks) * Careful: may trigger autocommands that reload the buffer. * Returns FAIL when lines could not be saved, OK otherwise. */ -int u_savecommon(buf_T *buf, - linenr_T top, linenr_T bot, - linenr_T newbot, int reload) +int u_savecommon(buf_T *buf, linenr_T top, linenr_T bot, linenr_T newbot, int reload) { linenr_T lnum; long i; - u_header_T *uhp; - u_header_T *old_curhead; - u_entry_T *uep; - u_entry_T *prev_uep; + u_header_T *uhp; + u_header_T *old_curhead; + u_entry_T *uep; + u_entry_T *prev_uep; long size; if (!reload) { @@ -381,8 +379,9 @@ int u_savecommon(buf_T *buf, #ifdef U_DEBUG uhp->uh_magic = UH_MAGIC; #endif - } else + } else { uhp = NULL; + } /* * If we undid more than we redid, move the entry lists before and @@ -399,7 +398,7 @@ int u_savecommon(buf_T *buf, */ while (buf->b_u_numhead > get_undolevel(buf) && buf->b_u_oldhead != NULL) { - u_header_T *uhfree = buf->b_u_oldhead; + u_header_T *uhfree = buf->b_u_oldhead; if (uhfree == old_curhead) { // Can't reconnect the branch, delete all of it. @@ -459,11 +458,12 @@ int u_savecommon(buf_T *buf, uhp->uh_walk = 0; uhp->uh_entry = NULL; uhp->uh_getbot_entry = NULL; - uhp->uh_cursor = curwin->w_cursor; /* save cursor pos. for undo */ - if (virtual_active() && curwin->w_cursor.coladd > 0) + uhp->uh_cursor = curwin->w_cursor; // save cursor pos. for undo + if (virtual_active() && curwin->w_cursor.coladd > 0) { uhp->uh_cursor_vcol = getviscol(); - else + } else { uhp->uh_cursor_vcol = -1; + } // save changed and buffer empty flag for undo uhp->uh_flags = (buf->b_changed ? UH_CHANGED : 0) + @@ -499,8 +499,9 @@ int u_savecommon(buf_T *buf, uep = u_get_headentry(buf); prev_uep = NULL; for (i = 0; i < 10; ++i) { - if (uep == NULL) + if (uep == NULL) { break; + } /* If lines have been inserted/deleted we give up. * Also when the line was included in a multi-line save. */ @@ -509,14 +510,14 @@ int u_savecommon(buf_T *buf, != (uep->ue_bot == 0 ? buf->b_ml.ml_line_count + 1 : uep->ue_bot)) - : uep->ue_lcount != buf->b_ml.ml_line_count) + : uep->ue_lcount != buf->b_ml.ml_line_count) || (uep->ue_size > 1 && top >= uep->ue_top && top + 2 <= uep->ue_top + uep->ue_size + 1)) { break; } - /* If it's the same line we can skip saving it again. */ + // If it's the same line we can skip saving it again. if (uep->ue_size == 1 && uep->ue_top == top) { if (i > 0) { /* It's not the last entry: get ue_bot for the last @@ -609,25 +610,25 @@ int u_savecommon(buf_T *buf, // magic at start of undofile -# define UF_START_MAGIC "Vim\237UnDo\345" -# define UF_START_MAGIC_LEN 9 +#define UF_START_MAGIC "Vim\237UnDo\345" +#define UF_START_MAGIC_LEN 9 // magic at start of header -# define UF_HEADER_MAGIC 0x5fd0 +#define UF_HEADER_MAGIC 0x5fd0 // magic after last header -# define UF_HEADER_END_MAGIC 0xe7aa +#define UF_HEADER_END_MAGIC 0xe7aa // magic at start of entry -# define UF_ENTRY_MAGIC 0xf518 +#define UF_ENTRY_MAGIC 0xf518 // magic after last entry -# define UF_ENTRY_END_MAGIC 0x3581 +#define UF_ENTRY_END_MAGIC 0x3581 // 2-byte undofile version number -# define UF_VERSION 3 +#define UF_VERSION 3 -/* extra fields for header */ -# define UF_LAST_SAVE_NR 1 +// extra fields for header +#define UF_LAST_SAVE_NR 1 -/* extra fields for uhp */ -# define UHP_SAVE_NR 1 +// extra fields for uhp +#define UHP_SAVE_NR 1 static char_u e_not_open[] = N_("E828: Cannot open undo file for writing: %s"); @@ -640,7 +641,7 @@ void u_compute_hash(buf_T *buf, char_u *hash) { context_sha256_T ctx; linenr_T lnum; - char_u *p; + char_u *p; sha256_start(&ctx); for (lnum = 1; lnum <= buf->b_ml.ml_line_count; lnum++) { @@ -688,7 +689,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) // Loop over 'undodir'. When reading find the first file that exists. // When not reading use the first directory that exists or ".". - dirp = (char *) p_udir; + dirp = (char *)p_udir; while (*dirp != NUL) { size_t dir_len = copy_option_part((char_u **)&dirp, (char_u *)dir_name, MAXPATHL, ","); @@ -698,7 +699,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) const size_t ffname_len = strlen(ffname); undo_file_name = xmalloc(ffname_len + 6); memmove(undo_file_name, ffname, ffname_len + 1); - char *const tail = (char *) path_tail((char_u *) undo_file_name); + char *const tail = (char *)path_tail((char_u *)undo_file_name); const size_t tail_len = strlen(tail); memmove(tail + 1, tail, tail_len + 1); *tail = '.'; @@ -754,8 +755,7 @@ char *u_get_undo_file_name(const char *const buf_ffname, const bool reading) /// /// @param[in] mesg Identifier of the corruption kind. /// @param[in] file_name File in which error occurred. -static void corruption_error(const char *const mesg, - const char *const file_name) +static void corruption_error(const char *const mesg, const char *const file_name) FUNC_ATTR_NONNULL_ALL { EMSG3(_("E825: Corrupted undo file (%s): %s"), mesg, file_name); @@ -763,8 +763,8 @@ static void corruption_error(const char *const mesg, static void u_free_uhp(u_header_T *uhp) { - u_entry_T *nuep; - u_entry_T *uep; + u_entry_T *nuep; + u_entry_T *uep; uep = uhp->uh_entry; while (uep != NULL) { @@ -890,8 +890,7 @@ static bool serialize_uhp(bufinfo_T *bi, u_header_T *uhp) return true; } -static u_header_T *unserialize_uhp(bufinfo_T *bi, - const char *file_name) +static u_header_T *unserialize_uhp(bufinfo_T *bi, const char *file_name) { u_header_T *uhp = xmalloc(sizeof(u_header_T)); memset(uhp, 0, sizeof(u_header_T)); @@ -999,7 +998,7 @@ static bool serialize_extmark(bufinfo_T *bi, ExtmarkUndoObject extup) undo_write_bytes(bi, (uintmax_t)extup.type, 4); if (!undo_write(bi, (uint8_t *)&(extup.data.splice), sizeof(ExtmarkSplice))) { - return false; + return false; } } else if (extup.type == kExtmarkMove) { undo_write_bytes(bi, (uintmax_t)UF_ENTRY_MAGIC, 2); @@ -1013,8 +1012,7 @@ static bool serialize_extmark(bufinfo_T *bi, ExtmarkUndoObject extup) return true; } -static ExtmarkUndoObject *unserialize_extmark(bufinfo_T *bi, bool *error, - const char *filename) +static ExtmarkUndoObject *unserialize_extmark(bufinfo_T *bi, bool *error, const char *filename) { UndoObjectType type; uint8_t *buf = NULL; @@ -1039,7 +1037,7 @@ static ExtmarkUndoObject *unserialize_extmark(bufinfo_T *bi, bool *error, } extup->data.move = *(ExtmarkMove *)buf; } else { - goto error; + goto error; } xfree(buf); @@ -1080,8 +1078,7 @@ static bool serialize_uep(bufinfo_T *bi, u_entry_T *uep) return true; } -static u_entry_T *unserialize_uep(bufinfo_T * bi, bool *error, - const char *file_name) +static u_entry_T *unserialize_uep(bufinfo_T * bi, bool *error, const char *file_name) { u_entry_T *uep = xmalloc(sizeof(u_entry_T)); memset(uep, 0, sizeof(u_entry_T)); @@ -1171,24 +1168,23 @@ static void unserialize_visualinfo(bufinfo_T *bi, visualinfo_T *info) /// @param[in] buf Buffer for which undo file is written. /// @param[in] hash Hash value of the buffer text. Must have #UNDO_HASH_SIZE /// size. -void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, - char_u *const hash) +void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, char_u *const hash) FUNC_ATTR_NONNULL_ARG(3, 4) { - u_header_T *uhp; + u_header_T *uhp; char *file_name; int mark; #ifdef U_DEBUG int headers_written = 0; #endif int fd; - FILE *fp = NULL; + FILE *fp = NULL; int perm; bool write_ok = false; bufinfo_T bi; if (name == NULL) { - file_name = u_get_undo_file_name((char *) buf->b_ffname, false); + file_name = u_get_undo_file_name((char *)buf->b_ffname, false); if (file_name == NULL) { if (p_verbose > 0) { verbose_enter(); @@ -1198,7 +1194,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, return; } } else { - file_name = (char *) name; + file_name = (char *)name; } /* @@ -1221,16 +1217,18 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, * file, and delete it. */ if (os_path_exists((char_u *)file_name)) { if (name == NULL || !forceit) { - /* Check we can read it and it's an undo file. */ + // Check we can read it and it's an undo file. fd = os_open(file_name, O_RDONLY, 0); if (fd < 0) { if (name != NULL || p_verbose > 0) { - if (name == NULL) + if (name == NULL) { verbose_enter(); + } smsg(_("Will not overwrite with undo file, cannot read: %s"), file_name); - if (name == NULL) + if (name == NULL) { verbose_leave(); + } } goto theend; } else { @@ -1240,12 +1238,14 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, if (len < UF_START_MAGIC_LEN || memcmp(mbuf, UF_START_MAGIC, UF_START_MAGIC_LEN) != 0) { if (name != NULL || p_verbose > 0) { - if (name == NULL) + if (name == NULL) { verbose_enter(); + } smsg(_("Will not overwrite, this is not an undo file: %s"), file_name); - if (name == NULL) + if (name == NULL) { verbose_leave(); + } } goto theend; } @@ -1276,7 +1276,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, } #ifdef U_DEBUG - /* Check there is no problem in undo info before writing. */ + // Check there is no problem in undo info before writing. u_check(FALSE); #endif @@ -1323,7 +1323,7 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, mark = ++lastmark; uhp = buf->b_u_oldhead; while (uhp != NULL) { - /* Serialize current UHP if we haven't seen it */ + // Serialize current UHP if we haven't seen it if (uhp->uh_walk != mark) { uhp->uh_walk = mark; #ifdef U_DEBUG @@ -1334,19 +1334,20 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, } } - /* Now walk through the tree - algorithm from undo_time(). */ - if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark) + // Now walk through the tree - algorithm from undo_time(). + if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != mark) { uhp = uhp->uh_prev.ptr; - else if (uhp->uh_alt_next.ptr != NULL - && uhp->uh_alt_next.ptr->uh_walk != mark) + } else if (uhp->uh_alt_next.ptr != NULL + && uhp->uh_alt_next.ptr->uh_walk != mark) { uhp = uhp->uh_alt_next.ptr; - else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL - && uhp->uh_next.ptr->uh_walk != mark) + } else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL + && uhp->uh_next.ptr->uh_walk != mark) { uhp = uhp->uh_next.ptr; - else if (uhp->uh_alt_prev.ptr != NULL) + } else if (uhp->uh_alt_prev.ptr != NULL) { uhp = uhp->uh_alt_prev.ptr; - else + } else { uhp = uhp->uh_next.ptr; + } } if (undo_write_bytes(&bi, (uintmax_t)UF_HEADER_END_MAGIC, 2)) { @@ -1361,14 +1362,15 @@ void u_write_undo(const char *const name, const bool forceit, buf_T *const buf, write_error: fclose(fp); - if (!write_ok) + if (!write_ok) { EMSG2(_("E829: write error in undo file: %s"), file_name); + } #ifdef HAVE_ACL if (buf->b_ffname != NULL) { vim_acl_T acl; - /* For systems that support ACL: get the ACL from the original file. */ + // For systems that support ACL: get the ACL from the original file. acl = mch_get_acl(buf->b_ffname); mch_set_acl((char_u *)file_name, acl); mch_free_acl(acl); @@ -1376,8 +1378,9 @@ write_error: #endif theend: - if (file_name != name) + if (file_name != name) { xfree(file_name); + } } /// Loads the undo tree from an undo file. @@ -1385,8 +1388,7 @@ theend: /// a bit more verbose. /// Otherwise use curbuf->b_ffname to generate the undo file name. /// "hash[UNDO_HASH_SIZE]" must be the hash value of the buffer text. -void u_read_undo(char *name, const char_u *hash, - const char_u *orig_name FUNC_ATTR_UNUSED) +void u_read_undo(char *name, const char_u *hash, const char_u *orig_name FUNC_ATTR_UNUSED) FUNC_ATTR_NONNULL_ARG(2) { u_header_T **uhp_table = NULL; @@ -1394,7 +1396,7 @@ void u_read_undo(char *name, const char_u *hash, char *file_name; if (name == NULL) { - file_name = u_get_undo_file_name((char *) curbuf->b_ffname, true); + file_name = u_get_undo_file_name((char *)curbuf->b_ffname, true); if (file_name == NULL) { return; } @@ -1418,7 +1420,7 @@ void u_read_undo(char *name, const char_u *hash, } #endif } else { - file_name = (char *) name; + file_name = (char *)name; } if (p_verbose > 0) { @@ -1465,7 +1467,7 @@ void u_read_undo(char *name, const char_u *hash, verbose_enter(); } give_warning((char_u *) - _("File contents changed, cannot use undo info"), true); + _("File contents changed, cannot use undo info"), true); if (name == NULL) { verbose_leave(); } @@ -1508,15 +1510,15 @@ void u_read_undo(char *name, const char_u *hash, } int what = undo_read_byte(&bi); switch (what) { - case UF_LAST_SAVE_NR: - last_save_nr = undo_read_4c(&bi); - break; + case UF_LAST_SAVE_NR: + last_save_nr = undo_read_4c(&bi); + break; - default: - // field not supported, skip - while (--len >= 0) { - (void)undo_read_byte(&bi); - } + default: + // field not supported, skip + while (--len >= 0) { + (void)undo_read_byte(&bi); + } } } @@ -1559,7 +1561,7 @@ void u_read_undo(char *name, const char_u *hash, size_t amount = num_head * sizeof(int) + 1; int *uhp_table_used = xmalloc(amount); memset(uhp_table_used, 0, amount); -# define SET_FLAG(j) ++ uhp_table_used[j] +# define SET_FLAG(j) ++uhp_table_used[j] #else # define SET_FLAG(j) #endif @@ -1666,10 +1668,11 @@ void u_read_undo(char *name, const char_u *hash, error: xfree(line_ptr); if (uhp_table != NULL) { - for (long i = 0; i < num_read_uhps; i++) + for (long i = 0; i < num_read_uhps; i++) { if (uhp_table[i] != NULL) { u_free_uhp(uhp_table[i]); } + } xfree(uhp_table); } @@ -1844,7 +1847,7 @@ bool u_undo_and_forget(int count) to_forget->uh_alt_next.ptr = NULL; curbuf->b_u_curhead->uh_alt_prev.ptr = to_forget->uh_alt_prev.ptr; curbuf->b_u_seq_cur = curbuf->b_u_curhead->uh_next.ptr ? - curbuf->b_u_curhead->uh_next.ptr->uh_seq : 0; + curbuf->b_u_curhead->uh_next.ptr->uh_seq : 0; } else if (curbuf->b_u_newhead) { curbuf->b_u_seq_cur = curbuf->b_u_newhead->uh_seq; } @@ -1876,8 +1879,9 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event) u_newcount = 0; u_oldcount = 0; - if (curbuf->b_ml.ml_flags & ML_EMPTY) + if (curbuf->b_ml.ml_flags & ML_EMPTY) { u_oldcount = -1; + } while (count--) { /* Do the change warning now, so that it triggers FileChangedRO when * needed. This may cause the file to be reloaded, that must happen @@ -1894,7 +1898,7 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event) } // nothing to undo if (curbuf->b_u_numhead == 0 || curbuf->b_u_curhead == NULL) { - /* stick curbuf->b_u_curhead at end */ + // stick curbuf->b_u_curhead at end curbuf->b_u_curhead = curbuf->b_u_oldhead; beep_flush(); if (count == startcount - 1) { @@ -1919,8 +1923,9 @@ static void u_doit(int startcount, bool quiet, bool do_buf_event) /* Advance for next redo. Set "newhead" when at the end of the * redoable changes. */ - if (curbuf->b_u_curhead->uh_prev.ptr == NULL) + if (curbuf->b_u_curhead->uh_prev.ptr == NULL) { curbuf->b_u_newhead = curbuf->b_u_curhead; + } curbuf->b_u_curhead = curbuf->b_u_curhead->uh_prev.ptr; } } @@ -1941,8 +1946,8 @@ void undo_time(long step, bool sec, bool file, bool absolute) long closest_start; long closest_seq = 0; long val; - u_header_T *uhp = NULL; - u_header_T *last; + u_header_T *uhp = NULL; + u_header_T *last; int mark; int nomark = 0; // shut up compiler int round; @@ -1958,8 +1963,9 @@ void undo_time(long step, bool sec, bool file, bool absolute) u_newcount = 0; u_oldcount = 0; - if (curbuf->b_ml.ml_flags & ML_EMPTY) + if (curbuf->b_ml.ml_flags & ML_EMPTY) { u_oldcount = -1; + } /* "target" is the node below which we want to be. * Init "closest" to a value we can't reach. */ @@ -1975,23 +1981,26 @@ void undo_time(long step, bool sec, bool file, bool absolute) * the last write, count that as moving one file-write, so * that ":earlier 1f" undoes all changes since the last save. */ uhp = curbuf->b_u_curhead; - if (uhp != NULL) + if (uhp != NULL) { uhp = uhp->uh_next.ptr; - else + } else { uhp = curbuf->b_u_newhead; - if (uhp != NULL && uhp->uh_save_nr != 0) + } + if (uhp != NULL && uhp->uh_save_nr != 0) { /* "uh_save_nr" was set in the last block, that means * there were no changes since the last write */ target = curbuf->b_u_save_nr_cur + step; - else - /* count the changes since the last write as one step */ + } else { + // count the changes since the last write as one step target = curbuf->b_u_save_nr_cur + step + 1; - if (target <= 0) + } + if (target <= 0) { /* Go to before first write: before the oldest change. Use * the sequence number for that. */ dofile = false; + } } else { - /* Moving forward to a newer write. */ + // Moving forward to a newer write. target = curbuf->b_u_save_nr_cur + step; if (target > curbuf->b_u_save_nr_last) { /* Go to after last write: after the latest change. Use @@ -2000,11 +2009,13 @@ void undo_time(long step, bool sec, bool file, bool absolute) dofile = false; } } - } else + } else { target = curbuf->b_u_seq_cur + step; + } if (step < 0) { - if (target < 0) + if (target < 0) { target = 0; + } closest = -1; } else { if (dosec) { @@ -2044,10 +2055,11 @@ void undo_time(long step, bool sec, bool file, bool absolute) mark = ++lastmark; nomark = ++lastmark; - if (curbuf->b_u_curhead == NULL) /* at leaf of the tree */ + if (curbuf->b_u_curhead == NULL) { // at leaf of the tree uhp = curbuf->b_u_newhead; - else + } else { uhp = curbuf->b_u_curhead; + } while (uhp != NULL) { uhp->uh_walk = mark; @@ -2065,17 +2077,17 @@ void undo_time(long step, bool sec, bool file, bool absolute) * "b_u_seq_cur"). When the timestamp is equal find the * highest/lowest sequence number. */ if ((step < 0 ? uhp->uh_seq <= curbuf->b_u_seq_cur - : uhp->uh_seq > curbuf->b_u_seq_cur) + : uhp->uh_seq > curbuf->b_u_seq_cur) && ((dosec && val == closest) ? (step < 0 ? uhp->uh_seq < closest_seq - : uhp->uh_seq > closest_seq) - : closest == closest_start + : uhp->uh_seq > closest_seq) + : closest == closest_start || (val > target ? (closest > target ? val - target <= closest - target : val - target <= target - closest) - : (closest > target + : (closest > target ? target - val <= closest - target : target - val <= target - closest)))) { closest = val; @@ -2090,38 +2102,41 @@ void undo_time(long step, bool sec, bool file, bool absolute) break; } - /* go down in the tree if we haven't been there */ + // go down in the tree if we haven't been there if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark - && uhp->uh_prev.ptr->uh_walk != mark) + && uhp->uh_prev.ptr->uh_walk != mark) { uhp = uhp->uh_prev.ptr; - - /* go to alternate branch if we haven't been there */ + } + // go to alternate branch if we haven't been there else if (uhp->uh_alt_next.ptr != NULL && uhp->uh_alt_next.ptr->uh_walk != nomark - && uhp->uh_alt_next.ptr->uh_walk != mark) + && uhp->uh_alt_next.ptr->uh_walk != mark) { uhp = uhp->uh_alt_next.ptr; - + } /* go up in the tree if we haven't been there and we are at the * start of alternate branches */ else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL && uhp->uh_next.ptr->uh_walk != nomark && uhp->uh_next.ptr->uh_walk != mark) { - /* If still at the start we don't go through this change. */ - if (uhp == curbuf->b_u_curhead) + // If still at the start we don't go through this change. + if (uhp == curbuf->b_u_curhead) { uhp->uh_walk = nomark; + } uhp = uhp->uh_next.ptr; } else { - /* need to backtrack; mark this node as useless */ + // need to backtrack; mark this node as useless uhp->uh_walk = nomark; - if (uhp->uh_alt_prev.ptr != NULL) + if (uhp->uh_alt_prev.ptr != NULL) { uhp = uhp->uh_alt_prev.ptr; - else + } else { uhp = uhp->uh_next.ptr; + } } } - if (uhp != NULL) /* found it */ + if (uhp != NULL) { // found it break; + } if (absolute) { EMSGN(_("E830: Undo number %" PRId64 " not found"), step); @@ -2129,10 +2144,11 @@ void undo_time(long step, bool sec, bool file, bool absolute) } if (closest == closest_start) { - if (step < 0) + if (step < 0) { MSG(_("Already at oldest change")); - else + } else { MSG(_("Already at newest change")); + } return; } @@ -2153,10 +2169,11 @@ target_zero: change_warning(curbuf, 0); uhp = curbuf->b_u_curhead; - if (uhp == NULL) + if (uhp == NULL) { uhp = curbuf->b_u_newhead; - else + } else { uhp = uhp->uh_next.ptr; + } if (uhp == NULL || (target > 0 && uhp->uh_walk != mark) || (uhp->uh_seq == target && !above)) { @@ -2264,21 +2281,21 @@ target_zero: /// @param do_buf_event If `true`, send buffer updates. static void u_undoredo(int undo, bool do_buf_event) { - char_u **newarray = NULL; + char_u **newarray = NULL; linenr_T oldsize; linenr_T newsize; linenr_T top, bot; linenr_T lnum; linenr_T newlnum = MAXLNUM; long i; - u_entry_T *uep, *nuep; - u_entry_T *newlist = NULL; + u_entry_T *uep, *nuep; + u_entry_T *newlist = NULL; int old_flags; int new_flags; fmark_T namedm[NMARKS]; visualinfo_T visualinfo; bool empty_buffer; // buffer became empty - u_header_T *curhead = curbuf->b_u_curhead; + u_header_T *curhead = curbuf->b_u_curhead; /* Don't want autocommands using the undo structures here, they are * invalid till the end. */ @@ -2307,8 +2324,9 @@ static void u_undoredo(int undo, bool do_buf_event) for (uep = curhead->uh_entry; uep != NULL; uep = nuep) { top = uep->ue_top; bot = uep->ue_bot; - if (bot == 0) + if (bot == 0) { bot = curbuf->b_ml.ml_line_count + 1; + } if (top > curbuf->b_ml.ml_line_count || top >= bot || bot > curbuf->b_ml.ml_line_count + 1) { unblock_autocmds(); @@ -2317,8 +2335,8 @@ static void u_undoredo(int undo, bool do_buf_event) return; } - oldsize = bot - top - 1; /* number of lines before undo */ - newsize = uep->ue_size; /* number of lines after undo */ + oldsize = bot - top - 1; // number of lines before undo + newsize = uep->ue_size; // number of lines after undo if (top < newlnum) { /* If the saved cursor is somewhere in this undo block, move it to @@ -2332,13 +2350,15 @@ static void u_undoredo(int undo, bool do_buf_event) /* Use the first line that actually changed. Avoids that * undoing auto-formatting puts the cursor in the previous * line. */ - for (i = 0; i < newsize && i < oldsize; ++i) - if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0) + for (i = 0; i < newsize && i < oldsize; ++i) { + if (STRCMP(uep->ue_array[i], ml_get(top + 1 + i)) != 0) { break; + } + } if (i == newsize && newlnum == MAXLNUM && uep->ue_next == NULL) { newlnum = top; curwin->w_cursor.lnum = newlnum + 1; - } else if (i < newsize) { + } else if (i < newsize) { newlnum = top + i; curwin->w_cursor.lnum = newlnum + 1; } @@ -2347,12 +2367,12 @@ static void u_undoredo(int undo, bool do_buf_event) empty_buffer = false; - /* delete the lines between top and bot and save them in newarray */ + // delete the lines between top and bot and save them in newarray if (oldsize > 0) { newarray = xmalloc(sizeof(char_u *) * (size_t)oldsize); - /* delete backwards, it goes faster in most cases */ + // delete backwards, it goes faster in most cases for (lnum = bot - 1, i = oldsize; --i >= 0; --lnum) { - /* what can we do when we run out of memory? */ + // what can we do when we run out of memory? newarray[i] = u_save_line(lnum); /* remember we deleted the last line in the buffer, and a * dummy empty line will be inserted */ @@ -2361,10 +2381,11 @@ static void u_undoredo(int undo, bool do_buf_event) } ml_delete(lnum, false); } - } else + } else { newarray = NULL; + } - /* insert the lines in u_array between top and bot */ + // insert the lines in u_array between top and bot if (newsize) { for (lnum = top, i = 0; i < newsize; ++i, ++lnum) { /* @@ -2395,13 +2416,15 @@ static void u_undoredo(int undo, bool do_buf_event) changed_lines(top + 1, 0, bot, newsize - oldsize, do_buf_event); - /* set '[ and '] mark */ - if (top + 1 < curbuf->b_op_start.lnum) + // set '[ and '] mark + if (top + 1 < curbuf->b_op_start.lnum) { curbuf->b_op_start.lnum = top + 1; - if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum) + } + if (newsize == 0 && top + 1 > curbuf->b_op_end.lnum) { curbuf->b_op_end.lnum = top + 1; - else if (top + newsize > curbuf->b_op_end.lnum) + } else if (top + newsize > curbuf->b_op_end.lnum) { curbuf->b_op_end.lnum = top + newsize; + } u_newcount += newsize; u_oldcount += oldsize; @@ -2424,7 +2447,7 @@ static void u_undoredo(int undo, bool do_buf_event) undo_info = kv_A(curhead->uh_extmark, i); extmark_apply_undo(undo_info, undo); } - // redo + // redo } else { for (i = 0; i < (int)kv_size(curhead->uh_extmark); i++) { undo_info = kv_A(curhead->uh_extmark, i); @@ -2482,17 +2505,20 @@ static void u_undoredo(int undo, bool do_buf_event) * Otherwise the cursor should go to the first undone line. */ if (curhead->uh_cursor.lnum + 1 == curwin->w_cursor.lnum - && curwin->w_cursor.lnum > 1) + && curwin->w_cursor.lnum > 1) { --curwin->w_cursor.lnum; + } if (curwin->w_cursor.lnum <= curbuf->b_ml.ml_line_count) { if (curhead->uh_cursor.lnum == curwin->w_cursor.lnum) { curwin->w_cursor.col = curhead->uh_cursor.col; - if (virtual_active() && curhead->uh_cursor_vcol >= 0) + if (virtual_active() && curhead->uh_cursor_vcol >= 0) { coladvance((colnr_T)curhead->uh_cursor_vcol); - else + } else { curwin->w_cursor.coladd = 0; - } else + } + } else { beginline(BL_SOL | BL_FIX); + } } else { /* We get here with the current cursor line being past the end (eg * after adding lines at the end of the file, and then undoing it). @@ -2502,23 +2528,25 @@ static void u_undoredo(int undo, bool do_buf_event) curwin->w_cursor.coladd = 0; } - /* Make sure the cursor is on an existing line and column. */ + // Make sure the cursor is on an existing line and column. check_cursor(); - /* Remember where we are for "g-" and ":earlier 10s". */ + // Remember where we are for "g-" and ":earlier 10s". curbuf->b_u_seq_cur = curhead->uh_seq; - if (undo) + if (undo) { /* We are below the previous undo. However, to make ":earlier 1s" * work we compute this as being just above the just undone change. */ curbuf->b_u_seq_cur = curhead->uh_next.ptr ? - curhead->uh_next.ptr->uh_seq : 0; + curhead->uh_next.ptr->uh_seq : 0; + } - /* Remember where we are for ":earlier 1f" and ":later 1f". */ + // Remember where we are for ":earlier 1f" and ":later 1f". if (curhead->uh_save_nr != 0) { - if (undo) + if (undo) { curbuf->b_u_save_nr_cur = curhead->uh_save_nr - 1; - else + } else { curbuf->b_u_save_nr_cur = curhead->uh_save_nr; + } } /* The timestamp can be the same for multiple changes, just use the one of @@ -2534,17 +2562,18 @@ static void u_undoredo(int undo, bool do_buf_event) /// If we deleted or added lines, report the number of less/more lines. /// Otherwise, report the number of changes (this may be incorrect /// in some cases, but it's better than nothing). -static void u_undo_end( - bool did_undo, ///< just did an undo - bool absolute, ///< used ":undo N" - bool quiet) +/// +/// @param did_undo just did an undo +/// @param absolute used ":undo N" +static void u_undo_end(bool did_undo, bool absolute, bool quiet) { - char *msgstr; - u_header_T *uhp; + char *msgstr; + u_header_T *uhp; char_u msgbuf[80]; - if ((fdo_flags & FDO_UNDO) && KeyTyped) + if ((fdo_flags & FDO_UNDO) && KeyTyped) { foldOpenCursor(); + } if (quiet || global_busy // no messages until global is finished @@ -2552,28 +2581,30 @@ static void u_undo_end( return; } - if (curbuf->b_ml.ml_flags & ML_EMPTY) + if (curbuf->b_ml.ml_flags & ML_EMPTY) { --u_newcount; + } u_oldcount -= u_newcount; - if (u_oldcount == -1) + if (u_oldcount == -1) { msgstr = N_("more line"); - else if (u_oldcount < 0) + } else if (u_oldcount < 0) { msgstr = N_("more lines"); - else if (u_oldcount == 1) + } else if (u_oldcount == 1) { msgstr = N_("line less"); - else if (u_oldcount > 1) + } else if (u_oldcount > 1) { msgstr = N_("fewer lines"); - else { + } else { u_oldcount = u_newcount; - if (u_newcount == 1) + if (u_newcount == 1) { msgstr = N_("change"); - else + } else { msgstr = N_("changes"); + } } if (curbuf->b_u_curhead != NULL) { - /* For ":undo N" we prefer a "after #N" message. */ + // For ":undo N" we prefer a "after #N" message. if (absolute && curbuf->b_u_curhead->uh_next.ptr != NULL) { uhp = curbuf->b_u_curhead->uh_next.ptr; did_undo = false; @@ -2600,14 +2631,13 @@ static void u_undo_end( } } - smsg_attr_keep( - 0, - _("%" PRId64 " %s; %s #%" PRId64 " %s"), - u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount, - _(msgstr), - did_undo ? _("before") : _("after"), - uhp == NULL ? (int64_t)0L : (int64_t)uhp->uh_seq, - msgbuf); + smsg_attr_keep(0, + _("%" PRId64 " %s; %s #%" PRId64 " %s"), + u_oldcount < 0 ? (int64_t)-u_oldcount : (int64_t)u_oldcount, + _(msgstr), + did_undo ? _("before") : _("after"), + uhp == NULL ? (int64_t)0L : (int64_t)uhp->uh_seq, + msgbuf); } /// u_sync: stop adding to the current entry list @@ -2634,7 +2664,7 @@ void u_sync(bool force) void ex_undolist(exarg_T *eap) { garray_T ga; - u_header_T *uhp; + u_header_T *uhp; int mark; int nomark; int changes = 1; @@ -2657,28 +2687,29 @@ void ex_undolist(exarg_T *eap) add_time(IObuff + STRLEN(IObuff), IOSIZE - STRLEN(IObuff), uhp->uh_time); if (uhp->uh_save_nr > 0) { - while (STRLEN(IObuff) < 33) + while (STRLEN(IObuff) < 33) { STRCAT(IObuff, " "); + } vim_snprintf_add((char *)IObuff, IOSIZE, - " %3ld", uhp->uh_save_nr); + " %3ld", uhp->uh_save_nr); } GA_APPEND(char_u *, &ga, vim_strsave(IObuff)); } uhp->uh_walk = mark; - /* go down in the tree if we haven't been there */ + // go down in the tree if we haven't been there if (uhp->uh_prev.ptr != NULL && uhp->uh_prev.ptr->uh_walk != nomark && uhp->uh_prev.ptr->uh_walk != mark) { uhp = uhp->uh_prev.ptr; ++changes; } - /* go to alternate branch if we haven't been there */ + // go to alternate branch if we haven't been there else if (uhp->uh_alt_next.ptr != NULL && uhp->uh_alt_next.ptr->uh_walk != nomark - && uhp->uh_alt_next.ptr->uh_walk != mark) + && uhp->uh_alt_next.ptr->uh_walk != mark) { uhp = uhp->uh_alt_next.ptr; - + } /* go up in the tree if we haven't been there and we are at the * start of alternate branches */ else if (uhp->uh_next.ptr != NULL && uhp->uh_alt_prev.ptr == NULL @@ -2687,20 +2718,20 @@ void ex_undolist(exarg_T *eap) uhp = uhp->uh_next.ptr; --changes; } else { - /* need to backtrack; mark this node as done */ + // need to backtrack; mark this node as done uhp->uh_walk = nomark; - if (uhp->uh_alt_prev.ptr != NULL) + if (uhp->uh_alt_prev.ptr != NULL) { uhp = uhp->uh_alt_prev.ptr; - else { + } else { uhp = uhp->uh_next.ptr; --changes; } } } - if (GA_EMPTY(&ga)) + if (GA_EMPTY(&ga)) { MSG(_("Nothing to undo")); - else { + } else { sort_strings((char_u **)ga.ga_data, ga.ga_len); msg_start(); @@ -2757,17 +2788,18 @@ void u_unchanged(buf_T *buf) */ void u_find_first_changed(void) { - u_header_T *uhp = curbuf->b_u_newhead; - u_entry_T *uep; + u_header_T *uhp = curbuf->b_u_newhead; + u_entry_T *uep; linenr_T lnum; - if (curbuf->b_u_curhead != NULL || uhp == NULL) - return; /* undid something in an autocmd? */ - - /* Check that the last undo block was for the whole file. */ + if (curbuf->b_u_curhead != NULL || uhp == NULL) { + return; // undid something in an autocmd? + } + // Check that the last undo block was for the whole file. uep = uhp->uh_entry; - if (uep->ue_top != 0 || uep->ue_bot != 0) + if (uep->ue_top != 0 || uep->ue_bot != 0) { return; + } for (lnum = 1; lnum < curbuf->b_ml.ml_line_count && lnum <= uep->ue_size; lnum++) { @@ -2778,7 +2810,7 @@ void u_find_first_changed(void) } } if (curbuf->b_ml.ml_line_count != uep->ue_size) { - /* lines added or deleted at the end, put the cursor there */ + // lines added or deleted at the end, put the cursor there clearpos(&(uhp->uh_cursor)); uhp->uh_cursor.lnum = lnum; } @@ -2790,27 +2822,30 @@ void u_find_first_changed(void) */ void u_update_save_nr(buf_T *buf) { - u_header_T *uhp; + u_header_T *uhp; ++buf->b_u_save_nr_last; buf->b_u_save_nr_cur = buf->b_u_save_nr_last; uhp = buf->b_u_curhead; - if (uhp != NULL) + if (uhp != NULL) { uhp = uhp->uh_next.ptr; - else + } else { uhp = buf->b_u_newhead; - if (uhp != NULL) + } + if (uhp != NULL) { uhp->uh_save_nr = buf->b_u_save_nr_last; + } } static void u_unch_branch(u_header_T *uhp) { - u_header_T *uh; + u_header_T *uh; for (uh = uhp; uh != NULL; uh = uh->uh_prev.ptr) { uh->uh_flags |= UH_CHANGED; - if (uh->uh_alt_next.ptr != NULL) - u_unch_branch(uh->uh_alt_next.ptr); /* recursive */ + if (uh->uh_alt_next.ptr != NULL) { + u_unch_branch(uh->uh_alt_next.ptr); // recursive + } } } @@ -2829,11 +2864,11 @@ static u_entry_T *u_get_headentry(buf_T *buf) /* * u_getbot(): compute the line number of the previous u_save - * It is called only when b_u_synced is false. + * It is called only when b_u_synced is false. */ static void u_getbot(buf_T *buf) { - u_entry_T *uep; + u_entry_T *uep; linenr_T extra; uep = u_get_headentry(buf); // check for corrupt undo list @@ -2864,95 +2899,91 @@ static void u_getbot(buf_T *buf) buf->b_u_synced = true; } -/* - * Free one header "uhp" and its entry list and adjust the pointers. - */ -static void -u_freeheader( - buf_T *buf, - u_header_T *uhp, - u_header_T **uhpp // if not NULL reset when freeing this header -) +/// Free one header "uhp" and its entry list and adjust the pointers. +/// +/// @param uhpp if not NULL reset when freeing this header +static void u_freeheader(buf_T *buf, u_header_T *uhp, u_header_T **uhpp) { - u_header_T *uhap; + u_header_T *uhap; /* When there is an alternate redo list free that branch completely, * because we can never go there. */ - if (uhp->uh_alt_next.ptr != NULL) + if (uhp->uh_alt_next.ptr != NULL) { u_freebranch(buf, uhp->uh_alt_next.ptr, uhpp); + } - if (uhp->uh_alt_prev.ptr != NULL) + if (uhp->uh_alt_prev.ptr != NULL) { uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL; + } - /* Update the links in the list to remove the header. */ - if (uhp->uh_next.ptr == NULL) + // Update the links in the list to remove the header. + if (uhp->uh_next.ptr == NULL) { buf->b_u_oldhead = uhp->uh_prev.ptr; - else + } else { uhp->uh_next.ptr->uh_prev.ptr = uhp->uh_prev.ptr; + } - if (uhp->uh_prev.ptr == NULL) + if (uhp->uh_prev.ptr == NULL) { buf->b_u_newhead = uhp->uh_next.ptr; - else + } else { for (uhap = uhp->uh_prev.ptr; uhap != NULL; - uhap = uhap->uh_alt_next.ptr) + uhap = uhap->uh_alt_next.ptr) { uhap->uh_next.ptr = uhp->uh_next.ptr; + } + } u_freeentries(buf, uhp, uhpp); } -/* - * Free an alternate branch and any following alternate branches. - */ -static void -u_freebranch( - buf_T *buf, - u_header_T *uhp, - u_header_T **uhpp // if not NULL reset when freeing this header -) +/// Free an alternate branch and any following alternate branches. +/// +/// @param uhpp if not NULL reset when freeing this header +static void u_freebranch(buf_T *buf, u_header_T *uhp, u_header_T **uhpp) { - u_header_T *tofree, *next; + u_header_T *tofree, *next; /* If this is the top branch we may need to use u_freeheader() to update * all the pointers. */ if (uhp == buf->b_u_oldhead) { - while (buf->b_u_oldhead != NULL) + while (buf->b_u_oldhead != NULL) { u_freeheader(buf, buf->b_u_oldhead, uhpp); + } return; } - if (uhp->uh_alt_prev.ptr != NULL) + if (uhp->uh_alt_prev.ptr != NULL) { uhp->uh_alt_prev.ptr->uh_alt_next.ptr = NULL; + } next = uhp; while (next != NULL) { tofree = next; - if (tofree->uh_alt_next.ptr != NULL) - u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); /* recursive */ + if (tofree->uh_alt_next.ptr != NULL) { + u_freebranch(buf, tofree->uh_alt_next.ptr, uhpp); // recursive + } next = tofree->uh_prev.ptr; u_freeentries(buf, tofree, uhpp); } } -/* - * Free all the undo entries for one header and the header itself. - * This means that "uhp" is invalid when returning. - */ -static void -u_freeentries( - buf_T *buf, - u_header_T *uhp, - u_header_T **uhpp // if not NULL reset when freeing this header -) +/// Free all the undo entries for one header and the header itself. +/// This means that "uhp" is invalid when returning. +/// +/// @param uhpp if not NULL reset when freeing this header +static void u_freeentries(buf_T *buf, u_header_T *uhp, u_header_T **uhpp) { - u_entry_T *uep, *nuep; + u_entry_T *uep, *nuep; - /* Check for pointers to the header that become invalid now. */ - if (buf->b_u_curhead == uhp) + // Check for pointers to the header that become invalid now. + if (buf->b_u_curhead == uhp) { buf->b_u_curhead = NULL; - if (buf->b_u_newhead == uhp) - buf->b_u_newhead = NULL; /* freeing the newest entry */ - if (uhpp != NULL && uhp == *uhpp) + } + if (buf->b_u_newhead == uhp) { + buf->b_u_newhead = NULL; // freeing the newest entry + } + if (uhpp != NULL && uhp == *uhpp) { *uhpp = NULL; + } for (uep = uhp->uh_entry; uep != NULL; uep = nuep) { nuep = uep->ue_next; @@ -2973,8 +3004,9 @@ u_freeentries( */ static void u_freeentry(u_entry_T *uep, long n) { - while (n > 0) + while (n > 0) { xfree(uep->ue_array[--n]); + } xfree((char_u *)uep->ue_array); #ifdef U_DEBUG uep->ue_magic = 0; @@ -2999,16 +3031,19 @@ void u_clearall(buf_T *buf) */ void u_saveline(linenr_T lnum) { - if (lnum == curbuf->b_u_line_lnum) /* line is already saved */ + if (lnum == curbuf->b_u_line_lnum) { // line is already saved return; - if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) /* should never happen */ + } + if (lnum < 1 || lnum > curbuf->b_ml.ml_line_count) { // should never happen return; + } u_clearline(); curbuf->b_u_line_lnum = lnum; - if (curwin->w_cursor.lnum == lnum) + if (curwin->w_cursor.lnum == lnum) { curbuf->b_u_line_colnr = curwin->w_cursor.col; - else + } else { curbuf->b_u_line_colnr = 0; + } curbuf->b_u_line_ptr = u_save_line(lnum); } @@ -3033,7 +3068,7 @@ void u_clearline(void) void u_undoline(void) { colnr_T t; - char_u *oldp; + char_u *oldp; if (curbuf->b_u_line_ptr == NULL || curbuf->b_u_line_lnum > curbuf->b_ml.ml_line_count) { @@ -3054,8 +3089,9 @@ void u_undoline(void) curbuf->b_u_line_ptr = oldp; t = curbuf->b_u_line_colnr; - if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum) + if (curwin->w_cursor.lnum == curbuf->b_u_line_lnum) { curbuf->b_u_line_colnr = curwin->w_cursor.col; + } curwin->w_cursor.col = t; curwin->w_cursor.lnum = curbuf->b_u_line_lnum; check_cursor_col(); @@ -3106,8 +3142,8 @@ bool bufIsChanged(buf_T *buf) { // In a "prompt" buffer we do respect 'modified', so that we can control // closing the window by setting or resetting that option. - return (!bt_dontwrite(buf) || bt_prompt(buf)) - && (buf->b_changed || file_ff_differs(buf, true)); + return (!bt_dontwrite(buf) || bt_prompt(buf)) + && (buf->b_changed || file_ff_differs(buf, true)); } // Return true if any buffer has changes. Also buffers that are not written. diff --git a/src/nvim/version.c b/src/nvim/version.c index 7c197f1b7f..bc06ef0b98 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -6,32 +6,32 @@ /// Nvim was forked from Vim 7.4.160. /// Vim originated from Stevie version 3.6 (Fish disk 217) by GRWalter (Fred). -#include <inttypes.h> #include <assert.h> +#include <inttypes.h> #include <limits.h> #include "nvim/api/private/helpers.h" -#include "nvim/vim.h" #include "nvim/ascii.h" #include "nvim/buffer.h" -#include "nvim/iconv.h" -#include "nvim/version.h" #include "nvim/charset.h" +#include "nvim/iconv.h" +#include "nvim/lua/executor.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/screen.h" #include "nvim/strings.h" -#include "nvim/lua/executor.h" +#include "nvim/version.h" +#include "nvim/vim.h" // version info generated by the build system #include "auto/versiondef.h" // for ":version", ":intro", and "nvim --version" #ifndef NVIM_VERSION_MEDIUM -#define NVIM_VERSION_MEDIUM "v" STR(NVIM_VERSION_MAJOR)\ -"." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH)\ -NVIM_VERSION_PRERELEASE +# define NVIM_VERSION_MEDIUM "v" STR(NVIM_VERSION_MAJOR)\ + "." STR(NVIM_VERSION_MINOR) "." STR(NVIM_VERSION_PATCH)\ + NVIM_VERSION_PRERELEASE #endif #define NVIM_VERSION_LONG "NVIM " NVIM_VERSION_MEDIUM @@ -50,23 +50,23 @@ char *version_cflags = "Compilation: " NVIM_VERSION_CFLAGS; static char *features[] = { #ifdef HAVE_ACL -"+acl", + "+acl", #else -"-acl", + "-acl", #endif #if defined(HAVE_ICONV) -"+iconv", + "+iconv", #else -"-iconv", + "-iconv", #endif #ifdef FEAT_TUI -"+tui", + "+tui", #else -"-tui", + "-tui", #endif -NULL + NULL }; // clang-format off @@ -2019,7 +2019,7 @@ void ex_version(exarg_T *eap) /// @param wrap static void version_msg_wrap(char_u *s, int wrap) { - int len = (int)vim_strsize(s) + (wrap ? 2 : 0); + int len = vim_strsize(s) + (wrap ? 2 : 0); if (!got_int && (len < Columns) @@ -2070,7 +2070,7 @@ void list_in_columns(char_u **items, int size, int current) // Find the length of the longest item, use that + 1 as the column width. int i; for (i = 0; size < 0 ? items[i] != NULL : i < size; i++) { - int l = (int)vim_strsize(items[i]) + (i == current ? 2 : 0); + int l = vim_strsize(items[i]) + (i == current ? 2 : 0); if (l > width) { width = l; diff --git a/src/nvim/vim.h b/src/nvim/vim.h index f61f9a5e01..62536a0600 100644 --- a/src/nvim/vim.h +++ b/src/nvim/vim.h @@ -305,18 +305,6 @@ enum { FOLD_TEXT_LEN = 51 }; //!< buffer size for get_foldtext() #include "nvim/buffer_defs.h" // buffer and windows #include "nvim/ex_cmds_defs.h" // Ex command defines -// Used for flags in do_in_path() -#define DIP_ALL 0x01 // all matches, not just the first one -#define DIP_DIR 0x02 // find directories instead of files -#define DIP_ERR 0x04 // give an error message when none found -#define DIP_START 0x08 // also use "start" directory in 'packpath' -#define DIP_OPT 0x10 // also use "opt" directory in 'packpath' -#define DIP_NORTP 0x20 // do not use 'runtimepath' -#define DIP_NOAFTER 0x40 // skip "after" directories -#define DIP_AFTER 0x80 // only use "after" directories -#define DIP_LUA 0x100 // also use ".lua" files -#define DIP_DIRFILE 0x200 // find both files and directories - // Lowest number used for window ID. Cannot have this many windows per tab. #define LOWEST_WIN_ID 1000 diff --git a/src/nvim/viml/parser/expressions.c b/src/nvim/viml/parser/expressions.c index c2aa923c49..39687a8e8d 100644 --- a/src/nvim/viml/parser/expressions.c +++ b/src/nvim/viml/parser/expressions.c @@ -936,7 +936,7 @@ static const char *intchar2str(const int ch) } #ifdef UNIT_TESTING -#include <stdio.h> +# include <stdio.h> REAL_FATTR_UNUSED static inline void viml_pexpr_debug_print_ast_node(const ExprASTNode *const *const eastnode_p, @@ -971,14 +971,14 @@ static inline void viml_pexpr_debug_print_token(const ParserState *const pstate, { fprintf(stderr, "\ntkn: %s\n", viml_pexpr_repr_token(pstate, token, NULL)); } -#define PSTACK(msg) \ +# define PSTACK(msg) \ viml_pexpr_debug_print_ast_stack(&ast_stack, #msg) -#define PSTACK_P(msg) \ +# define PSTACK_P(msg) \ viml_pexpr_debug_print_ast_stack(ast_stack, #msg) -#define PNODE_P(eastnode_p, msg) \ +# define PNODE_P(eastnode_p, msg) \ viml_pexpr_debug_print_ast_node((const ExprASTNode *const *)eastnode_p, \ (#msg)) -#define PTOKEN(tkn) \ +# define PTOKEN(tkn) \ viml_pexpr_debug_print_token(pstate, tkn) #endif diff --git a/src/nvim/window.c b/src/nvim/window.c index 400962f993..ff97eaa757 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -62,7 +62,7 @@ #define NOWIN (win_T *)-1 // non-existing window -# define ROWS_AVAIL (Rows - p_ch - tabline_height()) +#define ROWS_AVAIL (Rows - p_ch - tabline_height()) /// flags for win_enter_ext() typedef enum { @@ -90,7 +90,7 @@ void do_window(int nchar, long Prenum, int xchar) Prenum1 = Prenum == 0 ? 1 : Prenum; -# define CHECK_CMDWIN \ +#define CHECK_CMDWIN \ do { \ if (cmdwin_type != 0) { \ EMSG(_(e_cmdwin)); \ @@ -853,12 +853,12 @@ void ui_ext_win_position(win_T *wp) bool east = c.anchor & kFloatAnchorEast; bool south = c.anchor & kFloatAnchorSouth; - int comp_row = (int)row - (south ? wp->w_height : 0); - int comp_col = (int)col - (east ? wp->w_width : 0); + int comp_row = (int)row - (south ? wp->w_height_outer : 0); + int comp_col = (int)col - (east ? wp->w_width_outer : 0); comp_row += grid->comp_row; comp_col += grid->comp_col; - comp_row = MAX(MIN(comp_row, Rows-wp->w_height_outer-1), 0); - comp_col = MAX(MIN(comp_col, Columns-wp->w_width_outer), 0); + comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - 1), 0); + comp_col = MAX(MIN(comp_col, Columns - wp->w_width_outer), 0); wp->w_winrow = comp_row; wp->w_wincol = comp_col; bool valid = (wp->w_redr_type == 0); diff --git a/src/uncrustify.cfg b/src/uncrustify.cfg index 08296a644a..db0c50ff22 100644 --- a/src/uncrustify.cfg +++ b/src/uncrustify.cfg @@ -1,4 +1,4 @@ -# Uncrustify-0.73.0-168-f20a083e +# Uncrustify-0.73.0-195-1f883c691 # # General options @@ -259,7 +259,7 @@ sp_before_byref_func = ignore # ignore/add/remove/force/not_defined # following word. # # Default: force -sp_after_type = ignore # ignore/add/remove/force/not_defined +sp_after_type = force # ignore/add/remove/force/not_defined # Add or remove space between 'decltype(...)' and word, # brace or function call. @@ -447,12 +447,18 @@ sp_before_ellipsis = ignore # ignore/add/remove/force/not_defined # Add or remove space between a type and '...'. sp_type_ellipsis = ignore # ignore/add/remove/force/not_defined +# Add or remove space between a '*' and '...'. +sp_ptr_type_ellipsis = ignore # ignore/add/remove/force/not_defined + # (D) Add or remove space between a type and '?'. sp_type_question = ignore # ignore/add/remove/force/not_defined # Add or remove space between ')' and '...'. sp_paren_ellipsis = ignore # ignore/add/remove/force/not_defined +# Add or remove space between '&&' and '...'. +sp_byref_ellipsis = ignore # ignore/add/remove/force/not_defined + # Add or remove space between ')' and a qualifier such as 'const'. sp_paren_qualifier = ignore # ignore/add/remove/force/not_defined @@ -507,6 +513,12 @@ sp_sizeof_ellipsis = ignore # ignore/add/remove/force/not_defined # Add or remove space between 'sizeof...' and '('. sp_sizeof_ellipsis_paren = ignore # ignore/add/remove/force/not_defined +# Add or remove space between '...' and a parameter pack. +sp_ellipsis_parameter_pack = ignore # ignore/add/remove/force/not_defined + +# Add or remove space between a parameter pack and '...'. +sp_parameter_pack_ellipsis = ignore # ignore/add/remove/force/not_defined + # Add or remove space between 'decltype' and '('. sp_decltype_paren = ignore # ignore/add/remove/force/not_defined @@ -523,14 +535,21 @@ sp_inside_braces_struct = ignore # ignore/add/remove/force/not_defined sp_inside_braces_oc_dict = ignore # ignore/add/remove/force/not_defined # Add or remove space after open brace in an unnamed temporary -# direct-list-initialization. +# direct-list-initialization +# if statement is a brace_init_lst +# works only if sp_brace_brace is set to ignore. sp_after_type_brace_init_lst_open = ignore # ignore/add/remove/force/not_defined # Add or remove space before close brace in an unnamed temporary -# direct-list-initialization. +# direct-list-initialization +# if statement is a brace_init_lst +# works only if sp_brace_brace is set to ignore. sp_before_type_brace_init_lst_close = ignore # ignore/add/remove/force/not_defined -# Add or remove space inside an unnamed temporary direct-list-initialization. +# Add or remove space inside an unnamed temporary direct-list-initialization +# if statement is a brace_init_lst +# works only if sp_brace_brace is set to ignore +# works only if sp_before_type_brace_init_lst_close is set to ignore. sp_inside_type_brace_init_lst = ignore # ignore/add/remove/force/not_defined # Add or remove space inside '{' and '}'. @@ -775,7 +794,7 @@ sp_sign = ignore # ignore/add/remove/force/not_defined # applied, as in '(--x)' or 'y++;'. # # Default: remove -sp_incdec = ignore # ignore/add/remove/force/not_defined +sp_incdec = remove # ignore/add/remove/force/not_defined # Add or remove space before a backslash-newline at the end of a line. # @@ -2020,7 +2039,7 @@ nl_constr_colon = ignore # ignore/add/remove/force/not_defined # Whether to collapse a two-line namespace, like 'namespace foo\n{ decl; }' # into a single line. If true, prevents other brace newline rules from turning -# such code into four lines. +# such code into four lines. If true, it also preserves one-liner namespaces. nl_namespace_two_to_one_liner = false # true/false # Whether to remove a newline in simple unbraced if statements, turning them @@ -3029,7 +3048,7 @@ mod_sort_oc_property_nullability_weight = 0 # number # Add or remove indentation of preprocessor directives inside #if blocks # at brace level 0 (file-level). -pp_indent = ignore # ignore/add/remove/force/not_defined +pp_indent = remove # ignore/add/remove/force/not_defined # Whether to indent #if/#else/#endif at the brace level. If false, these are # indented from column 1. @@ -3044,7 +3063,7 @@ pp_indent_at_level = false # true/false pp_indent_count = 1 # unsigned number # Add or remove space after # based on pp_level of #if blocks. -pp_space = ignore # ignore/add/remove/force/not_defined +pp_space = force # ignore/add/remove/force/not_defined # Sets the number of spaces per level added with pp_space. pp_space_count = 0 # unsigned number @@ -3255,29 +3274,29 @@ debug_truncate = 0 # unsigned number # `macro-close END_MESSAGE_MAP` # # -set PREPROC FUNC_API_CHECK_TEXTLOCK -set PREPROC FUNC_API_DEPRECATED_SINCE -set PREPROC FUNC_API_FAST -set PREPROC FUNC_API_LUA_ONLY -set PREPROC FUNC_API_NOEXPORT -set PREPROC FUNC_API_REMOTE_ONLY -set PREPROC FUNC_API_SINCE -set PREPROC FUNC_ATTR_ALWAYS_INLINE -set PREPROC FUNC_ATTR_CONST -set PREPROC FUNC_ATTR_MALLOC -set PREPROC FUNC_ATTR_NONNULL_ALL -set PREPROC FUNC_ATTR_NONNULL_ARG -set PREPROC FUNC_ATTR_NONNULL_RET -set PREPROC FUNC_ATTR_NORETURN -set PREPROC FUNC_ATTR_NO_SANITIZE_UNDEFINED -set PREPROC FUNC_ATTR_PRINTF -set PREPROC FUNC_ATTR_PURE -set PREPROC FUNC_ATTR_UNUSED -set PREPROC FUNC_ATTR_WARN_UNUSED_RESULT -set PREPROC REAL_FATTR_ALWAYS_INLINE -set PREPROC REAL_FATTR_CONST -set PREPROC REAL_FATTR_NONNULL_ALL -set PREPROC REAL_FATTR_PURE -set PREPROC REAL_FATTR_WARN_UNUSED_RESULT +set QUESTION FUNC_API_CHECK_TEXTLOCK +set QUESTION FUNC_API_DEPRECATED_SINCE +set QUESTION FUNC_API_FAST +set QUESTION FUNC_API_LUA_ONLY +set QUESTION FUNC_API_NOEXPORT +set QUESTION FUNC_API_REMOTE_ONLY +set QUESTION FUNC_API_SINCE +set QUESTION FUNC_ATTR_ALWAYS_INLINE +set QUESTION FUNC_ATTR_CONST +set QUESTION FUNC_ATTR_MALLOC +set QUESTION FUNC_ATTR_NONNULL_ALL +set QUESTION FUNC_ATTR_NONNULL_ARG +set QUESTION FUNC_ATTR_NONNULL_RET +set QUESTION FUNC_ATTR_NORETURN +set QUESTION FUNC_ATTR_NO_SANITIZE_UNDEFINED +set QUESTION FUNC_ATTR_PRINTF +set QUESTION FUNC_ATTR_PURE +set QUESTION FUNC_ATTR_UNUSED +set QUESTION FUNC_ATTR_WARN_UNUSED_RESULT +set QUESTION REAL_FATTR_ALWAYS_INLINE +set QUESTION REAL_FATTR_CONST +set QUESTION REAL_FATTR_NONNULL_ALL +set QUESTION REAL_FATTR_PURE +set QUESTION REAL_FATTR_WARN_UNUSED_RESULT # option(s) with 'not default' value: 62 # diff --git a/src/xdiff/xdiffi.c b/src/xdiff/xdiffi.c index f35ac5d0b0..cfcbb5d982 100644 --- a/src/xdiff/xdiffi.c +++ b/src/xdiff/xdiffi.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - /* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi diff --git a/src/xdiff/xemit.c b/src/xdiff/xemit.c index 23c6e2d993..b578e7a9d5 100644 --- a/src/xdiff/xemit.c +++ b/src/xdiff/xemit.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - /* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi diff --git a/src/xdiff/xhistogram.c b/src/xdiff/xhistogram.c index 3c84f35626..8598a8550d 100644 --- a/src/xdiff/xhistogram.c +++ b/src/xdiff/xhistogram.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - /* * Copyright (C) 2010, Google Inc. * and other copyright owners as documented in JGit's IP log. diff --git a/src/xdiff/xpatience.c b/src/xdiff/xpatience.c index 5f547ca5c0..f78c897ad8 100644 --- a/src/xdiff/xpatience.c +++ b/src/xdiff/xpatience.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - /* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003-2016 Davide Libenzi, Johannes E. Schindelin diff --git a/src/xdiff/xprepare.c b/src/xdiff/xprepare.c index f13822e4fa..abeb8fb84e 100644 --- a/src/xdiff/xprepare.c +++ b/src/xdiff/xprepare.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - /* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi diff --git a/src/xdiff/xutils.c b/src/xdiff/xutils.c index 65aa50497d..f13a854536 100644 --- a/src/xdiff/xutils.c +++ b/src/xdiff/xutils.c @@ -1,6 +1,3 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - /* * LibXDiff by Davide Libenzi ( File Differential Library ) * Copyright (C) 2003 Davide Libenzi diff --git a/test/config/paths.lua.in b/test/config/paths.lua.in index 7fe5d8ad80..e3979981ba 100644 --- a/test/config/paths.lua.in +++ b/test/config/paths.lua.in @@ -19,5 +19,6 @@ if module.test_luajit_prg == '' then end end table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/include") +table.insert(module.include_paths, "${CMAKE_BINARY_DIR}/src/nvim/auto") return module diff --git a/test/functional/api/command_spec.lua b/test/functional/api/command_spec.lua index 37331d11c7..6f929ad1ca 100644 --- a/test/functional/api/command_spec.lua +++ b/test/functional/api/command_spec.lua @@ -21,7 +21,7 @@ describe('nvim_get_commands', function() it('validates input', function() eq('builtin=true not implemented', pcall_err(meths.get_commands, {builtin=true})) - eq('unexpected key: foo', pcall_err(meths.get_commands, + eq("Invalid key: 'foo'", pcall_err(meths.get_commands, {foo='blah'})) end) diff --git a/test/functional/api/keymap_spec.lua b/test/functional/api/keymap_spec.lua index 4194945645..dd8eef7ca0 100644 --- a/test/functional/api/keymap_spec.lua +++ b/test/functional/api/keymap_spec.lua @@ -436,16 +436,16 @@ describe('nvim_set_keymap, nvim_del_keymap', function() end) it('error on invalid optnames', function() - eq('Invalid key: silentt', + eq("Invalid key: 'silentt'", pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {silentt = true})) - eq('Invalid key: sidd', + eq("Invalid key: 'sidd'", pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {sidd = false})) - eq('Invalid key: nowaiT', + eq("Invalid key: 'nowaiT'", pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {nowaiT = false})) end) it('error on <buffer> option key', function() - eq('Invalid key: buffer', + eq("Invalid key: 'buffer'", pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', {buffer = true})) end) @@ -454,8 +454,8 @@ describe('nvim_set_keymap, nvim_del_keymap', function() -- note: need '%' to escape hyphens, which have special meaning in lua it('throws an error when given non-boolean value for '..opt, function() local opts = {} - opts[opt] = 2 - eq('Gave non-boolean value for an opt: '..opt, + opts[opt] = 'fooo' + eq(opt..' is not a boolean', pcall_err(meths.set_keymap, 'n', 'lhs', 'rhs', opts)) end) end diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua index ffef6a6066..6bcf8dd91f 100644 --- a/test/functional/api/vim_spec.lua +++ b/test/functional/api/vim_spec.lua @@ -12,10 +12,12 @@ local funcs = helpers.funcs local iswin = helpers.iswin local meths = helpers.meths local matches = helpers.matches +local mkdir_p = helpers.mkdir_p local ok, nvim_async, feed = helpers.ok, helpers.nvim_async, helpers.feed local is_os = helpers.is_os local parse_context = helpers.parse_context local request = helpers.request +local rmdir = helpers.rmdir local source = helpers.source local next_msg = helpers.next_msg local tmpname = helpers.tmpname @@ -1117,7 +1119,7 @@ describe('API', function() describe('nvim_get_context', function() it('validates args', function() - eq('unexpected key: blah', + eq("Invalid key: 'blah'", pcall_err(nvim, 'get_context', {blah={}})) eq('invalid value for key: types', pcall_err(nvim, 'get_context', {types=42})) @@ -1574,6 +1576,18 @@ describe('API', function() end) describe('nvim_list_runtime_paths', function() + setup(function() + local pathsep = helpers.get_pathsep() + mkdir_p('Xtest'..pathsep..'a') + mkdir_p('Xtest'..pathsep..'b') + end) + teardown(function() + rmdir 'Xtest' + end) + before_each(function() + meths.set_current_dir 'Xtest' + end) + it('returns nothing with empty &runtimepath', function() meths.set_option('runtimepath', '') eq({}, meths.list_runtime_paths()) @@ -1601,8 +1615,7 @@ describe('API', function() local long_path = ('/a'):rep(8192) meths.set_option('runtimepath', long_path) local paths_list = meths.list_runtime_paths() - neq({long_path}, paths_list) - eq({long_path:sub(1, #(paths_list[1]))}, paths_list) + eq({}, paths_list) end) end) diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua index c49d6405f4..11755a9d97 100644 --- a/test/functional/api/window_spec.lua +++ b/test/functional/api/window_spec.lua @@ -1,8 +1,9 @@ local helpers = require('test.functional.helpers')(after_each) local clear, nvim, curbuf, curbuf_contents, window, curwin, eq, neq, - ok, feed, insert, eval = helpers.clear, helpers.nvim, helpers.curbuf, + ok, feed, insert, eval, tabpage = helpers.clear, helpers.nvim, helpers.curbuf, helpers.curbuf_contents, helpers.window, helpers.curwin, helpers.eq, - helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval + helpers.neq, helpers.ok, helpers.feed, helpers.insert, helpers.eval, + helpers.tabpage local poke_eventloop = helpers.poke_eventloop local curwinmeths = helpers.curwinmeths local funcs = helpers.funcs @@ -11,6 +12,7 @@ local NIL = helpers.NIL local meths = helpers.meths local command = helpers.command local pcall_err = helpers.pcall_err +local assert_alive = helpers.assert_alive -- check if str is visible at the beginning of some line local function is_visible(str) @@ -206,7 +208,7 @@ describe('API/win', function() end) end) - describe('{get,set}_option', function() + describe('nvim_win_get_option, nvim_win_set_option', function() it('works', function() curwin('set_option', 'colorcolumn', '4,3') eq('4,3', curwin('get_option', 'colorcolumn')) @@ -224,6 +226,18 @@ describe('API/win', function() pcall_err(curwin, 'get_option', 'statusline')) eq('', eval('&l:statusline')) -- confirm local value was not copied end) + + it('after switching windows #15390', function() + nvim('command', 'tabnew') + local tab1 = unpack(nvim('list_tabpages')) + local win1 = unpack(tabpage('list_wins', tab1)) + window('set_option', win1, 'statusline', 'window-status') + nvim('command', 'split') + nvim('command', 'wincmd J') + nvim('command', 'wincmd j') + eq('window-status', window('get_option', win1, 'statusline')) + assert_alive() + end) end) describe('get_position', function() @@ -354,13 +368,13 @@ describe('API/win', function() local win = meths.open_win(0, true, { relative='editor', row=10, col=10, width=50, height=10 }) - local tabpage = eval('tabpagenr()') + local tab = eval('tabpagenr()') command('tabprevious') eq(1, eval('tabpagenr()')) meths.win_close(win, false) - eq(1001, meths.tabpage_get_win(tabpage).id) - helpers.assert_alive() + eq(1001, meths.tabpage_get_win(tab).id) + assert_alive() end) end) diff --git a/test/functional/core/startup_spec.lua b/test/functional/core/startup_spec.lua index ff93f88a2d..bf2559f8d7 100644 --- a/test/functional/core/startup_spec.lua +++ b/test/functional/core/startup_spec.lua @@ -20,6 +20,7 @@ local retry = helpers.retry local rmdir = helpers.rmdir local sleep = helpers.sleep local iswin = helpers.iswin +local startswith = helpers.startswith local write_file = helpers.write_file local meths = helpers.meths @@ -310,7 +311,8 @@ describe('startup', function() end) local function pack_clear(cmd) - clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}} + -- add packages after config dir in rtp but before config/after + clear{args={'--cmd', 'set packpath=test/functional/fixtures', '--cmd', 'let paths=split(&rtp, ",")', '--cmd', 'let &rtp = paths[0]..",test/functional/fixtures,test/functional/fixtures/middle,"..join(paths[1:],",")', '--cmd', cmd}, env={XDG_CONFIG_HOME='test/functional/fixtures/'}} end @@ -351,12 +353,57 @@ describe('startup', function() it("handles the correct order with start packages and after/", function() pack_clear [[ lua _G.test_loadorder = {} vim.cmd "runtime! filen.lua" ]] - eq({'ordinary', 'FANCY', 'ordinary after', 'FANCY after'}, exec_lua [[ return _G.test_loadorder ]]) + eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with start packages and after/ after startup", function() + pack_clear [[ lua _G.test_loadorder = {} ]] + command [[ runtime! filen.lua ]] + eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with globpath(&rtp, ...)", function() + pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]] + command [[ + for x in globpath(&rtp, "filen.lua",1,1) + call v:lua.dofile(x) + endfor + ]] + eq({'ordinary', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + + local rtp = meths.get_option'rtp' + ok(startswith(rtp, 'test/functional/fixtures/nvim,test/functional/fixtures/pack/*/start/*,test/functional/fixtures/start/*,test/functional/fixtures,test/functional/fixtures/middle,'), 'rtp='..rtp) end) it("handles the correct order with opt packages and after/", function() pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! superspecial\nruntime! filen.lua" ]] - eq({'ordinary', 'SuperSpecial', 'FANCY', 'SuperSpecial after', 'ordinary after', 'FANCY after'}, exec_lua [[ return _G.test_loadorder ]]) + eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with opt packages and after/ after startup", function() + pack_clear [[ lua _G.test_loadorder = {} ]] + command [[ + packadd! superspecial + runtime! filen.lua + ]] + eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'FANCY after', 'SuperSpecial after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with opt packages and globpath(&rtp, ...)", function() + pack_clear [[ set loadplugins | lua _G.test_loadorder = {} ]] + command [[ + packadd! superspecial + for x in globpath(&rtp, "filen.lua",1,1) + call v:lua.dofile(x) + endfor + ]] + eq({'ordinary', 'SuperSpecial', 'FANCY', 'mittel', 'SuperSpecial after', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + end) + + it("handles the correct order with a package that changes packpath", function() + pack_clear [[ lua _G.test_loadorder = {} vim.cmd "packadd! funky\nruntime! filen.lua" ]] + eq({'ordinary', 'funky!', 'FANCY', 'mittel', 'FANCY after', 'ordinary after'}, exec_lua [[ return _G.test_loadorder ]]) + eq({'ordinary', 'funky!', 'mittel', 'ordinary after'}, exec_lua [[ return _G.nested_order ]]) end) end) diff --git a/test/functional/editor/meta_key_spec.lua b/test/functional/editor/meta_key_spec.lua index 2a9541ba96..c219204409 100644 --- a/test/functional/editor/meta_key_spec.lua +++ b/test/functional/editor/meta_key_spec.lua @@ -11,15 +11,20 @@ describe('meta-keys #8226 #13042', function() end) it('ALT/META, normal-mode', function() - -- Unmapped ALT-chords behave as ESC+c + -- Unmapped ALT-chord behaves as ESC+c. insert('hello') feed('0<A-x><M-x>') expect('llo') + -- Unmapped ALT-chord resolves isolated (non-ALT) ESC mapping. #13086 #15869 + command('nnoremap <ESC> A<lt>ESC><Esc>') + command('nnoremap ; A;<Esc>') + feed('<A-;><M-;>') + expect('llo<ESC>;<ESC>;') -- Mapped ALT-chord behaves as mapped. command('nnoremap <M-l> Ameta-l<Esc>') command('nnoremap <A-j> Aalt-j<Esc>') feed('<A-j><M-l>') - expect('lloalt-jmeta-l') + expect('llo<ESC>;<ESC>;alt-jmeta-l') end) it('ALT/META, visual-mode', function() @@ -27,11 +32,15 @@ describe('meta-keys #8226 #13042', function() insert('peaches') feed('viw<A-x>viw<M-x>') expect('peach') + -- Unmapped ALT-chord resolves isolated (non-ALT) ESC mapping. #13086 #15869 + command('vnoremap <ESC> A<lt>ESC>') + feed('viw<A-;><ESC>viw<M-;><ESC>') + expect('peach<ESC>;<ESC>;') -- Mapped ALT-chord behaves as mapped. command('vnoremap <M-l> Ameta-l<Esc>') command('vnoremap <A-j> Aalt-j<Esc>') feed('viw<A-j>viw<M-l>') - expect('peachalt-jmeta-l') + expect('peach<ESC>;<ESC>;alt-jmeta-l') end) it('ALT/META insert-mode', function() diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua index 9579525502..8e03d9a46e 100644 --- a/test/functional/fixtures/fake-lsp-server.lua +++ b/test/functional/fixtures/fake-lsp-server.lua @@ -43,11 +43,11 @@ end local function read_message() local line = io.read("*l") local length = line:lower():match("content%-length:%s*(%d+)") - return vim.fn.json_decode(io.read(2 + length):sub(2)) + return vim.json.decode(io.read(2 + length):sub(2)) end local function send(payload) - io.stdout:write(format_message_with_content_length(vim.fn.json_encode(payload))) + io.stdout:write(format_message_with_content_length(vim.json.encode(payload))) end local function respond(id, err, result) @@ -564,6 +564,35 @@ function tests.decode_nil() } end + +function tests.code_action_with_resolve() + skeleton { + on_init = function() + return { + capabilities = { + codeActionProvider = { + resolveProvider = true + } + } + } + end; + body = function() + notify('start') + local cmd = { + title = 'Command 1', + command = 'dummy1' + } + expect_request('textDocument/codeAction', function() + return nil, { cmd, } + end) + expect_request('codeAction/resolve', function() + return nil, cmd + end) + notify('shutdown') + end; + } +end + -- Tests will be indexed by TEST_NAME local kill_timer = vim.loop.new_timer() diff --git a/test/functional/fixtures/middle/filen.lua b/test/functional/fixtures/middle/filen.lua new file mode 100644 index 0000000000..fce50cc776 --- /dev/null +++ b/test/functional/fixtures/middle/filen.lua @@ -0,0 +1 @@ +table.insert(_G.test_loadorder, "mittel") diff --git a/test/functional/fixtures/pack/foo/opt/funky/filen.lua b/test/functional/fixtures/pack/foo/opt/funky/filen.lua new file mode 100644 index 0000000000..a33b83c2a7 --- /dev/null +++ b/test/functional/fixtures/pack/foo/opt/funky/filen.lua @@ -0,0 +1,12 @@ +table.insert(_G.test_loadorder, "funky!") + +if not _G.nesty then + _G.nesty = true + local save_order = _G.test_loadorder + _G.test_loadorder = {} + _G.vim.o.pp = "" -- funky! + vim.cmd [[runtime! filen.lua ]] + _G.nested_order = _G.test_loadorder + _G.test_loadorder = save_order + _G.nesty = nil +end diff --git a/test/functional/lua/diagnostic_spec.lua b/test/functional/lua/diagnostic_spec.lua index 9397af9d9f..45aa4915cd 100644 --- a/test/functional/lua/diagnostic_spec.lua +++ b/test/functional/lua/diagnostic_spec.lua @@ -874,6 +874,26 @@ describe('vim.diagnostic', function() return count_extmarks(diagnostic_bufnr, diagnostic_ns) ]]) end) + + it('sets signs', function() + local result = exec_lua [[ + vim.diagnostic.config({ + signs = true, + }) + + local diagnostics = { + make_error('Error', 1, 1, 1, 2), + make_warning('Warning', 3, 3, 3, 3), + } + + vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) + + return vim.fn.sign_getplaced(diagnostic_bufnr, {group = '*'})[1].signs + ]] + + eq({2, 'DiagnosticSignError'}, {result[1].lnum, result[1].name}) + eq({4, 'DiagnosticSignWarn'}, {result[2].lnum, result[2].name}) + end) end) describe('show_line_diagnostics()', function() @@ -995,37 +1015,6 @@ describe('vim.diagnostic', function() end) end) - describe('set_signs()', function() - -- TODO(tjdevries): Find out why signs are not displayed when set from Lua...?? - pending('sets signs by default', function() - exec_lua [[ - vim.diagnostic.config({ - update_in_insert = true, - signs = true, - }) - - local diagnostics = { - make_error('Delayed Diagnostic', 1, 1, 1, 2), - make_error('Delayed Diagnostic', 3, 3, 3, 3), - } - - vim.api.nvim_win_set_buf(0, diagnostic_bufnr) - vim.diagnostic.set(diagnostic_ns, diagnostic_bufnr, diagnostics) - - vim.diagnostic._set_signs(diagnostic_ns, diagnostic_bufnr, diagnostics) - -- return vim.fn.sign_getplaced() - ]] - - nvim("input", "o") - nvim("input", "<esc>") - - -- TODO(tjdevries): Find a way to get the signs to display in the test... - eq(nil, exec_lua [[ - return im.fn.sign_getplaced()[1].signs - ]]) - end) - end) - describe('setloclist()', function() it('sets diagnostics in lnum order', function() local loc_list = exec_lua [[ diff --git a/test/functional/lua/json_spec.lua b/test/functional/lua/json_spec.lua new file mode 100644 index 0000000000..fbb21bfd57 --- /dev/null +++ b/test/functional/lua/json_spec.lua @@ -0,0 +1,133 @@ +local helpers = require('test.functional.helpers')(after_each) +local clear = helpers.clear +local NIL = helpers.NIL +local exec_lua = helpers.exec_lua +local eq = helpers.eq + +describe('vim.json.decode function', function() + before_each(function() + clear() + end) + + it('parses null, true, false', function() + eq(NIL, exec_lua([[return vim.json.decode('null')]])) + eq(true, exec_lua([[return vim.json.decode('true')]])) + eq(false, exec_lua([[return vim.json.decode('false')]])) + end) + + it('parses integer numbers', function() + eq(100000, exec_lua([[return vim.json.decode('100000')]])) + eq(-100000, exec_lua([[return vim.json.decode('-100000')]])) + eq(100000, exec_lua([[return vim.json.decode(' 100000 ')]])) + eq(-100000, exec_lua([[return vim.json.decode(' -100000 ')]])) + eq(0, exec_lua([[return vim.json.decode('0')]])) + eq(0, exec_lua([[return vim.json.decode('-0')]])) + end) + + it('parses floating-point numbers', function() + -- This behavior differs from vim.fn.json_decode, which return '100000.0' + eq('100000', exec_lua([[return tostring(vim.json.decode('100000.0'))]])) + eq(100000.5, exec_lua([[return vim.json.decode('100000.5')]])) + eq(-100000.5, exec_lua([[return vim.json.decode('-100000.5')]])) + eq(-100000.5e50, exec_lua([[return vim.json.decode('-100000.5e50')]])) + eq(100000.5e50, exec_lua([[return vim.json.decode('100000.5e50')]])) + eq(100000.5e50, exec_lua([[return vim.json.decode('100000.5e+50')]])) + eq(-100000.5e-50, exec_lua([[return vim.json.decode('-100000.5e-50')]])) + eq(100000.5e-50, exec_lua([[return vim.json.decode('100000.5e-50')]])) + eq(100000e-50, exec_lua([[return vim.json.decode('100000e-50')]])) + eq(0.5, exec_lua([[return vim.json.decode('0.5')]])) + eq(0.005, exec_lua([[return vim.json.decode('0.005')]])) + eq(0.005, exec_lua([[return vim.json.decode('0.00500')]])) + eq(0.5, exec_lua([[return vim.json.decode('0.00500e+002')]])) + eq(0.00005, exec_lua([[return vim.json.decode('0.00500e-002')]])) + + eq(-0.0, exec_lua([[return vim.json.decode('-0.0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0.0e0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0.0e+0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0.0e-0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0e-0')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0e-2')]])) + eq(-0.0, exec_lua([[return vim.json.decode('-0e+2')]])) + + eq(0.0, exec_lua([[return vim.json.decode('0.0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0.0e0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0.0e+0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0.0e-0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0e-0')]])) + eq(0.0, exec_lua([[return vim.json.decode('0e-2')]])) + eq(0.0, exec_lua([[return vim.json.decode('0e+2')]])) + end) + + it('parses containers', function() + eq({1}, exec_lua([[return vim.json.decode('[1]')]])) + eq({NIL, 1}, exec_lua([[return vim.json.decode('[null, 1]')]])) + eq({['1']=2}, exec_lua([[return vim.json.decode('{"1": 2}')]])) + eq({['1']=2, ['3']={{['4']={['5']={{}, 1}}}}}, + exec_lua([[return vim.json.decode('{"1": 2, "3": [{"4": {"5": [ [], 1]}}]}')]])) + end) + + it('parses strings properly', function() + eq('\n', exec_lua([=[return vim.json.decode([["\n"]])]=])) + eq('', exec_lua([=[return vim.json.decode([[""]])]=])) + eq('\\/"\t\b\n\r\f', exec_lua([=[return vim.json.decode([["\\\/\"\t\b\n\r\f"]])]=])) + eq('/a', exec_lua([=[return vim.json.decode([["\/a"]])]=])) + -- Unicode characters: 2-byte, 3-byte + eq('«',exec_lua([=[return vim.json.decode([["«"]])]=])) + eq('ફ',exec_lua([=[return vim.json.decode([["ફ"]])]=])) + end) + + it('parses surrogate pairs properly', function() + eq('\240\144\128\128', exec_lua([[return vim.json.decode('"\\uD800\\uDC00"')]])) + end) + + it('accepts all spaces in every position where space may be put', function() + local s = ' \t\n\r \t\r\n \n\t\r \n\r\t \r\t\n \r\n\t\t \n\r\t \r\n\t\n \r\t\n\r \t\r \n\t\r\n \n \t\r\n \r\t\n\t \r\n\t\r \n\r \t\n\r\t \r \t\n\r \n\t\r\t \n\r\t\n \r\n \t\r\n\t' + local str = ('%s{%s"key"%s:%s[%s"val"%s,%s"val2"%s]%s,%s"key2"%s:%s1%s}%s'):gsub('%%s', s) + eq({key={'val', 'val2'}, key2=1}, exec_lua([[return vim.json.decode(...)]], str)) + end) + +end) + +describe('vim.json.encode function', function() + before_each(function() + clear() + end) + + it('dumps strings', function() + eq('"Test"', exec_lua([[return vim.json.encode('Test')]])) + eq('""', exec_lua([[return vim.json.encode('')]])) + eq('"\\t"', exec_lua([[return vim.json.encode('\t')]])) + eq('"\\n"', exec_lua([[return vim.json.encode('\n')]])) + -- vim.fn.json_encode return \\u001B + eq('"\\u001b"', exec_lua([[return vim.json.encode('\27')]])) + eq('"þÿþ"', exec_lua([[return vim.json.encode('þÿþ')]])) + end) + + it('dumps numbers', function() + eq('0', exec_lua([[return vim.json.encode(0)]])) + eq('10', exec_lua([[return vim.json.encode(10)]])) + eq('-10', exec_lua([[return vim.json.encode(-10)]])) + end) + + it('dumps floats', function() + eq('10.5', exec_lua([[return vim.json.encode(10.5)]])) + eq('-10.5', exec_lua([[return vim.json.encode(-10.5)]])) + eq('-1e-05', exec_lua([[return vim.json.encode(-1e-5)]])) + end) + + it('dumps lists', function() + eq('[]', exec_lua([[return vim.json.encode({})]])) + eq('[[]]', exec_lua([[return vim.json.encode({{}})]])) + eq('[[],[]]', exec_lua([[return vim.json.encode({{}, {}})]])) + end) + + it('dumps dictionaries', function() + eq('{}', exec_lua([[return vim.json.encode(vim.empty_dict())]])) + eq('{"d":[]}', exec_lua([[return vim.json.encode({d={}})]])) + end) + + it('dumps vim.NIL', function() + eq('null', exec_lua([[return vim.json.encode(vim.NIL)]])) + end) + +end) diff --git a/test/functional/lua/ui_spec.lua b/test/functional/lua/ui_spec.lua new file mode 100644 index 0000000000..94f1b5840b --- /dev/null +++ b/test/functional/lua/ui_spec.lua @@ -0,0 +1,46 @@ +local helpers = require('test.functional.helpers')(after_each) +local eq = helpers.eq +local exec_lua = helpers.exec_lua +local clear = helpers.clear + +describe('vim.ui', function() + before_each(function() + clear() + end) + + + describe('select', function() + it('can select an item', function() + local result = exec_lua[[ + local items = { + { name = 'Item 1' }, + { name = 'Item 2' }, + } + local opts = { + format_item = function(entry) + return entry.name + end + } + local selected + local cb = function(item) + selected = item + end + -- inputlist would require input and block the test; + local choices + vim.fn.inputlist = function(x) + choices = x + return 1 + end + vim.ui.select(items, opts, cb) + vim.wait(100, function() return selected ~= nil end) + return {selected, choices} + ]] + eq({ name = 'Item 1' }, result[1]) + eq({ + 'Select one of:', + '1: Item 1', + '2: Item 2', + }, result[2]) + end) + end) +end) diff --git a/test/functional/plugin/lsp/codelens_spec.lua b/test/functional/plugin/lsp/codelens_spec.lua index e48a0ad260..c8b75e65fc 100644 --- a/test/functional/plugin/lsp/codelens_spec.lua +++ b/test/functional/plugin/lsp/codelens_spec.lua @@ -58,5 +58,33 @@ describe('vim.lsp.codelens', function() ]], bufnr) eq({[1] = {'Lens1', 'LspCodeLens'}}, virtual_text_chunks) + + end) + it('codelens uses client commands', function() + local fake_uri = "file:///fake/uri" + local cmd = exec_lua([[ + fake_uri = ... + local bufnr = vim.uri_to_bufnr(fake_uri) + vim.fn.bufload(bufnr) + vim.api.nvim_buf_set_lines(bufnr, 0, -1, false, {'One line'}) + local lenses = { + { + range = { + start = { line = 0, character = 0, }, + ['end'] = { line = 0, character = 8 } + }, + command = { title = 'Lens1', command = 'Dummy' } + }, + } + vim.lsp.codelens.on_codelens(nil, lenses, {method='textDocument/codeLens', client_id=1, bufnr=bufnr}) + local cmd_called = nil + vim.lsp.commands['Dummy'] = function(command) + cmd_called = command + end + vim.api.nvim_set_current_buf(bufnr) + vim.lsp.codelens.run() + return cmd_called + ]], fake_uri) + eq({ command = 'Dummy', title = 'Lens1' }, cmd) end) end) diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua index 27f2d2536f..8f9b194690 100644 --- a/test/functional/plugin/lsp_spec.lua +++ b/test/functional/plugin/lsp_spec.lua @@ -132,37 +132,38 @@ local function test_rpc_server(config) end describe('LSP', function() - describe('server_name specified', function() - before_each(function() - clear_notrace() - -- Run an instance of nvim on the file which contains our "scripts". - -- Pass TEST_NAME to pick the script. - local test_name = "basic_init" - exec_lua([=[ - lsp = require('vim.lsp') - local test_name, fixture_filename, logfile = ... - function test__start_client() - return lsp.start_client { - cmd_env = { - NVIM_LOG_FILE = logfile; - }; - cmd = { - vim.v.progpath, '-Es', '-u', 'NONE', '--headless', - "-c", string.format("lua TEST_NAME = %q", test_name), - "-c", "luafile "..fixture_filename; - }; - root_dir = vim.loop.cwd(); - } - end - TEST_CLIENT1 = test__start_client() - ]=], test_name, fake_lsp_code, fake_lsp_logfile) - end) + before_each(function() + clear_notrace() - after_each(function() - exec_lua("lsp._vim_exit_handler()") - -- exec_lua("lsp.stop_all_clients(true)") - end) + -- Run an instance of nvim on the file which contains our "scripts". + -- Pass TEST_NAME to pick the script. + local test_name = "basic_init" + exec_lua([=[ + lsp = require('vim.lsp') + local test_name, fixture_filename, logfile = ... + function test__start_client() + return lsp.start_client { + cmd_env = { + NVIM_LOG_FILE = logfile; + }; + cmd = { + vim.v.progpath, '-Es', '-u', 'NONE', '--headless', + "-c", string.format("lua TEST_NAME = %q", test_name), + "-c", "luafile "..fixture_filename; + }; + root_dir = vim.loop.cwd(); + } + end + TEST_CLIENT1 = test__start_client() + ]=], test_name, fake_lsp_code, fake_lsp_logfile) + end) + after_each(function() + exec_lua("lsp._vim_exit_handler()") + -- exec_lua("lsp.stop_all_clients(true)") + end) + + describe('server_name specified', function() it('start_client(), stop_client()', function() retry(nil, 4000, function() eq(1, exec_lua('return #lsp.get_active_clients()')) @@ -334,7 +335,6 @@ describe('LSP', function() } end) it('workspace/configuration returns NIL per section if client was started without config.settings', function() - clear_notrace() fake_lsp_server_setup('workspace/configuration no settings') eq({ NIL, NIL, }, exec_lua [[ local result = { @@ -2022,83 +2022,6 @@ describe('LSP', function() end) end) - describe('lsp.util.make_floating_popup_options', function() - before_each(function() - exec_lua [[ - local bufnr = vim.uri_to_bufnr("file:///fake/uri") - local winheight = vim.fn.winheight(0) - for i = 1, winheight do - vim.api.nvim_buf_set_lines(bufnr, 0, 0, false, {''}) - end - vim.api.nvim_win_set_buf(0, bufnr) - vim.api.nvim_win_set_cursor(0, {winheight, 0}) - ]] - end) - - local function popup_row(opts) - return exec_lua([[ - return vim.lsp.util.make_floating_popup_options(...).row - ]], 2, 2, opts) - end - - local err_pattern = "^Error executing lua: %.%.%./util%.lua:0: invalid floating preview border: .*%. :help vim%.api%.nvim_open_win%(%)$" - - it('calculates default border height correctly', function() - eq(0, popup_row()) - end) - - it('calculates string border height correctly', function() - eq(0, popup_row({border = 'none'})) - eq(-2, popup_row({border = 'single'})) - eq(-2, popup_row({border = 'double'})) - eq(-2, popup_row({border = 'rounded'})) - eq(-2, popup_row({border = 'solid'})) - eq(-1, popup_row({border = 'shadow'})) - end) - - it('error on invalid string border', function() - matches(err_pattern, pcall_err(popup_row, {border = ''})) - matches(err_pattern, pcall_err(popup_row, {border = 'invalid'})) - end) - - it('error on invalid array border length', function() - matches(err_pattern, pcall_err(popup_row, {border = {}})) - matches(err_pattern, pcall_err(popup_row, {border = {'', '', ''}})) - matches(err_pattern, pcall_err(popup_row, {border = {'', '', '', '', ''}})) - end) - - it('error on invalid array border member type', function() - matches(err_pattern, pcall_err(popup_row, {border = {0}})) - end) - - it('calculates 8-array border height correctly', function() - eq(0, popup_row({border = {'', '', '', '', '', '', '', ''}})) - eq(-2, popup_row({border = {'', '~', '', '~', '', '~', '', '~'}})) - eq(-1, popup_row({border = {'', '', '', '~', '', '~', '', ''}})) - eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}}})) - eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', '', '', {'~', 'NormalFloat'}, '', ''}})) - end) - - it('calculates 4-array border height correctly', function() - eq(0, popup_row({border = {'', '', '', ''}})) - eq(-2, popup_row({border = {'', '~', '', '~'}})) - eq(0, popup_row({border = {'', '', '', {'~', 'NormalFloat'}}})) - eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}, '', ''}})) - end) - - it('calculates 2-array border height correctly', function() - eq(0, popup_row({border = {'', ''}})) - eq(-2, popup_row({border = {'', '~'}})) - eq(-2, popup_row({border = {'', {'~', 'NormalFloat'}}})) - end) - - it('calculates 1-array border height correctly', function() - eq(0, popup_row({border = {''}})) - eq(-2, popup_row({border = {'~'}})) - eq(-2, popup_row({border = {{'~', 'NormalFloat'}}})) - end) - end) - describe('lsp.util._make_floating_popup_size', function() before_each(function() exec_lua [[ contents = @@ -2376,26 +2299,43 @@ describe('LSP', function() describe('vim.lsp.buf.code_action', function() it('Calls client side command if available', function() - eq(1, exec_lua [[ - local dummy_calls = 0 - vim.lsp.commands.dummy = function() - dummy_calls = dummy_calls + 1 - end - local actions = { - { - title = 'Dummy command', - command = 'dummy', - }, - } - -- inputlist would require input and block the test; - vim.fn.inputlist = function() - return 1 + local client + local expected_handlers = { + {NIL, {}, {method="shutdown", client_id=1}}; + {NIL, {}, {method="start", client_id=1}}; + } + test_rpc_server { + test_name = 'code_action_with_resolve', + on_init = function(client_) + client = client_ + end, + on_setup = function() + end, + on_exit = function(code, signal) + eq(0, code, "exit code", fake_lsp_logfile) + eq(0, signal, "exit signal", fake_lsp_logfile) + end, + on_handler = function(err, result, ctx) + eq(table.remove(expected_handlers), {err, result, ctx}) + if ctx.method == 'start' then + exec_lua([[ + vim.lsp.commands['dummy1'] = function(cmd) + vim.lsp.commands['dummy2'] = function() + end + end + local bufnr = vim.api.nvim_get_current_buf() + vim.lsp.buf_attach_client(bufnr, TEST_RPC_CLIENT_ID) + vim.fn.inputlist = function() + return 1 + end + vim.lsp.buf.code_action() + ]]) + elseif ctx.method == 'shutdown' then + eq('function', exec_lua[[return type(vim.lsp.commands['dummy2'])]]) + client.stop() + end end - local params = {} - local handler = require'vim.lsp.handlers'['textDocument/codeAction'] - handler(nil, actions, { method = 'textDocument/codeAction', params = params }, nil) - return dummy_calls - ]]) + } end) end) describe('vim.lsp.commands', function() diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index 4373d17890..8074f91215 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -790,3 +790,511 @@ end]] helpers.assert_alive() end) end) + +describe('decorations: virtual lines', function() + local screen, ns + before_each(function() + clear() + screen = Screen.new(50, 12) + screen:attach() + screen:set_default_attr_ids { + [1] = {bold=true, foreground=Screen.colors.Blue}; + [2] = {foreground = Screen.colors.Cyan4}; + [3] = {background = Screen.colors.Yellow1}; + [4] = {bold = true}; + [5] = {background = Screen.colors.Yellow, foreground = Screen.colors.Blue}; + [6] = {foreground = Screen.colors.Blue}; + [7] = {foreground = Screen.colors.SlateBlue}; + [8] = {background = Screen.colors.WebGray, foreground = Screen.colors.DarkBlue}; + [9] = {foreground = Screen.colors.Brown}; + } + + ns = meths.create_namespace 'test' + end) + + local example_text = [[ +if (h->n_buckets < new_n_buckets) { // expand + khkey_t *new_keys = (khkey_t *)krealloc((void *)h->keys, new_n_buckets * sizeof(khkey_t)); + h->keys = new_keys; + if (kh_is_map && val_size) { + char *new_vals = krealloc( h->vals_buf, new_n_buckets * val_size); + h->vals_buf = new_vals; + } +}]] + + it('works with one line', function() + insert(example_text) + feed 'gg' + meths.buf_set_extmark(0, ns, 1, 33, { + virt_lines={ {{">> ", "NonText"}, {"krealloc", "Identifier"}, {": change the size of an allocation"}}}; + virt_lines_above=true; + }) + + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + {1:>> }{2:krealloc}: change the size of an allocation | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + + feed '/krealloc<cr>' + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + {1:>> }{2:krealloc}: change the size of an allocation | + khkey_t *new_keys = (khkey_t *){3:^krealloc}((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + /krealloc | + ]]} + + -- virtual line remains anchored to the extmark + feed 'i<cr>' + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {1:>> }{2:krealloc}: change the size of an allocation | + {3:^krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + {4:-- INSERT --} | + ]]} + + feed '<esc>3+' + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {1:>> }{2:krealloc}: change the size of an allocation | + {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + ^char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + | + ]]} + + meths.buf_set_extmark(0, ns, 5, 0, { + virt_lines = { {{"^^ REVIEW:", "Todo"}, {" new_vals variable seems unneccesary?", "Comment"}} }; + }) + -- TODO: what about the cursor?? + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buck^ets * val_size); | + {5:^^ REVIEW:}{6: new_vals variable seems unneccesary?} | + h->vals_buf = new_vals; | + } | + | + ]]} + + meths.buf_clear_namespace(0, ns, 0, -1) + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *) | + {3:krealloc}((void *)h->keys, new_n_buckets * sizeof(k| + hkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = {3:krealloc}( h->vals_buf, new_n_| + buck^ets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + end) + + + it('works with text at the beginning of the buffer', function() + insert(example_text) + feed 'gg' + + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + {1:~ }| + | + ]]} + + meths.buf_set_extmark(0, ns, 0, 0, { + virt_lines={ + {{"refactor(khash): ", "Special"}, {"take size of values as parameter"}}; + {{"Author: Dev Devsson, "}, {"Tue Aug 31 10:13:37 2021", "Comment"}}; + }; + virt_lines_above=true; + right_gravity=false; + }) + + -- placing virt_text on topline does not automatically cause a scroll + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + {1:~ }| + | + ]], unchanged=true} + + feed '<c-b>' + screen:expect{grid=[[ + {7:refactor(khash): }take size of values as parameter | + Author: Dev Devsson, {6:Tue Aug 31 10:13:37 2021} | + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + | + ]]} + end) + + it('works with text et the end of the buffer', function() + insert(example_text) + feed 'G' + + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + {1:~ }| + | + ]]} + + local id = meths.buf_set_extmark(0, ns, 7, 0, { + virt_lines={{{"Grugg"}}}; + right_gravity=false; + }) + + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + Grugg | + | + ]]} + + meths.buf_del_extmark(0, ns, id) + screen:expect{grid=[[ + if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + ^} | + {1:~ }| + | + ]]} + end) + + it('works with a block scrolling up', function() + screen:try_resize(30, 7) + insert("aa\nbb\ncc\ndd\nee\nff\ngg\nhh") + feed 'gg' + + meths.buf_set_extmark(0, ns, 6, 0, { + virt_lines={ + {{"they see me"}}; + {{"scrolling", "Special"}}; + {{"they"}}; + {{"hatin'", "Special"}}; + }; + }) + + screen:expect{grid=[[ + ^aa | + bb | + cc | + dd | + ee | + ff | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^bb | + cc | + dd | + ee | + ff | + gg | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^cc | + dd | + ee | + ff | + gg | + they see me | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^dd | + ee | + ff | + gg | + they see me | + {7:scrolling} | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^ee | + ff | + gg | + they see me | + {7:scrolling} | + they | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^ff | + gg | + they see me | + {7:scrolling} | + they | + {7:hatin'} | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^gg | + they see me | + {7:scrolling} | + they | + {7:hatin'} | + hh | + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + they see me | + {7:scrolling} | + they | + {7:hatin'} | + ^hh | + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + {7:scrolling} | + they | + {7:hatin'} | + ^hh | + {1:~ }| + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + they | + {7:hatin'} | + ^hh | + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + {7:hatin'} | + ^hh | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + + feed '<c-e>' + screen:expect{grid=[[ + ^hh | + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + {1:~ }| + | + ]]} + end) + + it('works with sign and numbercolumns', function() + insert(example_text) + feed 'gg' + command 'set number signcolumn=yes' + screen:expect{grid=[[ + {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {8: }{9: }d | + {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {8: }{9: }t)); | + {8: }{9: 3 } h->keys = new_keys; | + {8: }{9: 4 } if (kh_is_map && val_size) { | + {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | + {8: }{9: }new_n_buckets * val_size); | + {8: }{9: 6 } h->vals_buf = new_vals; | + {8: }{9: 7 } } | + | + ]]} + + meths.buf_set_extmark(0, ns, 2, 0, { + virt_lines={ + {{"Some special", "Special"}}; + {{"remark about codes", "Comment"}}; + }; + }) + + screen:expect{grid=[[ + {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {8: }{9: }d | + {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {8: }{9: }t)); | + {8: }{9: 3 } h->keys = new_keys; | + {8: }{9: }{7:Some special} | + {8: }{9: }{6:remark about codes} | + {8: }{9: 4 } if (kh_is_map && val_size) { | + {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | + {8: }{9: }new_n_buckets * val_size); | + | + ]]} + + meths.buf_set_extmark(0, ns, 2, 0, { + virt_lines={ + {{"Some special", "Special"}}; + {{"remark about codes", "Comment"}}; + }; + virt_lines_leftcol=true; + }) + screen:expect{grid=[[ + {8: }{9: 1 }^if (h->n_buckets < new_n_buckets) { // expan| + {8: }{9: }d | + {8: }{9: 2 } khkey_t *new_keys = (khkey_t *)krealloc((v| + {8: }{9: }oid *)h->keys, new_n_buckets * sizeof(khkey_| + {8: }{9: }t)); | + {8: }{9: 3 } h->keys = new_keys; | + {7:Some special} | + {6:remark about codes} | + {8: }{9: 4 } if (kh_is_map && val_size) { | + {8: }{9: 5 } char *new_vals = krealloc( h->vals_buf, | + {8: }{9: }new_n_buckets * val_size); | + | + ]]} + end) + + + it('works with hard tabs', function() + insert(example_text) + feed 'gg' + meths.buf_set_extmark(0, ns, 1, 0, { + virt_lines={ {{">>", "NonText"}, {"\tvery\ttabby", "Identifier"}, {"text\twith\ttabs"}}}; + }) + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + {1:>>}{2: very tabby}text with tabs | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + + command 'set tabstop=4' + screen:expect{grid=[[ + ^if (h->n_buckets < new_n_buckets) { // expand | + khkey_t *new_keys = (khkey_t *)krealloc((void *)| + h->keys, new_n_buckets * sizeof(khkey_t)); | + {1:>>}{2: very tabby}text with tabs | + h->keys = new_keys; | + if (kh_is_map && val_size) { | + char *new_vals = krealloc( h->vals_buf, new_n_| + buckets * val_size); | + h->vals_buf = new_vals; | + } | + } | + | + ]]} + end) + +end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index ccf5f963d1..e57c63bb0f 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -109,6 +109,21 @@ describe('float window', function() assert_alive() end) + it('closed immediately by autocmd after win_enter #15548', function() + eq('Error executing lua: [string "<nvim>"]:0: Window was closed immediately', + pcall_err(exec_lua, [[ + vim.cmd "autocmd BufLeave * ++once quit!" + local buf = vim.api.nvim_create_buf(true, true) + vim.api.nvim_open_win(buf, true, { + relative = "win", + row = 0, col = 0, + width = 1, height = 1, + noautocmd = false, + }) + ]])) + assert_alive() + end) + it('opened with correct height', function() local height = exec_lua([[ vim.api.nvim_set_option("winheight", 20) @@ -1514,7 +1529,7 @@ describe('float window', function() it('API has proper error messages', function() local buf = meths.create_buf(false,false) - eq("Invalid key 'bork'", + eq("Invalid key: 'bork'", pcall_err(meths.open_win,buf, false, {width=20,height=2,bork=true})) eq("'win' key is only valid with relative='win'", pcall_err(meths.open_win,buf, false, {width=20,height=2,relative='editor',row=0,col=0,win=0})) @@ -1527,13 +1542,15 @@ describe('float window', function() eq("'relative' requires 'row'/'col' or 'bufpos'", pcall_err(meths.open_win,buf, false, {width=20,height=2,relative='editor'})) eq("'width' key must be a positive Integer", - pcall_err(meths.open_win,buf, false, {width=-1,height=2,relative='editor'})) + pcall_err(meths.open_win,buf, false, {width=-1,height=2,relative='editor', row=0, col=0})) eq("'height' key must be a positive Integer", - pcall_err(meths.open_win,buf, false, {width=20,height=-1,relative='editor'})) + pcall_err(meths.open_win,buf, false, {width=20,height=-1,relative='editor', row=0, col=0})) eq("'height' key must be a positive Integer", - pcall_err(meths.open_win,buf, false, {width=20,height=0,relative='editor'})) - eq("Must specify 'width' and 'height'", - pcall_err(meths.open_win,buf, false, {relative='editor'})) + pcall_err(meths.open_win,buf, false, {width=20,height=0,relative='editor', row=0, col=0})) + eq("Must specify 'width'", + pcall_err(meths.open_win,buf, false, {relative='editor', row=0, col=0})) + eq("Must specify 'height'", + pcall_err(meths.open_win,buf, false, {relative='editor', row=0, col=0, width=2})) end) it('can be placed relative window or cursor', function() @@ -1866,6 +1883,293 @@ describe('float window', function() end end) + it('always anchor to corner including border', function() + screen:try_resize(40,13) + meths.buf_set_lines(0, 0, -1, true, {'just some example text', 'some more example text'}) + feed('ggeee') + command('below split') + if multigrid then + screen:expect([[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ]]) + else + screen:expect([[ + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {5:[No Name] [+] }| + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + local buf = meths.create_buf(false, false) + meths.buf_set_lines(buf, 0, -1, true, {' halloj! ', + ' BORDAA '}) + local win = meths.open_win(buf, false, {relative='cursor', width=9, height=2, row=1, col=-2, border="double"}) + + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 6 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════════╝}| + ]], float_pos={ + [6] = {{id = 1003}, "NW", 4, 1, 14, true} + }} + else + screen:expect([[ + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {5:[No Name] [+] }| + just some exampl^e text | + some more exam{5:╔═════════╗} | + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═════════╝}{0: }| + {4:[No Name] [+] }| + | + ]]) + end + + meths.win_set_config(win, {relative='cursor', row=0, col=-2, anchor='NE'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 6 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════════╝}| + ]], float_pos={ + [6] = {{id = 1003}, "NE", 4, 0, 14, true} + }} + else + screen:expect([[ + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {5:[No Name] [+] }| + jus{5:╔═════════╗}pl^e text | + som{5:║}{1: halloj! }{5:║}ple text | + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {0:~ }{5:╚═════════╝}{0: }| + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + meths.win_set_config(win, {relative='cursor', row=1, col=-2, anchor='SE'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 6 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════════╝}| + ]], float_pos={ + [6] = {{id = 1003}, "SE", 4, 1, 14, true} + }} + else + screen:expect([[ + just some example text | + some more example text | + {0:~ }| + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {5:[No║}{1: BORDAA }{5:║ }| + jus{5:╚═════════╝}pl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + + meths.win_set_config(win, {relative='cursor', row=0, col=-2, anchor='SW'}) + if multigrid then + screen:expect{grid=[[ + ## grid 1 + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + [2:----------------------------------------]| + {5:[No Name] [+] }| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + [4:----------------------------------------]| + {4:[No Name] [+] }| + [3:----------------------------------------]| + ## grid 2 + just some example text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 3 + | + ## grid 4 + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + ## grid 6 + {5:╔═════════╗}| + {5:║}{1: halloj! }{5:║}| + {5:║}{1: BORDAA }{5:║}| + {5:╚═════════╝}| + ]], float_pos={ + [6] = {{id = 1003}, "SW", 4, 0, 14, true} + }} + else + screen:expect([[ + just some example text | + some more example text | + {0:~ }{5:╔═════════╗}{0: }| + {0:~ }{5:║}{1: halloj! }{5:║}{0: }| + {0:~ }{5:║}{1: BORDAA }{5:║}{0: }| + {5:[No Name] [+] ╚═════════╝ }| + just some exampl^e text | + some more example text | + {0:~ }| + {0:~ }| + {0:~ }| + {4:[No Name] [+] }| + | + ]]) + end + end) + it('can be placed relative text in a window', function() screen:try_resize(30,5) local firstwin = meths.get_current_win().id diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua index 7bca741ae3..d3fe38ef52 100644 --- a/test/functional/ui/mouse_spec.lua +++ b/test/functional/ui/mouse_spec.lua @@ -1384,4 +1384,128 @@ describe('ui/mouse/input', function() end) -- level 3 - wrapped end) + + it('getmousepos works correctly', function() + local winwidth = meths.get_option('winwidth') + -- Set winwidth=1 so that window sizes don't change. + meths.set_option('winwidth', 1) + command('tabedit') + local tabpage = meths.get_current_tabpage() + insert('hello') + command('vsplit') + local opts = { + relative='editor', + width=12, + height=1, + col=8, + row=1, + anchor='NW', + style='minimal', + border='single', + focusable=1 + } + local float = meths.open_win(meths.get_current_buf(), false, opts) + command('redraw') + local lines = meths.get_option('lines') + local columns = meths.get_option('columns') + + -- Test that screenrow and screencol are set properly for all positions. + for row = 0, lines - 1 do + for col = 0, columns - 1 do + -- Skip the X button that would close the tab. + if row ~= 0 or col ~= columns - 1 then + meths.input_mouse('left', 'press', '', 0, row, col) + meths.set_current_tabpage(tabpage) + local mousepos = funcs.getmousepos() + eq(row + 1, mousepos.screenrow) + eq(col + 1, mousepos.screencol) + -- All other values should be 0 when clicking on the command line. + if row == lines - 1 then + eq(0, mousepos.winid) + eq(0, mousepos.winrow) + eq(0, mousepos.wincol) + eq(0, mousepos.line) + eq(0, mousepos.column) + end + end + end + end + + -- Test that mouse position values are properly set for the floating window + -- with a border. 1 is added to the height and width to account for the + -- border. + for win_row = 0, opts.height + 1 do + for win_col = 0, opts.width + 1 do + local row = win_row + opts.row + local col = win_col + opts.col + meths.input_mouse('left', 'press', '', 0, row, col) + local mousepos = funcs.getmousepos() + eq(float.id, mousepos.winid) + eq(win_row + 1, mousepos.winrow) + eq(win_col + 1, mousepos.wincol) + local line = 0 + local column = 0 + if win_row > 0 and win_row < opts.height + 1 + and win_col > 0 and win_col < opts.width + 1 then + -- Because of border, win_row and win_col don't need to be + -- incremented by 1. + line = math.min(win_row, funcs.line('$')) + column = math.min(win_col, #funcs.getline(line) + 1) + end + eq(line, mousepos.line) + eq(column, mousepos.column) + end + end + + -- Test that mouse position values are properly set for the floating + -- window, after removing the border. + opts.border = 'none' + meths.win_set_config(float, opts) + command('redraw') + for win_row = 0, opts.height - 1 do + for win_col = 0, opts.width - 1 do + local row = win_row + opts.row + local col = win_col + opts.col + meths.input_mouse('left', 'press', '', 0, row, col) + local mousepos = funcs.getmousepos() + eq(float.id, mousepos.winid) + eq(win_row + 1, mousepos.winrow) + eq(win_col + 1, mousepos.wincol) + local line = math.min(win_row + 1, funcs.line('$')) + local column = math.min(win_col + 1, #funcs.getline(line) + 1) + eq(line, mousepos.line) + eq(column, mousepos.column) + end + end + + -- Test that mouse position values are properly set for ordinary windows. + -- Set the float to be unfocusable instead of closing, to additionally test + -- that getmousepos does not consider unfocusable floats. (see discussion + -- in PR #14937 for details). + opts.focusable = false + meths.win_set_config(float, opts) + command('redraw') + for nr = 1, 2 do + for win_row = 0, funcs.winheight(nr) - 1 do + for win_col = 0, funcs.winwidth(nr) - 1 do + local row = win_row + funcs.win_screenpos(nr)[1] - 1 + local col = win_col + funcs.win_screenpos(nr)[2] - 1 + meths.input_mouse('left', 'press', '', 0, row, col) + local mousepos = funcs.getmousepos() + eq(funcs.win_getid(nr), mousepos.winid) + eq(win_row + 1, mousepos.winrow) + eq(win_col + 1, mousepos.wincol) + local line = math.min(win_row + 1, funcs.line('$')) + local column = math.min(win_col + 1, #funcs.getline(line) + 1) + eq(line, mousepos.line) + eq(column, mousepos.column) + end + end + end + + -- Restore state and release mouse. + command('tabclose!') + meths.set_option('winwidth', winwidth) + meths.input_mouse('left', 'release', '', 0, 0, 0) + end) end) |