Format c++ files using clang-format

Add git pre-commit hooks to help with automatic formatting.
This commit is contained in:
Leroy Hopson 2021-06-06 19:58:50 +07:00 committed by Leroy Hopson
parent 99989d19e4
commit 6e455738b8
10 changed files with 981 additions and 760 deletions

View file

@ -3,22 +3,20 @@
#include "pseudoterminal.h" #include "pseudoterminal.h"
#endif #endif
extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
{ godot::Godot::gdnative_init(o);
godot::Godot::gdnative_init(o);
} }
extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) extern "C" void GDN_EXPORT
{ godot_gdnative_terminate(godot_gdnative_terminate_options *o) {
godot::Godot::gdnative_terminate(o); godot::Godot::gdnative_terminate(o);
} }
extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) {
{ godot::Godot::nativescript_init(handle);
godot::Godot::nativescript_init(handle);
godot::register_tool_class<godot::Terminal>(); godot::register_tool_class<godot::Terminal>();
#if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX) #if defined(PLATFORM_LINUX) || defined(PLATFORM_OSX)
godot::register_class<godot::Pseudoterminal>(); godot::register_class<godot::Pseudoterminal>();
#endif #endif
} }

View file

@ -9,168 +9,147 @@
#include <pty.h> #include <pty.h>
#endif #endif
#if defined(PLATFORM_OSX) #if defined(PLATFORM_OSX)
#include <util.h>
#include <sys/ioctl.h> #include <sys/ioctl.h>
#include <util.h>
#endif #endif
using namespace godot; using namespace godot;
void Pseudoterminal::_register_methods() void Pseudoterminal::_register_methods() {
{
register_method("_init", &Pseudoterminal::_init); register_method("_init", &Pseudoterminal::_init);
register_method("_ready", &Pseudoterminal::_ready); register_method("_ready", &Pseudoterminal::_ready);
register_method("write", &Pseudoterminal::write); register_method("write", &Pseudoterminal::write);
register_method("resize", &Pseudoterminal::resize); register_method("resize", &Pseudoterminal::resize);
register_signal<Pseudoterminal>((char *)"data_sent", "data", GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY); register_signal<Pseudoterminal>((char *)"data_sent", "data",
register_signal<Pseudoterminal>((char *)"exited", "status", GODOT_VARIANT_TYPE_INT); GODOT_VARIANT_TYPE_POOL_BYTE_ARRAY);
register_signal<Pseudoterminal>((char *)"exited", "status",
GODOT_VARIANT_TYPE_INT);
} }
Pseudoterminal::Pseudoterminal() Pseudoterminal::Pseudoterminal() {}
{
Pseudoterminal::~Pseudoterminal() { pty_thread.join(); }
void Pseudoterminal::_init() {
bytes_to_write = 0;
pty_thread = std::thread(&Pseudoterminal::process_pty, this);
} }
Pseudoterminal::~Pseudoterminal() void Pseudoterminal::process_pty() {
{ int fd;
pty_thread.join(); char *name;
} int status;
void Pseudoterminal::_init() should_process_pty = true;
{
bytes_to_write = 0;
pty_thread = std::thread(&Pseudoterminal::process_pty, this);
}
void Pseudoterminal::process_pty() struct termios termios = {};
{ termios.c_iflag = IGNPAR | ICRNL;
int fd; termios.c_oflag = 0;
char *name; termios.c_cflag = B38400 | CRTSCTS | CS8 | CLOCAL | CREAD;
int status; termios.c_lflag = ICANON;
termios.c_cc[VMIN] = 1;
termios.c_cc[VTIME] = 0;
should_process_pty = true; pid_t pty_pid = forkpty(&fd, NULL, NULL, NULL);
struct termios termios = {}; if (pty_pid == -1) {
termios.c_iflag = IGNPAR | ICRNL; ERR_PRINT(
termios.c_oflag = 0; String("Error forking pty: {0}").format(Array::make(strerror(errno))));
termios.c_cflag = B38400 | CRTSCTS | CS8 | CLOCAL | CREAD; should_process_pty = false;
termios.c_lflag = ICANON; return;
termios.c_cc[VMIN] = 1; } else if (pty_pid == 0) {
termios.c_cc[VTIME] = 0; /* Child */
pid_t pty_pid = forkpty(&fd, NULL, NULL, NULL); char termenv[11] = {"TERM=xterm"};
putenv(termenv);
if (pty_pid == -1) char colortermenv[20] = {"COLORTERM=truecolor"};
{ putenv(colortermenv);
ERR_PRINT(String("Error forking pty: {0}").format(Array::make(strerror(errno))));
should_process_pty = false;
return;
}
else if (pty_pid == 0)
{
/* Child */
char termenv[11] = {"TERM=xterm"}; char *shell = getenv("SHELL");
putenv(termenv); char *argv[] = {basename(shell), NULL};
execvp(shell, argv);
} else {
Vector2 zero = Vector2(0, 0);
char colortermenv[20] = {"COLORTERM=truecolor"}; /* Parent */
putenv(colortermenv); while (1) {
{
std::lock_guard<std::mutex> guard(size_mutex);
if (size != zero) {
struct winsize ws;
memset(&ws, 0, sizeof(ws));
ws.ws_col = size.x;
ws.ws_row = size.y;
char *shell = getenv("SHELL"); ioctl(fd, TIOCSWINSZ, &ws);
char *argv[] = { basename(shell), NULL };
execvp(shell, argv);
}
else
{
Vector2 zero = Vector2(0, 0);
/* Parent */
while (1)
{
{
std::lock_guard<std::mutex> guard(size_mutex);
if (size != zero)
{
struct winsize ws;
memset(&ws, 0, sizeof(ws));
ws.ws_col = size.x;
ws.ws_row = size.y;
ioctl(fd, TIOCSWINSZ, &ws);
}
}
if (waitpid(pty_pid, &status, WNOHANG))
{
emit_signal("exited", status);
return;
}
int ready = -1;
fd_set read_fds;
fd_set write_fds;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
FD_SET(fd, &write_fds);
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
ready = select(fd + 1, &read_fds, &write_fds, NULL, &timeout);
if (ready > 0)
{
if (FD_ISSET(fd, &write_fds))
{
std::lock_guard<std::mutex> guard(write_buffer_mutex);
if (bytes_to_write > 0)
{
::write(fd, write_buffer, bytes_to_write);
bytes_to_write = 0;
}
}
if (FD_ISSET(fd, &read_fds))
{
std::lock_guard<std::mutex> guard(read_buffer_mutex);
int ret;
int bytes_read = 0;
bytes_read = read(fd, read_buffer, MAX_READ_BUFFER_LENGTH);
// TODO: handle error (-1)
if (bytes_read <= 0)
continue;
PoolByteArray data = PoolByteArray();
data.resize(bytes_read);
memcpy(data.write().ptr(), read_buffer, bytes_read);
emit_signal("data_sent", PoolByteArray(data));
}
}
} }
}
if (waitpid(pty_pid, &status, WNOHANG)) {
emit_signal("exited", status);
return;
}
int ready = -1;
fd_set read_fds;
fd_set write_fds;
FD_ZERO(&read_fds);
FD_SET(fd, &read_fds);
FD_SET(fd, &write_fds);
struct timeval timeout;
timeout.tv_sec = 10;
timeout.tv_usec = 0;
ready = select(fd + 1, &read_fds, &write_fds, NULL, &timeout);
if (ready > 0) {
if (FD_ISSET(fd, &write_fds)) {
std::lock_guard<std::mutex> guard(write_buffer_mutex);
if (bytes_to_write > 0) {
::write(fd, write_buffer, bytes_to_write);
bytes_to_write = 0;
}
}
if (FD_ISSET(fd, &read_fds)) {
std::lock_guard<std::mutex> guard(read_buffer_mutex);
int ret;
int bytes_read = 0;
bytes_read = read(fd, read_buffer, MAX_READ_BUFFER_LENGTH);
// TODO: handle error (-1)
if (bytes_read <= 0)
continue;
PoolByteArray data = PoolByteArray();
data.resize(bytes_read);
memcpy(data.write().ptr(), read_buffer, bytes_read);
emit_signal("data_sent", PoolByteArray(data));
}
}
} }
}
} }
void Pseudoterminal::_ready() void Pseudoterminal::_ready() {}
{
void Pseudoterminal::write(PoolByteArray data) {
std::lock_guard<std::mutex> guard(write_buffer_mutex);
bytes_to_write = data.size();
memcpy(write_buffer, data.read().ptr(), bytes_to_write);
} }
void Pseudoterminal::write(PoolByteArray data) void Pseudoterminal::resize(Vector2 new_size) {
{ std::lock_guard<std::mutex> guard(size_mutex);
std::lock_guard<std::mutex> guard(write_buffer_mutex); size = new_size;
bytes_to_write = data.size();
memcpy(write_buffer, data.read().ptr(), bytes_to_write);
}
void Pseudoterminal::resize(Vector2 new_size)
{
std::lock_guard<std::mutex> guard(size_mutex);
size = new_size;
} }

