
    f                     r   d dl mZmZmZ d dlZd dlZd dlZd dlZd dlm	Z	  e	        	 d dl
mZ d dlmZ d dlmZmZmZ  ej(                  d      Z G d d	      Z G d
 d      Z G d de      Z G d de      Z G d de      Z G d de      Z G d de      Z G d d      Zy# e$ r
ZY dZ[dZ[ww xY w)    )divisionprint_functionunicode_literalsN)require_gi_versions)AtspiTextSpan)KeyCode	Modifiersunicode_str
TextDomainc                   "    e Zd ZdZd Zd Zd Zy)TextDomainsz, Collection of all recognized text domains. c                 l    t               t               t               t               t	               g| _        y N)DomainTerminal	DomainURLDomainPasswordDomainGenericText	DomainNOP_domainsselfs    4/usr/lib/python3/dist-packages/Onboard/TextDomain.py__init__zTextDomains.__init__+   s)     ()"')*,"    c                 R    | j                   D ]  } |j                  di |s|c S  y )N )r   matches)r   kwargsdomains      r   
find_matchzTextDomains.find_match5   s0    mm 	Fv~~''	 r   c                      | j                   d   S )N)r   r   s    r   get_nop_domainzTextDomains.get_nop_domain;   s    }}R  r   N)__name__
__module____qualname____doc__r   r"   r%   r   r   r   r   r   (   s    6!r   r   c                       e Z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 Zd Zd Zd Zd Z ej(                  dej*                        Zd Zy)r   zN
    Abstract base class as a catch-all for domain specific functionalty.
    c                 "    t               | _        y r   )PartialURLParser_url_parserr   s    r   r   zTextDomain.__init__D   s    +-r   c                 *    d|j                  dg       v S )NText
interfacesgetr   r    s     r   r   zTextDomain.matchesG   s     L"555r   c                      y)z: Called on being selected as the currently active domain. Nr   r   s    r   init_domainzTextDomain.init_domainL   s    r   c                     t               S r   )NotImplementedErrorr   keyboard
accessibles      r   read_contextzTextDomain.read_contextP   s    "$$r   c                      y)N r   r   s    r   get_text_begin_markerz TextDomain.get_text_begin_markerS   s    r   c                 T   d}t        j                  d|      }|r|d   }| j                  j                  |      r| j                  j	                  |      }|S | j                  |      }|r|}| j                  j                  |      r d|z   }| j                  j	                  |      }|S )a  
        Get word separator to add after inserting a prediction choice.

        Doctests:

        >>> from os.path import join

        # URL
        >>> d = DomainGenericText()
        >>> d.get_auto_separator("word http")
        ' '
        >>> d.get_auto_separator("word http://www")
        '.'
        >>> d.get_auto_separator("word http://www.domain.org/path")
        '/'
        >>> d.get_auto_separator("word http://www.domain.org/path/document.ext")
        ''

        # filename
        >>> import tempfile
        >>> from os.path import abspath, dirname, join
        >>> def touch(fn):
        ...     with open(fn, mode="w") as f: n = f.write("")
        >>> td = tempfile.TemporaryDirectory(prefix="test_onboard _")
        >>> dir = td.name

        >>> touch(join(dir, "onboard-defaults.conf.example"))
        >>> touch(join(dir, "onboard-defaults.conf.example.another"))
        >>>
        >>> d.get_auto_separator("/etc")
        '/'
        >>> d.get_auto_separator(join(dir, "onboard-defaults"))
        '.'
        >>> d.get_auto_separator(join(dir, "onboard-defaults.conf"))
        '.'
        >>> d.get_auto_separator(join(dir, "onboard-defaults.conf.example"))
        ''
        >>> d.get_auto_separator(join(dir, "onboard-defaults.conf.example.another"))
        ' '

        # filename in directory with spaces
        >>> import tempfile
        >>> td = tempfile.TemporaryDirectory(prefix="test onboard _")
        >>> dir = td.name
        >>> fn = os.path.join(dir, "onboard-defaults.conf.example")
        >>> touch(fn)
        >>> d.get_auto_separator(join(dir, "onboard-defaults"))
        '.'

        # no false positives for valid filenames before the current token
        >>> d.get_auto_separator(dir + "/onboard-defaults no-file")
        ' '
         z(\s+)r$   file://)resplitr-   is_maybe_urlget_auto_separator_search_valid_file_name_is_maybe_filename)r   context	separatorstringsstringfnurls          r   rE   zTextDomain.get_auto_separatorV   s    l 	 ((8W-R[F,,V4 ,,??G	  11':F##66v>#f,C $ 0 0 C CC HIr   c                 v   d}t        t        |t        |                  D ]  }dj                  |d|z
  d       }| j                  j                  |      s6t        j                  j                  |      sVt        j                  j                  |      r|c S t        j                  |dz         }|s|c S  y)aT  
        Search for a valid filename backwards across separators.

        Doctests:
        >>> import tempfile, re, os.path
        >>>
        >>> d = DomainGenericText()
        >>>
        >>> td = tempfile.TemporaryDirectory(prefix="test onboard _")
        >>> dir = td.name
        >>> fn1 = os.path.join(dir, "file")
        >>> fn2 = os.path.join(dir, "file with many spaces")
        >>> with open(fn1, mode="w") as f: n = f.write("")
        >>> with open(fn2, mode="w") as f: n = f.write("")

        # simple file in dir with spaces must return as filename
        >>> strings = re.split(r'(\s+)', fn1)
        >>> "/test onboard" in d._search_valid_file_name(strings)
        True

        # file with spaces in dir with spaces must return as filename
        >>> strings = re.split(r'(\s+)', fn2)
        >>> "/test onboard" in d._search_valid_file_name(strings)
        True

        # random string after a valid file must not be confused with a filename
        >>> strings = re.split(r'(\s+)', fn2 + " no-file")
        >>> d._search_valid_file_name(strings) is None
        True
           r=   r$   N*)rangeminlenjoinr-   rG   ospathisabsexistsglob)r   rJ   max_sectionsirL   filess         r   rF   z"TextDomain._search_valid_file_name   s    @ s<W67 	AA(B 2226ww}}R  77>>"%I 28,I	" r   c                    |j                         }|j                         }|j                         |z
  }|j                         |z
  }| j	                  |      \  }}t        |      D ]v  \  }}	||	d   k  s||	d   kD  s||   }
