'Parse out the Perl expression at the given trigger and return
a CITDL expression for it (and possibly a variable prefixj
filter).
Returns a 2-tuple:
(<CITDL-expression>, <variable-prefix-filter>)
For all triggers except TRG_FORM_DEFN, we parse out the Perl
expression preceding the trigger position, simplify the
expression (by removing whitespace, etc.) and translate that to
an appropriate CITDL (*) expression. Set to None if there is no
appropriate such expression. For TRG_FORM_DEFN triggers we first
move forward to the end of the current word.
As well, a variable prefix filter may be returned, useful for
post-processing of completions. For example:
Perl code           CITDL expression    prefix filter
Foo::Bar<|>::       Foo::Bar            None
$Foo::Bar<|>::      Foo::Bar            $
Optimization Notes:
- We can throw out Perl expressions with function calls
because CodeIntel does not currently handle return values.
- We can throw out Perl exprs that span an EOL: 80/20 rule. (We
currently don\'t throw these away, though.)
- Abort at hash and list indexing: the types of elements in these
objects are not tracked by CodeIntel.
- Punt on Perl references, e.g. \$foo, \@bar. XXX I wonder if I can
just ignore references and assume the user is doing the right
thing. I.e. I just presume that a reference is dereferenced
properly when required. Dunno.
- Currently we don\'t really make use of the styling info because we
abort at indexing, function call arguments, etc. where recognizing
string/number/regex boundaries would be useful. This info might be
useful later if this algorithm is beefed up.
- Ignore ampersand, e.g. &foo. This is just an old way to call perl
functions - bug 87870, we can just ignore it for codeintel.
Examples:
GIVEN                       LEADING EXPR            CITDL EXPR
split <|>                   split                   split
chmod(<|>                   chmod                   chmod
$Foo::bar(<|>               $Foo::bar               Foo.$bar
&$make_coffee(<|>           &$make_coffee           $make_coffee
Win32::OLE-><|>             Win32::OLE              Win32::OLE
Win32::OLE->GetObject(<|>   Win32::OLE->GetObject   Win32::OLE.GetObject
split join <|>              join                    join
foo->bar(<|>                foo->bar                foo.bar
Note that the trigger character is sometimes necessary to resolve
ambiguity. Given "Foo::Bar" without the trailing trigger char, we
cannot know if the CITDL should be "Foo.Bar" or "Foo::Bar":
GIVEN               CITDL EXPR
Foo::Bar::<|>       Foo::Bar
$Foo::Bar::<|>      Foo::Bar
Foo::Bar-><|>       Foo::Bar
Foo::Bar(<|>        Foo.Bar
Foo::Bar <|>        Foo.Bar
$Foo::Bar-><|>      Foo.$Bar
$Foo::Bar(<|>       Foo.$Bar
$Foo::Bar <|>       Foo.$Bar
* http://specs.tl.activestate.com/kd/kd-0100.html#citdl'

def citdl_expr_and_prefix_filter_from_trg(self, buf, trg):

    DEBUG = False
    if DEBUG:
        print
        print banner(('citdl_expr_and_prefix_filter_from_trg    @    %d' % trg.pos))
        print markup_text(buf.accessor.text, trg_pos=trg.pos)
        print banner(None, '-')
    if trg.implicit:
        skip_styles = buf.implicit_completion_skip_styles
    else:
        skip_styles = {}
    (filter, citdl) = (None, [])
    accessor = buf.accessor
    LIMIT = max(0, (trg.pos - 100))
    if (trg.form == TRG_FORM_DEFN):
        p = trg.pos
        length = accessor.length()
        while (p < length):
            if (not _is_perl_var_char(accessor.char_at_pos(p))):
                break
            p += 1
        if ((p >= 2) and (accessor.text_range((p - 2), p) in ('->', '::'))):
            p = (p - 2)
        if DEBUG:
            print ("'defn'-trigger:    adjust    position    %d" % (p - trg.pos))
    else:
        p = (trg.pos - trg.length)
    p -= 1
    while (p >= LIMIT):
        if DEBUG:
            print ('look    for    Perl    var    at    end    of    %r' % accessor.text_range(LIMIT, (p + 1)))
        match = self._perl_var_pat.search(accessor.text_range(LIMIT, (p + 1)))
        if (not match):
            if DEBUG:
                if ((p - LIMIT) > 20):
                    segment = ('...' + accessor.text_range((p - 20), (p + 1)))
                else:
                    segment = accessor.text_range(LIMIT, (p + 1))
                print ('could    not    match    a    Perl    var    off    %r' % segment)
            citdl = None
            break
        prefix = (match.group('prefix') or '')
        if ('&' in prefix):
            prefix = prefix.replace('&', '')
        scope = match.group('scope')
        name = match.group('name')
        trg_ch = None
        try:
            trg_ch = accessor.char_at_pos((p + 1))
        except IndexError as ex:
            if (trg.form != TRG_FORM_DEFN):
                log.warn("text    does    not    include    trailing    trigger    char    to    resolve    possible    ambiguities    in    '%s'", match.group(0))
        if (trg_ch == ':'):
            citdl.insert(0, (scope + name))
            filter = prefix
        elif ((trg_ch == '-') and (not prefix)):
            citdl.insert(0, (scope + name))
        else:
            citdl.insert(0, (prefix + name))
            if scope:
                scope = scope[:(-2)]
                if scope:
                    citdl.insert(0, scope)
        p -= len(match.group(0))
        if DEBUG:
            print ('parse    out    Perl    var:    %r    (prefix=%r,    scope=%r,    name=%r):    %r' % (match.group(0), prefix, scope, name, citdl))
        WHITESPACE = tuple('     DCTB \n\r\x0b\x0c')
        while ((p >= LIMIT) and (accessor.char_at_pos(p) in WHITESPACE)):
            p -= 1
        if ((p >= LIMIT) and (accessor.style_at_pos(p) in skip_styles)):
            if DEBUG:
                style = accessor.style_at_pos(p)
                style_names = buf.style_names_from_style_num(style)
                print ('stop    at    style    to    ignore:    %r    (%s    %s)' % (accessor.char_at_pos(p), style, style_names))
            break
        elif ((p >= (LIMIT + 1)) and (accessor.text_range((p - 1), (p + 1)) == '->')):
            if DEBUG:
                print "parse    out    '->'"
            p -= 2
            while ((p >= LIMIT) and (accessor.char_at_pos(p) in WHITESPACE)):
                p -= 1
            continue
        else:
            break
    if citdl:
        retval = ('.'.join(citdl), filter)
    else:
        retval = (None, filter)
    if DEBUG:
        print ('returning:    %r' % (retval,))
        banner('done')
    return retval
