├── lua └── penvim │ ├── funcs │ ├── init.lua │ ├── string.lua │ ├── table.lua │ └── filedir.lua │ ├── langs │ ├── init.lua │ └── languages │ │ ├── lua.lua │ │ └── frameworks │ │ └── react.lua │ ├── rooter │ └── init.lua │ ├── project_env │ └── init.lua │ ├── indentor │ └── init.lua │ └── init.lua ├── LICENSE └── README.md /lua/penvim/funcs/init.lua: -------------------------------------------------------------------------------- 1 | 2 | local M={} 3 | 4 | 5 | M.filedir = require("penvim.funcs.filedir") 6 | M.string = require("penvim.funcs.string") 7 | M.table = require("penvim.funcs.table") 8 | 9 | 10 | return M 11 | 12 | -------------------------------------------------------------------------------- /lua/penvim/funcs/string.lua: -------------------------------------------------------------------------------- 1 | 2 | local M = {} 3 | 4 | 5 | function M.split(inputstr, sep) 6 | -- https://stackoverflow.com/a/7615129 7 | if sep == nil then sep = "%s" end 8 | local t={} 9 | for str in string.gmatch(inputstr, "([^"..sep.."]+)") do 10 | table.insert(t, str) 11 | end 12 | return t 13 | end 14 | 15 | 16 | return M 17 | -------------------------------------------------------------------------------- /lua/penvim/langs/init.lua: -------------------------------------------------------------------------------- 1 | 2 | local M={} 3 | local filetype = vim.bo.filetype 4 | 5 | 6 | function M.load_langs() 7 | if filetype == "lua" then 8 | require('penvim/langs/languages/lua') -- LUA 9 | else 10 | if filetype == "javascriptreact" or filetype == "typescriptreact" then 11 | require('penvim/langs/languages/lua/frameworks/react') -- REACT 12 | end 13 | end 14 | end 15 | 16 | 17 | return M 18 | 19 | -------------------------------------------------------------------------------- /lua/penvim/langs/languages/lua.lua: -------------------------------------------------------------------------------- 1 | 2 | 3 | vim.opt.softtabstop = 4 4 | vim.opt.shiftwidth = 4 -- spaces per tab (when shifting), when using the >> or << commands, shift lines by 4 spaces 5 | vim.opt.tabstop = 4 -- spaces per tab 6 | vim.opt.smarttab = true -- / indent/dedent in leading whitespace 7 | vim.opt.autoindent = true -- maintain indent of current line 8 | vim.opt.expandtab = false -- don't expand tabs into spaces 9 | 10 | -------------------------------------------------------------------------------- /lua/penvim/langs/languages/frameworks/react.lua: -------------------------------------------------------------------------------- 1 | 2 | vim.opt.softtabstop = 2 3 | vim.opt.shiftwidth = 2 -- spaces per tab (when shifting), when using the >> or << commands, shift lines by 4 spaces 4 | vim.opt.tabstop = 2 -- spaces per tab 5 | vim.opt.expandtab = true -- expand tabs into spaces 6 | 7 | -- set filetype to typescriptreact if document if javascriptreact 8 | if vim.bo.filetype == "javascriptreact" then 9 | vim.api.nvim_command("set filetype=typescriptreact") 10 | end 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Ali Shahid 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 | -------------------------------------------------------------------------------- /lua/penvim/rooter/init.lua: -------------------------------------------------------------------------------- 1 | 2 | local M={} 3 | local funcs = require("penvim.funcs.filedir") 4 | 5 | 6 | function M.load_rooter() 7 | local current_file_dir = vim.fn.expand('%:p:h') 8 | if not funcs.file_exists(current_file_dir) then 9 | return 10 | end 11 | local current_file_dir_init = current_file_dir 12 | local splited_dir = funcs.dirsplit(current_file_dir) 13 | local patterns = vim.g.penvim_rooter_patterns 14 | local possible_dirs = {current_file_dir.."/"} 15 | local os_user = funcs.osuser() 16 | local os_home_dir = "/home/" .. os_user .. "/" 17 | local final_dir = os_home_dir 18 | local pattern_exist=false 19 | 20 | for _, dir in ipairs(splited_dir) do 21 | if dir=="home" or dir== os_user then 22 | goto continue 23 | end 24 | current_file_dir = current_file_dir .. "/../" 25 | current_file_dir = funcs.normalize_dir(current_file_dir) 26 | possible_dirs[#possible_dirs+1] = current_file_dir 27 | ::continue:: 28 | end 29 | 30 | for _, dir in ipairs(possible_dirs) do 31 | for _, pattern in ipairs(patterns) do 32 | pattern = dir .. pattern 33 | pattern_exist = funcs.file_exists(pattern) 34 | if pattern_exist then 35 | final_dir = dir 36 | goto bottom 37 | end 38 | end 39 | end 40 | ::bottom:: 41 | -- change directory to directory of current file if no project detects 42 | if final_dir == os_home_dir then 43 | final_dir = current_file_dir_init 44 | end 45 | vim.api.nvim_set_current_dir(final_dir) 46 | end 47 | 48 | return M 49 | 50 | -------------------------------------------------------------------------------- /lua/penvim/funcs/table.lua: -------------------------------------------------------------------------------- 1 | 2 | local M = {} 3 | 4 | --[[ 5 | Ordered table iterator, allow to iterate on the natural order of the keys of a table. 6 | Author: http://lua-users.org/wiki/SortedIteration 7 | useful link: https://www.hiveworkshop.com/threads/syncedtable.332894/ 8 | ]] 9 | 10 | local function __genOrderedIndex( t ) 11 | local orderedIndex = {} 12 | for key in pairs(t) do 13 | table.insert( orderedIndex, key ) 14 | end 15 | table.sort( orderedIndex ) 16 | return orderedIndex 17 | end 18 | 19 | local function orderedNext(t, state) 20 | -- Equivalent of the next function, but returns the keys in the alphabetic 21 | -- order. We use a temporary ordered key table that is stored in the 22 | -- table being iterated. 23 | 24 | local key = nil 25 | --print("orderedNext: state = "..tostring(state) ) 26 | if state == nil then 27 | -- the first time, generate the index 28 | t.__orderedIndex = __genOrderedIndex( t ) 29 | key = t.__orderedIndex[1] 30 | else 31 | -- fetch the next value 32 | for i = 1 ,#t.__orderedIndex do 33 | if t.__orderedIndex[i] == state then 34 | key = t.__orderedIndex[i+1] 35 | end 36 | end 37 | end 38 | 39 | if key then 40 | return key, t[key] 41 | end 42 | 43 | -- no more value to return, cleanup 44 | t.__orderedIndex = nil 45 | end 46 | 47 | function M.SyncedTable(t) 48 | -- Equivalent of the pairs() function on tables. Allows to iterate 49 | -- in order 50 | return orderedNext, t, nil 51 | end 52 | 53 | 54 | return M 55 | -------------------------------------------------------------------------------- /lua/penvim/funcs/filedir.lua: -------------------------------------------------------------------------------- 1 | 2 | -- collections of functions that can handle files and directories 3 | 4 | local M={} 5 | 6 | 7 | -- check given path is dir return True else false 8 | function M.is_dir(path) 9 | local file = io.open(path, 'r') 10 | local ok, error, code = file:read(1) 11 | file:close() 12 | return code == 21 13 | end 14 | 15 | 16 | function M.dirsplit(inputstr) 17 | -- https://stackoverflow.com/a/7615129/11812032 18 | local sep = "/" 19 | local t = {} 20 | for str in string.gmatch(inputstr, "([^" .. sep .. "]+)") do 21 | table.insert(t, str) 22 | end 23 | return t 24 | end 25 | 26 | 27 | -- check if file exist 28 | function M.file_exists(fname) 29 | local f = io.open(fname, "r") 30 | if f ~= nil then 31 | io.close(f) 32 | return true 33 | else 34 | return false 35 | end 36 | end 37 | 38 | 39 | function os.capture(cmd, raw) 40 | -- https://gist.github.com/dukeofgaming/453cf950abd99c3dc8fc 41 | local handle = assert(io.popen(cmd, 'r')) 42 | local output = assert(handle:read('*a')) 43 | handle:close() 44 | if raw then return output end 45 | output = string.gsub(string.gsub(string.gsub(output, '^%s+', ''), '%s+$', ''), '[\n\r]+', ' ') 46 | return output 47 | end 48 | 49 | 50 | function M.osuser() 51 | return os.capture("whoami") 52 | end 53 | 54 | 55 | -- normalize dir 56 | -- /home/user/../user/download/../ --> /home/user 57 | function M.normalize_dir(dir) 58 | local stack = {} 59 | local split_dir = M.dirsplit(dir) 60 | for _, r in ipairs(split_dir) do 61 | table.insert(stack, r) 62 | if r == ".." then 63 | stack = {unpack(stack, 1, #stack-2)} 64 | end 65 | end 66 | local str = "" 67 | for _, i in ipairs(stack) do 68 | str = str .. "/" .. i 69 | end 70 | return str .. "/" 71 | end 72 | 73 | 74 | return M 75 | 76 | -------------------------------------------------------------------------------- /lua/penvim/project_env/init.lua: -------------------------------------------------------------------------------- 1 | 2 | --[[ 3 | create .__nvim__.lua file in your project root directory and 4 | define options/configs in that file. 5 | 6 | 1. check if ~/.__nvim__.lua file exist 7 | if exists: load ~/.__nvim__.lua 8 | 2. check if .__nvim__.lua file exist in the project 9 | if exist: load .__nvim__.lua config 10 | NOTE: 11 | .__nvim__.lua is default config file name and it can be change 12 | with 'vim.g.penvim_project_config' (eg: vim.g.penvim_project_config='config.lua') 13 | ]] 14 | 15 | 16 | local M= {} 17 | local filedir = require("penvim.funcs.filedir") 18 | local str = require("penvim.funcs.string") 19 | local table = require("penvim.funcs.table") 20 | local os_user = filedir.osuser() 21 | local config_name = vim.g.penvim_project_config -- project_env config name 22 | local home_con_file = "/home/"..os_user.."/"..config_name 23 | -- local buff_num = vim.api.nvim_get_current_buf() 24 | local filetype = vim.bo.filetype 25 | 26 | 27 | local function conf_file() 28 | --[[ if .__nvim__.lua file exist, return it with full location else return false ]] 29 | 30 | local conff 31 | local curr_file_dir = vim.fn.expand('%:p:h') .. "/" 32 | 33 | -- if directory is not a child of /home/use_name/ then return home's config file (if exist) 34 | if filedir.dirsplit(curr_file_dir)[2] ~= os_user then 35 | if filedir.file_exists(home_con_file) then 36 | return home_con_file 37 | else 38 | return false 39 | end 40 | end 41 | 42 | for _, _ in ipairs(filedir.dirsplit(curr_file_dir)) do 43 | conff = curr_file_dir .. config_name 44 | if filedir.file_exists(conff) then 45 | return curr_file_dir .. config_name 46 | else 47 | curr_file_dir = filedir.normalize_dir(curr_file_dir.."../") 48 | end 49 | end 50 | 51 | return false 52 | end 53 | 54 | 55 | local function apply_config(config) 56 | for option, value in pairs(config) do 57 | vim.opt[option] = value 58 | end 59 | end 60 | 61 | 62 | local function load_config(config_file) 63 | 64 | local configs = dofile(config_file) 65 | 66 | -- exit if config file is empty 67 | if configs == nil then return end 68 | 69 | for lang, config in table.SyncedTable(configs) do 70 | if lang == "all" then 71 | apply_config(config) 72 | 73 | elseif string.find(lang, "_") then 74 | local langs = str.split(lang, "-") 75 | for _, lan in pairs(langs) do 76 | if lan == filetype then 77 | apply_config(config) 78 | end 79 | end 80 | else 81 | if lang == filetype then 82 | apply_config(config) 83 | end 84 | end 85 | end 86 | end 87 | 88 | 89 | function M.load_project_config() 90 | local config_file = conf_file() 91 | 92 | -- only load config file if config file exist 93 | if config_file then 94 | if config_file == home_con_file then 95 | load_config(home_con_file) 96 | else 97 | if filedir.file_exists(home_con_file) then 98 | load_config(home_con_file) 99 | end 100 | load_config(config_file) 101 | end 102 | end 103 | end 104 | 105 | 106 | return M 107 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 |

