Add indentation engine and other fixes
This commit is contained in:
@@ -14,6 +14,7 @@ struct CompletionItem {
|
||||
std::optional<std::string> documentation;
|
||||
bool is_markup = false;
|
||||
bool deprecated = false;
|
||||
bool asis = true;
|
||||
std::string sort;
|
||||
std::string filter;
|
||||
bool snippet = false;
|
||||
@@ -37,6 +38,7 @@ struct CompletionSession {
|
||||
HoverBox hover;
|
||||
uint32_t doc = UINT32_MAX;
|
||||
std::atomic<bool> hover_dirty = false;
|
||||
int version;
|
||||
|
||||
CompletionSession() : box(this) {}
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define EDITOR_H
|
||||
|
||||
#include "editor/completions.h"
|
||||
#include "editor/indents.h"
|
||||
#include "editor/spans.h"
|
||||
#include "io/knot.h"
|
||||
#include "io/sysio.h"
|
||||
@@ -52,8 +53,9 @@ struct Editor {
|
||||
HoverBox hover;
|
||||
bool diagnostics_active;
|
||||
DiagnosticBox diagnostics;
|
||||
int lsp_version = 1;
|
||||
std::atomic<int> lsp_version = 1;
|
||||
CompletionSession completion;
|
||||
IndentationEngine indents;
|
||||
};
|
||||
|
||||
Editor *new_editor(const char *filename_arg, Coord position, Coord size);
|
||||
@@ -72,8 +74,6 @@ void cursor_right(Editor *editor, uint32_t number);
|
||||
void scroll_up(Editor *editor, int32_t number);
|
||||
void scroll_down(Editor *editor, uint32_t number);
|
||||
void ensure_cursor(Editor *editor);
|
||||
void indent_line(Editor *editor, uint32_t row);
|
||||
void dedent_line(Editor *editor, uint32_t row);
|
||||
void ensure_scroll(Editor *editor);
|
||||
void handle_editor_event(Editor *editor, KeyEvent event);
|
||||
void edit_erase(Editor *editor, Coord pos, int64_t len);
|
||||
@@ -93,9 +93,6 @@ void word_boundaries_exclusive(Editor *editor, Coord coord, uint32_t *prev_col,
|
||||
std::vector<Fold>::iterator find_fold_iter(Editor *editor, uint32_t line);
|
||||
bool add_fold(Editor *editor, uint32_t start, uint32_t end);
|
||||
bool remove_fold(Editor *editor, uint32_t line);
|
||||
uint32_t leading_indent(const char *line, uint32_t len);
|
||||
uint32_t get_indent(Editor *editor, Coord cursor);
|
||||
bool closing_after_cursor(const char *line, uint32_t len, uint32_t col);
|
||||
void editor_lsp_handle(Editor *editor, json msg);
|
||||
void apply_lsp_edits(Editor *editor, std::vector<TextEdit> edits, bool move);
|
||||
void completion_resolve_doc(Editor *editor);
|
||||
|
||||
152
include/editor/indents.h
Normal file
152
include/editor/indents.h
Normal file
@@ -0,0 +1,152 @@
|
||||
#ifndef EDITOR_INDENTS_H
|
||||
#define EDITOR_INDENTS_H
|
||||
|
||||
#include "utils/utils.h"
|
||||
|
||||
static const std::unordered_map<std::string, uint8_t> 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<std::string, const std::vector<std::string>>
|
||||
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<std::string, const std::vector<std::string>>
|
||||
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<std::string, const std::vector<std::string>>
|
||||
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<std::string, const std::vector<std::string>>
|
||||
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, 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:
|
||||
// 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
|
||||
Reference in New Issue
Block a user