View file

@ -3,49 +3,47 @@
#include <Godot.hpp> #include <Godot.hpp>
#include <Node.hpp> #include <Node.hpp>
#include <thread>
#include <mutex> #include <mutex>
#include <thread>
namespace godot namespace godot {
{
class Pseudoterminal : public Node class Pseudoterminal : public Node {
{ GODOT_CLASS(Pseudoterminal, Node)
GODOT_CLASS(Pseudoterminal, Node)
public: public:
static const int MAX_READ_BUFFER_LENGTH = 1024; static const int MAX_READ_BUFFER_LENGTH = 1024;
static const int MAX_WRITE_BUFFER_LENGTH = 1024; static const int MAX_WRITE_BUFFER_LENGTH = 1024;
private: private:
std::thread pty_thread; std::thread pty_thread;
bool should_process_pty; bool should_process_pty;
char write_buffer[MAX_WRITE_BUFFER_LENGTH]; char write_buffer[MAX_WRITE_BUFFER_LENGTH];
int bytes_to_write; int bytes_to_write;
std::mutex write_buffer_mutex; std::mutex write_buffer_mutex;
char read_buffer[MAX_READ_BUFFER_LENGTH]; char read_buffer[MAX_READ_BUFFER_LENGTH];
int bytes_to_read; int bytes_to_read;
std::mutex read_buffer_mutex; std::mutex read_buffer_mutex;
Vector2 size; Vector2 size;
std::mutex size_mutex; std::mutex size_mutex;
void process_pty(); void process_pty();
public: public:
static void _register_methods(); static void _register_methods();
Pseudoterminal(); Pseudoterminal();
~Pseudoterminal(); ~Pseudoterminal();
void _init(); void _init();
void _ready(); void _ready();
void write(PoolByteArray data); void write(PoolByteArray data);
void resize(Vector2 size); void resize(Vector2 size);
}; };
} // namespace godot } // namespace godot
#endif // PSEUDOTERMINAL_H #endif // PSEUDOTERMINAL_H

File diff suppressed because it is too large Load diff

View file

@ -1,78 +1,75 @@
#ifndef TERMINAL_H #ifndef TERMINAL_H
#define TERMINAL_H #define TERMINAL_H
#include <libtsm.h>
#include <Control.hpp> #include <Control.hpp>
#include <Font.hpp> #include <Font.hpp>
#include <Godot.hpp> #include <Godot.hpp>
#include <libtsm.h>
#include <map> #include <map>
#include <vector> #include <vector>
namespace godot namespace godot {
{
class Terminal : public Control class Terminal : public Control {
{ GODOT_CLASS(Terminal, Control)
GODOT_CLASS(Terminal, Control)
public: public:
struct cell struct cell {
{ char ch[5];
char ch[5]; struct tsm_screen_attr attr;
struct tsm_screen_attr attr; };
}; static const struct cell empty_cell;
static const struct cell empty_cell;
public: public:
typedef std::vector<std::vector<struct cell>> Cells; typedef std::vector<std::vector<struct cell>> Cells;
typedef std::vector<struct cell> Row; typedef std::vector<struct cell> Row;
Cells cells; Cells cells;
Ref<InputEventKey> input_event_key; Ref<InputEventKey> input_event_key;
protected: protected:
tsm_screen *screen; tsm_screen *screen;
tsm_vte *vte; tsm_vte *vte;
private: private:
static const uint8_t default_color_palette[TSM_COLOR_NUM][3]; static const uint8_t default_color_palette[TSM_COLOR_NUM][3];
static const std::map<std::pair<int64_t, int64_t>, uint32_t> keymap; static const std::map<std::pair<int64_t, int64_t>, uint32_t> keymap;
Vector2 cell_size; Vector2 cell_size;
std::map<int, Color> palette = {}; std::map<int, Color> palette = {};
std::map<String, Ref<Font>> fontmap = {}; std::map<String, Ref<Font>> fontmap = {};
void update_size(); void update_size();
void update_theme(); void update_theme();
std::pair<Color, Color> get_cell_colors(int row, int col); std::pair<Color, Color> get_cell_colors(int row, int col);
void draw_background(int row, int col, Color bgcol); void draw_background(int row, int col, Color bgcol);
void draw_foreground(int row, int col, Color fgcol); void draw_foreground(int row, int col, Color fgcol);
public: public:
static void _register_methods(); static void _register_methods();
Terminal(); Terminal();
~Terminal(); ~Terminal();
void _init(); void _init();
void _ready(); void _ready();
void _notification(int what); void _notification(int what);
void _gui_input(Variant event); void _gui_input(Variant event);
void _draw(); void _draw();
void write(Variant data); void write(Variant data);
int rows; int rows;
int cols; int cols;
bool sleep; bool sleep;
uint8_t color_palette[TSM_COLOR_NUM][3]; uint8_t color_palette[TSM_COLOR_NUM][3];
tsm_age_t framebuffer_age; tsm_age_t framebuffer_age;
}; };
} // namespace godot } // namespace godot
#endif // TERMINAL_H #endif // TERMINAL_H

21
misc/hooks/README.md Normal file
View file

@ -0,0 +1,21 @@
# Git hooks for GodotXterm
This folder contains git hooks meant to be installed locally by GodotXterm
contributors to make sure they comply with our requirements.
## List of hooks
- Pre-commit hook for gdformat: Applies formatting to staged gdscript files
using the [GDScript Toolkit](https://github.com/Scony/godot-gdscript-toolkit) by Pawel Lampe et al.
- Pre-commit hook for clang-format: Applies clang-format to the staged files
before accepting a commit; blocks the commit and generates a patch if the
style is not respected.
Should work on Linux and macOS. You may need to edit the file if your
clang-format binary is not in the $PATH, or if you want to enable colored
output with pygmentize.
## Installation
Symlink (or copy) all the files from this folder (except this README) into your .git/hooks folder, and make sure
the hooks and helper scripts are executable.

View file

@ -0,0 +1,48 @@
#!/bin/sh
# Provide the canonicalize filename (physical filename with out any symlinks)
# like the GNU version readlink with the -f option regardless of the version of
# readlink (GNU or BSD).
# This file is part of a set of unofficial pre-commit hooks available
# at github.
# Link: https://github.com/githubbrowser/Pre-commit-hooks
# Contact: David Martin, david.martin.mailbox@googlemail.com
###########################################################
# There should be no need to change anything below this line.
# Canonicalize by recursively following every symlink in every component of the
# specified filename. This should reproduce the results of the GNU version of
# readlink with the -f option.
#
# Reference: http://stackoverflow.com/questions/1055671/how-can-i-get-the-behavior-of-gnus-readlink-f-on-a-mac
canonicalize_filename () {
local target_file="$1"
local physical_directory=""
local result=""
# Need to restore the working directory after work.
local working_dir="`pwd`"
cd -- "$(dirname -- "$target_file")"
target_file="$(basename -- "$target_file")"
# Iterate down a (possible) chain of symlinks
while [ -L "$target_file" ]
do
target_file="$(readlink -- "$target_file")"
cd -- "$(dirname -- "$target_file")"
target_file="$(basename -- "$target_file")"
done
# Compute the canonicalized name by finding the physical path
# for the directory we're in and appending the target file.
physical_directory="`pwd -P`"
result="$physical_directory/$target_file"
# restore the working directory after work.
cd -- "$working_dir"
echo "$result"
}

50
misc/hooks/pre-commit Executable file
View file

@ -0,0 +1,50 @@
#!/bin/sh
# Git pre-commit hook that runs multiple hooks specified in $HOOKS.
# Make sure this script is executable. Bypass hooks with git commit --no-verify.
# This file is part of a set of unofficial pre-commit hooks available
# at github.
# Link: https://github.com/githubbrowser/Pre-commit-hooks
# Contact: David Martin, david.martin.mailbox@googlemail.com
###########################################################
# CONFIGURATION:
# pre-commit hooks to be executed. They should be in the same .git/hooks/ folder
# as this script. Hooks should return 0 if successful and nonzero to cancel the
# commit. They are executed in the order in which they are listed.
HOOKS="pre-commit-gdformat"
HOOKS="pre-commit-clang-format"
###########################################################
# There should be no need to change anything below this line.
. "$(dirname -- "$0")/canonicalize_filename.sh"
# exit on error
set -e
# Absolute path to this script, e.g. /home/user/bin/foo.sh
SCRIPT="$(canonicalize_filename "$0")"
# Absolute path this script is in, thus /home/user/bin
SCRIPTPATH="$(dirname -- "$SCRIPT")"
for hook in $HOOKS
do
echo "Running hook: $hook"
# run hook if it exists
# if it returns with nonzero exit with 1 and thus abort the commit
if [ -f "$SCRIPTPATH/$hook" ]; then
"$SCRIPTPATH/$hook"
if [ $? != 0 ]; then
exit 1
fi
else
echo "Error: file $hook not found."
echo "Aborting commit. Make sure the hook is in $SCRIPTPATH and executable."
echo "You can disable it by removing it from the list in $SCRIPT."
echo "You can skip all pre-commit hooks with --no-verify (not recommended)."
exit 1
fi
done

View file

@ -0,0 +1,148 @@
#! /usr/bin/env nix-shell
#! nix-shell -i sh -p clang
# git pre-commit hook that runs a clang-format stylecheck.
# Features:
# - abort commit when commit does not comply with the style guidelines
# - create a patch of the proposed style changes
# Modifications for clang-format by rene.milk@wwu.de
# This file is part of a set of unofficial pre-commit hooks available
# at github.
# Link: https://github.com/githubbrowser/Pre-commit-hooks
# Contact: David Martin, david.martin.mailbox@googlemail.com
# Some quality of life modifications made for Godot Engine.
##################################################################
# SETTINGS
# Set path to clang-format binary
# CLANG_FORMAT="/usr/bin/clang-format"
CLANG_FORMAT=`which clang-format`
# Remove any older patches from previous commits. Set to true or false.
# DELETE_OLD_PATCHES=false
DELETE_OLD_PATCHES=false
# Only parse files with the extensions in FILE_EXTS. Set to true or false.
# If false every changed file in the commit will be parsed with clang-format.
# If true only files matching one of the extensions are parsed with clang-format.
# PARSE_EXTS=true
PARSE_EXTS=true
# File types to parse. Only effective when PARSE_EXTS is true.
# FILE_EXTS=".c .h .cpp .hpp"
FILE_EXTS=".c .h .cpp .hpp .cc .hh .cxx .m .mm .inc .java .glsl"
# Use pygmentize instead of cat to parse diff with highlighting.
# Install it with `pip install pygments` (Linux) or `easy_install Pygments` (Mac)
# READER="pygmentize -l diff"
READER=cat
##################################################################
# There should be no need to change anything below this line.
. "$(dirname -- "$0")/canonicalize_filename.sh"
# exit on error
set -e
# check whether the given file matches any of the set extensions
matches_extension() {
local filename=$(basename "$1")
local extension=".${filename##*.}"
local ext
for ext in $FILE_EXTS; do [[ "$ext" == "$extension" ]] && return 0; done
return 1
}
# necessary check for initial commit
if git rev-parse --verify HEAD >/dev/null 2>&1 ; then
against=HEAD
else
# Initial commit: diff against an empty tree object
against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi
if [ ! -x "$CLANG_FORMAT" ] ; then
printf "Error: clang-format executable not found.\n"
printf "Set the correct path in $(canonicalize_filename "$0").\n"
exit 1
fi
# create a random filename to store our generated patch
prefix="pre-commit-clang-format"
suffix="$(date +%s)"
patch="/tmp/$prefix-$suffix.patch"
# clean up any older clang-format patches
$DELETE_OLD_PATCHES && rm -f /tmp/$prefix*.patch
# create one patch containing all changes to the files
git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;
do
# ignore thirdparty files
if grep -q "thirdparty" <<< $file; then
continue;
fi
# ignore file if we do check for file extensions and the file
# does not match any of the extensions specified in $FILE_EXTS
if $PARSE_EXTS && ! matches_extension "$file"; then
continue;
fi
# clang-format our sourcefile, create a patch with diff and append it to our $patch
# The sed call is necessary to transform the patch from
# --- $file timestamp
# +++ - timestamp
# to both lines working on the same file and having a/ and b/ prefix.
# Else it can not be applied with 'git apply'.
"$CLANG_FORMAT" -style=file "$file" | \
diff -u "$file" - | \
sed -e "1s|--- |--- a/|" -e "2s|+++ -|+++ b/$file|" >> "$patch"
done
# if no patch has been generated all is ok, clean up the file stub and exit
if [ ! -s "$patch" ] ; then
printf "Files in this commit comply with the clang-format rules.\n"
rm -f "$patch"
exit 0
fi
# a patch has been created, notify the user and exit
printf "\nThe following differences were found between the code to commit "
printf "and the clang-format rules:\n\n"
$READER "$patch"
printf "\n"
# Allows us to read user input below, assigns stdin to keyboard
exec < /dev/tty
while true; do
read -p "Do you want to apply that patch (Y - Apply, N - Do not apply, S - Apply and stage files)? [Y/N/S] " yn
case $yn in
[Yy] ) git apply $patch;
printf "The patch was applied. You can now stage the changes and commit again.\n\n";
break
;;
[Nn] ) printf "\nYou can apply these changes with:\n git apply $patch\n";
printf "(may need to be called from the root directory of your repository)\n";
printf "Aborting commit. Apply changes and commit again or skip checking with";
printf " --no-verify (not recommended).\n\n";
break
;;
[Ss] ) git apply $patch;
git diff-index --cached --diff-filter=ACMR --name-only $against -- | while read file;
do git add $file;
done
printf "The patch was applied and the changed files staged. You can now commit.\n\n";
break
;;
* ) echo "Please answer yes or no."
;;
esac
done
exit 1 # we don't commit in any case

23
misc/hooks/pre-commit-gdformat Executable file
View file

@ -0,0 +1,23 @@
#! /usr/bin/env nix-shell
#! nix-shell -i sh -p python38
GDTOOLKIT_VERSION=f5e2746d146200ec07ac6acb6fb378fd4c64f3f0
FILES=$(git diff --cached --name-only --diff-filter=ACMR | grep '\.gd$' | sed 's| |\\ |g')
[ -z "$FILES" ] && exit 0
# Setup GDScript Toolkit.
if [ ! -f .venv/bin/activate ] || ! source .venv/bin/activate; then
python -m venv .venv && source .venv/bin/activate;
fi
if ! gdformat --version; then
pip install git+git://github.com/Scony/godot-gdscript-toolkit@${gdtoolkit_version};
fi
# Format all selected files.
echo "$FILES" | xargs gdformat
# Add back the formatted files to staging.
echo "$FILES" | xargs git add
exit 0