[Add] Lua syntax highlighting to the code editor.
This commit is contained in:
parent
cb7c5d8a95
commit
420b570902
@ -8,6 +8,7 @@
|
||||
#include "terminal.h"
|
||||
#include "game_state.h"
|
||||
#include "gfx/types.h"
|
||||
#include "ui/editor.h"
|
||||
#include "ui/window_action.h"
|
||||
|
||||
Terminal::Terminal(GameState* game_state)
|
||||
@ -179,7 +180,8 @@ void Terminal::render(const RenderContext& context, int x, int y_screen, int y_g
|
||||
/* Render text view for the input right after prompt. */
|
||||
float input_x_pos = x + padding + (prompt_str.length() * 8.5f); /* Estimate width */
|
||||
float input_width = width - (input_x_pos-x);
|
||||
_input_view->render_text_content(context.ui_renderer, input_x_pos, prompt_line_y, input_width, line_height);
|
||||
SyntaxTheme theme; /* Terminal doesn't need highlighting, just a default theme. */
|
||||
_input_view->render_text_content(context.ui_renderer, theme, input_x_pos, prompt_line_y, input_width, line_height);
|
||||
|
||||
context.ui_renderer->flush_text();
|
||||
|
||||
@ -188,7 +190,7 @@ void Terminal::render(const RenderContext& context, int x, int y_screen, int y_g
|
||||
|
||||
if(context.show_cursor) {
|
||||
context.ui_renderer->begin_shapes();
|
||||
_input_view->render_cursor(context.ui_renderer, input_x_pos, prompt_line_y, input_width, line_height);
|
||||
_input_view->render_cursor(context.ui_renderer, theme, input_x_pos, prompt_line_y, input_width, line_height);
|
||||
context.ui_renderer->flush_shapes();
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
#include "editor.h"
|
||||
#include <memory>
|
||||
|
||||
#include "text_view.h"
|
||||
#include "gfx/types.h"
|
||||
#include "ui/window_action.h"
|
||||
|
||||
@ -59,13 +61,13 @@ void Editor::render(const RenderContext& context, int x, int y_screen, int y_gl,
|
||||
|
||||
/* Pass 2: Main text view. */
|
||||
context.ui_renderer->begin_text();
|
||||
_view->render_text_content(context.ui_renderer, x, content_y, width, content_height);
|
||||
_view->render_text_content(context.ui_renderer, _theme, x, content_y, width, content_height);
|
||||
context.ui_renderer->flush_text();
|
||||
|
||||
/* Pass 3: Editor cursor. */
|
||||
if(context.show_cursor) {
|
||||
context.ui_renderer->begin_shapes();
|
||||
_view->render_cursor(context.ui_renderer, x, content_y, width, content_height);
|
||||
_view->render_cursor(context.ui_renderer, _theme, x, content_y, width, content_height);
|
||||
context.ui_renderer->flush_shapes();
|
||||
}
|
||||
|
||||
|
||||
@ -5,10 +5,20 @@
|
||||
#include "gfx/types.h"
|
||||
#include "i_window_content.h"
|
||||
#include "ui/text_buffer.h"
|
||||
#include "text_view.h"
|
||||
#include "ui/window_action.h"
|
||||
#include "ui/menu_bar.h"
|
||||
|
||||
class TextView;
|
||||
|
||||
struct SyntaxTheme {
|
||||
Color normal = { 1.0f, 1.0f, 1.0f };
|
||||
Color keyword = { 0.8f, 0.6f, 1.0f };
|
||||
Color string = { 1.0f, 0.8f, 0.6f };
|
||||
Color number = { 0.6f, 1.0f, 0.8f };
|
||||
Color comment = { 0.6f, 0.6f, 0.6f };
|
||||
Color line_num = { 0.5f, 0.6f, 0.7f };
|
||||
};
|
||||
|
||||
class Editor : public IWindowContent {
|
||||
public:
|
||||
Editor(void);
|
||||
@ -31,5 +41,6 @@ private:
|
||||
std::unique_ptr<MenuBar> _menu_bar;
|
||||
bool _should_close;
|
||||
WindowAction _pending_action;
|
||||
SyntaxTheme _theme;
|
||||
std::string _filename;
|
||||
};
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include "text_view.h"
|
||||
#include "gfx/types.h"
|
||||
#include "ui/editor.h"
|
||||
#include "ui/text_buffer.h"
|
||||
#include "ui/ui_renderer.h"
|
||||
|
||||
@ -10,7 +11,14 @@ TextView::TextView(TextBuffer* buffer, bool handle_ret, bool show_line_numbers)
|
||||
_buffer(buffer),
|
||||
_scroll_offset(0),
|
||||
_handle_ret(handle_ret),
|
||||
_show_line_numbers(show_line_numbers) {}
|
||||
_show_line_numbers(show_line_numbers) {
|
||||
|
||||
_lua_keywords = {
|
||||
"and", "break", "do", "else", "elseif", "end", "false", "for",
|
||||
"function", "if", "in", "local", "nil", "not", "or", "repeat",
|
||||
"return", "then", "true", "until", "while"
|
||||
};
|
||||
}
|
||||
|
||||
TextView::~TextView(void) {}
|
||||
|
||||
@ -75,11 +83,77 @@ void TextView::scroll(int amount, int content_height) {
|
||||
}
|
||||
}
|
||||
|
||||
void TextView::render_text_content(UIRenderer* ui_renderer, int x, int y, int width, int height) {
|
||||
if(!_buffer) return;
|
||||
std::vector<Token> TextView::_tokenize_line(const std::string& line) {
|
||||
std::vector<Token> tokens;
|
||||
std::string current_token_text;
|
||||
TokenType current_token_type = TokenType::NORMAL;
|
||||
|
||||
const Color text_color = { 1.0f, 1.0f, 1.0f };
|
||||
const Color line_num_color = { 0.5f, 0.6f, 0.7f };
|
||||
for(size_t i = 0; i < line.length(); ++i) {
|
||||
char c = line[i];
|
||||
|
||||
/* Check for comments. */
|
||||
if(i + 1 < line.length() && c == '-' && line[i+1] == '-') {
|
||||
if(!current_token_text.empty()) {
|
||||
tokens.push_back({current_token_type, current_token_text});
|
||||
}
|
||||
tokens.push_back({TokenType::COMMENT, line.substr(i)});
|
||||
return tokens;
|
||||
}
|
||||
|
||||
/* Check for strings. */
|
||||
if(c == '"') {
|
||||
if(!current_token_text.empty()) {
|
||||
tokens.push_back({current_token_type, current_token_text});
|
||||
}
|
||||
size_t end_quote = line.find('"', i+1);
|
||||
if(end_quote == std::string::npos) {
|
||||
tokens.push_back({TokenType::STRING, line.substr(i)});
|
||||
return tokens;
|
||||
}
|
||||
tokens.push_back({TokenType::STRING, line.substr(i, end_quote - i+1)});
|
||||
i = end_quote;
|
||||
current_token_text = "";
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for numbers. */
|
||||
if(isdigit(c) && (current_token_text.empty() || current_token_type == TokenType::NUMBER)) {
|
||||
current_token_type = TokenType::NUMBER;
|
||||
current_token_text += c;
|
||||
} else if(isalnum(c) || c == '_') { /* Keywords and identifiers. */
|
||||
if(current_token_type != TokenType::NORMAL) {
|
||||
tokens.push_back({current_token_type, current_token_text});
|
||||
current_token_text = "";
|
||||
}
|
||||
current_token_type = TokenType::NORMAL;
|
||||
current_token_text += c;
|
||||
} else { /* Delimiters. */
|
||||
if(!current_token_text.empty()) {
|
||||
if(_lua_keywords.count(current_token_text)) {
|
||||
tokens.push_back({TokenType::KEYWORD, current_token_text});
|
||||
} else {
|
||||
tokens.push_back({current_token_type, current_token_text});
|
||||
}
|
||||
current_token_text = "";
|
||||
}
|
||||
tokens.push_back({TokenType::NORMAL, std::string(1, c)});
|
||||
current_token_type = TokenType::NORMAL;
|
||||
}
|
||||
}
|
||||
|
||||
if(!current_token_text.empty()) {
|
||||
if(_lua_keywords.count(current_token_text)) {
|
||||
tokens.push_back({TokenType::KEYWORD, current_token_text});
|
||||
} else {
|
||||
tokens.push_back({current_token_type, current_token_text});
|
||||
}
|
||||
}
|
||||
return tokens;
|
||||
}
|
||||
|
||||
void TextView::render_text_content(UIRenderer* ui_renderer, const SyntaxTheme& theme,
|
||||
int x, int y, int width, int height) {
|
||||
if(!_buffer) return;
|
||||
|
||||
const float line_height = 20.0f; /* TODO: Get font metrics? */
|
||||
const float padding = 5.0f;
|
||||
@ -102,18 +176,29 @@ void TextView::render_text_content(UIRenderer* ui_renderer, int x, int y, int wi
|
||||
float line_num_text_width =
|
||||
ui_renderer->get_text_renderer()->get_text_width(line_num_str.c_str(), 1.0f);
|
||||
ui_renderer->render_text(line_num_str.c_str(), x+padding+(gutter_width-line_num_text_width-10),
|
||||
current_y+18, line_num_color);
|
||||
current_y+18, theme.line_num);
|
||||
}
|
||||
|
||||
/* Render line content. */
|
||||
std::string line = _buffer->get_line(i);
|
||||
ui_renderer->render_text(line.c_str(), x+padding+gutter_width, current_y+18, text_color);
|
||||
float current_x = x+padding + gutter_width;
|
||||
std::vector<Token> tokens = _tokenize_line(_buffer->get_line(i));
|
||||
for(const auto& token : tokens) {
|
||||
Color color = theme.normal;
|
||||
switch(token.type) {
|
||||
case TokenType::KEYWORD: color = theme.keyword; break;
|
||||
case TokenType::STRING: color = theme.string; break;
|
||||
case TokenType::NUMBER: color = theme.number; break;
|
||||
case TokenType::COMMENT: color = theme.comment; break;
|
||||
default: break;
|
||||
}
|
||||
ui_renderer->render_text(token.text.c_str(), current_x, current_y+18, color);
|
||||
current_x += ui_renderer->get_text_renderer()->get_text_width(token.text.c_str(), 1.0f);
|
||||
}
|
||||
current_y += line_height;
|
||||
}
|
||||
}
|
||||
|
||||
void TextView::render_cursor(UIRenderer* ui_renderer, int x, int y, int width, int height) {
|
||||
const Color text_color = { 1.0f, 1.0f, 1.0f };
|
||||
void TextView::render_cursor(UIRenderer* ui_renderer, const SyntaxTheme& theme,
|
||||
int x, int y, int width, int height) {
|
||||
const float line_height = 20.0f; /* TODO: Get font metrics? */
|
||||
const float padding = 5.0f;
|
||||
const float gutter_width = _show_line_numbers ? 40.0f : 0.0f;
|
||||
@ -124,6 +209,6 @@ void TextView::render_cursor(UIRenderer* ui_renderer, int x, int y, int width, i
|
||||
float cursor_x = x + padding + gutter_width + text_width;
|
||||
float cursor_y = y + (cursor_pos.row - _scroll_offset) * line_height;
|
||||
if(cursor_y >= y && cursor_y < y + height) {
|
||||
ui_renderer->draw_rect(cursor_x, cursor_y, 2, line_height, text_color);
|
||||
ui_renderer->draw_rect(cursor_x, cursor_y, 2, line_height, theme.normal);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL3/SDL_events.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "ui/text_buffer.h"
|
||||
#include "ui/ui_renderer.h"
|
||||
#include "ui/editor.h" /* For SyntaxTheme */
|
||||
|
||||
class TextRenderer;
|
||||
|
||||
enum class TokenType {
|
||||
NORMAL,
|
||||
KEYWORD,
|
||||
STRING,
|
||||
NUMBER,
|
||||
COMMENT
|
||||
};
|
||||
|
||||
struct Token {
|
||||
TokenType type;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
class TextView {
|
||||
public:
|
||||
TextView(TextBuffer* buffer, bool handle_ret, bool show_line_numbers);
|
||||
@ -14,12 +31,17 @@ public:
|
||||
|
||||
bool handle_event(SDL_Event* event);
|
||||
void scroll(int amount, int content_height);
|
||||
void render_text_content(UIRenderer* ui_renderer, int x, int y, int width, int height);
|
||||
void render_cursor(UIRenderer* ui_renderer, int x, int y, int width, int height);
|
||||
void render_text_content(UIRenderer* ui_renderer, const SyntaxTheme& theme,
|
||||
int x, int y, int width, int height);
|
||||
void render_cursor(UIRenderer* ui_renderer, const SyntaxTheme& theme,
|
||||
int x, int y, int width, int height);
|
||||
|
||||
private:
|
||||
std::vector<Token> _tokenize_line(const std::string& line);
|
||||
|
||||
TextBuffer* _buffer;
|
||||
int _scroll_offset;
|
||||
bool _handle_ret;
|
||||
bool _show_line_numbers;
|
||||
std::unordered_set<std::string> _lua_keywords;
|
||||
};
|
||||
|
||||
Loading…
Reference in New Issue
Block a user