first commit

This commit is contained in:
Vyn 2024-08-04 21:00:15 +02:00
commit 8a543a4a5a
13 changed files with 9621 additions and 0 deletions

3
.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
merger/obj
merger/codingame-editor-sync
merger/.clangd

31
README.md Normal file
View file

@ -0,0 +1,31 @@
# codingame-ide-sync
This repo contains 2 parts:
- A **merger program**: Provides an HTTP route that merge all your code and return the result.
- A **firefox extension**: Call the merger's HTTP route and directly update your codingame's IDE.
Because of the way it works (for now), it **only works with C++ code, and only if you don't split implementation (.cpp) from definition (.h)**.
Basically you want to use one `main.cpp` file that includes other `something.hpp` files.
### Build the Merger
Requires `gcc` and `make`
```sh
cd merger
make
```
### Load firefox extension
1. Write `about:debugging` in your firefox url bar.
2. Go in the "This Firefox" tab on the left
3. Click on "Load Temporary Add-On..." and select the `manifest.json` in the `extension` directory.
### Usage
1. Run the merger with `./codingame-ide-sync ./path/to/main.cpp`.
2. Ensure the firefox extension is loaded.
You should see red borders on the page and an update status above the codingame's ide (you might need to reload the page).

26
extension/manifest.json Normal file
View file

@ -0,0 +1,26 @@
{
"manifest_version": 2,
"name": "Codingame-ide-sync",
"version": "1.0",
"description": "Merge all your code from your local dev environment in 1 file and copy it automatically into the codingame IDE",
"icons": {},
"content_scripts": [
{
"matches": [
"https://www.codingame.com/ide/puzzle/*",
"https://www.codingame.com/ide/challenge/*"
],
"js": ["utils.js", "sync.js"]
}
],
"permissions": [
"<all_urls>",
"tabs",
"activeTab"
]
}

64
extension/sync.js Normal file
View file

@ -0,0 +1,64 @@
document.body.style.border = "1px solid red"; // Just to see if it's active
const PORT = "8080"
let textInfo = null
async function lifeCheck() {
const res = await fetch("http://localhost:${PORT}/ping")
return res.status === 0
}
async function fetchCode() {
try {
const res = await fetch(`http://localhost:${PORT}/code`)
if (res.status !== 200) {
log("Fetching code failed")
return null
}
const code = await res.text();
return code;
} catch (e) {
log("Fetching code failed")
return null
}
}
const updateCode = async (e) => {
debug("Fetching code")
const code = await fetchCode()
if (!code) {
textInfo.textContent = `[CG-Sync] Update failed`
return
}
textInfo.textContent = `[CG-Sync] Last update : ${new Date().toLocaleTimeString()}`
let data = {status: "updateCode", code}
data = cloneInto(data, window);
const ev = new CustomEvent('ExternalEditorToIDE', { detail: data });
window.document.dispatchEvent(ev);
}
window.document.addEventListener('focus', updateCode);
log("Script started")
// We wait before injecting cg-sync state elements
setTimeout(async () => {
log("Loading elements in page")
textInfo = document.createElement("button")
textInfo.textContent = `[CG-Sync] Waiting for first sync...`
textInfo.style = "color: rgb(0, 255, 255)"
textInfo.onclick = () => updateCode()
const actionRowDocument = window.document.getElementsByClassName("code-header")
actionRowDocument[0].children[0].after(textInfo)
updateCode()
}, 3000)

7
extension/utils.js Normal file
View file

@ -0,0 +1,7 @@
function log(data) {
console.log(`[cg-sync] ${data}`)
}
function debug(data) {
console.debug(`[cg-sync] ${data}`)
}

42
merger/Makefile Normal file
View file