||   }| j                  j                  |
      s| j                  j                  |
      sYt        ||d         }t        ||d         }x ||z   ||z
  ||| fS )a  
        Grow span before learning to include e.g. whole URLs.

        Doctests:
        >>> d = DomainGenericText()

        # Span doesn't grow for simple words
        >>> d.grow_learning_span(TextSpan(8, 1, "word1 word2 word3"))
        (8, 1, 'r')

        # Span grows to include a complete URL
        >>> d.grow_learning_span(TextSpan(13, 1, "http://www.domain.org"))
        (0, 21, 'http://www.domain.org')

        # Span grows to include multiple complete URLs
        >>> d.grow_learning_span(TextSpan(19, 13, "http://www.domain.org word http://slashdot.org"))
        (0, 46, 'http://www.domain.org word http://slashdot.org')

        # Span grows to include a complete filename
        >>> d.grow_learning_span(TextSpan(10, 1, "word1 /usr/bin/bash word2"))
        (6, 13, '/usr/bin/bash')

        # Edge cases
        >>> d.grow_learning_span(TextSpan(6, 0, "word1 /usr/bin/bash word2"))
        (6, 0, '')
        >>> d.grow_learning_span(TextSpan(19, 0, "word1 /usr/bin/bash word2"))
        (19, 0, '')
        >>> d.grow_learning_span(TextSpan(6, 1, "word1 /usr/bin/bash word2"))
        (6, 13, '/usr/bin/bash')
        >>> d.grow_learning_span(TextSpan(18, 1, "word1 /usr/bin/bash word2"))
        (6, 13, '/usr/bin/bash')

        # Large text with text offset>0: returned position must be offset too
        >>> d.grow_learning_span(TextSpan(116, 1,
        ...     "word1 /usr/bin/bash word2", 100))
        (106, 13, '/usr/bin/bash')
           r   )get_text
