Моя жизнь в редакторе Padre

1 октября 2011 г.

Мое знакомство с Padre

Конечно для программирования на Perl раньше я использовал и Emacs и Vim (и использую его сейчас, если нужно что-то быстро отредактировать или просмотреть).Все началось с того, что я посмотрел ролик гениального программиста Сабо Габора, но плагин к Padre у меня не заработал.

YAPC::EU RIGA 2011

Я поехал на YAPC::Europe 2011 “Modern Perl” и там, на кахатоне, Габор объяснил в чем моя ошибка, оказывается я не мог активировать (My.pm) плагин, потому что у меня просто стояла старая версия в переменной и нужно было поменять с 0.40 на 0.66 в следующем коде плагина My.pm:

sub padre_interfaces {
return (
'Padre::Plugin' => 0.66,
'Padre::Constant' => 0.66,
);
}
/code]
Отлично, подумал я, самый простой плагин, показывающий информацию о Padre (self->show_about) заработал:

sub show_about {
my $self = shift;
# Locate this plugin
my $path = File::Spec->catfile(
Padre::Constant::CONFIG_DIR,
qw{ plugins Padre Plugin My.pm }
);
# Generate the About dialog
my $about = Wx::AboutDialogInfo->new;
$about->SetName('My Plug-in');
$about->SetDescription( <<"END_MESSAGE" );
The philosophy behind Padre is that every Perl programmer
should be able to easily modify and improve their own editor.
To help you get started, we've provided you with your own plug-in.
It is located in your configuration directory at:
$path
Open it with with Padre and you'll see an explanation on how to add items.
END_MESSAGE
# Show the About dialog
Wx::AboutBox($about);
return;
}

Может быть можно придумать, что-нибудь поинтереснее.
В начале, у меня была просто задача замены слешей с \ (windows) на / (unix) — задача элементарная, но мне нужно было менять их часто и хотелось один раз уже написать регулярное выражение для этого, повесить на пункт меню, а лучше на
shortcut и забыть об этом, и я написал простой макрос:

sub replace_slash {
my $self = shift;
my $main = $self->main;
my $doc = Padre::Current->document;
my $text = $doc->text_get();
$text =~ s#\\#/#g;
$doc->text_set($text);
return;
}

привязал к меню

sub menu_plugins_simple {
my $self = shift;
return $self->plugin_name => [
'About' => sub { $self->show_about },
'&Replace_slash' => sub { $self->replace_slash },
];
}

и был счастлив, в любое время я его мог теперь вызвать из меню.
Потом мне понадобилось удалять цифры после копирования текста из vi c параметром set nu:

