Commits

Herbert Breunung committed dc95674

new file design

Comments (0)

Files changed (37)

+=head1 End Goal
+
+
+full dynamic introspection (highlighting and other services are based
+on 100% behaviour of the language)
+
+unlimited, restorable (after app restart) undo, which saves branches too and is synched
+with version control integration
+
+talk to remote tty terminal
+
+full vi, emacs and org-mode compatibality
+=head1 End Goal
+
+full dynamic introspection (highlighting and other services are based
+on 100% behaviour of the language)
+
+unlimited, restorable (after app restart) undo, which saves branches too and is synched
+with version control integration
+
+talk to remote tty terminal
+
+full vi, emacs and org-mode compatibality
 =head1 Index of Kephra User Documentation
 
+
+CompleteProgramming	set of rules that organizes our development
+Components		visual components and their layout
+EndGoal			technical details of the vision we lean toward
+Internals		
+LongFeatureList		
+Overview		
+Philosophy		
+Sanddrum		a vi like command language
+StyleGuide		
+Versioning		about our version numbers and releases
+
+=head1 Index of Kephra User Documentation
+

doc/Internals.pod

+=head1 Kephra::Internals
+
+If you want to touch the Kephra sources -
+this document explains where to find what and roughly how it works.
+The terminology is explained in the glossary at the end.
+In case you learn better by looking at the good documented code - start at Kephra::API.
+
+=head2 NAMESPACES
+
+In each file is only one package with exactly the same name as the file.
+
+With every directory you go deeper into the tree the modules will get more
+specific. Thats why the directory "lib/Kephra" has only the most common
+tools and libraries. Other dir (a good first approximation is the main menu):
+
+=over 4
+
+=item * API - API (proxy) for most cross module calls and plugins to access central data structures
+
+=item * App - visuals
+
+=over 4
+
+=item * Bar - container widgets (menu bar, toolbar, tab bar, status bar, etc.)
+
+=item * Editor - editor widget helper
+
+=item * Panel - with sizer assembled visual units (snippet lib, io unit, etc.)
+
+=item * Part - functional units (main part, side panel, etc,)
+
+=back
+
+=item * Config - data
+
+=over 4
+
+=item * Default - built in fall back configs when certain files are in trouble
+
+=back
+
+=item * Document - document properties
+
+=item * Edit - text manipulation functions
+
+=item * Files - all sorts of IO
+
+=item * Plugins - namespace of users extentions
+
+=back
+
+
+
+=head2 MODULES
+
+=over 4
+
+=item * Kephra
+
+global vars like version number and the init process:
+finding configs, setting dirs, forking to the worker, loading libs, starting app
+
+
+=item * Kephra::API
+
+- interface to most important functions all modules and plugins should use
+- provides also simplified aliases to Kephra::API::* namespaces
+- as long as the first version number (see L<Versioning>) does not change,
+  no call will be renamed or removed
+
+=item * Kephra::API::Command
+
+- Every function the user is able to trigger has an CommandID
+  and gets called over this API.
+- In head of every module after package and use and vars are the definitions
+  which rgister these commands in this data structure.
+- Its for monitoring, triggers, macros and other introspection.
+- Also helps to make simple menu and toolbar definitions (are lists of CommandIDs).
+
+=item * Kephra::API::DocumentStash
+
+- Find docs by any attribute or categories.
+
+=item * Kephra::API::Event
+
+- trigger, freeze, thaw, add and remove events or calls attached to them
+- covers also internal calls
+
+=item * Kephra::API::GuiBuilder
+
+- transform wiki syntax => data structure => GUI and back
+
+=item * Kephra::API::KeyMap
+
+- managing the widgets mappings of key combo => CommandID
+
+=item * Kephra::API::Macro
+
+- record, play, store and load macros
+
+=item * Kephra::API::Plugin
+
+- load, reload and unsubscribe plugins
+
+=item * Kephra::API::Sandrum
+
+- vi like shortcut language for triggering commands from the IO Unit by typing
+
+=item * Kephra::App
+
+- root object of all GUI
+- handles GUI related boot and shutdown sequence
+- derived from Wx::App
+
+=item * Kephra::App::Bar::Tab
+
+- base TabBar, previous DocBar
+
+=item * Kephra::App::Dialog
+
+- all dialogs are called from here
+
+=item * Kephra::App::Editor
+
+- main class of the editing widgets
+
+=item * Kephra::App::Focus
+
+- keeps track in which widget got to make save way back if needed
+
+=item * Kephra::App::Menu
+
+- compiles menu definitions to data structures and them into menus
+
+=item * Kephra::App::PaintBar
+=item * Kephra::App::Panel  - wrapper for Wx::Panel, uses K::App::Sizer
+=item * Kephra::App::Sizer
+=item * Kephra::App::Splitter
+=item * Kephra::App::Toolbar
+=item * Kephra::App::Util  -  GUI helper
+=item * Kephra::App::Window  -  main window with all layout
+
+=item * Kephra::Config  -  config menu function (internal data handling)
+
+=item * Kephra::Document  -  document menu (doc properties)
+
+=item * Kephra::Edit  -  basics functions for the edit menu
+
+=item * Kephra::File  -  most file menu functions
+
+=item * Kephra::Help  -  display documentation
+
+=item * Kephra::Log  -  logger
+
+=item * Kephra::Worker  -  does heavy lifting work in the background, so app stays reactive 
+
+=back
+
+=head1 Glossary
+
+=head2 App
+
+everything visible and GUI (Wx) related
+
+=head2 App::Part
+
+visual area dedicated for one purpose, editor is the most prominent,
+but ther are also FileBrowser, IOUnit and more
+
+=head2 Editor
+
+one widget for editing text
+
+=head2 Edit
+
+namespace for the actual editing operations
+
+=head2 Panel
+
+area to place widget on, can be under anything, even under each editor
+a Kephra::App::Panel is a helper class that manages its sizer
+(visibility and ordering of elements)
+
+=head1 Document
+
+=head2  Interdependency Of Core Modules
+
+=head2  Modules vs Objects
+
+=head2  Boot Stages
+
+Just using the fact that when modules are loded (after the fork)
+there main code (outside the sub) is run. At this time we create the
+basic command list and the definition which module gets which part of
+the global config (actual loading happens later).
+
+=head2  Worker Fork
+
+
+=cut
+
 =head1 Kephra Overview
 
-Welcome to the root document of the Kephra user documentation.
-All paths are short from here.
+Welcome to the root document of the Kephra dev documentation.
 If you are new, it is the best point to start from.
 
-=head2 Philosophy
+=head2 Index
 
-=head3 Main Objective
+CompleteProgramming	set of rules that organizes our development
+Components		visual components and their layout
+EndGoal			technical details of the vision we lean toward
+Internals		structure of the sources, meaning of terms and names
+LongFeatureList		
+Philosophy		theoretical goal and rationale for our technical decisions
+Sanddrum		a vi like command language
+StyleGuide		formating and other rules for writing Kephra sources
+Versioning		about our version numbers and releases
 
 =head3 Inspirations
 
-  Nedit, Kommodo, Vim, Emacs, Geany, Gedit
+  Nedit, Kommodo, Vim, Emacs, Geany, Gedit, LightTable, Tincta, Proton, Phase 5
 
 =head3 Design Decisions
 

doc/Overview.pod~

+=head1 Kephra Overview
+
+Welcome to the root document of the Kephra dev documentation.
+If you are new, it is the best point to start from.
+
+=head2 Index
+
+CompleteProgramming	set of rules that organizes our development
+Components		visual components and their layout
+EndGoal			technical details of the vision we lean toward
+Internals		
+LongFeatureList		
+Philosophy		
+Sanddrum		a vi like command language
+StyleGuide		
+Versioning		about our version numbers and releases
+
+=head2 Philosophy
+
+=head3 Main Objective
+
+=head3 Inspirations
+
+  Nedit, Kommodo, Vim, Emacs, Geany, Gedit
+
+=head3 Design Decisions
+
+=head2 Techology
+
+=head2 Main Features
+
+

doc/Philosophy.pod