text_beginbeginend_split_growth_sections	enumerater-   rD   rG   rR   max)r   	text_spantextoffsetra   rb   sectionsspansr[   ssectionspans               r   grow_learning_spanzTextDomain.grow_learning_span   s    L ##%%%'"V+6)55d;%e$ 	,DAqqt|ad
"1+Qx##009##66w?tAw/Ec47+C	, v~sU{DsO;;r   c                      yNTr   )r   r:   poslengths       r   can_record_insertionzTextDomain.can_record_insertion      r   c                      yrp   r   r   s    r   can_give_keypress_feedbackz%TextDomain.can_give_keypress_feedback  rt   r   c                      yNFr   r   section_spans     r   can_spell_checkzTextDomain.can_spell_check      r   c                      yrx   r   ry   s     r   can_auto_correctzTextDomain.can_auto_correct  r|   r   c                      yrx   r   r   has_begin_of_texts     r   can_auto_punctuatezTextDomain.can_auto_punctuate  r|   r   c                      y)6 Can give word suggestions before typing has started? Tr   r   s    r   can_suggest_before_typingz$TextDomain.can_suggest_before_typing  s    r   c                      y)NTNr   r   keycodemod_masks      r   handle_key_presszTextDomain.handle_key_press   s    r   z	[^\s?#@]+c                     | j                   j                  |      }g }g }|D ]@  }|j                  |j                                |j                  |j	                                B ||fS )a  
        Split text at whitespace and other delimiters where
        growing learning spans should stop.

        Doctests:
        >>> d = DomainGenericText()
        >>> d._split_growth_sections("word1 www.domain.org word2. http://test")
        (['word1', 'www.domain.org', 'word2.', 'http://test'], [(0, 5), (6, 20), (21, 27), (28, 39)])

        >>> d._split_growth_sections("http://www.domain.org/?p=1#anchor")
        (['http://www.domain.org/', 'p=1', 'anchor'], [(0, 22), (23, 26), (27, 33)])

        >>> d._split_growth_sections("http://user:pass@www.domain.org")
        (['http://user:pass', 'www.domain.org'], [(0, 16), (17, 31)])
        )_growth_sections_patternfinditerappendgrouprm   )r   rg   r   tokensrj   ms         r   rc   z!TextDomain._split_growth_sections%  sc      //88> 	#AMM!'')$LL"	# u}r   N)r&   r'   r(   r)   r   r   r5   r;   r>   rE   rF   rn   rs   rv   r{   r~   r   r   r   rB   compileDOTALLr   rc   r   r   r   r   r   ?   st    .6
%GT2h7<r  *rzz,		Br   c                   "    e Zd ZdZd Zd Zd Zy)r   z+ Do-nothing domain, no focused accessible. c                      yrp   r   r3   s     r   r   zDomainNOP.matchesA  rt   r   c                 "    dddt               ddfS )Nr=   r   Fr   r8   s      r   r;   zDomainNOP.read_contextD  s    2q(*eQ..r   c                      y)z@ Get word separator to add after inserting a prediction choice. r=   r   r   rH   s     r   rE   zDomainNOP.get_auto_separatorG  s    r   N)r&   r'   r(   r)   r   r;   rE   r   r   r   r   r   >  s    5/r   r   c                       e Zd ZdZd Zd Zy)r   z( Do-nothing domain for password entries c                 Z    |j                  d      t        j                  j                  k(  S Nrole)r2   r   RolePASSWORD_TEXTr3   s     r   r   zDomainPassword.matchesO  s     zz&!UZZ%=%===r   c                      yrx   r   r   s    r   rv   z)DomainPassword.can_give_keypress_feedbackR  r|   r   N)r&   r'   r(   r)   r   rv   r   r   r   r   r   L  s    2>r   r   c                   4    e Zd ZdZd Zd Zd Zd Zd Zd Z	y)	r   z' Default domain for generic text entry c                 .    t        j                  | fi |S r   )r   r   r3   s     r   r   zDomainGenericText.matchesY  s    !!$1&11r   c                 l   |j                         }	 |j                         }|@|j                         }|dk  r't        j	                  dj                  ||             |}||f}|j                  |d   t        j                  j                        }t        |j                        j                  dd      }t        |d   |j                   z
  d      }	t        |d   dz
  d      }
t#        |d   dz   |      }	 |j%                  |
|      }t        |      }t'        |d   |d
   |d   z
  ||
      }|d|d   |
z
   }|
