Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

some changes to make it work like Emacs vertical file completion. #185

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion advanced_new_file/commands/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from .cut_to_file import AdvancedNewFileCutToFile
from .move_file_command import AdvancedNewFileMove, AdvancedNewFileMoveAtCommand
from .copy_file_command import AdvancedNewFileCopy, AdvancedNewFileCopyAtCommand
from .project_file_command import AdvancedNewFileProjectFileCommand

__all__ = [
"AnfReplaceCommand",
Expand All @@ -21,5 +22,6 @@
"AdvancedNewFileCopy",
"AdvancedNewFileCopyAtCommand",
"AnfRemoveRegionContentAndRegionCommand",
"AdvancedNewFileCutToFile"
"AdvancedNewFileCutToFile",
"AdvancedNewFileProjectFileCommand"
]
101 changes: 65 additions & 36 deletions advanced_new_file/commands/command_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@


class AdvancedNewFileBase(object):
static_input_panel_view = None

def __init__(self, window):
super()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No-op. Can be removed or replaced with super().__init__().

self.window = window
Expand Down Expand Up @@ -270,6 +268,38 @@ def __translate_alias(self, path):
def input_panel_caption(self):
return ""

def get_active_window_settings(self):
return sublime.active_window().settings()

def set_input_view(self, input_view):
self.get_active_window_settings().set("anf_input_view", input_view.id())

def get_input_view(self):
return sublime.View(self.get_active_window_settings().get("anf_input_view"))


def get_active_view_settings(self):
view = self.get_input_view()
if view:
return view.settings()
else:
return self.get_active_window_settings()

def clear_input_view(self):
self.get_active_view_settings().erase("anf_input_view")

def set_input_view_content(self, content):
self.get_active_view_settings().set("anf_input_view_content", content)

def get_input_view_content(self):
return self.get_active_view_settings().get("anf_input_view_content")

def clear_input_view_content(self):
self.get_active_view_settings().erase("anf_input_view_content")

def clear_input_view_project_files(self):
self.get_active_view_settings().erase("anf_input_view_project_files")
Comment on lines +289 to +302
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Imo these functions provide barely any value over their bodies. Besides, I'm unsure what the purpose of get_active_view_settings is when in reality it falls back back to the window's settings?

If you want to store data per window, you should probably do that explicitly (and all the time instead of only sometimes). However, I'm unsure if a window's settings are persisted on disk on exit, so this may actually result in leaking outdated data. An alternative would be a separate mapping that associates window-specific data with the window's id and is sufficiently cleared when the panel is closed. Usually I'd suggest storing that in the window command's instance variables, but since this plugin uses multiple commands that's not really an option.


def show_filename_input(self, initial):
caption = self.input_panel_caption()

Expand All @@ -286,8 +316,8 @@ def show_filename_input(self, initial):
self.input_panel_view.settings().set("anf_panel", True)
if self.settings.get(CURSOR_BEFORE_EXTENSION_SETTING):
self.__place_cursor_before_extension(self.input_panel_view)
AdvancedNewFileBase.static_input_panel_view = self.input_panel_view
self.__update_filename_input('')
self.set_input_view(self.input_panel_view)
self.__update_filename_input(initial)

def __update_filename_input(self, path_in):
new_content = path_in
Expand All @@ -296,9 +326,9 @@ def __update_filename_input(self, path_in):
if self.view is not None:
self.view.erase_status("AdvancedNewFile2")

input_view = AdvancedNewFileBase.static_input_panel_view
input_view = self.get_input_view()
if path_in.endswith("\t"):
creation_path, candidate, completion_list = self.parse_status_line(self.get_status_line()) # type: ignore
creation_path, candidate, completion_list = self.get_input_view_content()
new_content = self.completion_input(path_in.replace("\n", "").replace("\t", ""), candidate)
elif path_in.endswith("\n"):
path_in = path_in.replace("\n", "")
Expand All @@ -312,25 +342,41 @@ def __update_filename_input(self, path_in):
self.window.run_command("hide_panel", {"cancel": True})
return
else:
completion_list = self.completion.hint(path_in)
completion_list = self.get_completion_list(path_in)
if completion_list:
candidate = completion_list[0]
completion_list.remove(candidate)
else:
candidate = ''


if input_view:
input_view.hide_popup()
try:
input_view.hide_popup()
except Exception as e:
print("hide_popup", e)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug print?

if input_view and new_content != path_in:
input_view.run_command("anf_replace", {"content": new_content})
else:
base, path = self.split_path(path_in)
status_line = generate_creation_path(self.settings, base, path, True) + '|' + candidate + str(completion_list)
creation_path = generate_creation_path(self.settings, base, path, True)
status_line = self.create_status_line(creation_path, candidate, completion_list)
self.set_input_view_content((creation_path, candidate, completion_list))

if self.settings.get(SHOW_PATH_SETTING, False):
self.update_status_message(status_line)
if input_view and candidate and not new_content.endswith(candidate):
input_view.show_popup('<strong>' + candidate + '</strong><br/>' + '<br/>'.join(completion_list))
if not new_content.endswith(candidate):
self.show_input_popup(candidate, completion_list)

