first commit
This commit is contained in:
commit
8a543a4a5a
13 changed files with 9621 additions and 0 deletions
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
@ -0,0 +1,3 @@
|
|||
merger/obj
|
||||
merger/codingame-editor-sync
|
||||
merger/.clangd
|
31
README.md
Normal file
31
README.md
Normal 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
26
extension/manifest.json
Normal 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
64
extension/sync.js
Normal 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
7
extension/utils.js
Normal 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
42
merger/Makefile
Normal 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
BIN
merger/codingame-ide-sync
Executable file
Binary file not shown.
48
merger/src/File.cpp
Normal file
48
merger/src/File.cpp
Normal 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
28
merger/src/File.h
Normal 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;
|
||||
};
|
5
merger/src/codingame-sync.h
Normal file
5
merger/src/codingame-sync.h
Normal 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
9286
merger/src/httplib.h
Normal file
File diff suppressed because it is too large
Load diff
31
merger/src/main.cpp
Normal file
31
merger/src/main.cpp
Normal 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
50
merger/src/merge.cpp
Normal 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;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue