aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/common.py156
-rw-r--r--src/editor.py178
-rw-r--r--src/line.py2
3 files changed, 200 insertions, 136 deletions
diff --git a/src/common.py b/src/common.py
new file mode 100644
index 0000000..476cd38
--- /dev/null
+++ b/src/common.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+'''
+pytagomacs – An Emacs like key–value editor library for Python
+
+Copyright © 2013 Mattias Andrée (maandree@member.fsf.org)
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see <http://www.gnu.org/licenses/>.
+'''
+import os
+
+
+
+INACTIVE_COLOUR = '34'
+'''
+:str? The colour of an inactive line
+'''
+
+ACTIVE_COLOUR = '01;34'
+'''
+:str? The colour of an active line
+'''
+
+SELECTED_COLOUR = '44;37'
+'''
+:str? The colour of a selected text
+'''
+
+STATUS_COLOUR = '07'
+'''
+:str? The colour of the status bar
+'''
+
+ALERT_COLOUR = None
+'''
+:str? The colour of the alert message
+'''
+
+KILLRING_LIMIT = 50
+'''
+:int The maximum size of the killring
+'''
+
+EDITRING_LIMIT = 100
+'''
+:int The maximum size of the editring
+'''
+
+
+atleast = lambda x, minimum : (x is not None) and (x >= minimum)
+'''
+Test that a value is defined and of at least a minimum value
+'''
+
+limit = lambda x_min, x, x_max : min(max(x_min, x), x_max)
+'''
+Limit a value to a closed set
+'''
+
+ctrl = lambda key : chr(ord(key) ^ ord('@'))
+'''
+Return the symbol for a specific letter pressed in combination with Ctrl
+'''
+
+backspace = lambda x : (ord(x) == 127) or (ord(x) == 8)
+'''
+Check if a key stroke is a backspace key stroke
+'''
+
+
+
+class Jump():
+ '''
+ Create a cursor jump that can either be included in a print statement
+ as a string or invoked
+
+ @param y:int The row, 1 based
+ @param x:int The column, 1 based
+ @string :str|()→void Functor that can be treated as a string for jumping
+ '''
+ def __init__(self, y, x):
+ self.string = '\033[%i;%iH' % (y, x)
+ def __str__(self):
+ return self.string
+ def __call__(self):
+ print(self.string, end = '')
+
+
+## Load extension and configurations via pytagomacsrc.
+config_file = None
+# Possible auto-selected configuration scripts,
+# earlier ones have precedence, we can only select one.
+files = []
+def add_files(var, *ps, multi = False):
+ if var == '~':
+ try:
+ # Get the home (also known as initial) directory of the real user
+ import pwd
+ var = pwd.getpwuid(os.getuid()).pw_dir
+ except:
+ return
+ else:
+ # Resolve environment variable or use empty string if none is selected
+ if (var is None) or (var in os.environ) and (not os.environ[var] == ''):
+ var = '' if var is None else os.environ[var]
+ else:
+ return
+ paths = [var]
+ # Split environment variable value if it is a multi valeu variable
+ if multi and os.pathsep in var:
+ paths = [v for v in var.split(os.pathsep) if not v == '']
+ # Add files according to patterns
+ for p in ps:
+ p = p.replace('/', os.sep).replace('%', 'pytagomacs')
+ for v in paths:
+ files.append(v + p)
+add_files('XDG_CONFIG_HOME', '/%/%rc', '/%rc')
+add_files('HOME', '/.config/%/%rc', '/.config/%rc', '/.%rc')
+add_files('~', '/.config/%/%rc', '/.config/%rc', '/.%rc')
+add_files('XDG_CONFIG_DIRS', '/%rc', multi = True)
+add_files(None, '/etc/%rc')
+for file in files:
+ # If the file we exists,
+ if os.path.exists(file):
+ # select it,
+ config_file = file
+ # and stop trying files with lower precedence.
+ break
+if config_file is not None:
+ code = None
+ # Read configuration script file
+ with open(config_file, 'rb') as script:
+ code = script.read()
+ # Decode configurion script file and add a line break
+ # at the end to ensure that the last line is empty.
+ # If it is not, we will get errors.
+ code = code.decode('utf-8', 'error') + '\n'
+ # Compile the configuration script,
+ code = compile(code, config_file, 'exec')
+ # and run it, with it have the same
+ # globals as this module, so that it can
+ # not only use want we have defined, but
+ # also redefine it for us.
+ exec(code, globals())
+
diff --git a/src/editor.py b/src/editor.py
index 0b49e77..bc73348 100644
--- a/src/editor.py
+++ b/src/editor.py
@@ -18,7 +18,6 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
-import os
import sys
import string
from subprocess import Popen, PIPE
@@ -30,151 +29,55 @@ _ = gettext.gettext
from killring import *
from editring import *
+from common import *
from line import *
-INACTIVE_COLOUR = '34'
-'''
-:str? The colour of an inactive line
-'''
-
-ACTIVE_COLOUR = '01;34'
-'''
-:str? The colour of an active line
-'''
-
-SELECTED_COLOUR = '44;37'
-'''
-:str? The colour of a selected text
-'''
-
-STATUS_COLOUR = '07'
-'''
-:str? The colour of the status bar
-'''
-
-ALERT_COLOUR = None
-'''
-:str? The colour of the alert message
-'''
-
-KILLRING_LIMIT = 50
-'''
-:int The maximum size of the killring
-'''
-
-EDITRING_LIMIT = 100
-'''
-:int The maximum size of the editring
-'''
-
-
-atleast = lambda x, minimum : (x is not None) and (x >= minimum)
-'''
-Test that a value is defined and of at least a minimum value
-'''
-
-limit = lambda x_min, x, x_max : min(max(x_min, x), x_max)
-'''
-Limit a value to a closed set
-'''
-
-ctrl = lambda key : chr(ord(key) ^ ord('@'))
-'''
-Return the symbol for a specific letter pressed in combination with Ctrl
-'''
+## TODO widthless characters should be ignored when calculating the size a text
+## TODO implement undo history
+##
+## Until the user has halted for 1 second (configurably) or has navigated using arrow keys or alternative key combinations,
+## edits should be accumulated and then stored in the editring. The edits is stored when the next keystroke is made, there
+## should not be a timer waits for the user to idle.
+##
-backspace = lambda x : (ord(x) == 127) or (ord(x) == 8)
-'''
-Check if a key stroke is a backspace key stroke
-'''
+_copy, _cut, _kill, _delete, _erase = Line.copy, Line.cut, Line.kill, Line.delete, Line.erase
+_yank, _yank_cycle, _move_point = Line.yank, Line.yank_cycle, Line.move_point
+_swap_mark, _override = Line.swap_mark, Line.override
+## Editing methods to wrap for undo history
+def full_edit(self, func):
+ # TODO commit changes, if any
+ rc = func(self)
+ # TODO commit changes, if any
+ # TODO reset
+ return rc
-## Load extension and configurations via pytagomacsrc.
-config_file = None
-# Possible auto-selected configuration scripts,
-# earlier ones have precedence, we can only select one.
-files = []
-def add_files(var, *ps, multi = False):
- if var == '~':
- try:
- # Get the home (also known as initial) directory of the real user
- import pwd
- var = pwd.getpwuid(os.getuid()).pw_dir
- except:
- return
+def partial_edit(self, func, with_return = True):
+ # TODO commit changes, if any, if one second has elapsed, and then reset
+ if with_return:
+ return func(self)
else:
- # Resolve environment variable or use empty string if none is selected
- if (var is None) or (var in os.environ) and (not os.environ[var] == ''):
- var = '' if var is None else os.environ[var]
- else:
- return
- paths = [var]
- # Split environment variable value if it is a multi valeu variable
- if multi and os.pathsep in var:
- paths = [v for v in var.split(os.pathsep) if not v == '']
- # Add files according to patterns
- for p in ps:
- p = p.replace('/', os.sep).replace('%', 'pytagomacs')
- for v in paths:
- files.append(v + p)
-add_files('XDG_CONFIG_HOME', '/%/%rc', '/%rc')
-add_files('HOME', '/.config/%/%rc', '/.config/%rc', '/.%rc')
-add_files('~', '/.config/%/%rc', '/.config/%rc', '/.%rc')
-add_files('XDG_CONFIG_DIRS', '/%rc', multi = True)
-add_files(None, '/etc/%rc')
-for file in files:
- # If the file we exists,
- if os.path.exists(file):
- # select it,
- config_file = file
- # and stop trying files with lower precedence.
- break
-if config_file is not None:
- code = None
- # Read configuration script file
- with open(config_file, 'rb') as script:
- code = script.read()
- # Decode configurion script file and add a line break
- # at the end to ensure that the last line is empty.
- # If it is not, we will get errors.
- code = code.decode('utf-8', 'error') + '\n'
- # Compile the configuration script,
- code = compile(code, config_file, 'exec')
- # and run it, with it have the same
- # globals as this module, so that it can
- # not only use want we have defined, but
- # also redefine it for us.
- exec(code, globals())
-
+ func(self)
+break_edit = lambda self, func : full_edit(self, func)
-class Jump():
- '''
- Create a cursor jump that can either be included in a print statement
- as a string or invoked
-
- @param y:int The row, 1 based
- @param x:int The column, 1 based
- @string :str|()→void Functor that can be treated as a string for jumping
- '''
- def __init__(self, y, x):
- self.string = '\033[%i;%iH' % (y, x)
- def __str__(self):
- return self.string
- def __call__(self):
- print(self.string, end = '')
+Line.copy = lambda self : break_edit(self, _copy)
+Line.cut = lambda self : full_edit(self, _cut)
+Line.kill = lambda self : full_edit(self, _kill)
+Line.yank = lambda self : partial_edit(self, _yank)
+Line.yank_cycle = lambda self : partial_edit(self, _yank_cycle)
+Line.swap_mark = lambda self : break_edit(self, _swap_mark)
+def __move_point(self, delta):
+ return break_edit(self, lambda s : _move_point(s, delta))
+Line.move_point = __move_point
-## TODO widthless characters should be ignored when calculating the size a text
+def __override(self, insert, override = True):
+ partial_edit(self, lambda s : _override(s, insert, override), False);
+Line.override = __override
-## TODO implement undo history
-##
-## Until the user has halted for 1 second (configurably) or has navigated using arrow keys or alternative key combinations,
-## edits should be accumulated and then stored in the editring. The edits is stored when the next keystroke is made, there
-## should not be a timer waits for the user to idle.
-##
class TextArea():
'''
@@ -370,12 +273,16 @@ class TextArea():
def ensure_y():
nonlocal stored
+ updates = False
if self.y < self.offy:
self.offy = self.y
+ updates = True
if self.y - self.offy > self.height - 3:
self.offy = self.y - self.height + 3
- update_status()
- redraw()
+ updates = True
+ if updates:
+ update_status()
+ redraw()
def letter_type(char): ## XXX how do we do this with unicode support
return (char in string.whitespace) or (char in string.punctuation)
@@ -453,6 +360,7 @@ class TextArea():
elif d == ctrl('Y'): edit(lambda L : L.yank(), _('Killring is empty'))
elif d == ctrl('R'): self.editring.change_direction()
elif d in (ctrl('_'), ctrl('U')):
+ ## TODO history break
if self.editring.is_empty():
self.alert(_('Nothing to undo'))
else:
diff --git a/src/line.py b/src/line.py
index 1355a38..4ec4000 100644
--- a/src/line.py
+++ b/src/line.py
@@ -18,7 +18,7 @@ GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
'''
-from editor import *
+from common import *
class Line():