Kephra / lib / Kephra / Edit / Format.pm

use strict;
use warnings;

use Kephra::API;
use Kephra::Config;

#package Kephra::Edit::Format;
package Kephra::App::Editor;

Kephra::API::Command::register({
 'edit-goto-block-up'    =>{sub=>'$ed->ParaUp',                    label=>'Block Up',    keys=>'ctrl+up' },
 'edit-goto-block-down'  =>{sub=>'$ed->ParaDown',                  label=>'Block Down',  keys=>'ctrl+down' },
 'edit-select-block-up'  =>{sub=>'$ed->ParaUpExtend',              label=>'Block Up',    keys=>'ctrl+shift+up' },
 'edit-select-block-down'=>{sub=>'$ed->ParaDownExtend',            label=>'Block Down',  keys=>'ctrl+shift+down' },
 'edit-indent-level'     =>{sub=>'$ed->indent_level',              label=>'Indent Level',keys=>'alt+right' },
 'edit-dedent-level'     =>{sub=>'$ed->dedent_level',              label=>'Dedent Level',keys=>'alt+left' },
 'edit-move-line-up'     =>{sub=>'$ed->move_sel_or_line_up',       label=>'Line Up',     keys=>'alt+up' },
 'edit-move-line-down'   =>{sub=>'$ed->move_sel_or_line_down',     label=>'Line Down',   keys=>'alt+down' },
 'edit-move-page-up'     =>{sub=>'$ed->move_sel_or_line_page_up',  label=>'Page Up',     keys=>'' },
 'edit-move-page-down'   =>{sub=>'$ed->move_sel_or_line_page_down',label=>'Page Down',   keys=>'' },
 'edit-move-doc-begin'   =>{sub=>'$ed->move_sel_or_line_begin',    label=>'Doc Begin',   keys=>'alt+home' },
 'edit-move-doc-end'     =>{sub=>'$ed->move_sel_or_line_end',      label=>'Doc End',     keys=>'alt+end' },
 'edit-move-word-left'   =>{sub=>'$ed->move_sel_or_word_left',     label=>'Word Left',   keys=>'alt+shift+left' },
 'edit-move-word-right'  =>{sub=>'$ed->move_sel_or_word_right',    label=>'Word Right',  keys=>'alt+shift+right' },
});


