aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--TECHNOLOGY.md10
-rw-r--r--core/locale.conf4
-rw-r--r--core/locales/locale.ar.conf4
-rw-r--r--core/locales/locale.de.conf2
-rw-r--r--core/locales/locale.es.conf4
-rw-r--r--core/locales/locale.fr.conf4
-rw-r--r--core/locales/locale.it.conf4
-rw-r--r--core/locales/locale.pl.conf4
-rw-r--r--core/locales/locale.ru.conf2
-rw-r--r--core/locales/locale.sv.conf4
-rw-r--r--doc/manual.md260
-rw-r--r--modules/lua/lua.luadoc36
-rw-r--r--modules/textadept/find.lua206
-rw-r--r--modules/textadept/keys.lua2
-rw-r--r--src/Makefile72
-rw-r--r--src/lutf8libext.patch178
-rw-r--r--src/scintilla.patch161
-rw-r--r--src/textadept.c26
-rw-r--r--src/tre.patch49
19 files changed, 599 insertions, 433 deletions
diff --git a/TECHNOLOGY.md b/TECHNOLOGY.md
index 569d047d..06044914 100644
--- a/TECHNOLOGY.md
+++ b/TECHNOLOGY.md
@@ -31,6 +31,10 @@ Textadept's core text editing component is Scintilla.
=[Scintilla][] - Scintilla=
Textadept uses Scintilla as its core text editing component.
+=[TRE][] - Regular Expression Library=
+ Textadept replaces Scintilla's bare-bones Regex capabilities with TRE.
+ Approximate matching is not turned on.
+
=[Scinterm][]\* - Scintilla for curses=
Textadept uses Scinterm as its core text editing component for the terminal
version.
@@ -56,10 +60,6 @@ Lua and includes a few external libraries.
=[LuaFileSystem][] - Library for accessing directories and file attributes=
Textadept uses LFS for accessing the host filesystem.
-=[luautf8][] - Library for additional UTF-8 string utilities=
- Textadept uses a subset of luautf8 for Lua pattern matching with UTF-8
- patterns.
-
=[lspawn][]\* - Lua module for spawning processes=
Textadept uses lspawn for spawning asynchronous processes.
@@ -67,13 +67,13 @@ Lua and includes a few external libraries.
[GTK+]: http://www.gtk.org
[Scintilla]: http://scintilla.org
+[TRE]: https://github.com/laurikari/tre
[Lua]: http://www.lua.org
[LuaJIT]: http://luajit.org
[Scintillua]: http://foicica.com/scintillua
[Scinterm]: http://foicica.com/scinterm
[LPeg]: http://www.inf.puc-rio.br/~roberto/lpeg/lpeg.html
[LuaFileSystem]: http://keplerproject.github.io/luafilesystem/
-[luautf8]: https://github.com/starwing/luautf8/
[lspawn]: http://foicica.com/hg/lspawn
[gtDialog]: http://foicica.com/gtdialog/
[ncurses]: http://invisible-island.net/ncurses/
diff --git a/core/locale.conf b/core/locale.conf
index 8f1d8cf2..2101982f 100644
--- a/core/locale.conf
+++ b/core/locale.conf
@@ -75,7 +75,7 @@ _Replace = _Replace
Replace _All = Replace _All
_Match case = _Match case
_Whole word = _Whole word
-_Lua pattern = _Lua pattern
+Rege_x = Rege_x
_In files = _In files
Find in Files = Find in Files
Find: = Find:
@@ -97,7 +97,7 @@ Replace: = Replace:
[All] = [All]
Case(F1) = Case(F1)
Word(F2) = Word(F2)
-Pattern(F3) = Pattern(F3)
+Regex(F3) = Regex(F3)
Files(F4) = Files(F4)
# modules/textadept/keys.lua
diff --git a/core/locales/locale.ar.conf b/core/locales/locale.ar.conf
index 5d241fdd..93556bd5 100644
--- a/core/locales/locale.ar.conf
+++ b/core/locales/locale.ar.conf
@@ -76,7 +76,7 @@ _Replace = اس_تبدل
Replace _All = استبدال ال_كل
_Match case = _طابق الحالة
_Whole word = _كل الكلمة
-_Lua pattern = _قالب لُوَ
+Rege_x = Rege_x
_In files = _في الملفات
Find in Files = ابحث في الملفات
Find: = ا_بحث
@@ -98,7 +98,7 @@ Replace: = استبدل:
[All] = [الكل]
Case(F1) = حالة(F1)
Word(F2) = كلمة(F2)
-Pattern(F3) = قالب(F3)
+Regex(F3) = Regex(F3)
Files(F4) = ملفات(F4)
# modules/textadept/keys.lua
diff --git a/core/locales/locale.de.conf b/core/locales/locale.de.conf
index fb9d5818..f71de9d8 100644
--- a/core/locales/locale.de.conf
+++ b/core/locales/locale.de.conf
@@ -76,7 +76,7 @@ _Replace = Ersetzen
Replace _All = Alle ersetzen
_Match case = Groß-/Kleinschreibung berücksichtigen
_Whole word = Vollständiges Wort
-_Lua pattern = _Lua-Pattern
+Rege_x = Rege_x
_In files = _In Dateien
Find in Files = In Dateien suchen
Find: = Suchen:
diff --git a/core/locales/locale.es.conf b/core/locales/locale.es.conf
index 9ca09494..67628165 100644
--- a/core/locales/locale.es.conf
+++ b/core/locales/locale.es.conf
@@ -76,7 +76,7 @@ _Replace = _Reemplazar
Replace _All = Reemplazar _todo
_Match case = Concid. _mayús/minús
_Whole word = Palabra _completa
-_Lua pattern = Patrón de _Lua
+Rege_x = Rege_x
_In files = En ficher_os
Find in Files = Buscar en ficheros
Find: = Buscar:
@@ -98,7 +98,7 @@ Replace: = Reemplazar:
[All] = [Todo]
Case(F1) = Concid.(F1)
Word(F2) = Palabra(F2)
-Pattern(F3) = Patrón(F3)
+Regex(F3) = Regex(F3)
Files(F4) = Ficheros(F4)
# modules/textadept/keys.lua
diff --git a/core/locales/locale.fr.conf b/core/locales/locale.fr.conf
index 4a054071..642dc59a 100644
--- a/core/locales/locale.fr.conf
+++ b/core/locales/locale.fr.conf
@@ -77,7 +77,7 @@ _Replace = Remp_lacer
Replace _All = _Tout remplacer
_Match case = Respecter la _casse
_Whole word = _Mot entier
-_Lua pattern = Motif L_ua
+Rege_x = Rege_x
_In files = Dans les f_ichiers
Find in Files = Rechercher dans les fichiers
Find: = Rechercher:
@@ -99,7 +99,7 @@ Replace: = Remplacer:
[All] = [Tout]
Case(F1) = Casse(F1)
Word(F2) = Mot entier(F2)
-Pattern(F3) = Motif(F3)
+Regex(F3) = Regex(F3)
Files(F4) = Fichiers(F4)
# modules/textadept/keys.lua
diff --git a/core/locales/locale.it.conf b/core/locales/locale.it.conf
index 4bd3996f..c5c29481 100644
--- a/core/locales/locale.it.conf
+++ b/core/locales/locale.it.conf
@@ -76,7 +76,7 @@ _Replace = S_ostituisci
Replace _All = Sostituisci t_utti
_Match case = Distin_gui min./maiusc.
_Whole word = Paro_la intera
-_Lua pattern = Mo_dello Lua
+Rege_x = Rege_x
_In files = I_n più file
Find in Files = Trova nei file
Find: = Cerca:
@@ -98,7 +98,7 @@ Replace: = Sostituisci:
[All] = [Tutto]
Case(F1) = Min./maiusc.(F1)
Word(F2) = Parola intera(F2)
-Pattern(F3) = Modello(F3)
+Regex(F3) = Regex(F3)
Files(F4) = File(F4)
# modules/textadept/keys.lua
diff --git a/core/locales/locale.pl.conf b/core/locales/locale.pl.conf
index 6864a64e..df052245 100644
--- a/core/locales/locale.pl.conf
+++ b/core/locales/locale.pl.conf
@@ -76,7 +76,7 @@ _Replace = Za_mień
Replace _All = Zamień _wszystkie
_Match case = _Dopasuj wielkość liter
_Whole word = _Cały wyraz
-_Lua pattern = Wzorzec _Lua
+Rege_x = Rege_x
_In files = _W plikach
Find in Files = Znajdź w plikach
Find: = Znajdź:
@@ -98,7 +98,7 @@ Replace: = Zamień:
[All] = [Wszystkie]
Case(F1) = Wielkość (F1)
Word(F2) = Wyraz (F2)
-Pattern(F3) = Wzorzec (F3)
+Regex(F3) = Regex(F3)
Files(F4) = Pliki (F4)
# modules/textadept/keys.lua
diff --git a/core/locales/locale.ru.conf b/core/locales/locale.ru.conf
index 44d207ae..081dec03 100644
--- a/core/locales/locale.ru.conf
+++ b/core/locales/locale.ru.conf
@@ -76,7 +76,7 @@ _Replace = _Заменить
Replace _All = Заменить _всё
_Match case = _Учитывать регистр
_Whole word = _Слово целиком
-_Lua pattern = _Шаблон lua
+Rege_x = Rege_x
_In files = _В файлах
Find in Files = Найти в файлах
Find: = Найти:
diff --git a/core/locales/locale.sv.conf b/core/locales/locale.sv.conf
index ffbca884..c67ef832 100644
--- a/core/locales/locale.sv.conf
+++ b/core/locales/locale.sv.conf
@@ -76,7 +76,7 @@ _Replace = E_rsätt
Replace _All = Ers_ätt alla
_Match case = _Matcha gemener/versaler
_Whole word = Helt _ord
-_Lua pattern = _Lua pattern
+Rege_x = Rege_x
_In files = _I filer
Find in Files = Sök i filer
Find: = Sök:
@@ -98,7 +98,7 @@ Replace: = Replace:
[All] = [All]
Case(F1) = Case(F1)
Word(F2) = Word(F2)
-Pattern(F3) = Pattern(F3)
+Regex(F3) = Regex(F3)
Files(F4) = Files(F4)
# modules/textadept/keys.lua
diff --git a/doc/manual.md b/doc/manual.md
index 992c1bd2..6d0ea5ba 100644
--- a/doc/manual.md
+++ b/doc/manual.md
@@ -333,9 +333,8 @@ splitting. Lua also has complete control over all views.
## Find & Replace Pane
This compact pane is a great way to slice and dice through your document or a
-directory of files. It even supports finding and replacing text using Lua
-patterns and Lua code. The pane is available only when you need it and quickly
-gets out of your way when you do not, minimizing distractions.
+directory of files. The pane is available only when you need it and quickly gets
+out of your way when you do not, minimizing distractions.
## Command Entry
@@ -705,13 +704,8 @@ renders the entire line eligible for moving.
## Find & Replace
`Ctrl+F` (`⌘F` on Mac OSX | `M-F` or `M-S-F` in curses) brings up the Find &
-Replace pane. In addition to offering the usual find and replace with "Match
-Case" and "Whole Word" options and find/replace history, Textadept supports
-finding with [Lua patterns](#Lua.Patterns) and replacing with Lua captures and
-even Lua code! For example: replacing all `%w+` with `%(string.upper('%0'))`
-upper-cases all words in the buffer. Replacement text only recognizes Lua
-captures (`%`_`n`_) from a Lua pattern search, but always allows embedded Lua
-code enclosed in `%()`.
+Replace pane. It has the usual find and replace with "Match Case", "Whole Word",
+and "[Regex](#Regular.Expressions)" options, along with find/replace history
Note the `Ctrl+G`, `Ctrl+Shift+G`, `Ctrl+Alt+R`, `Ctrl+Alt+Shift+R` key bindings
for find next, find previous, replace, and replace all (`⌘G`, `⌘⇧G`, `^R`, and
@@ -741,7 +735,7 @@ Double-clicking a search result jumps to it in the file, as do the the
`Ctrl+Alt+G` and `Ctrl+Alt+Shift+G` (`^⌘G` and `^⌘⇧G` | none) key bindings for
cycling through results. Textadept does not support replacing in files directly.
You must "Find in Files" first, and then "Replace All" for each file containing
-a result. The "Match Case", "Whole Word", and "Lua pattern" flags still apply.
+a result. The "Match Case", "Whole Word", and "Regex" flags still apply.
_Warning_: currently, the [find API][] provides the only means to specify a
file-type filter. While the default filter excludes many common binary files
@@ -1468,26 +1462,27 @@ Not only can you define your own key bindings that can do pretty much anything
with Textadept (interact with and manipulate buffer contents, prompt for input
with dialogs, spawn processes, etc.), but you can also listen in on the plethora
of [events][] Textadept emits in order to script nearly every aspect of the
-editor's behavior. Not a fan of Lua patterns in buffer searches? Textadept emits
-an [`events.FIND`][] event every time the "Find Next" and "Find Prev" buttons
-are clicked. You can listen for that event and perform your own searches with
-regex, PEGs, or any other search format you can think of. Would you rather have
-the "Search -> Find" menu option (or key binding) start a search with the word
-under the caret already in the find & replace pane's search box? Create a Lua
-function that populates [`ui.find.find_entry_text`][] and [shows the pane][],
-and then re-assign the "Search -> Find" [menu action][]'s existing function to
-the one you just created. Would you like to have Textadept auto-save files as
-you switch between buffers? Connect the [`io.save_file()`][] function to the
-[`events.BUFFER_BEFORE_SWITCH`][] event. "Textadept gives you complete control
-over the entire application using Lua" is not an exaggeration!
+editor's behavior. Would you rather have the "Search -> Find" menu option (or
+key binding) start a search with the word under the caret already in the find &
+replace pane's search box? Create a Lua function that populates
+[`ui.find.find_entry_text`][] and [shows the pane][], and then re-assign the
+"Search -> Find" [menu action][]'s existing function to the one you just
+created. Would you like to have Textadept auto-save files as you switch between
+buffers? Connect the [`io.save_file()`][] function to the
+[`events.BUFFER_BEFORE_SWITCH`][] event. Would you like the ability to execute
+arbitrary code in order to transform replacement text while performing find &
+replace? Textadept emits an [`events.REPLACE`][] event every time the "Replace"
+button is clicked. You can listen for that event and perform your own
+replacements. "Textadept gives you complete control over the entire application
+using Lua" is not an exaggeration!
[`io.open_file()`]: api.html#io.open_file
[`events.FILE_OPENED`]: api.html#events.FILE_OPENED
[event]: api.html#events
-[`events.FIND`]: api.html#events.FIND
[`ui.find.find_entry_text`]: api.html#ui.find.find_entry_text
[`io.save_file()`]: api.html#io.save_file
[`events.BUFFER_BEFORE_SWITCH`]: api.html#events.BUFFER_BEFORE_SWITCH
+[`events.REPLACE`]: api.html#events.REPLACE
[shows the pane]: api.html#ui.find.focus
[menu action]: api.html#textadept.menu.menubar
@@ -1819,6 +1814,213 @@ Textadept has a [mailing list][] and a [wiki][].
- - -
+## Regular Expressions
+
+Textadept uses [TRE][] as its regular expression library. TRE is a "lightweight,
+robust, and efficient POSIX compliant regexp matching library".
+
+The following is from the [TRE Regexp Syntax][].
+
+This section describes the POSIX 1003.2 extended RE (ERE) syntax as implemented
+by TRE, and the TRE extensions to the ERE syntax. A simple Extended Backus-Naur
+Form (EBNF) style notation is used to describe the grammar.
+
+**Alternation operator**
+
+ extended-regexp ::= branch
+ | extended-regexp "|" branch
+
+An extended regexp (ERE) is one or more branches, separated by `|`. An ERE
+matches anything that matches one or more of the branches.
+
+**Catenation of REs**
+
+ branch ::= piece
+ | branch piece
+
+A branch is one or more pieces concatenated. It matches a match for the first
+piece, followed by a match for the second piece, and so on.
+
+ piece ::= atom
+ | atom repeat-operator
+ | atom approx-settings
+
+A piece is an atom possibly followed by a repeat operator or an expression
+controlling approximate matching parameters for the atom.
+
+ atom ::= "(" extended-regexp ")"
+ | bracket-expression
+ | "."
+ | assertion
+ | literal
+ | back-reference
+ | "(?#" comment-text ")"
+ | "(?" options ")" extended-regexp
+ | "(?" options ":" extended-regexp ")"
+
+An atom is either an ERE enclosed in parenthesis, a bracket expression, a `.`
+(period), an assertion, or a literal.
+
+The dot (`.`) matches any single character.
+
+Comment-text can contain any characters except for a closing parenthesis `)`.
+The text in the comment is completely ignored by the regex parser and it used
+solely for readability purposes.
+
+**Repeat operators**
+
+ repeat-operator ::= "*"
+ | "+"
+ | "?"
+ | bound
+ | "*?"
+ | "+?"
+ | "??"
+ | bound ?
+
+An atom followed by `*` matches a sequence of 0 or more matches of the atom. `+`
+is similar to `*`, matching a sequence of 1 or more matches of the atom. An atom
+followed by `?` matches a sequence of 0 or 1 matches of the atom.
+
+A bound is one of the following, where *m* and *n* are unsigned decimal integers
+between 0 and `RE_DUP_MAX`:
+
+1. {*m*,*n*}
+2. {*m*,}
+3. {*m*}
+
+An atom followed by [1] matches a sequence of *m* through *n* (inclusive)
+matches of the atom. An atom followed by [2] matches a sequence of *m* or more
+matches of the atom. An atom followed by [3] matches a sequence of exactly *m*
+matches of the atom.
+
+Adding a `?` to a repeat operator makes the subexpression minimal, or
+non-greedy. Normally a repeated expression is greedy, that is, it matches as
+many characters as possible. A non-greedy subexpression matches as few
+characters as possible. Note that this does not (always) mean the same thing as
+matching as many or few repetitions as possible.
+
+**Bracket expressions**
+
+ bracket-expression ::= "[" item+ "]"
+ | "[^" item+ "]"
+
+A bracket expression specifies a set of characters by enclosing a nonempty list
+of items in brackets. Normally anything matching any item in the list is
+matched. If the list begins with `^` the meaning is negated; any character
+matching no item in the list is matched.
+
+An item is any of the following:
+
+* A single character, matching that character.
+* Two characters separated by `-`. This is shorthand for the full range of
+ characters between those two (inclusive) in the collating sequence. For
+ example, `[0-9]` in ASCII matches any decimal digit.
+* A collating element enclosed in `[.` and `.]`, matching the collating element.
+ This can be used to include a literal `-` or a multi-character collating
+ element in the list.
+* A collating element enclosed in `[=` and `=]` (an equivalence class), matching
+ all collating elements with the same primary collation weight as that element,
+ including the element itself.
+* The name of a character class enclosed in `[:` and `:]`, matching any
+ character belonging to the class. The set of valid names depends on the
+ `LC_CTYPE` category of the current locale, but the following names are valid
+ in all locales:
+ + `alnum` -- alphanumeric characters
+ + `alpha` -- alphabetic characters
+ + `blank` -- blank characters
+ + `cntrl` -- control characters
+ + `digit` -- decimal digits (0 through 9)
+ + `graph` -- all printable characters except space
+ + `lower` -- lower-case letters
+ + `print` -- printable characters including space
+ + `punct` -- printable characters not space or alphanumeric
+ + `space` -- white-space characters
+ + `upper` -- upper case letters
+ + `xdigit` -- hexadecimal digits
+
+To include a literal `-` in the list, make it either the first or last item, the
+second endpoint of a range, or enclose it in `[.` and `.]` to make it a
+collating element. To include a literal `]` in the list, make it either the
+first item, the second endpoint of a range, or enclose it in `[.` and `.]`. To
+use a literal `-` as the first endpoint of a range, enclose it in `[.` and `.].`
+
+**Assertions**
+
+ assertion ::= "^"
+ | "$"
+ | "\" assertion-character
+
+The expressions `^` and `$` are called "left anchor" and "right anchor",
+respectively. The left anchor matches the empty string at the beginning of the
+string. The right anchor matches the empty string at the end of the string.
+
+An assertion-character can be any of the following:
+
+* `<` -- Beginning of word
+* `>` -- End of word
+* `b` -- Word boundary
+* `B` -- Non-word boundary
+* `d` -- Digit character (equivalent to `[[:digit:]]`)
+* `D` -- Non-digit character (equivalent to `[^[:digit:]]`)
+* `s` -- Space character (equivalent to `[[:space:]]`)
+* `S` -- Non-space character (equivalent to `[^[:space:]]`)
+* `w` -- Word character (equivalent to `[[:alnum:]_]`)
+* `W` -- Non-word character (equivalent to `[^[:alnum:]_]`)
+
+**Literals**
+
+ literal ::= ordinary-character
+ | "\x" ["1"-"9" "a"-"f" "A"-"F"]{0,2}
+ | "\x{" ["1"-"9" "a"-"f" "A"-"F"]* "}"
+ | "\" character
+
+A literal is either an ordinary character (a character that has no other
+significance in the context), an 8 bit hexadecimal encoded character (e.g.
+`\x1B`), a wide hexadecimal encoded character (e.g. `\x{263a}`), or an escaped
+character. An escaped character is a `\` followed by any character, and matches
+that character. Escaping can be used to match characters which have a special
+meaning in regexp syntax. A `\` cannot be the last character of an ERE. Escaping
+also allows you to include a few non-printable characters in the regular
+expression. These special escape sequences include:
+
+* `\a` -- Bell character (ASCII code 7)
+* `\e` -- Escape character (ASCII code 27)
+* `\f` -- Form-feed character (ASCII code 12)
+* `\n` -- New-line/line-feed character (ASCII code 10)
+* `\r` -- Carriage return character (ASCII code 13)
+* `\t` -- Horizontal tab character (ASCII code 9)
+
+An ordinary character is just a single character with no other significance, and
+matches that character. A `{` followed by something else than a digit is
+considered an ordinary character.
+
+**Back references**
+
+ back-reference ::= "\" ["1"-"9"]
+
+A back reference is a backslash followed by a single non-zero decimal digit *d*.
+It matches the same sequence of characters matched by the *d*th parenthesized
+subexpression.
+
+**Options**
+
+ options ::= ["i" "n" "r" "U"]* ("-" ["i" "n" "r" "U"]*)?
+
+Options allow compile time options to be turned on/off for particular parts of
+the regular expression. If the option is specified in the first section, it is
+turned on. If it is specified in the second section (after the `-`), it is
+turned off.
+
+* `i` -- Case insensitive.
+* `n` -- Forces special handling of the new line character.
+* `r` -- Causes the regex to be matched in a right associative manner rather than
+ the normal left associative manner.
+* `U` -- Forces repetition operators to be non-greedy unless a `?` is appended.
+
+[TRE]: https://github.com/laurikari/tre
+[TRE Regexp Syntax]: http://laurikari.net/tre/documentation/regex-syntax/
+
## Lua Patterns
The following is from the [Lua 5.3 Reference Manual][].
@@ -1993,6 +2195,8 @@ goto\_view(n, relative) |Changed |[goto\_view][](view)
**ui.find** | |
FILTER |Renamed |[find\_in\_files\_filter][]
find\_in\_files(dir) |Changed |[find\_in\_files][](dir, filter)
+lua |Changed |[regex][]
+lua\_pattern\_label\_text |Changed |[regex\_label\_text][]
**view** | |
goto\_buffer(n, relative) |Changed |[goto\_buffer][](buffer)
**textadept.editing** | |
@@ -2035,6 +2239,8 @@ MAX\_RECENT\_FILES |Renamed |[max\_recent\_files][]
[goto\_view]: api.html#ui.goto_view
[find\_in\_files\_filter]: api.html#ui.find.find_in_files_filter
[find\_in\_files]: api.html#ui.find.find_in_files
+[regex]: api.html#ui.find.regex
+[regex\_label\_text]: api.html#ui.find.regex_label_text
[goto\_buffer]: api.html#view.goto_buffer
[auto\_pairs]: api.html#textadept.editing.auto_pairs
[typeover\_chars]: api.html#textadept.editing.typeover_chars
@@ -2089,6 +2295,12 @@ use of, you can put the following in your *~/.textadept/init.lua*:
end
end)
+#### Find & Replace Changes
+
+Find & replace with Lua patterns has been superceded by find & replace with
+Regular Expressions (regex). The [Regular Expressions](#Regular.Expressions)
+section above covers the regex syntax that Textadept supports.
+
### Textadept 7 to 8
Textadept 8 upgraded its internal copy of Lua from [5.2 to 5.3][]. Nearly all
diff --git a/modules/lua/lua.luadoc b/modules/lua/lua.luadoc
index 4cbe77ea..54316c96 100644
--- a/modules/lua/lua.luadoc
+++ b/modules/lua/lua.luadoc
@@ -2049,39 +2049,3 @@ function lfs.touch(filepath [, atime [, mtime]]) end
-- Returns true if the operation was successful; in case of error, it returns
-- nil plus an error string.
function lfs.unlock(filehandle[, start[, length]]) end
-
----
--- UTF-8 version of `string.byte`.
-function utf8.byte(s [, i [, j]]) end
-
----
--- UTF-8 version of `string.find`.
-function utf8.find(s, pattern [, init [, plain]]) end
-
----
--- UTF-8 version of `string.gmatch`.
-function utf8.gmatch(s, pattern) end
-
----
--- UTF-8 version of `string.gsub`.
-function utf8.gsub(s, pattern, repl [, n]) end
-
----
--- UTF-8 version of `string.lower`.
-function utf8.lower(s) end
-
----
--- UTF-8 version of `string.match`.
-function utf8.match(s, pattern [, init]) end
-
----
--- UTF-8 version of `string.reverse`.
-function utf8.reverse(s) end
-
----
--- UTF-8 version of `string.sub`.
-function utf8.sub(s, i [, j]) end
-
----
--- UTF-8 version of `string.upper`.
-function utf8.upper(s) end
diff --git a/modules/textadept/find.lua b/modules/textadept/find.lua
index 30f9d0e3..6bb35d39 100644
--- a/modules/textadept/find.lua
+++ b/modules/textadept/find.lua
@@ -16,8 +16,8 @@ local M = ui.find
-- Match search text only when it is surrounded by non-word characters in
-- searches.
-- The default value is `false`.
--- @field lua (bool)
--- Interpret search text as a Lua pattern.
+-- @field regex (bool)
+-- Interpret search text as a Regular Expression.
-- The default value is `false`.
-- @field in_files (bool)
-- Find search text in a list of files.
@@ -46,8 +46,8 @@ local M = ui.find
-- @field whole_word_label_text (string, Write-only)
-- The text of the "Whole word" label.
-- This is primarily used for localization.
--- @field lua_pattern_label_text (string, Write-only)
--- The text of the "Lua pattern" label.
+-- @field regex_label_text (string, Write-only)
+-- The text of the "Regex" label.
-- This is primarily used for localization.
-- @field in_files_label_text (string, Write-only)
-- The text of the "In files" label.
@@ -71,8 +71,7 @@ M.replace_button_text = not CURSES and _L['_Replace'] or _L['[Replace]']
M.replace_all_button_text = not CURSES and _L['Replace _All'] or _L['[All]']
M.match_case_label_text = not CURSES and _L['_Match case'] or _L['Case(F1)']
M.whole_word_label_text = not CURSES and _L['_Whole word'] or _L['Word(F2)']
-M.lua_pattern_label_text = not CURSES and _L['_Lua pattern'] or
- _L['Pattern(F3)']
+M.regex_label_text = not CURSES and _L['Rege_x'] or _L['Regex(F3)']
M.in_files_label_text = not CURSES and _L['_In files'] or _L['Files(F4)']
M.INDIC_FIND = _SCINTILLA.next_indic_number()
@@ -135,10 +134,10 @@ local function find(text, next, flags, no_wrap, wrapped)
flags = 0
if M.match_case then flags = flags + buffer.FIND_MATCHCASE end
if M.whole_word then flags = flags + buffer.FIND_WHOLEWORD end
- if M.lua then flags = flags + 8 end
- if M.in_files then flags = flags + 16 end
+ if M.regex then flags = flags + buffer.FIND_REGEXP end
+ if M.in_files then flags = flags + 0x1000000 end -- next after 0x800000
end
- if flags >= 16 then M.find_in_files() return end -- not performed here
+ if flags >= 0x1000000 then M.find_in_files() return end -- not performed here
local first_visible_line = buffer.first_visible_line -- for 'no results found'
-- If text is selected, assume it is from the current search and increment the
@@ -148,36 +147,10 @@ local function find(text, next, flags, no_wrap, wrapped)
buffer:goto_pos(buffer:position_relative(pos, next and 1 or -1))
end
- local pos = -1
- if flags < 8 then
- -- Scintilla search.
- buffer:search_anchor()
- pos = buffer['search_'..(next and 'next' or 'prev')](buffer, flags, text)
- M.captures = nil -- clear captures from any previous Lua pattern searches
- elseif flags < 16 then
- -- Lua pattern search.
- -- Note: I do not trust utf8.find completely, so only use it if there are
- -- UTF-8 characters in the pattern. Otherwise default to string.find.
- local lib_find = not text:find('[\xC2-\xF4]') and string.find or utf8.find
- local s = next and buffer.current_pos or 0
- local e = next and buffer.length or buffer.current_pos
- local patt = text:gsub('\\[abfnrtv\\]', escapes)
- if not next then patt = '^.*()'..patt end
- local caps = {lib_find(buffer:text_range(s, e), patt)}
- M.captures = {table.unpack(caps, next and 3 or 4)}
- if #caps > 0 and caps[2] >= caps[1] then
- if lib_find == string.find then
- -- Positions are bytes.
- pos, e = s + caps[next and 1 or 3] - 1, s + caps[2]
- else
- -- Positions are characters, which may be multiple bytes.
- pos = buffer:position_relative(s, caps[next and 1 or 3] - 1)
- e = buffer:position_relative(s, caps[2])
- end
- M.captures[0] = buffer:text_range(pos, e)
- buffer:set_sel(e, pos)
- end
- end
+ -- Scintilla search.
+ buffer:search_anchor()
+ local f = buffer['search_'..(next and 'next' or 'prev')]
+ local pos = f(buffer, flags, text)
buffer:scroll_range(buffer.anchor, buffer.current_pos)
-- If nothing was found, wrap the search.
@@ -243,9 +216,9 @@ end
---
-- Searches directory *dir* or the user-specified directory for files that match
-- search text and search options (subject to optional filter *filter*), and
--- prints the results to a buffer titled "Files Found".
--- Use the `find_text`, `match_case`, `whole_word`, and `lua` fields to set the
--- search text and option flags, respectively.
+-- prints the results to a buffer titled "Files Found", highlighting found text.
+-- Use the `find_text`, `match_case`, `whole_word`, and `regex` fields to set
+-- the search text and option flags, respectively.
-- @param dir Optional directory path to search. If `nil`, the user is prompted
-- for one.
-- @param filter Optional filter for files and directories to exclude. The
@@ -261,60 +234,53 @@ function M.find_in_files(dir, filter)
}
if not dir then return end
- local text = M.find_entry_text
- if not M.lua then text = text:gsub('([().*+?^$%%[%]-])', '%%%1') end
- if not M.match_case then text = text:lower() end
- if M.whole_word then text = '%f[%w_]'..text..'%f[^%w_]' end -- TODO: wordchars
-
if buffer._type ~= _L['[Files Found Buffer]'] then preferred_view = view end
ui.silent_print = false
- ui._print(_L['[Files Found Buffer]'], _L['Find:']..' '..text)
+ ui._print(_L['[Files Found Buffer]'], _L['Find:']..' '..M.find_entry_text)
buffer.indicator_current = M.INDIC_FIND
+ local ff_buffer = buffer
- -- Note: I do not trust utf8.find completely, so only use it if there are
- -- UTF-8 characters in the pattern. Otherwise default to string.find.
- local lib_find = not text:find('[\xC2-\xF4]') and string.find or utf8.find
+ local buffer = buffer.new() -- temporary buffer
+ local flags = 0
+ if M.match_case then flags = flags + buffer.FIND_MATCHCASE end
+ if M.whole_word then flags = flags + buffer.FIND_WHOLEWORD end
+ if M.regex then flags = flags + buffer.FIND_REGEXP end
+ buffer.search_flags = flags
+ local text = M.find_entry_text
local found = false
lfs.dir_foreach(dir, function(filename)
- local match_case = M.match_case
+ buffer:clear_all()
+ buffer:empty_undo_buffer()
local f = io.open(filename, 'rb')
- local binary, line_num = nil, 1
- for line in f:lines() do
- local s, e = lib_find(match_case and line or line:lower(), text)
- if s and e then
- found = true
- if binary == nil then
- local pos = f:seek()
- f:seek('set') -- rewind
- binary = f:read(65536):find('\0')
- f:seek('set', pos) -- restore
- end
- local utf8_filename = filename:iconv('UTF-8', _CHARSET)
- if not binary then
- buffer:append_text(string.format('%s:%d:%s\n', utf8_filename,
- line_num, line))
- local pos = buffer:position_from_line(buffer.line_count - 2) +
- #utf8_filename + #tostring(line_num) + 2
- if lib_find == string.find then
- -- Positions are bytes.
- buffer:indicator_fill_range(pos + s - 1, e - s + 1)
- else
- -- Positions are characters, which may be multiple bytes.
- s = buffer:position_relative(pos, s - 1)
- e = buffer:position_relative(pos, e)
- buffer:indicator_fill_range(s, e - s)
- end
- else
- buffer:append_text(string.format('%s:1:%s\n', utf8_filename,
- _L['Binary file matches.']))
- break
- end
+ while f:read(0) do buffer:append_text(f:read(1048576)) end
+ --buffer:set_text(f:read('*a'))
+ f:close()
+ local binary = nil -- determine lazily for performance reasons
+ buffer:target_whole_document()
+ while buffer:search_in_target(text) > -1 do
+ found = true
+ if binary == nil then binary = buffer:text_range(0, 65536):find('\0') end
+ local utf8_filename = filename:iconv('UTF-8', _CHARSET)
+ if not binary then
+ local line_num = buffer:line_from_position(buffer.target_start)
+ local line = buffer:get_line(line_num)
+ ff_buffer:append_text(string.format('%s:%d:%s', utf8_filename,
+ line_num + 1, line))
+ local pos = ff_buffer.length - #line +
+ buffer.target_start - buffer:position_from_line(line_num)
+ ff_buffer:indicator_fill_range(pos,
+ buffer.target_end - buffer.target_start)
+ if not line:find('\n$') then ff_buffer:append_text('\n') end
+ else
+ ff_buffer:append_text(string.format('%s:1:%s\n', utf8_filename,
+ _L['Binary file matches.']))
+ break
end
- line_num = line_num + 1
+ buffer:set_target_range(buffer.target_end, buffer.length)
end
- f:close()
end, filter or M.find_in_files_filter)
- if not found then buffer:append_text(_L['No results found']) end
+ if not found then ff_buffer:append_text(_L['No results found']) end
+ buffer:delete() -- delete temporary buffer
ui._print(_L['[Files Found Buffer]'], '') -- goto end, set save pos, etc.
end
@@ -322,39 +288,14 @@ end
-- `find()` is called first, to select any found text. The selected text is
-- then replaced by the specified replacement text.
-- This function ignores "Find in Files".
--- @param rtext The text to replace found text with. It can contain both Lua
--- capture items (`%n` where 1 <= `n` <= 9) for Lua pattern searches and `%()`
--- sequences for embedding Lua code for any search.
+-- @param rtext The text to replace found text with. It can contain regex
+-- capture groups (`\d` where 0 <= `d` <= 9).
-- @see find
local function replace(rtext)
if buffer.selection_empty then return end
if M.in_files then M.in_files = false end
buffer:target_from_selection()
- rtext = rtext:gsub('\\[abfnrtv\\]', escapes):gsub('%%%%', '\\037')
- if M.captures then
- for i = 0, #M.captures do
- rtext = rtext:gsub('%%'..i, (M.captures[i]:gsub('%%', '%%%%')))
- end
- end
- local ok
- ok, rtext = pcall(string.gsub, rtext, '%%(%b())', function(code)
- code = code:gsub('[\a\b\f\n\r\t\v\\]', escapes)
- local result = assert(load('return '..code))()
- return tostring(result):gsub('\\[abfnrtv\\]', escapes)
- end)
- if ok then
- buffer:replace_target(rtext:gsub('\\037', '%%'))
- buffer:goto_pos(buffer.target_end) -- 'find' text after this replacement
- else
- ui.dialogs.msgbox{
- title = _L['Error'], text = _L['An error occured:'],
- informative_text = rtext:match(':1:(.+)$') or rtext:match(':%d+:(.+)$'),
- icon = 'gtk-dialog-error'
- }
- -- Since find is called after replace returns, have it 'find' the current
- -- text again, rather than the next occurance so the user can fix the error.
- buffer:goto_pos(buffer.current_pos)
- end
+ buffer[not M.regex and 'replace_target' or 'replace_target_re'](buffer, rtext)
end
events.connect(events.REPLACE, replace)
@@ -399,15 +340,15 @@ events.connect(events.REPLACE_ALL, replace_all)
-- Returns whether or not the given buffer is a files found buffer.
local function is_ff_buf(buf) return buf._type == _L['[Files Found Buffer]'] end
---
--- Jumps to the source of the find in files search result on line number *line*
--- in the buffer titled "Files Found" or, if *line* is `nil`, jumps to the next
--- or previous search result, depending on boolean *next*.
--- @param line The line number in the files found buffer that contains the
+-- Jumps to the source of the find in files search result on line number
+-- *line_num* in the buffer titled "Files Found" or, if *line_num* is `nil`,
+-- jumps to the next or previous search result, depending on boolean *next*.
+-- @param line_num The line number in the files found buffer that contains the
-- search result to go to.
-- @param next Optional flag indicating whether to go to the next search result
--- or the previous one. Only applicable when *line* is `nil` or `false`.
+-- or the previous one. Only applicable when *line_num* is `nil` or `false`.
-- @name goto_file_found
-function M.goto_file_found(line, next)
+function M.goto_file_found(line_num, next)
local ff_view, ff_buf = nil, nil
for i = 1, #_VIEWS do
if is_ff_buf(_VIEWS[i].buffer) then ff_view = _VIEWS[i] break end
@@ -419,27 +360,40 @@ function M.goto_file_found(line, next)
if ff_view then ui.goto_view(ff_view) else view:goto_buffer(ff_buf) end
-- If no line was given, find the next search result.
- if not line and next ~= nil then
+ if not line_num and next ~= nil then
if next then buffer:line_end() else buffer:home() end
buffer:search_anchor()
local f = buffer['search_'..(next and 'next' or 'prev')]
- local pos = f(buffer, buffer.FIND_REGEXP, '^.+:[0-9]+:.+$')
+ local pos = f(buffer, buffer.FIND_REGEXP, '^.+:\\d+:.+$')
if pos == -1 then
buffer:goto_line(next and 0 or buffer.line_count)
buffer:search_anchor()
- pos = f(buffer, buffer.FIND_REGEXP, '^.+:[0-9]+:.+$')
+ pos = f(buffer, buffer.FIND_REGEXP, '^.+:\\d+:.+$')
end
if pos == -1 then return end
- line = buffer:line_from_position(pos)
+ line_num = buffer:line_from_position(pos)
end
- buffer:goto_line(line)
+ buffer:goto_line(line_num)
-- Goto the source of the search result.
- local utf8_filename, line_num = buffer:get_cur_line():match('^(.+):(%d+):.+$')
+ local line = buffer:get_cur_line()
+ local utf8_filename, pos
+ utf8_filename, line_num, pos = line:match('^(.+):(%d+):().+$')
if not utf8_filename then return end
textadept.editing.select_line()
+ pos = buffer.anchor + pos - 1 -- absolute position of result text on line
+ local s = buffer:indicator_end(M.INDIC_FIND, pos)
+ local e = buffer:indicator_end(M.INDIC_FIND, s + 1)
+ if buffer:line_from_position(s) == buffer:line_from_position(pos) then
+ s, e = s - pos, e - pos -- relative to line start
+ else
+ s, e = 0, 0 -- binary file notice, or highlighting was somehow removed
+ end
ui.goto_file(utf8_filename:iconv(_CHARSET, 'UTF-8'), true, preferred_view)
textadept.editing.goto_line(line_num - 1)
+ if buffer:line_from_position(buffer.current_pos + s) == line_num - 1 then
+ buffer:set_sel(buffer.current_pos + e, buffer.current_pos + s)
+ end
end
events.connect(events.KEYPRESS, function(code)
if keys.KEYSYMS[code] == '\n' and is_ff_buf(buffer) then
diff --git a/modules/textadept/keys.lua b/modules/textadept/keys.lua
index 1a810969..96c51373 100644
--- a/modules/textadept/keys.lua
+++ b/modules/textadept/keys.lua
@@ -209,7 +209,7 @@ local M = {}
-- Up |⇡ |^N |Cycle forward through history
-- N/A |N/A |F1 |Toggle "Match Case"
-- N/A |N/A |F2 |Toggle "Whole Word"
--- N/A |N/A |F3 |Toggle "Lua Pattern"
+-- N/A |N/A |F3 |Toggle "Regex"
-- N/A |N/A |F4 |Toggle "Find in Files"
--
-- †: Some terminals interpret ^Z as suspend.
diff --git a/src/Makefile b/src/Makefile
index 315f696d..a3c15ec8 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -97,7 +97,8 @@ endif
# Scintilla.
sci_flags = -pedantic $(plat_flag) -DSCI_LEXER -DNDEBUG -DNO_CXX11_REGEX \
- -Iscintilla/include -Iscintilla/src -Iscintilla/lexlib -Wall
+ -DSCI_OWNREGEX -Iscintilla/include -Iscintilla/src \
+ -Iscintilla/lexlib -Itre/lib -Wall
sci_objs = AutoComplete.o CallTip.o CaseConvert.o CaseFolder.o Catalogue.o \
CellBuffer.o CharClassify.o ContractionState.o Decoration.o \
@@ -109,6 +110,9 @@ sci_lex_objs = Accessor.o CharacterSet.o LexerBase.o LexerModule.o \
LexerNoExceptions.o LexerSimple.o PropSetSimple.o \
StyleContext.o WordList.o
sci_gtk_objs = PlatGTK.o ScintillaGTK.o
+regex_objs = regcomp.o regerror.o regexec.o tre-ast.o tre-compile.o \
+ tre-filter.o tre-match-backtrack.o tre-match-parallel.o tre-mem.o \
+ tre-parse.o tre-stack.o xmalloc.o
lexlpeg_objs = LexLPeg.o LexLPegjit.o LexLPeg-curses.o LexLPegjit-curses.o
# Textadept.
@@ -124,9 +128,9 @@ lua_objs = lapi.o lcode.o lctype.o ldebug.o ldo.o ldump.o lfunc.o lgc.o \
lstring.o ltable.o ltm.o lundump.o lvm.o lzio.o \
lauxlib.o lbaselib.o lbitlib.o lcorolib.o ldblib.o liolib.o \
lmathlib.o loadlib.o loslib.o lstrlib.o ltablib.o lutf8lib.o
-lua_lib_objs = lpcap.o lpcode.o lpprint.o lptree.o lpvm.o lfs.o lutf8libext.o
+lua_lib_objs = lpcap.o lpcode.o lpprint.o lptree.o lpvm.o lfs.o
luajit_lib_objs = lpcapjit.o lpcodejit.o lpprintjit.o lptreejit.o lpvmjit.o \
- lfsjit.o lutf8libjit.o lutf8libextjit.o
+ lfsjit.o lutf8libjit.o
lua_spawn_objs = lspawn.o lspawnjit.o lspawn-curses.o lspawnjit-curses.o
gtdialog_objs = gtdialog.o gtdialog-curses.o
termkey_unix_objs = driver-ti.o driver-csi.o
@@ -160,6 +164,8 @@ $(sci_gtk_objs): %.o: scintilla/gtk/%.cxx
$(CROSS)$(CXX) -c $(CXXFLAGS) $(sci_flags) $(GTK_CFLAGS) $< -o $@
scintilla-marshal.o: scintilla/gtk/scintilla-marshal.c
$(CROSS)$(CC) -c $(CFLAGS) $(GTK_CFLAGS) $< -o $@
+$(regex_objs): %.o: tre/lib/%.c
+ $(CROSS)$(CC) -c $(CFLAGS) -Itre/lib $< -o $@
ScintillaTerm.o: scintilla/term/ScintillaTerm.cxx
$(CROSS)$(CXX) -c $(CXXFLAGS) $(sci_flags) $(CURSES_CFLAGS) $< -o $@
$(lexlpeg_objs): LexLPeg.cxx
@@ -217,43 +223,45 @@ lutf8libjit.o: LUA_CFLAGS += -Ilua/src
# Executables.
textadept: $(sci_objs) $(sci_lex_objs) $(sci_gtk_objs) scintilla-marshal.o \
- LexLPeg.o textadept.o $(lua_objs) $(lua_lib_objs) lspawn.o gtdialog.o
+ $(regex_objs) LexLPeg.o textadept.o $(lua_objs) $(lua_lib_objs) \
+ lspawn.o gtdialog.o
$(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(GTK_LIBS) $(LDFLAGS)
textadeptjit: $(sci_objs) $(sci_lex_objs) $(sci_gtk_objs) scintilla-marshal.o \
- LexLPegjit.o textadeptjit.o $(luajit_lib_objs) $(libluajit) \
- lspawnjit.o gtdialog.o
+ $(regex_objs) LexLPegjit.o textadeptjit.o $(luajit_lib_objs) \
+ $(libluajit) lspawnjit.o gtdialog.o
$(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(GTK_LIBS) $(LDFLAGS)
-textadept-curses: $(sci_objs) $(sci_lex_objs) ScintillaTerm.o LexLPeg-curses.o \
- textadept-curses.o $(lua_objs) $(lua_lib_objs) \
- lspawn-curses.o gtdialog-curses.o termkey.o \
+textadept-curses: $(sci_objs) $(sci_lex_objs) ScintillaTerm.o $(regex_objs) \
+ LexLPeg-curses.o textadept-curses.o $(lua_objs) \
+ $(lua_lib_objs) lspawn-curses.o gtdialog-curses.o termkey.o \
$(termkey_unix_objs) $(cdk_objs)
$(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(CURSES_LIBS) $(LDFLAGS)
-textadeptjit-curses: $(sci_objs) $(sci_lex_objs) ScintillaTerm.o \
+textadeptjit-curses: $(sci_objs) $(sci_lex_objs) ScintillaTerm.o $(regex_objs) \
LexLPegjit-curses.o textadeptjit-curses.o \
$(luajit_lib_objs) $(libluajit) lspawnjit-curses.o \
gtdialog-curses.o termkey.o $(termkey_unix_objs) \
$(cdk_objs)
$(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(CURSES_LIBS) $(LDFLAGS)
textadept.exe: $(sci_objs) $(sci_lex_objs) $(sci_gtk_objs) scintilla-marshal.o \
- LexLPeg.o textadept.o textadept_rc.o $(lua_objs) \
+ $(regex_objs) LexLPeg.o textadept.o textadept_rc.o $(lua_objs) \
$(lua_lib_objs) lspawn.o gtdialog.o
$(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(GTK_LIBS) $(LDFLAGS)
textadeptjit.exe: $(sci_objs) $(sci_lex_objs) $(sci_gtk_objs) \
- scintilla-marshal.o LexLPegjit.o textadeptjit.o \
- textadept_rc.o $(luajit_lib_objs) $(libluajit) lspawnjit.o \
- gtdialog.o
+ scintilla-marshal.o $(regex_objs) LexLPegjit.o \
+ textadeptjit.o textadept_rc.o $(luajit_lib_objs) \
+ $(libluajit) lspawnjit.o gtdialog.o
$(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(GTK_LIBS) $(LDFLAGS)
textadept-curses.exe: $(sci_objs) $(sci_lex_objs) ScintillaTerm.o \
- LexLPeg-curses.o textadept-curses.o textadept_rc.o \
- $(lua_objs) $(lua_lib_objs) lspawn-curses.o \
- gtdialog-curses.o termkey.o $(termkey_win32_objs) \
- $(cdk_objs)
+ $(regex_objs) LexLPeg-curses.o textadept-curses.o \
+ textadept_rc.o $(lua_objs) $(lua_lib_objs) \
+ lspawn-curses.o gtdialog-curses.o termkey.o \
+ $(termkey_win32_objs) $(cdk_objs)
$(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(CURSES_LIBS) $(LDFLAGS)
textadeptjit-curses.exe: $(sci_objs) $(sci_lex_objs) ScintillaTerm.o \
- LexLPegjit-curses.o textadeptjit-curses.o \
- textadept_rc.o $(luajit_lib_objs) $(libluajit) \
- lspawnjit-curses.o gtdialog-curses.o termkey.o \
- $(termkey_win32_objs) $(cdk_objs)
+ $(regex_objs) LexLPegjit-curses.o \
+ textadeptjit-curses.o textadept_rc.o \
+ $(luajit_lib_objs) $(libluajit) lspawnjit-curses.o \
+ gtdialog-curses.o termkey.o $(termkey_win32_objs) \
+ $(cdk_objs)
$(CROSS)$(CXX) $(CXXFLAGS) -o ../$@ $^ $(CURSES_LIBS) $(LDFLAGS)
# Install/uninstall.
@@ -361,8 +369,8 @@ osx-app: ../textadept ../textadeptjit ../textadept-curses \
# External dependencies.
-base_deps = scintilla scintilla/term scintillua lua lualibs luajit gtdialog \
- cdk ../doc/bombay
+base_deps = scintilla tre scintilla/term scintillua lua lualibs luajit \
+ gtdialog cdk ../doc/bombay
deps: $(base_deps) termkey
win32-deps: $(base_deps) win32gtk win32curses
osx-deps: $(base_deps) gtkosx termkey
@@ -380,12 +388,12 @@ else
endif
scintilla_tgz = scintilla366.tgz
+tre_zip = cdce45e8dd7a3b36954022b4a4d3570e1ac5a4f8.zip
scinterm_zip = scinterm_1.8.zip
scintillua_zip = scintillua_3.6.5-1.zip
lua_tgz = lua-5.3.3.tar.gz
lpeg_tgz = lpeg-1.0.0.tar.gz
lfs_zip = v_1_6_3.zip
-luautf8_zip = 0.1.1.zip
lspawn_zip = lspawn_1.5.zip
luajit_tgz = LuaJIT-2.0.3.tar.gz
libluajit_tgz = libluajit_2.0.3.x86_64.tgz
@@ -405,6 +413,10 @@ $(scintilla_tgz): ; wget http://prdownloads.sourceforge.net/scintilla/$@ -O $@
scintilla: scintilla.patch | $(scintilla_tgz)
mkdir $@ && tar xzf $| -C $@ && mv $@/*/* $@
patch -d $@ -N -p1 < $<
+$(tre_zip): ; wget https://github.com/laurikari/tre/archive/$@ -O $@
+tre: tre.patch | $(tre_zip)
+ mkdir $@ && unzip -d $@ $| && mv $@/*/* $@
+ patch -d $@ -N -p1 < $<
$(scinterm_zip): ; wget $(scinterm_url) -O $@
scintilla/term: | $(scinterm_zip) ; mkdir $@ && unzip -d $@ $| && mv $@/*/* $@
scintillua: ../lexers LexLPeg.cxx
@@ -415,24 +427,16 @@ LexLPeg.cxx: | ../lexers ; ln -s $|/$@ $@
$(lua_tgz): ; wget http://www.lua.org/ftp/$@
$(lpeg_tgz): ; wget http://www.inf.puc-rio.br/~roberto/lpeg/$@
$(lfs_zip): ; wget http://github.com/keplerproject/luafilesystem/archive/$@
-$(luautf8_zip): ; wget https://github.com/starwing/luautf8/archive/$@
$(lspawn_zip): ; wget $(lspawn_url) -O $@
lua: lua.patch | $(lua_tgz)
mkdir $@ && tar xzf $| -C $@ && mv $@/*/* $@
patch -d $@ -N -p1 < $<
-lualibs: lua/src/lib/lpeg lua/src/lib/lfs lua/src/lib/lutf8libext \
- lua/src/lib/lspawn
+lualibs: lua/src/lib/lpeg lua/src/lib/lfs lua/src/lib/lspawn
lua/src/lib/lpeg: | $(lpeg_tgz)
mkdir -p $@ && tar xzf $| -C $@ && mv $@/*/*.c $@/*/*.h $(dir $@)
lua/src/lib/lfs: lfs.patch | $(lfs_zip)
mkdir -p $@ && unzip -d $@ $| && mv $@/*/src/*.c $@/*/src/*.h $(dir $@)
patch -d $(dir $@) -N -p1 < $<
-lua/src/lib/lutf8libext: lutf8libext.patch | $(luautf8_zip)
- mkdir -p $@ && unzip -d $@ $| && mv $@/*/*.h $(dir $@)
- # Rename lutf8lib.c in order to prevent conflicts with Lua's lutf8lib.c,
- # needed by LuaJIT.
- mv $@/*/lutf8lib.c $(dir $@)lutf8libext.c
- patch -d $(dir $@) -N -p1 < $<
lua/src/lib/lspawn: | $(lspawn_zip)
mkdir -p $@ && unzip -d $@ $| && mv $@/*/*.c $(dir $@)
lua/src/lib/lutf8lib.c: lutf8libjit.patch
diff --git a/src/lutf8libext.patch b/src/lutf8libext.patch
deleted file mode 100644
index 5dd975ce..00000000
--- a/src/lutf8libext.patch
+++ /dev/null
@@ -1,178 +0,0 @@
---- lutf8libext.c 2016-03-26 09:39:32.469103890 -0400
-+++ lib/lutf8libext.c 2016-03-26 13:29:06.770738807 -0400
-@@ -199,8 +199,10 @@
- define_category(space)
- define_converter(tolower)
- define_converter(toupper)
-+#if 0
- define_converter(totitle)
- define_converter(tofold)
-+#endif
-
- #undef define_category
- #undef define_converter
-@@ -223,6 +225,7 @@
- return 0;
- }
-
-+#if 0
- static int utf8_width(unsigned ch, int ambi_is_single) {
- if (find_in_range(doublewidth_table, table_size(doublewidth_table), ch))
- return 2;
-@@ -234,6 +237,7 @@
- return 0;
- return 1;
- }
-+#endif
-
-
- /* string module compatible interface */
-@@ -258,11 +262,13 @@
- luaL_addlstring(b, buff, n);
- }
-
-+#if 0
- static lua_Integer byterelat(lua_Integer pos, size_t len) {
- if (pos >= 0) return pos;
- else if (0u - (size_t)pos > len) return 0;
- else return (lua_Integer)len + pos + 1;
- }
-+#endif
-
- static int u_posrange(const char **ps, const char **pe,
- lua_Integer posi, lua_Integer posj) {
-@@ -281,6 +287,7 @@
- return *ps < *pe;
- }
-
-+#if 0
- static int Lutf8_len(lua_State *L) {
- size_t len;
- const char *s = luaL_checklstring(L, 1, &len);
-@@ -292,6 +299,7 @@
- lua_pushinteger(L, (lua_Integer)utf8_length(s+posi, s+posj+1));
- return 1;
- }
-+#endif
-
- static int Lutf8_sub(lua_State *L) {
- const char *e, *s = check_utf8(L, 1, &e);
-@@ -344,11 +352,13 @@
- static int Lutf8_upper(lua_State *L)
- { return convert(L, utf8_toupper); }
-
-+#if 0
- static int Lutf8_title(lua_State *L)
- { return convert(L, utf8_totitle); }
-
- static int Lutf8_fold(lua_State *L)
- { return convert(L, utf8_tofold); }
-+#endif
-
- static int Lutf8_byte(lua_State *L) {
- size_t n = 0;
-@@ -367,6 +377,7 @@
- return n;
- }
-
-+#if 0
- static int Lutf8_codepoint(lua_State *L) {
- const char *e, *s = check_utf8(L, 1, &e);
- size_t len = e-s;
-@@ -643,6 +654,7 @@
- lua_pushinteger(L, 0);
- return 1;
- }
-+#endif
-
-
- /* utf8 pattern matching implement */
-@@ -1265,21 +1277,26 @@
-
- #define UTF8PATT "[\0-\x7F\xC2-\xF4][\x80-\xBF]*"
-
--LUALIB_API int luaopen_utf8(lua_State *L) {
-+LUALIB_API int luaopen_utf8_ext(lua_State *L) {
- luaL_Reg libs[] = {
- #define ENTRY(name) { #name, Lutf8_##name }
-+#if 0
- ENTRY(offset),
- ENTRY(codes),
- ENTRY(codepoint),
-
- ENTRY(len),
-+#endif
- ENTRY(sub),
- ENTRY(reverse),
- ENTRY(lower),
- ENTRY(upper),
-+#if 0
- ENTRY(title),
- ENTRY(fold),
-+#endif
- ENTRY(byte),
-+#if 0
- ENTRY(char),
- ENTRY(escape),
- ENTRY(insert),
-@@ -1289,6 +1306,7 @@
- ENTRY(width),
- ENTRY(widthindex),
- ENTRY(ncasecmp),
-+#endif
- ENTRY(find),
- ENTRY(gmatch),
- ENTRY(gsub),
-@@ -1297,15 +1315,17 @@
- { NULL, NULL }
- };
-
-+lua_getglobal(L, "utf8");
- #if LUA_VERSION_NUM >= 502
-- luaL_newlib(L, libs);
-+ luaL_setfuncs(L, libs, 0);
- #else
-- lua_createtable(L, 0, sizeof(libs)/sizeof(libs[0]));
- luaL_register(L, NULL, libs);
- #endif
-
-+#if 0
- lua_pushliteral(L, UTF8PATT);
- lua_setfield(L, -2, "charpattern");
-+#endif
-
- return 1;
- }
---- unidata.h 2015-05-31 04:48:35.000000000 -0400
-+++ lib/unidata.h 2016-03-26 13:01:16.951664669 -0400
-@@ -904,6 +904,7 @@
- { 0x3000, 0x3000, 1 },
- };
-
-+#if 0
- static struct range_table unprintable_table[] = {
- { 0xAD, 0x34F, 674 },
- { 0x61C, 0x115F, 2883 },
-@@ -921,6 +922,7 @@
- { 0x1D173, 0x1D17A, 1 },
- { 0xE0000, 0xE0FFF, 1 },
- };
-+#endif
-
- static struct range_table graph_table[] = {
- { 0x20, 0x7E, 1 },
-@@ -2547,6 +2549,7 @@
- { 0x118C0, 0x118DF, 1, -32 },
- };
-
-+#if 0
- static struct conv_table totitle_table[] = {
- { 0x61, 0x7A, 1, -32 },
- { 0xB5, 0xB5, 1, 743 },
-@@ -3060,5 +3063,6 @@
- { 0xF0000, 0xFFFFD, 1 },
- { 0x100000, 0x10FFFD, 1 },
- };
-+#endif
-
- #endif /* unidata_h */
diff --git a/src/scintilla.patch b/src/scintilla.patch
index a6111696..4731fef2 100644
--- a/src/scintilla.patch
+++ b/src/scintilla.patch
@@ -30,3 +30,164 @@ diff -r eb69b2b4bb85 gtk/ScintillaGTK.cxx
object_class->finalize = Destroy;
#if GTK_CHECK_VERSION(3,0,0)
widget_class->get_preferred_width = GetPreferredWidth;
+diff -r bfdfb44eb777 src/Document.cxx
+--- a/src/Document.cxx Sun May 22 08:57:20 2016 +1000
++++ b/src/Document.cxx Mon Jul 04 15:23:05 2016 -0400
+@@ -2845,3 +2845,157 @@
+ #endif
+
+ #endif
++
++#include "tre.h"
++
++class TreRegex : public RegexSearchBase {
++public:
++ explicit TreRegex() : lastS(NULL), lastSLen(0) {}
++ virtual ~TreRegex() { if (lastS) free(lastS), tre_regfree(&preg); }
++ virtual long FindText(Document *doc, int minPos, int maxPos, const char *s,
++ bool caseSensitive, bool word, bool wordStart, int flags,
++ int *length);
++ virtual const char *SubstituteByPosition(Document *doc, const char *text,
++ int *length);
++private:
++ char *lastS;
++ int lastSLen;
++ regex_t preg;
++ regmatch_t pmatch[10];
++ std::string substituted;
++};
++
++long TreRegex::FindText(Document *doc, int minPos, int maxPos, const char *s,
++ bool caseSensitive, bool, bool, int,
++ int *length) {
++ // Determine the search range. (From Document.cxx::RESearchRange.)
++ int increment, startPos, endPos;
++ if (minPos <= maxPos)
++ increment = 1, startPos = minPos, endPos = maxPos;
++ else
++ increment = -1, startPos = maxPos, endPos = minPos;
++ // Range endpoints should not be inside DBCS characters, but just in case,
++ // move them.
++ startPos = doc->MovePositionOutsideChar(startPos, 1, false);
++ endPos = doc->MovePositionOutsideChar(endPos, 1, false);
++ int lineRangeStart = doc->LineFromPosition(startPos);
++ int lineRangeEnd = doc->LineFromPosition(endPos);
++ if (increment == 1 && startPos >= doc->LineEnd(lineRangeStart) &&
++ lineRangeStart < lineRangeEnd) {
++ // The start position is at end of line or between line end characters.
++ lineRangeStart++;
++ startPos = doc->LineStart(lineRangeStart);
++ } else if (increment == -1 && startPos <= doc->LineStart(lineRangeStart) &&
++ lineRangeStart > lineRangeEnd) {
++ // The start position is at beginning of line.
++ lineRangeStart--;
++ startPos = doc->LineEnd(lineRangeStart);
++ }
++
++ // Compile the regex or used the cached one.
++ if (!lastS || lastSLen != *length || strncmp(lastS, s, *length) != 0) {
++ int cflags = REG_EXTENDED | (!caseSensitive ? REG_ICASE : 0) | REG_NEWLINE;
++ if (tre_regncomp(&preg, s, *length, cflags) != REG_OK) return -1;
++ if (lastS) free(lastS);
++ lastS = static_cast<char *>(malloc(*length + 1));
++ strncpy(lastS, s, *length);
++ lastS[*length] = '\0';
++ lastSLen = *length;
++ }
++
++ // Perform the matching.
++ int pos = -1, lenRet = 0;
++ const char *string = doc->BufferPointer();
++ size_t len = endPos - startPos;
++ int eflags = ((startPos != doc->LineStart(lineRangeStart)) ? REG_NOTBOL : 0) |
++ ((endPos != doc->LineEnd(lineRangeEnd)) ? REG_NOTEOL : 0);
++ int success = tre_regnexec(&preg, string + startPos, len, 10, pmatch, eflags) == REG_OK;
++ if (success) {
++ for (int i = 0; i < 10 && pmatch[i].rm_so != -1; i++)
++ pmatch[i].rm_so += startPos, pmatch[i].rm_eo += startPos; // adjust
++ pos = pmatch[0].rm_so, lenRet = pmatch[0].rm_eo - pmatch[0].rm_so;
++ if (increment == -1) {
++ // Check for the last match on this line.
++ int repetitions = 1000; // break out of infinite loop
++ while (success && pmatch[0].rm_eo <= endPos && repetitions--) {
++ success = tre_regnexec(&preg, string + pos + 1, len - (pos + 1), 10,
++ pmatch, eflags) == REG_OK;
++ if (success) {
++ for (int i = 0; i < 10 && pmatch[i].rm_so != -1; i++)
++ pmatch[i].rm_so += pos + 1, pmatch[i].rm_eo += pos + 1; // adjust
++ if (pmatch[0].rm_eo <= minPos)
++ pos = pmatch[0].rm_so, lenRet = pmatch[0].rm_eo - pmatch[0].rm_so;
++ else
++ success = 0;
++ }
++ }
++ }
++ }
++ *length = lenRet;
++ return pos;
++}
++
++const char *TreRegex::SubstituteByPosition(Document *doc, const char *text,
++ int *length) {
++ substituted.clear();
++ for (int j = 0; j < *length; j++) {
++ if (text[j] == '\\') {
++ if (text[j + 1] >= '0' && text[j + 1] <= '9') {
++ unsigned int patNum = text[j + 1] - '0';
++ unsigned int len = pmatch[patNum].rm_eo - pmatch[patNum].rm_so;
++ if (len > 0) // will be -1 for a match that did not occur
++ substituted.append(doc->BufferPointer() + pmatch[patNum].rm_so, len);
++ j++;
++ } else {
++ j++;
++ switch (text[j]) {
++ case 'a':
++ substituted.push_back('\a');
++ break;
++ case 'b':
++ substituted.push_back('\b');
++ break;
++ case 'f':
++ substituted.push_back('\f');
++ break;
++ case 'n':
++ substituted.push_back('\n');
++ break;
++ case 'r':
++ substituted.push_back('\r');
++ break;
++ case 't':
++ substituted.push_back('\t');
++ break;
++ case 'v':
++ substituted.push_back('\v');
++ break;
++ case '\\':
++ substituted.push_back('\\');
++ break;
++ default:
++ substituted.push_back('\\');
++ j--;
++ }
++ }
++ } else {
++ substituted.push_back(text[j]);
++ }
++ }
++ *length = static_cast<int>(substituted.length());
++ return substituted.c_str();
++}
++
++#ifdef SCI_NAMESPACE
++
++RegexSearchBase *Scintilla::CreateRegexSearch(CharClassify *charClassTable) {
++ return new TreRegex();
++}
++
++#else
++
++RegexSearchBase *CreateRegexSearch(CharClassify *charClassTable) {
++ return new TreRegex();
++}
++
++#endif
diff --git a/src/textadept.c b/src/textadept.c
index a1ee9e5c..a9a044f3 100644
--- a/src/textadept.c
+++ b/src/textadept.c
@@ -146,7 +146,7 @@ static GtkWidget *findbox, *find_entry, *replace_entry, *flabel, *rlabel;
#define repl_text gtk_entry_get_text(GTK_ENTRY(replace_entry))
typedef GtkWidget * FindButton;
static FindButton fnext_button, fprev_button, r_button, ra_button;
-static GtkWidget *match_case, *whole_word, *lua_pattern, *in_files;
+static GtkWidget *match_case, *whole_word, *regex, *in_files;
typedef GtkListStore ListStore;
static ListStore *find_store, *repl_store;
#define toggled(w) gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w))
@@ -195,7 +195,7 @@ static char *find_text, *repl_text, *flabel, *rlabel;
typedef enum {fnext_button, r_button, fprev_button, ra_button} FindButton;
static int find_options[4];
static int *match_case = &find_options[0], *whole_word = &find_options[1],
- *lua_pattern = &find_options[2], *in_files = &find_options[3];
+ *regex = &find_options[2], *in_files = &find_options[3];
static char *button_labels[4], *option_labels[4];
typedef char * ListStore;
static ListStore find_store[10], repl_store[10];
@@ -239,7 +239,7 @@ static void new_buffer(sptr_t);
static Scintilla *new_view(sptr_t);
static int lL_init(lua_State *, int, char **, int);
LUALIB_API int luaopen_lpeg(lua_State *), luaopen_lfs(lua_State *);
-LUALIB_API int luaopen_utf8_ext(lua_State *), luaopen_spawn(lua_State *);
+LUALIB_API int luaopen_spawn(lua_State *);
LUALIB_API int lspawn_pushfds(lua_State *), lspawn_readfds(lua_State *);
/**
@@ -522,8 +522,8 @@ static int lfind__index(lua_State *L) {
lua_pushboolean(L, toggled(match_case));
else if (strcmp(key, "whole_word") == 0)
lua_pushboolean(L, toggled(whole_word));
- else if (strcmp(key, "lua") == 0)
- lua_pushboolean(L, toggled(lua_pattern));
+ else if (strcmp(key, "regex") == 0)
+ lua_pushboolean(L, toggled(regex));
else if (strcmp(key, "in_files") == 0)
lua_pushboolean(L, toggled(in_files));
else
@@ -550,8 +550,8 @@ static int lfind__newindex(lua_State *L) {
toggle(match_case, lua_toboolean(L, -1));
else if (strcmp(key, "whole_word") == 0)
toggle(whole_word, lua_toboolean(L, -1));
- else if (strcmp(key, "lua") == 0)
- toggle(lua_pattern, lua_toboolean(L, -1));
+ else if (strcmp(key, "regex") == 0)
+ toggle(regex, lua_toboolean(L, -1));
else if (strcmp(key, "in_files") == 0)
toggle(in_files, lua_toboolean(L, -1));
else if (strcmp(key, "find_label_text") == 0)
@@ -570,8 +570,8 @@ static int lfind__newindex(lua_State *L) {
set_option_label(match_case, 0, lua_tostring(L, 3));
else if (strcmp(key, "whole_word_label_text") == 0)
set_option_label(whole_word, 1, lua_tostring(L, 3));
- else if (strcmp(key, "lua_pattern_label_text") == 0)
- set_option_label(lua_pattern, 2, lua_tostring(L, 3));
+ else if (strcmp(key, "regex_label_text") == 0)
+ set_option_label(regex, 2, lua_tostring(L, 3));
else if (strcmp(key, "in_files_label_text") == 0)
set_option_label(in_files, 3, lua_tostring(L, 3));
else
@@ -1534,7 +1534,7 @@ static int lL_init(lua_State *L, int argc, char **argv, int reinit) {
#endif
}
lua_pushinteger(L, (sptr_t)L), lua_setglobal(L, "_LUA");
- luaL_openlibs(L), lL_openlib(L, utf8_ext);
+ luaL_openlibs(L);
lL_openlib(L, lpeg), lL_openlib(L, lfs), lL_openlib(L, spawn);
lua_newtable(L);
@@ -2214,7 +2214,7 @@ static GtkWidget *new_findbox() {
ra_button = gtk_button_new_with_mnemonic("Replace _All");
match_case = gtk_check_button_new_with_mnemonic("_Match case");
whole_word = gtk_check_button_new_with_mnemonic("_Whole word");
- lua_pattern = gtk_check_button_new_with_mnemonic("_Lua pattern");
+ regex = gtk_check_button_new_with_mnemonic("Rege_x");
in_files = gtk_check_button_new_with_mnemonic("_In files");
gtk_label_set_mnemonic_widget(GTK_LABEL(flabel), find_entry);
@@ -2230,7 +2230,7 @@ static GtkWidget *new_findbox() {
attach(ra_button, 3, 4, 1, 2, FILL(SHRINK), FILL(SHRINK), 0, 0);
attach(match_case, 4, 5, 0, 1, FILL(SHRINK), FILL(SHRINK), 5, 0);
attach(whole_word, 4, 5, 1, 2, FILL(SHRINK), FILL(SHRINK), 5, 0);
- attach(lua_pattern, 5, 6, 0, 1, FILL(SHRINK), FILL(SHRINK), 5, 0);
+ attach(regex, 5, 6, 0, 1, FILL(SHRINK), FILL(SHRINK), 5, 0);
attach(in_files, 5, 6, 1, 2, FILL(SHRINK), FILL(SHRINK), 5, 0);
signal(fnext_button, "clicked", f_clicked);
@@ -2245,7 +2245,7 @@ static GtkWidget *new_findbox() {
gtk_widget_set_can_focus(ra_button, FALSE);
gtk_widget_set_can_focus(match_case, FALSE);
gtk_widget_set_can_focus(whole_word, FALSE);
- gtk_widget_set_can_focus(lua_pattern, FALSE);
+ gtk_widget_set_can_focus(regex, FALSE);
gtk_widget_set_can_focus(in_files, FALSE);
return findbox;
diff --git a/src/tre.patch b/src/tre.patch
new file mode 100644
index 00000000..d2457d21
--- /dev/null
+++ b/src/tre.patch
@@ -0,0 +1,49 @@
+diff -r c8148182a381 lib/tre-config.h
+--- /dev/null Thu Jan 01 00:00:00 1970 +0000
++++ b/lib/tre-config.h Mon Jul 04 13:22:19 2016 -0400
+@@ -0,0 +1,45 @@
++/* tre-config.h.in. This file has all definitions that are needed in
++ `tre.h'. Note that this file must contain only the bare minimum
++ of definitions without the TRE_ prefix to avoid conflicts between
++ definitions here and definitions included from somewhere else. */
++
++/* Define to 1 if you have the <libutf8.h> header file. */
++#undef HAVE_LIBUTF8_H
++
++/* Define to 1 if the system has the type `reg_errcode_t'. */
++#undef HAVE_REG_ERRCODE_T
++
++/* Define to 1 if you have the <sys/types.h> header file. */
++#undef HAVE_SYS_TYPES_H
++
++/* Define to 1 if you have the <wchar.h> header file. */
++#undef HAVE_WCHAR_H
++
++/* Define if you want to enable approximate matching functionality. */
++#undef TRE_APPROX
++
++/* Define to enable multibyte character set support. */
++#undef TRE_MULTIBYTE
++
++/* Define to the absolute path to the system tre.h */
++#undef TRE_SYSTEM_REGEX_H_PATH
++
++/* Define to include the system regex.h from tre.h */
++#undef TRE_USE_SYSTEM_REGEX_H
++
++/* Define to enable wide character (wchar_t) support. */
++#undef TRE_WCHAR
++
++/* TRE version string. */
++#define TRE_VERSION "0.8.0"
++
++/* TRE version level 1. */
++#undef TRE_VERSION_1
++
++/* TRE version level 2. */
++#undef TRE_VERSION_2
++
++/* TRE version level 3. */
++#undef TRE_VERSION_3
++
++#define TRE_REGEX_T_FIELD value