Purging some remaining t2tfiles

This commit is contained in:
Olivier Keshavjee 2016-03-30 11:36:25 +02:00
parent 7e954eab5e
commit 47d87115e6
5 changed files with 28 additions and 1187 deletions

View file

@ -0,0 +1,19 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
from PyQt5.QtCore import QRegExp
from PyQt5.QtGui import QTextCursor
def MDFormatSelection(editor, style):
"""
Formats the current selection of ``editor`` in the format given by ``style``,
style being:
0: bold
1: italic
2: code
"""
print("Formatting:", style, " (Unimplemented yet !)")
# FIXME

View file

@ -1,376 +0,0 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import re
from PyQt5.QtCore import QRegExp
from PyQt5.QtGui import QTextCursor
def t2tFormatSelection(editor, style):
"""
Formats the current selection of ``editor`` in the format given by ``style``,
style being:
0: bold
1: italic
2: underline
3: strike
4: code
5: tagged
"""
print("Formatting:", style)
formatChar = "*/_-`'"[style]
# FIXME: doesn't work if selection spans over several blocks.
cursor = editor.textCursor()
cursor.beginEditBlock()
if cursor.hasSelection():
pass
else:
# If no selection, selects the word in which the cursor is now
cursor.movePosition(QTextCursor.StartOfWord,
QTextCursor.MoveAnchor)
cursor.movePosition(QTextCursor.EndOfWord,
QTextCursor.KeepAnchor)
if not cursor.hasSelection():
# No selection means we are outside a word,
# so we insert markup and put cursor in the middle
cursor.insertText(formatChar * 4)
cursor.setPosition(cursor.position() - 2)
cursor.endEditBlock()
editor.setTextCursor(cursor)
# And we are done
return
# Get start and end of selection
start = cursor.selectionStart() - cursor.block().position()
end = cursor.selectionEnd() - cursor.block().position()
if start > end:
start, end = end, start
# Whole block
text = cursor.block().text()
# Adjusts selection to exclude the markup
while text[start:start + 1] == formatChar:
start += 1
while text[end - 1:end] == formatChar:
end -= 1
# Get the text without formatting, and the array of format
fText, fArray = textToFormatArrayNoMarkup(text)
# Get the translated start and end of selection in the unformated text
tStart, tEnd = translateSelectionToUnformattedText(text, start, end)
# We want only the array that contains the propper formatting
propperArray = fArray[style]
if 0 in propperArray[tStart:tEnd]:
# have some unformated text in the selection, so we format the
# whole selection
propperArray = propperArray[:tStart] + [1] * \
(tEnd - tStart) + propperArray[tEnd:]
else:
# The whole selection is already formatted, so we remove the
# formatting
propperArray = propperArray[:tStart] + [0] * \
(tEnd - tStart) + propperArray[tEnd:]
fArray = fArray[0:style] + [propperArray] + fArray[style + 1:]
text = reformatText(fText, fArray)
# Replaces the whole block
cursor.movePosition(QTextCursor.StartOfBlock)
cursor.movePosition(QTextCursor.EndOfBlock,
QTextCursor.KeepAnchor)
cursor.insertText(text)
cursor.setPosition(end + cursor.block().position())
cursor.endEditBlock()
editor.setTextCursor(cursor)
def t2tClearFormat(editor):
"""Clears format on ``editor``'s current selection."""
cursor = editor.textCursor()
cursor.beginEditBlock()
text = cursor.selectedText()
t, a = textToFormatArrayNoMarkup(text)
cursor.insertText(t)
cursor.endEditBlock()
editor.setTextCursor(cursor)
def textToFormatArray(text):
"""
Take some text and returns an array of array containing informations
about how the text is formatted:
r = [ [0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0],
[0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1],
...
]
Each sub-array is for one of the beautifier:
0: bold
1: italic
2: underline
3: strike
4: code
5: tagged
Each sub-array contains an element for each character of the text, with the
value 1 if it is formatted in the specific format, -1 if it is markup, and
0 otherwise.
removeMarks returns a both the array and a string, in which all of the
formatting marks have been removed.
"""
result = []
for markup in ["\*", "/", "_", "-", "`", "\'"]:
rList = []
r = QRegExp(r'(' + markup * 2 + ')(.+)(' + markup * 2 + ')')
r.setMinimal(True)
pos = r.indexIn(text, 0)
lastPos = 0
while pos >= 0:
# We have a winner
rList += [0] * (pos - lastPos)
rList += [2] * 2
rList += [1] * len(r.cap(2))
rList += [2] * 2
lastPos = pos + len(r.cap(0))
pos = r.indexIn(text, len(rList))
if len(rList) < len(text):
rList += [0] * (len(text) - len(rList))
result.append(rList)
return result
def textToFormatArrayNoMarkup(text):
"""
Same as textToFormatArray, except that it removes all the markup from the
text and returns two elements:
the array
the text without markup
"""
r = textToFormatArray(text)
result = [[], [], [], [], [], []]
rText = ""
for i in range(len(text)):
t = max([k[i] for k in r]) # kind of flattens all the format array
if t != 2:
rText += text[i]
[result[k].append(r[k][i]) for k in range(len(r))]
return rText, result
def translateSelectionToUnformattedText(text, start, end):
"""
Translate the start / end of selection from a formatted text to an
unformatted one.
"""
r = textToFormatArray(text)
rStart, rEnd = start, end
for i in range(len(text)):
t = max([k[i] for k in r]) # kind of flattens all the format array
if t == 2: # t == 2 means this character is markup
if i <= start: rStart -= 1
if i < end: rEnd -= 1
return rStart, rEnd
def translateSelectionToFormattedText(text, start, end):
"""
Translate the start / end of selection from a formatted text to an
unformatted one.
"""
r = textToFormatArray(text)
rStart, rEnd = start, end
for i in range(len(text)):
t = max([k[i] for k in r]) # kind of flattens all the format array
if t == 2: # t == 2 means this character is markup
if i <= start: rStart -= 1
if i < end: rEnd -= 1
return rStart, rEnd
def printArray(array):
print(("".join([str(j) for j in array])))
def printArrays(arrays):
for i in arrays: printArray(i)
def reformatText(text, markupArray):
"""
Takes a text without formatting markup, and an array generated by
textToFormatArray, and adds the propper markup.
"""
rText = ""
markup = ["**", "//", "__", "--", "``", "''"]
for k, m in enumerate(markupArray):
# m = markupArray[k]
_open = False # Are we in an _openned markup
d = 0
alreadySeen = []
for i, t in enumerate(text):
insert = False
if not _open and m[i] == 1:
insert = True
_open = True
if _open and m[i] == 0:
insert = True
_open = False
if _open and m[i] > 1:
z = i
while m[z] == m[i]: z += 1
if m[z] != 1 and not m[i] in alreadySeen:
insert = True
_open = False
alreadySeen.append(m[i])
if insert:
rText += markup[k]
for j, m2 in enumerate(markupArray):
# The other array still have the same length
if j > k:
# Insert 2 for bold, 3 for italic, etc.
m2.insert(i + d, k + 2)
m2.insert(i + d, k + 2)
alreadySeen = []
d += 2
rText += t
if _open:
rText += markup[k]
for j, m2 in enumerate(markupArray):
# The other array still have the same length
if j > k:
# Insert 2 for bold, 3 for italic, etc.
m2.insert(i + d, k + 2)
m2.insert(i + d, k + 2)
text = rText
rText = ""
# Clean up
# Exclude first and last space of the markup
for markup in ["\*", "/", "_", "-", "`", "\'"]:
# r = QRegExp(r'(' + markup * 2 + ')(\s+)(.+)(' + markup * 2 + ')')
# r.setMinimal(True)
# text.replace(r, "\\2\\1\\3\\4")
text = re.sub(r'(' + markup * 2 + ')(\s+?)(.+?)(' + markup * 2 + ')',
"\\2\\1\\3\\4",
text)
# r = QRegExp(r'(' + markup * 2 + ')(.+)(\s+)(' + markup * 2 + ')')
# r.setMinimal(True)
# text.replace(r, "\\1\\2\\4\\3")
text = re.sub(r'(' + markup * 2 + ')(.+?)(\s+?)(' + markup * 2 + ')',
"\\1\\2\\4\\3",
text)
return text
def cleanFormat(text):
"""Makes markup clean (removes doubles, etc.)"""
t, a = textToFormatArrayNoMarkup(text)
return reformatText(t, a)
class State:
NORMAL = 0
TITLE_1 = 1
TITLE_2 = 2
TITLE_3 = 3
TITLE_4 = 4
TITLE_5 = 5
NUMBERED_TITLE_1 = 6
NUMBERED_TITLE_2 = 7
NUMBERED_TITLE_3 = 8
NUMBERED_TITLE_4 = 9
NUMBERED_TITLE_5 = 10
TITLES = [TITLE_1, TITLE_2, TITLE_3, TITLE_4, TITLE_5, NUMBERED_TITLE_1,
NUMBERED_TITLE_2, NUMBERED_TITLE_3, NUMBERED_TITLE_4,
NUMBERED_TITLE_5]
# AREA
COMMENT_AREA = 11
CODE_AREA = 12
RAW_AREA = 13
TAGGED_AREA = 14
# AREA MARKUP
COMMENT_AREA_BEGINS = 15
COMMENT_AREA_ENDS = 16
CODE_AREA_BEGINS = 17
CODE_AREA_ENDS = 18
RAW_AREA_BEGINS = 19
RAW_AREA_ENDS = 20
TAGGED_AREA_BEGINS = 21
TAGGED_AREA_ENDS = 22
# LINE
COMMENT_LINE = 30
CODE_LINE = 31
RAW_LINE = 32
TAGGED_LINE = 33
SETTINGS_LINE = 34
BLOCKQUOTE_LINE = 35
HORIZONTAL_LINE = 36
HEADER_LINE = 37
# LIST
LIST_BEGINS = 40
LIST_ENDS = 41
LIST_EMPTY = 42
LIST_BULLET = 43
LIST_BULLET_ENDS = 44
LIST = [40, 41, 42] + list(range(100, 201))
# TABLE
TABLE_LINE = 50
TABLE_HEADER = 51
# OTHER
MARKUP = 60
LINKS = 61
MACRO = 62
DEFAULT = 63
@staticmethod
def titleLevel(state):
"""
Returns the level of the title, from the block state.
"""
return {
State.TITLE_1: 1,
State.TITLE_2: 2,
State.TITLE_3: 3,
State.TITLE_4: 4,
State.TITLE_5: 5,
State.NUMBERED_TITLE_1: 1,
State.NUMBERED_TITLE_2: 2,
State.NUMBERED_TITLE_3: 3,
State.NUMBERED_TITLE_4: 4,
State.NUMBERED_TITLE_5: 5,
}.get(state, -1)

