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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions AdvancedNewFile.sublime-settings
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,13 @@
// When specifying initial input, this boolean will place the cursor prior to the .<content>
"cursor_before_extension": false,

// Ignore regex patters
"filter_regex": ["\\.DS_Store", "\\.git"]
// Ignore regex patterns
"filter_regex": ["\\.DS_Store", "\\.git"],

// Use Chinese Pinyin to filter candidate in completion list
"use_pinyin_to_filter": false,

// miliseconds to delay when input char to complete.
// This only work for project file, because some projects maybe very large.
"completion_after_milliseconds": 100
}
3 changes: 2 additions & 1 deletion Default.sublime-commands
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
{ "caption": "ANF: Delete File", "command": "advanced_new_file_delete"},
{ "caption": "ANF: Delete Current File", "command": "advanced_new_file_delete", "args": {"current": true}},
{ "caption": "ANF: Copy Current File", "command": "advanced_new_file_copy" },
{ "caption": "ANF: Cut to File", "command": "advanced_new_file_cut_to_file" }
{ "caption": "ANF: Cut to File", "command": "advanced_new_file_cut_to_file" },
{ "caption": "ANF: Project File", "command": "advanced_new_file_project_file" }
]
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ The plugin does not contain any menu commands by default. To add them yourself,
## Keymaps
If you have issues with keymaps, consider running [FindKeyConflicts](https://github.com/skuroda/FindKeyConflicts), also available through the package manager. Alternatively, set command logging to true by entering `sublime.log_commands(True)` in the Sublime Text console.

- `tab` or `ctrl+j` to choose the first candidate
- `ctrl+n` change to the next candidate in the completion list
- `ctrl+p` change to the prev candidate in the completion list
- `ctrl+l` change to the updir of the input path
- `enter` choose the first candidate when popup, otherwise confirm(finish) the operation when no popup

### Windows
`ctrl+alt+n`: General keymap to create new files.

Expand Down Expand Up @@ -273,6 +279,10 @@ In Sublime Text 2, the name of the folder will be the actual name of the folder,
###### Current Working Directory
To specify the current working directory, simply type a colon, without any preceding text. Alternatively, set `relative_from_current` to `true` in your settings. Paths specified as relative paths will then begin from the current working directory.

## TODO
### project scope settings
`filter_regex` and `use_pinyin_to_filter` settings are project related. We can set these in `.sublime-project` file for project specific.

## Notes
Thanks to Dima Kukushkin ([xobb1t](https://github.com/xobb1t)) for the original work on this plugin. Also, thank you to [facelessuser](https://github.com/facelessuser), and by extension biermeester and matthjes for the idea of platform specific settings. Additional thanks to [kemayo](https://github.com/kemayo) for the work in identifying git executable.

Expand Down
6 changes: 5 additions & 1 deletion advanced_new_file/anf_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@
DEFAULT_NEW_FILE = "empty_filename_action"
CURSOR_BEFORE_EXTENSION_SETTING = "cursor_before_extension"
FILTER_REGEX_SETTING = "filter_regex"
USE_PINYIN_TO_FILTER_SETTING = "use_pinyin_to_filter"
COMPLETION_DELAY_SETTING = "completion_after_milliseconds"


SETTINGS = [
Expand Down Expand Up @@ -82,7 +84,9 @@
COPY_FILE_DEFAULT_ROOT_SETTING,
DEFAULT_NEW_FILE,
CURSOR_BEFORE_EXTENSION_SETTING,
FILTER_REGEX_SETTING
FILTER_REGEX_SETTING,
USE_PINYIN_TO_FILTER_SETTING,
COMPLETION_DELAY_SETTING,
]

NIX_ROOT_REGEX = r"^/"
Expand Down
37 changes: 32 additions & 5 deletions advanced_new_file/commands/command_base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import errno
import functools
import os
import re
import sublime
Expand Down Expand Up @@ -300,9 +301,11 @@ def clear_input_view_content(self):
def clear_input_view_project_files(self):
self.get_active_view_settings().erase("anf_input_view_project_files")

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

self.input_char_count = 0
self.completion_delay = completion_delay
self.input_panel_view = self.window.show_input_panel(
caption, initial,
self.on_done, self.__update_filename_input, self.clear
Expand All @@ -320,24 +323,45 @@ def show_filename_input(self, initial):
self.__update_filename_input(initial)

def __update_filename_input(self, path_in):
self.input_char_count += 1
if not path_in or path_in.endswith("\t") or path_in.endswith("\n"):
self.__update_filename_input_lazy(path_in, self.input_char_count)
else:
sublime.set_timeout(
functools.partial(self.__update_filename_input_lazy, path_in, self.input_char_count),
self.completion_delay)

def __update_filename_input_lazy(self, path_in, count):
if self.input_char_count != count:
return
self.input_char_count = 0

new_content = path_in
if self.settings.get(COMPLETION_TYPE_SETTING) == "windows":
if "prev_text" in dir(self) and self.prev_text != path_in:
if self.view is not None:
self.view.erase_status("AdvancedNewFile2")

input_view = self.get_input_view()
if '/~/' in path_in:
index = path_in.rindex('/~/')
new_content = path_in[index + 1:]
if '//' in path_in:
index = path_in.rindex('//')
new_content = path_in[index + 1:]
if input_view and new_content != path_in:
input_view.run_command("anf_replace", {"content": new_content})
return

if path_in.endswith("\t"):
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", "")
path_in = path_in.replace("\t", "").replace("\n", "")
if input_view:
# print("visible", input_view.is_popup_visible())
if input_view.is_popup_visible():
input_view.run_command("insert", {"characters": "\t"})
else:
# print("end panel")
self.on_done(path_in)
self.window.run_command("hide_panel", {"cancel": True})
return
Expand Down Expand Up @@ -510,7 +534,10 @@ def get_cursor_path(self):
path = re.sub('^"|\'', '', re.sub('"|\'$', '', path.strip()))
break

return path
if "/" in path:
return path
else:
return ""
Comment on lines +537 to +540
Copy link
Member

Choose a reason for hiding this comment

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

I don't understand this logic here, but the return value doesn't seem to be used anyway?


def _expand_default_path(self, path):
current_file = self.view.file_name()
Expand Down
5 changes: 4 additions & 1 deletion advanced_new_file/commands/project_file_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ def __init__(self, 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))
print(self.settings)
completion_delay = self.settings.get(COMPLETION_DELAY_SETTING, 100)
print(completion_delay)
Comment on lines +19 to +21
Copy link
Member

Choose a reason for hiding this comment

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

Debug prints

self.show_filename_input(self.generate_initial_path(initial_path), completion_delay)

def get_project_folder(self):
return sublime.active_window().folders()[0]
Expand Down
13 changes: 4 additions & 9 deletions advanced_new_file/completions/completion_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,9 @@ def generate_auto_complete(self, base, iterable_var):
return sugg

def compare_entries(self, compare_entry, compare_base):
# if self.settings.get(IGNORE_CASE_SETTING):
# compare_entry = compare_entry.lower()
# compare_base = compare_base.lower()

# return compare_entry.startswith(compare_base)
# turn to fuzzy match
pattern = get_str_pattern(compare_base, self.settings.get(IGNORE_CASE_SETTING, True))
# fuzzy match
pattern = get_str_pattern(compare_base, self.settings.get(IGNORE_CASE_SETTING, True),
self.settings.get(USE_PINYIN_TO_FILTER_SETTING, False))
return re.match(pattern, compare_entry) is not None

def complete_for_folder(self, path_in):
Expand All @@ -131,8 +127,7 @@ def complete_for_project(self, path_in):
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)
Expand Down
16 changes: 9 additions & 7 deletions advanced_new_file/completions/fuzzy_sort.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,16 @@
# import numpy as np

def sort_by_fuzzy(query, choices, limit:int = 0):
FichteFoll marked this conversation as resolved.
Show resolved Hide resolved
if not query or not choices:
return choices
choices_ratio = {}
for choice in choices:
choices_ratio[choice] = levenshtein_ratio(query, choice)
if not choices:
return []
if not query:
result = choices
else:
choices_ratio = {}
for choice in choices:
choices_ratio[choice] = levenshtein_ratio(query, choice)
Copy link
Member

Choose a reason for hiding this comment

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

Better use a dict comprehension now.

result = [key[0] for key in sorted(choices_ratio.items(), key=itemgetter(1), reverse=True)]

# print(choices_ratio)
result = [key[0] for key in sorted(choices_ratio.items(), key=itemgetter(1), reverse=True)]
if limit > 0 and len(result) > limit:
return result[0:limit]
else:
Expand Down
20 changes: 13 additions & 7 deletions advanced_new_file/completions/pinyin_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,32 @@
PINYINLIB_SIMPLIFIED_CHAR_PATTERN.append('|'.join([c for c in PINYINLIB_SIMPLIFIED_CHAR_LIST[i]]))
# print('|'.join([c for c in PINYINLIB_SIMPLIFIED_CHAR_LIST[i]]))

def get_char_pattern(ch, ignore_case=True):
def get_char_pattern(ch, ignore_case=True, use_pinyin=False):
result = ''
if ord(ch) >= ord('a') and ord(ch) <= ord('z'):
result += ch + '|'
if ignore_case:
result += chr(ord(ch) - ord('a') + ord('A')) + '|'
result += ch + '|' + PINYINLIB_SIMPLIFIED_CHAR_PATTERN[ord(ch) - ord('a')]
if use_pinyin:
result += PINYINLIB_SIMPLIFIED_CHAR_PATTERN[ord(ch) - ord('a')]
if result.endswith('|'):
result = result[0:-1]
elif ord(ch) >= ord('A') and ord(ch) <= ord('Z'):
result += ch + '|'
if ignore_case:
result += chr(ord(ch) - ord('A') + ord('a')) + '|'
result += ch + '|' + PINYINLIB_SIMPLIFIED_CHAR_PATTERN[ord(ch) - ord('A')]
if use_pinyin:
result += PINYINLIB_SIMPLIFIED_CHAR_PATTERN[ord(ch) - ord('A')]
if result.endswith('|'):
result = result[0:-1]
else:
result = ch
if result.endswith('|'):
result = result[0:-1]
return result

def get_str_pattern(st, ignore_case=True):
def get_str_pattern(st, ignore_case=True, use_pinyin=False):
result = '.*'
for c in st:
result += '(' + get_char_pattern(c, ignore_case) + ').*'
result += '(' + get_char_pattern(c, ignore_case, use_pinyin) + ').*'
return result


Expand Down