Hi, this is my second version of the RegexTester.
Bugfixes and new features.
I hope the comment section explains everything.
In case there is something unclear, let me know.
Cheers
Claudia
importtime# -----------------------------------------------------------------------------importrefromctypesimportwindll,byref,wintypes,WINFUNCTYPE,Structure,sizeof# -----------------------------------------------------------------------------# You need to indicate, in the TEMP_FILE_NAME variable, below, the# fully qualified file name of the TEXT file containing the REGEX(ES) to test !## Note : DOUBLE backslash is needed ( e.g. D:\\tests\\RegexTests.txt )## The COMMENT_CHAR can be changed, if needed, but keep in mind that, if your regex# begins with a COMMENT_CHAR, you must escape it, to make the whole regex working.# ( For instance the regex \#.* colors all the comments of a python file )## If you do not want to be informed, by coloring the current regex, that it didn't match# set the INFORM_ABOUT_NO_MATCHES flag to False## 2 shortcuts can be configured:# - JUMP_FORWARD_SHORTCUT = to jump to matches in forward mode,# - JUMP_BACKWARD_SHORTCUT = for backward mode,## IMPORTANT:# If using modifiers (SHIFT, CTRL and ALT) the ordering is critical.# A shortcut is defined as a python string using the plus sign to# separate modifiers from letters and function keys.## E.g. 'SHIFT+CTRL+O' means you have to press SHIFT, CTRL and the literal O together.# As already said, if you would define 'CTRL+SHIFT+O' it won't work as the script# expects that SHIFT is the first modifier, if used, followed by CTRL and then ALT key.## One can only define shortcuts which aren't used by notepad, scintilla# and plugins already. Meaning, you cannot define a shortcut 'CTRL+F'# as this is already used to show the file dialog## The possible function keys are listed in dictionary FUNC_KEYS.# Basically, the keys are ESC and F1 to F12# ---------------------------- <CONFIGURATION AREA> ---------------------------TEMP_FILE_NAME='C:\\RegexTester.txt'COMMENT_CHAR='#'INFORM_ABOUT_NO_MATCHES=TrueJUMP_FORWARD_SHORTCUT='F9'JUMP_BACKWARD_SHORTCUT='SHIFT+F9'# ORDERING IS CRITICAL!! SHIFT CTRL ALTDEBUG_ON=True# ---------------------------- </CONFIGURATION AREA> ---------------------------FUNC_KEYS={0x1B:'ESC',0x70:'F1',0x71:'F2',0x72:'F3',0x73:'F4',0x74:'F5',0x75:'F6',0x76:'F7',0x77:'F8',0x78:'F9',0x79:'F10',0x7A:'F11',0x7B:'F12',}# -----------------------------------------------------------------------------# winapi constants - used by ctypesWM_KEYDOWN=0x0100WM_KEYUP=0x0101WM_SYSKEYDOWN=0x104WM_SYSKEYUP=0x105WM_SETFOUCS=0x07WM_KILLFOUCS=0x08VK_SHIFT=0x10VK_CONTROL=0x11VK_MENU=VK_ALT=0x12VK_UP=0x26VK_DOWN=0x28WndProcType=WINFUNCTYPE(wintypes.LONG,wintypes.HWND,wintypes.UINT,wintypes.WPARAM,wintypes.LPARAM)GWL_WNDPROC=-4# -----------------------------------------------------------------------------_g=globals()REGEX_TESTER_IS_RUNNING=_g.get('REGEX_TESTER_IS_RUNNING',False)OLD_WND_PROC=_g.get('OLD_WND_PROC',None)REGEX_TESTER_HWND=_g.get('REGEX_TESTER_HWND',None)REGEX_TESTER_INPUT_TAB=_g.get('REGEX_TESTER_INPUT_TAB',0)COLORED_DOCS_LIST=_g.get('COLORED_DOCS_LIST',[])TIME_REGEX=FalseUSE_PYTHON_ENGINE=False# -----------------------------------------------------------------------------editor1.indicSetStyle(8,INDICATORSTYLE.CONTAINER)editor1.indicSetFore(8,(100,215,100))editor1.indicSetAlpha(8,55)editor1.indicSetOutlineAlpha(8,255)editor1.indicSetUnder(8,True)editor1.indicSetStyle(9,INDICATORSTYLE.ROUNDBOX)editor1.indicSetFore(9,(195,215,184))editor1.indicSetAlpha(9,55)editor1.indicSetOutlineAlpha(9,255)editor1.indicSetUnder(9,True)editor1.indicSetStyle(10,INDICATORSTYLE.ROUNDBOX)editor1.indicSetFore(10,(95,215,184))editor1.indicSetAlpha(10,55)editor1.indicSetOutlineAlpha(10,255)editor1.indicSetUnder(10,True)editor2.indicSetStyle(11,INDICATORSTYLE.STRAIGHTBOX)editor2.indicSetFore(11,(255,0,0))# (192,192,192) (255,0,0)editor2.indicSetAlpha(11,155)editor2.indicSetOutlineAlpha(11,255)editor2.indicSetUnder(11,True)editor2.indicSetStyle(12,INDICATORSTYLE.STRAIGHTBOX)editor2.indicSetFore(12,(0,255,0))editor2.indicSetAlpha(12,155)editor2.indicSetOutlineAlpha(12,255)editor2.indicSetUnder(12,True)editor1.indicSetStyle(13,INDICATORSTYLE.STRAIGHTBOX)editor1.indicSetFore(13,(0,0,255))# (192,192,192) (0, 0, 255)editor1.indicSetAlpha(13,155)editor1.indicSetOutlineAlpha(13,55)editor1.indicSetUnder(13,True)editor2.indicSetStyle(14,INDICATORSTYLE.STRAIGHTBOX)editor2.indicSetFore(14,(255,120,0))# (192,192,192) (255,0,0)editor2.indicSetAlpha(14,155)editor2.indicSetOutlineAlpha(14,255)editor2.indicSetUnder(14,True)# -----------------------------------------------------------------------------IS_ODD=FalsePREVIOUS_REGEX={}# POS_FIRST_OCCURANCE = None# POS_LAST_OCCURANCE = NoneNO_MATCH_FOUND=TrueMATCH_POSITIONS={}CURRENT_BUFFER_ID=NoneLAST_POSITION=-1FORWARD_SEARCH=0BACKWARD_SEARCH=1REGEX_LINE_CLEAR=0REGEX_LINE_NO_MATCH=1REGEX_LINE_INVALID=-1# -----------------------------------------------------------------------------classGUITHREADINFO(Structure):_fields_=[('cbSize',wintypes.DWORD),('flags',wintypes.DWORD),('hwndActive',wintypes.HWND),('hwndFocus',wintypes.HWND),('hwndCapture',wintypes.HWND),('hwndMenuOwner',wintypes.HWND),('hwndMoveSize',wintypes.HWND),('hwndCaret',wintypes.HWND),('rcCaret',wintypes.RECT)]defget_focused_window():guiThreadInfo=GUITHREADINFO(cbSize=sizeof(GUITHREADINFO))windll.user32.GetGUIThreadInfo(0,byref(guiThreadInfo))returnguiThreadInfo.hwndFocus# -----------------------------------------------------------------------------defsignature(func,args,kwargs,result):func=func.__name__result=unicode(result)try:returnu'{}({}, {}) -> {}'.format(func,unicode(args),unicode(kwargs),result)except:returnu'{}(...) -> {}'.format(func,result)# -----------------------------------------------------------------------------deftime_function(func):definner(*args,**kwargs):start=time.time()result=func(*args,**kwargs)stop=time.time()re_engine=u'python'ifUSE_PYTHON_ENGINEelseu'boost'call_signature=signature(func,args,kwargs,result)console.write(u'{} took {} seconds using {} re engine\n'.format(call_signature,stop-start,re_engine))returnresultreturninner# -----------------------------------------------------------------------------defmatch_found(m):globalIS_ODD# global POS_FIRST_OCCURANCE# global POS_LAST_OCCURANCEglobalMATCH_POSITIONSglobalNO_MATCH_FOUND_match_positions=[]ifDEBUG_ON:_m=m.span()console.write(u'Match:{} = {}\n'.format(_m,editor1.getTextRange(*_m)))ifm.lastindex>0:editor1.setIndicatorCurrent(8)editor1.indicatorFillRange(m.span(0)[0],m.span(0)[1]-m.span(0)[0])_match_positions.append(m.span())foriinrange(1,m.lastindex+1):if(m.span(i)[0]!=m.span(0)[0])or(m.span(i)[1]!=m.span(0)[1]):editor1.setIndicatorCurrent(9ifIS_ODDelse10)editor1.indicatorFillRange(m.span(i)[0],m.span(i)[1]-m.span(i)[0])IS_ODD=FalseifIS_ODDelseTrue# POS_LAST_OCCURANCE = m.span(i)else:editor1.setIndicatorCurrent(9ifIS_ODDelse10)editor1.indicatorFillRange(m.span(0)[0],m.span(0)[1]-m.span(0)[0])IS_ODD=FalseifIS_ODDelseTrue# POS_LAST_OCCURANCE = m.span(0)_match_positions.append(m.span())MATCH_POSITIONS[CURRENT_BUFFER_ID].extend(_match_positions)# if POS_FIRST_OCCURANCE == None:# POS_FIRST_OCCURANCE = m.span(0)NO_MATCH_FOUND=False# -----------------------------------------------------------------------------defset_current_buffer_id():globalCURRENT_BUFFER_IDdoc_idx=notepad.getCurrentDocIndex(0)CURRENT_BUFFER_ID=notepad.getFiles()[doc_idx][1]# -----------------------------------------------------------------------------deftrack_document():globalCOLORED_DOCS_LISTset_current_buffer_id()ifnotCURRENT_BUFFER_IDinCOLORED_DOCS_LIST:COLORED_DOCS_LIST.append(CURRENT_BUFFER_ID)# -----------------------------------------------------------------------------defclear_indicator():foriin[8,9,10,13]:editor1.setIndicatorCurrent(i)editor1.indicatorClearRange(0,editor1.getTextLength())# -----------------------------------------------------------------------------defget_regex_flags():globalTIME_REGEXglobalUSE_PYTHON_ENGINETIME_REGEX=FalseUSE_PYTHON_ENGINE=Falseflags=0flag_line=editor2.getLine(0)forcharinrange(flag_line.find('[')+1,flag_line.find(']')):ifflag_line[char].upper()=='I':flags|=re.IGNORECASEelifflag_line[char].upper()=='L':flags|=re.LOCALEelifflag_line[char].upper()=='M':flags|=re.MULTILINEelifflag_line[char].upper()=='S':flags|=re.DOTALLelifflag_line[char].upper()=='U':flags|=re.UNICODEelifflag_line[char].upper()=='X':flags|=re.VERBOSEelifflag_line[char].upper()=='T':TIME_REGEX=Trueelifflag_line[char].upper()=='P':USE_PYTHON_ENGINE=Truereturnflags# -----------------------------------------------------------------------------@time_functiondefregex_exec(pattern,flags):try:foriinrange(1):# editor1.research(pattern, lambda m: m, flags)editor1.research(pattern,match_found,flags)returnTrueexcept:returnFalsedefregex():# global POS_FIRST_OCCURANCEglobalPREVIOUS_REGEXglobalNO_MATCH_FOUNDglobalMATCH_POSITIONSglobalIS_ODDIS_ODD=FalseMATCH_POSITIONS[CURRENT_BUFFER_ID]=[]clear_indicator()pattern=u''if(editor2.getSelections()==1)and(editor2.getSelectionEmpty()==False):start=editor2.lineFromPosition(editor2.getSelectionNStart(0))end=editor2.lineFromPosition(editor2.getSelectionNEnd(0))foriinrange(start,end+1):pattern+=editor2.getLine(i).rstrip('\r\n')else:pattern=current_regex()PREVIOUS_REGEX[CURRENT_BUFFER_ID]=patternNO_MATCH_FOUND=Trueif(pattern!=''andpattern!='.'andpattern!='()'andnotpattern.startswith(COMMENT_CHAR)andnotpattern.isspace()andeditor2.lineFromPosition(editor2.getCurrentPos())>0):# POS_FIRST_OCCURANCE = Noneregex_flag=get_regex_flags()ifTIME_REGEX:ifregex_exec(pattern,regex_flag):track_document()else:NO_MATCH_FOUND=Noneelse:try:ifUSE_PYTHON_ENGINE:txt=editor1.getText()forminre.finditer(pattern,txt,regex_flag):match_found(m)else:editor1.research(pattern,match_found,regex_flag)track_document()except:NO_MATCH_FOUND=Noneelse:NO_MATCH_FOUND=None# -----------------------------------------------------------------------------defcurrent_regex():returneditor2.getCurLine().rstrip()# -----------------------------------------------------------------------------defregex_tester_doc_is_current_doc():returnREGEX_TESTER_INPUT_TAB==notepad.getCurrentBufferID()# -----------------------------------------------------------------------------defscroll_to_position(_position):if_positionisnotNone:start_pos,end_pos=_positioneditor1.setIndicatorCurrent(13)editor1.indicatorFillRange(start_pos,end_pos-start_pos)current_line=editor1.lineFromPosition(start_pos)editor1.ensureVisible(current_line)editor1.gotoPos(start_pos)editor1.verticalCentreCaret()editor1.setXCaretPolicy(CARETPOLICY.EVEN|CARETPOLICY.JUMPS,10)editor1.scrollCaret()# -----------------------------------------------------------------------------defget_current_regex_position_and_length():current_line=editor2.lineFromPosition(editor2.getCurrentPos())ifcurrent_line!=0andnoteditor2.getLine(current_line).startswith(COMMENT_CHAR):lenght_line=editor2.lineLength(current_line)position=editor2.positionFromLine(current_line)returnposition,lenght_linereturnNone,None# -----------------------------------------------------------------------------defmark_regex_line(match_indicator):position,lenght=get_current_regex_position_and_length()ifposition:ifmatch_indicator==REGEX_LINE_NO_MATCH:editor2.setIndicatorCurrent(14)editor2.indicatorFillRange(position,lenght)elifmatch_indicator==REGEX_LINE_INVALID:editor2.setIndicatorCurrent(11)editor2.indicatorFillRange(position,lenght)else:foriin[11,14]:editor2.setIndicatorCurrent(i)editor2.indicatorClearRange(0,editor2.getTextLength())# -----------------------------------------------------------------------------defregex_tester_updateui_callback(args):if(regex_tester_doc_is_current_doc()andPREVIOUS_REGEX.get(CURRENT_BUFFER_ID,'')!=current_regex()):mark_regex_line(REGEX_LINE_CLEAR)regex()ifNO_MATCH_FOUNDandINFORM_ABOUT_NO_MATCHES:mark_regex_line(REGEX_LINE_NO_MATCH)elifNO_MATCH_FOUNDisNone:mark_regex_line(REGEX_LINE_INVALID)# else:# scroll_to_position(POS_FIRST_OCCURANCE)# -----------------------------------------------------------------------------defregex_tester_file_before_close_callback(args):if(args['bufferID']==REGEX_TESTER_INPUT_TAB):stop_regex_tester()# -----------------------------------------------------------------------------defregex_tester_buffer_activated_callback(args):globalPREVIOUS_REGEXset_current_buffer_id()ifnotPREVIOUS_REGEX.has_key(CURRENT_BUFFER_ID):PREVIOUS_REGEX[CURRENT_BUFFER_ID]=''ifnotREGEX_TESTER_IS_RUNNING:globalCOLORED_DOCS_LISTifargs['bufferID']inCOLORED_DOCS_LIST:clear_indicator()COLORED_DOCS_LIST.remove(args['bufferID'])iflen(COLORED_DOCS_LIST)==0:notepad.clearCallbacks([NOTIFICATION.BUFFERACTIVATED])# -----------------------------------------------------------------------------defjump_through_matches(search_direction):globalMATCH_POSITIONSglobalLAST_POSITIONifMATCH_POSITIONS.has_key(CURRENT_BUFFER_ID):editor1.setIndicatorCurrent(13)editor1.indicatorClearRange(0,editor1.getTextLength())match_count=len(MATCH_POSITIONS[CURRENT_BUFFER_ID])ifmatch_count==0:returnelifmatch_count==1:_position=MATCH_POSITIONS[CURRENT_BUFFER_ID][0]else:current_position=editor1.getCurrentPos()ifsearch_direction==0:whileTrue:_position=MATCH_POSITIONS[CURRENT_BUFFER_ID].pop(0)MATCH_POSITIONS[CURRENT_BUFFER_ID].append(_position)if_position[0]!=LAST_POSITION:breakelse:whileTrue:_position=MATCH_POSITIONS[CURRENT_BUFFER_ID].pop()MATCH_POSITIONS[CURRENT_BUFFER_ID].insert(0,_position)if_position[0]!=LAST_POSITION:breakLAST_POSITION=_position[0]scroll_to_position(_position)# -----------------------------------------------------------------------------defregex_tester_doc_already_exists():files=notepad.getFiles()f=[i[1][1]foriinenumerate(files)ifi[1][0].upper()==TEMP_FILE_NAME.upper()]iff:returnf[0]else:return0# -----------------------------------------------------------------------------defcolor_regex_tester_status():status_line=editor2.getLine(0)ifREGEX_TESTER_IS_RUNNING:start=status_line.find('[')stop=status_line.find(']')+1editor2.setIndicatorCurrent(12)editor2.indicatorFillRange(0,20)editor2.setIndicatorCurrent(12)editor2.indicatorFillRange(start,stop-start)else:editor2.setIndicatorCurrent(12)editor2.indicatorClearRange(0,len(status_line))# -----------------------------------------------------------------------------defstart_regex_tester():ifTEMP_FILE_NAME=='':notepad.messageBox('You need to indicate, in the TEMP_FILE_NAME variable,\n'+'the fully qualified file name of the TEXT file containing '+'the REGEX(ES) to test','TEMP_FILE_NAME is not set')returnFalsecurrent_document=0ifnotepad.getCurrentView()==1elsenotepad.getCurrentBufferID()globalREGEX_TESTER_INPUT_TABREGEX_TESTER_INPUT_TAB=regex_tester_doc_already_exists()ifREGEX_TESTER_INPUT_TAB==0:notepad.open(TEMP_FILE_NAME)ifnotepad.getCurrentFilename().upper()==TEMP_FILE_NAME.upper():REGEX_TESTER_INPUT_TAB=notepad.getCurrentBufferID()else:notepad.messageBox('Could not open specified file\n'+'{0}'.format(TEMP_FILE_NAME),'Regex Tester Startup Failed',0)returnFalseelse:notepad.activateBufferID(REGEX_TESTER_INPUT_TAB)ifnotepad.getCurrentView()!=1:notepad.menuCommand(MENUCOMMAND.VIEW_GOTO_ANOTHER_VIEW)STATUS_LINE='RegexTester isActive [] flags:sitp, 'STATUS_LINE+='fwd:{} bwd:{}\r\n'.format(JUMP_FORWARD_SHORTCUT,JUMP_BACKWARD_SHORTCUT,)current_status_line=editor2.getLine(0)if'RegexTester'incurrent_status_line:editor2.replace('RegexTester inActive','RegexTester isActive')else:editor2.insertText(0,STATUS_LINE)globalREGEX_TESTER_IS_RUNNINGREGEX_TESTER_IS_RUNNING=Truecolor_regex_tester_status()set_current_buffer_id()globalPREVIOUS_REGEXPREVIOUS_REGEX[CURRENT_BUFFER_ID]=''editor.callbackSync(regex_tester_updateui_callback,[SCINTILLANOTIFICATION.UPDATEUI])ifcurrent_document!=0:notepad.activateBufferID(current_document)editor2.setFocus(True)editor2.gotoLine(0)notepad.save()globalREGEX_TESTER_HWNDREGEX_TESTER_HWND=get_focused_window()notepad.callback(regex_tester_file_before_close_callback,[NOTIFICATION.FILEBEFORECLOSE])notepad.callback(regex_tester_buffer_activated_callback,[NOTIFICATION.BUFFERACTIVATED])returnTrue# -----------------------------------------------------------------------------defstop_regex_tester():editor.clearCallbacks([SCINTILLANOTIFICATION.UPDATEUI])notepad.clearCallbacks([NOTIFICATION.FILEBEFORECLOSE])notepad.activateBufferID(REGEX_TESTER_INPUT_TAB)ifregex_tester_doc_is_current_doc():editor2.replace('RegexTester isActive','RegexTester inActive')notepad.save()globalREGEX_TESTER_IS_RUNNINGREGEX_TESTER_IS_RUNNING=Falseclear_indicator()color_regex_tester_status()mark_regex_line(0)globalMATCH_POSITIONSMATCH_POSITIONS={}_hook.unregister()editor1.setFocus(True)# -----------------------------------------------------------------------------classHook():def__init__(self):self.nppHandle=windll.user32.FindWindowA('Notepad++',None)self.oldWndProc=Noneself.SHIFT_PRESSED=Falseself.CTRL_PRESSED=Falseself.ALT_PRESSED=Falsedefregister(self):ifREGEX_TESTER_HWND:self.new_wnd_proc=WndProcType(self.sciWndProc)windll.kernel32.SetLastError(0)self.oldWndProc=windll.user32.SetWindowLongA(REGEX_TESTER_HWND,GWL_WNDPROC,self.new_wnd_proc)ifself.oldWndProc:globalOLD_WND_PROCOLD_WND_PROC=self.oldWndProcelse:_err='GetLastError:{}'.format(windll.kernel32.GetLastError())notepad.messageBox('Could not register hook:\n{}\n'.format(_err)+'Shortcuts won\'t work','Register Hook Failure',0)defunregister(self):ifOLD_WND_PROC:self.oldWndProc=OLD_WND_PROCwindll.kernel32.SetLastError(0)dummy=windll.user32.SetWindowLongA(REGEX_TESTER_HWND,GWL_WNDPROC,self.oldWndProc)ifnotdummy:_err='GetLastError:{}'.format(windll.kernel32.GetLastError())notepad.messageBox('Could not unregister hook:\n{}\n'.format(_err)+'It is recommended to save data and restart npp to prevent data loss','Unregister Hook Failure',0)defsciWndProc(self,hWnd,msg,wParam,lParam):ifmsgin[WM_KEYDOWN,WM_SYSKEYDOWN]:ifwParam==VK_SHIFT:self.SHIFT_PRESSED=TrueelifwParam==VK_CONTROL:self.CTRL_PRESSED=TrueelifwParam==VK_ALT:self.ALT_PRESSED=Trueelifmsgin[WM_KEYUP,WM_SYSKEYUP]:ifwParam==VK_SHIFT:self.SHIFT_PRESSED=FalseelifwParam==VK_CONTROL:self.CTRL_PRESSED=FalseelifwParam==VK_ALT:self.ALT_PRESSED=Falseelse:modifier='SHIFT+'ifself.SHIFT_PRESSEDelse''modifier+='CTRL+'ifself.CTRL_PRESSEDelse''modifier+='ALT+'ifself.ALT_PRESSEDelse''ifwParaminFUNC_KEYS:key_combo='{}{}'.format(modifier,FUNC_KEYS[wParam])else:key_combo='{}{}'.format(modifier,chr(wParam))func=dict_func.get(key_combo,None)iffunc:func()elifmsg==WM_KILLFOUCS:self.SHIFT_PRESSED=Falseself.CTRL_PRESSED=Falseself.ALT_PRESSED=Falsereturnwindll.user32.CallWindowProcA(self.oldWndProc,hWnd,msg,wParam,lParam)# -----------------------------------------------------------------------------defmain():ifREGEX_TESTER_IS_RUNNING:stop_regex_tester()else:ifstart_regex_tester():_hook.register()# -----------------------------------------------------------------------------dict_func={JUMP_FORWARD_SHORTCUT:lambda:jump_through_matches(FORWARD_SEARCH),JUMP_BACKWARD_SHORTCUT:lambda:jump_through_matches(BACKWARD_SEARCH),}# JUMP_TO_FIRST_SHORTCUT : lambda: scroll_to_position(POS_FIRST_OCCURANCE),# JUMP_TO_LAST_SHORTCUT : lambda: scroll_to_position(POS_LAST_OCCURANCE)}_hook=Hook()main()
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi, this is my second version of the RegexTester.
Bugfixes and new features.
I hope the comment section explains everything.
In case there is something unclear, let me know.
Cheers
Claudia