Skip to content

Commit 718ff7c

Browse files
committed
Through the view.show_popup to achieve the function of goto-definition. Added record and macro goto-definition function.
1 parent 7b5351c commit 718ff7c

File tree

6 files changed

+169
-112
lines changed

6 files changed

+169
-112
lines changed

Default.sublime-mousemap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,9 @@
33
// "modifiers": ["ctrl"], "button": "button1",
44
// "command": "goto",
55
// "press_command": "drag_select"
6+
// },
7+
// {
8+
// "modifiers": ["ctrl"], "button": "button2",
9+
// "command": "jump_back",
610
// }
711
]

erl_autocompletion.py

Lines changed: 19 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
import sublime_plugin, sublime, re, os, sys, shutil
44

55
cache = {}
6+
go_to = None
67

78
def plugin_loaded():
89
global cache
10+
global go_to
911

1012
cache_dir = os.path.join(sublime.cache_path(), GLOBAL_SET['package_name'])
1113
cache['libs'] = DataCache([get_erl_lib_dir()], 'libs', cache_dir)
@@ -16,6 +18,8 @@ def plugin_loaded():
1618
cache['project'] = DataCache(project_folder, 'project', cache_dir)
1719
cache['project'].build_data_async()
1820

21+
go_to = GoTo()
22+
1923
def plugin_unloaded():
2024
from package_control import events
2125

@@ -29,16 +33,7 @@ def plugin_unloaded():
2933
plugin_loaded()
3034
unload_handler = plugin_unloaded
3135

32-
class SaveFileRebuildListener(sublime_plugin.EventListener):
33-
def on_post_save(self, view):
34-
caret = view.sel()[0].a
35-
36-
if not ('source.erlang' in view.scope_name(caret)):
37-
return
38-
39-
cache['project'].build_data_async()
40-
41-
class CompletionsListener(sublime_plugin.EventListener):
36+
class ErlListener(sublime_plugin.EventListener):
4237
def on_query_completions(self, view, prefix, locations):
4338
if cache['libs'].libs == {}:
4439
return
@@ -62,103 +57,24 @@ def on_query_completions(self, view, prefix, locations):
6257
if module_name in cache['project'].libs:
6358
return (cache['project'].libs[module_name], flag)
6459

65-
class GotoCommand(sublime_plugin.TextCommand, DataCache):
66-
def __init__(self, view):
67-
sublime_plugin.TextCommand.__init__(self, view)
68-
DataCache.__init__(self)
69-
self.window = sublime.active_window()
70-
71-
def run(self, edit):
72-
line_str = self.__get_line_str(self.view)
73-
74-
math = self.re_dict['take_mf'].search(line_str)
75-
if math is not None and (len(math.groups()) == 2):
76-
module_name = math.group(1)
77-
fun_name = math.group(2)
78-
79-
key = (module_name, fun_name)
80-
if key in cache['libs'].fun_postion:
81-
self.__window_quick_panel_open_window(cache['libs'].fun_postion[key])
82-
elif key in cache['project'].fun_postion:
83-
self.__window_quick_panel_open_window(cache['project'].fun_postion[key])
84-
85-
return
60+
def on_text_command(self, view, command_name, args):
61+
if command_name == 'goto' and 'event' in args:
62+
event = args['event']
63+
point = view.window_to_text((event['x'], event['y']))
8664

