Changeset 2233
- Timestamp:
- 12/28/08 22:03:07 (3 years ago)
- File:
-
- 1 edited
-
trunk/Padre/lib/Padre/Document.pm (modified) (14 diffs)
Legend:
- Unmodified
- Added
- Removed
-
trunk/Padre/lib/Padre/Document.pm
r2225 r2233 5 5 =head1 NAME 6 6 7 Padre::Document - Padre Document A bstraction Layer7 Padre::Document - Padre Document API 8 8 9 9 =head1 DESCRIPTION 10 10 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. 11 The B<Padre::Document> class provides a base class, default implementation 12 and API documentation for document type support in L<Padre>. 13 14 As an API, it allows L<Padre> developers and plugin authors to implement 15 extended support for various document types in Padre, while ensuring that 16 a naive default document implementation exists that allows Padre to provide 17 basic support (syntax highlighting mainly) for many document types without 18 the need to install extra modules unless you need the extra functionality. 19 20 =head2 Document Type Registration 21 22 Padre uses MIME types as the fundamental identifier when working with 23 documents. Files are typed at load-time based on file extension (with a 24 simple heuristic fallback when opening files with no extension). 25 26 Many of the MIME types are unofficial X-style identifiers, but in cases 27 without an official type, Padre will try to use the most popular 28 identifier (based on research into the various language communities). 29 30 Each supported mime has a mapping to a Scintilla lexer (for syntax 31 highlighting), and an optional mapping to the class that provides enhanced 32 support for that document type. 33 34 Plugins that implement support for a document type provide a 35 C<registered_documents> method that the PluginManager will call as needed. 36 37 Plugin authors should B<not> load the document classes in advance, they 38 will be automatically loaded by Padre as needed. 39 40 Padre does B<not> currently support opening non-text files. 19 41 20 42 =head1 METHODS … … 30 52 use Padre::Util (); 31 53 use Padre::Wx (); 32 use Padre ;54 use Padre (); 33 55 34 56 our $VERSION = '0.22'; 35 57 58 # NOTE: This is probably a bad place to store this 36 59 my $unsaved_number = 0; 37 60 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 53 69 our %EXT_MIME = ( 54 70 ada => 'text/x-adasrc', … … 92 108 ); 93 109 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. 102 114 our %MIME_LEXER = ( 103 115 'text/x-adasrc' => Wx::wxSTC_LEX_ADA, # CONFIRMED 104 116 '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 106 121 'text/x-c++src' => Wx::wxSTC_LEX_CPP, # CONFIRMED 107 122 'text/css' => Wx::wxSTC_LEX_CSS, # CONFIRMED … … 126 141 'application/x-tcl' => Wx::wxSTC_LEX_TCL, # CONFIRMED 127 142 '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 129 148 'text/x-yaml' => Wx::wxSTC_LEX_YAML, # CONFIRMED 130 149 'application/x-pir' => Wx::wxSTC_LEX_CONTAINER, # CONFIRMED … … 132 151 'application/x-perl6' => Wx::wxSTC_LEX_CONTAINER, # CONFIRMED 133 152 'text/plain' => Wx::wxSTC_LEX_NULL, # CONFIRMED 153 ); 154 155 # This is the mime-type to document class mapping 156 our %MIME_CLASS = ( 157 'application/x-perl' => 'Padre::Document::Perl', 158 'text/x-pod' => 'Padre::Document::POD', 134 159 ); 135 160 … … 164 189 filename => $file, 165 190 ); 166 167 191 168 192 $file is optional and if given it will be loaded in the document 169 193 170 194 mime-type is defined by the guess_mimetype function 171 172 TODO describe173 174 $editor is required and is a Padre::Wx::Editor object175 195 176 196 =cut … … 180 200 my $self = bless { @_ }, $class; 181 201 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 } 183 208 184 209 unless ( $self->get_mimetype ) { … … 207 232 } 208 233 234 sub 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 209 249 sub guess_mimetype { 210 250 my $self = shift; 211 251 212 # default mime-type of new files, should be configurable in the GUI213 if (not $self->filename) {252 # Default mime-type of new files, should be configurable in the GUI 253 unless ( $self->filename ) { 214 254 return 'application/x-perl'; 215 255 } … … 235 275 } 236 276 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 title255 # (For reasons I don't understand yet)256 return ' ' . $str;257 }258 }259 260 277 # For ts without a newline type 261 278 # TODO: get it from config … … 279 296 # TODO get from config 280 297 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 309 sub is_new { 310 return !! ( not defined $_[0]->filename ); 311 } 312 313 sub is_modified { 314 return !! ( $_[0]->editor->GetModify ); 315 } 316 317 sub 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. 324 sub 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 ???? 338 sub 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 345 sub 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]; 281 349 } 282 350 … … 386 454 } 387 455 456 =pod 457 458 =head2 reload 459 460 Reload the current file discarding changes in the editor. 461 462 Returns true on success false on failure. Error message will be in $doc->errstr; 463 464 TODO: In the future it should backup the changes in case the user regrets the action. 465 466 =cut 467 468 sub 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 481 sub text_get { 482 $_[0]->editor->GetText; 483 } 484 485 sub text_set { 486 $_[0]->editor->SetText($_[1]); 487 } 488 489 sub 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 388 502 sub lexer { 389 503 my $self = shift; … … 393 507 } 394 508 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 510 sub 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 523 sub remove_color { 411 524 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 537 sub get_indentation_style { 429 538 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 561 Given a hash reference with the keys C<use_tabs>, 562 C<tabwidth>, and C<indentwidth>, set the document's editor's 563 indentation style. 564 565 Without an argument, falls back to what C<get_indentation_style> 566 returns. 449 567 450 568 =cut 451 569 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>. 570 sub 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 589 Automatically infer the indentation style of the document using 590 L<Text::FindIndent>. 591 592 Returns a hash reference containing the keys C<use_tabs>, 593 C<tabwidth>, and C<indentwidth>. It is suitable for passing 594 to C<set_indendentation_style>. 526 595 527 596 =cut 528 597 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 598 sub 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 554 645 555 646 sub project_dir { … … 591 682 } 592 683 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 694 sub keywords { 695 return {}; 696 } 697 698 sub get_functions { 699 return (); 700 } 701 702 sub get_function_regex { 703 return ''; 704 } 705 706 sub pre_process { 707 return 1; 708 } 709 710 sub stats { 711 my ($self) = @_; 712 713 my ( $lines, $chars_with_space, $chars_without_space, $words, $is_readonly ) 714 = (0) x 5; 606 715 607 716 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 749 NOT IMPLEMENTED IN THE BASE CLASS 750 751 See also: C<check_syntax_in_background>! 752 753 By default, this method will only check the syntax if 754 the document has changed since the last check. Specify 755 the C<force =E<gt> 1> parameter to override this. 756 757 An implementation in a derived class needs to return an arrayref of 758 syntax problems. 759 760 Each entry in the array has to be an anonymous hash with the 761 following keys: 762 763 =over 4 764 765 =item * line 766 767 The line where the problem resides 768 769 =item * msg 770 771 A short description of the problem 772 773 =item * severity 774 775 A flag indicating the problem class: Either 'B<W>' (warning) or 'B<E>' (error) 776 777 =item * desc 778 779 A longer description with more information on the error (currently 780 not used but intended to be) 781 782 =back 783 784 Returns an empty arrayref if no problems can be found. 785 786 Returns B<undef> if nothing has changed since the last invocation. 787 788 Must return the problem list even if nothing has changed when a 789 param is present which evaluates to B<true>. 790 791 =head2 check_syntax_in_background 792 793 NOT IMPLEMENTED IN THE BASE CLASS 794 795 Checking the syntax of documents can take a long time. 796 Therefore, this method essentially works the same as 797 C<check_syntax>, but works its magic in a background task 798 instead. That means it cannot return the syntax-check 799 structure but instead optionally calls a callback 800 you pass in as the C<on_finish> parameter. 801 802 If you don't specify that parameter, the default 803 syntax-check-pane updating code will be run after finishing 804 the check. If you do specify a callback, the first parameter 805 will be the task object. You can 806 run the default updating code by executing the 807 C<update_gui()> method of the task object. 808 809 By default, this method will only check the syntax if 810 the document has changed since the last check. Specify 811 the C<force =E<gt> 1> parameter to override this. 812 813 =cut 814 815 816 817 818 ##################################################################### 819 # Document Manipulation Methods 650 820 651 821 # … … 661 831 sub comment_lines_str {} 662 832 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 846 sub autocomplete { 847 my $self = shift; 668 848 669 849 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); 799 878 } 800 879
Note: See TracChangeset
for help on using the changeset viewer.
