diff --git a/CHANGELOG.md b/CHANGELOG.md
index b9914b7..6e307f0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,16 @@
Change Log
==========
+## [v0.1.64](https://github.com/richrd/suplemon/tree/v0.1.64) (2017-12-17) compared to previous master branch.
+[Full Changelog](https://github.com/richrd/suplemon/compare/v0.1.63...v0.1.64)
+
+**Implemented enhancements:**
+
+- Add bulk_delete and sort_lines commands.
+- Lots of code style fixes and improvements. Credit @Gnewbee
+- Add xclip support for system clipboard. Credit @LChris314
+- Added command docs to readme and help.
+
## [v0.1.63](https://github.com/richrd/suplemon/tree/v0.1.63) (2017-10-05) compared to previous master branch.
[Full Changelog](https://github.com/richrd/suplemon/compare/v0.1.62...v0.1.63)
diff --git a/README.md b/README.md
index f461fb1..92f6fe0 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ http://github.com/richrd/suplemon
* Find, Find next and Find all (Ctrl + F, Ctrl + D, Ctrl + A)
* Custom keyboard shortcuts (and easy-to-use defaults)
* Mouse support
- * Restores cursor positions in when reopenning files
+ * Restores cursor and scroll positions when reopenning files
* Extensions (easy to write your own)
* Lots more...
@@ -81,7 +81,7 @@ No dependencies outside the Python Standard Library required.
* Flake8
> For showing linting for Python files.
- * xsel
+ * xsel or xclip
> For system clipboard support on X Window (Linux).
* pbcopy / pbpaste
@@ -97,13 +97,6 @@ It is as easy as nano, and has much of the power of Sublime Text. It also suppor
to allow all kinds of customizations. To get more help hit ```Ctrl + H``` in the editor.
Suplemon is licensed under the MIT license.
-## Goals
- 1. [X] Create a command line text editor with built in multi cursor support. It's awesome!
- 2. [X] Usability should be even better and easier than nano. It's on par with desktop editors.
- 3. [X] Multi cursor should be comparable to Sublime Text.
- 4. [X] Develop Suplemon with Suplemon!!! I've used Suplemon for a long time as my main
- editor (replacing ST and nano) for all developement, Git commits and everything else.
-
## Configuration
### Main Config
@@ -215,6 +208,127 @@ To view the default keymap file run ```keymap default```
* Scroll Wheel Up / Down
> Scroll up & down.
+## Commands
+
+Suplemon has various add-ons that implement extra features.
+The commands can be run with Ctrl + E and the prompt has autocomplete to make running them faster.
+The available commands and their descriptions are:
+
+ * autocomplete
+
+ A simple autocompletion module.
+
+ This adds autocomplete support for the tab key. It uses a word
+ list scanned from all open files for completions. By default it suggests
+ the shortest possible match. If there are no matches, the tab action is
+ run normally.
+
+ * autodocstring
+
+ Simple module for adding docstring placeholders.
+
+ This module is intended to generate docstrings for Python functions.
+ It adds placeholders for descriptions, arguments and return data.
+ Function arguments are crudely parsed from the function definition
+ and return statements are scanned from the function body.
+
+ * bulk_delete
+
+ Bulk delete lines and characters.
+ Asks what direction to delete in by default.
+
+ Add 'up' to delete lines above highest cursor.
+ Add 'down' to delete lines below lowest cursor.
+ Add 'left' to delete characters to the left of all cursors.
+ Add 'right' to delete characters to the right of all cursors.
+
+ * comment
+
+ Toggle line commenting based on current file syntax.
+
+ * config
+
+ Shortcut for openning the config files.
+
+ * diff
+
+ View a diff of the current file compared to it's on disk version.
+
+ * eval
+
+ Evaluate a python expression and show the result in the status bar.
+
+ If no expression is provided the current line(s) are evaluated and
+ replaced with the evaluation result.
+
+ * keymap
+
+ Shortcut to openning the keymap config file.
+
+ * linter
+
+ Linter for suplemon.
+
+ * lower
+
+ Transform current lines to lower case.
+
+ * lstrip
+
+ Trim whitespace from beginning of current lines.
+
+ * paste
+
+ Toggle paste mode (helpful when pasting over SSH if auto indent is enabled)
+
+ * reload
+
+ Reload all add-on modules.
+
+ * replace_all
+
+ Replace all occurrences in all files of given text with given replacement.
+
+ * reverse
+
+ Reverse text on current line(s).
+
+ * rstrip
+
+ Trim whitespace from the end of lines.
+
+ * save
+
+ Save the current file.
+
+ * save_all
+
+ Save all currently open files. Asks for confirmation.
+
+ * sort_lines
+
+ Sort current lines.
+
+ Sorts alphabetically by default.
+ Add 'length' to sort by length.
+ Add 'reverse' to reverse the sorting.
+
+ * strip
+
+ Trim whitespace from start and end of lines.
+
+ * tabstospaces
+
+ Convert tab characters to spaces in the entire file.
+
+ * toggle_whitespace
+
+ Toggle visually showing whitespace.
+
+ * upper
+
+ Transform current lines to upper case.
+
## Support
@@ -238,29 +352,6 @@ PRs are very welcome and appreciated.
When making PRs make sure to set the target branch to `dev`. I only push to master when releasing new versions.
-## Todo
- * [ ] Design proper API for plugins/extensions/macros
- * [ ] Documentation for v 1.0.0
-
-## Wishlist (Stuff that would be nice, but not planning to do yet. *Maybe* for 2.0.0)
- * [ ] Core
- * [ ] Setting for enabling/disabling undo for cursor changes
- * [ ] Selections
- * [ ] List of recent files
- * [X] Optionally Remember cursor positions in files (and restore when opened again)
- * [ ] Read only viewer
- * ~~And disable editing~~ Don't disable editing. Instead enable save as.
- * [ ] Extensions:
- * [ ] Peer to peer colaborative editing. Could be implemented as an extension.
- * [ ] Auto backup. Activate on n changes or every n seconds
- * [ ] File selector, kind of like what nano has
- * [ ] This should be implemented as an extension
- * [ ] Could be triggered with a key binding (and/or override open file)
- * [ ] Need to refactor App class to support views instead of just files
- * [ ] A view could be an editor or an extension ui
- * [ ] Extensions should be able to control both status bars and key legend
-
-
## Rationale
For many the command line is a different environment for text editing.
Most coders are familiar with GUI text editors and for many vi and emacs
diff --git a/suplemon/config.py b/suplemon/config.py
index 4eabde3..c39be5b 100644
--- a/suplemon/config.py
+++ b/suplemon/config.py
@@ -7,7 +7,6 @@
import json
import logging
-from . import helpers
from . import suplemon_module
@@ -161,7 +160,7 @@ def remove_config_comments(self, data):
cleaned = []
for line in lines:
line = line.strip()
- if helpers.starts(line, "//") or helpers.starts(line, "#"):
+ if line.startswith(("//", "#")):
continue
cleaned.append(line)
return "\n".join(cleaned)
diff --git a/suplemon/editor.py b/suplemon/editor.py
index edbeb97..4a1d813 100644
--- a/suplemon/editor.py
+++ b/suplemon/editor.py
@@ -484,13 +484,12 @@ def copy(self):
copy_buffer = []
# Get all lines with cursors on them
line_nums = self.get_lines_with_cursors()
- i = 0
- while i < len(line_nums):
+
+ for i in range(len(line_nums)):
# Get the line
line = self.lines[line_nums[i]]
# Put it in our temporary buffer
copy_buffer.append(line.get_data())
- i += 1
self.set_buffer(copy_buffer)
self.store_action_state("copy")
@@ -502,8 +501,7 @@ def cut(self):
line_nums = self.get_lines_with_cursors()
# Sort from last to first (invert order)
line_nums = line_nums[::-1]
- i = 0
- while i < len(line_nums): # Iterate from last to first
+ for i in range(len(line_nums)): # Iterate from last to first
# Make sure we don't completely remove the last line
if len(self.lines) == 1:
cut_buffer.append(self.lines[0])
@@ -517,7 +515,6 @@ def cut(self):
cut_buffer.append(line)
# Move all cursors below the current line up
self.move_y_cursors(line_no, -1)
- i += 1
self.move_cursors() # Make sure cursors are in valid places
# Reverse the buffer to get correct order and store it
self.set_buffer(cut_buffer[::-1])
diff --git a/suplemon/help.py b/suplemon/help.py
index 2f4a1fb..ab69614 100644
--- a/suplemon/help.py
+++ b/suplemon/help.py
@@ -133,4 +133,120 @@
and then typing the command name. Commands are extensions and are stored in
the modules folder in the Suplemon installation.
+ * autocomplete
+
+ A simple autocompletion module.
+
+ This adds autocomplete support for the tab key. It uses a word
+ list scanned from all open files for completions. By default it suggests
+ the shortest possible match. If there are no matches, the tab action is
+ run normally.
+
+ * autodocstring
+
+ Simple module for adding docstring placeholders.
+
+ This module is intended to generate docstrings for Python functions.
+ It adds placeholders for descriptions, arguments and return data.
+ Function arguments are crudely parsed from the function definition
+ and return statements are scanned from the function body.
+
+ * bulk_delete
+
+ Bulk delete lines and characters.
+ Asks what direction to delete in by default.
+
+ Add 'up' to delete lines above highest cursor.
+ Add 'down' to delete lines below lowest cursor.
+ Add 'left' to delete characters to the left of all cursors.
+ Add 'right' to delete characters to the right of all cursors.
+
+ * comment
+
+ Toggle line commenting based on current file syntax.
+
+ * config
+
+ Shortcut for openning the config files.
+
+ * diff
+
+ View a diff of the current file compared to it's on disk version.
+
+ * eval
+
+ Evaluate a python expression and show the result in the status bar.
+
+ If no expression is provided the current line(s) are evaluated and
+ replaced with the evaluation result.
+
+ * keymap
+
+ Shortcut to openning the keymap config file.
+
+ * linter
+
+ Linter for suplemon.
+
+ * lower
+
+ Transform current lines to lower case.
+
+ * lstrip
+
+ Trim whitespace from beginning of current lines.
+
+ * paste
+
+ Toggle paste mode (helpful when pasting over SSH if auto indent is enabled)
+
+ * reload
+
+ Reload all add-on modules.
+
+ * replace_all
+
+ Replace all occurrences in all files of given text with given replacement.
+
+ * reverse
+
+ Reverse text on current line(s).
+
+ * rstrip
+
+ Trim whitespace from the end of lines.
+
+ * save
+
+ Save the current file.
+
+ * save_all
+
+ Save all currently open files. Asks for confirmation.
+
+ * sort_lines
+
+ Sort current lines.
+
+ Sorts alphabetically by default.
+ Add 'length' to sort by length.
+ Add 'reverse' to reverse the sorting.
+
+ * strip
+
+ Trim whitespace from start and end of lines.
+
+ * tabstospaces
+
+ Convert tab characters to spaces in the entire file.
+
+ * toggle_whitespace
+
+ Toggle visually showing whitespace.
+
+ * upper
+
+ Transform current lines to upper case.
+
+
"""
diff --git a/suplemon/helpers.py b/suplemon/helpers.py
index fdd26aa..409fdd3 100644
--- a/suplemon/helpers.py
+++ b/suplemon/helpers.py
@@ -19,27 +19,6 @@ def curr_time_sec():
return time.strftime("%H:%M:%S")
-def starts(s, what):
- """Check if a string begins with given string or any one in given list."""
- if isinstance(what, str):
- what = [what]
- for item in what:
- if s.find(item) == 0:
- return True
- return False
-
-
-def ends(s, what):
- """Check if a string ends with given string or any one in given list."""
- s = s[::-1]
- if isinstance(what, str):
- what = [what]
- for item in what:
- if s.find(item[::-1]) == 0:
- return True
- return False
-
-
def multisplit(data, delimiters):
pattern = "|".join(map(re.escape, delimiters))
return re.split(pattern, data)
diff --git a/suplemon/linelight/css.py b/suplemon/linelight/css.py
index 386abde..e44f35d 100644
--- a/suplemon/linelight/css.py
+++ b/suplemon/linelight/css.py
@@ -1,4 +1,3 @@
-from suplemon import helpers
from suplemon.linelight.color_map import color_map
@@ -9,14 +8,14 @@ def get_comment(self):
def get_color(self, raw_line):
color = color_map["white"]
line = raw_line.strip()
- if helpers.starts(line, "@import"):
+ if line.startswith("@import"):
color = color_map["blue"]
- elif helpers.starts(line, "$"):
+ elif line.startswith("$"):
color = color_map["green"]
- elif helpers.starts(line, "/*") or helpers.ends(line, "*/"):
+ elif line.startswith("/*") or line.endswith("*/"):
color = color_map["magenta"]
- elif helpers.starts(line, "{") or helpers.ends(line, "}") or helpers.ends(line, "{"):
+ elif line.startswith("{") or line.endswith(("}", "{")):
color = color_map["cyan"]
- elif helpers.ends(line, ";"):
+ elif line.endswith(";"):
color = color_map["yellow"]
return color
diff --git a/suplemon/linelight/html.py b/suplemon/linelight/html.py
index 8c05167..92b49c7 100644
--- a/suplemon/linelight/html.py
+++ b/suplemon/linelight/html.py
@@ -1,4 +1,3 @@
-from suplemon import helpers
from suplemon.linelight.color_map import color_map
@@ -9,12 +8,12 @@ def get_comment(self):
def get_color(self, raw_line):
color = color_map["white"]
line = raw_line.strip()
- if helpers.starts(line, ["#", "//", "/*", "*/", ""]):
+ elif line.endswith(("*/", "-->")):
color = color_map["magenta"]
- elif helpers.starts(line, "<"):
+ elif line.startswith("<"):
color = color_map["cyan"]
- elif helpers.ends(line, ">"):
+ elif line.endswith(">"):
color = color_map["cyan"]
return color
diff --git a/suplemon/linelight/js.py b/suplemon/linelight/js.py
index fe93ec5..c98e653 100644
--- a/suplemon/linelight/js.py
+++ b/suplemon/linelight/js.py
@@ -1,4 +1,3 @@
-from suplemon import helpers
from suplemon.linelight.color_map import color_map
@@ -9,14 +8,14 @@ def get_comment(self):
def get_color(self, raw_line):
color = color_map["white"]
line = raw_line.strip()
- if helpers.starts(line, "function"):
+ if line.startswith("function"):
color = color_map["cyan"]
- elif helpers.starts(line, ["return"]):
+ elif line.startswith("return"):
color = color_map["red"]
- elif helpers.starts(line, "this."):
+ elif line.startswith("this."):
color = color_map["cyan"]
- elif helpers.starts(line, ["//", "/*", "*/", "*"]):
+ elif line.startswith(("//", "/*", "*/", "*")):
color = color_map["magenta"]
- elif helpers.starts(line, ["if", "else", "for ", "while ", "continue", "break"]):
+ elif line.startswith(("if", "else", "for ", "while ", "continue", "break")):
color = color_map["yellow"]
return color
diff --git a/suplemon/linelight/json.py b/suplemon/linelight/json.py
index 8a7c682..6cc0bf8 100644
--- a/suplemon/linelight/json.py
+++ b/suplemon/linelight/json.py
@@ -1,4 +1,3 @@
-from suplemon import helpers
from suplemon.linelight.color_map import color_map
@@ -9,8 +8,8 @@ def get_comment(self, line):
def get_color(self, raw_line):
color = color_map["white"]
line = raw_line.strip()
- if helpers.starts(line, ["{", "}"]):
+ if line.startswith(("{", "}")):
color = color_map["yellow"]
- elif helpers.starts(line, "\""):
+ elif line.startswith("\""):
color = color_map["green"]
return color
diff --git a/suplemon/linelight/md.py b/suplemon/linelight/md.py
index d10830a..d033f08 100644
--- a/suplemon/linelight/md.py
+++ b/suplemon/linelight/md.py
@@ -1,4 +1,3 @@
-from suplemon import helpers
from suplemon.linelight.color_map import color_map
@@ -9,12 +8,12 @@ def get_comment(self, line):
def get_color(self, raw_line):
color = color_map["white"]
line = raw_line.strip()
- if helpers.starts(line, ["*", "-"]): # List
+ if line.startswith(("*", "-")): # List
color = color_map["cyan"]
- elif helpers.starts(line, "#"): # Header
+ elif line.startswith("#"): # Header
color = color_map["green"]
- elif helpers.starts(line, ">"): # Item desription
+ elif line.startswith(">"): # Item description
color = color_map["yellow"]
- elif helpers.starts(raw_line, " "): # Code
+ elif raw_line.startswith(" "): # Code
color = color_map["magenta"]
return color
diff --git a/suplemon/linelight/php.py b/suplemon/linelight/php.py
index 8f8f043..0854e2f 100644
--- a/suplemon/linelight/php.py
+++ b/suplemon/linelight/php.py
@@ -1,4 +1,3 @@
-from suplemon import helpers
from suplemon.linelight.color_map import color_map
@@ -9,20 +8,20 @@ def get_comment(self):
def get_color(self, raw_line):
color = color_map["white"]
line = raw_line.strip()
- keywords = ["if", "else", "finally", "try", "catch", "foreach",
- "while", "continue", "pass", "break"]
- if helpers.starts(line, ["include", "require"]):
+ keywords = ("if", "else", "finally", "try", "catch", "foreach",
+ "while", "continue", "pass", "break")
+ if line.startswith(("include", "require")):
color = color_map["blue"]
- elif helpers.starts(line, ["class", "public", "private", "function"]):
+ elif line.startswith(("class", "public", "private", "function")):
color = color_map["green"]
- elif helpers.starts(line, "def"):
+ elif line.startswith("def"):
color = color_map["cyan"]
- elif helpers.starts(line, ["return"]):
+ elif line.startswith("return"):
color = color_map["red"]
- elif helpers.starts(line, "$"):
+ elif line.startswith("$"):
color = color_map["cyan"]
- elif helpers.starts(line, ["#", "//", "/*", "*/"]):
+ elif line.startswith(("#", "//", "/*", "*/")):
color = color_map["magenta"]
- elif helpers.starts(line, keywords):
+ elif line.startswith(keywords):
color = color_map["yellow"]
return color
diff --git a/suplemon/linelight/py.py b/suplemon/linelight/py.py
index c149ebe..6abfc35 100644
--- a/suplemon/linelight/py.py
+++ b/suplemon/linelight/py.py
@@ -1,4 +1,3 @@
-from suplemon import helpers
from suplemon.linelight.color_map import color_map
@@ -9,20 +8,20 @@ def get_comment(self):
def get_color(self, raw_line):
color = color_map["white"]
line = raw_line.strip()
- keywords = ["if", "elif", "else", "finally", "try", "except",
- "for ", "while ", "continue", "pass", "break"]
- if helpers.starts(line, ["import", "from"]):
+ keywords = ("if", "elif", "else", "finally", "try", "except",
+ "for ", "while ", "continue", "pass", "break")
+ if line.startswith(("import", "from")):
color = color_map["blue"]
- elif helpers.starts(line, "class"):
+ elif line.startswith("class"):
color = color_map["green"]
- elif helpers.starts(line, "def"):
+ elif line.startswith("def"):
color = color_map["cyan"]
- elif helpers.starts(line, ["return", "yield"]):
+ elif line.startswith(("return", "yield")):
color = color_map["red"]
- elif helpers.starts(line, "self."):
+ elif line.startswith("self."):
color = color_map["cyan"]
- elif helpers.starts(line, ["#", "//", "\"", "'", ":"]):
+ elif line.startswith(("#", "//", "\"", "'", ":")):
color = color_map["magenta"]
- elif helpers.starts(line, keywords):
+ elif line.startswith(keywords):
color = color_map["yellow"]
return color
diff --git a/suplemon/main.py b/suplemon/main.py
index 0d1c1d7..6d04136 100644
--- a/suplemon/main.py
+++ b/suplemon/main.py
@@ -30,9 +30,9 @@ def __init__(self, filenames=None, config_file=None):
:param str filenames[*]: Path to a file to load
"""
self.version = __version__
- self.inited = 0
- self.running = 0
- self.debug = 1
+ self.inited = False
+ self.running = False
+ self.debug = True
self.block_rendering = False
# Set default variables
@@ -44,6 +44,11 @@ def __init__(self, filenames=None, config_file=None):
self.global_buffer = []
self.event_bindings = {}
+ self.config = None
+ self.ui = None
+ self.modules = None
+ self.themes = None
+
# Maximum amount of inputs to process at once
self.max_input = 100
@@ -110,14 +115,14 @@ def init(self):
self.themes = themes.ThemeLoader(self)
# Indicate that initialization is complete
- self.inited = 1
+ self.inited = True
return True
def exit(self):
"""Stop the main loop and exit."""
self.trigger_event_before("app_exit")
- self.running = 0
+ self.running = False
def run(self):
"""Run the app via the ui wrapper."""
@@ -157,7 +162,7 @@ def load(self):
"Python 3.3 or higher."
.format(version=ver))
self.load_files()
- self.running = 1
+ self.running = True
self.trigger_event_after("app_loaded")
def on_input(self, event):
@@ -175,14 +180,12 @@ def main_loop(self):
got_input = False
# Run through max 100 inputs (so the view is updated at least every 100 characters)
- i = 0
- while i < self.max_input:
+ for i in range(self.max_input):
event = self.ui.get_input(False) # non-blocking
if not event:
break # no more inputs to process at this time
- i += 1
got_input = True
self.on_input(event)
@@ -530,14 +533,10 @@ def trigger_event_after(self, event):
def toggle_fullscreen(self):
"""Toggle full screen editor."""
display = self.config["display"]
- if display["show_top_bar"]:
- display["show_top_bar"] = 0
- display["show_bottom_bar"] = 0
- display["show_legend"] = 0
- else:
- display["show_top_bar"] = 1
- display["show_bottom_bar"] = 1
- display["show_legend"] = 1
+ show_indicators = not display["show_top_bar"]
+ display["show_top_bar"] = show_indicators
+ display["show_bottom_bar"] = show_indicators
+ display["show_legend"] = show_indicators
# Virtual curses windows need to be resized
self.ui.resize()
diff --git a/suplemon/module_loader.py b/suplemon/module_loader.py
index dfdc7c3..2441df7 100644
--- a/suplemon/module_loader.py
+++ b/suplemon/module_loader.py
@@ -21,25 +21,34 @@ def __init__(self, app=None):
def load(self):
"""Find and load available modules."""
self.logger.debug("Loading modules...")
+ names = self.get_module_names()
+ for name in names:
+ module = self.load_single(name)
+ if module:
+ # Load and store the module instance
+ inst = self.load_instance(module)
+ if inst:
+ self.modules[module[0]] = inst
+
+ def get_module_names(self):
+ """Get names of loadable modules."""
+ names = []
dirlist = os.listdir(self.module_path)
for item in dirlist:
- # Skip 'hidden' dot files
- if item[0] == ".":
+ # Skip 'hidden' dot files and files beginning with and underscore
+ if item.startswith((".", "_")):
continue
parts = item.split(".")
if len(parts) < 2:
+ # Can't find file extension
continue
name = parts[0]
ext = parts[-1]
-
- # only load .py modules that don't begin with an underscore
- if ext == "py" and name[0] != "_":
- module = self.load_single(name)
- if module:
- # Load and store the module instance
- inst = self.load_instance(module)
- if inst:
- self.modules[module[0]] = inst
+ # only load .py modules
+ if ext != "py":
+ continue
+ names.append(name)
+ return names
def load_instance(self, module):
"""Initialize a module."""
@@ -64,7 +73,24 @@ def load_single(self, name):
mod.module["status"] = False
return name, mod.module
+ def extract_docs(self):
+ """Get names and docs of runnable modules and print as markdown."""
+ names = sorted(self.get_module_names())
+ for name in names:
+ name, module = self.load_single(name)
+ # Skip modules that can't be run expicitly
+ if module["class"].run.__module__ == "suplemon.suplemon_module":
+ continue
+ # Skip undocumented modules
+ if not module["class"].__doc__:
+ continue
+ docstring = module["class"].__doc__
+ docstring = "\n " + docstring.strip()
+
+ doc = " * {0}\n{1}\n".format(name, docstring)
+ print(doc)
+
if __name__ == "__main__":
ml = ModuleLoader()
- ml.load()
+ ml.extract_docs()
diff --git a/suplemon/modules/application_state.py b/suplemon/modules/application_state.py
index db81054..8174657 100644
--- a/suplemon/modules/application_state.py
+++ b/suplemon/modules/application_state.py
@@ -7,6 +7,12 @@
class ApplicationState(Module):
+ """
+ Stores the state of open files when exiting the editor and restores when files are reopened.
+
+ Cursor positions and scroll position are stored and restored.
+ """
+
def init(self):
self.init_logging(__name__)
self.bind_event_after("app_loaded", self.on_load)
@@ -31,8 +37,8 @@ def get_file_state(self, file):
"""Get the state of a single file."""
editor = file.get_editor()
state = {
- "cursors": [cursor.tuple() for cursor in editor.get_cursors()],
- "scroll_pos": editor.get_scroll_pos(),
+ "cursors": [cursor.tuple() for cursor in editor.cursors],
+ "scroll_pos": editor.scroll_pos,
"hash": self.get_hash(editor),
}
return state
@@ -47,7 +53,7 @@ def get_hash(self, editor):
def set_file_state(self, file, state):
"""Set the state of a file."""
file.editor.set_cursors(state["cursors"])
- file.editor.set_scroll_pos(state["scroll_pos"])
+ file.editor.scroll_pos = state["scroll_pos"]
def store_states(self):
"""Store the states of opened files."""
diff --git a/suplemon/modules/autocomplete.py b/suplemon/modules/autocomplete.py
index bfe131b..d930a7e 100644
--- a/suplemon/modules/autocomplete.py
+++ b/suplemon/modules/autocomplete.py
@@ -10,7 +10,7 @@ class AutoComplete(Module):
"""
A simple autocompletion module.
- This module adds autocomplete support for the tab event. It uses a word
+ This adds autocomplete support for the tab key. It uses a word
list scanned from all open files for completions. By default it suggests
the shortest possible match. If there are no matches, the tab action is
run normally.
@@ -61,7 +61,7 @@ def get_match(self, word):
# Build list of suitable matches
candidates = []
for candidate in self.word_list:
- if helpers.starts(candidate, word) and len(candidate) > len(word):
+ if candidate.startswith(word) and len(candidate) > len(word):
candidates.append(candidate)
# Find the shortest match
# TODO: implement cycling through matches
diff --git a/suplemon/modules/autodocstring.py b/suplemon/modules/autodocstring.py
index dfff5ca..ecc91be 100644
--- a/suplemon/modules/autodocstring.py
+++ b/suplemon/modules/autodocstring.py
@@ -34,7 +34,7 @@ def run(self, app, editor, args):
cursor = editor.get_cursor()
line = editor.get_line(cursor.y)
line_data = line.get_data()
- if not helpers.starts(line_data.strip(), "def "):
+ if not line_data.strip().startswith("def "):
app.set_status("Current line isn't a function definition.")
return False
@@ -126,15 +126,13 @@ def get_function_returns(self, editor, line_number):
:param line_number: Line number of the function definition.
:return: Boolean indicating wether the function something.
"""
- i = line_number+1
- while i < len(editor.lines):
+ for i in range(line_number+1, len(editor.lines)):
line = editor.get_line(i)
data = line.get_data().strip()
- if helpers.starts(data, "def "):
+ if data.startswith("def "):
break
- if helpers.starts(data, "return "):
+ if data.startswith("return "):
return True
- i += 1
return False
diff --git a/suplemon/modules/bulk_delete.py b/suplemon/modules/bulk_delete.py
new file mode 100644
index 0000000..87cb9e3
--- /dev/null
+++ b/suplemon/modules/bulk_delete.py
@@ -0,0 +1,75 @@
+# -*- encoding: utf-8
+
+from suplemon.suplemon_module import Module
+
+
+class BulkDelete(Module):
+ """
+ Bulk delete lines and characters.
+ Asks what direction to delete in by default.
+
+ Add 'up' to delete lines above highest cursor.
+ Add 'down' to delete lines below lowest cursor.
+ Add 'left' to delete characters to the left of all cursors.
+ Add 'right' to delete characters to the right of all cursors.
+ """
+
+ def init(self):
+ self.directions = ["up", "down", "left", "right"]
+
+ def handler(self, prompt, event):
+ # Get arrow keys from prompt
+ if event.key_name in self.directions:
+ prompt.set_data(event.key_name)
+ prompt.on_ready()
+ return True # Disable normal key handling
+
+ def run(self, app, editor, args):
+ direction = args.lower()
+ if not direction:
+ direction = app.ui.query_filtered("Press arrow key in direction to delete:", handler=self.handler)
+
+ if direction not in self.directions:
+ app.set_status("Invalid direction.")
+ return False
+
+ # Delete entire lines
+ if direction == "up":
+ pos = editor.get_first_cursor()
+ length = len(editor.lines)
+ editor.lines = editor.lines[pos.y:]
+ delta = length - len(editor.lines)
+ # If lines were removed, move the cursors up the same amount
+ if delta:
+ editor.move_cursors((0, -delta))
+
+ elif direction == "down":
+ pos = editor.get_last_cursor()
+ editor.lines = editor.lines[:pos.y+1]
+
+ # Delete from start or end of lines
+ else:
+ # Select min/max function based on direction
+ func = min if direction == "left" else max
+ # Get all lines with cursors
+ line_indices = editor.get_lines_with_cursors()
+ for line_no in line_indices:
+ # Get all cursors for the line
+ line_cursors = editor.get_cursors_on_line(line_no)
+ # Get the leftmost of rightmost x coordinate
+ x = func(line_cursors, key=lambda c: c.x).x
+
+ # Delete correct part of the line
+ line = editor.lines[line_no]
+ if direction == "left":
+ line.data = line.data[x:]
+ # Also move cursors appropriately when deleting left side
+ [c.move_left(x) for c in line_cursors]
+ else:
+ line.data = line.data[:x]
+
+
+module = {
+ "class": BulkDelete,
+ "name": "bulk_delete",
+}
diff --git a/suplemon/modules/clock.py b/suplemon/modules/clock.py
index 7fbed3d..473a069 100644
--- a/suplemon/modules/clock.py
+++ b/suplemon/modules/clock.py
@@ -7,6 +7,7 @@
class Clock(Module):
"""Shows a clock in the top status bar."""
+
def get_status(self):
s = time.strftime("%H:%M")
if self.app.config["app"]["use_unicode_symbols"]:
diff --git a/suplemon/modules/comment.py b/suplemon/modules/comment.py
index 50eb04c..aeb1dba 100644
--- a/suplemon/modules/comment.py
+++ b/suplemon/modules/comment.py
@@ -5,7 +5,7 @@
class Comment(Module):
- """Toggles line commenting based on current file syntax."""
+ """Toggle line commenting based on current file syntax."""
def run(self, app, editor, args):
"""Comment the current line(s)."""
@@ -24,13 +24,13 @@ def run(self, app, editor, args):
target = str(line).strip()
w = helpers.whitespace(line) # Amount of whitespace at line start
# If the line starts with comment syntax
- if helpers.starts(target, comment[0]):
+ if target.startswith(comment[0]):
# Reconstruct the whitespace and add the line
new_line = (" "*w) + line[w+len(comment[0]):]
# If comment end syntax exists
if comment[1]:
# Try to remove it from the end of the line
- if helpers.ends(new_line, comment[1]):
+ if new_line.endswith(comment[1]):
new_line = new_line[:-1*len(comment[1])]
# Store the modified line
# editor.lines[lnum] = Line(new_line)
diff --git a/suplemon/modules/config.py b/suplemon/modules/config.py
index 0bda121..0f6fee7 100644
--- a/suplemon/modules/config.py
+++ b/suplemon/modules/config.py
@@ -6,7 +6,8 @@
class SuplemonConfig(config.ConfigModule):
- """Shortcut to openning the keymap config file."""
+ """Shortcut for openning the config files."""
+
def init(self):
self.config_name = "defaults.json"
self.config_default_path = os.path.join(self.app.path, "config", self.config_name)
diff --git a/suplemon/modules/date.py b/suplemon/modules/date.py
index b98fcde..ecc2e4e 100644
--- a/suplemon/modules/date.py
+++ b/suplemon/modules/date.py
@@ -6,6 +6,8 @@
class Date(Module):
+ """Shows the current date without year in the top status bar."""
+
def get_status(self):
s = time.strftime("%d.%m.")
if self.app.config["app"]["use_unicode_symbols"]:
diff --git a/suplemon/modules/diff.py b/suplemon/modules/diff.py
index 5add943..4e79c42 100644
--- a/suplemon/modules/diff.py
+++ b/suplemon/modules/diff.py
@@ -7,6 +7,7 @@
class Diff(Module):
"""View a diff of the current file compared to it's on disk version."""
+
def run(self, app, editor, args):
curr_file = app.get_file()
curr_path = curr_file.get_path()
diff --git a/suplemon/modules/eval.py b/suplemon/modules/eval.py
index 0135bfd..8e3bd92 100644
--- a/suplemon/modules/eval.py
+++ b/suplemon/modules/eval.py
@@ -4,6 +4,13 @@
class Eval(Module):
+ """
+ Evaluate a python expression and show the result in the status bar.
+
+ If no expression is provided the current line(s) are evaluated and
+ replaced with the evaluation result.
+ """
+
def run(self, app, editor, args):
if not args:
return self.evaluate_lines(editor)
diff --git a/suplemon/modules/keymap.py b/suplemon/modules/keymap.py
index df48ca6..a126d82 100644
--- a/suplemon/modules/keymap.py
+++ b/suplemon/modules/keymap.py
@@ -7,6 +7,7 @@
class KeymapConfig(config.ConfigModule):
"""Shortcut to openning the keymap config file."""
+
def init(self):
self.config_name = "keymap.json"
self.config_default_path = os.path.join(self.app.path, "config", self.config_name)
diff --git a/suplemon/modules/linter.py b/suplemon/modules/linter.py
index 65ff15e..f6d7d52 100644
--- a/suplemon/modules/linter.py
+++ b/suplemon/modules/linter.py
@@ -9,6 +9,8 @@
class Linter(Module):
+ """Linter for suplemon."""
+
def init(self):
self.init_logging(__name__)
@@ -80,8 +82,7 @@ def lint_file(self, file):
return False
editor = file.get_editor()
- line_no = 0
- while line_no < len(editor.lines):
+ for line_no in range(len(editor.lines)):
line = editor.lines[line_no]
if line_no+1 in linting.keys():
line.linting = linting[line_no+1]
@@ -89,7 +90,6 @@ def lint_file(self, file):
else:
line.linting = False
line.reset_number_color()
- line_no += 1
def get_msgs_on_line(self, editor, line_no):
line = editor.lines[line_no]
diff --git a/suplemon/modules/lower.py b/suplemon/modules/lower.py
index a4e1824..0679dc3 100644
--- a/suplemon/modules/lower.py
+++ b/suplemon/modules/lower.py
@@ -4,6 +4,8 @@
class Lower(Module):
+ """Transform current lines to lower case."""
+
def run(self, app, editor, args):
line_nums = []
for cursor in editor.cursors:
diff --git a/suplemon/modules/lstrip.py b/suplemon/modules/lstrip.py
index 32b8497..f4cab49 100644
--- a/suplemon/modules/lstrip.py
+++ b/suplemon/modules/lstrip.py
@@ -4,6 +4,8 @@
class LStrip(Module):
+ """Trim whitespace from beginning of current lines."""
+
def run(self, app, editor, args):
# TODO: move cursors in sync with line contents
line_nums = editor.get_lines_with_cursors()
diff --git a/suplemon/modules/paste.py b/suplemon/modules/paste.py
index 130e255..ecbf547 100644
--- a/suplemon/modules/paste.py
+++ b/suplemon/modules/paste.py
@@ -4,6 +4,8 @@
class Paste(Module):
+ """Toggle paste mode (helpful when pasting over SSH if auto indent is enabled)"""
+
def init(self):
# Flag for paste mode
self.active = False
diff --git a/suplemon/modules/reload.py b/suplemon/modules/reload.py
index f00971d..ee443ee 100644
--- a/suplemon/modules/reload.py
+++ b/suplemon/modules/reload.py
@@ -4,7 +4,8 @@
class Reload(Module):
- """Reload all addon modules."""
+ """Reload all add-on modules."""
+
def run(self, app, editor, args):
self.app.modules.load()
diff --git a/suplemon/modules/replace_all.py b/suplemon/modules/replace_all.py
index c5d2e3c..b093a53 100644
--- a/suplemon/modules/replace_all.py
+++ b/suplemon/modules/replace_all.py
@@ -4,6 +4,8 @@
class ReplaceAll(Module):
+ """Replace all occurrences in all files of given text with given replacement."""
+
def run(self, app, editor, args):
r_from = self.app.ui.query("Replace text:")
if not r_from:
diff --git a/suplemon/modules/reverse.py b/suplemon/modules/reverse.py
index c131334..47937fe 100644
--- a/suplemon/modules/reverse.py
+++ b/suplemon/modules/reverse.py
@@ -4,6 +4,8 @@
class Reverse(Module):
+ """Reverse text on current line(s)."""
+
def run(self, app, editor, args):
line_nums = []
for cursor in editor.cursors:
diff --git a/suplemon/modules/rstrip.py b/suplemon/modules/rstrip.py
index c41e98f..8eb6e00 100644
--- a/suplemon/modules/rstrip.py
+++ b/suplemon/modules/rstrip.py
@@ -4,7 +4,8 @@
class RStrip(Module):
- """Strips whitespace from end of line."""
+ """Trim whitespace from the end of lines."""
+
def run(self, app, editor, args):
line_nums = editor.get_lines_with_cursors()
for n in line_nums:
diff --git a/suplemon/modules/save.py b/suplemon/modules/save.py
index 3bd76b5..24db054 100644
--- a/suplemon/modules/save.py
+++ b/suplemon/modules/save.py
@@ -4,6 +4,8 @@
class Save(Module):
+ """Save the current file."""
+
def run(self, app, editor, args):
return app.save_file()
diff --git a/suplemon/modules/save_all.py b/suplemon/modules/save_all.py
index 54b4fe8..d608c83 100644
--- a/suplemon/modules/save_all.py
+++ b/suplemon/modules/save_all.py
@@ -4,6 +4,8 @@
class SaveAll(Module):
+ """Save all currently open files. Asks for confirmation."""
+
def run(self, app, editor, args):
if not self.app.ui.query_bool("Save all files?", False):
return False
diff --git a/suplemon/modules/sort_lines.py b/suplemon/modules/sort_lines.py
new file mode 100644
index 0000000..6994f2d
--- /dev/null
+++ b/suplemon/modules/sort_lines.py
@@ -0,0 +1,41 @@
+# -*- encoding: utf-8
+
+from suplemon.suplemon_module import Module
+
+
+class SortLines(Module):
+ """
+ Sort current lines.
+
+ Sorts alphabetically by default.
+ Add 'length' to sort by length.
+ Add 'reverse' to reverse the sorting.
+ """
+
+ def sort_normal(self, line):
+ return line.data
+
+ def sort_length(self, line):
+ return len(line.data)
+
+ def run(self, app, editor, args):
+ args = args.lower()
+
+ sorter = self.sort_normal
+ reverse = True if "reverse" in args else False
+ if "length" in args:
+ sorter = self.sort_length
+
+ indices = editor.get_lines_with_cursors()
+ lines = [editor.get_line(i) for i in indices]
+
+ sorted_lines = sorted(lines, key=sorter, reverse=reverse)
+
+ for i, line in enumerate(sorted_lines):
+ editor.lines[indices[i]] = line
+
+
+module = {
+ "class": SortLines,
+ "name": "sort_lines",
+}
diff --git a/suplemon/modules/strip.py b/suplemon/modules/strip.py
index 75373a4..af16325 100644
--- a/suplemon/modules/strip.py
+++ b/suplemon/modules/strip.py
@@ -4,7 +4,8 @@
class Strip(Module):
- """Strips whitespace from start and end of line."""
+ """Trim whitespace from start and end of lines."""
+
def run(self, app, editor, args):
line_nums = editor.get_lines_with_cursors()
for n in line_nums:
diff --git a/suplemon/modules/system_clipboard.py b/suplemon/modules/system_clipboard.py
index 165a630..5cd41cd 100644
--- a/suplemon/modules/system_clipboard.py
+++ b/suplemon/modules/system_clipboard.py
@@ -6,14 +6,19 @@
class SystemClipboard(Module):
+ """Integrates the system clipboard with suplemon."""
+
def init(self):
self.init_logging(__name__)
if self.has_xsel_support():
self.clipboard_type = "xsel"
elif self.has_pb_support():
self.clipboard_type = "pb"
+ elif self.has_xclip_support():
+ self.clipboard_type = "xclip"
else:
- self.logger.warning("Can't use system clipboard. Install 'xsel' or 'pbcopy' for system clipboard support.")
+ self.logger.warning(
+ "Can't use system clipboard. Install 'xsel' or 'pbcopy' or 'xclip' for system clipboard support.")
return False
self.bind_event_before("insert", self.insert)
self.bind_event_after("copy", self.copy)
@@ -35,6 +40,8 @@ def get_clipboard(self):
command = ["xsel", "-b"]
elif self.clipboard_type == "pb":
command = ["pbpaste", "-Prefer", "txt"]
+ elif self.clipboard_type == "xclip":
+ command = ["xclip", "-selection", "clipboard", "-out"]
else:
return False
data = subprocess.check_output(command, universal_newlines=True)
@@ -48,6 +55,8 @@ def set_clipboard(self, data):
command = ["xsel", "-i", "-b"]
elif self.clipboard_type == "pb":
command = ["pbcopy"]
+ elif self.clipboard_type == "xclip":
+ command = ["xclip", "-selection", "clipboard", "-in"]
else:
return False
p = subprocess.Popen(command, stdin=subprocess.PIPE)
@@ -64,6 +73,10 @@ def has_xsel_support(self):
output = self.get_output(["xsel", "--version"])
return output
+ def has_xclip_support(self):
+ output = self.get_output(["which", "xclip"]) # xclip -version outputs to stderr
+ return output
+
def get_output(self, cmd):
try:
process = subprocess.Popen(cmd, stdout=subprocess.PIPE)
diff --git a/suplemon/modules/tabstospaces.py b/suplemon/modules/tabstospaces.py
index 68d5229..a775f0c 100644
--- a/suplemon/modules/tabstospaces.py
+++ b/suplemon/modules/tabstospaces.py
@@ -4,12 +4,12 @@
class TabsToSpaces(Module):
+ """Convert tab characters to spaces in the entire file."""
+
def run(self, app, editor, args):
- i = 0
- for line in editor.lines:
+ for i, line in enumerate(editor.lines):
new = line.data.replace("\t", " "*editor.config["tab_width"])
editor.lines[i].set_data(new)
- i += 1
module = {
diff --git a/suplemon/modules/toggle_whitespace.py b/suplemon/modules/toggle_whitespace.py
index fc985f4..30b0173 100644
--- a/suplemon/modules/toggle_whitespace.py
+++ b/suplemon/modules/toggle_whitespace.py
@@ -4,6 +4,8 @@
class ToggleWhitespace(Module):
+ """Toggle visually showing whitespace."""
+
def run(self, app, editor, args):
# Toggle the boolean
new_value = not self.app.config["editor"]["show_white_space"]
diff --git a/suplemon/modules/upper.py b/suplemon/modules/upper.py
index b79f4ab..9706699 100644
--- a/suplemon/modules/upper.py
+++ b/suplemon/modules/upper.py
@@ -4,6 +4,8 @@
class Upper(Module):
+ """Transform current lines to upper case."""
+
def run(self, app, editor, args):
line_nums = []
for cursor in editor.cursors:
diff --git a/suplemon/prompt.py b/suplemon/prompt.py
index d7a3b5e..d9c9731 100644
--- a/suplemon/prompt.py
+++ b/suplemon/prompt.py
@@ -12,8 +12,8 @@ class Prompt(Editor):
"""An input prompt based on the Editor."""
def __init__(self, app, window):
Editor.__init__(self, app, window)
- self.ready = 0
- self.canceled = 0
+ self.ready = False
+ self.canceled = False
self.input_func = lambda: False
self.caption = ""
@@ -34,14 +34,14 @@ def set_input_source(self, input_func):
def on_ready(self):
"""Accepts the current input."""
- self.ready = 1
+ self.ready = True
return
def on_cancel(self):
"""Cancels the input prompt."""
self.set_data("")
- self.ready = 1
- self.canceled = 1
+ self.ready = True
+ self.canceled = True
return
def line_offset(self):
@@ -129,6 +129,30 @@ def get_input(self, caption="", initial=False):
return False
+class PromptFiltered(Prompt):
+ """An input prompt that allows intercepting and filtering input events."""
+
+ def __init__(self, app, window, handler=None):
+ Prompt.__init__(self, app, window)
+ self.prompt_handler = handler
+
+ def handle_input(self, event):
+ """Handle special bindings for the prompt."""
+ # The cancel and accept keys are kept for concistency
+ if event.key_name in ["ctrl+c", "escape"]:
+ self.on_cancel()
+ return False
+ if event.key_name == "enter":
+ self.on_ready()
+ return False
+
+ if self.prompt_handler and self.prompt_handler(self, event):
+ # If the prompt handler returns True the default action is skipped
+ return True
+
+ return Editor.handle_input(self, event)
+
+
class PromptAutocmp(Prompt):
"""An input prompt with basic autocompletion."""
diff --git a/suplemon/ui.py b/suplemon/ui.py
index b6e0737..5793985 100644
--- a/suplemon/ui.py
+++ b/suplemon/ui.py
@@ -8,7 +8,7 @@
import logging
from wcwidth import wcswidth
-from .prompt import Prompt, PromptBool, PromptFile, PromptAutocmp
+from .prompt import Prompt, PromptBool, PromptFiltered, PromptFile, PromptAutocmp
from .key_mappings import key_map
# Curses can't be imported yet but we'll
@@ -108,6 +108,13 @@ def __init__(self, app):
self.logger = logging.getLogger(__name__)
self.warned_old_curses = 0
self.limited_colors = True
+ self.screen = None
+ self.current_yx = None
+ self.text_input = None
+ self.header_win = None
+ self.status_win = None
+ self.editor_win = None
+ self.legend_win = None
def init(self):
"""Set ESC delay and then import curses."""
@@ -502,6 +509,12 @@ def query_bool(self, text, default=False):
result = self._query(text, default, PromptBool)
return result
+ def query_filtered(self, text, initial="", handler=None):
+ """Get an arbitrary string from the user with input filtering."""
+ prompt_inst = PromptFiltered(self.app, self.status_win, handler=handler)
+ result = self._query(text, initial, inst=prompt_inst)
+ return result
+
def query_file(self, text, initial=""):
"""Get a file path from the user."""
result = self._query(text, initial, PromptFile)
diff --git a/suplemon/viewer.py b/suplemon/viewer.py
index 706608a..565f82d 100644
--- a/suplemon/viewer.py
+++ b/suplemon/viewer.py
@@ -83,6 +83,9 @@ def __init__(self, app, window):
"find_all": self.find_all, # Ctrl + A
}
+ self.pygments_syntax = None # Needs to be implemented in derived classes
+ self.lexer = None # Needs to be implemented in derived classes
+
def init(self):
pass
@@ -101,23 +104,19 @@ def get_size(self):
y, x = self.window.getmaxyx()
return (x, y)
- def get_scroll_pos(self):
- return (self.y_scroll, self.x_scroll)
-
- def get_y_scroll(self):
- return self.y_scroll
+ @property
+ def scroll_pos(self):
+ return self.y_scroll, self.x_scroll
- def get_x_scroll(self):
- return self.x_scroll
+ @scroll_pos.setter
+ def scroll_pos(self, pos):
+ self.y_scroll = pos[0]
+ self.x_scroll = pos[1]
def get_cursor(self):
"""Return the main cursor."""
return self.cursors[0]
- def get_cursors(self):
- """Return list of all cursors."""
- return self.cursors
-
def get_first_cursor(self):
"""Get the first (primary) cursor."""
highest = None
@@ -205,10 +204,6 @@ def set_config(self, config):
self.config = config
self.set_cursor_style(self.config["cursor_style"])
- def set_scroll_pos(self, pos):
- self.y_scroll = pos[0]
- self.x_scroll = pos[1]
-
def set_cursor_style(self, cursor_style):
"""Set cursor style.
@@ -234,6 +229,12 @@ def set_single_cursor(self, cursor):
"""Discard all cursors and place a new one."""
self.cursors = [Cursor(cursor)]
+ def setup_linelight(self):
+ raise NotImplementedError("Needs to be implemented in derived classes")
+
+ def setup_highlight(self):
+ raise NotImplementedError("Needs to be implemented in derived classes")
+
def set_file_extension(self, ext):
"""Set the file extension."""
ext = ext.lower()
@@ -247,14 +248,6 @@ def add_cursor(self, cursor):
"""Add a new cursor. Accepts a x,y tuple or a Cursor instance."""
self.cursors.append(Cursor(cursor))
- def pad_lnum(self, n):
- """Pad line number with zeroes."""
- # TODO: move to helpers
- s = str(n)
- while len(s) < self.line_offset()-1:
- s = "0" + s
- return s
-
def max_line_length(self):
"""Get maximum line length that fits in the editor."""
return self.get_size()[0]-self.line_offset()-1
@@ -312,11 +305,10 @@ def render(self):
return
self.window.erase()
- i = 0
max_y = self.get_size()[1]
max_len = self.max_line_length()
# Iterate through visible lines
- while i < max_y:
+ for i in range(max_y):
x_offset = self.line_offset()
lnum = i + self.y_scroll
if lnum >= len(self.lines): # Make sure we have a line to show
@@ -325,7 +317,8 @@ def render(self):
line = self.lines[lnum]
if self.config["show_line_nums"]:
curs_color = curses.color_pair(line.number_color)
- self.window.addstr(i, 0, self.pad_lnum(lnum+1)+" ", curs_color)
+ padded_num = str(lnum+1).zfill(self.line_offset()-1)
+ self.window.addstr(i, 0, padded_num+" ", curs_color)
pos = (x_offset, i)
try:
@@ -333,7 +326,6 @@ def render(self):
except:
self.logger.error("Failed rendering line #{0} @{1} DATA:'{2}'!".format(lnum+1, pos, line),
exc_info=True)
- i += 1
self.render_cursors()
def render_line_contents(self, line, pos, x_offset, max_len):
@@ -409,6 +401,9 @@ def render_line_pygments(self, line, pos, x_offset, max_len):
first_token = False
x_offset += len(text)
+ def get_line_color(self, line):
+ raise NotImplementedError("Needs to be implemented in derived classes")
+
def render_line_linelight(self, line, pos, x_offset, max_len):
"""Render line with naive line based highlighting."""
y = pos[1]
@@ -805,6 +800,9 @@ def find_query(self):
if what:
self.find(what)
+ def store_action_state(self, state):
+ raise NotImplementedError("Needs to be implemented in derived classes")
+
def find(self, what, findall=False):
"""Find what in data (from top to bottom). Adds a cursor when found."""
# Sorry for this colossal function