sub replace_start_digit {
my $self = shift;
my $main = $self->main;
my $doc = Padre::Current->document;
my $text = $doc->text_get();
$text =~ s/^\s*[0-9]//mg;
$doc->text_set($text);
return;
}
[/code

<h4>PerlCritic — Graphical Interface from ActivePerl</h4>
Дальше я вспомнил, что мне очень нравится perlcritic от ActiveState и хотелось бы вызывать его прямо из Padre:

sub run_perlcritic_from_activeperl {
my $self = shift;
my $main = $self->main;
my $doc = Padre::Current->document;
my $filename = $doc->filename;
my $exec_shell =
q{C:\Perl\bin\wperl.exe -x "C:\Perl\bin\perlcritic-gui" c:\Users\nmishin\Documents\git\perlcritic\perlcritic_profile.perlcriticrc }
. $filename
. q{ --run};
$main->message($exec_shell);
my $a = run_shell($exec_shell);
return;
}

sub run_shell {
my ($cmd) = @_;
use IPC::Open3 'open3';
use Carp;
use English qw(-no_match_vars);
my @args = ();
my $EMPTY = q{};
my $ret = undef;
my ( $HIS_IN, $HIS_OUT, $HIS_ERR ) = ( $EMPTY, $EMPTY, $EMPTY );
my $childpid = open3( $HIS_IN, $HIS_OUT, $HIS_ERR, $cmd, @args );
$ret = print {$HIS_IN} "stuff\n";
close $HIS_IN or croak "unable to close: $HIS_IN $ERRNO";
; # Give end of file to kid.
if ($HIS_OUT) {
my @outlines = <$HIS_OUT>; # Read till EOF.
$ret = print " STDOUT:\n", @outlines, "\n";
}
if ($HIS_ERR) {
my @errlines = <$HIS_ERR>; # XXX: block potential if massive
$ret = print " STDERR:\n", @errlines, "\n";
}
close $HIS_OUT or croak "unable to close: $HIS_OUT $ERRNO";
#close $HIS_ERR or croak "unable to close: $HIS_ERR $ERRNO";#bad..todo
waitpid $childpid, 0;
if ($CHILD_ERROR) {
$ret = print "That child exited with wait status of $CHILD_ERROR\n";
}
return 1;
}

А на padre irc мне помогли, сказав, как вызывать команду клавиатурной комбинацией — это был талантливейший хакер Alias
и я смог, написав так,

sub menu_plugins_simple {
my $self = shift;
return $self->plugin_name => [
'About' => sub { $self->show_about },
'&Replace_slash' => sub { $self->replace_slash },
'Replace_start_digit' => sub { $self->replace_start_digit },
"Run_perlcritic_from_activeperl\tCtrl-Alt-F3" =>
sub { $self->run_perlcritic_from_activeperl },
];
}

запускать perlcritic с визуальным интерфейсом из Activestate прямо из Padre по Ctrl-Alt-F3.

Улучшаем макрос замены (редактирования)

У макроса, описанного выше есть недостаток — он делает изменения только с 1 строкой текста, я решил исправить это допущение, добавив функцию:

sub change_text {
my $self = shift;
my $replace_text = shift;
my $main = $self->main;
my $doc = Padre::Current->document;
my $text = $doc->text_get();
my @all_text_lines = split "\n", $text;
my $out_line = "";
#change every line in text
for my $current_line (@all_text_lines) {
$out_line .= &$replace_text($current_line);
}
$doc->text_set($out_line);
return;
}

и теперь можно было писать функции следующего вида:

sub jira_link {
my $self = shift;
my $ref_replace_text = sub {
my $in_text = shift;
$in_text =~
s{([A-Z]+-\d+)\s+}{[$1\|http://jira.gto.intranet.db.com:2020/jira/browse/$1]};
return $in_text;
};
change_text( $self, $ref_replace_text );
return;
}

sub replace_space {
my $self = shift;
my $ref_replace_text = sub {
my $in_text = shift;
$in_text =~ s{(\w+)\s+(\w+)\s+(\w+)}{$1,$2,$3};
return $in_text;
};
change_text( $self, $ref_replace_text );
return;
}

sub word_to_sql {
my $self = shift;
my $ref_replace_text = sub {
my $in_text = shift;
$in_text =~ s{(\w+)}{'$1',};
return $in_text;
};
change_text( $self, $ref_replace_text );
return;
}

не задумываясь, как они будут работать в Padre, а размышляя только о правильно регулярном выражении и я даже написал темплейт для нее:

sub template_of_replace {
my $self = shift;
my $main = $self->main;
my $doc = Padre::Current->document;
my $text = $doc->text_get();
my $template = <<'END_MESSAGE';
sub word_to_sql {
my $self = shift;
my $ref_replace_text = sub {
my $in_text = shift; 
###Change this text
$in_text =~ s{(\w+)}{'$1',};
return $in_text;
};
change_text( $self, $ref_replace_text );
return;
}
END_MESSAGE

my $editor = Padre::Current->editor;
$editor->ReplaceSelection('');
my $pos = $editor->GetCurrentPos;
$editor->InsertText( $pos, $template );
return;
}

Эти макросы значительно упростили мне жизнь, тем более, что идеи для них можно легко найти в исходниках падре, например в том же плагине perltidy.

Поиск идей в исходном коде.

Padre обладает удивительным свойством, как в Delphi, при нажатии на название модуля мышкой с зажатой клавишей CTRL, он в массиве @INC находит этот модуль, открывает его в редакторе и мы можем видеть, как он реализован, а в статусной строке можно видеть путь к модулю, по нему можно увидеть, что все модули Padre лежат в директории c:\Strawberry\perl\site\lib\Padre.
А еще лучше сказать
Search->Find in files CTRL-SHIFT-F:

Search->Find in files CTRL-SHIFT-F:

и далее в области поиска написать, например, 'Perl Tidy' (название элемента меню, которое по идее должно быть в искомом модуле):

Perl Tidy

после этого можно увидеть путь к модулю:

Путь к модулю

с нужным функционалом (например, я искал, как работать c выделенным текстом) и открыть его в Padre.

 

Работа с базой данных из Padre

И последнее. На работе я работаю с СУБД Oracle. Модуль DBD::Oracle мне так и не удалось установить на Windows 7, а под cygwin он успешно откомпилировался. И свои тесты я запускаю в cygwin. Также я использую sqlsh. Оказалось, что если вызывать cygwin'овский perl, то DBD::Oracle виден и можно коннектиться к базе.
Используя cygwin_here.bat:

@echo off
REM --- cygwin_here.bat ------------------------------------------------------
REM function: Start Cygwin in current directory
REM args: - 1..9
REM Setting `CHERE_INVOKING' prevents /etc/profile from issuing `cd $HOME'
set CHERE_INVOKING=1
C:\cygwin\bin\bash --login -i %1 %2 %3 %4 %5 %6 %7 %8 %9

Можно вызвать sqlsh из cygwin используя интерактивный режим:

c:\cygwin\cygwin_here.bat sqlsh 
-d DBI:Oracle:BUSINESS.DE.DB.COM
-u mishnik -p password -i 
< c:\Users\nmishin\Documents\svn\misc\chunk_status\sqlsh_command.sqlsh

А также отказавшись от загрузки bash_completion.sh:

mv /etc/profile.d/bash_completion.sh{,.disabled}

можно ускорить загрузку cygwin'а.
И далее я написал обертку для выполнения sql-запросов в Padre:

sub execute_selection_in_oracle {
my $main = shift;
# Tidy the current selected text
my $current = $main->current;
my $text = $current->text;
# Generate the About dialog
#my $about = Wx::AboutDialogInfo->new;
#$about->SetName('Show_selection');
use File::Temp qw/ tempfile tempdir /;
#$fh = tempfile();
#my ( $fh, $out_file ) = tempfile();
my ( undef, $out_file ) = tempfile(
'tmp_sql_XXXXXX',
OPEN => 0,
UNLINK => 0,
DIR => 'c:/Users/nmishin/Documents/git/cygwin',
SUFFIX => '.dat',
);
my ( undef, $log_file ) = tempfile(
'tmp_log_XXXXXX',
OPEN => 0,
UNLINK => 0,
DIR => 'c:/Users/nmishin/Documents/git/cygwin',
SUFFIX => '.dat',
);
my ( undef, $query_file ) = tempfile(
'tmp_qry_XXXXXX',
OPEN => 0,
UNLINK => 0,
DIR => 'c:/Users/nmishin/Documents/git/cygwin',
SUFFIX => '.dat',
);
#my $out_file = get_temp_filename();
if ( $text !~ m/;/xms ) { $text .= ';' }
#print "file:$filename\n";
my $sqlsqh_command = <<"END_SQLSQH_COMMAND";
set multiline on;
ALTER SESSION SET NLS_DATE_FORMAT = 'DD.MM.YYYY HH24:MI';
ALTER SESSION SET CURRENT_SCHEMA = RWA_OWNER;
set log-mode box;
log commands $log_file;
log queries $query_file;
$text
no log;
exit; 
END_SQLSQH_COMMAND

my $main_object = $main->main;
$main_object->message($sqlsqh_command);
open my $out, '>', $out_file
or croak "Couldn't open '$out_file': $OS_ERROR";
print {$out} $sqlsqh_command
or croak "Couldn't write '$out_file': $OS_ERROR";
close $out or croak "Couldn't close '$out_file': $OS_ERROR";
#c:\cygwin\cygwin_here.bat sqlsh -d DBI:Oracle:BUSINESS.DE.DB.COM -u mishnik -p password -i < c:\Users\nmishin\Documents\svn\misc\chunk_status\sqlsh_command.sqlsh
my $exec_shell =
q{c:\cygwin\cygwin_here.bat sqlsh -d DBI:Oracle:BUSINESS.DE.DB.COM -u mishnik -p password -i < }
. $out_file;
$main_object->message($exec_shell);
my $cmd_file = 'c:/Users/nmishin/Documents/git/cygwin/sqlsh_command.bat';
open my $cmd_out, '>', $cmd_file
or croak "Couldn't open '$cmd_file': $OS_ERROR";
print {$cmd_out} $exec_shell
or croak "Couldn't write '$cmd_file': $OS_ERROR";
close $cmd_out or croak "Couldn't close '$cmd_file': $OS_ERROR";
# $main->message($exec_shell);
my $a = run_shell($exec_shell);
my $sql_result = $text . "\n" . read_file($query_file);
my $editor = Padre::Current->editor;
$editor->ReplaceSelection('');
my $pos = $editor->GetCurrentPos;
$editor->InsertText( $pos, $sql_result );
return;

}

И теперь на Ctrl-Alt-F7 у меня выделенный в Padre select запускается в gywin'овском sqlsh и результат возвращается в редактор. Супер. Я счастлив.

Итоговое menu:

sub menu_plugins_simple {
my $self = shift;
return $self->plugin_name => [
'About' => sub { $self->show_about },
'&Replace_slash' => sub { $self->replace_slash },
'Replace_start_digit' => sub { $self->replace_start_digit },
"Run_perlcritic_from_activeperl\tCtrl-Alt-F3" =>
sub { $self->run_perlcritic_from_activeperl },
'Template_of_module' => sub { $self->template_of_module },
"Jira_link\tCtrl-Alt-F5" => sub { $self->jira_link2 },
"Replace_Space\tCtrl-Alt-F6" => sub { $self->seplace_space },
"Word_to_sql" => sub { $self->word_to_sql },
"Template_of_replace" => sub { $self->template_of_replace },
"Execute_selection_in_Oracle\tCtrl-Alt-F7" =>
sub { $self->execute_selection_in_oracle },
'Date_time' => sub { $self->date_time },
# 'A Sub-Menu...' => [
# 'Sub-Menu Entry' => sub { $self->yet_another_method },
# ],
];
}

На github лежит конечный вариант моего плагина. Всем спасибо за внимание.

Теги:
рубрика Программирование
  • Похожие статьи
  • Предыдущие из рубрики