<commit_before>import sublime, sublime_plugin
import re, string, os, sys, functools, mmap, imp

try:
    from SystemVerilog.verilogutil import verilogutil
    from SystemVerilog.verilogutil import sublimeutil
except ImportError:
    sys.path.append(os.path.join(os.path.dirname(__file__), "verilogutil"))
    import verilogutil
    import sublimeutil

def plugin_loaded():
    imp.reload(verilogutil)
    imp.reload(sublimeutil)

list_module_files = {}
lmf_update_ongoing = False

############################################################################
# Helper function: retrieve type info with support for import statement
def type_info(view,txt,varname):
    ti = verilogutil.get_type_info(txt,varname)
    if not ti or not ti['type']:
        ti = type_info_from_import(view,txt,varname)
    return ti

def type_info_file(view,fname,varname):
    ti = verilogutil.get_type_info_file(fname,varname)
    if not ti or not ti['type']:
        with open(fname) as f:
            flines = f.read()
            ti = type_info_from_import(view,flines,varname)
    return ti

def type_info_from_import(view,txt,varname):
    ti = {'decl':None,'type':None,'array':"",'bw':"", 'name':varname, 'tag':None, 'value':None}
    for m in re.finditer(r'\bimport\s+(.+?);',txt,flags=re.MULTILINE):
        for mp in re.finditer(r'\b(\w+)(\:\:[\w\*]+)+',m.groups()[0],flags=re.MULTILINE):
            # print('Package: {0} '.format(mp.groups()[0]))
            pi = lookup_package(view,mp.groups()[0])
            if pi:
                for x in pi['member']:
                    if x['name']==varname:
                        ti = x
                        break
        if ti['type']:
            break
    return ti

########################################
def lookup_module(view,mname):
    mi = None
    filelist = view.window().lookup_symbol_in_index(mname)
    if filelist:
        # Check if module is defined in current file first
        fname = view.file_name()
        flist_norm = [sublimeutil.normalize_fname(f[0]) for f in filelist]
        if fname in flist_norm:
            _,_,rowcol = filelist[flist_norm.index(fname)]
            mi = verilogutil.parse_module_file(fname,mname)
        if mi:
            mi['fname'] = (fname,rowcol[0],rowcol[1])
        # Consider first file with a valid module definition to be the correct one
        else:
            for f in filelist:
                fname, display_fname, rowcol = f
                fname = sublimeutil.normalize_fname(fname)
                mi = verilogutil.parse_module_file(fname,mname)
                if mi:
                    mi['fname'] = (fname,rowcol[0],rowcol[1])
                    break
    # print('[SV:lookup_module] {0}'.format(mi))
    return mi

def lookup_package(view,pkgname):
    pi = None
    mi = None
    filelist = view.window().lookup_symbol_in_index(pkgname)
    if filelist:
        # Check if module is defined in current file first
        fname = view.file_name()
        flist_norm = [sublimeutil.normalize_fname(f[0]) for f in filelist]
        if fname in flist_norm:
            _,_,rowcol = filelist[flist_norm.index(fname)]
            mi = verilogutil.parse_package_file(fname,pkgname)
        # Consider first file with a valid module definition to be the correct one
        if mi:
            pi = {'type': 'package', 'member': mi, 'fname':(fname,rowcol[0],rowcol[1])}
        else:
            for f in filelist:
                fname, display_fname, rowcol = f
                fname = sublimeutil.normalize_fname(fname)
                mi = verilogutil.parse_package_file(fname,pkgname)
                if mi:
                    pi = {'type': 'package', 'member': mi, 'fname':(fname,rowcol[0],rowcol[1])}
                    break
    # print('[SV:lookup_package] {0}'.format(pi))
    return pi

def lookup_function(view,funcname):
    fi = None
    filelist = view.window().lookup_symbol_in_index(funcname)
    # print('Files for {0} = {1}'.format(funcname,filelist))
    if filelist:
        # Check if function is defined in current file first
        fname = view.file_name()
        flist_norm = [sublimeutil.normalize_fname(f[0]) for f in filelist]
        if fname in flist_norm:
            _,_,rowcol = filelist[flist_norm.index(fname)]
            with open(fname,'r') as f:
                flines = str(f.read())
            flines = verilogutil.clean_comment(flines)
            fi = verilogutil.parse_function(flines,funcname)
        if fi:
            fi['fname'] = (fname,rowcol[0],rowcol[1])
        # Consider first file with a valid function definition to be the correct one
        else:
            for f in filelist:
                fname, display_fname, rowcol = f
                fname = sublimeutil.normalize_fname(fname)
                with open(fname,'r') as f:
                    flines = str(f.read())
                flines = verilogutil.clean_comment(flines)
                fi = verilogutil.parse_function(flines,funcname)
                if fi:
                    fi['fname'] = (fname,rowcol[0],rowcol[1])
                    break
    return fi

