Skip to content

Commit

Permalink
Better Goto Definition
Browse files Browse the repository at this point in the history
It's now possible to navigate deeper and you can now also define a new
start file to return to.
  • Loading branch information
Zinggi committed Feb 17, 2013
1 parent d891e58 commit f1c53ab
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 81 deletions.
5 changes: 3 additions & 2 deletions Context.sublime-menu
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
[
{ "caption": "-" },
{
"caption": "UnrealScript Goto Definition",
"command": "unreal_goto_definition"
"caption": "UnrealScript Goto Definition (new start point, alt + LMB)",
"command": "unreal_goto_definition",
"args": {"b_new_start_point": true}
},
{ "caption": "-" }
]
3 changes: 2 additions & 1 deletion Default (Linux).sublime-keymap
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[
{
"keys": ["f10"], "command": "unreal_goto_definition"
"keys": ["f10"], "command": "unreal_goto_definition",
"args": {"b_new_start_point": false}
},
// Add event right before auto-completion to insert a dynamic snippet
{
Expand Down
3 changes: 2 additions & 1 deletion Default (OSX).sublime-keymap
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
[
{
"keys": ["f10"], "command": "unreal_goto_definition"
"keys": ["f10"], "command": "unreal_goto_definition",
"args": {"b_new_start_point": false}
},
// Add event right before auto-completion to insert a dynamic snippet
{
Expand Down
4 changes: 3 additions & 1 deletion Default (Windows).sublime-keymap
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[
{
"keys": ["f10"], "command": "unreal_goto_definition"
"keys": ["f10"],
"command": "unreal_goto_definition",
"args": {"b_new_start_point": false}
},
// Add event right before auto-completion to insert a dynamic snippet
{
Expand Down
3 changes: 2 additions & 1 deletion Default.sublime-mousemap
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"modifiers": ["alt"],
"press_command": "drag_select",
"press_args": {"by": "words"},
"command": "unreal_goto_definition"
"command": "unreal_goto_definition",
"args": {"b_new_start_point": true}
}
]
2 changes: 1 addition & 1 deletion Main.sublime-menu
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[
{ "caption": "-" },
{
"caption": "UnrealScript Goto Declaration", "id": "unreal_script_goto_declaration", "command": "unreal_goto_definition"
"caption": "UnrealScript Goto Declaration (or back)", "id": "unreal_script_goto_declaration", "command": "unreal_goto_definition", "args": {"b_new_start_point": false}
}
]
},
Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,29 +35,32 @@ Planned
* **Object-oriented goto declaration command**
* Goto declaration currently doesn't work on statements such as Controller.bIsPlayer
* **Add support for enumerations, structs and CONST**
* **Better build system**
* **Your suggestion here...**


Installation
------------
**For now use Manual method, because it's not yet included in Package Control**

**Very easy with [Package Control](http://wbond.net/sublime_packages/package_control "http://wbond.net/sublime_packages/package_control") right inside Sublime Text 2 (Package Control needs to be installed):**
**Very easy with [Package Control](http://wbond.net/sublime_packages/package_control "http://wbond.net/sublime_packages/package_control") right inside Sublime Text 2 (Package Control needs to be installed):**

1. Ctrl + shift + P
2. Search for "inst", hit enter
3. Search for "UnrealScriptIDE", hit enter
1. Ctrl + shift + P
2. Search for "inst", hit enter
3. Search for "UnrealScriptIDE", hit enter

**Manually (not recommended):**

1. Clone or download this package
2. Put it into your Packages directory (find using 'Preferences' -> 'Browse Packages...')

**please note:**
UnrealScriptIDE will only work properly if you add the src folder as a project.
----------------
UnrealScriptIDE will **only** work properly if you add the **Src** folder as a project.
To do so, goto 'Project' -> 'Add Folder To Project...' -> add the Src folder (/UDK/UDK-201*-**/Development/Src/)

------------
*For syntax highlighting, various snippets and integrated build system I'd recommend downloading the package "UnrealScript"
*For syntax highlighting, various snippets and a not working build system I'd recommend downloading the package "UnrealScript"
by Michael Alexander with "Package Control" or manually from [here](https://github.com/beefsack/unrealscript-sublime "https://github.com/beefsack/unrealscript-sublime") to truly make this an UnrealScript IDE*

My auto-complete settings
Expand Down
173 changes: 105 additions & 68 deletions UnrealScriptAutocomplete.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
#-----------------------------------------------------------------------------------
# UnrealScript Auto-complete Plug-in
#-----------------------------------------------------------------------------------
# This plug in displays UnrealScript Functions with parameters.
# This plug-in displays UnrealScript Functions with parameters.
# It searches in all parent classes of the current class.
# Uses context sensitive completions.
#
# TODO:
# -autocomplete classes after extends
# -save all classes (name, file_name, description) when first UnrealScript file is opened:
# store in _classes, don't clear them
# optimize search file by using the new _classes
# create a class for _classes
# use classes in autocompletion suggestions and display description
#
# -load auto complete suggestions from current object before a '.'
# -same for goto definition
#
Expand Down Expand Up @@ -50,8 +56,10 @@ def hide_panel(self, view):
print "'NoneType' object has no attribute 'run_command'"


# opens the definition of the current selected word.
# if b_new_start_point is true, the current cursor position will be stored.
class UnrealGotoDefinitionCommand(sublime_plugin.TextCommand, HelperObject):
def run(self, edit):
def run(self, edit, b_new_start_point=False):
active_file = self.view.file_name()
if is_unrealscript_file(active_file):
region_word = self.view.word(self.view.sel()[0])
Expand All @@ -60,51 +68,52 @@ def run(self, edit):

global last_location, current_location

if last_location == None:
# Save current position so we can return to it
row, col = self.view.rowcol(self.view.sel()[0].begin())
last_location = "%s:%d" % (active_file, row + 1)
# if no word is selected or the cursor is at the beginning of the line, return to last location
row, col = self.view.rowcol(self.view.sel()[0].begin())
if last_location != None and (word.strip() == "" or col == 0):
if current_location == active_file:
window.open_file(last_location, sublime.ENCODED_POSITION)
window.open_file(last_location, sublime.ENCODED_POSITION)
last_location = None
return

# search the current word in functions, variables and classes and if found, open the file.
_class = self.get_class(word)
if _class != None:
self.open_file(_class[1], 1, b_new_start_point)
return

function = self.get_function(word)
if function != None:
if os.path.exists(function.file_name()):
window.open_file(function.file_name() + ':' + str(function.line_number()) + ':0', sublime.ENCODED_POSITION | sublime.TRANSIENT) # somehow calling this twice fixes a bug that makes sublime crash...
window.open_file(function.file_name() + ':' + str(function.line_number()) + ':0', sublime.ENCODED_POSITION | sublime.TRANSIENT)
current_location = function.file_name()
else:
print function.file_name() + 'does not exist'
self.open_file(function.file_name(), function.line_number(), b_new_start_point)
return

variable = self.get_variable(word)
if variable != None:
if os.path.exists(variable.file_name()):
window.open_file(variable.file_name() + ':' + str(variable.line_number()) + ':0', sublime.ENCODED_POSITION | sublime.TRANSIENT)
window.open_file(variable.file_name() + ':' + str(variable.line_number()) + ':0', sublime.ENCODED_POSITION | sublime.TRANSIENT)
current_location = variable.file_name()
else:
print variable.file_name() + 'does not exist'
return
_class = self.get_class(word)
if _class != None:
if os.path.exists(_class[1]):
window.open_file(_class[1] + ":1:0", sublime.ENCODED_POSITION | sublime.TRANSIENT)
window.open_file(_class[1] + ":1:0", sublime.ENCODED_POSITION | sublime.TRANSIENT)
current_location = _class[1]
else:
print _class[1] + 'does not exist'
self.open_file(variable.file_name(), variable.line_number(), b_new_start_point)
return

# if the word wasn't found:
print_to_panel(self.view, word + " not found")
sublime.set_timeout(lambda: self.hide_panel(self.view), OPEN_TIME * 1000)

def open_file(self, file_name, line_number=1, b_new_start_point=False):
global last_location, current_location
active_file = self.view.file_name()
window = sublime.active_window()

if last_location == None or b_new_start_point:
# Save current position so we can return to it
row, col = self.view.rowcol(self.view.sel()[0].begin())
if last_location != None and (word.strip() == "" or col == 0):
if current_location == active_file:
window.open_file(last_location, sublime.ENCODED_POSITION)
window.open_file(last_location, sublime.ENCODED_POSITION)
last_location = None
return
last_location = None
else:
print_to_panel(self.view, word + " not found")
last_location = "%s:%d" % (active_file, row + 1)

sublime.set_timeout(lambda: self.hide_panel(self.view), OPEN_TIME * 1000)
if os.path.exists(file_name):
# somehow calling this twice fixes a bug that makes sublime crash...
window.open_file(file_name + ':' + str(line_number) + ':0', sublime.ENCODED_POSITION | sublime.TRANSIENT)
window.open_file(file_name + ':' + str(line_number) + ':0', sublime.ENCODED_POSITION | sublime.TRANSIENT)
current_location = file_name
else:
print file_name + 'does not exist'

def get_function(self, name):
global functions_reference
Expand All @@ -128,6 +137,10 @@ def get_class(self, name):
return _class
return None

#================================================================================
#================Autocompletion==================================================
#================================================================================


# base class for adding new auto-complete suggestions
class UnrealScriptAutocomplete:
Expand All @@ -137,7 +150,7 @@ class UnrealScriptAutocomplete:
_variables = []
# store all parent classes and information. (class_name, class_path, description)
_classes = []
# stores all functions to use on a file
# stores all functions, variables and classes to use on a file
_functions_for_file = []

def clear(self):
Expand All @@ -156,32 +169,47 @@ def add_var(self, var_modifiers, var_name, comment, line_number, file_name, desc

def get_function(self, name):
for function in self._functions:
if function.function_name() == name:
if function.function_name().lower() == name.lower():
return function
return None

def get_variable(self, name):
for variable in self._variables:
if variable.name() == name:
if variable.name().lower() == name.lower():
return variable
return None

# (class_name, class_path, description)
def get_class(self, name):
for _class in self._class:
if _class[0].lower() == name.lower():
return _class
return None

def save_functions_to_list(self, filename):
names = [x[1] for x in self._functions_for_file]

if filename not in names:
self._functions_for_file.append((self._functions, filename, self._variables, self._classes))

def get_autocomplete_list(self, word, b_only_var=False):
def get_autocomplete_list(self, word, b_only_var=False, b_only_classes=False):
autocomplete_list = []
for variable in self._variables:
if word.lower() in variable.name().lower():
autocomplete_list.append((variable.name(), variable.name()))
if not b_only_var:
for function in self._functions:
if word.lower() in function.function_name().lower():
function_str = function.function_name() + '\t(' + function.arguments() + ')' # add arguments
autocomplete_list.append((function_str, function.function_name()))

# filter relevant items:
for _class in self._classes:
if word.lower() in _class[0].lower():
autocomplete_list.append((_class[0], _class[0]))

if not b_only_classes:
for variable in self._variables:
if word.lower() in variable.name().lower():
autocomplete_list.append((variable.name(), variable.name()))

if not b_only_var:
for function in self._functions:
if word.lower() in function.function_name().lower():
function_str = function.function_name() + '\t(' + function.arguments() + ')' # add arguments
autocomplete_list.append((function_str, function.function_name()))

return autocomplete_list

Expand Down Expand Up @@ -211,7 +239,7 @@ def on_activated(self, view):
if view.file_name() != None and is_unrealscript_file(view.file_name()):
open_folder_arr = view.window().folders() # Gets all opened folders in the Sublime Text editor.

if view.file_name() not in self._filenames: # if the wasn't parsed before
if view.file_name() not in self._filenames: # if the file wasn't parsed before
self._filenames.append(view.file_name())
self.add_thread(view.file_name(), open_folder_arr) # create a new thread to search for relevant functions for the active file

Expand All @@ -237,18 +265,28 @@ def on_query_completions(self, view, prefix, locations):
completions = []

if current_file != None and is_unrealscript_file(current_file):
# check if in at class declaration:
line = view.line(view.sel()[0])
line_contents = view.substr(line)
if "class" in line_contents and "extends" in line_contents:
print "get classes"
return self.get_autocomplete_list(prefix, False, True)

# if is in defaultproperties, only get variables:
line_number = 1000000
defaultproperties_region = view.find('defaultproperties', 0, sublime.IGNORECASE)
if defaultproperties_region:
(line_number, col) = view.rowcol(defaultproperties_region.a)
else:
# no defaultproperties found
return self.get_autocomplete_list(prefix)

(row, col) = view.rowcol(view.sel()[0].begin())
if row > line_number:
# below defaultproperties
return self.get_autocomplete_list(prefix, True)
else:
# above defaultproperties
return self.get_autocomplete_list(prefix)

return (completions, sublime.INHIBIT_EXPLICIT_COMPLETIONS)
Expand Down Expand Up @@ -328,6 +366,7 @@ def clear_all(self):


# create a new thread for scanning one file, as this may take a while.
# parses one file and creates a new thread for the parent class
class FunctionsCollectorThread(threading.Thread):
def __init__(self, collector, filename, timeout_seconds, open_folder_arr):
self.collector = collector
Expand All @@ -340,25 +379,23 @@ def run(self): # gets called when the thread is created
if self.filename != None:
self.save_functions(self.filename) # parse current file

file_lines = open(self.filename, 'rU')
description = ""

for line in file_lines:
description += line
classline = re.match(r'(class\b.+\bextends )(\b.+\b)', line.lower()) # get class declaration line of current file
if classline != None:
parent_class_name = classline.group(2) # get parent class

# search open folders for parent class file
for folder in self.open_folder_arr:
parent_file = self.search_file(folder, parent_class_name)
self.collector._classes.append((parent_class_name, parent_file, description))
if parent_file != None:
self.save_functions(parent_file)
self.collector.add_thread(parent_file, self.open_folder_arr) # create a new thread to do the same stuff on the parent_file
else:
print "parent file not found in: ", folder
break
with open(self.filename, 'rU') as file_lines:
for line in file_lines:
description += line
classline = re.match(r'(class\b.+\bextends )(\b.+\b)', line.lower()) # get class declaration line of current file
if classline != None:
parent_class_name = classline.group(2) # get parent class

# search open folders for parent class file
for folder in self.open_folder_arr:
parent_file = self.search_file(folder, parent_class_name)
self.collector._classes.append((parent_class_name, parent_file, description))
if parent_file != None:
self.collector.add_thread(parent_file, self.open_folder_arr) # create a new thread to do the same stuff on the parent_file
else:
print "parent file not found in: ", folder
break

self.stop()

Expand Down

0 comments on commit f1c53ab

Please sign in to comment.