The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Text::Editor::Easy - A perl module to edit perl code with syntax highlighting and more.

VERSION

Version 0.49

WHY ANOTHER EDITOR ?

There are IDE (or editors) that are currently in active development in perl : for instance, Padre and Kephra. Let's be confident that these projects bring us good designing tools that will be, at last, written in perl.

Still, I would like a different IDE that what we can find now, and, as these IDE are still in development, perhaps they won't reach my needs once finished. There are now lots of dynamic langages, like perl. The potential of these dynamic langages is not fully used with a standard IDE and static programmation. I wish we could build a RAD tool (Rapid Application Development) which would generate "dynamic applications" : that is, applications that you can modify while they are running. This editor module will try to be the first part of this tremendous task.

I want the editor to be programmer-oriented : you should be able to use it like a perl module from your perl programs. And I would also like the generated applications from the IDE to be programmer-oriented : the code of these applications should be accessible during execution and should be modifiable. Programmers should help themselves instead of constantly building "user-oriented" applications (a "programmer-oriented" application can still be used by a simple user). Have a look at http://sgrommier.free.fr/perl/ if you want more explanations about that.

This perl editor module comes with a perl editor program : 'Editor.pl' is an application that uses 'Text::Editor::Easy' instances to edit perl code.

The module enables you to manipulate a highly multi-threaded graphical object. Several demos are provided with the program. To run them and have a glance at the capabilities of this module, launch the perl program "Editor.pl". See README file for installation instructions.

SYNOPSIS

The demos (10 demos to be tested from the "Editor.pl" program) will show you better examples of how to call this module.

 use Text::Editor::Easy;
 
 my $editor = Text::Editor::Easy->new;
 
 $editor->insert("Hello world\nSecond line\nlast line");
 
 $editor->save("my_file.tst");

WHY MULTI-THREAD ?

This module is object-oriented. Once an instance is created, numerous methods are accessible (maybe too much, for now !). New methods can be added on the fly with, why not, new threads associated with these new methods.

Sometimes, you need to consume CPU to achieve your goal. But this shouldn't block the user who interactively use your graphical module : the interface of the module (especially, method "create_new_server") allows you to create threads as simply as you create a new variables. See module Text::Editor::Easy::Comm for the thread mecanism.

Using threads, you can make 'real interactive' applications : a 'Cancel' button that works, for instance. All you have to do is to work in an interruptible way which is not possible in a mono-thread application. Thus, graphical applications (with interactive users) should always be multi-threaded.

Threads are not only used for speed or interactivity. With private variables, they allow you to partition your code. So you don't have a large program with a huge amount of data to manage but a lot of little threads, specialized in a much simpler task with fewer variables to manage. The only remaining problem is how to communicate with all these "working together threads" : the Text::Editor::Easy::Comm provide the solution. All you have to do is define a new thread associated with your new methods. When the new methods are called (by any thread, the new created one or any other one), your new thread will be called automatically and the response will be automatically provided to the initial calling thread (in the context of the caller). Easy, isn't it ! Again, see module Text::Editor::Easy::Comm for the thread mecanism.

GRAPHIC

The graphical part of the module is handled mainly by Text::Editor::Easy::Abstract. The "Abstract" name has been given because, even if I use Tk for now, there is no Tk calls in all the Abstract module. Tk calls are concentrated in Text::Editor::Easy::Graphic::Tk_Glue module : other "Graphic glue" modules are possible. I think of "Gtk", "Console", and why not "Qt" or "OpenGl" ? There is a limited communicating object (a "Text::Editor::Easy::Graphic") between the Abstract module and the glue module : this is the interface. This interface may change a little in order to allow other "Glue module" to be written, but, of course, all graphic glue modules will have to use the same interface.

You can see the "Text::Editor::Easy" as a super graphical layer above other layers. I imagine a generator where you design an application in your preferred graphical user interface but the generated application could run (maybe in a limited way) in "Console mode". Constant re-use is the key to hyper-productivity.

METHODS

NEW

 my $editor = Text::Editor::Easy->new(
     {
         'file'   => 'my_file.t3d',
         'events' => {
             'clic' => {
                 'sub' => 'my_clic_sub',
             },
         }
     }
 );

This function creates and returns a Text::Editor::Easy instance. A Text::Editor::Easy instance is a scalar reference so that you can't do anything with it... except call any object method. This function accepts either no parameter or a hash reference which defines the options for the creation. Here are these options :

zone

file

growing_file

save_info

bloc

focus

sub

'events' option, event management

If you want to define a special behavior in response to user events you have to write special code and reference this code so that it can be executed. You can reference this code during the instance creation.

To have more information about 'Events', look at Text::Editor::Easy::Events.

INSERT

    $editor->insert("Hello");    # Simple insert
    
    # Multi-lines insert with list context
    my @lines = $editor->insert("\n\nA non empty line\nAnother non empty line\n\n");
    
    # Using options
    my $last_line = $editor->insert(
        "...adding data\nLast line",
        {
            'line' => $lines[3],
            'cursor' => 'at_start',
        }
    );

The insert method encapsulates horrible code for you (and believe me, my code is terrible !). It accepts one or 2 parameters : the first is the string to be inserted, the second, optional, is a hash reference that may change default behavior.

The string can be a single character or several millions of them. Only, you should know that, from time to time, a carriage return (or line feed, or both : just "\n" in perl) should separate your string in reasonably short lines. Perl has no limit except your memory, but the Text::Editor::Easy module would slow down badly if it had to display lines of several thousands characters.

By default, the insertion is made at the cursor position and the cursor position is updated to the last character of the string you have inserted. If the cursor is visible before the insertion, the cursor remains visible at the end of the insertion (maybe at the bottom of the screen). Note that insert method may replace text too if the "Inser" key have been pressed (or set) for the editor instance, but this happens only for the first line.

This method returns Text::Editor::Easy::Line instance(s). In scalar context, only the last inserted line is returned. In list context, all inserted or changed lines are returned. If your string does not contain any "\n", then scalar and list context return the same thing as only one line is changed. You shouldn't use list context for huge string (the 'line' instance creation consumes memory and CPU).

The hash reference, with its options, allows you to modify default behavior :

  • insertion point before the insertion

  • how to insert text

  • cursor position after the insertion

  • how to display things after the insertion

Why have all these options been added to the basic 'insert' method ? Because an 'insert' call is all that : an insertion point is chosen, text is inserted in a precise way, cursor position is changed and, if text is long enough, your editor may look quite different. As all these things are done implicitly, it seems legitim that options let you define each step explicitly.

insertion point, 'line' and 'pos' options

By default, the insertion is made at the cursor position. You can change that using 'line' and 'pos' options. The 'line' option must specify a valid 'line' instance. The 'pos' option indicates the position of the insertion point in the line. You may use only one of these 2 options :

  • if only the 'pos' option is provided, the line remains the line where the cursor was before insertion : you just change the position in that line.

  • if only the 'line' option is provided, the default position is the end of the line you have given.

Note that if you use an insertion point option (either 'pos' or 'line', or both), the cursor position is no more changed by default : it remains where it was before the insertion unless you specify a cursor position.

how to insert text, 'replace' option

    'replace' => 1, # will replace text (only in the first line)

By default, the editor uses the current "insert" config to insert the text. The config is linked to the "Inser" key and if the user have pressed it. You can force your "insert" config using the 'replace' option. Set to 1 (or true), existing text will be replaced (only in the first line and according to the length of your first inserted line too). Set to 0 (or false), text will be inserted.

cursor position, 'cursor' option

This option tells where to set the cursor after the insertion has been made :

 'cursor' => 'at_start',

will set the cursor before the first character that has been inserted. In fact, this option souldn't move the cursor unless you have used an insertion point.

 'cursor' => 'at_end',

will set the cursor after the last character that has been inserted. This is the default behavior of insert method (only useful when you have changed the default insertion point).

You can also use a reference of array with 2 values (the second is optionnal) to set the cursor position.

 'cursor' => [ 'line_0', 3 ];      # will set the cursor at the position 3 in the first line modified by insert
                                  # 'line_' is followed by the number of the inserted line
 'cursor' => [ 'line_2', 0 ];      # will position the cursor at the beginning of the 3rd inserted line
 'cursor' => [ 'line_2' ];         # will position the cursor at the end of the 3rd inserted line
 'cursor' => [ 'line_end', 0 ];    # will position the cursor at the beginning of the last inserted line
 'cursor' => [ $line, $number ];   # will position the cursor in line $line (line instance), position $number (integer)