View file

@ -1,547 +0,0 @@
#!/usr/bin/python
# -*- coding: utf8 -*-
# This is aiming at implementing every rule from www.txt2tags.org/rules.html
# But we're not there yet.
# FIXME: macro words not hilighted properly if at the begining of a line.
# TODO: parse %!postproc et !%preproc, et si la ligne se termine par une couleur en commentaire (%#FF00FF),
# utiliser cette couleur pour highlighter. Permet des règles customisées par document, facilement.
import re
from PyQt5.QtCore import QRegExp, QDir, QFileInfo
from PyQt5.QtGui import QTextBlockFormat, QTextCursor, QTextCharFormat, QBrush
from manuskript.ui.editors.basicHighlighter import basicHighlighter
from manuskript.ui.editors.blockUserData import blockUserData
from manuskript.ui.editors.t2tFunctions import State, textToFormatArray
from manuskript.ui.editors.t2tHighlighterStyle import t2tHighlighterStyle
class t2tHighlighter(basicHighlighter):
"""Syntax highlighter for the Txt2Tags language.
"""
def __init__(self, editor, style="Default"):
basicHighlighter.__init__(self, editor)
# Stupid variable that fixes the loss of QTextBlockUserData.
self.thisDocument = editor.document()
self.style = t2tHighlighterStyle(self.editor, self._defaultCharFormat, style)
self.inDocRules = []
rules = [
(r'^\s*[-=_]{20,}\s*$', State.HORIZONTAL_LINE),
(r'^\s*(\+{1})([^\+].*[^\+])(\+{1})(\[[A-Za-z0-9_-]*\])?\s*$', State.NUMBERED_TITLE_1),
(r'^\s*(\+{2})([^\+].*[^\+])(\+{2})(\[[A-Za-z0-9_-]*\])?\s*$', State.NUMBERED_TITLE_2),
(r'^\s*(\+{3})([^\+].*[^\+])(\+{3})(\[[A-Za-z0-9_-]*\])?\s*$', State.NUMBERED_TITLE_3),
(r'^\s*(\+{4})([^\+].*[^\+])(\+{4})(\[[A-Za-z0-9_-]*\])?\s*$', State.NUMBERED_TITLE_4),
(r'^\s*(\+{5})([^\+].*[^\+])(\+{5})(\[[A-Za-z0-9_-]*\])?\s*$', State.NUMBERED_TITLE_5),
(r'^\s*(={1})([^=].*[^=])(={1})(\[[A-Za-z0-9_-]*\])?\s*$', State.TITLE_1),
(r'^\s*(={2})([^=].*[^=])(={2})(\[[A-Za-z0-9_-]*\])?\s*$', State.TITLE_2),
(r'^\s*(={3})([^=].*[^=])(={3})(\[[A-Za-z0-9_-]*\])?\s*$', State.TITLE_3),
(r'^\s*(={4})([^=].*[^=])(={4})(\[[A-Za-z0-9_-]*\])?\s*$', State.TITLE_4),
(r'^\s*(={5})([^=].*[^=])(={5})(\[[A-Za-z0-9_-]*\])?\s*$', State.TITLE_5),
(r'^%!.*$', State.SETTINGS_LINE),
(r'^%[^!]?.*$', State.COMMENT_LINE),
(r'^\t.+$', State.BLOCKQUOTE_LINE),
(r'^(```)(.+)$', State.CODE_LINE),
(r'^(""")(.+)$', State.RAW_LINE),
(r'^(\'\'\')(.+)$', State.TAGGED_LINE),
(r'^\s*[-+:] [^ ].*$', State.LIST_BEGINS),
(r'^\s*[-+:]\s*$', State.LIST_ENDS),
(r'^ *\|\| .*$', State.TABLE_HEADER),
(r'^ *\| .*$', State.TABLE_LINE)
]
# Generate rules to identify blocks
State.Rules = [(QRegExp(pattern), state)
for (pattern, state) in rules]
State.Recursion = 0
def setDefaultCharFormat(self, cf):
self._defaultCharFormat = cf
self.setStyle()
self.rehighlight()
def highlightBlock(self, text):
"""Apply syntax highlighting to the given block of text.
"""
basicHighlighter.highlightBlockBefore(self, text)
# Check if syntax highlighting is enabled
if self.style is None:
default = QTextBlockFormat()
QTextCursor(self.currentBlock()).setBlockFormat(default)
print("t2tHighlighter.py: is style supposed to be None?")
return
block = self.currentBlock()
oldState = blockUserData.getUserState(block)
self.identifyBlock(block)
# formatBlock prevent undo/redo from working
# TODO: find a todo/undo compatible way of formatting block
# self.formatBlock(block)
state = blockUserData.getUserState(block)
data = blockUserData.getUserData(block)
inList = self.isList(block)
op = self.style.format(State.MARKUP)
# self.setFormat(0, len(text), self.style.format(State.DEFAULT))
# InDocRules: is it a settings which might have a specific rule,
# a comment which contains color infos, or a include conf?
# r'^%!p[or][se]t?proc[^\s]*\s*:\s*\'(.*)\'\s*\'.*\''
rlist = [QRegExp(r'^%!p[or][se]t?proc[^\s]*\s*:\s*((\'[^\']*\'|\"[^\"]*\")\s*(\'[^\']*\'|\"[^\"]*\"))'),
# pre/postproc
QRegExp(r'^%.*\s\((.*)\)'), # comment
QRegExp(r'^%!includeconf:\s*([^\s]*)\s*')] # includeconf
for r in rlist:
if r.indexIn(text) != -1:
self.parseInDocRules()
# Format the whole line:
for lineState in [
State.BLOCKQUOTE_LINE,
State.HORIZONTAL_LINE,
State.HEADER_LINE,
]:
if not inList and state == lineState:
self.setFormat(0, len(text), self.style.format(lineState))
for (lineState, marker) in [
(State.COMMENT_LINE, "%"),
(State.CODE_LINE, "```"),
(State.RAW_LINE, "\"\"\""),
(State.TAGGED_LINE, "'''"),
(State.SETTINGS_LINE, "%!")
]:
if state == lineState and \
not (inList and state == State.SETTINGS_LINE):
n = 0
# If it's a comment, we want to highlight all '%'.
if state == State.COMMENT_LINE:
while text[n:n + 1] == "%":
n += 1
n -= 1
# Apply Format
self.setFormat(0, len(marker) + n, op)
self.setFormat(len(marker) + n,
len(text) - len(marker) - n,
self.style.format(lineState))
# If it's a setting, we might do something
if state == State.SETTINGS_LINE:
# Target
r = QRegExp(r'^%!([^\s]+)\s*:\s*(\b\w*\b)$')
if r.indexIn(text) != -1:
setting = r.cap(1)
val = r.cap(2)
if setting == "target" and \
val in self.editor.main.targetsNames:
self.editor.fileWidget.preview.setPreferredTarget(val)
# Pre/postproc
r = QRegExp(r'^%!p[or][se]t?proc[^\s]*\s*:\s*((\'[^\']*\'|\"[^\"]*\")\s*(\'[^\']*\'|\"[^\"]*\"))')
if r.indexIn(text) != -1:
p = r.pos(1)
length = len(r.cap(1))
self.setFormat(p, length, self.style.makeFormat(base=self.format(p),
fixedPitch=True))
# Tables
for lineState in [State.TABLE_LINE, State.TABLE_HEADER]:
if state == lineState:
for i, t in enumerate(text):
if t == "|":
self.setFormat(i, 1, op)
else:
self.setFormat(i, 1, self.style.format(lineState))
# Lists
# if text == " p": print(data.isList())
if data.isList():
r = QRegExp(r'^\s*[\+\-\:]? ?')
r.indexIn(text)
self.setFormat(0, r.matchedLength(), self.style.format(State.LIST_BULLET))
# if state == State.LIST_BEGINS:
# r = QRegExp(r'^\s*[+-:] ')
# r.indexIn(text)
# self.setFormat(0, r.matchedLength(), self.style.format(State.LIST_BULLET))
if state == State.LIST_ENDS:
self.setFormat(0, len(text), self.style.format(State.LIST_BULLET_ENDS))
# Titles
if not inList and state in State.TITLES:
r = [i for (i, s) in State.Rules if s == state][0]
pos = r.indexIn(text)
if pos >= 0:
f = self.style.format(state)
# Uncomment for markup to be same size as title
# op = self.formats(preset="markup",
# base=self.formats(preset=state))
self.setFormat(r.pos(2), len(r.cap(2)), f)
self.setFormat(r.pos(1), len(r.cap(1)), op)
self.setFormat(r.pos(3), len(r.cap(3)), op)
# Areas: comment, code, raw tagged
for (begins, middle, ends) in [
(State.COMMENT_AREA_BEGINS, State.COMMENT_AREA, State.COMMENT_AREA_ENDS),
(State.CODE_AREA_BEGINS, State.CODE_AREA, State.CODE_AREA_ENDS),
(State.RAW_AREA_BEGINS, State.RAW_AREA, State.RAW_AREA_ENDS),
(State.TAGGED_AREA_BEGINS, State.TAGGED_AREA, State.TAGGED_AREA_ENDS),
]:
if state == middle:
self.setFormat(0, len(text), self.style.format(middle))
elif state in [begins, ends]:
self.setFormat(0, len(text), op)
# Inline formatting
if state not in [
# State.COMMENT_AREA,
# State.COMMENT_LINE,
State.RAW_AREA,
State.RAW_LINE,
State.CODE_AREA,
State.CODE_LINE,
State.TAGGED_AREA,
State.TAGGED_LINE,
State.SETTINGS_LINE,
State.HORIZONTAL_LINE,
] and state not in State.TITLES:
formatArray = textToFormatArray(text)
# InDocRules
for (r, c) in self.inDocRules:
i = re.finditer(r.decode('utf8'), text, re.UNICODE)
for m in i:
f = self.format(m.start())
l = m.end() - m.start()
if "," in c:
c1, c2 = c.split(",")
self.setFormat(m.start(), l,
self.style.makeFormat(color=c1, bgcolor=c2, base=f))
else:
self.setFormat(m.start(), l,
self.style.makeFormat(color=c, base=f))
# Links
if state not in [State.COMMENT_LINE, State.COMMENT_AREA]:
r = QRegExp(r'\[(\[[^\]]*\])?[^\]]*\s*([^\s]+)\]')
r.setMinimal(False)
pos = r.indexIn(text)
links = []
while pos >= 0:
# TODO: The text should not be formatted if [**not bold**]
# if max([k[pos] for k in formatArray]) == 0 or 1 == 1:
self.setFormat(pos, 1,
self.style.format(State.MARKUP))
self.setFormat(pos + 1, len(r.cap(0)) - 1,
self.style.format(State.LINKS))
self.setFormat(pos + len(r.cap(0)) - 1, 1,
self.style.format(State.MARKUP))
if r.pos(2) > 0:
_f = QTextCharFormat(self.style.format(State.LINKS))
_f.setForeground(QBrush(_f.foreground()
.color().lighter()))
_f.setFontUnderline(True)
self.setFormat(r.pos(2), len(r.cap(2)), _f)
links.append([pos, len(r.cap(0))]) # To remember for the next highlighter (single links)
pos = r.indexIn(text, pos + 1)
# Links like www.theologeek.ch, http://www.fsf.org, ...
# FIXME: - "http://adresse et http://adresse" is detected also as italic
# - some error, like "http://adress.htm." also color the final "."
# - also: adresse@email.com, ftp://, www2, www3, etc.
# - But for now, does the job
r = QRegExp(r'http://[^\s]*|www\.[a-zA-Z0-9-_]+\.[a-zA-Z0-9-_]+[^\s]*')
# r.setMinimal(True)
pos = r.indexIn(text)
while pos >= 0:
for k in links:
# print pos, k[0], k[1]
if k[0] < pos < k[0] + k[1]: # already highlighted
break
else:
self.setFormat(pos, len(r.cap(0)), self.style.format(State.LINKS))
pos = r.indexIn(text, pos + 1)
# Bold, Italic, Underline, Code, Tagged, Strikeout
for i, t in enumerate(text):
f = self.format(i)
beautifiers = [k[i] for k in formatArray]
self.setFormat(i, 1, self.style.beautifyFormat(f, beautifiers))
# Macro words
for r in [r'(%%)\b\w+\b', r'(%%)\b\w+\b\(.+\)']:
r = QRegExp(r)
r.setMinimal(True)
pos = r.indexIn(text)
while pos >= 0:
if max([k[pos] for k in formatArray]) == 0:
self.setFormat(pos, len(r.cap(0)),
self.style.format(State.MACRO))
pos = r.indexIn(text, pos + 1)
# Highlighted word (for search)
if self.editor.highlightWord:
if self.editor.highligtCS and self.editor.highlightWord in text or \
not self.editor.highlightCs and self.editor.highlightWord.lower() in text.lower():
# if self.editor.highlightCS:
# s = self.editor.highlightWord
# else:
# s = self.editor.highlightWord.toLower()
# print(s)
p = text.indexOf(self.editor.highlightWord, cs=self.editor.highlightCS)
while p >= 0:
self.setFormat(p, len(self.editor.highlightWord),
self.style.makeFormat(preset="higlighted", base=self.format(p)))
p = text.indexOf(self.editor.highlightWord, p + 1, cs=self.editor.highlightCS)
### Highlight Selection
### TODO: way to slow, find another way.
##sel = self.editor.textCursor().selectedText()
##if len(sel) > 5: self.keywordRules.append((QRegExp(sel), "selected"))
## Do keyword formatting
# for expression, style in self.keywordRules:
# expression.setMinimal( True )
# index = expression.indexIn(text, 0)
## There might be more than one on the same line
# while index >= 0:
# length = expression.cap(0).length()
# f = self.formats(preset=style, base=self.formats(index))
# self.setFormat(index, length, f)
# index = expression.indexIn(text, index + length)
basicHighlighter.highlightBlockAfter(self, text)
def identifyBlock(self, block):
"""Identifies what block type it is, and set userState and userData
accordingly."""
text = block.text()
data = blockUserData.getUserData(block)
# Header Lines
# No header line here
# if block.blockNumber() == 0:
# block.setUserState(State.HEADER_LINE)
# return
# elif block.blockNumber() in [1, 2] and \
# self.document().findBlockByNumber(0).text():
# block.setUserState(State.HEADER_LINE)
# return
state = 0
inList = False
blankLinesBefore = 0
# if text.contains(QRegExp(r'^\s*[-+:] [^ ].*[^-+]{1}\s*$')):
if QRegExp(r'^\s*[-+:] [^ ].*[^-+]{1}\s*$').indexIn(text) != -1:
state = State.LIST_BEGINS
# List stuff
if self.isList(block.previous()) or state == State.LIST_BEGINS:
inList = True
# listLevel and leadingSpaces
# FIXME: not behaving exactly correctly...
lastData = blockUserData.getUserData(block.previous())
if state == State.LIST_BEGINS:
leadingSpaces = QRegExp(r'[-+:]').indexIn(text, 0)
data.setLeadingSpaces(leadingSpaces)
data.setListSymbol(text[leadingSpaces])
if self.isList(block.previous()):
# The last block was also a list.
# We need to check if this is the same level, or a sublist
if leadingSpaces > lastData.leadingSpaces():
# This is a sublevel list
data.setListLevel(lastData.listLevel() + 1)
else:
# This is same level
data.setListLevel(lastData.listLevel())
else:
data.setListLevel(1)
else:
data.setListLevel(lastData.listLevel())
data.setLeadingSpaces(lastData.leadingSpaces())
data.setListSymbol(lastData.listSymbol())
# Blank lines before (two = end of list)
blankLinesBefore = self.getBlankLines(block.previous())
if not QRegExp(r'^\s*$').indexIn(block.previous().text()) != -1 and \
not blockUserData.getUserState(block.previous()) in [State.COMMENT_LINE,
State.COMMENT_AREA, State.COMMENT_AREA_BEGINS,
State.COMMENT_AREA_ENDS]:
blankLinesBefore = 0
elif not blockUserData.getUserState(block.previous()) in \
[State.COMMENT_LINE, State.COMMENT_AREA,
State.COMMENT_AREA_BEGINS, State.COMMENT_AREA_ENDS]:
blankLinesBefore += 1
if blankLinesBefore == 2:
# End of list.
blankLinesBefore = 0
inList = False
if inList and QRegExp(r'^\s*$').indexIn(text) != -1:
state = State.LIST_EMPTY
# Areas
for (begins, middle, ends, marker) in [
(State.COMMENT_AREA_BEGINS, State.COMMENT_AREA, State.COMMENT_AREA_ENDS, "^%%%\s*$"),
(State.CODE_AREA_BEGINS, State.CODE_AREA, State.CODE_AREA_ENDS, "^```\s*$"),
(State.RAW_AREA_BEGINS, State.RAW_AREA, State.RAW_AREA_ENDS, "^\"\"\"\s*$"),
(State.TAGGED_AREA_BEGINS, State.TAGGED_AREA, State.TAGGED_AREA_ENDS, '^\'\'\'\s*$'),
]:
if QRegExp(marker).indexIn(text) != -1:
if blockUserData.getUserState(block.previous()) in [begins, middle]:
state = ends
break
else:
state = begins
break
if blockUserData.getUserState(block.previous()) in [middle, begins]:
state = middle
break
# Patterns (for lines)
if not state:
for (pattern, lineState) in State.Rules:
pos = pattern.indexIn(text)
if pos >= 0:
state = lineState
break
if state in [State.BLOCKQUOTE_LINE, State.LIST_ENDS]:
# FIXME: doesn't work exactly. Closes only the current level, not
# FIXME: the whole list.
inList = False
if inList and not state == State.LIST_BEGINS:
state += 100
if blankLinesBefore:
state += 100
block.setUserState(state)
block.setUserData(data)
def formatBlock(self, block):
"""
Formats the block according to its state.
"""
# TODO: Use QTextDocument format presets, and QTextBlock's
# TODO: blockFormatIndex. And move that in t2tHighlighterStyle.
state = block.userState()
blockFormat = QTextBlockFormat()
if state in [State.BLOCKQUOTE_LINE,
State.HEADER_LINE] + State.LIST:
blockFormat = self.style.formatBlock(block, state)
QTextCursor(block).setBlockFormat(blockFormat)
def getBlankLines(self, block):
"""Returns if there is a blank line before in the list."""
state = block.userState()
if state >= 200:
return 1
else:
return 0
def isList(self, block):
"""Returns TRUE if the block is in a list."""
if block.userState() == State.LIST_BEGINS or \
block.userState() >= 100:
return True
def setStyle(self, style="Default"):
if style in t2tHighlighterStyle.validStyles:
self.style = t2tHighlighterStyle(self.editor, self._defaultCharFormat, style)
else:
self.style = None
self.rehighlight()
def setFontPointSize(self, size):
self.defaultFontPointSize = size
self.style = t2tHighlighterStyle(self.editor, self.style.name)
self.rehighlight()
def parseInDocRules(self):
oldRules = self.inDocRules
self.inDocRules = []
t = self.thisDocument.toPlainText()
# Get all conf files
confs = []
lines = t.split("\n")
for l in lines:
r = QRegExp(r'^%!includeconf:\s*([^\s]*)\s*')
if r.indexIn(l) != -1:
confs.append(r.cap(1))
# Try to load conf files
for c in confs:
try:
import codecs
f = self.editor.fileWidget.file
d = QDir.cleanPath(QFileInfo(f).absoluteDir().absolutePath() + "/" + c)
file = codecs.open(d, 'r', "utf-8")
except:
print(("Error: cannot open {}.".format(c)))
continue
# We add the content to the current lines of the current document
lines += file.readlines() # lines.extend(file.readlines())
# b = self.thisDocument.firstBlock()
lastColor = ""
# while b.isValid():
for l in lines:
text = l # b.text()
r = QRegExp(r'^%!p[or][se]t?proc[^\s]*\s*:\s*(\'[^\']*\'|\"[^\"]*\")\s*(\'[^\']*\'|\"[^\"]*\")')
if r.indexIn(text) != -1:
rule = r.cap(1)[1:-1]
# Check if there was a color-comment above that post/preproc bloc
if lastColor:
self.inDocRules.append((str(rule), lastColor))
# Check if previous block is a comment like it should
else:
previousText = lines[lines.indexOf(l) - 1] # b.previous().text()
r = QRegExp(r'^%.*\s\((.*)\)')
if r.indexIn(previousText) != -1:
lastColor = r.cap(1)
self.inDocRules.append((str(rule), lastColor))
else:
lastColor = ""
# b = b.next()
if oldRules != self.inDocRules:
# Rules have changed, we need to rehighlight
# print("Rules have changed.", len(self.inDocRules))
# self.rehighlight() # Doesn't work (seg fault), why?
pass
# b = self.thisDocument.firstBlock()
# while b.isValid():
# for (r, c) in self.inDocRules:
# r = QRegExp(r)
# pos = r.indexIn(b.text())
# if pos >= 0:
# print("rehighlighting:", b.text())
# self.rehighlightBlock(b)
# break
# b = b.next()

