@@ -297,6 +297,14 @@ def keyPressEvent(self, event):
297297 bracket_pairs = {'(' : ')' , '[' : ']' , '{' : '}' , '"' : '"' , "'" : "'" }
298298 if event .text () in bracket_pairs :
299299 cursor = self .textCursor ()
300+ # Prüfe ob Cursor innerhalb eines String-Literals ist
301+ full_text = self .toPlainText ()
302+ cursor_pos = cursor .position ()
303+ mask = self ._build_string_comment_mask (full_text )
304+ if cursor_pos < len (mask ) and mask [cursor_pos ]:
305+ # Innerhalb von String/Kommentar: kein Auto-Close
306+ super ().keyPressEvent (event )
307+ return
300308 super ().keyPressEvent (event )
301309 cursor = self .textCursor ()
302310 cursor .insertText (bracket_pairs [event .text ()])
@@ -383,8 +391,55 @@ def matchBrackets(self):
383391
384392 self .highlightCurrentLine ()
385393
394+ @staticmethod
395+ def _build_string_comment_mask (text : str ) -> list :
396+ """Gibt eine Bool-Maske zurück: True an jeder Position die in einem String oder Kommentar liegt."""
397+ mask = [False ] * len (text )
398+ i = 0
399+ n = len (text )
400+ while i < n :
401+ # Triple-quotes (muessen vor single-quotes geprueft werden)
402+ for delim in ('"""' , "'''" ):
403+ if text [i :i + 3 ] == delim :
404+ end = text .find (delim , i + 3 )
405+ if end == - 1 :
406+ end = n - 3
407+ end += 3
408+ for j in range (i , min (end , n )):
409+ mask [j ] = True
410+ i = end
411+ break
412+ else :
413+ # Single-line comment
414+ if text [i ] == '#' :
415+ j = i
416+ while j < n and text [j ] != '\n ' :
417+ mask [j ] = True
418+ j += 1
419+ i = j
420+ # Single/double quoted string
421+ elif text [i ] in ('"' , "'" ):
422+ q = text [i ]
423+ mask [i ] = True
424+ i += 1
425+ while i < n and text [i ] != q :
426+ if text [i ] == '\\ ' :
427+ mask [i ] = True
428+ i += 1 # escape char
429+ if i < n :
430+ mask [i ] = True
431+ i += 1
432+ if i < n :
433+ mask [i ] = True # closing quote
434+ i += 1
435+ else :
436+ i += 1
437+ return mask
438+
386439 def find_matching_bracket (self , text : str , pos : int , bracket : str ) -> int :
387- """Findet die passende Klammer"""
440+ """Findet die passende Klammer; ignoriert Strings und Kommentare."""
441+ mask = self ._build_string_comment_mask (text )
442+
388443 if bracket in self .OPEN_BRACKETS :
389444 # Suche vorwärts
390445 target = self .BRACKETS [bracket ]
@@ -397,20 +452,21 @@ def find_matching_bracket(self, text: str, pos: int, bracket: str) -> int:
397452 direction = - 1
398453 start = pos - 1
399454 end = - 1
400-
455+
401456 count = 1
402457 i = start
403-
458+
404459 while i != end :
405- char = text [i ]
406- if char == bracket :
407- count += 1
408- elif char == target :
409- count -= 1
410- if count == 0 :
411- return i
460+ if not mask [i ]:
461+ char = text [i ]
462+ if char == bracket :
463+ count += 1
464+ elif char == target :
465+ count -= 1
466+ if count == 0 :
467+ return i
412468 i += direction
413-
469+
414470 return None
415471
416472 def emitCursorPosition (self ):
@@ -731,11 +787,11 @@ def __init__(self, editor: 'CodeEditor', parent=None):
731787 self .setHorizontalScrollBarPolicy (Qt .ScrollBarAlwaysOff )
732788 self .setTextInteractionFlags (Qt .NoTextInteraction )
733789 self .setCursor (Qt .PointingHandCursor )
734-
790+
735791 # Sehr kleine Schrift für Minimap
736792 font = QFont ("Consolas" , 1 )
737793 self .setFont (font )
738-
794+
739795 # Styling
740796 self .setStyleSheet ("""
741797 QPlainTextEdit {
@@ -744,21 +800,31 @@ def __init__(self, editor: 'CodeEditor', parent=None):
744800 border-left: 1px solid #333;
745801 }
746802 """ )
747-
803+
748804 self .setFixedWidth (80 )
749805 self .setLineWrapMode (QPlainTextEdit .NoWrap )
750-
806+
751807 # Viewport-Rechteck
752808 self .viewport_rect = QRect ()
753-
809+
810+ # Debounce-Timer für textChanged (verhindert Update bei jedem Tastendruck)
811+ self ._update_timer = QTimer ()
812+ self ._update_timer .setSingleShot (True )
813+ self ._update_timer .setInterval (300 )
814+ self ._update_timer .timeout .connect (self ._do_update_content )
815+
754816 # Verbindungen
755- self .editor .textChanged .connect (self .update_content )
817+ self .editor .textChanged .connect (self ._update_timer . start )
756818 self .editor .verticalScrollBar ().valueChanged .connect (self .update_viewport )
757-
819+
758820 self .update_content ()
759821
760822 def update_content (self ):
761- """Aktualisiert den Minimap-Inhalt"""
823+ """Aktualisiert den Minimap-Inhalt sofort (z.B. beim ersten Laden)."""
824+ self ._do_update_content ()
825+
826+ def _do_update_content (self ):
827+ """Führt die eigentliche Minimap-Aktualisierung durch."""
762828 self .setPlainText (self .editor .toPlainText ())
763829 self .update_viewport ()
764830
@@ -831,13 +897,19 @@ def __init__(self, editor: 'CodeEditor'):
831897 self .editor = editor
832898 self .folded_blocks = set () # Set von gefalteten Block-Nummern
833899 self .foldable_blocks = {} # {block_number: end_block_number}
834-
900+
835901 self .setFixedWidth (14 )
836902 self .setCursor (Qt .PointingHandCursor )
837-
903+
904+ # Debounce-Timer für textChanged (verhindert Update bei jedem Tastendruck)
905+ self ._fold_timer = QTimer ()
906+ self ._fold_timer .setSingleShot (True )
907+ self ._fold_timer .setInterval (300 )
908+ self ._fold_timer .timeout .connect (self .update_foldable_blocks )
909+
838910 # Verbindungen
839911 self .editor .blockCountChanged .connect (self .update_foldable_blocks )
840- self .editor .textChanged .connect (self .update_foldable_blocks )
912+ self .editor .textChanged .connect (self ._fold_timer . start )
841913
842914 def update_foldable_blocks (self ):
843915 """Ermittelt faltbare Blöcke (def, class, if, for, etc.)"""
@@ -3435,9 +3507,9 @@ def run_script_external(self):
34353507 tmp_path .write_text (code , encoding = 'utf-8' )
34363508
34373509 if sys .platform == "win32" :
3438- subprocess .Popen (f' start cmd /k python " { tmp_path } " ' , shell = True )
3510+ subprocess .Popen ([ 'cmd' , '/c' , ' start' , '' , ' cmd' , '/k' , ' python' , str ( tmp_path )] )
34393511 else :
3440- subprocess .Popen (['x-terminal-emulator' , '-e' , f 'python3 " { tmp_path } "' ])
3512+ subprocess .Popen (['x-terminal-emulator' , '-e' , 'python3' , str ( tmp_path ) ])
34413513
34423514 def build_exe (self ):
34433515 if not shutil .which ("pyinstaller" ):
@@ -3492,7 +3564,7 @@ def scan_external_tools(self):
34923564
34933565 def run_external_tool (self , path ):
34943566 if sys .platform == "win32" :
3495- subprocess .Popen (f' start cmd /k python " { path } " ' , shell = True )
3567+ subprocess .Popen ([ 'cmd' , '/c' , ' start' , '' , ' cmd' , '/k' , ' python' , str ( path )] )
34963568 else :
34973569 subprocess .Popen (['python3' , str (path )])
34983570
0 commit comments