+=head1 Kephra Philosophy
+
+
+=head2  Main Principle
+
+... is simply freedom. Because even programmers are human beings in the first
+place and inherently want to feel free, joyful and creative (productive).
+
+And you are not free if it doesn't run on your operating system ...,
+is gigantic, slow, has a lot of dependencies or a restricitve license
+or can't just be copied around.
+
+The usage is also crippled if important features are missing or
+you have to learn something you don't want to in order to get your work done.
+
+The beginner is glad to do his stuff by simple and visually-assisted methods,
+however experts are happiest when they control every detail and achieve their
+goals as fast as they can think.
+True freedom consideres both types of users and a smooth path to become an expert.
+
+Freedom also means not to get stuck in old ways,
+but combine the best (not the most popular) ideas available and not being
+afraid to make own inventions.
+
+And the full delight can only manifest if it's beautiful on every level. 
+
+
+
+=head2  Strategies
+
+
+=head3 Simple
+
+Aiming toward less code and only necessary functions is the first rule
+for several reasons. Yes it is the basis for a small, fast, clean and maintainable
+program. But also the only option since our dev team is frankly basically just me.
+
+=head3 Mature
+
+Because half baked solutions are not really enjoyable,
+we try to develope our features to functional and visual perfection
+before we publish them in a version marked as stable.
+Thats why Kephra has very few open construction zones at a time.
+
+=head3 Complete
+
+All the tools you need should be on one workbench.
+Either provided by Kephra or well integrated, originating from the operating
+system or third party software. This enables uninterrupted workflows and
+functions (or macros) that solve complex tasks at once.
+
+Kephra makes it easy to integrate your favorite program too.
+
+=head3 Individual
+
+An editor should fully match all preferences of its user,
+that spends so much time with it.
+Several types of communication with the program (like notepad, vi, emacs) we
+plan to support and most features and visuals can be configured and disabled
+via a typed commands, a dialog and config files.
+Yet many setting can be changed most quickly in place of their display by mouse.
+For deeper modifications use the extensive plugin API, that can be even
+left behind (with risk). There is no special plugin language - it's all Perl,
+which also supports your individual coding style.
+
+=head3 Unix-Like
+
+To achieve the proposed simplicity and power Kephra has a similar design like Unix. 
+That means: it contains a few flexible tools solve the fundamental tasks thoroughly.
+Only difference: for the most common tasks we already added some sugar,
+ready-to-use functions that combine several of these tools.
+
+Consequently are "introspection" and "shell integration" more than buzzwords to us.
+Use the tool you like best within your editor and with some smart data pre- and
+postprocessing the is more possible than you you might think.
+
+=head3 Graphical
+
+To search for new and advanced graphical input and display methods was one of
+the original reasons for Kephra being founded. Graphics allow a much higher
+information density than text, which is very needed in a software world that
+is getting ever more complex. 
+
+=head3 Information-Centered
+
+I believe that editor can assist their users much more in creating, sorting,
+storing and sharing their relevant data in many formats like templates, 
+macros, settings, colouring schemes or even plugins. It may start with small
+text snippet you want to set aside and save for later reuse that can grow
+step by step into a plugin.
+
+
+But also while normal usage, the editor can remember details to prevent repetitive work.
+It's important to us seeing you as the owner of all your data. 
+
+
+=head2  DISCLAIMER
+
+Like platons politeia, this document describes the ideal state of version 1.0.

doc/StyleGuide.pod

+
+
+=head1 Kephra Coding Style
+
+
+=head2 Perl Defaults
+
+    use v5.12;
+    use warnings;
+
+
+=head2 Technical Details
+
+    bracketing     = K & R
+    indention size = 4
+    use of tabs    = yes
+    line with      = 80 (not strict)
+    utf            = only in strings
+    perl version   = 5.12
+    pragmas        = warnings
+
+
+=head2  What Part Of Perl We Use Or Avoid
+
+
+
+
+=head2 Organisation
+
+1 module - 1 name - 1 namespace
+
+
+
+=head2 Naming
+
+We try to reuse same names for same (seldom similar) purpose,
+throughout all modules,
+or the same data part through different structures.
+
+We also try to lean toward natural Perl names (eg 'sub' 'coderef', 'push').
+
+
+=head3 module/class Names
+
+camelcase                      ~~ LikeThis
+
+
+=head3 sub/method Names
+
+lowercase words separated by _ ~~ like_this
+
+routines only to be called inside a module start with one _
+avoid to call these from outside by all means
+
+create not make when we generate something
+
+
+=head3 Variable Names
+
+lowercase words separated by _ ~~ like_this
+We say active, no more current or recent.
+
+$self   reference of the class itself
+
+$ed     in most cases the active editor pane
+        (was renamed from $ep to prevent simple copy and paste of old code)
+
+$doc    active document
+
+$tabbar active tab bar
+
+$file   full file path, unless oyur specific like file_name, file_path etc.
+
+$win    main window
+
+
+=head3 Command Names
+
+words separated by -
+
+
+=head3 Event Names
+
+words separated by .
+
+=head3 Key Definitions
+
+keys separated by +

doc/Versioning.pod

+=head1 Kephra Versioning Schema
+
+=head2 Goals
+
+=over 2
+
+=item * simple (linear as a watch)
+
+=item * measures development status not time
+
+=item * no extras (Alpha, Beta, Release Candidate, Patchlevel)
+
+=back
+
+=head2 General Pattern
+
+  Revision . Stable . Testing . Development
+
+If one number is raised, all to the left get a reset to zero.
+Trailing zeros can be omited - 0.5 is the same as 0.5.0.0
+
+Some similar idea is known as semantic versioning L<http://semver.org>.
+
+=head3 Development
+
+Development number is changed after every important commit.
+Normally after every day of development.
+
+Development releases come with no warranty whatsoever.
+
+=head3 Testing
+
+Testing number is raised if a new feature or change/set has to attract
+the attention of the testers.
+
+Testing releases come with all known bugs disabled (if possible).
+Interfaces glitches may occur.
+
+=head3 Stable
+
+Stable number goes up if several greater new features are in
+and all known bugs are out.
+If there are still bugs,
+they have to be disabled with the feature there are sitting on.
+
+Stable releases should have a polished interface and no bugs.
+If one appears in 0.3 it will be fixed ASAP with 0.3.0.1 a.s.o.
+
+During a time window before releasing a stable version,
+all new features will go into a new branch, which will become 0.3.1.
+The next testing should be released soon after the stable is out.
+
+=head3 Revision
+
+Has the word vision in it. When I got my initial vision we are at 1.0.
+If I have a new one after that or we change a fundamental technology
+there is gonna be a 2.0.
+
+Or in case we just want to break backward compatibility of the Kephra::API,
+it is a signal to all Plugin authors, they have to adapt.
+
+=head2 What Changed?
+
+We dropped the special patchlevel number.
+The version 0.3 Patchlevel 5 is now 0.3.0.5.

lib/Kephra/API/Command.pm

