Add indentation engine and other fixes
This commit is contained in:
@@ -88,6 +88,7 @@ static const std::unordered_map<uint8_t, LSP> kLsps = {
|
||||
"--stdio",
|
||||
nullptr,
|
||||
}}},
|
||||
#define LUA_LS 12
|
||||
{12,
|
||||
{"lua-language-server",
|
||||
{
|
||||
@@ -204,7 +205,6 @@ static const std::unordered_map<std::string, Language> kLanguages = {
|
||||
{"query", {"query", LANG(query), 0, 0x7E57C2, " "}},
|
||||
{"regex", {"regex", LANG(regex), 0, 0x9E9E9E, ".*"}},
|
||||
{"ini", {"ini", LANG(ini), 0, 0x6d8086, " "}},
|
||||
|
||||
};
|
||||
|
||||
static const std::unordered_map<std::string, std::string> kExtToLang = {
|
||||
@@ -257,6 +257,7 @@ static const std::unordered_map<std::string, std::string> kExtToLang = {
|
||||
{"toml", "toml"},
|
||||
{"yaml", "yaml"},
|
||||
{"yml", "yaml"},
|
||||
{"clangd", "yaml"},
|
||||
};
|
||||
|
||||
static const std::unordered_map<std::string, std::string> kMimeToLang = {
|
||||
|
||||
@@ -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
|
||||
@@ -67,7 +67,7 @@ static json client_capabilities = {
|
||||
{"resolveSupport", {{"properties", {"documentation"}}}},
|
||||
{"insertReplaceSupport", true},
|
||||
{"labelDetailsSupport", true},
|
||||
{"insertTextModeSupport", {{"valueSet", {1}}}},
|
||||
{"insertTextModeSupport", {{"valueSet", {1, 2}}}},
|
||||
{"deprecatedSupport", true}}},
|
||||
{"completionItemKind",
|
||||
{{"valueSet", {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
|
||||
|
||||
@@ -61,6 +61,7 @@ struct Match {
|
||||
|
||||
#define MAX(a, b) ((a) > (b) ? (a) : (b))
|
||||
#define MIN(a, b) ((a) < (b) ? (a) : (b))
|
||||
#define ABS(x) ((x) < 0 ? -(x) : (x))
|
||||
#define UNUSED(x) (void)(x)
|
||||
#define USING(x) UNUSED(sizeof(x))
|
||||
|
||||
@@ -69,14 +70,16 @@ std::string percent_encode(const std::string &s);
|
||||
std::string percent_decode(const std::string &s);
|
||||
uint32_t count_clusters(const char *line, size_t len, size_t from, size_t to);
|
||||
std::string trim(const std::string &s);
|
||||
std::string substitute_fence(const std::string &documentation,
|
||||
const std::string &lang);
|
||||
|
||||
int display_width(const char *str, size_t len);
|
||||
uint32_t get_visual_col_from_bytes(const char *line, uint32_t len,
|
||||
uint32_t byte_limit);
|
||||
uint32_t get_bytes_from_visual_col(const char *line, uint32_t len,
|
||||
uint32_t target_visual_col);
|
||||
int utf8_byte_offset_to_utf16(const char *s, size_t byte_pos);
|
||||
size_t utf16_offset_to_utf8(const char *s, int utf16_pos);
|
||||
uint32_t utf8_byte_offset_to_utf16(const char *s, uint32_t byte_pos);
|
||||
uint32_t utf16_offset_to_utf8(const char *s, uint32_t utf16_pos);
|
||||
|
||||
void log(const char *fmt, ...);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user