Vala, le futur

Il y a quelques jours j’ai lu ce post, qui suggérait d’utiliser Rust dans le développement d’applications et de bibliothèques GNOME. Dans ce billet, l’auteur, qui a utilisé et utilise Vala, dit qu’il aime ce langage, mais qu’un de ces principaux problèmes est qu’il est très dur à déboguer. Et je confirme, Vala est une horreur à déboguer : le compilateur génère parfois des erreurs dans le code C alors qu’il ne devrait pas, il n’y a pas de débogueur « officiel », et donc on se débrouille avec GDB et/ou Nemiver en mettant des points d’arrêts sur les noms des fonctions en C. Au final j’utilise plus souvent la technique des print un peu partout dans mon code, pour voir à quel niveau ça plante et quelle tête ont les différentes variables à ce moment-là. Bref, ça fait des sorties de trois kilomètres de long, c’est sale et pas super efficace…

En lisant les commentaires, on voit que ce n’est pas le seul reproche qui est fait à Vala : il est difficile d’être sûr que la prochaine version du compilateur sera compatible avec l’actuelle. Le projet n’a jamais atteint de version stable depuis sa création, il y a environ dix ans.

J’ai moi-même des reproches à faire à l’écosystème Vala (même si j’adore ce langage). Pour commencer, l’installation de bibliothèques est très compliqué. Si on a une distribution Linux dérivé de Debian ou de Fedora, il ne devrait pas y avoir trop de problèmes, mais dès qu’on part dans quelque chose d’un peu plus exotique, voir sur du Windows ou du Mac OS X, ça devient très problématique. Essayez de compiler VSGI sous Windows, vous allez hurler : une fois que vous aurez remplacer les parties du code ne fonctionnant que sous UNIX-likes par des versions plus mutiplateformes, vous devrez installer les différentes dépendances. Prenons par exemple la bibliothèque FastCGI utilisé par ce projet : le fichier .vapi est fourni, mais pour trouver le fichier .h et la DLL qui correspond, c’est une autre histoire !

Un autre problème : c’est la documentation. Valadoc.org pourrait être un million de fois mieux fait. Il y a trop de JavaScript inutile partout, certaines parties n’ont pas la moindre petite explication (ou alors c’est la documentation de l’API correspondante en C, avec les noms de méthode en C…), depuis quelque temps la recherche ne marche plus (heureusement un miroir avec une recherche fonctionelle a été mis en place) et les liens ne valent rien : essayer par exemple d’accéder à la documentation du type string, avec ce lien http://www.valadate.org:8000/#!api=glib-2.0/string, vous allez arriver sur la page d’accueil à tous les coups ! Il n’y a pas d’API, alors que ça pourrait être très utile pour, par exemple, Valhalla. Ajouter un paquet à l’air assez complexe (il faut passer par GitHub, puis attendre que le site soit mis à jour et surtout ça à l’air d’être un mix de différents types de fichiers…), lors qu’un formulaire avec envoi de fichiers du code source (ou juste un lien vers un dépôt Git/autre) puis une validation par l’administrateur du site me semble bien mieux (plus logique, plus simple pour tout le monde). L’outil pour générer la documentation en local n’est aussi disponible que sous Debian et dérivé, et je n’ai pas réussi à le compiler sous Fedora (je n’ai même pas tenté Windows, vous imaginez l’horreur que ça doit être). Et enfin, même si ça n’est pas très grave, c’est un peu un comble, car le site n’est pas écrit en Vala, mais en PHP ! Et on sait faire des sites en Vala : il suffit d’utiliser Valum, Valse ou même quelque chose de plus bas niveau comme LibSoup !

Il y a aussi un manque de ressource pour les vrais débutants (tutoriels pour ceux qui ont déjà programmé, et pas toujours très à jour). Et si on a un problème on doit en général passer par la mailing-list, ce qui ne me donne pas vraiment l’impression d’être en 2016.

Pour résumer, Vala : - Est une horreur à déboguer ; - Est instable (pas sûr que les différentes versions seront compatibles) ; - N’a pas de moyen simple et multiplateforme d’installer des bibliothèques (à l’instar de pip, gem, npm, cargo et tant d’autres) ; - A une documentation pour le moins… pas terrible ; - Est réservé aux personnes ayant déjà une certaine expérience en programmation.

La mort de Vala ?

À ce stade de la réflexion, on pourrait se dire qu’il vaut mieux laisser mourrir Vala, et passer progressivement à Rust comme l’a suggéré Alberto Ruiz sur son blog. J’ai juste envie de répondre : non. Vala est fantastique : il représente un gain de temps énorme (quand on a pas à déboguer le code), et il allie une syntaxe très agréable, compréhensible et haut-niveau (et il ne l’est pas seulement dans la syntaxe, il l’est réellement), tout en gardant une vitesse d’exécution exceptionnelle et il permet de générer du code réutilisable dans n’importe quel autre langage grâce à GObject. À l’heure de l’Internet des Objets, à l’heure où des applications plus intelligentes que jamais doivent tenir dans des grille-pains, prendre peu de placer dans la mémoire et être rapide est un grand avantage pour un langage, mais si en plus il permet de développer des applications complexes (IA, réseau, etc) et peu de temps et de lignes, ce langage peut être réellement utile. Vala est resté cantonné à quelques applications GNOME, mais il pourrait tourner n’importe où : du plus puissant serveur Linux, au PC Windows, en passant par l’iPhone, la tablette Android ou la carte Arduino. En se débrouillant bien, je suis sûr qu’il y a moyen de créer des bibliothèques qui supportent ses plateformes en ne fournissant qu’une seule interface de développement, grâce à Vala.

Mais bon, Rust peut surement en faire autant, alors pourquoi garder Vala ? Déjà parce que même si le langage n’est pas massivement utilisé, un certains nombre de logiciels, peut-être que vous utilisez sont écrits en Vala, et si on abandonne le langage, je ne pense pas que les développeurs aient envie de tout réécrire en Rust (ou autre) depuis zéro. Ensuite, c’est très subjectif, mais je trouve que Vala a une syntaxe très élégante, surtout si on respecte les conventions de style d’elementary OS. Et enfin, si vous voulez créer une bibliothèque utilisable à peu près dans n’importe quel autre langage, Vala est surement la meilleure solution, grâce à GObject et la syntaxe qui permet d’être très productif par rapport au C.

