Skip to content

Commit

Permalink
Merge pull request #9422 from goanpeca/enh/warnings
Browse files Browse the repository at this point in the history
PR: Enhance the display of warnings and errors
  • Loading branch information
ccordoba12 committed May 30, 2019
2 parents fefac85 + 31b3599 commit 8c8c038
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 34 deletions.
50 changes: 34 additions & 16 deletions spyder/plugins/editor/panels/linenumber.py
Expand Up @@ -38,10 +38,13 @@ def __init__(self, editor):

# Markers
self._markers_margin = True
self._markers_margin_width = 15
self.error_pixmap = ima.icon('error').pixmap(QSize(14, 14))
self.warning_pixmap = ima.icon('warning').pixmap(QSize(14, 14))
self.todo_pixmap = ima.icon('todo').pixmap(QSize(14, 14))

# Icons
self.error_icon = ima.icon('error')
self.warning_icon = ima.icon('warning')
self.info_icon = ima.icon('information')
self.hint_icon = ima.icon('hint')
self.todo_icon = ima.icon('todo')

# Line number area management
self._margin = True
Expand All @@ -68,13 +71,13 @@ def paintEvent(self, event):
active_block = self.editor.textCursor().block()
active_line_number = active_block.blockNumber() + 1

def draw_pixmap(ytop, pixmap):
def draw_pixmap(xleft, ytop, pixmap):
if not QT55_VERSION:
pixmap_height = pixmap.height()
else:
# scale pixmap height to device independent pixels
pixmap_height = pixmap.height() / pixmap.devicePixelRatio()
painter.drawPixmap(0, ytop + (font_height-pixmap_height) / 2,
painter.drawPixmap(xleft, ytop + (font_height-pixmap_height) / 2,
pixmap)

for top, line_number, block in self.editor.visible_blocks:
Expand All @@ -93,19 +96,33 @@ def draw_pixmap(ytop, pixmap):
Qt.AlignRight | Qt.AlignBottom,
to_text_string(line_number))

size = self.get_markers_margin() - 2
icon_size = QSize(size, size)

data = block.userData()
if self._markers_margin and data:
if data.code_analysis:
for source, code, severity, message in data.code_analysis:
error = severity == DiagnosticSeverity.ERROR
if error:
break
if error:
draw_pixmap(top, self.error_pixmap)
else:
draw_pixmap(top, self.warning_pixmap)
errors = 0
warnings = 0
infos = 0
hints = 0
for _, _, sev, _ in data.code_analysis:
errors += sev == DiagnosticSeverity.ERROR
warnings += sev == DiagnosticSeverity.WARNING
infos += sev == DiagnosticSeverity.INFORMATION
hints += sev == DiagnosticSeverity.HINT

if errors:
draw_pixmap(1, top, self.error_icon.pixmap(icon_size))
elif warnings:
draw_pixmap(1, top, self.warning_icon.pixmap(icon_size))
elif infos:
draw_pixmap(1, top, self.info_icon.pixmap(icon_size))
elif hints:
draw_pixmap(1, top, self.hint_icon.pixmap(icon_size))

if data.todo:
draw_pixmap(top, self.todo_pixmap)
draw_pixmap(1, top, self.todo_icon.pixmap(icon_size))

def leaveEvent(self, event):
"""Override Qt method."""
Expand Down Expand Up @@ -173,7 +190,8 @@ def compute_width(self):

def get_markers_margin(self):
if self._markers_margin:
return self._markers_margin_width
font_height = self.editor.fontMetrics().height() + 2
return font_height
else:
return 0

Expand Down
60 changes: 46 additions & 14 deletions spyder/plugins/editor/widgets/codeeditor.py
Expand Up @@ -538,16 +538,18 @@ def _handle_hover(self):
uri, cursor = self.get_uri_at(pos)
text = self.get_word_at(pos)
if uri:
ctrl_text = ' Cmd' if sys.platform == "darwin" else ' Ctrl'
ctrl_text = 'Cmd' if sys.platform == "darwin" else 'Ctrl'

if uri.startswith('file://'):
hint_text = ctrl_text + ' + click to open file \n'
hint_text = ctrl_text + ' + click to open file'
elif uri.startswith('mailto:'):
hint_text = ctrl_text + ' + click to send email \n'
hint_text = ctrl_text + ' + click to send email'
elif uri.startswith('http'):
hint_text = ctrl_text + ' + click to open url \n'
hint_text = ctrl_text + ' + click to open url'
else:
hint_text = ctrl_text + ' + click to open \n'
hint_text = ctrl_text + ' + click to open'

hint_text = '<span>&nbsp;{}&nbsp;</span>'.format(hint_text)