def lookup_type(view, t):
    ti = None
    if '::' in t:
        t = t.split('::')[-1]
    filelist = view.window().lookup_symbol_in_index(t)
    if filelist:
        # Check if symbol is defined in current file first
        fname = view.file_name()
        flist_norm = [sublimeutil.normalize_fname(f[0]) for f in filelist]
        if fname in flist_norm:
            _,_,rowcol = filelist[flist_norm.index(fname)]
            # print(t + ' defined in current file' + str(fname))
            ti = type_info_file(view,fname,t)
        if ti and ti['type']:
            ti['fname'] = (fname,rowcol[0],rowcol[1])
        # Consider first file with a valid type definition to be the correct one
        else:
            for f in filelist:
                fname, display_fname, rowcol = f
                fname = sublimeutil.normalize_fname(fname)
                # Parse only systemVerilog file. Check might be a bit too restrictive ...
                # print(t + ' defined in ' + str(fname))
                if fname.lower().endswith(('sv','svh', 'v', 'vh')):
                    ti = type_info_file(view,fname,t)
                    if ti['type']:
                        ti['fname'] = (fname,rowcol[0],rowcol[1])
                        break
    # print('[SV:lookup_type] {0}'.format(ti))
    return ti

########################################
# Create module instantiation skeleton #
class VerilogModuleInstCommand(sublime_plugin.TextCommand):

    #TODO: Run the search in background and keep a cache to improve performance
    def run(self,edit):
        global list_module_files
        if len(self.view.sel())>0 :
            r = self.view.sel()[0]
            scope = self.view.scope_name(r.a)
            if 'meta.module.inst' in scope:
                self.view.run_command("verilog_module_reconnect")
                return
        self.window = sublime.active_window()
        # Populate the list_module_files:
        #  - if it exist use latest version and display panel immediately while running an update
        #  - if not display panel only when list is ready
        projname = self.window.project_file_name()
        if not projname:
            list_module_files['__NONE__'] = []
            for v in self.window.views():
                if v and v.file_name():
                    list_module_files['__NONE__'].append(os.path.abspath(v.file_name()))
            self.on_list_done('__NONE__')
        elif projname not in list_module_files:
            sublime.set_timeout_async(functools.partial(self.get_list_file,projname,functools.partial(self.on_list_done,projname)), 0)
            sublime.status_message('Please wait while module list is being built')
        elif not lmf_update_ongoing:
            sublime.set_timeout_async(functools.partial(self.get_list_file,projname), 0)
            self.on_list_done(projname)

    def get_list_file(self, projname, callback=None):
        global list_module_files
        global lmf_update_ongoing
        lmf_update_ongoing = True
        lmf = []
        for folder in sublime.active_window().folders():
            for root, dirs, files in os.walk(folder):
                for fn in files:
                    if fn.lower().endswith(('.v','.sv')):
                        ffn = os.path.join(root,fn)
                        f = open(ffn)
                        if os.stat(ffn).st_size:
                            s = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
                            if s.find(b'module') != -1:
                                lmf.append(ffn)
        sublime.status_message('List of module files updated')
        list_module_files[projname] = lmf[:]
        lmf_update_ongoing = False
        if callback:
            callback()

    def on_list_done(self,projname):
        self.window.show_quick_panel(list_module_files[projname], functools.partial(self.on_select_file_done,projname))

    def on_select_file_done(self, projname, index):
        if index >= 0:
            fname = list_module_files[projname][index]
            with open(fname, "r") as f:
                flines = str(f.read())
            self.ml=re.findall(r'^\s*module\s+(\w+)',flines,re.MULTILINE);
            if len(self.ml)<2:
                self.view.run_command("verilog_do_module_parse", {"args":{'fname': fname, 'mname':r'\w+'}})
            else:
                sublime.set_timeout_async(lambda: self.window.show_quick_panel(self.ml, functools.partial(self.on_select_module_done,fname)),0)

    def on_select_module_done(self, fname, index):
        if index >= 0:
            self.view.run_command("verilog_do_module_parse", {"args":{'fname': fname, 'mname':self.ml[index]}})

class VerilogDoModuleParseCommand(sublime_plugin.TextCommand):

    def run(self, edit, args):
        self.fname = args['fname']
        #TODO: check for multiple module in the file
        settings = self.view.settings()
        self.pm = verilogutil.parse_module_file(self.fname, args['mname'])
        self.param_explicit = settings.get('sv.param_explicit',False)
        self.param_propagate = settings.get('sv.param_propagate',False)
        # print(self.pm)
        if self.pm is not None:
            self.param_value = []
            if self.pm['param'] and settings.get('sv.fillparam'):
                self.cnt = 0
                self.show_prompt()
            else:
                self.view.run_command("verilog_do_module_inst", {"args":{'pm':self.pm, 'pv':self.param_value, 'text':self.fname}})

    def on_prompt_done(self, content):
        if not content.startswith("Default"):
            self.param_value.append({'name':self.pm['param'][self.cnt]['name'] , 'value': content});
        elif self.param_explicit :
            self.param_value.append({'name':self.pm['param'][self.cnt]['name'] , 'value': content[9:]});
        self.cnt += 1
        if not self.pm['param']:
            return
        if self.cnt < len(self.pm['param']):
            self.show_prompt()
        else:
            self.view.run_command("verilog_do_module_inst", {"args":{'pm':self.pm, 'pv':self.param_value, 'text':self.fname}})

    def show_prompt(self):
        p = self.pm['param'][self.cnt]
        if self.param_propagate:
            default = 'parameter '
            if p['decl']:
                default += p['decl'] + ' '
            default += '{0} = {1}'.format(p['name'],p['value'])
        else:
            default = 'Default: {0}'.format(p['value'])
        panel = sublime.active_window().show_input_panel(p['name'], default, self.on_prompt_done, None, None)
        #select the whole line (to ease value change)
        r = panel.line(panel.sel()[0])
        panel.sel().clear()
        panel.sel().add(r)


