gvim中进行verilog语言信号追踪、显示拓扑插件

插件使用方法及功能:

vtags 是一款在gvim下实现类似verdi的信号追踪、显示拓扑等功能的插件。vtags插件完全使用python实现,目前实现功能包括信号追踪、宏定义追踪、显示模块拓扑、快速打开文件、保存和打开gvim快照、添加断点等功能。

支持功能和快捷键:

快捷键

功能

gi

进入子模块

gu

回到上层模块

<Space><Left>

追信号源,或宏定义

<Space><Right>

追信号目的

<Space><Down>

回退

<Space><Up>

向前

<Space> + v

显示策略,和展开收回策略条目

<Space> + c

添加记录点

<Space> + b

添加基本模块

<Space> + d

删除记录点或基本模块

<Space> +h

固定当前窗口

<Space>

快速访问

<Space> + s

储存快照

gvim/vim

加载快照

详细信息看《二:开始使用及支持功能和快捷键》

注意:

(1)在code目录下通过vtags生成vtags.db后,第一次打开verilog code时需要编译生成的database,所以第一打开code比较慢,之后打开code就会非常迅速。

 

安装方式:

1. 下载插件代码,解压并复制到自选的安装路径下。下面以安装目录为“~/vtags-1.20/为例”

   代码下载地址: (1)https://pan.baidu.com/s/1kVfRXFx

                         (2)http://www.vim.org/scripts/script.php?script_id=5494

2. 在linux配置文件中添加别名。

  ~/.cshrc  中添加:alias vtags 'python  ~/vtags-1.20/vtags.py'

 或  ~/.bashrc 中添加:alias=vtags 'python  ~/vtags-1.20/vtags.py'

  source ~/.cshrc 或 source ~/.bashre 使设置生效。

3. 在vim配置文件中加入插件。

  ~/.vimrc  中添加: source  ~/vtags-1.20/vtags_vim_api.vim

4. 本步骤可选,可以在vtags-1.20/vim_glb_config.py中设置插件的一些全局设置,

  你也可以使用vtags时生成的局部配置文件vim_local_config.py中进行设置。

5. cd 到code目录使用命令:vtags, 在code目录下生成base文件 vtags.db, 用gvim打开代码就可以使用上面定义快捷键中功能。

注: 要求python版本2.7,详情请看《用户使用指南》。

 

code 源码:

"""
https://my.oschina.net/u/2520885
"""
#===============================================================================
# Copyright (C) 2016 by Jun Cao

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#===============================================================================

# 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 <Space><left>/<Space><right> the max number of history trace valid
max_roll_trace_depth        = 1000

# when <Space>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

# 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

# weather show sidebar or not
show_sidebar                = True


"""
https://my.oschina.net/u/2520885
"""
#===============================================================================
# Copyright (C) 2016 by Jun Cao

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#===============================================================================
__version__ = "1.20"
__project_url__ = "https://my.oschina.net/u/2520885"

import os
import sys
import re
import pickle


#print help
help = ''
try:
    help = sys.argv[1]
except:
    pass
if help in ['-h','-help']:
    print("(1) generate vtags at code dir, use command \"vtags\" ;"                                    )
    print("(2) config vtags vim at vtags gen dir \"/vtags.db/vim_local_config.py\","                   )
    print("    config items and detail look vim_local_config.py notes;"                                )
    print("(3) support action in vim window:"                                                          )
    print("        1) gi             : if cursor on module call, go in submodule;"                     )
    print("        2) gu             : if cur module called before, go upper module;"                  )
    print("        3) <Space><Left>  : trace cursor word signal source;"                               )
    print("        4) <Space><Right> : trace cursor word signal dest;"                                 )
    print("        5) <Space><Down>  : roll back;"                                                     )
    print("        6) <Space><Up>    : go forward;"                                                    )
    print("        7) <Space>v       : show current module topo "                                      )
    print("                            or fold/unfold sidebar items;"                                  )
    print("        8) <Space>c       : add current cursor as checkpoint, can go back directly;"        )
    print("        9) <Space>b       : add current cursor module as basemodule, not show in topo;"     )
    print("        10) <Space>       : in sidebar or report win, just go cursor line link;"            )
    print("        11) <Space>h      : hold cursor win, will not auto close it;"                       )
    print("        12) <Space>d      : in sidebar, delete valid items(base_module, checkpoint...);"    )
    print("        13) <Space>s      : save current vim snapshort,"                                    )
    print("                            use \"gvim/vim\" without input file to reload snapshort;"       )
    exit()


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)

vtags_install_path = cur_file_dir()
sys.path.insert(0,vtags_install_path)

sed_vtags_install_path = re.sub('/','\/',vtags_install_path)
os.system('sed -ig \'s/vtags_install_path =.*/vtags_install_path = "%s"/g\' %s/vtags_vim_api.vim' %(sed_vtags_install_path, vtags_install_path) )
os.system('sed -ig \'s/let is_vtags_installed =.*/let is_vtags_installed = 1/g\' %s/vtags_vim_api.vim' %(vtags_install_path) )

vtags_folder_path = os.getcwd() + '/vtags.db'
os.system('mkdir -p %s'%(vtags_folder_path))
if os.path.isfile(vtags_folder_path + '/vtag_gen_log.txt'):
    os.system('rm -rf '+vtags_folder_path+'/vtag_gen_log.txt')

import Lib.GLB as GLB
G = GLB.G
GLB.vtags_run_log_path[0] = vtags_folder_path + '/vtags_run.log'
import Lib.Code as Code
import Lib.View as View
from Lib.Base import *

# filelist:
#     use "//" as note sign
#     support dir_path and file path mix 
def get_all_verilog_files_from_filelist(filelist_path):
    verilog_file_paths = []
    # get ~ path
    home_path = os.popen('echo ~').readlines()[0].rstrip('\n').rstrip('/')
    for l in open(filelist_path,'r').readlines():
        l = l.strip('\n')
        l = re.sub('//.*','',l)
        l = re.sub('^~', home_path, l)
        if re.match('\s*$',l):
            continue
        path = l
        if os.path.isdir(path):
            c_files_path     = os.popen('find ' + dir_path + ' -type f 2>/dev/null').readlines()
            c_files_path     = [ d_l.rstrip('\n') for d_l in c_files_path ]
            for c_f in c_files_path:
                if get_file_path_postfix(c_f) not in G['SupportVerilogPostfix']:
                    continue
                verilog_file_paths.append(c_f)
        if os.path.isfile(path):
            if get_file_path_postfix(path) not in G['SupportVerilogPostfix']:
                continue
            verilog_file_paths.append(path)
    return verilog_file_paths

# get all verilog files inf and merge
#   modules_inf  = { module_name: module_inf }
#   defines_inf  = { macro_name : [ define_inf ] }
#   files_inf    = { file_name  : file_inf }
#       module_inf    = {  'module_name'        : module_name
#                         ,'file_path'          : f
#                         ,'line_range_in_file' : (module_start_line_num, module_end_line_num)
#                         ,'sub_modules'        : sub_modules }
#       define_inf    = {  "name" : xxx
#                         ,"path" : f 
#                         ,"pos"  : (line_num, colum_num)  # name first char pos
#                         ,'code_line' : `define xxx .... }
#       file_inf      = {  'glb_defines'   : [ define_inf ] 
#                         ,'module_infs'   : [ module_inf ]
#                         ,'module_calls'  : [ call_sub_inf ]
#                         ,'file_edit_inf' : { 'create_time': ..., 'last_modify_time': ...}
#                       call_sub_inf  =  { 'inst_name'     : inst_name
#                                         ,'module_name'   : modu_name
#                                         ,'match_range'   : match_range }
def get_verilog_files_code_inf(paths):
    # step 1/2 get all module/define inf
    all_file_module_inf = {}
    all_file_define_inf = {}
    all_module_name     = set()
    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 = get_single_verilog_file_module_inf(f)
        cur_file_define_inf = get_single_verilog_file_define_inf(f)
        # add to result
        all_file_module_inf[f] = cur_file_module_inf
        all_file_define_inf[f] = cur_file_define_inf
        all_module_name        = all_module_name | set([ mi['module_name'] for mi in cur_file_module_inf])
    print('')
    # step 2/2 get all file sub call inf
    all_file_subcall_inf = {}
    patten = get_submodule_match_patten(all_module_name)
    print('step 2/2:')
    for i,f in enumerate(paths):
        PrintDebug(f)
        show_progress_bar( i, len(paths))
        all_file_subcall_inf[f] = get_single_verilog_file_subcall_inf(f, patten, all_module_name)
    print('')
    # merge to all_file_inf
    all_file_inf = {}
    for i,f in enumerate(paths):
        add_single_verilog_file_submodule_inf_to_module_inf( all_file_module_inf[f], all_file_subcall_inf[f] )
        all_file_inf[f] = {
             'glb_defines'   : all_file_define_inf[f]
            ,'module_infs'   : all_file_module_inf[f]
            ,'module_calls'  : all_file_subcall_inf[f]
            ,'file_edit_inf' : { 'create_time': os.path.getctime(f), 'last_modify_time': os.path.getmtime(f)}
        }
    modules_inf , global_defines_inf = gen_modules_and_defines_inf(all_file_inf)
    return {
         'files_inf'   : all_file_inf
        ,'modules_inf' : modules_inf
        ,'defines_inf' : global_defines_inf
    }

#------------------------function for get module inf--------------------
filelist_path = ''
verilog_file_paths = []
if filelist_path:
    verilog_file_paths = get_all_verilog_files_from_filelist(filelist_path)
else:
    c_dir_path           = os.getcwd()
    c_dir_files_path     = os.popen('find ' + c_dir_path + ' -type f 2>/dev/null').readlines()
    c_dir_files_path     = [ d_l.rstrip('\n') for d_l in c_dir_files_path ]
    for c_f in c_dir_files_path:
        if get_file_path_postfix(c_f) not in G['SupportVerilogPostfix']:
            continue
        verilog_file_paths.append(c_f)

# get all code inf
verilog_code_inf = get_verilog_files_code_inf(verilog_file_paths)
modules_inf  = verilog_code_inf['modules_inf']
files_inf    = verilog_code_inf['files_inf'  ]
defines_inf  = verilog_code_inf['defines_inf']

# set base module
base_modules     = set()
base_threshold  = G['BaseModuleInf']['BaseModuleThreshold']
module_inst_num = {}
for m in modules_inf:
    for sm in modules_inf[m]['sub_modules']:
        module_name = sm['module_name']
        module_inst_num.setdefault(module_name,0)
        module_inst_num[module_name] += 1
for m in module_inst_num:
    if module_inst_num[m] >= base_threshold:
        base_modules.add(m)

# change vim HDLTags path

fp = open(vtags_folder_path + '/files_inf.py','w')
fp.write('FileInf = %s \n'%(files_inf.__str__()))
fp.write('HDLTagsActive = True \n')
fp.close()
if not os.path.isfile(vtags_folder_path + '/vim_local_config.py'):
    os.system('cp %s/vim_glb_config.py %s/vim_local_config.py'%(vtags_install_path, vtags_folder_path))
if not os.path.isfile(vtags_folder_path+'/base_modules.pkl'):
    output = open(vtags_folder_path+'/base_modules.pkl','wb')
    pickle.dump(base_modules, output)
    output.close()


"""
" https://my.oschina.net/u/2520885
"""
"===============================================================================
" Copyright (C) 2016 by Jun Cao

" Permission is hereby granted, free of charge, to any person obtaining a copy
" of this software and associated documentation files (the "Software"), to deal
" in the Software without restriction, including without limitation the rights
" to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
" copies of the Software, and to permit persons to whom the Software is
" furnished to do so, subject to the following conditions:

" The above copyright notice and this permission notice shall be included in
" all copies or substantial portions of the Software.

" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
" IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
" FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
" AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
" LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
" OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
" THE SOFTWARE.
"===============================================================================

function! VimPythonExtend() 
python << EOF
import os
import sys
vtags_install_path = "/mnt/hgfs/VMShare/my_tools/vtags-1.20"
if os.path.isdir(vtags_install_path):
    sys.path.insert(0,vtags_install_path)
    from Lib.API import *
EOF
endfunction

let is_vtags_installed = 1

if is_vtags_installed == 1
    "vi_HDLTags_begin-----------------------------------
    call VimPythonExtend()
    map gi                   :py try_go_into_submodule()           <CR>
    map gu                   :py try_go_upper_module()             <CR>
    map <Space><Left>        :py try_trace_signal_sources()        <CR>
    map <Space><Right>       :py try_trace_signal_destinations()   <CR>
    map <Space><Down>        :py try_roll_back()                   <CR>
    map <Space><Up>          :py try_go_forward()                  <CR>
    map <Space>v             :py try_show_frame()                  <CR> 
    map <Space>c             :py try_add_check_point()             <CR> 
    map <Space>b             :py try_add_base_module()             <CR> 
    map <Space>              :py try_space_operation()             <CR>
    map <Space>h             :py try_hold_current_win()            <CR>
    map <Space>d             :py try_del_operation()               <CR>
    map <Space>s             :py try_save_env_snapshort()          <CR>
    "vi_HDLTags_end-------------------------------------
endif

"""
https://my.oschina.net/u/2520885
"""
#===============================================================================
# Copyright (C) 2016 by Jun Cao

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#===============================================================================

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.Code as Code
import Lib.View as View
PrintReport = View.PrintReport
import Lib.GLB as GLB
G = GLB.G
from Lib.Base import *

# init gen module_inf, and glb define
if G['HDLTagsActive']:
    G['ModuleInf'], G['CodeDefineInf'] = gen_modules_and_defines_inf(G['FileInf'])

# open snapshort wins
if G['HDLTagsActive'] and G['EnvSnapshortWinsInf']:
    OldOpenWinTrace = [p for p in G['WorkWin_Inf']['OpenWinTrace']]
    G['WorkWin_Inf']['OpenWinTrace'].insert(0,vim.current.buffer.name)
    for w_inf in G['EnvSnapshortWinsInf']:
        c_path   = w_inf['path']
        c_cursor = w_inf['cursor']
        if os.path.isfile(c_path):
            View.Open(c_path)
            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:
            PrintDebug('Warning: reload file not exit ! file: %s'%(c_path))
    for w_inf in G['EnvSnapshortWinsInf']:
        c_size  = w_inf['size']
        c_path   = w_inf['path']
        if os.path.isfile(c_path):
            View.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]:
        View.refresh_topo()
        View.show_base_module()
    PrintReport('reload snapshort finish !')
elif G['HDLTagsActive']:
    # treat the first win as work win , if cur win is hdl code, and add first trace point
    first_cursor_inf = View.get_cur_cursor_inf()
    if first_cursor_inf['hdl_type'] == 'verilog':
        G['WorkWin_Inf']['OpenWinTrace'].append(first_cursor_inf['file_path'])
        View.add_trace_point()
    PrintDebug('Point: initial new env finish !')


# shortcut key: gi
def go_into_submodule(): 
    cursor_inf       = View.get_cur_cursor_inf()
    cur_module_inf   = cursor_inf['cur_module_inf']
    if not cur_module_inf:
        PrintReport('Warning: cur cursor not in a valid module, no submodule call !')
        return
    call_module_name = cur_module_inf['module_name']
    sub_call_inf     = Code.get_module_call_sub_module_io_inf(cursor_inf['line'], cursor_inf['pos'], cursor_inf['file_path'])
    if not sub_call_inf:
        PrintReport('Warning: cur cursor not on recgnized subcall !')
        return
    sub_module_name  = sub_call_inf['sub_module_name'] 
    sub_module_inf   = get_module_inf(sub_module_name)
    if not sub_module_inf:
        PrintReport('Warning: sub module %s no module inf in database !'%(sub_module_name))
        return
    sub_signal_name  = sub_call_inf['sub_io_name']
    sub_match_pos    = []
    if sub_signal_name:
        sub_match_pos    = sub_call_inf['sub_match_pos']
    else:
        sub_signal_name = sub_module_name
        sub_match_pos   = sub_module_inf['module_pos']
    call_sub_inf     = sub_call_inf['call_sub_inf']      # GLB call sub inf(atom)
    sub_module_path  = sub_module_inf['file_path']
    set_module_last_call_inf(sub_module_name, call_module_name, call_sub_inf['inst_name'])
    View.add_trace_point()
    View.go_win( sub_module_path, sub_match_pos, sub_signal_name)

def try_go_into_submodule():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        go_into_submodule()
    else:
        try: go_into_submodule()
        except: pass

# shortcut key: gu
def go_upper_module(): 
    cursor_inf        = View.get_cur_cursor_inf()
    cur_module_inf    = cursor_inf['cur_module_inf']
    if not cur_module_inf:
        PrintReport('Warning: cur line not in a valid verilog module, or current module not in database !')
        return
    cur_last_call_inf = get_module_last_call_inf(cur_module_inf['module_name'])
    if not cur_last_call_inf:
        PrintReport('Warning: cur module %s not called by upper module before !'%(cur_module_inf['module_name']))
        return
    upper_module_name  = cur_last_call_inf['upper_module_name']
    upper_call_sub_inf = cur_last_call_inf['upper_call_inf']
    upper_match_pos    = upper_call_sub_inf['match_pos']
    upper_inst_name    = upper_call_sub_inf['inst_name']
    View.add_trace_point()
    View.go_win( upper_module_name, upper_match_pos, upper_inst_name)

def try_go_upper_module():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        go_upper_module()
    else:
        try: go_upper_module()
        except: pass


# shortcut key: <Space><Left>
def trace_signal_sources():
    if G['IgnoreNextSpaceOp']:
        G['IgnoreNextSpaceOp'] = False
        PrintDebug('Care: not do this trace source op ,bucause <space> is come from unknow reason !')
        return
    cursor_inf = View.get_cur_cursor_inf()
    trace_signal_name = cursor_inf['word']
    if not trace_signal_name:
        PrintReport("Warning: Current cursor not on signal name, can not trace source !")
        return
    # case0: if cur cursor on a macro, go macro define
    if Code.trace_glb_define_signal('source', cursor_inf): return
    # case1: if cur cursor on io signal, need cross to upper module
    if Code.trace_io_signal('source', cursor_inf): return
    # case2: if cur cursor on module call io line go to submodule io
    if Code.trace_module_call_io_signal('source', cursor_inf): 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) :
        View.show_next_trace_result('source')
        return
    # case4: trace a new normal(not io, sub call io) signal
    if Code.trace_normal_signal('source', cursor_inf): return

def try_trace_signal_sources():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        trace_signal_sources()
    else:
        try: trace_signal_sources()
        except: pass