87-
math = self.re_dict['take_fun'].search(line_str)
88-
if math is not None and (len(math.groups()) == 1):
89-
fun_name = math.group(1)
90-
91-
filepath = self.view.file_name()
92-
libs_key = ('erlang', fun_name)
93-
cur_module = self.get_module_from_path(filepath)
94-
project_key = (cur_module, fun_name)
95-
if libs_key in cache['libs'].fun_postion:
96-
self.__window_quick_panel_open_window(cache['libs'].fun_postion[libs_key])
97-
else:
98-
row_id = 1
99-
cur_view_postion = {}
100-
all_cur_view_fun = []
101-
module = self.get_module_from_path(filepath)
102-
code = self.view.substr(sublime.Region(0, self.view.size()))
103-
for line in code.split('\n'):
104-
funhead = self.re_dict['funline'].search(line)
105-
if funhead is not None:
106-
fun_name = funhead.group(1)
107-
param_str = funhead.group(2)
108-
param_list = self.format_param(param_str)
109-
key = (module, fun_name)
110-
param_len = len(param_list)
111-
format_fun_name = '{0}/{1}'.format(fun_name, param_len)
112-
113-
if (key, param_len) not in all_cur_view_fun:
114-
if key not in cur_view_postion:
115-
cur_view_postion[key] = []
116-
cur_view_postion[key].append((format_fun_name, filepath, row_id))
117-
all_cur_view_fun.append((key, param_len))
118-
row_id += 1
119-
120-
self.__window_quick_panel_open_window(cur_view_postion[project_key])
121-
122-
return
123-
124-
def __get_line_str(self, view):
125-
location = view.sel()[0].begin()
126-
line_region = view.line(location)
127-
line_str = view.substr(line_region)
65+
if not view.match_selector(point, "source.erlang"):
66+
return
12867

129-
return line_str
68+
go_to.run(point, view, cache)
13069

131-
def __window_quick_panel_open_window(self, options):
132-
self.point = self.view.sel()[0]
133-
self.options = options
70+
def on_post_save(self, view):
71+
caret = view.sel()[0].a
13472

135-
if len(options) == 1:
136-
(_fun_name, path, row) = options[0]
137-
self.window.open_file('{}:{}:0'.format(path, row), sublime.ENCODED_POSITION)
73+
if not ('source.erlang' in view.scope_name(caret)):
13874
return
13975

140-
self.window.show_quick_panel(
141-
[self.__show_option(o) for o in options],
142-
self._jump_to_in_window,
143-
on_highlight=partial(self._jump_to_in_window, transient=sublime.TRANSIENT))
144-
145-
def __show_option(self, options):
146-
return '{1}:{0} line: {2}'.format(*options)
147-
148-
def _jump_to_in_window(self, index, line_number=None, param_cnt=None, transient=0):
149-
150-
try:
151-
if self.view.sel()[0] != self.point:
152-
self.view.sel().clear()
153-
self.view.sel().add(self.point)
154-
except AttributeError:
155-
pass
156-
157-
if isinstance(index, int):
158-
if index == -1:
159-
self.window.focus_view(self.view)
160-
self.view.show(self.point)
161-
return
76+
cache['project'].build_data_async()
16277

163-
(fun_name, filename, line_number) = self.options[index]
164-
self.window.open_file('{0}:{1}'.format(filename, line_number or 0), sublime.ENCODED_POSITION|transient)
78+
class GotoCommand(sublime_plugin.TextCommand):
79+
def run(self, edit):
80+
return

util/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
from .data_cache import DataCache
2-
from .settings import get_erl_lib_dir, get_settings_param, GLOBAL_SET
2+
from .settings import get_erl_lib_dir, get_settings_param, GLOBAL_SET
3+
from .go_to import GoTo

util/data_cache.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class DataCache:
55
def __init__(self, dir = '', data_type = '', cache_dir = ''):
66
self.dir = dir
77
self.libs = {}
8-
self.fun_postion = {}
8+
self.fun_position = {}
99
self.modules = []
1010
self.data_type = data_type
1111
self.cache_dir = cache_dir
@@ -42,9 +42,9 @@ def build_module_dict(self, filepath):
4242
format_fun_name = '{0}/{1}'.format(fun_name, param_len)
4343
self.libs[module].append(('{0}\tfunction'.format(format_fun_name), completion))
4444

45-
if (module, fun_name) not in self.fun_postion:
46-
self.fun_postion[(module, fun_name)] = []
47-
self.fun_postion[(module, fun_name)].append((format_fun_name, filepath, row_id))
45+
if (module, fun_name) not in self.fun_position:
46+
self.fun_position[(module, fun_name)] = []
47+
self.fun_position[(module, fun_name)].append((format_fun_name, filepath, row_id))
4848
row_id += 1
4949
self.modules.append({'trigger' : '{0}\tmodule'.format(module), 'contents' : module})
5050