class VerilogDoModuleInstCommand(sublime_plugin.TextCommand):
    #TODO: check base indentation
    def run(self, edit, args):
        settings = self.view.settings()
        isAutoConnect = settings.get('sv.autoconnect',False)
        isParamOneLine = settings.get('sv.param_oneline',True)
        isInstOneLine = settings.get('sv.inst_oneline',True)
        isColumnAlignment = settings.get('sv.param_port_alignment',True)
        indent_level = settings.get('sv.decl_indent')
        param_decl = ''
        pm = args['pm']
        # print(pm)
        # Update Module information with parameter value for later signal declaration using correct type
        for p in args['pv']:
            for pmp in pm['param']:
                if pmp['name']==p['name']:
                    if p['value'].startswith('parameter') or p['value'].startswith('localparam'):
                        pmp['value']= p['name']
                        param_decl +=  indent_level*'\t' + p['value'] + ';\n'
                        m = re.search(r"(?P<name>\w+)\s*=",p['value'])
                        p['value'] = m.group('name')
                    else:
                        pmp['value']=p['value']
                    break
        # print('[VerilogDoModuleInstCommand] pm = '+ str(pm))
        decl = ''
        ac = {}
        wc = {}
        # Add signal port declaration
        if isAutoConnect and pm['port']:
            (decl,ac,wc) = self.get_connect(self.view, settings, pm)
            #Find location where to insert signal declaration: default to just before module instantiation
            if decl or param_decl:
                r = self.get_region_decl(self.view,settings)
                self.view.insert(edit, r, '\n'+param_decl+decl)
                sublime.status_message('Adding ' + str(len(decl.splitlines())) + ' signals declaration' )
        inst_name = settings.get('sv.instance_prefix','') + pm['name'] + settings.get('sv.instance_suffix','')
        # Check if instantiation can fit on one line only
        if isInstOneLine :
            len_inst = len(pm['name']) + 1 + len(inst_name) + 2
            if len(args['pv']) > 0:
                len_inst += 2
                for p in args['pv']:
                    len_inst += len(p['name']) + len(p['value']) + 5
            if len_inst+3 > settings.get('sv.max_line_length',120):
                isParamOneLine = False
            elif pm['port']:
                for p in pm['port']:
                    len_inst += len(p['name']) + 5
                    if p['name'] in ac.keys():
                        len_inst+= len(ac[p['name']])
                    else :
                        len_inst+= len(p['name'])
            if len_inst+3 > settings.get('sv.max_line_length',120):
                isInstOneLine = False
        # Instantiation
        inst = pm['name'] + " "
        # Parameters: bind only parameters for which a value different from default was set
        if len(args['pv']) > 0:
            if isParamOneLine or not isColumnAlignment:
                max_len = 0
            else:
                max_len = max([len(x['name']) for x in args['pv']])
            inst += "#("
            if not isParamOneLine:
                inst += "\n"
            for i in range(len(args['pv'])):
                if not isParamOneLine:
                    inst += "\t"
                inst+= "." + args['pv'][i]['name'].ljust(max_len) + "("+args['pv'][i]['value']+")"
                if i<len(args['pv'])-1:
                    inst+=","
                if not isParamOneLine:
                    inst+="\n"
                elif i<len(args['pv'])-1:
                    inst+=" "
            inst += ") "
        #Port binding
        inst +=  inst_name + " ("
        if not isInstOneLine:
             inst+="\n"
        if pm['port']:
            # Get max length of a port to align everything
            if isInstOneLine or not isColumnAlignment:
                max_len_p = 0
                max_len_s = 0
            else :
                max_len_p = max([len(x['name']) for x in pm['port']])
                max_len_s = max_len_p
            # print('Autoconnect dict = ' + str([ac[x] for x in ac]))
                if len(ac)>0 :
                    max_len_s = max([len(ac[x]) for x in ac])
                    if max_len_p>max_len_s:
                        max_len_s = max_len_p
            for i in range(len(pm['port'])):
                portname = pm['port'][i]['name']
                if not isInstOneLine:
                    inst += "\t"
                inst+= "." + portname.ljust(max_len_p) + "("
                if isAutoConnect:
                    if portname in ac.keys():
                        inst+= ac[portname].ljust(max_len_s)
                    else :
                        inst+= portname.ljust(max_len_s)
                inst+= ")"
                if i<len(pm['port'])-1:
                    inst+=","
                if not isInstOneLine:
                    if portname in wc.keys():
                        inst+=" // TODO: Check connection ! " + wc[portname]
                    inst+="\n"
                elif i<len(pm['port'])-1:
                    inst+=" "
        inst += ");\n"
        self.view.insert(edit, self.view.sel()[0].a, inst)
        # Status report
        nb_decl = len(decl.splitlines())
        s = ''
        if nb_decl:
            s+= 'Adding ' + str(nb_decl) + ' signal(s) declaration(s)\n'
        if len(ac)>0 :
            s+= 'Non-perfect name match for ' + str(len(ac)) + ' port(s) : ' + str(ac) + '\n'
        if len(wc)>0 :
            s+= 'Found ' + str(len(wc)) + ' mismatch(es) for port(s): ' + str([x for x in wc.keys()]) + '\n'
        if s!='':
            sublimeutil.print_to_panel(s,'SystemVerilog')

    def get_region_decl(self, view, settings, r=None):
        if not r:
            r = view.sel()[0].begin()
        s = settings.get('sv.decl_start','')
        if s!='' :
            r_start = view.find(s,0,sublime.LITERAL)
            if r_start :
                s = settings.get('sv.decl_end','')
                r_stop = None
                if s!='':
                    r_stop = view.find(s,r_start.a,sublime.LITERAL)
                # Find first empty Find line
                if r_stop:
                    r_tmp = view.find_by_class(r_stop.a,False,sublime.CLASS_EMPTY_LINE)
                else :
                    r_tmp = view.find_by_class(r_start.a,True,sublime.CLASS_EMPTY_LINE)
                if r_tmp:
                    r = r_tmp
        return r

    def get_connect(self,view,settings,pm):
        # Init return variable
        decl = ""
        ac = {} # autoconnection (entry is port name)
        wc = {} # warning connection (entry is port name)
        # get settings
        port_prefix = settings.get('sv.autoconnect_port_prefix', [])
        port_suffix = settings.get('sv.autoconnect_port_suffix', [])
        indent_level = settings.get('sv.decl_indent', 1)
        #default signal type to logic, except verilog file use wire (if type is implicit)
        fname = view.file_name()
        sig_type = 'logic'
        if fname: # handle case where view is a scracth buffer and has no filename
            if fname.endswith('.v'):
                sig_type = 'wire'
        # read file to be able to check existing declaration
        flines = view.substr(sublime.Region(0, view.size()))
        mi = verilogutil.parse_module(flines)
        signal_dict = {}
        for ti in mi['port']:
            signal_dict[ti['name']] = ti
        for ti in mi['signal']:
            signal_dict[ti['name']] = ti
        # print ('Signal Dict = ' + str(signal_dict))
        signal_dict_text = ''
        for (name,ti) in signal_dict.items():
            signal_dict_text += name+'\n'
        # print ('Signal Dict = ' + signal_dict_text)
        if pm['param']:
            param_dict = {p['name']:p['value'] for p in pm['param']}
        else:
            param_dict = {}
        # print(param_dict)
        # Add signal declaration
        for p in pm['port']:
            pname = p['name']
            #Remove suffix/prefix of port name
            for prefix in port_prefix:
                if pname.startswith(prefix):<commit_msg>Fix issue #109 : Module instantiation file list can use unsaved project files (regression introduce in 2.9.0 Fix issue #67  : If a file was removed or added, the module instantiation could use the wrong file for module parsing<commit_after>import sublime, sublime_plugin
import re, string, os, sys, functools, mmap, imp

try:
    from SystemVerilog.verilogutil import verilogutil
    from SystemVerilog.verilogutil import sublimeutil