-use v5.12;
-use warnings;
-
-package Kephra::API::Command;                             # callable by the user
-
-my %list;     # by ID
-#my %keyproxy;# by keycode
-my %switchvar; #switchvar vor cmd definitions
-my $done_init = 0;
-
-
-sub      _raw_list { \%list }
-sub sub_exists     { no strict 'refs'; !!*{ $_[0] }{CODE} if $_[0] }
-sub package_exists { no strict 'refs'; !!%{ $_[0] . '::' } if $_[0] }
-sub calling_module { ( caller(1) )[0] }
-
-sub switch_variables {
-	my ($var_def) = shift;
-	my $api = 'Kephra::API';
-	my $caller = calling_module();
-	return Kephra::Log::error("only callable by $api", 1) if $caller ne $api;
-	#return Kephra::Log::error("call me just once", 1) if keys %switchvar;
-	return Kephra::Log::error("need a hashref", 1) unless ref $var_def eq 'HASH';
-	for my $var (keys %$var_def){
-		delete $var_def->{$var} unless substr($var, 0, 1) eq '$';
-		delete $var_def->{$var} unless sub_exists( $api .'::'. $var_def->{$var}{'switch'} );
-		delete $var_def->{$var} unless package_exists ( $var_def->{$var}{'class'} );
-	}
-	%switchvar = %$var_def;
-	compile( keys %list );
-	$done_init = 1;
-}
-
-sub register {
-	my $cmd = shift;
-	return Kephra::Log::error("cmd def have to be in a hash ref, not $cmd", 1)
-		unless ref $cmd eq 'HASH';
-	my $caller = calling_module();
-	for my $ID (keys %$cmd) {
-		Kephra::Log::warning( 
-			"$cmd already registered to do ". property($ID, 'sub')." by ".
-			property($ID, 'source')  ), next if registered( $ID );
-		#for (qw/sub options state event label help keys icon/){} filter input later
-		$list{$ID}           = $cmd->{$ID};
-		$list{$ID}{'source'} = $caller;
-		compile($ID) if $done_init;
-	}
-}
-
-sub compile {
-	for my $cmd_ID (@_) {
-		my $cmd = $list{ $cmd_ID };
-		my $sub = $cmd->{'sub'};
-		Kephra::Log::warning( "$cmd_ID lacks value on hashkey 'sub'"), next unless $sub;
-
-		if ( substr($sub, 0, 1) eq '$'){
-			my $method_pos = index($sub, '->');
-			my $var = substr($sub, 0, $method_pos);
-			Kephra::Log::warning
-				("unknown switchvar $var in $cmd_ID 'sub' entry", 1), next
-					unless ref $switchvar{ $var };
-			my $method = $switchvar{$var}{'class'}.'::'.substr($sub, $method_pos+2);
-			# move that check into test suite
-			#Kephra::Log::warning ("called unknown method with $sub", 1), next
-			#	unless sub_exists($method) ;
-			
-			$cmd->{'coderef'} = 'Kephra::API::' . $switchvar{$var}{'switch'} . '()' .
-			                      substr($sub, $method_pos);
-		}
-		elsif ( index($sub, '::') == -1) {
-			$cmd->{'coderef'} = $cmd->{'source'} . '::' . $sub
-		}
-		else { $cmd->{'coderef'} = $sub }
-
-		# add parameter if are any
-		$cmd->{'coderef'} .= exists $cmd->{'parameter'} # just one parameter yet
-			? "( '" . $cmd->{'parameter'} . "' )"
-			: '()';
-		$cmd->{'coderef'} = eval 'sub { ' . $cmd->{'coderef'} . ' }';
-
-		Kephra::API::KeyMap::register_keys({ $cmd_ID => $cmd->{'keys'} }) if $cmd->{'keys'};
-	}
-}
-
-
-sub run {
-	my $cmd = shift;
-	$cmd = [$cmd] unless ref $cmd eq ref [];
-	my $return;
-	for (@$cmd) { $return = $list{$_}{'coderef'}->() if exists $list{$_} }
-	$return;
-}
-sub run_by_keycode {
-	my ($code, $map) = @_;
-}
-
-sub registered      { 1 if defined $_[0]  and exists $list{ $_[0] }             }
-sub property_exists { 1 if registered($_[0]) and exists $list{ $_[0] }{ $_[1] } }
-sub add {}
-
-sub all_properties { 
-	if ( registered($_[0]) ) { $list{$_[0]} }
-	else { Kephra::Log::warning('requested data of unknown command '.$_[0], 1) }
-}
-sub property       { $list{$_[0]}{$_[1]}         if property_exists(@_) }
-sub set_property   { $list{$_[0]}{$_[1]} = $_[2] if property_exists(@_) }
-
-
-1;
-
-__END__
-
-=head1 Command Definition
-
-cmd_ID => {
-	coderef   => compiled from sub, source and option, saves state
-	sub       => 'Kephra::File::new',
-	parameter => [],
-	source    => package that registered that cmd
-	state     => return value of the call or antother coderef
-	event     => '',
-	label     => 'New',
-	help      => '', # help text to be shown in statusbar or as popup
-	keys      => 'Ctrl + N',
-	icon      => '',
-	bitmap    => '',  #Wx::Bitmap Object
-}

lib/Kephra/API/DocumentStash.pm

-use v5.12;
-use warnings;
-use Wx;
-use Kephra::App::Editor;
-use Kephra::App::Panel;
-use Kephra::Document;
-
-package Kephra::API::DocumentStash;
-my %document = ('ID'=>{});             # keys of subhashes: ID editor panel file
-my $active_doc;
-my $lowest_free_ID = 1;
-my $lowest_free_anon_NR = 1;
-
-use constant DOC_CLASS => 'Kephra::Document';
-use Scalar::Util qw(blessed looks_like_number);
-
-
-sub active_doc    { $active_doc || '' }
-sub active_editor { $active_doc->editor->{Kephra::API::tabbar()} if $active_doc }
-sub active_panel  { $active_doc->panel->{Kephra::API::tabbar()} if $active_doc }
-sub set_active    {
-	my $doc = find( shift );
-	$active_doc = $doc if $doc;
-}
-sub previous      { App::Kephra::Focus::last( DOC_CLASS ) }
-sub all_docs      { values %{$document{'ID'}} }
-
-sub is_doc { return 1 if blessed($_[0]) and $_[0]->isa( DOC_CLASS ); return 0 }
-
-
-sub add_doc {
-	my $doc = shift;
-	Kephra::Log::warning('only accepting ' . DOC_CLASS . " objects, not $doc!", 1) unless is_doc($doc);
-	$document{'ID'}{$lowest_free_ID} = $doc;
-	if (ref $doc->editor eq 'HASH') {
-		#add_doc_instance($doc, $_) for keys $doc->editor;
-	}
-	if ($doc->file_path) {
-		$document{'file'}{$doc->file_path} = $doc;
-	} else {
-		$doc->anon_NR( $lowest_free_anon_NR++ );
-		$document{'anon'}{$doc->anon_NR} = $doc;
-	}
-	$lowest_free_ID++;
-}
-
-
-sub rename_file {
-	my $doc = find( shift );
-	Kephra::Log::warning('need a ' . DOC_CLASS . " thats in stash, not $doc!", 1)
-		unless is_doc($doc);
-	my $file = $doc->prev_file_path;
-	delete $document{'file'}{$file} if $file and $document{'file'}{$file};
-	if ($doc->file_path){
-		$document{'file'}{$doc->file_path} = $doc;
-		unless ($doc->prev_file_path) {
-			delete $document{'anon'}{$doc->anon_NR};
-			$doc->anon_NR(0);
-		}
-	} else {
-		if ($doc->prev_file_path) {
-			$doc->anon_NR( $lowest_free_anon_NR++ );
-			$document{'anon'}{$doc->anon_NR} = $doc;
-		}
-	}
-}
-
-
-sub remove_doc {
-	my $doc = shift;
-#say "remove $doc ", scalar keys %{$document{'ID'}}, ' ',scalar keys %{$document{'file'}}; 
-	Kephra::Log::warning('need a ' . DOC_CLASS . ' thats in stash, not $doc!', 1)
-		unless is_doc($doc);
-	delete $document{'ID'}{$doc->ID} if $doc->ID;
-	#remove_doc_instance($doc, $_) for values $doc->panel;
-	delete $document{'file'}{$doc->file_path} if $doc->file_path;
-#say "removed $doc ", scalar keys %{$document{'ID'}}, ' ',scalar keys %{$document{'file'}}; 
-}
-
-
-sub add_doc_instance {
-	my ($doc, $bar) = @_;
-	return Kephra::Log::warning('need as first parameter a'.DOC_CLASS.' thats in stash, not $doc!', 1)
-		unless is_doc($doc);
-	#return Kephra::Log::warning("need a Kephra::App::Bar::Document instance as second parameter, not $bar")
-	#	unless blessed($bar) eq 'Kephra::App::Bar::Document';
-	$document{'panel'} { $doc->panel->{$bar}  } = $doc;
-	$document{'editor'}{ $doc->editor->{$bar} } = $doc;
-}
-
-
-sub remove_doc_instance {
-	my ($doc, $bar) = @_;
-	return Kephra::Log::warning('need as first parameter a'.DOC_CLASS.' thats in stash, not $doc!', 1)
-		unless is_doc($doc);
-	return Kephra::Log::warning("need a Kephra::App::Bar::Document instance as second parameter, not $bar")
-		unless blessed($bar) eq 'Kephra::App::Bar::Document';
-	delete $document{'panel'}{ $doc->panel->{$bar} };
-	delete $document{'editor'}{ $doc->editor->{$bar} };
-}
-
-
-sub find {
-	my $any = shift;
-	Kephra::Log::warning("need a parameter!", 1) unless defined $any and $any;
-	return     looks_like_number($any)                                     ? $document{'ID'}    {$any}
-	         : ref($any) eq ''                                             ? $document{'file'}  {$any}
-	         : $any->isa('Kephra::App::Panel')                             ? $document{'panel'} {$any}
-	         : $any->isa('Kephra::App::Editor')                            ? $document{'editor'}{$any}
-	         :($any->isa(DOC_CLASS) and $document{'ID'}{$any->ID} eq $any) ? $any
-	         :                                                               undef;
-}
-
-
-sub file_loaded {
-	my $file_path = shift;
-	#Kephra::Log::warning("needs a file path as input!") unless $file_path and -e $file_path;
-	for my $doc ( all_docs() ) {
-		return 1 if $doc->file_path and $doc->file_path eq $file_path;
-	}
-}
-
-
-sub all_editors { keys %{$document{'editor'}} }
-sub do_with_all_editors {
-	
-}
-
-1;

