You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Traceback (most recent call last):
File "E:\Programs\Sublime Text 3\sublime_plugin.py", line 610, in on_selection_modified_async
callback.on_selection_modified_async(v)
File "C:\Users\OciXCrom\AppData\Roaming\Sublime Text 3\Packages\AmxxEditor\AmxxEditor.py", line 280, in on_selection_modified_async
self.inteltip_function(view, region)
File "C:\Users\OciXCrom\AppData\Roaming\Sublime Text 3\Packages\AmxxEditor\AmxxEditor.py", line 331, in inteltip_function
node = nodes[file_name]
KeyError: 'E:\\Servers\\iPlay.bg Jailbreak\\cstrike\\addons\\amxmodx\\scripting\\jb_extreme.sma'
try:
if "include_path.pawn" in scope :
self.inteltip_include(view, region)
else :
self.inteltip_function(view, region)
except Exception as error:
print("on_selection_modified_async exception: %s" % error)
Exception in thread Thread-2:
Traceback (most recent call last):
File "./python3.3/threading.py", line 901, in _bootstrap_inner
File "C:\Users\OciXCrom\AppData\Roaming\Sublime Text 3\Packages\AmxxEditor\AmxxEditor.py", line 820, in run
self.process(file_name, view_buffer)
File "C:\Users\OciXCrom\AppData\Roaming\Sublime Text 3\Packages\AmxxEditor\AmxxEditor.py", line 831, in process
self.load_from_file(view_file_name, include, current_node, current_node, base_includes)
File "C:\Users\OciXCrom\AppData\Roaming\Sublime Text 3\Packages\AmxxEditor\AmxxEditor.py", line 878, in load_from_file
includes = includes_re.findall(f.read())
File "./python3.3/encodings/cp1252.py", line 23, in decode
UnicodeDecodeError: 'charmap' codec can't decode byte 0x8f in position 256: character maps to <undefined>
Older code used/fixed:
#!/usr/bin/env python3# -*- coding: UTF-8 -*-## Licensing## Copyright (C) 2013-2016 ppalex7 <https://github.com/ppalex7/SourcePawnCompletions># Copyright (C) 2016-2017 AMXX-Editor by Destro <https://forums.alliedmods.net/showthread.php?t=284385># Copyright (C) 2017-2018 Evandro Coan <https://github.com/evandrocoan/AmxxEditor>## Redistributions of source code must retain the above# copyright notice, this list of conditions and the# following disclaimer.## Redistributions in binary form must reproduce the above# copyright notice, this list of conditions and the following# disclaimer in the documentation and/or other materials# provided with the distribution.## Neither the name Evandro Coan nor the names of any# contributors may be used to endorse or promote products# derived from this software without specific prior written# permission.## This program is free software; you can redistribute it and/or modify it# under the terms of the GNU General Public License as published by the# Free Software Foundation; either version 3 of the License, or ( at# your option ) any later version.## This program is distributed in the hope that it will be useful, but# WITHOUT ANY WARRANTY; without even the implied warranty of# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU# General Public License for more details.## You should have received a copy of the GNU General Public License# along with this program. If not, see <http://www.gnu.org/licenses/>.importosimportreimportstringimportsysimportsublime, sublime_pluginimportwebbrowserimportdatetimeimporttimeimporturllib.requestfromcollectionsimportdefaultdict, OrderedDictfromqueueimport*fromthreadingimportTimer, Threadsys.path.append(os.path.dirname(__file__))
fromenum34importEnumimportwatchdog.eventsimportwatchdog.observersimportwatchdog.utilsfromwatchdog.utils.bricksimportOrderedSetQueuefromos.pathimportbasenameimportlogging# Enable editor debug messages: (bitwise)## 0 - Disabled debugging.# 1 - Errors messages.# 2 - Outputs when it starts a file parsing.# 4 - General messages.# 8 - Analyzer parser.# 16 - Autocomplete debugging.# 32 - Function parsing debugging.# 63 - All debugging levels at the same time.fromdebug_toolsimportgetLoggerlog=getLogger( 1, __name__ )
# log = getLogger( 1, __name__, file="amxxeditor.txt", mode='w' )EDITOR_VERSION="3.0_zz"CURRENT_PACKAGE_NAME=__package__g_is_package_loading=TrueclassFUNC_TYPES(Enum):
function=0public=1stock=2forward=3native=4# print( FUNC_TYPES(0) )g_constants_list=set()
g_inteltip_style=""g_enable_inteltip=Falseg_enable_buildversion=Falseg_delay_time=1.0g_include_dir=set()
g_add_paremeters=Falseg_new_file_syntax="Packages/%s/%sPawn.sublime-syntax"% (CURRENT_PACKAGE_NAME, CURRENT_PACKAGE_NAME)
g_word_autocomplete=Falseg_function_autocomplete=FalseprocessingSetQueue=OrderedSetQueue()
processingSetQueueSet=set()
nodes=dict()
file_observer=watchdog.observers.Observer()
includes_re=re.compile('^[\\s]*#include[\\s]+[<"]([^>"]+)[>"]', re.MULTILINE)
local_re=re.compile('\\.(sma|inc)$')
function_re=re.compile(r'^[\w_\d: ]*[\w_\d]\(')
defplugin_unloaded():
globalg_is_package_loadingg_is_package_loading=Truesettings=sublime.load_settings("%s.sublime-settings"%CURRENT_PACKAGE_NAME)
settings.clear_on_change(CURRENT_PACKAGE_NAME)
log.delete()
defplugin_loaded():
settings=sublime.load_settings("%s.sublime-settings"%CURRENT_PACKAGE_NAME)
install_build_systens("AmxxEditor.sh")
install_build_systens("AmxxEditor.bat")
install_setting_file("%s.sublime-settings"%CURRENT_PACKAGE_NAME)
install_setting_file("AmxxEditorConsole.sublime-settings")
# Fixes the settings dialog showing up when installing the package for the first timeglobalg_is_package_loadingg_is_package_loading=Truesublime.set_timeout( unlock_is_package_loading, 10000 )
on_settings_modified();
settings.add_on_change(CURRENT_PACKAGE_NAME, on_settings_modified)
defunlock_is_package_loading():
globalg_is_package_loadingg_is_package_loading=Falsedefinstall_build_systens(target_file_name):
target_folder=CURRENT_PACKAGE_NAMEtarget_file=os.path.join( sublime.packages_path(), "User", target_folder, target_file_name )
input_file_string=sublime.load_resource( "Packages/%s/%s"% ( CURRENT_PACKAGE_NAME, target_file_name ) )
target_directory=os.path.join( sublime.packages_path(), "User", target_folder )
attempt_to_install_file( target_directory, target_file, input_file_string )
definstall_setting_file( target_file_name ):
target_file=os.path.join( sublime.packages_path(), "User", target_file_name )
input_file_string=sublime.load_resource( "Packages/%s/%s"% ( CURRENT_PACKAGE_NAME, target_file_name ) )
target_directory=os.path.join( sublime.packages_path(), "User" )
attempt_to_install_file( target_directory, target_file, input_file_string )
defattempt_to_install_file( target_directory, target_file, input_file_string ):
ifnotos.path.exists( target_directory ):
os.makedirs( target_directory )
# How can I force Python's file.write() to use the same newline format in Windows as in Linux (“\r\n” vs. “\n”)?# https://stackoverflow.com/questions/9184107/how-can-i-force-pythons-file-write-to-use-the-same-newline-format-in-windows## TypeError: 'str' does not support the buffer interface# https://stackoverflow.com/questions/5471158/typeerror-str-does-not-support-the-buffer-interfaceifnotos.path.exists( target_file ):
text_file=open( target_file, "wb", errors="ignore" )
text_file.write( bytes(input_file_string, 'UTF-8') )
text_file.close()
defunload_handler() :
file_observer.stop()
process_thread.stop()
processingSetQueue.put(("", ""))
sublime.load_settings("%s.sublime-settings"%CURRENT_PACKAGE_NAME).clear_on_change(CURRENT_PACKAGE_NAME)
classNewAmxxIncludeCommand(sublime_plugin.WindowCommand):
defrun(self):
new_file("inc")
classNewAmxxPluginCommand(sublime_plugin.WindowCommand):
defrun(self):
new_file("sma")
defnew_file(file_type):
view=sublime.active_window().new_file()
view.set_name("untitled."+file_type)
plugin_template=sublime.load_resource("Packages/%s/default.%s"% (CURRENT_PACKAGE_NAME, file_type))
plugin_template=plugin_template.replace("\r", "")
view.run_command("insert_snippet", {"contents": plugin_template})
sublime.set_timeout_async( lambda: set_new_file_syntax( view ), 0 )
defset_new_file_syntax( view ):
view.set_syntax_file(g_new_file_syntax)
classAboutAmxxEditorCommand(sublime_plugin.WindowCommand):
defrun(self):
about="Sublime AmxxEditor v"+EDITOR_VERSION+" by Destro\n\n\n"about+="CREDITs:\n"about+="- Great:\n"about+=" ppalex7 (SourcePawn Completions)\n\n"about+="- Contributors:\n"about+=" sasske (white color scheme)\n"about+=" addons_zz (npp color scheme)\n"about+=" KliPPy (build version)\n"about+=" Mistrick (mistrick color scheme)\n"about+="\nhttps://amxmodx-es.com/showthread.php?tid=12316\n"sublime.message_dialog(about)
classAmxxBuildVerCommand(sublime_plugin.TextCommand):
defrun(self, edit):
region=self.view.find("^#define\s+(?:PLUGIN_)?VERSION\s+\".+\"", 0, sublime.IGNORECASE)
ifregion==None :
region=self.view.find("new\s+const\s+(?:PLUGIN_)?VERSION\s*\[\s*\]\s*=\s*\".+\"", 0, sublime.IGNORECASE)
ifregion==None :
returnline=self.view.substr(region)
result=re.match("(.*\"(?:v)?\d{1,2}\.\d{1,2}\.(?:\d{1,2}-)?)(\d+)(b(?:eta)?)?\"", line)
ifnotresult :
returnbuild=int(result.group(2))
build+=1beta=result.group(3)
ifnotbeta :
beta=""self.view.replace(edit, region, result.group(1) +str(build) +beta+'\"')
classAmxxEditor(sublime_plugin.EventListener):
def__init__(self) :
process_thread.start()
self.delay_queue=Nonefile_observer.start()
defon_window_command(self, window, cmd, args) :
ifcmd!="build" :
returnview=window.active_view()
ifnotis_amxmodx_file(view) ornotg_enable_buildversion :
returnview.run_command("amxx_build_ver")
defon_selection_modified_async(self, view) :
ifnotis_amxmodx_file(view) ornotg_enable_inteltip :
returnregion=view.sel()[0]
scope=view.scope_name(region.begin())
log(4, "(inteltip) scope_name: [%s]"%scope)
ifnot"support.function"inscopeandnot"include_path.pawn"inscopeorregion.size() >1 :
view.hide_popup()
view.add_regions("inteltip", [ ])
returntry:
if"include_path.pawn"inscope :
self.inteltip_include(view, region)
else :
self.inteltip_function(view, region)
exceptExceptionaserror:
print("on_selection_modified_async exception: %s"%error)
definteltip_include(self, view, region) :
location=view.word(region).end() +1line=view.substr(view.line(region))
include=includes_re.match(line).group(1)
file_name_view=view.file_name()
iffile_name_viewisNone:
returnelse:
( file_name, the_include_exists ) =get_file_name( file_name_view, include )
ifnotthe_include_exists :
returnlink_local=file_name+'#'ifnot'.'ininclude :
link_web=include+'#'include+=".inc"else :
link_web=Nonehtml='<style>'+g_inteltip_style+'</style>'html+='<div class="top">'html+='<a class="file" href="'+link_local+'">'+include+'</a>'iflink_web :
html+=' | <a class="file" href="'+link_web+'">WebAPI</a>'html+='</div><div class="bottom">'html+='<span class="func_type">Location:</span><br>'html+='<span class="func_name">'+file_name+'</span>'html+='</div>'view.show_popup(html, 0, location, max_width=700, on_navigate=self.on_navigate)
definteltip_function(self, view, region) :
file_name=view.file_name()
ifnotfile_name:
returnword_region=view.word(region)
location=word_region.end() +1search_func=view.substr(word_region)
doctset=set()
visited=set()
found=Nonenode=nodes[file_name]
self.generate_doctset_recur(node, doctset, visited)
forfuncindoctset :
ifsearch_func==func.function_name :
found=funciffound.function_type!=FUNC_TYPES.public :
breakiffound:
log(4, "param2: [%s]"%simple_escape(found.parameters))
filename=os.path.basename(found.file_name)
iffound.function_type :
iffound.return_type :
link_local=found.file_name+'#'+FUNC_TYPES(found.function_type).name+' '+found.return_type+':'+found.function_nameelse :
link_local=found.file_name+'#'+FUNC_TYPES(found.function_type).name+' '+found.function_namelink_web=filename.rsplit('.', 1)[0] +'#'+found.function_nameelse :
link_local=found.file_name+'#'+'^'+found.function_namelink_web=''log( 4, "link_local: %s", link_local )
html='<style>'+g_inteltip_style+'</style>'html+='<div class="top">'############################## TOPhtml+='<a class="file" href="'+link_local+'\\(">'+os.path.basename(found.file_name)+'</a>'iflink_web:
html+=' | <a class="file" href="'+link_web+'">WebAPI</a>'html+='</div><div class="bottom">'############################## BOTTOMhtml+='<span class="func_type">'+FUNC_TYPES(found.function_type).name \
+':</span> <span class="func_name">'+found.function_name+'</span>'html+='<br>'html+='<span class="params">Params:</span> <span class="params_definition">('+simple_escape(found.parameters) +')</span>'html+='<br>'iffound.return_type :
html+='<span class="return">Return:</span> <span class="return_type">'+found.return_type+'</span>'html+='</div>'############################## END# log( 1, "html: %s", html )view.show_popup(html, 0, location, max_width=700, on_navigate=self.on_navigate)
view.add_regions("inteltip", [ word_region ], "inteltip.pawn")
else:
view.hide_popup()
view.add_regions("inteltip", [ ])
defon_navigate(self, link) :
(file, search) =link.split('#')
if"."infile :
view=sublime.active_window().open_file(file);
defdo_position() :
ifview.is_loading():
sublime.set_timeout(do_position, 100)
else :
r=view.find(search, 0, sublime.IGNORECASE)
view.sel().clear()
view.sel().add(r)
view.show(r)
do_position()
else :
webbrowser.open_new_tab("http://www.amxmodx.org/api/"+file+"/"+search)
defon_activated_async(self, view) :
view_size=view.size()
log(4, "on_activated_async(2)")
log(4, "( on_activated_async ) view.match_selector(0, 'source.sma'): "+str( view.match_selector(0, 'source.sma') ))
# log(4, "( on_activated_async ) nodes: " + str( nodes ))log(4, "( on_activated_async ) view.substr(): \n" \
+view.substr( sublime.Region( 0, view_sizeifview_size<200else200 ) ))
ifnotis_amxmodx_file(view):
log(4, "( on_activated_async ) returning on` if not is_amxmodx_file(view)")
returnifnotview.file_name() innodes :
log(4, "( on_activated_async ) returning on` if not view.file_name() in nodes")
add_to_queue(view)
defon_modified_async(self, view) :
self.add_to_queue_delayed(view)
defon_post_save_async(self, view) :
self.add_to_queue_now(view)
defon_load_async(self, view) :
self.add_to_queue_now(view)
defadd_to_queue_now(self, view) :
ifnotis_amxmodx_file(view):
returnadd_to_queue(view)
defadd_to_queue_delayed(self, view) :
ifnotis_amxmodx_file(view):
returnifself.delay_queueisnotNone :
self.delay_queue.cancel()
ifg_delay_time>0.3:
self.delay_queue=Timer( float( g_delay_time ), add_to_queue_forward, [ view ] )
self.delay_queue.start()
defon_query_completions(self, view, prefix, locations):
""" This is a forward called by Sublime Text when it is about to show the use completions. See: https://www.sublimetext.com/docs/3/api_reference.html#sublime_plugin.ViewEventListener """view_file_name=view.file_name()
ifis_amxmodx_file(view):
# # # Autocompletion issue# # # https://github.com/evandrocoan/AmxxEditor/issues/9# # temporarily masking word_separators# # https://github.com/SublimeTextIssues/Core/issues/819# word_separators = view.settings().get("word_separators")# view.settings().set("word_separators", "")# sublime.set_timeout(lambda: view.settings().set("word_separators", word_separators), 0)ifview_file_nameisNone:
view_file_name=str( view.buffer_id() )
# Just in case it is not processed yetifnotview_file_nameinnodes:
log(4, "( on_query_completions ) Adding buffer id "+view_file_name+" in nodes")
add_to_queue_forward( view )
# The queue is not processed yet, so there is nothing to showifg_word_autocomplete:
log( 16, "(new buffer) Word autocomplete")
returnNoneelse:
log( 16, "(new buffer) Without word autocomplete")
return ( [], sublime.INHIBIT_WORD_COMPLETIONS )
ifg_word_autocomplete:
log( 16, "(Buffer) Word autocomplete + function")
returnself.generate_funcset( view_file_name, view, prefix, locations )
else:
log( 16, "(Buffer) Without word autocomplete + function")
return ( self.generate_funcset( view_file_name, view, prefix, locations ), sublime.INHIBIT_WORD_COMPLETIONS )
else:
ifg_word_autocomplete:
log( 16, "(File) Word autocomplete + function")
returnself.generate_funcset( view_file_name, view, prefix, locations )
else:
log( 16, "(File) Without word autocomplete + function")
return ( self.generate_funcset( view_file_name, view, prefix, locations ), sublime.INHIBIT_WORD_COMPLETIONS )
log( 16, "No completions")
returnNonedefgenerate_funcset( self, file_name, view, prefix, locations ) :
words_list= []
funcs_list= []
funcs_word_list= []
iffile_nameinnodes:
node=nodes[file_name]
visited=set()
ifnotview.match_selector(locations[0], 'string') :
self.generate_funcset_recur( node, visited, funcs_list, funcs_word_list )
ifg_word_autocomplete:
start_time=time.time()
iflen( locations ) >0:
view_words=view.extract_completions( prefix, locations[0] )
else:
view_words=view.extract_completions( prefix )
# This view goes first to prioritize matches close to cursor position.forwordinview_words:
# Remove the annoying `(` on the stringword=word.replace('$', '\\$').split('(')[0]
ifwordnotinfuncs_word_list:
words_list.append( ( word, word ) )
iftime.time() -start_time>0.05:
break# log( 16, "( generate_funcset ) funcs_list size: %d" % len( funcs_list ) )# log( 16, "( generate_funcset ) funcs_list items: " + str( sort_nicely( funcs_list ) ) )returnwords_list+funcs_listdefgenerate_funcset_recur( self, node, visited, funcs_list, funcs_word_list ) :
ifnodeinvisited :
returnvisited.add( node )
forchildinnode.children :
self.generate_funcset_recur( child, visited, funcs_list, funcs_word_list )
funcs_list.extend( node.funcs_list )
funcs_word_list.extend( node.words_list )
defgenerate_doctset_recur(self, node, doctset, visited) :
ifnodeinvisited :
returnvisited.add(node)
forchildinnode.children :
self.generate_doctset_recur(child, doctset, visited)
doctset.update(node.doct)
defis_amxmodx_file(view) :
returnview.match_selector(0, 'source.sma')
defon_settings_modified():
log(4, "on_settings_modified" )
globalg_enable_inteltipglobalg_new_file_syntaxglobalg_word_autocompleteglobalg_function_autocompletesettings=sublime.load_settings("%s.sublime-settings"%CURRENT_PACKAGE_NAME)
invalid=is_invalid_settings(settings)
ifinvalid:
ifnotg_is_package_loading:
sublime.message_dialog("AmxxEditor:\n\n"+invalid)
g_enable_inteltip=0return# check package pathpackages_path=os.path.join( sublime.packages_path(), CURRENT_PACKAGE_NAME )
ifnotos.path.isdir(packages_path) :
os.mkdir(packages_path)
# fix-pathfix_path(settings, 'include_directory')
# Get the set color schemepopup_color_scheme=settings.get('popup_color_scheme')
# popUp.CSSglobalg_inteltip_styleg_inteltip_style=sublime.load_resource("Packages/%s/%s-popup.css"% (CURRENT_PACKAGE_NAME, popup_color_scheme))
g_inteltip_style=g_inteltip_style.replace("\r", "") # fix win/linux newlines# cache settingglobalg_enable_buildversion, g_delay_time, g_add_paremetersg_enable_inteltip=settings.get('enable_inteltip', True)
g_enable_buildversion=settings.get('enable_buildversion', False)
g_word_autocomplete=settings.get('word_autocomplete', False)
g_function_autocomplete=settings.get('function_autocomplete', False)
g_new_file_syntax=settings.get('amxx_file_syntax', g_new_file_syntax)
log.debug_level=settings.get('debug_level', 1)
g_delay_time=settings.get('live_refresh_delay', 1.0)
g_add_paremeters=settings.get('add_function_parameters', False)
g_include_dir.clear()
include_directory=settings.get('include_directory', './include')
ifisinstance( include_directory, list ):
forpathininclude_directory:
real_path=os.path.realpath( path )
ifos.path.isdir( real_path ): g_include_dir.add( real_path )
else:
real_path=os.path.realpath( include_directory )
ifos.path.isdir( real_path ): g_include_dir.add( real_path )
file_observer.unschedule_all()
log(4, "( on_settings_modified ) debug_level: %d", log.debug_level)
log(4, "( on_settings_modified ) g_include_dir: %s", g_include_dir)
log(4, "( on_settings_modified ) g_add_paremeters: %s", g_add_paremeters)
fordirectorying_include_dir:
file_observer.schedule( file_event_handler, directory, True )
defis_invalid_settings(settings):
general_error="You are not set correctly settings for AmxxEditor.\n\n"setting_names= [ "include_directory", "popup_color_scheme", "amxx_file_syntax" ]
forsetting_nameinsetting_names:
result=general_settings_checker( settings, setting_name, general_error )
ifresult:
returnresultpath_prefix=""setting_name="include_directory"default_value="F:\\SteamCMD\\steamapps\\common\\Half-Life\\czero\\addons\\amxmodx\\scripting\\include"checker=lambdafile_path: os.path.exists( file_path )
result=path_settings_checker( settings, setting_name, default_value, path_prefix, checker )
ifresult:
returnresultpath_prefix=os.path.dirname( sublime.packages_path() )
setting_name="amxx_file_syntax"default_value=g_new_file_syntaxchecker=lambdafile_path: os.path.exists( file_path ) oris_inside_sublime_package( file_path )
result=path_settings_checker( settings, setting_name, default_value, path_prefix, checker )
ifresult:
returnresultdefgeneral_settings_checker(settings, setting_name, general_error):
setting_value=settings.get( setting_name )
ifsetting_valueisNone:
returngeneral_error+"Missing `%s` value."%setting_namedefpath_settings_checker(settings, setting_name, default_value, prefix_path, checker):
setting_value=settings.get( setting_name )
ifsetting_value!=default_value:
full_path=os.path.normpath( os.path.join( prefix_path, setting_value ) )
ifnotchecker( full_path ):
lines= \
[
"The setting `%s` is not configured correctly. The following path does not exists:\n\n"%setting_name,
"%s (%s)"% (setting_value, full_path),
"\n\nPlease, go to the following menu and fix the setting:\n\n""`AmxxEditor -> Configure AMXX-Autocompletion Settings`\n\n",
"`Preferences -> Packages Settings -> AmxxEditor -> Configure AMXX-Autocompletion Settings`",
]
text="".join( lines )
print( "\n"+text.replace( "\n\n", "\n" ) )
returntextdefis_inside_sublime_package(file_path):
try:
packages_start=file_path.find( "Packages" )
packages_relative_path=file_path[packages_start:].replace( "\\", "/" )
# log( 1, "is_inside_sublime_package, packages_relative_path: " + str( packages_relative_path ) )sublime.load_binary_resource( packages_relative_path )
returnTrueexceptIOError:
returnFalsedeffix_path(settings, key) :
org_path=settings.get(key)
iforg_pathis"${file_path}" :
returnpath=os.path.normpath(org_path)
ifos.path.isdir(path):
path+='/'settings.set(key, path)
defsort_nicely( words_set ):
""" Sort the given iterable in the way that humans expect. """convert=lambdatext: int(text) iftext.isdigit() elsetextalphanum_key=lambdakey: [ convert(c) forcinre.split('([0-9]+)', key[0]) ]
returnsorted( words_set, key=alphanum_key )
defadd_to_queue_forward(view) :
ifg_delay_time>0.3:
sublime.set_timeout_async( lambda: add_to_queue( view ), float( g_delay_time ) *1000.0 )
defadd_to_queue(view) :
""" The view can only be accessed from the main thread, so run the regex now and process the results later """log( 4, "( add_to_queue ) view.file_name(): %s", view.file_name() )
# When the view is not saved, we need to use its buffer id, instead of its file name.view_file_name=view.file_name()
ifview_file_nameisNone :
view_file_name=str( view.buffer_id() )
ifview_file_namenotinprocessingSetQueueSet:
processingSetQueueSet.add( view_file_name )
processingSetQueue.put( ( view_file_name, view.substr( sublime.Region( 0, view.size() ) ) ) )
include_directory=os.path.realpath( os.path.join( os.path.dirname( view_file_name ), "include" ) )
ifinclude_directorynoting_include_dir:
ifos.path.isdir( include_directory ):
g_include_dir.add( include_directory )
file_observer.schedule( file_event_handler, include_directory, True )
defadd_include_to_queue(file_name) :
iffile_namenotinprocessingSetQueueSet:
processingSetQueueSet.add( file_name )
processingSetQueue.put((file_name, None))
classIncludeFileEventHandler(watchdog.events.FileSystemEventHandler) :
def__init__(self) :
watchdog.events.FileSystemEventHandler.__init__(self)
defon_created(self, event) :
sublime.set_timeout(lambda: on_modified_main_thread(event.src_path), 0)
defon_modified(self, event) :
sublime.set_timeout(lambda: on_modified_main_thread(event.src_path), 0)
defon_deleted(self, event) :
sublime.set_timeout(lambda: on_deleted_main_thread(event.src_path), 0)
defon_modified_main_thread(file_path) :
ifnotis_active(file_path) :
add_include_to_queue(file_path)
defon_deleted_main_thread(file_path) :
ifis_active(file_path) :
returnnode=nodes.get(file_path)
ifnodeisNone :
returnnode.remove_all_children_and_funcs()
defis_active(file_name) :
returnsublime.active_window().active_view().file_name() ==file_nameclassProcessQueueThread(watchdog.utils.DaemonThread) :
defrun(self) :
whileself.should_keep_running() :
(file_name, view_buffer) =processingSetQueue.get()
try:
processingSetQueueSet.remove( file_name )
except:
pass# When the `view_buffer` is None, it means we are processing a file on the disk, instead# of a file on an Sublime Text View (its text buffer).ifview_bufferisNone :
self.process_existing_include(file_name)
else :
self.process(file_name, view_buffer)
defprocess(self, view_file_name, view_buffer) :
base_includes=set()
(current_node, node_added) =get_or_add_node(view_file_name)
# Here we parse the text file to know which modules it is including.includes=includes_re.findall(view_buffer)
# Now for each module it is including we load that include file to the autocomplete list.forincludeinincludes:
self.load_from_file(view_file_name, include, current_node, current_node, base_includes)
# For each module it was loaded but it not present on the current file we just switched,# we remove that include file to the autocomplete list.forremoved_nodeincurrent_node.children.difference(base_includes) :
current_node.remove_child(removed_node)
# To process the current file functions for autocompleteprocess_buffer(view_buffer, current_node)
defprocess_existing_include(self, file_name) :
current_node=nodes.get(file_name)
ifcurrent_nodeisNoneornotos.path.exists( file_name ):
returnbase_includes=set()
withopen(file_name, 'r', errors="ignore") asf :
log(2, "(analyzer) Processing Include File %s"%file_name)
includes=includes_re.findall(f.read())
forincludeinincludes:
self.load_from_file(file_name, include, current_node, current_node, base_includes)
forremoved_nodeincurrent_node.children.difference(base_includes) :
current_node.remove_child(removed_node)
process_include_file(current_node)
defload_from_file(self, view_file_name, base_file_name, parent_node, base_node, base_includes) :
(file_name, exists) =get_file_name(view_file_name, base_file_name)
ifnotexists :
log(1, "(analyzer) Include File Not Found: %s"%base_file_name)
return
(node, node_added) =get_or_add_node(file_name)
parent_node.add_child(node)
ifparent_node==base_node :
base_includes.add(node)
ifnotnode_added :
returnwithopen(file_name, 'r', errors="ignore") asf :
log(2, "(analyzer) Processing Include File %s"%file_name)
includes=includes_re.findall(f.read())
forincludeinincludes :
self.load_from_file(view_file_name, include, node, base_node, base_includes)
process_include_file(node)
defget_file_name(view_file_name, base_file_name) :
log(4, "g_include_dir: %s", g_include_dir)
path_exists=False# True, if `base_file_name` is a include file name, instead of full file pathiflocal_re.search(base_file_name) ==None:
fordirectorying_include_dir:
file_name=os.path.join(directory, base_file_name+'.inc')
ifos.path.exists(file_name):
path_exists=Truebreakelse:
file_name=os.path.join(os.path.dirname(view_file_name), base_file_name)
path_exists=os.path.exists(file_name)
return (file_name, path_exists)
defget_or_add_node(file_name) :
""" Here if `file_name` is a buffer id as a string, I just check if the buffer exists. However if it is a file name, I need to check if its a buffer id is present here, and if so, I must to remove it and create a new node with the file name. This is necessary because the file could be just create, parsed and then saved. Therefore after did so, we need to keep reusing its buffer. But as it is saved we are using its file name instead of its buffer id, then we need to remove the buffer id in order to avoid duplicated entries. Though I am not implementing this here to save time and performance """node=nodes.get(file_name)
ifnodeisNone :
node=Node(file_name)
nodes[file_name] =nodereturn (node, True)
return (node, False)
# ============= NEW CODE ------------------------------------------------------------------------------------------------------------classTooltipDocumentation(object):
def__init__(self, function_name, parameters, file_name, function_type, return_type):
""" For `function_type` see FUNC_TYPES. """self.function_name=function_nameself.parameters=parametersself.file_name=file_nameself.function_type=function_typeself.return_type=return_typeclassNode(object):
def__init__(self, file_name) :
self.file_name=file_nameself.doct=set()
self.children=set()
self.parents=set()
# They are list to keep orderingself.funcs_list= []
self.words_list= []
self.words_set=set()
try:
float(file_name)
self.isFromBufferOnly=TrueexceptValueError:
self.isFromBufferOnly=Falsedefadd_child(self, node) :
self.children.add(node)
node.parents.add(self)
defremove_child(self, node) :
self.children.remove(node)
node.parents.remove(self)
iflen(node.parents) <=0 :
nodes.pop(node.file_name)
defremove_all_children_and_funcs(self) :
forchildinself.children :
self.remove_child(node)
self.doct.clear()
self.funcs_list.clear()
self.words_list.clear()
classTextReader(object):
def__init__(self, text):
self.text=text.splitlines()
self.position=-1defreadline(self) :
self.position+=1ifself.position<len(self.text) :
retval=self.text[self.position]
ifretval=='' :
return'\n'else :
returnretvalelse :
return''classPawnParse(object):
def__init__(self) :
self.node=Noneself.isTheCurrentFile=Falseself.save_const_timer=Noneself.constants_count=0defstart( self, pFile, node, isTheCurrentFile=False ) :
""" When the buffer is not None, it is always the current file. """log(8, "(analyzer) CODE PARSE Start [%s]"%node.file_name)
self.isTheCurrentFile=isTheCurrentFileself.file=pFileself.file_name=os.path.basename(node.file_name)
self.node=nodeself.found_comment=Falseself.found_enum=Falseself.is_to_skip_brace=Falseself.enum_contents=''self.brace_level=0self.restore_buffer=Noneself.is_to_skip_next_line=Falseself.if_define_brace_level=0self.else_defined_brace_level=0self.if_define_level=0self.else_define_level=0self.is_on_if_define= []
self.is_on_else_define= []
self.node.doct.clear()
self.node.funcs_list.clear()
self.node.words_list.clear()
self.node.words_set.clear()
self.start_parse()
ifself.constants_count!=len(g_constants_list) :
ifself.save_const_timer :
self.save_const_timer.cancel()
self.save_const_timer=Timer(4.0, self.save_constants)
self.save_const_timer.start()
log(8, "(analyzer) CODE PARSE End [%s]"%node.file_name)
defsave_constants(self) :
self.save_const_timer=Noneself.constants_count=len(g_constants_list)
windows=sublime.windows()
# If you have a project within 10000 files, each time this is updated, will for sublime to# process again all the files. Therefore only allow this on project with no files to index.## If someone is calling this, it means there are some windows with a AMXX file open. Therefore# we do not care to check whether that window has a project or not and there will always be# constants to save.forwindowinwindows:
# log(4, "(save_constants) window.id(): " + str( window.id() ) )# log(4, "(save_constants) window.folders(): " + str( window.folders() ) )# log(4, "(save_constants) window.project_file_name(): " + str( window.project_file_name() ) )iflen( window.folders() ) >0:
log( 4, "(save_constants) Not saving this time." )
returnconstants="___test"forconsting_constants_list :
constants+="|"+constsyntax="%YAML 1.2\n---\nscope: source.sma\nhidden: true\ncontexts:\n main:\n - match: \\b(" \
+constants+")\\b\s*(?!\()\n scope: constant.vars.pawn\n\n"file_name=os.path.join(sublime.packages_path(), CURRENT_PACKAGE_NAME, "AmxxEditorConsts.sublime-syntax")
f=open(file_name, 'w', errors="ignore")
f.write(syntax)
f.close()
log(8, "(analyzer) call save_constants()")
defread_line(self) :
ifself.restore_buffer :
line=self.restore_bufferself.restore_buffer=Noneelse :
line=self.file.readline()
iflen(line) >0 :
returnlineelse :
returnNonedefread_string(self, current_line) :
current_line=current_line.replace('\t', ' ').strip()
while' 'incurrent_line :
current_line=current_line.replace(' ', ' ')
current_line=current_line.lstrip()
result=''i=0# log( 1, str( current_line ) )buffer_length=len(current_line)
whilei<buffer_length :
ifcurrent_line[i] =='/'andi+1<len(current_line):
ifcurrent_line[i+1] =='/' :
self.brace_level+=result.count('{') -result.count('}')
returnresultelifcurrent_line[i+1] =='*' :
self.found_comment=Truei+=1elifnotself.found_comment :
result+='/'elifself.found_comment :
ifcurrent_line[i] =='*'andi+1<len(current_line) andcurrent_line[i+1] =='/' :
self.found_comment=Falsei+=1elifnot (i>0andcurrent_line[i] ==' 'andcurrent_line[i-1] ==' '):
result+=current_line[i]
i+=1self.brace_level+=result.count('{') -result.count('}')
returnresultdefskip_function_block(self, current_line) :
inChar=FalseinString=Falsenum_brace=0current_line=current_line+' 'self.is_to_skip_brace=Falsewhilecurrent_lineisnotNoneandcurrent_line.isspace() :
current_line=self.read_line()
whilecurrent_lineisnotNone :
# log( 32, "skip_function_block: " + current_line )i=0pos=0lastChar=''penultimateChar=''forcincurrent_line :
i+=1ifnotinStringandnotinCharandlastChar=='*'andc=='/' :
self.found_comment=FalseifnotinStringandnotinCharandself.found_comment:
penultimateChar=lastCharlastChar=ccontinueifnotinStringandnotinCharandlastChar=='/'andc=='*' :
self.found_comment=TruepenultimateChar=lastCharlastChar=ccontinueifnotinStringandnotinCharandc=='/'andlastChar=='/' :
breakifc=='"' :
ifinStringandlastChar!='^' :
inString=Falseelse :
inString=TrueifnotinStringandc=='\'' :
ifinCharandlastChar!='^' :
inChar=Falseelse :
inChar=True# This is hard stuff. We need to fix the parsing for the following problem:## public on_damage(id)# {# #if defined DAMAGE_RECIEVED# if ( is_user_connected(id) && is_user_connected(attacker) )# {# #else# if ( is_user_connected(attacker) )# {# #endif# }# return PLUGIN_CONTINUE# }# public death_hook()# {# {# new kuid = get_user_userid(killer)# }# }## Above here we may notice, there are 2 braces opening but only one brace close.# Therefore, we will skip the rest of the source code if we do not handle the braces# definitions between the `#if` and `#else` macro clauses.## To keep track about where we are, we need to keep track about how much braces# levels are being opened and closed using the variables `self.if_define_brace_level`# and `self.else_defined_brace_level`. And finally at the end of it all on the `#endif`,# we update the `num_brace` with the correct brace level.#ifnotinStringandnotinChar :
# Flags when we enter and leave the `#if ... #else ... #endif` blocksifpenultimateChar=='#':
# Cares of `#if`iflastChar=='i'andc=='f':
++self.if_define_levelself.is_on_if_define.append( True )
# Cares of `#else` and `#end`eliflastChar=='e':
ifc=='l':
++self.else_define_levelself.is_on_if_define.append( False )
self.is_on_else_define.append( True )
elifc=='n':
# Decrement the `#else` level, only if it existsiflen( self.is_on_if_define ) >0:
ifnotself.is_on_if_define[ -1 ]:
self.is_on_if_define.pop()
iflen( self.is_on_else_define ) >0:
--self.else_define_levelself.is_on_else_define.pop()
iflen( self.is_on_if_define ) >0:
--self.if_define_levelself.is_on_if_define.pop()
# If there are unclosed levels on the preprocessor, fix the `num_brace` levelextra_levels=max( self.else_defined_brace_level, self.if_define_brace_level )
num_brace-=extra_levels# Both must to be equals, so just reset their levels.self.if_define_brace_level-=extra_levelsself.else_defined_brace_level-=extra_levels# Flags when we enter and leave the braces `{ ... }` blocksifc=='{':
num_brace+=1self.is_to_skip_brace=Trueiflen( self.is_on_if_define ) >0:
ifself.is_on_if_define[ -1 ] :
self.if_define_brace_level+=1else:
self.else_defined_brace_level+=1elifc=='}':
pos=inum_brace-=1iflen( self.is_on_if_define ) >0:
ifself.is_on_if_define[ -1 ] :
self.if_define_brace_level-=1else:
self.else_defined_brace_level-=1penultimateChar=lastCharlastChar=c# log( 32, "num_brace: %d" % num_brace )# log( 32, "if_define_brace_level: %d" % self.if_define_brace_level )# log( 32, "else_defined_brace_level: %d" % self.else_defined_brace_level )# log( 32, "is_on_if_define: " + str( self.is_on_if_define ) )# log( 32, "is_on_else_define: " + str( self.is_on_else_define ) )# log( 32, "" )ifnum_brace==0 :
self.restore_buffer=current_line[pos:]
returncurrent_line=self.read_line()
defis_valid_name(self, name) :
ifnotnameornotname[0].isalpha() andname[0] !='_' :
returnFalsereturnre.match('^[\w_]+$', name) isnotNonedefadd_constant(self, name) :
fixname=re.search('(\\w*)', name)
iffixname :
name=fixname.group(1)
g_constants_list.add(name)
defadd_enum(self, current_line) :
current_line=current_line.strip()
ifcurrent_line=='' :
returnsplit=current_line.split('[')
self.add_constant(split[0])
self.add_general_autocomplete(current_line, 'enum', split[0])
log(8, "(analyzer) parse_enum add: [%s] -> [%s]"% (current_line, split[0]))
defadd_general_autocomplete(self, name, info, autocomplete) :
ifnamenotinself.node.words_set:
self.node.words_set.add( name )
self.node.words_list.append( name )
ifself.node.isFromBufferOnlyorself.isTheCurrentFile:
self.node.funcs_list.append( ["{}\t {}".format( name, info ), autocomplete] )
else:
self.node.funcs_list.append( ["{} \t{} - {}".format( name, self.file_name, info ), autocomplete] )
defadd_function_autocomplete(self, name, info, autocomplete, param_count) :
show_name=name+"("+str( param_count ) +")"self.node.words_set.add( name )
self.node.words_list.append( name )
# We do not check whether `if name in words` because we can have several functions# with the same name but different parametersifself.node.isFromBufferOnlyorself.isTheCurrentFile:
self.node.funcs_list.append( ["{}\t {}".format( show_name, info ), autocomplete] )
else:
self.node.funcs_list.append( ["{} \t{} - {}".format( show_name, self.file_name, info ), autocomplete] )
defadd_word_autocomplete(self, name) :
""" Used to add a word to the auto completion of the current current_line. Therefore, it does not need the file name as the auto completion for words from other files/sources. """ifnamenotinself.node.words_list:
self.node.words_set.add( name )
self.node.words_list.append( name )
ifself.isTheCurrentFile:
self.node.funcs_list.append( [name, name] )
else:
self.node.funcs_list.append( ["{}\t {}".format( name, self.file_name ), name] )
defstart_parse(self) :
whileTrue :
current_line=self.read_line()
# log( 1, str( current_line ) )ifcurrent_lineisNone :
breakcurrent_line=self.read_string(current_line)
iflen(current_line) <=0 :
continue#if "sma" in self.node.file_name :# print("read: skip:[%d] brace_level:[%d] buff:[%s]" % (self.is_to_skip_next_line, self.brace_level, current_line))ifself.is_to_skip_next_line :
self.is_to_skip_next_line=Falsecontinueifcurrent_line.startswith('#pragma deprecated') :
current_line=self.read_line()
ifcurrent_lineisnotNoneandcurrent_line.startswith('stock ') :
self.skip_function_block(current_line)
elifcurrent_line.startswith('#define ') :
current_line=self.parse_define(current_line)
elifcurrent_line.startswith('const ') :
current_line=self.parse_const(current_line)
elifcurrent_line.startswith('enum ') orcurrent_line=='enum':
self.found_enum=Trueself.enum_contents=''elifcurrent_line.startswith('new ') :
self.parse_variable(current_line)
elifcurrent_line.startswith('public ') :
self.parse_function(current_line, 1)
elifcurrent_line.startswith('stock ') :
""" new STOCK_TEST1[] = "something"; const STOCK_TEST2[] = "something"; stock STOCK_TEST3[] = "something"; stock const STOCK_TEST4[] = "something"; stock bool:xs_vec_equal(const Float:vec1[], const Float:vec2[]) { } stock xs_vec_add(const Float:in1[], const Float:in2[], Float:out[]) { } """ifcurrent_line.split('(')[0].find(" const ") >-1:
current_line=current_line[6:]
self.parse_const(current_line)
else:
matches=function_re.search(current_line)
log( 8, 'current_line: %s', current_line )
log( 8, 'matches: %s', matches )
ifmatches==None:
current_line="new "+current_line[6:]
self.parse_variable(current_line)
else:
self.parse_function(current_line, 2)
elifcurrent_line.startswith('forward ') :
self.parse_function(current_line, 3)
elifcurrent_line.startswith('native ') :
self.parse_function(current_line, 4)
elifnotself.found_enumandnotcurrent_line[0] =='#' :
self.parse_function(current_line, 0)
ifself.found_enum :
self.parse_enum(current_line)
defparse_define(self, current_line) :
define=re.search('#define[\\s]+([^\\s]+)[\\s]+(.+)', current_line)
ifdefine :
current_line=''name=define.group(1)
value=define.group(2).strip()
count=0params=name.split('(')
name=params[0]
params_count=0iflen( params ) ==2:
params=params[1].split(',')
comma_count=len( params )
params_count=comma_count# If we entered here, there are at least one parameterparams="${1:param1}"items=range( 2, comma_count+1 )
foriteminitems:
params+=", "+'${%d:param%d}'% ( item, item )
else:
params=""ifparams_count>0:
self.add_function_autocomplete( name, 'define: '+value, name+"("+params+")", params_count )
else:
self.add_general_autocomplete( name, 'define: '+value, name )
self.add_constant( name )
log(8, "(analyzer) parse_define add: [%s]"%name)
defparse_const(self, current_line) :
current_line=current_line[6:]
log(8, "(analyzer) current_line: [%s]"%current_line)
split=current_line.split('=', 1)
iflen(split) <2 :
returnname=split[0].strip()
value=split[1].strip()
newline=value.find(';')
if (newline!=-1) :
self.restore_buffer=value[newline+1:].strip()
value=value[0:newline]
self.add_constant(name)
self.add_general_autocomplete(name, 'const: '+value, name)
log(8, "(analyzer) parse_const add: [%s]"%name)
defparse_variable(self, current_line) :
ifcurrent_line.startswith('new const ') :
current_line=current_line[10:]
else :
current_line=current_line[4:]
varName=""lastChar=''i=0pos=0num_brace=0multiLines=TrueskipSpaces=FalseparseName=TrueinBrackets=FalseinBraces=FalseinString=FalsewhilemultiLines :
multiLines=Falseforcincurrent_line :
i+=1if (c=='"') :
if (inStringandlastChar!='^') :
inString=Falseelse :
inString=Trueif (inString==False) :
if (c=='{') :
num_brace+=1inBraces=Trueelif (c=='}') :
num_brace-=1if (num_brace==0) :
inBraces=FalseifskipSpaces :
ifc.isspace() :
continueelse :
skipSpaces=FalseparseName=TrueifparseName :
if (c==':') :
varName=''elif (c==' 'orc=='='orc==';'orc==',') :
varName=varName.strip()
if (varName!='') :
self.add_word_autocomplete( varName )
log(8, "(analyzer) parse_variable add: [%s]"%varName)
varName=''parseName=FalseinBrackets=Falseelif (c=='[') :
inBrackets=Trueelif (inBrackets==False) :
varName+=cif (inString==FalseandinBrackets==FalseandinBraces==False) :
ifnotparseNameandc==';' :
self.restore_buffer=current_line[i:].strip()
returnif (c==',') :
skipSpaces=TruelastChar=cif (c!=',') :
varName=varName.strip()
ifvarName!='' :
self.add_word_autocomplete( varName )
log(8, "(analyzer) parse_variable add: [%s]"%varName)
else :
multiLines=Truecurrent_line=' 'whilecurrent_lineisnotNoneandcurrent_line.isspace() :
current_line=self.read_line()
defparse_enum(self, current_line) :
pos=current_line.find('}')
ifpos!=-1 :
current_line=current_line[0:pos]
self.found_enum=Falseself.enum_contents='%s\n%s'% (self.enum_contents, current_line)
current_line=''ignore=Falseifnotself.found_enum :
pos=self.enum_contents.find('{')
self.enum_contents=self.enum_contents[pos+1:]
forcinself.enum_contents :
ifc=='='orc=='#' :
ignore=Trueelifc=='\n':
ignore=Falseelifc==':' :
current_line=''continueelifc==',' :
self.add_enum(current_line)
current_line=''ignore=Falsecontinueifnotignore :
current_line+=cself.add_enum(current_line)
current_line=''defparse_function(self, current_line, type) :
multi_line=Falsetemp=''full_func_str=Noneopen_paren_found=Falsewhilecurrent_lineisnotNone :
current_line=current_line.strip()
ifnotopen_paren_found :
parenpos=current_line.find('(')
ifparenpos==-1 :
returnopen_paren_found=Trueifopen_paren_found :
pos=current_line.find(')')
ifpos!=-1 :
full_func_str=current_line[0:pos+1]
current_line=current_line[pos+1:]
if (multi_line) :
full_func_str='%s%s'% (temp, full_func_str)
breakmulti_line=Truetemp='%s%s'% (temp, current_line)
current_line=self.read_line()
ifcurrent_lineisNone :
returncurrent_line=self.read_string(current_line)
iffull_func_strisnotNone :
error=self.parse_function_params(full_func_str, type)
ifnoterrorandtype<=2 :
self.skip_function_block(current_line)
ifnotself.is_to_skip_brace :
self.is_to_skip_next_line=True#print("skip_brace: error:[%d] type:[%d] found:[%d] skip:[%d] func:[%s]" % (error, type, self.is_to_skip_brace, self.is_to_skip_next_line, full_func_str))defparse_function_params(self, func, function_type) :
iffunction_type==0 :
remaining=funcelse :
split=func.split(' ', 1)
remaining=split[1]
split=remaining.split('(', 1)
iflen(split) <2 :
log(4, "(analyzer) parse_params return1: [%s]"%split)
return1remaining=split[1]
returntype=''funcname_and_return=split[0].strip()
split_funcname_and_return=funcname_and_return.split(':')
iflen(split_funcname_and_return) >1 :
funcname=split_funcname_and_return[1].strip()
returntype=split_funcname_and_return[0].strip()
else :
funcname=split_funcname_and_return[0].strip()
iffuncname.startswith("operator") :
return0ifnotself.is_valid_name(funcname) :
log(4, "(analyzer) parse_params invalid name: [%s]"%funcname)
return1remaining=remaining.strip()
ifremaining==')':
params= []
else:
params=remaining.strip()[:-1].split(',')
ifg_add_paremeters:
i=1autocomplete=funcname+'('forparaminparams:
ifi>1:
autocomplete+=', 'autocomplete+='${%d:%s}'% (i, param.strip())
i+=1autocomplete+=')'else:
autocomplete=funcname+"()"self.add_function_autocomplete(funcname, FUNC_TYPES(function_type).name, autocomplete, len( params ))
self.node.doct.add(TooltipDocumentation(funcname, func[func.find("(")+1:-1], self.node.file_name, function_type, returntype))
log(8, "(analyzer) parse_params add: [%s]"%func)
return0defprocess_buffer(text, node) :
ifg_function_autocomplete:
text_reader=TextReader(text)
pawnParse.start(text_reader, node, True)
defprocess_include_file(node) :
withopen(node.file_name, errors="ignore") asfile :
pawnParse.start(file, node)
defsimple_escape(html) :
returnhtml.replace('&', '&')
pawnParse=PawnParse()
process_thread=ProcessQueueThread()
file_event_handler=IncludeFileEventHandler()
The text was updated successfully, but these errors were encountered:
Older code used/fixed:
The text was updated successfully, but these errors were encountered: