我发现了这些匿名枚举者
SCLEX_CONTAINER
、SCLEX_NULL
、SCLEX_PYTHON
、SCLEX_CPP
内QsciScintillaBase
我想将上面的内容设置为 scintilla 编辑器实例的当前词法分析器。
对于实现的词法分析器,我知道我可以通过
self.editor.setLexer(QsciLexerPython(self.editor))
等从 asm、avs、bash 到 vhdl、xml 和 yaml 来做到这一点。
但是枚举中也有未实现的东西,例如
SCLEX_FORTH
、SCLEX_ERLANG
、SCLEX_GUI4CLI
等。
我试过这个
self.editor.SendScintilla(self.editor.SCI_SETLEXER, self.editor.SCLEX_ERLANG)
但它不起作用。 python 解释器确实没有错误地解析它,但只是没有突出显示和折叠。
所以基本上我的问题是如何使用这些枚举器设置词法分析器而不重新实现和子类化
QsciLexerCustom
,正如你所知,不要重复自己并重建轮子两次。
任何帮助将不胜感激!
顺便说一句,我在 Windows 11 PC 上使用 python 3.12 和 PyQt5 Qscintilla 2.14.1
TL;DR:是的,可以。
您不需要子类化
QsciLexerCustom
并从头开始定义整个词汇规则。相反,您正在寻找的是裸继承 QsciLexer
子类化。
没有其他废话介绍,让我先向您展示整个生殖示例代码(抱歉不是一个最小的代码):
(根据您提供的代码片段,我相信您正在使用Python,因此我用Python编写了代码,但格式与C++相同):
# This defines the interface to the abstract QsciLexerAsm class.
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.Qsci import *
import platform
import sys
# For testing purposes only.
if "QsciLexerAsm" in dir():
del QsciLexerAsm
class QsciLexerAsm(QsciLexer):
propertyChanged = pyqtSignal(str, str)
Default: int = 0
Comment: int = 1
Number: int = 2
DoubleQuotedString: int = 3
Operator: int = 4
Identifier: int = 5
CPUInstruction: int = 6
FPUInstruction: int = 7
Register: int = 8
Directive: int = 9
DirectiveOperand: int = 11
BlockComment: int = 12
SingleQuotedString: int = 13
UnclosedString: int = 14
ExtendedInstruction: int = 16
CommentDirective: int = 17
__fold_comments: bool
__fold_compact: bool
__comment_delimiter: str
__fold_syntax_based: bool
def __init__(self, parent: QObject=None) -> None:
super(QsciLexerAsm, self).__init__(parent)
self.__fold_comments = True
self.__fold_compact = True
self.__comment_delimiter = '~'
self.__fold_syntax_based = True
def __del__(self) -> None:
del self
def language(self) -> str:
return "ASM"
def lexer(self) -> str:
return "asm"
def defaultColor(self, style: int) -> QColor:
match style:
case QsciLexerAsm.Comment | QsciLexerAsm.BlockComment:
return QColor(0x00, 0x7f, 0x00)
case QsciLexerAsm.Number:
return QColor(0x00, 0x7f, 0x7f)
case QsciLexerAsm.DoubleQuotedString | QsciLexerAsm.SingleQuotedString:
return QColor(0x7f, 0x00, 0x7f)
case QsciLexerAsm.Operator | QsciLexerAsm.UnclosedString:
return QColor(0x00, 0x00, 0x00)
case QsciLexerAsm.CPUInstruction:
return QColor(0x00, 0x00, 0x7f)
case QsciLexerAsm.FPUInstruction | QsciLexerAsm.Directive | QsciLexerAsm.DirectiveOperand:
return QColor(0x00, 0x00, 0xff)
case QsciLexerAsm.Register:
return QColor(0x46, 0xaa, 0x03)
case QsciLexerAsm.ExtendedInstruction:
return QColor(0xb0, 0x00, 0x40)
case QsciLexerAsm.CommentDirective:
return QColor(0x66, 0xaa, 0x00)
return super(QsciLexerAsm, self).defaultColor(style)
def defaultEolFill(self, style: int) -> bool:
if style == QsciLexerAsm.UnclosedString:
return True
return super(QsciLexerAsm, self).defaultEolFill(style)
def defaultFont(self, style: int) -> QFont:
f: QFont = QFont()
match style:
case QsciLexerAsm.Operator | QsciLexerAsm.CPUInstruction | QsciLexerAsm.Register:
f = super(QsciLexerAsm, self).defaultFont(style)
f.setBold(True)
case QsciLexerAsm.Comment | QsciLexerAsm.BlockComment:
if platform.system() == "Windows":
f = QFont("Comic Sans MS", 9)
elif platform.system() == "Darwin":
f = QFont("Comic Sans MS", 12)
else:
f = QFont("Bitstream Vera Serif", 9)
case _:
f = super(QsciLexerAsm, self).defaultFont(style)
return f
def defaultPaper(self, style: int) -> QColor:
if style == QsciLexerAsm.UnclosedString:
return QColor(0xe0, 0xc0, 0xe0)
return super(QsciLexerAsm, self).defaultPaper(style)
def keywords(self, set_: int) -> str:
if set_ == 1:
return (
"" # <- CPU instructions, replace this by copying official docs
)
if set_ == 2:
return (
"" # <- FPU instructions, replace this by copying official docs
)
if set_ == 3:
return (
"" # <- Register names, replace this by copying official docs
)
if set_ == 4:
return (
"" # <- Directives, replace this by copying official docs
)
if set_ == 5:
return (
"" # <- Directive Operands, replace this by copying official docs
)
if set_ == 6:
return (
"" # <- Extended Instructions, replace this by copying official docs
)
return ""
def description(self, style: int) -> str:
match style:
case QsciLexerAsm.Default:
return self.tr("Default")
case QsciLexerAsm.Comment:
return self.tr("Comment")
case QsciLexerAsm.Number:
return self.tr("Number")
case QsciLexerAsm.DoubleQuotedString:
return self.tr("Double-quoted string")
case QsciLexerAsm.Operator:
return self.tr("Operator")
case QsciLexerAsm.Identifier:
return self.tr("Identifier")
case QsciLexerAsm.CPUInstruction:
return self.tr("CPU instruction")
case QsciLexerAsm.FPUInstruction:
return self.tr("FPU instruction")
case QsciLexerAsm.Register:
return self.tr("Register")
case QsciLexerAsm.Directive:
return self.tr("Directive")
case QsciLexerAsm.DirectiveOperand:
return self.tr("Directive operand")
case QsciLexerAsm.BlockComment:
return self.tr("Block comment")
case QsciLexerAsm.SingleQuotedString:
return self.tr("Single-quoted string")
case QsciLexerAsm.UnclosedString:
return self.tr("Unclosed string")
case QsciLexerAsm.ExtendedInstruction:
return self.tr("Extended instruction")
case QsciLexerAsm.CommentDirective:
return self.tr("Comment directive")
return ""
def refreshProperties(self) -> None:
self.__setCommentProp()
self.__setCompactProp()
self.__setCommentDelimiterProp()
self.__setSyntaxBasedProp()
def foldComments(self) -> bool:
return self.__fold_comments
def foldCompact(self) -> bool:
return self.__fold_compact
def commentDelimiter(self) -> str:
return self.__comment_delimiter
def foldSyntaxBased(self) -> bool:
return self.__fold_syntax_based
def setFoldComments(self, fold: bool) -> None:
self.__fold_comments = fold
self.__setCommentProp()
def setFoldCompact(self, fold: bool) -> None:
self.__fold_compact = fold
self.__setCompactProp()
@pyqtSlot(str)
def setCommentDelimiter(self, delimiter: str) -> None:
self.__comment_delimiter = delimiter
self.__setDelimiterProp()
@pyqtSlot(bool)
def setFoldSyntaxBased(self, syntax_based: bool) -> None:
self.__fold_syntax_based = syntax_based
self.__setSyntaxBasedProp()
def readProperties(self, qs: QSettings, prefix: str) -> bool:
self.__fold_comments = bool(qs.value(prefix + "foldcomments", True))
self.__fold_compact = bool(qs.value(prefix + "foldcompact", True))
self.__comment_delimiter = str(qs.value(prefix + "commentdelimiter",
str('~')))
self.__fold_syntax_based = bool(qs.value(prefix + "foldsyntaxbased", True))
return True
def writeProperties(self, qs: QSettings, prefix: str) -> bool:
qs.setValue(prefix + "foldcomments", self.__fold_comments)
qs.setValue(prefix + "foldcompact", self.__fold_compact)
qs.setValue(prefix + "commentdelimiter", self.__comment_delimiter)
qs.setValue(prefix + "foldsyntaxbased", self.__fold_syntax_based)
return True
def __setCommentProp(self):
self.propertyChanged.emit("fold.asm.comment.multiline",
("1" if self.__fold_comments else 0))
def __setCompactProp(self):
self.propertyChanged.emit("fold.compact", ("1" if self.__fold_compact else 0))
def __setCommentDelimiterProp(self):
self.propertyChanged.emit("lexer.asm.comment.delimiter",
self.__comment_delimiter)
def __setSyntaxBasedProp(self):
self.propertyChanged.emit("fold.asm.syntax.based",
("1" if self.__fold_syntax_based else 0))
# Example
if __name__ == "__main__":
app: QApplication = QApplication(sys.argv)
w: QsciScintilla = QsciScintilla()
lexer: QsciLexerAsm = QsciLexerAsm(w)
w.setLexer(lexer)
w.show()
sys.exit(app.exec())
我只有 Asm 自定义解决方法词法分析器实现,但听说您正在使用 QScintilla 2.14,我很抱歉它已实现,所以我无法为您提供一个很好的示例,因为它已经实现了。但逻辑是一样的。
结果应如下所示:
如您所提到的,在 Windows 11 上运行,使用 Python 3.12。看起来与官方词法分析器相同,不是吗?
让我们仔细看看。启用并增强此解决方法的魔力在于:
def lexer(self) -> str:
return "asm"
编辑器读取词法标识符并相应地设置内部词法分析器,这是在编辑器上采用词法分析器的唯一方法。我想这就是为什么您尝试过的代码不起作用,因为这些枚举器仅供内部使用。这也是为什么继承了
QsciLexer
抽象类后必须重新实现这个方法的原因。
您可能会问,这些词法分析器的标识符是什么,例如
Baan
、Gui4Cli
等?
它们在
/scintilla/include/SciLexer.h
标头中定义。由于它是一个纯C++文件,你可能无法在Python包中找到它。因此,您需要导航到此处仔细查看。
正如你所看到的,有枚举器,就像你提到的那样,从第17行,
SCLEX_CONTAINER
,SCLEX_NULL
,SCLEX_PYTHON
,SCLEX_CPP
,SCLEX_HTML
等,直到第142行SCLEX_AUTOMATIC
. 枚举器的名称无一例外都是词法分析器的标识符,并且必须由 QsciLexer.lexer() -> str
或 const char *QsciLexer::lexer()
重写方法返回。
例如
Default
中的Comment
、UnclosedString
、QsciLexerCPP
; CPUInstruction
中的QsciLexerAsm
等。您还可以在SciLexer.h
中找到它,从第143行开始,宏(或#define
后的标识符)以SCE_
开头。下面的名称几乎与 lexer()
返回的词法分析器标识符相同,但有一些特殊约定:
Convention
= "lexer identifier"
P
="python"
C
= "cpp"
或 "cppnocase"
H
="html"
HJ
= "html"
的 JavaScript 部分HJA
= "html"
的 ASP JavaScript 部分HB
= "html"
的 VB 脚本部分HBA
= "html"
的 ASP VB 脚本部分HP
= "html"
的Python部分HPA
= "html"
的 ASP Python 部分HPHP
= "html"
的 PHP 部分PL
="perl"
RB
="ruby"
B
="basic"
PROPS
="properties"
L
="latex"
Err
="errorlist"
等
例如,
setSmartHighlighting(bool)
中的QsciLexerPascal
。
您可以通过一次创建三个方法来完成此操作:
set...()
、...()
和私有__set...Prop()
;以及重写 readProperties()
和 writeProperties()
方法,如上面的代码所示。
set...Prop()
的属性字符串(例如__setSyntaxBasedProp()
中的“fold.asm.syntax.based”)可以在每个词法分析器文件中找到。例如,在QsciLexerBasic
(想象力)中,您可以拥有(想象力):
@pyqtSlot(bool)
def setBasicExplicitComment(self, explicit: bool) -> None:
self.__basic_explicit_comment = explicit
self.__setExplicitCommentProp()
def basicExplicitComment(self) -> bool:
return self.__basic_explicit_comment
def __setExplicitCommentProp(self) -> None:
self.propertyChanged.emit("fold.basic.comment.explicit", ("1" if self.__basic_explicit_comment else "0")
来源:这里
记住,您需要将
propertyChanged = pyqtSignal(str, str)
信号定义为类属性(不要在 __init__
! 中定义它),原生 Scintilla 中 bool
的值是 "1"
的 str-value True
和 "0"
代表 False
,而不是 bool
或隐式 1
或 0
!
如果有什么不明白的地方,请不要犹豫,在下面评论我。