~ubuntu-branches/ubuntu/vivid/vim-ultisnips/vivid

« back to all changes in this revision

Viewing changes to pythonx/UltiSnips/text_objects/_transformation.py

  • Committer: Package Import Robot
  • Author(s): Michael Fladischer
  • Date: 2014-10-12 18:11:54 UTC
  • Revision ID: package-import@ubuntu.com-20141012181154-1jeoj467dh2l5f2e
Tags: upstream-3.0
ImportĀ upstreamĀ versionĀ 3.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# encoding: utf-8
 
3
 
 
4
"""Implements TabStop transformations."""
 
5
 
 
6
import re
 
7
import sys
 
8
 
 
9
from UltiSnips.text import unescape, fill_in_whitespace
 
10
from UltiSnips.text_objects._mirror import Mirror
 
11
 
 
12
def _find_closing_brace(string, start_pos):
 
13
    """Finds the corresponding closing brace after start_pos."""
 
14
    bracks_open = 1
 
15
    for idx, char in enumerate(string[start_pos:]):
 
16
        if char == '(':
 
17
            if string[idx+start_pos-1] != '\\':
 
18
                bracks_open += 1
 
19
        elif char == ')':
 
20
            if string[idx+start_pos-1] != '\\':
 
21
                bracks_open -= 1
 
22
            if not bracks_open:
 
23
                return start_pos+idx+1
 
24
 
 
25
def _split_conditional(string):
 
26
    """Split the given conditional 'string' into its arguments."""
 
27
    bracks_open = 0
 
28
    args = []
 
29
    carg = ""
 
30
    for idx, char in enumerate(string):
 
31
        if char == '(':
 
32
            if string[idx-1] != '\\':
 
33
                bracks_open += 1
 
34
        elif char == ')':
 
35
            if string[idx-1] != '\\':
 
36
                bracks_open -= 1
 
37
        elif char == ':' and not bracks_open and not string[idx-1] == '\\':
 
38
            args.append(carg)
 
39
            carg = ""
 
40
            continue
 
41
        carg += char
 
42
    args.append(carg)
 
43
    return args
 
44
 
 
45
def _replace_conditional(match, string):
 
46
    """Replaces a conditional match in a transformation."""
 
47
    conditional_match = _CONDITIONAL.search(string)
 
48
    while conditional_match:
 
49
        start = conditional_match.start()
 
50
        end = _find_closing_brace(string, start+4)
 
51
        args = _split_conditional(string[start+4:end-1])
 
52
        rv = ""
 
53
        if match.group(int(conditional_match.group(1))):
 
54
            rv = unescape(_replace_conditional(match, args[0]))
 
55
        elif len(args) > 1:
 
56
            rv = unescape(_replace_conditional(match, args[1]))
 
57
        string = string[:start] + rv + string[end:]
 
58
        conditional_match = _CONDITIONAL.search(string)
 
59
    return string
 
60
 
 
61
_ONE_CHAR_CASE_SWITCH = re.compile(r"\\([ul].)", re.DOTALL)
 
62
_LONG_CASEFOLDINGS = re.compile(r"\\([UL].*?)\\E", re.DOTALL)
 
63
_DOLLAR = re.compile(r"\$(\d+)", re.DOTALL)
 
64
_CONDITIONAL = re.compile(r"\(\?(\d+):", re.DOTALL)
 
65
class _CleverReplace(object):
 
66
    """Mimics TextMates replace syntax."""
 
67
 
 
68
    def __init__(self, expression):
 
69
        self._expression = expression
 
70
 
 
71
    def replace(self, match):
 
72
        """Replaces 'match' through the correct replacement string."""
 
73
        transformed = self._expression
 
74
        # Replace all $? with capture groups
 
75
        transformed = _DOLLAR.subn(
 
76
                lambda m: match.group(int(m.group(1))), transformed)[0]
 
77
 
 
78
        # Replace Case switches
 
79
        def _one_char_case_change(match):
 
80
            """Replaces one character case changes."""
 
81
            if match.group(1)[0] == 'u':
 
82
                return match.group(1)[-1].upper()
 
83
            else:
 
84
                return match.group(1)[-1].lower()
 
85
        transformed = _ONE_CHAR_CASE_SWITCH.subn(
 
86
                _one_char_case_change, transformed)[0]
 
87
 
 
88
        def _multi_char_case_change(match):
 
89
            """Replaces multi character case changes."""
 
90
            if match.group(1)[0] == 'U':
 
91
                return match.group(1)[1:].upper()
 
92
            else:
 
93
                return match.group(1)[1:].lower()
 
94
        transformed = _LONG_CASEFOLDINGS.subn(
 
95
                _multi_char_case_change, transformed)[0]
 
96
        transformed = _replace_conditional(match, transformed)
 
97
        return unescape(fill_in_whitespace(transformed))
 
98
 
 
99
# flag used to display only one time the lack of unidecode
 
100
UNIDECODE_ALERT_RAISED = False
 
101
class TextObjectTransformation(object):
 
102
    """Base class for Transformations and ${VISUAL}."""
 
103
 
 
104
    def __init__(self, token):
 
105
        self._convert_to_ascii = False
 
106
 
 
107
        self._find = None
 
108
        if token.search is None:
 
109
            return
 
110
 
 
111
        flags = 0
 
112
        self._match_this_many = 1
 
113
        if token.options:
 
114
            if "g" in token.options:
 
115
                self._match_this_many = 0
 
116
            if "i" in token.options:
 
117
                flags |= re.IGNORECASE
 
118
            if "a" in token.options:
 
119
                self._convert_to_ascii = True
 
120
 
 
121
        self._find = re.compile(token.search, flags | re.DOTALL)
 
122
        self._replace = _CleverReplace(token.replace)
 
123
 
 
124
    def _transform(self, text):
 
125
        """Do the actual transform on the given text."""
 
126
        global UNIDECODE_ALERT_RAISED  # pylint:disable=global-statement
 
127
        if self._convert_to_ascii:
 
128
            try:
 
129
                import unidecode
 
130
                text = unidecode.unidecode(text)
 
131
            except Exception:  # pylint:disable=broad-except
 
132
                if UNIDECODE_ALERT_RAISED == False:
 
133
                    UNIDECODE_ALERT_RAISED = True
 
134
                    sys.stderr.write(
 
135
                        "Please install unidecode python package in order to "
 
136
                        "be able to make ascii conversions.\n")
 
137
        if self._find is None:
 
138
            return text
 
139
        return self._find.subn(
 
140
                self._replace.replace, text, self._match_this_many)[0]
 
141
 
 
142
class Transformation(Mirror, TextObjectTransformation):
 
143
    """See module docstring."""
 
144
 
 
145
    def __init__(self, parent, ts, token):
 
146
        Mirror.__init__(self, parent, ts, token)
 
147
        TextObjectTransformation.__init__(self, token)
 
148
 
 
149
    def _get_text(self):
 
150
        return self._transform(self._ts.current_text)