Changeset 2233


Ignore:
Timestamp:
12/28/08 22:03:07 (3 years ago)
Author:
adamk
Message:

Starting Padre::Document refactoring by reorganising the methods into sections.

Padre::Document->setup was doing nothing, merge it back into new

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Padre/lib/Padre/Document.pm

    r2225 r2233  
    55=head1 NAME 
    66 
    7 Padre::Document - Padre Document Abstraction Layer 
     7Padre::Document - Padre Document API 
    88 
    99=head1 DESCRIPTION 
    1010 
    11 This is an internal module of L<Padre> that provides a  
    12 logical document abstraction, allowing Padre to associate  
    13 several Wx elements with the one document. 
    14  
    15 The objective would be to allow the use of this module without 
    16 loading Wx. 
    17  
    18 Currently there are still interdependencies that need to be cleaned. 
     11The B<Padre::Document> class provides a base class, default implementation 
     12and API documentation for document type support in L<Padre>. 
     13 
     14As an API, it allows L<Padre> developers and plugin authors to implement 
     15extended support for various document types in Padre, while ensuring that 
     16a naive default document implementation exists that allows Padre to provide 
     17basic support (syntax highlighting mainly) for many document types without 
     18the need to install extra modules unless you need the extra functionality. 
     19 
     20=head2 Document Type Registration 
     21 
     22Padre uses MIME types as the fundamental identifier when working with 
     23documents. Files are typed at load-time based on file extension (with a 
     24simple heuristic fallback when opening files with no extension). 
     25 
     26Many of the MIME types are unofficial X-style identifiers, but in cases 
     27without an official type, Padre will try to use the most popular 
     28identifier (based on research into the various language communities). 
     29 
     30Each supported mime has a mapping to a Scintilla lexer (for syntax 
     31highlighting), and an optional mapping to the class that provides enhanced 
     32support for that document type. 
     33 
     34Plugins that implement support for a document type provide a 
     35C<registered_documents> method that the PluginManager will call as needed. 
     36 
     37Plugin authors should B<not> load the document classes in advance, they 
     38will be automatically loaded by Padre as needed. 
     39 
     40Padre does B<not> currently support opening non-text files. 
    1941 
    2042=head1 METHODS 
     
    3052use Padre::Util    (); 
    3153use Padre::Wx      (); 
    32 use Padre; 
     54use Padre          (); 
    3355 
    3456our $VERSION = '0.22'; 
    3557 
     58# NOTE: This is probably a bad place to store this 
    3659my $unsaved_number = 0; 
    3760 
    38 # see Wx-0.86/ext/stc/cpp/st_constants.cpp for extension 
    39 # There might be some constants are defined in  
    40 #  wxWidgets-2.8.8/contrib/include/wx/stc/stc.h  
    41 # but not (yet) in  
    42 #  Wx-0.86/ext/stc/cpp/st_constants.cpp 
    43 # so we have to hard-code their numeric value. 
    44  
    45     # asp => wxSTC_LEX_ASP, #in ifdef 
    46 #   , 
    47 #   f     => wxSTC_LEX_FORTRAN, 
    48     #     => wxSTC_LEX_VB, # What's the difference between VB and VBSCRIPT? 
    49  
    50 # partially made-up MIME-types; some parts extracted from /etc/mime.types 
    51 # Someone should go over and see if there are official mime-type definitions 
    52 # missing from the languages list 
     61 
     62 
     63 
     64 
     65##################################################################### 
     66# Document Registration 
     67 
     68# This is the primary file extension to mime-type mapping 
    5369our %EXT_MIME = ( 
    5470    ada   => 'text/x-adasrc', 
     
    92108); 
    93109 
    94 our %MIME_CLASS = ( 
    95     'text/x-pod'             => 'Padre::Document::POD', 
    96     'application/x-perl'     => 'Padre::Document::Perl', 
    97 ); 
    98  
    99 # Document types marked here with CONFIRMED have be checked to confirm that 
    100 # the MIME type is either the official type, or the primary one in use by 
    101 # the relevant language communities. 
     110# This is the mime-type to Scintilla lexer mapping. 
     111# Lines marked with CONFIRMED indicate that the mime-typehas been checked 
     112# to confirm that the MIME type is either the official type, or the primary 
     113# one in use by the relevant language community. 
    102114our %MIME_LEXER = ( 
    103115    'text/x-adasrc'          => Wx::wxSTC_LEX_ADA,       # CONFIRMED 
    104116    'text/x-asm'             => Wx::wxSTC_LEX_ASM,       # CONFIRMED 
    105     'application/x-bat'      => Wx::wxSTC_LEX_BATCH,     # CONFIRMED (application/x-msdos-program includes .exe and .com) 
     117 
     118    # application/x-msdos-program includes .exe and .com) 
     119    'application/x-bat'      => Wx::wxSTC_LEX_BATCH,     # CONFIRMED 
     120 
    106121    'text/x-c++src'          => Wx::wxSTC_LEX_CPP,       # CONFIRMED 
    107122    'text/css'               => Wx::wxSTC_LEX_CSS,       # CONFIRMED 
     
    126141    'application/x-tcl'      => Wx::wxSTC_LEX_TCL,       # CONFIRMED 
    127142    'text/vbscript'          => Wx::wxSTC_LEX_VBSCRIPT,  # CONFIRMED 
    128     'text/xml'               => Wx::wxSTC_LEX_XML,       # CONFIRMED (text/xml specifically means "human-readable XML") 
     143 
     144    # text/xml specifically means "human-readable XML". 
     145    # This is prefered to the more-generic application/xml 
     146    'text/xml'               => Wx::wxSTC_LEX_XML,       # CONFIRMED 
     147 
    129148    'text/x-yaml'            => Wx::wxSTC_LEX_YAML,      # CONFIRMED 
    130149    'application/x-pir'      => Wx::wxSTC_LEX_CONTAINER, # CONFIRMED 
     
    132151    'application/x-perl6'    => Wx::wxSTC_LEX_CONTAINER, # CONFIRMED 
    133152    'text/plain'             => Wx::wxSTC_LEX_NULL,      # CONFIRMED 
     153); 
     154 
     155# This is the mime-type to document class mapping 
     156our %MIME_CLASS = ( 
     157    'application/x-perl'     => 'Padre::Document::Perl', 
     158    'text/x-pod'             => 'Padre::Document::POD', 
    134159); 
    135160 
     
    164189      filename => $file, 
    165190  ); 
    166   
    167191 
    168192$file is optional and if given it will be loaded in the document 
    169193 
    170194mime-type is defined by the guess_mimetype function 
    171  
    172 TODO describe 
    173  
    174  $editor is required and is a Padre::Wx::Editor object 
    175195 
    176196=cut 
     
    180200    my $self  = bless { @_ }, $class; 
    181201 
    182     $self->setup; 
     202    if ( $self->{filename} ) { 
     203        $self->load_file; 
     204    } else { 
     205        $unsaved_number++; 
     206        $self->{newline_type} = $self->_get_default_newline_type; 
     207    } 
    183208 
    184209    unless ( $self->get_mimetype ) { 
     
    207232} 
    208233 
     234sub last_sync { 
     235    return $_[0]->{_timestamp}; 
     236} 
     237 
     238 
     239 
     240 
     241 
     242##################################################################### 
     243# Bad/Ugly/Broken Methods 
     244# These don't really completely belong in this class, but there's 
     245# currently nowhere better for them. Some break API boundaries... 
     246# NOTE: This is NOT an excuse to invent somewhere new that's just as 
     247# innappropriate just to get them out of here. 
     248 
    209249sub guess_mimetype { 
    210250    my $self = shift; 
    211251 
    212     # default mime-type of new files, should be configurable in the GUI 
    213     if (not $self->filename) { 
     252    # Default mime-type of new files, should be configurable in the GUI 
     253    unless ( $self->filename ) { 
    214254        return 'application/x-perl'; 
    215255    } 
     
    235275} 
    236276 
    237 sub setup { 
    238     my $self = shift; 
    239     if ( $self->{filename} ) { 
    240         $self->load_file; 
    241     } else { 
    242         $unsaved_number++; 
    243         $self->{newline_type} = $self->_get_default_newline_type; 
    244     } 
    245 } 
    246  
    247 sub get_title { 
    248     my $self = shift; 
    249     if ( $self->{filename} ) { 
    250         return File::Basename::basename( $self->{filename} ); 
    251     } else { 
    252         my $str = sprintf(Wx::gettext("Unsaved %d"), $unsaved_number); 
    253  
    254         # A bug in Wx requires a space at the front of the title 
    255         # (For reasons I don't understand yet) 
    256         return ' ' . $str; 
    257     } 
    258 } 
    259  
    260277# For ts without a newline type 
    261278# TODO: get it from config 
     
    279296    # TODO get from config 
    280297    return 0; 
     298} 
     299 
     300 
     301 
     302 
     303 
     304##################################################################### 
     305# Disk Interaction Methods 
     306# These methods implement the interaction between the document and the 
     307# filesystem. 
     308 
     309sub is_new { 
     310    return !! ( not defined $_[0]->filename ); 
     311} 
     312 
     313sub is_modified { 
     314    return !! ( $_[0]->editor->GetModify ); 
     315} 
     316 
     317sub is_saved { 
     318    return !! ( defined $_[0]->filename and not $_[0]->is_modified ); 
     319} 
     320 
     321# Returns true if this is a new document that is too insignificant to 
     322# bother checking with the user before throwing it away. 
     323# Usually this is because it's empty or just has a space or two in it. 
     324sub is_unused { 
     325    my $self = shift; 
     326    return '' unless $self->is_new; 
     327    return 1  unless $self->is_modified; 
     328    return 1  unless $self->text_get =~ /[^ \t]/s; 
     329    return ''; 
     330} 
     331 
     332# Returns true if file has changed on the disk  
     333# since load time or the last time we saved it. 
     334# Check if the file on the disk has changed 
     335# 1) when document gets the focus (gvim, notepad++) 
     336# 2) when we try to save the file (gvim) 
     337# 3) every time we type something ???? 
     338sub has_changed_on_disk { 
     339    my ($self) = @_; 
     340    return 0 if not defined $self->filename; 
     341    return 0 if not defined $self->last_sync; 
     342    return $self->last_sync < $self->time_on_file ? 1 : 0; 
     343} 
     344 
     345sub time_on_file { 
     346    return 0 if not defined $_[0]->filename; 
     347    return 0 if not -e $_[0]->filename; 
     348    return (stat($_[0]->filename))[9]; 
    281349} 
    282350 
     
    386454} 
    387455 
     456=pod 
     457 
     458=head2 reload 
     459 
     460Reload the current file discarding changes in the editor. 
     461 
     462Returns true on success false on failure. Error message will be in $doc->errstr; 
     463 
     464TODO: In the future it should backup the changes in case the user regrets the action. 
     465 
     466=cut 
     467 
     468sub reload { 
     469    my ($self) = @_; 
     470 
     471    my $filename = $self->filename or return; 
     472    return $self->load_file; 
     473} 
     474 
     475 
     476 
     477 
     478##################################################################### 
     479# Basic Content Manipulation 
     480 
     481sub text_get { 
     482    $_[0]->editor->GetText; 
     483} 
     484 
     485sub text_set { 
     486    $_[0]->editor->SetText($_[1]); 
     487} 
     488 
     489sub text_like { 
     490    my $self = shift; 
     491    return !! ( $self->text_get =~ /$_[0]/m ); 
     492} 
     493 
     494 
     495 
     496 
     497 
     498##################################################################### 
     499# GUI Integration Methods 
     500 
     501# Determine the Scintilla lexer to use 
    388502sub lexer { 
    389503    my $self = shift; 
     
    393507} 
    394508 
    395 sub is_new { 
    396     return !! ( not defined $_[0]->filename ); 
    397 } 
    398  
    399 sub is_modified { 
    400     return !! ( $_[0]->editor->GetModify ); 
    401 } 
    402  
    403 # check if the file on the disk has changed 
    404 # 1) when document gets the focus (gvim, notepad++) 
    405 # 2) when we try to save the file (gvim) 
    406 # 3) every time we type something ???? 
    407  
    408 # returns if file has changed on the disk  
    409 # since load time or the last time we saved 
    410 sub has_changed_on_disk { 
     509# What should be shown in the notebook tab 
     510sub get_title { 
     511    my $self = shift; 
     512    if ( $self->{filename} ) { 
     513        return File::Basename::basename( $self->{filename} ); 
     514    } else { 
     515        my $str = sprintf(Wx::gettext("Unsaved %d"), $unsaved_number); 
     516 
     517        # A bug in Wx requires a space at the front of the title 
     518        # (For reasons I don't understand yet) 
     519        return ' ' . $str; 
     520    } 
     521} 
     522 
     523sub remove_color { 
    411524    my ($self) = @_; 
    412     return 0 if not defined $self->filename; 
    413     return 0 if not defined $self->last_sync; 
    414     return $self->last_sync < $self->time_on_file ? 1 : 0; 
    415 } 
    416  
    417 sub time_on_file { 
    418     return 0 if not defined $_[0]->filename; 
    419     return 0 if not -e $_[0]->filename; 
    420     return (stat($_[0]->filename))[9]; 
    421 } 
    422  
    423 sub last_sync { 
    424     return $_[0]->{_timestamp}; 
    425 } 
    426  
    427 # A new document that isn't worth saving 
    428 sub is_unused { 
     525 
     526    my $editor = $self->editor; 
     527    # TODO this is strange, do we really need to do it with all? 
     528    for my $i ( 0..31 ) { 
     529        $editor->StartStyling(0, $i); 
     530        $editor->SetStyling($editor->GetLength, 0); 
     531    } 
     532 
     533    return; 
     534} 
     535 
     536# TODO: experimental 
     537sub get_indentation_style { 
    429538    my $self = shift; 
    430     return '' unless $self->is_new; 
    431     return 1  unless $self->is_modified; 
    432     return 1  if     $self->text_get eq ''; 
    433     return ''; 
    434 } 
    435  
    436 sub is_saved { 
    437     return !! ( defined $_[0]->filename and not $_[0]->is_modified ); 
    438 } 
    439  
    440 =pod 
    441  
    442 =head2 reload 
    443  
    444 Reload the current file discarding changes in the editor. 
    445  
    446 Returns true on success false on failure. Error message will be in $doc->errstr; 
    447  
    448 TODO: In the future it should backup the changes in case the user regrets the action. 
     539    my $config = Padre->ide->config; 
     540 
     541    # TODO: (document >) project > config 
     542 
     543    my $style; 
     544    if ($config->{editor_auto_indentation_style}) { 
     545        # TODO: This should be cached? What's with newish documents then? 
     546        $style = $self->guess_indentation_style; 
     547    } 
     548    else { 
     549        $style = { 
     550            use_tabs    => $config->{editor_use_tabs}, 
     551            tabwidth    => $config->{editor_tabwidth}, 
     552            indentwidth => $config->{editor_indentwidth}, 
     553        }; 
     554    } 
     555     
     556    return $style; 
     557} 
     558 
     559=head2 set_indentation_style 
     560 
     561Given a hash reference with the keys C<use_tabs>, 
     562C<tabwidth>, and C<indentwidth>, set the document's editor's 
     563indentation style. 
     564 
     565Without an argument, falls back to what C<get_indentation_style> 
     566returns. 
    449567 
    450568=cut 
    451569 
    452 sub reload { 
    453     my ($self) = @_; 
    454  
    455     my $filename = $self->filename or return; 
    456     return $self->load_file; 
    457 } 
    458  
    459 =pod 
    460  
    461 =head2 check_syntax_in_background 
    462  
    463 NOT IMPLEMENTED IN THE BASE CLASS 
    464  
    465 Checking the syntax of documents can take a long time. 
    466 Therefore, this method essentially works the same as 
    467 C<check_syntax>, but works its magic in a background task 
    468 instead. That means it cannot return the syntax-check 
    469 structure but instead optionally calls a callback 
    470 you pass in as the C<on_finish> parameter. 
    471  
    472 If you don't specify that parameter, the default 
    473 syntax-check-pane updating code will be run after finishing 
    474 the check. If you do specify a callback, the first parameter 
    475 will be the task object. You can 
    476 run the default updating code by executing the 
    477 C<update_gui()> method of the task object. 
    478  
    479 By default, this method will only check the syntax if 
    480 the document has changed since the last check. Specify 
    481 the C<force =E<gt> 1> parameter to override this. 
    482  
    483 =head2 check_syntax 
    484  
    485 NOT IMPLEMENTED IN THE BASE CLASS 
    486  
    487 See also: C<check_syntax_in_background>! 
    488  
    489 By default, this method will only check the syntax if 
    490 the document has changed since the last check. Specify 
    491 the C<force =E<gt> 1> parameter to override this. 
    492  
    493 An implementation in a derived class needs to return an arrayref of 
    494 syntax problems. 
    495  
    496 Each entry in the array has to be an anonymous hash with the  
    497 following keys: 
    498  
    499 =over 4 
    500  
    501 =item * line 
    502  
    503 The line where the problem resides 
    504  
    505 =item * msg 
    506  
    507 A short description of the problem 
    508  
    509 =item * severity 
    510  
    511 A flag indicating the problem class: Either 'B<W>' (warning) or 'B<E>' (error) 
    512  
    513 =item * desc 
    514  
    515 A longer description with more information on the error (currently  
    516 not used but intended to be) 
    517  
    518 =back 
    519  
    520 Returns an empty arrayref if no problems can be found. 
    521  
    522 Returns B<undef> if nothing has changed since the last invocation. 
    523  
    524 Must return the problem list even if nothing has changed when a  
    525 param is present which evaluates to B<true>. 
     570sub set_indentation_style { 
     571    my $self   = shift; 
     572    my $style  = shift || $self->get_indentation_style; 
     573    my $editor = $self->editor; 
     574 
     575    # The display width of literal tab characters (ne "indentation width"!) 
     576    $editor->SetTabWidth( $style->{tabwidth} ); 
     577 
     578    # The actual indentation width in COLUMNS! 
     579    $editor->SetIndent( $style->{indentwidth} ); 
     580 
     581    # Use tabs for indentation where possible? 
     582    $editor->SetUseTabs(  $style->{use_tabs} ); 
     583 
     584    return(); 
     585} 
     586 
     587=head2 guess_indentation_style 
     588 
     589Automatically infer the indentation style of the document using 
     590L<Text::FindIndent>. 
     591 
     592Returns a hash reference containing the keys C<use_tabs>, 
     593C<tabwidth>, and C<indentwidth>. It is suitable for passing 
     594to C<set_indendentation_style>. 
    526595 
    527596=cut 
    528597 
    529  
    530  
    531  
    532 ##################################################################### 
    533 # Content Manipulation 
    534  
    535 sub text_get { 
    536     $_[0]->editor->GetText; 
    537 } 
    538  
    539 sub text_set { 
    540     $_[0]->editor->SetText($_[1]); 
    541 } 
    542  
    543 sub text_like { 
    544     my $self = shift; 
    545     return !! ( $self->text_get =~ /$_[0]/m ); 
    546 } 
    547  
    548  
    549  
    550  
    551  
    552 ##################################################################### 
    553 # Project Methods 
     598sub guess_indentation_style { 
     599    my $self  = shift; 
     600 
     601    require Text::FindIndent; 
     602    my $indentation = Text::FindIndent->parse($self->text_get); 
     603 
     604    my $style; 
     605    if ($indentation =~ /^t\d+/) { # we only do ONE tab 
     606        $style = { 
     607            use_tabs    => 1, 
     608            tabwidth    => 8, 
     609            indentwidth => 8, 
     610        }; 
     611    } 
     612    elsif ($indentation =~ /^s(\d+)/) { 
     613        $style = { 
     614            use_tabs    => 0, 
     615            tabwidth    => 8, 
     616            indentwidth => $1, 
     617        }; 
     618    } 
     619    elsif ($indentation =~ /^m(\d+)/) { 
     620        $style = { 
     621            use_tabs    => 1, 
     622            tabwidth    => 8, 
     623            indentwidth => $1, 
     624        }; 
     625    } 
     626    else { 
     627        # fallback 
     628        my $config = Padre->ide->config; 
     629        $style = { 
     630            use_tabs    => $config->{editor_use_tabs}, 
     631            tabwidth    => $config->{editor_tabwidth}, 
     632            indentwidth => $config->{editor_indentwidth}, 
     633        }; 
     634    } 
     635     
     636    return $style; 
     637} 
     638 
     639 
     640 
     641 
     642 
     643##################################################################### 
     644# Project Integration Methods 
    554645 
    555646sub project_dir { 
     
    591682} 
    592683 
    593 # abstract method, each subclass should implement it 
    594 sub keywords           { return {} } 
    595 sub get_functions      { return () } 
    596 sub get_function_regex { return '' } 
    597 sub pre_process        { return 1  } 
    598  
    599 # should return ($length, @words) 
    600 # where $length is the length of the prefix to be replaced by one of the words 
    601 # or 
    602 # return ($error_message) 
    603 # in case of some error 
    604 sub autocomplete { 
    605     my $self   = shift; 
     684 
     685 
     686 
     687 
     688##################################################################### 
     689# Document Analysis Methods 
     690 
     691# Abstract methods, each subclass should implement it 
     692# TODO: Clearly this isn't ACTUALLY abstract (since they exist) 
     693 
     694sub keywords { 
     695    return {}; 
     696} 
     697 
     698sub get_functions { 
     699    return (); 
     700} 
     701 
     702sub get_function_regex { 
     703    return ''; 
     704} 
     705 
     706sub pre_process { 
     707    return 1; 
     708} 
     709 
     710sub stats { 
     711    my ($self) = @_; 
     712     
     713    my ( $lines, $chars_with_space, $chars_without_space, $words, $is_readonly ) 
     714        = (0) x 5; 
    606715 
    607716    my $editor = $self->editor; 
    608     my $pos    = $editor->GetCurrentPos; 
    609     my $line   = $editor->LineFromPosition($pos); 
    610     my $first  = $editor->PositionFromLine($line); 
    611  
    612     # line from beginning to current position 
    613     my $prefix = $editor->GetTextRange($first, $pos); 
    614        $prefix =~ s{^.*?(\w+)$}{$1}; 
    615     my $last   = $editor->GetLength(); 
    616     my $text   = $editor->GetTextRange(0, $last); 
    617     my $pre_text  = $editor->GetTextRange(0, $first+length($prefix));  
    618     my $post_text = $editor->GetTextRange($first, $last);  
    619  
    620     my $regex; 
    621     eval { $regex = qr{\b($prefix\w+)\b} }; 
    622     if ($@) { 
    623         return ("Cannot build regex for '$prefix'"); 
    624     } 
    625  
    626     my %seen; 
    627     my @words; 
    628     push @words ,grep { ! $seen{$_}++ } reverse ($pre_text =~ /$regex/g); 
    629     push @words , grep { ! $seen{$_}++ } ($post_text =~ /$regex/g); 
    630  
    631     if (@words > 20) { 
    632         @words = @words[0..19]; 
    633     } 
    634  
    635     return (length($prefix), @words); 
    636 } 
    637  
    638 sub remove_color { 
    639     my ($self) = @_; 
    640  
    641     my $editor = $self->editor; 
    642     # TODO this is strange, do we really need to do it with all? 
    643     for my $i ( 0..31 ) { 
    644         $editor->StartStyling(0, $i); 
    645         $editor->SetStyling($editor->GetLength, 0); 
    646     } 
    647  
    648     return; 
    649 } 
     717    my $src = $editor->GetSelectedText; 
     718    my $code; 
     719    if ( $src ) { 
     720        $code = $src; 
     721 
     722        my $code2 = $code; # it's ugly, need improvement 
     723        $code2 =~ s/\r\n/\n/g; 
     724        $lines = 1; # by default 
     725        $lines++ while ( $code2 =~ /[\r\n]/g ); 
     726        $chars_with_space = length($code); 
     727    } else { 
     728        $code = $self->text_get; 
     729 
     730        # I trust editor more 
     731        $lines = $editor->GetLineCount(); 
     732        $chars_with_space = $editor->GetTextLength(); 
     733        $is_readonly = $editor->GetReadOnly(); 
     734    } 
     735 
     736    $words++ while ( $code =~ /\b\w+\b/g ); 
     737    $chars_without_space++ while ( $code =~ /\S/g ); 
     738 
     739    my $filename = $self->filename; 
     740     
     741    return ( $lines, $chars_with_space, $chars_without_space, $words, $is_readonly,  
     742            $filename, $self->{newline_type}, $self->{encoding} ); 
     743} 
     744 
     745=pod 
     746 
     747=head2 check_syntax 
     748 
     749NOT IMPLEMENTED IN THE BASE CLASS 
     750 
     751See also: C<check_syntax_in_background>! 
     752 
     753By default, this method will only check the syntax if 
     754the document has changed since the last check. Specify 
     755the C<force =E<gt> 1> parameter to override this. 
     756 
     757An implementation in a derived class needs to return an arrayref of 
     758syntax problems. 
     759 
     760Each entry in the array has to be an anonymous hash with the  
     761following keys: 
     762 
     763=over 4 
     764 
     765=item * line 
     766 
     767The line where the problem resides 
     768 
     769=item * msg 
     770 
     771A short description of the problem 
     772 
     773=item * severity 
     774 
     775A flag indicating the problem class: Either 'B<W>' (warning) or 'B<E>' (error) 
     776 
     777=item * desc 
     778 
     779A longer description with more information on the error (currently  
     780not used but intended to be) 
     781 
     782=back 
     783 
     784Returns an empty arrayref if no problems can be found. 
     785 
     786Returns B<undef> if nothing has changed since the last invocation. 
     787 
     788Must return the problem list even if nothing has changed when a  
     789param is present which evaluates to B<true>. 
     790 
     791=head2 check_syntax_in_background 
     792 
     793NOT IMPLEMENTED IN THE BASE CLASS 
     794 
     795Checking the syntax of documents can take a long time. 
     796Therefore, this method essentially works the same as 
     797C<check_syntax>, but works its magic in a background task 
     798instead. That means it cannot return the syntax-check 
     799structure but instead optionally calls a callback 
     800you pass in as the C<on_finish> parameter. 
     801 
     802If you don't specify that parameter, the default 
     803syntax-check-pane updating code will be run after finishing 
     804the check. If you do specify a callback, the first parameter 
     805will be the task object. You can 
     806run the default updating code by executing the 
     807C<update_gui()> method of the task object. 
     808 
     809By default, this method will only check the syntax if 
     810the document has changed since the last check. Specify 
     811the C<force =E<gt> 1> parameter to override this. 
     812 
     813=cut 
     814 
     815 
     816 
     817 
     818##################################################################### 
     819# Document Manipulation Methods 
    650820 
    651821# 
     
    661831sub comment_lines_str {} 
    662832 
    663 sub stats { 
    664     my ($self) = @_; 
    665      
    666     my ( $lines, $chars_with_space, $chars_without_space, $words, $is_readonly ) 
    667         = (0) x 5; 
     833 
     834 
     835 
     836 
     837##################################################################### 
     838# Unknown Methods 
     839# Dumped here because it's not clear which section they belong in 
     840 
     841# should return ($length, @words) 
     842# where $length is the length of the prefix to be replaced by one of the words 
     843# or 
     844# return ($error_message) 
     845# in case of some error 
     846sub autocomplete { 
     847    my $self   = shift; 
    668848 
    669849    my $editor = $self->editor; 
    670     my $src = $editor->GetSelectedText; 
    671     my $code; 
    672     if ( $src ) { 
    673         $code = $src; 
    674  
    675         my $code2 = $code; # it's ugly, need improvement 
    676         $code2 =~ s/\r\n/\n/g; 
    677         $lines = 1; # by default 
    678         $lines++ while ( $code2 =~ /[\r\n]/g ); 
    679         $chars_with_space = length($code); 
    680     } else { 
    681         $code = $self->text_get; 
    682  
    683         # I trust editor more 
    684         $lines = $editor->GetLineCount(); 
    685         $chars_with_space = $editor->GetTextLength(); 
    686         $is_readonly = $editor->GetReadOnly(); 
    687     } 
    688  
    689     $words++ while ( $code =~ /\b\w+\b/g ); 
    690     $chars_without_space++ while ( $code =~ /\S/g ); 
    691  
    692     my $filename = $self->filename; 
    693      
    694     return ( $lines, $chars_with_space, $chars_without_space, $words, $is_readonly,  
    695             $filename, $self->{newline_type}, $self->{encoding} ); 
    696 } 
    697  
    698  
    699 # TODO: experimental 
    700 sub get_indentation_style { 
    701     my $self = shift; 
    702     my $config = Padre->ide->config; 
    703  
    704     # TODO: (document >) project > config 
    705  
    706     my $style; 
    707     if ($config->{editor_auto_indentation_style}) { 
    708         # TODO: This should be cached? What's with newish documents then? 
    709         $style = $self->guess_indentation_style; 
    710     } 
    711     else { 
    712         $style = { 
    713             use_tabs    => $config->{editor_use_tabs}, 
    714             tabwidth    => $config->{editor_tabwidth}, 
    715             indentwidth => $config->{editor_indentwidth}, 
    716         }; 
    717     } 
    718      
    719     return $style; 
    720 } 
    721  
    722  
    723 =head2 set_indentation_style 
    724  
    725 Given a hash reference with the keys C<use_tabs>, 
    726 C<tabwidth>, and C<indentwidth>, set the document's editor's 
    727 indentation style. 
    728  
    729 Without an argument, falls back to what C<get_indentation_style> 
    730 returns. 
    731  
    732 =cut 
    733  
    734 sub set_indentation_style { 
    735     my $self = shift; 
    736  
    737     my $style  = shift || $self->get_indentation_style; 
    738     my $editor = $self->editor; 
    739     # The display width of literal tab characters (ne "indentation width"!) 
    740     $editor->SetTabWidth( $style->{tabwidth} ); 
    741     # The actual indentation width in COLUMNS! 
    742     $editor->SetIndent( $style->{indentwidth} ); 
    743     # Use tabs for indentation where possible? 
    744     $editor->SetUseTabs(  $style->{use_tabs} ); 
    745     return(); 
    746 } 
    747  
    748  
    749 =head2 guess_indentation_style 
    750  
    751 Automatically infer the indentation style of the document using 
    752 L<Text::FindIndent>. 
    753  
    754 Returns a hash reference containing the keys C<use_tabs>, 
    755 C<tabwidth>, and C<indentwidth>. It is suitable for passing 
    756 to C<set_indendentation_style>. 
    757  
    758 =cut 
    759  
    760 sub guess_indentation_style { 
    761     my $self  = shift; 
    762  
    763     require Text::FindIndent; 
    764     my $indentation = Text::FindIndent->parse($self->text_get); 
    765  
    766     my $style; 
    767     if ($indentation =~ /^t\d+/) { # we only do ONE tab 
    768         $style = { 
    769             use_tabs    => 1, 
    770             tabwidth    => 8, 
    771             indentwidth => 8, 
    772         }; 
    773     } 
    774     elsif ($indentation =~ /^s(\d+)/) { 
    775         $style = { 
    776             use_tabs    => 0, 
    777             tabwidth    => 8, 
    778             indentwidth => $1, 
    779         }; 
    780     } 
    781     elsif ($indentation =~ /^m(\d+)/) { 
    782         $style = { 
    783             use_tabs    => 1, 
    784             tabwidth    => 8, 
    785             indentwidth => $1, 
    786         }; 
    787     } 
    788     else { 
    789         # fallback 
    790         my $config = Padre->ide->config; 
    791         $style = { 
    792             use_tabs    => $config->{editor_use_tabs}, 
    793             tabwidth    => $config->{editor_tabwidth}, 
    794             indentwidth => $config->{editor_indentwidth}, 
    795         }; 
    796     } 
    797      
    798     return $style; 
     850    my $pos    = $editor->GetCurrentPos; 
     851    my $line   = $editor->LineFromPosition($pos); 
     852    my $first  = $editor->PositionFromLine($line); 
     853 
     854    # line from beginning to current position 
     855    my $prefix = $editor->GetTextRange($first, $pos); 
     856       $prefix =~ s{^.*?(\w+)$}{$1}; 
     857    my $last   = $editor->GetLength(); 
     858    my $text   = $editor->GetTextRange(0, $last); 
     859    my $pre_text  = $editor->GetTextRange(0, $first+length($prefix));  
     860    my $post_text = $editor->GetTextRange($first, $last);  
     861 
     862    my $regex; 
     863    eval { $regex = qr{\b($prefix\w+)\b} }; 
     864    if ($@) { 
     865        return ("Cannot build regex for '$prefix'"); 
     866    } 
     867 
     868    my %seen; 
     869    my @words; 
     870    push @words ,grep { ! $seen{$_}++ } reverse ($pre_text =~ /$regex/g); 
     871    push @words , grep { ! $seen{$_}++ } ($post_text =~ /$regex/g); 
     872 
     873    if (@words > 20) { 
     874        @words = @words[0..19]; 
     875    } 
     876 
     877    return (length($prefix), @words); 
    799878} 
    800879 
Note: See TracChangeset for help on using the changeset viewer.