except ImportError:
    sys.path.append(os.path.join(os.path.dirname(__file__), "verilogutil"))
    import verilogutil
    import sublimeutil

def plugin_loaded():
    imp.reload(verilogutil)
    imp.reload(sublimeutil)

list_module_files = {}
lmf_update_ongoing = False

############################################################################
# Helper function: retrieve type info with support for import statement
def type_info(view,txt,varname):
    ti = verilogutil.get_type_info(txt,varname)
    if not ti or not ti['type']:
        ti = type_info_from_import(view,txt,varname)
    return ti

def type_info_file(view,fname,varname):
    ti = verilogutil.get_type_info_file(fname,varname)
    if not ti or not ti['type']:
        with open(fname) as f:
            flines = f.read()
            ti = type_info_from_import(view,flines,varname)
    return ti

def type_info_from_import(view,txt,varname):
    ti = {'decl':None,'type':None,'array':"",'bw':"", 'name':varname, 'tag':None, 'value':None}
    for m in re.finditer(r'\bimport\s+(.+?);',txt,flags=re.MULTILINE):
        for mp in re.finditer(r'\b(\w+)(\:\:[\w\*]+)+',m.groups()[0],flags=re.MULTILINE):
            # print('Package: {0} '.format(mp.groups()[0]))
            pi = lookup_package(view,mp.groups()[0])
            if pi:
                for x in pi['member']:
                    if x['name']==varname:
                        ti = x
                        break
        if ti['type']:
            break
    return ti

########################################
def lookup_module(view,mname):
    mi = None
    filelist = view.window().lookup_symbol_in_index(mname)
    if filelist:
        # Check if module is defined in current file first
        fname = view.file_name()
        flist_norm = [sublimeutil.normalize_fname(f[0]) for f in filelist]
        if fname in flist_norm:
            _,_,rowcol = filelist[flist_norm.index(fname)]
            mi = verilogutil.parse_module_file(fname,mname)
        if mi:
            mi['fname'] = (fname,rowcol[0],rowcol[1])
        # Consider first file with a valid module definition to be the correct one
        else:
            for f in filelist:
                fname, display_fname, rowcol = f
                fname = sublimeutil.normalize_fname(fname)
                mi = verilogutil.parse_module_file(fname,mname)
                if mi:
                    mi['fname'] = (fname,rowcol[0],rowcol[1])
                    break
    # print('[SV:lookup_module] {0}'.format(mi))
    return mi

def lookup_package(view,pkgname):
    pi = None
    mi = None
    filelist = view.window().lookup_symbol_in_index(pkgname)
    if filelist:
        # Check if module is defined in current file first
        fname = view.file_name()
        flist_norm = [sublimeutil.normalize_fname(f[0]) for f in filelist]
        if fname in flist_norm:
            _,_,rowcol = filelist[flist_norm.index(fname)]
            mi = verilogutil.parse_package_file(fname,pkgname)
        # Consider first file with a valid module definition to be the correct one
        if mi:
            pi = {'type': 'package', 'member': mi, 'fname':(fname,rowcol[0],rowcol[1])}
        else:
            for f in filelist:
                fname, display_fname, rowcol = f
                fname = sublimeutil.normalize_fname(fname)
                mi = verilogutil.parse_package_file(fname,pkgname)
                if mi:
                    pi = {'type': 'package', 'member': mi, 'fname':(fname,rowcol[0],rowcol[1])}
                    break
    # print('[SV:lookup_package] {0}'.format(pi))
    return pi

def lookup_function(view,funcname):
    fi = None
    filelist = view.window().lookup_symbol_in_index(funcname)
    # print('Files for {0} = {1}'.format(funcname,filelist))
    if filelist:
        # Check if function is defined in current file first
        fname = view.file_name()
        flist_norm = [sublimeutil.normalize_fname(f[0]) for f in filelist]
        if fname in flist_norm:
            _,_,rowcol = filelist[flist_norm.index(fname)]
            with open(fname,'r') as f:
                flines = str(f.read())
            flines = verilogutil.clean_comment(flines)
            fi = verilogutil.parse_function(flines,funcname)
        if fi:
            fi['fname'] = (fname,rowcol[0],rowcol[1])
        # Consider first file with a valid function definition to be the correct one
        else:
            for f in filelist:
                fname, display_fname, rowcol = f
                fname = sublimeutil.normalize_fname(fname)
                with open(fname,'r') as f:
                    flines = str(f.read())
                flines = verilogutil.clean_comment(flines)
                fi = verilogutil.parse_function(flines,funcname)
                if fi:
                    fi['fname'] = (fname,rowcol[0],rowcol[1])
                    break
    return fi

