Loïc Faugeron Technical Blog

Redaktilo 1.3 06/11/2014

TL;DR: Redaktilo is a library which wraps array manipulation to make it feel like using a text editor.

Six months ago, I talked about a silly library I was working on.

I've just released it's version 1.3, so I figured that maybe it's time to talk about it as the mature and usefull project it has become.

Statistics

First, here's some meaningless figures:

History

In my two previous pet projects (GnugatWizardBundle and Fossil), as well as in professional projects, I've encountered the need to manipulate lines (from a file or a static string).

I've done 3 different implementations, each less reusable than the other, so I started to think that there was something to do here.

That's how Redaktilo is born!

Public API

My purpose was to create a single service which would be used as a text editor ("redaktilo" means "editor", in esperanto):

<?php

namespace Gnugat\Redaktilo;

class Editor
{
    // Filesystem operations:
    public function open($filename, $force = false);
    public function save(File $file);

    // Line selection
    public function jumpAbove(Text $text, $pattern, $location = null);
    public function jumpBelow(Text $text, $pattern, $location = null);

    // Line detection
    public function hasAbove(Text $text, $pattern, $location = null);
    public function hasBelow(Text $text, $pattern, $location = null);

    // Line manipulations
    public function insertAbove(Text $text, $addition, $location = null);
    public function insertBelow(Text $text, $addition, $location = null);
    public function replace(Text $text, $replacement, $location = null);
    public function replaceAll(Text $text, $pattern, $replacement);
    public function remove(Text $text, $location = null);

    // You can execute custom commands!
    public function run($name, array $input);
}

This service is stateless, it manipulates the following model:

<?php

namespace Gnugat\Redaktilo;

class Text
{
    public function getLines();
    public function setLines(array $lines);
    public function getLength();

    public function getLineBreak();
    public function setLineBreak($lineBreak);

    public function getCurrentLineNumber();
    public function setCurrentLineNumber($lineNumber);
    public function getLine($lineNumber = null);
    public function setLine($line, $lineNumber = null);
}

Note: The File model extends Text and adds filename getter and setter.

As you can see, everything is centered around a current line, but specifying directly a line number is possible (that's the meaning of the $location argument).

Facade and extension points

To be honest, Editor doesn't do anything, it just wraps some low level services. Amongst those, we have:

Those two are actually here to provide extension points: if you need to look for a line with something else than a regular expression, you can create your own SearchStrategy, register it in SearchEngine and it will be available directly from the Editor!

If the manipulations provided out of the box aren't sufficient, you can create your own Command, register it in CommandInvoker and it will be available directly from the Editor!

Commands rely on user input, which is why Loïck worked on InputSanitizer to validate it. If the ones provided aren't enough, you can still create your own implementations.

Finally, I'd like to highlight Wouter's work on the EditorFatory helper: it is really cumbersome to instantiate manually Editor... But thanks to this factory, you'll never have to do it by yourself!

Note: Working on a Symfony2 project? You can use RedaktiloBundle, which also provides tags to register custom search strategies and commands.

What's next?

Loïck is currently improving the library's exceptions for the version 1.4.

For version 1.5, two new methods will be added to Text: incrementCurrentLineNumber and decrementCurrentLineNumber, to avoid the following:

<?php

require __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\EditorFactory;

$editor = EditorFactory::createEditor();
$file = $editor->open('/tmp/monty-python.txt');

// Currently
$currentLineNumber = $file->getCurrentLineNumber();
$file->setCurrentLineNumber($currentLineNumber + 2);

// Soon...
$file->incrementCurrentLineNumber(2);

For the final version 1.6, a strong documentation of the deprecations will be done, leading the way to version 2.0 where those will be removed (50% of the code base will vanish!).

Note: As I'm using Redaktilo everyday, I might discover new improvements to be done, leading the given version numbers above to change.

Frequently Asked Questions

Is it a GUI / CLI tool?

A question I've been often asked, because describing Redaktilo as a text editor might be a bit misleading. It is a library which feels like an editor, but it is in no way an actual editor you might run.

Here's an example of what you might be doing currently without Redaktilo:

<?php

$lines = file('/tmp/monty-python.txt');
$currentLineNumber = 42;
$section = array_slice($lines, 0, $current, true); // Lines from 0 to 42
$reversedSection = array_reverse($section, true);
$found = preg_grep('/a knewt/', $reversedSection); // finding the first occurence, above line 42
$currentLineNumber = key($found);

array_splice($lines, $currentLineNumber + 1, 0, 'new line below "a knewt"');
$content = implode("\n", $lines); // Let's hope it wasn't a file created on Windows.
file_put_contents($content, '/tmp/monty-python');

With redaktilo:

<?php

require __DIR__.'/vendor/autoload.php';

use Gnugat\Redaktilo\EditorFactory;

$editor = EditorFactory::createEditor();
$file = $editor->open('/tmp/monty-python');
$editor->jumpAbove($file, '/a knewt/', 42);
$editor->insertBelow($file, 'new line below "a knewt"');
$editor->save($file);

Is it a code editor?

Because the code example on the README is about inserting a new line in the AppKernel file of a Symfony2 application, I also get this question.

Redaktilo can edit any text file, so it includes source files. But keep in mind that it only manipulates lines, when you might want to manipulate an AST to edit code.

I'm working on a new silly project: Medio. It heavily relies on Redaktilo to actually edit code: so you can make a code editor with it, but again before you start ask yourself if an AST wouldn't be better.

Conclusion

Redaktilo is a library which makes line manipulation dead easy! It provides extension points and will soon reach version 2 which will mark its final maturity point.

Until then, you can still use it safely, it's been stable for 11 releases now.