├── .gitignore ├── .editorconfig ├── init.vim ├── README.md ├── Makefile ├── src └── config │ ├── options.c │ └── keymaps.c ├── init.c └── nvim.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.so 2 | *.json 3 | lua/ 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | indent_style = space 2 | indent_size = 2 3 | -------------------------------------------------------------------------------- /init.vim: -------------------------------------------------------------------------------- 1 | lua << EOF 2 | local config_path = vim.fn.stdpath("config") 3 | if not vim.loop.fs_stat(config_path .. "/lua/init.so") then 4 | vim.cmd.cd(config_path) 5 | vim.cmd.make() -- compile C sources 6 | end 7 | require "init" 8 | EOF 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [Neovim config written in C](https://www.reddit.com/r/neovim/comments/10o62tl/introducing_neovim_config_written_in_c/) 2 | 3 | ![](https://user-images.githubusercontent.com/56817415/215325852-d7423562-f215-4a3d-b195-0be901c1d5fa.png) 4 | 5 | # Installation 6 | 7 | ```sh 8 | git clone https://github.com/nullchilly/CatNvim ~/.config/CatNvim 9 | NVIM_APPNAME="CatNvim" nvim 10 | ``` 11 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: 2 | mkdir -p lua/config 3 | gcc -o lua/init.so init.c -std=gnu11 -O2 -shared -fPIC -Wl,-undefined -Wl,dynamic_lookup 4 | gcc -o lua/config/options.so src/config/options.c -std=gnu11 -O2 -shared -fPIC -Wl,-undefined -Wl,dynamic_lookup 5 | gcc -o lua/config/keymaps.so src/config/keymaps.c -std=gnu11 -O2 -shared -fPIC -Wl,-undefined -Wl,dynamic_lookup 6 | clean: 7 | rm -rf init.so 8 | rm -rf lua/ 9 | -------------------------------------------------------------------------------- /src/config/options.c: -------------------------------------------------------------------------------- 1 | #include "../../nvim.h" 2 | 3 | int luaopen_config_options() { 4 | // Nightly 5 | o("laststatus", i(3)); // Thanks famiu 6 | o("cmdheight", i(0)); // Thanks shougo 7 | o("splitkeep", s("screen")); // Thanks luukvbaal 8 | 9 | // Indentline 10 | o("expandtab", False); 11 | o("shiftwidth", i(2)); 12 | o("tabstop", i(2)); 13 | 14 | // Numbers 15 | o("number", True); 16 | o("numberwidth", i(2)); 17 | o("relativenumber", True); 18 | 19 | // Keys 20 | o("whichwrap", s("<>[]hlb,s")); 21 | g("mapleader", s(" ")); 22 | 23 | // Filetypes 24 | g("python_recommended_style", i(0)); 25 | g("tex_flavor", s("latex")); 26 | 27 | return 1; 28 | } 29 | -------------------------------------------------------------------------------- /init.c: -------------------------------------------------------------------------------- 1 | #include "nvim.h" 2 | 3 | int luaopen_init(lua_State *L) { 4 | char *lazypath = concat_str(get_xdg_home(1), "/lazy/lazy.nvim/"); 5 | if (os_isdir(lazypath) == false) { 6 | system(concat_str("git clone --filter=blob:none " 7 | "https://github.com/folke/lazy.nvim.git --branch=stable ", 8 | lazypath)); 9 | } 10 | opt("runtimepath", runtimepath_default(false), lazypath); 11 | // clang-format off 12 | require_setup("lazy", LUA_TABLE( 13 | { 14 | spec = { 15 | { 16 | 'catppuccin/nvim', 17 | name = 'catppuccin', 18 | opts = { 19 | color_overrides = { 20 | mocha = { 21 | base = '#000000', 22 | mantle = '#000000', 23 | crust = '#000000', 24 | }, 25 | }, 26 | } 27 | }, 28 | { 29 | 'LazyVim/LazyVim', 30 | import = 'lazyvim.plugins', 31 | opts = { colorscheme = 'catppuccin' } 32 | } 33 | }, 34 | install = { 35 | colorscheme = { 'catppuccin' } 36 | } 37 | })); 38 | return 1; 39 | } 40 | -------------------------------------------------------------------------------- /src/config/keymaps.c: -------------------------------------------------------------------------------- 1 | #include "../../nvim.h" 2 | 3 | int luaopen_config_keymaps() { 4 | // Don't copy when pasting over selection 5 | map("v", "p", "\"_dP"); 6 | map("v", "P", "\"_dp"); 7 | 8 | // bufferline 9 | for (int i = 1; i <= 9; i++) { 10 | fmap("n", "", ":%dtabn", i); 11 | } 12 | 13 | map("n", "", ":BufferLineCycleNext "); 14 | map("n", "", ":BufferLineMoveNext "); 15 | map("n", "", ":BufferLineCyclePrev "); 16 | map("n", "", ":BufferLineMovePrev "); 17 | map("n", "x", ":bp|sp|bn|bd! "); 18 | 19 | // Center cursor 20 | map("n", "", "zz"); 21 | 22 | // manage windows 23 | map("n", "", "h"); 24 | map("n", "", "j"); 25 | map("n", "", "k"); 26 | map("n", "", "l"); 27 | 28 | map("n", "", "5<"); 29 | map("n", "", "5>"); 30 | map("n", "", "-"); 31 | map("n", "", "+"); 32 | 33 | // nvimtree 34 | map("n", "", ":Neotree toggle"); 35 | 36 | // telescope 37 | map("n", "fd", ":Telescope find_files "); 38 | map("n", "fw", ":Telescope live_grep "); 39 | 40 | return 1; 41 | } 42 | -------------------------------------------------------------------------------- /nvim.h: -------------------------------------------------------------------------------- 1 | // Reference: 2 | // https://github.com/neovim/neovim/blob/master/src/nvim/api/private/defs.h 3 | #ifndef NVIM_H 4 | #define NVIM_H 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define ARRAY_DICT_INIT KV_INITIAL_VALUE 14 | #define STRING_INIT \ 15 | { .data = NULL, .size = 0 } 16 | #define OBJECT_INIT \ 17 | { .type = kObjectTypeNil } 18 | #define ERROR_INIT \ 19 | { .type = kErrorTypeNone, .msg = NULL } 20 | #define kvec_t(type) \ 21 | struct { \ 22 | size_t size; \ 23 | size_t capacity; \ 24 | type *items; \ 25 | } 26 | 27 | // Basic types 28 | typedef enum { 29 | kErrorTypeNone = -1, 30 | kErrorTypeException, 31 | kErrorTypeValidation, 32 | } ErrorType; 33 | 34 | typedef struct { 35 | ErrorType type; 36 | char *msg; 37 | } Error; 38 | 39 | typedef struct object Object; 40 | typedef kvec_t(Object) Array; 41 | 42 | typedef bool Boolean; 43 | typedef int64_t Integer; 44 | typedef double Float; 45 | 46 | typedef struct { 47 | char *data; 48 | size_t size; 49 | } String; 50 | 51 | typedef struct key_value_pair KeyValuePair; 52 | typedef kvec_t(KeyValuePair) Dictionary; 53 | 54 | typedef enum { 55 | kObjectTypeNil = 0, 56 | kObjectTypeBoolean, 57 | kObjectTypeInteger, 58 | kObjectTypeFloat, 59 | kObjectTypeString, 60 | kObjectTypeArray, 61 | kObjectTypeDictionary, 62 | kObjectTypeLuaRef, 63 | // EXT types, cannot be split or reordered, see #EXT_OBJECT_TYPE_SHIFT 64 | kObjectTypeBuffer, 65 | kObjectTypeWindow, 66 | kObjectTypeTabpage, 67 | } ObjectType; 68 | 69 | // List of possible XDG variables 70 | typedef enum { 71 | kXDGNone = -1, 72 | kXDGConfigHome, ///< XDG_CONFIG_HOME 73 | kXDGDataHome, ///< XDG_DATA_HOME 74 | kXDGCacheHome, ///< XDG_CACHE_HOME 75 | kXDGStateHome, ///< XDG_STATE_HOME 76 | kXDGRuntimeDir, ///< XDG_RUNTIME_DIR 77 | kXDGConfigDirs, ///< XDG_CONFIG_DIRS 78 | kXDGDataDirs, ///< XDG_DATA_DIRS 79 | } XDGVarType; 80 | 81 | typedef int LuaRef; 82 | struct object { 83 | ObjectType type; 84 | union { 85 | Boolean boolean; 86 | Integer integer; 87 | Float floating; 88 | String string; 89 | Array array; 90 | Dictionary dictionary; 91 | LuaRef luaref; 92 | } data; 93 | }; 94 | 95 | #endif // NVIM_API_PRIVATE_DEFS_H 96 | 97 | #define Dict(name) KeyDict_##name 98 | 99 | typedef struct { 100 | Object scope; 101 | Object win; 102 | Object buf; 103 | } KeyDict_option; 104 | 105 | typedef struct { 106 | Object desc; 107 | Object expr; 108 | Object script; 109 | Object silent; 110 | Object unique; 111 | Object nowait; 112 | Object noremap; 113 | Object callback; 114 | Object replace_keycodes; 115 | } KeyDict_keymap; 116 | 117 | #define True \ 118 | (Object) { .type = kObjectTypeBoolean, .data.boolean = true } 119 | #define False \ 120 | (Object) { .type = kObjectTypeBoolean, .data.boolean = false } 121 | 122 | extern char *get_xdg_home(const int); 123 | extern char *stdpaths_get_xdg_var(const XDGVarType idx); 124 | extern char *stdpaths_user_data_subpath(const char *fname); 125 | extern bool os_isdir(const char *name); 126 | extern char *runtimepath_default(bool clean_arg); 127 | void nvim_set_option_value(uint64_t channel_id, String name, Object value, 128 | Dict(option) * opts, Error *err); 129 | extern char *concat_str(const char *restrict str1, const char *restrict str2); 130 | void opt(char *k, char *old, char *new) { 131 | KeyDict_option o = {}; 132 | Error e = ERROR_INIT; 133 | char *x = concat_str(concat_str(old, ","), new); 134 | Object v = {.type = kObjectTypeString, 135 | .data.string = (String){x, strlen(x) - 1}}; 136 | nvim_set_option_value(0, (String){.data = k, .size = strlen(k)}, v, &o, &e); 137 | } 138 | extern int do_cmdline_cmd(const char *cmd); 139 | 140 | #define ms(x) \ 141 | (String) { x, sizeof(x) / sizeof(char) - 1 } 142 | void nvim_set_keymap(uint64_t channel_id, String mode, String lhs, String rhs, 143 | Dict(keymap) * opts, Error *err); 144 | #define map(m, l, r) \ 145 | do { \ 146 | KeyDict_keymap o = {.noremap = True, .silent = True}; \ 147 | Error e = ERROR_INIT; \ 148 | nvim_set_keymap(0, ms(m), ms(l), ms(r), &o, &e); \ 149 | } while (0) 150 | 151 | #define fs(x) \ 152 | (String) { x, strlen(x) } 153 | 154 | void nvim_set_keymap(uint64_t channel_id, String mode, String lhs, String rhs, 155 | Dict(keymap) * opts, Error *err); 156 | 157 | #define fmap(m, lhs, rhs, i) \ 158 | do { \ 159 | KeyDict_keymap o = {.noremap = True, .silent = True}; \ 160 | Error e = ERROR_INIT; \ 161 | char l[] = lhs, r[] = rhs; \ 162 | sprintf(l, l, i), sprintf(r, r, i); \ 163 | nvim_set_keymap(0, fs(m), fs(l), fs(r), &o, &e); \ 164 | } while (0) 165 | 166 | #define i(x) \ 167 | (Object) { .type = kObjectTypeInteger, .data.integer = x } 168 | #define s(x) \ 169 | (Object) { \ 170 | .type = kObjectTypeString, .data.string = (String) { \ 171 | x, sizeof(x) / sizeof(char) - 1 \ 172 | } \ 173 | } 174 | 175 | extern void nvim_set_var(String name, Object value, Error *err); 176 | #define g(k, v) \ 177 | do { \ 178 | Error e = ERROR_INIT; \ 179 | nvim_set_var((String){.data = k, .size = sizeof(k) / sizeof(char) - 1}, v, \ 180 | &e); \ 181 | } while (0) 182 | 183 | #define o(k, v) \ 184 | do { \ 185 | KeyDict_option o = {}; \ 186 | Error e = ERROR_INIT; \ 187 | nvim_set_option_value( \ 188 | 0, (String){.data = k, .size = sizeof(k) / sizeof(char) - 1}, v, &o, \ 189 | &e); \ 190 | } while (0) 191 | 192 | typedef struct lua_State lua_State; 193 | #define LUA_TABLE(...) #__VA_ARGS__ 194 | #define require_setup(m, s) \ 195 | do { \ 196 | do_cmdline_cmd("lua require('" m "').setup" s); \ 197 | } while (0) 198 | --------------------------------------------------------------------------------