def lookup_type(view, t):
    ti = None
    if '::' in t:
        t = t.split('::')[-1]
    filelist = view.window().lookup_symbol_in_index(t)
    if filelist:
        # Check if symbol is defined in current file first
        fname = view.file_name()
        flist_norm = [sublimeutil.normalize_fname(f[0]) for f in filelist]
        if fname in flist_norm:
            _,_,rowcol = filelist[flist_norm.index(fname)]
            # print(t + ' defined in current file' + str(fname))
            ti = type_info_file(view,fname,t)
        if ti and ti['type']:
            ti['fname'] = (fname,rowcol[0],rowcol[1])
        # Consider first file with a valid type definition to be the correct one
        else:
            for f in filelist:
                fname, display_fname, rowcol = f
                fname = sublimeutil.normalize_fname(fname)
                # Parse only systemVerilog file. Check might be a bit too restrictive ...
                # print(t + ' defined in ' + str(fname))
                if fname.lower().endswith(('sv','svh', 'v', 'vh')):
                    ti = type_info_file(view,fname,t)
                    if ti['type']:
                        ti['fname'] = (fname,rowcol[0],rowcol[1])
                        break
    # print('[SV:lookup_type] {0}'.format(ti))
    return ti

########################################
# Create module instantiation skeleton #
class VerilogModuleInstCommand(sublime_plugin.TextCommand):

    #TODO: Run the search in background and keep a cache to improve performance
    def run(self,edit):
        global list_module_files
        if len(self.view.sel())>0 :
            r = self.view.sel()[0]
            scope = self.view.scope_name(r.a)
            if 'meta.module.inst' in scope:
                self.view.run_command("verilog_module_reconnect")
                return
        self.window = sublime.active_window()
        # Populate the list_module_files:
        #  - If no folder in current project, just list open files
        #  - if it exist use latest version and display panel immediately while running an update
        #  - if not display panel only when list is ready
        projname = self.window.project_file_name()
        if not sublime.active_window().folders():
            list_module_files['__NONE__'] = []
            for v in self.window.views():
                if v and v.file_name():
                    list_module_files['__NONE__'].append(os.path.abspath(v.file_name()))
            self.on_list_done('__NONE__')
        elif projname not in list_module_files:
            sublime.set_timeout_async(functools.partial(self.get_list_file,projname,functools.partial(self.on_list_done,projname)), 0)
            sublime.status_message('Please wait while module list is being built')
        elif not lmf_update_ongoing:
            # Create a copy so that the background update does not change the content of the list
            list_module_files['__COPY__'] = list_module_files[projname][:]
            # Start background update of the list
            sublime.set_timeout_async(functools.partial(self.get_list_file,projname), 0)
            # Display quick panel
            self.on_list_done('__COPY__')

    def get_list_file(self, projname, callback=None):
        global list_module_files
        global lmf_update_ongoing
        lmf_update_ongoing = True
        lmf = []
        for folder in sublime.active_window().folders():
            for root, dirs, files in os.walk(folder):
                for fn in files:
                    if fn.lower().endswith(('.v','.sv')):
                        ffn = os.path.join(root,fn)
                        f = open(ffn)
                        if os.stat(ffn).st_size:
                            s = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
                            if s.find(b'module') != -1:
                                lmf.append(ffn)
        sublime.status_message('List of module files updated')
        list_module_files[projname] = lmf[:]
        lmf_update_ongoing = False
        if callback:
            callback()

    def on_list_done(self,projname):
        self.window.show_quick_panel(list_module_files[projname], functools.partial(self.on_select_file_done,projname))

    def on_select_file_done(self, projname, index):
        if index >= 0:
            fname = list_module_files[projname][index]
            try:
                with open(fname, "r") as f:
                    flines = str(f.read())
                self.ml=re.findall(r'^\s*module\s+(\w+)',flines,re.MULTILINE);
                if len(self.ml)<2:
                    self.view.run_command("verilog_do_module_parse", {"args":{'fname': fname, 'mname':r'\w+'}})
                else:
                    sublime.set_timeout_async(lambda: self.window.show_quick_panel(self.ml, functools.partial(self.on_select_module_done,fname)),0)
            except FileNotFoundError:
                print('[VerilogModule.Instance] File not found ')

    def on_select_module_done(self, fname, index):
        if index >= 0:
            self.view.run_command("verilog_do_module_parse", {"args":{'fname': fname, 'mname':self.ml[index]}})