self.show_tooltip(text=hint_text, at_point=pos)
return
Expand Down Expand Up @@ -1887,18 +1889,48 @@ def hide_tooltip(self):

def show_code_analysis_results(self, line_number, block_data):
"""Show warning/error messages."""
from spyder.config.base import get_image_path
# Diagnostic severity
icons = {
DiagnosticSeverity.ERROR: 'error',
DiagnosticSeverity.WARNING: 'warning',
DiagnosticSeverity.INFORMATION: 'information',
DiagnosticSeverity.HINT: 'hint',
}

code_analysis = block_data.code_analysis
template = '{0} [{1}]: {2}'
msglist = [template.format(src, code, msg) for src, code, _sev, msg
in code_analysis]

self.show_tooltip(
title=_("Code analysis"),
text='\n'.join(msglist),
title_color='#129625',
at_line=line_number,
# Size must be adapted from font
fm = self.fontMetrics()
size = fm.height()
template = (
'<img src="data:image/png;base64, {}"'
' height="{size}" width="{size}" />&nbsp;'
'{} <i>({} {})</i>'
)
self.highlight_line_warning(block_data)

msglist = []
sorted_code_analysis = sorted(code_analysis, key=lambda i: i[2])
for src, code, sev, msg in sorted_code_analysis:
if '[' in msg and ']' in msg:
# Remove extra redundant info from pyling messages
msg = msg.split(']')[-1]

msg = msg.strip()
# Avoid messing TODO, FIXME
msg = msg[0].upper() + msg[1:]
base_64 = ima.base64_from_icon(icons[sev], size, size)
msglist.append(template.format(base_64, msg, src,
code, size=size))

if msglist:
self.show_tooltip(
title=_("Code analysis"),
text='\n'.join(msglist),
title_color='#129625',
at_line=line_number,
)
self.highlight_line_warning(block_data)

def highlight_line_warning(self, block_data):
self.clear_extra_selections('code_analysis')
Expand Down
16 changes: 15 additions & 1 deletion spyder/utils/icon_manager.py
Expand Up @@ -5,13 +5,15 @@
# (see spyder/__init__.py for details)

# Standard library imports
import base64
import os
import os.path as osp
import mimetypes as mime
import sys

# Third party imports
from qtpy.QtGui import QIcon
from qtpy.QtCore import QBuffer, QByteArray
from qtpy.QtGui import QIcon, QImage
from qtpy.QtWidgets import QStyle, QWidget

# Local imports
Expand Down Expand Up @@ -161,6 +163,8 @@
'gotoline': [('fa.sort-numeric-asc',), {'color': MAIN_FG_COLOR}],
'error': [('fa.times-circle',), {'color': 'darkred'}],
'warning': [('fa.warning',), {'color': 'orange'}],
'information': [('fa.info-circle',), {'color': '#3775a9'}],
'hint': [('fa.lightbulb-o',), {'color': 'yellow'}],
'todo': [('fa.exclamation',), {'color': '#3775a9'}],
'ipython_console': [('spyder.ipython-logo-alt',), {'color': MAIN_FG_COLOR}],
'ipython_console_t': [('spyder.ipython-logo-alt',), {'color':'gray'}],
Expand Down Expand Up @@ -464,3 +468,13 @@ def get_icon_by_extension(fname, scale_factor):
icon_by_extension = icon(
application_icons[bin_name], scale_factor)
return icon_by_extension


def base64_from_icon(icon_name, width, height):
"""Convert icon to base64 encoding."""
icon_obj = icon(icon_name)
image = QImage(icon_obj.pixmap(width, height).toImage())
byte_array = QByteArray()
buffer = QBuffer(byte_array)
image.save(buffer, "PNG")
return byte_array.toBase64().data().decode()
10 changes: 7 additions & 3 deletions spyder/widgets/mixins.py
Expand Up @@ -204,7 +204,6 @@ def _format_text(self, title=None, signature=None, text=None,
lines = text.split('\n')
if len(lines) > max_lines:
text = '\n'.join(lines[:max_lines]) + ' ...'
text = '\n' + text

text = text.replace('\n', '<br>')
template += BASE_TEMPLATE.format(
Expand All @@ -217,8 +216,13 @@ def _format_text(self, title=None, signature=None, text=None,
help_text = ''
if inspect_word:
if display_link:
help_text = ('Click anywhere in this tooltip for '
'additional help')
help_text = (
'<span style="font-size:{size}pt;">'
'Click anywhere in this tooltip for additional help'
'</span>'.format(
size=title_size + 1,
)
)
else:
shortcut = self._get_inspect_shortcut()
if shortcut:
Expand Down

0 comments on commit 8c8c038

Please sign in to comment.