3 |

PenVim

4 | 5 |

6 | project's root directory and documents indentation detector with project based config loader 7 |
8 |
9 |
10 |

11 |

12 | 13 | 14 |
15 | 16 | Docs 17 | Request-Feature/Issues 18 |
19 | ![Contributors](https://img.shields.io/github/contributors/shaeinst/penvim?color=dark-green) ![Issues](https://img.shields.io/github/issues/shaeinst/penvim) ![License](https://img.shields.io/github/license/shaeinst/penvim) ![Forks](https://img.shields.io/github/forks/shaeinst/penvim?style=social) ![Stargazers](https://img.shields.io/github/stars/shaeinst/penvim?style=social) 20 |
21 | 22 | 23 | 24 | 25 | ## Table Of Contents 26 | 27 | * [About the Project](#about-the-project) 28 | * [Examples](#examples-) 29 | * [Getting Started](#getting-started) 30 | * [Prerequisites](#prerequisites) 31 | * [Installation](#installation) 32 | * [Setup](#setup) 33 | * [License](#license) 34 | * [Acknowledgements](#acknowledgements) 35 | 36 | 37 | ## About The Project 38 | 39 | This plugin (Penvim) has 4 purposes: 40 | 1. change current working directory to project's root directory. 41 | 2. detect indentation of Document (Source Code) and set indentation related config according to detected indentation 42 | 3. load config defined in project's root directory 43 | 4. set options according to Language's Standard Style Guide (not implemented yet...) 44 | 45 | 46 | ## Getting Started 47 | Install PenVim using your favorite package manager. 48 | 49 | 50 | ### Prerequisites 51 | * neovim >= 0.7 52 | 53 | 54 | ### Installation 55 | 56 | using ```vim-plug``` 57 | ```lua 58 | Plug 'Abstract-IDE/penvim' 59 | ``` 60 | or using packer.nvim 61 | ```lua 62 | use {'Abstract-IDE/penvim'} 63 | ``` 64 | 65 | 66 | ## Setup 67 | 68 | ```lua 69 | require("penvim").setup() -- use defaults 70 | ``` 71 | #### Full Configuration 72 | ```lua 73 | require("penvim").setup({ 74 | rooter = { 75 | enable = true, -- enable/disable rooter 76 | patterns = {'.__nvim__.lua', '.git', 'node_modules'} 77 | }, 78 | indentor = { 79 | enable = true, -- enable/disable indentor 80 | indent_length = 4, -- tab indent width 81 | accuracy = 5, -- positive integer. higher the number, the more accurate result (but affects the startup time) 82 | disable_types = { 83 | 'help','dashboard','dashpreview','NvimTree','vista','sagahover', 'terminal', 84 | }, 85 | }, 86 | project_env = { 87 | enable = true, -- enable/disable project_env 88 | config_name = '.__nvim__.lua' -- config file name 89 | }, 90 | }) 91 | ``` 92 | 93 | 94 | ## Examples : 95 |
96 | 97 | sample, config defined in project's root directory 98 | 99 | 100 | ```lua 101 | -- .__nvim__.lua 102 | return { 103 | -- for all file types 104 | all = { 105 | tabstop = 4, -- spaces per tab 106 | cursorline = true, -- highlight current line 107 | relativenumber = true, -- show relative line number 108 | number = true, -- show line numbers 109 | }, 110 | 111 | -- for filetype lua 112 | lua = { 113 | smarttab = true, -- / indent/dedent in leading whitespace 114 | softtabstop = 4, 115 | shiftwidth = 4, -- spaces per tab (when shifting), when using the >> or << commands, shift lines by 4 spaces 116 | }, 117 | 118 | -- for filetype python and javascript 119 | py_js = { 120 | tabstop = 4, -- spaces per tab 121 | wrap = false, -- don't automatically wrap on load 122 | } 123 | } 124 | ``` 125 |
126 | 127 | 128 | ## To-Do 129 | * testing 130 | * implement to set option according to Language's Standard Style Guide 131 | * optimize code 132 | 133 | 134 | ## License 135 | Distributed under the MIT License. See [LICENSE](https://github.com/shaeinst/penvim/blob/main/LICENSE) for more information. 136 | 137 | 138 | ## Acknowledgements 139 | * for [README](https://readme.shaankhan.dev/) 140 | 141 | -------------------------------------------------------------------------------- /lua/penvim/indentor/init.lua: -------------------------------------------------------------------------------- 1 | 2 | local M = {} 3 | local g = vim.g 4 | local fn = vim.fn 5 | local api = vim.api 6 | local opt = vim.opt 7 | 8 | 9 | local function whitespace_type(line) 10 | 11 | local whitespace = line:match("^%s*"):len() 12 | if whitespace == 0 then 13 | return { type = "blank", } 14 | end 15 | 16 | local tabspace = line:match("^%\t*"):len() 17 | if tabspace > 0 then 18 | return { 19 | type = "tab", 20 | no_of_tab = tabspace 21 | } 22 | end 23 | 24 | return { 25 | type = "space", 26 | no_of_space = whitespace 27 | } 28 | end 29 | 30 | 31 | local function block_comment(line) 32 | -- function to check if current line is the start of block comment 33 | -- and if yes then return its right pair 34 | 35 | local white_space = " " 36 | ::loop:: 37 | local space = line:match("^%"..white_space) 38 | if space == white_space then 39 | white_space = white_space .. " " 40 | goto loop 41 | end 42 | 43 | local c_style = line:match("^/%*") -- /* 44 | local html_style = line:match("^$" 51 | elseif lua_style then return "%]=*%]$" 52 | elseif python_style_double then return "\"%\"%\"$" 53 | elseif python_style_single then return "\'%\'%\'$" 54 | else return nil 55 | end 56 | end 57 | 58 | 59 | local function match_pattern(line, pattern) 60 | return line:match(pattern) 61 | end 62 | 63 | 64 | function M.load_indentor() 65 | 66 | local buffer_num = api.nvim_get_current_buf() -- current buffer number 67 | local loc = api.nvim_buf_line_count(buffer_num) -- total lines of code in current file 68 | local current_line_num = 1 69 | local current_line_content, whitespace, whitespace_t 70 | local stack_tab = 0 71 | local stack_space = 0 72 | local space_list = {} 73 | local accuracy = g.penvim_indentor_accuracy 74 | 75 | if loc == 1 then 76 | goto loop_end 77 | end 78 | 79 | ::loop_continue:: 80 | 81 | if current_line_num > loc or stack_space > accuracy or stack_tab > accuracy then 82 | goto loop_end 83 | end 84 | 85 | current_line_content = fn.getline(current_line_num) 86 | 87 | ----------------------------------------------- 88 | -- operations for BLOCK COMMENT 89 | ----------------------------------------------- 90 | Block_comment = block_comment(current_line_content) 91 | 92 | -- if line is not a block comment, handle the operation to the whitespace 93 | if not Block_comment then 94 | goto whitespace 95 | end 96 | 97 | -- any block comment on same line have length of > 3 means bolck comment doesn't exist on same line 98 | if #current_line_content < 4 then 99 | goto loop_block 100 | end 101 | -- check if block comment exist on same single line 102 | if match_pattern(current_line_content, Block_comment) then 103 | current_line_num = current_line_num + 1 104 | goto loop_continue 105 | end 106 | 107 | ::loop_block:: 108 | -- if Block Comment, then operate until Right pair of comment is found 109 | if Block_comment then 110 | for _=current_line_num, loc do 111 | current_line_num = current_line_num + 1 112 | current_line_content = fn.getline(current_line_num) 113 | if match_pattern(current_line_content, Block_comment) then 114 | current_line_num = current_line_num + 1 115 | goto loop_continue 116 | end 117 | end 118 | end 119 | ----------------------------------------------- 120 | 121 | ----------------------------------------------- 122 | -- operations for WHITESPACE 123 | ----------------------------------------------- 124 | ::whitespace:: 125 | whitespace = whitespace_type(current_line_content) 126 | whitespace_t = whitespace['type'] 127 | 128 | if whitespace_t == "blank" then 129 | current_line_num = current_line_num + 1 130 | goto loop_continue 131 | end 132 | if whitespace_t == "tab" then 133 | stack_tab = stack_tab + 1 134 | current_line_num = current_line_num + 1 135 | goto loop_continue 136 | end 137 | if whitespace_t == "space" then 138 | stack_space = stack_space + 1 139 | space_list[#space_list+1] = whitespace.no_of_space 140 | current_line_num = current_line_num + 1 141 | goto loop_continue 142 | end 143 | ----------------------------------------------- 144 | ::loop_end:: 145 | 146 | local indent_length = g.penvim_indentor_length 147 | local tab_set = false 148 | local space_set = false 149 | 150 | opt.smarttab = true -- / indent/dedent in leading whitespace 151 | opt.autoindent = true -- maintain indent of current line 152 | 153 | if stack_tab > stack_space then 154 | -- set tab 155 | opt.tabstop = indent_length -- spaces per tab 156 | opt.shiftwidth = indent_length -- spaces per tab (when shifting), when using the >> or << commands, shift lines by 4 spaces 157 | opt.softtabstop = indent_length -- Number of spaces that a counts for while performing editing operations, like inserting a or using . 158 | opt.expandtab = false -- Always uses spaces instead of tab characters (et). 159 | tab_set = true 160 | end 161 | if stack_space > stack_tab then 162 | -- set space 163 | local space_length 164 | if #space_list <= 1 then 165 | space_length = space_list[1] 166 | else 167 | space_length = math.min(unpack(space_list)) 168 | end 169 | 170 | opt.tabstop = space_length -- Size of a hard tabstop (ts). 171 | opt.shiftwidth = space_length -- Size of an indentation (sw). 172 | opt.softtabstop = 0 -- Number of spaces a counts for. When 0, featuer is off (sts). 173 | opt.expandtab = true -- Always uses spaces instead of tab characters (et). 174 | space_set = true 175 | end 176 | 177 | if not tab_set and not space_set then 178 | -- use default 179 | local indent_type = g.penvim_indentor_type -- default auto, auto|space|tab 180 | 181 | if indent_type == "tab" then 182 | -- TAB 183 | opt.softtabstop = indent_length 184 | opt.shiftwidth = indent_length -- spaces per tab (when shifting), when using the >> or << commands, shift lines by 4 spaces 185 | opt.tabstop = indent_length -- Size of a hard tabstop (ts). 186 | elseif indent_type == "space" then 187 | -- SPACE 188 | opt.softtabstop = 0 -- Number of spaces a counts for. When 0, featuer is off (sts). 189 | opt.expandtab = true -- Always uses spaces instead of tab characters (et). 190 | opt.shiftwidth = indent_length -- spaces per tab (when shifting), when using the >> or << commands, shift lines by 4 spaces 191 | opt.tabstop = indent_length -- Size of a hard tabstop (ts). 192 | else 193 | -- TODO -- 194 | -- auto 195 | return 196 | end 197 | end 198 | end 199 | 200 | 201 | return M 202 | 203 | -------------------------------------------------------------------------------- /lua/penvim/init.lua: -------------------------------------------------------------------------------- 1 | 2 | local M={} 3 | local g = vim.g 4 | local api = vim.api 5 | local bo = vim.bo 6 | 7 | 8 | local default = { 9 | project_env = { 10 | enable = true, 11 | config_name = '.__nvim__.lua' 12 | }, 13 | 14 | langs = { 15 | enable = false 16 | }, 17 | 18 | rooter = { 19 | enable = true, 20 | patterns = { 21 | '.__nvim__.lua', '.git', 'node_modules', '.sln', '.svn', 'wwwroot', '.projectile', 22 | 'rebar.config', -- Rebar project file 23 | 'project.clj', --Leiningen project file 24 | 'build.boot', --Boot-clj project file 25 | 'deps.edn', --Clojure CLI project file 26 | 'SConstruct', --Scons project file 27 | 'default.nix', --Nix project file 28 | 'flake.nix', --Nix flake project file 29 | 'pom.xml', --Maven project file 30 | 'build.sbt', --SBT project file 31 | 'build.sc', --Mill project file 32 | 'gradlew', --Gradle wrapper script 33 | 'build.gradle', --Gradle project file 34 | '.ensime', --Ensime configuration file 35 | 'Gemfile', --Bundler file 36 | 'requirements.txt', --Pip file 37 | 'setup.py', --Setuptools file 38 | 'tox.ini', --Tox file 39 | 'composer.json', --Composer project file 40 | 'Cargo.toml', --Cargo project file 41 | 'mix.exs', --Elixir mix project file 42 | 'stack.yaml', --Haskell’s stack tool based project 43 | 'dune-project', --OCaml Dune project file 44 | 'info.rkt', --Racket package description file 45 | 'DESCRIPTION', --R package description file 46 | --TAGS etags/ctags are usually in the root of project', --GTAGS GNU Global tags 47 | 'configure.in', --autoconf old style 48 | 'configure.ac', --autoconf new style 49 | 'cscope.out', --cscope 50 | 'go.mod', -- go project 51 | } 52 | }, 53 | 54 | -- indentor 55 | indentor = { 56 | enable = true, -- enable/disable indentor 57 | indent_length = 4, -- tab indent width or no of spaces in case of indent_type 58 | indent_type = "auto", -- if file is new or it doesn't have any indentation (auto, tab, space) 59 | accuracy = 5, -- positive integer. higher the number, the more accurate result (but affects the startup time) 60 | disable_types = { 61 | 'help','dashboard','dashpreview','NvimTree','vista','sagahover' 62 | }, 63 | }, 64 | } 65 | 66 | 67 | function M.setup(options) 68 | 69 | -- default options 70 | --------------------------------------- 71 | -- project_env default config 72 | g.penvim_project_enable = default.project_env.enable 73 | g.penvim_project_config = default.project_env.config_name 74 | -- langs default config 75 | g.penvim_langs_enable = default.langs.enable 76 | -- rooter default config 77 | g.penvim_rooter_enable = default.rooter.enable 78 | g.penvim_rooter_patterns = default.rooter.patterns 79 | -- indentor default config 80 | g.penvim_indentor_enable = default.indentor.enable 81 | g.penvim_indentor_length = default.indentor.indent_length 82 | g.penvim_indentor_indent = default.indentor.indent_type 83 | g.penvim_indentor_accuracy = default.indentor.accuracy 84 | local disable_types = default.indentor.disable_types 85 | 86 | -- 87 | local filetype = bo.filetype 88 | local buftype = bo.buftype 89 | 90 | 91 | -- overide default options with user-defined options 92 | --------------------------------------- 93 | if options ~= nil then 94 | -- project_env 95 | if options.project_env ~= nil then 96 | if options.project_env.enable ~= nil then 97 | g.penvim_project_enable = options.project_env.enable 98 | end 99 | if options.project_env.config_name ~=nil then 100 | g.penvim_project_config = options.project_env.config_name 101 | end 102 | end 103 | 104 | -- langs 105 | if options.langs ~= nil and options.langs.enable ~= nil then 106 | g.penvim_langs_enable = options.langs.enable 107 | end 108 | 109 | -- rooter 110 | if options.rooter ~= nil then 111 | if options.rooter.enable ~= nil then 112 | g.penvim_rooter_enable = options.rooter.enable 113 | end 114 | if options.rooter.patterns ~= nil then 115 | local default_pattern = default.rooter.patterns 116 | local option_pattern = options.rooter.patterns 117 | 118 | for _, value in pairs(option_pattern) do 119 | default_pattern[#default_pattern+1] = value 120 | end 121 | g.penvim_rooter_patterns = default_pattern 122 | end 123 | end 124 | 125 | -- indentor 126 | if options.indentor ~= nil then 127 | if options.indentor.enable ~= nil then 128 | g.penvim_indentor_enable = options.indentor.enable 129 | end 130 | if options.indentor.indent_length ~=nil then 131 | g.penvim_indentor_length = options.indentor.indent_length 132 | end 133 | if options.indentor.indent_type ~=nil then 134 | g.penvim_indentor_indent = options.indentor.indent_type 135 | end 136 | if options.indentor.accuracy ~=nil then 137 | g.penvim_indentor_accuracy = options.indentor.accuracy 138 | end 139 | if options.indentor.disable_types ~=nil then 140 | local default_disable_types = default.indentor.disable_types 141 | local option_disable_types = options.indentor.disable_types 142 | for _, value in pairs(option_disable_types) do 143 | default_disable_types[#default_disable_types+1] = value 144 | end 145 | disable_types = default_disable_types 146 | end 147 | end 148 | end 149 | 150 | local group = api.nvim_create_augroup("PenvimAutoGroup", {clear=true}) 151 | 152 | -- TODO: 153 | -- language 154 | --------------------------------------- 155 | if g.penvim_langs_enable then 156 | api.nvim_create_autocmd( 157 | "BufEnter", 158 | { 159 | pattern = "*", 160 | group = group, 161 | command = "lua require('penvim.langs').load_langs()" 162 | } 163 | ) 164 | end 165 | 166 | -- project environment 167 | --------------------------------------- 168 | if g.penvim_project_enable then 169 | api.nvim_create_autocmd( 170 | "BufEnter", 171 | { 172 | pattern = "*", 173 | group = group, 174 | command = "lua require('penvim.project_env').load_project_config()" 175 | } 176 | ) 177 | end 178 | 179 | -- rooter 180 | --------------------------------------- 181 | if g.penvim_rooter_enable then 182 | api.nvim_create_autocmd( 183 | "BufEnter", 184 | { 185 | pattern = "*", 186 | group = group, 187 | command = "lua require('penvim.rooter').load_rooter()" 188 | } 189 | ) 190 | end 191 | 192 | -- TODO: 193 | -- indentor 194 | --------------------------------------- 195 | if g.penvim_indentor_enable then 196 | 197 | -- don't load indentor if filetype is passed in options 198 | for _, ft in pairs(disable_types) do 199 | if ft==filetype or ft==buftype then 200 | return 201 | end 202 | end 203 | 204 | api.nvim_create_autocmd( 205 | "BufEnter", 206 | { 207 | pattern = "*", 208 | group = group, 209 | command = "lua require('penvim.indentor').load_indentor()" 210 | } 211 | ) 212 | end 213 | 214 | end 215 | 216 | 217 | return M 218 | 219 | --------------------------------------------------------------------------------