# shortcut key: <Space><Right>
def trace_signal_destinations():
    if G['IgnoreNextSpaceOp']:
        G['IgnoreNextSpaceOp'] = False
        PrintDebug('Care: not do this trace source op ,bucause <space> is come from unknow reason !')
        return
    cursor_inf = View.get_cur_cursor_inf()
    trace_signal_name = cursor_inf['word']
    if not trace_signal_name:
        PrintReport("Warning: Current cursor not on signal name, can not trace dest!")
        return
    # case0: if cur cursor on io signal, need cross to upper module
    if Code.trace_io_signal('dest', cursor_inf): return
    # case1: if cur cursor on module call io line go to submodule io
    if Code.trace_module_call_io_signal('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) :
        View.show_next_trace_result('dest')
        return
    # case3: if cur cursor on a macro, go macro define
    if Code.trace_glb_define_signal('dest', cursor_inf): return
    # case4: trace a new normal(not io, sub call io) signal
    Code.trace_normal_signal('dest', cursor_inf)

def try_trace_signal_destinations():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        trace_signal_destinations()
    else:
        try: trace_signal_destinations()
        except: pass


# shortcut key: <Space><Down> 
def roll_back():
    if G['IgnoreNextSpaceOp']:
        G['IgnoreNextSpaceOp'] = False
        PrintDebug('Care: not do this trace source op ,bucause <space> 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("Warning: roll backed to the oldest trace point now !")
        return
    # go to the trace point
    cur_point = TracePoints[cur_nonius]
    G['OpTraceInf']['Nonius'] = cur_nonius
    View.go_win( cur_point['path'], cur_point['pos'], cur_point['key'])
    return

def try_roll_back():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        roll_back()
    else:
        try: roll_back()
        except: pass


# shortcut key: <Space><Up> 
def go_forward():
    if G['IgnoreNextSpaceOp']:
        G['IgnoreNextSpaceOp'] = False
        PrintDebug('Care: not do this trace source op ,bucause <space> is come from unknow reason !')
        return
    cur_nonius        = G['OpTraceInf']['Nonius'] + 1
    TracePoints       = G['OpTraceInf']['TracePoints']
    if cur_nonius >= len(TracePoints):
        PrintReport("Warning: go forward to the newest trace point now !")
        return
    cur_point = TracePoints[cur_nonius]
    G['OpTraceInf']['Nonius'] = cur_nonius
    View.go_win( cur_point['path'], cur_point['pos'], cur_point['key'])
    return

def try_go_forward():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        go_forward()
    else:
        try: go_forward()
        except: pass


# shortcut key: <space>
def space_operation():
    if G['IgnoreNextSpaceOp']:
        G['IgnoreNextSpaceOp'] = False
        PrintDebug('Care: not do this trace source op ,bucause <space> is come from unknow reason !')
        return
    cursor_inf = View.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'] ]:
        cur_frame_link = G['VimBufferLineFileLink'][cursor_inf['file_path']][cursor_inf['line_num']]
        View.add_trace_point()
        if cur_frame_link and cur_frame_link['path']:
            View.go_win( cur_frame_link['path'], cur_frame_link['pos'], cur_frame_link['key'])
            View.add_trace_point()
        else:
            PrintReport('Warning: No file link in current line ! ')
    else:
        PrintReport('Warning: No space operation in current file ! ')

def try_space_operation():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        space_operation()
    else:
        try: space_operation()
        except: pass

# shortcut key: <s-t>
def show_frame():
    G["IgnoreNextSpaceOp"] = G['FixExtraSpace']
    if cur_in_frame():
        cursor_line = vim.current.window.cursor[0] - 1 
        View.frame_line_fold_operation(cursor_line)
    else:
        View.show_topo()
        View.show_check_point(False)
        View.show_base_module(False)
    return

def try_show_frame():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        show_frame()
    else:
        try: show_frame()
        except: pass
    return

# shortcut key: <Space>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['HDLTagsActive']: return
    if G['Debug']:
        hold_current_win()
    else:
        try: hold_current_win()
        except: pass


# shortcut key: <Space>c
def add_check_point():
    G["IgnoreNextSpaceOp"] = G['FixExtraSpace']
    cursor_inf   = View.get_cur_cursor_inf()
    level        = G['CheckPointInf']['TopFoldLevel'] + 1 
    key          = G['Frame_Inf']['FoldLevelSpace']*level + cursor_inf['word']
    link         = {
         'type'     : 'check_point'
        ,'key'      : cursor_inf['word']
        ,'pos'      : cursor_inf['pos']
        ,'path'     : cursor_inf['file_path']
        ,'fold_inf' : { 'fold_status': 'fix', 'level': level }
    }
    G['CheckPointInf']['CheckPoints'].insert(0, {'key': key, 'link': link })
    if len(G['CheckPointInf']['CheckPoints']) > G['CheckPointInf']['MaxNum']:
        del G['CheckPointInf']['CheckPoints'][-1]
    View.show_check_point()

def try_add_check_point():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        add_check_point()
    else:
        try: add_check_point()
        except: pass

# shortcut key: <Space>b
def add_base_module():
    G["IgnoreNextSpaceOp"] = G['FixExtraSpace']
    cursor_inf    = View.get_cur_cursor_inf()
    cursor_module = cursor_inf['word']
    if not cursor_module:
        PrintReport('Warning: cursor not on a valid word ! ')
        return
    if not get_module_inf(cursor_module):
        PrintReport('Warning: cursor words: %s not a recgnized module name ! will no file link ! '%(cursor_module))
    if cursor_module in G['BaseModuleInf']['BaseModules']:
        PrintReport('Care: module %s is already base module ! '%(cursor_module))
        return
    G['BaseModuleInf']['BaseModules'].add(cursor_module)
    update_base_module_pickle()
    View.show_base_module()
    View.refresh_topo()


def try_add_base_module():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        add_base_module()
    else:
        try: add_base_module()
        except: pass

#------------------------------
# shortcut key: <Space>d
def del_operation():
    if not cur_in_frame():
        PrintReport('Warning: 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('Warning: Cur line no del function ! ')
        return
    # delete a check point, if link has path means a valid link
    if (cur_file_link['type'] == 'check_point') and (cur_file_link['fold_inf']['level'] > G['CheckPointInf']['TopFoldLevel']):
        G["IgnoreNextSpaceOp"] = G['FixExtraSpace']
        check_point_begin_line_num = 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 ]
        View.show_check_point()
        return
    # del a base module
    if (cur_file_link['type'] == 'base_module') and (cur_file_link['fold_inf']['level'] > G['BaseModuleInf']['TopFoldLevel']): 
        G["IgnoreNextSpaceOp"] = G['FixExtraSpace']
        G['BaseModuleInf']['BaseModules'].remove(cur_file_link['key'])
        update_base_module_pickle()
        View.show_base_module()
        View.refresh_topo()
        return
    PrintReport('Warning: Cur line no del function ! ')

def try_del_operation():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        del_operation()
    else:
        try: del_operation()
        except: pass

#----------------------------------------
# shortcut key: <Space>s
def try_save_env_snapshort():
    if not G['HDLTagsActive']: return
    if G['Debug']:
        if G['SaveEnvSnapshort_F']():
            PrintReport('save env snapshort success !')
    else:
        try: 
            if G['SaveEnvSnapshort_F']():
                PrintReport('save env snapshort success !')
        except: pass

"""
https://my.oschina.net/u/2520885
"""
#===============================================================================
# Copyright (C) 2016 by Jun Cao

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#===============================================================================

import sys
try:
    import vim
except: 
    pass
import os
import re
import pickle
import GLB
G = GLB.G
# function to print debug
PrintDebug  = G['PrintDebug_F']

def get_valid_code( str ):
    str = re.sub('(^\s*)|(\s*$)', '' ,str)
    str = re.sub('//.*', '' ,str)
    str = re.sub('^`.*', '' ,str)  # bug ^\s*`.*
    return str

def get_valid_code_leave_head_space( str ):
    str = re.sub('\s*$', '' ,str)
    str = re.sub('//.*', '' ,str)
    str = re.sub('^`.*', '' ,str)
    return str

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']

def get_path_for_name(path_or_name):
    if path_or_name == 'Frame':
        return G["Frame_Inf"]["Frame_Path"]
    if path_or_name == 'Report':
        return G["Report_Inf"]["Report_Path"]
    may_module_inf = get_module_inf(path_or_name)
    if may_module_inf:
        return may_module_inf['file_path']
    return path_or_name

def get_file_name_from_path(path):
    return re.sub('.*/','',path)

def get_full_word(line, y):
    pre_part  = ( re.match('^(?P<pre>\w*)',(line[:y])[::-1]).group('pre') )[::-1]
    post_part = re.match('^(?P<post>\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_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 ''

# --------------------------------------------------------
# ok
def recgnize_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)
    if not re_match:
        re_match = re.match('(?P<prefix>.*\(\s*)(?P<real_io>(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_inf = decode_egreped_verilog_io_line(egrep_io_line)['io_infs']
    return 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 }

#-----------------------------------------------------------
# ok
def search_verilog_code_use_grep(key, path, row_range = ()):
    search_result = []
    if not path:
        path = vim.current.buffer.name
    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<pre>^|\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 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   = []
    sub_func_modules, sub_base_modules = get_sub_func_base_module(topo_module)
    # first deal sub func module, show "inst(module)"
    sub_func_modules_inst_names = list(sub_func_modules)
    sub_func_modules_inst_names.sort()
    for c_sub_inst_name in sub_func_modules_inst_names:
        c_call_sub_inf    = sub_func_modules[c_sub_inst_name]
        c_sub_module_name = c_call_sub_inf['module_name']
        # gen show data
        c_str      = '%s%s(%s)'%(topo_prefix, c_sub_inst_name, c_sub_module_name)
        topo_datas.append(c_str)
        # gen link
        c_topo_link = {
                 'type'           : 'topo'
                ,'topo_inst_name' : ''
                ,'key'            : ''
                ,'pos'            : ''
                ,'path'           : ''
                ,'fold_inf'       : {'fold_status':'off', 'level': sub_level}
        }
        c_sub_module_inf = get_module_inf(c_sub_module_name)
        if c_sub_module_inf:
            c_topo_link['topo_inst_name'] = c_sub_inst_name
            c_topo_link['key'           ] = c_sub_module_name
            c_topo_link['pos'           ] = c_sub_module_inf['module_pos']
            c_topo_link['path'          ] = c_sub_module_inf['file_path']
            # show cur module, then all submodule, last call set to cur module
            set_module_last_call_inf(c_sub_module_name, topo_module, c_call_sub_inf['inst_name'])
        topo_links.append(c_topo_link)
    sub_base_modules_names = list(sub_base_modules)
    sub_base_modules_names.sort()
    if len(sub_base_modules_names) > 0:
        # deal base , show "module(n)"
        # add one to sep func and base
        topo_datas.append(topo_prefix+'------')
        c_topo_link = {
             'type'           : 'topo'
            ,'topo_inst_name' : ''
            ,'key'            : ''
            ,'pos'            : ()
            ,'path'           : ''
            ,'fold_inf'       : {'fold_status':'on', 'level': sub_level}
        }
        topo_links.append(c_topo_link)
        for c_sub_module_name in sub_base_modules_names:
            # deal data
            c_sub_inst_n = len(sub_base_modules[c_sub_module_name])
            c_str = '%s%s(%d)'%(topo_prefix,c_sub_module_name,c_sub_inst_n)
            topo_datas.append(c_str)
            # deal link
            c_topo_link = {
                     'type'           : 'topo'
                    ,'topo_inst_name' : ''
                    ,'key'            : ''
                    ,'pos'            : ''
                    ,'path'           : ''
                    ,'fold_inf'       : {'fold_status':'off', 'level': sub_level}
            }
            c_sub_module_inf = get_module_inf(c_sub_module_name)
            if c_sub_module_inf:
                c_topo_link['key' ] = c_sub_module_name
                c_topo_link['pos' ] = c_sub_module_inf['module_pos']
                c_topo_link['path'] = c_sub_module_inf['file_path']
            topo_links.append(c_topo_link)
    return topo_datas, topo_links

def get_fram_check_point_inf():
    datas = []
    links = []
    for cp in G['CheckPointInf']['CheckPoints']:
        datas.append(cp['key'])    
        links.append(cp['link'])
    return datas, links   

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_modules      = list(G['BaseModuleInf']['BaseModules'])
    base_modules.sort()
    for bm in base_modules:
        key  = base_module_space + bm
        link = {
             'type'     : 'base_module'
            ,'key'      : bm
            ,'pos'      : ''
            ,'path'     : ''
            ,'fold_inf' : { 'fold_status': 'fix', 'level': base_module_level }
        }
        bm_module_inf = get_module_inf(bm)
        if bm_module_inf:
            link['pos']  = bm_module_inf['module_pos']
            link['path'] = bm_module_inf['file_path']
        datas.append(key)
        links.append(link)
    return datas, links

###########################################################
#----------------------------------------------------------
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('\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
    match_io_type   = re.match('(?P<io_type>\w+)\s*(?P<other>.*)',valid_code_line)
    assert(match_io_type)
    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<r_names_str>\w+(\s*,\s*\w+)*)\s*(?P<r_other>.*)',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<signal_type>\w*)\s*(?P<other>.*)',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(left_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 
        }
    return {'io_infs':io_infs, 'name_list':names}

#------------------------------------------------
# for code in verilog modules, from pos to find next pair ")"
# case 0:
# if pos is "(" | ( A...B )                              
# pos           | ^                                       
# rest          |  parm_line= A...B,  end_pos  = B 's pos 
# case 1:
# if pos not "("| ( A...B )                              
# pos           |   ^                                       
# rest          |   parm_line= A...B,  end_pos  = B 's pos
def get_str_to_next_right_bracket(module_lines, pos):
    assert(0),'not used not realized '

















































def get_str_to_pre_left_bracket(module_lines, pos):
    assert(0),'not used not realized '

















































#-------------------------------------------
# for situation: 
# case0  : module_name inst_name (.a(A), .b(B), .c(C)) |
#        |                        ^                    | input pos
#        | ^0          ^1                              | module_range[0]
# case1  : module_name #( .a(A), .b(B), .c(C) ) inst_name ( ... )
#        |                ^                            | input pos
#        | ^                                           | module_range[0]  
# case2  : module_name #( A, B, C ) inst_name ( .x(X), .y(Y) )
#        |                                      ^      | input pos
#        | ^                                           | module_range[0]  
# case3  : module_name ... inst_name[a:b] ...
# case4  : module_name ... inst_name[a:b] (A,B,C)
def get_verilog_sub_module_inf_from_dot_line( pos, module_lines, module_start_line_num):
    dot_line = get_valid_code(module_lines[pos[0]])
    assert(dot_line[pos[1]] == '.')
    module_name =  ''
    inst_name   =  ''
    match_range =  []
    match_pos   =  ()
    # first deal pre dot line
    pre_line_end       = False
    dot_pre_line       = dot_line[:pos[1]+1] # module_name inst_name (.
    pre_next_line_num  = pos[0] - 1
    search_case0       = '' 
    search_case12      = ''
    while not pre_line_end:
        semicolon_y    = dot_pre_line.find(';')
        if semicolon_y != -1:
            pre_line_end = True
            dot_pre_line = dot_pre_line[semicolon_y + 1:]
        search_case0 = re.search('(^|\W)(?P<m_name>\w+)\s+(?P<i_name>\w+(\s*\[[^\[\]]*\])?)\s*\(\s*\.$',dot_pre_line)
        if search_case0:
            module_name    = search_case0.group('m_name')
            inst_name      = search_case0.group('i_name')
            break
        search_case12 = re.search('(^|\W)(?P<m_name>\w+)\s*#\s*\(', dot_pre_line)
        if search_case12:
            module_name    = search_case12.group('m_name')
            match_case2_inst_name   = re.match('\.\s*\(\s*(?P<r_i_name>(\s*\][^\[\]]*\[\s*)?\w+)',dot_pre_line[::-1]) # inst_name ( .
            if match_case2_inst_name:
                inst_name      = match_case2_inst_name.group('r_i_name')[::-1]
            break
        if pre_next_line_num >= 0:
            dot_pre_line = get_valid_code(module_lines[pre_next_line_num]) + ' ' + dot_pre_line
            pre_next_line_num -= 1
        else:
            pre_line_end = True
    # if not match anyone then unrecgize
    if not (search_case0 or search_case12):
        PrintDebug('Error: 0 cur dot match cannot recgnize ! line: %s , pos: %s'%(dot_line, pos.__str__()))
        return False
    # if no inst name need decode dot back line
    if not inst_name:  # case1: module_name #( .a(A), .b(B), .c(C) ) inst_name ( ... )
        back_line_end       = False
        dot_back_line       = dot_line[pos[1]:] # .a(A), .b(B), .c(C))
        back_next_line_num  = pos[0] + 1
        search_case1_inst_name = '' 
        while not back_line_end:
            semicolon_y    = dot_back_line.find(';')
            if semicolon_y != -1:
                back_line_end = True
                dot_back_line = dot_back_line[:semicolon_y]
            # inst_name ( .x
            search_case1_inst_name = re.search('\)\s*(?P<i_name>\w+(\s*\[[^\[\]]*\])?)\s*\(',dot_back_line)
            if search_case1_inst_name:
                inst_name = search_case1_inst_name.group('i_name')
                break
            if back_next_line_num < len(module_lines):
                dot_back_line = dot_back_line + ' ' +get_valid_code(module_lines[back_next_line_num])
                back_next_line_num += 1
            else:
                back_line_end = True
    if not inst_name:
        PrintDebug('Error: 1 cur dot match cannot recgnize inst name ! line: %s , pos: %s'%(dot_line, pos.__str__()))
        return False
    match_range    = [ pre_next_line_num + 1 + module_start_line_num, -1 ]
    module_y       = module_lines[pre_next_line_num + 1].find(module_name)
    assert(module_y != -1)
    match_pos      = ( pre_next_line_num + 1 + module_start_line_num, module_y )
    return {
         'inst_name'     : inst_name
        ,'module_name'   : module_name
        ,'match_range'   : match_range
        ,'match_pos'     : match_pos
    }

# case 0: dff # (...) my_dff(a, b, c);  in only one line
def get_verilog_sub_module_inf_from_pound_line( pos, module_lines, module_start_line_num):
    pound_line = get_valid_code(module_lines[pos[0]])
    match_sub  = re.match('\s*(?P<module_name>\w+)\s+#\s*\(.*\)\s*(?P<inst_name>\w+)\s*\(.*\)\s*;\s*', pound_line)
    if match_sub:
        return {
             'inst_name'   : match_sub.group('inst_name')
            ,'module_name' : match_sub.group('module_name')
            ,'match_range' : [ pos[0] + module_start_line_num, pos[0] + module_start_line_num ]
            ,'match_pos'   : ( pos[0] + module_start_line_num, module_lines[pos[0]].find(match_sub.group('module_name')) )
        }
    return False

# for current valid module lines
# find each pair           : (".xxx(...)"  ";")
# get the each pair result :{  'inst_name'     : inst_name
                            # ,'module_name'   : modu_name
                            # ,'match_range'   : match_range
                            # ,'match_pos'     : match_pos}
# finial result is         : [ pair_result0, pair_result1, ... ]
def get_verilog_sub_module_inf(module_lines, module_start_line_num, gen_vtags_log_path = ''):
    sub_module_inf        = []
    has_call_sub_not_end  = False
    find_next = True
    for i ,l in enumerate(module_lines):
        if find_next:
            assert(not has_call_sub_not_end),'already start new search, should not has unfinish subcall'
            cur_sub_module_inf   = {}
            cur_match_right_part = ''
            if l.find('.') != -1:
                l  = get_valid_code(l)
                s0 = re.search('(^|\W)\.\w+\s*\(', l)
                if not s0:
                    continue
                # no matter recgnize or not , must wait next ";", continue search
                find_next = False
                # get dot pos
                dot_colm = ''
                if l[0] is '.':
                    dot_colm = 0
                else:
                    dot_colm = s0.span()[0] + 1
                assert(dot_colm != '')
                cur_match_right_part = l[s0.span()[1]:]
                # get cur sub module inf
                cur_sub_module_inf = get_verilog_sub_module_inf_from_dot_line( (i, dot_colm), module_lines, module_start_line_num)
            elif l.find('#') != -1:
                l  = get_valid_code(l)
                s1 = re.search('#\s*\(', l)
                if not s1:
                    continue
                # no matter recgnize or not , must wait next ";", continue search
                find_next = False
                # get dot pos
                pound_colm = ''
                dot_colm = s1.span()[0]
                cur_match_right_part  = l[s1.span()[1]:]
                # get cur sub module inf
                cur_sub_module_inf = get_verilog_sub_module_inf_from_pound_line( (i, pound_colm), module_lines, module_start_line_num)
            else:
                continue
            # find result in two way
            if cur_sub_module_inf:
                assert(not has_call_sub_not_end)
                sub_module_inf.append(cur_sub_module_inf)
                has_call_sub_not_end = True
            else:
                PrintDebug( 'Error : not recgnize %d: %s '%(i+module_start_line_num, l ), gen_vtags_log_path )
            # no matter find or not , current back line has valid ";", continue find new match
            if cur_match_right_part.find(';') != -1:
                find_next = True
            # if end at current line, set the call range [1]
            if has_call_sub_not_end and cur_match_right_part.find(';') != -1:
                sub_module_inf[-1]['match_range'][1] = i + module_start_line_num # start line and end line the same
                has_call_sub_not_end = False
            continue
        if (not find_next) and l.find(';') != -1:
            if get_valid_code(l).find(';') != -1:
                # if current not find next, and match a valid ";", need continue search
                find_next = True
                # if has unended sub call, and find a valid ";", set last call sub range[1]
                if has_call_sub_not_end:
                    sub_module_inf[-1]['match_range'][1] = i + module_start_line_num # end line is the first ; line
                    has_call_sub_not_end = False
        # if has_call_sub_not_end and (not find_next) and l.find(';') != -1:
        if has_call_sub_not_end and l.find(';') != -1:
            if get_valid_code(l).find(';') != -1:
                sub_module_inf[-1]['match_range'][1] = i + module_start_line_num # end line is the first ; line
                has_call_sub_not_end = False
    if has_call_sub_not_end:
        PrintDebug('Error : call sub not end at end module , try module end as callsub end ! : %s'%(sub_module_inf[-1].__str__()), gen_vtags_log_path)
        sub_module_inf[-1]['match_range'][1] = len(module_lines) - 1 + module_start_line_num # end line is the first ; line
    return sub_module_inf


#-------------------------------------------
#   modules_inf  = { module_name: module_inf }
#   defines_inf  = { macro_name : [ define_inf ] }
#   files_inf    = { file_name  : file_inf }
#       module_inf    = {  'module_name'        : module_name
#                         ,'file_path'          : f
#                         ,'line_range_in_file' : (module_start_line_num, module_end_line_num)
#                         ,'module_pos'         : module_pos
#                         ,'sub_modules'        : sub_modules }
#       define_inf    = {  "name" : xxx
#                         ,"path" : f
#                         ,"pos"  : (line_num, colum_num)  # name first char pos
#                         ,'code_line' : `define xxx .... }
#       file_inf      = {  'glb_defines'   : [ define_inf ]
#                         ,'module_infs'   : [ module_inf ]
#                         ,'module_calls'  : [ call_sub_inf ]
#                         ,'file_edit_inf' : { 'create_time': ..., 'last_modify_time': ...}
#                       call_sub_inf  =  { 'inst_name'     : inst_name
#                                         ,'module_name'   : module_name
#                                         ,'match_range'   : match_range
#                                         ,'match_pos'     : match_pos }
def gen_modules_and_defines_inf(files_inf):
    modules_inf                   = {}
    global_defines_inf            = {}
    for c_file_path in files_inf:
        c_file_inf               = files_inf[c_file_path]
        # merge defines
        c_file_glb_defines       = c_file_inf['glb_defines']
        for d in c_file_glb_defines:
            d_name = d['name'] 
            global_defines_inf.setdefault(d_name,[])
            global_defines_inf[d_name].append(d)
        # merge modules_inf
        c_file_module_infs       = c_file_inf['module_infs']
        for m in c_file_module_infs:
            mn = m['module_name']
            if mn in modules_inf:
                PrintDebug('Error: module %s has multip defines ! in %s '%(mn, [ modules_inf[mn]['file_path'], m['file_path'] ].__str__() ))
            else:
                modules_inf.setdefault(mn, None)
            modules_inf[mn] = m
    return modules_inf, global_defines_inf

def updata_file_pickle_inf(path):
    PrintDebug('Care: updata database, file: %s'%(path))
    if get_file_path_postfix(path) not in G['SupportVerilogPostfix']:
        PrintDebug('Warning: updata_file_pickle_inf: file not verilog file ! file: %s'%(path))
        return False
    if not os.path.isfile(path):
        PrintDebug('Error: updata_file_pickle_inf: file not exit ! file: %s'%(path))
        return
    new_file_modules_inf = get_single_verilog_file_code_inf(path)
    glb_defines          = new_file_modules_inf['glb_defines']    #[ define_inf ] 
    module_infs          = new_file_modules_inf['module_infs']    #[ module_inf ]
    # updata files_inf
    if path not in G['FileInf']:
        # if add new inf just add
        G['FileInf'][path] = new_file_modules_inf
        # add glb_defines
        for gd in glb_defines:
            gd_name = gd['name']
            G['CodeDefineInf'].setdefault(gd_name,[])
            G['CodeDefineInf'][gd_name].append( gd )
        # add module infs
        for m_inf in module_infs:
            m_name = m_inf['module_name']
            if m_name in G['ModuleInf']:
                PrintDebug('Error: module %s define twice ! used last at %s, %s'%(m_name, G['ModuleInf'][m_name]['file_path'], path))
            G['ModuleInf'][m_name] = m_inf
    else:
        # need refresh old and add new, so just gen new inf
        G['FileInf'][path] = new_file_modules_inf
        G['ModuleInf'], G['CodeDefineInf'] = gen_modules_and_defines_inf(G['FileInf'])
    # update pickles
    if not os.path.isdir(G['VTagsPath']):
        os.system('mkdir -p '+G['VTagsPath'])
    fp = open(G['VTagsPath'] + '/files_inf.py','w')
    fp.write('FileInf = %s \n'%(G['FileInf'].__str__()))
    fp.write('HDLTagsActive = True \n')
    fp.close()

def get_module_inf(module_name):
    if module_name not in G['ModuleInf']:
        PrintDebug('Warning:get_module_inf: "%s" not konwn module !'%(module_name) )
        return False
    module_inf       = G['ModuleInf'][module_name]
    module_path      = module_inf['file_path']
    cur_inf_time     = G['FileInf'][module_path]['file_edit_inf']['last_modify_time']
    last_modify_time = os.path.getmtime(module_path)
    # cur module file not modify, then inf valid and return
    if cur_inf_time == last_modify_time:
        return module_inf
    # if cur module file modify , update Module_inf and return new
    updata_file_pickle_inf(module_path)
    return get_module_inf(module_name)

#----------------------------------------------------
def get_line_inf_from_cur_file_inf(line_num, file_inf):
    line_module_inf   = {}
    line_call_sub_inf = {}
    module_infs  = file_inf['module_infs' ] #[ module_inf ]
    module_calls = file_inf['module_calls'] #[ call_sub_inf ]
    # first get current line module inf
    for m_inf in module_infs:
        c_module_range = m_inf['line_range_in_file']
        if c_module_range[1] < line_num:
            continue
        if line_num < c_module_range[0]:
            break
        line_module_inf = m_inf
    # second get current line call sub inf
    for c_inf in module_calls:
        c_call_range = c_inf['match_range']
        if c_call_range[1] < line_num:
            continue
        if line_num < c_call_range[0]:
            break
        line_call_sub_inf = c_inf
    return  {
         'line_module_inf'   : line_module_inf
        ,'line_call_sub_inf' : line_call_sub_inf
    }

def get_file_line_inf(line_num, path = ''):
    if not path:
        path = vim.current.buffer.name
    if get_file_path_postfix(path) not in G['SupportVerilogPostfix']:
        PrintDebug('Warning: get_file_line_inf: file not verilog file ! file: %s'%(path))
        return False
    if path not in G['FileInf']:
        updata_file_pickle_inf(path)
        if path not in G['FileInf']:
            PrintDebug('Warning: get_file_line_inf: %s has no file database !'%(path) )
            return False
    cur_inf_time     = G['FileInf'][path]['file_edit_inf']['last_modify_time']
    last_modify_time = os.path.getmtime(path)
    # cur module file not modify, then inf valid and return
    if cur_inf_time == last_modify_time:
        return get_line_inf_from_cur_file_inf( line_num, G['FileInf'][path] )
    # if cur module file modify , update Module_inf and return new
    updata_file_pickle_inf(path)
    return get_file_line_inf(line_num, path)

#----------------------------------------------
# { module_name:'', 'call_inf':atom}

def get_module_last_call_inf(module_name):
    if module_name not in G['ModuleLastCallInf']:
        return False
    upper_module_name = G['ModuleLastCallInf'][module_name]['upper_module_name']
    upper_inst_name   = G['ModuleLastCallInf'][module_name]['upper_inst_name']
    if upper_module_name not in G['ModuleInf']:
        return False
    if upper_inst_name not in G['ModuleInf'][upper_module_name]['sub_calls']:
        return False
    upper_call_inf = G['ModuleInf'][upper_module_name]['sub_calls'][upper_inst_name]
    return {'upper_module_name': upper_module_name, 'upper_call_inf': upper_call_inf}

def set_module_last_call_inf(sub_module_name, upper_module_name, upper_inst_name):
    G['ModuleLastCallInf'][sub_module_name] = { 'upper_module_name': upper_module_name, 'upper_inst_name': upper_inst_name }


#----------------------------------------------
# for a module's submodule, sep function module and base module
def get_sub_func_base_module(module_name):
    func_modules   = {} # inst:module
    base_modules   = {} # module : [inst0,inst1...]
    sub_modules    = []
    if module_name in G['ModuleInf']:
        sub_modules = G['ModuleInf'][module_name]['sub_modules']
    for sm in sub_modules:
        inst_name   =  sm['inst_name']
        module_name =  sm['module_name']
        if module_name in G['BaseModuleInf']['BaseModules']:
            base_modules.setdefault(module_name,[])
            base_modules[module_name].append(inst_name)
        else:
            if inst_name in func_modules: # has to same inst name, may be use `ifdefine sep
                new_inst_name = inst_name+'_'+str(sm['match_range'][0])
                func_modules[new_inst_name] = sm
                continue
            func_modules[inst_name] = sm
    return func_modules, base_modules

#----------------------------------------------
# update function base information, no need added each times
def update_base_module_pickle():
    pkl_output = open(G['VTagsPath'] + '/base_modules.pkl','wb')
    pickle.dump(G['BaseModuleInf']['BaseModules'], pkl_output)
    pkl_output.close()

#----------------------------------------------
# topo/checkpoint/basemodule line range ,in frame file
def get_frame_range_inf():
    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['type'] == 'topo'):
            if not has_topo:
                topo_range[0] = i
                has_topo      = True
            topo_range[1] = i
        if link and (link['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['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 }


#----------------------------------------------------
def get_submodule_match_patten(all_module_name):
    patten_char_set_list = []
    len_2_modules = {}
    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 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

#----------------------------------------------------
def get_single_verilog_file_module_inf(f):
    all_module_start_end_lines = os.popen('egrep -n -h \'^\s*(module|endmodule)\>\' %s'%(f)).readlines()
    cur_file_module_inf = []
    has_module_not_end  = False
    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<name>(|`)\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_pos            = ( module_start_line_num, cur_start_end_line_code.find(module_name) )
            # if pre module not end, set new module start pre line as pre module end line
            if has_module_not_end:
                PrintDebug('Error: module:"%s" in file:"%s", no "endmodule" !'%(cur_file_module_inf[-1]['module_name'],f) )
                cur_file_module_inf[-1]['line_range_in_file'][1] = module_start_line_num - 1
            cur_file_module_inf.append(
                {  'module_name'        : module_name
                  ,'file_path'          : f
                  ,'line_range_in_file' : [module_start_line_num, -1]
                  ,'sub_modules'        : None  # []
                  ,'module_pos'         : module_pos
                }
            )
            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) )
                continue
            module_end_line_num = cur_start_end_line_num
            cur_file_module_inf[-1]['line_range_in_file'][1] = module_end_line_num
            has_module_not_end  = False
            i += 1
            continue
        i += 1
    if has_module_not_end:
        PrintDebug( 'Error: module:"%s" in file:"%s", no "endmodule" !'%(cur_file_module_inf[-1]['module_name'],f) )
    return cur_file_module_inf


# for current file line match all patten: `define xxx ....
# patten_result = {
#     "name" : xxx
#    ,"path" : f
#    ,"pos"  : (line_num, colum_num)  # name first char pos
#    ,'code_line' : `define xxx ....
# }
# finial return [ patten_result0, patten_result1 ]
def get_single_verilog_file_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<name>\w+)',code_line)
        name        = ''
        colum_num   = -1
        if match_name:
            name        = match_name.group('name')
            colum_num   = code_line.find(name)
        if colum_num != -1:
            global_define_inf.append(
                { "name" : name
                 ,"path" : f
                 ,"pos"  : (line_num, colum_num)
                 ,'code_line' : code_line }
            )
    return global_define_inf

def get_single_line_sub_call_inf(egrep_line, all_module_names):
    sp     = egrep_line.split(':')
    l_num  = int(sp[0]) - 1
    l_code = re.sub('//.*', '', ':'.join(sp[1:]))
    if l_code.find(';') == -1:
        return False
    # match_name = re.match('\s*(?P<m_n>(|`)\w+)\s*(?P<other>.*;)\s*$',l_code)
    match_name = re.match('\s*(?P<m_n>(|`)\w+)\s*(?P<other>.*;)',l_code)
    assert(match_name),'%s,%s'%(egrep_line, l_code)
    module_name = match_name.group('m_n')
    if module_name not in all_module_names:
        return False
    other  =  match_name.group('other')
    inst_name_patten = '\w+((\s*\[[^\[\]]*\])|)'
    search_i_n = re.search('(^(?P<i_n0>%s))|(#.*\)\s*(?P<i_n1>%s)\s*\()'%(inst_name_patten,inst_name_patten),other)
    if not search_i_n:
        PrintDebug( 'Warning: match module name %s, but no inst name !' )
        return False
    inst_name = search_i_n.group('i_n0')
    if not inst_name:
        inst_name = search_i_n.group('i_n1')
    assert(inst_name)
    return {
         'module_name': module_name
        ,'inst_name'  : inst_name
        ,'match_range': [l_num, l_num]
        ,'match_pos'  : [l_num, l_code.find(module_name)]
    }

def get_mult_line_sub_call_inf(egrep_line, f_lines, all_module_names):
    sp        = egrep_line.split(':')
    l_num     = int(sp[0]) - 1
    code_line = re.sub('//.*', '', ':'.join(sp[1:]))
    # get module name
    module_name = ''
    module_pos  = (-1,-1)
    match_name  = re.match('^\s*(?P<m_n>(|`)\w+)\s*(?P<other>.*)',code_line)
    assert(match_name)
    module_name = match_name.group('m_n')
    if module_name not in all_module_names:
        return False
    module_pos = (l_num, code_line.find(module_name))
    # get inst name
    inst_name = ''
    inst_name_patten = '\w+((\s*\[[^\[\]]*\])|)'
    other  =  match_name.group('other')
    i = l_num + 1
    max_i = len(f_lines)
    line_end = False
    while not other and i < max_i and not line_end:
        next_code_line_with_semi = re.sub('((^\s+)|(^\s*`.*)|(//.*))','',f_lines[i].strip('\n'))
        next_code_line_no_semi   = re.sub(';.*','',next_code_line_with_semi)
        other = next_code_line_no_semi
        if len(next_code_line_no_semi) != len(next_code_line_with_semi):
            line_end = True
        i += 1
    match_inst_name_no_parm = re.match('^(?P<i_n>%s)'%(inst_name_patten),other)
    if match_inst_name_no_parm:
        inst_name = match_inst_name_no_parm.group('i_n')
    elif other[0] != '#':
        PrintDebug('Warning: un recgnize 0 module match ! %s ||| %s'%(egrep_line, other))
        return False
    # del has parm inst_name
    search_inst_name_has_parm = re.search('#\s*\(.*\)\s*(?P<i_n>%s)\s*\('%(inst_name_patten),other)
    if search_inst_name_has_parm:
        inst_name = search_inst_name_has_parm.group('i_n')
    while i < max_i and not line_end:
        next_code_line_with_semi = re.sub('((^\s+)|(^\s*`.*)|(//.*))','',f_lines[i].strip('\n'))
        next_code_line_no_semi   = re.sub(';.*','',next_code_line_with_semi)
        other = other +' '+next_code_line_no_semi
        if len(next_code_line_no_semi) != len(next_code_line_with_semi):
            line_end = True
        i += 1
        search_inst_name_has_parm = re.search('#\s*\(.*\)\s*(?P<i_n>%s)\s*\('%(inst_name_patten),other)
        if search_inst_name_has_parm:
            inst_name = search_inst_name_has_parm.group('i_n')
            break
    if not inst_name:
        PrintDebug('Warning: un recgnize 1 module match ! %s ||| %d ||| %s '%(egrep_line, i, str(line_end) ) )
        return False
    # get cur call end line
    end_line_num = max_i
    while i < max_i and not line_end:
        next_code_line = re.sub('(^`.*)|(//.*)','',f_lines[i])
        if next_code_line.find(';') != -1:
            line_end = True
            break
        i += 1
    if not line_end:
        PrintDebug('Warning: cur sub call no end ";" ! %s'%(egrep_line))
    else:
        end_line_num = i
    # return result
    return {
         'module_name': module_name
        ,'inst_name'  : inst_name
        ,'match_range': [l_num, end_line_num]
        ,'match_pos'  : module_pos
    }

def get_single_verilog_file_subcall_inf(f, patten, all_module_names):
    egrep_match_lines  = os.popen('egrep -n -h \'^\s*(%s)\>\' %s'%(patten,f)).readlines()
    # start get sub call inf
    if not egrep_match_lines:
        return []
    file_sub_call_infs = []
    c0_cnt = 0
    c1_cnt = 0
    f_lines = open(f,'r').readlines()
    for egrep_l in egrep_match_lines:
        # case0: signal line call
        c0_rst = get_single_line_sub_call_inf(egrep_l, all_module_names)
        if c0_rst:
            c0_cnt += 1
            file_sub_call_infs.append(c0_rst)
            continue
        # case1: mult line call
        c1_rst = get_mult_line_sub_call_inf(egrep_l, f_lines, all_module_names)
        if c1_rst:
            c1_cnt += 1
            file_sub_call_infs.append(c1_rst)
            continue
    PrintDebug('subcall: one_line/mult_line = %d/%d'%( c0_cnt,c1_cnt) )
    return file_sub_call_infs


def add_single_verilog_file_submodule_inf_to_module_inf( file_module_inf, file_subcall_inf ):
    s_inf_i = 0
    for m_inf in file_module_inf:
        m_inf['sub_modules'] = []
        m_inf['sub_calls'] = {}
        m_range = m_inf['line_range_in_file']
        while s_inf_i < len(file_subcall_inf):
            s_range = file_subcall_inf[s_inf_i]['match_range']
            # cur subcall in cur module
            if m_range[0] <= s_range[0] and s_range[1] <= m_range[1]:
                m_inf['sub_modules'].append(file_subcall_inf[s_inf_i])
                m_inf['sub_calls'][file_subcall_inf[s_inf_i]['inst_name']] = file_subcall_inf[s_inf_i]
                s_inf_i += 1
                continue
            elif s_range[0] < m_range[0]:
                PrintDebug('Error: subcall %s not in valid module !'%(file_subcall_inf[s_inf_i].__str__()))
                s_inf_i += 1
            elif s_range[1] > m_range[1]:
                if s_range[0] < m_range[0]:
                    PrintDebug('Error: subcall %s cross two module !'%(file_subcall_inf[s_inf_i].__str__()))
                break
            else:
                assert(0)
    return

def get_single_verilog_file_code_inf(f):
    # gen cur module and define inf
    new_file_module_inf = get_single_verilog_file_module_inf(f)
    new_file_define_inf = get_single_verilog_file_define_inf(f)
    # gen new all_module_names, del old current file add new
    new_module_names    = set([ mi['module_name'] for mi in new_file_module_inf ])
    old_file_module_inf = G['FileInf'][f]['module_infs']
    old_module_names    = set([ mi['module_name'] for mi in old_file_module_inf ])
    all_module_name     = ( set(G['ModuleInf']) - old_module_names ) | new_module_names
    # get file sub call inf
    patten = get_submodule_match_patten(all_module_name)
    new_file_subcall_inf = get_single_verilog_file_subcall_inf(f, patten, all_module_name)
    # merge to file_inf
    add_single_verilog_file_submodule_inf_to_module_inf( new_file_module_inf, new_file_subcall_inf )
    new_file_inf = {
         'glb_defines'   : new_file_define_inf
        ,'module_infs'   : new_file_module_inf
        ,'module_calls'  : new_file_subcall_inf
        ,'file_edit_inf' : { 'create_time': os.path.getctime(f), 'last_modify_time': os.path.getmtime(f)}
    }
    return new_file_inf

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*((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)

"""
https://my.oschina.net/u/2520885
"""
#===============================================================================
# Copyright (C) 2016 by Jun Cao

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#===============================================================================

import sys
import re
import os
try:
    import vim
except: 
    pass
import GLB
G = GLB.G
from Base import*
import View
PrintReport = View.PrintReport

########################################################
def get_sub_io_signal_name_from_sub_call_line(call_line, y):
    word = get_full_word(call_line, y)
    # if | .xxx(xxxx)
    #  y |   ^
    #    |     ^    ^ // call_sub_assign_signal_str
    # cur_word is sub_io_signal_name
    if re.match('\w+\.', call_line[:y+1][::-1]):
        call_sub_assign_signal_str = re.sub('(^\w*)|(\.\w+\(.*)', '', call_line[y:])
        call_sub_signals = set( re.findall('\w+',call_sub_assign_signal_str) )
        sub_call_io      = word
        return { 'call_sub_signals': call_sub_signals
                ,'sub_call_io'     : word
                ,'sub_call_io_num' : None }
    # if | .xxx(xxxx)
    #  y |        ^
    #    |     ^    ^ // call_sub_assign_signal_str
    s0 = re.search('\(\s*(?P<sub_call_io>\w+)\.',call_line[:y+1][::-1])
    if s0:
        sub_call_io = s0.group('sub_call_io')[::-1]
        call_sub_assign_and_right = call_line[y-s0.span()[0]:]
        assert(call_sub_assign_and_right[0] == '(')
        call_sub_assign_signal_str = re.sub('\.\w+\s*\(.*', '', call_line[y:])
        call_sub_signals = set( re.findall('\w+',call_sub_assign_signal_str) )
        return { 'call_sub_signals': call_sub_signals
                ,'sub_call_io'     : sub_call_io
                ,'sub_call_io_num' : None }
    # if module_name #(parm) inst_name( a, b,     c)
    # if module_name         inst_name( a, b,     c)
    #                                      y
    # call_sub_signals                     set(b)
    # sub_call_io_num                      1
    # sub_call_io                          ''
    if word:
        s1 = re.search('\)\s*\w+\s*\(',call_line[:y+1])
        if not s1:
            s1 = re.match('\s*\w+\s*\w+\s*\(',call_line[:y+1])
        full_match_s1 = True
        if s1:
            pre_sub_call_signal_str = call_line[s1.span()[1]:y+1]
            pre_sub_call_signals    = pre_sub_call_signal_str.split(',')
            assert(pre_sub_call_signals)
            for sc in pre_sub_call_signals:
                if not re.match('\s*(\w+)|(\w+\s*\[[^\[\]]+\])\s*$',sc):
                    full_match_s1 = False
            if full_match_s1:
                return { 'call_sub_signals': set([word])
                        ,'sub_call_io'     : ''
                        ,'sub_call_io_num' : len(pre_sub_call_signals) - 1 }
    return None

# 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   = get_module_inf(module_name)
    if not module_inf:
        return False
    module_path  = module_inf['file_path']
    module_range = module_inf['line_range_in_file']
    if io_name: # get cur io inf
        io_inf       = {}
        io_lines     =  os.popen('sed -n \'%d,%dp\' %s | egrep -n -h \'^\s*(input|output)\>.*\<%s\>\''%(module_range[0]+1, module_range[1]+1, module_path, io_name)).readlines()
        if len(io_lines) == 0:
            PrintDebug('Error: module: %s \'s io: %s define not found !'%(module_name,io_name))
            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:
                PrintDebug('Error: module: %s \'s io: %s define multiple times !'%(module_name,io_name))
        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]
            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:
            PrintDebug('Warning: get_io_inf, io_name is parm name ,not a io !')
            return False
    else: # get module all io inf
        all_io_inf    = []
        cur_module_code_range = module_inf['line_range_in_file']
        all_io_lines  =  os.popen('sed -n \'%d,%dp\' %s | egrep -n -h \'^\s*(input|output)\>\''%(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

def get_module_call_sub_module_io_inf(call_line, io_pos, call_file_path):
    call_line_num  = io_pos[0]
    # if database has no this file return
    if call_file_path not in G['FileInf']:
        PrintDebug("Warning: get_module_call_sub_module_io_inf : cur file has not in hdltags database, file: %s !"%(call_file_path))
        return False
    file_line_inf     = get_file_line_inf(call_line_num, call_file_path)
    line_call_sub_inf = {}
    line_module_inf   = {}
    if file_line_inf:
        line_call_sub_inf = file_line_inf['line_call_sub_inf']
        line_module_inf   = file_line_inf['line_module_inf']
    # if cursor line not no sub call , return
    if not line_call_sub_inf:
        PrintDebug("Warning: get_module_call_sub_module_io_inf: cur line %d not on sub call ! "%(call_line_num))
        return False
    sub_call_signal_inf    = get_sub_io_signal_name_from_sub_call_line(call_line, io_pos[1]) # may be parm
    # call module name
    assert(line_module_inf),'is in sub call, must be valid mudule'
    call_module_name  = line_module_inf['module_name']
    # valid cursor on sub call
    sub_module_name   = line_call_sub_inf['module_name']
    sub_module_path   = ''
    sub_module_inf    = get_module_inf(sub_module_name)
    if sub_module_inf:
        sub_module_path  = sub_module_inf['file_path']
    # sub_match_pos means cursor call io signal in sub module io pos
    sub_io_inf        = {} 
    call_sub_signals  = set()
    if sub_call_signal_inf:
        call_sub_signals = sub_call_signal_inf['call_sub_signals']
        sub_call_io      = sub_call_signal_inf['sub_call_io']
        sub_call_io_num  = sub_call_signal_inf['sub_call_io_num']
        if sub_call_io:
            sub_io_inf    = get_io_inf(sub_module_name, sub_call_io)
        elif sub_call_io_num != None:
            all_io_inf    = get_io_inf(sub_module_name)
            assert(sub_call_io_num < len(all_io_inf))
            sub_io_inf    = all_io_inf[sub_call_io_num]
    sub_match_pos     = ()
    sub_io_type       = ''
    sub_io_line       = ''
    sub_io_name       = ''
    if sub_io_inf:
        sub_io_name   = sub_io_inf['name']
        sub_match_pos = sub_io_inf['name_pos']
        sub_io_type   = sub_io_inf['io_type']
        sub_io_line   = sub_io_inf['code_line']
    return {
         'sub_io_name'      : sub_io_name    
        ,'sub_module_name'  : sub_module_name
        ,'sub_module_path'  : sub_module_path
        ,'sub_match_pos'    : sub_match_pos  
        ,'sub_io_type'      : sub_io_type
        ,'sub_io_line'      : sub_io_line
        ,'sub_io_inf'       : sub_io_inf    
        ,'call_sub_signals' : call_sub_signals
        ,'call_sub_inf'     : line_call_sub_inf  
        ,'call_module_name' : call_module_name }

#########################function for trace##############################

# ok
def get_upper_module_call_io_inf(cur_module_name , cur_io_name):
    cur_module_last_call_inf = get_module_last_call_inf(cur_module_name)
    if not cur_module_last_call_inf:
        PrintDebug("Warning: get_upper_module_call_io_inf: module %s, not called before, no upper module !"%(cur_module_name))
        return False
    upper_module_name  = cur_module_last_call_inf['upper_module_name']
    upper_call_inf     = cur_module_last_call_inf['upper_call_inf']
    upper_module_inf   = get_module_inf(upper_module_name)
    assert(upper_module_inf),'upper module %s call %s before, upper should has inf in database !'%(upper_module_name, cur_module_name)
    upper_module_path  = upper_module_inf['file_path']
    # get upper call, match this signal pos
    upper_call_lines   = open(upper_module_path,'r').readlines()
    upper_call_pos     = upper_call_inf['match_pos'] # initial to call inst line
    upper_matched      = False
    for i in range( upper_call_inf['match_range'][0] , upper_call_inf['match_range'][1] + 1 ):
        f0 = upper_call_lines[i].find(cur_io_name)
        if f0 == -1:
            continue
        s0 = re.search('(?P<pre>^|\W)%s(\W|$)'%(cur_io_name) , re.sub('//.*','',upper_call_lines[i]))
        if s0:
            colum_num = s0.span()[0] + len(s0.group('pre'))
            upper_call_pos = (i, colum_num)
            upper_matched  = True 
            break
    assert(upper_matched),'upper called so should be match, cur_io_name:%s, %s '%(upper_call_inf['match_range'].__str__(), cur_io_name)
    upper_call_line   = upper_call_lines[upper_call_pos[0]]
    return {
         'module_name' : upper_module_name
        ,'call_pos'    : upper_call_pos
        ,'call_line'   : upper_call_line
        ,'module_path' : upper_module_path 
    }


def get_cur_appear_is_source_or_dest(key, code_lines, appear_pos):
    a_x, a_y = appear_pos
    appear_code_line = re.sub('(//.*)|(^\s*`.*)', '', code_lines[a_x] )
    # case 0 cur pos in note return not source and dest
    if len(appear_code_line) - 1 < a_y:
        return 'None'
    # case 1 is io
    if (appear_code_line.find('input') != -1) or (appear_code_line.find('output') != -1):
        match_io_type = re.match('\s*(?P<io_type>(input|output))\W',appear_code_line) # may input a,b,c
        match_io_name = re.match('\s*[;,]?\s*(?P<r_names>\w+(\s*,\s*\w+)*)',appear_code_line[::-1]) # may input a,b,c
        if match_io_type and match_io_name:
            io_type  = match_io_type.group('io_type')
            io_names = match_io_name.group('r_names')[::-1]
            io_names = set(re.split('\s*,\s*',io_names))
            if (io_type == 'input') and (key in io_names):
                return 'Source'
            if (io_type == 'output') and (key in io_names):
                return 'Dest'
        elif match_io_type:
            PrintDebug('Error: recgnize_signal_assign_line: unrecgnize io line: '+appear_code_line)
            return 'None'
    # case 2 cur pos in case/casez/for/if (...key...) then it's dest
    match_case2 = False
    c2 = re.search( '(^|\W)(case|casez|for|if|while)\s*\(' , appear_code_line)
    if c2:
        appear_code_right_line = appear_code_line[c2.span()[1]:]
        unmatch_bracket_count = 1
        end_match_patten      = '^'
        end_y                 = len(appear_code_right_line) - 1
        all_brackets = re.findall('\(|\)', appear_code_right_line)
        for b in all_brackets:
            if b == '(':
                unmatch_bracket_count += 1
            else:
                unmatch_bracket_count -= 1
            end_match_patten = end_match_patten + '[^()]*\\'+b
            if unmatch_bracket_count == 0:
                end_y = re.search(end_match_patten, appear_code_right_line).span()[1] - 1
                break
        end_y = c2.span()[1] + end_y
        if end_y >= a_y:
            return 'Dest'
        else:
            # if key not in (...), then use ) right str as real appear_code_line
            match_case2 = True
            appear_code_line = appear_code_line[end_y + 1:]
    # case 3 cur line has = at left or right
    assign_patten = '([^=>!]=[^=<>])'
    # ... =|<= ... key : is dest
    if re.search(assign_patten, appear_code_line[:a_y + 1]):
        return 'Dest'
    # key ... =|<= ... : is source
    if re.search(assign_patten, appear_code_line[a_y:]):
        return 'Source'
    # case 4 if not match case2(if match no pre line) post full line sep by ";" has =|<=, it's dest
    if not match_case2:
        pre_full_line = get_verilog_pre_full_line(code_lines, appear_pos)
        if re.search(assign_patten, pre_full_line):
            return 'Dest'
    # case 5 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[:a_y + 1]):
        return 'Source'
    # case 6 unrecgnize treat as maybe dest/source
    return 'Maybe'


# ok
def clear_last_trace_inf( trace_type ):
    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']['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']['Path']         = ''

# #-------------------trace_io_signal---------------------------
# del get_cur_module_inf
def real_trace_io_signal(trace_type, cursor_inf, io_signal_inf):
    assert(trace_type in ['dest', 'source']),'only trace dest/source'
    # verilog
    if (trace_type is 'dest') and (io_signal_inf['io_type'] != 'output'):
        PrintDebug('Warning: 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('Warning: 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   = cursor_inf['cur_module_inf']
    if not cur_module_inf:
        PrintDebug('Warning: cur file not in database, will not go upper ! file: %s'(cursor_inf['file_path']))
        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'])
    if not upper_module_call_inf:
        PrintReport('Warning: no upper module call this module before !')
        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['module_name']
    upper_call_pos    = upper_module_call_inf['call_pos']
    upper_call_line   = upper_module_call_inf['call_line']
    upper_module_path = upper_module_call_inf['module_path']
    show_str          = '%s %d : %s'%(upper_module_name, upper_call_pos[0]+1, upper_call_line)
    file_link         = {'key':io_signal_inf['name'], 'pos': upper_call_pos, 'path': upper_module_path}
    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']['Path']       = cursor_inf['file_path']
    else :
        G['TraceInf']['LastTraceSource']['Sure'].append(trace_result)
        G['TraceInf']['LastTraceSource']['SignalName'] = cursor_inf['word']
        G['TraceInf']['LastTraceSource']['Path']       = cursor_inf['file_path']
    # show dest/source to report win, and go first trace
    PrintReport(spec_case = trace_type)
    View.show_next_trace_result(trace_type)
    return True

# ok
def trace_io_signal(trace_type, cursor_inf):
    trace_signal_name  = cursor_inf['word']
    io_signal_infs     = recgnize_io_signal_line(cursor_inf['line'], cursor_inf['line_num'])
    if not io_signal_infs:
        PrintDebug('Warning: trace_io_signal: not io signal')
        return False # not io signal
    # if trace_signal_name != io_signal_inf['name']:
    if trace_signal_name not in io_signal_infs:
        PrintDebug('Warning: trace_io_signal: is io signal but not traced signal')
        return False # is io signal but not traced signal
    if trace_type in ['source','dest']:
        return real_trace_io_signal(trace_type, cursor_inf, io_signal_infs[trace_signal_name])
    assert(0),'unkonw tarce type %s' %(trace_type)

#-------------------------------------------------------------
# ok
def real_trace_module_call_io_signal(trace_type, sub_call_inf, cursor_inf):
    assert(trace_type in ['source', 'dest'])
    if trace_type == 'source' and sub_call_inf['sub_io_type'] != 'output':
        return False # submodule not source, just pass
    elif trace_type == 'dest' and sub_call_inf['sub_io_type'] != 'input':
        return False # submodule not source, just pass
    # has sub module and in submodule signal is out, then it's source
    sub_module_name         = sub_call_inf['sub_module_name']
    sub_module_path         = sub_call_inf['sub_module_path']
    sub_module_match_pos    = sub_call_inf['sub_match_pos']
    sub_module_match_line   = sub_call_inf['sub_io_line']
    sub_module_signal_name  = sub_call_inf['sub_io_name']
    show_str     = '%s %d : %s'%(sub_module_name, sub_module_match_pos[0]+1, sub_module_match_line)
    file_link    = {'key':sub_module_signal_name, 'pos': sub_module_match_pos, 'path': sub_module_path}
    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']['Path']       = cursor_inf['file_path']
    else: # dest
        G['TraceInf']['LastTraceDest']['Sure'].append(trace_result)
        G['TraceInf']['LastTraceDest']['SignalName'] = cursor_inf['word']
        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  = sub_call_inf['call_module_name']
    call_sub_inf     = sub_call_inf['call_sub_inf']
    set_module_last_call_inf(sub_module_name, cur_module_name, call_sub_inf['inst_name'])
    # show source to report win, and go first trace
    PrintReport(spec_case = trace_type)
    View.show_next_trace_result(trace_type)
    return True

# ok
# del is_module_call_range
def trace_module_call_io_signal(trace_type, cursor_inf):
    sub_call_inf = get_module_call_sub_module_io_inf(cursor_inf['line'], cursor_inf['pos'], cursor_inf['file_path'])
    if not sub_call_inf:
        PrintDebug('Warning: trace_module_call_io_signal: not in module call io')
        return False # not in module call io
    if sub_call_inf['sub_module_name'] == cursor_inf['word']:
        PrintReport('Warning: trace key is a submodule call, module name , no source !')
        return True
    if not sub_call_inf['sub_io_name']:
        PrintDebug('Warning: trace_module_call_io_signal: is module call ,but unrecgnize io name !')
        return False
    clear_last_trace_inf( trace_type )
    return real_trace_module_call_io_signal(trace_type, sub_call_inf, cursor_inf)


# #---------------------------------------------------------------------
def real_trace_normal_signal(trace_type, signal_appear_pos_line, cursor_inf):
    assert(trace_type in ['source', 'dest'])
    clear_last_trace_inf(trace_type)
    if trace_type == 'source':
        G['TraceInf']['LastTraceSource']['SignalName'] = cursor_inf['word']
        G['TraceInf']['LastTraceSource']['Path']       = cursor_inf['file_path']
    else: 
        G['TraceInf']['LastTraceDest']['SignalName'] = cursor_inf['word']
        G['TraceInf']['LastTraceDest']['Path']       = cursor_inf['file_path']
    trace_signal_name = cursor_inf['word']
    cur_module_inf    = cursor_inf['cur_module_inf'] # already qualify
    cur_module_name   = cur_module_inf['module_name']
    cur_module_path   = cur_module_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 = get_cur_appear_is_source_or_dest(trace_signal_name, [signal_appear_line], (0,appear_pos[1]) )
            if dest_or_source != source:
                continue
            input_is_only_source = True
            show_str = '%s %d : %s'%(cur_module_name, appear_pos[0]+1, appear_line)
            file_link = {'key':trace_signal_name, 'pos': appear_pos, 'path': cur_module_path}
            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
        # module call assign range
        sub_call_inf = get_module_call_sub_module_io_inf(appear_line, appear_pos, cur_module_path)
        if sub_call_inf:
            if trace_signal_name in sub_call_inf['call_sub_signals']:
                # cur is subcall but not io name not match trace name go next
                if not sub_call_inf['sub_io_type']:
                    appear_dest_or_source = True
                elif sub_call_inf['sub_io_type'] == 'output':
                    appear_is_source      = True
                elif sub_call_inf['sub_io_type'] == 'input':
                    appear_is_dest        = True
            else:
                PrintDebug('Warning: subcall match on sub io name, not on assign name ! %s,%s'%(appear_pos.__str__(), appear_line))
                continue
        else:
            # not module call then check if a assign signal
            dest_or_source = get_cur_appear_is_source_or_dest(trace_signal_name, cursor_inf['codes'], appear_pos)
            if dest_or_source == 'Dest':
                appear_is_dest = True
            elif dest_or_source == 'Source':
                appear_is_source = True
            elif dest_or_source == 'Maybe':
                appear_dest_or_source = True
            else:
                PrintDebug('Warning: match not source or dest ! %s : %s'%(appear_pos.__str__(), appear_line))
        # finial add to source/dest
        show_str = '%s %d : %s'%(cur_module_name, appear_pos[0]+1, appear_line)
        file_link = {'key':trace_signal_name, 'pos': appear_pos, 'path': cur_module_path}
        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("Warning: 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("Warning: Not find signal dest !")
            return True
    # show source to report win, and go first trace
    PrintReport(spec_case = trace_type)
    View.show_next_trace_result(trace_type)
    return True

def trace_normal_signal(trace_type, cursor_inf):
    cur_module_inf    = cursor_inf['cur_module_inf']
    if not cur_module_inf:
        PrintDebug('Warning: 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['line_range_in_file'] )
    return real_trace_normal_signal(trace_type, signal_appear_pos_line, cursor_inf)

#----------------------------------------------------
def trace_glb_define_signal(trace_type, cursor_inf):
    assert(trace_type in ['dest', 'source'])
    cur_line  = cursor_inf['line']
    cur_word  = cursor_inf['word']
    if cur_line.find('`') == -1:
        return False
    s0        = re.search('(?P<prefix>^|\W)%s(\W|$)'%(cur_word),cur_line)
    if not s0:
        return False
    if s0.group('prefix') != '`':
        return False
    if cur_word not in G['CodeDefineInf']:
        PrintReport('Warning: cur macro: \"%s\", not has find in database !'%(cur_word))
        return True
    cur_define_infs = G['CodeDefineInf'][cur_word]
    clear_last_trace_inf(trace_type)
    for inf in cur_define_infs: # {name path pos code_line}
        file_name = re.sub('.*/','',inf['path'])
        show_str = '%s %d : %s'%(file_name, inf['pos'][0]+1, inf['code_line'])
        file_link = {'key':cur_word, 'pos': inf['pos'], 'path': inf['path']}
        trace_result = {'show': show_str, 'file_link': file_link}
        if trace_type == 'source':
            G['TraceInf']['LastTraceSource']['SignalName'] = cursor_inf['word']
            G['TraceInf']['LastTraceSource']['Path']       = cursor_inf['file_path']
            G['TraceInf']['LastTraceSource']['Sure'].append(trace_result)
        else: # dest
            PrintReport('Warning: cur not support trace macro dest !')
            return True
    # show source to report win, and go first trace
    PrintReport(spec_case = trace_type)
    View.show_next_trace_result(trace_type)
    return True

"""
https://my.oschina.net/u/2520885
"""
#===============================================================================
# Copyright (C) 2016 by Jun Cao

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#===============================================================================

import sys
sys.path.append('../')
import vim_glb_config as glb_config

import os
import re
import pickle

vim_start_open_file = ''
try:
    import vim
    vim_start_open_file = vim.current.buffer.name
except:
    vim_start_open_file = '|vim_not_open|'
    pass


# cur call path
cur_path = os.getcwd()

# find most resent vtags path
hdl_tags_path = ''
while cur_path and cur_path[0] == '/':
    if os.path.isdir(cur_path + '/vtags.db'):
        hdl_tags_path = cur_path + '/vtags.db'
        break
    cur_path = re.sub('/[^/]*$','',cur_path)

# get local config
config        = glb_config
try:
    if hdl_tags_path:
        sys.path.append(hdl_tags_path)
        import vim_local_config as local_config
        config = local_config
except:
    pass


# get next empty frame, report,log report index, first try del Frame, Report
def del_old_logs():
    ls_a_f = [ f.strip('\n') for f in os.popen('ls -a ' + hdl_tags_path).readlines() ]
    used_log_index = set()
    for f in ls_a_f:
        match_swp = re.match('\.(Frame\.HF|Report\.HF|run\.log)(?P<idx>\d+)\.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 ' + hdl_tags_path).readlines() ]
    for f in ls_f:
        match_idx = re.match('(Frame\.HF|Report\.HF|run\.log)(?P<idx>\d+)', 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'%(hdl_tags_path,f) )
    return

empty_log_index = 0
if hdl_tags_path:
    del_old_logs()
    while os.path.isfile(hdl_tags_path + '/run.log'   + str(empty_log_index)) or \
          os.path.isfile(hdl_tags_path + '/Frame.HF'  + str(empty_log_index)) or \
          os.path.isfile(hdl_tags_path + '/Report.HF' + str(empty_log_index)):
        empty_log_index += 1

# if in generate vtags situation, print log to vtags.db/vtags_run.log
vtags_run_log_path = ['']
# run log path
run_log_path = hdl_tags_path + '/run.log'+str(empty_log_index)
def PrintDebug( str, out_path = ''):
    if vtags_run_log_path[0]:
        output = open( vtags_run_log_path[0], 'a')
        output.write(str+'\n')
        output.close()
        return
    if not config.debug_mode:
        return
    if out_path:
        output = open( out_path, 'a')
        output.write(str+'\n')
        output.close()
        return
    if hdl_tags_path:
        output = open( run_log_path ,'a')
        output.write(str+'\n')
        output.close()

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

HDLTagsActive = True
# if cur open a valid file, and file not verilog file not act vtags
if vim_start_open_file \
   and (get_file_path_postfix(vim_start_open_file) not in config.support_verilog_postfix):
    HDLTagsActive = False

# get file inf
FileInf       = {}
try:
    if hdl_tags_path and HDLTagsActive:
        import files_inf
        HDLTagsActive = files_inf.HDLTagsActive
        FileInf       = files_inf.FileInf
    else:
        HDLTagsActive = False
except:
    HDLTagsActive = False

BaseModules   = set()
if HDLTagsActive:
    # get base module inf
    try:
        pkl_input     = open(hdl_tags_path + '/base_modules.pkl','rb')
        BaseModules   = pickle.load(pkl_input)
        pkl_input.close()
    except:
        pass

# function -------------------------------------------------
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(hdl_tags_path + '/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

# structure -----------------------------------------------------

# frame file_link = { 
#                      'type'     : '', topo                      | check_point          | base_module
#                     'key'      : '', topo module name          | add check point word | base module name
#                     'pos'      : '', module def pos            | add pos              | module pos
#                     'path'     : '', module def file path      | add file path        | module def file path
#                     'fold_inf' :                              {}, 'fold_status': on/off/fix     
#                                                                 , 'level'     : n           
#                                    
#                   }
Frame_Inf = {
     "Frame_Win_x"        : config.frame_window_width      # frame window width
    ,"Frame_Path"         : ''
    ,"FoldLevelSpace"     : config.frame_fold_level_space
}
Frame_Inf['Frame_Path'] = hdl_tags_path + '/' + "Frame.HF" + str(empty_log_index)


# report file_link = {
#                      'key'   :  '' ,  signal_name
#                     'pos'   :  '' ,  match_pos
#                     'path'  :  '' ,  match_path
# }
Report_Inf = {
     "Report_Win_y"       : config.report_window_height        # report window height
    ,"Report_Path"        : hdl_tags_path + '/' + "Report.HF"
}
Report_Inf['Report_Path'] = hdl_tags_path + '/' + "Report.HF" + str(empty_log_index)


WorkWin_Inf ={
     "MaxNum"       : config.max_open_work_window_number
    ,"OpenWinTrace" : []
}

# all vim buffer line file link { path:[...]}
VimBufferLineFileLink = {}

TraceInf = {
     'LastTraceSource' : {'Maybe':[], 'Sure':[], 'ShowIndex': 0, 'SignalName':'', 'Path':'' } # Maybe[{'show':'', 'file_link':{ 'key':'','pos':(l,c),'path':'' } }] 
    ,'LastTraceDest'   : {'Maybe':[], 'Sure':[], 'ShowIndex': 0, 'SignalName':'', '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
}

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
}

G = {
     'HDLTagsActive'         : HDLTagsActive
    ,'SupportVHDLPostfix'    : set([])
    ,'SupportVerilogPostfix' : set(config.support_verilog_postfix)
    ,'ModuleInf'             : {}
    ,'ModuleLastCallInf'     : {}           # {module_name:{ upper_module_name:'', 'upper_inst_name':inst_name} }
    ,'FileInf'               : FileInf
    ,'CodeDefineInf'         : {}           # {name: [ {name path pos code_line} ]}
    ,'OpTraceInf'            : OpTraceInf
    ,"Debug"                 : config.debug_mode    # debug mode
    ,"ShowReport"            : config.show_report
    ,"ShowFrame"             : config.show_sidebar
    ,"PrintDebug_F"          : PrintDebug   # 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 flod has a else space, not do space op
    ,"EnvSnapshortWinsInf"   : []
    ,"SaveEnvSnapshort_F"    : save_env_snapshort
    ,"VTagsPath"             : hdl_tags_path
}



# has save history sence then just repaly it
start_with_empty_file = False
if not vim_start_open_file :
    start_with_empty_file = True

EnvSnapshort  = {}
if HDLTagsActive and start_with_empty_file and os.path.isfile(hdl_tags_path + '/env_snapshort.pkl'):
    pkl_input       = open(hdl_tags_path + '/env_snapshort.pkl','rb')
    c_snapshort     = pickle.load(pkl_input)
    if c_snapshort['snapshort_dir_path'] == os.getcwd():
        os.system('echo \'do you want reload vim snapshort ? (y/n): \'')
        yes_or_no = raw_input()
        if yes_or_no.lower() in ['y','yes']:
            EnvSnapshort  = c_snapshort
    pkl_input.close()

if EnvSnapshort:
    reload_env_snapshort(EnvSnapshort)

"""
https://my.oschina.net/u/2520885
"""
#===============================================================================
# Copyright (C) 2016 by Jun Cao

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#===============================================================================

import sys
import re
try:
    import vim
except: 
    pass
import os
import re
from Base import *
from Win import *
import GLB
G = GLB.G

#--------------------------------------
SnapshotStack = []

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_num      = cur_pos[0] 
    cur_line          = vim.current.buffer[cur_line_num]
    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)

def snapshort_pop():
    pop_snapshort = SnapshotStack[-1]
    del SnapshotStack[-1]
    go_win( pop_snapshort['path'], pop_snapshort['pos'], pop_snapshort['key'])
#--------------------------------------

def Show(path): # just show frame win , and not go to that window
    path    = get_path_for_name(path)
    Act_Win = Cur_Act_Win()
    if path not in Act_Win:
        snapshort_push()
        Open(path)
        snapshort_pop()
    return

#--------------------------------------

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'] ]:
        PrintDebug('Warning: Frame and Report not add trace point !')
        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)


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)
    cur_call_sub_inf = {}
    cur_module_inf   = {}
    cur_line_inf     = get_file_line_inf(cur_line_num, cur_file_path)
    if cur_line_inf:
        cur_call_sub_inf = cur_line_inf['line_call_sub_inf']
        cur_module_inf   = cur_line_inf['line_module_inf']
    cur_module_name  = ''
    if cur_module_inf: 
        cur_module_name = cur_module_inf['module_name']
    else:
        PrintDebug('Warning: get_cur_cursor_inf: current cursor %s not in module, file: %s ! '%(cur_cursor.__str__(), 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
             ,'cur_call_sub_inf' : cur_call_sub_inf
             ,'cur_module_inf'   : cur_module_inf
             ,'codes'            : cur_codes }

# ok
# report file_link = {
#                      'key'   :  '' ,  signal_name
#                     'pos'   :  '' ,  match_pos
#                     'path'  :  '' ,  match_path
# }
#----for python edition 2.7 + 
# def PrintReport(*show, file_link = {}, spec_case = '', mode = 'a'):
#     # normal show a string 
#     show_str = ' '.join([ i.__str__() for i in show ])
#----for python edition 2.6
def PrintReport(show = '', file_link = {}, spec_case = '', mode = 'a'):
    if not G['ShowReport']:
        return
    has_self_snap_short = False
    if not cur_in_report():
        snapshort_push()
        Open('Report')
        has_self_snap_short = True
    show_str = show
    if show_str: 
        edit_vim_buffer('Report', [show_str], file_links = [file_link], mode = mode)
    # show trace source result
    if spec_case == 'source':
        edit_vim_buffer('Report', "---------------------------source--------------------------------")
        t_data      = []
        t_file_link = []
        for Sure in G['TraceInf']['LastTraceSource']['Sure']:
            t_data.append( Sure['show'] )
            t_file_link.append( Sure['file_link'] )
        edit_vim_buffer('Report', t_data, t_file_link)
        edit_vim_buffer('Report', "------------------------maybe source-----------------------------")
        t_data      = []
        t_file_link = []
        for Maybe in G['TraceInf']['LastTraceSource']['Maybe']:
            t_data.append( Maybe['show'] )
            t_file_link.append( Maybe['file_link'] )
        edit_vim_buffer('Report', t_data, t_file_link)
        edit_vim_buffer('Report', "----------------------------END----------------------------------")
        edit_vim_buffer('Report', "")
    # show trace dest result
    if spec_case == 'dest':
        edit_vim_buffer('Report', "---------------------------dest--------------------------------")
        t_data      = []
        t_file_link = []
        for Sure in G['TraceInf']['LastTraceDest']['Sure']:
            t_data.append( Sure['show'] )
            t_file_link.append( Sure['file_link'] )
        edit_vim_buffer('Report', t_data, t_file_link)
        edit_vim_buffer('Report', "------------------------maybe dest-----------------------------")
        t_data      = []
        t_file_link = []
        for Maybe in G['TraceInf']['LastTraceDest']['Maybe']:
            t_data.append( Maybe['show'] )
            t_file_link.append( Maybe['file_link'] )
        edit_vim_buffer('Report', t_data, t_file_link)
        edit_vim_buffer('Report', "----------------------------END----------------------------------")
        edit_vim_buffer('Report', "")
    # go report to the last line, and return
    assert(cur_in_report())
    # if mode == 'a':
    vim.current.window.cursor = (len(vim.current.buffer) - 1 , 0)
    vim.command('w!')
    if has_self_snap_short:
        snapshort_pop()

# ok
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('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()
        go_win( cur_file_link['path'], cur_file_link['pos'], cur_file_link['key'] )
    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('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()
        go_win( cur_file_link['path'], cur_file_link['pos'], cur_file_link['key'])
    else:
        assert(0)

#--------------------------------------------------------------------------
def gen_top_topo_data_link(topo_module):
    topo_datas   = []
    topo_links   = []
    topo_module_inf = 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 = {
         'type'           : 'topo'
        ,'topo_inst_name' : ''
        ,'key'            : ''
        ,'pos'            : ''
        ,'path'           : ''
        ,'fold_inf'       : {'fold_status':'on', 'level': TopTopoLevel - 1 }
    }
    topo_links.append(topo_link)
    # add cur module name
    topo_datas.append(TopTopoPrefix + topo_module + ':')
    topo_link = {
         'type'           : 'topo'
        ,'topo_inst_name' : ''
        ,'key'            : topo_module
        ,'pos'            : topo_module_inf['module_pos']
        ,'path'           : topo_module_inf['file_path']
        ,'fold_inf'       : {'fold_status':'on', 'level': TopTopoLevel}
    }
    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
    return topo_datas, topo_links


def edit_frame(data = [], file_links = [], mode = 'a', n = 0, del_range = ()):
    has_self_snap_short = False
    if not cur_in_frame():
        snapshort_push()
        Open('Frame')
        has_self_snap_short = True
    edit_vim_buffer( path_or_name = 'Frame', data = data, file_links = file_links, mode = mode, n = n, del_range = del_range)
    # go frame w! and go back
    assert(cur_in_frame())
    vim.command('w!')
    if has_self_snap_short:
        snapshort_pop()

#---------------------------------------
def show_base_module(fold = True):
    frame_data   = []
    frame_link   = []
    # if frame not show ,show it
    Show("Frame")
    # add initial line
    level        = G['BaseModuleInf']['TopFoldLevel']
    key          = G['Frame_Inf']['FoldLevelSpace']*level + 'BaseModules:'
    link         = {
         'type'     : 'base_module'
        ,'key'      : ''
        ,'pos'      : ''
        ,'path'     : ''
        ,'fold_inf' : { 'fold_status': 'on', 'level': level }
    }
    frame_data.append(key)
    frame_link.append(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_inf']['fold_status'] = 'off'
    frame_data = frame_data + cp_data
    frame_link = frame_link + cp_link
    # del old cp, add new cp
    if has_base_module: # del
        edit_frame(mode = 'del', del_range = base_module_range)
    edit_frame(data = frame_data, file_links = frame_link, mode = 'i', n = base_module_range[0])
    return True

#-------------------------------------
def show_check_point(fold = True):
    frame_data   = []
    frame_link   = []
    # if frame not show ,show it
    Show("Frame")
    # add initial line
    level        = G['CheckPointInf']['TopFoldLevel']
    key          = G['Frame_Inf']['FoldLevelSpace']*level + 'CheckPoints:'
    link         = {
         'type'     : 'check_point'
        ,'key'      : ''
        ,'pos'      : ''
        ,'path'     : ''
        ,'fold_inf' : { 'fold_status': 'on', 'level': level }
    }
    frame_data.append(key)
    frame_link.append(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']
    cp_data = []
    cp_link = []
    if fold:
        cp_data, cp_link  = get_fram_check_point_inf()
    else:
        frame_link[-1]['fold_inf']['fold_status'] = 'off'
    frame_data = frame_data + cp_data
    frame_link = frame_link + cp_link
    # del old cp, add new cp
    if has_check_point: # del
        edit_frame(mode = 'del', del_range = check_point_range)
    edit_frame(data = frame_data, file_links = frame_link, mode = 'i', n = check_point_range[0])
    return True

#---------------------------------------
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("Warning: Current only support verilog !")
            return False
        # get current module inf
        cur_module_inf  = cursor_inf['cur_module_inf']
        # current not at module lines, just return
        if not cur_module_inf:
            PrintReport("Warning: Current cursor not in valid module !")
            return False
        topo_module_name = cur_module_inf['module_name']
    else:
        if topo_module_name not in G['ModuleInf']:
            PrintReport("Warning: show topo module %s not have database !"%(topo_module_name))
            return False
    # if frame not show ,show it
    Show("Frame")
    # 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_frame(mode = 'del', del_range = topo_range)
    edit_frame(data = topo_data, file_links = topo_link, mode = 'i', n = topo_range[0])
    return True

def iteration_fold_no_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']
    c_topo_links = G['VimBufferLineFileLink'][frame_path][c_topo_range[0] : c_topo_range[1]]
    for i,lk in enumerate(c_topo_links):
        if not( lk and (lk['fold_inf']['fold_status'] == 'off') and lk['key'] ):
            continue
        if lk['topo_inst_name']:
            c_inst_module_pair = (lk['topo_inst_name'], lk['key'])
            if c_inst_module_pair in inst_module_pairs:
                fold_frame_line(lk, i+c_topo_range[0], lk['fold_inf']['level'], 'topo')
                iteration_fold_no_module(inst_module_pairs, base_modules)
                return
        else:
            if lk['key'] in base_modules:
                fold_frame_line(lk, i+c_topo_range[0], lk['fold_inf']['level'], 'topo')
                iteration_fold_no_module(inst_module_pairs, base_modules)
                return
    return

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']
    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['fold_inf']['fold_status'] == 'on') and lk['key'] ):
            continue
        if lk['topo_inst_name']:
            old_fold_inst_module_pairs.add( (lk['topo_inst_name'], lk['key']) )
        else:
            if lk['key'] in G['BaseModuleInf']['BaseModules']:
                old_fold_base_modules.add(lk['key'])
    # start new topo
    new_topo_module_name = G['TopoInf']['CurModule']
    show_topo(new_topo_module_name)
    # iteration opened old folded topo
    iteration_fold_no_module(old_fold_inst_module_pairs, old_fold_base_modules)

#---------------------------------------
def unfold_frame_line(frame_links, frame_line, cur_frame_level, cur_frame_type):
    assert(frame_links[frame_line]['fold_inf']['fold_status'] == 'on')
    G['VimBufferLineFileLink'][ G['Frame_Inf']['Frame_Path'] ][frame_line]['fold_inf']['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]['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]['fold_inf']['level'] <= cur_frame_level:
            unfold_end_line_num = i - 1
            break
    # 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_frame(mode = 'del', del_range = (frame_line + 1, unfold_end_line_num))
        return True
    # else some trouble
    assert(0),'shold not happen !'

def fold_frame_line(cur_line_link, frame_line, cur_frame_level, cur_frame_type):
    assert(cur_line_link['fold_inf']['fold_status'] == 'off')
    G['VimBufferLineFileLink'][ G['Frame_Inf']['Frame_Path'] ][frame_line]['fold_inf']['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
        # cur_line_link['key'] is the cur topo line module name
        cur_module_name = cur_line_link['key']
        if  not cur_module_name:
            PrintReport('Warning: cur topo line has no module name !')
            return 
        if cur_module_name not in G['ModuleInf']:
            PrintReport('Warning: cur module: \"%s\" has no database !'%(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_frame(data = sub_topo_data, file_links = sub_topo_link, mode = 'i', n = frame_line + 1)
    elif cur_frame_type == 'check_point':
        show_check_point()
    elif cur_frame_type == 'base_module':
        show_base_module()
    else:
        PrintReport('Warning: no operation in this line !')
    return

def frame_line_fold_operation(frame_line):
    frame_path     = G['Frame_Inf']['Frame_Path']
    frame_links    = G['VimBufferLineFileLink'][frame_path]
    cur_line_link  = frame_links[frame_line]
    if not cur_line_link :
        PrintReport('Warning: cur frame line no fold operation !')
        return
    cur_frame_type  = cur_line_link['type']
    cur_frame_level = cur_line_link['fold_inf']['level']
    cur_fold_state  = cur_line_link['fold_inf']['fold_status']
    if cur_fold_state == 'off':
        fold_frame_line(cur_line_link, frame_line, cur_frame_level, cur_frame_type)
    elif cur_fold_state == 'on':
        unfold_frame_line(frame_links, frame_line, cur_frame_level, cur_frame_type)
    else:
        PrintReport('Warning: cur frame line no fold operation !')
        return

"""
https://my.oschina.net/u/2520885
"""
#===============================================================================
# Copyright (C) 2016 by Jun Cao

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#===============================================================================

try:
    import vim
except: 
    pass
import sys
import re
import os
import GLB
G = GLB.G
from Base import *

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

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

def Cur_Act_Win():
    Act_Win = set()
    for w in vim.windows:
        Act_Win.add(w.buffer.name)
    return Act_Win

def Open(name):
    path = get_path_for_name(name)
    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()
    elif path == G['Report_Inf']["Report_Path"]:
        Open_Report_Win()
    else:
        Open_Work_Win(path)
    Reset_Win_Size()
    Jump_To_Win(path)
    assert(vim.current.buffer.name == path)

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)

def Open_Frame_Win():
    G['VimBufferLineFileLink'].setdefault(G["Frame_Inf"]["Frame_Path"],[{}])
    vim.command("vertical topleft sp " + G["Frame_Inf"]["Frame_Path"])

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"])

def Open_Work_Win(path):
    # 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')
        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])
            vim.command('vsp '+path)
        else: # case 1: opened all work win, just replace the oldest open work win
            Jump_To_Win(G['WorkWin_Inf']['OpenWinTrace'][0])
            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])
            vim.command('vsp '+path)
        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"])
            vim.command('sp '+path)
        else:
            vim.command('vsp '+path)
    # finial add path to trace
    assert(vim.current.buffer.name == path)
    G['WorkWin_Inf']['OpenWinTrace'].append(path)

def get_file_vim_buffer(path):
    for i,b in enumerate(vim.buffers):
        if b.name == path:
            return b
    # path buffer not open
    vim.command("bad "+path)
    assert(vim.buffers[ len(vim.buffers) - 1].name == path)
    return vim.buffers[ len(vim.buffers) - 1]

# edit the vim buffer, with put data at some position
# mode : a --after     : append data after current buffer data
#        b --before    : append data before current buffer data
#        w --write     : clear old buffer data, and write data
#        i --insert    : inster list to n
#        del -- delete : delete del_range lines
#        del_range     : (start,end) del range, include end 
def edit_vim_buffer(path_or_name = '', data = [], file_links = [], mode = 'a', n = 0, del_range = ()):
    # weather to edit link buffer
    need_edit_buffer_file_link = False
    if data == [] and mode != 'del':
        PrintDebug('Warning: edit_vim_buffer: edit file with empty data= [], file:%s !'%(path_or_name))
        return
    if type(data) is str:
        data = [data]
    path = get_path_for_name(path_or_name)
    # some time edit other buffer, may change cur window cursor or add empty line to cur buffer file,
    # so must edit current buffer
    assert(vim.current.buffer.name == path),'%s,%s'%(vim.current.buffer.name, path)
    if path in [G['Report_Inf']['Report_Path'], G['Frame_Inf']['Frame_Path']]:
        need_edit_buffer_file_link = True
        if file_links:
            assert(len(data) == len(file_links))
        else:
            file_links = [ {} for i in range(len(data))]
    # PrintDebug('edit_vim_buffer path_or_name:'+path_or_name)
    # PrintDebug('edit_vim_buffer data:'+data.__str__())
    # PrintDebug('edit_vim_buffer file_links:'+file_links.__str__())
    # PrintDebug('edit_vim_buffer mode:'+mode.__str__())
    # PrintDebug('edit_vim_buffer n:'+n.__str__())
    # PrintDebug('edit_vim_buffer del_range:'+del_range.__str__())
    t_buffer = get_file_vim_buffer(path)
    assert(t_buffer)
    if mode is 'w':
        del t_buffer[:]
        t_buffer.append(data)
        del t_buffer[:1]
        if need_edit_buffer_file_link:
            G["VimBufferLineFileLink"][path] = file_links
    elif mode is 'a':
        t_buffer.append(data)
        if need_edit_buffer_file_link:
            G["VimBufferLineFileLink"].setdefault(path,[])
            G["VimBufferLineFileLink"][path] = G["VimBufferLineFileLink"][path] + file_links 
    elif mode is 'b':
        t_buffer.append(data, 0)
        if need_edit_buffer_file_link:
            G["VimBufferLineFileLink"].setdefault(path,[])
            G["VimBufferLineFileLink"][path] = file_links + G["VimBufferLineFileLink"][path]  
    elif mode is 'i':
        while len(t_buffer) <= n+1:
            t_buffer.append('')
            if need_edit_buffer_file_link:
                G["VimBufferLineFileLink"][path].append({})
        t_buffer.append(data, n)
        if need_edit_buffer_file_link:
            G["VimBufferLineFileLink"].setdefault(path,[])
            G["VimBufferLineFileLink"][path] = G["VimBufferLineFileLink"][path][:n] + file_links + G["VimBufferLineFileLink"][path][n:]
    elif mode is 'del':
        assert(del_range != () )
        if type(del_range) is int:
            del t_buffer[del_range]
            del G["VimBufferLineFileLink"][path][del_range]
        elif type(del_range) in [ tuple, list ]:
            del t_buffer[del_range[0]:del_range[1]+1]
            del G["VimBufferLineFileLink"][path][del_range[0]:del_range[1]+1]
        else:
            assert(0)

def go_win( path_or_name = '', pos = (), search_word = ''):
    if not path_or_name:
        return
        # path_or_name = vim.current.buffer.name
    Open(path_or_name)
    if re.search('\w+',search_word):
        vim.current.window.cursor = (1,0) # search from top in case match to left vim warning
        vim.command('/\c\<'+search_word+'\>')
    if pos:
        cursor = (pos[0]+1, pos[1])
        vim.current.window.cursor = cursor

############################################################################






 

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

简单同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值