lib/Kephra/API/Event.pm

-use v5.12;
-use warnings;
-
-package Kephra::API::Event;
-
-my %event;
-
-sub add_callback {}
-sub del_callback {}
-sub trigger_callback {}
-sub freeze_callback {}
-sub thaw_callback{}
-
-sub add_event {}
-sub del_event {}
-sub trigger_event {}
-sub freeze_event {}
-sub thaw_event{}
-
-sub global_halt {}
-sub global_restore {}
-
-
-1;

lib/Kephra/API/GuiBuilder.pm

-use v5.12;
-use warnings;
-
-package Kephra::API::GuiBuilder;
-
-1;

lib/Kephra/API/KeyMap.pm

-use strict;
-use warnings;
-use Wx;
-use Kephra::API;
-
-package Kephra::API::KeyMap;
-
-my %definition;
-my %code;
-my %mod_key_value = ( shift => 1000, ctrl => 2000, alt  => 4000);
-my %key_value = (
-	left  => &Wx::WXK_LEFT,      right => &Wx::WXK_RIGHT,
-	up    => &Wx::WXK_UP,         down => &Wx::WXK_DOWN,
-	pageup=> &Wx::WXK_PAGEUP, pagedown => &Wx::WXK_PAGEDOWN,
-	home  => &Wx::WXK_HOME,        end => &Wx::WXK_END,
-	delete => &Wx::WXK_DELETE,  insert => &Wx::WXK_INSERT,
-	back  => &Wx::WXK_BACK,        tab => &Wx::WXK_TAB,
-	esc   => &Wx::WXK_ESCAPE,    
-	enter => &Wx::WXK_RETURN,    space => &Wx::WXK_SPACE,
-	F1 => &Wx::WXK_F1, F2 => &Wx::WXK_F2,  F3 => &Wx::WXK_F3,  F4 => &Wx::WXK_F4,
-	F5 => &Wx::WXK_F5, F6 => &Wx::WXK_F6,  F7 => &Wx::WXK_F7,  F8 => &Wx::WXK_F8,
-	F9 => &Wx::WXK_F9,F10 => &Wx::WXK_F10,F11 => &Wx::WXK_F11,F12 => &Wx::WXK_F12,
-	pound => 35, plus => 43, minus => 45, sharp => 47, tilde => 92, 
-	num_left  => &Wx::WXK_NUMPAD_LEFT,  num_right=> &Wx::WXK_NUMPAD_RIGHT,
-	num_up    => &Wx::WXK_NUMPAD_UP,    num_down => &Wx::WXK_NUMPAD_DOWN,
-	num_space => &Wx::WXK_NUMPAD_SPACE, num_tab  => &Wx::WXK_NUMPAD_TAB,
-	num_enter => &Wx::WXK_NUMPAD_ENTER, num_F1   => &Wx::WXK_NUMPAD_F1,
-);
-my $main_map = 'editor';
-
-sub apply {}
-sub build {}
-sub copy {}
-sub overlay {}
-sub register_map {
-}
-
-sub register_keys {
-	my ($keys, $map) = @_;
-	#return Kephra::Log::warning("got no hashref") unless ref $keys eq ref {};
-	$map = $main_map unless defined $map;
-	for my $cmd (keys %$keys) {
-		$definition{$map}{ $cmd } = $keys->{$cmd};
-		my $code = keycode_from_definition( $keys->{$cmd} );
-		unless (exists $code{$map}{ $code }) { $code{$map}{ $code } = $cmd }
-		else {
-			#Kephra::Log::warning("$cmd tried to register the already taken keycode $code");
-		}
-	}
-}
-
-sub keycode_is_registered {
-	my ($code, $map) = @_;
-	#Kephra::Log::warning('got no key code') unless $code;
-	$map = $main_map unless defined $map;
-	return 1 if defined $code{$map}{$code} and $code{$map}{$code}
-}
-
-sub cmd_from_keycode {
-	my ($code, $map) = @_;
-	#Kephra::Log::warning('got no key code') unless $code;
-	$map = $main_map unless defined $map;
-	$code{$map}{$code};
-}
-
-sub keycode_from_definition {
-	my $def = shift;
-	$def =~ tr/ 	//d;
-	#Kephra::Log::warning('got no key definition') unless $def;
-
-	my @key = split '\+', $def;           # only + can combine key in definition
-	my $code = length($key[-1]) == 1 ? ord uc $key[-1] : $key_value{ $key[-1] };
-	#Kephra::Log::warning('don\'t know this key '.$key[-1]) unless $code;
-
-	$code += $mod_key_value{ shift(@key) } while @key > 1;
-	#Kephra::Log::warning("got unknown key definition $def") unless $code;
-	return $code;
-}
-
-sub keycode_from_event {
-	my $event = shift;
-	#Kephra::Log::warning ("got no event, but $event") unless ref $event and $event->isa('Wx::Event');
-	my $code = $event->GetKeyCode;
-	$code += $mod_key_value{'shift'} if $event->ShiftDown;
-	$code += $mod_key_value{'ctrl'} if $event->ControlDown;
-	$code += $mod_key_value{'alt'} if $event->AltDown;
-	return $code;
-}
-
-sub react_on_event {
-	my ($event, $map) = @_;
-	#Kephra::Log::warning ("got no event, but $event") unless ref $event and $event->isa('Wx::Event');
-	$map = $main_map unless defined $map;
-	my $key = keycode_from_event($event);
-	#Kephra::API::log("pressed key $key inside the ".(caller)[0]);
-	if (keycode_is_registered($key)){
-			my $cmd = cmd_from_code($key);
-			#Kephra::API::log("run command: $cmd");
-			#Kephra::API::Command::run( $cmd );
-	}
-	else {$event->Skip}
-}
-
-1;

lib/Kephra/API/Log.pm

Empty file removed.

lib/Kephra/API/MacroRecorder.pm

-use v5.12;
-use warnings;
-
-package Kephra::API::MacroRecorder;
-
-1;

lib/Kephra/API/Plugin.pm

-use v5.12;
-use warnings;
-
-package Kephra::API::Plugin;
-
-1;

lib/Kephra/API/Sandrum.pm

-use v5.12;
-use warnings;
-
-package Kephra::API::Sandrum;
-
-1;

lib/Kephra/App/DSL.pm

+use v5.12;
+use warnings;
+
+package Kephra::API::GuiBuilder;
+
+1;

lib/Kephra/App/KeyMap.pm

