Project

Profile

Help

Revision dc5fb9be

Added by Francisco Del Roio about 2 years ago

Changes:

  • Method `OnMessage` rewritten completely.
  • Method `OnPostMessage` adapted for changes I did in `OnMessage`.

I think that these changes fixes #144.

View differences:

src/accesspanel/extensions/ansi.py
26 26
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 27
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 28

  
29
import re
29
import re, string
30 30

  
31 31
import wx
32 32
import wx.lib.colourdb
33 33

  
34
from accesspanel.extensions.base import BaseExtension
34
from .base import BaseExtension
35 35

  
36 36
## Constants
37 37
# Regular expressions to capture ANSI codes
38
RE_ANSI = re.compile(r'\x1b\[([^m]*)m', re.UNICODE)
39 38
RE_CODE = re.compile(r"^(?:(\d+)(?:\;(\d+)(?:\;(\d+))?)?)?$", re.UNICODE)
40 39

  
41 40
# Brightness
......
80 79
    """
81 80

  
82 81
    def __init__(self, panel):
83
        BaseExtension.__init__(self, panel)
82
        super().__init__(panel)
84 83
        wx.lib.colourdb.updateColourDB()
85 84
        self.brightness = NORMAL
86 85
        self.foreground = wx.BLACK
......
129 128

  
130 129
    def OnMessage(self, message):
131 130
        """Interpret the ANSI codes."""
131

  
132
        # Variables
132 133
        point = self.panel.editing_pos
133
        pos = []
134

  
135
        # Browse through the ANSI codes
136
        for match in reversed(list(RE_ANSI.finditer(message))):
137
            code = match.group(1)
138

  
139
            # Extract the brightness, foreground and background
140
            codes = RE_CODE.search(code)
141
            if codes:
142
                brightness, foreground, background = codes.groups()
143

  
144
                # All 3 variables can be None
145
                if brightness == 1:
146
                    brightness = BRIGHT
147
                elif brightness == 4:
148
                    brightness = UNDERLINE
149
                else:
150
                    brightness = NORMAL
151

  
152
                # Extract the foreground color
153
                if foreground:
154
                    foreground = int(foreground)
155
                    if brightness == NORMAL:
156
                        foreground = self.normal_colors.get(foreground + 10)
157
                    elif brightness == BRIGHT:
158
                        foreground = self.bright_colors.get(foreground + 10)
159
                    elif brightness == UNDERLINE:
160
                        foreground = self.dark_colors.get(foreground + 10)
161

  
162
                # Extract the background color
163
                if background:
164
                    background = int(background)
165
                    if brightness == NORMAL:
166
                        background = self.normal_colors.get(background)
167
                    elif brightness == BRIGHT:
168
                        background = self.bright_colors.get(background)
169
                    elif brightness == UNDERLINE:
170
                        background = self.dark_colors.get(background)
171

  
172
                if foreground is None:
173
                    foreground = self.default_foreground
174

  
175
                if brightness:
176
                    self.brightness = brightness
177

  
178
                if background is None:
179
                    if self.brightness == BRIGHT:
180
                        background = wx.BLACK
181
                    elif self.brightness == UNDERLINE:
182
                        background = wx.YELLOW
183
                    else:
184
                        background = self.default_background
185

  
186
                start = match.start()
187
                end = match.end()
188
                pos.append((start, end, foreground, background))
189

  
190
        begin_tag = True
191
        updated_pos = 0
192
        last_mod = None
193
        for start, end, foreground, background in reversed(pos):
194
            if begin_tag:
195
                begin_tag = False
134
        ansi_stage = 1
135
        ansi_buffer = ""
136
        clean_buffer = ""
137
        char_index = 0
138

  
139
        def select_colors(code):
140
            """
141
            Transforms ANSI sequences in a format specifier
