#ifndef EDITOR_INDENTS_H #define EDITOR_INDENTS_H #include "utils/utils.h" static const std::unordered_map kLangtoIndent = { {"make", 1}, {"yaml", 2}}; // this indents the newline one level when the line (on the curser before \n is // inserted) matches this at its end (stripped of whitespace) static const std::unordered_map> kLangtoBlockStartsEnd = { {"bash", {"then", "do", "in", "{", "(", "\\", "&&", "||", "|"}}, {"c", {"{", "(", ":"}}, {"cpp", {"{", "(", ":"}}, {"h", {"{", "(", ":"}}, {"css", {"{", "("}}, {"fish", {"{", "(", "^", "&&", "||", "|"}}, {"go", {"{", "(", ":"}}, {"gomod", {"{", "(", ":"}}, {"haskell", {"do", "where", "then", "else", "of"}}, {"javascript", {"{", "(", "[", ":"}}, {"typescript", {"{", "(", "[", ":"}}, {"json", {"{", "[", ":"}}, {"jsonc", {"{", "[", ":"}}, {"ruby", {"then", "else", "begin", "{", "(", "["}}, {"lua", {"then", "do", "else", "repeat", "{", "(", "["}}, {"python", {":", "(", "[", "{"}}, {"rust", {"{", "(", "[", ":"}}, {"php", {"{", "(", "[", ":"}}, {"nginx", {"{"}}, {"yaml", {":"}}, {"sql", {"("}}, {"make", {":"}}, {"gdscript", {":", "(", "[", "{"}}, }; // this indents the newline one level when the line (on the curser before \n is // inserted) matches this at its start (stripped of whitespace) static const std::unordered_map> kLangtoBlockStartsStart = { {"c", {"if", "for", "while"}}, {"cpp", {"if", "for", "while"}}, {"h", {"if", "for", "while"}}, {"fish", {"if", "else", "for", "while", "switch", "case", "function"}}, {"javascript", {"if", "for", "while"}}, {"typescript", {"if", "for", "while"}}, {"ruby", {"if", "do", "when", "rescue", "class", "module", "def", "unless", "until", "elsif", "ensure"}}, {"lua", {"function"}}, {"nginx", {"{"}}, }; // This dedents the line (under the cursor before \n is inserted) when the line // matches this fully (stripped of whitespace) static const std::unordered_map> kLangtoBlockEndsFull = { {"bash", {"fi", "done", "esac", "}", ")"}}, {"c", {"}", ")"}}, {"cpp", {"}", ")"}}, {"h", {"}", ")"}}, {"css", {"}", ")"}}, {"fish", {"end"}}, {"go", {"}", ")"}}, {"gomod", {"}", ")"}}, {"javascript", {"}", ")", "]"}}, {"typescript", {"}", ")", "]"}}, {"json", {"}", "]"}}, {"jsonc", {"}", "]"}}, {"ruby", {"end", "else", "}", ")", "]"}}, {"lua", {"else", "}", ")", "]"}}, {"python", {"}", ")", "]", "else:"}}, {"rust", {"}", ")", "]"}}, {"php", {"}", ")", "]", "else:", "endif;", "endfor;", "endwhile;", "endswitch;", "endcase;", "endfunction;"}}, {"nginx", {"}"}}, {"sql", {")"}}, {"gdscript", {"}", ")", "]"}}, }; // This dedents the line (under the cursor before \n is inserted) when the line // matches this at its start (stripped of whitespace) static const std::unordered_map> kLangtoBlockEndsStart = { {"c", {"case", "default:", "} else"}}, {"cpp", {"case", "default:", "} else"}}, {"h", {"case", "default:", "} else"}}, {"fish", {"else if"}}, {"go", {"case", "default:", "} else"}}, {"gomod", {"}", ")"}}, {"javascript", {"case", "default:"}}, {"typescript", {"case", "default:"}}, {"json", {"}", "]"}}, {"python", {"elif"}}, {"jsonc", {"}", "]"}}, {"ruby", {"when", "elsif", "rescue", "ensure"}}, {"lua", {"end", "elseif", "until"}}, {"rust", {"case", "default:", "} else"}}, {"php", {"case", "default:", "} else"}}, }; struct IndentationEngine { // tabs = 1, spaces = 2+ uint8_t indent = 0; struct Editor *editor = nullptr; void compute_indent(Editor *n_editor); void insert_new_line(Coord cursor); void insert_tab(Coord cursor); uint32_t set_indent(uint32_t row, int64_t indent_level); uint32_t indent_line(uint32_t row); uint32_t dedent_line(uint32_t row); void indent_block(uint32_t start_row, uint32_t end_row, int delta); void indent_block(uint32_t start, uint32_t end); void dedent_block(uint32_t start, uint32_t end); // fixes a autocomplete block's indentation char *block_to_asis(Coord cursor, std::string source, uint32_t *out_len); private: const std::vector *start_end = nullptr; const std::vector *start_start = nullptr; const std::vector *end_full = nullptr; const std::vector *end_start = nullptr; // TODO: Ignore comments/strings too // returns the indent level of the line itself or of the previous non-empty uint32_t indent_expected(uint32_t row); // returns the indent level of the line uint32_t indent_real(char *line, uint32_t len); }; inline static bool ends_with(const std::string &str, const std::string &suffix) { const size_t str_len = str.size(); const size_t suf_len = suffix.size(); if (suf_len > str_len) return false; for (size_t i = 0; i < suf_len; i++) if (str[str_len - suf_len + i] != suffix[i]) return false; return true; } inline static bool starts_with(const std::string &str, const std::string &prefix) { const size_t str_len = str.size(); const size_t pre_len = prefix.size(); if (pre_len > str_len) return false; for (size_t i = 0; i < pre_len; i++) if (str[i] != prefix[i]) return false; return true; } #endif