+use strict;
+use warnings;
+use Wx;
+use Kephra::API;
+
+package Kephra::API::KeyMap;
+
+my %definition;
+my %code;
+my %mod_key_value = ( shift => 1000, ctrl => 2000, alt  => 4000);
+my %key_value = (
+	left  => &Wx::WXK_LEFT,      right => &Wx::WXK_RIGHT,
+	up    => &Wx::WXK_UP,         down => &Wx::WXK_DOWN,
+	pageup=> &Wx::WXK_PAGEUP, pagedown => &Wx::WXK_PAGEDOWN,
+	home  => &Wx::WXK_HOME,        end => &Wx::WXK_END,
+	delete => &Wx::WXK_DELETE,  insert => &Wx::WXK_INSERT,
+	back  => &Wx::WXK_BACK,        tab => &Wx::WXK_TAB,
+	esc   => &Wx::WXK_ESCAPE,    
+	enter => &Wx::WXK_RETURN,    space => &Wx::WXK_SPACE,
+	F1 => &Wx::WXK_F1, F2 => &Wx::WXK_F2,  F3 => &Wx::WXK_F3,  F4 => &Wx::WXK_F4,
+	F5 => &Wx::WXK_F5, F6 => &Wx::WXK_F6,  F7 => &Wx::WXK_F7,  F8 => &Wx::WXK_F8,
+	F9 => &Wx::WXK_F9,F10 => &Wx::WXK_F10,F11 => &Wx::WXK_F11,F12 => &Wx::WXK_F12,
+	pound => 35, plus => 43, minus => 45, sharp => 47, tilde => 92, 
+	num_left  => &Wx::WXK_NUMPAD_LEFT,  num_right=> &Wx::WXK_NUMPAD_RIGHT,
+	num_up    => &Wx::WXK_NUMPAD_UP,    num_down => &Wx::WXK_NUMPAD_DOWN,
+	num_space => &Wx::WXK_NUMPAD_SPACE, num_tab  => &Wx::WXK_NUMPAD_TAB,
+	num_enter => &Wx::WXK_NUMPAD_ENTER, num_F1   => &Wx::WXK_NUMPAD_F1,
+);
+my $main_map = 'editor';
+
+sub apply {}
+sub build {}
+sub copy {}
+sub overlay {}
+sub register_map {
+}
+
+sub register_keys {
+	my ($keys, $map) = @_;
+	#return Kephra::Log::warning("got no hashref") unless ref $keys eq ref {};
+	$map = $main_map unless defined $map;
+	for my $cmd (keys %$keys) {
+		$definition{$map}{ $cmd } = $keys->{$cmd};
+		my $code = keycode_from_definition( $keys->{$cmd} );
+		unless (exists $code{$map}{ $code }) { $code{$map}{ $code } = $cmd }
+		else {
+			#Kephra::Log::warning("$cmd tried to register the already taken keycode $code");
+		}
+	}
+}
+
+sub keycode_is_registered {
+	my ($code, $map) = @_;
+	#Kephra::Log::warning('got no key code') unless $code;
+	$map = $main_map unless defined $map;
+	return 1 if defined $code{$map}{$code} and $code{$map}{$code}
+}
+
+sub cmd_from_keycode {
+	my ($code, $map) = @_;
+	#Kephra::Log::warning('got no key code') unless $code;
+	$map = $main_map unless defined $map;
+	$code{$map}{$code};
+}
+
+sub keycode_from_definition {
+	my $def = shift;
+	$def =~ tr/ 	//d;
+	#Kephra::Log::warning('got no key definition') unless $def;
+
+	my @key = split '\+', $def;           # only + can combine key in definition
+	my $code = length($key[-1]) == 1 ? ord uc $key[-1] : $key_value{ $key[-1] };
+	#Kephra::Log::warning('don\'t know this key '.$key[-1]) unless $code;
+
+	$code += $mod_key_value{ shift(@key) } while @key > 1;
+	#Kephra::Log::warning("got unknown key definition $def") unless $code;
+	return $code;
+}
+
+sub keycode_from_event {
+	my $event = shift;
+	#Kephra::Log::warning ("got no event, but $event") unless ref $event and $event->isa('Wx::Event');
+	my $code = $event->GetKeyCode;
+	$code += $mod_key_value{'shift'} if $event->ShiftDown;
+	$code += $mod_key_value{'ctrl'} if $event->ControlDown;
+	$code += $mod_key_value{'alt'} if $event->AltDown;
+	return $code;
+}
+
+sub react_on_event {
+	my ($event, $map) = @_;
+	#Kephra::Log::warning ("got no event, but $event") unless ref $event and $event->isa('Wx::Event');
+	$map = $main_map unless defined $map;
+	my $key = keycode_from_event($event);
+	#Kephra::API::log("pressed key $key inside the ".(caller)[0]);
+	if (keycode_is_registered($key)){
+			my $cmd = cmd_from_code($key);
+			#Kephra::API::log("run command: $cmd");
+			#Kephra::API::Command::run( $cmd );
+	}
+	else {$event->Skip}
+}
+
+1;

lib/Kephra/Boss.pm

Empty file added.

lib/Kephra/Command.pm

+use v5.12;
+use warnings;
+
+package Kephra::API::Command;                             # callable by the user
+
+my %list;     # by ID
+#my %keyproxy;# by keycode
+my %switchvar; #switchvar vor cmd definitions
+my $done_init = 0;
+
+
+sub      _raw_list { \%list }
+sub sub_exists     { no strict 'refs'; !!*{ $_[0] }{CODE} if $_[0] }
+sub package_exists { no strict 'refs'; !!%{ $_[0] . '::' } if $_[0] }
+sub calling_module { ( caller(1) )[0] }
+
+sub switch_variables {
+	my ($var_def) = shift;
+	my $api = 'Kephra::API';
+	my $caller = calling_module();
+	return Kephra::Log::error("only callable by $api", 1) if $caller ne $api;
+	#return Kephra::Log::error("call me just once", 1) if keys %switchvar;
+	return Kephra::Log::error("need a hashref", 1) unless ref $var_def eq 'HASH';
+	for my $var (keys %$var_def){
+		delete $var_def->{$var} unless substr($var, 0, 1) eq '$';
+		delete $var_def->{$var} unless sub_exists( $api .'::'. $var_def->{$var}{'switch'} );
+		delete $var_def->{$var} unless package_exists ( $var_def->{$var}{'class'} );
+	}
+	%switchvar = %$var_def;
+	compile( keys %list );
+	$done_init = 1;
+}
+
+sub register {
+	my $cmd = shift;
+	return Kephra::Log::error("cmd def have to be in a hash ref, not $cmd", 1)
+		unless ref $cmd eq 'HASH';
+	my $caller = calling_module();
+	for my $ID (keys %$cmd) {
+		Kephra::Log::warning( 
+			"$cmd already registered to do ". property($ID, 'sub')." by ".
+			property($ID, 'source')  ), next if registered( $ID );
+		#for (qw/sub options state event label help keys icon/){} filter input later
+		$list{$ID}           = $cmd->{$ID};
+		$list{$ID}{'source'} = $caller;
+		compile($ID) if $done_init;
+	}
+}
+
+sub compile {
+	for my $cmd_ID (@_) {
+		my $cmd = $list{ $cmd_ID };
+		my $sub = $cmd->{'sub'};
+		Kephra::Log::warning( "$cmd_ID lacks value on hashkey 'sub'"), next unless $sub;
+
+		if ( substr($sub, 0, 1) eq '$'){
+			my $method_pos = index($sub, '->');
+			my $var = substr($sub, 0, $method_pos);
+			Kephra::Log::warning
+				("unknown switchvar $var in $cmd_ID 'sub' entry", 1), next
+					unless ref $switchvar{ $var };
+			my $method = $switchvar{$var}{'class'}.'::'.substr($sub, $method_pos+2);
+			# move that check into test suite
+			#Kephra::Log::warning ("called unknown method with $sub", 1), next
+			#	unless sub_exists($method) ;
+			
+			$cmd->{'coderef'} = 'Kephra::API::' . $switchvar{$var}{'switch'} . '()' .
+			                      substr($sub, $method_pos);
+		}
+		elsif ( index($sub, '::') == -1) {
+			$cmd->{'coderef'} = $cmd->{'source'} . '::' . $sub
+		}
+		else { $cmd->{'coderef'} = $sub }
+
+		# add parameter if are any
+		$cmd->{'coderef'} .= exists $cmd->{'parameter'} # just one parameter yet
+			? "( '" . $cmd->{'parameter'} . "' )"
+			: '()';
+		$cmd->{'coderef'} = eval 'sub { ' . $cmd->{'coderef'} . ' }';
+
+		Kephra::API::KeyMap::register_keys({ $cmd_ID => $cmd->{'keys'} }) if $cmd->{'keys'};
+	}
+}
+
+
+sub run {
+	my $cmd = shift;
+	$cmd = [$cmd] unless ref $cmd eq ref [];
+	my $return;
+	for (@$cmd) { $return = $list{$_}{'coderef'}->() if exists $list{$_} }
+	$return;
+}
+sub run_by_keycode {
+	my ($code, $map) = @_;
+}
+
+sub registered      { 1 if defined $_[0]  and exists $list{ $_[0] }             }
+sub property_exists { 1 if registered($_[0]) and exists $list{ $_[0] }{ $_[1] } }
+sub add {}
+
+sub all_properties { 
+	if ( registered($_[0]) ) { $list{$_[0]} }
+	else { Kephra::Log::warning('requested data of unknown command '.$_[0], 1) }
+}
+sub property       { $list{$_[0]}{$_[1]}         if property_exists(@_) }
+sub set_property   { $list{$_[0]}{$_[1]} = $_[2] if property_exists(@_) }
+
+
+1;
+
+__END__
+
+=head1 Command Definition
+
+cmd_ID => {
+	coderef   => compiled from sub, source and option, saves state
+	sub       => 'Kephra::File::new',
+	parameter => [],
+	source    => package that registered that cmd
+	state     => return value of the call or antother coderef
+	event     => '',
+	label     => 'New',
+	help      => '', # help text to be shown in statusbar or as popup
+	keys      => 'Ctrl + N',
+	icon      => '',
+	bitmap    => '',  #Wx::Bitmap Object
+}