class VerilogDoModuleParseCommand(sublime_plugin.TextCommand):

    def run(self, edit, args):
        self.fname = args['fname']
        #TODO: check for multiple module in the file
        settings = self.view.settings()
        self.pm = verilogutil.parse_module_file(self.fname, args['mname'])
        self.param_explicit = settings.get('sv.param_explicit',False)
        self.param_propagate = settings.get('sv.param_propagate',False)
        # print(self.pm)
        if self.pm is not None:
            self.param_value = []
            if self.pm['param'] and settings.get('sv.fillparam'):
                self.cnt = 0
                self.show_prompt()
            else:
                self.view.run_command("verilog_do_module_inst", {"args":{'pm':self.pm, 'pv':self.param_value, 'text':self.fname}})

    def on_prompt_done(self, content):
        if not content.startswith("Default"):
            self.param_value.append({'name':self.pm['param'][self.cnt]['name'] , 'value': content});
        elif self.param_explicit :
            self.param_value.append({'name':self.pm['param'][self.cnt]['name'] , 'value': content[9:]});
        self.cnt += 1
        if not self.pm['param']:
            return
        if self.cnt < len(self.pm['param']):
            self.show_prompt()
        else:
            self.view.run_command("verilog_do_module_inst", {"args":{'pm':self.pm, 'pv':self.param_value, 'text':self.fname}})

    def show_prompt(self):
        p = self.pm['param'][self.cnt]
        if self.param_propagate:
            default = 'parameter '
            if p['decl']:
                default += p['decl'] + ' '
            default += '{0} = {1}'.format(p['name'],p['value'])
        else:
            default = 'Default: {0}'.format(p['value'])
        panel = sublime.active_window().show_input_panel(p['name'], default, self.on_prompt_done, None, None)
        #select the whole line (to ease value change)
        r = panel.line(panel.sel()[0])
        panel.sel().clear()
        panel.sel().add(r)