Abandonner Vala ne semble donc pas vraiment possible. Seuleument le projet est en manque de nouveaux contributeurs, les principaux développeurs n’ayant plus le temps de s’en occuper. Et je pense que si le projet n’attire pas, c’est parce que les outils utilisés sont trop old-schools : cgit et Bugzilla, ça donne pas trop envie de contribuer. Ce n’est pas le workflow auquel la plupart des développeurs sont maintenant habitués avec Github/Gitlab. Et si Vala était hébergé sur un de ces deux services, j’aurais, personnellement, déjà contribué. Ensuite il n’y a pas à ma connaissance d’intégration continue, ce qui semble un peu problématique pour ce genre de projet…

Comment améliorer les choses ?

Pour améliorer les choses, il faudrait :

  • Améliorer les outils, ce qui passe par :
    • Avoir un npm-like. Je me suis lancé dans la création de poulp, arteymix (le principal développeur de Valum) avait commencé à réfléchir à drakkar, mais le projet est encore au stade de la spécification. On peut déjà utiliser des sous-modules Git et Meson pour se faciliter un peu la tâche, mais ce n’est pas idéal ;
    • Avoir un vrai IDE. Il y a plein d’éditeurs de texte qui supportent plus ou moins bien Vala, mais en général on se limite à de la coloration syntaxique. GNOME Builder avait l’air plutôt intéressant, mais le support de Vala n’a pas l’air d’être une priorité, et c’est pas encore ça. J’ai écrit Valhalla (un plugin pour Atom pour aider à écrire du Vala), mais il manque encore beaucoup de choses pour arriver à quelque chose d’utilisable et surtout d’utile au quotidien. Il y a aussi elementary-ide, mais il est lui aussi en cours de développement, et niveau multiplateforme je ne suis pas sûr que ça soit trop ça…
    • Faciliter le débogage, mais ça se range aussi dans la catégorie IDE.
  • Avoir des bibliothèques intéréssantes pour à peu près tout : sites web, GUI, machine learning, etc ;
  • Avoir une documentation meilleure que l’actuelle. Ça passe par :
    • Réécrire le site de Valadoc depuis zéro et refaire tout ce qui ne va pas (voir plus haut), en ajoutant la possibilité de modifier les descriptions via une interface simple ;
    • Rendre l’outil de génération de documentation locale (la commande valadoc) multiplateforme et installable facilement ;
    • Avoir des tutoriels à jour et que même des grands débutants en programmation peuvent aborder, des exemples pour les différentes bibliothèques, des guides pas-à-pas, bref des ressources pour ne jamais rester bloqué face à une bibliothèque qu’on ne sait pas utiliser et être obligé d’aller chercher la documentation C.

Déjà, si on arrive à ça, Vala deviendra mille fois plus utilisable. Ensuite j’ai quelques idées d’améliorations sympa à apporter au projet en lui-même :

  • Déjà, fixer un maximum de bugs, surtout que certains trainent depuis plusieurs années maintenant ;
  • Au niveau du langage, pourvoir définir des opérateurs personnalisés serait un gros plus. Et avoir ce qui s’appelle en C# des méthodes d’extension aussi (c’est juste des idées qui me passe par la tête et je suis sûr qu’il y aurait plein de choses à ajouter) ;
  • Ensuite il faudrait « moderniser » le développement : installer une instance de Gitlab et mettre en place de la CI ça doit être faisable, non ? Bon en fait, GNOME veut garder son workflow actuel (je ne sais pas pourquoi) et donc à moins de forker le projet, ça risque d’être compliqué…
  • Rendre Vala vraiment multiplateforme : pouvoir compiler pour différentes plateformes et pouvoir transpiler vers plusieurs langages : on passe une option au compilateur et il nous génère le code Swift de notre application qu’on pourra ensuite compiler pour iOS, avec une autre option il génère du JavaScript près à être utilisé dans un navigateur, ou bien une application écrite en Rust (un peu comme ce que fait Haxe). Mais bon, arriver à ça va être très compliqué, même si le langage deviendrait alors super intéressant. On peut déjà avancer vers plus de multiplateforme en facilitant le développement et la compilation sous Windows et Mac OS X.

Et bien sûr, communiquer, parler du projet, sans quoi même s’il est génial, personne n’en entendra parler.

Conclusion

On peut faire quelque chose de génial de Vala, il faut juste le faire. En écrivant cet article, je me suis rendu compte qu’il fallait que je me concentre sur poulp et Valhalla, car parmi tous mes projets ce sont ces deux-là qui sont les plus importants pour le futur de Vala.

Je ne dis pas non plus qu’utiliser Rust pour GNOME est une mauvaise idée : au contraire si il permet de faire gagner du temps et de sécuriser certaines parties du code qui en ont besoin, c’est très bien. Au final, passer du tout C qu’on a un peu actuellement dans GNOME vers du C + Vala + Rust, respectivement pour ce qui peut rester en C, ce qui est utile de passer à du plus haut niveau (pour gagner du temps) et ce qui a besoin d’être « sécurisé ».

Et juste un petit mot à propos de la série d’article sur Valhalla : le dernier article n’arrivera pas tout de suite parce que le module de documentation risque de pas mal changer dans les prochaines versions. J’attendrai donc d’avoir un fonctionnement plus stable pour en parler.

Le fonctionnement de Valhalla, partie 3 : afficher les suggestions

Et voici donc le second article de cette série où je vous explique le fonctionnement du package Atom que je développe : Valhalla. Aujourd’hui, on va voir comment Valhalla fait pour afficher ses suggestions.

Tout ce passe dans dans le fichier provider.js (ou presque). On y trouve une classe (ValaProvider) divisée en trois grandes parties :

  • L’initialisation, où on va notamment lancer la découpe de notre code, comme on l’a vu dans la partie précédente. On va aussi préciser à Atom que le moteur de suggestions n’est valable que dans du Vala ;
  • Le moteur de suggestions, qui va dire quelle suggestions afficher en fonction du contexte, en se basant sur le code qu’on a découpé auparavant ;
  • Des fonctions pour nous aider un peu, par exemple pour afficher facilement une méthode dans les suggestions ou pour déterminer le type d’une expression.

On va surtout s’intéresser à la deuxième partie, c’est là que la plupart des choses se font.

Le moteur de suggestions

Pour obtenir ses suggestions, Atom va exécuter la fonction getSuggestions de notre classe (on verra plus tard comment il sait quelle fonction prendre). Cette fonction retourne une Promise qui est résolue avec un tableau contenant les suggestions sous forme d’objets, ressemblant généralement à ça.

{
    text: 'Suggestion',
    type: 'function',
    leftLabel: 'Super',
    rightLabel: 'Génial',
    description: 'Une super suggestion, vraiment géniale !',
    descriptionMoreURL: 'https://bat41.gitlab.io'
}