lib/Kephra/Config/File/Location.pm

-use v5.10;
-use strict;
-use warnings;
-use Cwd;
-use FindBin;
-
-package Kephra::Config::File::Location;
-my $base_path = $Kephra::MODE eq 'test'
-	? Cwd::cwd()
-	: File::UserConfig->configdir( dist => $Kephra::NAME );
-
-
-;
-
-
-sub init {
-	#$base_path
-	#say Cwd::cwd();
-	#say Kephra::configdir();
-	#say $base_path;
-	#my $path = File::Spec->catfile(Cwd::cwd(), __FILE__);
-	#say rindex($path, 'Kephra'
-	#my $base_madule_name = (split '::', __PACKAGE__)[0];
-	#say $FindBin::Bin;
-	#say File::Spec->catfile(Cwd::cwd(), __FILE__) if -e $path;
-	#say File::UserConfig->configdir('Kephra');
-	
-	#say File::HomeDir->my_home;
-}
-
-
-
-1;

lib/Kephra/Config/Global.pm

Empty file added.

lib/Kephra/Config/Tree.pm

-use strict;
-use warnings;
-
-package Kephra::Config::Tree;
-
-# just merge complex structures, prefer first on conflicts
-sub merge {}
-
-# positiv if there keys in forst but not second HoH
-sub conflict {}
-
-# only leave structures at keys that also exist in second HoH
-sub trunc {}
-
-# 
-sub update_layout {}
-
-# convert HoH to flat Hash
-sub flat {}
-# convert flat hash to HoH
-sub restructure {}
-
-1;

lib/Kephra/Data/Tree.pm

+use strict;
+use warnings;
+
+package Kephra::Config::Tree;
+
+# just merge complex structures, prefer first on conflicts
+sub merge {}
+
+# positiv if there keys in forst but not second HoH
+sub conflict {}
+
+# only leave structures at keys that also exist in second HoH
+sub trunc {}
+
+# 
+sub update_layout {}
+
+# convert HoH to flat Hash
+sub flat {}
+# convert flat hash to HoH
+sub restructure {}
+
+1;

lib/Kephra/DocumentStash.pm

+use v5.12;
+use warnings;
+use Wx;
+use Kephra::App::Editor;
+use Kephra::App::Panel;
+use Kephra::Document;
+
+package Kephra::API::DocumentStash;
+my %document = ('ID'=>{});             # keys of subhashes: ID editor panel file
+my $active_doc;
+my $lowest_free_ID = 1;
+my $lowest_free_anon_NR = 1;
+
+use constant DOC_CLASS => 'Kephra::Document';
+use Scalar::Util qw(blessed looks_like_number);
+
+
+sub active_doc    { $active_doc || '' }
+sub active_editor { $active_doc->editor->{Kephra::API::tabbar()} if $active_doc }
+sub active_panel  { $active_doc->panel->{Kephra::API::tabbar()} if $active_doc }
+sub set_active    {
+	my $doc = find( shift );
+	$active_doc = $doc if $doc;
+}
+sub previous      { App::Kephra::Focus::last( DOC_CLASS ) }
+sub all_docs      { values %{$document{'ID'}} }
+
+sub is_doc { return 1 if blessed($_[0]) and $_[0]->isa( DOC_CLASS ); return 0 }
+
+
+sub add_doc {
+	my $doc = shift;
+	Kephra::Log::warning('only accepting ' . DOC_CLASS . " objects, not $doc!", 1) unless is_doc($doc);
+	$document{'ID'}{$lowest_free_ID} = $doc;
+	if (ref $doc->editor eq 'HASH') {
+		#add_doc_instance($doc, $_) for keys $doc->editor;
+	}
+	if ($doc->file_path) {
+		$document{'file'}{$doc->file_path} = $doc;
+	} else {
+		$doc->anon_NR( $lowest_free_anon_NR++ );
+		$document{'anon'}{$doc->anon_NR} = $doc;
+	}
+	$lowest_free_ID++;
+}
+
+
+sub rename_file {
+	my $doc = find( shift );
+	Kephra::Log::warning('need a ' . DOC_CLASS . " thats in stash, not $doc!", 1)
+		unless is_doc($doc);
+	my $file = $doc->prev_file_path;
+	delete $document{'file'}{$file} if $file and $document{'file'}{$file};
+	if ($doc->file_path){
+		$document{'file'}{$doc->file_path} = $doc;
+		unless ($doc->prev_file_path) {
+			delete $document{'anon'}{$doc->anon_NR};
+			$doc->anon_NR(0);
+		}
+	} else {
+		if ($doc->prev_file_path) {
+			$doc->anon_NR( $lowest_free_anon_NR++ );
+			$document{'anon'}{$doc->anon_NR} = $doc;
+		}
+	}
+}
+
+
+sub remove_doc {
+	my $doc = shift;
+#say "remove $doc ", scalar keys %{$document{'ID'}}, ' ',scalar keys %{$document{'file'}}; 
+	Kephra::Log::warning('need a ' . DOC_CLASS . ' thats in stash, not $doc!', 1)
+		unless is_doc($doc);
+	delete $document{'ID'}{$doc->ID} if $doc->ID;
+	#remove_doc_instance($doc, $_) for values $doc->panel;
+	delete $document{'file'}{$doc->file_path} if $doc->file_path;
+#say "removed $doc ", scalar keys %{$document{'ID'}}, ' ',scalar keys %{$document{'file'}}; 
+}
+
+
+sub add_doc_instance {
+	my ($doc, $bar) = @_;
+	return Kephra::Log::warning('need as first parameter a'.DOC_CLASS.' thats in stash, not $doc!', 1)
+		unless is_doc($doc);
+	#return Kephra::Log::warning("need a Kephra::App::Bar::Document instance as second parameter, not $bar")
+	#	unless blessed($bar) eq 'Kephra::App::Bar::Document';
+	$document{'panel'} { $doc->panel->{$bar}  } = $doc;
+	$document{'editor'}{ $doc->editor->{$bar} } = $doc;
+}
+
+
+sub remove_doc_instance {
+	my ($doc, $bar) = @_;
+	return Kephra::Log::warning('need as first parameter a'.DOC_CLASS.' thats in stash, not $doc!', 1)
+		unless is_doc($doc);
+	return Kephra::Log::warning("need a Kephra::App::Bar::Document instance as second parameter, not $bar")
+		unless blessed($bar) eq 'Kephra::App::Bar::Document';
+	delete $document{'panel'}{ $doc->panel->{$bar} };
+	delete $document{'editor'}{ $doc->editor->{$bar} };
+}
+
+
+sub find {
+	my $any = shift;
+	Kephra::Log::warning("need a parameter!", 1) unless defined $any and $any;
+	return     looks_like_number($any)                                     ? $document{'ID'}    {$any}
+	         : ref($any) eq ''                                             ? $document{'file'}  {$any}
+	         : $any->isa('Kephra::App::Panel')                             ? $document{'panel'} {$any}
+	         : $any->isa('Kephra::App::Editor')                            ? $document{'editor'}{$any}
+	         :($any->isa(DOC_CLASS) and $document{'ID'}{$any->ID} eq $any) ? $any
+	         :                                                               undef;
+}
+
+
+sub file_loaded {
+	my $file_path = shift;
+	#Kephra::Log::warning("needs a file path as input!") unless $file_path and -e $file_path;
+	for my $doc ( all_docs() ) {
+		return 1 if $doc->file_path and $doc->file_path eq $file_path;
+	}
+}
+
+
+sub all_editors { keys %{$document{'editor'}} }
+sub do_with_all_editors {
+	
+}
+
+1;

lib/Kephra/Event.pm

+use v5.12;
+use warnings;
+
+package Kephra::API::Event;
+
+my %event;
+
+sub add_callback {}
+sub del_callback {}
+sub trigger_callback {}
+sub freeze_callback {}
+sub thaw_callback{}
+
+sub add_event {}
+sub del_event {}
+sub trigger_event {}
+sub freeze_event {}
+sub thaw_event{}
+
+sub global_halt {}
+sub global_restore {}
+
+
+1;