@ -0,0 +1,42 @@
COMPILER = g++
NAME = codingame-ide-sync
FLAGS = -std=c++20 -Wall -Werror -Wextra
#ex: INCLUDES = -I libs/FTXUI/include -I libs/json-parser/include
INCLUDES=
SOURCES = \
main \
File \
merge \
SRC_WITHOUT_SUFFIX = $(addprefix src/, $(SOURCES))
SRC = $(addsuffix .cpp, $(SRC_WITHOUT_SUFFIX))
OBJ = $(SRC:src/%.cpp=obj/%.o)
all: $(NAME)
$(NAME): dir $(OBJ)
${COMPILER} $(FLAGS) $(OBJ) -o $(NAME)
dir:
if [ ! -d "obj" ]; then mkdir obj; fi
obj/%.o: src/%.cpp src/*.h
${COMPILER} $(FLAGS) $(INCLUDES) -c $< -o $@
clean:
rm -f $(OBJ)
fclean: clean
rm -f $(NAME)
re: fclean all
run: all
./${NAME}

BIN
merger/codingame-ide-sync Executable file

Binary file not shown.

48
merger/src/File.cpp Normal file
View file

@ -0,0 +1,48 @@
#include "File.h"
#include <iostream>
#include <iterator>
#include <optional>
#include <ostream>
#include <regex>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
File::File(std::string filePath) : filePath(filePath) {
content = "";
std::ifstream file(filePath);
if (!file.is_open()) {
throw new std::runtime_error("Can't open file " + filePath);
}
std::string line;
while(std::getline(file, line)) {
content += line + '\n';
lines.push_back(line);
}
file.close();
}
std::string File::getContent() {
return content;
}
std::string File::getName() const {
auto lastSlashIndex = filePath.rfind('/');
if (lastSlashIndex == std::string::npos) {
return filePath;
}
return filePath.substr(lastSlashIndex + 1);
}
const std::vector<std::string>& File::getLines() const {
return lines;
}
std::string File::getDirectoryPath() const {
auto lastSlashIndex = filePath.rfind('/');
if (lastSlashIndex == std::string::npos) {
return ".";
}
return filePath.substr(0, lastSlashIndex);
}

28
merger/src/File.h Normal file
View file

@ -0,0 +1,28 @@
#pragma once
#include <cstddef>
#include <exception>
#include <fstream>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
class File {
public:
File(std::string filePath);
std::string getName() const;
std::string getContent();
const std::vector<std::string>& getLines() const;
std::string getDirectoryPath() const;
private:
bool contentAlreadyRead = false;
std::string filePath;
std::string content;
std::vector<std::string> lines;
};

View file

@ -0,0 +1,5 @@
#include <optional>
#include <string>
std::string merge(const std::string& entryFilePath);
std::optional<std::string> extractFilePathFromInclude(const std::string& line);

9286
merger/src/httplib.h Normal file

File diff suppressed because it is too large Load diff

31
merger/src/main.cpp Normal file
View file

@ -0,0 +1,31 @@
#include "codingame-sync.h"
#include "httplib.h"
#include <format>
#include <iostream>
#include <ostream>
#include <string>
int main(int argc, char** argv) {
if (argc != 2) {
std::cout << "You must provide the entry file as first argument" << std::endl;
return 1;
}
std::cout << "Merger starter successfully" << std::endl;
httplib::Server server;
server.Get("/code", [&](const httplib::Request &, httplib::Response &res) {
try {
auto mergeResult = merge(argv[1]);
std::cout << "Code merged successfully" << std::endl;
res.set_content(mergeResult, "text/plain");
} catch (std::exception* e) {
std::cout << "Exception : " << e->what() << std::endl;
res.status = 500;
}
});
server.listen("0.0.0.0", 8080);
return 0;
}

50
merger/src/merge.cpp Normal file
View file

@ -0,0 +1,50 @@
#include "codingame-sync.h"
#include "File.h"
#include <regex>
void copyFileTo(std::string* to, const File& file, std::map<std::string, bool>* includedFiles) {
(*to) += "/** cg-sync -> " + file.getName() + " **/\n\n";
for (auto& line : file.getLines()) {
auto includeFilePath = extractFilePathFromInclude(line);
if (includeFilePath.has_value()) {
if ((*includedFiles).find(includeFilePath.value()) != (*includedFiles).end())
continue;
(*includedFiles)[includeFilePath.value()] = true;
std::string relativeIncludedFilePath = file.getDirectoryPath();
File includedFile(relativeIncludedFilePath + "/" + includeFilePath.value());
copyFileTo(to, includedFile, includedFiles);
} else {
(*to) += line + "\n";
}
}
}
std::string merge(const std::string& entryFilePath) {
std::string mergedFileContent = "";
std::map<std::string, bool> includedFiles;
std::string currentFilePath = entryFilePath;
File file(currentFilePath);
std::string currentFileContent = file.getContent();
copyFileTo(&mergedFileContent, file, &includedFiles);
std::ofstream newFile("merge.cpp");
if (!newFile.is_open()) {
newFile << mergedFileContent;
}
newFile.close();
return mergedFileContent;
}
// Returns a string_view from the line provided as argument
std::optional<std::string> extractFilePathFromInclude(const std::string& line) {
std::smatch res;
std::regex re("#include \"(.+)\"");
std::regex_match(line, res, re);
if (res.size() == 2) {
return std::make_optional(res.str(1));
}
return std::nullopt;
}