dk(  }d}|||	|||fS # t        $ r+}t        j                  dt        |      z          Y d}~yd}~ww xY w# t        $ r+}t        j                  d	t        |      z          Y d}~yd}~ww xY w)z0 Extract prediction context from the accessible Nr   zDomainGenericText.read_context(): Atspi.Text.get_caret_offset() returned invalid {}. Pretending the cursor is at the end of the text at offset {}.z(DomainGenericText.read_context(), text: 
r=      d   z)DomainGenericText.read_context(), text2: r^   )get_selectionget_character_countget_caret_offset_loggerwarningformatget_text_at_offsetr   TextBoundaryType
LINE_START	Exceptioninfor   contentreplacere   start_offsetrR   r_   r	   )r   r9   r:   	selectioncountrh   rexline
line_caretra   rb   rg   selection_spanrH   begin_of_textbegin_of_text_offsets                    r   r;   zDomainGenericText.read_context\  s    ,,.		224E #446
 A:OO %@
 &,VFE%:< #F#V,	--!e44??AA 199%--dB716:
IaL3&*IaL3&.	&&uc2D 4  ")A,	!y|0K"&/,	!u,-
 z>35 	5;  	LLC$R) *		  	LLD$R) *		s0   A?E 7E? 	E<!E77E<?	F3!F..F3c                     |j                         }| j                  j                  |       xr | j                  j                  |       S )aF  
        Can we auto-correct this span?.

        Doctests:
        >>> d = DomainGenericText()
        >>> d.can_spell_check(TextSpan(0, 3, "abc"))
        True
        >>> d.can_spell_check(TextSpan(4, 3, "abc def"))
        True
        >>> d.can_spell_check(TextSpan(0, 20, "http://www.domain.org"))
        False
        )get_span_textr-   rD   rG   )r   rz   rl   s      r   r{   z!DomainGenericText.can_spell_check  sL     ,,.##0099 @##66w??	@r   c                 $    | j                  |      S )z1
        Can we auto-correct this span?.
        )r{   ry   s     r   r~   z"DomainGenericText.can_auto_correct  s     ##L11r   c                      yrp   r   r   s     r   r   z$DomainGenericText.can_auto_punctuate  rt   r   c                      y)Nz	<bot:txt>r   r   s    r   r>   z'DomainGenericText.get_text_begin_marker      r   N)
r&   r'   r(   r)   r   r;   r{   r~   r   r>   r   r   r   r   r   V  s$    12;5z$2r   r   c                       e Zd ZdZ ed dD              Z ed dD              Zd Zd Zd Z		 dd
Z
d Zd Zd Zd Zd Zd Zd Zy	)r   z. Terminal entry, in particular gnome-terminal c              #   d   K   | ](  }t        j                  |t         j                         * y wr   rB   r   UNICODE.0ps     r   	<genexpr>zDomainTerminal.<genexpr>  s#      1RZZ2::6    .0)	z^gdb$ z^>>> z^In \[[0-9]*\]: z^:z^/z^\?z\$ z# z^.*?@.*?/.*?> c              #   d   K   | ](  }t        j                  |t         j                         * y wr   r   r   s     r   r   zDomainTerminal.<genexpr>  s#      'Qrzz!RZZ'@ 'r   )z^\(.*\)`.*': c                     t        j                  | fi |xr, |j                  d      t        j                  j
                  k(  S r   )r   r   r2   r   r   TERMINALr3   s     r   r   zDomainTerminal.matches  s:    !!$1&1 9zz&!UZZ%8%88	9r   c                      y r   r   r   s    r   r5   zDomainTerminal.init_domain  s    r   c           	         	 |j                         }| j                  |||j                               \  }}}}}	|rd}
|}nd}
d}dj                  |      }dj                  |dd       }t        |d||z   |t        |      z
        }|||	||
|f}|S # t        $ r+}t        j                  dt	        |      z          Y d}~yd}~ww xY w)z@
        Extract prediction context from the accessible
        zDomainTerminal.read_context(): NTFr=   r$   r   )
r   r   r   r   r   _get_text_after_promptget_last_typed_was_separatorrT   r	   rS   )r   r9   r:   rh   r   context_linesprompt_lengthr   
line_startr   r   r   rH   before_liner   results                   r   r;   zDomainTerminal.read_context  s    	002F ''F5579 	C}dJ

  M#- !M#' ''-(ggmCR01!&!"-"4",s;/?"?A 4^!575  	LL:$R) *		s   B 	C!B??CNc                 L   |j                  |t        j                  j                        }t	        |j
                        }|j                  }||z
  }g }d}	|d| }
||d dk(  r|r|j                         r|
dz  }
t        d      D ]  }| j                  |
      rg }d}	 n| j                  |
      }	|j                  d|
