2019-03-31 02:16:18 +00:00
|
|
|
# -*- Mode: Python; coding: utf-8; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
|
|
### BEGIN LICENSE
|
|
|
|
# Copyright (C) 2012, Wolf Vollprecht <w.vollprecht@gmail.com>
|
|
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
|
|
# under the terms of the GNU General Public License version 3, as published
|
|
|
|
# by the Free Software Foundation.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful, but
|
|
|
|
# WITHOUT ANY WARRANTY; without even the implied warranties of
|
|
|
|
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
|
|
|
|
# PURPOSE. See the GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
# You should have received a copy of the GNU General Public License along
|
|
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
### END LICENSE
|
|
|
|
|
|
|
|
import re
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
import gi
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
from gi.overrides import GLib
|
2019-03-31 02:16:18 +00:00
|
|
|
|
|
|
|
from uberwriter import helpers
|
|
|
|
|
|
|
|
gi.require_version('Gtk', '3.0')
|
|
|
|
from gi.repository import Gtk
|
|
|
|
from gi.repository import Pango
|
|
|
|
|
|
|
|
|
|
|
|
class MarkupHandler:
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
# Maximum number of characters for which to markup synchronously.
|
|
|
|
max_char_sync = 100000
|
|
|
|
|
|
|
|
# Regular expressions for various markdown constructs.
|
2019-03-31 02:16:18 +00:00
|
|
|
regex = {
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
"ITALIC": re.compile(r"(\*|_)(.+?)\1"),
|
|
|
|
"BOLD": re.compile(r"(\*\*|__)(.+?)\1"),
|
|
|
|
"BOLDITALIC": re.compile(r"(\*\*\*|___)(.+?)\1"),
|
|
|
|
"STRIKETHROUGH": re.compile(r"~~.+?~~"),
|
2019-04-11 01:30:32 +00:00
|
|
|
"LINK": re.compile(r"(\[).*(\]\(.+?\))"),
|
2019-05-01 15:40:14 +00:00
|
|
|
"HORIZONTALRULE": re.compile(r"\n\n([ ]{0,3}[*\-_]{3,}[ ]*)\n\n", re.MULTILINE),
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
"LIST": re.compile(r"^((?:\t|[ ]{4})*)[\-*+] .+", re.MULTILINE),
|
|
|
|
"NUMERICLIST": re.compile(r"^((\d|[a-z]|#)+[.)]) ", re.MULTILINE),
|
|
|
|
"NUMBEREDLIST": re.compile(r"^((?:\t|[ ]{4})*)((?:\d|[a-z])+[.)]) .+", re.MULTILINE),
|
|
|
|
"BLOCKQUOTE": re.compile(r"^[ ]{0,3}(?:>|(?:> )+).+", re.MULTILINE),
|
|
|
|
"HEADER": re.compile(r"^[ ]{0,3}(#{1,6}) [^\n]+", re.MULTILINE),
|
2019-05-01 15:40:14 +00:00
|
|
|
"HEADER_UNDER": re.compile(r"^\n[ ]{0,3}\w.+\n[ ]{0,3}[=\-]{3,}", re.MULTILINE),
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
"CODE": re.compile(r"(?:^|\n)[ ]{0,3}(([`~]{3}).+?[ ]{0,3}\2)(?:\n|$)", re.DOTALL),
|
|
|
|
"TABLE": re.compile(r"^[\-+]{5,}\n(.+?)\n[\-+]{5,}\n", re.DOTALL),
|
|
|
|
"MATH": re.compile(r"[$]{1,2}([^` ].+?[^`\\ ])[$]{1,2}"),
|
2019-03-31 02:16:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, text_view):
|
|
|
|
self.text_view = text_view
|
|
|
|
self.text_buffer = text_view.get_buffer()
|
|
|
|
|
|
|
|
# Styles
|
|
|
|
buffer = self.text_buffer
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
self.italic = buffer.create_tag('italic',
|
|
|
|
weight=Pango.Weight.NORMAL,
|
|
|
|
style=Pango.Style.ITALIC)
|
|
|
|
|
|
|
|
self.bold = buffer.create_tag('bold',
|
|
|
|
weight=Pango.Weight.BOLD,
|
|
|
|
style=Pango.Style.NORMAL)
|
|
|
|
|
|
|
|
self.bolditalic = buffer.create_tag('bolditalic',
|
|
|
|
weight=Pango.Weight.BOLD,
|
|
|
|
style=Pango.Style.ITALIC)
|
|
|
|
|
|
|
|
self.strikethrough = buffer.create_tag('strikethrough', strikethrough=True)
|
|
|
|
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
self.horizontalrule = buffer.create_tag('centertext',
|
|
|
|
justification=Gtk.Justification.CENTER)
|
2019-03-31 02:16:18 +00:00
|
|
|
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
self.plaintext = buffer.create_tag('plaintext',
|
|
|
|
weight=Pango.Weight.NORMAL,
|
|
|
|
style=Pango.Style.NORMAL,
|
|
|
|
strikethrough=False,
|
|
|
|
justification=Gtk.Justification.LEFT)
|
2019-03-31 02:16:18 +00:00
|
|
|
|
2019-04-26 23:43:06 +00:00
|
|
|
self.table = buffer.create_tag('table',
|
|
|
|
wrap_mode=Gtk.WrapMode.NONE,
|
|
|
|
pixels_above_lines=0,
|
|
|
|
pixels_below_lines=0)
|
2019-03-31 02:16:18 +00:00
|
|
|
|
|
|
|
self.mathtext = buffer.create_tag('mathtext')
|
|
|
|
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
self.graytext = buffer.create_tag('graytext',
|
|
|
|
foreground='gray',
|
|
|
|
weight=Pango.Weight.NORMAL,
|
|
|
|
style=Pango.Style.NORMAL)
|
|
|
|
|
|
|
|
# Margin and indents
|
|
|
|
# A baseline margin is set to allow negative offsets for formatting headers, lists, etc
|
|
|
|
self.margins_indents = {}
|
2019-04-26 23:43:06 +00:00
|
|
|
self.baseline_margin = 0
|
|
|
|
self.char_width = 0
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
self.update_margins_indents()
|
2019-03-31 02:16:18 +00:00
|
|
|
|
|
|
|
# Style
|
|
|
|
self.on_style_updated()
|
|
|
|
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
self.version = 0
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
def on_style_updated(self, *_):
|
|
|
|
(found, color) = self.text_view.get_style_context().lookup_color('math_text_color')
|
|
|
|
if not found:
|
|
|
|
(_, color) = self.text_view.get_style_context().lookup_color('foreground_color')
|
|
|
|
self.mathtext.set_property("foreground", color.to_string())
|
|
|
|
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
def apply(self):
|
|
|
|
self.version = self.version + 1
|
|
|
|
if self.text_buffer.get_char_count() < self.max_char_sync:
|
|
|
|
self.do_apply()
|
2019-03-31 02:16:18 +00:00
|
|
|
else:
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
GLib.idle_add(self.do_apply, self.version)
|
|
|
|
|
|
|
|
def do_apply(self, version=None):
|
|
|
|
if version is not None and version != self.version:
|
|
|
|
return
|
|
|
|
|
|
|
|
buffer = self.text_buffer
|
|
|
|
start = buffer.get_start_iter()
|
|
|
|
end = buffer.get_end_iter()
|
|
|
|
offset = 0
|
2019-03-31 02:16:18 +00:00
|
|
|
|
|
|
|
text = buffer.get_slice(start, end, False)
|
|
|
|
|
|
|
|
# Remove tags
|
|
|
|
buffer.remove_tag(self.italic, start, end)
|
|
|
|
buffer.remove_tag(self.bold, start, end)
|
|
|
|
buffer.remove_tag(self.bolditalic, start, end)
|
|
|
|
buffer.remove_tag(self.strikethrough, start, end)
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
buffer.remove_tag(self.horizontalrule, start, end)
|
|
|
|
buffer.remove_tag(self.plaintext, start, end)
|
|
|
|
buffer.remove_tag(self.table, start, end)
|
2019-03-31 02:16:18 +00:00
|
|
|
buffer.remove_tag(self.mathtext, start, end)
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
for tag in self.margins_indents.values():
|
2019-03-31 02:16:18 +00:00
|
|
|
buffer.remove_tag(tag, start, end)
|
|
|
|
buffer.remove_tag(self.graytext, start, end)
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
buffer.remove_tag(self.graytext, start, end)
|
2019-03-31 02:16:18 +00:00
|
|
|
|
|
|
|
# Apply "_italic_" tag (italic)
|
|
|
|
matches = re.finditer(self.regex["ITALIC"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
|
|
|
buffer.apply_tag(self.italic, start_iter, end_iter)
|
|
|
|
|
|
|
|
# Apply "**bold**" tag (bold)
|
|
|
|
matches = re.finditer(self.regex["BOLD"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
|
|
|
buffer.apply_tag(self.bold, start_iter, end_iter)
|
|
|
|
|
|
|
|
# Apply "***bolditalic***" tag (bold/italic)
|
|
|
|
matches = re.finditer(self.regex["BOLDITALIC"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
|
|
|
buffer.apply_tag(self.bolditalic, start_iter, end_iter)
|
|
|
|
|
|
|
|
# Apply "~~strikethrough~~" tag (strikethrough)
|
|
|
|
matches = re.finditer(self.regex["STRIKETHROUGH"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
|
|
|
buffer.apply_tag(self.strikethrough, start_iter, end_iter)
|
|
|
|
|
2019-04-11 01:30:32 +00:00
|
|
|
matches = re.finditer(self.regex["LINK"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start(1))
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end(1))
|
|
|
|
buffer.apply_tag(self.graytext, start_iter, end_iter)
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start(2))
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end(2))
|
|
|
|
buffer.apply_tag(self.graytext, start_iter, end_iter)
|
|
|
|
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
# Apply "---" horizontal rule tag (center)
|
|
|
|
matches = re.finditer(self.regex["HORIZONTALRULE"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start(1))
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end(1))
|
|
|
|
buffer.apply_tag(self.horizontalrule, start_iter, end_iter)
|
|
|
|
|
2019-03-31 02:16:18 +00:00
|
|
|
# Apply "* list" tag (offset)
|
|
|
|
matches = re.finditer(self.regex["LIST"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
|
|
|
# Lists use character+space (eg. "* ")
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
length = 2
|
2019-03-31 02:16:18 +00:00
|
|
|
nest = len(match.group(1).replace(" ", "\t"))
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
margin = -length - 2 * nest
|
|
|
|
indent = -length - 2 * length * nest
|
|
|
|
buffer.apply_tag(self.get_margin_indent_tag(margin, indent), start_iter, end_iter)
|
2019-03-31 02:16:18 +00:00
|
|
|
|
|
|
|
# Apply "1. numbered list" tag (offset)
|
|
|
|
matches = re.finditer(self.regex["NUMBEREDLIST"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
|
|
|
# Numeric lists use numbers/letters+dot/parens+space (eg. "123. ")
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
length = len(match.group(2)) + 1
|
2019-03-31 02:16:18 +00:00
|
|
|
nest = len(match.group(1).replace(" ", "\t"))
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
margin = -length - 2 * nest
|
|
|
|
indent = -length - 2 * length * nest
|
|
|
|
buffer.apply_tag(self.get_margin_indent_tag(margin, indent), start_iter, end_iter)
|
2019-03-31 02:16:18 +00:00
|
|
|
|
|
|
|
# Apply "> blockquote" tag (offset)
|
|
|
|
matches = re.finditer(self.regex["BLOCKQUOTE"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
buffer.apply_tag(self.get_margin_indent_tag(2, -2), start_iter, end_iter)
|
2019-03-31 02:16:18 +00:00
|
|
|
|
|
|
|
# Apply "#" tag (offset + bold)
|
|
|
|
matches = re.finditer(self.regex["HEADER"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
margin = -len(match.group(1)) - 1
|
|
|
|
buffer.apply_tag(self.get_margin_indent_tag(margin, 0), start_iter, end_iter)
|
2019-03-31 02:16:18 +00:00
|
|
|
buffer.apply_tag(self.bold, start_iter, end_iter)
|
|
|
|
|
|
|
|
# Apply "======" header underline tag (bold)
|
|
|
|
matches = re.finditer(self.regex["HEADER_UNDER"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
|
|
|
buffer.apply_tag(self.bold, start_iter, end_iter)
|
|
|
|
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
# Apply "```" code tag (offset)
|
|
|
|
matches = re.finditer(self.regex["CODE"], text)
|
2019-03-31 02:16:18 +00:00
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start(1))
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end(1))
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
buffer.apply_tag(self.get_margin_indent_tag(0, 2), start_iter, end_iter)
|
|
|
|
buffer.apply_tag(self.plaintext, start_iter, end_iter)
|
2019-03-31 02:16:18 +00:00
|
|
|
|
|
|
|
# Apply "---" table tag (wrap/pixels)
|
|
|
|
matches = re.finditer(self.regex["TABLE"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
|
|
|
buffer.apply_tag(self.table, start_iter, end_iter)
|
|
|
|
|
|
|
|
# Apply "$math$" tag (colorize)
|
|
|
|
matches = re.finditer(self.regex["MATH"], text)
|
|
|
|
for match in matches:
|
|
|
|
start_iter = buffer.get_iter_at_offset(offset + match.start())
|
|
|
|
end_iter = buffer.get_iter_at_offset(offset + match.end())
|
|
|
|
buffer.apply_tag(self.mathtext, start_iter, end_iter)
|
|
|
|
|
|
|
|
# Apply focus mode tag (grey out before/after current sentence)
|
|
|
|
if self.text_view.focus_mode:
|
|
|
|
cursor_iter = buffer.get_iter_at_mark(buffer.get_insert())
|
|
|
|
start_sentence = cursor_iter.copy()
|
|
|
|
start_sentence.backward_sentence_start()
|
|
|
|
end_sentence = cursor_iter.copy()
|
|
|
|
end_sentence.forward_sentence_end()
|
|
|
|
if start.compare(start_sentence) <= 0:
|
|
|
|
buffer.apply_tag(self.graytext, start, start_sentence)
|
|
|
|
if end.compare(end_sentence) >= 0:
|
|
|
|
buffer.apply_tag(self.graytext, end_sentence, end)
|
|
|
|
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
# Margin and indent are cumulative. They differ in two ways:
|
|
|
|
# * Margin is always in the beginning, which means it effectively only affects the first line
|
|
|
|
# of multi-line text. Indent is applied to every line.
|
|
|
|
# * Margin level can be negative, as a baseline margin exists from which it can be subtracted.
|
|
|
|
# Indent is always positive, or 0.
|
|
|
|
def get_margin_indent_tag(self, margin_level, indent_level):
|
|
|
|
level = (margin_level, indent_level)
|
|
|
|
if level not in self.margins_indents:
|
|
|
|
margin, indent = self.get_margin_indent(margin_level, indent_level)
|
2019-04-26 23:43:06 +00:00
|
|
|
tag = self.text_buffer.create_tag(
|
|
|
|
"margin_indent_{}_{}".format(margin_level, indent_level),
|
|
|
|
left_margin=margin, indent=indent)
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
self.margins_indents[level] = tag
|
|
|
|
return tag
|
|
|
|
else:
|
|
|
|
return self.margins_indents[level]
|
|
|
|
|
2019-04-17 00:03:37 +00:00
|
|
|
def get_margin_indent(self, margin_level, indent_level, baseline_margin=None, char_width=None):
|
|
|
|
if baseline_margin is None:
|
2019-04-26 23:43:06 +00:00
|
|
|
baseline_margin = self.text_view.props.left_margin
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
if char_width is None:
|
2019-03-31 02:16:18 +00:00
|
|
|
char_width = helpers.get_char_width(self.text_view)
|
2019-04-17 00:03:37 +00:00
|
|
|
margin = max(baseline_margin + char_width * margin_level, 0)
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
indent = char_width * indent_level
|
|
|
|
return margin, indent
|
2019-03-31 02:16:18 +00:00
|
|
|
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
def update_margins_indents(self):
|
2019-04-26 23:43:06 +00:00
|
|
|
baseline_margin = self.text_view.props.left_margin
|
2019-03-31 02:16:18 +00:00
|
|
|
char_width = helpers.get_char_width(self.text_view)
|
|
|
|
|
2019-04-26 23:43:06 +00:00
|
|
|
# Bail out if neither the baseline margin nor character width change
|
|
|
|
if baseline_margin == self.baseline_margin and char_width == self.char_width:
|
|
|
|
return
|
|
|
|
self.baseline_margin = baseline_margin
|
|
|
|
self.char_width = char_width
|
|
|
|
|
|
|
|
# Adjust tab size
|
2019-03-31 02:16:18 +00:00
|
|
|
tab_array = Pango.TabArray.new(1, True)
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
tab_array.set_tab(0, Pango.TabAlign.LEFT, 4 * char_width)
|
2019-03-31 02:16:18 +00:00
|
|
|
self.text_view.set_tabs(tab_array)
|
|
|
|
|
2019-04-26 23:43:06 +00:00
|
|
|
# Adjust margins and indents
|
Add support for code blocks, improve overall markup handling
This commit adds markup support for code blocks, styling them in a
conservative manner, similar to blockquotes, solely indenting them.
Partially fixes #90
Code-wise, this means marking up around the cursor becomes exponentially
more complex, as a change in one line can affect multiple lines. Solving
it is non-trivial, so the whole document is always marked up.
Marking up the whole document is irrelevant for small to medium
documents, but can incur in a performance penalty for
very large documents (empirical testing: 1M characters takes ~0.15s).
To alleviate this, GLib.idle_add is used to ensure that markup is only
parsed and applied when the UI is idle. Again, small to medium-sized
documents see no difference. For very large documents, markup will be
slightly delayed to allow for a fluid typing experience.
It's important to note that the previous flows frequently used full
document markup: paste, focus mode, and search and replace.
In some extreme cases, doubly parsing (eg. paste + text change).
For very large documents, doing any of these actions would freeze the UI
unconditionally, so in more ways than one this is an upgrade.
Lastly, it's a little overzealous: with over 1M characters the UI itself
struggles more than parsing.
In sum:
* Markup is always applied to the whole document
* The code is simpler
* There is never double work
* Markup is applied when the UI is idle, for a more smooth experience
* Multi-line formatting is now possible to do reliably
2019-04-10 01:59:00 +00:00
|
|
|
for level, tag in self.margins_indents.items():
|
2019-04-17 00:03:37 +00:00
|
|
|
margin, indent = self.get_margin_indent(*level, baseline_margin, char_width)
|
2019-04-26 23:43:06 +00:00
|
|
|
tag.set_properties(left_margin=margin, indent=indent)
|