diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100755 index 0000000..589c00c --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,9 @@ +# Author + +* Jimmy Situ + +# Contributors + +* Jun Cao +* Jiangmiao Lei +* Lin Zhang diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100755 index 0000000..8bc0179 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,15 @@ +# Changelog + +## 1.11 - 11/14/2016 +## 1.22 - 01/07/2017 +## 1.23 - 01/15/2017 +## 2.0 - 02/24/2017 +## 2.10 - 03/19/2017 +## 2.11 - 04/24/2017 +## 2.12 - 05/04/2017 +## 2.20 - 07/09/2017 +## 2.21 - 09/04/2017 +## 2.22 - 28/07/2018 +## 2.23 - 05/09/2018 +## 2.24 - 08/09/2018 +## 2.24.1- 10/27/2018 diff --git a/InlineLib/FrameLib.py b/InlineLib/FrameLib.py new file mode 100755 index 0000000..c8f94b7 --- /dev/null +++ b/InlineLib/FrameLib.py @@ -0,0 +1,527 @@ +""" +http://www.vim.org/scripts/script.php?script_id=5494 +""" +#=============================================================================== +# BSD 2-Clause License + +# Copyright (c) 2016, CaoJun +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# * 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. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#=============================================================================== + +import sys +import re +try: + import vim +except: + pass +import os +import re +import Lib.GLB as GLB +G = GLB.G +from Lib.BaseLib import * +from InlineLib.ViewLib import * +import Lib.FileInfLib as FileInfLib + +#------------------------------------------------------------------------------- +# topo/checkpoint/basemodule line range ,in frame file +#------------------------------------------------------------------------------- +def get_frame_range_inf(): + fram_file_link = [] + if G['Frame_Inf']['Frame_Path'] in G["VimBufferLineFileLink"]: + fram_file_link = G["VimBufferLineFileLink"][G['Frame_Inf']['Frame_Path']] + # get topo range , default 0,0 + has_topo = False + has_check_point = False + has_base_module = False + topo_range = [0, 0] + check_point_range = [0, 0] + base_module_range = [0, 0] + for i,link in enumerate(fram_file_link): + if link and (link['action_parm_dic']['Type'] == 'topo'): + if not has_topo: + topo_range[0] = i + has_topo = True + topo_range[1] = i + if link and (link['action_parm_dic']['Type'] == 'check_point'): + if not has_check_point: + check_point_range[0] = i + has_check_point = True + check_point_range[1] = i + if link and (link['action_parm_dic']['Type'] == 'base_module'): + if not has_base_module: + base_module_range[0] = i + has_base_module = True + base_module_range[1] = i + # if no topo ,next topo start at [0,0] + if not has_topo: + topo_range = [0, 0] + # check point initial start at topo end + 2 + if not has_check_point: + check_point_range[0] = topo_range[1] + 2 + check_point_range[1] = topo_range[1] + 2 + # base module initial at check point end + 2 + if not has_base_module: + base_module_range[0] = check_point_range[1] + 2 + base_module_range[1] = check_point_range[1] + 2 + return { 'topo_range' : tuple(topo_range) + ,'check_point_range' : tuple(check_point_range) + ,'base_module_range' : tuple(base_module_range) + ,'has_topo' : has_topo + ,'has_check_point' : has_check_point + ,'has_base_module' : has_base_module } + + +#------------------------------------------------------------------------------- +# topo relate function +#------------------------------------------------------------------------------- +# for a module's submodule, sep function module and base module +def get_sub_func_base_module(module_name): + func_instance_name_submodule_name_list = [] + base_submodule_name_to_instance_list_dic = {} + # first get current module inf + module_inf = FileInfLib.get_module_inf(module_name) + if not module_inf: + return func_instance_name_submodule_name_list, base_submodule_name_to_instance_list_dic + # get the subcall inf list for current module + if module_inf.setdefault('subcall_instance_list',None) == None: + assert(FileInfLib.module_inf_add_subcall_instance_list(module_inf)) + subcall_instance_list = module_inf['subcall_instance_list'] + for subcall_inf in subcall_instance_list: + instance_name = subcall_inf['instance_name'] + submodule_name = subcall_inf['submodule_name'] + if submodule_name in G['BaseModuleInf']['BaseModules']: + base_submodule_name_to_instance_list_dic.setdefault(submodule_name,[]) + base_submodule_name_to_instance_list_dic[submodule_name].append(instance_name) + else: + func_instance_name_submodule_name_list.append( (instance_name, submodule_name) ) + return func_instance_name_submodule_name_list, base_submodule_name_to_instance_list_dic + + +# this function used to get the subcall topo inf +def get_fram_topo_sub_inf(topo_module, cur_level): + sub_level = cur_level + 1 + topo_prefix = G['Frame_Inf']['FoldLevelSpace'] * sub_level + topo_datas = [] + topo_links = [] + func_instance_name_submodule_name_list, base_submodule_name_to_instance_list_dic = get_sub_func_base_module(topo_module) + # first deal sub func module, show "inst(module)" + for instance_name, submodule_name in func_instance_name_submodule_name_list: + # gen show data + c_str = '%s%s(%s)'%(topo_prefix, instance_name, submodule_name) + topo_datas.append(c_str) + # gen link + c_topo_link_parm = { + 'Type' : 'topo' # fold_unfold_frame_action() + ,'fold_level' : sub_level # fold_unfold_frame_action() + ,'fold_status' : 'off' # fold_unfold_frame_action() + ,'topo_module' : submodule_name # fold_unfold_frame_action() + ,'go_module_name' : submodule_name # for fold_unfold_frame_action() + } + c_topo_link = gen_hyperlink(['go_module_action', 'fold_unfold_frame_action'], c_topo_link_parm, Type = 'topo') + c_topo_link['payload_dic']['topo_instance_name'] = instance_name + submodule_inf = FileInfLib.get_module_inf(submodule_name) + if submodule_inf: + # show cur module, then all submodule, last call set to cur module + FileInfLib.set_module_last_call_inf(submodule_name, topo_module, instance_name) + topo_links.append(c_topo_link) + # deal base modules + base_submodule_name_list = list(base_submodule_name_to_instance_list_dic) + base_submodule_name_list.sort() + if len(base_submodule_name_list) > 0: + # deal base , show "module(n)" + # add one to sep func and base + topo_datas.append(topo_prefix+'------') + c_topo_link_parm = { + 'Type' : 'topo' # fold_unfold_frame_action() + ,'fold_level' : sub_level # fold_unfold_frame_action() + ,'fold_status' : 'fix' # fold_unfold_frame_action() + ,'topo_module' : '' # fold_unfold_frame_action() + ,'go_module_name' : '' # for fold_unfold_frame_action() + } + c_topo_link = gen_hyperlink(['go_module_action', 'fold_unfold_frame_action'], c_topo_link_parm, Type = 'topo') + c_topo_link['payload_dic']['topo_instance_name'] = '' + topo_links.append(c_topo_link) + for submodule_name in base_submodule_name_list: + # deal data + instance_num = len(base_submodule_name_to_instance_list_dic[submodule_name]) + c_str = '%s%s(%d)'%(topo_prefix, submodule_name, instance_num) + topo_datas.append(c_str) + # deal link + c_topo_link_parm = { + 'Type' : 'topo' # fold_unfold_frame_action() + ,'fold_level' : sub_level # fold_unfold_frame_action() + ,'fold_status' : 'off' # fold_unfold_frame_action() + ,'topo_module' : submodule_name # fold_unfold_frame_action() + ,'go_module_name' : submodule_name # for fold_unfold_frame_action() + } + c_topo_link = gen_hyperlink(['go_module_action', 'fold_unfold_frame_action'], c_topo_link_parm, Type = 'topo') + c_topo_link['payload_dic']['topo_instance_name'] = '' + topo_links.append(c_topo_link) + assert( len(topo_datas) == len(topo_links) ) + return topo_datas, topo_links + +# used to generate topo data and file_link for current module +def gen_top_topo_data_link(topo_module): + topo_datas = [] + topo_links = [] + topo_module_inf = FileInfLib.get_module_inf(topo_module) + if not topo_module_inf: + PrintDebug('Error: get topo module name %s, should has module inf !'%(topo_module)) + return topo_datas, topo_links + TopTopoLevel = G['TopoInf']['TopFoldLevel'] + TopTopoPrefix = G['Frame_Inf']['FoldLevelSpace'] * TopTopoLevel + # add first topo line + topo_datas.append(TopTopoPrefix + 'ModuleTopo:') + topo_link_parm = { + 'Type' : 'topo' # fold_unfold_frame_action() + ,'fold_level' : TopTopoLevel - 1 # fold_unfold_frame_action() + ,'fold_status' : 'on' # fold_unfold_frame_action() + ,'topo_module' : '' # fold_unfold_frame_action() + ,'go_module_name' : '' # for fold_unfold_frame_action() + } + topo_link = gen_hyperlink(['go_module_action', 'fold_unfold_frame_action'], topo_link_parm, Type = 'topo') + topo_link['payload_dic']['topo_instance_name'] = '' + topo_links.append(topo_link) + # add cur module name + topo_datas.append(TopTopoPrefix + topo_module + ':') + topo_link_parm = { + 'Type' : 'topo' # fold_unfold_frame_action() + ,'fold_level' : TopTopoLevel # fold_unfold_frame_action() + ,'fold_status' : 'on' # fold_unfold_frame_action() + ,'topo_module' : topo_module # fold_unfold_frame_action() + ,'go_module_name' : topo_module # for fold_unfold_frame_action() + } + topo_link = gen_hyperlink(['go_module_action', 'fold_unfold_frame_action'], topo_link_parm, Type = 'topo') + topo_link['payload_dic']['topo_instance_name'] = '' + topo_links.append(topo_link) + # gen current module sub function module, and base module topo inf + sub_module_data, sub_module_link = get_fram_topo_sub_inf(topo_module, 0) + topo_datas = topo_datas + sub_module_data + topo_links = topo_links + sub_module_link + assert( len(topo_datas) == len(topo_links) ) + return topo_datas, topo_links + +# this function used to show the module's topo +def show_topo(topo_module_name = ''): + if not topo_module_name: + cursor_inf = get_cur_cursor_inf() + if cursor_inf['hdl_type'] != 'verilog': + # if not in support file type(verilog,vhdl) just return + PrintReport("Note: Current only support verilog !") + return False + # get current module inf + cur_module_inf = None + cur_line_inf = FileInfLib.get_file_line_inf(cursor_inf['line_num'], cursor_inf['file_path']) + if cur_line_inf: + cur_module_inf = cur_line_inf['module_inf'] + # current not at module lines, just return + if not cur_module_inf: + PrintReport("Note: Current cursor not in valid module !") + return False + topo_module_name = cur_module_inf['module_name'] + else: + topo_module_inf = FileInfLib.get_module_inf(topo_module_name) + if not topo_module_inf: + PrintReport("Note: show topo module %s not have database !"%(topo_module_name)) + return False + # if frame not show ,show it + Show(G['Frame_Inf']['Frame_Path']) + # current module must has module inf + G['TopoInf']['CurModule'] = topo_module_name # note cur topo name for refresh + range_inf = get_frame_range_inf() + has_topo = range_inf['has_topo'] + topo_range = range_inf['topo_range'] + topo_data, topo_link = gen_top_topo_data_link(topo_module_name) + # del old topo, add new topo + if has_topo: # del + edit_vim_buffer_and_file_link( path = G['Frame_Inf']['Frame_Path'], mode = 'del', del_range = topo_range ) + edit_vim_buffer_and_file_link( G['Frame_Inf']['Frame_Path'], topo_data, topo_link, add_index = topo_range[0] ) + return True + +# this function call by refrech_topo , to iteration open fold +def iteration_fold_on_module(inst_module_pairs, base_modules): + c_frame_range_inf = get_frame_range_inf() + if not c_frame_range_inf['has_topo']: + return + frame_path = G['Frame_Inf']['Frame_Path'] + c_topo_range = c_frame_range_inf['topo_range'] + assert( frame_path in G['VimBufferLineFileLink'] ) + c_topo_links = G['VimBufferLineFileLink'][frame_path][c_topo_range[0] : c_topo_range[1] + 1] + for i,lk in enumerate(c_topo_links): + if not( lk and (lk['action_parm_dic']['fold_status'] == 'off') and lk['action_parm_dic']['topo_module'] ): + continue + if lk['payload_dic']['topo_instance_name']: + c_inst_module_pair = (lk['payload_dic']['topo_instance_name'], lk['action_parm_dic']['topo_module']) + if c_inst_module_pair in inst_module_pairs: + fold_frame_line(i+c_topo_range[0], lk['action_parm_dic']['fold_level'], 'topo', lk['action_parm_dic']['topo_module']) + iteration_fold_on_module(inst_module_pairs, base_modules) + return + else: + if lk['action_parm_dic']['topo_module'] in base_modules: + fold_frame_line(i+c_topo_range[0], lk['action_parm_dic']['fold_level'], 'topo', lk['action_parm_dic']['topo_module']) + iteration_fold_on_module(inst_module_pairs, base_modules) + return + return + + +# this function used refresh topo, when add a base module +def refresh_topo(): + # get all folded module or inst pair + old_frame_range_inf = get_frame_range_inf() + if not old_frame_range_inf['has_topo']: + return + frame_path = G['Frame_Inf']['Frame_Path'] + old_topo_range = old_frame_range_inf['topo_range'] + assert(frame_path in G['VimBufferLineFileLink']) + old_topo_links = G['VimBufferLineFileLink'][frame_path][old_topo_range[0] + 2 : old_topo_range[1] + 1] + old_fold_inst_module_pairs = set() + old_fold_base_modules = set() + for lk in old_topo_links: + if not( lk and (lk['action_parm_dic']['fold_status'] == 'on') and lk['action_parm_dic']['topo_module'] ): + continue + if lk['payload_dic']['topo_instance_name']: + old_fold_inst_module_pairs.add( (lk['payload_dic']['topo_instance_name'], lk['action_parm_dic']['topo_module']) ) + else: + if lk['action_parm_dic']['topo_module'] in G['BaseModuleInf']['BaseModules']: + old_fold_base_modules.add(lk['action_parm_dic']['topo_module']) + # start new topo + new_topo_module_name = G['TopoInf']['CurModule'] + show_topo(new_topo_module_name) + # iteration opened old folded topo + iteration_fold_on_module(old_fold_inst_module_pairs, old_fold_base_modules) + +#------------------------------------------------------------------------------- +# checkpoint relate function +#------------------------------------------------------------------------------- +# this function used to show check points +def show_check_point(fold = True): + frame_data = [] + frame_link = [] + # if frame not show ,show it + Show(G['Frame_Inf']['Frame_Path']) + # add initial line + level = G['CheckPointInf']['TopFoldLevel'] + show_str = G['Frame_Inf']['FoldLevelSpace']*level + 'CheckPoints:' + file_link_parm = { + 'Type' : 'check_point' # fold_unfold_frame_action() + ,'fold_level' : level # fold_unfold_frame_action() + ,'fold_status' : 'on' # fold_unfold_frame_action() + ,'go_path' : '' # go_file_action() + ,'go_pos' : () # go_file_action() + ,'go_word' : '' # go_file_action() + } + file_link = gen_hyperlink(['go_file_action', 'fold_unfold_frame_action'], file_link_parm, Type = 'check_point') + frame_data.append(show_str) + frame_link.append(file_link) + # add check points + range_inf = get_frame_range_inf() + has_check_point = range_inf['has_check_point'] + check_point_range = range_inf['check_point_range'] + if fold: + for cp in G['CheckPointInf']['CheckPoints']: + frame_data.append(cp['key']) + frame_link.append(cp['link']) + else: + frame_link[-1]['fold_status'] = 'off' + assert(len(frame_data)==len(frame_link)) + # del old cp, add new cp + if has_check_point: # del + edit_vim_buffer_and_file_link( path = G['Frame_Inf']['Frame_Path'], mode = 'del', del_range = check_point_range ) + edit_vim_buffer_and_file_link( G['Frame_Inf']['Frame_Path'], frame_data, frame_link, add_index = check_point_range[0] ) + return True + + +#------------------------------------------------------------------------------- +# basemodule relate function +#------------------------------------------------------------------------------- +# update function base information, no need added each times +def update_base_module_pickle(): + pkl_output = open(G['VTagsPath'] + '/pickle/all_basemodule_name_set.pkl','wb') + pickle.dump(G['BaseModuleInf']['BaseModules'], pkl_output) + pkl_output.close() + +# this function used to get base module frame data/link +def get_fram_base_module_inf(): + datas = [] + links = [] + base_module_level = G['BaseModuleInf']['TopFoldLevel'] + 1 + base_module_space = G['Frame_Inf']['FoldLevelSpace'] * base_module_level + base_module_name_list = list(G['BaseModuleInf']['BaseModules']) + base_module_name_list.sort() + for base_module_name in base_module_name_list: + show_str = base_module_space + base_module_name + file_link_parm = { + 'Type' : 'base_module' # fold_unfold_frame_action() + ,'fold_level' : base_module_level # fold_unfold_frame_action() + ,'fold_status' : 'fix' # fold_unfold_frame_action() + ,'go_module_name' : base_module_name # for fold_unfold_frame_action() + } + file_link = gen_hyperlink(['go_module_action', 'fold_unfold_frame_action'], file_link_parm, Type = 'base_module') + datas.append(show_str) + links.append(file_link) + assert(len(datas) == len(links)) + return datas, links + +# this function used to show the current base module +def show_base_module(fold = True): + frame_data = [] + frame_link = [] + # if frame not show ,show it + Show(G['Frame_Inf']['Frame_Path']) + # add initial line + level = G['BaseModuleInf']['TopFoldLevel'] + show_str = G['Frame_Inf']['FoldLevelSpace']*level + 'BaseModules:' + file_link_parm = { + 'Type' : 'base_module' # fold_unfold_frame_action() + ,'fold_level' : level # fold_unfold_frame_action() + ,'fold_status' : 'on' # fold_unfold_frame_action() + ,'go_module_name' : '' # for fold_unfold_frame_action() + } + file_link = gen_hyperlink(['go_module_action', 'fold_unfold_frame_action'], file_link_parm, Type = 'base_module') + frame_data.append(show_str) + frame_link.append(file_link) + # add check points + range_inf = get_frame_range_inf() + has_base_module = range_inf['has_base_module'] + base_module_range = range_inf['base_module_range'] + cp_data = [] + cp_link = [] + if fold: + cp_data, cp_link = get_fram_base_module_inf() + else: + frame_link[-1]['fold_status'] = 'off' + frame_data = frame_data + cp_data + frame_link = frame_link + cp_link + assert(len(frame_data) == len(frame_link)) + # del old cp, add new cp + if has_base_module: # del + base_module_range = ( base_module_range[0] + 1, base_module_range[1] ) + frame_data = frame_data[1:] + frame_link = frame_link[1:] + edit_vim_buffer_and_file_link( path = G['Frame_Inf']['Frame_Path'], mode = 'del', del_range = base_module_range ) + edit_vim_buffer_and_file_link( G['Frame_Inf']['Frame_Path'], frame_data, frame_link, add_index = base_module_range[0] ) + return True + +#------------------------------------------------------------------------------- +# fold/unfold function +#------------------------------------------------------------------------------- +def unfold_frame_line(frame_links, frame_line, cur_frame_level, cur_frame_type): + assert(frame_links[frame_line]['action_parm_dic']['fold_status'] == 'on') + assert(G['Frame_Inf']['Frame_Path'] in G['VimBufferLineFileLink']) + G['VimBufferLineFileLink'][ G['Frame_Inf']['Frame_Path'] ][frame_line]['action_parm_dic']['fold_status'] = 'off' + unfold_end_line_num = frame_line + for i in range(frame_line+1, len(frame_links)): + # if cur not have file link, then cur is unflod end + if not frame_links[i]: + unfold_end_line_num = i - 1 + break + # if has file link ,but not topo inf then unflod end + if frame_links[i]['action_parm_dic']['Type'] != cur_frame_type: + unfold_end_line_num = i - 1 + break + # if is topo , but level <= cur level then unflod end + if frame_links[i]['action_parm_dic']['fold_level'] <= cur_frame_level: + unfold_end_line_num = i - 1 + break + unfold_end_line_num = i + # if cur module has no sub module then just return + if unfold_end_line_num == frame_line: + return True + # else edit the frame buffer and file link, del the unflod lines + if unfold_end_line_num > frame_line: + edit_vim_buffer_and_file_link( path = G['Frame_Inf']['Frame_Path'], mode = 'del', del_range = (frame_line + 1, unfold_end_line_num) ) + return True + # else some trouble + assert(0),'shold not happen !' + + +# this function used to fold frame line +def fold_frame_line(frame_line, cur_frame_level, cur_frame_type, cur_module_name = ''): + assert(G['Frame_Inf']['Frame_Path'] in G['VimBufferLineFileLink']) + G['VimBufferLineFileLink'][ G['Frame_Inf']['Frame_Path'] ][frame_line]['action_parm_dic']['fold_status'] = 'on' + if cur_frame_type == 'topo': + # if cur is ModuleTopo: line, show refresh topo + if cur_frame_level == G['TopoInf']['TopFoldLevel'] - 1: + topo_module_name = G['TopoInf']['CurModule'] + show_topo(topo_module_name) + return + if not cur_module_name: + PrintReport('Note: cur topo line has no module name !') + return + if not FileInfLib.get_module_inf(cur_module_name): + PrintReport('Note: current module: \"%s\" not found in design !'%(cur_module_name)) + return + # get cur module sub module inf + sub_topo_data, sub_topo_link = get_fram_topo_sub_inf(cur_module_name, cur_frame_level) + # add cur module topo inf to frame + edit_vim_buffer_and_file_link( G['Frame_Inf']['Frame_Path'], sub_topo_data, sub_topo_link, add_index = frame_line + 1 ) + elif cur_frame_type == 'check_point': + show_check_point() + elif cur_frame_type == 'base_module': + show_base_module() + else: + PrintReport('Note: no operation in this line !') + return + +# this function used to the frame window fold +def frame_line_fold_operation(frame_line): + frame_path = G['Frame_Inf']['Frame_Path'] + if frame_path not in G['VimBufferLineFileLink']: + PrintReport('Note: cur frame line no fold operation !') + return + frame_links = G['VimBufferLineFileLink'][frame_path] + if frame_line >= len(frame_links): + PrintReport('Note: cur frame line no fold operation !') + return + cur_line_link = frame_links[frame_line] + if not cur_line_link : + PrintReport('Note: cur frame line no fold operation !') + return + intime_parms_dic = { + 'frame_line' : frame_line + ,'frame_links' : frame_links } + cur_line_link['intime_parms_dic'] = intime_parms_dic + do_hyperlink(cur_line_link, 'fold_unfold_frame_action') + +def fold_unfold_frame_action(intime_parms_dic, Type, fold_level, fold_status, topo_module = ''): + frame_line = intime_parms_dic['frame_line'] + frame_links = intime_parms_dic['frame_links'] + if fold_status == 'off': + fold_frame_line(frame_line, fold_level, Type, topo_module) + elif fold_status == 'on': + unfold_frame_line(frame_links, frame_line, fold_level, Type) + else: + PrintReport('Note: cur frame line no fold operation !') + return +register_hyperlink_action( fold_unfold_frame_action, description = 'this link function fold or unfold frame lines' ) + +# hyperlink action go_module_action +def go_module_action( go_module_name ): + module_inf = FileInfLib.get_module_inf(go_module_name) + if not module_inf: + PrintReport('Warning: module:%s not find in design !'%(go_module_name)) + return False + go_win( module_inf['file_path'], module_inf['module_name_match_pos'], go_module_name) + return True +register_hyperlink_action( go_module_action, description = 'this link function goto the module define position' ) diff --git a/InlineLib/InlineAPI.py b/InlineLib/InlineAPI.py new file mode 100755 index 0000000..8ab3ef8 --- /dev/null +++ b/InlineLib/InlineAPI.py @@ -0,0 +1,566 @@ +""" +http://www.vim.org/scripts/script.php?script_id=5494 +""" +#=============================================================================== +# BSD 2-Clause License + +# Copyright (c) 2016, CaoJun +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# * 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. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#=============================================================================== + +import os +import sys +# import vim, when gen vtags it will no vim,so use try +try: + import vim +except: + pass +# import normal lib +import re +import Lib.GLB as GLB +G = GLB.G +from Lib.BaseLib import * +from InlineLib.ViewLib import * +import Lib.CodeLib as CodeLib +import InlineLib.FrameLib as FrameLib +import Lib.FileInfLib as FileInfLib + +# open snapshort wins +if G['InlineActive'] and G['EnvSnapshortWinsInf']: + # snapshort's work window trace + OldOpenWinTrace = [p for p in G['WorkWin_Inf']['OpenWinTrace']] + # add cur buffer in cur work window trace + G['WorkWin_Inf']['OpenWinTrace'].insert(0,vim.current.buffer.name) + # for all snapshort window(include work win, hold win ...) + # first open saved window + for w_inf in G['EnvSnapshortWinsInf']: + c_path = w_inf['path'] + c_cursor = w_inf['cursor'] + if os.path.isfile(c_path): + Open(c_path) + # if new open window not in old work win, del from new work win + # trace , maybe hold win, reload need also hold + if c_path not in OldOpenWinTrace: + if G['WorkWin_Inf']['OpenWinTrace'] and G['WorkWin_Inf']['OpenWinTrace'][-1] == c_path: + del G['WorkWin_Inf']['OpenWinTrace'][-1] + else: + PrintReport('Note: reload file not exit ! file: %s'%(c_path)) + # resize the reload window + for w_inf in G['EnvSnapshortWinsInf']: + c_size = w_inf['size'] + c_path = w_inf['path'] + if os.path.isfile(c_path): + Open(c_path) + vim.current.window.width = c_size[0] + vim.current.window.height = c_size[1] + # because base module may be changed so refresh topo and show base + if G['Frame_Inf']['Frame_Path'] in [ w.buffer.name for w in vim.windows]: + FrameLib.refresh_topo() + FrameLib.show_base_module() + PrintReport('Note: reload snapshort finish !') +elif G['InlineActive']: + # treat the first win as work win , if cur win is hdl code, and add first trace point + first_cursor_inf = get_cur_cursor_inf() + if first_cursor_inf['hdl_type'] == 'verilog': + G['WorkWin_Inf']['OpenWinTrace'].append(first_cursor_inf['file_path']) + add_trace_point() + #print('vtags initialization successfully !') + + +# shortcut key: gi +def go_into_submodule(): + cursor_inf = get_cur_cursor_inf() + subcall_cursor_inf = CodeLib.get_subcall_pos_inf( cursor_inf['file_path'], cursor_inf['pos'], cursor_inf['codes'] ) + if not subcall_cursor_inf: + PrintReport('Note: not found module instance at current cursor line, do-nothing !') + return True + assert(subcall_cursor_inf['subcall_inf']) + # for case subcall recoginize not accuracy, may because of '`ifdef, `endif ...' + if subcall_cursor_inf['subcall_inf']['inaccuracy']: + PrintReport('Warning: carefull the trace result, subcall module:%s, instance:%s inaccuracy !'%(subcall_cursor_inf['subcall_inf']['module_name'], subcall_cursor_inf['subcall_inf']['instance_name'])) + # it is subcall must go subcall, has 2 case: + # case 1, cursor on valid io connect, go submodule io line + # case 2, cursor not on valid io connect, just go submodule define line + submodule_name = subcall_cursor_inf['subcall_inf']['submodule_name'] + submodule_inf = FileInfLib.get_module_inf(submodule_name) + if not submodule_inf: + PrintReport('Warning: can not find module:%s define in design, do-nothing !'%(submodule_name)) + return True + go_path = submodule_inf['file_path'] + go_word = submodule_name + go_pos = submodule_inf['module_name_match_pos'] + if subcall_cursor_inf['submodule_io_inf']: # case 1 + go_word = subcall_cursor_inf['submodule_io_inf']['name'] + go_pos = subcall_cursor_inf['submodule_io_inf']['name_pos'] + # note upper module information + subcall_instance_name = subcall_cursor_inf['subcall_inf']['instance_name'] + cur_module_inf = subcall_cursor_inf['module_inf'] + cur_module_name = cur_module_inf['module_name'] + FileInfLib.set_module_last_call_inf(submodule_name, cur_module_name, subcall_instance_name) + # go submodule + add_trace_point() + go_win( go_path, go_pos, go_word) + + +def try_go_into_submodule(): + if not G['InlineActive']: return + if G['Debug']: + go_into_submodule() + else: + try: go_into_submodule() + except: pass + + +# shortcut key: gu +def go_upper_module(): + cursor_inf = get_cur_cursor_inf() + # get cur module name + cur_module_name = '' + cur_module_inf = None + cur_line_inf = FileInfLib.get_file_line_inf(cursor_inf['line_num'], cursor_inf['file_path']) + if cur_line_inf: + cur_module_inf = cur_line_inf['module_inf'] + if not cur_module_inf: + PrintReport('Note: current cursor not in valid module, do-nothing !') + return + cur_module_name = cur_module_inf['module_name'] + # get cur module last call upper module inf + cur_last_call_inf = FileInfLib.get_module_last_call_inf(cur_module_name) + already_get_upper = False + if cur_last_call_inf: + upper_subcall_inf = cur_last_call_inf['upper_subcall_inf'] + upper_module_path = upper_subcall_inf['file_path'] + upper_submodule_name_match_pos = upper_subcall_inf['submodule_name_match_pos'] + upper_instance_name = upper_subcall_inf['instance_name'] + add_trace_point() + go_win( upper_module_path, upper_submodule_name_match_pos, upper_instance_name) + already_get_upper = True + # even has upper, also should list all the poss upper + line_and_link_list = CodeLib.get_upper_module_line_and_link_list( cur_module_name ) + if line_and_link_list: + # i = 0 + link_list = [] + line_list = [] + # pre inf + line_list.append('Knock "" to choise upper module you want: ') + line_list.append('') + link_list.append( {} ) + link_list.append( {} ) + line_list += line_and_link_list['line_list'] + link_list += line_and_link_list['link_list'] + mounted_line_inf = MountPrintLines(line_list, label = 'Possible Upper', link_list = link_list) + mounted_line_list = mounted_line_inf['line_list'] + mounted_link_list = mounted_line_inf['link_list'] + # add a empty line below + mounted_line_list.append('') + mounted_link_list.append({}) + if link_list: + add_trace_point() + assert( len(mounted_line_list) == len(mounted_link_list) ) + PrintReport(mounted_line_list, mounted_link_list, MountPrint = True ) + if not already_get_upper: + if len(line_and_link_list['line_list']) == 1: + for link in link_list: + if link: + do_hyperlink(link, ['go_file_action', 'add_module_last_call_action']) # first valid link + break + else: + # len(mounted_line_list) + 1 is the lines relative to the last report line + # -4 is skip first 4 unused line + go_win( G['Report_Inf']['Report_Path'] , (-(len(mounted_line_list) + 1 -4), 49) ) + else: + PrintReport('Note: module %s not called by upper module before !'%(cur_module_name)) + return + +def try_go_upper_module(): + if not G['InlineActive']: return + if G['Debug']: + go_upper_module() + else: + try: go_upper_module() + except: pass + +# shortcut key: mt +def print_module_trace(): + cursor_inf = get_cur_cursor_inf() + # get cur module name + cur_module_name = '' + cur_module_inf = None + cur_line_inf = FileInfLib.get_file_line_inf(cursor_inf['line_num'], cursor_inf['file_path']) + if cur_line_inf: + cur_module_inf = cur_line_inf['module_inf'] + if not cur_module_inf: + PrintReport('Note: current cursor not in valid module, do-nothing !') + return + cur_module_name = cur_module_inf['module_name'] + # recursion get all trace + full_traces = [] + FileInfLib.recursion_get_module_trace(cur_module_name, [], full_traces) + print_strs = [] + i_offset = 0 # used to control multi same trace case + for i, traces in enumerate(full_traces): + c_trace_strs = [ traces[-1]['cur_module_name'] ] + for t in traces[::-1]: + c_str = '%s(%s)'%(t['instance_name'], t['submodule_name']) + if c_str not in c_trace_strs: + c_trace_strs.append(c_str) + else: + i_offset -= 1 + print_strs.append( '%d : %s'%(i + i_offset, ' -> '.join(c_trace_strs) ) ) + mounted_line_list = MountPrintLines(print_strs, label = 'Module Trace')['line_list'] + mounted_line_list.append('') + mounted_line_list.append('') + PrintReport(mounted_line_list, MountPrint = True ) + # edit_vim_buffer_and_file_link(G['Report_Inf']['Report_Path'], mounted_line_list) + return + +def try_print_module_trace(): + if not G['InlineActive']: return + if G['Debug']: + print_module_trace() + else: + try: print_module_trace() + except: pass + +# shortcut key: +def trace_signal_sources(): + if G['IgnoreNextSpaceOp']: + G['IgnoreNextSpaceOp'] = False + PrintDebug('Trace: not do this trace source op ,bucause is come from unknow reason !') + return + cursor_inf = get_cur_cursor_inf() + trace_signal_name = cursor_inf['word'] + if not trace_signal_name: + PrintReport("Note: current cursor not on signal name, do-nothing !") + return + # case0: if cur cursor on a macro, go macro define + if CodeLib.trace_glb_define_signal('source', cursor_inf): return + # case1: if cur cursor on io signal, need cross to upper module + if CodeLib.trace_io_signal('source', cursor_inf, report_level = 0 ): return + # case2: if cur cursor on module call io line go to submodule io + if CodeLib.trace_signal_at_subcall_lines('source', cursor_inf, report_level = 0 ): return + # case3: trace signal same as pre trace signal, just show next result + if (G['TraceInf']['LastTraceSource']['Path'] == cursor_inf['file_path']) \ + and (G['TraceInf']['LastTraceSource']['SignalName'] == trace_signal_name) \ + and (G['TraceInf']['LastTraceSource']['ValidLineRange'][0] <= cursor_inf['line_num']) \ + and (G['TraceInf']['LastTraceSource']['ValidLineRange'][1] >= cursor_inf['line_num']) : + show_next_trace_result('source') + PrintDebug('Trace: trace_signal_sources, just show next result!') + return + # case4: trace a new normal(not io, sub call io) signal + if CodeLib.trace_normal_signal('source', cursor_inf): return + +def try_trace_signal_sources(): + if not G['InlineActive']: return + if G['Debug']: + trace_signal_sources() + else: + try: trace_signal_sources() + except: pass + + +# shortcut key: +def trace_signal_destinations(): + if G['IgnoreNextSpaceOp']: + G['IgnoreNextSpaceOp'] = False + PrintDebug('Trace: not do this trace source op ,bucause is come from unknow reason !') + return + cursor_inf = get_cur_cursor_inf() + trace_signal_name = cursor_inf['word'] + if not trace_signal_name: + PrintReport("Note: Current cursor not on signal name, can not trace dest!") + return + # case0: if cur cursor on io signal, need cross to upper module + if CodeLib.trace_io_signal('dest', cursor_inf, report_level = 0 ): return + # case1: if cur cursor on module call io line go to submodule io + if CodeLib.trace_signal_at_subcall_lines('dest', cursor_inf): return + # case2: trace signal same as pre trace signal, just show next result + if (G['TraceInf']['LastTraceDest']['Path'] == cursor_inf['file_path']) \ + and (G['TraceInf']['LastTraceDest']['SignalName'] == trace_signal_name) \ + and (G['TraceInf']['LastTraceDest']['ValidLineRange'][0] <= cursor_inf['line_num']) \ + and (G['TraceInf']['LastTraceDest']['ValidLineRange'][1] >= cursor_inf['line_num']) : + show_next_trace_result('dest') + return + # case3: if cur cursor on a macro, go macro define + if CodeLib.trace_glb_define_signal('dest', cursor_inf): return + # case4: trace a new normal(not io, sub call io) signal + CodeLib.trace_normal_signal('dest', cursor_inf) + +def try_trace_signal_destinations(): + if not G['InlineActive']: return + if G['Debug']: + trace_signal_destinations() + else: + try: trace_signal_destinations() + except: pass + + +# shortcut key: +def roll_back(): + if G['IgnoreNextSpaceOp']: + G['IgnoreNextSpaceOp'] = False + PrintDebug('Trace: not do this trace source op ,bucause is come from unknow reason !') + return + cur_nonius = G['OpTraceInf']['Nonius'] - 1 + TracePoints = G['OpTraceInf']['TracePoints'] + # if reach to the oldest trace point just return + if cur_nonius < 0: + PrintReport("Note: roll backed to the oldest trace point now !") + return + # go to the trace point + cur_point = TracePoints[cur_nonius] + G['OpTraceInf']['Nonius'] = cur_nonius + go_win( cur_point['path'], cur_point['pos'], cur_point['key']) + return + +def try_roll_back(): + if not G['InlineActive']: return + if G['Debug']: + roll_back() + else: + try: roll_back() + except: pass + + +# shortcut key: +def go_forward(): + if G['IgnoreNextSpaceOp']: + G['IgnoreNextSpaceOp'] = False + PrintDebug('Trace: not do this trace source op ,bucause is come from unknow reason !') + return + cur_nonius = G['OpTraceInf']['Nonius'] + 1 + TracePoints = G['OpTraceInf']['TracePoints'] + if cur_nonius >= len(TracePoints): + PrintReport("Note: go forward to the newest trace point now !") + return + cur_point = TracePoints[cur_nonius] + G['OpTraceInf']['Nonius'] = cur_nonius + go_win( cur_point['path'], cur_point['pos'], cur_point['key']) + return + +def try_go_forward(): + if not G['InlineActive']: return + if G['Debug']: + go_forward() + else: + try: go_forward() + except: pass + + +# shortcut key: +def space_operation(): + if G['IgnoreNextSpaceOp']: + G['IgnoreNextSpaceOp'] = False + PrintDebug('Trace: not do this trace source op ,bucause is come from unknow reason !') + return + cursor_inf = get_cur_cursor_inf() + # if cur in Frame or Report, show file link files + if cursor_inf['file_path'] in [ G['Frame_Inf']['Frame_Path'], G['Report_Inf']['Report_Path'] ]: + # bug fix if no link add before here will out of range + cur_frame_link = {} + if cursor_inf['line_num'] < len( G['VimBufferLineFileLink'][cursor_inf['file_path']] ): + cur_frame_link = G['VimBufferLineFileLink'][cursor_inf['file_path']][cursor_inf['line_num']] + add_trace_point() + if not cur_frame_link: + PrintReport('Note: No file link in current line ! ') + return + # for single_action_link + if cur_frame_link['type'] == 'single_action_link': + do_hyperlink(cur_frame_link) + add_trace_point() + return + # for topo and base_module, need refresh + if cur_frame_link['type'] in ['topo', 'base_module']: + do_hyperlink(cur_frame_link, 'go_module_action') + add_trace_point() + return + # for check_point + if cur_frame_link['type'] == 'check_point': + do_hyperlink(cur_frame_link, 'go_file_action') + add_trace_point() + return + # for possible_upper + if cur_frame_link['type'] == 'possible_upper': + do_hyperlink(cur_frame_link, ['go_file_action', 'add_module_last_call_action']) + add_trace_point() + return + # for possible_trace_upper + if cur_frame_link['type'] == 'possible_trace_upper': + do_hyperlink(cur_frame_link, ['add_module_last_call_action', 'trace_io_signal_action']) + add_trace_point() + return + return + +def try_space_operation(): + if not G['InlineActive']: return + if G['Debug']: + space_operation() + else: + try: space_operation() + except: pass + + +# shortcut key: v +def show_frame(): + G["IgnoreNextSpaceOp"] = G['FixExtraSpace'] + if cur_in_frame(): + cursor_line = vim.current.window.cursor[0] - 1 + FrameLib.frame_line_fold_operation(cursor_line) + else: + FrameLib.show_topo() + FrameLib.show_check_point(False) + FrameLib.show_base_module(False) + return + +def try_show_frame(): + if not G['InlineActive']: return + if G['Debug']: + show_frame() + else: + try: show_frame() + except: pass + return + + +# shortcut key: h +def hold_current_win(): + cur_path = vim.current.buffer.name + # just del current win frome work win, then will not auto close current win + for i,path in enumerate(G['WorkWin_Inf']['OpenWinTrace']): + if cur_path == path: + del G['WorkWin_Inf']['OpenWinTrace'][i] + break + +def try_hold_current_win(): + if not G['InlineActive']: return + if G['Debug']: + hold_current_win() + else: + try: hold_current_win() + except: pass + + +# shortcut key: c +def add_check_point(): + G["IgnoreNextSpaceOp"] = G['FixExtraSpace'] + cursor_inf = get_cur_cursor_inf() + level = G['CheckPointInf']['TopFoldLevel'] + 1 + key = G['Frame_Inf']['FoldLevelSpace']*level + cursor_inf['word'] + link_parm = { + 'Type' : 'check_point' # fold_unfold_frame_action() + ,'fold_level' : level # fold_unfold_frame_action() + ,'fold_status' : 'fix' # fold_unfold_frame_action() + ,'go_path' : cursor_inf['file_path'] # go_file_action() + ,'go_pos' : cursor_inf['pos'] # go_file_action() + ,'go_word' : cursor_inf['word'] # go_file_action() + ,'last_modify_time' : os.path.getmtime( cursor_inf['file_path'] ) + } + link = gen_hyperlink(['go_file_action', 'fold_unfold_frame_action'], link_parm, Type = 'check_point') + G['CheckPointInf']['CheckPoints'].insert(0, {'key': key, 'link': link }) + if len(G['CheckPointInf']['CheckPoints']) > G['CheckPointInf']['MaxNum']: + del G['CheckPointInf']['CheckPoints'][-1] + FrameLib.show_check_point() + +def try_add_check_point(): + if not G['InlineActive']: return + if G['Debug']: + add_check_point() + else: + try: add_check_point() + except: pass + + +# shortcut key: b +def add_base_module(): + G["IgnoreNextSpaceOp"] = G['FixExtraSpace'] + cursor_inf = get_cur_cursor_inf() + cursor_module = cursor_inf['word'] + if not cursor_module: + PrintReport('Note: cursor not on a valid word ! ') + return + if cursor_module in G['BaseModuleInf']['BaseModules']: + PrintReport('Note: module %s is already base module ! '%(cursor_module)) + return + G['BaseModuleInf']['BaseModules'].add(cursor_module) + FrameLib.update_base_module_pickle() + FrameLib.show_base_module() + FrameLib.refresh_topo() + +def try_add_base_module(): + if not G['InlineActive']: return + if G['Debug']: + add_base_module() + else: + try: add_base_module() + except: pass + + +# shortcut key: d +def del_operation(): + if not cur_in_frame(): + PrintReport('Note: Cur file no del function ! ') + return + cur_path = vim.current.buffer.name + cur_line_num = vim.current.window.cursor[0] - 1 + cur_file_link = G['VimBufferLineFileLink'][cur_path][cur_line_num] + if not cur_file_link: + PrintReport('Note: Cur line no del function ! ') + return + # delete a check point, if link has path means a valid link + if (cur_file_link['action_parm_dic']['Type'] == 'check_point') and (cur_file_link['action_parm_dic']['fold_level'] > G['CheckPointInf']['TopFoldLevel']): + G["IgnoreNextSpaceOp"] = G['FixExtraSpace'] + check_point_begin_line_num = FrameLib.get_frame_range_inf()['check_point_range'][0] + del_index = cur_line_num - check_point_begin_line_num - 1 + del G['CheckPointInf']['CheckPoints'][ del_index ] + FrameLib.show_check_point() + return + # del a base module + if (cur_file_link['action_parm_dic']['Type'] == 'base_module') and (cur_file_link['action_parm_dic']['fold_level'] > G['BaseModuleInf']['TopFoldLevel']): + G["IgnoreNextSpaceOp"] = G['FixExtraSpace'] + G['BaseModuleInf']['BaseModules'].remove(cur_file_link['action_parm_dic']['go_module_name']) + FrameLib.update_base_module_pickle() + FrameLib.show_base_module() + FrameLib.refresh_topo() + return + PrintReport('Note: Cur line no del function ! ') + +def try_del_operation(): + if not G['InlineActive']: return + if G['Debug']: + del_operation() + else: + try: del_operation() + except: pass + +# shortcut key: s +def try_save_env_snapshort(): + if not G['InlineActive']: return + if G['Debug']: + if G['SaveEnvSnapshort_F'](): + PrintReport('Note: save env snapshort success !') + else: + try: + if G['SaveEnvSnapshort_F'](): + PrintReport('Note: save env snapshort success !') + except: pass diff --git a/InlineLib/ViewLib.py b/InlineLib/ViewLib.py new file mode 100755 index 0000000..ceaae33 --- /dev/null +++ b/InlineLib/ViewLib.py @@ -0,0 +1,377 @@ +""" +http://www.vim.org/scripts/script.php?script_id=5494 +""" +#=============================================================================== +# BSD 2-Clause License + +# Copyright (c) 2016, CaoJun +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# * 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. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#=============================================================================== + +import sys +import re +try: + import vim +except: + pass +import os +import re +import Lib.GLB as GLB +G = GLB.G +from Lib.BaseLib import * +from InlineLib.WinLib import * + +# hyperlink action go_file_action +def go_file_action( go_path, go_pos = (), go_word = '', last_modify_time = 0.0, report_stale = True): + if not os.path.exists(go_path): + PrintReport('Warning: file not exists ! file:%s'%(go_path)) + return False + if report_stale and (last_modify_time != 0.0): + if not check_inf_valid(go_path, last_modify_time): + PrintReport('Warning: file modified before, this link maybe stale ! file: %s'%(go_path)) + go_win( go_path, go_pos, go_word) + return True +register_hyperlink_action( go_file_action, description = 'this link function goto the dest file position' ) + + +#------------------------------------------------------------------------------- +# here used to temporary store current cursor location +# and return when need +#------------------------------------------------------------------------------- +SnapshotStack = [] + +# save cursor location +def snapshort_push(): + cur_cursor = vim.current.window.cursor + cur_pos = (cur_cursor[0]-1, cur_cursor[1]) # minus 1 because cursor start from 1, and lines start from 0 + cur_line = vim.current.buffer[cur_pos[0]] + cur_word = get_full_word(cur_line, cur_pos[1]) + cur_file_path = vim.current.buffer.name + cur_snapshort = {"path": cur_file_path, "pos":cur_pos, "key":cur_word} + SnapshotStack.append(cur_snapshort) + +# reload cursor location +def snapshort_pop(): + pop_snapshort = SnapshotStack[-1] + del SnapshotStack[-1] + go_win( pop_snapshort['path'], pop_snapshort['pos'], pop_snapshort['key']) + +#------------------------------------------------------------------------------- +# this function used to let the path show in window, but maybe not cursor window +#------------------------------------------------------------------------------- +def Show(path): # just show frame win , and not go to that window + Act_Win = Cur_Act_Win() + if path not in Act_Win: + snapshort_push() + Open(path) + snapshort_pop() + return + +#------------------------------------------------------------------------------- +# this function used to add a trace point used by , +#------------------------------------------------------------------------------- +def add_trace_point(): + cur_cursor = vim.current.window.cursor + cur_file_path = vim.current.buffer.name + if cur_file_path in [ G['Frame_Inf']['Frame_Path'], G['Report_Inf']['Report_Path'] ]: + return + cur_pos = (cur_cursor[0]-1, cur_cursor[1]) # minus 1 because cursor start from 1, and lines start from 0 + cur_line_num = cur_pos[0] + cur_line = vim.current.buffer[cur_line_num] + cur_word = get_full_word(cur_line, cur_pos[1]) + cur_trace_point = {"path": cur_file_path, "pos":cur_pos, "key":cur_word} + cur_nonius = G['OpTraceInf']['Nonius'] + TracePoints = G['OpTraceInf']['TracePoints'] + # when roll back, and add from middle of queue, just clear old trace point after cur insert index + # | 0 | 1 | 2 | 3 | 4 | + # ^ if len 5, nonius <= 3 then del 4 + if cur_nonius <= (len(TracePoints) - 2): + del TracePoints[cur_nonius + 1 : ] + # add a new point to TracePoints + # if cur add is equ to pre not add + if not TracePoints: + TracePoints.append(cur_trace_point) + else: + pre_point = TracePoints[-1] + if cur_trace_point != pre_point: + TracePoints.append(cur_trace_point) + # if length bigger than TraceDepth del + TraceDepth = G['OpTraceInf']['TraceDepth'] + while (len(TracePoints) > TraceDepth): + del TracePoints[0] + # if add new point ,nonius assign to len(TracePoints) + # | 0 | 1 | 2 | 3 | 4 | + # ^ because roll back will first sub 1 + G['OpTraceInf']['Nonius'] = len(TracePoints) + + +#------------------------------------------------------------------------------- +# this function used to get current cursor information +#------------------------------------------------------------------------------- +def get_cur_cursor_inf(): + cur_cursor = vim.current.window.cursor + cur_line_num = cur_cursor[0] - 1 # minus 1 because cursor start from 1, and lines start from 0 + cur_colm_num = cur_cursor[1] + cur_line = vim.current.buffer[cur_line_num] + cur_word = get_full_word(cur_line, cur_cursor[1]) + cur_codes = vim.current.buffer + cur_file_path = vim.current.buffer.name + cur_hdl_type = get_file_hdl_type(cur_file_path) + return { 'cursor' : cur_cursor + ,'pos' : (cur_line_num, cur_colm_num) + ,'line_num' : cur_line_num + ,'colm_num' : cur_colm_num + ,'line' : cur_line + ,'word' : cur_word + ,'file_path' : cur_file_path + ,'hdl_type' : cur_hdl_type + ,'codes' : cur_codes } + + +#------------------------------------------------------------------------------- +# this function edit the vim buffer and corresponding file_links +# +# vim_buffer file_links +# ---------------- ---------------------------- +# |0: | |0: {} | +# |1: topo_module | |1: {type:topo,....} | +# |2: check_point | |2: {type:check_point,...} | +# |3: ... | ----> |3: {} | +# |4: trace_result | |4: {type:trace_result,...} | +# | | | | +# | | | | +# ---------------- ----------------------------- +# +# mode : add/del +# add_index: 0 add to top, -1: add to bottom, -2: clear all and add, other add to line n +# del_range: int del line, range del reange +# +# care file_link add when valid in, so before will no filelink or on file_link index +#------------------------------------------------------------------------------- +def edit_vim_buffer_and_file_link(path = '', data = [], file_link = [], mode = 'add', add_index = -1, del_range = None): + if mode == 'add' and add_index != 2 and data == []: + return + # because of vim bug reason, edit buffer not current cursor in may + # happen add line to current cursor buffer, so we make sure when edit, cursor + # must in current buffer win, after edit will go back to pre cousor pos + edit_current_buffer = False + if path == vim.current.buffer.name: + edit_current_buffer = True + if not edit_current_buffer: + snapshort_push() + Open(path) + assert(path == vim.current.buffer.name) + edit_buffer = vim.current.buffer + if mode == 'del': # deal delete case + # if del buffer has valid file_link, del too + if type(del_range) is int: + del_range = [del_range, del_range] + assert(type(del_range) in [ tuple, list ]),'Error: unsupport del_range: %s.'%(del_range.__str__()) + if path in G["VimBufferLineFileLink"]: + del G["VimBufferLineFileLink"][path][ del_range[0] : del_range[1]+1 ] + del edit_buffer[ del_range[0] : del_range[1]+1 ] + elif mode == 'add': # deal add/insert case + # if data is a string, put in list + if type(data) == str: + data = [ data ] + # start add file_link when the first valid file_link comes with data. + # if a valid file_link comes, file_link will align to buffer data, + # for empty line file_link set to None + need_add_file_link = False + if file_link: + assert( len(data) == len(file_link) ),'%s,%s'%(data.__str__(),file_link.__str__()) + # if current path no file link before, or buffer length > file_link length + # add file_link and align it + G["VimBufferLineFileLink"].setdefault(path, [] ) + G["VimBufferLineFileLink"][path] = G["VimBufferLineFileLink"][path] + [None]*( len(edit_buffer) - len(G["VimBufferLineFileLink"][path]) ) + need_add_file_link = True + if add_index == 0: # add to top + edit_buffer.append(data, 0) + if need_add_file_link: + G["VimBufferLineFileLink"][path] = file_link + G["VimBufferLineFileLink"][path] + elif add_index == -1: # add to bottom + edit_buffer.append(data) + if need_add_file_link: + G["VimBufferLineFileLink"][path] = G["VimBufferLineFileLink"][path] + file_link + elif add_index == -2: # clear all and add top + del edit_buffer[:] + if data != []: + edit_buffer.append(data) + del edit_buffer[:1] + if need_add_file_link and data != []: + G["VimBufferLineFileLink"][path] = file_link + else: # insert to add_index + assert(type(add_index) == int and add_index > 0) + # careful insert maybe insert to n, which n > len(buffer) need add '' + edit_buffer_len = len(edit_buffer) + if edit_buffer_len < add_index : + edit_buffer.append( ['']*(add_index - edit_buffer_len + 1) ) # add 1 because when fold last line, first del then add, del will change cursor if no empty line below + if need_add_file_link: + G["VimBufferLineFileLink"][path] = G["VimBufferLineFileLink"][path] + [None]*(add_index - edit_buffer_len + 1) + # insert data + edit_buffer.append(data, add_index) + if need_add_file_link: + G["VimBufferLineFileLink"][path] = G["VimBufferLineFileLink"][path][:add_index] + file_link + G["VimBufferLineFileLink"][path][add_index:] + else: + assert(0),'Error: unsupport mode: %s.'%(mode.__str__()) + # save the change for frame and report + # if cur_in_frame() or cur_in_report(): + # vim.command('w!') + # go back to pre cursor if not edit current buffer + if not edit_current_buffer: + snapshort_pop() + + +def cur_in_frame(): + return vim.current.buffer.name == G['Frame_Inf']['Frame_Path'] + +def cur_in_report(): + return vim.current.buffer.name == G['Report_Inf']['Report_Path'] + + +# this function used to show report to report window +# because of vim bug reason, edit buffer not current cursor in may +# happen add line to current cursor buffer, so we make sure when edit, cursor +# must in current buffer win, after edit will go back to pre cousor pos +def PrintReport(show = '', file_link = {}, spec_case = '', mode = 'a', report_level = 0, MountPrint = False ): + if report_level > 0 or (not G['InlineActive']): + PrintDebug(show) + return + # config use this control weather print report + if not G['ShowReport']: + return + # jump to report file, before edit + has_self_snap_short = False + if not cur_in_report(): + snapshort_push() + Open(G['Report_Inf']['Report_Path']) + has_self_snap_short = True + # MountPrint + if MountPrint: + if file_link: + assert(len(show) == len(file_link)) + edit_vim_buffer_and_file_link(G['Report_Inf']['Report_Path'], show, file_link) + else: + edit_vim_buffer_and_file_link(G['Report_Inf']['Report_Path'], show) + elif show: # show_str = show + assert(type(show) == str) + if show[0:8] == 'Warning:' or show[0:6] == 'Error:': + edit_vim_buffer_and_file_link( G['Report_Inf']['Report_Path'], '' ) + show_len = len(show) + edit_vim_buffer_and_file_link( G['Report_Inf']['Report_Path'], '*'*(80) ) + edit_vim_buffer_and_file_link( G['Report_Inf']['Report_Path'], show ) + edit_vim_buffer_and_file_link( G['Report_Inf']['Report_Path'], '*'*(80) ) + edit_vim_buffer_and_file_link( G['Report_Inf']['Report_Path'], '' ) + else: + edit_vim_buffer_and_file_link( G['Report_Inf']['Report_Path'], show ) + # show trace source result + if spec_case == 'source': + line_list = [] + link_list = [] + for Sure in G['TraceInf']['LastTraceSource']['Sure']: + line_list.append( Sure['show'].strip('\n') ) + link_list.append( Sure['file_link'] ) + line_list.append('') + link_list.append({}) + if G['TraceInf']['LastTraceSource']['Maybe']: + line_list.append('\nlable\n:Maybe Source') + link_list.append({}) + for Maybe in G['TraceInf']['LastTraceSource']['Maybe']: + line_list.append( Maybe['show'] ) + link_list.append( Maybe['file_link'] ) + MountPrint = MountPrintLines(line_list, 'Trace Source', link_list, end_star = False) + edit_vim_buffer_and_file_link(G['Report_Inf']['Report_Path'], "") + edit_vim_buffer_and_file_link(G['Report_Inf']['Report_Path'], MountPrint['line_list'], MountPrint['link_list']) + edit_vim_buffer_and_file_link(G['Report_Inf']['Report_Path'], "") + # show trace dest result + elif spec_case == 'dest': + line_list = [] + link_list = [] + for Sure in G['TraceInf']['LastTraceDest']['Sure']: + line_list.append( Sure['show'].strip('\n') ) + link_list.append( Sure['file_link'] ) + line_list.append('') + link_list.append({}) + if G['TraceInf']['LastTraceDest']['Maybe']: + line_list.append('\nlable\n:Maybe Dest') + link_list.append({}) + for Maybe in G['TraceInf']['LastTraceDest']['Maybe']: + line_list.append( Maybe['show'] ) + link_list.append( Maybe['file_link'] ) + MountPrint = MountPrintLines(line_list, 'Trace Dest', link_list, end_star = False) + edit_vim_buffer_and_file_link(G['Report_Inf']['Report_Path'], "") + edit_vim_buffer_and_file_link(G['Report_Inf']['Report_Path'], MountPrint['line_list'], MountPrint['link_list']) + edit_vim_buffer_and_file_link(G['Report_Inf']['Report_Path'], "") + # go report to the last line, and return + assert(cur_in_report()) + vim.current.window.cursor = (len(vim.current.buffer) - 1 , 0) + # vim.command('w!') + vim.command('hid') + Open(G['Report_Inf']['Report_Path']) + if has_self_snap_short: + snapshort_pop() + +# this function used to go next trace result, when repeat use trace option to +# same signal +def show_next_trace_result( trace_type ): + if trace_type == 'source': + cur_show_index = G['TraceInf']['LastTraceSource']["ShowIndex"] + sure_source_len = len(G['TraceInf']['LastTraceSource']['Sure']) + maybe_source_len = len(G['TraceInf']['LastTraceSource']['Maybe']) + if (sure_source_len + maybe_source_len) == 0: + PrintReport('Note: not find source !') + return + cur_file_link = {} + if cur_show_index < sure_source_len: + cur_file_link = G['TraceInf']['LastTraceSource']['Sure'][cur_show_index]['file_link'] + else: + cur_file_link = G['TraceInf']['LastTraceSource']['Maybe'][cur_show_index - sure_source_len]['file_link'] + G['TraceInf']['LastTraceSource']["ShowIndex"] = (cur_show_index + 1) % (sure_source_len + maybe_source_len) + add_trace_point() + do_hyperlink(cur_file_link) + # go_win( cur_file_link['go_path'], cur_file_link['go_pos'], cur_file_link['go_word'] ) + elif trace_type == 'dest': + cur_show_index = G['TraceInf']['LastTraceDest']["ShowIndex"] + sure_dest_len = len(G['TraceInf']['LastTraceDest']['Sure']) + maybe_dest_len = len(G['TraceInf']['LastTraceDest']['Maybe']) + if (sure_dest_len + maybe_dest_len) == 0: + PrintReport('Note: not find dest !') + return + cur_file_link = {} + if cur_show_index < sure_dest_len: + cur_file_link = G['TraceInf']['LastTraceDest']['Sure'][cur_show_index]['file_link'] + else: + cur_file_link = G['TraceInf']['LastTraceDest']['Maybe'][cur_show_index - sure_dest_len]['file_link'] + G['TraceInf']['LastTraceDest']["ShowIndex"] = (cur_show_index + 1) % (sure_dest_len + maybe_dest_len) + add_trace_point() + do_hyperlink(cur_file_link) + # go_win( cur_file_link['go_path'], cur_file_link['go_pos'], cur_file_link['go_word']) + else: + assert(0) + + + + + + diff --git a/InlineLib/WinLib.py b/InlineLib/WinLib.py new file mode 100755 index 0000000..1cedf0d --- /dev/null +++ b/InlineLib/WinLib.py @@ -0,0 +1,265 @@ +""" +http://www.vim.org/scripts/script.php?script_id=5494 +""" +#=============================================================================== +# BSD 2-Clause License + +# Copyright (c) 2016, CaoJun +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# * 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. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#=============================================================================== + +try: + import vim +except: + pass +import sys +import re +import os +import Lib.GLB as GLB +G = GLB.G +from Lib.BaseLib import * + +# this fulction used to reset Frame win and Report win size to: +#------------------------------------------------------------- +#| | | +#| | | +#| | | +#| | | +#| | Work | +#| FRAME | | +#| | | +#| | | +#| | | +#| | | +#| |-----------------------------------------|----------- +#| | | | +#| | Report | Report_Win_y +#| | | | +#|-----------------|------------------------------------------------------ +#|<- Frame_Win_x ->| +def Reset_Win_Size(): + cur_act_wins = Cur_Act_Win() + if G['Report_Inf']['Report_Path'] in cur_act_wins: + Jump_To_Win(G['Report_Inf']['Report_Path']) + vim.command('wincmd J') + vim.current.window.height = G['Report_Inf']['Report_Win_y'] + if G['Frame_Inf']['Frame_Path'] in cur_act_wins: + Jump_To_Win(G['Frame_Inf']['Frame_Path']) + vim.command('wincmd H') + vim.current.window.width = G['Frame_Inf']['Frame_Win_x'] + return + +# this function used to del closed window in the open window trace +def Refresh_OpenWinTrace(): + cur_act_win_path = Cur_Act_Win() + cur_act_work_win_path = cur_act_win_path - set([ G["Report_Inf"]["Report_Path"], G["Frame_Inf"]["Frame_Path"] ]) + i = 0 + while i < len(G['WorkWin_Inf']['OpenWinTrace']) : + c_path = G['WorkWin_Inf']['OpenWinTrace'][i] + if c_path not in cur_act_work_win_path: + del G['WorkWin_Inf']['OpenWinTrace'][i] + else: + i += 1 + return + +# this function get all the opened window file path +def Cur_Act_Win(): + Act_Win = set() + for w in vim.windows: + Act_Win.add(w.buffer.name) + return Act_Win + +# this functhon used to open a file +# if file already open jump to that window +# if file not opened, and opened window not beyond max open win number, open a new window +# if file not opened, and opened window beyond max open win number, close a old and open a new window +def Open(path): + Act_Win = Cur_Act_Win() + if path in Act_Win: # win has open and just jump to than window + Jump_To_Win(path) + elif path == G['Frame_Inf']["Frame_Path"]: + Open_Frame_Win() + Reset_Win_Size() + elif path == G['Report_Inf']["Report_Path"]: + Open_Report_Win() + Reset_Win_Size() + else: + Open_Work_Win(path) + Jump_To_Win(path) + assert(vim.current.buffer.name == path) + +# if current path already opened, jump to the path windows +def Jump_To_Win(path): + cur_act_wins = Cur_Act_Win() + assert(path in cur_act_wins) + start_path = vim.current.buffer.name + if start_path == path: + return + vim.command('wincmd w') + cur_path = vim.current.buffer.name + while cur_path != start_path: + if cur_path == path: + break + vim.command("wincmd w") + cur_path = vim.current.buffer.name + assert(vim.current.buffer.name == path),'vim.current.buffer.name: %s, path: %s'%(vim.current.buffer.name, path) + +# open a new frame window at most left +def Open_Frame_Win(): + G['VimBufferLineFileLink'].setdefault(G["Frame_Inf"]["Frame_Path"],[{}]) + vim.command("vertical topleft sp " + G["Frame_Inf"]["Frame_Path"]) + +# open a new report window at most bottom +def Open_Report_Win(): + G['VimBufferLineFileLink'].setdefault(G["Report_Inf"]["Report_Path"],[{}]) + vim.command("bot sp " + G["Report_Inf"]["Report_Path"]) + if G["Frame_Inf"]["Frame_Path"] in Cur_Act_Win(): + Jump_To_Win(G["Frame_Inf"]["Frame_Path"]) + vim.command('wincmd H') + Jump_To_Win(G["Report_Inf"]["Report_Path"]) + +# check if file already opened in other window +def has_swp_file(path): + seprate_path_and_file = re.match('(?P.*/)?(?P[^/]+)$', path) + assert(seprate_path_and_file) + # get file path + file_path = '' + if seprate_path_and_file.group('path'): + file_path = seprate_path_and_file.group('path') + # get swp file name + swp_file_name = '.%s.swp'%(seprate_path_and_file.group('file')) + # check if exist swp file + if os.path.exists(file_path + swp_file_name): + return True + return False + +# open a new work window, if opened beyond threshold, close a old win for new +def Open_Work_Win(path): + # weather need resize report and frame win + need_resize_frame_report_win = False + # path must valid + assert(os.path.isfile(path)) + # refresh open work win trace + Refresh_OpenWinTrace() + # leave at most G['WorkWin_Inf']['MaxNum'] work win + win_num_need_to_close = len(G['WorkWin_Inf']['OpenWinTrace']) - G['WorkWin_Inf']['MaxNum'] + for i in range(win_num_need_to_close): + win_path_need_close = G['WorkWin_Inf']['OpenWinTrace'][i] + Jump_To_Win(win_path_need_close) + vim.command('q') + need_resize_frame_report_win = True + del G['WorkWin_Inf']['OpenWinTrace'][i] + # if has work win + cur_work_win_num = len(G['WorkWin_Inf']['OpenWinTrace']) + if cur_work_win_num > 0: + # case 0: has work win, and num less than max + # just go last work win, and vsp a new win + if cur_work_win_num < G['WorkWin_Inf']['MaxNum']: + Jump_To_Win(G['WorkWin_Inf']['OpenWinTrace'][-1]) + # special for file already opened just open in read only mode + if has_swp_file(path): + print('found ".%s.swp" so open in read only mode !'%(path)) + vim.command('vsp | view '+path) + else: + vim.command('vsp '+path) + need_resize_frame_report_win = True + else: # case 1: opened all work win, just replace the oldest open work win + Jump_To_Win(G['WorkWin_Inf']['OpenWinTrace'][0]) + # special for file already opened just open in read only mode + if has_swp_file(path): + print('found ".%s.swp" so open in read only mode !'%(path)) + vim.command('e | view '+path) + else: + vim.command('e '+path) + del G['WorkWin_Inf']['OpenWinTrace'][0] # replace [0], just del old + else: # cur no work win + cur_act_win_paths = Cur_Act_Win() + cur_act_hold_wins = cur_act_win_paths - set([G["Report_Inf"]["Report_Path"], G["Frame_Inf"]["Frame_Path"]]) + # if has hold win, go hold win, vsp + if cur_act_hold_wins: + Jump_To_Win(list(cur_act_hold_wins)[0]) + # special for file already opened just open in read only mode + if has_swp_file(path): + print('found ".%s.swp" so open in read only mode !'%(path)) + vim.command('vsp | view '+path) + else: + vim.command('vsp '+path) + need_resize_frame_report_win = True + elif G["Report_Inf"]["Report_Path"] in cur_act_win_paths: + # if no hold win, has report , go report sp new + Jump_To_Win(G["Report_Inf"]["Report_Path"]) + # special for file already opened just open in read only mode + if has_swp_file(path): + print('found ".%s.swp" so open in read only mode !'%(path)) + vim.command('sp | view '+path) + else: + vim.command('sp '+path) + need_resize_frame_report_win = True + else: + vim.command('vsp '+path) + need_resize_frame_report_win = True + if need_resize_frame_report_win: + Reset_Win_Size() + Jump_To_Win(path) + # finial add path to trace + assert(vim.current.buffer.name == path) + G['WorkWin_Inf']['OpenWinTrace'].append(path) + +# go to the window and cursor to pos, and used search highlight word +def go_win( path = '', pos = (), highlight_word = ''): + if not path or not os.path.isfile(path): + return + Open(path) + # fix bug for search instance_name[4:0] + valid_highlight = re.search('\w+',highlight_word) + if valid_highlight: + valid_highlight_word = valid_highlight.group() + vim.current.window.cursor = (1,0) # search from top in case match to left vim warning + vim.command('/\c\<'+valid_highlight_word+'\>') + if pos: + max_x = len( vim.current.buffer ) - 1 # len( vim.current.buffer[ len(vim.current.buffer) - 1 ] ) ) + valid_pos = [max_x, None] + # row + if (pos[0] < 0) and ((max_x + pos[0]) >= 0): + valid_pos[0] = max_x + pos[0] + 1 # -1 is the last line + if (pos[0] >= 0) and (pos[0] <= max_x): + valid_pos[0] = pos[0] + # colum + max_y = len( vim.current.buffer[ valid_pos[0] ] ) + valid_pos[1] = max_y + if (pos[1] < 0) and ((max_y + pos[1]) >= 0): + valid_pos[1] = max_y + pos[1] + 1 # -1 is the last char + if (pos[1] >= 0) and (pos[1] <= max_y): + valid_pos[1] = pos[1] + vim.current.window.cursor = (valid_pos[0] + 1, valid_pos[1] ) + + +# this function used to open file and go to file lines +def open_file_separately(file_path, jump_to_line): + assert(os.path.isfile(file_path)) + assert(type(jump_to_line) == int and jump_to_line >= 0 ) + os.system('gvim %s +%d'%(file_path, jump_to_line + 1)) # add 1 because gvim line start from 1 + return True + + diff --git a/InlineLib/__init__.py b/InlineLib/__init__.py new file mode 100755 index 0000000..e69de29 diff --git a/LICENSE b/LICENSE new file mode 100755 index 0000000..26f42c7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,26 @@ +BSD 2-Clause License + +Copyright (c) 2018, Jimmy Situ +Copyright (c) 2016, CaoJun +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* 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. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Lib/BaseLib.py b/Lib/BaseLib.py new file mode 100755 index 0000000..fd09a73 --- /dev/null +++ b/Lib/BaseLib.py @@ -0,0 +1,454 @@ +""" +http://www.vim.org/scripts/script.php?script_id=5494 +""" +#=============================================================================== +# BSD 2-Clause License + +# Copyright (c) 2018, Jimmy Situ +# Copyright (c) 2016, CaoJun +# All rights reserved. + +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: + +# * 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. + +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#=============================================================================== + +import sys +import os +import re +import pickle +import inspect +import Lib.GLB as GLB +G = GLB.G +# function to print debug +PrintDebug = G['PrintDebug_F'] + +def MountPrintLines(line_list, label = '', link_list = None, length = 80, Print = False, end_star = True, no_end_line = False): + final_line_list = [] + final_link_list = [] + # get real width + max_line_length = length + if end_star: + for l in line_list: + # not support change line + # '\n' should not in MountPrintLines line list, so we use \nlable\n as lable id + if l.find('\n') != -1: + if re.match('\nlable\n:', l): + continue + assert( False ) + if (len(l) + 4) > max_line_length: + max_line_length = (len(l) + 4) # +4 because need add - '| ' --- ' |' + # start generate first two line + final_line_list.append( '_' * max_line_length ) + final_line_list.append( '_' * ((max_line_length - len(label))>>1) + label + '_' * ((max_line_length - len(label) + 1)>>1)) + final_line_list.append( '*' + ' ' * (max_line_length - 2) + '*') + final_link_list.append( {} ) + final_link_list.append( {} ) + final_link_list.append( {} ) + # add input line_list to frame + for i,l in enumerate(line_list): + # special care for sub lable + match_sub_label = re.match('^\nlable\n:(?P.*)', l) + if match_sub_label: + sub_label = match_sub_label.group('sub_label') + final_line_list.append( '*' + '-' * ((max_line_length -2 - len(sub_label))>>1) + sub_label + '-' * ((max_line_length -2 - len(sub_label) + 1)>>1) + '*') + else:# normal case + l = '* ' + l + ' ' * ( max_line_length - len(l) - 4 ) + ' *' + final_line_list.append( l ) + # for link + if link_list: + final_link_list.append( link_list[i] ) + else: + final_link_list.append( {} ) + # add end line + final_line_list.append( '*_' + '_' * (max_line_length - 4) + '_*') + final_link_list.append( {} ) + else: + # start generate first two line + final_line_list.append( '_' * max_line_length ) + final_line_list.append( '_' * ((max_line_length - len(label))>>1) + label + '_' * ((max_line_length - len(label) + 1)>>1)) + final_line_list.append( '*') + final_link_list.append( {} ) + final_link_list.append( {} ) + final_link_list.append( {} ) + # add input line_list to frame + for i,l in enumerate(line_list): + # special care for sub lable + match_sub_label = re.match('^\nlable\n:(?P.*)', l) + if match_sub_label: + sub_label = match_sub_label.group('sub_label') + final_line_list.append( '*' + '-' * ((max_line_length -2 - len(sub_label))>>1) + sub_label + '-' * ((max_line_length -2 - len(sub_label) + 1)>>1) + '-') + else:# normal case + l = '* ' + l + ' ' * ( max_line_length - len(l) - 4 ) + ' ' + final_line_list.append( l ) + # for link + if link_list: + final_link_list.append( link_list[i] ) + else: + final_link_list.append( {} ) + # add end line + if not no_end_line: + final_line_list.append( '**' + '*' * (max_line_length - 4) + '**') + final_link_list.append( {} ) + else: + final_line_list.append( '' ) + final_link_list.append( {} ) + assert( len(final_line_list) == len(final_link_list) ) + if Print: + for l in final_line_list: print(l) + return {'line_list': final_line_list, 'link_list': final_link_list} + + +def PrintTime(prefix,t): + if False: + time_str = re.sub('\..*','',str(t*1000000)) + PrintDebug(prefix+time_str) + +def get_full_word(line, y): + pre_part = ( re.match('^(?P
\w*)',(line[:y])[::-1]).group('pre') )[::-1]
+    post_part = re.match('^(?P\w*)', line[y:]).group('post')
+    return pre_part + post_part
+
+def get_file_path_postfix(file_path):
+    if type(file_path) != str:
+        return ''
+    split_by_dot = file_path.split('.')
+    if len(split_by_dot) < 2 : # which means file_path has no postfix
+        return ''
+    # post_fix = split_by_dot[-1].lower() # postfix don't care case
+    post_fix = split_by_dot[-1]           # postfix care case
+    return post_fix
+
+def get_file_hdl_type(file_path):
+    postfix = get_file_path_postfix(file_path)
+    if postfix in G['SupportVHDLPostfix']:
+        return 'vhdl'
+    elif postfix in G['SupportVerilogPostfix']:
+        return 'verilog'
+    else:
+        return ''
+
+def get_valid_code(code_line, mode = ['note','macro_code']):
+    if 'note' in mode:
+        pos = code_line.find('//')
+        if pos != -1:
+            code_line = code_line[:pos]
+        pos = code_line.find('/*')
+        if pos != -1:
+            code_line = re.sub('/\*.*?\*/', '', code_line)
+    if 'macro_code' in mode:
+        if re.match('\s*(`define|`ifdef|`ifndef|`else|`endif)',code_line):
+            code_line = ''
+    return code_line
+
+
+
+
+# this use egrep to find all the signal appear pos,code_line
+def search_verilog_code_use_grep(key, path, row_range = ()):
+    search_result = []
+    match_lines    = os.popen('egrep -n -h \'(^|\W)%s(\W|$)\' %s'%(key, path)).readlines()
+    for l in match_lines:
+        l = l.strip('\n')
+        split0 = l.split(':')
+        line_num   = int(split0[0]) - 1
+        code_line  = ':'.join(split0[1:])
+        if row_range and ( line_num not in range(row_range[0], row_range[1]+1 ) ):
+            continue
+        # del note see if has key
+        s0 = re.search('(?P
^|\W)%s(\W|$)'%(key), re.sub('//.*','',code_line) )
+        if s0:
+            colum_num  = s0.span()[0] + len(s0.group('pre'))
+            match_pos  = (line_num, colum_num)
+            line       = code_line
+            search_result.append( (match_pos, line) )
+    return search_result
+
+
+def show_progress_bar( i, i_max, show_char = '#', show_width = 20):
+    i += 1 # count from 1
+    i_max_len = len(str(i_max))
+    i_len     = len(str(i))
+    i_str     = ' '*(i_max_len-i_len)+str(i)
+    i_max_str = str(i_max)
+    prefix    = '%s/%s: '%(i_str,i_max_str)
+    pass_str  = show_char * int(((i*show_width)/i_max))
+    empty_str = ' '*(show_width - len(pass_str))
+    progress_bar = '[%s%s]'%(pass_str,empty_str)
+    tool_len  = len(prefix) + show_width
+    sys.stdout.write(' '*tool_len + '\r')
+    sys.stdout.flush()
+    sys.stdout.write(prefix + progress_bar)
+
+def get_vhdl_full_line(codes, start_pos, direction):
+    pass
+
+def get_verilog_pre_full_line(codes, start_pos):
+    pre_full_line = ''
+    start_x, start_y = start_pos
+    start_line = codes[start_x][:start_y+1]
+    start_line = start_line.strip('\n')
+    start_line = re.sub('//.*','',start_line)
+    colon_y    = start_line.rfind(';')
+    if colon_y != -1:
+        pre_full_line = start_line[colon_y+1:]
+    else:
+        pre_full_line = start_line
+        for i in range(start_x-1,-1,-1):
+            t_line = codes[i].strip('\n')
+            t_line = re.sub('//.*', '', t_line)
+            t_colon_y = t_line.rfind(';')
+            if t_colon_y != -1:
+                pre_full_line = t_line[t_colon_y+1:] + ' ' + pre_full_line
+                break
+            else:
+                pre_full_line = t_line + ' ' +pre_full_line
+    return pre_full_line
+
+def get_verilog_post_full_line(codes, start_pos):
+    post_full_line = ''
+    start_x, start_y = start_pos
+    start_line = codes[start_x][start_y:]
+    start_line = start_line.strip('\n')
+    start_line = re.sub('//.*','',start_line)
+    colon_y    = start_line.find(';')
+    if colon_y != -1:
+        pre_full_line = start_line[:colon_y+1]
+    else:
+        pre_full_line = start_line
+        for i in range(start_x+1,len(codes)):
+            t_line = codes[i].strip('\n')
+            t_line = re.sub('//.*', '', t_line)
+            t_colon_y = t_line.find(';')
+            if t_colon_y != -1:
+                pre_full_line = pre_full_line + ' ' + t_line[: t_colon_y+1]
+                break
+            else:
+                pre_full_line = pre_full_line + ' ' + t_line
+    return pre_full_line
+
+def get_verilog_full_line(codes, start_pos, direction):
+    if direction == -1:  # 0 <- x
+        return get_verilog_pre_full_line(codes, start_pos)
+    elif direction == 1: #      x -> n
+        return get_verilog_post_full_line(codes, start_pos)
+    elif direction == 0: # 0 <- x -> n
+        return get_verilog_pre_full_line(codes, start_pos) + get_verilog_post_full_line(codes, start_pos)[1:] # [1:] because start char at both part
+    else:
+        return ''
+
+def get_full_line( codes, hdl_type, start_pos, direction = 0):
+    if hdl_type == 'vhdl':
+        return get_vhdl_full_line(codes, start_pos, direction)
+    elif hdl_type == 'verilog':
+        return get_verilog_full_line(codes, start_pos, direction)
+    else:
+        return ''
+
+# this function used to save/reload inf used pickle
+def pickle_save(data, path):
+    output = open(path, 'wb')
+    pickle.dump(data, output)
+    output.close()
+
+def pickle_reload(path):
+    data      = None
+    if os.path.isfile(path):
+        pkl_input = open(path,'rb')
+        data      = pickle.load(pkl_input)
+        pkl_input.close()
+    return data
+
+def check_inf_valid(path, last_modify_time = None):
+    if not os.path.isfile(path):
+        return False
+    if last_modify_time != None:
+        if os.path.getmtime(path) != last_modify_time:
+            return False
+    return True
+
+# this function return a list of index, for level1 bracket comma
+def get_bracket_pair_index(code_line, start_bracket_depth):
+    # split bracket and comma
+    split_by_left_bracket  = code_line.split('(')
+    split_by_right_bracket = code_line.split(')')
+    # get all the left_bracket appear colum in code_line
+    last_left_bracket_y   = -1  # left_bracket in code_line
+    left_bracket_appear_y = []
+    for pace in split_by_left_bracket:
+        last_left_bracket_y = last_left_bracket_y + len(pace) + 1
+        left_bracket_appear_y.append(last_left_bracket_y)
+    assert(left_bracket_appear_y[-1] == len(code_line))
+    del left_bracket_appear_y[-1:] # del last split pace y
+    # get all the left_bracket appear colum in code_line
+    last_right_bracket_y   = -1  # right_bracket in code_line
+    right_bracket_appear_y = []
+    for pace in split_by_right_bracket:
+        last_right_bracket_y = last_right_bracket_y + len(pace) + 1
+        right_bracket_appear_y.append(last_right_bracket_y)
+    assert(right_bracket_appear_y[-1] == len(code_line))
+    del right_bracket_appear_y[-1:] # del last split pace y
+    # get all the y need care
+    left_bracket_appear_y_set  = set(left_bracket_appear_y)
+    right_bracket_appear_y_set = set(right_bracket_appear_y)
+    assert( not( left_bracket_appear_y_set & right_bracket_appear_y_set ) )
+    active_y = list( left_bracket_appear_y_set | right_bracket_appear_y_set )
+    active_y.sort()
+    # for each active_y do follow actions
+    cur_bracket_depth               = start_bracket_depth
+    in_level1_left_bracket_y_list   = []
+    out_level1_right_bracket_y_list = []
+    for y in active_y:
+        if y in left_bracket_appear_y_set:
+            cur_bracket_depth += 1
+            if cur_bracket_depth == 1:
+                in_level1_left_bracket_y_list.append(y)
+        if y in right_bracket_appear_y_set:
+            cur_bracket_depth -= 1
+            if cur_bracket_depth == 0:
+                out_level1_right_bracket_y_list.append(y)
+    return { 'end_bracket_depth'               : cur_bracket_depth
+            ,'in_level1_left_bracket_y_list'   : in_level1_left_bracket_y_list
+            ,'out_level1_right_bracket_y_list' : out_level1_right_bracket_y_list }
+
+#------------------------------------------------------------------------------
+# hyperlink function
+#------------------------------------------------------------------------------
+hyperlink_action_dic = {}
+
+def register_hyperlink_action( action_func, description = '' ):
+    assert(inspect.isfunction(action_func) and type(description) == str)
+    action = action_func.__name__
+    assert(action not in hyperlink_action_dic)
+    action_func.description     = description
+    hyperlink_action_dic[action] = action_func
+    return True
+
+def check_hyperlink_legal(action_list, action_parm_dic):
+    def check_action_valid(action, action_parm_dic):
+        if action not in hyperlink_action_dic:
+            return False
+        action_func = hyperlink_action_dic[action]
+        arg_spec = inspect.getargspec(action_func)
+        # mast need arg has no default value
+        must_need_arg_list = []
+        arg_to_default_value_map = {}
+        if arg_spec.defaults == None:
+            must_need_arg_list = arg_spec.args
+        else:
+            must_need_arg_list         = [ arg for arg in arg_spec.args[:-(len(arg_spec.defaults))] ]
+            for i,v in enumerate(arg_spec.defaults[::-1]):
+                arg_to_default_value_map[ arg_spec.args[-(i+1)] ] = v
+        for arg in must_need_arg_list:
+            if arg not in action_parm_dic:
+                return False
+        # arg has default value type must same
+        for arg in arg_to_default_value_map:
+            if arg in action_parm_dic:
+                if type( action_parm_dic[arg] ) != type(arg_to_default_value_map[arg]):
+                    return False
+        return True
+    # if only one action, you can use action name as trigger
+    if type(action_list) == str:
+        return check_action_valid(action_list, action_parm_dic)
+    #if multi action, need check each action func parameter valid
+    for action in action_list:
+        if not check_action_valid(action, action_parm_dic):
+            return False
+    return True
+
+def gen_hyperlink(action_list, action_parm_dic, Type = 'single_action_link'):
+    # if only one action, you can use action name as trigger
+    if type(action_list) == str:
+        action_list = [ action_list ]
+    # if G['Debug']: assert( check_hyperlink_legal(action_list, action_parm_dic) )
+    hyperlink = {
+         'type'               : Type
+        ,'action_list'        : action_list
+        ,'action_parm_dic'    : action_parm_dic
+        ,'intime_parms_dic'   : None
+        ,'payload_dic'        : {}
+    }
+    return hyperlink
+
+def do_action_function( action_func, parm_dic ):
+    arg_spec           = inspect.getargspec(action_func)
+    func_parm_str_list = []
+    for arg in arg_spec.args:
+        if arg in parm_dic:
+            func_parm_str_list.append( '%s = parm_dic["%s"]'%(arg, arg) )
+    func_parm = ', '.join( func_parm_str_list )
+    PrintDebug('Trace: do_action_function: "return_result = %s(%s)'%(action_func.__name__, func_parm))
+    return_result = None
+    exec( "return_result = action_func(%s)"%(func_parm) )
+    return return_result
+
+def do_hyperlink( hyperlink, trigger_list = []):
+    action_list        = hyperlink['action_list']
+    action_parm_dic    = hyperlink['action_parm_dic']
+    intime_parms_dic   = hyperlink['intime_parms_dic']
+    assert('intime_parms_dic' not in action_parm_dic)
+    action_parm_dic['intime_parms_dic'] = intime_parms_dic
+    # if no trigger must only one action for this link
+    if not trigger_list:
+        if len(action_list) != 1:
+            PrintDebug( "do_hyperlink 0: action_parm_dic = %s"%(action_parm_dic.__str__()) )
+            PrintDebug( "do_hyperlink 0: hyperlink = %s"%(hyperlink.__str__()) )
+            hyperlink['intime_parms_dic'] = None
+            del action_parm_dic['intime_parms_dic']
+            return False
+        if not check_hyperlink_legal(action_list, action_parm_dic):
+            PrintDebug( "do_hyperlink 1: action_parm_dic = %s"%(action_parm_dic.__str__()) )
+            hyperlink['intime_parms_dic'] = None
+            del action_parm_dic['intime_parms_dic']
+            return False
+        do_action_function( hyperlink_action_dic[action_list[0]], action_parm_dic )
+        hyperlink['intime_parms_dic'] = None
+        del action_parm_dic['intime_parms_dic']
+        return True
+    # if just do one action, trigger_list can be action string
+    if type(trigger_list) == str:
+        if not check_hyperlink_legal(trigger_list, action_parm_dic):
+            PrintDebug( "do_hyperlink 1: action_parm_dic = %s"%(action_parm_dic.__str__()) )
+            hyperlink['intime_parms_dic'] = None
+            del action_parm_dic['intime_parms_dic']
+            return False
+        do_action_function( hyperlink_action_dic[ trigger_list ], action_parm_dic )
+        hyperlink['intime_parms_dic'] = None
+        del action_parm_dic['intime_parms_dic']
+        return True
+    # if has trigger_list, trigger action one by one
+    # first check all action can work
+    for trigger in trigger_list:
+        if trigger not in action_list:
+            PrintDebug( "do_hyperlink: trigger not in action_list" )
+            hyperlink['intime_parms_dic'] = None
+            del action_parm_dic['intime_parms_dic']
+            return False
+        if not check_hyperlink_legal( trigger, action_parm_dic):
+            PrintDebug( "do_hyperlink: not check_hyperlink_legal(action_list[trigger], action_parm_dic)" )
+            hyperlink['intime_parms_dic'] = None
+            del action_parm_dic['intime_parms_dic']
+            return False
+    for trigger in trigger_list:
+        do_action_function( hyperlink_action_dic[ trigger ], action_parm_dic )
+    hyperlink['intime_parms_dic'] = None
+    del action_parm_dic['intime_parms_dic']
+    return True
diff --git a/Lib/CodeLib.py b/Lib/CodeLib.py
new file mode 100755
index 0000000..c822de8
--- /dev/null
+++ b/Lib/CodeLib.py
@@ -0,0 +1,1167 @@
+"""
+http://www.vim.org/scripts/script.php?script_id=5494
+"""
+#===============================================================================
+# BSD 2-Clause License
+
+# Copyright (c) 2016, CaoJun
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * 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.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#===============================================================================
+
+import sys
+import re
+import os
+import Lib.GLB as GLB
+G = GLB.G
+from Lib.BaseLib import *
+from InlineLib.ViewLib import *
+import Lib.FileInfLib as FileInfLib
+
+
+# this function calculate the io number for subcall_code_line's last word 
+def calculate_last_word_io_number(subcall_code_line):
+    # get the code after io_connect init left bracket, include bracket
+    # module_name          instance_name(b,c,d ... // case0 subcall_code_line
+    # module_name #(a , b) instance_name(b,c,d ... // case1 subcall_code_line
+    #                                   ^^^^^^^^^^ // code after io_connect init left bracket
+    # module_name          instance_name0(a,b,c...), // case0 or 1 
+    #                      instance_name1(a,b,c...), // case2
+    #                      ...
+    #                      instance_name2(a,b,c...); // case2
+    code_after_io_connect_init_left_bracket = ''
+    search_io_connect_case = re.match('^\s*\w+\s+\w+\s*(?P\(.*)', subcall_code_line) # case0
+    if not search_io_connect_case:
+        search_io_connect_case = re.search('\)\s*\w+\s*(\[[^\[\]]*\])?\s*(?P\(.*)', subcall_code_line) # case1
+    if not search_io_connect_case:
+        search_io_connect_case = re.match('(,)?\s*\w+\s*(\[[^\[\]]*\])?\s*(?P\(.*)',subcall_code_line) # case2
+    if search_io_connect_case:
+        code_after_io_connect_init_left_bracket = search_io_connect_case.group('after_part')
+    else: # not in io_connect code
+        return -1
+    # for the after code, change some char for count the comma in bracket level 1
+    code_after_io_connect_init_left_bracket = treat_by_bracket_fmt(code_after_io_connect_init_left_bracket)
+    # count the comma in bracket level 1
+    comma_inf = bracket_level1_comma_index_list(code_after_io_connect_init_left_bracket, 0)
+    # valid in_connect in_level1_left_bracket_y_list must be 1
+    # if in_level1_left_bracket_y_list == 0 means not in io_connect
+    # if in_level1_left_bracket_y_list > 1 means last pos already out of io_connect
+    if len(comma_inf['in_level1_left_bracket_y_list']) != 1:
+        return -1
+    # the in_level1_left_bracket_y_list need add to the valid_level1_comma_y_list
+    # because first ',' means has 2 io_connect
+    real_io_connect_num = len( comma_inf['in_level1_left_bracket_y_list'] + comma_inf['valid_level1_comma_y_list'] )
+    return real_io_connect_num - 1  # start from 0
+
+
+# this function used to get the submodule io name inf
+# if current subcall assign use .xxx(yyy), then return xxx as io name
+# if current subcall assugn use (a,b,c), then return nubmer of io of input pos
+def get_submodule_io_name_inf(subcall_lines, relative_pos):
+    pos_line = subcall_lines[relative_pos[0]].strip('\n')
+    # pos_line = re.sub('(^\s*`.*)|(//.*)','',pos_line)
+    pos_line = get_valid_code(pos_line)
+    if not( relative_pos[1] < len(pos_line) ):
+        PrintDebug('Trace: get_submodule_io_name_inf: current cursor not in valid subcall line !')
+        return False
+    pos_word = get_full_word(pos_line, relative_pos[1])
+    if not pos_word :
+        PrintDebug('Trace: get_submodule_io_name_inf: current cursor not on valid word !')
+        return False  
+    pre_pos_part  = pos_line[ : relative_pos[1] + 1 ] # include pos char
+    # case 0:
+    # ... .xxx(yyy | zzz ) ...  // pos_line
+    #       ^            // relative_pos[1]
+    # submodule_io_name = xxx
+    # pos_word          = xxx
+    if re.match('\w+\.', pre_pos_part[::-1]):
+        submodule_io_name = pos_word
+        # post_pos_part = pos_line[ relative_pos[1] : ]     # include pos char
+        # cur_line_io_connected_logic = re.sub('(^\w*)|(\.\w+\(.*)', '', post_pos_part)
+        # io_connected_signals = set( re.findall('\w+' , cur_line_io_connected_logic) )
+        return (submodule_io_name, pos_word)
+    # case 1:
+    # ... .xxx(yyy | zzz ) ...  // pos_line
+    #                 ^         // relative_pos[1]
+    # submodule_io_name = xxx
+    # pos_word          = zzz
+    case1 = re.search('\(\s*(?P\w+)\.',pre_pos_part[::-1])
+    if case1:
+        submodule_io_name = (case1.group('reverse_submodule_io_name'))[::-1]
+        # check match valid, valid when cursor word not out ".xxx(..." bracket pair
+        match_right_bracket_left_code = ((pre_pos_part[::-1])[:case1.span()[0]+1])[::-1]
+        assert(match_right_bracket_left_code[0] == '(')
+        bracket_pair_index = get_bracket_pair_index(match_right_bracket_left_code, 0)
+        if not bracket_pair_index['out_level1_right_bracket_y_list']:
+            return (submodule_io_name, pos_word)
+    # case 1.1:
+    # i:   .xxx( yyy ,
+    # i+1        zzz )     # pos_line
+    #             ^        # pos
+    # submodule_io_name = xxx
+    # pos_word          = zzz
+    for i in range(relative_pos[0] - 1, -1, -1): # from pre line to begining
+        # cur_line = re.sub('(^\s*`.*)|(//.*)', '',subcall_lines[i].strip('\n'))
+        cur_line = get_valid_code(subcall_lines[i].strip('\n'))
+        pre_pos_part = cur_line + ' ' + pre_pos_part
+        if pre_pos_part.find('.') == -1:
+            continue
+        case1 = re.search('\(\s*(?P\w+)\.',pre_pos_part[::-1])
+        if case1:
+            submodule_io_name = (case1.group('reverse_submodule_io_name'))[::-1]
+            # check match valid, valid when cursor word not out ".xxx(..." bracket pair
+            match_right_bracket_left_code = ((pre_pos_part[::-1])[:case1.span()[0]+1])[::-1]
+            assert(match_right_bracket_left_code[0] == '(')
+            bracket_pair_index = get_bracket_pair_index(match_right_bracket_left_code, 0)
+            if not bracket_pair_index['out_level1_right_bracket_y_list']:
+                return (submodule_io_name, pos_word)
+            else:
+                break
+    # case 2:
+    # ... module_name          instance_name(a, b, c);    # maybe mul line
+    # ... module_name #(parms) instance_name(a, b, c);
+    #                                           ^         # pos
+    # submodule_io_name = 1, the number of io's
+    # pos_word          = b
+    submodule_io_name = calculate_last_word_io_number(pre_pos_part)
+    if submodule_io_name >= 0:
+        return (submodule_io_name, pos_word)
+    PrintDebug( 'Check: get_submodule_io_name_inf: current subcall line can not recognize ! %s '%(pos_line) )
+    return False
+
+
+# this function used to return the io inf for current line
+def decode_egreped_verilog_io_line(o_io_line):
+    # exp: 
+    #   365:output alu_frf_part_p0_w;
+    #   366:output [127:0] alu_frf_data_p0_w;
+    #   357:output [THR_WIDTH-1:0] alu_dst_cond_tid_w
+    #   368:output reg  alu_frf_part_p0_w;
+    #   369:output wire [127:0] alu_frf_data_p0_w;
+    #   370:output reg  [THR_WIDTH-1:0] alu_dst_cond_tid_w
+    #   388:input [width-1 : 0]  A,B;
+    # split by ":" |  388:input [width-1 : 0]  A,B;
+    # split0       |   0 ^      1        ^  2
+    split0    = o_io_line.split(':')
+    line_num  = int(split0[0]) - 1   # -1 because egrep form 1, our line from 0
+    code_line = ':'.join(split0[1:])
+    # valid code line is code_line del note, and change all \s+ to ' '
+    # valid_code_line = re.sub('(//.*)|(^\s+)|(\s+$)','',code_line)
+    # valid_code_line = (re.sub('//.*','',code_line)).strip()
+    valid_code_line = get_valid_code( code_line )
+    valid_code_line = re.sub('\s+',' ',valid_code_line)
+    valid_code_line = re.sub('\W*$', '',valid_code_line)# del end ";" or ","
+    # io type is the first word in valid_code_line
+    # fix bug for "module_name ( input ..." can not reconize
+    # match_io_type   = re.match('(?P\w+)\s*(?P.*)',valid_code_line)
+    match_io_type   = re.search( '(^|\W)(?Pinput|output|inout)\s*(?P.*)' ,valid_code_line)
+    if not match_io_type: # fix bug for input/output in note
+        return {'io_infs':{}, 'name_list':[]}
+    io_type         = match_io_type.group('io_type')
+    other           = match_io_type.group('other').strip(' ')
+    # other: [width-1 : 0]  A,B | wire [127:0] alu_frf_data_p0_w | alu_frf_part_p0_w
+    # get name, name is the last word or words sep by ',' ; reverse it and reverse back
+    # exp: A | A,B | A,B,C
+    match_name = re.match('\s*(?P\w+(\s*,\s*\w+)*)\s*(?P.*)',other[::-1])
+    assert(match_name),'%s | %s'%(other,code_line)
+    other      = (match_name.group('r_other')[::-1]).strip(' ')
+    names_str  = match_name.group('r_names_str')[::-1]
+    names      = re.sub('\s+','',names_str).split(',')
+    names_pos  = []
+    if len(names) == 1:
+        colum = re.search('\W%s(\W|$)'%(names[0]),code_line).span()[0] + 1
+        names_pos = [ ( line_num, colum ) ]
+    else:
+        for n in names:
+            colum = re.search('\W%s(\W|$)'%(n),code_line).span()[0] + 1
+            names_pos.append( (line_num, colum) )
+    # signal_type is the first word of other, maybe empty
+    # case0 : empty
+    # case1 : reg
+    # case2 : reg  [THR_WIDTH-1:0]
+    # case3 : [127:0]
+    signal_type  =  'wire'
+    if other:
+        match_signal_type = re.match('\s*(?P\w*)\s*(?P.*)',other)
+        assert(match_signal_type)
+        m_signal_type = match_signal_type.group('signal_type')
+        if m_signal_type:
+            signal_type = m_signal_type
+        other = match_signal_type.group('other').strip(' ')
+    # other is empty or [a : b]
+    left_index   =  ''
+    right_index  =  ''
+    size         =  1
+    if other:
+        assert(other[0] == '[' and other[-1] == ']'),'%s'%(other)
+        indexs = other[1:-1].split(':')
+        if len(indexs) == 2:
+            left_index  = indexs[0].strip(' ')
+            right_index = indexs[1].strip(' ')
+        try:
+            left_index  = int(left_index)
+            right_index = int(right_index)
+            size        = right_index - left_index + 1
+        except:
+            size        = other
+    # may a line has mulity names
+    io_infs = {}
+    for i, name in enumerate(names):
+        io_infs[name] = {
+              "name"        : name
+            , "io_type"     : io_type
+            , "left"        : left_index
+            , "right"       : right_index
+            , "size"        : size
+            , 'line_num'    : line_num
+            , 'name_pos'    : names_pos[i]
+            , 'code_line'   : code_line
+            , 'signal_type' : signal_type 
+        }
+    assert(len(io_infs) == len(names))
+    return {'io_infs':io_infs, 'name_list':names}
+
+# if has io_name return cur io inf
+# else return all io inf of current module
+#io_inf = 
+    #      "name"        : name
+    #    , "io_type"     : io_type
+    #    , "left"        : left_index
+    #    , "right"       : right_index
+    #    , "size"        : size
+    #    , 'line_num'    : line_num
+    #    , 'name_pos'    : (line_num, colm_num)
+    #    , 'code_line'   : code_line
+    #    , 'signal_type' : signal_type }
+def get_io_inf(module_name, io_name = ''):
+    module_inf   = FileInfLib.get_module_inf(module_name)
+    if not module_inf:
+        PrintReport('Note: module %s database not found!'%(module_name))
+        return False
+    module_path  = module_inf['file_path']
+    module_range = module_inf['module_line_range']
+    if io_name: # get cur io inf
+        io_inf       = {}
+        # io_lines     =  os.popen('sed -n \'%d,%dp\' %s | egrep -n -h \'^\s*(input|output|inout)\>.*\<%s\>\''%(module_range[0]+1, module_range[1]+1, module_path, io_name)).readlines()
+        io_lines     =  os.popen('sed -n \'%d,%dp\' %s | egrep -n -h \'(^|\W)(input|output|inout)\>.*\<%s\>\''%(module_range[0]+1, module_range[1]+1, module_path, io_name)).readlines()
+        if len(io_lines) == 0:
+            PrintReport('RTL Error: not found io: "%s" in module: "%s", path: "%s" !'%(io_name, module_name, module_path))
+            return False
+        if len(io_lines) > 1:
+            l_i = 0
+            while l_i < len(io_lines):
+                if not re.search('\W%s(\W|$)'%(io_name), re.sub('//.*','',io_lines[l_i])):
+                    del io_lines[l_i]
+                    continue
+                l_i += 1
+                continue
+            if len(io_lines) > 1:
+                PrintReport('RTL Error: module: "%s" \'s io: "%s" define multiple times ! path: "%s" '%(module_name, io_name, module_path))
+        line = io_lines[0]
+        assert(line.find(io_name) != -1)
+        io_inf = decode_egreped_verilog_io_line(line)['io_infs']
+        if io_name in io_inf:
+            # because use "sed ... | grep ..." so the line number is not the real number need add sed started line num
+            # io_inf[io_name]['line_num'] = io_inf[io_name]['line_num'] + module_range[0] 1.2
+            io_inf[io_name]['line_num'] = io_inf[io_name]['line_num'] + module_range[0] # decode_egreped_verilog_io_line already -1
+            io_inf[io_name]['name_pos'] = ( io_inf[io_name]['line_num'], io_inf[io_name]['name_pos'][1] )
+            return io_inf[io_name]
+        else:
+            PrintReport('Note: not found io: "%s" in module: "%s",maybe a parm name ! path: "%s" !'%(io_name, module_name, module_path))
+            return False
+    else: # get module all io inf list in order
+        all_io_inf    = []
+        cur_module_code_range = module_inf['module_line_range']
+        # all_io_lines  =  os.popen('sed -n \'%d,%dp\' %s | egrep -n -h \'^\s*(input|output|inout)\>\''%(cur_module_code_range[0]+1, cur_module_code_range[1]+1, module_path)).readlines()
+        all_io_lines  =  os.popen('sed -n \'%d,%dp\' %s | egrep -n -h \'(^|\W)(input|output|inout)\>\''%(cur_module_code_range[0]+1, cur_module_code_range[1]+1, module_path)).readlines()
+        for line in all_io_lines:
+            line      = line.rstrip('\n')
+            egrep_io_infs = decode_egreped_verilog_io_line(line)
+            io_inf        = egrep_io_infs['io_infs']
+            name_list     = egrep_io_infs['name_list']
+            if not io_inf:
+                PrintDebug('Error: module: %s, line: %s, can not decode by decode_egreped_verilog_io_line() ! file: %s'%(module_name, line, module_path))
+                continue
+            for io_name in name_list:
+                assert(io_name in io_inf)
+                c_io_inf = io_inf[io_name]
+                c_io_inf['line_num'] = c_io_inf['line_num'] + cur_module_code_range[0]
+                c_io_inf['name_pos'] = (c_io_inf['line_num'], c_io_inf['name_pos'][1])
+                all_io_inf.append( c_io_inf )
+        return all_io_inf
+
+
+# this function used when cursor on a subcall line
+# if cursor word is io_connected signal, get the 
+# connect_signal (cursor word) and corresponding submodule io inf
+# return ( pos_word, submodule_io_inf)
+def get_subcall_connect_inf(submodule_name, subcall_lines, relative_pos):
+    # get io_name and pos word
+    name_inf = get_submodule_io_name_inf(subcall_lines, relative_pos)
+    if name_inf == False:
+        PrintDebug('Trace: get_subcall_connect_inf : pos not on valid connect !')
+        return False
+    submodule_io_name, pos_word = name_inf
+    # get submodule io inf
+    submodule_io_inf = None
+    if type(submodule_io_name) == str: # is io name
+        assert(submodule_io_name.strip() != '')
+        submodule_io_inf = get_io_inf(submodule_name, submodule_io_name)
+        if not submodule_io_inf:
+            PrintDebug('Trace: get_subcall_connect_inf: no submodule_io_inf !')
+            return False
+    else:
+        assert(type(submodule_io_name) == int and submodule_io_name >= 0)
+        all_submodule_io_inf_list = get_io_inf(submodule_name)
+        if not all_submodule_io_inf_list:
+            PrintDebug('Trace: get_subcall_connect_inf: no submodule_io_inf !')
+            return False
+        assert(submodule_io_name < len(all_submodule_io_inf_list)),"%s"%([submodule_io_name, all_submodule_io_inf_list].__str__())
+        submodule_io_inf = all_submodule_io_inf_list[submodule_io_name]
+    return ( pos_word, submodule_io_inf)
+
+
+# this function used to get cursor pos line module_inf, subcall_inf, submodule_io_inf
+def get_subcall_pos_inf( module_file_path, cursor_pos, file_codes = [] ):
+    # get cut line module inf and subcall inf
+    subcall_line_inf = FileInfLib.get_file_line_inf(cursor_pos[0], module_file_path)
+    if not subcall_line_inf:
+        PrintDebug('Trace: get_subcall_pos_inf: has no subcall_line_inf !')
+        return False
+    module_inf  = subcall_line_inf['module_inf']
+    subcall_inf = subcall_line_inf['subcall_inf']
+    # if has subcall_inf means cursor no subcall line, get submodule_io_inf, else return false
+    if not subcall_inf:
+        PrintDebug('Trace: get_subcall_pos_inf: has no subcall_inf.')
+        return False
+    assert(module_inf), 'has subcall_inf must has module_inf'
+    # to get submodule io inf
+    if not file_codes:
+        assert(os.path.isfile(module_file_path))
+        file_codes = open(module_file_path,'r').readlines()
+    pos_word            = ''
+    submodule_io_inf    = None
+    submodule_name      = subcall_inf['submodule_name']
+    subcall_line_range  = subcall_inf['subcall_line_range']
+    subcall_lines       = file_codes[ subcall_line_range[0] : subcall_line_range[1] + 1 ]
+    assert( cursor_pos[0] >= subcall_line_range[0] and cursor_pos[0] <= subcall_line_range[1])
+    relative_pos        = (cursor_pos[0] - subcall_line_range[0], cursor_pos[1] )
+    subcall_connect_inf = get_subcall_connect_inf(submodule_name, subcall_lines, relative_pos)
+    if subcall_connect_inf:
+        PrintDebug('Trace: get_subcall_pos_inf: has subcall_connect_inf.')
+        pos_word, submodule_io_inf = subcall_connect_inf
+    return { 'pos_word'         : pos_word
+            ,'submodule_io_inf' : submodule_io_inf
+            ,'module_inf'       : module_inf
+            ,'subcall_inf'      : subcall_inf }
+
+
+# this function return a list of index, for level1 bracket comma
+def bracket_level1_comma_index_list(code_line, start_bracket_depth):
+    # split bracket and comma
+    split_by_left_bracket  = code_line.split('(')
+    split_by_right_bracket = code_line.split(')')
+    split_by_comma         = code_line.split(',')
+    # get all the comma appear colum in code_line
+    last_comma_y           = -1  # colum in code_line 
+    comma_appear_y = []
+    for pace in split_by_comma:
+        last_comma_y = last_comma_y + len(pace) + 1
+        comma_appear_y.append(last_comma_y)
+    assert(comma_appear_y[-1] == len(code_line))
+    del comma_appear_y[-1:] # del last split pace y
+    # get all the left_bracket appear colum in code_line
+    last_left_bracket_y   = -1  # left_bracket in code_line 
+    left_bracket_appear_y = []
+    for pace in split_by_left_bracket:
+        last_left_bracket_y = last_left_bracket_y + len(pace) + 1
+        left_bracket_appear_y.append(last_left_bracket_y)
+    assert(left_bracket_appear_y[-1] == len(code_line))
+    del left_bracket_appear_y[-1:] # del last split pace y
+    # get all the left_bracket appear colum in code_line
+    last_right_bracket_y   = -1  # right_bracket in code_line 
+    right_bracket_appear_y = []
+    for pace in split_by_right_bracket:
+        last_right_bracket_y = last_right_bracket_y + len(pace) + 1
+        right_bracket_appear_y.append(last_right_bracket_y)
+    assert(right_bracket_appear_y[-1] == len(code_line))
+    del right_bracket_appear_y[-1:] # del last split pace y
+    # get all the y need care
+    comma_appear_y_set         = set(comma_appear_y)
+    left_bracket_appear_y_set  = set(left_bracket_appear_y)
+    right_bracket_appear_y_set = set(right_bracket_appear_y)
+    assert( not( comma_appear_y_set        & left_bracket_appear_y_set  ) )
+    assert( not( comma_appear_y_set        & right_bracket_appear_y_set ) )
+    assert( not( left_bracket_appear_y_set & right_bracket_appear_y_set ) )
+    active_y = list( comma_appear_y_set | left_bracket_appear_y_set | right_bracket_appear_y_set )
+    active_y.sort()
+    # for each active_y do follow actions
+    cur_bracket_depth               = start_bracket_depth
+    valid_comma_y_list              = []
+    in_level1_left_bracket_y_list   = []
+    out_level1_right_bracket_y_list = []
+    for y in active_y:
+        if (y in comma_appear_y_set) and cur_bracket_depth == 1:
+            valid_comma_y_list.append(y)
+        if y in left_bracket_appear_y_set:
+            cur_bracket_depth += 1
+            if cur_bracket_depth == 1:
+                in_level1_left_bracket_y_list.append(y)
+        if y in right_bracket_appear_y_set:
+            cur_bracket_depth -= 1
+            if cur_bracket_depth == 0:
+                out_level1_right_bracket_y_list.append(y)
+    return { 'end_bracket_depth'               : cur_bracket_depth
+            ,'valid_level1_comma_y_list'       : valid_comma_y_list
+            ,'in_level1_left_bracket_y_list'   : in_level1_left_bracket_y_list
+            ,'out_level1_right_bracket_y_list' : out_level1_right_bracket_y_list
+    } 
+
+# for count bracket deepth for comma, when find subcall io connect
+# need treat below char to bracket
+def treat_by_bracket_fmt( line ):
+    # for not count { a, b, c} at signal connect assign
+    line = line.replace('{','(')
+    line = line.replace('}',')')
+    return line
+
+
+# this function used to find the subcall io connect signal pos by submodule io index
+def get_subcall_io_connect_signal_pos_from_io_index(io_index, subcall_inf, upper_module_codes):
+    submodule_name  = subcall_inf['submodule_name']
+    instance_name   = subcall_inf['instance_name']
+    subcall_range   = subcall_inf['subcall_line_range']
+    # find the io connect start left bracket pos
+    # case0.0: module_name          instance_name(a,b...
+    # case0.1: module_name #(x,y,z) instance_name(a,b...
+    # case1: instance_name(a,bc...)
+    #        module_name instance0(...),
+    #                    instance1(...);
+    # case2: module_name instance_name();
+    io_connect_init_left_brackek_x = -1 # line number
+    io_connect_init_left_brackek_y = -1 # colum number
+    pre_valid_code  = ''
+    post_valid_code = '' # valid code from left bracket pos
+    next_line_index = subcall_range[0]
+    max_line_index  = subcall_range[1] + 1
+    patten_module   = '%s'%(submodule_name)
+    patten_instance = '%s\s*(\[[^\[\]]*\])?'%(instance_name)
+    patten_post     = '(?P\(.*)'
+    code_to_current_line = ''
+    post_valid_code      = ''
+    while next_line_index < max_line_index:
+        cur_index_code = get_valid_code(upper_module_codes[next_line_index].strip('\n'))
+        cur_line_index = next_line_index
+        next_line_index += 1
+        pre_line_code_length = len(code_to_current_line) + 1 # +1 because add a space between two line
+        code_to_current_line = code_to_current_line + ' ' + cur_index_code
+        match_case0 = re.match('(?P\s*%s\W(.*\W)?%s\s*)%s'%(patten_module, patten_instance, patten_post), code_to_current_line)
+        if match_case0:
+            post_valid_code = match_case0.group('post_part')
+            io_connect_init_left_brackek_x = cur_line_index
+            io_connect_init_left_brackek_y = len(match_case0.group('pre_part')) - pre_line_code_length
+            break
+        match_case1 = re.match('(?P\s*(,)?\s*%s\s*)%s'%(patten_instance, patten_post), code_to_current_line)
+        if match_case1:
+            post_valid_code = match_case1.group('post_part')
+            io_connect_init_left_brackek_x = cur_line_index
+            io_connect_init_left_brackek_y = len(match_case1.group('pre_part')) - pre_line_code_length
+            break
+    assert(io_connect_init_left_brackek_x != -1)
+    # from io connect start left bracket pos to subcall end to get the real 
+    # connect single inf
+    indexed_io_connect_pre_comma_pos  = ()
+    io_index_relative_to_current_line = io_index
+    cur_bracket_depth     = 0
+    cur_valid_code        = post_valid_code
+    cur_comma_inf         = bracket_level1_comma_index_list( treat_by_bracket_fmt(cur_valid_code), cur_bracket_depth)
+    # leve1 in left bracket follow a io_connect so need add it
+    cur_full_comma_y_list = cur_comma_inf['in_level1_left_bracket_y_list'] + cur_comma_inf['valid_level1_comma_y_list']
+    cur_full_comma_y_list.sort()
+    cur_finded_io_connect_num = len( cur_full_comma_y_list )
+    cur_bracket_depth = cur_comma_inf['end_bracket_depth']
+    # io_index_relative_to_current_line = io_index_relative_to_current_line - cur_finded_io_connect_num
+    # if still not find the last comma, looped to find it until subcall end
+    # i = io_connect_init_left_brackek_x + 1
+    still_in_init_line = 1
+    while not (io_index_relative_to_current_line < cur_finded_io_connect_num) and (next_line_index < max_line_index):
+        still_in_init_line = 0
+        cur_valid_code    = get_valid_code(upper_module_codes[next_line_index].strip('\n'))
+        cur_line_index    = next_line_index
+        next_line_index   += 1
+        io_index_relative_to_current_line = io_index_relative_to_current_line - cur_finded_io_connect_num
+        cur_comma_inf     = bracket_level1_comma_index_list( treat_by_bracket_fmt(cur_valid_code) , cur_bracket_depth)
+        cur_bracket_depth = cur_comma_inf['end_bracket_depth']
+        cur_full_comma_y_list = cur_comma_inf['in_level1_left_bracket_y_list'] + cur_comma_inf['valid_level1_comma_y_list']
+        cur_full_comma_y_list.sort()
+        cur_finded_io_connect_num = len( cur_full_comma_y_list )
+    # assert(io_index_relative_to_current_line < cur_finded_io_connect_num)
+    if not (io_index_relative_to_current_line < cur_finded_io_connect_num):
+        return () # for case 2
+    indexed_io_connect_pre_comma_pos = ( next_line_index - 1, cur_full_comma_y_list[io_index_relative_to_current_line] )
+    # from comma pos to get the first valid word and treat that as the subcall io connect key pos
+    comma_or_init_bracket_pos = cur_full_comma_y_list[io_index_relative_to_current_line]
+    post_part  = cur_valid_code[comma_or_init_bracket_pos:]
+    assert(post_part[0] in [',','('] ),'%s'%(post_part)
+    search_key = re.search('\w',post_part)
+    match_in_first_line = 1
+    while (not search_key) and (next_line_index < max_line_index):
+        cur_bracket_depth = cur_comma_inf['end_bracket_depth']
+        cur_valid_code    = get_valid_code(upper_module_codes[next_line_index].strip('\n'))
+        cur_line_index    = next_line_index
+        next_line_index += 1
+        search_key = re.search('\w',cur_valid_code)
+        if search_key:
+            match_in_first_line = 0
+    # assert(search_key)
+    if not search_key: # for case2: module_name instance_name(); should no search key
+        return () # for case 2
+    key_x = next_line_index - 1
+    key_y = (io_connect_init_left_brackek_y*still_in_init_line + comma_or_init_bracket_pos)*match_in_first_line + search_key.span()[0]
+    return (key_x, key_y)
+
+
+# this function used to get the upper module io inf
+def get_upper_module_call_io_inf(cur_module_name , cur_io_name, report_level = 1):
+    cur_module_last_call_inf = FileInfLib.get_module_last_call_inf(cur_module_name)
+    if not cur_module_last_call_inf:
+        PrintReport("Note: module %s not called before, no upper module !"%(cur_module_name), report_level = report_level )
+        return False
+    upper_module_inf   = cur_module_last_call_inf['upper_module_inf']
+    assert(upper_module_inf),'upper module %s call %s before, upper should has inf in database !'%(upper_module_name, cur_module_name)
+    upper_module_name  = upper_module_inf['module_name']
+    upper_subcall_inf  = cur_module_last_call_inf['upper_subcall_inf']
+    # for case subcall recoginize not accuracy, may because of '`ifdef, `endif ...'
+    if upper_subcall_inf['inaccuracy']: 
+        PrintReport('Warning: carefull the trace result, subcall module:%s, instance:%s inaccuracy !'%(upper_subcall_inf['module_name'], upper_subcall_inf['instance_name']))
+    upper_module_path  = upper_module_inf['file_path']
+    # get upper call, match this signal pos
+    # case0: .xxx(yyy) case use egrep get thesignal pos
+    subcall_range = upper_subcall_inf['subcall_line_range']
+    # sed -n 'a,bp', a count from 1, so subcall_range[0]/[1] need +1
+    # pos: a -> b , sed : a+1 -> b+1, egrep: 1 -> 1+b-a = k, => b = k + a -1 
+    # cur_io_matched_upper_connect_lines  =  os.popen('sed -n \'%d,%dp\' %s | egrep -n -h \'\.\s*%s\s*\(\''%(subcall_range[0] + 1, subcall_range[1] + 1, upper_module_path, cur_io_name)).readlines()
+    cur_io_matched_upper_connect_lines  =  os.popen('sed -n \'%d,%dp\' %s | egrep -n -h \'\.\s*%s(\W|$)\''%(subcall_range[0] + 1, subcall_range[1] + 1, upper_module_path, cur_io_name)).readlines()
+    for egrep_l in cur_io_matched_upper_connect_lines:
+        split_rst = (egrep_l.strip('\n')).split(':')
+        line_num  = int(split_rst[0]) + subcall_range[0] - 1
+        code_line = ':'.join(split_rst[1:])
+        valid_code_line = re.sub('//.*','',code_line)
+        # valid_match = re.match('(?P.*\.\s*)%s\s*\('%(cur_io_name),valid_code_line)
+        valid_match = re.match('(?P.*\.\s*)%s(\W|$)'%(cur_io_name),valid_code_line)
+        if valid_match:
+            colum_num = len(valid_match.group('pre_char'))
+            cur_io_upper_connect_line = code_line
+            cur_io_upper_connect_pos  = (line_num, colum_num)
+            return { 'upper_module_name'       : upper_module_name
+                    ,'upper_module_path'       : upper_module_path
+                    ,'io_upper_connect_line'   : cur_io_upper_connect_line
+                    ,'io_upper_connect_signal' : cur_io_name
+                    ,'io_upper_connect_pos'    : cur_io_upper_connect_pos }
+    # case 1: 
+    #   module_name instance_name (a,b,c)
+    #   module_name #() instance_name (a,b,c)
+    upper_module_codes = open(upper_module_path,'r').readlines()
+    # get the current io name index in ordered all module io
+    cur_io_index = -1
+    submodule_name       = upper_subcall_inf['submodule_name']
+    all_submodule_io_inf = get_io_inf(submodule_name)
+    for i,inf in enumerate(all_submodule_io_inf):
+        if inf['name'] == cur_io_name:
+            cur_io_index = i
+            break
+    assert(cur_io_index != -1)
+    # get the io_connect_line_inf
+    io_connect_signal_pos = get_subcall_io_connect_signal_pos_from_io_index(cur_io_index, upper_subcall_inf, upper_module_codes)
+    if not io_connect_signal_pos:
+        PrintReport('Warning: not find io connect, current module:%s io :%s, upper module: %s '%(cur_module_name, cur_io_name, upper_module_name), report_level = report_level )
+        return False
+    match_io_upper_connect_signal = re.match('(?P\w+)', upper_module_codes[io_connect_signal_pos[0]][io_connect_signal_pos[1]:])
+    # assert(match_io_upper_connect_signal),'valid io connect signal pos:"%s", must be a valid word !'%(io_connect_signal_pos.__str__())
+    if match_io_upper_connect_signal: 
+        io_upper_connect_signal = match_io_upper_connect_signal.group('match_name')
+    else: # for subcall like: module instance(); should not match
+        io_upper_connect_signal = ''
+    return { 'upper_module_name'       : upper_module_name
+            ,'upper_module_path'       : upper_module_path
+            ,'io_upper_connect_line'   : upper_module_codes[io_connect_signal_pos[0]]
+            ,'io_upper_connect_signal' : io_upper_connect_signal
+            ,'io_upper_connect_pos'    : io_connect_signal_pos }
+
+
+def current_appear_is_dest_or_source(key, code_lines, appear_pos):
+    a_x, a_y         = appear_pos
+    appear_code_line = get_valid_code( code_lines[a_x] )
+    # case 0 cur pos in note return not source and dest
+    if len(appear_code_line) - 1 < a_y:
+        return 'unkonwn'
+    # case 1 is io
+    match_io = re.match('\s*(?P(input|inout|output))\W', appear_code_line)
+    if match_io:
+        # incase key is not a valid signal, such as "input"
+        match_io_name = re.match('\s*[;,]?\s*(?P\w+(\s*,\s*\w+)*)',appear_code_line[::-1]) # may input a,b,c
+        if match_io_name:
+            io_names = match_io_name.group('r_names')[::-1]
+            io_names = set(re.split('\s*,\s*',io_names))
+            if key in io_names:
+                if match_io.group('io_type') == 'input':
+                    return 'source'
+                if match_io.group('io_type') == 'output':
+                    return 'dest'
+                if match_io.group('io_type') == 'inout':
+                    return 'source_and_dest'
+        PrintDebug('Error: recognize_signal_assign_line: unrecognize io line: '+appear_code_line)
+        return 'unkonwn'
+    # case 2 cur pos in case/casez/for/if (...key...) then it's dest
+    # this must put before assign to must case as: if(key) asas <= 
+    #     key in if(...) so this is dest not source
+    pre_full_line = get_verilog_pre_full_line(code_lines, appear_pos)
+    case2_patten = 'case|casez|for|if|while|always'
+    # find the last case2 match
+    r_pre_full_line = pre_full_line[::-1]
+    r_case2_patten  = case2_patten[::-1]
+    r_march_case2   = re.search('(^|\W)(%s)(\W|$)'%(r_case2_patten), r_pre_full_line)
+    if r_march_case2:
+        r_match_y        = r_march_case2.span()[1]
+        case2_match_code = (r_pre_full_line[:r_match_y+1])[::-1]
+        index_inf        = get_bracket_pair_index(case2_match_code, 0)
+        # just in bracket and not out means it's dest
+        if (len(index_inf['in_level1_left_bracket_y_list']) == 1) and (len(index_inf['out_level1_right_bracket_y_list']) == 0):
+            return 'dest'
+    # case 3 check assign case
+    assign_patten = '(\s|\w|^)(=|<=)(\s|\w|$)'
+    # (1) check pre_full_line for dest, exp: ... <= key
+    search_dest = re.search(assign_patten, pre_full_line)
+    if search_dest:
+        match_right_part = pre_full_line[search_dest.span()[0]+1:]
+        # fix bug : for(...;xx = xx) ... key ; this not dest 
+        # ... =|<= key, key must not leave =|<= scope
+        # check it by assume has "(" at =|<= left, and 
+        # make sure not leave this bracket 
+        index_inf        = get_bracket_pair_index(match_right_part, 1)
+        if len(index_inf['out_level1_right_bracket_y_list']) == 0:
+            return 'dest'
+    # (2) check current line for source
+    # most case current line has =|<=
+    if re.search(assign_patten, appear_code_line[a_y:]):
+        return 'source'
+    # (3) post full line sep by ";" has =|<=, it's source
+    post_full_line = get_verilog_post_full_line(code_lines, appear_pos)
+    if re.search(assign_patten, post_full_line):
+        return 'source'
+    # other case treat as false
+    return 'unkonwn'
+
+
+def clear_last_trace_inf( trace_type = 'both' ):
+    trace_type = 'both' # i thing each time clear all is much more friendly
+    if trace_type in ['source','both']:
+        G['TraceInf']['LastTraceSource']['Maybe']          = []
+        G['TraceInf']['LastTraceSource']['Sure']           = []
+        G['TraceInf']['LastTraceSource']['ShowIndex']      = 0
+        G['TraceInf']['LastTraceSource']['SignalName']     = ''
+        G['TraceInf']['LastTraceSource']['ValidLineRange'] = [-1,-1]
+        G['TraceInf']['LastTraceSource']['Path']           = ''
+    if trace_type in ['dest','both']:
+        G['TraceInf']['LastTraceDest']['Maybe']            = []
+        G['TraceInf']['LastTraceDest']['Sure']             = []
+        G['TraceInf']['LastTraceDest']['ShowIndex']        = 0
+        G['TraceInf']['LastTraceDest']['SignalName']       = ''
+        G['TraceInf']['LastTraceDest']['ValidLineRange']   = [-1,-1]
+        G['TraceInf']['LastTraceDest']['Path']             = ''
+
+
+# this function trace the signal when need crose module from
+# submodule io line to upper module subcall line
+def real_trace_io_signal(trace_type, cursor_inf, io_signal_inf, report_level = 1):
+    assert(trace_type in ['dest', 'source']),'only trace dest/source'
+    # verilog
+    if (trace_type is 'dest') and (io_signal_inf['io_type'] != 'output'):
+        PrintDebug('Trace: real_trace_io_signal: not output signal, not dest')
+        return False # not output signal, not dest
+    if (trace_type is 'source') and (io_signal_inf['io_type'] != 'input'):
+        PrintDebug('Trace: real_trace_io_signal: not input signal, not source')
+        return False # not input signal, not source
+    # trace a input signal
+    clear_last_trace_inf( trace_type )  # clear pre trace dest/source result
+    cur_module_inf     = None
+    cur_line_inf       = FileInfLib.get_file_line_inf(cursor_inf['line_num'], cursor_inf['file_path'])
+    if cur_line_inf:
+        cur_module_inf = cur_line_inf['module_inf']
+    if not cur_module_inf:
+        if not cur_line_inf('hdl_type'):
+            PrintReport('Warning: current cursor no module found, unsupport postfix "%s" !'%(get_file_path_postfix(cur_line_inf['file_path'])), report_level = report_level)
+            return True
+        PrintReport('Warning: current cursor no module found ! ', report_level = report_level)
+        return True
+    cur_module_name  = cur_module_inf['module_name']
+    upper_module_call_inf = get_upper_module_call_io_inf(cur_module_name , io_signal_inf['name'], report_level = report_level)
+    if not upper_module_call_inf:
+        # if no upper call just get all upper module and maunlly choise one as upper
+        trace_upper_inf = {  'trace_type'  :trace_type
+                            ,'cursor_inf'  :cursor_inf
+                            ,'report_level':report_level }
+        line_and_link_list = get_upper_module_line_and_link_list( cur_module_name, trace_upper_inf = trace_upper_inf )
+        if not line_and_link_list:
+            return True
+        # i = 0 
+        link_list = []
+        line_list = []
+        # pre inf
+        line_list.append('Knock "" to choise upper module you want trace to: ')
+        line_list.append('')
+        link_list.append( {} )
+        link_list.append( {} )
+        line_list += line_and_link_list['line_list']
+        link_list += line_and_link_list['link_list']
+        mounted_line_inf  = MountPrintLines(line_list, label = 'Possible Trace Upper', link_list = link_list)
+        mounted_line_list = mounted_line_inf['line_list']
+        mounted_link_list = mounted_line_inf['link_list']
+        # add a empty line below
+        mounted_line_list.append('')
+        mounted_link_list.append({})
+        add_trace_point()
+        assert( len(mounted_line_list) == len(mounted_link_list) )
+        PrintReport(mounted_line_list, mounted_link_list, MountPrint = True )
+        if len(line_and_link_list['line_list']) == 1:
+            for link in link_list:
+                if link:
+                    do_hyperlink(link, ['add_module_last_call_action', 'trace_io_signal_action']) # first valid link
+                    break
+        else:
+            # len(mounted_line_list) + 1 is the lines relative to the last report line
+            # -4 is skip first 4 unused line
+            go_win( G['Report_Inf']['Report_Path'] , (-(len(mounted_line_list) + 1 -4), 57) )
+        return True # this dest/source but not found upper module
+    # has upper module go to upper module call location
+    upper_module_name   = upper_module_call_inf['upper_module_name']
+    upper_call_pos      = upper_module_call_inf['io_upper_connect_pos']
+    upper_call_line     = upper_module_call_inf['io_upper_connect_line']
+    upper_module_path   = upper_module_call_inf['upper_module_path']
+    upper_signal_name   = upper_module_call_inf['io_upper_connect_signal']
+    show_str            = '%s %d : %s'%(upper_module_name, upper_call_pos[0]+1, upper_call_line)
+    file_link_parm_dic  = { 'type'             : 'trace_result'
+                           ,'last_modify_time' : os.path.getmtime( upper_module_path )
+                           ,'go_path'          : upper_module_path
+                           ,'go_pos'           : upper_call_pos
+                           ,'go_word'          : upper_signal_name }
+    file_link           = gen_hyperlink('go_file_action', file_link_parm_dic)
+    trace_result        = {'show': show_str, 'file_link': file_link}
+    if trace_type is 'dest':
+        G['TraceInf']['LastTraceDest']['Sure'].append(trace_result)
+        G['TraceInf']['LastTraceDest']['SignalName']     = cursor_inf['word']
+        G['TraceInf']['LastTraceDest']['ValidLineRange'] = cur_module_inf['module_line_range']
+        G['TraceInf']['LastTraceDest']['Path']           = cursor_inf['file_path']
+    else :
+        G['TraceInf']['LastTraceSource']['Sure'].append(trace_result)
+        G['TraceInf']['LastTraceSource']['SignalName']     = cursor_inf['word']
+        G['TraceInf']['LastTraceSource']['ValidLineRange'] = cur_module_inf['module_line_range']
+        G['TraceInf']['LastTraceSource']['Path']           = cursor_inf['file_path']
+    # show dest/source to report win, and go first trace
+    PrintReport(spec_case = trace_type)
+    show_next_trace_result(trace_type)
+    return True
+
+
+# if current is a io signal report the io_infs,
+# else return false
+def recognize_io_signal_line(line, line_num):
+    # pre check if is not io
+    if line.find('input') == -1 and line.find('output') == -1:
+        return False
+    line = line.strip('\n')
+    line = re.sub('//.*','',line) # del notes
+    # raw re match
+    re_match = re.match('\s*(input|output)\W',line)
+    # for module ( input ... case
+    if not re_match:
+        re_match = re.match('(?P.*\(\s*)(?P(input|output)\W.*)',line)
+        if re_match:
+            prefix       = re_match.group('prefix')
+            real_io_line = re_match.group('real_io')
+            line         = ' '*(len(prefix)) + real_io_line
+        else:
+            return False
+    # match used egrep io line decode function
+    egrep_io_line = str(line_num)+':'+line
+    io_infs = decode_egreped_verilog_io_line(egrep_io_line)['io_infs']
+    return io_infs
+    #      "name"        : name
+    #    , "io_type"     : io_type
+    #    , "left"        : left_index
+    #    , "right"       : right_index
+    #    , "size"        : size
+    #    , 'line_num'    : line_num
+    #    , 'name_pos'    : (line_num, colm_num)
+    #    , 'code_line'   : code_line
+    #    , 'signal_type' : signal_type }
+
+
+# this function trace the signal when need crose module from
+# submodule io line to upper module subcall line
+def trace_io_signal(trace_type, cursor_inf, report_level = 1):
+    trace_signal_name  = cursor_inf['word']
+    io_signal_infs     = recognize_io_signal_line(cursor_inf['line'], cursor_inf['line_num'])
+    if not io_signal_infs:
+        PrintDebug('Trace: trace_io_signal: not io signal')
+        return False # not io signal
+    # is io line but trace not a io signal, maybe 
+    # macro define, such as [`EXP_MACRO:0] ...
+    if trace_signal_name not in io_signal_infs:
+        PrintDebug('Trace: trace_io_signal: is io signal but not traced signal')
+        return False 
+    if trace_type in ['source','dest']:
+        return real_trace_io_signal(trace_type, cursor_inf, io_signal_infs[trace_signal_name], report_level = report_level)
+    assert(0),'unkonw tarce type %s' %(trace_type)
+# hyperlink action trace_io_signal_action
+def trace_io_signal_action(trace_type, cursor_inf, report_level):
+    trace_io_signal(trace_type, cursor_inf, report_level)
+register_hyperlink_action( trace_io_signal_action, description = 'this link function use to trace input cursor io ' )
+
+
+# this function used when current is really a subcall and get subcall inf
+# add subcall io line to trace result and go to submodule io line
+def real_trace_signal_at_subcall_lines(trace_type, subcall_cursor_inf, cursor_inf, report_level = 1):
+    assert(trace_type in ['source', 'dest'])
+    if trace_type == 'source' and subcall_cursor_inf['submodule_io_inf']['io_type'] != 'output':
+        return False # submodule not source, just pass
+    elif trace_type == 'dest' and subcall_cursor_inf['submodule_io_inf']['io_type'] != 'input':
+        return False # submodule not source, just pass
+    # has sub module and in submodule signal is out, then it's source
+    subcall_inaccuracy        = subcall_cursor_inf['subcall_inf']['inaccuracy']
+    submodule_name            = subcall_cursor_inf['subcall_inf']['submodule_name']
+    if subcall_inaccuracy: # for case subcall recoginize not accuracy, may because of '`ifdef, `endif ...'
+        PrintReport('Warning: carefull the trace result !  current cursor subcall module:%s, instance:%s inaccuracy !'%(submodule_name, subcall_cursor_inf['subcall_inf']['instance_name']))
+    submodule_inf             = FileInfLib.get_module_inf(submodule_name)
+    assert(submodule_inf)
+    submodule_path            = submodule_inf['file_path']
+    submodule_matched_io_pos  = subcall_cursor_inf['submodule_io_inf']['name_pos']
+    submodule_matched_io_line = subcall_cursor_inf['submodule_io_inf']['code_line']
+    submodule_matched_io_name = subcall_cursor_inf['submodule_io_inf']['name']
+    show_str                  = '%s %d : %s'%(submodule_name, submodule_matched_io_pos[0]+1, submodule_matched_io_line)
+    file_link_parm_dic        = { 'type'             : 'trace_result'
+                                 ,'last_modify_time' : os.path.getmtime( submodule_path )
+                                 ,'go_path'          : submodule_path
+                                 ,'go_pos'           : submodule_matched_io_pos
+                                 ,'go_word'          : submodule_matched_io_name }
+    file_link                 = gen_hyperlink('go_file_action', file_link_parm_dic)
+    trace_result              = {'show': show_str, 'file_link': file_link}
+    if trace_type == 'source':
+        G['TraceInf']['LastTraceSource']['Sure'].append(trace_result)
+        G['TraceInf']['LastTraceSource']['SignalName']     = cursor_inf['word']
+        G['TraceInf']['LastTraceSource']['ValidLineRange'] = ( cursor_inf['line_num'], cursor_inf['line_num'] )
+        G['TraceInf']['LastTraceSource']['Path']           = cursor_inf['file_path']
+    else: # dest
+        G['TraceInf']['LastTraceDest']['Sure'].append(trace_result)
+        G['TraceInf']['LastTraceDest']['SignalName']     = cursor_inf['word']
+        G['TraceInf']['LastTraceDest']['ValidLineRange'] = ( cursor_inf['line_num'], cursor_inf['line_num'] )
+        G['TraceInf']['LastTraceDest']['Path']           = cursor_inf['file_path']
+    # go to sub module code now, so cur module is the sub module last call
+    cur_module_name           = subcall_cursor_inf['module_inf']['module_name']
+    subcall_instance_name     = subcall_cursor_inf['subcall_inf']['instance_name']
+    FileInfLib.set_module_last_call_inf(submodule_name, cur_module_name, subcall_instance_name)
+    # show source to report win, and go first trace
+    PrintReport(spec_case = trace_type)
+    show_next_trace_result(trace_type)
+    return True
+
+
+# this function used to trace signal at subcall lines 
+def trace_signal_at_subcall_lines(trace_type, cursor_inf, report_level = 1):
+    subcall_cursor_inf = get_subcall_pos_inf(cursor_inf['file_path'], cursor_inf['pos'], cursor_inf['codes'])
+    if not subcall_cursor_inf:
+        PrintDebug('Trace: trace_signal_at_subcall_lines: cursor not at subcall lines !')
+        return False # not in module call io
+    if not subcall_cursor_inf['submodule_io_inf']:
+        PrintDebug('Trace: trace_signal_at_subcall_lines: is module call , no submodule_io_inf !')
+        return False
+    clear_last_trace_inf( trace_type )
+    return real_trace_signal_at_subcall_lines(trace_type, subcall_cursor_inf, cursor_inf, report_level = report_level)
+
+# this function used to trace signal for normal case
+def real_trace_normal_signal(trace_type, signal_appear_pos_line, cursor_inf):
+    assert(trace_type in ['source', 'dest'])
+    trace_signal_name  = cursor_inf['word']
+    cur_module_inf     = None
+    cur_line_inf       = FileInfLib.get_file_line_inf(cursor_inf['line_num'], cursor_inf['file_path'])
+    if cur_line_inf:
+        cur_module_inf = cur_line_inf['module_inf'] 
+    assert(cur_module_inf) # already qualify
+    cur_module_name    = cur_module_inf['module_name']
+    cur_module_path    = cur_module_inf['file_path']
+    # initial the trace inf
+    clear_last_trace_inf(trace_type)
+    if trace_type == 'source':
+        G['TraceInf']['LastTraceSource']['SignalName']     = cursor_inf['word']
+        G['TraceInf']['LastTraceSource']['ValidLineRange'] = cur_module_inf['module_line_range']
+        G['TraceInf']['LastTraceSource']['Path']           = cursor_inf['file_path']
+    else: 
+        G['TraceInf']['LastTraceDest']['SignalName']     = cursor_inf['word']
+        G['TraceInf']['LastTraceDest']['ValidLineRange'] = cur_module_inf['module_line_range']
+        G['TraceInf']['LastTraceDest']['Path']           = cursor_inf['file_path']
+    # add optimizing for signal such like clk, used by many times, but only io, or sub call is source
+    input_is_only_source = False
+    if trace_type == 'source' and len(signal_appear_pos_line) > G['TraceInf']['TraceSourceOptimizingThreshold']:
+        for appear_pos, appear_line in signal_appear_pos_line:
+            signal_appear_line = cursor_inf['codes'][appear_pos[0]]
+            if signal_appear_line.find('input') == -1:
+                continue
+            dest_or_source = current_appear_is_dest_or_source( trace_signal_name, [signal_appear_line], (0,appear_pos[1]) )
+            if dest_or_source in ['source', 'source_and_dest']:
+                input_is_only_source = True
+                show_str = '%s %d : %s'%(cur_module_name, appear_pos[0]+1, appear_line)
+                file_link_parm_dic = {   'type'             : 'trace_result'
+                                        ,'last_modify_time' : os.path.getmtime( cur_module_path )
+                                        ,'go_path'          : cur_module_path
+                                        ,'go_pos'           : appear_pos
+                                        ,'go_word'          : trace_signal_name }
+                file_link    = gen_hyperlink('go_file_action', file_link_parm_dic)
+                trace_result = {'show': show_str, 'file_link': file_link}
+                G['TraceInf']['LastTraceSource']['Sure'].append(trace_result)
+                break
+    # if found a input as source, should be the only source, clear appear pos to jump, normal search
+    if input_is_only_source:
+        signal_appear_pos_line = []
+    # appear_pos (line number, column), deal each match to find source
+    for appear_pos, appear_line in signal_appear_pos_line:
+        appear_dest_or_source     = False
+        appear_is_dest            = False
+        appear_is_source          = False
+        # check if a io or assign signal
+        dest_or_source = current_appear_is_dest_or_source( trace_signal_name, cursor_inf['codes'], appear_pos )
+        if dest_or_source in ['source', 'source_and_dest']:
+            appear_is_source = True
+        if dest_or_source in ['dest', 'source_and_dest']:
+            appear_is_dest   = True
+        # not assign signal check module call
+        submodule_and_subinstance = ''
+        if not (appear_is_dest or appear_is_source):
+            subcall_cursor_inf = get_subcall_pos_inf(cur_module_path, appear_pos, cursor_inf['codes'])
+            if subcall_cursor_inf:
+                # cur is subcall but not io name not match trace name go next
+                if not subcall_cursor_inf['submodule_io_inf']:
+                    appear_dest_or_source = True
+                elif subcall_cursor_inf['submodule_io_inf']['io_type'] in ['output', 'inout']:
+                    assert(trace_signal_name == subcall_cursor_inf['pos_word']),'%s != %s'%(trace_signal_name, subcall_cursor_inf['pos_word'])
+                    appear_is_source      = True
+                    submodule_and_subinstance = ':%s(%s)'%(subcall_cursor_inf['subcall_inf']['instance_name'],subcall_cursor_inf['subcall_inf']['submodule_name'])
+                    # for case subcall recoginize not accuracy, may because of '`ifdef, `endif ...'
+                    if subcall_cursor_inf['subcall_inf']['inaccuracy']: 
+                        PrintReport('Warning: carefull the trace result, subcall module:%s, instance:%s inaccuracy !'%(subcall_cursor_inf['subcall_inf']['module_name'], subcall_cursor_inf['subcall_inf']['instance_name']))
+                elif subcall_cursor_inf['submodule_io_inf']['io_type'] in ['input','inout']:
+                    assert(trace_signal_name == subcall_cursor_inf['pos_word']),'%s != %s'%(trace_signal_name, subcall_cursor_inf['pos_word'])
+                    appear_is_dest        = True
+                    submodule_and_subinstance = ':%s(%s)'%(subcall_cursor_inf['subcall_inf']['instance_name'],subcall_cursor_inf['subcall_inf']['submodule_name'])
+                    # for case subcall recoginize not accuracy, may because of '`ifdef, `endif ...'
+                    if subcall_cursor_inf['subcall_inf']['inaccuracy']: 
+                        PrintReport('Warning: carefull the trace result, subcall module:%s, instance:%s inaccuracy !'%(subcall_cursor_inf['subcall_inf']['module_name'], subcall_cursor_inf['subcall_inf']['instance_name']))
+                else:
+                    appear_dest_or_source = True
+            else:
+                appear_dest_or_source = True
+        assert(appear_is_source or appear_is_dest or appear_dest_or_source),'appear: "%s" must be some case !'%(appear_line)
+        assert( not ((appear_is_source or appear_is_dest) and appear_dest_or_source) ),'appear: "%s" if is dest or source , should not be maybe'%(appear_line)
+        # finial add to source/dest
+        show_str = '%s %d : %s'%(cur_module_name+submodule_and_subinstance, appear_pos[0]+1, appear_line)
+        file_link_parm_dic = {  'type'             : 'trace_result'
+                                ,'last_modify_time' : os.path.getmtime( cur_module_path )
+                                ,'go_path'          : cur_module_path
+                                ,'go_pos'           : appear_pos
+                                ,'go_word'          : trace_signal_name }
+        file_link    = gen_hyperlink('go_file_action', file_link_parm_dic)
+        trace_result = {'show': show_str, 'file_link': file_link}
+        if trace_type == 'source':
+            if appear_dest_or_source:
+                G['TraceInf']['LastTraceSource']['Maybe'].append(trace_result)
+            elif appear_is_source:
+                G['TraceInf']['LastTraceSource']['Sure'].append(trace_result)
+        else: # trace dest
+            if appear_dest_or_source:
+                G['TraceInf']['LastTraceDest']['Maybe'].append(trace_result)
+            elif appear_is_dest:
+                G['TraceInf']['LastTraceDest']['Sure'].append(trace_result)
+        continue
+    # finish get all dest/source
+    if trace_type == 'source':
+        finded_source_num       = len(G['TraceInf']['LastTraceSource']['Sure'])
+        finded_maybe_source_num = len(G['TraceInf']['LastTraceSource']['Maybe'])
+        # not find signal source
+        if not (finded_source_num + finded_maybe_source_num):
+            PrintReport("Note: Not find signal source !")
+            return True
+    else: # dest
+        finded_dest_num       = len(G['TraceInf']['LastTraceDest']['Sure'])
+        finded_maybe_dest_num = len(G['TraceInf']['LastTraceDest']['Maybe'])
+        # not find signal dest
+        if not (finded_dest_num + finded_maybe_dest_num):
+            PrintReport("Note: Not find signal dest !")
+            return True
+    # show source to report win, and go first trace
+    PrintReport(spec_case = trace_type)
+    show_next_trace_result(trace_type)
+    return True
+
+
+def trace_normal_signal(trace_type, cursor_inf):
+    cur_module_inf   = None
+    cur_line_inf     = FileInfLib.get_file_line_inf(cursor_inf['line_num'], cursor_inf['file_path'])
+    if cur_line_inf:
+        cur_module_inf = cur_line_inf['module_inf']  
+    if not cur_module_inf:
+        PrintDebug('Trace: cur file has no module inf, may be no database or cur line not in module, file: %s '%(cursor_inf['file_path']))
+        return False
+    # just use grep get all signal appear in current file to speed up signal search
+    signal_appear_pos_line = search_verilog_code_use_grep( cursor_inf['word'], cursor_inf['file_path'], cur_module_inf['module_line_range'] )
+    return real_trace_normal_signal(trace_type, signal_appear_pos_line, cursor_inf)
+
+
+# this function used to trace macro
+def trace_glb_define_signal(trace_type, cursor_inf):
+    assert(trace_type in ['dest', 'source'])
+    cur_word  = cursor_inf['word']
+    cur_line  = cursor_inf['line']
+    if cur_line.find('`') == -1: # bucause most case not trace macro, so it's worth pre check
+        PrintDebug('Trace: trace_glb_define_signal: %s not macro_name !'%(cur_word))
+        return False
+    # ...`XXX...
+    #      ^        cursor pos
+    #     XXX       cur word
+    # ...`XX        pre_pos_part
+    pre_pos_part = cur_line[:cursor_inf['pos'][1] + 1]
+    match_macro  = re.match('\w+`' , pre_pos_part[::-1])
+    if not match_macro:
+        PrintDebug('Trace: trace_glb_define_signal: %s not macro_name !'%(cur_word))
+        return False
+    # no matter get trace result or not trace done, need clear old trace result
+    clear_last_trace_inf(trace_type)
+    # cur_word is macro get macro inf list
+    cur_macro_inf_list = FileInfLib.get_macro_inf_list( cur_word )
+    if not cur_macro_inf_list:
+        PrintReport('Warning: not find macro: %s define in design !'%(cur_word))
+        return True
+    # if trace_type == 'dest': for macro no dest just source
+    if trace_type == 'dest':
+        PrintReport('None: macro: %s can not trace dest, only support trace source !'%(cur_word))
+        return True
+    # valid trace macro source
+    for macro_inf in cur_macro_inf_list: # {name path pos code_line}
+        file_name    = re.sub('.*/','',macro_inf['file_path'])
+        show_str     = '%s %d : %s'%(file_name, macro_inf['macro_name_match_pos'][0]+1, macro_inf['code_line'])
+        file_link_parm_dic  = { 'type'             : 'trace_result'
+                               ,'last_modify_time' : os.path.getmtime( macro_inf['file_path'] )
+                               ,'go_path'          : macro_inf['file_path']
+                               ,'go_pos'           : macro_inf['macro_name_match_pos']
+                               ,'go_word'          : cur_word }
+        file_link           = gen_hyperlink('go_file_action', file_link_parm_dic)
+        trace_result = {'show': show_str, 'file_link': file_link}
+        G['TraceInf']['LastTraceSource']['SignalName']     = cursor_inf['word']
+        G['TraceInf']['LastTraceSource']['ValidLineRange'] = (cursor_inf['line_num'], cursor_inf['line_num'])
+        G['TraceInf']['LastTraceSource']['Path']           = cursor_inf['file_path']
+        G['TraceInf']['LastTraceSource']['Sure'].append(trace_result)
+    # show source to report win, and go first trace
+    PrintReport(spec_case = trace_type)
+    show_next_trace_result(trace_type)
+    return True
+
+def get_upper_module_line_and_link_list( cur_module_name, trace_upper_inf = {} ):
+    # even has upper, also should list all the poss upper
+    call_me_subcall_inf_list = FileInfLib.get_call_me_subcall_inf_list( cur_module_name )
+    if call_me_subcall_inf_list:
+        # i = 0 
+        link_list = []
+        line_list = []
+        stale_call_me_subcall_inf_list = []
+        for i, subcall_inf in enumerate(call_me_subcall_inf_list):
+            if check_inf_valid( subcall_inf['file_path'], subcall_inf['last_modify_time']):
+                c_file_link = None
+                c_print_str = None
+                if not trace_upper_inf: # must go upper
+                    c_file_link_parm_dic    = {  'type'                : 'possible_upper'
+                                                # for go_file_action
+                                                ,'last_modify_time'    : os.path.getmtime( subcall_inf['file_path'] )
+                                                ,'go_path'             : subcall_inf['file_path']
+                                                ,'go_pos'              : subcall_inf['submodule_name_match_pos']
+                                                ,'go_word'             : subcall_inf['submodule_name'] 
+                                                # for add_module_last_call_action
+                                                ,'sub_module_name'     : subcall_inf['submodule_name']
+                                                ,'upper_module_name'   : subcall_inf['cur_module_name']
+                                                ,'upper_instance_name' : subcall_inf['instance_name']  }
+                    c_file_link = gen_hyperlink(['go_file_action', 'add_module_last_call_action'], c_file_link_parm_dic, 'possible_upper')
+                    c_print_str = '%d : %s -> %s (%s)'%(i, subcall_inf['cur_module_name'], subcall_inf['instance_name'], subcall_inf['submodule_name'])
+                else:
+                    c_file_link_parm_dic    = {  'type'                : 'possible_trace_upper'
+                                                # for trace_io_signal_action
+                                                ,'trace_type'          : trace_upper_inf['trace_type']
+                                                ,'cursor_inf'          : trace_upper_inf['cursor_inf']
+                                                ,'report_level'        : trace_upper_inf['report_level']
+                                                # for add_module_last_call_action
+                                                ,'sub_module_name'     : subcall_inf['submodule_name']
+                                                ,'upper_module_name'   : subcall_inf['cur_module_name']
+                                                ,'upper_instance_name' : subcall_inf['instance_name']  }
+                    c_file_link_parm_dic['cursor_inf']['codes'] = None # fix bug for vim.buffer cannot pickle when +s
+                    c_file_link = gen_hyperlink(['trace_io_signal_action', 'add_module_last_call_action'], c_file_link_parm_dic, 'possible_trace_upper')
+                    c_print_str = '%d : %s -> %s (%s)'%(i, subcall_inf['cur_module_name'], subcall_inf['instance_name'], subcall_inf['submodule_name'])
+                link_list.append( c_file_link )
+                line_list.append( c_print_str )
+            else:
+                stale_call_me_subcall_inf_list.append( subcall_inf )
+        if stale_call_me_subcall_inf_list:
+            line_list.append( 'Stale:' )
+            line_list.append( '( stale means upper module file has been modified )' )
+            link_list.append( {} )
+            link_list.append( {} )
+            for subcall_inf in stale_call_me_subcall_inf_list:
+                # current is for stale possible call
+                c_file_link = None
+                c_print_str = None
+                if not trace_upper_inf: # must go upper
+                    c_file_link_parm_dic    = {  'type'                : 'possible_upper'
+                                                # for go_file_action
+                                                ,'last_modify_time'    : os.path.getmtime( subcall_inf['file_path'] )
+                                                ,'go_path'             : subcall_inf['file_path']
+                                                ,'go_pos'              : subcall_inf['submodule_name_match_pos']
+                                                ,'go_word'             : subcall_inf['submodule_name'] 
+                                                # for add_module_last_call_action
+                                                ,'sub_module_name'     : subcall_inf['submodule_name']
+                                                ,'upper_module_name'   : subcall_inf['cur_module_name']
+                                                ,'upper_instance_name' : subcall_inf['instance_name']  }
+                    c_file_link = gen_hyperlink(['go_file_action', 'add_module_last_call_action'], c_file_link_parm_dic, 'possible_upper')
+                    c_print_str = '%d : %s -> %s (%s)'%(i, subcall_inf['cur_module_name'], subcall_inf['instance_name'], subcall_inf['submodule_name'])
+                else:
+                    c_file_link_parm_dic    = {  'type'                : 'possible_trace_upper'
+                                                # for trace_io_signal_action
+                                                ,'trace_type'          : trace_upper_inf['trace_type']
+                                                ,'cursor_inf'          : trace_upper_inf['cursor_inf']
+                                                ,'report_level'        : trace_upper_inf['report_level']
+                                                # for add_module_last_call_action
+                                                ,'sub_module_name'     : subcall_inf['submodule_name']
+                                                ,'upper_module_name'   : subcall_inf['cur_module_name']
+                                                ,'upper_instance_name' : subcall_inf['instance_name']  }
+                    c_file_link_parm_dic['cursor_inf']['codes'] = None # fix bug for vim.buffer cannot pickle when +s
+                    c_file_link = gen_hyperlink(['trace_io_signal_action', 'add_module_last_call_action'], c_file_link_parm_dic, 'possible_trace_upper')
+                    c_print_str = '%d : %s -> %s (%s)'%(i, subcall_inf['cur_module_name'], subcall_inf['instance_name'], subcall_inf['submodule_name'])
+                link_list.append( c_file_link )
+                line_list.append( c_print_str )
+        return {"line_list":line_list, 'link_list':link_list}
+    return {}
+
diff --git a/Lib/ExceptionLib.py b/Lib/ExceptionLib.py
new file mode 100755
index 0000000..0d68d7e
--- /dev/null
+++ b/Lib/ExceptionLib.py
@@ -0,0 +1,44 @@
+"""
+http://www.vim.org/scripts/script.php?script_id=5494
+"""
+#===============================================================================
+# BSD 2-Clause License
+
+# Copyright (c) 2016, CaoJun
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * 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.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#===============================================================================
+
+# exception used to raise when not found vtags db
+# used to speed up none vtags vim file open
+class VtagsDBNotFoundExcept(BaseException):
+    """docstring for VtagsDBNotFoundExcept"""
+    def __init__(self):
+        super(VtagsDBNotFoundExcept, self).__init__()
+
+# exception used to raise when not a valid vtags file opened
+# used to speed up none vtags vim file open
+class VtagsUnsupportFileExcept(BaseException):
+    """docstring for VtagsUnsupportFileExcept"""
+    def __init__(self):
+        super(VtagsUnsupportFileExcept, self).__init__()
\ No newline at end of file
diff --git a/Lib/FileInfLib.py b/Lib/FileInfLib.py
new file mode 100755
index 0000000..e963cde
--- /dev/null
+++ b/Lib/FileInfLib.py
@@ -0,0 +1,1275 @@
+"""
+http://www.vim.org/scripts/script.php?script_id=5494
+"""
+#===============================================================================
+# BSD 2-Clause License
+
+# Copyright (c) 2016, CaoJun
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * 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.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#===============================================================================
+
+import sys
+import re
+import os
+import re
+import Lib.GLB as GLB
+G = GLB.G
+from Lib.BaseLib import *
+from InlineLib.ViewLib import *
+
+def get_shortpath(real_path, create = False):
+    path_split = real_path.split('/')
+    file_name  = path_split[-1]
+    # if file name length is ok just return
+    if len(file_name) < G["MaxFileNameLength"]:
+        return real_path
+    # file name length is to long reduce it
+    #    init map if not
+    Short2RealPathMap = G["Short2RealPathMap"]
+    Real2ShortPathMap = G["Real2ShortPathMap"]
+    if Short2RealPathMap == None: # means not initial, try to initial it from pickle file
+        Short2RealPathMap = pickle_reload(G['VTagsPath']+'/pickle/short_to_real_path_map.pkl')
+        if Short2RealPathMap == None: # if still None means no pkl file, just inital it to empty
+            Short2RealPathMap = {}
+        # get all reduced path
+        for sp in Short2RealPathMap:
+            assert( Short2RealPathMap[sp] not in Real2ShortPathMap )
+            Real2ShortPathMap[ Short2RealPathMap[sp] ] = sp
+        G["Short2RealPathMap"] = Short2RealPathMap
+        G["Real2ShortPathMap"] = Real2ShortPathMap
+    #   if path already exist return sort path
+    if real_path in Real2ShortPathMap:
+        return Real2ShortPathMap[real_path]
+    #   generate short path name
+    if create == True:
+        path_split[-1] = "SortPath_%s.pkl"%(len(Short2RealPathMap))
+        short_path = '/'.join( path_split )
+        Short2RealPathMap[ short_path ] = real_path
+        Real2ShortPathMap[ real_path ] = short_path
+        return short_path
+    else:
+        # assert(0), "not create new map, path should has match !"
+        return ''
+
+# if has file_list : get all the design file path from filelist
+# else  get all design file from current folder
+def get_all_design_file_path_from_filelist(file_list = ''):
+    # get all the dir_path and file_path from file_list
+    file_or_dir_path_set = set()
+    if file_list:
+        assert(os.path.isfile(file_list)),'Error: filelist: %s , not exist !'%(file_list)
+        file_or_dir_path_list_raw = open(file_list, 'r').readlines()
+        for l in file_or_dir_path_list_raw:
+            l = (l.split('//')[0]).strip()
+            if l != '':
+                file_or_dir_path_set.add(l)
+    else:
+        cur_dir_path  = os.getcwd()
+        file_or_dir_path_set.add(cur_dir_path)
+    # get design file from dir_path and file path, and updata design postfix through file in filelist
+    # pyhon not recognize "~" as home dir, need change "~" to abs home path
+    # get the abs home path
+    home_path = os.popen('echo ~').readlines()[0].rstrip('\n').rstrip('/')
+    # for each line in file_or_dir_path_set, to get all the verilog files supported
+    # get all the verilog files path
+    # (1) add all the file path to file list and add corresponding postfix to verilog
+    #     supported postfix
+    # (2) add all the dir path the dir_path_set
+    # (3) save all the postfix, new updated
+    # (4) for each dir path , find all the verilog files
+    file_path_set = set()
+    dir_path_set  = set()
+    add_postfix   = False
+    for file_or_dir_path in file_or_dir_path_set:
+        file_or_dir_path = re.sub('^~', home_path, file_or_dir_path)
+        if re.match('\s*$',file_or_dir_path):
+            continue
+        if os.path.isdir(file_or_dir_path):    # step (2)
+            # need change to abs path first
+            dir_path_set.add(os.path.realpath(file_or_dir_path))
+        elif os.path.isfile(file_or_dir_path): # step (1)
+            file_path_set.add(os.path.realpath(file_or_dir_path))
+            cur_file_postfix = get_file_path_postfix(file_or_dir_path)
+            if not cur_file_postfix:
+                continue
+            if cur_file_postfix not in G['SupportVerilogPostfix']:
+                G['SupportVerilogPostfix'].add(cur_file_postfix)
+                add_postfix = True
+    # step 3
+    if add_postfix:
+        pkl_output = open(G['VTagsPath'] + '/file_list_postfix.pkl','wb')
+        pickle.dump(G['SupportVerilogPostfix'], pkl_output)
+        pkl_output.close()
+    # step 4
+    postfix_patten = '|'.join(list(G['SupportVerilogPostfix']))
+    for dir_path in dir_path_set:
+        # cur_dir_all_files   = os.popen('find ' + dir_path + ' -type f 2>/dev/null').readlines()
+        #cur_dir_all_files    = os.popen('find %s -type f 2>/dev/null | egrep "\.(%s)$"'%(dir_path,postfix_patten)).readlines()
+        cur_dir_all_files    = os.popen('find %s -path \'*vtags.db\' -a -prune -o -type f 2>/dev/null | egrep "\.(%s)$"'%(dir_path,postfix_patten)).readlines()
+        cur_dir_all_files    = [ d_l.rstrip('\n') for d_l in cur_dir_all_files ]
+        for f in cur_dir_all_files:
+            assert(get_file_path_postfix(f) in G['SupportVerilogPostfix']),'%s'%(f)
+            file_path_set.add(f)
+    return file_path_set
+
+
+# this function used get all the files's verilog code_inf
+# return is a dic, key is the file_path in paths, 
+def init_get_file_path_to_code_inf_dic(paths):
+    # step 1/2 get all module/define inf
+    file_path_to_module_inf_dic   = {}
+    file_path_to_macro_define_dic = {}
+    all_module_name_set           = set() # a set of all the module names finded in design
+    print('step 1/2:')
+    for i,f in enumerate(paths):
+        show_progress_bar( i, len(paths))
+        PrintDebug(f)
+        # gen cur module and define inf
+        # cur_file_module_inf is a list of module_inf ordered by appeared line num in file
+        # module_inf = { 'module_name'            : ''
+        #               ,'module_line_range'      : ()
+        #               ,'module_name_match_pos'  : () 
+        #               -------------------------------------------
+        #               ,'file_path'                   : ''        # set when add to G['ModuleNameToModuleInfListDic']
+        #               ,'subcall_instance_list'       : None      # [subcall_inf,...] set when first open topo
+        #               ,'instance_to_subcall_inf_dic' : None      # add one by one when used
+        #              }
+        cur_file_module_inf = get_single_verilog_file_module_inf(f)
+        # cur_file_macro_inf is a list of macro_inf ordered by appeared line num in file
+        # macro_inf = { "macro_name"              : ''
+        #              ,"macro_name_match_pos"    : (line_num, colum_num)  # name first char pos
+        #              ,'code_line'               : `define xxx ....}
+        #               -------------------------------------------
+        #              ,"file_path"               : ''  # no needed
+        cur_file_macro_inf  = get_single_verilog_file_macro_define_inf(f)
+        # add to result
+        file_path_to_module_inf_dic[f]   = cur_file_module_inf
+        file_path_to_macro_define_dic[f] = cur_file_macro_inf
+        all_module_name_set              = all_module_name_set | set([ mi['module_name'] for mi in cur_file_module_inf])
+    print('')
+    # step 2/2 get all file sub call inf
+    file_path_to_subcall_inf_dic = {}
+    patten = get_submodule_match_patten(all_module_name_set)
+    print('step 2/2:')
+    for i,f in enumerate(paths):
+        PrintDebug(f)
+        show_progress_bar( i, len(paths))
+        # get_single_verilog_file_subcall_inf return a list of subcall_inf ordered by appeared line num in file
+        # subcall_inf = { 'submodule_name'           : ''
+        #                 'instance_name'            : ''
+        #                 'subcall_line_range'       : ()
+        #                 'submodule_name_match_pos' : () }
+        file_path_to_subcall_inf_dic[f] = get_single_verilog_file_subcall_inf(f, patten, all_module_name_set, file_path_to_module_inf_dic[f])
+    print('')
+    # merge to file_path_to_code_inf_dic
+    # file_inf = { 'macro_inf_list'   : [] # list of macro_inf
+    #             ,'module_inf_list'  : [] # list of module_inf
+    #             ,'subcall_inf_list' : [] # list of subcall_inf
+    #             ,'last_modify_time' : os.path.getmtime(f) }
+    file_path_to_code_inf_dic = {}
+    for f in paths:
+        file_path_to_code_inf_dic[f] = { 
+             'module_inf_list'  : file_path_to_module_inf_dic[f]   # list of module_inf
+            ,'macro_inf_list'   : file_path_to_macro_define_dic[f] # list of macro_inf
+            ,'subcall_inf_list' : file_path_to_subcall_inf_dic[f]  # list of subcall_inf
+            ,'last_modify_time' : os.path.getmtime(f) }
+    return file_path_to_code_inf_dic
+
+
+# this function used to get current line inf from file_inf
+# if current line in module, return module_inf
+# if current line in submodule, return subcall_inf
+def get_file_line_inf(line_num, path):
+    updata_file_inf(path)
+    if path not in G['FileInf']:
+        PrintDebug('Trace: get_file_line_inf: %s has no file database !'%(path) )
+        return False
+    assert(G['FileInf'][path]['last_modify_time'] == os.path.getmtime(path))
+    return get_line_inf_from_cur_file_inf( line_num, G['FileInf'][path] )
+
+
+# use this function to get the module inf from module name
+def get_module_inf(module_name, report_level = 1):
+    # if module in G['ModuleNameToModuleInfListDic']
+    if module_name in G['ModuleNameToModuleInfListDic']:
+        # del stale inf
+        i = 0
+        while i < len(G['ModuleNameToModuleInfListDic'][module_name]):
+            t_module_inf = G['ModuleNameToModuleInfListDic'][module_name][i]
+            if not check_inf_valid(t_module_inf['file_path'], t_module_inf['last_modify_time']):
+                del G['ModuleNameToModuleInfListDic'][module_name][i]
+                continue
+            i += 1
+        # if has multi valid module define print report
+        if len(G['ModuleNameToModuleInfListDic'][module_name]) > 1:
+            # PrintReport('Warning: module:%s has %d define ! choise first  of %s '%(module_name, len(G['ModuleNameToModuleInfListDic']), [ module_inf['file_path'] + ':' + str(module_inf['module_line_range'][0]+1) for module_inf in G['ModuleNameToModuleInfListDic'] ] ))
+            PrintReport('Warning: module:%s has %d define ! choise first  of %s '%(module_name, len(G['ModuleNameToModuleInfListDic'][module_name]), [ module_inf['file_path'] + ':' + str(module_inf['module_line_range'][0]+1) for module_inf in G['ModuleNameToModuleInfListDic'][module_name] ] ))
+        # return a valid module inf
+        if len(G['ModuleNameToModuleInfListDic'][module_name]) > 0:
+            return G['ModuleNameToModuleInfListDic'][module_name][0]
+    # no valid module inf in G, updata module path one by one
+    onload_G_ModuleNameToFilePathListDic()
+    changed_ModuleNameToFilePathListDic = False
+    i = 0
+    while (module_name in G['ModuleNameToFilePathListDic']) and ( i < len(G['ModuleNameToFilePathListDic'][module_name]) ):
+        t_path = G['ModuleNameToFilePathListDic'][module_name][i]
+        updata_file_inf(t_path)
+        if (module_name in G['ModuleNameToModuleInfListDic']) and len(G['ModuleNameToModuleInfListDic'][module_name]) > 0:
+            break
+        # still not get module_inf, then this path is stale, del it
+        del G['ModuleNameToFilePathListDic'][module_name][i]
+        changed_ModuleNameToFilePathListDic = True
+    if changed_ModuleNameToFilePathListDic:
+        module_name_to_file_path_list_dic_pkl_path = G['VTagsPath']+'/pickle/module_name_to_file_path_list_dic.pkl'
+        pickle_save(G['ModuleNameToFilePathListDic'], module_name_to_file_path_list_dic_pkl_path)
+    # if has multi valid module define print report
+    if (module_name in G['ModuleNameToModuleInfListDic']) and len(G['ModuleNameToModuleInfListDic'][module_name]) > 1:
+        # PrintReport('Warning: module:%s has %d define ! choise first  of %s '%(module_name, len(G['ModuleNameToModuleInfListDic']), [ module_inf['file_path'] + ':' + str(module_inf['module_line_range'][0]+1) for module_inf in G['ModuleNameToModuleInfListDic'] ] ))
+        PrintReport('Warning: module:%s has %d define ! choise first  of %s '%(module_name, len(G['ModuleNameToModuleInfListDic'][module_name]), [ module_inf['file_path'] + ':' + str(module_inf['module_line_range'][0]+1) for module_inf in G['ModuleNameToModuleInfListDic'][module_name] ] ))
+    # return a valid module inf
+    if (module_name in G['ModuleNameToModuleInfListDic']) and len(G['ModuleNameToModuleInfListDic'][module_name]) > 0:
+        return G['ModuleNameToModuleInfListDic'][module_name][0]
+    # get here means not find module_inf at current vtags.db, if config allow refresh vtags.db, do it 
+    if G['RefreshDBValid']:
+        refresh_vtags_db()
+    # if has multi valid module define print report
+    if (module_name in G['ModuleNameToModuleInfListDic']) and len(G['ModuleNameToModuleInfListDic'][module_name]) > 1:
+        # PrintReport('Warning: module:%s has %d define ! choise first  of %s '%(module_name, len(G['ModuleNameToModuleInfListDic']), [ module_inf['file_path'] + ':' + str(module_inf['module_line_range'][0]+1) for module_inf in G['ModuleNameToModuleInfListDic'] ] ))
+        PrintReport('Warning: module:%s has %d define ! choise first  of %s '%(module_name, len(G['ModuleNameToModuleInfListDic'][module_name]), [ module_inf['file_path'] + ':' + str(module_inf['module_line_range'][0]+1) for module_inf in G['ModuleNameToModuleInfListDic'][module_name] ] ))
+    # return a valid module inf
+    if (module_name in G['ModuleNameToModuleInfListDic']) and len(G['ModuleNameToModuleInfListDic'][module_name]) > 0:
+        return G['ModuleNameToModuleInfListDic'][module_name][0]
+    PrintReport('Warning: module: %s not found in design !'%(module_name), report_level = report_level )
+    return False
+
+
+
+#############################################################################################
+#-------------------------------------------------------------------------------
+# note subcall upper module inf
+# this function used to get the last call upper module inf
+#-------------------------------------------------------------------------------
+
+
+# this function used to save last call upper module inf
+def set_module_last_call_inf(sub_module_name, upper_module_name, upper_instance_name):
+    G['ModuleLastCallInf'][sub_module_name] = { 'upper_module_name': upper_module_name, 'upper_instance_name': upper_instance_name }
+# hyperlink action add_module_last_call_action
+def add_module_last_call_action(sub_module_name, upper_module_name, upper_instance_name):
+    set_module_last_call_inf(sub_module_name, upper_module_name, upper_instance_name)
+register_hyperlink_action( add_module_last_call_action, description = 'this link function add module last call' )
+
+def get_module_last_call_inf(module_name):
+    if module_name not in G['ModuleLastCallInf']:
+        return False
+    his_upper_module_name     = G['ModuleLastCallInf'][module_name]['upper_module_name']
+    his_upper_instance_name   = G['ModuleLastCallInf'][module_name]['upper_instance_name']
+    upper_module_inf          = get_module_inf(his_upper_module_name)
+    if not upper_module_inf:
+        PrintDebug('Trace: get_module_last_call_inf: history upper module %s not exist now !'%(his_upper_module_name))
+        return False
+    # to get upper module_to_instance subcall_inf
+    if upper_module_inf.setdefault('instance_to_subcall_inf_dic',None) == None:
+        assert(module_inf_add_instance_to_subcall_inf_dic(upper_module_inf))
+    if his_upper_instance_name not in upper_module_inf['instance_to_subcall_inf_dic']:
+        PrintDebug('Trace: get_module_last_call_inf: history instance_name %s not exist now !'%(his_upper_instance_name))
+        return False
+    subcall_inf = upper_module_inf['instance_to_subcall_inf_dic'][his_upper_instance_name]
+    return {'upper_module_inf': upper_module_inf, 'upper_subcall_inf': subcall_inf}
+
+# this function used line range to get current module's subcall list
+def get_the_subcall_instance_list(module_line_range, module_file_subcall_order_list):
+    subcall_instance_list = []
+    for subcall_inf in module_file_subcall_order_list:
+        subcall_line_range = subcall_inf['subcall_line_range']
+        if subcall_line_range[1] < module_line_range[0]:
+            continue
+        if subcall_line_range[0] > module_line_range[1]:
+            break
+        assert( module_line_range[0] <= subcall_line_range[0] and subcall_line_range[1] <= module_line_range[1] )
+        subcall_instance_list.append(subcall_inf)
+    return subcall_instance_list
+
+# this function add subcall_instance_list to module_inf
+def module_inf_add_subcall_instance_list(module_inf):
+    module_line_range = module_inf['module_line_range']
+    module_file_path  = module_inf['file_path']
+    assert(module_file_path in G['FileInf'])
+    module_file_subcall_order_list = G['FileInf'][module_file_path]['subcall_inf_list']
+    subcall_instance_list = get_the_subcall_instance_list(module_line_range, module_file_subcall_order_list)
+    module_inf['subcall_instance_list'] = subcall_instance_list
+    # check old subcall inf the same with new vtags edition 
+    if G['Debug']:
+        for subcall_inf in module_inf['subcall_instance_list']:
+            assert( subcall_inf['cur_module_name'] == module_inf['module_name'] ),'%s'%(subcall_inf.__str__())
+    return True
+
+# this function add instance_to_subcall_inf_dic to module_inf
+def module_inf_add_instance_to_subcall_inf_dic(module_inf):
+    if module_inf.setdefault('subcall_instance_list',None) == None :
+        assert(module_inf_add_subcall_instance_list(module_inf))
+    instance_to_subcall_inf_dic = {}
+    for subcall_inf in module_inf['subcall_instance_list']:
+        if subcall_inf['instance_name'] in instance_to_subcall_inf_dic:
+            PrintDebug('Warning: instance name "%s" used multi times, in module:%s, file: %s'%(subcall_inf['instance_name'], module_inf['module_name'], module_inf['file_path'] ))
+        instance_to_subcall_inf_dic[subcall_inf['instance_name']] = subcall_inf
+    module_inf['instance_to_subcall_inf_dic'] = instance_to_subcall_inf_dic
+    return True
+
+
+def refresh_vtags_db():
+    # 1. get the refresh vtags db inf
+    vtags_db_refresh_inf_pkl_path = G['VTagsPath']+'/pickle/vtags_db_refresh_inf.pkl'
+    vtags_db_refresh_inf = pickle_reload(vtags_db_refresh_inf_pkl_path)
+    if not vtags_db_refresh_inf:
+        PrintDebug('Error: no filelist found in current dir, can not refresh !')
+        return False
+    print(' refreshing design database ... ')
+    file_list = vtags_db_refresh_inf['file_list']
+    file_path_to_last_modify_time_dic = vtags_db_refresh_inf['file_path_to_last_modify_time_dic']
+    # 2 get current all design file
+    old_design_file_path_set = set(file_path_to_last_modify_time_dic)
+    cur_design_file_path_set = get_all_design_file_path_from_filelist(file_list)
+    # 3. for file current del, rm corresponding inf
+    deled_file_path_set = old_design_file_path_set - cur_design_file_path_set
+    changed_vtags_db_refresh_inf = False
+    for file_path in deled_file_path_set:
+        file_pkl_path = get_shortpath( G['VTagsPath']+'/pickle/design__%s.pkl'%(file_path.replace('/','__')), create = False )
+        if os.path.isfile(file_pkl_path):
+            os.system('rm -f %s'%(file_pkl_path))
+        del file_path_to_last_modify_time_dic[file_path]
+        changed_vtags_db_refresh_inf = True
+    # 4. get stale file and new add file
+    stale_and_new_file_path_set = set()
+    for file_path in cur_design_file_path_set:
+        if file_path in file_path_to_last_modify_time_dic:
+            last_modify_time = file_path_to_last_modify_time_dic[file_path]
+            if check_inf_valid(file_path, last_modify_time):
+                continue
+        stale_and_new_file_path_set.add(file_path)
+    # 5 for each stale file and new add file
+    if not stale_and_new_file_path_set:
+        if changed_vtags_db_refresh_inf:
+            vtags_db_refresh_inf = {
+                 'file_list'                         : file_list
+                ,'file_path_to_last_modify_time_dic' : file_path_to_last_modify_time_dic }
+            vtags_db_refresh_inf_pkl_path = G['VTagsPath']+'/pickle/vtags_db_refresh_inf.pkl'
+            pickle_save(vtags_db_refresh_inf, vtags_db_refresh_inf_pkl_path)
+        return True
+    changed_ModuleNameToFilePathListDic = False
+    changed_MacroNameToMacroInfListDic  = False
+    onload_G_ModuleNameToFilePathListDic()
+    file_path_to_code_inf_dic = get_file_path_to_code_inf_dic(stale_and_new_file_path_set, set(G['ModuleNameToFilePathListDic']))
+    merge_subcall_inf_list    = []
+    for f in file_path_to_code_inf_dic:
+        # updata path last modify time
+        file_path_to_last_modify_time_dic[f] = file_path_to_code_inf_dic[f]['last_modify_time']
+        # updata module_name to file path dic
+        module_inf_list = file_path_to_code_inf_dic[f]['module_inf_list'  ]
+        for module_inf in module_inf_list:
+            add_ModuleNameToModuleInfListDic(module_inf)
+            if add_ModuleNameToFilePathListDic(module_inf):
+                changed_ModuleNameToFilePathListDic = True
+        # updata macro
+        macro_inf_list = file_path_to_code_inf_dic[f]['macro_inf_list']
+        for macro_inf in macro_inf_list:
+            add_MacroNameToMacroInfListDic(macro_inf)
+            changed_MacroNameToMacroInfListDic = True
+        # updata code_inf
+        del file_path_to_code_inf_dic[f]['macro_inf_list']
+        G['FileInf'][f] = file_path_to_code_inf_dic[f]
+        # get all subcall inf of new files
+        merge_subcall_inf_list += file_path_to_code_inf_dic[f]['subcall_inf_list']
+    # 6 update call me subcall inf for all new subcall in cuurent new file
+    add_ModuleNameToCallMeSubcallInfListDic( merge_subcall_inf_list )
+    # 7 pickle save new inf
+    os.system('mkdir -p %s'%(G['VTagsPath']+'/pickle'))
+    # 1) pickle file_list,file_path_to_last_modify_time_dic or refresh vtags.db
+    vtags_db_refresh_inf = {
+         'file_list'                         : file_list
+        ,'file_path_to_last_modify_time_dic' : file_path_to_last_modify_time_dic
+    }
+    vtags_db_refresh_inf_pkl_path = G['VTagsPath']+'/pickle/vtags_db_refresh_inf.pkl'
+    pickle_save(vtags_db_refresh_inf, vtags_db_refresh_inf_pkl_path)
+    # 2 pickle module_name_to_file_path_list_dic, for refresh single file subcall_inf
+    if changed_ModuleNameToFilePathListDic:
+        module_name_to_file_path_list_dic_pkl_path = G['VTagsPath']+'/pickle/module_name_to_file_path_list_dic.pkl'
+        pickle_save(G['ModuleNameToFilePathListDic'], module_name_to_file_path_list_dic_pkl_path)
+    # 3 pick all macro inf
+    if changed_MacroNameToMacroInfListDic:
+        macro_name_to_macro_inf_list_dic_pkl_path = G['VTagsPath']+'/pickle/macro_name_to_macro_inf_list_dic.pkl'
+        pickle_save(G['MacroNameToMacroInfListDic'], macro_name_to_macro_inf_list_dic_pkl_path)
+    # 4 pick file_path_to_code_inf_dic
+    # pick save all f inf
+    for f in file_path_to_code_inf_dic:
+        code_inf = file_path_to_code_inf_dic[f]
+        code_inf_pkl_path = G['VTagsPath']+'/pickle/design__%s.pkl'%(f.replace('/','__'))
+        pickle_save(code_inf, get_shortpath( code_inf_pkl_path, creat = True) )
+    pickle_save(G["Short2RealPathMap"], G['VTagsPath']+'/pickle/short_to_real_path_map.pkl')
+    return True
+
+def add_ModuleNameToModuleInfListDic( module_inf ):
+    module_name = module_inf['module_name']
+    G['ModuleNameToModuleInfListDic'].setdefault(module_name,[])
+    # del old
+    i = 0
+    while i < len(G['ModuleNameToModuleInfListDic'][module_name]):
+        t_module_inf = G['ModuleNameToModuleInfListDic'][module_name][i]
+        if not check_inf_valid(t_module_inf['file_path'], t_module_inf['last_modify_time']):
+            del G['ModuleNameToModuleInfListDic'][module_name][i]
+            continue
+        else: 
+            # already has newest module inf, if they are same with new add, just del old
+            # add module_name_match_pos incase same module has multi define of same module_name
+            if (t_module_inf['file_path'], t_module_inf['module_name'], t_module_inf['module_name_match_pos']) == \
+                (module_inf['file_path'], module_inf['module_name'], module_inf['module_name_match_pos']):
+                del G['ModuleNameToModuleInfListDic'][module_name][i]
+                continue
+        i += 1
+    # add new
+    G['ModuleNameToModuleInfListDic'][module_name].append(module_inf) 
+
+def add_ModuleNameToFilePathListDic(module_inf):
+    real_added = False
+    onload_G_ModuleNameToFilePathListDic()
+    module_name = module_inf['module_name']
+    module_path = module_inf['file_path']
+    G['ModuleNameToFilePathListDic'].setdefault(module_name,[])
+    if module_path not in G['ModuleNameToFilePathListDic'][module_name]:
+        G['ModuleNameToFilePathListDic'][ module_name ].append(module_path)
+        real_added = True
+    return real_added
+
+def add_MacroNameToMacroInfListDic(macro_inf):
+    onload_G_MacroNameToMacroInfListDic()
+    macro_name = macro_inf['macro_name']
+    G['MacroNameToMacroInfListDic'].setdefault(macro_name,[])
+    # del old
+    i = 0
+    while i < len(G['MacroNameToMacroInfListDic'][macro_name]):
+        t_macro_inf = G['MacroNameToMacroInfListDic'][macro_name][i]
+        if not check_inf_valid(t_macro_inf['file_path'], t_macro_inf['last_modify_time']):
+            del G['MacroNameToMacroInfListDic'][macro_name][i]
+            continue
+        i += 1
+    G['MacroNameToMacroInfListDic'][macro_name].append(macro_inf)
+
+
+def get_file_path_to_code_inf_dic(paths, all_module_name_set):
+    # step 1/2 get all module/define inf
+    file_path_to_module_inf_dic   = {}
+    file_path_to_macro_define_dic = {}
+    # print('step 1/2:')
+    for i,f in enumerate(paths):
+        # show_progress_bar( i, len(paths))
+        # PrintDebug(f)
+        # gen cur module and define inf
+        # cur_file_module_inf is a list of module_inf ordered by appeared line num in file
+        # module_inf = { 'module_name'            : ''
+        #               ,'module_line_range'      : ()
+        #               ,'module_name_match_pos'  : () 
+        #               -------------------------------------------
+        #               ,'file_path'                   : ''        # set when add to G['ModuleNameToModuleInfListDic']
+        #               ,'subcall_instance_list'       : None      # [subcall_inf,...] set when first open topo
+        #               ,'instance_to_subcall_inf_dic' : None      # add one by one when used
+        #              }
+        cur_file_module_inf = get_single_verilog_file_module_inf(f)
+        # cur_file_macro_inf is a list of macro_inf ordered by appeared line num in file
+        # macro_inf = { "macro_name"              : ''
+        #              ,"macro_name_match_pos"    : (line_num, colum_num)  # name first char pos
+        #              ,'code_line'               : `define xxx ....}
+        #               -------------------------------------------
+        #              ,"file_path"               : ''  # no needed
+        cur_file_macro_inf  = get_single_verilog_file_macro_define_inf(f)
+        # add to result
+        file_path_to_module_inf_dic[f]   = cur_file_module_inf
+        file_path_to_macro_define_dic[f] = cur_file_macro_inf
+        all_module_name_set              = all_module_name_set | set([ mi['module_name'] for mi in cur_file_module_inf])
+    # print('')
+    # step 2/2 get all file sub call inf
+    file_path_to_subcall_inf_dic = {}
+    patten = get_submodule_match_patten(all_module_name_set)
+    # print('step 2/2:')
+    for i,f in enumerate(paths):
+        # PrintDebug(f)
+        # show_progress_bar( i, len(paths))
+        # get_single_verilog_file_subcall_inf return a list of subcall_inf ordered by appeared line num in file
+        # subcall_inf = { 'submodule_name'           : ''
+        #                 'instance_name'            : ''
+        #                 'subcall_line_range'       : ()
+        #                 'submodule_name_match_pos' : () }
+        file_path_to_subcall_inf_dic[f] = get_single_verilog_file_subcall_inf(f, patten, all_module_name_set, file_path_to_module_inf_dic[f])
+    # print('')
+    # merge to file_path_to_code_inf_dic
+    # file_inf = { 'macro_inf_list'   : [] # list of macro_inf
+    #             ,'module_inf_list'  : [] # list of module_inf
+    #             ,'subcall_inf_list' : [] # list of subcall_inf
+    #             ,'last_modify_time' : os.path.getmtime(f) }
+    file_path_to_code_inf_dic = {}
+    for f in paths:
+        file_path_to_code_inf_dic[f] = { 
+             'module_inf_list'  : file_path_to_module_inf_dic[f]   # list of module_inf
+            ,'macro_inf_list'   : file_path_to_macro_define_dic[f] # list of macro_inf
+            ,'subcall_inf_list' : file_path_to_subcall_inf_dic[f]  # list of subcall_inf
+            ,'last_modify_time' : os.path.getmtime(f) }
+    return file_path_to_code_inf_dic
+
+
+# this function get the line inf from file_inf
+# if line in module, return module_inf
+# if line is subcall, return subcall_inf
+def get_line_inf_from_cur_file_inf(line_num, file_inf):
+    line_module_inf   = {}
+    line_subcall_inf  = {}
+    module_inf_list   = file_inf['module_inf_list' ]
+    subcall_inf_list  = file_inf['subcall_inf_list']
+    # first get current line module inf
+    for module_inf in module_inf_list:
+        cur_module_line_range = module_inf['module_line_range']
+        if cur_module_line_range[1] < line_num:
+            continue
+        if line_num < cur_module_line_range[0]:
+            break
+        line_module_inf = module_inf
+    # second get current line call sub inf
+    for subcall_inf in subcall_inf_list:
+        cur_subcall_line_range = subcall_inf['subcall_line_range']
+        if cur_subcall_line_range[1] < line_num:
+            continue
+        if line_num < cur_subcall_line_range[0]:
+            break
+        line_subcall_inf = subcall_inf
+    return  {
+         'module_inf'   : line_module_inf
+        ,'subcall_inf'  : line_subcall_inf
+    }
+
+
+def updata_file_inf(path):
+    # not update for a no verilog file
+    if get_file_path_postfix(path) not in G['SupportVerilogPostfix']:
+        PrintDebug('Trace: updata_file_pickle_inf: file not verilog file ! file: %s'%(path))
+        return False
+    # not updata for a non exist file
+    if not os.path.isfile(path):
+        PrintDebug('Trace: updata_file_pickle_inf: file not exit ! file: %s'%(path))
+        if path in G['FileInf']:
+            del G['FileInf'][path]
+        return False
+    # if has not load in G['FileInf'] try get through pickle
+    if path not in G['FileInf']:
+        PrintDebug('Trace: updata_file_pickle_inf: reload pkl for file: %s'%(path))
+        reload_pkl_file_code_inf(path)
+    # if load from pickle, check modify time
+    if path in G['FileInf']:
+        inf_mtime  = G['FileInf'][path]['last_modify_time']
+        if check_inf_valid(path, inf_mtime):
+            return True
+    # if mtime not match, or path no pickle inf, 
+    refresh_or_add_new_single_file_code_inf(path)
+    return True
+
+
+# this function used to reload file_inf to G['FileInf'] through pickle
+def reload_pkl_file_code_inf(path):
+    pkl_path = G['VTagsPath']+'/pickle/design__%s.pkl'%(path.replace('/','__'))
+    short_path = get_shortpath(pkl_path, create = False)
+    code_inf   = pickle_reload(short_path)
+    if code_inf != None:
+        G['FileInf'][path] = code_inf
+        # updata module_inf module_inf
+        new_file_module_inf_list  = code_inf['module_inf_list']
+        for module_inf in new_file_module_inf_list:
+            add_ModuleNameToModuleInfListDic( module_inf )
+        return True
+    return False
+
+# when file modified , refresh it 
+def refresh_or_add_new_single_file_code_inf(path):
+    PrintReport('Note: refresh file: %s !'%(path))
+    # not update for a no verilog file
+    assert(get_file_path_postfix(path) in G['SupportVerilogPostfix'])
+    # not updata for a non exist file
+    assert(os.path.isfile(path))
+    # get_single_verilog_file_code_inf return
+    # file_inf = { 'macro_inf_list'   : [] # list of macro_inf
+    #             ,'module_inf_list'  : [] # list of module_inf
+    #             ,'subcall_inf_list' : [] # list of subcall_inf
+    #             ,'last_modify_time' : os.path.getmtime(f) }
+    new_file_code_inf  = get_single_verilog_file_code_inf(path)
+    # refresh macro inf
+    new_file_macro_inf_list = new_file_code_inf['macro_inf_list']
+    if new_file_macro_inf_list:
+        for macro_inf in new_file_macro_inf_list:
+            add_MacroNameToMacroInfListDic(macro_inf)
+        macro_name_to_macro_inf_list_dic_pkl_path = G['VTagsPath']+'/pickle/macro_name_to_macro_inf_list_dic.pkl'
+        pickle_save(G['MacroNameToMacroInfListDic'], macro_name_to_macro_inf_list_dic_pkl_path)
+    # refresh module_inf
+    new_file_module_inf_list  = new_file_code_inf['module_inf_list']
+    for module_inf in new_file_module_inf_list:
+        add_ModuleNameToModuleInfListDic(module_inf)
+    # refresh file_inf
+    del new_file_code_inf['macro_inf_list']
+    G['FileInf'][path] = new_file_code_inf
+    code_inf_pkl_path  = G['VTagsPath']+'/pickle/design__%s.pkl'%(path.replace('/','__'))
+    pickle_save(new_file_code_inf, get_shortpath(code_inf_pkl_path, create = True))
+    pickle_save(G["Short2RealPathMap"], G['VTagsPath']+'/pickle/short_to_real_path_map.pkl')
+    # update call me subcall inf for all new subcall in cuurent new file
+    add_ModuleNameToCallMeSubcallInfListDic(new_file_code_inf['subcall_inf_list'])
+
+
+def get_single_verilog_file_code_inf(f):
+    # new_file_module_inf is a list of module_inf ordered by appeared line num in file
+    # module_inf = { 'module_name'            : ''
+    #               ,'module_line_range'      : ()
+    #               ,'sub_modules'            : None  # [] just inst_name and module_name pair set when first use
+    #               ,'module_name_match_pos'  : () }
+    new_file_module_inf = get_single_verilog_file_module_inf(f)
+    # new_file_define_inf is a list of macro_inf ordered by appeared line num in file
+    # macro_inf = { "macro_name"              : ''
+    #              ,"macro_name_match_pos"    : (line_num, colum_num)  # name first char pos
+    #              ,'code_line'               : `define xxx ....}
+    new_file_define_inf = get_single_verilog_file_macro_define_inf(f)
+    # need get current new all_module_names to generate, subcall patthen
+    # gen new all_module_names, just add current file add new, can not del old incase 
+    # old module may at other files, on harm for recognize more submodule
+    onload_G_ModuleNameToFilePathListDic()
+    old_all_module_name_set      = set(G['ModuleNameToFilePathListDic'])
+    new_module_names             = set( [ mi['module_name'] for mi in new_file_module_inf ] )
+    all_module_name              =  old_all_module_name_set | new_module_names
+    # get file sub call inf
+    patten = get_submodule_match_patten(all_module_name)
+    # new_file_subcall_inf return a list of subcall_inf ordered by appeared line num in file
+    # subcall_inf = { 'submodule_name'           : ''
+    #                 'instance_name'            : ''
+    #                 'subcall_line_range'       : ()
+    #                 'submodule_name_match_pos' : () }
+    new_file_subcall_inf = get_single_verilog_file_subcall_inf(f, patten, all_module_name, new_file_module_inf)
+    # merge to file_inf
+    new_file_inf = {
+         'module_inf_list'   : new_file_module_inf
+        ,'macro_inf_list'    : new_file_define_inf
+        ,'subcall_inf_list'  : new_file_subcall_inf
+        ,'last_modify_time'  : os.path.getmtime(f)
+    }
+    return new_file_inf
+
+
+# current function get all the module define in current file and 
+# return a module_inf list
+# module_inf =  {  'module_name'            : ''
+#                 ,'module_line_range'      : ()
+#                 ,'subcall_instance_list'  : None  # [] subcall_inf pair
+#                 ,'module_name_match_pos'  : () }
+#                 ---------------------------------
+#                 ,'file_path'              : ''    # added when put to ModuleNameToModuleInfListDic
+def get_single_verilog_file_module_inf(f):
+    # get all the module and endmodule line at current file path
+    all_module_start_end_lines  = os.popen('egrep -n -h \'^\s*(module|endmodule)\>\' %s'%(f)).readlines()
+    cur_file_all_module_inf     = []      # module inf in current file
+    has_module_not_end          = False   # has module start and not endmodule yet
+    i = 0
+    while i < len(all_module_start_end_lines):
+        cur_start_end_line      = all_module_start_end_lines[i]
+        cur_start_end_line_num  = int(cur_start_end_line.split(':')[0]) - 1
+        cur_start_end_line_code = ':'.join( cur_start_end_line.split(':')[1:] )
+        match_module_start      = re.match('\s*module\s+(?P(|`)\w+)', cur_start_end_line_code) # some module use macro as name so (|`)
+        if match_module_start:
+            module_name           = match_module_start.group('name')
+            module_start_line_num = cur_start_end_line_num
+            module_name_match_pos = ( module_start_line_num, cur_start_end_line_code.find(module_name) )
+            # if pre module not end, see if it's used `ifdef define different module io
+            # if yes skip the last module define, use the pre one
+            # if not set new module start pre line as pre module end line
+            if has_module_not_end:
+                module_name_not_ended = cur_file_all_module_inf[-1]['module_name']
+                # if pre no ended module name the same as cur module name, means use macro define module twice
+                # pass current module define
+                if module_name_not_ended == module_name:
+                    i += 1
+                    continue
+                # else set new module start pre line as pre module end line
+                PrintDebug('Error: module:"%s" in file:"%s", no "endmodule" !'%(cur_file_all_module_inf[-1]['module_name'],f) )
+                cur_file_all_module_inf[-1]['module_line_range'][1] = module_start_line_num - 1
+                # get end line number, translate to tuple
+                cur_file_all_module_inf[-1]['module_line_range']    = tuple(cur_file_all_module_inf[-1]['module_line_range'])
+            # add cur line match module to all module inf list
+            cur_file_all_module_inf.append(
+                {  'module_name'            : module_name
+                  ,'module_line_range'      : [module_start_line_num, -1]
+                  ,'subcall_instance_list'  : None  # [] just inst_name and module_name pair, set when first use
+                  ,'module_name_match_pos'  : module_name_match_pos  
+                  ,'file_path'              : f
+                  ,'last_modify_time'       : os.path.getmtime(f) } )
+            has_module_not_end = True
+            i += 1
+            continue
+        match_module_end  = re.match('\s*endmodule(\W|$)', cur_start_end_line_code)
+        if match_module_end:
+            if not has_module_not_end:
+                PrintDebug( 'Error: line: %s "endmodule" has no correlation module define ! file: %s '%(match_module_end,f) )
+                i += 1
+                continue
+            module_end_line_num = cur_start_end_line_num
+            cur_file_all_module_inf[-1]['module_line_range'][1] = module_end_line_num
+            # get end line number, translate to tuple
+            cur_file_all_module_inf[-1]['module_line_range']    = tuple(cur_file_all_module_inf[-1]['module_line_range'])
+            has_module_not_end  = False
+            i += 1
+            continue
+        i += 1
+    # if module has no endmodule until file end, treat -1 as module end
+    if has_module_not_end:
+        PrintDebug( 'Error: module:"%s" in file:"%s", no "endmodule" !'%(cur_file_all_module_inf[-1]['module_name'],f) )
+        # get end line number, translate to tuple
+        cur_file_all_module_inf[-1]['module_line_range'] = tuple(cur_file_all_module_inf[-1]['module_line_range'])
+    return cur_file_all_module_inf
+
+
+# this function used get all the macro definitions
+# return is a list of macro_inf
+# macro_inf = {
+#     "macro_name"            : ''
+#    ,"macro_name_match_pos"  : (line_num, colum_num)  # name first char pos
+#    ,'code_line'             : `define xxx .... }
+#    ---------------------------------
+#    ,"file_path"             : '' # added when in vtags run
+def get_single_verilog_file_macro_define_inf(f):
+    global_define_inf   = []
+    global_define_lines = os.popen('egrep -n -h \'^\s*`define\W\' %s'%(f)).readlines()
+    for l in global_define_lines:
+        split0     = l.split(':')
+        line_num   = int(split0[0]) - 1
+        code_line  = ':'.join(split0[1:])
+        match_name = re.match('\s*`define\s*(?P\w+)',code_line)
+        name       = ''
+        colum_num  = -1
+        if match_name:
+            name      = match_name.group('name')
+            colum_num = code_line.find(name)
+            global_define_inf.append(
+                { 'macro_name'            : name
+                 ,'macro_name_match_pos'  : (line_num, colum_num)
+                 ,'code_line'             : code_line
+                 ,'file_path'             : f
+                 ,'last_modify_time'      : os.path.getmtime(f) } )
+    return global_define_inf
+
+# this function used to generate match patten used for
+# grep quict search , hear can speed up, will do it 
+# latter
+def get_submodule_match_patten(all_module_name):
+    patten_char_set_list = []
+    len_2_modules = {}
+    # seplate by module_name langth
+    for m_n in all_module_name:
+        l = len(m_n)
+        len_2_modules.setdefault(l,[])
+        len_2_modules[l].append(m_n)
+    l_pattens = []
+    # for different length get seprate match pattens
+    for l in len_2_modules:
+        l_m = len_2_modules[l]
+        l_patten = '(['+ (']['.join(map(''.join, map(set,zip(*l_m))))) + '])'
+        l_pattens.append(l_patten)
+    patten = '(' + '|'.join(l_pattens) + ')'
+    return patten
+
+
+# this function used to get all the subcall_inf in current files
+# return is a list of subcall_inf 
+# subcall_inf = { 'submodule_name'           : ''
+#                 'instance_name'            : ''
+#                 'subcall_line_range'       : ()
+#                 'submodule_name_match_pos' : () }
+def get_single_verilog_file_subcall_inf(f, patten, all_module_names, module_inf_list):
+    # first get all match patten lines
+    egrep_match_lines  = os.popen('egrep -n -h \'^\s*(%s)\>\' %s'%(patten,f)).readlines()
+    # start get sub call inf
+    # if no match return empty list
+    if not egrep_match_lines:
+        return []
+    file_sub_call_infs = []
+    # c0/1_cnt is counter used to monitor subcall case used to make speedup decision
+    c0_cnt = 0   
+    c1_cnt = 0
+    # get current file lines
+    f_lines = open(f,'r').readlines()
+    already_pass_line_num = 0
+    for egrep_l in egrep_match_lines:
+        egrep_l = egrep_l.strip('\n')
+        # get the first word, it maybe the subcall module name, add (|`) because some module name
+        # use macro
+        match_name = re.match('^(?P\d+):(?P\s*)(?P(`)?\w+)\s*(?P.*)', egrep_l)
+        assert(match_name),'%s should lead by a valid words !'%(egrep_l)
+        maybe_submodule_name = match_name.group('maybe_submodule_name')
+        # if cur name not a right match just go next
+        if maybe_submodule_name not in all_module_names:
+            continue
+        # if is subcall get submodule inf
+        cur_submodule_name            = maybe_submodule_name
+        # get the submodule_name_match_pos
+        cur_submodule_match_line_num  = int( match_name.group('line_num') ) - 1 # minus 1 because egrep line num start from 1
+        if cur_submodule_match_line_num < already_pass_line_num:
+            PrintDebug('Trace: get_single_verilog_file_subcall_inf : match passed before ! line:%d, file:%s'%(cur_submodule_match_line_num,f))
+            continue
+        cur_submodule_pre_space_len   = len( match_name.group('pre_space') ) 
+        cur_submodule_match_colum_num = cur_submodule_pre_space_len
+        submodule_name_match_pos = (cur_submodule_match_line_num,  cur_submodule_match_colum_num)
+        #-----------------------
+        # get the instance name
+        #-----------------------
+        cur_submodule_instance_name = ''
+        ended_semicolon_line_num    = -1
+        # cur_subcall_post_part       = re.sub('(^\s*)|(//.*)', '', match_name.group('post_part'))
+        cur_subcall_post_part       = re.sub('//.*', '', match_name.group('post_part'))
+        cur_subcall_post_part       = cur_subcall_post_part.strip()
+        if cur_subcall_post_part.find(';') != -1: # incase it's one line instance
+            ended_semicolon_line_num = cur_submodule_match_line_num
+            cur_subcall_post_part = re.sub(';.*','',cur_subcall_post_part)
+        # if cur_subcall_post_part is empty add new line until first char not empty
+        next_line_index = cur_submodule_match_line_num + 1
+        max_line_index  = len(f_lines)
+        # for case "module_name #n ... instance_name(...)" for verif code
+        if cur_subcall_post_part[0:1] == '#':
+            cur_subcall_post_part = (re.sub('#(\d+|`\w+)','',cur_subcall_post_part)).strip()
+        while (cur_subcall_post_part == '') and (next_line_index < max_line_index) and (ended_semicolon_line_num == -1):
+            # next_code_line = re.sub('(^\s*)|(^\s*`.*)|(//.*)|(\s*$)','',f_lines[next_line_index].strip('\n'))
+            # next_code_line = re.sub('(^\s*`.*)|(//.*)','',f_lines[next_line_index].strip('\n'))
+            next_code_line = get_valid_code(f_lines[next_line_index].strip('\n'))
+            cur_line_index = next_line_index
+            next_line_index += 1
+            # see if cur subcall ended by cur line
+            if next_code_line.find(';') != -1:
+                ended_semicolon_line_num = cur_line_index
+                next_code_line = re.sub(';.*','',next_code_line)
+            # add new line before ";" to cur_subcall_post_part if not ''
+            cur_subcall_post_part = next_code_line.strip()
+            # for case "module_name #n ... instance_name(...)" for verif code
+            if cur_subcall_post_part[0:1] == '#':
+                cur_subcall_post_part = (re.sub('#(\d+|`\w+)','',cur_subcall_post_part)).strip()
+        # instance_name appear case:
+        # case 1: module_name        instance_name ( ... ) ;
+        # case 2: module_name #(...) instance_name (...);
+        # if cur_subcall_post_part still '', means code has error, not find instance_name untill ";" or file end 
+        if cur_subcall_post_part == '':
+            PrintDebug('Error: subcall egrep_l : %s in file : %s can not recognized !'%(egrep_l, f))
+            continue
+        # if is case 2 cur_subcall_post_part[0] must be '#'
+        io_connect_init_left_bracket_right_part = None
+        # for case 2, first go to the end ")" pos
+        # ... #(....)
+        #           ^   //get this ")" pos: out_level1_right_bracket_y_list[0]
+        if cur_subcall_post_part[0] == '#': # mast be case 2
+            # ... #(....)
+            #           ^   //get this ")" pos: out_level1_right_bracket_y_list[0]
+            current_bracket_depth          = 0
+            current_code_line              = cur_subcall_post_part
+            bracket_pair_index = get_bracket_pair_index(current_code_line, current_bracket_depth)
+            current_bracket_depth            = bracket_pair_index['end_bracket_depth']
+            out_level1_right_bracket_y_list  = bracket_pair_index['out_level1_right_bracket_y_list']
+            while (not out_level1_right_bracket_y_list) and (ended_semicolon_line_num == -1) and (next_line_index < max_line_index):
+                # current_code_line = re.sub('(^\s*`.*)|(//.*)','',f_lines[next_line_index].strip('\n'))
+                current_code_line = get_valid_code(f_lines[next_line_index].strip('\n'))
+                cur_line_index = next_line_index
+                next_line_index += 1
+                if current_code_line.find(';') != -1:
+                    ended_semicolon_line_num = cur_line_index
+                    current_code_line = re.sub(';.*','',current_code_line)
+                bracket_pair_index = get_bracket_pair_index(current_code_line, current_bracket_depth)
+                current_bracket_depth            = bracket_pair_index['end_bracket_depth']
+                out_level1_right_bracket_y_list  = bracket_pair_index['out_level1_right_bracket_y_list']
+            if not out_level1_right_bracket_y_list:
+                PrintDebug('Error: match_case2 subcall egrep_l : %s in file : %s can not recognized !'%(egrep_l, f))
+                continue
+            assert(current_code_line[out_level1_right_bracket_y_list[0]] == ')')
+            cur_subcall_post_part = (current_code_line[out_level1_right_bracket_y_list[0]+1:]).strip()
+        # second, match the instance name for case 0 or 1
+        # case 1: module_name        instance_name ( ... ) ;
+        # case 2: module_name #(...) instance_name (...);
+        #                            ^                      //  cur_subcall_post_part from here
+        # patten0 = '(?P\w+)\s*(\[[^\[\]]*\])?'
+        patten0 = '(?P\w+(\s*\[[^\[\]]*\])?)'
+        patten1 = '(?P\(.*)'
+        full_match  = False
+        match_case1 = re.match('(\s*(?P%s)\s*(?P%s)?)|(\s*$)'%(patten0,patten1), cur_subcall_post_part)
+        if not match_case1:# must has some error,report a error
+            PrintDebug('Error: match_case0or1 0 subcall egrep_l : %s in file : %s can not recognized !'%(egrep_l, f))
+            continue
+        full_match = match_case1.group('p1')
+        while (not full_match) and (ended_semicolon_line_num == -1) and (next_line_index < max_line_index):
+            # next_code_line = re.sub('(^\s*`.*)|(//.*)','',f_lines[next_line_index].strip('\n'))
+            next_code_line = get_valid_code(f_lines[next_line_index].strip('\n'))
+            cur_line_index = next_line_index
+            next_line_index += 1
+            if next_code_line.find(';') != -1:
+                ended_semicolon_line_num = cur_line_index
+                next_code_line = re.sub(';.*','',next_code_line)
+            cur_subcall_post_part = cur_subcall_post_part + ' ' + next_code_line
+            match_case1 = re.match('(\s*(?P%s)\s*(?P%s)?)|(\s*$)'%(patten0,patten1), cur_subcall_post_part)
+            if not match_case1:
+                PrintDebug('Error: match_case0or1 1 subcall egrep_l : %s in file : %s can not recognized !'%(egrep_l, f)+'***'+cur_subcall_post_part)
+                break
+            full_match = match_case1.group('p1')
+        if full_match:
+            cur_submodule_instance_name = match_case1.group('instance_name')
+            io_connect_init_left_bracket_right_part = match_case1.group('io_connect_init_left_bracket_right_part')
+        else:
+            PrintDebug('Error: match_case1 2 subcall egrep_l : %s in file : %s can not recognized !'%(egrep_l, f))
+            continue
+        # finial test if instance name match
+        if not cur_submodule_instance_name:
+            PrintDebug('Error: instance_name not find subcall egrep_l : %s in file : %s can not recognized !'%(egrep_l, f))
+            continue
+        assert(io_connect_init_left_bracket_right_part[0] == '(')
+        #-----------------------------
+        # get the subcall_end_line_num
+        #-----------------------------
+        # already end
+        if ended_semicolon_line_num != -1:
+            assert(cur_submodule_name)
+            assert(cur_submodule_instance_name)
+            cur_subcall_end_line_num     = ended_semicolon_line_num
+            assert(cur_submodule_match_line_num <= cur_subcall_end_line_num)
+            file_sub_call_infs.append( { 'submodule_name'           : cur_submodule_name
+                                        ,'cur_module_name'          : '' # module current submodule belong to
+                                        ,'instance_name'            : cur_submodule_instance_name
+                                        ,'subcall_line_range'       : (cur_submodule_match_line_num, cur_subcall_end_line_num)
+                                        ,'submodule_name_match_pos' : submodule_name_match_pos 
+                                        ,'file_path'                : f
+                                        ,'inaccuracy'               : False
+                                        ,'last_modify_time'         : os.path.getmtime(f) } )
+            already_pass_line_num = cur_subcall_end_line_num
+            continue
+        # cur not end
+        # ended_semicolon_line_num     = cur_subcall_end_line_num
+        current_bracket_depth        = 0
+        start_a_new_subcall_instance = True
+        new_subcall_instance_pending = False
+        while (start_a_new_subcall_instance or new_subcall_instance_pending) and next_line_index < max_line_index:
+            cur_line_index = next_line_index - 1
+            next_code_line  = ''
+            if start_a_new_subcall_instance:
+                assert(io_connect_init_left_bracket_right_part[0] == '(')
+                next_code_line = io_connect_init_left_bracket_right_part
+                start_a_new_subcall_instance = False
+                new_subcall_instance_pending = True
+                io_connect_init_left_bracket_right_part = ''
+            else:
+                next_code_line = get_valid_code(f_lines[next_line_index].strip('\n'))
+                if next_code_line.find(';') != -1:
+                    ended_semicolon_line_num = next_line_index
+                    next_code_line = re.sub(';.*','',next_code_line)
+                cur_line_index = next_line_index
+                next_line_index += 1
+            bracket_pair_index = get_bracket_pair_index(next_code_line, current_bracket_depth)
+            current_bracket_depth            = bracket_pair_index['end_bracket_depth']
+            out_level1_right_bracket_y_list  = bracket_pair_index['out_level1_right_bracket_y_list']
+            if not out_level1_right_bracket_y_list and ended_semicolon_line_num == -1:
+                continue
+            # if has ";" finish current
+            if ended_semicolon_line_num != -1:
+                assert(new_subcall_instance_pending)
+                inaccuracy = False
+                if not out_level1_right_bracket_y_list: # indistinct match can not trace back
+                    # may happend for used '`ifdef ... `else ...'
+                    PrintDebug('RTL Error: miss ")" before ";", at line:%d, file:%s '%(cur_line_index+1, f) )
+                    inaccuracy = True
+                assert(cur_submodule_name)
+                assert(cur_submodule_instance_name)
+                cur_subcall_end_line_num     = ended_semicolon_line_num
+                assert(cur_submodule_match_line_num <= cur_subcall_end_line_num)
+                file_sub_call_infs.append( { 'submodule_name'           : cur_submodule_name
+                                            ,'cur_module_name'          : '' # module current submodule belong to
+                                            ,'instance_name'            : cur_submodule_instance_name
+                                            ,'subcall_line_range'       : (cur_submodule_match_line_num, cur_subcall_end_line_num)
+                                            ,'submodule_name_match_pos' : submodule_name_match_pos 
+                                            ,'file_path'                : f
+                                            ,'inaccuracy'               : inaccuracy
+                                            ,'last_modify_time'         : os.path.getmtime(f) } )
+                already_pass_line_num = cur_subcall_end_line_num
+                new_subcall_instance_pending = False
+                break
+            # must be out_level1_right_bracket_y_list and not get ";"
+            # 1 first end current subcall
+            assert(out_level1_right_bracket_y_list)
+            assert(new_subcall_instance_pending)
+            assert(cur_submodule_name)
+            assert(cur_submodule_instance_name)
+            cur_subcall_end_line_num = cur_line_index
+            assert(cur_submodule_match_line_num <= cur_subcall_end_line_num)
+            file_sub_call_infs.append( { 'submodule_name'           : cur_submodule_name
+                                        ,'cur_module_name'          : '' # module current submodule belong to
+                                        ,'instance_name'            : cur_submodule_instance_name
+                                        ,'subcall_line_range'       : (cur_submodule_match_line_num, cur_subcall_end_line_num)
+                                        ,'submodule_name_match_pos' : submodule_name_match_pos 
+                                        ,'file_path'                : f
+                                        ,'inaccuracy'               : False
+                                        ,'last_modify_time'         : os.path.getmtime(f) } )
+            already_pass_line_num = cur_subcall_end_line_num
+            new_subcall_instance_pending = False
+            # 2 second find next instance start
+            # deal the singal module multi instance case
+            # module name ... instance0(...),
+            #                 instance1(...);
+            # if find the next instance name, continue. else break
+            if len(out_level1_right_bracket_y_list) > 1:
+                PrintDebug('Error: current not support, multi instance in one line ! line:%d, file:%s'%(cur_line_index, f))
+                break
+            assert(len(out_level1_right_bracket_y_list) == 1)
+            code_left_part   = next_code_line[out_level1_right_bracket_y_list[0]+1:]
+            assert(not (start_a_new_subcall_instance or new_subcall_instance_pending) )
+            while (next_line_index < max_line_index):
+                # next_code_line = re.sub('(^\s*`.*)|(//.*)','',f_lines[next_line_index].strip('\n'))
+                next_code_line = get_valid_code(f_lines[next_line_index].strip('\n'))
+                cur_line_index = next_line_index
+                next_line_index += 1
+                code_left_part = (code_left_part + ' ' + next_code_line).strip()
+                if code_left_part:
+                    if code_left_part[0] != ',':
+                        break
+                    # match: , instance_name (
+                    patten0 = ','
+                    # patten1 = '(?P\w+)\s*(\[[^\[\]]*\])?'
+                    patten1 = '(?P\w+(\s*\[[^\[\]]*\])?)'
+                    patten2 = '(?P\(.*)'
+                    match_next = re.match('(?P%s)\s*(?P%s)?\s*(?P%s)?\s*$'%(patten0,patten1,patten2), code_left_part)
+                    if not match_next:
+                        break
+                    if match_next.group('p2'): # full match then get new instance start
+                        cur_submodule_instance_name = match_next.group('instance_name')
+                        io_connect_init_left_bracket_right_part = match_next.group('io_connect_init_left_bracket_right_part')
+                        cur_submodule_match_line_num = cur_line_index
+                        # for signal module multi instance, match pos set to instance name(except for first one)
+                        instance_y = next_code_line.find(cur_submodule_instance_name)
+                        instance_x = cur_line_index
+                        if instance_y == -1:
+                            pre_line_code = get_valid_code(f_lines[cur_line_index-1].strip('\n'))
+                            instance_y = pre_line_code.find(cur_submodule_instance_name)
+                            instance_x = cur_line_index - 1
+                        assert(instance_y != -1)
+                        submodule_name_match_pos = (instance_x, instance_y)
+                        start_a_new_subcall_instance = True
+                        break
+        # if has pending unfinished
+        if new_subcall_instance_pending:
+            PrintDebug('Error: subcall no end identify ! line:%d, file:%s'%(cur_submodule_match_line_num, f))
+    # for each subcall inf in current file add cur module inf 
+    add_cur_file_cur_module_name_to_subcall_inf(module_inf_list, file_sub_call_infs)
+    return file_sub_call_infs
+
+
+def onload_G_MacroNameToMacroInfListDic():
+    # if MacroNameToMacroInfListDic not updata get from pickle
+    if G['MacroNameToMacroInfListDic'] == None:
+        macro_name_to_macro_inf_list_dic_pkl_path = G['VTagsPath']+'/pickle/macro_name_to_macro_inf_list_dic.pkl'
+        if os.path.isfile(macro_name_to_macro_inf_list_dic_pkl_path):
+            print('vtags is uploading macro define information for the first time ...')
+            G['MacroNameToMacroInfListDic'] = pickle_reload(macro_name_to_macro_inf_list_dic_pkl_path)
+        else:
+            G['MacroNameToMacroInfListDic'] = {}
+
+def get_macro_inf_list( macro_name ):
+    # if MacroNameToMacroInfListDic not updata get from pickle
+    onload_G_MacroNameToMacroInfListDic()
+    # if get macro inf list, refresh stale inf
+    if macro_name in G['MacroNameToMacroInfListDic']:
+        macro_inf_list = G['MacroNameToMacroInfListDic'][macro_name]
+        # del stale
+        stale_file_path_set = set()
+        i = 0
+        while i < len(macro_inf_list):
+            t_macro_inf = macro_inf_list[i]
+            if not check_inf_valid(t_macro_inf['file_path'], t_macro_inf['last_modify_time']):
+                stale_file_path_set.add(t_macro_inf['file_path'])
+                del macro_inf_list[i]
+                continue
+            i += 1
+        # updata stale file
+        for f in stale_file_path_set:
+            updata_file_inf(t_macro_inf['file_path'])
+        # if has valid inf return
+        if len(G['MacroNameToMacroInfListDic'][macro_name]) > 0:
+            return G['MacroNameToMacroInfListDic'][macro_name]
+    # if still not find valid macro inf, refresh vtags db if valid
+    if (macro_name not in G['MacroNameToMacroInfListDic']) and G['RefreshDBValid']:
+        refresh_vtags_db()
+    if macro_name in G['MacroNameToMacroInfListDic']:
+        return G['MacroNameToMacroInfListDic'][macro_name]
+    return []
+
+
+def onload_G_ModuleNameToFilePathListDic():
+    # if ModuleNameToFilePathListDic not updata get from pickle
+    if G['ModuleNameToFilePathListDic'] == None:
+        module_name_to_file_path_list_dic_pkl_path = G['VTagsPath']+'/pickle/module_name_to_file_path_list_dic.pkl'
+        if os.path.isfile(module_name_to_file_path_list_dic_pkl_path):
+            G['ModuleNameToFilePathListDic'] = pickle_reload(module_name_to_file_path_list_dic_pkl_path)
+        else:
+            G['ModuleNameToFilePathListDic'] = {}
+
+# this function used to add which module current submodule_inf beyond to
+def add_cur_file_cur_module_name_to_subcall_inf(module_inf_list, subcall_inf_list):
+    cur_line_module_i       = 0
+    cur_line_module_begin   = -1
+    cur_line_module_end     = -1
+    if module_inf_list:
+        cur_line_module_begin   = module_inf_list[cur_line_module_i]['module_line_range'][0]
+        cur_line_module_end     = module_inf_list[cur_line_module_i]['module_line_range'][1]
+    else:
+        PrintDebug("Error: file has subcall_inf but no module inf(maybe module define by macro), subcall_inf_list = %s"%(subcall_inf_list.__str__()))
+        return
+    cur_line_module_name    = module_inf_list[cur_line_module_i]['module_name']
+    for subcall_inf in subcall_inf_list:
+        submodule_name  = subcall_inf['submodule_name']
+        submodule_begin = subcall_inf['subcall_line_range'][0]
+        submodule_end   = subcall_inf['subcall_line_range'][1]
+        # first get current subcall correspond module name
+        # -----c0-------c1--------   // c0, c1 is cur module begin, end
+        # ----------------s0------   // s0, s1 is subcall begin, end. need forward cur module
+        # need s0 < c1
+        while cur_line_module_end < submodule_begin:
+            cur_line_module_i       += 1
+            assert( cur_line_module_i < len(module_inf_list) )
+            cur_line_module_begin   = module_inf_list[cur_line_module_i]['module_line_range'][0]
+            cur_line_module_end     = module_inf_list[cur_line_module_i]['module_line_range'][1]
+            cur_line_module_name    = module_inf_list[cur_line_module_i]['module_name']
+        # after while only valid case is
+        # -----c0-------c1--------   // c0, c1 is cur module begin, end
+        # -------s0---s1----------   // s0, s1 is subcall begin, end
+        if cur_line_module_begin <= submodule_begin and submodule_end <= cur_line_module_end:
+            subcall_inf['cur_module_name'] = cur_line_module_name
+            PrintDebug(subcall_inf.__str__())
+            continue
+        # other case all invalid, need report
+        # already s0 < c1
+        # -------c0-------c1------
+        # error case
+        # --s0-------s1-----------  # subcall cross module 
+        # --s0-s1-----------------  # subcall not have any correspond module  
+        # -----------s0------s1---- # subcall cross module 
+        if submodule_begin < cur_line_module_begin and submodule_end >= cur_line_module_begin:
+            PrintDebug("Error: subcall cross module, subcall_inf:%s \n module_inf:%s "%(subcall_inf.__str__(), module_inf_list[cur_line_module_i].__str__()))
+        if submodule_end < cur_line_module_begin:
+            PrintDebug("Error: subcall has no corresponding module, subcall_inf:%s "%(subcall_inf.__str__()))
+        if submodule_begin <= cur_line_module_end and submodule_end > cur_line_module_end:
+            PrintDebug("Error: subcall cross module, subcall_inf:%s \n module_inf:%s "%(subcall_inf.__str__(), module_inf_list[cur_line_module_i].__str__()))
+    return
+
+def onload_G_CallMeSubcallInf():
+    # if ModuleNameToCallMeSubcallInfListDic not updata get from pickle
+    if G['CallMeSubcallInf']['ModuleNameToCallMeSubcallInfListDic'] == None:
+        assert( G['CallMeSubcallInf']['MaskedCallMeSubmoduleSet'] == None )
+        call_me_subcall_inf_path = G['VTagsPath']+'/pickle/call_me_subcall_inf.pkl'
+        if os.path.isfile(call_me_subcall_inf_path):
+            call_me_subcall_inf = pickle_reload(call_me_subcall_inf_path)
+            G['CallMeSubcallInf']['ModuleNameToCallMeSubcallInfListDic'] = call_me_subcall_inf['ModuleNameToCallMeSubcallInfListDic']
+            G['CallMeSubcallInf']['MaskedCallMeSubmoduleSet']            = call_me_subcall_inf['MaskedCallMeSubmoduleSet']
+        else:
+            G['CallMeSubcallInf']['ModuleNameToCallMeSubcallInfListDic'] = {}
+            G['CallMeSubcallInf']['MaskedCallMeSubmoduleSet']            = set()
+
+def get_call_me_subcall_inf_list( module_name, create = False ):
+    onload_G_CallMeSubcallInf()
+    if module_name in G['CallMeSubcallInf']['ModuleNameToCallMeSubcallInfListDic']:
+        refresh_cur_call_me_subcall_inf_list( module_name )
+        return G['CallMeSubcallInf']['ModuleNameToCallMeSubcallInfListDic'][module_name]
+    elif create:
+        G['CallMeSubcallInf']['ModuleNameToCallMeSubcallInfListDic'][module_name] = []
+        return G['CallMeSubcallInf']['ModuleNameToCallMeSubcallInfListDic'][module_name]
+    return None
+
+def refresh_cur_call_me_subcall_inf_list( module_name ):    
+    # refresh call me inf when add new
+    assert( module_name in G['CallMeSubcallInf']['ModuleNameToCallMeSubcallInfListDic'] )
+    cur_call_me_subcall_inf_list = G['CallMeSubcallInf']['ModuleNameToCallMeSubcallInfListDic'][ module_name ]
+    i = 0
+    while i < len( cur_call_me_subcall_inf_list ):
+        cur_subcall_inf  = cur_call_me_subcall_inf_list[i]
+        call_module_file = cur_subcall_inf['file_path']
+        # if call module not change just continue
+        if check_inf_valid( cur_subcall_inf['file_path'], cur_subcall_inf['last_modify_time'] ):
+            i += 1
+            continue
+        # else must stale just delate, if refresh will add it when refresh
+        del cur_call_me_subcall_inf_list[i] # delate stale, if still exist must add to the end before
+    return
+
+def add_ModuleNameToCallMeSubcallInfListDic( subcall_inf_list ):
+    onload_G_CallMeSubcallInf()
+    # touched_module_set = set()
+    for new_subcall_inf in subcall_inf_list:
+        # should not add a stale subcall inf
+        assert( check_inf_valid(new_subcall_inf['file_path'], new_subcall_inf['last_modify_time']) )
+        # if module in masked then not add
+        new_submodule_name = new_subcall_inf['submodule_name']
+        if new_submodule_name in G['CallMeSubcallInf']['MaskedCallMeSubmoduleSet']:
+            continue
+        # touched_module_set.add( new_submodule_name )
+        # because get_call_me_subcall_inf_list has refreshed subcall inf
+        # so it must all valid
+        old_call_me_subcall_inf_list = get_call_me_subcall_inf_list( new_submodule_name, True )
+        # first all new
+        # if new add subcall inf(same file , same instance) already exist, replace it
+        i = 0
+        while i < len( old_call_me_subcall_inf_list ):
+            o_subcall_inf = old_call_me_subcall_inf_list[i]
+            assert( o_subcall_inf['submodule_name'] == new_subcall_inf['submodule_name'] )
+            if (o_subcall_inf['file_path'], o_subcall_inf['cur_module_name'], o_subcall_inf['instance_name']) == \
+               (new_subcall_inf['file_path'], new_subcall_inf['cur_module_name'], new_subcall_inf['instance_name']):
+                del old_call_me_subcall_inf_list[i]
+                continue
+            i += 1
+        old_call_me_subcall_inf_list.append( new_subcall_inf )
+    # last pickle save new CallMeSubcallInf
+    pickle_save(G['CallMeSubcallInf'], G['VTagsPath']+'/pickle/call_me_subcall_inf.pkl')
+    return True
+
+# need special care to loop case
+def recursion_get_module_trace( module_name, cur_trace, full_traces ):
+    call_me_subcall_inf_list = get_call_me_subcall_inf_list( module_name )
+    if not call_me_subcall_inf_list:
+        if cur_trace:
+            full_traces.append( cur_trace )
+        return
+    for subcall_inf in call_me_subcall_inf_list:
+        assert( subcall_inf['submodule_name'] == module_name),'%s,%s'%(subcall_inf.__str__(), module_name)
+        new_trace = [ t for t in cur_trace]
+        # need special care to loop case
+        loop_back = False
+        for n_subcall_inf in new_trace:
+            if n_subcall_inf['submodule_name'] == subcall_inf['submodule_name']:
+                loop_back = True
+                break
+        # normal add
+        new_trace.append( subcall_inf )
+        if loop_back:
+            return
+        # if subcall_inf has cur_module_name recursion get upper
+        # if 'cur_module_name' in subcall_inf:
+        if subcall_inf['cur_module_name']:
+            recursion_get_module_trace( subcall_inf['cur_module_name'], new_trace, full_traces )
+        else: # means current submodule has no module inf just pass, this maybe happen when module name define by macro
+            PrintReport("Warning: can not find module, maybe module name define by macro. in file: %s"%(subcall_inf['file_path']))
+    return
diff --git a/Lib/GLB.py b/Lib/GLB.py
new file mode 100755
index 0000000..3bdca45
--- /dev/null
+++ b/Lib/GLB.py
@@ -0,0 +1,446 @@
+"""
+http://www.vim.org/scripts/script.php?script_id=5494
+"""
+#===============================================================================
+# BSD 2-Clause License
+
+# Copyright (c) 2016, CaoJun
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * 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.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#===============================================================================
+
+import os
+import sys
+import re
+import pickle
+from Lib.ExceptionLib import *
+
+#-------------------------------------------------------------------------------
+# get next empty frame, report,log report index, first try del Frame, Report
+#-------------------------------------------------------------------------------
+# this function used to del not used old run.log in vtags.db
+def del_old_logs(vtags_db_folder_path):
+    ls_a_f = [ f.strip('\n') for f in os.popen('ls -a ' + vtags_db_folder_path).readlines() ]
+    used_log_index = set()
+    for f in ls_a_f:
+        match_swp = re.match('\.(Frame|Report|run)(?P\d+)(\.ZL|\.log)(\.v)?\.swp',f)
+        if match_swp:
+            used_log_index.add(int(match_swp.group('idx')))
+    ls_f   = [ f.strip('\n') for f in os.popen('ls ' + vtags_db_folder_path).readlines() ]
+    for f in ls_f:
+        match_idx = re.match('(Frame|Report|run)(?P\d+)(\.ZL|\.log)(\.v)?', f)
+        if not match_idx:
+            continue
+        cur_index = int(match_idx.group('idx'))
+        if cur_index in used_log_index:
+            continue
+        os.system('rm %s/%s'%(vtags_db_folder_path,f) )
+    return
+
+#-------------------------------------------------------------------------------
+# this function used to get the path postfix
+#-------------------------------------------------------------------------------
+def get_file_path_postfix(file_path):
+    split_by_dot = file_path.split('.')
+    if len(split_by_dot) < 2: # which means file_path has no postfix
+        return ''
+    post_fix = split_by_dot[-1]          # postfix care case
+    return post_fix
+
+# this function used to save env snapshort
+def save_env_snapshort():
+    snapshort = {}
+    # 0: save cur dir path, used to quality opne snapshort
+    snapshort['snapshort_dir_path'] = os.getcwd()
+    # 1: save Frame
+    snapshort['frame_file_lines'] = []
+    if os.path.isfile(G['Frame_Inf']['Frame_Path']):
+        snapshort['frame_file_lines'] = open(G['Frame_Inf']['Frame_Path'],'r').readlines()
+    # 2: save Report
+    snapshort['report_file_lines'] = []
+    if os.path.isfile(G['Report_Inf']['Report_Path']):
+        snapshort['report_file_lines'] = open(G['Report_Inf']['Report_Path'],'r').readlines()
+    # 3: save G
+    snapshort['G'] = {}
+    snapshort['G']['OpTraceInf']                   = {}
+    snapshort['G']['OpTraceInf']['TracePoints']    = G['OpTraceInf']['TracePoints'] 
+    snapshort['G']['OpTraceInf']['Nonius'     ]    = G['OpTraceInf']['Nonius'     ]
+    snapshort['G']['WorkWin_Inf']                  = {}
+    snapshort['G']['WorkWin_Inf']['OpenWinTrace']  = G['WorkWin_Inf']['OpenWinTrace']
+    snapshort['G']['VimBufferLineFileLink' ]       = G["VimBufferLineFileLink" ]
+    snapshort['G']["TraceInf"              ]       = G['TraceInf']
+    snapshort['G']['CheckPointInf']                = {}
+    snapshort['G']['CheckPointInf']['CheckPoints'] = G['CheckPointInf']['CheckPoints']
+    snapshort['G']['TopoInf']                      = {}
+    snapshort['G']['TopoInf']['CurModule']         = G['TopoInf']['CurModule']
+    snapshort['G']['ModuleLastCallInf']            = G['ModuleLastCallInf']
+    snapshort['G']['Frame_Inf']                    = {}
+    snapshort['G']['Frame_Inf']['Frame_Path']      = G['Frame_Inf']['Frame_Path']
+    snapshort['G']['Report_Inf']                   = {}
+    snapshort['G']['Report_Inf']['Report_Path']    = G['Report_Inf']['Report_Path']
+    # 4: save act windows inf
+    act_win_inf = []
+    for w in vim.windows:
+        c_file_path = w.buffer.name
+        if c_file_path == vim.current.buffer.name:
+            continue
+        c_cursor    = w.cursor
+        c_size      = (w.width, w.height)
+        act_win_inf.append({'path': c_file_path, 'cursor': c_cursor, 'size': c_size })
+    # last is current window
+    cur_file_path  = vim.current.buffer.name
+    cur_cursor     = vim.current.window.cursor   
+    cur_size       = (vim.current.window.width, vim.current.window.height)
+    act_win_inf.append({'path': cur_file_path, 'cursor': cur_cursor, 'size': cur_size })
+    snapshort['act_win_inf'] = act_win_inf
+    pkl_output = open(G['VTagsPath'] + '/pickle/env_snapshort.pkl','wb')
+    pickle.dump(snapshort, pkl_output)
+    pkl_output.close()
+    return True
+
+def reload_env_snapshort(snapshort):
+    # 1: reload G
+    snapshort_G = snapshort['G']
+    G['OpTraceInf']['TracePoints']    = snapshort_G['OpTraceInf']['TracePoints'] 
+    G['OpTraceInf']['Nonius'     ]    = snapshort_G['OpTraceInf']['Nonius'     ]
+    G['WorkWin_Inf']['OpenWinTrace']  = snapshort_G['WorkWin_Inf']['OpenWinTrace']
+    G['VimBufferLineFileLink' ]       = snapshort_G["VimBufferLineFileLink" ]
+    G["TraceInf"              ]       = snapshort_G['TraceInf']
+    G['CheckPointInf']['CheckPoints'] = snapshort_G['CheckPointInf']['CheckPoints']
+    G['TopoInf']['CurModule']         = snapshort_G['TopoInf']['CurModule']
+    G['ModuleLastCallInf']            = snapshort_G['ModuleLastCallInf']
+    G['Frame_Inf']['Frame_Path']      = snapshort_G['Frame_Inf']['Frame_Path']
+    G['Report_Inf']['Report_Path']    = snapshort_G['Report_Inf']['Report_Path']
+    # 2: reload Frame
+    os.system('touch ' + G['Frame_Inf']['Frame_Path'])
+    assert(os.path.isfile(G['Frame_Inf']['Frame_Path']))
+    frame_fp = open(G['Frame_Inf']['Frame_Path'],'w')
+    for l in snapshort['frame_file_lines']:
+        frame_fp.write(l)
+    frame_fp.close()
+    # 3: reload Report
+    os.system('touch ' + G['Report_Inf']['Report_Path'])
+    assert(os.path.isfile(G['Report_Inf']['Report_Path']))
+    report_fp = open(G['Report_Inf']['Report_Path'],'w')
+    for l in snapshort['report_file_lines']:
+        report_fp.write(l)
+    report_fp.close()
+    # 4: reload act windows inf need re open at API.py
+    G['EnvSnapshortWinsInf'] = snapshort['act_win_inf']
+    return
+
+def init_G_from_vtagsDB( vtags_db_folder_path = '', RaiseExcept = False ):
+    #-------------------------------------------------------------------------------
+    # get vtags.db
+    # find most resent vtags path from current folder to upper
+    #-------------------------------------------------------------------------------
+    if not vtags_db_folder_path:
+        cur_path = os.getcwd()
+        while cur_path and cur_path[0] == '/':
+            if os.path.isdir(cur_path + '/vtags.db'):
+                vtags_db_folder_path = cur_path + '/vtags.db'
+                break
+            cur_path = re.sub('/[^/]*$','',cur_path)
+    # if not found a valid vtags db and need raise except to speed up 
+    # none vtags vim open
+    if RaiseExcept and (not os.path.isdir(vtags_db_folder_path)):
+        raise VtagsDBNotFoundExcept
+    #-------------------------------------------------------------------------------
+    # get config
+    # get finial config, if has vtag.db local config use local, if not use install
+    # path glable config 
+    #-------------------------------------------------------------------------------
+    # first import vim glable config in install path
+    sys.path.append('../')
+    import vim_glb_config as glb_config
+    config        = glb_config
+    if vtags_db_folder_path:
+        vtags_db_folder_path = os.path.realpath(vtags_db_folder_path) # incase for link
+        sys.path.insert(0,vtags_db_folder_path)
+        # if already import vim_local_config del it
+        try:
+            del vim_local_config
+        except:
+            pass
+        # re import vim_local_config
+        try:
+            import vim_local_config
+            config = vim_local_config
+        except:
+            pass
+    #-------------------------------------------------------------------------------
+    # init get the supported design file postfix
+    # real supported postfix is config.support_verilog_postfix add postfix geted by 
+    # the input file list
+    #-------------------------------------------------------------------------------
+    file_list_postfix = set()
+    try:
+        pkl_input           = open(vtags_db_folder_path + '/file_list_postfix.pkl','rb')
+        file_list_postfix   = pickle.load(pkl_input)
+        pkl_input.close()
+    except:
+        pass
+    support_design_postfix_set = file_list_postfix | set(config.support_verilog_postfix)
+    # find the minimum number current not used as the next log postfix
+    valid_log_index = 0
+    if vtags_db_folder_path:
+        del_old_logs(vtags_db_folder_path)
+        all_file_names_in_vtags_db = " ".join( os.listdir(vtags_db_folder_path) )
+        while re.search( "(^|\s)(\.)?(((Frame|Report)%d\.ZL)|(run%d\.log))(\W|$)"%(valid_log_index, valid_log_index), all_file_names_in_vtags_db):
+            valid_log_index += 1
+    # stale now # file link used as a link to space option:
+    # stale now # ----------------------------------------------------------------------------------------------------------------------
+    # stale now # | type        | go_path       | go_pos          | go_word     | fold_status | fold_level | last_modify_time | discription
+    # stale now # |-------------|---------------|-----------------|-------------|-------------|------------|------------------|--------------
+    # stale now # | topo        | module path   | module name pos | module name | on/off      | n          | n                | Frame : link to topo line module
+    # stale now # | base_module | module path   | module name pos | module name | on/off      | n          | n                | Frame : link to base module
+    # stale now # | check_point | cp added path | cursor pos      | cursor word | on/off      | n          | n                | Frame : link to check point location
+    # stale now # | trace result| result path   | result match pos| trace signal|             |            |                  | Report: link to trace source dest
+    # stale now # ---------------------------------------------------------------------------------------------------------------------------
+    # stale now # all vim buffer line file link, a path to file link list dic
+    VimBufferLineFileLink = {}
+    
+    
+    Frame_Inf = {
+         "Frame_Win_x"        : config.frame_window_width      # frame window width
+        ,"Frame_Path"         : ''
+        ,"FoldLevelSpace"     : config.frame_fold_level_space
+    }
+    Frame_Inf['Frame_Path'] = vtags_db_folder_path + '/' + "Frame" + str(valid_log_index) + '.ZL'
+    
+    
+    Report_Inf = {
+         "Report_Win_y"       : config.report_window_height        # report window height
+        ,"Report_Path"        : ''
+    }
+    Report_Inf['Report_Path'] = vtags_db_folder_path + '/' + "Report" + str(valid_log_index) + '.ZL.v'
+    
+    
+    WorkWin_Inf ={
+         "MaxNum"       : config.max_open_work_window_number
+        ,"OpenWinTrace" : []
+    }
+    
+    TraceInf = {
+         'LastTraceSource' : {'Maybe':[], 'Sure':[], 'ShowIndex': 0, 'SignalName':'', 'ValidLineRange':[-1,-1], 'Path':'' } # Maybe[{'show':'', 'file_link':{ 'key':'','pos':(l,c),'path':'' } }] 
+        ,'LastTraceDest'   : {'Maybe':[], 'Sure':[], 'ShowIndex': 0, 'SignalName':'', 'ValidLineRange':[-1,-1], 'Path':'' }
+        ,'TraceSourceOptimizingThreshold' : config.trace_source_optimizing_threshold
+    }
+    
+    # operation trace
+    OpTraceInf = {
+         'TracePoints' : [] # {'path':'', "pos":(line, colum), 'key':''}
+        ,'TraceDepth'  : config.max_roll_trace_depth
+        ,'Nonius'      : -1  # roll nonius 
+    }
+    
+    TopoInf       = {
+         'CurModule'    : ''
+        ,'TopFoldLevel' : 0
+    }
+    
+    CheckPointInf = {
+         "MaxNum"         : config.max_his_check_point_num
+        ,"CheckPoints"    : []  #{}--- key: '', link: {}
+        ,"TopFoldLevel"   : 0
+    }
+    
+    #-------------------------------------------------------------------------------
+    # init the base module inf
+    #-------------------------------------------------------------------------------
+    BaseModules   = set()
+    # get base module inf
+    try:
+        pkl_input     = open(vtags_db_folder_path + '/pickle/all_basemodule_name_set.pkl','rb')
+        BaseModules   = pickle.load(pkl_input)
+        pkl_input.close()
+    except:
+        pass
+    
+    BaseModuleInf = {
+         "BaseModuleThreshold"  : config.base_module_threshold  # when module inst BaseModuleThreshold times, then default set it to base module
+        ,"BaseModules"          : BaseModules # module name set()
+        ,"TopFoldLevel"         : 0
+    }
+
+    CallMeSubcallInf = {
+         "AddUpperThreshold"                   : None
+        ,"ModuleNameToCallMeSubcallInfListDic" : None
+        ,"MaskedCallMeSubmoduleSet"            : None
+    }
+    try: # valid in vtags-2.20
+        CallMeSubcallInf["AddUpperThreshold"] = config.module_add_upper_threshold
+    except: # for pre version's local config
+        CallMeSubcallInf["AddUpperThreshold"] = glb_config.module_add_upper_threshold
+
+    # max file name length in current os    
+    try: # valid in vtags-2.22
+        MaxFileNameLength = config.max_file_name_length # max file file name length
+    except: # for pre version's local config
+        MaxFileNameLength = glb_config.max_file_name_length
+
+    G = {
+         'InlineActive'                        : True
+        ,'OfflineActive'                       : True
+        ,'SupportVHDLPostfix'                  : set([])
+        ,'SupportVerilogPostfix'               : support_design_postfix_set # 1.23 add filelist postfix and config postfix
+        ,'ModuleNameToModuleInfListDic'        : {}
+        ,'ModuleNameToFilePathListDic'         : None
+        ,'ModuleLastCallInf'                   : {}    # {module_name:{ upper_module_name:'', 'upper_inst_name':inst_name} }
+        ,'FileInf'                             : {}
+        ,'MacroNameToMacroInfListDic'          : None  # {name: [ {name path pos code_line} ]}
+        ,'OpTraceInf'                          : OpTraceInf
+        ,"Debug"                               : config.debug_mode    # debug mode
+        ,"RefreshDBValid"                      : config.dynamic_update_vtags_db
+        ,"ShowReport"                          : config.show_report
+        ,"PrintDebug_F"                        : None         # function to print debug
+        ,"Frame_Inf"                           : Frame_Inf    # Frame window inf
+        ,"Report_Inf"                          : Report_Inf   # report window inf
+        ,"WorkWin_Inf"                         : WorkWin_Inf  # win config
+        ,"VimBufferLineFileLink"               : VimBufferLineFileLink
+        ,"TraceInf"                            : TraceInf
+        ,"CheckPointInf"                       : CheckPointInf
+        ,"BaseModuleInf"                       : BaseModuleInf
+        ,'TopoInf'                             : TopoInf
+        ,"FixExtraSpace"                       : True         # some situation come extra space, need do nonthing
+        ,"IgnoreNextSpaceOp"                   : False        # just fold has a else space, not do space op
+        ,"EnvSnapshortWinsInf"                 : []
+        ,"SaveEnvSnapshort_F"                  : save_env_snapshort
+        ,"VTagsPath"                           : vtags_db_folder_path
+        ,"RunLogPath"                          : vtags_db_folder_path + '/run.log'+str(valid_log_index)
+        ,"CallMeSubcallInf"                    : CallMeSubcallInf
+        # add for path reduce
+        ,"Short2RealPathMap"                   : None # some times pickle file name is too long to creat a file, so use this map to reduce it.
+        ,"Real2ShortPathMap"                   : {}
+        ,"MaxFileNameLength"                   : MaxFileNameLength # max file file name length
+    }
+    return G
+
+
+#-------------------------------------------------------------------------------
+# if not open vim inline turn off
+# if open a empty file, try reload snapshort
+# if open a unsupport rtl inline turn off
+#-------------------------------------------------------------------------------
+vim_opened           = False
+try_reload_snapshort = False
+vim_start_open_file  = ''
+try:
+    import vim
+    vim_start_open_file = vim.current.buffer.name
+    if not vim_start_open_file: # if vim opened and open a empty file, means need reload snapshort
+        try_reload_snapshort = True
+    vim_opened  = True
+except:
+    pass
+
+#-------------------------------------------------------------------------------
+# init G 
+#-------------------------------------------------------------------------------
+# if no vim opened means it's Offline function, so even if no vtags.db
+# found in init_G_from_vtagsDB() it must not raise VtagsDBNotFoundExcept 
+# except because user can set vtags db use set_vtags_db_path()
+# if vim has opened, must has a valid vtags db, if not found just raise
+# VtagsDBNotFoundExcept and terminate the python run
+G = None
+if vim_opened:
+    G = init_G_from_vtagsDB( RaiseExcept = True )
+else:
+    G = init_G_from_vtagsDB()
+    G['InlineActive']  = False  # if no vim opened must not active inline function
+
+#-------------------------------------------------------------------------------
+# if not found vtags db nonthing can work
+#-------------------------------------------------------------------------------
+if not os.path.isdir(G['VTagsPath']):
+    G['OfflineActive'] = False
+    G['InlineActive']  = False
+
+# if vim opened and not reload snapshort and not open a supported rtl design
+# not active inline function
+if vim_opened and (not try_reload_snapshort) \
+    and (get_file_path_postfix(vim_start_open_file) not in G['SupportVerilogPostfix']):
+    G['InlineActive'] = False
+    # if not a valid vtags recgnize file and need raise except to speed up 
+    # none vtags vim open
+    raise VtagsUnsupportFileExcept
+
+#-------------------------------------------------------------------------------
+# this function used to print debug inf:
+# (1) generate vtags.db generate vtags.db/vtags_db.log
+# (2) when debug = True generate run.logN when gvim run
+#-------------------------------------------------------------------------------
+# if run cmd:"vtags" in generate vtags situation, print log to vtags.db/vtags_run.log
+vtags_db_log_path = ['']
+# run log path
+def PrintDebug( str, out_path = ''):
+    if vtags_db_log_path[0]:
+        output = open( vtags_db_log_path[0], 'a')
+        output.write(str+'\n')
+        output.close()
+        return
+    if not G['InlineActive'] and G['OfflineActive'] and G['Debug']:
+        print(str)
+    if out_path and G['Debug']:
+        output = open( out_path, 'a')
+        output.write(str+'\n')
+        output.close()
+        return
+    if G['InlineActive'] and G['Debug']:
+        output = open( G['RunLogPath'] ,'a')
+        output.write(str+'\n')
+        output.close()
+G['PrintDebug_F'] = PrintDebug
+
+#-------------------------------------------------------------------------------
+# if gvim without file and has saved history sence then just replay it
+#-------------------------------------------------------------------------------
+if try_reload_snapshort:
+    env_snapshort_path = G['VTagsPath'] + '/pickle/env_snapshort.pkl'
+    if os.path.isfile(env_snapshort_path):
+        pkl_input        = open(G['VTagsPath'] + '/pickle/env_snapshort.pkl','rb')
+        loaded_snapshort = pickle.load(pkl_input)
+        pkl_input.close()
+        if loaded_snapshort['snapshort_dir_path'] == os.getcwd(): # make sure save dir is the same to open dir
+            os.system('echo \'do you want reload vim snapshort ? (y/n): \'')
+            yes_or_no = raw_input()
+            if yes_or_no.lower() in ['y','yes']:
+                reload_env_snapshort(loaded_snapshort)
+
+def set_vtags_db_path(vtags_db_folder_path):
+    vtags_db_folder_path = vtags_db_folder_path.rstrip('/')
+    if vtags_db_folder_path[-8:] != 'vtags.db' or (not os.path.isdir(vtags_db_folder_path)):
+        return False
+    new_G = init_G_from_vtagsDB( vtags_db_folder_path )
+    for key in G:
+        G[key] = new_G[key]
+    G['InlineActive']  = False
+    return True
+
+
+
+
+
+
+
+
diff --git a/Lib/__init__.py b/Lib/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/OfflineLib/OfflineBaseLib.py b/OfflineLib/OfflineBaseLib.py
new file mode 100755
index 0000000..28f97ad
--- /dev/null
+++ b/OfflineLib/OfflineBaseLib.py
@@ -0,0 +1,178 @@
+"""
+http://www.vim.org/scripts/script.php?script_id=5494
+"""
+#===============================================================================
+# BSD 2-Clause License
+
+# Copyright (c) 2016, CaoJun
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * 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.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#===============================================================================
+
+import os
+import sys
+import re
+import pickle
+import inspect
+import copy
+
+# when vtags.py run, this function can return the dir path vtags.py exist.
+def cur_file_dir():
+     path = sys.path[0]
+     if os.path.isdir(path):
+         return path
+     elif os.path.isfile(path):
+         return os.path.dirname(path)
+
+# get the vtags.py's dir path, which is the install src path.
+vtags_install_path = cur_file_dir()
+
+#-------------------------------------------------------------------------------
+# import base libary
+#-------------------------------------------------------------------------------
+# add install dir path to python search path, to import all the needed python module
+sys.path.insert(0,vtags_install_path)
+import Lib.GLB as GLB
+G = GLB.G
+from Lib.BaseLib import *
+import Lib.CodeLib as CodeLib
+import Lib.FileInfLib as FileInfLib
+import InlineLib.WinLib as WinLib
+
+
+#-------------------------------------------------------------------------------
+# function get for user
+#-------------------------------------------------------------------------------
+
+# this function used to get module_inf for custom user
+def get_full_module_inf(module_name):
+    # valid when has vtags.db
+    if not G['OfflineActive']:
+        print('Error: no vtags.db found !')
+        return {}
+    # get pre module inf no subcall list    
+    cur_module_inf  = FileInfLib.get_module_inf(module_name)
+    if not cur_module_inf:
+        print('Error: module %s not found! \n'%(module_name))
+        return {}
+    if cur_module_inf.setdefault('subcall_instance_list',None) == None:
+        assert(FileInfLib.module_inf_add_subcall_instance_list(cur_module_inf))
+    # give a copy of module_inf incase someone change it
+    return copy.deepcopy(cur_module_inf)
+
+# this function used to open module and go to some lines
+def open_module_file(module_name):
+    cur_module_inf  = FileInfLib.get_module_inf(module_name)
+    if not cur_module_inf:
+        print('Error: module %s not found! \n'%(module_name))
+        return False
+    file_path = cur_module_inf['file_path']
+    module_name_match_pos = cur_module_inf['module_name_match_pos']
+    if not os.path.isfile(file_path):
+        print('Error: module file %s not exist! \n'%(file_path))
+        return False
+    WinLib.open_file_separately(file_path, module_name_match_pos[0])
+
+# this function used to get current module upper module
+def get_father_module_set( module_name, dir_path_set = None ):
+    upper_module_set= set()
+    if not dir_path_set:
+        dir_path_set = get_vtags_db_dir_path_set()
+    for dir_path in dir_path_set:
+        if not os.path.exists(dir_path):
+            continue
+        exclude_patten = '--exclude-dir="vtags.db"'
+        include_patten = '--include ' + (' --include '.join( ['"*.%s"'%(p) for p in G['SupportVerilogPostfix'] ] ))
+        match_lines = os.popen('egrep -n -r %s %s \'(^|\W)%s(\W|$)\' %s'%(exclude_patten, include_patten, module_name, dir_path)).readlines()
+        for l in match_lines:
+            try:
+                spl = l.split(':') #not support file name has ':'
+                file_path = spl[0]
+                line_num  = int(spl[1]) - 1
+                line_inf  = FileInfLib.get_file_line_inf(line_num, file_path)
+                submodule = line_inf['subcall_inf']['submodule_name']
+                upmodule  = line_inf['module_inf']['module_name']
+                if submodule == module_name:
+                    upper_module_set.add(upmodule) 
+            except:
+                continue
+    return upper_module_set
+
+def get_module_filelist(module_name):
+    def rec_gen_module_filelist(module_name, trace_file):
+        cur_module_inf  = get_full_module_inf(module_name)
+        if not cur_module_inf:
+            print('Warning: module %s not found! \n'%(module_name))
+            return
+        cur_module_path = cur_module_inf['file_path']
+        if (module_name, cur_module_path) in trace_file:
+            return
+        trace_file.add( (module_name, cur_module_path) )
+        subcall_instance_list = cur_module_inf['subcall_instance_list']
+        if not subcall_instance_list:
+            return
+        for subcall_inf in subcall_instance_list:
+            submodule_name =  subcall_inf['submodule_name']
+            rec_gen_module_filelist(submodule_name, trace_file)
+    # if no data base return []
+    if not G['OfflineActive']:
+        return []
+    trace_file = set()
+    rec_gen_module_filelist(module_name, trace_file)
+
+    return list( set( [ name_file[1] for name_file in trace_file ] ) )
+
+
+def get_module_io_inf(module_name):
+    return CodeLib.get_io_inf(module_name)
+            #   "name"        : name
+            # , "io_type"     : io_type
+            # , "left"        : left_index
+            # , "right"       : right_index
+            # , "size"        : size
+            # , 'line_num'    : line_num
+            # , 'name_pos'    : names_pos[i]
+            # , 'code_line'   : code_line
+            # , 'signal_type' : signal_type 
+
+
+
+#-------------------------------------------------------------------------------
+# base function
+#-------------------------------------------------------------------------------
+def get_vtags_db_dir_path_set():
+    # get file list
+    vtags_db_refresh_inf_pkl_path = G['VTagsPath']+'/pickle/vtags_db_refresh_inf.pkl'
+    vtags_db_refresh_inf = pickle_reload(vtags_db_refresh_inf_pkl_path)
+    file_list = vtags_db_refresh_inf['file_list']
+    dir_list  = open(file_list,'r').readlines()
+    dir_list  = [re.sub('//.*','',l.strip()) for l in dir_list]
+    dir_list  = [l.strip() for l in dir_list if l.strip() != '' ]
+    dir_path_set = set(dir_list)
+    return dir_path_set
+
+
+
+
+
+
diff --git a/OfflineLib/OfflineFuncLib.py b/OfflineLib/OfflineFuncLib.py
new file mode 100755
index 0000000..845bd39
--- /dev/null
+++ b/OfflineLib/OfflineFuncLib.py
@@ -0,0 +1,319 @@
+"""
+http://www.vim.org/scripts/script.php?script_id=5494
+"""
+#===============================================================================
+# BSD 2-Clause License
+
+# Copyright (c) 2016, CaoJun
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * 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.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#===============================================================================
+
+import os
+import sys
+import re
+import pickle
+import inspect
+
+# when vtags.py run, this function can return the dir path vtags.py exist.
+def cur_file_dir():
+     path = sys.path[0]
+     if os.path.isdir(path):
+         return path
+     elif os.path.isfile(path):
+         return os.path.dirname(path)
+
+# get the vtags.py's dir path, which is the install src path.
+vtags_install_path = cur_file_dir()
+
+#-------------------------------------------------------------------------------
+# import base libary
+#-------------------------------------------------------------------------------
+# add install dir path to python search path, to import all the needed python module
+sys.path.insert(0,vtags_install_path)
+import Lib.GLB as GLB
+G = GLB.G
+from Lib.BaseLib import *
+import Lib.CodeLib as CodeLib
+import Lib.FileInfLib as FileInfLib
+import OfflineLib.OfflineBaseLib as OfflineBaseLib
+
+
+# all function inf
+custom_function_inf = {}
+
+# offline_func_help
+def offline_func_help(Print = False):
+    help_str_list = []
+    help_str_list.append('supported offline functions: ')
+    help_str_list += show_func_help()
+    help_str_list.append('offline call exp: ')
+    help_str_list.append('    "vtags -func find topo " # used to find all function which name has string "topo" ')
+    help_str_list.append('    "vtags -func \'print_module_io( my_cpu )\'" # used to print module my_cpu\'s io ! ' )
+    help_str_list.append('    "vtags -func -db ~/design/vtags_db \'print_module_io( my_cpu )\'" # used specified vtags.db to get io' )
+    if Print: 
+        for l in help_str_list: print(l)
+    return help_str_list
+
+# used for vtags to call functions
+def function_run( parm_list ):
+    if parm_list == []:
+        MountPrintLines(offline_func_help(), label = 'Offline Function Help', Print = True)
+        return True
+    if parm_list[0] == 'find':
+        MountPrintLines(show_func_help( ''.join(parm_list[1:2])), label = 'Func Find Result', Print = True)
+        return
+    if parm_list[0] == '-db':
+        db_path = ''.join(parm_list[1:2])
+        if os.path.isdir(db_path):
+            GLB.set_vtags_db_path(db_path)
+            parm_list = parm_list[2:]
+        else:
+            print('Error: -db must follow a valid vtags_db dir path !')
+            return False
+    call_string = ' '.join(parm_list)
+    function_name, parm_list = decode_call_string(call_string)
+    if not function_name:
+        return False
+    if check_call_func_valid(function_name, parm_list):
+        return real_call_custom_function(function_name, parm_list)
+    return False
+
+
+# used for register a function to all function
+def register_function( function_class, description = '' ):
+    if not (inspect.isfunction(function_class) and type(description) == str):
+        print('Error: unsupport parameters for "register_function(function_class, description_string)"')
+        return
+    function_name = function_class.__name__
+    if function_name in custom_function_inf:
+        func  = custom_function_inf[func_name]
+        func_define = '%s(%s) : %s'(func_name, ', '.join(inspect.getargspec(func).args), func.description)
+        print('func:"%s" already registered ! %s'%(function_name, func_define))
+        return
+    function_class.description = description
+    custom_function_inf[function_name] = function_class
+    return
+
+# used to show all support function with key in name
+def show_func_help(key = '', Print = False):
+    func_str_list = []
+    func_name_list = list(custom_function_inf)
+    if key:
+        assert(type(key) == str),'only support str parms!'
+        func_name_list = [ fn for fn in func_name_list if fn.find(key) != -1]
+    func_name_list.sort()
+    for func_name in func_name_list:
+        func  = custom_function_inf[func_name]
+        func_define = '    %s( %s )    # %s'%(func_name, ', '.join(inspect.getargspec(func).args), func.description)
+        func_str_list.append(func_define)
+    if Print:
+        for l in func_str_list: print(l)
+    return func_str_list
+
+# decode the input call string to function and parms
+def decode_call_string(call_string):
+    match_func = re.match('(?P\w+)\s*\((?P.*)\)\s*$', call_string.strip())
+    if not match_func:
+        print('Error: %s not a valid function call format ! valid call is like "function_name( parm0, parm1, ...)".'%(call_string))
+        return None, []
+    function_name  = match_func.group('name')
+    parm_list      = [ p.strip() for p in (match_func.group('parms')).split(',') if p.strip() ]
+    return function_name, parm_list
+
+# check input parm is valid for some function
+def check_call_func_valid(function_name, parm_list):
+    if function_name not in custom_function_inf:
+        print('Error: func: "%s" not exist ! '%(function_name))
+        return False
+    func = custom_function_inf[function_name]
+    arg_num = len(inspect.getargspec(func).args)
+    arg_has_default = 0
+    if inspect.getargspec(func).defaults:
+        arg_has_default = len(inspect.getargspec(func).defaults)
+    if len(parm_list) >= (arg_num - arg_has_default) and len(parm_list) <= arg_num:
+        return True
+    print('Error: input parameters number not match function define! input:%d, need:[%s-%s]'%(len(parm_list), arg_num - arg_has_default, arg_num))
+    return False
+
+# real call custom function
+def real_call_custom_function(function_name, parm_list):
+    # the max number of custom function is 10
+    if len(parm_list)   == 0:
+        return custom_function_inf[function_name]()
+    elif len(parm_list) == 1:
+        return custom_function_inf[function_name](parm_list[0])
+    elif len(parm_list) == 2:
+        return custom_function_inf[function_name](parm_list[0], parm_list[1])
+    elif len(parm_list) == 3:
+        return custom_function_inf[function_name](parm_list[0], parm_list[1], parm_list[2])
+    elif len(parm_list) == 4:
+        return custom_function_inf[function_name](parm_list[0], parm_list[1], parm_list[2], parm_list[3])
+    elif len(parm_list) == 5:
+        return custom_function_inf[function_name](parm_list[0], parm_list[1], parm_list[2], parm_list[3], parm_list[4])
+    elif len(parm_list) == 6:
+        return custom_function_inf[function_name](parm_list[0], parm_list[1], parm_list[2], parm_list[3], parm_list[4], parm_list[5])
+    elif len(parm_list) == 7:
+        return custom_function_inf[function_name](parm_list[0], parm_list[1], parm_list[2], parm_list[3], parm_list[4], parm_list[5], parm_list[6])
+    elif len(parm_list) == 8:
+        return custom_function_inf[function_name](parm_list[0], parm_list[1], parm_list[2], parm_list[3], parm_list[4], parm_list[5], parm_list[6], parm_list[7])
+    elif len(parm_list) == 9:
+        return custom_function_inf[function_name](parm_list[0], parm_list[1], parm_list[2], parm_list[3], parm_list[4], parm_list[5], parm_list[6], parm_list[7], parm_list[8])
+    elif len(parm_list) == 10:
+        return custom_function_inf[function_name](parm_list[0], parm_list[1], parm_list[2], parm_list[3], parm_list[4], parm_list[5], parm_list[6], parm_list[7], parm_list[8], parm_list[9])
+    else:
+        print('Error: current custom func max support 10 parameters, "%d" give!'%(len(parm_list)))
+    return False
+
+
+
+#--------------------------------------------------------------------------------
+# custom function
+#--------------------------------------------------------------------------------
+# this function print module and all submodule's filelist
+def print_module_filelist(module_name):
+    if not G['OfflineActive']:
+        print('Error: no vtags.db found !')
+        return
+    filelist = OfflineBaseLib.get_module_filelist(module_name)
+    filelist.sort()
+    for file_path in filelist:
+        print(file_path)
+    print('')
+
+
+# this function get input module's instance trace
+# user define parameter:
+#    from_module : trace from this module, if not define, default is top module
+#    to_module   : trace finish to this module
+def print_module_trace(to_module, from_module = None):
+    full_traces = []
+    FileInfLib.recursion_get_module_trace(to_module, [], full_traces)
+    print_strs = []
+    i_offset = 0 # used to control multi same trace case
+    for i, traces in enumerate(full_traces):
+        c_trace_strs = [ traces[-1]['cur_module_name'] ]
+        for t in traces[::-1]:
+            c_str = '%s(%s)'%(t['instance_name'], t['submodule_name'])
+            if c_str not in c_trace_strs:
+                c_trace_strs.append(c_str)
+            else:
+                i_offset -= 1
+        print_strs.append( '%d : %s'%(i + i_offset, ' -> '.join(c_trace_strs) ) )
+    MountPrintLines(print_strs, label = 'Module Trace', Print = True)
+
+# this function show module's topology
+def print_module_topo(module_name, depth = 0, mask = 0, space = None):
+    # valid when has vtags.db
+    if not G['OfflineActive']:
+        print('Error: no vtags.db found !')
+        return
+    depth = int(depth)
+    mask  = int(mask)
+    if not space:
+        space = '    '
+    def rec_print_module_topo(module_name, instance_name, cur_depth):
+        if instance_name:
+            print( '%s%s(%s)'%( space*cur_depth, instance_name, module_name) )
+        else:
+            print( '%s%s:'%( space*cur_depth, module_name) )
+        if (cur_depth + 1 > depth) and (depth != 0):
+            return
+        tmp_module_inf = OfflineBaseLib.get_full_module_inf(module_name)
+        # for the submodule set the masked module, and instance times
+        mask_module_set  = set() | G['BaseModuleInf']['BaseModules']
+        instance_times_count = {}
+        module_instance_pair = []
+        for subcall_inf in tmp_module_inf['subcall_instance_list']:
+            submodule_name = subcall_inf['submodule_name']
+            instance_name  = subcall_inf['instance_name']
+            module_instance_pair.append( (submodule_name, instance_name ) )
+            instance_times_count.setdefault(submodule_name, 0)
+            instance_times_count[submodule_name] += 1
+            if instance_times_count[submodule_name] >= mask and mask != 0:
+                mask_module_set.add(submodule_name)
+        # sep masked and unmasked module
+        unmask_pairs  = []
+        masked_module = set()
+        for module,instance in module_instance_pair:
+            if module in mask_module_set:
+                masked_module.add(module)
+            else:
+                unmask_pairs.append( (module,instance) )
+        # first print unmask topo
+        for module,instance in unmask_pairs:
+            rec_print_module_topo(module, instance, cur_depth + 1)
+        # then for the masked module
+        if masked_module:
+            print( '%s------------'%( space*(cur_depth + 1)) )
+        for module in masked_module:
+            print( '%s%s(%d)'%( space*(cur_depth + 1),module, instance_times_count[module]) )
+    rec_print_module_topo(module_name,'',0)
+
+
+# this function fomat and print module's io
+def print_module_io(module_name):
+    # valid when has vtags.db
+    if not G['OfflineActive']:
+        print('Error: no vtags.db found !')
+        return
+    def extend(s,n):
+        assert(len(s) <= n),'%s,%d'%(s,n)
+        return s + ' '*(n-len(s))
+    module_io_inf_list = OfflineBaseLib.get_module_io_inf(module_name)
+    if not module_io_inf_list:
+        print('Error: not found %s in vtags database !'%(module_name))
+        return
+    # get max len
+    max_io_len    = len('name')
+    max_range_len = len('range')
+    max_type_len  = len('type')
+    for io_inf in module_io_inf_list:
+        if len(io_inf['name']) > max_io_len:
+            max_io_len = len(io_inf['name'])
+        io_range_len = len( str(io_inf['left']).strip() + ' : ' + str(io_inf['right']).strip() )
+        if io_range_len > max_range_len:
+            max_range_len = io_range_len
+        if len(io_inf['io_type']) > max_type_len:
+            max_type_len = len(io_inf['io_type'])
+    # print io
+    print('module: %s io inf:'%(module_name))
+    print('--%s------%s------%s--'%( '-'*max_type_len, '-'*max_io_len, '-'*max_range_len ) )
+    print('| %s    | %s    | %s |'%( extend('type',max_type_len), extend('name',max_io_len), extend('range', max_range_len) ) )
+    print('|-%s----|-%s----|-%s-|'%( '-'*max_type_len, '-'*max_io_len, '-'*max_range_len ) )
+    for io_inf in module_io_inf_list:
+        c_type  = io_inf['io_type']
+        c_name  = io_inf['name']
+        c_range = (str(io_inf['left']).strip() + ' : ' + str(io_inf['right']).strip()).strip().strip(':')
+        print('| %s    | %s    | %s |'%( extend(c_type,max_type_len), extend(c_name,max_io_len), extend(c_range, max_range_len) ) )
+    print('--%s------%s------%s--'%( '-'*max_type_len, '-'*max_io_len, '-'*max_range_len ) )
+
+
+#-------------------------------------------------------------------------------
+# register vtags -func function
+#-------------------------------------------------------------------------------
+register_function( print_module_trace, description = 'this function get input module\'s instance trace' )
+register_function( print_module_filelist, description = 'this function print module and all submodule\'s filelist' )
+register_function( print_module_topo, description = 'this function print module topology!' )
+register_function( print_module_io, description = 'this function print module io inf!' )
+register_function( OfflineBaseLib.open_module_file, description = 'this function used to open module and go to some lines' )
diff --git a/OfflineLib/__init__.py b/OfflineLib/__init__.py
new file mode 100755
index 0000000..e69de29
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100755
index 0000000..a5874c6
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,12 @@
+Metadata-Version: 2.0
+Name: vtags
+Version: 2.24
+Summary: Python subprocess interface
+Home-page: http://www.vim.org/scripts/script.php?script_id=5494
+Author: Jimmy Situ
+Author-email: web@jimmystone.cn
+License: Jimmy Situ
+Description: A fork of vtags from Jun Cao, update code for python3 support
+Platform: Unix
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3.7
diff --git a/README.md b/README.md
new file mode 100755
index 0000000..5aff096
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
+# Installation
+    (1) uncompress vtag-x.xx.tar.gz and move to install path anywhere you want
+    (2) add alias vtags.py to ~/.cshrc or ~/.bashrc depend on your os:
+        for .cshrc  : alias vtags 'python /vtags.py'
+        for .bashrc : alias vtags='python /vtags.py'
+    (3) source .cshrc or .bashrc
+    (4) add vtags_vim_api.vim to ~/.vimrc
+        source /vtags_vim_api.vim
+
+# Complete documentation see the "vtags-x.xx/doc"
diff --git a/custom_api_example.py b/custom_api_example.py
new file mode 100755
index 0000000..d11aed9
--- /dev/null
+++ b/custom_api_example.py
@@ -0,0 +1,41 @@
+"""
+http://www.vim.org/scripts/script.php?script_id=5494
+"""
+#===============================================================================
+# BSD 2-Clause License
+
+# Copyright (c) 2016, CaoJun
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * 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.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#===============================================================================
+
+import os,sys
+sys.path.append(' your vtags install path')
+import vtags_custom_api
+
+# set the vtas.db you want to used
+vtags_custom_api.set_vtags_db_path('vtags.db path you want to used')
+
+# now you can used function in vtags_custom_api to write your script
+# such as: 
+print(vtags_custom_api.get_module_instance_trace('l2'))
diff --git a/doc/vtags.txt b/doc/vtags.txt
new file mode 100644
index 0000000..b8fcb38
--- /dev/null
+++ b/doc/vtags.txt
@@ -0,0 +1,32 @@
+vtags.txt Verdi like, verilog code signal trace and show hierarchy script
+
+===============================================================================
+CONTENTS                                                        *vtags-contents*
+
+1. Usage...........................................................|vtags-usage
+
+
+===============================================================================
+1. Usage
+                                                                  *vtags-usage*
+shortcut key            function 
+    mt                  : print module trace from top to current cursor module.
+    gi                  : go into submodule 
+    gu                  : go upper module
+    gs                  : trace source
+    gd                  : trace destination
+    gf                  : go forward
+    gb                  : roll back
+           : trace source 
+          : trace destination 
+           : roll back 
+             : go forward 
+     + v         : view sidebar 
+     + c         : add checkpoint 
+     + b         : add basemodule 
+     + d         : delete 
+     +h          : hold cur window 
+                 : quick access 
+     + s         : save snapshort 
+    gvim/vim            : reload snapshort 
+
diff --git a/plugin/vtags_vim_api.vim b/plugin/vtags_vim_api.vim
new file mode 100755
index 0000000..65dc79c
--- /dev/null
+++ b/plugin/vtags_vim_api.vim
@@ -0,0 +1,78 @@
+"""
+" http://www.vim.org/scripts/script.php?script_id=5494
+"""
+"===============================================================================
+" BSD 2-Clause License
+" 
+" Copyright (c) 2016, CaoJun
+" All rights reserved.
+" 
+" Redistribution and use in source and binary forms, with or without
+" modification, are permitted provided that the following conditions are met:
+" 
+" * 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.
+" 
+" THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+" DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+" CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+" OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+"===============================================================================
+
+let s:path = expand(':p:h')
+
+function! VimPythonExtend()
+python3 << EOF
+import sys
+import os
+import vim
+vtags_install_path = vim.eval('s:path')
+assert(os.path.isdir(vtags_install_path))
+sys.path.insert(0,vtags_install_path)
+from Lib.ExceptionLib import *
+
+vim.command("let s:vtags_active = 0")
+try:
+    from InlineLib.InlineAPI import *
+    vim.command("let s:vtags_active = 1")
+except VtagsDBNotFoundExcept:
+    pass
+except VtagsUnsupportFileExcept:
+    pass
+
+EOF
+endfunction
+
+"vi_HDLTags_begin-----------------------------------
+call VimPythonExtend()
+if s:vtags_active == 1
+    map gi                   :py3 try_go_into_submodule()           
+    map gu                   :py3 try_go_upper_module()             
+    map mt                   :py3 try_print_module_trace()          
+    map         :py3 try_trace_signal_sources()        
+    map gs                   :py3 try_trace_signal_sources()        
+    map        :py3 try_trace_signal_destinations()   
+    map gd                   :py3 try_trace_signal_destinations()   
+    map         :py3 try_roll_back()                   
+    map gb                   :py3 try_roll_back()                   
+    map           :py3 try_go_forward()                  
+    map gf                   :py3 try_go_forward()                  
+    map v             :py3 try_show_frame()                  
+    map c             :py3 try_add_check_point()             
+    map b             :py3 try_add_base_module()             
+    map               :py3 try_space_operation()             
+    map h             :py3 try_hold_current_win()            
+    map d             :py3 try_del_operation()               
+    map s             :py3 try_save_env_snapshort()          
+endif
+"vi_HDLTags_end-------------------------------------
diff --git a/vim_glb_config.py b/vim_glb_config.py
new file mode 100755
index 0000000..9e7660a
--- /dev/null
+++ b/vim_glb_config.py
@@ -0,0 +1,74 @@
+"""
+http://www.vim.org/scripts/script.php?script_id=5494
+"""
+#===============================================================================
+# BSD 2-Clause License
+
+# Copyright (c) 2016, CaoJun
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * 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.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#===============================================================================
+
+# left sidebar window width
+frame_window_width          = 20
+
+# right bottom report window height
+report_window_height        = 8
+
+# max work window vtags auto opened, not include use self opened window and holded window
+max_open_work_window_number = 1
+
+# when use / the max number of history trace valid
+max_roll_trace_depth        = 1000
+
+# when c add check point, the max number of check point valid
+max_his_check_point_num     = 10
+
+# when gen the vtags database, in all verilog modules when some module called more then threshold times,
+# set this module to be base module, then show topo will not list it's inst one by one  
+base_module_threshold       = 200
+
+# when module inst module_add_upper_threshold times, then not current module not note upper module
+module_add_upper_threshold  = 10
+
+# supported verilog postfix, we only add postfix in below to data base
+support_verilog_postfix     = ['v']
+
+# open debug module or not, open debug module will print a lot debug log at vtags.db
+debug_mode                  = False
+
+# when trace source, match bigger than TraceSourceOptimizingThreshold, open opt func, mainly for signal like clk,rst ...
+trace_source_optimizing_threshold   = 20 
+
+# frame fold level space, use to set pre space num, if current level is 3 ,
+# and fold level space is ' ', then current line pre space is ' '*3 = '   ' 
+frame_fold_level_space      = '    '
+
+# weather show report or not
+show_report                 = True
+
+# will dynamic update vtags database
+dynamic_update_vtags_db     = True
+
+# max file name length in current os, used to deciside reduce pickle file name or not
+max_file_name_length        = 100
diff --git a/vtags.py b/vtags.py
new file mode 100755
index 0000000..ecd4fe9
--- /dev/null
+++ b/vtags.py
@@ -0,0 +1,308 @@
+#!/usr/bin/python
+"""
+http://www.vim.org/scripts/script.php?script_id=5494
+"""
+#===============================================================================
+# BSD 2-Clause License
+
+# Copyright (c) 2016, CaoJun
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * 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.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#===============================================================================
+__version__ = "2.24.1"
+__project_url__ = "http://www.vim.org/scripts/script.php?script_id=5494"
+
+import os
+import sys
+import re
+import pickle
+
+#-------------------------------------------------------------------------------
+#print help
+#-------------------------------------------------------------------------------
+help = ''
+try:
+    help = sys.argv[1]
+except:
+    pass
+
+#-------------------------------------------------------------------------------
+# get the install folder path
+#-------------------------------------------------------------------------------
+# when vtags.py run, this function can return the dir path vtags.py exist.
+def cur_file_dir():
+     path = sys.path[0]
+     if os.path.isdir(path):
+         return path
+     elif os.path.isfile(path):
+         return os.path.dirname(path)
+
+# get the vtags.py's dir path, which is the install src path.
+vtags_install_path = cur_file_dir()
+
+#-------------------------------------------------------------------------------
+# Offline function
+#-------------------------------------------------------------------------------
+offline_function_parms = None
+try:
+    if sys.argv[1] == '-func':
+        offline_function_parms = sys.argv[2:]
+except:
+    pass
+sys.path.insert(0,vtags_install_path)
+import OfflineLib.OfflineFuncLib as OfflineFuncLib
+if offline_function_parms != None:
+    OfflineFuncLib.function_run(offline_function_parms)
+    exit()
+
+#-------------------------------------------------------------------------------
+# when run vtags.py, create a folder named vtag.db at current dir
+# and also rm the old vtags_db.log if exist.
+#-------------------------------------------------------------------------------
+vtags_db_folder_path = os.getcwd() + '/vtags.db'
+
+#-------------------------------------------------------------------------------
+# import lib used in generate vtags.db
+#-------------------------------------------------------------------------------
+# add install dir path to python search path, to import all the needed python module
+sys.path.insert(0,vtags_install_path)
+# import the module used to generate vtags.db database
+import Lib.GLB as GLB
+G = GLB.G
+GLB.vtags_db_log_path[0] = vtags_db_folder_path + '/vtags_db.log'
+from Lib.BaseLib import *
+import Lib.FileInfLib as FileInfLib
+
+#-------------------------------------------------------------------------------
+# print help information
+#-------------------------------------------------------------------------------
+if help in ['-h','-help']:
+    help_str_list = []
+    help_str_list.append("(1) generate vtags at code dir, use command \"vtags\" or \"vtags -f folder_list\";"          )
+    help_str_list.append("(2) config vtags vim at vtags gen dir \"/vtags.db/vim_local_config.py\","                    )
+    help_str_list.append("    config items and detail look vim_local_config.py notes;"                                 )
+    help_str_list.append("(3) support action in vim window:"                                                           )
+    help_str_list.append("        1)  mt             : print the module trace from top module;"                        )
+    help_str_list.append("        2)  gi             : if cursor on module call, go in submodule;"                     )
+    help_str_list.append("        3)  gu             : if cur module called before, go upper module;"                  )
+    help_str_list.append("        4)    : trace cursor word signal source;"                               )
+    help_str_list.append("        5)   : trace cursor word signal dest;"                                 )
+    help_str_list.append("        6)    : roll back;"                                                     )
+    help_str_list.append("        7)      : go forward;"                                                    )
+    help_str_list.append("        8)  v       : show current module topo "                                      )
+    help_str_list.append("                             or fold/unfold sidebar items;"                                  )
+    help_str_list.append("        9)  c       : add current cursor as checkpoint, can go back directly;"        )
+    help_str_list.append("        10)  b      : add current cursor module as basemodule, not show in topo;"     )
+    help_str_list.append("        11)         : in sidebar or report win, just go cursor line link;"            )
+    help_str_list.append("        12) h       : hold cursor win, will not auto close it;"                       )
+    help_str_list.append("        13) d       : in sidebar, delete valid items(base_module, checkpoint...);"    )
+    help_str_list.append("        14) s       : save current vim snapshort,"                                    )
+    help_str_list.append("                             use \"gvim/vim\" without input file to reload snapshort;"       )
+    help_str_list += OfflineFuncLib.offline_func_help()
+    MountPrintLines(help_str_list, label = 'Vtags Help', Print = True)
+    exit()
+
+# special warning for case only use default 'v' as postfix
+if len(G['SupportVerilogPostfix']) == 1 and 'v' in G['SupportVerilogPostfix']:
+    warning_line_list = []
+    warning_line_list.append('Default config only treat "xxx.v" as verilog design files.')
+    warning_line_list.append('If you have other valid postfix please add it to :')
+    warning_line_list.append('  vtags.db/vim_local_config.py   : support_verilog_postfix= [...] (only change local config)')
+    warning_line_list.append('  or')
+    warning_line_list.append('  vtags-2.xx/vim_local_config.py : support_verilog_postfix= [...] (change global config)')
+    warning_line_list.append('Such as:')
+    warning_line_list.append('  support_verilog_postfix= [\'v\', \'V\', \'d\'] // add xxx.V, xxx.d as valid verilog design files' )
+    MountPrintLines(warning_line_list, label = 'Add Support Postfix', Print = True)
+    print('')
+
+#-------------------------------------------------------------------------------
+# when run vtags.py, create a folder named vtag.db at current dir
+# and also rm the old vtags_db.log if exist.
+#-------------------------------------------------------------------------------
+os.system('mkdir -p %s'%(vtags_db_folder_path))
+if os.path.isfile(vtags_db_folder_path + '/vtags_db.log'):
+    os.system('rm -rf '+vtags_db_folder_path+'/vtags_db.log')
+
+#-------------------------------------------------------------------------------
+# filelist support
+#-------------------------------------------------------------------------------
+# current vtags.db real_filelist
+vtags_file_list     = vtags_db_folder_path + '/design.filelist'
+filelist_filehandle = open(vtags_file_list, 'w')
+
+file_list = ''
+try:
+    if sys.argv[1] == '-f':
+        file_list = sys.argv[2].strip()
+except:
+    pass
+
+# for each dir in the filelist, try to add a vtags.db soft link to current dir's real vtags.db
+if file_list:
+    if not os.path.isfile(file_list): # if file_list not exist just exit
+        print("Error: filelist: %s not exist !"%(file_list))
+        exit(1)
+    else: # if file_list exist, try to add vtags.db's soft ln for each dir
+        dir_in_filelist = []
+        file_in_filelist = []
+        for f in open(file_list, 'r').readlines():
+            f = re.sub('//.*','', f).strip()
+            # get all dir for print
+            if os.path.isdir(f):
+                dir_in_filelist.append(f)
+            elif os.path.isfile(f):
+                file_in_filelist.append(f)
+        # if has filelist must has some value file or dir
+        if not dir_in_filelist + file_in_filelist:
+            print('Error: no valid file or dir in current filelist: %s'%(filelist))
+            exit(1)
+        # else generate vtags used abs filelist
+        for f in dir_in_filelist + file_in_filelist:
+            filelist_filehandle.write( os.path.realpath(f) + '\n')
+        filelist_filehandle.close()
+        # if has dir in file list need give warning to generate ln manully
+        line_list = []
+        line_list.append('Generated "vtags.db" put in current dir(./)                                  ')
+        line_list.append('If you want active vtags in other folders in filelist, you need add soft link')
+        line_list.append('manually.                                                                    ')
+        line_list.append('Folders in filelist:                                                         ')
+        for d in dir_in_filelist:
+            line_list.append('  %s'%(d))
+        line_list.append('Such as:')
+        for d in dir_in_filelist:
+            line_list.append('  ln -s ./vtags.db %s'%(d))
+        MountPrintLines(line_list, label = 'TAKE CARE', Print = True)
+else:
+    print("Note: no filelist, create vtags.db for current dir !")
+    filelist_filehandle.write( os.path.realpath( os.getcwd() ) + '\n')
+    filelist_filehandle.close()
+
+
+#-------------------------------------------------------------------------------
+# get all the verilog file code inf
+#-------------------------------------------------------------------------------
+# get all verilog files path from file_list
+design_file_path_set      = FileInfLib.get_all_design_file_path_from_filelist(vtags_file_list)
+
+# get all code inf
+file_path_to_code_inf_dic = FileInfLib.init_get_file_path_to_code_inf_dic(design_file_path_set)
+
+
+#-------------------------------------------------------------------------------
+# add more inf to module_inf, subcall_inf, macro_inf
+#-------------------------------------------------------------------------------
+module_name_to_file_path_list_dic = {}
+file_path_to_last_modify_time_dic = {}
+for f in file_path_to_code_inf_dic:
+    file_path_to_last_modify_time_dic[f] = file_path_to_code_inf_dic[f]['last_modify_time']
+    module_inf_list = file_path_to_code_inf_dic[f]['module_inf_list'  ]
+    for module_inf in module_inf_list:
+        module_name_to_file_path_list_dic.setdefault(module_inf['module_name'],[])
+        module_name_to_file_path_list_dic[ module_inf['module_name'] ].append(f)
+
+macro_name_to_macro_inf_list_dic = {}
+for f in file_path_to_code_inf_dic:
+    macro_inf_list = file_path_to_code_inf_dic[f]['macro_inf_list']
+    for macro_inf in macro_inf_list:
+        macro_name_to_macro_inf_list_dic.setdefault(macro_inf['macro_name'],[])
+        macro_name_to_macro_inf_list_dic[macro_inf['macro_name']].append(macro_inf)
+
+#-------------------------------------------------------------------------------
+# set base module
+#-------------------------------------------------------------------------------
+all_basemodule_name_set_pkl_path = vtags_db_folder_path+'/pickle/all_basemodule_name_set.pkl'
+all_basemodule_name_set      = set()
+masked_call_me_submodule_set = set()
+# if not os.path.isfile(all_basemodule_name_set_pkl_path):
+add_upper_threshold     = G['CallMeSubcallInf']['AddUpperThreshold']
+base_threshold          = G['BaseModuleInf']['BaseModuleThreshold']
+# first get all module instance number, if instance number bigger than base_threshold
+# tread corresponding module as base module
+module_name_to_instance_num_dic  = {}
+for f in file_path_to_code_inf_dic:
+    subcall_inf_list = file_path_to_code_inf_dic[f]['subcall_inf_list']
+    for subcall in subcall_inf_list:
+        submodule_name = subcall['submodule_name']
+        module_name_to_instance_num_dic.setdefault(submodule_name,0)
+        module_name_to_instance_num_dic[submodule_name] += 1
+# check if beyond the base_threshold
+for module_name in module_name_to_instance_num_dic:
+    if ( not os.path.isfile(all_basemodule_name_set_pkl_path) )and \
+       ( module_name_to_instance_num_dic[module_name] >= base_threshold) :
+        all_basemodule_name_set.add(module_name)
+    if module_name_to_instance_num_dic[module_name] > add_upper_threshold:
+        masked_call_me_submodule_set.add(module_name)
+
+#-------------------------------------------------------------------------------
+# generate module_name_to_call_me_subcall_inf_list_dic
+#-------------------------------------------------------------------------------
+module_name_to_call_me_subcall_inf_list_dic = {}
+for f in file_path_to_code_inf_dic:
+    subcall_inf_list = file_path_to_code_inf_dic[f]['subcall_inf_list']
+    for subcall_inf in subcall_inf_list:
+        submodule_name = subcall_inf['submodule_name']
+        if submodule_name not in masked_call_me_submodule_set:
+            module_name_to_call_me_subcall_inf_list_dic.setdefault(submodule_name, []).append( subcall_inf )
+
+#-------------------------------------------------------------------------------
+# pickle
+#-------------------------------------------------------------------------------
+os.system('mkdir -p %s'%(vtags_db_folder_path+'/pickle'))
+# 1 pickle file_list,file_path_to_last_modify_time_dic or refresh vtags.db
+vtags_db_refresh_inf = {
+     'file_list'                         : vtags_file_list
+    ,'file_path_to_last_modify_time_dic' : file_path_to_last_modify_time_dic
+}
+vtags_db_refresh_inf_pkl_path = vtags_db_folder_path+'/pickle/vtags_db_refresh_inf.pkl'
+pickle_save(vtags_db_refresh_inf, vtags_db_refresh_inf_pkl_path)
+# 2 pickle module_name_to_file_path_list_dic, for refresh single file subcall_inf
+module_name_to_file_path_list_dic_pkl_path = vtags_db_folder_path+'/pickle/module_name_to_file_path_list_dic.pkl'
+pickle_save(module_name_to_file_path_list_dic, module_name_to_file_path_list_dic_pkl_path)
+# 3 pick all macro inf
+macro_name_to_macro_inf_list_dic_pkl_path = vtags_db_folder_path+'/pickle/macro_name_to_macro_inf_list_dic.pkl'
+pickle_save(macro_name_to_macro_inf_list_dic, macro_name_to_macro_inf_list_dic_pkl_path)
+# 4 pick file_path_to_code_inf_dic
+# pick save all f inf
+for f in file_path_to_code_inf_dic:
+    code_inf = file_path_to_code_inf_dic[f]
+    del code_inf['macro_inf_list']
+    code_inf_pkl_path = vtags_db_folder_path+'/pickle/design__%s.pkl'%(f.replace('/','__'))
+    pickle_save(code_inf, FileInfLib.get_shortpath(code_inf_pkl_path, create = True) )
+pickle_save(G["Short2RealPathMap"], vtags_db_folder_path+'/pickle/short_to_real_path_map.pkl')
+
+# 5 used pickle save base module inf
+if not os.path.isfile(all_basemodule_name_set_pkl_path):
+    pickle_save(all_basemodule_name_set, all_basemodule_name_set_pkl_path)
+# 6 pickle save call me subcall inf dic
+call_me_subcall_inf = {
+     'ModuleNameToCallMeSubcallInfListDic' : module_name_to_call_me_subcall_inf_list_dic
+    ,'MaskedCallMeSubmoduleSet'            : masked_call_me_submodule_set
+}
+pickle_save(call_me_subcall_inf, vtags_db_folder_path+'/pickle/call_me_subcall_inf.pkl')
+
+
+#-------------------------------------------------------------------------------
+# copy glable config to vtags.db to generate local config if no local config
+#-------------------------------------------------------------------------------
+if not os.path.isfile(vtags_db_folder_path + '/vim_local_config.py'):
+    os.system('cp %s/vim_glb_config.py %s/vim_local_config.py'%(vtags_install_path, vtags_db_folder_path))
diff --git a/vtags_custom_api.py b/vtags_custom_api.py
new file mode 100755
index 0000000..3378be3
--- /dev/null
+++ b/vtags_custom_api.py
@@ -0,0 +1,115 @@
+"""
+http://www.vim.org/scripts/script.php?script_id=5494
+"""
+#===============================================================================
+# BSD 2-Clause License
+
+# Copyright (c) 2016, CaoJun
+# All rights reserved.
+
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are met:
+
+# * 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.
+
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#===============================================================================
+
+import os
+import sys
+import re
+import pickle
+import inspect
+
+# when vtags.py run, this function can return the dir path vtags.py exist.
+def cur_file_dir():
+     path = sys.path[0]
+     if os.path.isdir(path):
+         return path
+     elif os.path.isfile(path):
+         return os.path.dirname(path)
+
+# get the vtags.py's dir path, which is the install src path.
+vtags_install_path = cur_file_dir()
+
+#-------------------------------------------------------------------------------
+# import base libary
+#-------------------------------------------------------------------------------
+# add install dir path to python search path, to import all the needed python module
+sys.path.insert(0,vtags_install_path)
+import Lib.GLB as GLB
+G = GLB.G
+from Lib.BaseLib import *
+import Lib.CodeLib as CodeLib
+import Lib.FileInfLib as FileInfLib
+import OfflineLib.OfflineBaseLib as OfflineBaseLib
+
+# set_vtags_db_path( vtags_db_path )
+# this function used to set the vtags.db you want to access
+# exp: set_vtags_db_path('~/my_design/rtl/vtags.db')
+set_vtags_db_path          =  GLB.set_vtags_db_path
+
+
+# module_inf = get_full_module_inf(module_name)
+# this function used to get the module_inf:
+# module_inf = {
+#    'module_name'           # module name
+#   ,'file_path'             # module file path
+#   ,'module_line_range'     # module line range in file
+#   ,'module_name_match_pos' # module define position
+#   ,'subcall_instance_list' # a list of subcall_inf }
+# subcall_inf = {
+#    'submodule_name'           # instance module name
+#   ,'instance_name'            # instance name
+#   ,'subcall_line_range'       # instance code range in module file
+#   ,'submodule_name_match_pos' # instance call position }   
+get_full_module_inf        =  OfflineBaseLib.get_full_module_inf
+
+# open_module_file( module_name )
+# this function used to open module file with vim and jump to module define position
+# exp: open_module_file( 'my_module' )
+open_module_file           =  OfflineBaseLib.open_module_file
+
+# father_module_set = get_father_module_set( module_name )
+# this function used to get the futher modules of current module                   
+get_father_module_set      =  OfflineBaseLib.get_father_module_set
+
+# module_trace_list = get_module_instance_trace(module_name)
+# this module used th get the module instance trace in vtags database
+get_module_instance_trace  =  OfflineBaseLib.get_module_instance_trace
+
+# file_path_of_module_list = get_module_filelist( module_name )
+# this function used to get current module and all subcall module file path list
+get_module_filelist        =  OfflineBaseLib.get_module_filelist
+
+# io_inf_list = get_module_io_inf( module_name )
+# this function get the current module io inf list
+# io_inf = {
+#   "name"        : io name
+#   "io_type"     : io type(input, output, ioput)
+#   "left"        : left_index
+#   "right"       : right_index
+#   "size"        : size
+#   'line_num'    : line_num in file
+#   'name_pos'    : io name position in file
+#   'code_line'   : io code line
+#   'signal_type' : signal type(wire, reg ...)  }            
+get_module_io_inf          =  OfflineBaseLib.get_module_io_inf     
+
+            
+
+