Debugger/DebuggerInterfaceRuby.py

branch
Py2 comp.
changeset 3057
10516539f238
parent 2525
8b507a9a2d40
parent 2988
f53c03574697
child 3058
0a02c433f52d
equal deleted inserted replaced
3056:9986ec0e559a 3057:10516539f238
67 self.qsock = None 67 self.qsock = None
68 self.queue = [] 68 self.queue = []
69 # set default values for capabilities of clients 69 # set default values for capabilities of clients
70 self.clientCapabilities = ClientDefaultCapabilities 70 self.clientCapabilities = ClientDefaultCapabilities
71 71
72 self.codec = QTextCodec.codecForName(str(Preferences.getSystem("StringEncoding"))) 72 self.codec = QTextCodec.codecForName(
73 str(Preferences.getSystem("StringEncoding")))
73 74
74 if passive: 75 if passive:
75 # set translation function 76 # set translation function
76 if Preferences.getDebugger("PathTranslation"): 77 if Preferences.getDebugger("PathTranslation"):
77 self.translateRemote = \ 78 self.translateRemote = \
111 """ 112 """
112 Private method to start the debugger client process. 113 Private method to start the debugger client process.
113 114
114 @param program name of the executable to start (string) 115 @param program name of the executable to start (string)
115 @param arguments arguments to be passed to the program (list of string) 116 @param arguments arguments to be passed to the program (list of string)
116 @param environment dictionary of environment settings to pass (dict of string) 117 @param environment dictionary of environment settings to pass
118 (dict of string)
117 @return the process object (QProcess) or None 119 @return the process object (QProcess) or None
118 """ 120 """
119 proc = QProcess() 121 proc = QProcess()
120 if environment is not None: 122 if environment is not None:
121 env = [] 123 env = []
163 process = self.__startProcess(args[0], args[1:]) 165 process = self.__startProcess(args[0], args[1:])
164 if process is None: 166 if process is None:
165 E5MessageBox.critical(None, 167 E5MessageBox.critical(None,
166 self.trUtf8("Start Debugger"), 168 self.trUtf8("Start Debugger"),
167 self.trUtf8( 169 self.trUtf8(
168 """<p>The debugger backend could not be started.</p>""")) 170 """<p>The debugger backend could not be"""
171 """ started.</p>"""))
169 172
170 # set translation function 173 # set translation function
171 if Preferences.getDebugger("PathTranslation"): 174 if Preferences.getDebugger("PathTranslation"):
172 self.translateRemote = \ 175 self.translateRemote = \
173 Preferences.getDebugger("PathTranslationRemote") 176 Preferences.getDebugger("PathTranslationRemote")
208 process = self.__startProcess(args[0], args[1:], clientEnv) 211 process = self.__startProcess(args[0], args[1:], clientEnv)
209 if process is None: 212 if process is None:
210 E5MessageBox.critical(None, 213 E5MessageBox.critical(None,
211 self.trUtf8("Start Debugger"), 214 self.trUtf8("Start Debugger"),
212 self.trUtf8( 215 self.trUtf8(
213 """<p>The debugger backend could not be started.</p>""")) 216 """<p>The debugger backend could not be"""
217 """ started.</p>"""))
214 return process, self.__isNetworked 218 return process, self.__isNetworked
215 219
216 process = self.__startProcess(interpreter, 220 process = self.__startProcess(interpreter,
217 [debugClient, str(port), redirect, ipaddr], 221 [debugClient, str(port), redirect, ipaddr],
218 clientEnv) 222 clientEnv)
219 if process is None: 223 if process is None:
220 E5MessageBox.critical(None, 224 E5MessageBox.critical(None,
221 self.trUtf8("Start Debugger"), 225 self.trUtf8("Start Debugger"),
222 self.trUtf8("""<p>The debugger backend could not be started.</p>""")) 226 self.trUtf8(
227 """<p>The debugger backend could not be started.</p>"""))
223 return process, self.__isNetworked 228 return process, self.__isNetworked
224 229
225 def startRemoteForProject(self, port, runInConsole): 230 def startRemoteForProject(self, port, runInConsole):
226 """ 231 """
227 Public method to start a remote Ruby interpreter for a project. 232 Public method to start a remote Ruby interpreter for a project.
256 process = self.__startProcess(args[0], args[1:]) 261 process = self.__startProcess(args[0], args[1:])
257 if process is None: 262 if process is None:
258 E5MessageBox.critical(None, 263 E5MessageBox.critical(None,
259 self.trUtf8("Start Debugger"), 264 self.trUtf8("Start Debugger"),
260 self.trUtf8( 265 self.trUtf8(
261 """<p>The debugger backend could not be started.</p>""")) 266 """<p>The debugger backend could not be"""
267 """ started.</p>"""))
262 # set translation function 268 # set translation function
263 if project.getDebugProperty("PATHTRANSLATION"): 269 if project.getDebugProperty("PATHTRANSLATION"):
264 self.translateRemote = project.getDebugProperty("REMOTEPATH") 270 self.translateRemote = \
265 self.translateLocal = project.getDebugProperty("LOCALPATH") 271 project.getDebugProperty("REMOTEPATH")
272 self.translateLocal = \
273 project.getDebugProperty("LOCALPATH")
266 self.translate = self.__remoteTranslation 274 self.translate = self.__remoteTranslation
267 else: 275 else:
268 self.translate = self.__identityTranslation 276 self.translate = self.__identityTranslation
269 return process, self.__isNetworked 277 return process, self.__isNetworked
270 278
299 process = self.__startProcess(args[0], args[1:], clientEnv) 307 process = self.__startProcess(args[0], args[1:], clientEnv)
300 if process is None: 308 if process is None:
301 E5MessageBox.critical(None, 309 E5MessageBox.critical(None,
302 self.trUtf8("Start Debugger"), 310 self.trUtf8("Start Debugger"),
303 self.trUtf8( 311 self.trUtf8(
304 """<p>The debugger backend could not be started.</p>""")) 312 """<p>The debugger backend could not be"""
313 """ started.</p>"""))
305 return process, self.__isNetworked 314 return process, self.__isNetworked
306 315
307 process = self.__startProcess(interpreter, 316 process = self.__startProcess(interpreter,
308 [debugClient, str(port), redirect, ipaddr], 317 [debugClient, str(port), redirect, ipaddr],
309 clientEnv) 318 clientEnv)
310 if process is None: 319 if process is None:
311 E5MessageBox.critical(None, 320 E5MessageBox.critical(None,
312 self.trUtf8("Start Debugger"), 321 self.trUtf8("Start Debugger"),
313 self.trUtf8("""<p>The debugger backend could not be started.</p>""")) 322 self.trUtf8(
323 """<p>The debugger backend could not be started.</p>"""))
314 return process, self.__isNetworked 324 return process, self.__isNetworked
315 325
316 def getClientCapabilities(self): 326 def getClientCapabilities(self):
317 """ 327 """
318 Public method to retrieve the debug clients capabilities. 328 Public method to retrieve the debug clients capabilities.
388 """ 398 """
389 Public method to set the environment for a program to debug, run, ... 399 Public method to set the environment for a program to debug, run, ...
390 400
391 @param env environment settings (dictionary) 401 @param env environment settings (dictionary)
392 """ 402 """
393 self.__sendCommand('{0}{1}\n'.format(DebugProtocol.RequestEnv, str(env))) 403 self.__sendCommand('{0}{1}\n'.format(
394 404 DebugProtocol.RequestEnv, str(env)))
395 def remoteLoad(self, fn, argv, wd, traceInterpreter=False, autoContinue=True, 405
396 autoFork=False, forkChild=False): 406 def remoteLoad(self, fn, argv, wd, traceInterpreter=False,
407 autoContinue=True, autoFork=False, forkChild=False):
397 """ 408 """
398 Public method to load a new program to debug. 409 Public method to load a new program to debug.
399 410
400 @param fn the filename to debug (string) 411 @param fn the filename to debug (string)
401 @param argv the commandline arguments to pass to the program (string) 412 @param argv the commandline arguments to pass to the program (string)
402 @param wd the working directory for the program (string) 413 @param wd the working directory for the program (string)
403 @keyparam traceInterpreter flag indicating if the interpreter library should be 414 @keyparam traceInterpreter flag indicating if the interpreter library
404 traced as well (boolean) 415 should be traced as well (boolean)
405 @keyparam autoContinue flag indicating, that the debugger should not stop 416 @keyparam autoContinue flag indicating, that the debugger should not
406 at the first executable line (boolean) 417 stop at the first executable line (boolean)
407 @keyparam autoFork flag indicating the automatic fork mode (boolean) (ignored) 418 @keyparam autoFork flag indicating the automatic fork mode (boolean)
419 (ignored)
408 @keyparam forkChild flag indicating to debug the child after forking 420 @keyparam forkChild flag indicating to debug the child after forking
409 (boolean) (ignored) 421 (boolean) (ignored)
410 """ 422 """
411 self.__autoContinue = autoContinue 423 self.__autoContinue = autoContinue
412 424
413 wd = self.translate(wd, False) 425 wd = self.translate(wd, False)
414 fn = self.translate(os.path.abspath(fn), False) 426 fn = self.translate(os.path.abspath(fn), False)
415 self.__sendCommand('{0}{1}|{2}|{3}|{4:d}\n'.format( 427 self.__sendCommand('{0}{1}|{2}|{3}|{4:d}\n'.format(
416 DebugProtocol.RequestLoad, wd, fn, str(Utilities.parseOptionString(argv)), 428 DebugProtocol.RequestLoad, wd, fn,
417 traceInterpreter)) 429 str(Utilities.parseOptionString(argv)),
430 traceInterpreter))
418 431
419 def remoteRun(self, fn, argv, wd, autoFork=False, forkChild=False): 432 def remoteRun(self, fn, argv, wd, autoFork=False, forkChild=False):
420 """ 433 """
421 Public method to load a new program to run. 434 Public method to load a new program to run.
422 435
423 @param fn the filename to run (string) 436 @param fn the filename to run (string)
424 @param argv the commandline arguments to pass to the program (string) 437 @param argv the commandline arguments to pass to the program (string)
425 @param wd the working directory for the program (string) 438 @param wd the working directory for the program (string)
426 @keyparam autoFork flag indicating the automatic fork mode (boolean) (ignored) 439 @keyparam autoFork flag indicating the automatic fork mode (boolean)
440 (ignored)
427 @keyparam forkChild flag indicating to debug the child after forking 441 @keyparam forkChild flag indicating to debug the child after forking
428 (boolean) (ignored) 442 (boolean) (ignored)
429 """ 443 """
430 wd = self.translate(wd, False) 444 wd = self.translate(wd, False)
431 fn = self.translate(os.path.abspath(fn), False) 445 fn = self.translate(os.path.abspath(fn), False)
432 self.__sendCommand('{0}{1}|{2}|{3}\n'.format( 446 self.__sendCommand('{0}{1}|{2}|{3}\n'.format(
433 DebugProtocol.RequestRun, wd, fn, str(Utilities.parseOptionString(argv)))) 447 DebugProtocol.RequestRun, wd, fn,
448 str(Utilities.parseOptionString(argv))))
434 449
435 def remoteCoverage(self, fn, argv, wd, erase=False): 450 def remoteCoverage(self, fn, argv, wd, erase=False):
436 """ 451 """
437 Public method to load a new program to collect coverage data. 452 Public method to load a new program to collect coverage data.
438 453
439 @param fn the filename to run (string) 454 @param fn the filename to run (string)
440 @param argv the commandline arguments to pass to the program (string) 455 @param argv the commandline arguments to pass to the program (string)
441 @param wd the working directory for the program (string) 456 @param wd the working directory for the program (string)
442 @keyparam erase flag indicating that coverage info should be 457 @keyparam erase flag indicating that coverage info should be
443 cleared first (boolean) 458 cleared first (boolean)
459 @exception NotImplementedError raised to indicate that this interface
460 is not supported
444 """ 461 """
445 raise NotImplementedError("Interface not available.") 462 raise NotImplementedError("Interface not available.")
446 463
447 def remoteProfile(self, fn, argv, wd, erase=False): 464 def remoteProfile(self, fn, argv, wd, erase=False):
448 """ 465 """
449 Public method to load a new program to collect profiling data. 466 Public method to load a new program to collect profiling data.
450 467
451 @param fn the filename to run (string) 468 @param fn the filename to run (string)
452 @param argv the commandline arguments to pass to the program (string) 469 @param argv the commandline arguments to pass to the program (string)
453 @param wd the working directory for the program (string) 470 @param wd the working directory for the program (string)
454 @keyparam erase flag indicating that timing info should be cleared first (boolean) 471 @keyparam erase flag indicating that timing info should be cleared
472 first (boolean)
473 @exception NotImplementedError raised to indicate that this interface
474 is not supported
455 """ 475 """
456 raise NotImplementedError("Interface not available.") 476 raise NotImplementedError("Interface not available.")
457 477
458 def remoteStatement(self, stmt): 478 def remoteStatement(self, stmt):
459 """ 479 """
493 """ 513 """
494 Public method to continue the debugged program. 514 Public method to continue the debugged program.
495 515
496 @param special flag indicating a special continue operation (boolean) 516 @param special flag indicating a special continue operation (boolean)
497 """ 517 """
498 self.__sendCommand('{0}{1:d}\n'.format(DebugProtocol.RequestContinue, special)) 518 self.__sendCommand('{0}{1:d}\n'.format(
519 DebugProtocol.RequestContinue, special))
499 520
500 def remoteBreakpoint(self, fn, line, set, cond=None, temp=False): 521 def remoteBreakpoint(self, fn, line, set, cond=None, temp=False):
501 """ 522 """
502 Public method to set or clear a breakpoint. 523 Public method to set or clear a breakpoint.
503 524
507 @param cond condition of the breakpoint (string) 528 @param cond condition of the breakpoint (string)
508 @param temp flag indicating a temporary breakpoint (boolean) 529 @param temp flag indicating a temporary breakpoint (boolean)
509 """ 530 """
510 fn = self.translate(fn, False) 531 fn = self.translate(fn, False)
511 self.__sendCommand('{0}{1}@@{2:d}@@{3:d}@@{4:d}@@{5}\n'.format( 532 self.__sendCommand('{0}{1}@@{2:d}@@{3:d}@@{4:d}@@{5}\n'.format(
512 DebugProtocol.RequestBreak, fn, line, temp, set, cond)) 533 DebugProtocol.RequestBreak, fn, line, temp, set, cond))
513 534
514 def remoteBreakpointEnable(self, fn, line, enable): 535 def remoteBreakpointEnable(self, fn, line, enable):
515 """ 536 """
516 Public method to enable or disable a breakpoint. 537 Public method to enable or disable a breakpoint.
517 538
518 @param fn filename the breakpoint belongs to (string) 539 @param fn filename the breakpoint belongs to (string)
519 @param line linenumber of the breakpoint (int) 540 @param line linenumber of the breakpoint (int)
520 @param enable flag indicating enabling or disabling a breakpoint (boolean) 541 @param enable flag indicating enabling or disabling a breakpoint
542 (boolean)
521 """ 543 """
522 fn = self.translate(fn, False) 544 fn = self.translate(fn, False)
523 self.__sendCommand('{0}{1},{2:d},{3:d}\n'.format( 545 self.__sendCommand('{0}{1},{2:d},{3:d}\n'.format(
524 DebugProtocol.RequestBreakEnable, fn, line, enable)) 546 DebugProtocol.RequestBreakEnable, fn, line, enable))
525 547
538 def remoteWatchpoint(self, cond, set, temp=False): 560 def remoteWatchpoint(self, cond, set, temp=False):
539 """ 561 """
540 Public method to set or clear a watch expression. 562 Public method to set or clear a watch expression.
541 563
542 @param cond expression of the watch expression (string) 564 @param cond expression of the watch expression (string)
543 @param set flag indicating setting or resetting a watch expression (boolean) 565 @param set flag indicating setting or resetting a watch expression
566 (boolean)
544 @param temp flag indicating a temporary watch expression (boolean) 567 @param temp flag indicating a temporary watch expression (boolean)
545 """ 568 """
546 # cond is combination of cond and special (s. watch expression viewer) 569 # cond is combination of cond and special (s. watch expression viewer)
547 self.__sendCommand('{0}{1}@@{2:d}@@{3:d}\n'.format( 570 self.__sendCommand('{0}{1}@@{2:d}@@{3:d}\n'.format(
548 DebugProtocol.RequestWatch, cond, temp, set)) 571 DebugProtocol.RequestWatch, cond, temp, set))
550 def remoteWatchpointEnable(self, cond, enable): 573 def remoteWatchpointEnable(self, cond, enable):
551 """ 574 """
552 Public method to enable or disable a watch expression. 575 Public method to enable or disable a watch expression.
553 576
554 @param cond expression of the watch expression (string) 577 @param cond expression of the watch expression (string)
555 @param enable flag indicating enabling or disabling a watch expression (boolean) 578 @param enable flag indicating enabling or disabling a watch expression
579 (boolean)
556 """ 580 """
557 # cond is combination of cond and special (s. watch expression viewer) 581 # cond is combination of cond and special (s. watch expression viewer)
558 self.__sendCommand('{0}{1},{2:d}\n'.format( 582 self.__sendCommand('{0}{1},{2:d}\n'.format(
559 DebugProtocol.RequestWatchEnable, cond, enable)) 583 DebugProtocol.RequestWatchEnable, cond, enable))
560 584
561 def remoteWatchpointIgnore(self, cond, count): 585 def remoteWatchpointIgnore(self, cond, count):
562 """ 586 """
563 Public method to ignore a watch expression the next couple of occurrences. 587 Public method to ignore a watch expression the next couple of
588 occurrences.
564 589
565 @param cond expression of the watch expression (string) 590 @param cond expression of the watch expression (string)
566 @param count number of occurrences to ignore (int) 591 @param count number of occurrences to ignore (int)
567 """ 592 """
568 # cond is combination of cond and special (s. watch expression viewer) 593 # cond is combination of cond and special (s. watch expression viewer)
610 @param filter list of variable types to filter out (list of int) 635 @param filter list of variable types to filter out (list of int)
611 @param var list encoded name of variable to retrieve (string) 636 @param var list encoded name of variable to retrieve (string)
612 @param framenr framenumber of the variables to retrieve (int) 637 @param framenr framenumber of the variables to retrieve (int)
613 """ 638 """
614 self.__sendCommand('{0}{1}, {2:d}, {3:d}, {4}\n'.format( 639 self.__sendCommand('{0}{1}, {2:d}, {3:d}, {4}\n'.format(
615 DebugProtocol.RequestVariable, str(var), framenr, scope, str(filter))) 640 DebugProtocol.RequestVariable, str(var), framenr, scope,
641 str(filter)))
616 642
617 def remoteClientSetFilter(self, scope, filter): 643 def remoteClientSetFilter(self, scope, filter):
618 """ 644 """
619 Public method to set a variables filter list. 645 Public method to set a variables filter list.
620 646
632 """ 658 """
633 return 659 return
634 660
635 def remoteEval(self, arg): 661 def remoteEval(self, arg):
636 """ 662 """
637 Public method to evaluate arg in the current context of the debugged program. 663 Public method to evaluate arg in the current context of the debugged
664 program.
638 665
639 @param arg the arguments to evaluate (string) 666 @param arg the arguments to evaluate (string)
640 """ 667 """
641 self.__sendCommand('{0}{1}\n'.format(DebugProtocol.RequestEval, arg)) 668 self.__sendCommand('{0}{1}\n'.format(DebugProtocol.RequestEval, arg))
642 669
643 def remoteExec(self, stmt): 670 def remoteExec(self, stmt):
644 """ 671 """
645 Public method to execute stmt in the current context of the debugged program. 672 Public method to execute stmt in the current context of the debugged
673 program.
646 674
647 @param stmt statement to execute (string) 675 @param stmt statement to execute (string)
648 """ 676 """
649 self.__sendCommand('{0}{1}\n'.format(DebugProtocol.RequestExec, stmt)) 677 self.__sendCommand('{0}{1}\n'.format(DebugProtocol.RequestExec, stmt))
650 678
665 Public slot to get the a list of possible commandline completions 693 Public slot to get the a list of possible commandline completions
666 from the remote client. 694 from the remote client.
667 695
668 @param text the text to be completed (string) 696 @param text the text to be completed (string)
669 """ 697 """
670 self.__sendCommand("{0}{1}\n".format(DebugProtocol.RequestCompletion, text)) 698 self.__sendCommand("{0}{1}\n".format(
699 DebugProtocol.RequestCompletion, text))
671 700
672 def remoteUTPrepare(self, fn, tn, tfn, failed, cov, covname, coverase): 701 def remoteUTPrepare(self, fn, tn, tfn, failed, cov, covname, coverase):
673 """ 702 """
674 Public method to prepare a new unittest run. 703 Public method to prepare a new unittest run.
675 704
676 @param fn the filename to load (string) 705 @param fn the filename to load (string)
677 @param tn the testname to load (string) 706 @param tn the testname to load (string)
678 @param tfn the test function name to load tests from (string) 707 @param tfn the test function name to load tests from (string)
679 @param failed list of failed test, if only failed test should be run 708 @param failed list of failed test, if only failed test should be run
680 (list of strings) 709 (list of strings)
681 @param cov flag indicating collection of coverage data is requested (boolean) 710 @param cov flag indicating collection of coverage data is requested
711 (boolean)
682 @param covname filename to be used to assemble the coverage caches 712 @param covname filename to be used to assemble the coverage caches
683 filename (string) 713 filename (string)
684 @param coverase flag indicating erasure of coverage data is requested (boolean) 714 @param coverase flag indicating erasure of coverage data is requested
715 (boolean)
716 @exception NotImplementedError raised to indicate that this interface
717 is not supported
685 """ 718 """
686 raise NotImplementedError("Interface not available.") 719 raise NotImplementedError("Interface not available.")
687 720
688 def remoteUTRun(self): 721 def remoteUTRun(self):
689 """ 722 """
690 Public method to start a unittest run. 723 Public method to start a unittest run.
724
725 @exception NotImplementedError raised to indicate that this interface
726 is not supported
691 """ 727 """
692 raise NotImplementedError("Interface not available.") 728 raise NotImplementedError("Interface not available.")
693 729
694 def remoteUTStop(self): 730 def remoteUTStop(self):
695 """ 731 """
696 public method to stop a unittest run. 732 Public method to stop a unittest run.
733
734 @exception NotImplementedError raised to indicate that this interface
735 is not supported
697 """ 736 """
698 raise NotImplementedError("Interface not available.") 737 raise NotImplementedError("Interface not available.")
699 738
700 def __parseClientLine(self): 739 def __parseClientLine(self):
701 """ 740 """
781 stack = exclist[2:] 820 stack = exclist[2:]
782 except (IndexError, ValueError, SyntaxError): 821 except (IndexError, ValueError, SyntaxError):
783 exctype = None 822 exctype = None
784 excmessage = '' 823 excmessage = ''
785 stack = [] 824 stack = []
786 self.debugServer.signalClientException(exctype, excmessage, stack) 825 self.debugServer.signalClientException(
826 exctype, excmessage, stack)
787 continue 827 continue
788 828
789 if resp == DebugProtocol.ResponseSyntax: 829 if resp == DebugProtocol.ResponseSyntax:
790 exc = line[eoc:-1] 830 exc = line[eoc:-1]
791 exc = self.translate(exc, True) 831 exc = self.translate(exc, True)
796 except (IndexError, ValueError): 836 except (IndexError, ValueError):
797 message = None 837 message = None
798 fn = '' 838 fn = ''
799 ln = 0 839 ln = 0
800 cn = 0 840 cn = 0
801 self.debugServer.signalClientSyntaxError(message, fn, ln, cn) 841 self.debugServer.signalClientSyntaxError(
842 message, fn, ln, cn)
802 continue 843 continue
803 844
804 if resp == DebugProtocol.ResponseExit: 845 if resp == DebugProtocol.ResponseExit:
805 self.debugServer.signalClientExit(line[eoc:-1]) 846 self.debugServer.signalClientExit(line[eoc:-1])
806 continue 847 continue
817 self.debugServer.signalClientClearWatch(cond) 858 self.debugServer.signalClientClearWatch(cond)
818 continue 859 continue
819 860
820 if resp == DebugProtocol.ResponseBanner: 861 if resp == DebugProtocol.ResponseBanner:
821 version, platform, dbgclient = eval(line[eoc:-1]) 862 version, platform, dbgclient = eval(line[eoc:-1])
822 self.debugServer.signalClientBanner(version, platform, dbgclient) 863 self.debugServer.signalClientBanner(
864 version, platform, dbgclient)
823 continue 865 continue
824 866
825 if resp == DebugProtocol.ResponseCapabilities: 867 if resp == DebugProtocol.ResponseCapabilities:
826 cap, clType = eval(line[eoc:-1]) 868 cap, clType = eval(line[eoc:-1])
827 self.clientCapabilities = cap 869 self.clientCapabilities = cap

eric ide

mercurial