sub move_sel_or_word_left  { $_[0]->move_sel_or_word(-1) }
sub move_sel_or_word_right { $_[0]->move_sel_or_word(+1) }
sub move_sel_or_word {
	my ($self, $movement) = @_;
	my ($start, $end) = $self->GetSelection;
	return if $start == 0 and $movement < 0;
	return if $end == $self->GetLength and $movement > 0;
	my $move_wordwise;
	$self->BeginUndoAction;
	if ($start == $end) { # select the current word
		my $pos = $start;
		$start = $self->WordStartPosition($pos, 1);
		$end = $self->WordEndPosition($pos, 1);
		$self->SetSelection($start, $end);
		$move_wordwise = 1;
	} else {              # check if word is already is selected
		$move_wordwise = 1 if $start == $self->WordStartPosition($start, 1)
		                   and  $end == $self->WordEndPosition ($end, 1);
	}
	my $selection = $self->GetSelectedText;
	$self->ReplaceSelection('');
	if ($move_wordwise) {
		$self->InsertText( $start, $selection);
	} else {
		my $line = $self->LineFromPosition($start);
		my $pos;
		if ($start == $self->PositionFromLine($line) and $movement == -1){ 
			$pos = $self->GetLineEndPosition( $line-1 );
		}
		elsif ($start == $self->GetLineEndPosition($line) 
		and $self->GetLineCount-1 > $line and $movement == 1){
			$pos = $self->PositionFromLine( $line+1 );
		}
		else { $pos = $start + $movement }
		$self->InsertText( $pos, $selection);
		$self->SetSelection( $pos, $pos + length($selection) );
	}
	$self->EndUndoAction;
	#Kephra::API::log("$start, $end", 1);
}
sub move_sel_or_line_up      { $_[0]->move_sel_or_line( -1) }
sub move_sel_or_line_down    { $_[0]->move_sel_or_line( +1) }
sub move_sel_or_line_page_up { $_[0]->move_sel_or_line(-20) }
sub move_sel_or_line_page_down{$_[0]->move_sel_or_line(+20) }
sub move_sel_or_line_begin   { $_[0]->move_sel_or_line( -$_[0]->GetCurrentLine) }
sub move_sel_or_line_end     { 
	$_[0]->move_sel_or_line(   $_[0]->GetLineCount - $_[0]->GetCurrentLine-1 )
}
sub move_sel_or_line {
	my ($self, $movement) = @_;
	my ($start, $end) = $self->GetSelection;
	my $line_sel = [ $self->LineFromPosition($start), $self->LineFromPosition($end) ];
	my $last_line = $self->LineFromPosition( $self->GetLength );
	return if $line_sel->[0] == 0 and $movement < 0;
	# move inside document borders
	$movement = $line_sel->[0]              
		if $movement < 0 and $line_sel->[0]+$movement < 0;
	$movement = $last_line - $line_sel->[1]
		if $movement > 0 and $line_sel->[1]+$movement > $last_line;

	$self->BeginUndoAction;
	if ($start != $end){     # move selection
		$self->SetSelection( 
			$self->PositionFromLine(  $line_sel->[0] ),
			$self->GetLineEndPosition($line_sel->[1] )
		);
		my $selection = $self->GetSelectedText;
		$self->ReplaceSelection('');
		$self->GotoPos(1) if $self->GetCurrentPos == 0;
		$self->DeleteBack() if $selection;
		my $destination_line = $line_sel->[0] + $movement;
		my $pos = $self->PositionFromLine( $destination_line );
		$self->GotoPos($pos);
		$self->NewLine;
		$pos++ if $self->LineFromPosition( $self->GetLength ) <= $destination_line;
		$self->GotoPos($pos);
		$self->InsertText( $pos, $selection);
		$self->select_whole_lines( $line_sel->[0]+$movement, $line_sel->[1]+$movement );
	} else {                  # move one line
		my $col = $self->GetColumn($self->GetCurrentPos());
		if ($movement < 0){   # up
			my $line = $line_sel->[0];
			$self->GotoLine($line--), $self->LineTranspose for 1..abs $movement;
		} else {              # down
			my $line = $line_sel->[0]+1;
			$self->GotoLine($line++), $self->LineTranspose for 1..    $movement;
		}
		my $pos = $self->PositionFromLine ($line_sel->[0] + $movement) + $col;
		$self->SetSelection( $pos, $pos );
	}
	$self->EndUndoAction;
}

sub select_whole_lines {
	my ($self, $start, $end) = @_;
	$self->SetSelection( 
		$self->PositionFromLine($start),
		$self->GetLineEndPosition($end)
	);
}

sub indent_level { $_[0]->dent_level(+1) }
sub dedent_level { $_[0]->dent_level(-1) }
sub   dent_level {
	my ($self, $change, $lines) = @_;
	my ($start, $end) = $self->GetSelection;
	if ($start != $end){
		my $line_sel = [
			$self->LineFromPosition($start),
			$self->LineFromPosition($end)
		];
		# none empty line numbers of selection
		my @lines = grep {$self->PositionFromLine($_) != $self->GetLineEndPosition($_)}
						$line_sel->[0] .. $line_sel->[1];
		$self->BeginUndoAction;
		$self->_change_indentionlevel_level($change, \@lines);
		$self->select_whole_lines( @$line_sel );
		$self->EndUndoAction;
	} else {
		my $pos = $self->GetCurrentPos();
		my $line = $self->GetCurrentLine();
		my $old_indention = $self->GetLineIndentPosition($line);
		$self->BeginUndoAction;
		$self->_change_indentionlevel_level($change, $line);
		$pos += $self->GetLineIndentPosition($line) - $old_indention;
		$pos = $self->PositionFromLine($line) if $line > $self->LineFromPosition($pos)
		                                      or $pos == -1;
		$self->SetSelection( $pos, $pos );
		$self->EndUndoAction;
	}
}
sub _change_indentionlevel_level {
	my ($self, $change, $lines) = @_;
	my $tab_size = $self->GetIndent();
	$lines = $self->GetCurrentLine() unless defined $lines;
	$lines = [$lines] unless ref $lines eq ref [];
	for my $line (@$lines) {
		my $indent_level = int($self->GetLineIndentation($line) / $tab_size);
		$self->SetLineIndentation( $line, ($indent_level + $change) * $tab_size );
	}
}

1;
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.