def show_input_popup(self, candidate, completion_list):
try:
input_view = self.get_input_view()
if input_view and candidate:
input_view.show_popup('<strong>' + candidate + '</strong><br/>' + '<br/>'.join(completion_list), max_width=1024)
except Exception as e:
print("show_popup", e)

def get_completion_list(self, path_in):
return self.completion.complete_for_folder(path_in)

def completion_input(self, path_in, candidate):
pattern = r"(.*[/\\:])(.*)"
Expand Down Expand Up @@ -403,7 +449,9 @@ def clear(self):
if self.view is not None:
self.view.erase_status("AdvancedNewFile")
self.view.erase_status("AdvancedNewFile2")
AdvancedNewFileBase.static_input_panel_view = None
self.clear_input_view()
self.clear_input_view_content()
self.clear_input_view_project_files()

def create(self, filename):
base, filename = os.path.split(filename)
Expand Down Expand Up @@ -455,7 +503,10 @@ def get_cursor_path(self):
break
if (re.match(".*string.quoted.double", syntax) or
re.match(".*string.quoted.single", syntax)):
path = view.substr(view.extract_scope(region.begin()))
point = region.begin()
if (re.match(".*punctuation.definition.string.end", syntax)):
point -= 1
path = view.substr(view.extract_scope(point))
path = re.sub('^"|\'', '', re.sub('"|\'$', '', path.strip()))
break

Expand Down Expand Up @@ -507,9 +558,6 @@ def __place_cursor_before_extension(self, view):
cursors.clear()
cursors.add(sublime.Region(initial_position, initial_position))

def get_status_line(self):
return self.view.get_status("AdvancedNewFile")

def update_status_message(self, creation_path):
if self.view is not None:
self.view.set_status("AdvancedNewFile", creation_path)
Expand All @@ -522,25 +570,6 @@ def get_status_prefix(self):
def create_status_line(self, creation_path, candidate, completion_list):
return creation_path + '|' + candidate + str(completion_list)

def parse_status_line(self, status_line):
# Creating file at AdvancedNewFile/advanced_new_file/commands/|__init__.py['command_base.py']
if status_line:
status_line = status_line.strip()
index1 = status_line.rindex('[')
completion_list = status_line[index1:]
try:
completion_list = json.loads(completion_list.replace("'", '"'))
except Exception as e:
print("completion_list", completion_list, e)
raise e
index2 = status_line.rindex('|')
candidate = status_line[index2 + 1:index1]
# TODO: prefix_len = len(self.get_status_prefix())
creation_path = status_line[0:index2]
return (creation_path, candidate, completion_list)
else:
return ('', '', [])

def next_candidate(self, candidate, completion_list):
if candidate and completion_list:
# replace the candidate with the first, and append the old candidate to the last
Expand Down
42 changes: 25 additions & 17 deletions advanced_new_file/commands/new_file_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def entered_file_action(self, path):

def multi_file_action(self, paths):
for path in paths:
self.single_file_action(path, False)
self.single_file_action(path)

def single_file_action(self, path, apply_template=True):
attempt_open = True
Expand Down Expand Up @@ -127,29 +127,37 @@ def __init__(self, window):

def run(self):
self.run_setup()
# Creating file at AdvancedNewFile/advanced_new_file/commands/|__init__.py['__init__.py', 'command_base.py']
status_line = self.get_status_line()
creation_path, candidate, completion_list = self.parse_status_line(status_line) # type: ignore
try:
creation_path, candidate, completion_list = self.get_input_view_content()
except Exception:
# if the content is not init, then simulate the new input
input_view = self.get_input_view()
input_view.run_command("anf_replace", {"content": ""})
creation_path, candidate, completion_list = self.get_input_view_content()

candidate, completion_list = self.next_candidate(candidate, completion_list)
self.set_input_view_content((creation_path, candidate, completion_list))
self.update_status_message(self.create_status_line(creation_path, candidate, completion_list))
input_view = AdvancedNewFileBase.static_input_panel_view
if input_view:
input_view.show_popup('<strong>' + candidate + '</strong><br/>' + '<br/>'.join(completion_list))
self.show_input_popup(candidate, completion_list)

class AdvancedNewFilePrevCommand(AdvancedNewFileBase, sublime_plugin.WindowCommand):
def __init__(self, window):
super().__init__(window)

def run(self):
self.run_setup()
# Creating file at AdvancedNewFile/advanced_new_file/commands/|__init__.py['__init__.py', 'command_base.py']
status_line = self.get_status_line()
creation_path, candidate, completion_list = self.parse_status_line(status_line) # type: ignore
try:
creation_path, candidate, completion_list = self.get_input_view_content()
except Exception:
# if the content is not init, then simulate the new input
input_view = self.get_input_view()
input_view.run_command("anf_replace", {"content": ""})
creation_path, candidate, completion_list = self.get_input_view_content()

candidate, completion_list = self.prev_candidate(candidate, completion_list)
self.set_input_view_content((creation_path, candidate, completion_list))
self.update_status_message(self.create_status_line(creation_path, candidate, completion_list))
input_view = AdvancedNewFileBase.static_input_panel_view
if input_view and candidate and completion_list:
input_view.show_popup('<strong>' + candidate + '</strong><br/>' + '<br/>'.join(completion_list))
self.show_input_popup(candidate, completion_list)


class AdvancedNewFileUpdirCommand(AdvancedNewFileBase, sublime_plugin.WindowCommand):
Expand All @@ -158,11 +166,11 @@ def __init__(self, window):

def run(self):
self.run_setup()
view = AdvancedNewFileBase.static_input_panel_view
if view:
path_in = view.substr(sublime.Region(0, view.size()))
input_view = self.get_input_view()
if input_view:
path_in = input_view.substr(sublime.Region(0, input_view.size()))
new_content = self.updir(path_in)
view.run_command("anf_replace", {"content": new_content})
input_view.run_command("anf_replace", {"content": new_content})

def updir(self, path_in):
if not(path_in):
Expand Down
52 changes: 52 additions & 0 deletions advanced_new_file/commands/project_file_command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import sublime
import sublime_plugin
import os
import re
import xml.etree.ElementTree as ET

from .new_file_command import AdvancedNewFileNew
from ..lib.package_resources import get_resource
from ..anf_util import *


class AdvancedNewFileProjectFileCommand(AdvancedNewFileNew, sublime_plugin.WindowCommand):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this command?

def __init__(self, window):
super().__init__(window)

def run(self, is_python=False, initial_path=None):
self.is_python = is_python
self.run_setup()
self.show_filename_input(self.generate_initial_path(initial_path))

def get_project_folder(self):
return sublime.active_window().folders()[0]

def split_path(self, path_in):
return (self.get_project_folder(), path_in)

def entered_file_action(self, path):
if self.settings.get(SHELL_INPUT_SETTING, False):
self.multi_file_action(self.curly_brace_expansion(path))
else:
self.single_file_action(path)

def input_panel_caption(self):
caption = 'Enter a path for a project file'
return caption

def update_status_message(self, creation_path):
if self.view is not None:
self.view.set_status("AdvancedNewFile", "Project file at %s" % creation_path)
else:
sublime.status_message("Project file at %s" % creation_path)

def get_completion_list(self, path_in):
return self.completion.complete_for_project(path_in)

def set_input_view_project_files(self, project_files):
self.get_active_view_settings().set("anf_input_view_project_files", project_files)

def get_input_view_project_files(self):
return self.get_active_view_settings().get("anf_input_view_project_files")


40 changes: 37 additions & 3 deletions advanced_new_file/completions/completion_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ def generate_completion_list(self, path_in):
alias_list += self.generate_alias_auto_complete(filename)
alias_list += self.generate_project_auto_complete(filename)
base, path = self.command.split_path(path_in)
print("base,path", path_in, base, path)
# print("base,path", path_in, base, path)
full_path = generate_creation_path(self.settings, base, path)

directory, filename = os.path.split(full_path)
Expand All @@ -58,7 +58,7 @@ def generate_completion_list(self, path_in):

completion_list = alias_list + dir_list + file_list

return sort_by_fuzzy(filename, completion_list), alias_list, dir_list, file_list
return sort_by_fuzzy(filename, completion_list, 20), alias_list, dir_list, file_list

def filter_file(self, fname):
"""
Expand Down Expand Up @@ -108,7 +108,7 @@ def compare_entries(self, compare_entry, compare_base):
pattern = get_str_pattern(compare_base, self.settings.get(IGNORE_CASE_SETTING, True))
return re.match(pattern, compare_entry) is not None

def hint(self, path_in):
def complete_for_folder(self, path_in):
(completion_list, alias_list,
dir_list, file_list) = self.generate_completion_list(path_in)
new_completion_list = []
Expand All @@ -120,3 +120,37 @@ def hint(self, path_in):
path += ":"
new_completion_list.append(path)
return new_completion_list


def complete_for_project(self, path_in):
directory = self.command.get_project_folder()

completion_list = []
if os.path.isdir(directory):
files = self.command.get_input_view_project_files()
if not files:
files = self.get_files_recursively(directory, self.filter_file)
self.command.set_input_view_project_files(files)
else:
print("use the old files", str(len(files)))
for file in files:
if self.compare_entries(os.path.basename(file), path_in):
completion_list.append(file)

return sort_by_fuzzy(path_in, completion_list, 20)

def get_files_recursively(self, dir, filter_func=None):
if not os.path.isdir(dir):
return list(dir)
dirlist = os.walk(dir)
result = []
for root, _, files in dirlist:
rel_path = os.path.relpath(root, dir)
for file in files:
rel_file = os.path.join(rel_path, file)
if filter_func:
if filter_func(rel_file):
result.append(rel_file)
else:
result.append(rel_file)
return result