Skip to content

Commit

Permalink
Merge pull request #199 from richrd/dev
Browse files Browse the repository at this point in the history
v0.1.63
  • Loading branch information
richrd authored Nov 3, 2017
2 parents cc0ff42 + f5ef033 commit 66d0b9e
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 36 deletions.
19 changes: 18 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,24 @@
Change Log
==========

## [v0.1.62](https://github.com/richrd/suplemon/tree/v0.1.62) (2017-08-22) compared to previous master branch.

## [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)

**Implemented enhancements:**

- Add autocomplete to run command prompt (fixes #171)
- Increase battery status polling time to 60 sec (previously 10 sec)
- Change the top bar suplemon icon to a fancy unicode lemon.
- Add paste mode for better pasting over SSH (disables auto indentation)

**Fixed bugs:**

- Keep top bar statuses of modules in alphabetical order based on module name. (fixes #57)
- Prevent restoring file state if file has changed since last time (fixes #198)


## [v0.1.62](https://github.com/richrd/suplemon/tree/v0.1.62) (2017-09-25) compared to previous master branch.
[Full Changelog](https://github.com/richrd/suplemon/compare/v0.1.61...v0.1.62)

**Fixed bugs:**
Expand Down
34 changes: 24 additions & 10 deletions suplemon/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ def reload_config(self):
self.config.reload()
for f in self.files:
self.setup_editor(f.editor)
self.trigger_event_after("config_loaded")
self.ui.resize()
self.ui.refresh()

Expand Down Expand Up @@ -421,33 +422,34 @@ def go_to(self):
try:
input_str = int(lineno)
self.get_editor().go_to_pos(input_str)
except:
except ValueError:
pass
else:
try:
line_no = int(input_str)
self.get_editor().go_to_pos(line_no)
except:
except ValueError:
file_index = self.find_file(input_str)
if file_index != -1:
self.switch_to_file(file_index)

def find_file(self, s):
"""Return index of file matching string."""
# REFACTOR: Move to a helper function or implement in a module

# Case insensitive matching
s = s.lower()
i = 0

# First match files beginning with s
for file in self.files:
for i, file in enumerate(self.files):
if file.name.lower().startswith(s):
return i
i += 1
i = 0
# Then match files that contain s
for file in self.files:

# Then match any files that contain s
for i, file in enumerate(self.files):
if s in file.name.lower():
return i
i += 1

return -1

def run_command(self, data):
Expand All @@ -474,6 +476,7 @@ def run_module(self, module_name, args=""):
self.modules.modules[module_name].run(self, self.get_editor(), args)
return True
except:
# Catch any error when running a module just incase
self.set_status("Running command failed!")
self.logger.exception("Running command failed!")
return False
Expand Down Expand Up @@ -511,6 +514,7 @@ def trigger_event(self, event, when):
try:
val = cb(event)
except:
# Catch all errors in callbacks just incase
self.logger.error("Failed running callback: {0}".format(cb), exc_info=True)
continue
if val:
Expand Down Expand Up @@ -549,7 +553,17 @@ def toggle_mouse(self):

def query_command(self):
"""Run editor commands."""
data = self.ui.query("Command:")
if sys.version_info[0] < 3:
modules = self.modules.modules.iteritems()
else:
modules = self.modules.modules.items()

# Get built in operations
completions = [oper for oper in self.operations.keys()]
# Add runnable modules
completions += [name for name, m in modules if m.is_runnable()]

data = self.ui.query_autocmp("Command:", completions=sorted(completions))
if not data:
return False
self.run_command(data)
Expand Down
17 changes: 16 additions & 1 deletion suplemon/modules/application_state.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
# -*- encoding: utf-8


import hashlib

from suplemon.suplemon_module import Module


Expand Down Expand Up @@ -30,9 +33,17 @@ def get_file_state(self, file):
state = {
"cursors": [cursor.tuple() for cursor in editor.get_cursors()],
"scroll_pos": editor.get_scroll_pos(),
"hash": self.get_hash(editor),
}
return state

def get_hash(self, editor):
# We don't need cryptographic security so we just use md5
h = hashlib.md5()
for line in editor.lines:
h.update(line.get_data().encode("utf-8"))
return h.hexdigest()

def set_file_state(self, file, state):
"""Set the state of a file."""
file.editor.set_cursors(state["cursors"])
Expand All @@ -50,7 +61,11 @@ def restore_states(self):
for file in self.app.get_files():
path = file.get_path()
if path in self.storage.get_data().keys():
self.set_file_state(file, self.storage[path])
state = self.storage[path]
if "hash" not in state:
self.set_file_state(file, state)
elif state["hash"] == self.get_hash(file.get_editor()):
self.set_file_state(file, state)


module = {
Expand Down
2 changes: 1 addition & 1 deletion suplemon/modules/battery.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ class Battery(Module):
def init(self):
self.last_value = -1
self.checked = time.time()
self.interval = 10
self.interval = 60 # Seconds to wait until polling again

def value(self):
"""Get the battery charge percent and cache it."""
Expand Down
47 changes: 47 additions & 0 deletions suplemon/modules/paste.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# -*- encoding: utf-8

from suplemon.suplemon_module import Module


class Paste(Module):
def init(self):
# Flag for paste mode
self.active = False
# Initial state of auto indent
self.auto_indent_active = self.app.config["editor"]["auto_indent_newline"]
# Listen for config changes
self.bind_event_after("config_loaded", self.config_loaded)

def run(self, app, editor, args):
# Simply toggle pastemode when the command is run
self.active = not self.active
self.set_paste_mode(self.active)
self.show_confirmation()
return True

def config_loaded(self, e):
# Refresh the auto indent state when config is reloaded
self.auto_indent_active = self.app.config["editor"]["auto_indent_newline"]

def get_status(self):
# Return the paste mode status for the statusbar
return "[PASTEMODE]" if self.active else ""

def show_confirmation(self):
# Show a status message when pastemode is toggled
state = "activated" if self.active else "deactivated"
self.app.set_status("Paste mode " + state)

def set_paste_mode(self, active):
# Enable or disable auto indent
if active:
self.app.config["editor"]["auto_indent_newline"] = False
else:
self.app.config["editor"]["auto_indent_newline"] = self.auto_indent_active


module = {
"class": Paste,
"name": "paste",
"status": "bottom",
}
67 changes: 50 additions & 17 deletions suplemon/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,34 +129,36 @@ def get_input(self, caption="", initial=False):
return False


class PromptFile(Prompt):
"""An input prompt with path auto completion based on Prompt."""
class PromptAutocmp(Prompt):
"""An input prompt with basic autocompletion."""

def __init__(self, app, window):
def __init__(self, app, window, initial_items=[]):
Prompt.__init__(self, app, window)
# Wether the autocomplete feature is active
self.complete_active = 0
# Whether the autocomplete feature is active
self.complete_active = False
# Index of last item that was autocompleted
self.complete_index = 0
# Input path to use for autocompletion (stored when autocompletion is activated)
# Input data to use for autocompletion (stored when autocompletion is activated)
self.complete_data = ""
# Default autocompletable items
self.complete_items = initial_items

def handle_input(self, event):
"""Handle special bindings for the prompt."""
name = event.key_name
if self.complete_active:
# Allow accepting completed directories with enter
if name == "enter":
if os.path.isdir(self.get_data()):
if self.has_match():
self.deactivate_autocomplete()
return False
# Revert auto completion with esc
# Revert autocompletion with esc
if name == "escape":
self.revert_autocomplete()
self.deactivate_autocomplete()
return False
if name == "tab":
# Run auto completion when tab is pressed
# Run autocompletion when tab is pressed
self.autocomplete()
# Don't pass the event to the parent class
return False
Expand All @@ -166,7 +168,7 @@ def handle_input(self, event):
# Don't pass the event to the parent class
return False
else:
# If any key other than tab is pressed deactivate the auto completer
# If any key other than tab is pressed deactivate the autocompleter
self.deactivate_autocomplete()
Prompt.handle_input(self, event)

Expand All @@ -175,8 +177,8 @@ def autocomplete(self, previous=False):
if self.complete_active: # If the completer is active use the its initial input value
data = self.complete_data

name = os.path.basename(data)
items = self.get_path_contents(data) # Get directory listing of input path
name = self.get_completable_name(data)
items = self.get_completable_items(data)

# Filter the items by name if the input path contains a name
if name:
Expand All @@ -187,8 +189,8 @@ def autocomplete(self, previous=False):
return False

if not self.complete_active:
# Initialize the auto completor
self.complete_active = 1
# Initialize the autocompletor
self.complete_active = True
self.complete_data = data
self.complete_index = 0
else:
Expand All @@ -205,15 +207,27 @@ def autocomplete(self, previous=False):
self.complete_index = 0

item = items[self.complete_index]
new_data = os.path.join(os.path.dirname(data), item)
new_data = self.get_full_completion(data, item)
if len(items) == 1:
self.deactivate_autocomplete()
# Set the input data to the new path and move cursor to the end
# Set the input data to the completion and move cursor to the end
self.set_data(new_data)
self.end()

def get_completable_name(self, data=""):
return data

def get_completable_items(self, data=""):
return self.complete_items

def get_full_completion(self, data, item):
return item

def has_match(self):
return False

def deactivate_autocomplete(self):
self.complete_active = 0
self.complete_active = False
self.complete_index = 0
self.complete_data = ""

Expand All @@ -228,6 +242,25 @@ def filter_items(self, items, name):
name = name.lower()
return [item for item in items if item.lower().startswith(name)]


class PromptFile(PromptAutocmp):
"""An input prompt with path autocompletion based on PromptAutocmp."""

def __init__(self, app, window):
PromptAutocmp.__init__(self, app, window)

def has_match(self):
return os.path.isdir(os.path.expanduser(self.get_data()))

def get_completable_name(self, data=""):
return os.path.basename(data)

def get_completable_items(self, data=""):
return self.get_path_contents(data) # Get directory listing of input path

def get_full_completion(self, data, item):
return os.path.join(os.path.dirname(data), item)

def get_path_contents(self, path):
path = os.path.dirname(os.path.expanduser(path))
# If we get an empty path use the current directory
Expand Down
8 changes: 8 additions & 0 deletions suplemon/suplemon_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ def get_options(self):
"""Get module options."""
return self.options

def get_status(self):
"""Called by app when to get status bar contents."""
return ""

def set_name(self, name):
"""Set module name."""
self.name = name
Expand All @@ -134,6 +138,10 @@ def set_options(self, options):
"""Set module options."""
self.options = options

def is_runnable(self):
cls_method = getattr(Module, "run")
return self.run.__module__ != cls_method.__module__

def init_logging(self, name):
"""Initialize the module logger (self.logger).
Expand Down
Loading

0 comments on commit 66d0b9e

Please sign in to comment.