|	d        |dk(  r||	d }||	z  }||	z  }|	r nA|j                  |t        j                  j                        }t	        |j
                        }
 ||	|||f}|S )a	  
        Return text from the input area of the terminal after the prompt.

        Doctests:
        >>> class AtspiTextRangeMockup:
        ...     pass
        >>> class AccessibleMockup:
        ...     def __init__(self, text, width):
        ...         self._text = text
        ...         self._width = width
        ...     def get_text_at_offset(self, offset, boundary):
        ...         line = offset // self._width
        ...         lbegin = line * self._width
        ...         r = AtspiTextRangeMockup()
        ...         r.content = self._text[lbegin:lbegin+self._width]
        ...         r.start_offset = lbegin
        ...         return r
        ...     def get_text_before_offset(self, offset, boundary):
        ...         return self.get_text_at_offset(offset - self._width, boundary)
        ...     def is_byobu(self):
        ...         return False

        >>> d = DomainTerminal()

        # Single line
        >>> a = AccessibleMockup("abc$ ls /etc\n", 15)
        >>> d._get_text_after_prompt(a, 12)
        (['ls /etc'], 5, 'ls /etc\n', 5, 7)

        # Two lines
        >>> a = AccessibleMockup("abc$ ls /e"
        ...                      "tc\n", 10)
        >>> d._get_text_after_prompt(a, 12)
        (['ls /e', 'tc'], 5, 'tc\n', 10, 2)

        # Three lines: prompt not detected
        # More that two lines are not supported. The probability of detecting
        # "prompts" in random scrolling data rises with each additional line.
        >>> a = AccessibleMockup("abc$ ls /e"
        ...                      "tc/X11/xor"
        ...                      "g.conf.d\n", 10)
        >>> d._get_text_after_prompt(a, 28)
        (['tc/X11/xor', 'g.conf.d'], 0, 'g.conf.d\n', 20, 8)

        # Two lines with slash at the beginning of the second: detect vi
        # search prompt. Not ideal, but vi is important too.
        >>> a = AccessibleMockup("abc$ ls /etc"
        ...                      "/X11\n", 12)
        >>> d._get_text_after_prompt(a, 16)
        (['X11'], 1, 'X11\n', 13, 3)

        Nr   r@      r   )r   r   r   r   r   r   r   is_byoburQ   _find_blacklisted_prompt_find_promptinsertget_text_before_offset)r   r:   caret_offsetlast_typed_was_separatorr   r   r   r   r   r   lr[   r   s                r   r   z%DomainTerminal._get_text_after_prompt  sV   n ))%00;;=199%^^
!J.
* 
$# HAq 	'A ,,Q/ " $ --a0M  Amn$56AvMN+m+
m+
 11e44??AAAII&A+	'.  
J0r   c                 p    | j                   D ]'  }|j                  |      }|s|j                         c S  y)z
        Search for a prompt and return the offset where the user input starts.
        Until we find a better way just look for some common prompt patterns.
        r   )_prompt_patternssearchrb   r   rH   patternmatchs       r   r   zDomainTerminal._find_prompt]  s;    
 ,, 	#GNN7+Eyy{"	# r   c                 p    | j                   D ]'  }|j                  |      }|s|j                         c S  y r   )_prompt_blacklist_patternsr   rb   r   s       r   r   z'DomainTerminal._find_blacklisted_prompth  s9    66 	#GNN7+Eyy{"	# r   c                      y)Nz
<bot:term>r   r   s    r   r>   z$DomainTerminal.get_text_begin_markero  s    r   c                 H    | j                  ||      \  }}}}}t        |      S r   )r   bool)	r   r:   rh   rr   r   r   r   r   r   s	            r   rs   z#DomainTerminal.can_record_insertionr  s/    
 ''
F; 	C}dJ
M""r   c                      y)r   Fr   r   s    r   r   z(DomainTerminal.can_suggest_before_typingz  s     r   c                     |t         j                  k(  s|t         j                  k(  ry|t         j                  k(  r|t        j
                  z  ryy)z
        End recording and learn when pressing [Return]
        because text that is scrolled out of view is
        lost in a terminal.
        )FT)FFr   )r
   ReturnKP_EnterCr   CTRLr   s      r   r   zDomainTerminal.handle_key_press  s>     gnn$g&&&		!h&?r   c                     | S )z
        Only auto-punctuate in Terminal when no prompt was detected.
        Intention is to allow punctuation assistance in editors, but disable
        it when entering commands at the prompt, e.g. for "cd ..".
        r   r   s     r   r   z!DomainTerminal.can_auto_punctuate  s     %$$r   r   )r&   r'   r(   r)   tupler   r   r   r5   r;   r   r   r   r>   rs   r   r   r   r   r   r   r   r     sz    8 
!"  "' '!"' "9 F 9=cJ	#
%r   r   c                   (    e Zd ZdZd Zd Zd Zd Zy)r   z (Firefox) address bar c                 &    |j                  dd      S )N	is_urlbarFr1   r3   s     r   r   zDomainURL.matches  s    zz+u--r   c                 8    | j                   j                  |      S )zP
        Get word separator to add after inserting a prediction choice.
        )r-   rE   r   s     r   rE   zDomainURL.get_auto_separator  s     227;;r   c                      y)Nz	<bot:url>r   r   s    r   r>   zDomainURL.get_text_begin_marker  r   r   c                      yrx   r   ry   s     r   r{   zDomainURL.can_spell_check  r|   r   N)r&   r'   r(   r)   r   rE   r>   r{   r   r   r   r   r     s    !.<r   r   c                       e Zd ZdZg dZg dZg dZ eeez   ez         Zg dZ	ddgZ
e	e
z   Z ej                  dej                        Zd	 Zd
 Zd Zd Zd Zd Zed        Zy)r,   aA  
    Parse partial URLs and predict separators.
    Parsing is neither complete nor RFC prove but probably doesn't
    have to be either. The goal is to save key strokes for the
    most common cases.

    Doctests:
    >>> p = PartialURLParser()

    >>> p.tokenize_url('http://user:pass@www.do-mai_n.nl/path/name.ext')
    ['http', '://', 'user', ':', 'pass', '@', 'www', '.', 'do-mai_n', '.', 'nl', '/', 'path', '/', 'name', '.', 'ext']

    >>> p.tokenize_url('user:pass@www.do-mai_n.nl/path/name.ext')
    ['user', ':', 'pass', '@', 'www', '.', 'do-mai_n', '.', 'nl', '/', 'path', '/', 'name', '.', 'ext']

    >>> p.tokenize_url('www.do-mai_n.nl/path/name.ext')
    ['www', '.', 'do-mai_n', '.', 'nl', '/', 'path', '/', 'name', '.', 'ext']

    >>> p.tokenize_url('www.do-mai_n.nl')
    ['www', '.', 'do-mai_n', '.', 'nl']
    )aeroasiabizcatcomcoopr   intjobsmobimuseumnamenetorgproteltravelxxx)edugovmil)acadaeafagaialamanaoaqarasatauawaxazbabbbdbebfbgbhbibjbmbnbobrbsbtbvbwbybzcacccdcfcgchcickclcmcncocrcscucvcxcyczdddedjdkdmdodzeceeegehereseteufifjfkfmfofrgagbgdgegfggghgiglgmgngpgqgrgsgtgugwgyhkhmhnhrhthuidieiliminioiqirisitjejmjojpkekgkhkikmknkpkrkwkykzlalblclilklrlsltlulvlymamcmdmemgmhmkmlmmmnmompmqmrmsmtmumvmwmxmymznancnenfngninlnonpnrnunzompapepfpgphpkplpmpnprpsptpwpyqarB   rorsrurwsasbscsdsesgshsisjskslsmsnsosrssstsusvsysztctdtftgthtjtktltmtntotptrtttvtwtzuaugukusuyuzvavcvevgvivnvuwfwsyeytyuzazmzw)httphttpsftpfilemailtoaptz([\w-]+)|(\W+)c                 8    | j                   j                  |      S r   )_url_patternr   )r   rM   s     r   iter_urlzPartialURLParser.iter_url  s      ))#..r   c                 ~    | j                  |      D cg c]  }|j                         D ]  }||  c}}S c c}}w r   )r  groups)r   rM   r   r   s       r   tokenize_urlzPartialURLParser.tokenize_url  sL    "&--"4 G"',,.G  Gu G 	G Gs    9c                 V   | j                  |      }t        |      dk\  r*|d   }|d   }|| j                  v r|j                  d      ryt        |      dk\  rA|d   dk(  r9|d   dk(  r1	 |j	                  d	      }|d