Thanks to line_$number syntax, you can indicate cursor position on lines that do not exist before your call (because the call is creating them). You can still use an already existing line to set the cursor position (last example). In that case, you provide the 'line' instance in first position. With no second parameter, cursor will be set at the end of the line. Note that 'line_0' is not really useful as the first line always exists before insertion (it's either $editor->cursor->line or the insertion point you have chosen).

displaying after insertion, 'display' option

 'display' => [ 'line_2', { 'at' => 20, 'from' => 'middle' } ]; # the reference line is the second inserted line
 'display' => [ $editor->number(10) ];  # It's the line number 10 before the call ! Insertion can change this order...

Display option is an integrated call to the display method. As for the 'cursor' option, you may use a line_$number syntax to give the reference line that will be used for the display. Otherwise, the syntax is the same as the original display method : as 2 parameters may be provided, the 'display' option of the insert method is an array reference.

other options

There is an 'assist' option that can call specific sub to make special action each time a particular text have been inserted (typically, adding other text...). But the interface of this option is not yet fixed. In the same way, there will be a way to inhibit the event management as, at present, there is one event generated by an insert (the 'change' event). Maybe 'assist' option will be replaced by a generalized event management...

DISPLAY

    $editor->display( 
        $editor->number(23),
        {
            'at' => 'middle',
            'from' => 'middle',
        }
    );

The display method needs at least one parameter : the 'line' instance or 'display' instance. The second, a hash reference, is optionnal.

If your editor is not visible because it's under another one (see Text::Editor::Easy::Zone), you'll make it visible using "FOCUS" or "AT_TOP" methods. The display method is used to show the editor in a precise way. Displaying an editor doesn't mean much if you don't take a reference. The reference is the first parameter which is a line or a part of it with wrap mode enabled ('display'). When you have defined this reference, you can add options to precise where to put this reference in the screen.

By default (with no second parameter), the top of the 'line' (or 'display) will be at the 'middle of the top', that is, it's top ordinate will be one quarter of the screen height.

You can change where to display the reference line with 'at' and 'from' options.

'at' option

This option gives the ordinate (which is, by default, one quarter of the screen height). You can use 'top', 'bottom' or 'middle' values or an integer. The integer value will position the line precisely in the screen. But you should have receveid the number you give by another method rather than have chosen it yourself : this integer is the number of pixels from the top for graphical interface but will be the line number in console mode (in console mode, you can't be more precise than a line height).

    $editor->display( 
        $editor->number(23), { 'at' => $my_ordinate } # $my_ordinate should be an integer (or 'bottom', 'middle' or 'top')
    );

'from' option

As lines have a height, displaying a line at a precise ordinate doesn't tell what part of the line will be located at this ordinate. By default, it's the top of the line. For the 'from' option, you can use 'top', 'middle' and 'bottom'. Note that this option may change things a lot if the reference line is a multi-line : wrap mode and several displays for this line.

'no_check' option

By default, there may be adjustments. If the first line of the editor is under the top, or the last line over the bottom with sufficient lines to fill the screen, your precise positioning will be changed. You can avoid adjustments by setting the 'no_check' option to true.

FOCUS

Set the focus to the editor. It will be placed on top of the screen to be visible and will have the 'focus'. Any pressed key will be redirected to it : if the cursor belongs to a line that is displayed, it should be visible.

AT_TOP

Place the editor on top of the screen to make it visible. No action is made if editor was already on top or had the focus.

TRACE MECANISM

You can redirect standard prints and make special prints on debug files.

STANDARD PRINTS (STDOUT / STDERR)

There are 3 possible states for standard prints :

  • No redirection

  • Redirection in a file with no default action, but specific user actions are possible.

  • Redirection in a file with a complete analysis which is saved and can be checked later. Specific user actions are still possible.

A specific user action is just a user sub that is called each time a print matches some user conditions.

By default, there is no redirection.

With a complete analysis, a trace thread is created : this thread managed a small database (with SDBM_File) of all prints. A link is made between the exact position of the print in the redirection file and data (saved in other files) explaining the print :

  • Destination (STDOUT or STDERR)

  • Complete stack call at the time of the print

  • Which thread made the print and for what call_id.

Moreover, with a complete analysis, all calls between threads are traced the same way : an other small database, managed by the same thread, saves information on each 'call_id'.

If we use these 2 databases together, all prints can be traced in a 'multi-threaded' way.

Tracing STDIN / STDOUT from the start

 # Tracing from the very start : in a BEGIN bloc
 
 use Text::Editor::Easy { 
     'trace' => {
         'print' => {
             # Interface to be written
         }
     }
 };

Another possibility when redirection is not so urgent :

 use Text::Editor::Easy;
 
 # Tracing from the start of the execution (no instruction between the use and the configure)
 
 Text::Editor::Easy->configure( { 
     'trace' => {
         'print' => {
            # Configure method to be written
         }
     }
 } );

In the first case, the redirection is made in a BEGIN bloc (executed as soon as parsed) wheras in the second case, it's made during execution.

Generally, whith the "use" syntax, after the name of the module to use, you put a list of words that will be imported in your name space. But if the list contains just one parameter and if this parameter is a hash, you can do more than just import names : you can configure lots of things in a BEGIN bloc. This might be useful to have 'prints' from further BEGIN blocs (other 'use' calls...) catched for further analysis...

So, the 'configure' class method accepts a hash as single parameter. The 'configure' method can be called automatically with the 'use' syntax : the 'use' parameter is the same as the one in the configure method.

For the 'trace mecanism', only the 'trace' key is used. The value of this key is another hash. This inside hash accepts 'print' and 'full' keys :

  • print :

  • full :

Changing STDIN / STDOUT state

User action for 'selected prints'

SPECIAL PRINTS ON DEBUG FILES

In order to debug a special module, you can add 'special prints' on a special 'file handle'. Let's call this file a 'debug file'.

This special file has the following properties :

  • If you don't want traces any more, you can use a global option to unswitch all the 'debug prints' without deleting the print sentences.

  • You can trace modules as a whole (no traces in all modules, traces in all module), or trace them independantly (trace 'Foo.pm' and 'Bar.pm' only).

  • There can be 'redirection sequences' in order to debug a special function of the module : in this case, a new single file will contain the 'debug prints' of the function and for only one call. After the sequence, prints are made as usual on the global 'debug file' of the module.

To sum up, a print on the 'debug file' makes one the following action :

  • nothing, if traces are 'off' for this module (and if no sequence has been started)

  • a print on the global 'debug file' of the module (traces are 'on' for this module and no sequence has been started)

  • a print on a 'debug sequence file', the number of these files being linked to the module activity

The interface is too light at present ('manage_debug_file' funtion) and should be set.

FUNCTIONS

append

Insert text at the end of the "Text::Editor::Easy" instance. Not yet finished (only one line can be inserted, now).

cursor

Returns a Cursor object from a "Text::Editor::Easy" instance. See "Text::Editor::Easy::Cursor" for a list of methods available for the Cursor object.

delete_key

Delete one character. Used with "Text::Editor::Easy::Abstract". Should not be part of the interface...

display

Allow the use of Line object with the "display" instance method : the internal reference of the Line object, an integer, is given to the display sub of "Text::Editor::Easy::Abstract". Line objects are scalar reference (for encapsulation), and they can't be recovered between threads after a dump of the structure (the substitution is done by the calling thread but the "display execution" is done by the graphical thread with the help of the "File_manager" thread).

erase_text

Delete one or more characters. Used with "Text::Editor::Easy::Abstract". Should not be part of the interface...

file_name

In scalar context, this method returns the name of the file (without the path) if any (undef if the Text::Editor::Easy instance is a memory edition). In list context, returns the absolute path, the file name (without path), the relative path (to current path) and the name of the instance.

first

Returns a Line object that represents the first line of the "Text::Editor::Easy" instance.

get_in_zone

Given the name of a Zone object and a number, this class method returns the instance of the Text::Editor::Easy object if it is found. Undef otherwise :

get_line_number_from_ref

last

last_current

Class method : returns the editor instance who had the focus when ctrl-f was pressed.

manage_event

name

number

regexp

revert

save

save_action

screen

slurp

Call to editor_visual_search : replacement of line object (scalar reference, memory adress specific to one thread) by the internal reference of the line (common for all threads).

whose_file_name

Class method. Returns a "Text::Editor::Easy" object whose file name is the parameter given.

whose_name

Class method. Returns a "Text::Editor::Easy" object whose name is the parameter given.

kill

Maybe destroy would be a better name...

AUTHOR

Sebastien Grommier, <sgrommier at free.fr>

BUGS

This module is moving fast. Bugs are not yet managed.

Maybe you'd like to know that I started writing this Editor from scratch. I didn't take a single line to any existing editor. The very few editors I had a glance at were too tightly linked to a graphical user interface. Maybe you obtain faster execution results like that, but you do not recycle anything. I wanted an engine which you could plug to, a little like perl has been designed.

SUPPORT

The best support for this module is the "Editor.pl" program. Read the README file to install the module and launch the "Editor.pl" program.

To be in an editor allows you to display information interactively. Full documentation will be accessible from here with version 1.0.

In future versions, there will be a "video mode" : perl code to make the images and ogg files for the sound. These videos will cost almost nothing in space compared to actual compressed videos (the sound will be, indeed, the heaviest part of them).

All softwares should include "help videos" like what I describe : it would prove that what you are about to use is easy to manipulate and it would give you a quick interactive glance of all the possibilities. But most softwares are awfully limited (or just don't have the ability) when you want to drive them from a private program (yet, interactively, it's often very pretty, but I don't mind : I want POWER !). In my ill productive point of view, most softwares should be written again...

This is the reason why I'm designing the editor module and the editor program at the same time : the program asks for new needs, and the editor module grows according to these needs. When the editor program will be usable, the module should be powerful enough to be used by anybody, including the RAD tool and the applications generated by the RAD tool.

COPYRIGHT & LICENSE

Copyright 2008 - 2009 Sebastien Grommier, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.