Ce code donnera quelque chose comme ça :

Notre suggestion

On a aussi d’autres options plus avancés, si ça vous intéresse, vous pouvez aller jeter un coup d’œuil au wiki du package qui gère l’autocomplétion.

On crée donc un tableau vide qui contiendra toutes nos suggestions par la suite, puis on utilise la méthode qu’on a déjà vu pour explorer notre arbre de blocs de code, et on filtre en fonction du contexte.

// Version (très simplifiée) pour que vous compreniez le principe.
getSuggestions ({editor, bufferPosition, scopeDescriptor, prefix, activatedManually}) {
    // On obtient la ligne actuelle.
    var line = editor.getTextInRange([[bufferPosition.row, 0], bufferPosition]);

    return new Promise ((resolve) => {
        let suggestions = [];

        const trimLine = line.trim ();

        // La méthode récursive utilisée pour explorer les blocs de code.
        let explore = (scope) => {

            // Ici par exemple, si la ligne commence par `using `,
            // on affiche les différents espaces de noms disponibles
            if (trimLine.startsWith('using ')) {
                // Le bloc actuel est un espace de nom
                if (scope.data && scope.data.type == 'namespace') {
                    // Il commence comme ce que l’utilisateur a tapé
                    if (name.startsWith(prefix) || prefix == ' ') {
                        // show suggestion
                        let suggestion = {
                            text: name + ';',
                            type: 'import',
                            displayText: name,
                            description: `The ${name} namespace.`
                        };
                        suggestions.push (suggestion);
                    }
                }
            }

            // On explore les enfants
            for (child of scope.children) {
                explore (child);
            }
        }

        // On explore tout les scopes à la racine
        for (scope of this.manager.scopes) {
            explore (scope);
        }

        // ON envoie nos suggestions
        if (trimLine != '') {
            resolve(suggestions);
        }
    });
}

En réalité, c’est bien plus complexe que ça, mais le principe reste toujours le même.

Lier tout ça au reste d’Atom

On a maintenant un joli moteur de suggestions, sauf qu’Atom ne sait pas qu’il faut l’utiliser ! Résultat : il ne se passe rien…

Il faut savoir qu’à peu près tout dans Atom est un package (et c’est peut être la plus grande force de cet éditeur) : la fonction rechercher, l’affichage des onglets, des suggestions… Et les différents packages ont un moyen très simple de communiquer entre eux, appelé services. Un package propose un service, les autres peuvent y souscrire et ils échangent des information ainsi. C’est par exemple ce que fait autocomplete-plus, le package qui affiche les suggestions : on peut ainsi lui donner nos propres moteurs de suggestions. Et Valhalla utilise ce service, grâce à ce morceau de son manifeste.

"providedServices": {
  "autocomplete.provider": {
    "versions": {
      "2.0.0": "getProvider"
    }
  }
}

Ici, on indique à autocomplete-plus qu’il peut obtenir notre moteur grâce à la méthode getProvider, qui se trouve dans l’objet exporté par Valhalla (export default ou module.export) , situé dans valhalla.js. Cette méthode retourne simplement une instance de la classe ValaProvider ! Et voilà, Atom sait où aller chercher les suggestions pour le code Vala et peut les afficher.


Cet article est terminé, mais un autre devrait bientôt arriver. En tout cas j’espère qu’il vous aura intéressé.

Le fonctionnement de Valhalla, partie 2 : Découper le code

Cet article est donc le premier de la série où je vais vous expliquer le fonctionnement de Valhalla. Nous allons commencer par voir comment Valhalla découpe le code des différentes bibliothèques installées.

Tout d’abord, au lancement d’Atom, le pckage va être chargé, et la fonction activate que l’on trouve dans le fichier valhalla.js (on lui indique dans le manifeste). Dans cette fonction, on va faire tout un tas de chose, mais on va surtout initialiser une nouvelle instance de la classe ValaProvider qui fournit les suggestions lorsqu’on commence à taper quelque chose. Mais cette partie ne nous intéresse pas encore. C’est juste de ce bout de code, dans le constructeur de ValaProvider dont nous allons parler :

const vapiDir = atom.config.get('valhalla.vapiDir');
this.manager = new ScopeManager();

atom.workspace.observeTextEditors((editor) => {
    // dès qu'un éditeur est créé
    if (editor.getPath() && editor.getPath().endsWith('.vala')) {
        // si c'est un fichier Vala qui y est ouvert
        editor.onDidStopChanging((event) => {
            // quand il a été modifié, on le « découpe » (voir explication après)
            this.manager.parse(editor.getText(), editor.getPath());
        });
        // Et puis on le découpe aussi maintenant
        this.manager.parse(editor.getText(), editor.getPath());
    }
});

// On lit les fichiers .vapi
fs.readdir (vapiDir, (err, files) => {
    if (err) {
       console.error (err);
       return;
    }

    for (file of files) {
       if (file.endsWith('.vapi')) {
           let content = fs.readFileSync(path.join(vapiDir, file), 'utf-8');
           // On les « découpe »
           this.manager.parse(content, file);
       }
    }
});

Ici, on crée une nouvelle instance de la classe ScopeManager, qui a pour rôle de « découper » le code des bibliothèques installées (contenu dans des fichiers .vapi) et le code écrit par l’utilisateur d’Atom et nous en faire des objets, beaucoup plus simples à utiliser. Par exemple, après avoir été « découpée », une classe ressemblera à quelque chose comme ça :

{
    top: scopeParent, // Le scope parent (un espace de nom par exemple). Je ne l’ai pas réellement mis, car il contient une référence vers ces enfants, donc cet objet et on n’en finirait pas.
    at: [[0, 0] [10, 1]], // L’endroit où est définie cette classe dans le fichier.
    name: 'public class Hey : Object ', // La définition de la classe.
    children: [enfant1, enfant2, enfant3], // Les enfants de cette classe (méthodes, propriétés…), pareil que pour `top`, je ne les ait pas mis.
    file: 'bibli.vapi’, // Le fichier d' vient la classe.
    documentation: { // La documentation de cette classe. En général elle n’est pas dans les fichiers .vapi, mais dans les fichiers .vala (qui continnent le code écrit par l’utilisateur)
        short: 'Une super classe.',
        long: 'En vrai elle a trop la classe ! \n\n\n\n\n\n\n\n… \n\n\n Vous l’avez ?.'
    },
    vapi: true, // Indique si cette classe vient d’un fichier .vapi.
    data: { // Des données à propos de la classe, qu’on parse une fois et qu’on réutilise plein de fois.
        attributes: [ // Les attributs de cette classe.
            {
                name: 'CCode',
                parameters: 'cname = ' // Oui, il n’y a pas de valeur après le =, parce que c’est une expression entre " et qu’elles sont ignorées.
            }
        ],
        type: 'class', // Indique qu’on a affaire à une classe.
        name: 'Hey', // Le nom de la classe.
        access: 'public', // Sa visibilité.
        inherits: 'Object' // Les classes dont elle hérite
    }
}