k\  r|d| }n|}|d   | j                  v ryy# t
        $ r d}Y -w xY w)a  
        Is this maybe something looking like an URL?

        Doctests:
        >>> p = PartialURLParser()
        >>> p.is_maybe_url("http")
        False
        >>> p.is_maybe_url("http:")
        True
        >>> p.is_maybe_url("http://www.domain.org")
        True
        >>> p.is_maybe_url("www.domain.org")
        True
        >>> p.is_maybe_url("www.domain")
        False
        >>> p.is_maybe_url("www")
        False
        r   r   r^   :T   .   /   Nr$   F)r  rS   _all_schemes
startswithindex
ValueError_TLDs)r   rH   r   tokenseptokr!  hostnames          r   rD   zPartialURLParser.is_maybe_url  s    & ""7+ v;!AYEAYF)))f.?.?.D v;!ayCF1I$4"LL-E A:%fu~H%HB<4::- " Es   )B B('B(c                 
    d|v S )Nr  r   )r   rK   s     r   rG   z#PartialURLParser._is_maybe_filename  s    6M!r   c                    d}t        d      \  }}}}|}d}t        | j                  |            }	t        |	      D ]  \  }
}|j	                         }|d   }|d   }|r|}|
t        |	      dz
  k  r|	|
dz      j	                         d   }nd}||k(  r4|r2|dk(  rd}|}b|| j                  v rd}|}u|| j                  v rd	}|}|}||k(  r!|rd
}|d
k(  r|d
k7  r|| j                  v rd}|}||k(  rd}||k(  sd} ||k(  r<|s:d}|j                  |      r |t        |      d }| j                  |      }n|d
k(  sd}|d}|S )a	  
        Get word separator to add after inserting a prediction choice.

        Doctests:
        >>> p = PartialURLParser()
        >>> p.get_auto_separator("http")
        '://'
        >>> p.get_auto_separator("www")
        '.'
        >>> p.get_auto_separator("domain.org")
        '/'
        >>> p.get_auto_separator("www.domain.org")
        '/'
        >>> p.get_auto_separator("http://www.domain")
        '.'
        >>> p.get_auto_separator("http://www.domain.org")
        '/'
        >>> p.get_auto_separator("http://www.domain.co") # ambiguous co/ or co.uk/
        '/'
        >>> p.get_auto_separator("http://www.domain.co.uk")
        '/'
        >>> p.get_auto_separator("http://www.domain.co.uk/home")
        '/'
        >>> p.get_auto_separator("http://www.domain.co/home")
        '/'
        >>> p.get_auto_separator("http://www.domain.org/home")
        '/'
        >>> p.get_auto_separator("http://www.domain.org/home/index.html")
        ''
        >>> p.get_auto_separator("mailto")
        ':'

        # local files
        >>> import tempfile
        >>> from os.path import abspath, dirname, join
        >>> def touch(fn):
        ...     with open(fn, mode="w") as f: n = f.write("")
        >>> td = tempfile.TemporaryDirectory(prefix="test onboard _")
        >>> dir = td.name

        >>> touch(join(dir, "onboard-defaults.conf.example"))
        >>> touch(join(dir, "onboard-defaults.conf.example.another"))
        >>>
        >>> import glob
        >>> #glob.glob(dir+"/**")
        >>> p.get_auto_separator("file")
        ':///'
        >>> p.get_auto_separator("file:///home")
        '/'
        >>> p.get_auto_separator("file://"+join(dir, "onboard-defaults"))
        '.'
        >>> p.get_auto_separator("file://"+join(dir, "onboard-defaults.conf"))
        '.'
        >>> p.get_auto_separator("file://"+join(dir, "onboard-defaults.conf.example"))
        ''
        >>> p.get_auto_separator("file://"+join(dir, "onboard-defaults.conf.example.another"))
        ' '

        # Non-existing filename: we don't know, don't guess a separator
        >>> p.get_auto_separator("file:///tmp/onboard1234")
        ''

        # Non-existing filename: if basename has an extension assume we're done
        >>> p.get_auto_separator("file:///tmp/onboard1234.txt")
        ' '

        # Relative filename: we don't know current dir, return empty separator
        >>> p.get_auto_separator("file://tmp")
        ''

        Nr  r=   r   r^   r  z:///z://r  r  r  rA   r@   )rQ   r   r  rd   r  rS   _schemes