142
            """
143

  
144
            match = RE_CODE.match(code)
145

  
146
            # ANSI style sequences have tree parts
147
            p1, p2, p3 = match.groups()
148
            brightness, foreground, background = (None, None, None)
149

  
150
            if p1:
151
                p1 = int(p1.strip())
152

  
153
                if p1 in (0, 1, 4, 7):
154
                    brightness = p1
155
                elif p1 in range(30, 38):
156
                    foreground = p1
157
                elif p1 in range(40, 48):
158
                    background = p1
159

  
160
            if p2:
161
                p2 = int(p2.strip())
162

  
163
                if p2 in range(30, 40):
164
                    foreground = p2
165
                elif p2 in range(40, 50):
166
                    background = p2
167

  
168
            if p3:
169
                p3 = int(p3.strip())
170

  
171
                if p3 in range(40, 50):
172
                    background = p3
173

  
174
            if brightness == 1:
175
                brightness = BRIGHT
176
            elif brightness == 4:
177
                brightness = UNDERLINE
196 178
            else:
197
                eol = message.count("\r", 0, start - 1)
198
                begin_tag = True
199
                real_start, m_foreground, m_background = last_mod
200
                real_start += point
201
                real_start -= eol
202
                real_end = start - updated_pos + point
203
                real_end -= eol
204
                self.modifiers.append((real_start, real_end, m_foreground,
205
                        m_background))
206

  
207
                # If it's not the default color, mark an open tag
208
                if foreground != self.default_foreground or \
209
                        background != self.default_background:
210
                    begin_tag = False
211

  
212
            last_mod = (start - updated_pos, foreground, background)
213
            updated_pos += end - start
214

  
215
        # Remove the ANSI codes from the message
216
        for start, end, foreground, background in pos:
217
            message = message[:start] + message[end:]
218

  
219
        return message
179
                brightness = NORMAL
180

  
181

  
182
            # Now the colors
183
            colorlist = None
184

  
185
            if brightness == BRIGHT:
186
                colorlist = self.bright_colors
187
            elif brightness == UNDERLINE:
188
                colorlist = self.dark_colors
189
            else:
190
                colorlist = self.normal_colors
191

  
192
            if foreground:
193
                foreground = colorlist[foreground+10]
194
            else:
195
                foreground = self.default_foreground
196

  
197
            if background:
198
                background = colorlist[background]
199
            else:
200

  
201
                if brightness == BRIGHT:
202
                    background = wx.BLACK
203
                elif brightness == UNDERLINE:
204
                    background = wx.YELLOW
205
                else:
206
                    background = self.default_background
207

  
208
            return (foreground, background)
209

  
210
        # We must iterate over the entire message string
211
        while char_index < len(message):
212

  
213
            # First stage: look for a 0x1B character and switch to second stage
214
            if ansi_stage == 1:
215
                if message[char_index] == "\x1B":
216
                    ansi_stage = 2
217
                else:
218
                    clean_buffer += message[char_index]
219

  
220
            # Second stage: look for a validation character (an '[')
221
            elif ansi_stage == 2:
222
                if message[char_index] == "[":
223
                    ansi_stage = 3
224
                    ansi_buffer = ""
225
                else:
226
                    ansi_stage = 1
227

  
228
                    # Just add the two last characters
229
                    clean_buffer += message[char_index-1:char_index]
230

  
231
            # Last stage: process ANSI command with arguments
232
            elif ansi_stage == 3:
233
                if message[char_index] in string.digits+";":
234
                    ansi_buffer += message[char_index]
235

  
236
                # Now we have all arguments and the command (too generic detection for now...)
237
                else:
238
                    ansi_stage = 1
239
                    self.modifiers.append((point+len(clean_buffer), select_colors(ansi_buffer)))
240

  
241
                    ansi_buffer = ""
242

  
243
            char_index += 1
244

  
245

  
246
        return clean_buffer
220 247

  
221 248
    def PostMessage(self, message):
222
        for start, end, foreground, background in self.modifiers:
223
            range = self.panel.output.GetRange(start, end)
224
            self.panel.output.SetStyle(start, end, wx.TextAttr(
249
        """Applies ANSI style to text"""
250

  
251
        start = None
252
        last_mark = None
253

  
254
        for point, style in self.modifiers:
255
            if not last_mark:
256
                last_mark = style
257
                start = point
258
                continue
259

  
260
            # Unpack foreground and background from style tuple
261
            foreground, background = style
262

  
263
            self.panel.output.SetStyle(start, point, wx.TextAttr(
225 264
                    foreground, background))
265

  
266
            start = point
267
            last_mark = style
268

  
269
        self.modifiers = []

Also available in: Unified diff