├── after └── plugin │ └── cmp_fuzzy_buffer.lua ├── stylua.toml ├── lua └── cmp_fuzzy_buffer │ ├── compare.lua │ └── init.lua ├── LICENSE ├── .github └── workflows │ └── ci.yml ├── README.md └── doc └── cmp-fuzzy-buffer.txt /after/plugin/cmp_fuzzy_buffer.lua: -------------------------------------------------------------------------------- 1 | require'cmp'.register_source('fuzzy_buffer', require'cmp_fuzzy_buffer'.new()) 2 | 3 | -------------------------------------------------------------------------------- /stylua.toml: -------------------------------------------------------------------------------- 1 | indent_type = "Spaces" 2 | indent_width = 2 3 | column_width = 1200 4 | quote_style = "AutoPreferSingle" 5 | -------------------------------------------------------------------------------- /lua/cmp_fuzzy_buffer/compare.lua: -------------------------------------------------------------------------------- 1 | return function(entry1, entry2) 2 | if entry1.source.name == 'fuzzy_buffer' and entry2.source.name == 'fuzzy_buffer' then 3 | if entry1.completion_item.data.score == entry2.completion_item.data.score then 4 | return #entry1.completion_item.word < #entry2.completion_item.word 5 | else 6 | return (entry1.completion_item.data.score > entry2.completion_item.data.score) 7 | end 8 | else 9 | return nil 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 tzachar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: autogen 2 | on: 3 | push: 4 | branches: [main] 5 | paths: 6 | - "lua/**.lua" 7 | - "examples/**.lua" 8 | - "stylua.toml" 9 | - "README.md" 10 | - ".github/workflows/*.yml" 11 | 12 | # Cancel any in-progress CI runs for a PR if it is updated 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} 15 | cancel-in-progress: true 16 | 17 | 18 | jobs: 19 | autogen: 20 | runs-on: ubuntu-20.04 21 | timeout-minutes: 10 22 | permissions: 23 | contents: write 24 | pull-requests: write 25 | steps: 26 | - uses: actions/checkout@v3 27 | - name: panvimdoc 28 | uses: kdheepak/panvimdoc@v2.7.3 29 | with: 30 | vimdoc: cmp-fuzzy-buffer 31 | description: fuzzy word matching in buffer 32 | - name: Apply stylua 33 | uses: JohnnyMorganz/stylua-action@v2.0.0 34 | with: 35 | token: ${{ secrets.GITHUB_TOKEN }} 36 | args: --config-path=stylua.toml lua/ 37 | version: 0.14.1 38 | - name: Push changes 39 | if: ${{ !env.ACT }} 40 | uses: stefanzweifel/git-auto-commit-action@v4 41 | with: 42 | commit_message: "chore(build): auto-generate vimdoc / stylua" 43 | commit_user_name: "github-actions[bot]" 44 | commit_user_email: "github-actions[bot]@users.noreply.github.com" 45 | commit_author: "github-actions[bot] " 46 | -------------------------------------------------------------------------------- /lua/cmp_fuzzy_buffer/init.lua: -------------------------------------------------------------------------------- 1 | local api = vim.api 2 | local matcher = require('fuzzy_nvim') 3 | 4 | local defaults = { 5 | max_buffer_lines = 20000, 6 | max_match_length = 50, 7 | min_match_length = 1, 8 | max_matches = 15, 9 | fuzzy_extra_arg = 0, 10 | get_bufnrs = function() 11 | return { vim.api.nvim_get_current_buf() } 12 | end, 13 | } 14 | 15 | local function minmax(list) 16 | if #list == 0 then 17 | error('Zero length table to minmax') 18 | end 19 | 20 | local min = list[1] 21 | local max = list[1] 22 | for _, v in ipairs(list) do 23 | min = math.min(min, v) 24 | max = math.max(max, v) 25 | end 26 | return min, max 27 | end 28 | 29 | local source = {} 30 | 31 | source.extract_matches = function(self, line, first_match, last_match, is_cmd) 32 | local matches = {} 33 | local keyword_regex = self:regex([[\k]]) 34 | local space_regex = self:regex([[\s]]) 35 | local starts = {} 36 | local ends = {} 37 | 38 | for i = first_match, 0, -1 do 39 | if keyword_regex:match_str(line:sub(i, i)) == nil then 40 | table.insert(starts, i + 1) 41 | break 42 | end 43 | end 44 | for i = last_match, #line + 1 do 45 | if i == #line + 1 or space_regex:match_str(line:sub(i, i)) then 46 | table.insert(ends, i - 1) 47 | break 48 | elseif keyword_regex:match_str(line:sub(i, i)) == nil then 49 | table.insert(ends, i - 1) 50 | -- keep matching 51 | end 52 | end 53 | if is_cmd then 54 | table.insert(matches, line:sub(starts[1], ends[1])) 55 | else 56 | for _, first in ipairs(starts) do 57 | for _, last in ipairs(ends) do 58 | table.insert(matches, line:sub(first, last)) 59 | end 60 | end 61 | end 62 | return matches 63 | end 64 | 65 | source.new = function() 66 | local self = setmetatable({}, { __index = source }) 67 | self.regexes = {} 68 | return self 69 | end 70 | 71 | source.regex = function(self, pattern) 72 | self.regexes[pattern] = self.regexes[pattern] or vim.regex(pattern) 73 | return self.regexes[pattern] 74 | end 75 | 76 | source.get_keyword_pattern = function(self, params) 77 | params.option = vim.tbl_deep_extend('keep', params.option, defaults) 78 | if vim.api.nvim_get_mode().mode == 'c' then 79 | return string.format([=[.\{%d,}]=], params.option.min_match_length) 80 | else 81 | return string.format([=[\k\{%d,}]=], params.option.min_match_length) 82 | end 83 | end 84 | 85 | source.complete = function(self, params, callback) 86 | params.option = vim.tbl_deep_extend('keep', params.option, defaults) 87 | local is_cmd = (vim.api.nvim_get_mode().mode == 'c') 88 | -- in cmd mode we take all the line as a pattern 89 | local pattern = params.context.cursor_before_line:sub(params.offset) 90 | 91 | vim.schedule(function() 92 | local lines = {} 93 | for _, bufnr in ipairs(params.option.get_bufnrs()) do 94 | if api.nvim_buf_line_count(bufnr) <= params.option.max_buffer_lines then 95 | if (api.nvim_get_current_buf() == bufnr or 0 == bufnr) and not is_cmd then 96 | -- skip current line 97 | local current_line = params.context.cursor.line 98 | vim.list_extend(lines, api.nvim_buf_get_lines(bufnr, 0, current_line, false)) 99 | vim.list_extend(lines, api.nvim_buf_get_lines(bufnr, current_line + 1, -1, false)) 100 | else 101 | vim.list_extend(lines, api.nvim_buf_get_lines(bufnr, 0, -1, false)) 102 | end 103 | end 104 | end 105 | local completions = {} 106 | local set = {} 107 | local matches = matcher:filter(pattern, lines, params.option.fuzzy_extra_arg) 108 | for _, result in ipairs(matches) do 109 | local line, positions, score = unpack(result) 110 | local min, max = minmax(positions) 111 | local items = self:extract_matches(line, min, max, is_cmd) 112 | for _, item in ipairs(items) do 113 | if (is_cmd or item ~= pattern) and set[item] == nil and #item <= params.option.max_match_length then 114 | set[item] = true 115 | table.insert(completions, { 116 | word = (is_cmd and vim.fn.escape(item, '/?')) or item, 117 | label = item, 118 | -- cmp has a different notion of filtering completion items. We want 119 | -- all of out fuzzy matche to appear 120 | filterText = pattern, 121 | sortText = item, 122 | data = { score = score }, 123 | dup = 0, 124 | }) 125 | end 126 | end 127 | end 128 | -- keep top max_matches items 129 | table.sort(completions, function(a, b) 130 | return a.data.score > b.data.score 131 | end) 132 | completions = { unpack(completions, 1, params.option.max_matches) } 133 | 134 | callback({ 135 | items = completions, 136 | isIncomplete = true, 137 | }) 138 | end) 139 | end 140 | 141 | return source 142 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # cmp-fuzzy-buffer 2 | 3 | `nvim-cmp` source for fuzzy matched items from using the current buffer. 4 | Can use either `fzf` or `fzy`. 5 | 6 | # Installation 7 | 8 | Depends on [fuzzy.nvim](https://github.com/tzachar/fuzzy.nvim) (which depends 9 | either on `fzf` or on `fzy`). 10 | 11 | Using [Packer](https://github.com/wbthomason/packer.nvim/) with `fzf`: 12 | ```lua 13 | use {'nvim-telescope/telescope-fzf-native.nvim', run = 'make'} 14 | use "hrsh7th/nvim-cmp" 15 | use {'tzachar/cmp-fuzzy-buffer', requires = {'hrsh7th/nvim-cmp', 'tzachar/fuzzy.nvim'}} 16 | ``` 17 | 18 | Using [Packer](https://github.com/wbthomason/packer.nvim/) with `fzy`: 19 | ```lua 20 | use {'romgrk/fzy-lua-native', run = 'make'} 21 | use "hrsh7th/nvim-cmp" 22 | use {'tzachar/cmp-fuzzy-buffer', requires = {'hrsh7th/nvim-cmp', 'tzachar/fuzzy.nvim'}} 23 | ``` 24 | 25 | # Setup 26 | 27 | ```lua 28 | require'cmp'.setup { 29 | sources = cmp.config.sources({ 30 | { name = 'fuzzy_buffer' }, 31 | }) 32 | } 33 | ``` 34 | 35 | This plugin can also be used to perform `/` searches with cmdline mode of cmp: 36 | ```lua 37 | cmp.setup.cmdline('/', { 38 | sources = cmp.config.sources({ 39 | { name = 'fuzzy_buffer' } 40 | }) 41 | }) 42 | ``` 43 | 44 | In `/` search mode, the plugin will match the input string as is, without 45 | parsing out tokens. This enables fuzzy search containing, for example, spaces. 46 | 47 | 48 | *Note:* the plugin's name is `fuzzy_buffer` in `cmp`'s config. 49 | 50 | 51 | # Sorting and Filtering 52 | 53 | By default, `nvim-cmp` will filter out sequences which we matched. To prevent 54 | this, we use the searched-for pattern as an input for `filterText`, such that 55 | all matched strings will be returned. However, this causes `nvim-cmp` to badly 56 | sort our returned results. To solve this issue, and sort `cmp-fuzzy-path` 57 | results by the score returned by the fuzzy matcher, you can use the following: 58 | 59 | ```lua 60 | local compare = require('cmp.config.compare') 61 | 62 | cmp.setup { 63 | sorting = { 64 | priority_weight = 2, 65 | comparators = { 66 | require('cmp_fuzzy_buffer.compare'), 67 | compare.offset, 68 | compare.exact, 69 | compare.score, 70 | compare.recently_used, 71 | compare.kind, 72 | compare.sort_text, 73 | compare.length, 74 | compare.order, 75 | } 76 | }, 77 | } 78 | ``` 79 | 80 | # Configuration 81 | 82 | 83 | ## keyword_pattern (type: string) 84 | 85 | _Default:_ `[[\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%([\-.]\w*\)*\)]]` 86 | _ cmdline Default:_ `[=[[^[:blank:]].*]=]` 87 | 88 | A vim's regular expression for detecting the pattern to search with (starting 89 | from where the cursor currently is) 90 | 91 | ## indentifier_patter (type: vim regex) 92 | _Default:_ 93 | ```lua 94 | indentifier_patter = [=[[[:keyword:]]]=] 95 | ``` 96 | 97 | Used to find the best matched identifier to return based on the fuzzy patter and 98 | match results. 99 | 100 | ## non_indentifier_patter (type: vim regex) 101 | _Default:_ 102 | ```lua 103 | non_indentifier_patter = [=[[^[:keyword:]]]=], 104 | ``` 105 | 106 | Used to find the best matched identifier to return based on the fuzzy patter and 107 | match results. 108 | 109 | ## max_buffer_lines (type: int) 110 | 111 | The plugin will not work in buffers with more than `max_buffer_lines` lines for 112 | performance reasons. 113 | 114 | _Default:_ 20000 115 | 116 | ## max_match_length (type: int) 117 | 118 | Do not show matches longer than `max_match_length`. 119 | 120 | _Default:_ 50 121 | 122 | ## min_match_length (type: int) 123 | 124 | Do not show matches shorter than `min_match_length`. 125 | 126 | _Default:_ 1 127 | 128 | ## fuzzy_extra_arg 129 | 130 | This has different meaning depending on the fuzzy matching library used. 131 | For `fzf`, this is the `case_mode` argument: 0 = smart_case, 1 = ignore_case, 2 = respect_case. 132 | For `fzy`, this is the `is_case_sensitive` argument. Set to `false` to do case insensitive matching. 133 | 134 | _Default:_ 0 135 | 136 | ## get_bufnrs (type: function) 137 | 138 | Return a list of buffer numbers from which to get lines. 139 | 140 | _Default_: 141 | ```lua 142 | get_bufnrs = function() 143 | return { vim.api.nvim_get_current_buf() } 144 | end 145 | ``` 146 | 147 | If you want to use all loaded buffer, you can use the following setup: 148 | 149 | ```lua 150 | require'cmp'.setup { 151 | sources = cmp.config.sources({ 152 | { 153 | name = 'fuzzy_buffer' , 154 | option = { 155 | get_bufnrs = function() 156 | local bufs = {} 157 | for _, buf in ipairs(vim.api.nvim_list_bufs()) do 158 | local buftype = vim.api.nvim_buf_get_option(buf, 'buftype') 159 | if buftype ~= 'nofile' and buftype ~= 'prompt' then 160 | bufs[#bufs + 1] = buf 161 | end 162 | end 163 | return bufs 164 | end 165 | }, 166 | }, 167 | }) 168 | } 169 | ``` 170 | 171 | ## max_matches (type: int) 172 | 173 | Maximal number of matches to returl. 174 | 175 | _Default:_ 15 176 | -------------------------------------------------------------------------------- /doc/cmp-fuzzy-buffer.txt: -------------------------------------------------------------------------------- 1 | *cmp-fuzzy-buffer.txt* fuzzy word matching in buffer 2 | 3 | ============================================================================== 4 | Table of Contents *cmp-fuzzy-buffer-table-of-contents* 5 | 6 | 1. cmp-fuzzy-buffer |cmp-fuzzy-buffer-cmp-fuzzy-buffer| 7 | 2. Installation |cmp-fuzzy-buffer-installation| 8 | 3. Setup |cmp-fuzzy-buffer-setup| 9 | 4. Sorting and Filtering |cmp-fuzzy-buffer-sorting-and-filtering| 10 | 5. Configuration |cmp-fuzzy-buffer-configuration| 11 | - keyword_pattern (type: string)|cmp-fuzzy-buffer-keyword_pattern-(type:-string)| 12 | - indentifier_patter (type: vim regex)|cmp-fuzzy-buffer-indentifier_patter-(type:-vim-regex)| 13 | - non_indentifier_patter (type: vim regex)|cmp-fuzzy-buffer-non_indentifier_patter-(type:-vim-regex)| 14 | - max_buffer_lines (type: int)|cmp-fuzzy-buffer-max_buffer_lines-(type:-int)| 15 | - max_match_length (type: int)|cmp-fuzzy-buffer-max_match_length-(type:-int)| 16 | - min_match_length (type: int)|cmp-fuzzy-buffer-min_match_length-(type:-int)| 17 | - fuzzy_extra_arg |cmp-fuzzy-buffer-fuzzy_extra_arg| 18 | - get_bufnrs (type: function) |cmp-fuzzy-buffer-get_bufnrs-(type:-function)| 19 | - max_matches (type: int) |cmp-fuzzy-buffer-max_matches-(type:-int)| 20 | 21 | ============================================================================== 22 | 1. cmp-fuzzy-buffer *cmp-fuzzy-buffer-cmp-fuzzy-buffer* 23 | 24 | `nvim-cmp` source for fuzzy matched items from using the current buffer. Can 25 | use either `fzf` or `fzy`. 26 | 27 | ============================================================================== 28 | 2. Installation *cmp-fuzzy-buffer-installation* 29 | 30 | Depends on fuzzy.nvim (which depends 31 | either on `fzf` or on `fzy`). 32 | 33 | Using Packer with `fzf`: 34 | 35 | > 36 | use {'nvim-telescope/telescope-fzf-native.nvim', run = 'make'} 37 | use "hrsh7th/nvim-cmp" 38 | use {'tzachar/cmp-fuzzy-buffer', requires = {'hrsh7th/nvim-cmp', 'tzachar/fuzzy.nvim'}} 39 | < 40 | 41 | 42 | Using Packer with `fzy`: 43 | 44 | > 45 | use {'romgrk/fzy-lua-native', run = 'make'} 46 | use "hrsh7th/nvim-cmp" 47 | use {'tzachar/cmp-fuzzy-buffer', requires = {'hrsh7th/nvim-cmp', 'tzachar/fuzzy.nvim'}} 48 | < 49 | 50 | 51 | ============================================================================== 52 | 3. Setup *cmp-fuzzy-buffer-setup* 53 | 54 | > 55 | require'cmp'.setup { 56 | sources = cmp.config.sources({ 57 | { name = 'fuzzy_buffer' }, 58 | }) 59 | } 60 | < 61 | 62 | 63 | This plugin can also be used to perform `/` searches with cmdline mode of cmp: 64 | 65 | > 66 | cmp.setup.cmdline('/', { 67 | sources = cmp.config.sources({ 68 | { name = 'fuzzy_buffer' } 69 | }) 70 | }) 71 | < 72 | 73 | 74 | In `/` search mode, the plugin will match the input string as is, without 75 | parsing out tokens. This enables fuzzy search containing, for example, spaces. 76 | 77 | _Note:_ the plugin’s name is `fuzzy_buffer` in `cmp`’s config. 78 | 79 | ============================================================================== 80 | 4. Sorting and Filtering *cmp-fuzzy-buffer-sorting-and-filtering* 81 | 82 | By default, `nvim-cmp` will filter out sequences which we matched. To prevent 83 | this, we use the searched-for pattern as an input for `filterText`, such that 84 | all matched strings will be returned. However, this causes `nvim-cmp` to badly 85 | sort our returned results. To solve this issue, and sort `cmp-fuzzy-path` 86 | results by the score returned by the fuzzy matcher, you can use the following: 87 | 88 | > 89 | local compare = require('cmp.config.compare') 90 | 91 | cmp.setup { 92 | sorting = { 93 | priority_weight = 2, 94 | comparators = { 95 | require('cmp_fuzzy_buffer.compare'), 96 | compare.offset, 97 | compare.exact, 98 | compare.score, 99 | compare.recently_used, 100 | compare.kind, 101 | compare.sort_text, 102 | compare.length, 103 | compare.order, 104 | } 105 | }, 106 | } 107 | < 108 | 109 | 110 | ============================================================================== 111 | 5. Configuration *cmp-fuzzy-buffer-configuration* 112 | 113 | KEYWORD_PATTERN (TYPE: STRING)*cmp-fuzzy-buffer-keyword_pattern-(type:-string)* 114 | 115 | _Default:_ `[[\%(-\?\d\+\%(\.\d\+\)\?\|\h\w*\%([\-.]\w*\)*\)]]` _ cmdline 116 | Default:_ `[=[[^[:blank:]].*]=]` 117 | 118 | A vim’s regular expression for detecting the pattern to search with (starting 119 | from where the cursor currently is) 120 | 121 | INDENTIFIER_PATTER (TYPE: VIM REGEX)*cmp-fuzzy-buffer-indentifier_patter-(type:-vim-regex)* 122 | 123 | _Default:_ 124 | 125 | > 126 | indentifier_patter = [=[[[:keyword:]]]=] 127 | < 128 | 129 | 130 | Used to find the best matched identifier to return based on the fuzzy patter 131 | and match results. 132 | 133 | NON_INDENTIFIER_PATTER (TYPE: VIM REGEX)*cmp-fuzzy-buffer-non_indentifier_patter-(type:-vim-regex)* 134 | 135 | _Default:_ 136 | 137 | > 138 | non_indentifier_patter = [=[[^[:keyword:]]]=], 139 | < 140 | 141 | 142 | Used to find the best matched identifier to return based on the fuzzy patter 143 | and match results. 144 | 145 | MAX_BUFFER_LINES (TYPE: INT) *cmp-fuzzy-buffer-max_buffer_lines-(type:-int)* 146 | 147 | The plugin will not work in buffers with more than `max_buffer_lines` lines for 148 | performance reasons. 149 | 150 | _Default:_ 20000 151 | 152 | MAX_MATCH_LENGTH (TYPE: INT) *cmp-fuzzy-buffer-max_match_length-(type:-int)* 153 | 154 | Do not show matches longer than `max_match_length`. 155 | 156 | _Default:_ 50 157 | 158 | MIN_MATCH_LENGTH (TYPE: INT) *cmp-fuzzy-buffer-min_match_length-(type:-int)* 159 | 160 | Do not show matches shorter than `min_match_length`. 161 | 162 | _Default:_ 1 163 | 164 | FUZZY_EXTRA_ARG *cmp-fuzzy-buffer-fuzzy_extra_arg* 165 | 166 | This has different meaning depending on the fuzzy matching library used. For 167 | `fzf`, this is the `case_mode` argument: 0 = smart_case, 1 = ignore_case, 2 = 168 | respect_case. For `fzy`, this is the `is_case_sensitive` argument. Set to 169 | `false` to do case insensitive matching. 170 | 171 | _Default:_ 0 172 | 173 | GET_BUFNRS (TYPE: FUNCTION) *cmp-fuzzy-buffer-get_bufnrs-(type:-function)* 174 | 175 | Return a list of buffer numbers from which to get lines. 176 | 177 | _Default_: 178 | 179 | > 180 | get_bufnrs = function() 181 | return { vim.api.nvim_get_current_buf() } 182 | end 183 | < 184 | 185 | 186 | If you want to use all loaded buffer, you can use the following setup: 187 | 188 | > 189 | require'cmp'.setup { 190 | sources = cmp.config.sources({ 191 | { 192 | name = 'fuzzy_buffer' , 193 | option = { 194 | get_bufnrs = function() 195 | local bufs = {} 196 | for _, buf in ipairs(vim.api.nvim_list_bufs()) do 197 | local buftype = vim.api.nvim_buf_get_option(buf, 'buftype') 198 | if buftype ~= 'nofile' and buftype ~= 'prompt' then 199 | bufs[#bufs + 1] = buf 200 | end 201 | end 202 | return bufs 203 | end 204 | }, 205 | }, 206 | }) 207 | } 208 | < 209 | 210 | 211 | MAX_MATCHES (TYPE: INT) *cmp-fuzzy-buffer-max_matches-(type:-int)* 212 | 213 | Maximal number of matches to returl. 214 | 215 | _Default:_ 15 216 | 217 | Generated by panvimdoc 218 | 219 | vim:tw=78:ts=8:noet:ft=help:norl: 220 | --------------------------------------------------------------------------------