_protocolsr#  r   _get_filename_separator)r   rH   rI   SCHEMEPROTOCOLDOMAINPATH	componentlast_septokr   r!  r   r  r$  r%  next_septokfile_schemefilenames                     r   rE   z#PartialURLParser.get_auto_separator  s   P 	).q&&$	g./%g. +	LE5\\^FAYEAYF$s7|A~%%eAg.557: F"$*	$(	 -$)	$*	 $//1$'	$,	 $*	F" #I"c)"c)

*$'	$(	 D 	H$	W+	Z Y#K!!+."3{#3#45 88B	"c) #IYr   c                 (   d}t         j                  j                  |      rEt        j                  |dz         }|t        j                  |dz         z  }| j	                  ||      }|)t         j                  j                  |      }d|v rd}|S d}|S )z<
        Get auto separator for a partial filename.
        NrP   z/*r  r@   r=   )rU   rV   rW   rY   _get_separator_from_file_listbasename)r   r4  rI   r\   r7  s        r   r+  z(PartialURLParser._get_filename_separator  s     	77=="YYx#~.ETYYx$//E::8UKIww''1Hh	  	r   c           	      2   d}t        |       }t        |D cg c]  }|j                  |       r|||dz     c}      }t        |      dk(  rd|v rd|v rd}|S t        |      dk(  rd|v rd}|S t        |      dk(  r|j                         }|S |rd}|S c c}w )a  
        Extract separator from a list of matching filenames.

        Doctests:
        >>> p = PartialURLParser

        # no matching files: return None, assume new file we can't check
        >>> p._get_separator_from_file_list("/dir/file", [])

        # complete file: we're done, continue with space separator
        >>> p._get_separator_from_file_list("/dir/file.ext", ["/dir/file.ext"])
        ' '

        # multiple files with identical separator: return that separator
        >>> p._get_separator_from_file_list("/dir/file",
        ...                                ["/dir/file.ext1", "/dir/file.ext2"])
        '.'

        # multiple files with different separators: return empty separator
        >>> p._get_separator_from_file_list("/dir/file",
        ...                                ["/dir/file.ext", "/dir/file-ext"])
        ''

        # directory
        >>> p._get_separator_from_file_list("/dir",
        ...                                ["/dir/file.ext1", "/dir/file.ext2"])
        '/'
        >>> p._get_separator_from_file_list("/dir",
        ...                                ["/dir", "/dir/file.ext2"])
        '/'

        # multiple extensions
        >>> files = ["/dir/dir/file.ext1.ext2", "/dir/dir/file.ext1.ext3"]
        >>> p._get_separator_from_file_list("/dir/dir/file", files)
        '.'
        >>> p._get_separator_from_file_list("/dir/dir/file.ext1", files)
        '.'
        >>> p._get_separator_from_file_list("/dir/dir/file.ext1.ext2", files)
        ' '

        # partial path match
        >>> files = ["/dir/dir/file", "/dir/dir/file.ext1",
        ...          "/dir/dir/file.ext2"]
        >>> p._get_separator_from_file_list("/dir/dir/file", files)
        ''
        >>> p._get_separator_from_file_list("/dir/dir/file.ext1", files)
        ' '
        Nr^   r   r  r=   r@   )rS   setr   pop)r4  r\   rI   r   f
separatorss         r   r6  z.PartialURLParser._get_separator_from_file_list  s    d 	ME 4qLL2 Aac( 4 5
 z?aC:$5"
:JI  _!bJ&6I  _!"(I
  I!4s    BN)r&   r'   r(   r)   _gTLDs_usTLDs_ccTLDs	frozensetr#  r)  r*  r  rB   r   r   r  r  r  rD   rG   rE   r+  staticmethodr6  r   r   r   r,   r,     s    * F $GG4 fw&01E/HE"Jj(L2::/<L/G,^"GR( D Dr   r,   )
__future__r   r   r   rU   rB   rY   loggingOnboard.Versionr   gi.repositoryr   ImportErroreOnboard.TextChangesr	   Onboard.utilsr
   r   r   	getLoggerr   r   r   r   r   r   r   r   r,   r   r   r   <module>rK     s   ( B A 	 	   /  	# * @ @
'

L
)! !.| |~
 Y _
 _D[%Z [%|! &W WS  		s   B' 'B61B6