diff --git a/.github/workflows/cpp.yml b/.github/workflows/cpp.yml index 9af8448..bf2e3e7 100644 --- a/.github/workflows/cpp.yml +++ b/.github/workflows/cpp.yml @@ -39,7 +39,7 @@ jobs: - name: Set up C++ environment (macOS) if: matrix.os == 'macos-latest' run: | - brew install make cmake yaml-cpp + brew install yaml-cpp echo 'export PATH="/usr/local/opt/gcc/bin:$PATH"' >> ~/.bash_profile source ~/.bash_profile g++ --version diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 3620365..48ed209 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,7 +36,7 @@ jobs: - name: Set up C++ environment (macOS) if: matrix.os == 'macos-latest' run: | - brew install make cmake yaml-cpp + brew install yaml-cpp echo 'export PATH="/usr/local/opt/gcc/bin:$PATH"' >> ~/.bash_profile source ~/.bash_profile g++ --version diff --git a/CMakeLists.txt b/CMakeLists.txt index 389bf77..5a3e04a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,4 +32,3 @@ find_package(yaml-cpp REQUIRED CONFIG) # Add subdirectories add_subdirectory(src) add_subdirectory(tests) - diff --git a/Makefile b/Makefile index 329778d..c570be4 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,4 @@ +DEBUG ?= OFF PROJECT = CodeAstra BUILD_DIR = $(PWD)/build @@ -10,7 +11,7 @@ all: install build: @echo "Building $(PROJECT)..." @mkdir -p $(BUILD_DIR) - @cd $(BUILD_DIR) && cmake $(CMAKE_OPTIONS) + @cd $(BUILD_DIR) && cmake $(CMAKE_OPTIONS) -DDEBUG=$(DEBUG) clean: @echo "Cleaning the build directory..." @@ -36,4 +37,4 @@ test: build_tests run: @echo "Running $(PROJECT)..." - @./build/bin/$(PROJECT) \ No newline at end of file + @./build/bin/$(PROJECT) diff --git a/include/SyntaxManager.h b/include/SyntaxManager.h index 83c7234..60ef2a7 100644 --- a/include/SyntaxManager.h +++ b/include/SyntaxManager.h @@ -28,6 +28,7 @@ class SyntaxManager * @return A unique pointer to the appropriate syntax highlighter, or nullptr if not available. */ static std::unique_ptr createSyntaxHighlighter(const QString &extension, QTextDocument *doc); + static void initializeUserSyntaxConfig(); private: static std::unique_ptr createHighlighter(QTextDocument *doc, const std::vector &config, const QString &extension); diff --git a/resources.qrc b/resources.qrc index d6f8c6e..f622663 100644 --- a/resources.qrc +++ b/resources.qrc @@ -2,5 +2,11 @@ resources/app_icon.png resources/themes/dark.qss + resources/syntax/cxx.syntax.yaml + resources/syntax/python.syntax.yaml + resources/syntax/go.syntax.yaml + resources/syntax/tsx.syntax.yaml + resources/syntax/yaml.syntax.yaml + resources/syntax/markdown.syntax.yaml \ No newline at end of file diff --git a/config/cxx.syntax.yaml b/resources/syntax/cxx.syntax.yaml similarity index 93% rename from config/cxx.syntax.yaml rename to resources/syntax/cxx.syntax.yaml index cf011f8..62087a3 100644 --- a/config/cxx.syntax.yaml +++ b/resources/syntax/cxx.syntax.yaml @@ -22,7 +22,7 @@ keywords: color: "#FFDE00" # Yellow number: - - regex: "\\b\\d+\\b" + - regex: "\\b(0b[01]+|0o[0-7]+|0x[0-9a-fA-F]+|\\d+(\\.\\d+)?)\\b" color: "#0000FF" # Blue parenthesis: diff --git a/config/go.syntax.yaml b/resources/syntax/go.syntax.yaml similarity index 76% rename from config/go.syntax.yaml rename to resources/syntax/go.syntax.yaml index c596269..b828a7e 100644 --- a/config/go.syntax.yaml +++ b/resources/syntax/go.syntax.yaml @@ -10,5 +10,5 @@ keywords: color: "#E37100" # Orange number: - - regex: "\\b\\d+\\b" + - regex: "\\b(0b[01]+|0o[0-7]+|0x[0-9a-fA-F]+|\\d+(\\.\\d+)?)\\b" color: "#0000FF" # Blue diff --git a/config/markdown.syntax.yaml b/resources/syntax/markdown.syntax.yaml similarity index 100% rename from config/markdown.syntax.yaml rename to resources/syntax/markdown.syntax.yaml diff --git a/config/python.syntax.yaml b/resources/syntax/python.syntax.yaml similarity index 86% rename from config/python.syntax.yaml rename to resources/syntax/python.syntax.yaml index 7c3fc58..fc961d9 100644 --- a/config/python.syntax.yaml +++ b/resources/syntax/python.syntax.yaml @@ -20,5 +20,5 @@ keywords: color: "#E37100" # Orange number: - - regex: "\\b\\d+\\b" + - regex: "\\b(0b[01]+|0o[0-7]+|0x[0-9a-fA-F]+|\\d+(\\.\\d+)?)\\b" color: "#0000FF" # Blue diff --git a/resources/syntax/tsx.syntax.yaml b/resources/syntax/tsx.syntax.yaml new file mode 100644 index 0000000..3fbcf07 --- /dev/null +++ b/resources/syntax/tsx.syntax.yaml @@ -0,0 +1,36 @@ +extensions: [tsx, ts, js] + +keywords: + comment: + - regex: "//.*" + color: "#6A9955" # Green + - regex: "/\\*[^*]*\\*+(?:[^/*][^*]*\\*+)*/" + color: "#6A9955" # Green + + string: + - regex: "'(?:\\\\.|[^'\\\\])*'" + color: "#CE9178" # Reddish + - regex: "\"(?:\\\\.|[^\"\\\\])*\"" + color: "#CE9178" # Reddish + - regex: "`(?:\\\\.|[^`\\\\])*`" + color: "#DCDCAA" # Yellow for template literals + + number: + - regex: "\\b(0b[01]+|0o[0-7]+|0x[0-9a-fA-F]+|\\d+(\\.\\d+)?)\\b" + color: "#B5CEA8" # Light green + + keyword: + - regex: "\\b(?:let|const|var|function|class|interface|extends|implements|public|private|protected|static|new|return|if|else|for|while|do|switch|case|break|continue|try|catch|finally|throw|typeof|instanceof|in|of|void|async|await|import|from|export|default|as|enum|type|declare|namespace|abstract|readonly|get|set|yield|with|super|this)\\b" + color: "#569CD6" # Blue + + type: + - regex: "\\b(?:string|number|boolean|any|unknown|never|void|object|undefined|null|bigint|symbol)\\b" + color: "#4EC9B0" # Cyan + + boolean: + - regex: "\\b(?:true|false)\\b" + color: "#569CD6" # Blue + + operator: + - regex: "\\+|-|\\*|\\/|=|==|===|!=|!==|<|>|<=|>=|\\?|:|\\.|,|\\||&|\\^|~|!" + color: "#D4D4D4" # Light gray diff --git a/config/yaml.syntax.yaml b/resources/syntax/yaml.syntax.yaml similarity index 100% rename from config/yaml.syntax.yaml rename to resources/syntax/yaml.syntax.yaml diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 04d9147..00ce27a 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -43,16 +43,16 @@ qt_add_resources(APP_RESOURCES ${CMAKE_SOURCE_DIR}/resources.qrc) target_sources(${EXECUTABLE_NAME} PRIVATE ${APP_RESOURCES}) # OS-specific flags +option(DEBUG "Enable debug mode" OFF) + if(MSVC) target_compile_options(${EXECUTABLE_NAME} PRIVATE /W4 /WX /analyze /sdl /guard:cf) elseif(APPLE OR UNIX) target_compile_options(${EXECUTABLE_NAME} PRIVATE -Wall -Wextra -Wpedantic -Werror -Wshadow -Wconversion -Wsign-conversion -fsanitize=address,undefined -fstack-protector) target_link_options(${EXECUTABLE_NAME} PRIVATE -fsanitize=address,undefined) -endif() -# Copy config files -file(GLOB YAML_FILES "${CMAKE_SOURCE_DIR}/config/*.yaml") -file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/config) -foreach(YAML_FILE ${YAML_FILES}) - configure_file(${YAML_FILE} ${CMAKE_BINARY_DIR}/config/ COPYONLY) -endforeach() + # Add the preprocessor symbol only when DEBUG is ON + if (DEBUG) + target_compile_definitions(${EXECUTABLE_NAME} PRIVATE DEBUG=1) + endif() +endif() diff --git a/src/SyntaxManager.cpp b/src/SyntaxManager.cpp index 9a8bf8f..195c005 100644 --- a/src/SyntaxManager.cpp +++ b/src/SyntaxManager.cpp @@ -5,44 +5,93 @@ #include #include -std::unique_ptr SyntaxManager::createSyntaxHighlighter(const QString &extension, QTextDocument *doc) +void SyntaxManager::initializeUserSyntaxConfig() { - QString configPath = qgetenv("CONFIG_DIR"); - if (configPath.isEmpty()) + QString userSyntaxDir = QDir::homePath() + "/.config/codeastra/syntax"; + QDir dir(userSyntaxDir); + + if (!dir.exists()) { - configPath = "config"; - } + qDebug() << "[Setup] First run detected. Creating syntax config directory:" << userSyntaxDir; + + if (!dir.mkpath(".")) + { + qWarning() << "[Setup] Failed to create user syntax config directory:" << userSyntaxDir; + return; + } + + // List of default syntax files to copy + QDir defaultDir(":/resources/syntax/"); + QStringList defaultSyntaxFiles = defaultDir.entryList({"*.yaml", "*.yml"}, QDir::Files); + + for (const QString &fileName : defaultSyntaxFiles) + { + QString resourcePath = ":/resources/syntax/" + fileName; + QString destPath = userSyntaxDir + "/" + fileName; - QDir syntaxDir(configPath); + QFile resFile(resourcePath); + if (resFile.copy(destPath)) + { + qDebug() << "[Setup] Copied default config:" << fileName; + } + else + { + qWarning() << "[Setup] Failed to copy:" << resourcePath << "to" << destPath; + } + } + } + else + { + qDebug() << "[Setup] User syntax directory already exists. Skipping first-run config."; + } +} - QStringList yamlFiles = syntaxDir.entryList({"*.yaml", "*.yml"}, QDir::Files); - qDebug() << "Directory being scanned: " << syntaxDir.absolutePath(); +std::unique_ptr SyntaxManager::createSyntaxHighlighter(const QString &extension, QTextDocument *doc) +{ + QString baseDir; - if (syntaxDir.exists()) + if (qEnvironmentVariableIsSet("CONFIG_DIR")) { - qDebug() << "Directory exists."; + baseDir = qEnvironmentVariable("CONFIG_DIR"); } else { - qDebug() << "Directory does not exist."; + QString userSyntaxDir = QDir::homePath() + "/.config/codeastra/syntax"; + if (QDir(userSyntaxDir).exists()) + { + baseDir = userSyntaxDir; + } + else + { + baseDir = "config"; + } } +#ifdef DEBUG + qDebug() << "[SyntaxManager] Using config directory:" << baseDir; +#endif + + QDir syntaxDir(baseDir); + QStringList yamlFilePath = syntaxDir.entryList({"*.yaml", "*.yml"}, QDir::Files); + std::vector config; // Iterate over all YAML files and store their contents as separate nodes - for (const QString &fileName : yamlFiles) + for (const QString &fileName : yamlFilePath) { QFile file(syntaxDir.filePath(fileName)); if (file.open(QIODevice::ReadOnly | QIODevice::Text)) { YAML::Node fileConfig = YAML::Load(file.readAll().toStdString()); file.close(); - qDebug() << "Loaded YAML from: " << file.fileName(); - +#ifdef DEBUG + qDebug() << "[SyntaxManager] Loaded config for extension:" << extension << "from:" << file.fileName(); +#endif config.push_back(fileConfig); } else { - qDebug() << "Failed to open file: " << file.fileName(); + qWarning() << "[SyntaxManager] Failed to open syntax config for extension:" << extension << "at:" << yamlFilePath; + return nullptr; } } @@ -51,7 +100,11 @@ std::unique_ptr SyntaxManager::createSyntaxHighlighter(const std::unique_ptr SyntaxManager::createHighlighter(QTextDocument *doc, const std::vector &config, const QString &extension) { - qDebug() << "Creating highlighter for extension:" << extension; + +#ifdef DEBUG + qDebug() << "[SyntaxManager] Creating highlighter for extension:" << extension; +#endif + for (const auto &node : config) { if (node["extensions"]) @@ -67,10 +120,11 @@ std::unique_ptr SyntaxManager::createHighlighter(QTextDocume } else { - qDebug() << "No extensions key in YAML config."; + qDebug() << "[SyntaxManager] No extensions key in YAML config."; } } - - qDebug() << "No matching highlighter found for extension:" << extension; +#ifdef DEBUG + qDebug() << "[SyntaxManager] No matching highlighter found for extension:" << extension; +#endif return nullptr; } diff --git a/src/main.cpp b/src/main.cpp index 4a01dc5..a64dd5a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,4 +1,5 @@ #include "MainWindow.h" +#include "SyntaxManager.h" #include #include @@ -34,6 +35,7 @@ QIcon createRoundIcon(const QString &iconPath) QFont setFont() { + SyntaxManager::initializeUserSyntaxConfig(); QStringList preferreFontFamilies = {"Monaco", "Menlo", "Consolas", "Courier New", "Monospace"}; QStringList availableFamilies = QFontDatabase::families();