
    Jej                         d Z ddlZddlmZ ddlmZmZ ddlm	Z	m
Z
mZmZmZ ddlmZ ddlmZ ddZd Z G d	 d
e
      Z G d de      Z G d de      Z G d de      ZddZy)a  
This module contains utility functions/classes for Spyder's Editor.

Adapted from pyqode/core/api/utils.py of the
`PyQode project <https://github.com/pyQode/pyQode>`_.
Original file:
<https://github.com/pyQode/pyqode.core/blob/master/pyqode/core/api/utils.py>
    N)QTimerQt)QColorQTextBlockUserDataQTextCursor
QTextBlockQTextDocument)to_text_string)encodingc                     t        |       } | j                         dkD  r| j                  |      S | t        d      k(  rt        t        d      |dz         S | j	                  |dz         S )a1  
    Return color that is lighter or darker than the base color.

    If base_color.lightness is higher than 128, the returned color is darker
    otherwise is is lighter.

    :param base_color: The base color to drift from
    ;:param factor: drift factor (%)
    :return A lighter or darker color.
       z#000000z#101010   
   )r   	lightnessdarkerdrift_colorlighter)
base_colorfactors     D/usr/lib/python3/dist-packages/spyder/plugins/editor/utils/editor.pyr   r   !   sh     
#J#  ((	**vi0&2+>>%%frk22    c                 b    | j                         xr t        | j                         t              S )a4  
    Check if the block is safe to work with.

    A BlockUserData must have been set on the block while it was known to be
    safe.

    If an editor is cleared by editor.clear() or editor.set_text() for example,
    all old blocks will continue to report block.isValid() == True, but will
    raise a segmentation fault on almost all methods.

    One way to check if a block is valid is that the userData is reset to None
    or QTextBlockUserData. So if a block is known to have setUserData to
    BlockUserData, this fact can be used to check the block.
    )isValid
isinstanceuserDataBlockUserData)blocks    r   is_block_safer   6   s#     ==?Jz%..*:MJJr   c                       e Zd Z	 	 ddZd Zy)r   Nc                     t        j                  |        || _        d| _        d | _        g | _        g | _        d| _        || _        d | _	        d | _
        || _        || _        y )NF )r   __init__editor
breakpointbreakpoint_condition	bookmarkscode_analysistodocoloroedataimport_statementselection_startselection_end)selfr#   r)   r,   r-   s        r   r"   zBlockUserData.__init__I   sb    ##D)$(!	
 $.*r   c                 6   | j                   | j                  y| j                  j                         }| j                  j	                         }|j                  | j                   d         }|j                  |j                                |j                  t        j                         |j                  t        j                  | j                   d          |j                  | j                  d         }|j                  |j                         t        j                         |j                  t        j                  t        j                         |j                  t        j                  | j                  d   t        j                         t        |      S )zt
        Function to compute the selection.

        This is slow to call so it is only called when needed.
        Nline	character)n)mode)r2   r3   )r,   r-   r#   document
textCursorfindBlockByNumbersetPositionpositionmovePositionr   StartOfBlockNextCharacter
KeepAnchor)r.   r4   cursorr   block2s        r   
_selectionzBlockUserData._selectionX   sO    '4+=+=+E;;'')'')**4+?+?+GH5>>+,K445%%)=)=k)J 	 	L++v&(6??,k.D.DE$$;+A+A 	 	C%%););K)H'' 	 	) 6""r   )NNN)__name__
__module____qualname__r"   r?    r   r   r   r   H   s    ;?#+#r   r   c                   *    e Zd ZdZddZd Zd Zd Zy)DelayJobRunneraY  
    Utility class for running job after a certain delay.
    If a new request is made during this delay, the previous request is dropped
    and the timer is restarted for the new request.

    We use this to implement a cooldown effect that prevents jobs from being
    executed while the IDE is not idle.

    A job is a simple callable.
    c                     t               | _        || _        | j                  j                  j	                  | j
                         g | _        i | _        d | _        y)z
        :param delay: Delay to wait before running the job. This delay applies
        to all requests and cannot be changed afterwards.
        c                      y NrC   )xs    r   <lambda>z)DelayJobRunner.__init__.<locals>.<lambda>   s    r   N)	r   _timerdelaytimeoutconnect_exec_requested_job_args_kwargs_job)r.   rL   s     r   r"   zDelayJobRunner.__init__}   sG    
 h
##D$<$<=
"	r   c                     | j                          || _        || _        || _        | j                  j                  | j                         y)a]  
        Request a job execution.

        The job will be executed after the delay specified in the
        DelayJobRunner contructor elapsed if no other job is requested until
        then.

        :param job: job.
        :type job: callable
        :param args: job's position arguments
        :param kwargs: job's keyworded arguments
        N)cancel_requestsrR   rP   rQ   rK   startrL   )r.   jobargskwargss       r   request_jobzDelayJobRunner.request_job   s;     		
$**%r   c                 b    | j                   j                          d| _        d| _        d| _        y)zCancels pending requests.N)rK   stoprR   rP   rQ   r.   s    r   rT   zDelayJobRunner.cancel_requests   s'    	
r   c                     | j                   j                          	  | j                  | j                  i | j                   y# t
        $ r Y yw xY w)z6Execute the requested job after the timer has timeout.N)rK   r[   rR   rP   rQ   KeyErrorr\   s    r   rO   z"DelayJobRunner._exec_requested_job   sF    	DIItzz2T\\2 	 	s   &A 	AAN)i  )r@   rA   rB   __doc__r"   rY   rT   rO   rC   r   r   rE   rE   r   s    	
#&&	r   rE   c                       e Zd ZdZed        Zd ZddZd Zd Z	ddZ
d	 Zd
 Zd Zd Zd Zd Zd Zd Zd Zd Zd ZddZd Zd Zd ZddZd ZddZy) 
TextHelperz
    Text helper helps you manipulate the content of CodeEditor and extends the
    Qt text api for an easier usage.

    FIXME: Some of this methods are already implemented in CodeEditor, move
    and unify redundant methods.
    c                 Z    	 | j                         S # t        $ r | j                   cY S w xY wrH   )_editor_ref	TypeErrorr\   s    r   _editorzTextHelper._editor   s1    	$##%% 	$###	$s    **c                 f    	 t        j                  |      | _        y# t        $ r
 || _        Y yw xY w)z%:param editor: The editor to work on.N)weakrefrefrc   rd   )r.   r#   s     r   r"   zTextHelper.__init__   s/    	&&{{62D 	&%D	&s    00c                    t        || j                               }| j                  |      }|r'|j                  |j                  |j
                  |       |r'|j                  |j                  |j                  |       |r|j                         }| j                  |       | j                  j                  |       | j                  j                         r| j                  j                          n9| j                  j                  j                  | j                  j                         |rNt!        |      t!        |j#                               v r*| j                  j%                  |t&        j(                         |S )a  
        Moves the text cursor to the specified position.

        :param line: Number of the line to go to (0 based)
        :param column: Optional column number. Default is 0 (start of line).
        :param move: True to move the cursor. False will return the cursor
                     without setting it on the editor.
        :param word: Highlight the word, when moving to the line.
        :return: The new text cursor
        :rtype: QtGui.QTextCursor
        )min
line_count_move_cursor_tor9   Right
MoveAnchorr<   r   unfold_if_colapsedre   setTextCursor	isVisiblecenterCursorfocus_inrN   center_cursor_on_next_focusr
   textfindr	   FindCaseSensitively)r.   r0   column
end_columnmovewordtext_cursorr   s           r   	goto_linezTextHelper.goto_line   s    4*+**40$$[%6%68N8N%+-$$[%6%68N8N%/1%%'E##K0LL&&{3||%%'))+%%--LL<<>t,uzz|0LL!!$(I(IJr   c                    |j                         }	 | j                  j                  j                  d      }|j	                         ry|j                         }t        t        |j                  |               }|j                  }t        |      dkD  rZ|D ]U  }|j                  }| j                  j                         j                  |      }||v s=||   }|sE|j                  |       W | j                  j                  |       | j                  j	                         r| j                  j!                          yy# t"        $ r Y yw xY w)zfUnfold parent fold trigger if the block is collapsed.

        :param block: Block to unfold.
        FoldingPanelNr   )r   re   panelsgetrq   blockNumbersortedlistcurrent_treefolding_statuslenbeginr4   r6   toggle_fold_triggerrp   rr   r^   )	r.   r=   r   folding_panelfold_start_lineenclosing_regionsr   regionfold_statuss	            r   ro   zTextHelper.unfold_if_colapsed   s2   
 	, LL//33NCM  #//1O !'t**?;(= !> +99N$%)/ EF&,llO LL113EE')E&.8&4_&E&)==eDE LL&&v.||%%'))+ (1  		s   %D> >	E
	E
c                 R    | j                   j                         j                         S )zReturns the selected text.)re   r5   selectedTextr\   s    r   selected_textzTextHelper.selected_text
  s    ||&&(5577r   Nc                    | j                   }|s|j                         }|j                  }|j                         x}}|j	                         s|j                  |j                  |j                  d       	 |j                         d   }|j                  }|j                         }||v r
|dk7  r|dk7  s|j                         rn3	 |j                         }|j                  |       |j	                         s|r|j                  |       |j                         s|j                  |j                  |j                  d       |j                         d   }|j                         }||v r
|dk7  r|dk7  s|j                         rn2|j                         }|j                  |       |j                         s|j                  |       |j                  ||j                         |S # t        $ r Y w xY w)a3  
        Gets the word under cursor using the separators defined by
        :attr:`spyder.plugins.editor.widgets.codeeditor.CodeEditor.word_separators`.

        FIXME: This is not working because CodeEditor have no attribute
        word_separators

        .. note: Instead of returning the word string, this function returns
            a QTextCursor, that way you may get more information than just the
            string. To get the word, just call ``selectedText`` on the returned
            value.

        :param select_whole_word: If set to true the whole word is selected,
         else the selection stops at the cursor position.
        :param text_cursor: Optional custom text cursor (e.g. from a
            QTextDocument clone)
        :returns: The QTextCursor that contains the selected word.
           r   r2   t)re   r5   word_separatorsr8   atStartr9   Leftr<   r   isspace
IndexErrorr7   atEndrm   )	r.   select_whole_wordr|   r#   r   end_pos	start_poscharselected_txts	            r   word_under_cursorzTextHelper.word_under_cursor  s   &  ++-K 00)2244)%%'$$  +"8"8!=	"//1!4"("8"8*779 O3%,1D ' $,,.I##I. %%' ##G,!'')(():):)4)?)?D"//1!4*779 O3%,1D%..0''0 "'') 		*)?)?@+  s   5AG# #	G/.G/c                 x    | j                   }|j                  |j                        }| j                  d|      }|S )z
        Selects the word under the **mouse** cursor.

        :return: A QTextCursor with the word under mouse cursor selected.
        T)re   cursorForPosition_last_mouse_posr   r.   r#   r|   s      r   word_under_mouse_cursorz"TextHelper.word_under_mouse_cursorI  s;     ..v/E/EF,,T;?r   c                     | j                   j                         j                         | j                   j                         j                         fS )z
        Returns the QTextCursor position. The position is a tuple made up of
        the line number (0 based) and the column number (0 based).

        :return: tuple(line, column)
        )re   r5   r   columnNumberr\   s    r   cursor_positionzTextHelper.cursor_positionT  s@     '')557'')668: 	:r   c                 (    | j                         d   S )zV
        Returns the text cursor's line number.

        :return: Line number
        r   r   r\   s    r   current_line_nbrzTextHelper.current_line_nbr^       ##%a((r   c                 (    | j                         d   S )zZ
        Returns the text cursor's column number.

        :return: Column number
        r   r   r\   s    r   current_column_nbrzTextHelper.current_column_nbrf  r   r   c                 R    | j                   j                         j                         S )zt
        Returns the line count of the specified editor.

        :return: number of lines in the document.
        )re   r4   
blockCountr\   s    r   rk   zTextHelper.line_countn  s      ||$$&1133r   c                 x    | j                   j                         }|j                  |      }|j                         S )z
        Gets the text of the specified line.

        :param line_nbr: The line number of the text to get

        :return: Entire line's text
        :rtype: str
        )re   r4   r6   ru   )r.   line_nbrdocr   s       r   	line_textzTextHelper.line_textv  s2     ll##%%%h/zz|r   c                 h    | j                         r"| j                  | j                         dz
        S y)z}
        Gets the previous line text (relative to the current cursor pos).
        :return: previous line text (str)
        r   r!   )r   r   r\   s    r   previous_line_textzTextHelper.previous_line_text  s0    
   ">>$"7"7"9A"=>>r   c                 @    | j                  | j                               S )zb
        Returns the text of the current line.

        :return: Text of the current line
        )r   r   r\   s    r   current_line_textzTextHelper.current_line_text  s     ~~d33566r   c                     | j                   }| j                  |      }|j                  |j                         |j	                  |       |j                  |       y)z
        Replace an entire line with ``new_text``.

        :param line_nbr: line number of the line to change.
        :param new_text: The replacement text.

        N)re   rl   selectLineUnderCursor
insertTextrp   )r.   r   new_textr#   r|   s        r   set_line_textzTextHelper.set_line_text  sM     **84;667x([)r   c                     | j                   }|j                         }|j                  |j                  |j                         |j                  |j                         |j                          |j                          |j                  |       y)z&Removes the last line of the document.N)
re   r5   r9   Endrn   r   r   removeSelectedTextdeletePreviousCharrp   r   s      r   remove_last_linezTextHelper.remove_last_line  sm    '')  +2H2HI;667&&(&&([)r   c                     | j                   j                         }| j                   j                         j                  |dz
        }|j	                  |j                                |S )Nr   )re   r5   r4   r6   r7   r8   )r.   r0   r=   r   s       r   rl   zTextHelper._move_cursor_to  sO    ((*%%'99$q&A5>>+,r   c                    | j                   }|dk(  r| j                         dz
  }|dk  rd}| j                  |      }||kD  rQ|j                  |j                  |j
                  ||z
         |j                  |j                  |j
                         n||k  rw|j                  |j                  |j                         |j                  |j                  |j
                  ||z
         |j                  |j                  |j
                         n&|j                  |j                  |j
                         |r|j                  |       |S )a  
        Selects entire lines between start and end line numbers.

        This functions apply the selection and returns the text cursor that
        contains the selection.

        Optionally it is possible to prevent the selection from being applied
        on the code editor widget by setting ``apply_selection`` to False.

        :param start: Start line number (0 based)
        :param end: End line number (0 based). Use -1 to select up to the
            end of the document
        :param apply_selection: True to apply the selection before returning
         the QTextCursor.
        :returns: A QTextCursor that holds the requested selection
        r   r   )re   rk   rl   r9   Downr<   	EndOfLinern   UpStartOfLinerp   )r.   rU   endapply_selectionr#   r|   s         r   select_lineszTextHelper.select_lines  s.   " "9//#a'C19E**51;$$[%5%5%0%;%;S5[J$$[%:%:%0%;%;=5[$$[%:%:%0%;%;=$$[^^%0%;%;US[J$$[%<%<%0%;%;= $$[%:%:%0%;%;=  -r   c                    | j                   }|j                         j                  |      }|j                         rEt	        |j                  |      j                  |j                               j                               S |dk  ryt	        |j                  |j                               j                  |j                               j                               S )a  
        Computes line position on Y-Axis (at the center of the line) from line
        number.

        :param line_number: The line number for which we want to know the
                            position in pixels.
        :return: The center position of the line.
        r   )re   r4   r6   r   intblockBoundingGeometry
translatedcontentOffsettoppreviousbottom)r.   line_numberr#   r   s       r   line_pos_from_numberzTextHelper.line_pos_from_number  s     !33K@==?v33E:EE$$&((+/ /!v33 "",*V-A-A-C"DVVXO Or   c                     | j                   }|j                         j                         }|j                  D ]  \  }}}||cxk  r||z   k  s|c S   y)z
        Returns the line number from the y_pos.

        :param y_pos: Y pos in the editor
        :return: Line number (0 based), -1 if out of range
        r   )re   fontMetricsheightvisible_blocks)r.   y_posr#   r   r   r0   r   s          r   line_nbr_from_positionz!TextHelper.line_nbr_from_position  s`     ##%,,. & 5 5 	Cue+sV|+ ,	 r   c                     | j                   j                         }|j                  |j                         | j                   j	                         j                  |j                         |j                                y)zU
        Marks the whole document as dirty to force a full refresh. **SLOW**
        N)re   r5   r   Documentr4   markContentsDirtyselectionStartselectionEnd)r.   r|   s     r   mark_whole_doc_dirtyzTextHelper.mark_whole_doc_dirty  s]     ll--/;//011+2L2L2N2=2J2J2L	Nr   c                 2   | j                   j                         }|r |j                         }|j                         }|j	                  |       |r-|j                         |j                  |j                         | j                   j                  |       y)a   
        Inserts text at the cursor position.

        :param text: text to insert
        :param keep_position: Flag that specifies if the cursor position must
            be kept. Pass False for a regular insert (the cursor will be at
            the end of the inserted text).
        N)re   r5   r   r   r   r7   r<   rp   )r.   ru   keep_positionr|   ses         r   insert_textzTextHelper.insert_text  s|     ll--/**,A((*At$##A&##A{'='=>"";/r   c                    d }| j                   j                         }g }d}|j                  |d|      }|}	|j                         s |||	      rt	        |      }|j                  |j                         |j                         f       |j                  |j                         dz          |j                  |||      }|j                         s||fS )a-  
        Searches a text in a text document.

        :param text_cursor: Current text cursor
        :param search_txt: Text to search
        :param search_flags: QTextDocument.FindFlags
        :returns: the list of occurrences, the current occurrence index
        :rtype: tuple([], int)

        c                     |j                         | j                         k\  xr! |j                         | j                         k  S )z
            Compares two QTextCursor.

            :param cursor_a: cursor a
            :param cursor_b: cursor b

            :returns; True if both cursor are identical (same position, same
                selection)
            )r   r   )cursor_acursor_bs     r   compare_cursorsz/TextHelper.search_text.<locals>.compare_cursors&  sD     ++-1H1H1JJ G))+x/D/D/FFHr   r   r   r   )
re   r4   rv   isNullr   appendr   r   r7   r8   )
r.   r|   
search_txtsearch_flagsr   text_documentoccurrencesindexr=   original_cursors
             r   search_textzTextHelper.search_text  s    	H --/##J<@%--/v7K( 5 5 7 & 3 3 5 7 8v0145"''
FLIF --/ E!!r   c                    |g d}d}d}t        |t              r-t        |j                               dz
  }|j	                         }nQt        |t
              rA|j                         }|j                         |j                         z
  }|j	                         }||j                         }| j                  j                  }|r|j                  j                  }|D ]  }	|	j                  |cxk  r|	j                  |	j                  z   k  s0n 3|D ]K  }
|	j                  j!                         |	j                  j"                  k(  }||
   |	j                  k(  sG|sJ  y  y)a/  
        Checks if a block/cursor is a string or a comment.
        :param cursor_or_block: QTextCursor or QTextBlock
        :param formats: the list of color scheme formats to consider. By
            default, it will consider the following keys: 'comment', 'string',
            'docstring'.
        N)commentstring	docstringr   r   TF)r   r   r   ru   layoutr   r   r8   additionalFormatsre   syntax_highlightercolor_schemeformatsrU   lengthformat
objectType
UserObject)r.   cursor_or_blockr   r   posbadditional_formatsshref_formatsrfmt_typeis_user_objs               r   is_comment_or_stringzTextHelper.is_comment_or_stringA  s6    ?8Goz2o**,-1C$++-F5%%'A!**,qzz|;CXXZF!'!9!9!;00B oo55+ ,Aww#<188);<(/ ,H+,88+>+>+@+,88+>+>,?K +H 5 A$/'+,, r   )r   r   Tr!   )FN)r   r   T)TrH   )r@   rA   rB   r_   propertyre   r"   r}   ro   r   r   r   r   r   r   rk   r   r   r   r   r   rl   r   r   r   r   r   r   r  rC   r   r   ra   ra      s     $ $& D!,F89v	:))47**)VO(N0&$"L r   ra   c                       e Zd ZdZed        Zed        Zed        Zed        Zed        Z	ed        Z
ed        Zy	)
TextBlockHelperax  
    Helps retrieving the various part of the user state bitmask.

    This helper should be used to replace calls to
    ``QTextBlock.setUserState``/``QTextBlock.getUserState`` as well as
    ``QSyntaxHighlighter.setCurrentBlockState``/
    ``QSyntaxHighlighter.currentBlockState`` and
    ``QSyntaxHighlighter.previousBlockState``.

    The bitmask is made up of the following fields:

        - bit0 -> bit26: User state (for syntax highlighting)
        - bit26: fold trigger state
        - bit27-bit29: fold level (8 level max)
        - bit30: fold trigger flag

        - bit0 -> bit15: 16 bits for syntax highlighter user state (
          for syntax highlighting)
        - bit16-bit25: 10 bits for the fold level (1024 levels)
        - bit26: 1 bit for the fold trigger flag (trigger or not trigger)
        - bit27: 1 bit for the fold trigger state (expanded/collapsed)

    c                 @    | y| j                         }|dk(  r|S |dz  S )z
        Gets the user state, generally used for syntax highlighting.
        :param block: block to access
        :return: The block state

        r     	userStater   states     r   	get_statezTextBlockHelper.get_state|  s/     =!B;Lz!!r   c                 x    | y| j                         }|dk(  rd}|dz  }|dz  }||z  }| j                  |       y)z
        Sets the user state, generally used for syntax highlighting.

        :param block: block to modify
        :param state: new state value.
        :return:
        Nr   r   i  r  r  setUserState)r   r  
user_statehigher_parts       r   	set_statezTextBlockHelper.set_state  sQ     =__&
J :-5!r   c                 F    | y| j                         }|dk(  rd}|dz  dz	  S )z{
        Gets the block fold level.

        :param block: block to access.
        :returns: The block fold level
        r   r   i     r  r  s     r   get_fold_lvlzTextBlockHelper.get_fold_lvl  s4     =!B;E
"r))r   c                     | y| j                         }|dk(  rd}|dk\  rd}|dz  }||dz  z  }| j                  |       y)z
        Sets the block fold level.

        :param block: block to modify
        :param val: The new fold level [0-7]
        Nr   r   i  i |r  r  r   valr  s      r   set_fold_lvlzTextBlockHelper.set_fold_lvl  sV     =!B;E%<C5!r   c                 R    | y| j                         }|dk(  rd}t        |dz        S )z
        Checks if the block is a fold trigger.

        :param block: block to check
        :return: True if the block is a fold trigger (represented as a node in
            the fold panel)
        Fr   r   i   )r  boolr  s     r   is_fold_triggerzTextBlockHelper.is_fold_trigger  s4     =!B;EEJ&''r   c                     | y| j                         }|dk(  rd}|dz  }|t        |      dz  z  }| j                  |       y)z
        Set the block fold trigger flag (True means the block is a fold
        trigger).

        :param block: block to set
        :param val: value to set
        Nr   r   i{   r  r   r  r"  s      r   set_fold_triggerz TextBlockHelper.set_fold_trigger  sN     =!B;ESR5!r   c                     | y| j                         }|dk(  rd}|dz  }|t        |      dz  z  }| j                  |       y)z
        Sets the fold trigger state (collapsed or expanded).

        :param block: The block to modify
        :param val: The new trigger state (True=collapsed, False=expanded)
        Nr   r   iw   r*  r"  s      r   set_collapsedzTextBlockHelper.set_collapsed  sN     =!B;ESR5!r   N)r@   rA   rB   r_   staticmethodr  r  r   r$  r'  r+  r.  rC   r   r   r  r  d  s    . " " " "$ * * " "$ ( ( " "" " "r   r  c                 *   t        j                  |       d   }|j                  d      r|dd }|}|s`|t        j                  |       \  }}|j                         D ]3  }|j                         s|j                  d      r|dd }d|v s/d}2 |S  |S )zGet file language from filenamer   .Nz#!   python)ospsplitext
startswithr   read
splitlinesstrip)filenameru   extlanguage_encr0   shebangs          r   get_file_languager?    s    
,,x
 
#C
~~c!"gH<!x0JD$OO% 	D::<t$qr(w&'HO	 Or   )n   rH   )r_   rg   os.pathpathr4  qtpy.QtCorer   r   
qtpy.QtGuir   r   r   r   r	   spyder.py3compatr
   spyder.utilsr   r   r   r   objectrE   ra   r  r?  rC   r   r   <module>rH     sr      #' ' , !3*K$'#& '#T:V :zr rjM"f M"`r   