@@ -102,7 +102,7 @@ def build_data(self):
102102
all_filepath.append(os.path.join(root, file))
103103

104104
if all_filepath == []:
105-
(self.libs, self.fun_postion) = self.__load_data('completion')
105+
(self.libs, self.fun_position) = self.__load_data('completion')
106106
else:
107107
cache_info = self.__load_data('cache_info')
108108
all_filepath_md5 = hashlib.md5(json.dumps(all_filepath).encode('UTF-8')).hexdigest()
@@ -116,10 +116,10 @@ def build_data(self):
116116
self.modules.append({'trigger' : trigger, 'contents' : content})
117117

118118
self.__save_data('cache_info', new_cache_info)
119-
self.__save_data('completion', (self.libs, self.fun_postion))
119+
self.__save_data('completion', (self.libs, self.fun_position))
120120
self.__dump_json('erlang.sublime-completions', {'scope': 'source.erlang', 'completions': self.modules})
121121
else:
122-
(self.libs, self.fun_postion) = self.__load_data('completion')
122+
(self.libs, self.fun_position) = self.__load_data('completion')
123123

124124
def build_data_async(self):
125125
this = self

util/go_to.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
from .data_cache import DataCache
2+
import sublime, re, os
3+
4+
class GoTo(DataCache):
5+
def __init__(self):
6+
DataCache.__init__(self)
7+
self.max_col = 400
8+
9+
def run(self, point, view, cache):
10+
line_region = view.line(point)
11+
line_str = view.substr(line_region)
12+
word_region = view.word(point)
13+
word = view.substr(word_region)
14+
filepath = view.file_name()
15+
16+
maths = self.re_dict['take_mf'].findall(line_str)
17+
for math in maths:
18+
if word in math:
19+
if self.open_function_popup(view, point, math[0], math[1], cache['libs'].fun_position):
20+
return
21+
if self.open_function_popup(view, point, math[0], math[1], cache['project'].fun_position):
22+
return
23+
24+
maths = self.re_dict['take_fun'].findall(line_str)
25+
for math in maths:
26+
if word == math:
27+
module = self.get_module_from_path(filepath)
28+
if self.open_function_popup(view, point, 'erlang', math, cache['libs'].fun_position):
29+
return
30+
if self.open_function_popup(view, point, module, math, self.build_module_position(view, module, filepath)):
31+
return
32+
33+
maths = self.re_dict['take_record'].findall(line_str)
34+
for math in maths:
35+
if word == math:
36+
re_define = re.compile(r'(-\s*record\s*\([\s\n\r]*' + word + r'[\s\n\r]*,[\s\n\r]*{[^-]*}[\s\n\r]*\)\.)', re.MULTILINE|re.DOTALL)
37+
if self.open_hrl_popup(view, re_define, filepath, point):
38+
return
39+
40+
maths = self.re_dict['take_define'].findall(line_str)
41+
for math in maths:
42+
if word == math:
43+
re_define = re.compile(r'(-\s*define\s*\([\s\n\r]*' + word + r'[\s\n\r]*,[\s\n\r]*[^-]*\)\.)', re.MULTILINE|re.DOTALL)
44+
if self.open_hrl_popup(view, re_define, filepath, point):
45+
return
46+
47+
def open_function_popup(self, view, point, module, function, all_function):
48+
key = (module, function)
49+
if key in all_function:
50+
col = 0
51+
row = 1
52+
html_content = '<div>Definitions:</div>'
53+
for (name, path, row) in all_function[key]:
54+
add_str = '<div>{0}:{1} <a href="{2}:{3}:0">{2}:{3}</a></div>'.format(module, name, path, row)
55+
html_content += add_str
56+
col = max(len(add_str), col)
57+
row += 1
58+
59+
view.show_popup(html_content, max_height = self.get_height(row), max_width = self.get_width(col),
60+
flags = sublime.HIDE_ON_MOUSE_MOVE_AWAY, location = point,
61+
on_navigate = self.on_navigate_cb)
62+
return True
63+
return False
64+
65+
def build_module_position(self, view, module, filepath):
66+
row_id = 1
67+
cur_view_position = {}
68+
all_cur_view_fun = []
69+
code = self.get_view_code(view)
70+
for line in code.split('\n'):
71+
funhead = self.re_dict['funline'].search(line)
72+
if funhead is not None:
73+
fun_name = funhead.group(1)
74+
param_str = funhead.group(2)
75+
param_list = self.format_param(param_str)
76+
key = (module, fun_name)
77+
param_len = len(param_list)
78+
format_fun_name = '{0}/{1}'.format(fun_name, param_len)
79+
80+
if (key, param_len) not in all_cur_view_fun:
81+
if key not in cur_view_position:
82+
cur_view_position[key] = []
83+
cur_view_position[key].append((format_fun_name, filepath, row_id))
84+
all_cur_view_fun.append((key, param_len))
85+
row_id += 1
86+
return cur_view_position
87+
88+
def open_hrl_popup(self, view, re_define, filepath, point):
89+
with open(filepath, encoding = 'UTF-8', errors='ignore') as fd:
90+
content = fd.read()
91+
code = re.sub(self.re_dict['comment'], '\n', content)
92+
93+
re_newline = re.compile(r'\n')
94+
for m in re_define.finditer(code):
95+
start_line = len(re_newline.findall(code, 0, m.start(1))) + 1
96+
html_content = '<div>Definitions:</div>'
97+
record_define = '<div>{0}</div>'.format(m.group(1))
98+
reocrd_address = '<div><a href="{0}:{1}:0">{0}:{1}</a></div>'.format(filepath, start_line)
99+
record_define_len = len(record_define)
100+
if record_define_len > self.max_col:
101+
html_content += reocrd_address
102+
col = len(reocrd_address)
103+
else:
104+
html_content += record_define + reocrd_address
105+
col = max(record_define_len, len(reocrd_address))
106+
107+
view.show_popup(html_content, max_height = self.get_height(3), max_width = self.get_width(col),
108+
flags = sublime.HIDE_ON_MOUSE_MOVE_AWAY, location = point,
109+
on_navigate = self.on_navigate_cb)
110+
return True
111+
112+
(directory, filename) = os.path.split(filepath)
113+
for m in self.re_dict['take_include'].finditer(code):
114+
hrl_name = m.group(1)
115+
hrl_path = os.path.normpath(os.path.join(directory, hrl_name))
116+
if self.open_hrl_popup(view, re_define, hrl_path, point):
117+
return True
118+
119+
return False
120+
121+
def on_navigate_cb(self, address):
122+
sublime.active_window().open_file(address, sublime.ENCODED_POSITION)
123+
124+
def get_view_code(self, view):
125+
view_region = sublime.Region(0, view.size())
126+
code = view.substr(view_region)
127+
return code
128+
129+
def get_height(self, row):
130+
return row * 240
131+
132+
def get_width(self, col):
133+
return col * 20

util/settings.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@ def get_erl_lib_dir():
2929
'special_param': re.compile(r'(?:\{.*\})|(?:<<.*>>)|(?:\[.*\])'),
3030
'=' : re.compile(r'\s*=\s*\w+'),
3131
'take_mf' : re.compile(r'(\w+)\s*:\s*(\w+)\s*\('),
32-
'take_fun' : re.compile(r'(\w+)\s*\(')
32+
'take_fun' : re.compile(r'(\w+)\s*\('),
33+
'take_record' : re.compile(r'\#\s*(\w+)\s*[\{|.]'),
34+
'take_define' : re.compile(r'\?\s*(\w+)'),
35+
'take_include' : re.compile(r'-include\("([^\)]*)"\)')
3336
},
3437
'package_name' : 'Erl-AutoCompletion'
3538
}

0 commit comments

Comments
 (0)