class VerilogDoModuleInstCommand(sublime_plugin.TextCommand):
    #TODO: check base indentation
    def run(self, edit, args):
        settings = self.view.settings()
        isAutoConnect = settings.get('sv.autoconnect',False)
        isParamOneLine = settings.get('sv.param_oneline',True)
        isInstOneLine = settings.get('sv.inst_oneline',True)
        isColumnAlignment = settings.get('sv.param_port_alignment',True)
        indent_level = settings.get('sv.decl_indent')
        param_decl = ''
        pm = args['pm']
        # print(pm)
        # Update Module information with parameter value for later signal declaration using correct type
        for p in args['pv']:
            for pmp in pm['param']:
                if pmp['name']==p['name']:
                    if p['value'].startswith('parameter') or p['value'].startswith('localparam'):
                        pmp['value']= p['name']
                        param_decl +=  indent_level*'\t' + p['value'] + ';\n'
                        m = re.search(r"(?P<name>\w+)\s*=",p['value'])
                        p['value'] = m.group('name')
                    else:
                        pmp['value']=p['value']
                    break
        # print('[VerilogDoModuleInstCommand] pm = '+ str(pm))
        decl = ''
        ac = {}
        wc = {}
        # Add signal port declaration
        if isAutoConnect and pm['port']:
            (decl,ac,wc) = self.get_connect(self.view, settings, pm)
            #Find location where to insert signal declaration: default to just before module instantiation
            if decl or param_decl:
                r = self.get_region_decl(self.view,settings)
                self.view.insert(edit, r, '\n'+param_decl+decl)
                sublime.status_message('Adding ' + str(len(decl.splitlines())) + ' signals declaration' )
        inst_name = settings.get('sv.instance_prefix','') + pm['name'] + settings.get('sv.instance_suffix','')
        # Check if instantiation can fit on one line only
        if isInstOneLine :
            len_inst = len(pm['name']) + 1 + len(inst_name) + 2
            if len(args['pv']) > 0:
                len_inst += 2
                for p in args['pv']:
                    len_inst += len(p['name']) + len(p['value']) + 5
            if len_inst+3 > settings.get('sv.max_line_length',120):
                isParamOneLine = False
            elif pm['port']:
                for p in pm['port']:
                    len_inst += len(p['name']) + 5
                    if p['name'] in ac.keys():
                        len_inst+= len(ac[p['name']])
                    else :
                        len_inst+= len(p['name'])
            if len_inst+3 > settings.get('sv.max_line_length',120):
                isInstOneLine = False
        # Instantiation
        inst = pm['name'] + " "
        # Parameters: bind only parameters for which a value different from default was set
        if len(args['pv']) > 0:
            if isParamOneLine or not isColumnAlignment:
                max_len = 0
            else:
                max_len = max([len(x['name']) for x in args['pv']])
            inst += "#("
            if not isParamOneLine:
                inst += "\n"
            for i in range(len(args['pv'])):
                if not isParamOneLine:
                    inst += "\t"
                inst+= "." + args['pv'][i]['name'].ljust(max_len) + "("+args['pv'][i]['value']+")"
                if i<len(args['pv'])-1:
                    inst+=","
                if not isParamOneLine:
                    inst+="\n"
                elif i<len(args['pv'])-1:
                    inst+=" "
            inst += ") "
        #Port binding
        inst +=  inst_name + " ("
        if not isInstOneLine:
             inst+="\n"
        if pm['port']:
            # Get max length of a port to align everything
            if isInstOneLine or not isColumnAlignment:
                max_len_p = 0
                max_len_s = 0
            else :
                max_len_p = max([len(x['name']) for x in pm['port']])
                max_len_s = max_len_p
            # print('Autoconnect dict = ' + str([ac[x] for x in ac]))
                if len(ac)>0 :
                    max_len_s = max([len(ac[x]) for x in ac])
                    if max_len_p>max_len_s:
                        max_len_s = max_len_p
            for i in range(len(pm['port'])):
                portname = pm['port'][i]['name']
                if not isInstOneLine:
                    inst += "\t"
                inst+= "." + portname.ljust(max_len_p) + "("
                if isAutoConnect:
                    if portname in ac.keys():
                        inst+= ac[portname].ljust(max_len_s)
                    else :
                        inst+= portname.ljust(max_len_s)
                inst+= ")"
                if i<len(pm['port'])-1:
                    inst+=","
                if not isInstOneLine:
                    if portname in wc.keys():
                        inst+=" // TODO: Check connection ! " + wc[portname]
                    inst+="\n"
                elif i<len(pm['port'])-1:
                    inst+=" "
        inst += ");\n"
        self.view.insert(edit, self.view.sel()[0].a, inst)
        # Status report
        nb_decl = len(decl.splitlines())
        s = ''
        if nb_decl:
            s+= 'Adding ' + str(nb_decl) + ' signal(s) declaration(s)\n'
        if len(ac)>0 :
            s+= 'Non-perfect name match for ' + str(len(ac)) + ' port(s) : ' + str(ac) + '\n'
        if len(wc)>0 :
            s+= 'Found ' + str(len(wc)) + ' mismatch(es) for port(s): ' + str([x for x in wc.keys()]) + '\n'
        if s!='':
            sublimeutil.print_to_panel(s,'SystemVerilog')

    def get_region_decl(self, view, settings, r=None):
        if not r:
            r = view.sel()[0].begin()
        s = settings.get('sv.decl_start','')
        if s!='' :
            r_start = view.find(s,0,sublime.LITERAL)
            if r_start :
                s = settings.get('sv.decl_end','')
                r_stop = None
                if s!='':
                    r_stop = view.find(s,r_start.a,sublime.LITERAL)
                # Find first empty Find line
                if r_stop:
                    r_tmp = view.find_by_class(r_stop.a,False,sublime.CLASS_EMPTY_LINE)
                else :
                    r_tmp = view.find_by_class(r_start.a,True,sublime.CLASS_EMPTY_LINE)
                if r_tmp:
                    r = r_tmp
        return r

    def get_connect(self,view,settings,pm):
        # Init return variable
        decl = ""
        ac = {} # autoconnection (entry is port name)
        wc = {} # warning connection (entry is port name)
        # get settings
        port_prefix = settings.get('sv.autoconnect_port_prefix', [])
        port_suffix = settings.get('sv.autoconnect_port_suffix', [])
        indent_level = settings.get('sv.decl_indent', 1)
        #default signal type to logic, except verilog file use wire (if type is implicit)
        fname = view.file_name()
        sig_type = 'logic'
        if fname: # handle case where view is a scracth buffer and has no filename
            if fname.endswith('.v'):
                sig_type = 'wire'
        # read file to be able to check existing declaration
        flines = view.substr(sublime.Region(0, view.size()))
        mi = verilogutil.parse_module(flines)
        if not mi:
            print('[VerilogModule.get_connect] Unable to parse current module')
            return (decl,ac,wc)
        signal_dict = {}
        for ti in mi['port']:
            signal_dict[ti['name']] = ti
        for ti in mi['signal']:
            signal_dict[ti['name']] = ti
        # print ('Signal Dict = ' + str(signal_dict))
        signal_dict_text = ''
        for (name,ti) in signal_dict.items():
            signal_dict_text += name+'\n'
        # print ('Signal Dict = ' + signal_dict_text)
        if pm['param']:
            param_dict = {p['name']:p['value'] for p in pm['param']}
        else:
            param_dict = {}
        # print(param_dict)
        # Add signal declaration
        for p in pm['port']:
            pname = p['name']
            #Remove suffix/prefix of port name
            for prefix in port_prefix:
                if pname.startswith(prefix):
{'fraction_non_alphanumeric': 0.13253218463948307, 'fraction_numerical': 0.0039649517842283025, 'mean_word_length': 1.925605040813404, 'pattern_counts': {'":': 8, '<': 17, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1631530', 'n_tokens_mistral': 12724, 'n_tokens_neox': 12274, 'n_words': 2746}