View file

@ -1,252 +0,0 @@
#!/usr/bin/python
# -*- coding: utf8 -*-
# TODO: creates a general way to generate styles (and edit/import/export)
from PyQt5.QtCore import QRegExp, Qt
from PyQt5.QtGui import QFont, QTextBlockFormat, QColor, QFontMetrics, QTextCharFormat
from PyQt5.QtWidgets import qApp
from manuskript.ui.editors.blockUserData import blockUserData
from manuskript.ui.editors.t2tFunctions import State
class t2tHighlighterStyle():
"""Style for the Syntax highlighter for the Txt2Tags language.
"""
validStyles = ["Default", "Monospace"]
def __init__(self, editor, charFormat, name="Default"):
self.editor = editor
self.name = name
self._defaultCharFormat = charFormat
# Defaults
# self.defaultFontPointSize = self.editor.defaultFontPointSize
self.defaultFontFamily = qApp.font().family()
self.tabStopWidth = 40
self.setupEditor()
if self.name == "Default":
self.initDefaults()
# Temporary other theme
elif self.name == "Monospace":
self.defaultFontFamily = "Monospace"
self.initDefaults()
for i in self.styles:
f = self.styles[i]
f.setFontFixedPitch(True)
f.setFontFamily(self.defaultFontFamily)
f.setFontPointSize(self._defaultCharFormat.font().pointSize())
self.styles[i] = f
def setupEditor(self):
self.editor.setTabStopWidth(self.tabStopWidth)
def initDefaults(self):
self.styles = {}
for i in [State.CODE_AREA,
State.CODE_LINE,
State.COMMENT_AREA,
State.COMMENT_LINE,
State.SETTINGS_LINE,
State.BLOCKQUOTE_LINE,
State.RAW_AREA,
State.RAW_LINE,
State.TAGGED_AREA,
State.TAGGED_LINE,
State.TITLE_1,
State.TITLE_2,
State.TITLE_3,
State.TITLE_4,
State.TITLE_5,
State.NUMBERED_TITLE_1,
State.NUMBERED_TITLE_2,
State.NUMBERED_TITLE_3,
State.NUMBERED_TITLE_4,
State.NUMBERED_TITLE_5,
State.TABLE_HEADER,
State.TABLE_LINE,
State.HORIZONTAL_LINE,
State.MARKUP,
State.LIST_BULLET,
State.LIST_BULLET_ENDS,
State.LINKS,
State.MACRO,
State.DEFAULT,
State.HEADER_LINE]:
self.styles[i] = self.makeFormat(preset=i)
def format(self, state):
return self.styles[state]
def beautifyFormat(self, base, beautifiers):
"""Apply beautifiers given in beautifiers array to format"""
if max(beautifiers) == 2:
return self.makeFormat(preset=State.MARKUP, base=base)
else:
if beautifiers[0]: # bold
base.setFontWeight(QFont.Bold)
if beautifiers[1]: # italic
base.setFontItalic(True)
if beautifiers[2]: # underline
base.setFontUnderline(True)
if beautifiers[3]: # strikeout
base.setFontStrikeOut(True)
if beautifiers[4]: # code
base = self.makeFormat(base=base, preset=State.CODE_LINE)
if beautifiers[5]: # tagged
base = self.makeFormat(base=base, preset=State.TAGGED_LINE)
return base
def formatBlock(self, block, state):
"""Apply transformation to given block."""
blockFormat = QTextBlockFormat()
if state == State.BLOCKQUOTE_LINE:
# Number of tabs
n = block.text().indexOf(QRegExp(r'[^\t]'), 0)
blockFormat.setIndent(0)
blockFormat.setTextIndent(-self.tabStopWidth * n)
blockFormat.setLeftMargin(self.tabStopWidth * n)
# blockFormat.setRightMargin(self.editor.contentsRect().width()
# - self.editor.lineNumberAreaWidth()
# - fm.width("X") * self.editor.LimitLine
# + self.editor.tabStopWidth())
blockFormat.setAlignment(Qt.AlignJustify)
if self.name == "Default":
blockFormat.setTopMargin(5)
blockFormat.setBottomMargin(5)
elif state == State.HEADER_LINE:
blockFormat.setBackground(QColor("#EEEEEE"))
elif state in State.LIST:
data = blockUserData.getUserData(block)
if str(data.listSymbol()) in "+-":
blockFormat.setBackground(QColor("#EEFFEE"))
else:
blockFormat.setBackground(QColor("#EEEEFA"))
n = blockUserData.getUserData(block).leadingSpaces() + 1
f = QFontMetrics(QFont(self.defaultFontFamily,
self._defaultCharFormat.font().pointSize()))
fm = f.width(" " * n +
blockUserData.getUserData(block).listSymbol())
blockFormat.setTextIndent(-fm)
blockFormat.setLeftMargin(fm)
if blockUserData.getUserState(block) == State.LIST_BEGINS and \
self.name == "Default":
blockFormat.setTopMargin(5)
return blockFormat
def makeFormat(self, color='', style='', size='', base='', fixedPitch='',
preset='', title_level='', bgcolor=''):
"""
Returns a QTextCharFormat with the given attributes, using presets.
"""
_color = QColor()
# _format = QTextCharFormat()
# _format.setFont(self.editor.font())
# size = _format.fontPointSize()
_format = QTextCharFormat(self._defaultCharFormat)
# Base
if base:
_format = base
# Presets
if preset in [State.CODE_AREA, State.CODE_LINE, "code"]:
style = "bold"
color = "black"
fixedPitch = True
_format.setBackground(QColor("#EEEEEE"))
if preset in [State.COMMENT_AREA, State.COMMENT_LINE, "comment"]:
style = "italic"
color = "darkGreen"
if preset in [State.SETTINGS_LINE, "setting", State.MACRO]:
# style = "italic"
color = "magenta"
if preset in [State.BLOCKQUOTE_LINE]:
color = "red"
if preset in [State.HEADER_LINE]:
size *= 2
# print size
if preset in [State.RAW_AREA, State.RAW_LINE, "raw"]:
color = "blue"
if preset in [State.TAGGED_AREA, State.TAGGED_LINE, "tagged"]:
color = "purple"
if preset in State.TITLES:
style = "bold"
color = "darkRed" if State.titleLevel(preset) % 2 == 1 else "blue"
size = (self._defaultCharFormat.font().pointSize()
+ 11 - 2 * State.titleLevel(preset))
if preset == State.TABLE_HEADER:
style = "bold"
color = "darkMagenta"
if preset == State.TABLE_LINE:
color = "darkMagenta"
if preset == State.LIST_BULLET:
color = "red"
style = "bold"
fixedPitch = True
if preset == State.LIST_BULLET_ENDS:
color = "darkGray"
fixedPitch = True
if preset in [State.MARKUP, "markup"]:
color = "darkGray"
if preset in [State.HORIZONTAL_LINE]:
color = "cyan"
fixedPitch = True
if preset == State.LINKS:
color = "blue"
# style="underline"
if preset == "selected":
_format.setBackground(QColor("yellow"))
if preset == "higlighted":
bgcolor = "yellow"
# if preset == State.DEFAULT:
# size = self.defaultFontPointSize
# _format.setFontFamily(self.defaultFontFamily)
# Manual formatting
if color:
_color.setNamedColor(color)
_format.setForeground(_color)
if bgcolor:
_color.setNamedColor(bgcolor)
_format.setBackground(_color)
if 'bold' in style:
_format.setFontWeight(QFont.Bold)
if 'italic' in style:
_format.setFontItalic(True)
if 'strike' in style:
_format.setFontStrikeOut(True)
if 'underline' in style:
_format.setFontUnderline(True)
if size:
_format.setFontPointSize(size)
if fixedPitch:
_format.setFontFixedPitch(True)
return _format

View file

@ -11,6 +11,7 @@ from manuskript.enums import Outline
from manuskript.functions import AUC
from manuskript.functions import toString
from manuskript.models.outlineModel import outlineModel
from manuskript.ui.editors.MDFunctions import MDFormatSelection
from manuskript.ui.editors.MMDHighlighter import MMDHighlighter
from manuskript.ui.editors.basicHighlighter import basicHighlighter
from manuskript.ui.editors.textFormat import textFormat
@ -456,16 +457,12 @@ class textEditView(QTextEdit):
def applyFormat(self, _format):
if self._textFormat == "md":
# FIXME
print("Not implemented yet.")
# Model: from t2tFunctions
# if self._textFormat == "t2t":
# if _format == "Bold":
# t2tFormatSelection(self, 0)
# elif _format == "Italic":
# t2tFormatSelection(self, 1)
# elif _format == "Underline":
# t2tFormatSelection(self, 2)
# elif _format == "Clear":
# t2tClearFormat(self)
if _format == "Bold":
MDFormatSelection(self, 0)
elif _format == "Italic":
MDFormatSelection(self, 1)
elif _format == "Code":
MDFormatSelection(self, 2)
elif _format == "Clear":
MDFormatSelection(self)