169 lines
6.2 KiB
Python
169 lines
6.2 KiB
Python
import re
|
|
|
|
from PyQt5 import QtCore, QtGui, QtWidgets, Qsci
|
|
from igitar.constants import KNOWN_DIRECTIVES, KNOWN_VERSE_TYPES
|
|
|
|
PALETTE_ROLES = {
|
|
'window': QtGui.QPalette.WindowText,
|
|
'background': QtGui.QPalette.Background,
|
|
'windowtext': QtGui.QPalette.WindowText,
|
|
'foreground': QtGui.QPalette.Foreground,
|
|
'base': QtGui.QPalette.Base,
|
|
'alternatebase': QtGui.QPalette.AlternateBase,
|
|
'tooltipbase': QtGui.QPalette.ToolTipBase,
|
|
'tooltiptext': QtGui.QPalette.ToolTipText,
|
|
'placeholderText': QtGui.QPalette.PlaceholderText,
|
|
'text': QtGui.QPalette.Text,
|
|
'button': QtGui.QPalette.Button,
|
|
'buttontext': QtGui.QPalette.ButtonText,
|
|
'brighttext': QtGui.QPalette.BrightText,
|
|
'light': QtGui.QPalette.Light,
|
|
'midlight': QtGui.QPalette.Midlight,
|
|
'dark': QtGui.QPalette.Dark,
|
|
'mid': QtGui.QPalette.Mid,
|
|
'shadow': QtGui.QPalette.Shadow,
|
|
'highlight': QtGui.QPalette.Highlight,
|
|
'highlightedtext': QtGui.QPalette.HighlightedText,
|
|
'link': QtGui.QPalette.Link,
|
|
'linkvisited': QtGui.QPalette.LinkVisited
|
|
}
|
|
|
|
|
|
class ChordProStyle(object):
|
|
Default = 0
|
|
Keyword = 1
|
|
Identifier = 2
|
|
Chord = 3
|
|
|
|
@staticmethod
|
|
def to_string(style):
|
|
"""Return the name for the style"""
|
|
return {
|
|
ChordProStyle.Default: 'Default',
|
|
ChordProStyle.Keyword: 'Keyword',
|
|
ChordProStyle.Identifier: 'Identifier',
|
|
ChordProStyle.Chord: 'Chord'
|
|
}.get(style)
|
|
|
|
|
|
class ChordProLexer(Qsci.QsciLexerCustom):
|
|
|
|
def __init__(self, parent):
|
|
super().__init__(parent)
|
|
self.settings = QtCore.QSettings()
|
|
self.settings.beginGroup('syntax')
|
|
self.init_style()
|
|
|
|
def _get_color(self, name, default):
|
|
"""Get a QColor object from the settings"""
|
|
color = self.settings.value(name, default)
|
|
if isinstance(color, str):
|
|
if color.startswith('palette:'):
|
|
palette_role = PALETTE_ROLES[color.split(':', 1)[1].lower()]
|
|
color = QtWidgets.QApplication.palette().color(palette_role)
|
|
else:
|
|
color = QtGui.QColor(color)
|
|
return color
|
|
|
|
def _get_font(self, name):
|
|
"""Get a QFont object from the settings"""
|
|
font = self.settings.value(name, None)
|
|
if not font:
|
|
font = QtGui.QFont('monospace')
|
|
elif isinstance(font, str):
|
|
font = QtGui.QFont.fromString(font)
|
|
return font
|
|
|
|
def init_style(self):
|
|
"""Set up the colours and fonts"""
|
|
# Defaults
|
|
self.setDefaultColor(self._get_color('default-foreground', 'palette:windowtext'))
|
|
self.setDefaultPaper(self._get_color('default-background', 'palette:base'))
|
|
self.setDefaultFont(self._get_font('default-font'))
|
|
self.setColor(self._get_color('default-foreground', 'palette:windowtext'),
|
|
ChordProStyle.Default)
|
|
self.setPaper(self._get_color('default-background', 'palette:base'),
|
|
ChordProStyle.Default)
|
|
self.setFont(self._get_font('default-font'), ChordProStyle.Default)
|
|
# Keywords
|
|
kwfg = self._get_color('keywords-foreground', '#009fe3')
|
|
if kwfg:
|
|
self.setColor(kwfg, ChordProStyle.Keyword)
|
|
kwbg = self._get_color('keywords-background', None)
|
|
if kwbg:
|
|
self.setPaper(kwbg, ChordProStyle.Keyword)
|
|
kwft = self._get_font('keywords-font')
|
|
if kwft:
|
|
self.setFont(kwft, ChordProStyle.Keyword)
|
|
# Identifiers
|
|
idfg = self._get_color('identifiers-foreground', '#fdadff')
|
|
if idfg:
|
|
self.setColor(idfg, ChordProStyle.Identifier)
|
|
idbg = self._get_color('identifiers-background', None)
|
|
if idbg:
|
|
self.setPaper(idbg, ChordProStyle.Identifier)
|
|
idft = self._get_font('identifiers-font')
|
|
if idft:
|
|
self.setFont(idft, ChordProStyle.Identifier)
|
|
# Chords
|
|
chfg = self._get_color('chords-foreground', '#73ff6c')
|
|
if chfg:
|
|
self.setColor(chfg, ChordProStyle.Chord)
|
|
chbg = self._get_color('chords-background', None)
|
|
if chbg:
|
|
self.setPaper(chbg, ChordProStyle.Chord)
|
|
chft = self._get_font('chords-font')
|
|
if chft:
|
|
self.setFont(chft, ChordProStyle.Chord)
|
|
|
|
def language(self):
|
|
"""Return the language name"""
|
|
return "ChordPro"
|
|
|
|
def description(self, style):
|
|
"""Return the name for the style number"""
|
|
return ChordProStyle.to_string(style)
|
|
|
|
def styleText(self, start, end):
|
|
"""Style the text"""
|
|
|
|
keywords = ['verse', 'chorus', 'bridge'] + \
|
|
[d for t in KNOWN_DIRECTIVES for d in t] + \
|
|
['start_of_' + vt for vt in KNOWN_VERSE_TYPES] + \
|
|
['end_of_' + vt for vt in KNOWN_VERSE_TYPES]
|
|
is_directive = False
|
|
is_identifier = False
|
|
is_chord = False
|
|
pattern = re.compile(r'\s+|\w+|\W')
|
|
|
|
self.startStyling(start)
|
|
text = self.parent().text()[start:end]
|
|
|
|
token_list = [(token, len(bytearray(token, "utf-8"))) for token in pattern.findall(text)]
|
|
|
|
for idx, token in enumerate(token_list):
|
|
if token[0] == '{':
|
|
is_directive = True
|
|
self.setStyling(token[1], ChordProStyle.Keyword)
|
|
elif is_directive and token[0] in keywords:
|
|
self.setStyling(token[1], ChordProStyle.Keyword)
|
|
elif is_directive and token[0] == ':':
|
|
is_identifier = True
|
|
self.setStyling(token[1], ChordProStyle.Keyword)
|
|
elif is_directive and token[0] == '}':
|
|
is_directive = False
|
|
is_identifier = False
|
|
self.setStyling(token[1], ChordProStyle.Keyword)
|
|
elif is_identifier:
|
|
self.setStyling(token[1], ChordProStyle.Identifier)
|
|
elif token[0] == '[':
|
|
is_chord = True
|
|
self.setStyling(token[1], ChordProStyle.Chord)
|
|
elif is_chord and token[0] == ']':
|
|
is_chord = False
|
|
self.setStyling(token[1], ChordProStyle.Chord)
|
|
elif is_chord:
|
|
self.setStyling(token[1], ChordProStyle.Chord)
|
|
else:
|
|
self.setStyling(token[1], ChordProStyle.Default)
|