lib/Kephra/File/Local.pm

-use v5.12;
-use warnings;
-use Encode;
-use Encode::Guess;
-use Kephra::API;
-use Kephra::File;
-
-package Kephra::File::Local;
-
-
-sub read {
-	my $doc = shift;
-	my $file = Kephra::File::normalize_path( shift ) || $doc->file_path;
-	return Kephra::Log::warning("can't load nonexising file") unless $file and -e $file;
-	return Kephra::Log::warning("can't read $file") unless -r $file;
-	my $ed = $doc->editor->{ Kephra::API::docbar() };
-	CORE::open my $FH, '<', $file;
-	binmode($FH);
-	my $content = do { local $/; <$FH> };
-	if ($content) {
-
-		my @guesses = qw/utf-8 iso8859-1 latin1/;
-		my $guess = Encode::Guess::guess_encoding( $content, @guesses );
-		if ( ref($guess) and ref($guess) =~ m/^Encode::/ ) {
-			$doc->encoding( $guess->name );
-		} elsif ( $guess =~ m/utf8/ ) {
-			$doc->encoding( 'utf-8' );
-		} elsif ( $guess =~ m/or/ ) {
-			my @suggest_encodings = split /\sor\s/, "$guess";
-			$doc->encoding( $suggest_encodings[0] );
-		} else { $doc->encoding( 'utf-8' ) }
-		$content = Encode::decode( $doc->encoding,  $content );
-		#say $doc->encoding; 
-
-		$ed->SetText( $content );
-		$ed->SetSavePoint;
-	}
-}
-
-sub write {
-	my $doc = shift;
-	my $file = Kephra::File::normalize_path( shift ) || $doc->file_path;
-	return Kephra::Log::warning("need a file path") unless $file;
-	return Kephra::Log::warning("can't overwrite $file") if -e $file and not -w $file;
-	$doc->file_path($file) unless defined $doc->file_path and $doc->file_path eq $file;
-	my $ed = $doc->editor->{ Kephra::API::docbar() };
-	CORE::open my $FH, '> :raw :encoding('.$doc->encoding.')', $file;
-	print $FH $ed->GetText;
-	$ed->SetSavePoint;
-}
-
-
-1;

lib/Kephra/IO/LocalFile.pm

+use v5.12;
+use warnings;
+use Encode;
+use Encode::Guess;
+use Kephra::API;
+use Kephra::File;
+
+package Kephra::File::Local;
+
+
+sub read {
+	my $doc = shift;
+	my $file = Kephra::File::normalize_path( shift ) || $doc->file_path;
+	return Kephra::Log::warning("can't load nonexising file") unless $file and -e $file;
+	return Kephra::Log::warning("can't read $file") unless -r $file;
+	my $ed = $doc->editor->{ Kephra::API::docbar() };
+	CORE::open my $FH, '<', $file;
+	binmode($FH);
+	my $content = do { local $/; <$FH> };
+	if ($content) {
+
+		my @guesses = qw/utf-8 iso8859-1 latin1/;
+		my $guess = Encode::Guess::guess_encoding( $content, @guesses );
+		if ( ref($guess) and ref($guess) =~ m/^Encode::/ ) {
+			$doc->encoding( $guess->name );
+		} elsif ( $guess =~ m/utf8/ ) {
+			$doc->encoding( 'utf-8' );
+		} elsif ( $guess =~ m/or/ ) {
+			my @suggest_encodings = split /\sor\s/, "$guess";
+			$doc->encoding( $suggest_encodings[0] );
+		} else { $doc->encoding( 'utf-8' ) }
+		$content = Encode::decode( $doc->encoding,  $content );
+		#say $doc->encoding; 
+
+		$ed->SetText( $content );
+		$ed->SetSavePoint;
+	}
+}
+
+sub write {
+	my $doc = shift;
+	my $file = Kephra::File::normalize_path( shift ) || $doc->file_path;
+	return Kephra::Log::warning("need a file path") unless $file;
+	return Kephra::Log::warning("can't overwrite $file") if -e $file and not -w $file;
+	$doc->file_path($file) unless defined $doc->file_path and $doc->file_path eq $file;
+	my $ed = $doc->editor->{ Kephra::API::docbar() };
+	CORE::open my $FH, '> :raw :encoding('.$doc->encoding.')', $file;
+	print $FH $ed->GetText;
+	$ed->SetSavePoint;
+}
+
+
+1;

lib/Kephra/Internals.pod

-=head1 Kephra::Internals
-
-If you want to touch the Kephra sources -
-this document explains where to find what and roughly how it works.
-The terminology is explained in the glossary at the end.
-In case you learn better by looking at the good documented code - start at Kephra::API.
-
-=head2 NAMESPACES
-
-In each file is only one package with exactly the same name as the file.
-
-With every directory you go deeper into the tree the modules will get more
-specific. Thats why the directory "lib/Kephra" has only the most common
-tools and libraries. Other dir (a good first approximation is the main menu):
-
-=over 4
-
-=item * API - API (proxy) for most cross module calls and plugins to access central data structures
-
-=item * App - visuals
-
-=over 4
-
-=item * Bar - container widgets (menu bar, toolbar, tab bar, status bar, etc.)
-
-=item * Editor - editor widget helper
-
-=item * Panel - with sizer assembled visual units (snippet lib, io unit, etc.)
-
-=item * Part - functional units (main part, side panel, etc,)
-
-=back
-
-=item * Config - data
-
-=over 4
-
-=item * Default - built in fall back configs when certain files are in trouble
-
-=back
-
-=item * Document - document properties
-
-=item * Edit - text manipulation functions
-
-=item * Files - all sorts of IO
-
-=item * Plugins - namespace of users extentions
-
-=back
-
-
-
-=head2 MODULES
-
-=over 4
-
-=item * Kephra
-
-global vars like version number and the init process:
-finding configs, setting dirs, forking to the worker, loading libs, starting app
-
-
-=item * Kephra::API
-
-- interface to most important functions all modules and plugins should use
-- provides also simplified aliases to Kephra::API::* namespaces
-- as long as the first version number (see L<Versioning>) does not change,
-  no call will be renamed or removed
-
-=item * Kephra::API::Command
-
-- Every function the user is able to trigger has an CommandID
-  and gets called over this API.
-- In head of every module after package and use and vars are the definitions
-  which rgister these commands in this data structure.
-- Its for monitoring, triggers, macros and other introspection.
-- Also helps to make simple menu and toolbar definitions (are lists of CommandIDs).
-
-=item * Kephra::API::DocumentStash
-
-- Find docs by any attribute or categories.
-
-=item * Kephra::API::Event
-
-- trigger, freeze, thaw, add and remove events or calls attached to them
-- covers also internal calls
-
-=item * Kephra::API::GuiBuilder
-
-- transform wiki syntax => data structure => GUI and back
-
-=item * Kephra::API::KeyMap
-
-- managing the widgets mappings of key combo => CommandID
-
-=item * Kephra::API::Macro
-
-- record, play, store and load macros
-
-=item * Kephra::API::Plugin
-
-- load, reload and unsubscribe plugins
-
-=item * Kephra::API::Sandrum
-
-- vi like shortcut language for triggering commands from the IO Unit by typing
-
-=item * Kephra::App
-
-- root object of all GUI
-- handles GUI related boot and shutdown sequence
-- derived from Wx::App
-
-=item * Kephra::App::Bar::Tab
-
-- base TabBar, previous DocBar
-
-=item * Kephra::App::Dialog
-
-- all dialogs are called from here
-
-=item * Kephra::App::Editor
-
-- main class of the editing widgets
-
-=item * Kephra::App::Focus
-
-- keeps track in which widget got to make save way back if needed
-
-=item * Kephra::App::Menu
-
-- compiles menu definitions to data structures and them into menus
-
-=item * Kephra::App::PaintBar
-=item * Kephra::App::Panel  - wrapper for Wx::Panel, uses K::App::Sizer
-=item * Kephra::App::Sizer
-=item * Kephra::App::Splitter
-=item * Kephra::App::Toolbar
-=item * Kephra::App::Util  -  GUI helper
-=item * Kephra::App::Window  -  main window with all layout
-
-=item * Kephra::Config  -  config menu function (internal data handling)
-
-=item * Kephra::Document  -  document menu (doc properties)
-
-=item * Kephra::Edit  -  basics functions for the edit menu
-
-=item * Kephra::File  -  most file menu functions
-
-=item * Kephra::Help  -  display documentation
-
-=item * Kephra::Log  -  logger
-
-=item * Kephra::Worker  -  does heavy lifting work in the background, so app stays reactive 
-
-=back
-
-=head1 Glossary
-
-=head2 App
-
-everything visible and GUI (Wx) related
-
-=head2 App::Part
-
-visual area dedicated for one purpose, editor is the most prominent,
-but ther are also FileBrowser, IOUnit and more
-
-=head2 Editor
-
-one widget for editing text
-
-=head2 Edit
-
-namespace for the actual editing operations
-
-=head2 Panel
-
-area to place widget on, can be under anything, even under each editor
-a Kephra::App::Panel is a helper class that manages its sizer
-(visibility and ordering of elements)
-
-=head1 Document
-
-=head2  Interdependency Of Core Modules
-
-=head2  Modules vs Objects
-
-=head2  Boot Stages
-
-Just using the fact that when modules are loded (after the fork)
-there main code (outside the sub) is run. At this time we create the
-basic command list and the definition which module gets which part of
-the global config (actual loading happens later).
-
-=head2  Worker Fork
-
-
-=cut
-