Et voilà c’est tout beau, près à être réutilisé pour afficher des suggestions !

L’algorithme qui fait ça

Je vais essayer de décomposer les grandes lignes de la méthode parse qui est chargée de faire toute cette découpe :

  • On a un tableau (this.scopes), qui contient les objets représentant la racine de chaque fichier ;
  • On trouve celui du fichier courant, ou on le crée : c’est la variable currentScope ;
  • On passe chaque caractère du code un à un, en ignorant les commentaires, les expressions littérales ("chaîne de caractères", par exemple) et tout ce qui ne nous intéresse pas;
  • Les caractères sont ajoutés à une variable token (qui en fait n’est pas un seul token, mais plusieurs) ;
  • Si le charactère actuel est { ou ;, ça veut dire qu’on arrive à une déclaration :
    • On va alors utiliser tout plein d’expression rationnelles pour faire la découpe de notre token ;
    • On stocke ce qu’on a découpé dans la liste des enfants de currentScope, et l’objet qu’on a créé devient le currentScope ;
    • On réinitialise token;

Au final on se retrouve avec ce qu’on pourrait appeler un « arbre bi-directionnel » représentant les différentes déclarations de notre code. On peut alors l’explorer comme ceci :

const explore = (scope) => {
    // traitement des données du bloc de code…
    // par exemple pour faire des suggestions

    for (const child of scope.children) {
        explore (child);
    }
};

for (const scope of this.scopes) {
    explore (scope);
}

Optimisations

Au début, je n’avait de propriété data contenant les différentes informations sur le bloc de code correspondant (son nom, son type, ses arguments, etc). Je devait donc réutiliser des Regex à chaque fois pour découper mes objets. Ce qui en plus de prendre de la place, ralentissait beaucoup Atom (se taper 10000 fois une dizaine de Regex différentes, ça fait pas du bien). J’ai donc pris un peu de temps pour réécrire ça comme il faut.

Une erreur un peu plus subtile, vient du fait que je stockait mes RegEx en « dur » dans mon code, comme ça.

if (klassMatch = token.match(/^(public |private |internal )?(abstract )?class ([\w\.]+)( :( [\w\.,]+)+)?( ?{)?$/)) {
    // récupérer les informations
}

Sauf que JavaScript (et à peu près tous les langages en fait) compile les RegEx lors de leur création, ce qui prend pas mal de temps aussi. J’ai donc changé ça, en stockant toutes les RegEx dont j’ai besoin dans this.re. C’est marrant, mais ça fait gagner un temps fou !

Une autre erreur que j’ai assez souvent fait avant de m’en rendre compte, est d’utiliser beaucoup de console.log (genre dans une boucle sur 10000 éléments). Atom est vraiment beaucoup ralenti, et on peut même le faire planter comme ça ! Maintenant, je préfère mettre des points d’arrêts dans le déboggueur.


Voilà ce premier article est fini, j’espère que ça vous aura intéréssé ou aidé. Le prochain devrait sortir bientôt !

Le fonctionnnement de Valhalla, partie 2 : Découper le code

Cet article est donc le premier de la série où je vais vous expliquer le fonctionnnement de Valhalla. Nous allons commencer par voir comment Valhalla découpe le code des différentes bibliothèques installées.

Tout d'abord, au lancement d'Atom, le pckage va être chargé, et la fonction activate que l'on trouve dans le fichier valhalla.js (on lui indique dans le manifeste). Dans cette fonction, on va faire tout un tas de chose, mais on va surtout initialiser une nouvelle instance de la classe ValaProvider qui fournit les suggestions lorsqu'on commence à taper quelque chose. Mais cette partie ne nous intéresse pas encore. C'est juste de ce bout de code, dans le constructeur de ValaProvider dont nous allons parler :

const vapiDir = atom.config.get('valhalla.vapiDir');
this.manager = new ScopeManager();

atom.workspace.observeTextEditors((editor) => {
    // dès qu'un éditeur est créé
    if (editor.getPath() && editor.getPath().endsWith('.vala')) {
        // si c'est un fichier Vala qui y est ouvert
        editor.onDidStopChanging((event) => {
            // quand il a été modifié, on le « découpe » (voir explication après)
            this.manager.parse(editor.getText(), editor.getPath());
        });
        // Et puis on le découpe aussi maintenant
        this.manager.parse(editor.getText(), editor.getPath());
    }
});

// On lit les fichiers .vapi
fs.readdir (vapiDir, (err, files) => {
    if (err) {
       console.error (err);
       return;
    }

    for (file of files) {
       if (file.endsWith('.vapi')) {
           let content = fs.readFileSync(path.join(vapiDir, file), 'utf-8');
           // On les « découpe »
           this.manager.parse(content, file);
       }
    }
});

Ici, on crée une nouvelle instance de la classe ScopeManager, qui a pour rôle de « découper » le code des bibliothèques installées (contenu dans des fichiers .vapi) et le code écrit par l'utilisateur d'Atom et nous en faire des objets, beaucoup plus simples à utiliser. Par exemple, après avoir été « découpée », une classe ressemblera à quelque chose comme ça :

{
    top: scopeParent, // Le scope parent (un espace de nom par exemple). Je ne l'ai pas réellement mis, car il contient une référence vers ces enfants, donc cet objet et on n'en finirait pas.
    at: [[0, 0] [10, 1]], // L'endroit où est définie cette classe dans le fichier.
    name: 'public class Hey : Object ', // La définition de la classe.
    children: [enfant1, enfant2, enfant3], // Les enfants de cette classe (méthodes, propriétés ...), pareil que pour `top`, je ne les ait pas mis.
    file: 'bibli.vapi', // Le fichier d'où vient la classe.
    documentation: { // La documentation de cette classe. En général elle n'est pas dans les fichiers .vapi, mais dans les fichiers .vala (qui continnent le code écrit par l'utilisateur)
        short: 'Une super classe.',
        long: 'En vrai elle a trop la classe ! \n\n\n\n\n\n\n\n ... \n\n\n LOL.'
    },
    vapi: true, // Indique si cette classe vient d'un fichier .vapi.
    data: { // Des données à propos de la classe, qu'on parse une fois et qu'on réutilise plein de fois.
        attributes: [ // Les attributs de cette classe.
            {
                name: 'CCode',
                parameters: 'cname = ' // Oui, il n'y a pas de valeur après le =, parce que c'est une expression entre " et qu'elles sont ignorées.
            }
        ],
        type: 'class', // Indique qu'on a affaire à une classe.
        name: 'Hey', // Le nom de la classe.
        access: 'public', // Sa visibilité.
        inherits: 'Object' // Les classes dont elle hérite
    }
}

Et voilà c'est tout beau, près à être réutilisé pour afficher des suggestions !

L'algorithme qui fait ça

Je vais essayer de décomposer les grandes lignes de la méthode parse qui est chargée de faire toute cette découpe :

  • On a un tableau (this.scopes), qui contient les objets représentant la racine de chaque fichier ;
  • On trouve celui du fichier courant, ou on le crée : c'est la variable currentScope ;
  • On passe chaque caractère du code un à un, en ignorant les commentaires, les expressions litérales ("chaîne de caractères", par exemple) et tout ce qui ne nous intérèsse pas;
  • Les caractères sont ajoutés à une varibale token (qui en fait n'est pas un seul token, mais plusieurs) ;
  • Si le caractères actuel est { ou ;, ça veut dire qu'on arrive à une déclaration :
    • On va alors utiliset tout plein d'expression rationelles pour faire la découpe de notre token ;
    • On stocke ce qu'on a découpé dans la liste des enfants de currentScope, et l'objet qu'on a créé devient le currentScope ;
    • On réinitialise token;

Au final on se retrouve avec ce qu'on pourrait appeler un « arbre bi-directionnel » représentant les différentes déclarations de notre code. On peut alors l'explorer comme ceci :

const explore = (scope) => {
    // traitement des données du bloc de code ...
    // par exemple pour faire des suggestions

    for (const child of scope.children) {
        explore (child);
    }
};

for (const scope of this.scopes) {
    explore (scope);
}

Optimisations

Au début, je n'avait de propriété data contenant les différentes informations sur le bloc de code correspondant (son nom, son type, ses arguments, etc). Je devait donc réutiliser des Regex à chaque fois pour découper mes objets. Ce qui en plus de prendre de la place, ralentissait beaucoup Atom (se taper 10000 fois une dizaine de Regex différentes, ça fait pas du bien). J'ai donc pris un peu de temps pour réécrire ça comme il faut.

Une erreur un peu plus subtile, vient du fait que je stockait mes RegEx en « dur » dans mon code, comme ça.

if (klassMatch = token.match(/^(public |private |internal )?(abstract )?class ([\w\.]+)( :( [\w\.,]+)+)?( ?{)?$/)) {
    // récupérer les informations
}

Sauf que JavaScript (et à peu près tous les langages en fait) compile les RegEx lors de leur création, ce qui prend pas mal de temps aussi. J'ai donc changé ça, en stockant toutes les RegEx dont j'ai besoin dans this.re. C'est marrant, mais ça fait gagner un temps fou !

Une autre erreur que j'ai assez souvent fait avant de m'en rendre compte, est d'utiliser beaucoup de console.log (genre dans une boucle sur 10000 éléments). Atom est vraiment beaucoup ralenti, et on peut même le faire planter comme ça ! Maintenant, je préfère mettre des points d'arrêts dans le déboggueur.


Voilà ce premier article est fini, j'espère que ça vous aura intéréssé ou aidé. Le prochain devrait sortir bientôt !

Le fonctionnnement de Valhalla, partie 1 : Introduction

Pour ceux qui ne le savent pas, je développe un package pour Atom nommé Valhalla. Il est écrit en JavaScript (ES6) et vous pouvez voir le code (libre bien-sûr) sur GitHub. Ce package vous fournit tout un tas d'outils pour vous rendre l'écriture de code dans le langage Vala plus simple.

J'ai décidé de créer une petite série d'article pour vous expliquer comment fonctionne ce package, car la création de package Atom est assez mal documentée (et en français n'en parlons pas), et que même si je prévois quelque chose de plus complet (mais pas pour tout de suite), ça permet d'avoir un aperçu de comment Atom gère ses packages.

Principaux modules

Valhalla se décompose globalement ainsi :

  • Un module qui va chercher les fichiers .vapi (où sont décrits les différentes classes, méthodes, interfaces et autres disponibles avec les bibliothèques installées sur le sytème) et qui les décompose en objets JavaScript utilisables plus tard pour plusieurs choses ;
  • Un autre module qui affiche les suggestions, à partir de ce qu'on a récupérer dans les fichier .vapi ;
  • Un autre qui est chargé d'afficher une petite documentation, toujours grâce à ce qu'on a récupéré avant ;
  • Des petits outils comme la création instantanée de classe ou d'interfaces, l'intégration avec le package atom-build, ou encore une fonctionnalité « Entourer avec ... ».

Chacun de ses points aura son article détaillé dans la petite série d'article que je prévoie écrire. Au niveau du rythme, rien n'est vraiment prévu, même si je pense faire quelque chose comme un article par semaine. En tout cas le premier est déjà disponible, juste ici.

Le fonctionnement de Valhalla, partie 1 : Introduction

Pour ceux qui ne le savent pas, je développe un package pour Atom nommé Valhalla. Il est écrit en JavaScript (ES6) et vous pouvez voir le code (libre bien-sûr) sur GitHub. Ce package vous fournit tout un tas d’outils pour vous rendre l’écriture de code dans le langage Vala plus simple.

J’ai décidé de créer une petite série d’article pour vous expliquer comment fonctionne ce package, car la création de package Atom est assez mal documentée (et en français n’en parlons pas), et que même si je prévois quelque chose de plus complet (mais pas pour tout de suite), ça permet d’avoir un aperçu de comment Atom gère ses packages.

Principaux modules

Valhalla se décompose globalement ainsi :

  • Un module qui va chercher les fichiers .vapi (où sont décrits les différentes classes, méthodes, interfaces et autres disponibles avec les bibliothèques installées sur le sytème) et qui les décompose en objets JavaScript utilisables plus tard pour plusieurs choses ;
  • Un autre module qui affiche les suggestions, à partir de ce qu’on a récupéré dans les fichier .vapi ;
  • Un autre qui est chargé d’afficher une petite documentation, toujours grâce à ce qu’on a récupéré avant ;
  • Des petits outils comme la création instantanée de classe ou d’interfaces, l’intégration avec le package atom-build, ou encore une fonctionnalité « Entourer avec… ».

Chacun de ses points aura son article détaillé dans la petite série d’article que je prévoie écrire. Au niveau du rythme, rien n’est vraiment prévu, même si je pense faire quelque chose comme un article par semaine. En tout cas le premier est déjà disponible, juste ici.

Firefox Test Pilot : testez le futur Firefox

Firefox Test Pilot est un programme que Mozilla a lancé récemment. Il permet de tester de futures fonctionnalités de Firefox, encore au stade de l'expérimentation. Il faut cependant disposer d'un compte Firefox (dont vous disposez si vous utilisez Firefox Sync par exemple).

Ensuite, vous pourrez installer une extension comme toutes les autres à partir de laquelle vous pourrez choisir quelles fonctionnalités vous voulez tester.

Les fonctionnalités que j'utilise

J'ai donc installé les trois seules fonctions que l'on peut tester pour le moment : Activity Stream, Tab Center et Universal Search.

Activity Stream

Activity Stream change complètement la page nouvel onglet de Firefox. On a des sites visités récemments, ceux qu'on consulte souvent, ou des suggestions prises plus ou moins au hasard dans l'historique. L'affichage est assez sympathique.

Activity Stream

Il y a quand même quelques points négatifs. Notamment, la page met un peu de temps à s'afficher, et j'ai souvent déjà tapé ce que je veux dans la barre de recherche/adresse avant de voir le moindre élément s'afficher, ce qui gâche l'intérêt de cette page. Un autre problème est que ça doit pas mal analyser historique et autres pour afficher tout ça. Mais bon, je suppose que ça reste local, je ne vois pas pourquoi Mozilla aurait besoin de passer par un serveur pour faire ça.

Mais si le temps de chargement est amélioré, ça serait quand même beaucoup plus sympa d'avoir cette page à la place de celle qu'on a actuellement, qui est assez classique et dont les prévisualisations bogguent une foi sur deux ...

Universal Search

Cette fonction n'est juste pas au point. Voilà, c'est dit, maintenant c'est clair. Elle est censé offrir quelque chose comme l'omnibox de Chrome, c'est à dire une seule barre pour la recherche et l'adresse (ce qu'on peut d'ailleurs obtenir dans Firefox, en activant je ne sais plus trop quelle option). Et elle propose en plus des recherches sur d'autres sites (par exemple Amazon) ou des suggestions de site. Bon, je trouve pas ça super utile, mais passons ...

Seulement, chez moi du moins, elle boggue à un tel point que ma barre d'adresse est inutilisable ! J'ai la plupart du temps une barre blanche, sans rien ... Parfois j'ai des sites comme Google ou Instagram qui me sont proposés ...

Il faudra donc encore un peu de travail avant que ça marche ... Mais l'idée n'est pas mauvaise, l'interface serait bien allégée et plus simple si il n'y avait qu'une seule barre pour tout.

Tab Center

On arrive à mon extension préférée : Tab Center. Pour quelqu'un comme moi qui a très vite 42 onglets d'ouverts, cette extension est magique. Elle passe la barre d'onglet sur la gauche du navigateur, et la replie automatiquement quand on ne s'en sert pas.

Tab Center

C'est très pratique parce que du coup, dix onglets ne vont prendre qu'un tout petit peu de place sur le côté de l'écran, alors qu'ils auraient déjà rempli toute la longueur si ils avaient étés à l'horizontale. Donc vraiment, j'aime beaucoup. Le seul petit hic, c'est que j'avais des icônes (Lecteur RSS et notificateur Zeste de Savoir) qui étaient dans ma barre d'onglet et que je ne peux plus changer de place pour les voir en permanence ... Mais bon si je n'avais pas la flemme, je désactiverai l'extension, fairai mes modifications puis la réactiverai ...


J'espère que d'autres fonctionnalités vont bientôt arriver. C'est sympa de pouvoir avoir un aperçu de ce que pourrait devenir Firefox. J'espère que Activity Stream et Tab Center seront bientôt intégrés dans la version officielle (mais en option pour le second, ça peux être perturbant). Pour Universal Search, il faudra encore un peu de travail comme je l'ai dit.

Vérifier le style de son code Vala

Aujourd'hui, je me suis décidé à mettre en place un script qui vérifierait la qualité du code de mon projet, Valse, écrit en Vala. J'ai donc tir parti de Gitlab CI, qui est depuis quelques jours disponible sur les serveurs de Framagit.

Après quelques heures de travail, j'ai réussi à écrire ce script Ruby.

# encoding: UTF-8

def explore (top)
    files = Dir.entries(top)

    vala_files = []

    files.each do |file|
        if file == '.' or file == '..' or file == '.git'
            next
        end

        if file.end_with?('.vala')
            vala_files << file
        elsif File.directory?(top + '/' + file)
            in_subdir = explore(top + '/' + file)
            in_subdir.each do |sub_file|
                vala_files << file + '/' + sub_file
            end
        end
    end
    return vala_files
end

def check (file)
    content = ''
    errors = 0

    open(file,"r:UTF-8") do |f|
        line_num = 0
        in_comm = false
        while line = f.gets

            if line.index(/\*\//) != nil
                in_comm = false
                line = line.gsub(/.*\*\//, '')
            end

            if in_comm
                line_num += 1
                next
            end

            if line.index(/\/\*/) != nil
                in_comm = true
                line = line.gsub(/.*\/\*/, '')
            end

            # removing comments
            line = line.gsub(/\/\/.*/, '')
            content += line
            line_num += 1
            # don't use as
            if line.include?(' as ')
                puts 'In file ' + file + ', at line ' + line_num.to_s + ' : avoid using as.'
                errors += 1
            end

            #capitals const
            const_re = /const [[:graph:]]* (?<name>[[:graph:]]*)/
            res = const_re.match(line)
            if not res == nil
                name = res[1]
                maj_name = name.upcase
                if name != maj_name
                    puts 'In file ' + file + ', at line ' + line_num.to_s + ' : constant ' + name + ' should be named ' + maj_name
                    errors += 1
                end
            end

            # never forget the space before a (. NEVER
            space_re = /(?!\()[[:graph:]]*\(.*\)/
            space_res = space_re.match(line.gsub(/".*"/, ''))
            if not space_res == nil
                puts 'In file ' + file + ', at line ' + line_num.to_s + ' : you forgoten a space before a ('
                errors += 1
            end
        end
    end

    content = content.gsub(/\/\*.*\*\//su, '')

    # one class or interface by file
    if content.scan(' class ').length + content.scan(' interface ').length > 1
        puts 'In file ' + file + ' : too many classes or interfaces defined here.'
        errors += 1
    end

    if errors > 0
        return true
    else
        return false
    end

end

def main

    to_check = explore ('.')

    bad_files = 0

    to_check.each do |vala|
        # puts 'Checking file ' + vala
        if check(vala)
            bad_files += 1
        end
    end

    puts 'bad files : ' + bad_files.to_s + ', total : ' + to_check.length.to_s

    coverage = 100 - (100 * bad_files.to_f / to_check.length.to_f)
    puts 'Coverage : ' + coverage.to_s[0..4]
end

main

Ce petit bout de code vous permet de vérifier :

  • Qu'on utilise pas le mot-clé as qui peux renvoyer null ;
  • Que vous appelez vos constantes avec des majuscules ;
  • Que vous n'oubliez pas d'espace avant d'ouvrir des parenthèses ;
  • Que chaque fichier ne contienne qu'un seule classe ou interface.

J'ai essayé de suivre les lignes de conduites d'elementary OS, mais il y a de nombreux points que je n'ai pas implémenté. J'améliorerai surement le script dans le futur, mais au pire, c'est du libre, vous pouvez l'adapter.

Là, je sais que vous avez un question :

Mais comment on intègre ton script à notre projet ?

Intégrer ce script à votre projet

Donc bien-sûr, pour utiliser ce script, il vous faudra une instance de Gitlab.

Ensuite, il faut que vous mettiez le contenu du script dans un fichier à la racine de votre projet nommé style.rb (ou autre, mais il faudra alors adapter votre fichier .gitlab-ci.yml).

Ensuite, il va justement falloir créer le fichier .gitlab-ci.yml. On va juste y mettre ceci.

image: ruby:latest

style:
  script:
    - ruby style.rb

Ici, on ne fait rien de très compliqué : on utilise l'image Docker de la dernière version de Ruby, et on lance notre script.

Là où ça devient plus intéréssant c'est qu'on peux savoir à quel point le code est propre. Pour cela, il faut que vous alliez dans les paramètres de votre dépôt, que vous activiez les builds, puis que vous rentriez dans Test coverage parsing, cette expression régulière : Coverage : \d+.\d+.

Maintenant, si vous lancez une build, vous aurez un joli affichage de la qualité de votre code.

Qualité du code

Vous remarquerez aussi que j'utilise la colonne Coverage, qui est plutôt faite pour les tests, mais bon, on a pas trop le choix.

Tu en as vraiment besoin ?

Tu en as vraiment besoin ?

Je pense qu'il faudrait ce poser cette question à chaque fois qu'on ajoute quelque chose à un site web.

Aujourd'hui ce que l'utilisateur veut, c'est de la rapidité. Il veut avoir accès à l'information en un instant. Et je ne crois pas que la solution soit Google AMP, ni la mise ne place d'un écran de chargement entre deux page, qui ne fait en fait que ralentir le chargement (puisqu'on doit lui aussi le charger) ou encore la migration vers des serveurs plus puissants.

Et puis, pourquoi croyez vous que les navigateurs se dotent tous d'un mode lecture ? Tout simplement parce que les sites où le contenu devrait être au centre de tout, cherchent avant tout à vous faire visiter des pages, et donc ils ne placent pas le contenu au coeur de leurs pages.

Pour moi, la solution est plus simple. Il suffit de (re)venir vers quelque chose de sobre. Il faut concevoir ses sites de façon minimaliste. Et il faut le faire, aussi bien au niveau du code, que du site en lui-même, se focaliser uniquement sur l'essentiel. Je pense notamment au JavaScript, souvent inutile chargé uniquement pour traquer les visiteurs et les proposer du contenu lié (ce qui peut se faire simplement en fonction de la page actuelle, pas besoin de tout un historique).

Un visiteur ne viens pas sur votre site pour télécharger l'application mobile qui va avec. Il est là pour consulter le contenu. Votre site pourrait se limiter au contenu, sans avoir tous ces liens vers d'autres articles intéréssants, ces pubs pour des régimes, ou cette popup qui propose de s'abonner à votre newsletter.

Je pense qu'il faut aussi ne stocker que le nécéssaire. Les data-centers gigantesques des géants du web, qui contiennent à peu près tout ce que l'on peux savoir d'une personne, sont de véritables machines à polluer : la quantité d'électricité produite pour les faire fonctionner est tout simplement phénomènale. En plus de ça, il faut garder le maximum de chose en local, plutôt que d'envoyer tout sur le cloud. Le minimalisme se marie donc bien avec le logiciel libre.

Si on essaie de suivre ces règles, on peut facilement gagner quelques secondes de chargement et j'imagine quelques visiteurs fidèles. Imaginez un site comme celui du Monde, réduit au minimum. Non seulement le confort de lecture est grandement amélioré, mais en plus le chargement est accéléré.

Donc pour résumer, Bootstrap, JQuery, les boutons de partage «officiels», les scripts pour proposer du contenu lié, voire simplment traquer l'utilisateur, on oublie.

Et pour finir, je vous laisse sur une citation de Saint Exupéry.

Il semble que la perfection soit atteinte non quand il n'y a plus rien à ajouter, mais quand il n'y a plus rien à retrancher.

Héberger son blog sur Gitlab pages

Gitlab pages est un service permettant d'héberger des sites web statiques sur un dépôt Gitlab. Pour le moment, cette fonctionnalité n'est disponible que dans la version EE, c'est-à-dire payante (autrement dit pas de pages sur Framagit ou votre instance perso).

Gitlab pages vous permet uniquement d'héberger des pages statiques, ce qui est parfait pour un blog. Mais, comme il peut vite devenir fastidieux de taper du HTML et qu'on est quand même des geeks, on va utiliser Pelican. Ce petit script Python permet de transformer du markdown en HTML, et d'utiliser des templates Jinja2. En le combinant à Gitlab pages, on peut donc tranquillement rédiger ces textes en markdown, puis les compiler en HTML et les envoyer sur notre dépôt.

Sauf qu'on est des flemmards, et qu'on peut facilement automatiser le tout, à l'aide de Gitlab CI (qui est de toute façon un minimum nécéssaire pour utiliser Gitlab pages).

Cette article sera donc un petit guide pas-à-pas sur la création de votre blog sur Gitlab pages, avec Pelican.

Prérequis

Avant toute chose, assurez-vous d'avoir un compte sur https://gitlab.com. Si c'est le cas, créez un nouveau projet avec pour nom nom_dutilisateur_en_minuscule.gitlab.io (chez moi ça donne bat41.gitlab.io par exemple). Vous pouvez aussi le cloner en local si vous préférez, mais on ne va pas éditer de gros fichiers de code, donc votre IDE dernière génération et ces 56 plugins ne seront pas vraiment nécéssaire ...

Mettre en place Gitlab CI

Bien, maitenant, il va falloir configurer Gitlab CI pour qu'il envoie notre site Gitlab pages. Il faut donc créer un fichier .gitlab-ci.yml à la racine de votre projet. Ensuite, écrivez-y ceci.

image: python:2.7-alpine

pages:
  script:
  - pip install -r requirements.txt
  - pelican -s publishconf.py
  artifacts:
    paths:
    - public/

Peut-être que vous n'avez jamais travaillé avec Gitlab CI et que vous n'y comprenez rien, alors je vais tenter une explication de ce qu'on fait ici. Sur la première ligne, on indique qu'il faut utiliser l'image Docker appelée python-2.7-alpine, qui contient tout ce dont on a besoin pour utiliser Pelican (qui est écrit en Python). Ensuite, dans la partie pages, on décrit ce que doit faire Gitlab pages.

Tout d'abord, il va exécuter les commandes listées dans la partie script. Ici, on installe Pelican, grâce au fichier requirements.txt qu'on va créer juste après. Avec la seconde commande, on génère notre site.

La partie artifacts sert à indiquer quel dossier uploader sur Gitlab pages. Ici, on envoie le dossier public, où Pelican génère notre site.

On a vu qu'on allait devoir créer un fichier requirements.txt qui contiendrait les bibliothèques à installer. Il faut juste y mettre ceci.

pelican
markdown
typogrify

Ces trois bibliothèques sont Pelican et ses dépendances. Désormais, l'installation fonctionnerait, mais pas la compilation car le fichier de configuration de Pelican (publishconf.py) n'exite pas encore. On va y mettre ce code.

#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals

import os
import sys
sys.path.append(os.curdir)
from pelicanconf import *

SITEURL = '/'
RELATIVE_URLS = False

FEED_ALL_ATOM = 'feeds/all.atom.xml'
CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml'

DELETE_OUTPUT_DIRECTORY = True

Ce script ne fait pas grand chose : on configure la racine du site, les adresses des flux ATOM et on dit de supprimer le dossier généré. On y importe aussi le fichier pelicanconf.py qui sert à configurer notre site (on les sépare par convention, mais ici on n'en aurait pas vraiment besoin). On va d'ailleurs le modifier tout de suite. Pour commencer, on va y écrire ce code.

#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals

AUTHOR = u'Moi, le génial auteur de ce blog'
SITENAME = u'Le nom de mon super blog !'
SITEURL = ''

# Les dossiers de Pelican

PATH = 'content' # Le contenu
OUTPUT_PATH = 'public' # Le résultat

TIMEZONE = 'Europe/Paris'

DEFAULT_LANG = u'fr'

# On ne génère pas les flux ATOM et RSS
# Mais ce paramètre est remplacé dans publishconf.py

FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None
AUTHOR_FEED_ATOM = None
AUTHOR_FEED_RSS = None

# Les liens vers vos comptes sociaux

SOCIAL = (('Diaspora*', 'https://diasp.org/u/truc_muche'),
          ('Twitter', 'https://twitter.com/truc_muche'),)

DEFAULT_PAGINATION = 5

SITESUBTITLE = u'Un sous-titre qui est trop bien !'

Je pense que vous comprenez ce que l'on fait ici.

Il ne nous reste plus qu'à créer un post de test. Créez un fichier /content/posts/2016-test.md, puis tapez ceci.

Title: Test !
Date: 2016-04-21
Category: Blog
Tags: blog, test
Slug: test

Je fais un test. Avec du **MarkDown** !

Maintenant, votre site est prêt. Il va falloir lancer Gitlab CI pour qu'il envoie votre site sur Gitlab pages.

Configurer Gitlab CI

Dans le menu gauche de Gitlab, cliquez sur Settings pour configurer votre dépôt. Rendez-vous ensuite dans la section Runners et cliquez sur le bouton vert, Enable shared runners. Vous venez d'activer Gitlab CI.

Maintenant, il va falloir lancer un build. Pour cela, il faut que vous modifiez un peu le code de votre site. Changez par exemple le texte de votre article de test.

Une build devrait se lancer automatiquement. Une fois qu'elle est finie (pour voir où elle en est, allez dans l'onglet Builds) vous pouvez normalement accéder à votre site, à l'adresse https://nom_dutilisateur_en_minuscule.gitlab.io. Si vous n'arrivez pas à y accéder, vous pouvez connaître son adresse exacte en vous rendant dans les paramètres de votre dépôt, puis dans Pages.


J'espère que vous avez réussi à créer votre blog sans trop d'encombres et que ce petit tutoriel vous aura aidé.

Bienvenue

Bienvenue à tous sur ce blog !

Ce blog, c'est mon blog à moi, Bat', ou Baptiste IRL. J'y posterais des articles à propos d'informatique, libre notamment, mais je ne compte pas me limiter à cela.

Les articles traitant d'informatique seront aussi surement publiés sur Zeste de Savoir, dans ma tribune libre, une fois que la fonctionnalité sera disponible.

Quand je dis que je vais parler d'informatique, je penses que je vais parler de Vala, et de mon projet Valse, mais aussi des critiques, des petits projets pour le fun ou de conception de site web. Mais je ne veux pas me limiter à ces sujets, et il est possible que je parle de sujet plus ancrés dans le réel.

J'essaierai aussi de faire en sorte que sur les trois articles que je vais sortir, deux ne soient pas là pour m'excuser de ne pas écrire plus souvent.

Vous remarquerez aussi que les commentaires ne sont pas disponibles. C'est tout simplement parce que j'utilise Pelican pour gérer ce site et que le seul moyen (que je connaisse) d'avoir des commentaires est d'avoir Disqus. Donc, non pas de commentaires. Si vous voulez réagir, vous pouvez m'envoyer un mail à gelezbaptiste at openmailbox point org, commenter sur l'article Zeste de Savoir correspondant, si il existe ou sur le post Diaspora*, si j'y ai partagé l'article.

Sur ce, n'hésitez pas à vous abonner au flux RSS, ou à ajouter le site à vos favoris. Je vous dit à la prochaine. Merci de m'avoir lu !

P.S : Vous remarquerez cette originalité folle dans le nom du site. Un jour je trouverais un meilleur titre. Un jour ...