lib/Kephra/Philosophy.pod

-=head1 Kephra Philosophy
-
-
-=head2  Main Principle
-
-... is simply freedom. Because even programmers are human beings in the first
-place and inherently want to feel free, joyful and creative (productive).
-
-And you are not free if it doesn't run on your operating system ...,
-is gigantic, slow, has a lot of dependencies or a restricitve license
-or can't just be copied around.
-
-The usage is also crippled if important features are missing or
-you have to learn something you don't want to in order to get your work done.
-
-The beginner is glad to do his stuff by simple and visually-assisted methods,
-however experts are happiest when they control every detail and achieve their
-goals as fast as they can think.
-True freedom consideres both types of users and a smooth path to become an expert.
-
-Freedom also means not to get stuck in old ways,
-but combine the best (not the most popular) ideas available and not being
-afraid to make own inventions.
-
-And the full delight can only manifest if it's beautiful on every level. 
-
-
-
-=head2  Strategies
-
-
-=head3 Simple
-
-Aiming toward less code and only necessary functions is the first rule
-for several reasons. Yes it is the basis for a small, fast, clean and maintainable
-program. But also the only option since our dev team is frankly basically just me.
-
-=head3 Mature
-
-Because half baked solutions are not really enjoyable,
-we try to develope our features to functional and visual perfection
-before we publish them in a version marked as stable.
-Thats why Kephra has very few open construction zones at a time.
-
-=head3 Complete
-
-All the tools you need should be on one workbench.
-Either provided by Kephra or well integrated, originating from the operating
-system or third party software. This enables uninterrupted workflows and
-functions (or macros) that solve complex tasks at once.
-
-Kephra makes it easy to integrate your favorite program too.
-
-=head3 Individual
-
-An editor should fully match all preferences of its user,
-that spends so much time with it.
-Several types of communication with the program (like notepad, vi, emacs) we
-plan to support and most features and visuals can be configured and disabled
-via a typed commands, a dialog and config files.
-Yet many setting can be changed most quickly in place of their display by mouse.
-For deeper modifications use the extensive plugin API, that can be even
-left behind (with risk). There is no special plugin language - it's all Perl,
-which also supports your individual coding style.
-
-=head3 Unix-Like
-
-To achieve the proposed simplicity and power Kephra has a similar design like Unix. 
-That means: it contains a few flexible tools solve the fundamental tasks thoroughly.
-Only difference: for the most common tasks we already added some sugar,
-ready-to-use functions that combine several of these tools.
-
-Consequently are "introspection" and "shell integration" more than buzzwords to us.
-Use the tool you like best within your editor and with some smart data pre- and
-postprocessing the is more possible than you you might think.
-
-=head3 Graphical
-
-To search for new and advanced graphical input and display methods was one of
-the original reasons for Kephra being founded. Graphics allow a much higher
-information density than text, which is very needed in a software world that
-is getting ever more complex. 
-
-=head3 Information-Centered
-
-I believe that editor can assist their users much more in creating, sorting,
-storing and sharing their relevant data in many formats like templates, 
-macros, settings, colouring schemes or even plugins. It may start with small
-text snippet you want to set aside and save for later reuse that can grow
-step by step into a plugin.
-
-
-But also while normal usage, the editor can remember details to prevent repetitive work.
-It's important to us seeing you as the owner of all your data. 
-
-
-=head2  DISCLAIMER
-
-Like platons politeia, this document describes the ideal state of version 1.0.

lib/Kephra/PluginManager.pm

+use v5.12;
+use warnings;
+
+package Kephra::API::Plugin;
+
+1;

lib/Kephra/StyleGuide.pod.pm

-
-
-=head1 Kephra Coding Style
-
-
-=head2 Perl Defaults
-
-    use v5.12;
-    use warnings;
-
-
-=head2 Technical Details
-
-    bracketing     = K & R
-    indention size = 4
-    use of tabs    = yes
-    line with      = 80 (not strict)
-    utf            = only in strings
-    perl version   = 5.12
-    pragmas        = warnings
-
-
-=head2  What Part Of Perl We Use Or Avoid
-
-
-
-
-=head2 Organisation
-
-1 module - 1 name - 1 namespace
-
-
-
-=head2 Naming
-
-We try to reuse same names for same (seldom similar) purpose,
-throughout all modules,
-or the same data part through different structures.
-
-We also try to lean toward natural Perl names (eg 'sub' 'coderef', 'push').
-
-
-=head3 module/class Names
-
-camelcase                      ~~ LikeThis
-
-
-=head3 sub/method Names
-
-lowercase words separated by _ ~~ like_this
-
-routines only to be called inside a module start with one _
-avoid to call these from outside by all means
-
-create not make when we generate something
-
-
-=head3 Variable Names
-
-lowercase words separated by _ ~~ like_this
-We say active, no more current or recent.
-
-$self   reference of the class itself
-
-$ed     in most cases the active editor pane
-        (was renamed from $ep to prevent simple copy and paste of old code)
-
-$doc    active document
-
-$tabbar active tab bar
-
-$file   full file path, unless oyur specific like file_name, file_path etc.
-
-$win    main window
-
-
-=head3 Command Names
-
-words separated by -
-
-
-=head3 Event Names
-
-words separated by .
-
-=head3 Key Definitions
-
-keys separated by +

lib/Kephra/Usage.pod

Empty file removed.

lib/Kephra/Versioning.pod

-=head1 Kephra Versioning Schema
-
-=head2 Goals
-
-=over 2
-
-=item * simple (linear as a watch)
-
-=item * measures development status not time
-
-=item * no extras (Alpha, Beta, Release Candidate, Patchlevel)
-
-=back
-
-=head2 General Pattern
-
-  Revision . Stable . Testing . Development
-
-If one number is raised, all to the left get a reset to zero.
-Trailing zeros can be omited - 0.5 is the same as 0.5.0.0
-
-Some similar idea is known as semantic versioning L<http://semver.org>.
-
-=head3 Development
-
-Development number is changed after every important commit.
-Normally after every day of development.
-
-Development releases come with no warranty whatsoever.
-
-=head3 Testing
-
-Testing number is raised if a new feature or change/set has to attract
-the attention of the testers.
-
-Testing releases come with all known bugs disabled (if possible).
-Interfaces glitches may occur.
-
-=head3 Stable
-
-Stable number goes up if several greater new features are in
-and all known bugs are out.
-If there are still bugs,
-they have to be disabled with the feature there are sitting on.
-
-Stable releases should have a polished interface and no bugs.
-If one appears in 0.3 it will be fixed ASAP with 0.3.0.1 a.s.o.
-
-During a time window before releasing a stable version,
-all new features will go into a new branch, which will become 0.3.1.
-The next testing should be released soon after the stable is out.
-
-=head3 Revision
-
-Has the word vision in it. When I got my initial vision we are at 1.0.
-If I have a new one after that or we change a fundamental technology
-there is gonna be a 2.0.
-
-Or in case we just want to break backward compatibility of the Kephra::API,
-it is a signal to all Plugin authors, they have to adapt.
-
-=head2 What Changed?
-
-We dropped the special patchlevel number.
-The version 0.3 Patchlevel 5 is now 0.3.0.5.