Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
S
StarUML Evológica Plugin
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
3
Merge Requests
3
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
evologica
StarUML Evológica Plugin
Commits
224b6f5f
Commit
224b6f5f
authored
Oct 20, 2020
by
Jucelino Fonseca
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
ajustes
parent
5dd19c72
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
6428 additions
and
99 deletions
+6428
-99
SynFacilBasic.pas
EvoUMLPlugin/src/SynFacilBasic.pas
+1613
-0
SynFacilCompletion.pas
EvoUMLPlugin/src/SynFacilCompletion.pas
+2118
-0
SynFacilHighlighter.pas
EvoUMLPlugin/src/SynFacilHighlighter.pas
+2520
-0
ituExplorerV2.dfm
EvoUMLPlugin/src/ituExplorerV2.dfm
+2
-18
ituExplorerV2.pas
EvoUMLPlugin/src/ituExplorerV2.pas
+175
-81
No files found.
EvoUMLPlugin/src/SynFacilBasic.pas
0 → 100644
View file @
224b6f5f
{ SynFacilBasic
Unidad con rutinas básicas de SynFacilSyn.
Incluye la definición de la clase base: TSynFacilSynBase, que es la clase padre
de TSYnFacilSyn.
Además icnluye la definición del tipo "tFaTokContent" y el procesamiento de
expresiones regulares que son usadas por TSynFacilSyn.
Por Tito Hinostroza 02/12/2014 - Lima Perú
}
unit
SynFacilBasic
;
{$mode objfpc}{$H+}
interface
uses
SysUtils
,
Classes
,
SynEditHighlighter
,
strutils
,
Graphics
,
DOM
,
LCLIntf
,
LCLProc
,
SynEditHighlighterFoldBase
,
SynEditTypes
;
type
///////// Definiciones para manejo de tokens por contenido ///////////
//Tipo de expresión regular soportada. Las exp. regulares soportadas son
//simples. Solo incluyen literales de cadena o listas.
tFaRegExpType
=
(
tregString
,
//Literal de cadena: "casa"
tregChars
,
//Lista de caracteres: [A-Z]
tregChars01
,
//Lista de caracteres: [A-Z]?
tregChars0_
,
//Lista de caracteres: [A-Z]*
tregChars1_
//Lista de caracteres: [A-Z]+
);
//Acciones a ejecutar en las comparaciones
tFaActionOnMatch
=
(
aomNext
,
//pasa a la siguiente instrucción
aomExit
,
//termina la exploración
aomMovePar
,
//Se mueve a una posición específica
aomExitpar
//termina la exploración retomando una posición específica.
);
//Estructura para almacenar una instrucción de token por contenido
tFaTokContentInst
=
record
Chars
:
array
[#
0.
.#
255
]
of
ByteBool
;
//caracteres
Text
:
string
;
//cadena válida
expTyp
:
tFaRegExpType
;
//tipo de expresión
aMatch
:
integer
;
//atributo asignado en caso TRUE
aFail
:
integer
;
//atributo asignado en caso TRUE
//Campos para ejecutar instrucciones, cuando No cumple
actionFail
:
tFaActionOnMatch
;
destOnFail
:
integer
;
//posición destino
//Campos para ejecutar instrucciones, cuando cumple
actionMatch
:
tFaActionOnMatch
;
destOnMatch
:
integer
;
//posición destino
posFin
:
integer
;
//para guardar posición
end
;
ESynFacilSyn
=
class
(
Exception
);
//excepción del resaltador
{ tFaTokContent }
//Estructura para almacenar la descripción de los token por contenido
tFaTokContent
=
class
TokTyp
:
integer
;
//tipo de token por contenido
CaseSensitive
:
boolean
;
//Usado para comparación de literales de cadena
Instrucs
:
array
of
tFaTokContentInst
;
//Instrucciones del token por contenido
nInstruc
:
integer
;
//Cantidad de instrucciones
procedure
Clear
;
procedure
AddInstruct
(
exp
:
string
;
ifTrue
:
string
=
''
;
ifFalse
:
string
=
''
;
atMatch
:
integer
=
-
1
;
atFail
:
integer
=
-
1
);
procedure
AddRegEx
(
exp
:
string
;
Complete
:
boolean
=
false
);
private
function
AddItem
(
expTyp
:
tFaRegExpType
;
ifMatch
,
ifFail
:
string
):
integer
;
procedure
AddOneInstruct
(
var
exp
:
string
;
ifTrue
:
string
;
ifFalse
:
string
;
atMatch
:
integer
=
-
1
;
atFail
:
integer
=
-
1
);
end
;
///////// Definiciones básicas para el resaltador ///////////
//Identifica si un token es el delimitador inicial
TFaTypeDelim
=(
tdNull
,
//no es delimitado
tdUniLin
,
//es delimitador inicial de token delimitado de una línea
tdMulLin
,
//es delimitador inicial de token delimitado multilínea
tdConten1
,
//es delimitador inicial de token por contenido 1
tdConten2
,
//es delimitador inicial de token por contenido 2
tdConten3
,
//es delimitador inicial de token por contenido 3
tdConten4
);
//es delimitador inicial de token por contenido 4
//Tipos de coloreado de bloques
TFaColBlock
=
(
cbNull
,
//sin coloreado
cbLevel
,
//colorea bloques por nivel
cbBlock
);
//colorea bloques usando el color definido para cada bloque
TFaProcMetTable
=
procedure
of
object
;
//Tipo de procedimiento para procesar el token de
//acuerdo al caracter inicial.
TFaProcRange
=
procedure
of
object
;
//Procedimiento para procesar en medio de un rango.
TFaSynBlock
=
class
;
//definición adelantada
//Descripción de tokens especiales (identificador o símbolo)
TTokSpec
=
record
txt
:
string
;
//palabra clave (puede cambiar la caja y no incluir el primer caracter)
orig
:
string
;
//palabra clave tal cual se indica
TokPos
:
integer
;
//posición del token dentro de la línea
tTok
:
integer
;
//tipo de token
typDel
:
TFaTypeDelim
;
{indica si el token especial actual, es en realidad, el
delimitador inicial de un token delimitado o por contenido}
dEnd
:
string
;
//delimitador final (en caso de que sea delimitador)
pRange
:
TFaProcRange
;
//procedimiento para procesar el token o rango(si es multilinea)
folTok
:
boolean
;
//indica si el token delimitado, tiene plegado
chrEsc
:
char
;
//Caracter de escape de token delimitado. Si no se usa, contiene #0.
//propiedades para manejo de bloques y plegado de código
openBlk
:
boolean
;
//indica si el token es inicio de bloque de plegado
BlksToOpen
:
array
of
TFaSynBlock
;
//lista de referencias a los bloques que abre
closeBlk
:
boolean
;
//indica si el token es fin de bloque de plegado
BlksToClose
:
array
of
TFaSynBlock
;
//lista de referencias a los bloques que cierra
OpenSec
:
boolean
;
//indica si el token es inicio de sección de bloque
SecsToOpen
:
array
of
TFaSynBlock
;
//lista de bloques de los que es inicio de sección
firstSec
:
TFaSynBlock
;
//sección que se debe abrir al abrir el bloque
end
;
TEvBlockOnOpen
=
procedure
(
blk
:
TFaSynBlock
;
var
Cancel
:
boolean
)
of
object
;
TArrayTokSpec
=
array
of
TTokSpec
;
//clase para manejar la definición de bloques de sintaxis
TFaSynBlock
=
class
name
:
string
;
//nombre del bloque
index
:
integer
;
//indica su posición dentro de TFaListBlocks
showFold
:
boolean
;
//indica si se mostrará la marca de plegado
parentBlk
:
TFaSynBlock
;
//bloque padre (donde es válido el bloque)
BackCol
:
TColor
;
//color de fondo de un bloque
IsSection
:
boolean
;
//indica si es un bloque de tipo sección
UniqSec
:
boolean
;
//índica que es sección única
CloseParent
:
boolean
;
//indica que debe cerrar al blqoue padre al cerrarse
OnBeforeOpen
:
TEvBlockOnOpen
;
//evento de apertura de bloque
OnBeforeClose
:
TEvBlockOnOpen
;
//evento de cierre de bloque
end
;
TPtrATokEspec
=
^
TArrayTokSpec
;
//puntero a tabla
TPtrTokEspec
=
^
TTokSpec
;
//puntero a tabla
//Guarda información sobre un atributo de un nodo XML
TFaXMLatrib
=
record
//atributo XML
hay
:
boolean
;
//bandera de existencia
val
:
string
;
//valor en cadena
n
:
integer
;
//valor numérico
bol
:
boolean
;
//valor booleando (si aplica)
col
:
TColor
;
//valor de color (si aplica)
end
;
{ TSynFacilSynBase }
//Clase con métodos básicos para el resaltador
TSynFacilSynBase
=
class
(
TSynCustomFoldHighlighter
)
protected
fLine
:
PChar
;
//Puntero a línea de trabajo
tamLin
:
integer
;
//Tamaño de línea actual
fProcTable
:
array
[#
0.
.#
255
]
of
TFaProcMetTable
;
//tabla de métodos
fAtriTable
:
array
[#
0.
.#
255
]
of
integer
;
//tabla de atributos de tokens
posIni
:
Integer
;
//índice a inicio de token
posFin
:
Integer
;
//índice a siguiente token
fStringLen
:
Integer
;
//Tamaño del token actual
fToIdent
:
PChar
;
//Puntero a identificador
fTokenID
:
integer
;
//Id del token actual
charIni
:
char
;
//caracter al que apunta fLine[posFin]
posTok
:
integer
;
//para identificar el ordinal del token en una línea
CaseSensitive
:
boolean
;
//Para ignorar mayúscula/minúscula
charsIniIden
:
Set
of
char
;
//caracteres iniciales de identificador
lisTmp
:
TStringList
;
//lista temporal
fSampleSource
:
string
;
//código de muestra
function
GetSampleSource
:
String
;
override
;
protected
//identificadores especiales
CharsIdentif
:
array
[#
0.
.#
255
]
of
ByteBool
;
//caracteres válidos para identificadores
tc1
,
tc2
,
tc3
,
tc4
:
tFaTokContent
;
//Tablas para identificadores especiales
mA
,
mB
,
mC
,
mD
,
mE
,
mF
,
mG
,
mH
,
mI
,
mJ
,
mK
,
mL
,
mM
,
mN
,
mO
,
mP
,
mQ
,
mR
,
mS
,
mT
,
mU
,
mV
,
mW
,
mX
,
mY
,
mZ
:
TArrayTokSpec
;
//para mayúsculas
mA_
,
mB_
,
mC_
,
mD_
,
mE_
,
mF_
,
mG_
,
mH_
,
mI_
,
mJ_
,
mK_
,
mL_
,
mM_
,
mN_
,
mO_
,
mP_
,
mQ_
,
mR_
,
mS_
,
mT_
,
mU_
,
mV_
,
mW_
,
mX_
,
mY_
,
mZ_
:
TArrayTokSpec
;
//para minúsculas
m_
,
mDol
,
mArr
,
mPer
,
mAmp
,
mC3
:
TArrayTokSpec
;
mSym
:
TArrayTokSpec
;
//tabla de símbolos especiales
mSym0
:
TArrayTokSpec
;
//tabla temporal para símbolos especiales.
TabMayusc
:
array
[#
0.
.#
255
]
of
Char
;
//Tabla para conversiones rápidas a mayúscula
protected
//funciones básicas
function
BuscTokEspec
(
var
mat
:
TArrayTokSpec
;
cad
:
string
;
out
n
:
integer
;
TokPos
:
integer
=
0
):
boolean
;
function
ToListRegex
(
list
:
TFaXMLatrib
):
string
;
function
dStartRegex
(
tStart
,
tCharsStart
:
TFaXMLatrib
):
string
;
procedure
VerifDelim
(
delim
:
string
);
procedure
ValidAsigDelim
(
delAct
,
delNue
:
TFaTypeDelim
;
delim
:
string
);
procedure
ValidateParamStart
(
Start
:
string
;
var
ListElem
:
TStringList
);
function
KeyComp
(
var
r
:
TTokSpec
):
Boolean
;
function
CreaBuscTokEspec
(
var
mat
:
TArrayTokSpec
;
cad
:
string
;
out
i
:
integer
;
TokPos
:
integer
=
0
):
boolean
;
//procesamiento de XML
procedure
CheckXMLParams
(
n
:
TDOMNode
;
listAtrib
:
string
);
function
ReadXMLParam
(
n
:
TDOMNode
;
nomb
:
string
):
TFaXMLatrib
;
protected
//Métodos para tokens por contenido
procedure
metTokCont
(
const
tc
:
tFaTokContent
);
//inline;
procedure
metTokCont1
;
procedure
metTokCont2
;
procedure
metTokCont3
;
procedure
metTokCont4
;
protected
//Procesamiento de otros elementos
procedure
metIdent
;
procedure
metIdentUTF8
;
procedure
metNull
;
procedure
metSpace
;
procedure
metSymbol
;
public
//Funciones públicas
procedure
DefTokIdentif
(
dStart
,
Content
:
string
);
public
//Atributos y sus propiedades de acceso
//Atributos predefinidos
tkEol
:
TSynHighlighterAttributes
;
tkSymbol
:
TSynHighlighterAttributes
;
tkSpace
:
TSynHighlighterAttributes
;
tkIdentif
:
TSynHighlighterAttributes
;
tkNumber
:
TSynHighlighterAttributes
;
tkKeyword
:
TSynHighlighterAttributes
;
tkString
:
TSynHighlighterAttributes
;
tkComment
:
TSynHighlighterAttributes
;
//ID para los tokens
tnEol
:
integer
;
//id para los tokens salto de línea
tnSymbol
:
integer
;
//id para los símbolos
tnSpace
:
integer
;
//id para los espacios
tnIdentif
:
integer
;
//id para los identificadores
tnNumber
:
integer
;
//id para los números
tnKeyword
:
integer
;
//id para las palabras claves
tnString
:
integer
;
//id para las cadenas
tnComment
:
integer
;
//id para los comentarios
{Se crea el contenedor adicional Attrib[], para los atributos, porque aunque ya se
tiene Attribute[] en TSynCustomHighlighter, este está ordenado pro defecto y no
ayuda en ubicar a los attributos por su índice}
Attrib
:
array
of
TSynHighlighterAttributes
;
function
NewTokAttrib
(
TypeName
:
string
;
out
TokID
:
integer
):
TSynHighlighterAttributes
;
function
NewTokType
(
TypeName
:
string
;
out
TokAttrib
:
TSynHighlighterAttributes
):
integer
;
function
NewTokType
(
TypeName
:
string
):
integer
;
procedure
CreateAttributes
;
//limpia todos loa atributos
function
GetAttribByName
(
txt
:
string
):
TSynHighlighterAttributes
;
function
GetAttribIDByName
(
txt
:
string
):
integer
;
function
IsAttributeName
(
txt
:
string
):
boolean
;
protected
function
ProcXMLattribute
(
nodo
:
TDOMNode
):
boolean
;
public
//Inicializacoón
constructor
Create
(
AOwner
:
TComponent
);
override
;
end
;
function
ExtractRegExp
(
var
exp
:
string
;
var
str
:
string
;
var
listChars
:
string
):
tFaRegExpType
;
function
ExtractRegExpN
(
var
exp
:
string
;
out
RegexTyp
:
tFaRegExpType
):
string
;
function
ReplaceEscape
(
str
:
string
):
string
;
function
ColorFromStr
(
cad
:
string
):
TColor
;
implementation
const
//Mensajes de error generales
// ERR_START_NO_EMPTY = 'Parámetro "Start" No puede ser nulo';
// ERR_EXP_MUST_BE_BR = 'Expresión debe ser de tipo [lista de caracteres]';
// ERR_TOK_DELIM_NULL = 'Delimitador de token no puede ser nulo';
// ERR_NOT_USE_START = 'No se puede usar "Start" y "CharsStart" simultáneamente.';
// ERR_PAR_START_CHARS = 'Se debe definir el parámetro "Start" o "CharsStart".';
// ERR_TOK_DEL_IDE_ERR = 'Delimitador de token erróneo: %s (debe ser identificador)';
// ERR_IDEN_ALREA_DEL = 'Identificador "%s" ya es delimitador inicial.';
// ERR_INVAL_ATTR_LAB = 'Atributo "%s" no válido para etiqueta <%s>';
// ERR_BAD_PAR_STR_IDEN = 'Parámetro "Start" debe ser de la forma: "[A-Z]", en identificadores';
// ERR_BAD_PAR_CON_IDEN = 'Parámetro "Content" debe ser de la forma: "[A-Z]*", en identificadores';
ERR_START_NO_EMPTY
=
'Parameter "Start" can not be null'
;
ERR_EXP_MUST_BE_BR
=
'Expression must be like: [list of chars]'
;
ERR_TOK_DELIM_NULL
=
'Token delimiter can not be null'
;
ERR_NOT_USE_START
=
'Cannot use "Start" and "CharsStart" simultaneously.'
;
ERR_PAR_START_CHARS
=
'It must be defined "Start" or "CharsStart" parameter.'
;
ERR_TOK_DEL_IDE_ERR
=
'Bad Token delimiter: %s (must be identifier)'
;
ERR_IDEN_ALREA_DEL
=
'Identifier "%s" is already a Start delimiter.'
;
ERR_INVAL_ATTR_LAB
=
'Invalid attribute "%s" for label <%s>'
;
ERR_BAD_PAR_STR_IDEN
=
'Parameter "Start" must be like: "[A-Z]", in identifiers'
;
ERR_BAD_PAR_CON_IDEN
=
'Parameter "Content" must be like: "[A-Z]*", in identifiers'
;
//Mensajes de tokens por contenido
// ERR_EMPTY_INTERVAL = 'Error: Intervalo vacío.';
// ERR_EMPTY_EXPRES = 'Expresión vacía.';
// ERR_EXPECTED_BRACK = 'Se esperaba "]".';
// ERR_UNSUPPOR_EXP_ = 'Expresión no soportada.';
// ERR_INC_ESCAPE_SEQ = 'Secuencia de escape incompleta.';
// ERR_SYN_PAR_IFFAIL_ = 'Error de sintaxis en parámetro "IfFail": ';
// ERR_SYN_PAR_IFMATCH_ = 'Error de sintaxis en parámetro "IfMarch": ';
ERR_EMPTY_INTERVAL
=
'Error: Empty Interval.'
;
ERR_EMPTY_EXPRES
=
'Empty expression.'
;
ERR_EXPECTED_BRACK
=
'Expected "]".'
;
ERR_UNSUPPOR_EXP_
=
'Unsupported expression: '
;
ERR_INC_ESCAPE_SEQ
=
'Incomplete Escape sequence'
;
ERR_SYN_PAR_IFFAIL_
=
'Syntax error on Parameter "IfFail": '
;
ERR_SYN_PAR_IFMATCH_
=
'Syntax error on Parameter "IfMarch": '
;
var
bajos
:
string
[
128
];
altos
:
string
[
128
];
function
copyEx
(
txt
:
string
;
p
:
integer
):
string
;
//Versión sobrecargada de copy con 2 parámetros
begin
Result
:=
copy
(
txt
,
p
,
length
(
txt
));
end
;
//Funciones para el manejo de expresiones regulares
function
ExtractChar
(
var
txt
:
string
;
out
escaped
:
boolean
;
convert
:
boolean
):
string
;
//Extrae un caracter de una expresión regular. Si el caracter es escapado, devuelve
//TRUE en "escaped"
//Si covert = TRUE, reemplaza el caracter compuesto por uno solo.
var
c
:
byte
;
begin
escaped
:=
false
;
Result
:=
''
;
//valor por defecto
if
txt
=
''
then
exit
;
if
txt
[
1
]
=
'\'
then
begin
//caracter escapado
escaped
:=
true
;
if
length
(
txt
)
=
1
then
//verificación
raise
ESynFacilSyn
.
Create
(
ERR_INC_ESCAPE_SEQ
);
if
txt
[
2
]
in
[
'x'
,
'X'
]
then
begin
//caracter en hexadecimal
if
length
(
txt
)
<
4
then
//verificación
raise
ESynFacilSyn
.
Create
(
ERR_INC_ESCAPE_SEQ
);
if
convert
then
begin
//toma caracter hexdecimal
c
:=
StrToInt
(
'$'
+
copy
(
txt
,
3
,
2
));
Result
:=
Chr
(
c
);
end
else
begin
//no tranforma
Result
:=
copy
(
txt
,
1
,
4
);
end
;
txt
:=
copyEx
(
txt
,
5
);
end
else
begin
//se supone que es de tipo \A
//secuencia normal de dos caracteres
if
convert
then
begin
//hay que convertirlo
Result
:=
txt
[
2
];
end
else
begin
//lo toma tal cual
Result
:=
copy
(
txt
,
1
,
2
);
end
;
txt
:=
copyEx
(
txt
,
3
);
end
;
end
else
begin
//caracter normal
Result
:=
txt
[
1
];
txt
:=
copyEx
(
txt
,
2
);
end
;
end
;
function
ExtractChar
(
var
txt
:
string
):
char
;
//Versión simplificada de ExtractChar(). Extrae un caracter ya convertido. Si no hay
//más caracteres, devuelve #0
var
escaped
:
boolean
;
tmp
:
String
;
begin
if
txt
=
''
then
Result
:=
#
0
else
begin
tmp
:=
ExtractChar
(
txt
,
escaped
,
true
);
Result
:=
tmp
[
1
];
//se supone que siempre será de un solo caracter
end
;
end
;
function
ExtractCharN
(
var
txt
:
string
):
string
;
//Versión simplificada de ExtractChar(). Extrae un caracter sin convertir.
var
escaped
:
boolean
;
begin
Result
:=
ExtractChar
(
txt
,
escaped
,
false
);
end
;
function
ReplaceEscape
(
str
:
string
):
string
;
{Reemplaza las secuencias de escape por su caracter real. Las secuencias de
escape recnocidas son:
* Secuencia de 2 caracteres: "\#", donde # es un caracter cualquiera, excepto"x".
Esta secuencia equivale al caracter "#".
* Secuencia de 4 caracteres: "\xHH" o "\XHH", donde "HH" es un número hexadecimnal.
Esta secuencia representa a un caracter ASCII.
Dentro de las expresiones regulares de esta librería, los caracteres: "[", "*", "?",
"*", y "\", tienen significado especial, por eso deben "escaparse".
"\\" -> "\"
"\[" -> "["
"\*" -> "*"
"\?" -> "?"
"\+" -> "+"
"\x$$" -> caracter ASCII $$
}
begin
Result
:=
''
;
while
str
<>
''
do
Result
+=
ExtractChar
(
str
);
end
;
function
EscapeText
(
str
:
string
):
string
;
//Comvierte los caracteres que pueden tener significado especial en secuencias de
//escape para que se procesen como caracteres normales.
begin
str
:=
StringReplace
(
str
,
'\'
,
'\\'
,[
rfReplaceAll
]);
//debe hacerse primero
str
:=
StringReplace
(
str
,
'['
,
'\['
,[
rfReplaceAll
]);
str
:=
StringReplace
(
str
,
'*'
,
'\*'
,[
rfReplaceAll
]);
str
:=
StringReplace
(
str
,
'?'
,
'\?'
,[
rfReplaceAll
]);
str
:=
StringReplace
(
str
,
'+'
,
'\+'
,[
rfReplaceAll
]);
Result
:=
str
;
end
;
function
PosChar
(
ch
:
char
;
txt
:
string
):
integer
;
//Similar a Pos(). Devuelve la posición de un caracter que no este "escapado"
var
f
:
SizeInt
;
begin
f
:=
Pos
(
ch
,
txt
);
if
f
=
1
then
exit
(
1
);
//no hay ningún caracter antes.
while
(
f
>
0
)
and
(
txt
[
f
-
1
]=
'\'
)
do
begin
f
:=
PosEx
(
ch
,
txt
,
f
+
1
);
end
;
Result
:=
f
;
end
;
function
ExtractRegExp
(
var
exp
:
string
;
var
str
:
string
;
var
listChars
:
string
):
tFaRegExpType
;
{Extrae parte de una expresión regular y devuelve el tipo. Esta función se basa en
que toda expresión regular se puede reducir a literales de cadenas o listas (con o
sin cuantificador).
En los casos de listas de caracteres, expande los intervalos de tipo: A..Z, reemplaza
las secuencias de escape y devuelve la lista en "listChars".
En el caso de que sea un literal de cadena, reemplaza las secuencias de escape y
devuelve la cadena en "str".
Soporta todas las formas definidas en "tFaRegExpType".
Si encuentra error, genera una excepción.}
procedure
ValidateInterval
(
var
cars
:
string
);
{Valida un conjunto de caracteres, expandiendo los intervalos de tipo "A-Z", y
remplazando las secuencias de escape como: "\[", "\\", "\-", ...
El caracter "-", se considera como indicador de intervalo, a menos que se encuentre
en el primer o ùltimo caracter de la cadena, o esté escapado.
Si hay error genera una excepción.}
var
c
,
car1
,
car2
:
char
;
car
:
string
;
tmp
:
String
;
Invert
:
Boolean
;
carsSet
:
set
of
char
;
begin
//reemplaza intervalos
if
cars
=
''
then
raise
ESynFacilSyn
.
Create
(
ERR_EMPTY_INTERVAL
);
//Verifica si es lista invertida
Invert
:=
false
;
if
cars
[
1
]
=
'^'
then
begin
Invert
:=
true
;
//marca
cars
:=
copyEx
(
cars
,
2
);
//quita "^"
end
;
//Procesa contenido, reemplazando los caracteres escapados.
//Si el primer caracter es "-". lo toma literal, sin asumir error.
car1
:=
ExtractChar
(
cars
);
//Extrae caracter convertido. Se asume que es inicio de intervalo.
tmp
:=
car1
;
//inicia cadena para acumular.
car
:=
ExtractCharN
(
cars
);
//Eextrae siguiente. Sin convertir porque puede ser "\-"
while
car
<>
''
do
begin
if
car
=
'-'
then
begin
//es intervalo
car2
:=
ExtractChar
(
cars
);
//caracter final
if
car2
=
#
0
then
begin
//Es intervalo incompleto, podría genera error, pero mejor asumimos que es el caracter "-"
tmp
+=
'-'
;
break
;
//sale por que se supone que ya no hay más caracteres
end
;
//se tiene un intervalo que hay que reemplazar
for
c
:=
Chr
(
Ord
(
car1
)+
1
)
to
car2
do
//No se incluye "car1", porque ya se agregó
tmp
+=
c
;
end
else
begin
//simplemente acumula
car1
:=
ExtractChar
(
car
);
//Se asume que es inicio de intervalo. No importa perder "car"
tmp
+=
car1
;
//Es necesario, porque puede estar escapado
end
;
car
:=
ExtractCharN
(
cars
);
//extrae siguiente
end
;
cars
:=
StringReplace
(
tmp
,
'%HIGH%'
,
altos
,[
rfReplaceAll
]);
cars
:=
StringReplace
(
cars
,
'%ALL%'
,
bajos
+
altos
,[
rfReplaceAll
]);
//Verifica si debe invertir lista
if
Invert
then
begin
//Convierte a conjunto
carsSet
:=
[];
for
c
in
cars
do
carsSet
+=
[
c
];
//Agrega caracteres
cars
:=
''
;
for
c
:=
#
1
to
#
255
do
//no considera #0
if
not
(
c
in
carsSet
)
then
cars
+=
c
;
end
;
end
;
var
tmp
:
string
;
lastAd
:
String
;
begin
if
exp
=
''
then
raise
ESynFacilSyn
.
Create
(
ERR_EMPTY_EXPRES
);
//Reemplaza secuencias conocidas que equivalen a listas.
if
copy
(
exp
,
1
,
2
)
=
'\d'
then
begin
exp
:=
'[0-9]'
+
copyEx
(
exp
,
3
);
end
else
if
copy
(
exp
,
1
,
2
)
=
'\D'
then
begin
exp
:=
'[^0-9]'
+
copyEx
(
exp
,
3
);
end
else
if
copy
(
exp
,
1
,
2
)
=
'\a'
then
begin
exp
:=
'[A-Za-z]'
+
copyEx
(
exp
,
3
);
end
else
if
copy
(
exp
,
1
,
2
)
=
'\w'
then
begin
exp
:=
'[A-Za-z0-9_]'
+
copyEx
(
exp
,
3
);
end
else
if
copy
(
exp
,
1
,
2
)
=
'\W'
then
begin
exp
:=
'[^A-Za-z0-9_]'
+
copyEx
(
exp
,
3
);
end
else
if
copy
(
exp
,
1
,
2
)
=
'\s'
then
begin
exp
:=
' '
+
copyEx
(
exp
,
3
);
end
else
if
copy
(
exp
,
1
,
2
)
=
'\S'
then
begin
exp
:=
'[^ ]'
+
copyEx
(
exp
,
3
);
end
else
if
copy
(
exp
,
1
,
2
)
=
'\t'
then
begin
exp
:=
'\x09'
+
copyEx
(
exp
,
3
);
end
else
if
copy
(
exp
,
1
,
1
)
=
'.'
then
begin
exp
:=
'[\x01-\xFF]'
+
copyEx
(
exp
,
2
);
end
;
//analiza la secuencia
if
(
exp
[
1
]
=
'['
)
and
(
length
(
exp
)>
1
)
then
begin
//Es lista de caracteres
//Captura interior del intervalo.
exp
:=
CopyEx
(
exp
,
2
);
listChars
:=
''
;
tmp
:=
ExtractCharN
(
exp
);
//No convierte para no confundir "\]"
while
(
exp
<>
''
)
and
(
tmp
<>
']'
)
do
begin
listChars
+=
tmp
;
tmp
:=
ExtractCharN
(
exp
);
//No convierte para no confundir "\]"
end
;
if
(
tmp
<>
']'
)
then
//no se encontró ']'
raise
ESynFacilSyn
.
Create
(
ERR_EXPECTED_BRACK
);
//la norma es tener aquí, el contenido de la lista, pero manteniendo los caracteres escapados
ValidateInterval
(
listChars
);
//puede simplificar "listChars". También puede generar excepción
if
exp
=
''
then
begin
//Lista de tipo "[ ... ]"
Result
:=
tregChars
;
end
else
if
exp
[
1
]
=
'*'
then
begin
//Lista de tipo "[ ... ]* ... "
exp
:=
copyEx
(
exp
,
2
);
//extrae parte procesada
Result
:=
tregChars0_
end
else
if
exp
[
1
]
=
'?'
then
begin
//Lista de tipo "[ ... ]? ... "
exp
:=
copyEx
(
exp
,
2
);
//extrae parte procesada
Result
:=
tregChars01
end
else
if
exp
[
1
]
=
'+'
then
begin
//Lista de tipo "[ ... ]+ ... "
exp
:=
copyEx
(
exp
,
2
);
//extrae parte procesada
Result
:=
tregChars1_
end
else
begin
//No sigue ningún cuantificador, podrías er algún literal
Result
:=
tregChars
;
//Lista de tipo "[ ... ] ... "
end
;
end
else
if
(
length
(
exp
)=
1
)
and
(
exp
[
1
]
in
[
'*'
,
'?'
,
'+'
,
'['
])
then
begin
//Caso especial, no se usa escape, pero no es lista, ni cuantificador. Se asume
//caracter único
listChars
:=
exp
;
//'['+exp+']'
exp
:=
''
;
//ya no quedan caracteres
Result
:=
tregChars
;
exit
;
end
else
begin
//No inicia con lista. Se puede suponer que inicia con literal cadena.
{Pueden ser los casos:
Caso 0) "abc" (solo literal cadena, se extraerá la cadena "abc")
Caso 1) "abc[ ... " (válido, se extraerá la cadena "abc")
Caso 2) "a\[bc[ ... " (válido, se extraerá la cadena "a[bc")
Caso 3) "abc* ... " (válido, pero se debe procesar primero "ab")
Caso 4) "ab\\+ ... " (válido, pero se debe procesar primero "ab")
Caso 5) "a? ... " (válido, pero debe transformarse en lista)
Caso 6) "\[* ... " (válido, pero debe transformarse en lista)
}
str
:=
''
;
//para acumular
tmp
:=
ExtractCharN
(
exp
);
lastAd
:=
''
;
//solo por seguridad
while
tmp
<>
''
do
begin
if
tmp
=
'['
then
begin
//Empieza una lista. Caso 1 o 2
exp
:=
'['
+
exp
;
//devuelve el caracter
str
:=
ReplaceEscape
(
str
);
{ if length(str) = 1 then begin //verifica si tiene un caracter
listChars := str; //'['+str+']'
Result := tregChars; //devuelve como lista de un caracter
exit;
end;}
Result
:=
tregString
;
//es literal cadena
exit
;
//sale con lo acumulado en "str"
end
else
if
(
tmp
=
'*'
)
or
(
tmp
=
'?'
)
or
(
tmp
=
'+'
)
then
begin
str
:=
copy
(
str
,
1
,
length
(
str
)-
length
(
lastAd
));
//no considera el último caracter
if
str
<>
''
then
begin
//Hay literal cadena, antes de caracter y cuantificador. Caso 3 o 4
exp
:=
lastAd
+
tmp
+
exp
;
//devuelve el último caracter agregado y el cuantificador
str
:=
ReplaceEscape
(
str
);
if
length
(
str
)
=
1
then
begin
//verifica si tiene un caracter
listChars
:=
str
;
//'['+str+']'
Result
:=
tregChars
;
//devuelve como lista de un caracter
exit
;
end
;
Result
:=
tregString
;
//es literal cadena
exit
;
end
else
begin
//Hay caracter y cuantificador. . Caso 5 o 6
listChars
:=
ReplaceEscape
(
lastAd
);
//'['+lastAd+']'
//de "exp" ya se quitó: <caracter><cuantificador>
if
tmp
=
'*'
then
begin
//Lista de tipo "[a]* ... "
Result
:=
tregChars0_
end
else
if
tmp
=
'?'
then
begin
//Lista de tipo "[a]? ... "
Result
:=
tregChars01
end
else
if
tmp
=
'+'
then
begin
//Lista de tipo "[a]+ ... "
Result
:=
tregChars1_
end
;
//no hay otra opción
exit
;
end
;
end
;
str
+=
tmp
;
//agrega caracter
lastAd
:=
tmp
;
//guarda el último caracter agregado
tmp
:=
ExtractCharN
(
exp
);
//siguiente caracter
end
;
//Si llega aquí es porque no encontró cuantificador ni lista (Caso 0)
str
:=
ReplaceEscape
(
str
);
{ if length(str) = 1 then begin //verifica si tiene un caracter
listChars := str; //'['+str+']'
Result := tregChars; //devuelve como lista de un caracter
exit;
end;}
Result
:=
tregString
;
end
;
end
;
function
ExtractRegExpN
(
var
exp
:
string
;
out
RegexTyp
:
tFaRegExpType
):
string
;
{Extrae parte de una expresión regular y la devuelve como cadena . Actualiza el
tipo de expresión obtenida en "RegexTyp".
No Reemplaza las secuencias de excape ni los intervalos, devuelve el texto tal cual}
var
listChars
,
str
:
string
;
exp0
:
String
;
tam
:
Integer
;
begin
exp0
:=
exp
;
//guarda expresión tal cual
RegexTyp
:=
ExtractRegExp
(
exp
,
str
,
listChars
);
tam
:=
length
(
exp0
)
-
length
(
exp
);
//ve diferencia de tamaño
Result
:=
copy
(
exp0
,
1
,
tam
)
end
;
function
ColorFromStr
(
cad
:
string
):
TColor
;
//Convierte una cadena a Color
function
EsHexa
(
txt
:
string
;
out
num
:
integer
):
boolean
;
//Convierte un texto en un número entero. Si es numérico devuelve TRUE
var
i
:
integer
;
begin
Result
:=
true
;
//valor por defecto
num
:=
0
;
//valor por defecto
for
i
:=
1
to
length
(
txt
)
do
begin
if
not
(
txt
[
i
]
in
[
'0'
..
'9'
,
'a'
..
'f'
,
'A'
..
'F'
])
then
exit
(
false
);
//no era
end
;
//todos los dígitos son numéricos
num
:=
StrToInt
(
'$'
+
txt
);
end
;
var
r
,
g
,
b
:
integer
;
begin
if
(
cad
<>
''
)
and
(
cad
[
1
]
=
'#'
)
and
(
length
(
cad
)=
7
)
then
begin
//es código de color. Lo lee de la mejor forma
EsHexa
(
copy
(
cad
,
2
,
2
),
r
);
EsHexa
(
copy
(
cad
,
4
,
2
),
g
);
EsHexa
(
copy
(
cad
,
6
,
2
),
b
);
Result
:=
RGB
(
r
,
g
,
b
);
end
else
begin
//constantes de color
case
UpCase
(
cad
)
of
'WHITE'
:
Result
:=
rgb
($
FF
,$
FF
,$
FF
);
'SILVER'
:
Result
:=
rgb
($
C0
,$
C0
,$
C0
);
'GRAY'
:
Result
:=
rgb
($
80
,$
80
,$
80
);
'BLACK'
:
Result
:=
rgb
($
00
,$
00
,$
00
);
'RED'
:
Result
:=
rgb
($
FF
,$
00
,$
00
);
'MAROON'
:
Result
:=
rgb
($
80
,$
00
,$
00
);
'YELLOW'
:
Result
:=
rgb
($
FF
,$
FF
,$
00
);
'OLIVE'
:
Result
:=
rgb
($
80
,$
80
,$
00
);
'LIME'
:
Result
:=
rgb
($
00
,$
FF
,$
00
);
'GREEN'
:
Result
:=
rgb
($
00
,$
80
,$
00
);
'AQUA'
:
Result
:=
rgb
($
00
,$
FF
,$
FF
);
'TEAL'
:
Result
:=
rgb
($
00
,$
80
,$
80
);
'BLUE'
:
Result
:=
rgb
($
00
,$
00
,$
FF
);
'NAVY'
:
Result
:=
rgb
($
00
,$
00
,$
80
);
'FUCHSIA'
:
Result
:=
rgb
($
FF
,$
00
,$
FF
);
'PURPLE'
:
Result
:=
rgb
($
80
,$
00
,$
80
);
'MAGENTA'
:
Result
:=
rgb
($
FF
,$
00
,$
FF
);
'CYAN'
:
Result
:=
rgb
($
00
,$
FF
,$
FF
);
'BLUE VIOLET'
:
Result
:=
rgb
($
8
A
,$
2
B
,$
E2
);
'GOLD'
:
Result
:=
rgb
($
FF
,$
D7
,$
00
);
'BROWN'
:
Result
:=
rgb
($
A5
,$
2
A
,$
2
A
);
'CORAL'
:
Result
:=
rgb
($
FF
,$
7F
,$
50
);
'VIOLET'
:
Result
:=
rgb
($
EE
,$
82
,$
EE
);
end
;
end
;
end
;
{ tFaTokContent }
procedure
tFaTokContent
.
Clear
;
begin
CaseSensitive
:=
false
;
//por defecto
nInstruc
:=
0
;
setLength
(
Instrucs
,
0
);
end
;
function
tFaTokContent
.
AddItem
(
expTyp
:
tFaRegExpType
;
ifMatch
,
ifFail
:
string
):
integer
;
//Agrega un ítem a la lista Instrucs[]. Devuelve el número de ítems.
//Configura el comportamiento de la instrucción usando "ifMatch".
var
ifMatch0
,
ifFail0
:
string
;
function
extractIns
(
var
txt
:
string
):
string
;
//Extrae una instrucción (identificador)
var
p
:
Integer
;
begin
txt
:=
trim
(
txt
);
if
txt
=
''
then
exit
(
''
);
p
:=
1
;
while
(
p
<=
length
(
txt
))
and
(
txt
[
p
]
in
[
'A'
..
'Z'
])
do
inc
(
p
);
Result
:=
copy
(
txt
,
1
,
p
-
1
);
txt
:=
copyEx
(
txt
,
p
);
// Result := copy(txt,1,p);
// txt := copyEx(txt, p+1);
end
;
function
extractPar
(
var
txt
:
string
;
errMsg
:
string
):
integer
;
//Extrae un valor numérico
var
p
,
p0
:
Integer
;
sign
:
Integer
;
begin
txt
:=
trim
(
txt
);
if
txt
=
''
then
exit
(
0
);
if
txt
[
1
]
=
'('
then
begin
//caso esperado
p
:=
2
;
//explora
if
not
(
txt
[
2
]
in
[
'+'
,
'-'
,
'0'
..
'9'
])
then
//validación
raise
ESynFacilSyn
.
Create
(
errMsg
+
ifFail0
);
sign
:=
1
;
//signo por defecto
if
txt
[
2
]
=
'+'
then
begin
p
:=
3
;
//siguiente caracter
sign
:=
1
;
if
not
(
txt
[
3
]
in
[
'0'
..
'9'
])
then
raise
ESynFacilSyn
.
Create
(
errMsg
+
ifFail0
);
end
;
if
txt
[
2
]
=
'-'
then
begin
p
:=
3
;
//siguiente caracter
sign
:=
-
1
;
if
not
(
txt
[
3
]
in
[
'0'
..
'9'
])
then
raise
ESynFacilSyn
.
Create
(
errMsg
+
ifFail0
);
end
;
//Aquí se sabe que en txt[p], viene un númaro
p0
:=
p
;
//guarda posición de inicio
while
(
p
<=
length
(
txt
))
and
(
txt
[
p
]
in
[
'0'
..
'9'
])
do
inc
(
p
);
Result
:=
StrToInt
(
copy
(
txt
,
p0
,
p
-
p0
))
*
Sign
;
//lee como número
if
txt
[
p
]<>
')'
then
raise
ESynFacilSyn
.
Create
(
errMsg
+
ifFail0
);
inc
(
p
);
txt
:=
copyEx
(
txt
,
p
+
1
);
end
else
begin
raise
ESynFacilSyn
.
Create
(
errMsg
+
ifFail0
);
end
;
end
;
function
HavePar
(
var
txt
:
string
):
boolean
;
//Verifica si la cadena empieza con "("
begin
Result
:=
false
;
txt
:=
trim
(
txt
);
if
txt
=
''
then
exit
;
if
txt
[
1
]
=
'('
then
begin
//caso esperado
Result
:=
true
;
end
;
end
;
var
inst
:
String
;
n
:
Integer
;
begin
ifMatch0
:=
ifMatch
;
//guarda valor original
ifFail0
:=
ifFail
;
//guarda valor original
inc
(
nInstruc
);
n
:=
nInstruc
-
1
;
//último índice
setlength
(
Instrucs
,
nInstruc
);
Instrucs
[
n
].
expTyp
:=
expTyp
;
//tipo
Instrucs
[
n
].
actionMatch
:=
aomNext
;
//valor por defecto
Instrucs
[
n
].
actionFail
:=
aomExit
;
//valor por defecto
Instrucs
[
n
].
destOnMatch
:=
0
;
//valor por defecto
Instrucs
[
n
].
destOnFail
:=
0
;
//valor por defecto
Result
:=
nInstruc
;
//Configura comportamiento
if
ifMatch
<>
''
then
begin
ifMatch
:=
UpCase
(
ifMatch
);
while
ifMatch
<>
''
do
begin
inst
:=
extractIns
(
ifMatch
);
if
inst
=
'NEXT'
then
begin
//se pide avanzar al siguiente
Instrucs
[
n
].
actionMatch
:=
aomNext
;
end
else
if
inst
=
'EXIT'
then
begin
//se pide salir
if
HavePar
(
ifMatch
)
then
begin
//EXIT con parámetro
Instrucs
[
n
].
actionMatch
:=
aomExitpar
;
Instrucs
[
n
].
destOnMatch
:=
n
+
extractPar
(
ifMatch
,
ERR_SYN_PAR_IFMATCH_
);
end
else
begin
//EXIT sin parámetros
Instrucs
[
n
].
actionMatch
:=
aomExit
;
end
;
end
else
if
inst
=
'MOVE'
then
begin
Instrucs
[
n
].
actionMatch
:=
aomMovePar
;
//Mover a una posición
Instrucs
[
n
].
destOnMatch
:=
n
+
extractPar
(
ifMatch
,
ERR_SYN_PAR_IFMATCH_
);
end
else
begin
raise
ESynFacilSyn
.
Create
(
ERR_SYN_PAR_IFMATCH_
+
ifMatch0
);
end
;
ifMatch
:=
Trim
(
ifMatch
);
if
(
ifMatch
<>
''
)
and
(
ifMatch
[
1
]
=
';'
)
then
//quita delimitador
ifMatch
:=
copyEx
(
ifMatch
,
2
);
end
;
end
;
if
ifFail
<>
''
then
begin
ifFail
:=
UpCase
(
ifFail
);
while
ifFail
<>
''
do
begin
inst
:=
extractIns
(
ifFail
);
if
inst
=
'NEXT'
then
begin
//se pide avanzar al siguiente
Instrucs
[
n
].
actionFail
:=
aomNext
;
end
else
if
inst
=
'EXIT'
then
begin
//se pide salir
if
HavePar
(
ifFail
)
then
begin
//EXIT con parámetro
Instrucs
[
n
].
actionFail
:=
aomExitpar
;
Instrucs
[
n
].
destOnFail
:=
n
+
extractPar
(
ifFail
,
ERR_SYN_PAR_IFFAIL_
);
end
else
begin
//EXIT sin parámetros
Instrucs
[
n
].
actionFail
:=
aomExit
;
end
;
end
else
if
inst
=
'MOVE'
then
begin
Instrucs
[
n
].
actionFail
:=
aomMovePar
;
//Mover a una posición
Instrucs
[
n
].
destOnFail
:=
n
+
extractPar
(
ifFail
,
ERR_SYN_PAR_IFFAIL_
);
end
else
begin
raise
ESynFacilSyn
.
Create
(
ERR_SYN_PAR_IFFAIL_
+
ifFail0
);
end
;
ifFail
:=
Trim
(
ifFail
);
if
(
ifFail
<>
''
)
and
(
ifFail
[
1
]
=
';'
)
then
//quita delimitador
ifFail
:=
copyEx
(
ifFail
,
2
);
end
;
end
;
end
;
procedure
tFaTokContent
.
AddOneInstruct
(
var
exp
:
string
;
ifTrue
:
string
;
ifFalse
:
string
;
atMatch
:
integer
=-
1
;
atFail
:
integer
=-
1
);
{Agrega una y solo instrucción al token por contenido. Si encuentra más de una
instrucción, genera una excepción. Si se pone ifTrue en blnnco, se asumirá 'next',
si se pone "ifFalse" en blanco, se se asumirá 'exit'.
Este es el punto de entrada único para agregar una instrucción de Regex a
tFaTokContent}
var
list
:
String
;
str
:
string
;
n
:
Integer
;
c
:
Char
;
expr
:
string
;
t
:
tFaRegExpType
;
begin
if
exp
=
''
then
exit
;
//analiza
expr
:=
exp
;
//guarda, porque se va a trozar
t
:=
ExtractRegExp
(
exp
,
str
,
list
);
case
t
of
tregChars
,
//Es de tipo lista de caracteres [...]
tregChars01
,
//Es de tipo lista de caracteres [...]?
tregChars0_
,
//Es de tipo lista de caracteres [...]*
tregChars1_
:
//Es de tipo lista de caracteres [...]+
begin
n
:=
AddItem
(
t
,
ifTrue
,
ifFalse
)-
1
;
//agrega
if
atMatch
=-
1
then
Instrucs
[
n
].
aMatch
:=
TokTyp
//toma atributo de la clase
else
Instrucs
[
n
].
aMatch
:=
atMatch
;
if
atFail
=-
1
then
Instrucs
[
n
].
aFail
:=
TokTyp
//toma atributo de la clase
else
Instrucs
[
n
].
aFail
:=
atFail
;
//Configura caracteres de contenido
for
c
:=
#
0
to
#
255
do
Instrucs
[
n
].
Chars
[
c
]
:=
False
;
for
c
in
list
do
Instrucs
[
n
].
Chars
[
c
]
:=
True
;
end
;
tregString
:
begin
//Es de tipo texto literal
n
:=
AddItem
(
t
,
ifTrue
,
ifFalse
)-
1
;
//agrega
if
atMatch
=-
1
then
Instrucs
[
n
].
aMatch
:=
TokTyp
//toma atributo de la clase
else
Instrucs
[
n
].
aMatch
:=
atMatch
;
if
atFail
=-
1
then
Instrucs
[
n
].
aFail
:=
TokTyp
//toma atributo de la clase
else
Instrucs
[
n
].
aFail
:=
atFail
;
//configura cadena
if
CaseSensitive
then
Instrucs
[
n
].
Text
:=
str
else
Instrucs
[
n
].
Text
:=
UpCase
(
str
);
//ignora caja
end
;
else
raise
ESynFacilSyn
.
Create
(
ERR_UNSUPPOR_EXP_
+
expr
);
end
;
end
;
procedure
tFaTokContent
.
AddInstruct
(
exp
:
string
;
ifTrue
:
string
=
''
;
ifFalse
:
string
=
''
;
atMatch
:
integer
=-
1
;
atFail
:
integer
=-
1
);
//Agrega una instrucción para el procesamiento del token por contenido.
//Solo se debe indicar una instrucción, de otra forma se generará un error.
var
expr
:
String
;
begin
expr
:=
exp
;
//guarda, porque se va a trozar
AddOneInstruct
(
exp
,
ifTrue
,
ifFalse
,
atMatch
,
atFail
);
//si hay error genera excepción
//Si llegó aquí es porque se obtuvo una expresión válida, pero la
//expresión continua.
if
exp
<>
''
then
begin
raise
ESynFacilSyn
.
Create
(
ERR_UNSUPPOR_EXP_
+
expr
);
end
;
end
;
procedure
tFaTokContent
.
AddRegEx
(
exp
:
string
;
Complete
:
boolean
=
false
);
{Agrega una expresión regular (un conjunto de instrucciones sin opciones de control), al
token por contenido. Las expresiones regulares deben ser solo las soportadas.
Ejemplos son: "[0..9]*[\.][0..9]", "[A..Za..z]*"
Las expresiones se evalúan parte por parte. Si un token no coincide completamente con la
expresión regular, se considera al token, solamente hasta el punto en que coincide.
Si se produce algún error se generará una excepción.}
var
dToStart
:
Integer
;
begin
if
Complete
then
begin
//Cuando no coincide completamente, retrocede hasta el demimitador incial
dToStart
:=
0
;
//distamcia al inicio
while
exp
<>
''
do
begin
AddOneInstruct
(
exp
,
''
,
'exit(-'
+
IntToStr
(
dToStart
)
+
')'
);
Inc
(
dToStart
);
end
;
end
else
begin
//La coinicidencia puede ser parcial
while
exp
<>
''
do
begin
AddOneInstruct
(
exp
,
''
,
''
);
//en principio, siempre debe coger una expresión
end
;
end
;
end
;
{ TSynFacilSynBase }
function
TSynFacilSynBase
.
GetSampleSource
:
String
;
begin
Result
:=
fSampleSource
;
end
;
//funciones básicas
function
TSynFacilSynBase
.
BuscTokEspec
(
var
mat
:
TArrayTokSpec
;
cad
:
string
;
out
n
:
integer
;
TokPos
:
integer
=
0
):
boolean
;
//Busca una cadena en una matriz TArrayTokSpec. Si la ubica devuelve el índice en "n".
var
i
:
integer
;
begin
Result
:=
false
;
if
TokPos
=
0
then
begin
//búsqueda normal
for
i
:=
0
to
High
(
mat
)
do
begin
if
mat
[
i
].
txt
=
cad
then
begin
n
:=
i
;
exit
(
true
);
end
;
end
;
end
else
begin
//búsqueda con TokPos
for
i
:=
0
to
High
(
mat
)
do
begin
if
(
mat
[
i
].
txt
=
cad
)
and
(
TokPos
=
mat
[
i
].
TokPos
)
then
begin
n
:=
i
;
exit
(
true
);
end
;
end
;
end
;
end
;
function
TSynFacilSynBase
.
ToListRegex
(
list
:
TFaXMLatrib
):
string
;
//Reemplaza el contenido de una lista en foramto XML (p.ej. "A..Z") al formato de
//listas de expresiones regulares; "[A-Z]"
//Los caracteres "..", cambian a "-" y el caracter "-", cambia a "\-"
var
tmp
:
String
;
begin
tmp
:=
StringReplace
(
list
.
val
,
'-'
,
'\-'
,[
rfReplaceAll
]);
tmp
:=
StringReplace
(
tmp
,
'..'
,
'-'
,[
rfReplaceAll
]);
Result
:=
'['
+
tmp
+
']'
;
//completa con llaves
end
;
function
TSynFacilSynBase
.
dStartRegex
(
tStart
,
tCharsStart
:
TFaXMLatrib
):
string
;
//Lee los parámetros XML "Start" y "CharsStart"; y extrae el delimitador inicial
//a usar en formato de Expresión Regular.
begin
//validaciones
if
tStart
.
hay
and
tCharsStart
.
hay
then
begin
//No es un caso válido que se den los dos parámetros
raise
ESynFacilSyn
.
Create
(
ERR_NOT_USE_START
);
end
;
if
not
tStart
.
hay
and
not
tCharsStart
.
hay
then
begin
//Tampoco es un caso válido que no se de ninguno.
raise
ESynFacilSyn
.
Create
(
ERR_PAR_START_CHARS
);
end
;
//Hay uno u otro parámetro definido
if
tStart
.
hay
then
begin
Result
:=
EscapeText
(
tStart
.
val
);
//protege a los caracteres especiales
end
else
if
tCharsStart
.
hay
then
begin
Result
:=
ToListRegex
(
tCharsStart
);
//convierte a expresión regular como [a..z]
end
;
end
;
procedure
TSynFacilSynBase
.
VerifDelim
(
delim
:
string
);
//Verifica la validez de un delimitador para un token delimitado.
//Si hay error genera una excepción.
var
c
:
char
;
tmp
:
string
;
begin
//verifica contenido
if
delim
=
''
then
raise
ESynFacilSyn
.
Create
(
ERR_TOK_DELIM_NULL
);
//verifica si inicia con caracter de identificador.
if
delim
[
1
]
in
charsIniIden
then
begin
//Empieza como identificador. Hay que verificar que todos los demás caracteres
//sean también de identificador, de otra forma no se podrá reconocer el token.
tmp
:=
copy
(
delim
,
2
,
length
(
delim
)
);
for
c
in
tmp
do
if
not
CharsIdentif
[
c
]
then
begin
raise
ESynFacilSyn
.
Create
(
format
(
ERR_TOK_DEL_IDE_ERR
,[
delim
]));
end
;
end
;
end
;
procedure
TSynFacilSynBase
.
ValidateParamStart
(
Start
:
string
;
var
ListElem
:
TStringList
);
{Valida si la expresión del parámetro es de tipo <literal> o [<lista de cars>], de
otra forma generará una excepción.
Si es de tipo <literal>, valida que sea un delimitador válido.
Devuelve en "ListElem" una lista con con los caracteres (En el caso de [<lista de cars>])
o un solo elemento con una cadena (En el caso de <literal>). Por ejemplo:
Si Start = 'cadena', entonces se tendrá: ListElem = [ 'cadena' ]
Si Start = '[1..5]', entonces se tendrá: ListElem = ['0','1','2','3','4','5']
Si encuentra error, genera excepción.}
var
t
:
tFaRegExpType
;
listChars
:
string
;
str
:
string
;
c
:
Char
;
begin
if
Start
=
''
then
raise
ESynFacilSyn
.
Create
(
ERR_START_NO_EMPTY
);
t
:=
ExtractRegExp
(
Start
,
str
,
listChars
);
ListElem
.
Clear
;
if
Start
<>
''
then
//la expresión es más compleja
raise
ESynFacilSyn
.
Create
(
ERR_EXP_MUST_BE_BR
);
if
t
=
tregChars
then
begin
for
c
in
listChars
do
begin
ListElem
.
Add
(
c
);
end
;
end
else
if
t
=
tregString
then
begin
//lista simple o literal cadena
VerifDelim
(
str
);
//valida reglas
lisTmp
.
Add
(
str
);
end
else
//expresión de otro tipo
raise
ESynFacilSyn
.
Create
(
ERR_EXP_MUST_BE_BR
);
end
;
procedure
TSynFacilSynBase
.
ValidAsigDelim
(
delAct
,
delNue
:
TFaTypeDelim
;
delim
:
string
);
//Verifica si la asignación de delimitadores es válida. Si no lo es devuelve error.
begin
if
delAct
=
tdNull
then
exit
;
//No estaba inicializado, es totalente factible
//valida asignación de delimitador
if
(
delAct
in
[
tdUniLin
,
tdMulLin
])
and
(
delNue
in
[
tdUniLin
,
tdMulLin
])
then
begin
raise
ESynFacilSyn
.
Create
(
Format
(
ERR_IDEN_ALREA_DEL
,[
delim
]));
end
;
end
;
function
TSynFacilSynBase
.
KeyComp
(
var
r
:
TTokSpec
):
Boolean
;
inline
;
{Compara rápidamente una cadena con el token actual, apuntado por "fToIden".
El tamaño del token debe estar en "fStringLen"}
var
i
:
Integer
;
Temp
:
PChar
;
begin
Temp
:=
fToIdent
;
if
Length
(
r
.
txt
)
=
fStringLen
then
begin
//primera comparación
if
(
r
.
TokPos
<>
0
)
and
(
r
.
TokPos
<>
posTok
)
then
exit
(
false
);
//no coincide
Result
:=
True
;
//valor por defecto
for
i
:=
1
to
fStringLen
do
begin
if
TabMayusc
[
Temp
^]
<>
r
.
txt
[
i
]
then
exit
(
false
);
inc
(
Temp
);
end
;
end
else
//definitívamente es diferente
Result
:=
False
;
end
;
function
TSynFacilSynBase
.
CreaBuscTokEspec
(
var
mat
:
TArrayTokSpec
;
cad
:
string
;
out
i
:
integer
;
TokPos
:
integer
=
0
):
boolean
;
{Busca o crea el token especial indicado en "cad". Si ya existe, devuelve TRUE y
actualiza "i" con su posición. Si no existe. Crea el token especial y devuelve la
referencia en "i". Se le debe indicar la tabla a buscar en "mat"}
var
r
:
TTokSpec
;
begin
if
not
CaseSensitive
then
cad
:=
UpCase
(
cad
);
//cambia caja si es necesario
if
BuscTokEspec
(
mat
,
cad
,
i
,
TokPos
)
then
exit
(
true
);
//ya existe, devuelve en "i"
//no existe, hay que crearlo. Aquí se definen las propiedades por defecto
r
.
txt
:=
cad
;
//se asigna el nombre
r
.
TokPos
:=
TokPos
;
//se asigna ordinal del token
r
.
tTok
:=-
1
;
//sin tipo asignado
r
.
typDel
:=
tdNull
;
//no es delimitador
r
.
dEnd
:=
''
;
//sin delimitador final
r
.
pRange
:=
nil
;
//sin función de rango
r
.
folTok
:=
false
;
//sin plegado de token
r
.
chrEsc
:=
#
0
;
//sin caracter de escape
r
.
openBlk
:=
false
;
//sin plegado de bloque
r
.
closeBlk
:=
false
;
//sin plegado de bloque
r
.
OpenSec
:=
false
;
//no es sección de bloque
r
.
firstSec
:=
nil
;
//inicialmente no abre ningún bloque
i
:=
High
(
mat
)+
1
;
//siguiente posición
SetLength
(
mat
,
i
+
1
);
//hace espacio
mat
[
i
]
:=
r
;
//copia todo el registro
//sale indicando que se ha creado
Result
:=
false
;
end
;
//procesamiento de XML
function
TSynFacilSynBase
.
ReadXMLParam
(
n
:
TDOMNode
;
nomb
:
string
):
TFaXMLatrib
;
//Explora un nodo para ver si existe un atributo, y leerlo. Ignora la caja.
var
i
:
integer
;
cad
:
string
;
atri
:
TDOMNode
;
function
EsEntero
(
txt
:
string
;
out
num
:
integer
):
boolean
;
//convierte un texto en un número entero. Si es numérico devuelve TRUE
var
i
:
integer
;
begin
Result
:=
true
;
//valor por defecto
num
:=
0
;
//valor por defecto
for
i
:=
1
to
length
(
txt
)
do
begin
if
not
(
txt
[
i
]
in
[
'0'
..
'9'
])
then
exit
(
false
);
//no era
end
;
//todos los dígitos son numéricos
num
:=
StrToInt
(
txt
);
end
;
begin
Result
.
hay
:=
false
;
//Se asume que no existe
Result
.
val
:=
''
;
//si no encuentra devuelve vacío
Result
.
bol
:=
false
;
//si no encuentra devuelve Falso
Result
.
n
:=
0
;
//si no encuentra devuelve 0
for
i
:=
0
to
n
.
Attributes
.
Length
-
1
do
begin
atri
:=
n
.
Attributes
.
Item
[
i
];
if
UpCase
(
AnsiString
(
atri
.
NodeName
))
=
UpCase
(
nomb
)
then
begin
Result
.
hay
:=
true
;
//marca bandera
Result
.
val
:=
AnsiString
(
atri
.
NodeValue
);
//lee valor
Result
.
bol
:=
UpCase
(
atri
.
NodeValue
)
=
'TRUE'
;
//lee valor booleano
cad
:=
trim
(
AnsiString
(
atri
.
NodeValue
));
//valor sin espacios
//lee número
if
(
cad
<>
''
)
and
(
cad
[
1
]
in
[
'0'
..
'9'
])
then
//puede ser número
EsEntero
(
cad
,
Result
.
n
);
//convierte
//Lee color
Result
.
col
:=
ColorFromStr
(
cad
);
end
;
end
;
end
;
procedure
TSynFacilSynBase
.
CheckXMLParams
(
n
:
TDOMNode
;
listAtrib
:
string
);
//Valida la existencia completa de los nodos indicados. Si encuentra alguno más
//genera excepción. Los nodos deben estar separados por espacios.
var
i
,
j
:
integer
;
atri
:
TDOMNode
;
nombre
,
tmp
:
string
;
hay
:
boolean
;
begin
//Carga lista de atributos
lisTmp
.
Clear
;
//usa lista temproal
lisTmp
.
Delimiter
:=
' '
;
//StringReplace(listSym, #13#10, ' ',[rfReplaceAll]);
lisTmp
.
DelimitedText
:=
listAtrib
;
//Realiza la verificación
for
i
:=
0
to
n
.
Attributes
.
Length
-
1
do
begin
atri
:=
n
.
Attributes
.
Item
[
i
];
nombre
:=
UpCase
(
AnsiString
(
atri
.
NodeName
));
//verifica existencia
hay
:=
false
;
for
j
:=
0
to
lisTmp
.
Count
-
1
do
begin
tmp
:=
trim
(
lisTmp
[
j
]);
if
nombre
=
UpCase
(
tmp
)
then
begin
hay
:=
true
;
break
;
end
;
end
;
//verifica si no existe
if
not
hay
then
begin
//Este atributo está demás
raise
ESynFacilSyn
.
Create
(
format
(
ERR_INVAL_ATTR_LAB
,[
atri
.
NodeName
,
n
.
NodeName
]));
end
;
end
;
end
;
////Métodos para tokens por contenido
procedure
TSynFacilSynBase
.
metTokCont
(
const
tc
:
tFaTokContent
);
//inline;
//Procesa tokens por contenido
var
n
,
i
:
Integer
;
posFin0
:
Integer
;
nf
:
Integer
;
tam1
:
Integer
;
begin
fTokenID
:=
tc
.
TokTyp
;
//No debería ser necesario ya que se asignará después.
inc
(
posFin
);
//para pasar al siguiente caracter
n
:=
0
;
while
n
<
tc
.
nInstruc
do
begin
tc
.
Instrucs
[
n
].
posFin
:=
posFin
;
//guarda posición al iniciar
case
tc
.
Instrucs
[
n
].
expTyp
of
tregString
:
begin
//texo literal
//Rutina de comparación de cadenas
posFin0
:=
posFin
;
//para poder restaurar
i
:=
1
;
tam1
:=
length
(
tc
.
Instrucs
[
n
].
Text
)+
1
;
//tamaño +1
if
CaseSensitive
then
begin
//sensible a caja
while
(
i
<
tam1
)
and
(
tc
.
Instrucs
[
n
].
Text
[
i
]
=
fLine
[
posFin
])
do
begin
inc
(
posFin
);
inc
(
i
);
end
;
end
else
begin
//Ignora mayúcula/minúscula
while
(
i
<
tam1
)
and
(
tc
.
Instrucs
[
n
].
Text
[
i
]
=
TabMayusc
[
fLine
[
posFin
]])
do
begin
inc
(
posFin
);
inc
(
i
);
end
;
end
;
//verifica la coincidencia
if
i
=
tam1
then
begin
//cumple
fTokenID
:=
tc
.
Instrucs
[
n
].
aMatch
;
//pone atributo
case
tc
.
Instrucs
[
n
].
actionMatch
of
aomNext
:;
//no hace nada, pasa al siguiente elemento
aomExit
:
break
;
//simplemente sale
aomExitpar
:
begin
//sale con parámetro
nf
:=
tc
.
Instrucs
[
n
].
destOnMatch
;
//lee posición final
posFin
:=
tc
.
Instrucs
[
nf
].
posFin
;
//Debe moverse antes de salir
break
;
end
;
aomMovePar
:
begin
//se mueve a una posición
n
:=
tc
.
Instrucs
[
n
].
destOnMatch
;
//ubica posición
continue
;
end
;
end
;
end
else
begin
//no cumple
fTokenID
:=
tc
.
Instrucs
[
n
].
aFail
;
//pone atributo
posFin
:=
posFin0
;
//restaura posición
case
tc
.
Instrucs
[
n
].
actionFail
of
aomNext
:;
//no hace nada, pasa al siguiente elemento
aomExit
:
break
;
//simplemente sale
aomExitpar
:
begin
//sale con parámetro
nf
:=
tc
.
Instrucs
[
n
].
destOnFail
;
//lee posición final
posFin
:=
tc
.
Instrucs
[
nf
].
posFin
;
//Debe moverse antes de salir
break
;
end
;
aomMovePar
:
begin
//se mueve a una posición
n
:=
tc
.
Instrucs
[
n
].
destOnFail
;
//ubica posición
continue
;
end
;
end
;
end
;
end
;
tregChars
:
begin
//conjunto de caracteres: [ ... ]
//debe existir solo una vez
if
tc
.
Instrucs
[
n
].
Chars
[
fLine
[
posFin
]]
then
begin
//cumple el caracter
fTokenID
:=
tc
.
Instrucs
[
n
].
aMatch
;
//pone atributo
inc
(
posFin
);
//pasa a la siguiente instrucción
//Cumple el caracter
case
tc
.
Instrucs
[
n
].
actionMatch
of
aomNext
:;
//no hace nada, pasa al siguiente elemento
aomExit
:
break
;
//simplemente sale
aomExitpar
:
begin
//sale con parámetro
nf
:=
tc
.
Instrucs
[
n
].
destOnMatch
;
//lee posición final
posFin
:=
tc
.
Instrucs
[
nf
].
posFin
;
//Debe moverse antes de salir
break
;
end
;
aomMovePar
:
begin
//se mueve a una posición
n
:=
tc
.
Instrucs
[
n
].
destOnMatch
;
//ubica posición
continue
;
end
;
end
;
end
else
begin
//no se encuentra ningún caracter de la lista
fTokenID
:=
tc
.
Instrucs
[
n
].
aFail
;
//pone atributo
case
tc
.
Instrucs
[
n
].
actionFail
of
aomNext
:;
//no hace nada, pasa al siguiente elemento
aomExit
:
break
;
//simplemente sale
aomExitpar
:
begin
//sale con parámetro
nf
:=
tc
.
Instrucs
[
n
].
destOnFail
;
//lee posición final
posFin
:=
tc
.
Instrucs
[
nf
].
posFin
;
//Debe moverse antes de salir
break
;
end
;
aomMovePar
:
begin
//se mueve a una posición
n
:=
tc
.
Instrucs
[
n
].
destOnFail
;
//ubica posición
continue
;
end
;
end
;
end
;
end
;
tregChars01
:
begin
//conjunto de caracteres: [ ... ]?
//debe existir cero o una vez
if
tc
.
Instrucs
[
n
].
Chars
[
fLine
[
posFin
]]
then
begin
inc
(
posFin
);
//pasa a la siguiente instrucción
end
;
//siempre cumplirá este tipo, no hay nada que verificar
fTokenID
:=
tc
.
Instrucs
[
n
].
aMatch
;
//pone atributo
case
tc
.
Instrucs
[
n
].
actionMatch
of
aomNext
:;
//no hace nada, pasa al siguiente elemento
aomExit
:
break
;
//simplemente sale
aomExitpar
:
begin
//sale con parámetro
nf
:=
tc
.
Instrucs
[
n
].
destOnMatch
;
//lee posición final
posFin
:=
tc
.
Instrucs
[
nf
].
posFin
;
//Debe moverse antes de salir
break
;
end
;
aomMovePar
:
begin
//se mueve a una posición
n
:=
tc
.
Instrucs
[
n
].
destOnMatch
;
//ubica posición
continue
;
end
;
end
;
end
;
tregChars0_
:
begin
//conjunto de caracteres: [ ... ]*
//debe exitir 0 o más veces
while
tc
.
Instrucs
[
n
].
Chars
[
fLine
[
posFin
]]
do
begin
inc
(
posFin
);
end
;
//siempre cumplirá este tipo, no hay nada que verificar
fTokenID
:=
tc
.
Instrucs
[
n
].
aMatch
;
//pone atributo
//¿No debería haber código aquí también?
end
;
tregChars1_
:
begin
//conjunto de caracteres: [ ... ]+
//debe existir una o más veces
posFin0
:=
posFin
;
//para poder comparar
while
tc
.
Instrucs
[
n
].
Chars
[
fLine
[
posFin
]]
do
begin
inc
(
posFin
);
end
;
if
posFin
>
posFin0
then
begin
//Cumple el caracter
fTokenID
:=
tc
.
Instrucs
[
n
].
aMatch
;
//pone atributo
case
tc
.
Instrucs
[
n
].
actionMatch
of
aomNext
:;
//no hace nada, pasa al siguiente elemento
aomExit
:
break
;
//simplemente sale
aomExitpar
:
begin
//sale con parámetro
nf
:=
tc
.
Instrucs
[
n
].
destOnMatch
;
//lee posición final
posFin
:=
tc
.
Instrucs
[
nf
].
posFin
;
//Debe moverse antes de salir
break
;
end
;
aomMovePar
:
begin
//se mueve a una posición
n
:=
tc
.
Instrucs
[
n
].
destOnMatch
;
//ubica posición
continue
;
end
;
end
;
end
else
begin
//No cumple
fTokenID
:=
tc
.
Instrucs
[
n
].
aFail
;
//pone atributo
case
tc
.
Instrucs
[
n
].
actionFail
of
aomNext
:;
//no hace nada, pasa al siguiente elemento
aomExit
:
break
;
//simplemente sale
aomExitpar
:
begin
//sale con parámetro
nf
:=
tc
.
Instrucs
[
n
].
destOnFail
;
//lee posición final
posFin
:=
tc
.
Instrucs
[
nf
].
posFin
;
//Debe moverse antes de salir
break
;
end
;
aomMovePar
:
begin
//se mueve a una posición
n
:=
tc
.
Instrucs
[
n
].
destOnFail
;
//ubica posición
continue
;
end
;
end
;
end
;
end
;
end
;
inc
(
n
);
end
;
end
;
procedure
TSynFacilSynBase
.
metTokCont1
;
//Procesa tokens por contenido 1
begin
metTokCont
(
tc1
);
end
;
procedure
TSynFacilSynBase
.
metTokCont2
;
//Procesa tokens por contenido 2
begin
metTokCont
(
tc2
);
end
;
procedure
TSynFacilSynBase
.
metTokCont3
;
//Procesa tokens por contenido 3
begin
metTokCont
(
tc3
);
end
;
procedure
TSynFacilSynBase
.
metTokCont4
;
//Procesa tokens por contenido 3
begin
metTokCont
(
tc4
);
end
;
//Procesamiento de otros elementos
procedure
TSynFacilSynBase
.
metIdent
;
//Procesa el identificador actual
begin
inc
(
posFin
);
{debe incrementarse, para pasar a comparar los caracteres siguientes,
o de otra forma puede quedarse en un lazo infinito}
while
CharsIdentif
[
fLine
[
posFin
]]
do
inc
(
posFin
);
fTokenID
:=
tnIdentif
;
//identificador común
end
;
procedure
TSynFacilSynBase
.
metIdentUTF8
;
//Procesa el identificador actual. considerando que empieza con un caracter UTF8 (dos bytes)
begin
inc
(
posFin
);
{es UTF8, solo filtra por el primer caracter (se asume que el segundo
es siempre válido}
inc
(
posFin
);
{debe incrementarse, para pasar a comparar los caracteres siguientes,
o de otra forma puede quedarse en un lazo infinito}
while
CharsIdentif
[
fLine
[
posFin
]]
do
inc
(
posFin
);
fTokenID
:=
tnIdentif
;
//identificador común
end
;
procedure
TSynFacilSynBase
.
metNull
;
//Procesa la ocurrencia del cacracter #0
begin
fTokenID
:=
tnEol
;
//Solo necesita esto para indicar que se llegó al final de la línae
end
;
procedure
TSynFacilSynBase
.
metSpace
;
//Procesa caracter que es inicio de espacio
begin
fTokenID
:=
tnSpace
;
repeat
//captura todos los que sean espacios
Inc
(
posFin
);
until
(
fLine
[
posFin
]
>
#
32
)
or
(
posFin
=
tamLin
);
end
;
procedure
TSynFacilSynBase
.
metSymbol
;
begin
inc
(
posFin
);
while
(
fProcTable
[
fLine
[
posFin
]]
=
@
metSymbol
)
do
inc
(
posFin
);
fTokenID
:=
tnSymbol
;
end
;
//Funciones públicas
procedure
TSynFacilSynBase
.
DefTokIdentif
(
dStart
,
Content
:
string
);
{Define token para identificadores. Los parámetros deben ser intervalos.
El parámetro "dStart" deben ser de la forma: "[A..Za..z]"
El parámetro "charsCont" deben ser de la forma: "[A..Za..z]*"
Si los parámetros no cumplen con el formato se generará una excepción.
Se debe haber limpiado previamente con "ClearMethodTables"}
var
c
:
char
;
t
:
tFaRegExpType
;
listChars
:
string
;
str
:
string
;
begin
/////// Configura caracteres de inicio
if
dStart
=
''
then
exit
;
//protección
t
:=
ExtractRegExp
(
dStart
,
str
,
listChars
);
if
(
t
<>
tregChars
)
or
(
dStart
<>
''
)
then
//solo se permite el formato [ ... ]
raise
ESynFacilSyn
.
Create
(
ERR_BAD_PAR_STR_IDEN
);
//Agrega evento manejador en caracteres iniciales
charsIniIden
:=
[];
//inicia
for
c
in
listChars
do
begin
//permite cualquier caracter inicial
if
c
<#
128
then
begin
//caracter normal
fProcTable
[
c
]
:=
@
metIdent
;
charsIniIden
+=
[
c
];
//agrega
end
else
begin
//caracter UTF-8
fProcTable
[
c
]
:=
@
metIdentUTF8
;
charsIniIden
+=
[
c
];
//agrega
end
;
end
;
/////// Configura caracteres de contenido
t
:=
ExtractRegExp
(
Content
,
str
,
listChars
);
if
(
t
<>
tregChars0_
)
or
(
Content
<>
''
)
then
//solo se permite el formato [ ... ]*
raise
ESynFacilSyn
.
Create
(
ERR_BAD_PAR_CON_IDEN
);
//limpia matriz
for
c
:=
#
0
to
#
255
do
begin
CharsIdentif
[
c
]
:=
False
;
//aprovecha para crear la tabla de mayúsculas para comparaciones
if
CaseSensitive
then
TabMayusc
[
c
]
:=
c
else
begin
//pasamos todo a mayúscula
TabMayusc
[
c
]
:=
UpCase
(
c
);
end
;
end
;
//marca las posiciones apropiadas
for
c
in
listChars
do
CharsIdentif
[
c
]
:=
True
;
end
;
//Manejo de atributos
function
TSynFacilSynBase
.
NewTokAttrib
(
TypeName
:
string
;
out
TokID
:
integer
):
TSynHighlighterAttributes
;
{Crea un nuevo atributo y lo agrega al resaltador. Este debe ser el único punto de
entrada, para crear atributos en SynFacilSyn. En tokID, se devuelve el ID del nuevo tipo.
No hay funciones para eliminar atributs creados.}
var
n
:
Integer
;
begin
Result
:=
TSynHighlighterAttributes
.
Create
(
TypeName
);
n
:=
High
(
Attrib
)+
1
;
//tamaño
setlength
(
Attrib
,
n
+
1
);
//incrementa tamaño
Attrib
[
n
]
:=
Result
;
//guarda la referencia
tokID
:=
n
;
//devuelve ID
AddAttribute
(
Result
);
//lo registra en el resaltador
end
;
function
TSynFacilSynBase
.
NewTokType
(
TypeName
:
string
;
out
TokAttrib
:
TSynHighlighterAttributes
):
integer
;
{Crea un nuevo tipo de token, y devuelve la referencia al atributo en "TokAttrib".}
begin
TokAttrib
:=
NewTokAttrib
(
TypeName
,
Result
);
end
;
function
TSynFacilSynBase
.
NewTokType
(
TypeName
:
string
):
integer
;
{Versión simplificada de NewTokType, que devuelve directamente el ID del token}
begin
NewTokAttrib
(
TypeName
,
Result
);
end
;
procedure
TSynFacilSynBase
.
CreateAttributes
;
//CRea los atributos por defecto
begin
//Elimina todos los atributos creados, los fijos y los del usuario.
FreeHighlighterAttributes
;
setlength
(
Attrib
,
0
);
//limpia
{ Crea los atributos que siempre existirán. }
tkEol
:=
NewTokAttrib
(
'Eol'
,
tnEol
);
//atributo de nulos
tkSymbol
:=
NewTokAttrib
(
'Symbol'
,
tnSymbol
);
//atributo de símbolos
tkSpace
:=
NewTokAttrib
(
'Space'
,
tnSpace
);
//atributo de espacios.
tkIdentif
:=
NewTokAttrib
(
'Identifier'
,
tnIdentif
);
//Atributo para identificadores.
tkNumber
:=
NewTokAttrib
(
'Number'
,
tnNumber
);
//atributo de números
tkNumber
.
Foreground
:=
clFuchsia
;
tkKeyword
:=
NewTokAttrib
(
'Keyword'
,
tnKeyword
);
//atribuuto de palabras claves
tkKeyword
.
Foreground
:=
clGreen
;
tkString
:=
NewTokAttrib
(
'String'
,
tnString
);
//atributo de cadenas
tkString
.
Foreground
:=
clBlue
;
tkComment
:=
NewTokAttrib
(
'Comment'
,
tnComment
);
//atributo de comentarios
tkComment
.
Style
:=
[
fsItalic
];
tkComment
.
Foreground
:=
clGray
;
end
;
function
TSynFacilSynBase
.
GetAttribByName
(
txt
:
string
):
TSynHighlighterAttributes
;
{Devuelve la referencia de un atributo, recibiendo su nombre. Si no lo encuentra
devuelve NIL.}
var
i
:
Integer
;
begin
txt
:=
UpCase
(
txt
);
//ignora la caja
//También lo puede buscar en Attrib[]
for
i
:=
0
to
AttrCount
-
1
do
begin
if
Upcase
(
Attribute
[
i
].
Name
)
=
txt
then
begin
Result
:=
Attribute
[
i
];
//devuelve índice
exit
;
end
;
end
;
//No se encontró
exit
(
nil
);
end
;
function
TSynFacilSynBase
.
GetAttribIDByName
(
txt
:
string
):
integer
;
{Devuelve el identificador de un atributo, recibiendo su nombre. Si no lo encuentra
devuelve -1.}
var
i
:
Integer
;
begin
txt
:=
UpCase
(
txt
);
//ignora la caja
//Se tiene que buscar en Attrib[], proque allí están con los índices cprrectos
for
i
:=
0
to
AttrCount
-
1
do
begin
if
Upcase
(
Attrib
[
i
].
Name
)
=
txt
then
begin
Result
:=
i
;
//devuelve índice
exit
;
end
;
end
;
//No se encontró
exit
(-
1
);
end
;
function
TSynFacilSynBase
.
IsAttributeName
(
txt
:
string
):
boolean
;
//Verifica si una cadena corresponde al nombre de un atributo.
begin
//primera comparación
if
GetAttribByName
(
txt
)
<>
nil
then
exit
(
true
);
//puede que haya sido "NULL"
if
UpCase
(
txt
)
=
'NULL'
then
exit
(
true
);
//definitivamente no es
Result
:=
False
;
end
;
function
TSynFacilSynBase
.
ProcXMLattribute
(
nodo
:
TDOMNode
):
boolean
;
//Verifica si el nodo tiene la etiqueta <ATTRIBUTTE>. De ser así, devuelve TRUE y lo
//procesa. Si encuentra error, genera una excepción.
var
tName
:
TFaXMLatrib
;
tBackCol
:
TFaXMLatrib
;
tForeCol
:
TFaXMLatrib
;
tFrameCol
:
TFaXMLatrib
;
tFrameEdg
:
TFaXMLatrib
;
tFrameSty
:
TFaXMLatrib
;
tStyBold
:
TFaXMLatrib
;
tStyItal
:
TFaXMLatrib
;
tStyUnder
:
TFaXMLatrib
;
tStyStrike
:
TFaXMLatrib
;
tStyle
:
TFaXMLatrib
;
tipTok
:
TSynHighlighterAttributes
;
Atrib
:
TSynHighlighterAttributes
;
tokId
:
integer
;
begin
if
UpCase
(
nodo
.
NodeName
)
<>
'ATTRIBUTE'
then
exit
(
false
);
Result
:=
true
;
//encontró
////////// Lee parámetros //////////
tName
:=
ReadXMLParam
(
nodo
,
'Name'
);
tBackCol
:=
ReadXMLParam
(
nodo
,
'BackCol'
);
tForeCol
:=
ReadXMLParam
(
nodo
,
'ForeCol'
);
tFrameCol
:=
ReadXMLParam
(
nodo
,
'FrameCol'
);
tFrameEdg
:=
ReadXMLParam
(
nodo
,
'FrameEdg'
);
tFrameSty
:=
ReadXMLParam
(
nodo
,
'FrameSty'
);
tStyBold
:=
ReadXMLParam
(
nodo
,
'Bold'
);
tStyItal
:=
ReadXMLParam
(
nodo
,
'Italic'
);
tStyUnder
:=
ReadXMLParam
(
nodo
,
'Underline'
);
tStyStrike
:=
ReadXMLParam
(
nodo
,
'StrikeOut'
);
tStyle
:=
ReadXMLParam
(
nodo
,
'Style'
);
CheckXMLParams
(
nodo
,
'Name BackCol ForeCol FrameCol FrameEdg FrameSty '
+
'Bold Italic Underline StrikeOut Style'
);
////////// cambia atributo //////////
if
IsAttributeName
(
tName
.
val
)
then
begin
tipTok
:=
GetAttribByName
(
tName
.
val
);
//tipo de atributo
end
else
begin
//No existe, se crea.
tipTok
:=
NewTokAttrib
(
tName
.
val
,
tokId
);
end
;
//obtiene referencia
Atrib
:=
tipTok
;
//asigna la configuración del atributo
if
Atrib
<>
nil
then
begin
if
tBackCol
.
hay
then
Atrib
.
Background
:=
tBackCol
.
col
;
if
tForeCol
.
hay
then
Atrib
.
Foreground
:=
tForeCol
.
col
;
if
tFrameCol
.
hay
then
Atrib
.
FrameColor
:=
tFrameCol
.
col
;
if
tFrameEdg
.
hay
then
begin
case
UpCase
(
tFrameEdg
.
val
)
of
'AROUND'
:
Atrib
.
FrameEdges
:=
sfeAround
;
'BOTTOM'
:
Atrib
.
FrameEdges
:=
sfeBottom
;
'LEFT'
:
Atrib
.
FrameEdges
:=
sfeLeft
;
'NONE'
:
Atrib
.
FrameEdges
:=
sfeNone
;
end
;
end
;
if
tFrameSty
.
hay
then
begin
case
UpCase
(
tFrameSty
.
val
)
of
'SOLID'
:
Atrib
.
FrameStyle
:=
slsSolid
;
'DASHED'
:
Atrib
.
FrameStyle
:=
slsDashed
;
'DOTTED'
:
Atrib
.
FrameStyle
:=
slsDotted
;
'WAVED'
:
Atrib
.
FrameStyle
:=
slsWaved
;
end
;
end
;
if
tStyBold
.
hay
then
begin
//negrita
if
tStyBold
.
bol
then
Atrib
.
Style
:=
Atrib
.
Style
+[
fsBold
]
else
Atrib
.
Style
:=
Atrib
.
Style
-[
fsBold
];
end
;
if
tStyItal
.
hay
then
begin
//cursiva
if
tStyItal
.
bol
then
Atrib
.
Style
:=
Atrib
.
Style
+[
fsItalic
]
else
Atrib
.
Style
:=
Atrib
.
Style
-[
fsItalic
];
end
;
if
tStyUnder
.
hay
then
begin
//subrayado
if
tStyUnder
.
bol
then
Atrib
.
Style
:=
Atrib
.
Style
+[
fsUnderline
]
else
Atrib
.
Style
:=
Atrib
.
Style
-[
fsUnderline
];
end
;
if
tStyStrike
.
hay
then
begin
//tachado
if
tStyStrike
.
bol
then
Atrib
.
Style
:=
Atrib
.
Style
+[
fsStrikeOut
]
else
Atrib
.
Style
:=
Atrib
.
Style
-[
fsStrikeOut
];
end
;
if
tStyle
.
hay
then
begin
//forma alternativa
Atrib
.
Style
:=
Atrib
.
Style
-[
fsBold
]-[
fsItalic
]-[
fsUnderline
]-[
fsStrikeOut
];
if
Pos
(
'b'
,
tStyle
.
val
)<>
0
then
Atrib
.
Style
:=
Atrib
.
Style
+[
fsBold
];
if
Pos
(
'i'
,
tStyle
.
val
)<>
0
then
Atrib
.
Style
:=
Atrib
.
Style
+[
fsItalic
];
if
Pos
(
'u'
,
tStyle
.
val
)<>
0
then
Atrib
.
Style
:=
Atrib
.
Style
+[
fsUnderline
];
if
Pos
(
's'
,
tStyle
.
val
)<>
0
then
Atrib
.
Style
:=
Atrib
.
Style
+[
fsStrikeOut
];
end
;
end
;
end
;
constructor
TSynFacilSynBase
.
Create
(
AOwner
:
TComponent
);
begin
inherited
Create
(
AOwner
);
setlength
(
Attrib
,
0
);
end
;
var
i
:
integer
;
initialization
//prepara definición de comodines
bajos
[
0
]
:=
#
127
;
for
i
:=
1
to
127
do
bajos
[
i
]
:=
chr
(
i
);
//todo menos #0
altos
[
0
]
:=
#
128
;
for
i
:=
1
to
128
do
altos
[
i
]
:=
chr
(
i
+
127
);
end
.
EvoUMLPlugin/src/SynFacilCompletion.pas
0 → 100644
View file @
224b6f5f
{
SynFacilCompletion
==================
Por Tito Hinostroza
Pendientes:
* Incluir una forma simplficada de la forma <OpenOn AfterIdentif="Alter">, para simplificar
la definición clásica.
* Ver el trabajo de la librería con caracteres UTF-8 de dos bytes.
* Optimizar el método LookAround(), evitando tener que leer dos veces la línea actual
y de ser posible creando una rutina personalizada, en lugar de usar ExploreLine().
* Incluir el manejo de las ventanas de tipo "Tip", como ayuda para los parámetros de las
funciones.
* Hacer que la ventana de completado haga seguimiento del cursor, cuando este retrocede
mucho en un identificador.
* Realizar dos pasadas en la etiqueta <completion>, para que se puedan definir las listas
en cualquier parte.
}
{Descripción
============
Unidad que expande al resaltador TSynFacilSyn, para que pueda soportar configuraciones
de autocompletado de texto.
Se usa de forma similar a SynFacilSyn. Se debe crear un resaltador, pero ahora de la
clase TSynFacilComplet:
uses ... , SynFacilCompletion;
procedure TForm1.FormShow(Sender: TObject);
begin
//configure highlighter
hlt := TSynFacilComplet.Create(self); //my highlighter
SynEdit1.Highlighter := hlt; //optional if we are going to use SelectEditor()
hlt.LoadFromFile('./languages/ObjectPascal.xml'); //load syntax
hlt.SelectEditor(SynEdit1); //assign to editor
end;
Luego se debe interceptar los evento KeyUp y UTF8KeyPress, del SynEdit:
procedure TForm1.SynEdit1KeyUp(Sender: TObject; var Key: Word; Shift: TShiftState);
begin
hlt.KeyUp(Sender, Key, Shift);
end;
procedure TForm1.SynEdit1UTF8KeyPress(Sender: TObject; var UTF8Key: TUTF8Char);
begin
hlt.UTF8KeyPress(Sender, UTF8Key);
end;
Y se debe terminar correctamente:
procedure TForm1.FormDestroy(Sender: TObject);
begin
hlt.UnSelectEditor; //release editor (only necessary if we are to call to SelectEditor(), again)
hlt.Free; //destroy the highlighter
end;
Cuando se desea desaparecer la ventana de ayuda contextual por algún evento, se debe
llamar a CloseCompletionWindow().
}
unit
SynFacilCompletion
;
{$mode objfpc}{$H+}
//{$define Verbose}
interface
uses
Classes
,
SysUtils
,
fgl
,
Dialogs
,
XMLRead
,
DOM
,
LCLType
,
Graphics
,
Controls
,
SynEdit
,
SynEditHighlighter
,
SynEditTypes
,
SynEditKeyCmds
,
Lazlogger
,
SynFacilHighlighter
,
SynFacilBasic
,
SynCompletion
;
type
TFaTokInfoPtr
=
^
TFaTokInfo
;
{ TFaCompletItem }
TFaCompletItem
=
class
private
fCaption
:
string
;
//etiqueta a mostrar en el menú
Replac
:
string
;
//contenido a reemplazar
Descrip
:
string
;
//descripción del campo
idxIcon
:
integer
;
//índice al ícono
function
ExtractField
(
var
str
:
string
):
string
;
procedure
SetCaption
(
AValue
:
string
);
public
property
Caption
:
string
read
FCaption
write
SetCaption
;
function
StartWith
(
const
c
:
char
):
boolean
;
inline
;
function
StartWithU
(
const
c
:
char
):
boolean
;
inline
;
end
;
//TFaCompletItemPtr = ^TFaCompletItem;
TFaCompletItems
=
specialize
TFPGObjectList
<
TFaCompletItem
>;
//Filtros que se pueden aplicar a la lista mostrada
TFaFilterList
=
(
fil_None
,
//Sin filtro. Muestra todos
fil_LastTok
,
//por el tokem -1
fil_LastTokPart
,
//por el token -1, hasta donde está el cursor
fil_LastIdent
,
//por el identificador anterior (usa su propia rutina para identifificadores)
fil_LastIdentPart
//similar pero toma hasta el cursor
);
//Objeto lista para completado
{ TFaCompletionList }
TFaCompletionList
=
class
Name
:
string
;
Items
:
TFaCompletItems
;
//lista de las palabras disponibles
procedure
AddItems
(
list
:
string
;
idxIcon
:
integer
);
public
constructor
Create
;
destructor
Destroy
;
override
;
end
;
//Colección de listas
TFaCompletionLists
=
specialize
TFPGObjectList
<
TFaCompletionList
>;
//Tipo de Elemento del patrón
TFaTPatternElementKind
=
(
pak_none
,
//tipo no definido
pak_String
,
//es literal cadena
pak_Identif
,
//es token identificador (tkKeyword, tkIndetifier, ...)
pak_NoIdentif
,
//no es token identificador
pak_TokTyp
,
//es un tipo específico de token
pak_NoTokTyp
//no es un tipo específico de token
);
//Elemento del patrón
TFaPatternElement
=
record
patKind
:
TFaTPatternElementKind
;
str
:
string
;
//valor, cuando es del tipo pak_String
toktyp
:
integer
;
//valor cuando es de tipo pak_TokTyp o pak_NoTokTyp
end
;
TFaPatternElementPtr
=
^
TFaPatternElement
;
{Tipos de secuencias de escape que se puede indicar para el reemplazo de texto.
No son todas las secuencias de escape, sino solo las que necesitan procesarse
independientemente para ejecutar correctamente la acción de reemplazo.}
TFaCompletSeqType
=
(
csqNone
,
//no es secuencia de escape
csqCurPos
,
//secuencia que indica posición del cursor
csqTabSpa
//tabulación al nivel del primer caracter de línea anterior
);
//Entorno del cursor
{ TFaCursorEnviron }
TFaCursorEnviron
=
class
private
hlt
:
TSynFacilSyn
;
//referencia al resaltador que lo contiene
tokens
:
TATokInfo
;
//lista de tokens actuales
StartIdentif
:
integer
;
//inicio de identificador
function
ExtractStaticText
(
var
ReplaceSeq
:
string
;
out
seq
:
TFaCompletSeqType
):
string
;
procedure
InsertSequence
(
ed
:
TSynEdit
;
Pos1
,
Pos2
:
TPoint
;
ReplaceSeq
:
string
);
procedure
UpdateStartIdentif
;
public
inMidTok
:
boolean
;
//indica si el cursor está en medio de un token
tok0
:
TFaTokInfoPtr
;
//referencia al token actual.
tok_1
:
TFaTokInfoPtr
;
//referencia al token anterior.
tok_2
:
TFaTokInfoPtr
;
//referencia al token anterior a tok_1.
tok_3
:
TFaTokInfoPtr
;
//referencia al token anterior a tok_2.
CurX
:
Integer
;
//posición actual del cursor
CurY
:
Integer
;
//posición actual del cursor
curLine
:
string
;
//línea actual de exploración
curBlock
:
TFaSynBlock
;
//referencia al bloque actual
caseSen
:
boolean
;
//indica el estado de caja actual
procedure
LookAround
(
ed
:
TSynEdit
;
CaseSen0
:
boolean
);
//Las siguientes funciones, deben llaamrse después de lamar a LookAround()
function
HaveLastTok
:
boolean
;
function
LastTok
:
string
;
function
LastTokPart
:
string
;
function
HaveLastIdent
:
boolean
;
function
LastIdent
:
string
;
function
LastIdentPart
:
string
;
//Estas funciones implementan las acciones
procedure
ReplaceLastTok
(
ed
:
TSynEdit
;
ReplaceSeq
:
string
);
procedure
ReplaceLastIden
(
ed
:
TSynEdit
;
ReplaceSeq
:
string
);
procedure
Insert
(
ed
:
TSynEdit
;
ReplaceSeq
:
string
);
public
constructor
Create
(
hlt0
:
TSynFacilSyn
);
end
;
TFaOpenEvent
=
class
;
TFaOnLoadItems
=
procedure
(
opEve
:
TFaOpenEvent
;
curEnv
:
TFaCursorEnviron
;
out
Cancel
:
boolean
)
of
object
;
//Acciones válidas que se realizarán al seleccionar un ítem
TFAPatAction
=
(
pac_None
,
//no se realiza ninguna acción
pac_Default
,
//acción pro defecto
pac_Insert
,
//se inserta el texto seleccionado en la posición del cursor
pac_Rep_LastTok
//se reemplaza el token anterior
);
//Objeto evento de apertura
{ TFaOpenEvent }
TFaOpenEvent
=
class
private
hlt
:
TSynFacilSyn
;
//referencia al resaltador que lo contiene
{Los índices de elem[] representan posiciones relativas de tokens
[0] -> Token que está justo después del cursor (token actual)
[-1] -> Token que está antes del token actual
[-2] -> Token que está antes del token [-1]
[-3] -> Token que está antes del token [-2] }
elem
:
array
[-
3..0
]
of
TFaPatternElement
;
nBef
:
integer
;
//número de elementos válidos haste el ítem 0 (puede ser 0,1,2 o 3)
nAft
:
integer
;
//número de elementos válidos depués del ítem 0 (puede ser 0 o 1)
procedure
ExtractElementIn
(
var
befPat
:
string
;
patEle
:
TFaPatternElementPtr
;
var
ErrStr
:
string
);
function
MatchPatternElement
(
nPe
:
integer
;
tokX
:
TFaTokInfoPtr
;
CaseSens
:
boolean
):
boolean
;
function
MatchPatternBefore
(
const
curEnv
:
TFaCursorEnviron
):
boolean
;
function
MatchPatternAfter
(
const
curEnv
:
TFaCursorEnviron
):
boolean
;
function
MatchPattern
(
const
curEnv
:
TFaCursorEnviron
):
boolean
;
procedure
ShiftBeforePattern
;
public
name
:
string
;
//nombre del evento de apertura
startX
:
integer
;
//posición inicial del token o identificador de trabajo
filter
:
TFaFilterList
;
block
:
TFaSynBlock
;
//bloque donde es válido
Action
:
TFAPatAction
;
//Acción al seleccionar lista
OnLoadItems
:
TFaOnLoadItems
;
//Se llama antes de cargar los ítems.
procedure
FilterByChar
(
curEnv
:
TFaCursorEnviron
;
const
c
:
char
);
procedure
DoAction
(
ed
:
TSynEdit
;
env
:
TFaCursorEnviron
;
ReplaceSeq
:
string
);
procedure
FillFilteredIn
(
const
env
:
TFaCursorEnviron
;
lst
:
TStrings
);
//Llena Items en una lista
//manejo patrones
procedure
ClearBeforePatt
;
//limpia el patron anterior
procedure
ClearAfterPatt
;
//limpia el patron anterior
procedure
AddBeforeElement
(
var
befPat
:
string
;
out
ErrStr
:
string
);
procedure
AddAfterElement
(
var
aftPat
:
string
;
var
ErrStr
:
string
);
public
//Manejo de ítems
Items
:
TFaCompletItems
;
//Lista de las palabras disponibles para el completado
Lists
:
TFaCompletionLists
;
//Referencias a listas
Avails
:
TFaCompletItems
;
//Ítems a cargar cuando se active el patrón.
procedure
ClearItems
;
procedure
LoadItems
(
curEnv
:
TFaCursorEnviron
);
procedure
AddItem
(
txt
:
string
;
idxIcon
:
integer
);
procedure
AddItems
(
lst
:
TStringList
;
idxIcon
:
integer
);
procedure
AddItems
(
list
:
string
;
idxIcon
:
integer
);
procedure
AddList
(
Alist
:
TFaCompletionList
;
OnlyRef
:
boolean
);
procedure
ClearAvails
;
procedure
AddAvail
(
txt
:
string
);
//Rutina simple para agregar cadena a Avails
procedure
Clear
;
public
constructor
Create
(
hlt0
:
TSynFacilSyn
);
destructor
Destroy
;
override
;
end
;
//Lista de patrones
TFaOpenEvents
=
specialize
TFPGObjectList
<
TFaOpenEvent
>;
type
{ TSynCompletionF }
{Clase personalizada de "TSynCompletion" usada para el completado con "TSynFacilComplet"}
TSynCompletionF
=
class
(
TSynCompletion
)
function
OnSynCompletionPaintItem
(
const
{%H-}
AKey
:
string
;
ACanvas
:
TCanvas
;
X
,
Y
:
integer
;
{%H-}
IsSelected
:
boolean
;
Index
:
integer
):
boolean
;
public
IconList
:
TImageList
;
//lista de íconos
procedure
Refresh
;
constructor
Create
(
AOwner
:
TComponent
);
override
;
end
;
{ TSynFacilComplet }
//clase principal
TSynFacilComplet
=
class
(
TSynFacilSyn
)
procedure
FormKeyDown
(
Sender
:
TObject
;
var
Key
:
Word
;
Shift
:
TShiftState
);
procedure
OnCodeCompletion
(
var
Value
:
string
;
SourceValue
:
string
;
var
SourceStart
,
SourceEnd
:
TPoint
;
KeyChar
:
TUTF8Char
;
Shift
:
TShiftState
);
procedure
FormUTF8KeyPress
(
Sender
:
TObject
;
var
UTF8Key
:
TUTF8Char
);
protected
ed
:
TSynEdit
;
//referencia interna al editor
MenuComplet
:
TSynCompletionF
;
//menú contextual
curEnv
:
TFaCursorEnviron
;
//entorno del cursor
UtfKey
:
TUTF8Char
;
//tecla pulsada
SpecIdentifiers
:
TArrayTokSpec
;
SearchOnKeyUp
:
boolean
;
//bandera de control
procedure
MenuComplet_OnExecute
(
Sender
:
TObject
);
virtual
;
function
CheckForClose
:
boolean
;
procedure
FillCompletMenuFiltered
;
procedure
ProcCompletionLabel
(
nodo
:
TDOMNode
);
procedure
ReadSpecialIdentif
;
private
//Manejo de patrones de apertura
CompletLists
:
TFaCompletionLists
;
//colección de listas de compleatdo
function
FindOpenEventMatching
:
TFaOpenEvent
;
function
GetIconList
:
TImageList
;
procedure
ProcXMLOpenOn
(
nodo
:
TDOMNode
);
procedure
SetIconList
(
AValue
:
TImageList
);
public
//Manejo de patrones de apertura
OpenEvents
:
TFaOpenEvents
;
//lista de eventos de apertura
CurOpenEve
:
TFaOpenEvent
;
//evento de apertura que se aplica en el momento
function
FindOpenEvent
(
oeName
:
string
):
TFaOpenEvent
;
//Busca un evento de apertura
public
CompletionOn
:
boolean
;
//activa o desactiva el auto-completado
SelectOnEnter
:
boolean
;
//habilita la selección con enter
CaseSensComp
:
boolean
;
//Uso de caja, en autocompletado
OpenOnKeyUp
:
boolean
;
//habilita que se abra automáticamente al soltar una tecla
property
IconList
:
TImageList
read
GetIconList
write
SetIconList
;
function
AddOpenEvent
(
AfterPattern
,
BeforePattern
:
string
;
filter
:
TFaFilterList
):
TFaOpenEvent
;
function
AddComplList
(
lstName
:
string
):
TFaCompletionList
;
function
GetListByName
(
lstName
:
string
):
TFaCompletionList
;
procedure
LoadFromFile
(
const
Filename
:
string
);
override
;
function
LoadSyntaxFromPath
(
SourceFile
:
string
;
path
:
string
;
CaseSens
:
boolean
=
false
):
string
;
procedure
SelectEditor
(
ed0
:
TSynEdit
);
//inicia la ayuda contextual
procedure
UnSelectEditor
;
//termina la ayuda contextual con el editor
procedure
UTF8KeyPress
(
Sender
:
TObject
;
var
UTF8Key
:
TUTF8Char
);
procedure
KeyUp
(
Sender
:
TObject
;
var
Key
:
Word
;
Shift
:
TShiftState
);
procedure
OpenCompletionWindow
(
vKey
:
word
;
vShift
:
TShiftState
;
vUtfKey
:
TUTF8Char
);
procedure
CloseCompletionWindow
;
public
//Constructor y Destructor
constructor
Create
(
AOwner
:
TComponent
);
override
;
destructor
Destroy
;
override
;
end
;
//utilidades
function
ReadExtenFromXML
(
XMLfile
:
string
):
string
;
function
XMLFileHaveExten
(
XMLfile
:
string
;
exten
:
string
;
CaseSens
:
boolean
=
false
):
boolean
;
implementation
uses
SynEditMiscProcs
;
const
// ERR_ATTRIB_NO_EXIST = 'Atributo %s no existe. (etiqueta <COMPLETION ...>)';
// ERR_FILTER_NO_EXIST = 'Filtro %s no existe. (etiqueta <OpenOn ...>)';
// ERR_INVAL_LAB_COMP = 'Etiqueta %s no válida para etiqueta <COMPLETION ...>';
// ERR_INVAL_BLK_NAME = 'Nombre de bloque inválido.';
// ERROR_LOADING_ = 'Error loading: ';
// ERR_PAT_EXP_ENDSTR = 'Se esperaba fin de cadena';
// ERR_PAT_INVALID_ = 'Invalido: %s';
// ERR_PAT_SYNTAX_ERR = 'Error de sintaxis.';
// ERR_PAT_TOO_MAN_EL = 'Demasiados elementos.';
// ERR_PAR_AFT_PATT = 'Error en "BeforePattern"';
// ERR_PAR_BEF_PATT = 'Error en "AfterPattern"';
ERR_ATTRIB_NO_EXIST
=
'Attribute %s doesn''t exist. (label <OpenOn ...>)'
;
ERR_LIST_NO_EXIST
=
'List "%s" doesn''t exist. (label <OpenOn ...>)'
;
ERR_FILTER_NO_EXIST
=
'Filter %s doesn''t exist. (label <OpenOn ...>)'
;
ERR_ACTION_NO_EXIST
=
'Action %s doesn''t exist. (label <OpenOn ...>)'
;
ERR_INVAL_LAB_OPNON
=
'Invalid label %s for <OpenOn ...>'
;
ERR_INVAL_LAB_COMP
=
'Invalid label %s for <COMPLETION ...>'
;
ERR_INVAL_BLK_NAME
=
'Invalid block name.'
;
ERROR_LOADING_
=
'Error loading: '
;
ERR_PAT_EXP_ENDSTR
=
'Expected end of string'
;
ERR_PAT_INVALID_
=
'Invalid: %s'
;
ERR_PAT_SYNTAX_ERR
=
'Syntax error.'
;
ERR_PAT_TOO_MAN_EL
=
'Too many elements.'
;
ERR_PAR_AFT_PATT
=
'Error in "AfterPattern"'
;
ERR_PAR_BEF_PATT
=
'Error in "BeforePattern"'
;
//Constantes para manejar parámetros de <OpenOn>
WORD_CHARS
=
[
'a'
..
'z'
,
'0'
..
'9'
,
'A'
..
'Z'
,
'_'
];
STR_DELIM
=
[
''''
,
'"'
];
ALL_IDENTIF
=
'AllIdentifiers'
;
//Para el reconocimiento de identificadores, cuando se usa "fil_LastIdent" y "fil_LastIdentPart"
CHAR_STRT_IDEN
=
[
'a'
..
'z'
,
'A'
..
'Z'
,
'_'
];
CHAR_BODY_IDEN
=
CHAR_STRT_IDEN
+
[
'0'
..
'9'
];
function
ReadExtenFromXML
(
XMLfile
:
string
):
string
;
//Lee las extensiones que tiene definidas un archivo de sintaxis.
var
doc
:
TXMLDocument
;
atri
:
TDOMNode
;
i
:
Integer
;
begin
try
Result
:=
''
;
//por defecto
ReadXMLFile
(
doc
,
XMLfile
);
//carga archivo
//busca el parámetro "ext"
for
i
:=
0
to
doc
.
DocumentElement
.
Attributes
.
Length
-
1
do
begin
atri
:=
doc
.
DocumentElement
.
Attributes
.
Item
[
i
];
if
UpCase
(
atri
.
NodeName
)
=
'EXT'
then
begin
Result
:=
trim
(
AnsiString
(
atri
.
NodeValue
));
//valor sin espacios
end
;
end
;
doc
.
Free
;
//libera
except
on
E
:
Exception
do
begin
ShowMessage
(
ERROR_LOADING_
+
XMLfile
+
#
13
#
10
+
e
.
Message
);
doc
.
Free
;
end
;
end
;
end
;
function
XMLFileHaveExten
(
XMLfile
:
string
;
exten
:
string
;
CaseSens
:
boolean
=
false
):
boolean
;
//Indica si un archivo XML de sintaxis, tiene definida la extensión que se indica
//La comparación se puede hacer con o sin diferecnia de caja
var
lext
:
TStringList
;
s
:
String
;
tmp
:
String
;
begin
Result
:=
false
;
//por defecto
lext
:=
TStringList
.
Create
;
//crea lista
tmp
:=
ReadExtenFromXML
(
XMLfile
);
//lee archivo
lext
.
DelimitedText
:=
' '
;
lext
.
Text
:=
tmp
;
//divide
//busca de acuerdo a la caja
if
CaseSens
then
begin
for
s
in
lext
do
begin
if
s
=
exten
then
begin
//encontró
Result
:=
true
;
lext
.
Free
;
exit
;
end
;
end
;
end
else
begin
for
s
in
lext
do
begin
if
Upcase
(
s
)
=
Upcase
(
exten
)
then
begin
//encontró
Result
:=
true
;
lext
.
Free
;
exit
;
end
;
end
;
end
;
//No enecontró
lext
.
Free
;
end
;
{ TFaCompletItem }
function
TFaCompletItem
.
ExtractField
(
var
str
:
string
):
string
;
{Extrae un campo de la cadena. Los campos deben estar delimitado con "|" sin caracter
de escape}
function
EscapeBefore
(
i
:
integer
):
boolean
;
inline
;
begin
if
i
<
1
then
exit
(
false
);
if
str
[
i
-
1
]
=
'\'
then
exit
(
true
)
else
exit
(
false
);
end
;
var
psep
:
SizeInt
;
begin
psep
:=
pos
(
'|'
,
str
);
if
(
psep
=
0
)
or
EscapeBefore
(
psep
)
then
begin
//no hay separador de campos, es un caso simple
Result
:=
str
;
str
:=
''
;
end
else
begin
//hay separador
Result
:=
copy
(
str
,
1
,
psep
-
1
);
str
:=
copy
(
str
,
psep
+
1
,
length
(
str
));
end
;
end
;
procedure
TFaCompletItem
.
SetCaption
(
AValue
:
string
);
{Asigna el valor a Caption, separando los campos si es que vinieran codificados}
{Recibe una cadena que representa a un ítem y de el extrae los campos, si es que vinieran
codificados. El formato de la codificiacón es:
<texto a mostrar> | <texto a reemplazar> | <descripción>
}
function
ExecEscape
(
const
s
:
string
):
string
;
{Reemplaza las secuencias de escape para mostrarlas en el menú de completado.
Tal vez convenga hacer este reemplazo, en la rutina que muestra los ítems, por un
tema de velocidad.}
begin
Result
:=
StringReplace
(
s
,
'\n'
,
' '
,
[
rfReplaceAll
]);
Result
:=
StringReplace
(
Result
,
'\t'
,
' '
,
[
rfReplaceAll
]);
Result
:=
StringReplace
(
Result
,
'\u'
,
' '
,
[
rfReplaceAll
]);
Result
:=
StringReplace
(
Result
,
'\\'
,
'\'
,
[
rfReplaceAll
]);
Result
:=
StringReplace
(
Result
,
'\|'
,
'|'
,
[
rfReplaceAll
]);
Result
:=
StringReplace
(
Result
,
'\_'
,
''
,
[
rfReplaceAll
]);
end
;
var
txt1
,
txt2
:
String
;
begin
if
fCaption
=
AValue
then
Exit
;
txt1
:=
ExtractField
(
AValue
);
if
AValue
=
''
then
begin
//solo hay un campo
fCaption
:=
ExecEscape
(
txt1
);
Replac
:=
txt1
;
//los caracteres de escape se expandirán al reemplazar
Descrip
:=
''
;
end
else
begin
//hay al menos otro campo
txt2
:=
ExtractField
(
AValue
);
if
AValue
=
''
then
begin
//hay solo dos campos
fCaption
:=
ExecEscape
(
txt1
);
Replac
:=
txt2
;
//los caracteres de escape se expandirán al reemplazar
Descrip
:=
''
;
end
else
begin
//has 3 o más campos
fCaption
:=
ExecEscape
(
txt1
);
Replac
:=
txt2
;
//los caracteres de escape se expandirán al reemplazar
Descrip
:=
ExecEscape
(
AValue
);
end
;
end
;
end
;
function
TFaCompletItem
.
StartWith
(
const
c
:
char
):
boolean
;
begin
Result
:=
(
fCaption
<>
''
)
and
(
fCaption
[
1
]
=
c
);
end
;
function
TFaCompletItem
.
StartWithU
(
const
c
:
char
):
boolean
;
begin
Result
:=
(
fCaption
<>
''
)
and
(
UpCase
(
fCaption
[
1
])
=
c
);
end
;
{ TFaCompletionList }
procedure
TFaCompletionList
.
AddItems
(
list
:
string
;
idxIcon
:
integer
);
//Agrega una lista de ítems, separados por espacios, a la lista de completado
var
lst
:
TStringList
;
i
:
Integer
;
it
:
TFaCompletItem
;
begin
//divide
lst
:=
TStringList
.
Create
;
lst
.
Delimiter
:=
' '
;
lst
.
DelimitedText
:=
list
;
//agrega
for
i
:=
0
to
lst
.
Count
-
1
do
begin
it
:=
TFaCompletItem
.
Create
;
it
.
Caption
:=
lst
[
i
];
it
.
idxIcon
:=
idxIcon
;
Items
.
Add
(
it
);
end
;
lst
.
Destroy
;
end
;
constructor
TFaCompletionList
.
Create
;
begin
Items
:=
TFaCompletItems
.
Create
(
true
);
//lista con administración
end
;
destructor
TFaCompletionList
.
Destroy
;
begin
Items
.
Destroy
;
inherited
Destroy
;
end
;
{ TFaCursorEnviron }
constructor
TFaCursorEnviron
.
Create
(
hlt0
:
TSynFacilSyn
);
begin
hlt
:=
hlt0
;
end
;
procedure
TFaCursorEnviron
.
LookAround
(
ed
:
TSynEdit
;
CaseSen0
:
boolean
);
{Analiza el estado del cursor en el editor. Se supone que se debe llamar, después de
actualizar el editor. Actualiza: PosiCursor, curBlock, tok0, tok_1, tok_2
y tok_3. Utiliza punteros, para evitar perder tiempo creando copias.}
var
iTok0
:
integer
;
//índice al token actual
begin
caseSen
:=
CaseSen0
;
//actualiza estado
//valores por defecto
curBlock
:=
nil
;
//explora la línea con el resaltador
hlt
.
ExploreLine
(
ed
.
CaretXY
,
tokens
,
iTok0
);
curLine
:=
ed
.
Lines
[
ed
.
CaretY
-
1
];
//Se gaurda porque se va a necesitar
if
iTok0
=-
1
then
exit
;
//no ubica al token actual
tok0
:=
@
tokens
[
iTok0
];
//lee token actual token[0]
CurX
:=
ed
.
LogicalCaretXY
.
x
;
//usa posición física para comparar
CurY
:=
ed
.
LogicalCaretXY
.
y
;
inMidTok
:=
tokens
[
iTok0
].
posIni
+
1
<>
CurX
;
//actualiza bandera
//actualiza tok_1
if
inMidTok
then
begin
tok_1
:=
@
tokens
[
iTok0
];
if
iTok0
>
0
then
tok_2
:=
@
tokens
[
iTok0
-
1
]
else
tok_2
:=
nil
;
if
iTok0
>
1
then
tok_3
:=
@
tokens
[
iTok0
-
2
]
else
tok_3
:=
nil
;
end
else
begin
if
iTok0
>
0
then
tok_1
:=
@
tokens
[
iTok0
-
1
]
else
tok_1
:=
nil
;
if
iTok0
>
1
then
tok_2
:=
@
tokens
[
iTok0
-
2
]
else
tok_2
:=
nil
;
if
iTok0
>
2
then
tok_3
:=
@
tokens
[
iTok0
-
3
]
else
tok_3
:=
nil
;
end
;
//captura "curBlock"
curBlock
:=
tok0
^.
curBlk
;
//devuelve bloque
{$IFDEF Verbose}
DbgOut
(
' LookAround:('
);
if
tok_3
<>
nil
then
DbgOut
(
hlt
.
Attrib
[
tok_3
^.
TokTyp
].
Name
+
','
);
if
tok_2
<>
nil
then
DbgOut
(
hlt
.
Attrib
[
tok_2
^.
TokTyp
].
Name
+
','
);
if
tok_1
<>
nil
then
DbgOut
(
hlt
.
Attrib
[
tok_1
^.
TokTyp
].
Name
+
','
);
if
tok0
<>
nil
then
DbgOut
(
hlt
.
Attrib
[
tok0
^.
TokTyp
].
Name
);
debugln
(
')'
);
{$ENDIF}
end
;
{Las siguientes funciones, deben llamarse después de lamar a LookAround(). Deben ser de
ejecución rápida}
function
TFaCursorEnviron
.
HaveLastTok
:
boolean
;
inline
;
begin
Result
:=
(
tok_1
<>
nil
);
end
;
function
TFaCursorEnviron
.
LastTok
:
string
;
inline
;
{Devuelve el último token}
begin
Result
:=
tok_1
^.
txt
;
end
;
function
TFaCursorEnviron
.
LastTokPart
:
string
;
inline
;
{Devuelve el último token, truncado a la posición del cursor}
begin
// Result := copy(tok0^.txt,1,CurX-tok0^.posIni-1);
Result
:=
copy
(
tok_1
^.
txt
,
1
,
CurX
-
tok_1
^.
posIni
-
1
);
end
;
procedure
TFaCursorEnviron
.
UpdateStartIdentif
;
{Actualiza el índice al inicio del identificador anterior, a la posición actual del cursor.
Este es un algoritmo, un poco especial, porque los identificadores no se
definen para explorarlos hacia atrás.}
var
i
:
Integer
;
begin
StartIdentif
:=
-
1
;
//valor por defecto
if
CurX
<=
1
then
exit
;
//está al inicio
i
:=
CurX
-
1
;
//caracter anterior al cursor
{Se asume que el cursor, está después de un identificador y retrocede por los
caracteres hasta encontrar un caracter que pueda ser inicio de identificador}
while
(
i
>
0
)
and
(
curLine
[
i
]
in
CHAR_BODY_IDEN
)
do
begin
if
curLine
[
i
]
in
CHAR_STRT_IDEN
then
begin
StartIdentif
:=
i
;
//guarda una posible posición de inicio
end
;
dec
(
i
);
end
;
end
;
function
TFaCursorEnviron
.
HaveLastIdent
:
boolean
;
{Indica si hay un identificador antes del cursor. Debe llamarse siempre antes de
usar LastIdent().}
begin
UpdateStartIdentif
;
Result
:=
(
StartIdentif
<>
-
1
);
end
;
function
TFaCursorEnviron
.
LastIdent
:
string
;
{Devuelve el identificador anterior al cursor. Debe llamarse siempre despues de llamar
a HaveLastIdent}
var
i
:
Integer
;
begin
{Ya sabemos que hay identificador hasta antes del cursor, ahora debemos ver, hasta
dónde se extiende}
i
:=
CurX
;
while
curLine
[
i
]
in
CHAR_BODY_IDEN
do
//no debería ser necesario verificar el final
inc
(
i
);
Result
:=
copy
(
curLine
,
StartIdentif
,
i
-
StartIdentif
+
1
);
end
;
function
TFaCursorEnviron
.
LastIdentPart
:
string
;
{Devuelve el identificador anterior al cursor. Debe llamarse siempre despues de llamar
a HaveLastIdent}
begin
Result
:=
copy
(
curLine
,
StartIdentif
,
CurX
-
StartIdentif
);
end
;
{Estas funciones implementan las acciones. Procesan las secuencias de escape}
function
TFaCursorEnviron
.
ExtractStaticText
(
var
ReplaceSeq
:
string
;
out
seq
:
TFaCompletSeqType
):
string
;
{Extrae un fragmento de texto de "ReplaceSeq", que puede insertarse directamente en el editor,
sin necesidad de hacer cálculos de posición, o que no contengan comandos de posicionamiento
del cursor. La idea es que el texto que se devuelva aquí, se pueda insertar directamente
en el editor con una simple operación "Insert". El tipo de secuencia que produjo la ruptura,
se devuelve en "seq"}
function
ReplaceEscape
(
const
s
:
string
):
string
;
begin
Result
:=
StringReplace
(
s
,
'\n'
,
LineEnding
,
[
rfReplaceAll
]);
Result
:=
StringReplace
(
Result
,
'\t'
,
#
9
,
[
rfReplaceAll
]);
Result
:=
StringReplace
(
Result
,
'\|'
,
'|'
,
[
rfReplaceAll
]);
Result
:=
StringReplace
(
Result
,
'\\'
,
'\'
,
[
rfReplaceAll
]);
end
;
function
FirstPos
(
substrs
:
array
of
string
;
str
:
string
;
out
found
:
string
):
integer
;
{Busca la ocurrencia de cualquiera de las cadenas dadas en "substrs". Devuelve el índice
a la primera encontrada. Si no enceuntra ninguna, devuelve 0.}
var
i
,
p
:
Integer
;
limit
:
Integer
;
lin
:
string
;
begin
Result
:=
0
;
//valor inicial
found
:=
''
;
limit
:=
length
(
str
);
for
i
:=
0
to
high
(
substrs
)
do
begin
lin
:=
copy
(
str
,
1
,
limit
);
p
:=
Pos
(
substrs
[
i
],
lin
);
if
p
<>
0
then
begin
//encontró uno, compara
if
p
<
limit
then
begin
limit
:=
p
;
//restringe límite para la siguiente búsqueda
found
:=
substrs
[
i
];
//lo que enontró
Result
:=
p
;
end
;
end
;
end
;
end
;
var
p
:
Integer
;
hay
:
string
;
begin
//Detcta las secuencias de posición de cursor, o tabulación '\u'.
p
:=
FirstPos
([
'\_'
,
'\u'
],
ReplaceSeq
,
hay
);
//tabulación al primer caracter no blanco de la línea superior no blanca
if
hay
=
''
then
begin
//No hay secuecnia especial
Result
:=
ReplaceEscape
(
ReplaceSeq
);
seq
:=
csqNone
;
//no se ecnontró secuencia de ruptura
ReplaceSeq
:=
''
;
end
else
if
hay
=
'\_'
then
begin
//primero está la secuencia de cursor
Result
:=
ReplaceEscape
(
copy
(
ReplaceSeq
,
1
,
p
-
1
));
seq
:=
csqCurPos
;
//Indica secuencia de posicionamiento de cursor
ReplaceSeq
:=
copy
(
ReplaceSeq
,
p
+
2
,
length
(
ReplaceSeq
));
end
else
if
hay
=
'\u'
then
begin
//primero está la secuencia de tabulación
Result
:=
ReplaceEscape
(
copy
(
ReplaceSeq
,
1
,
p
-
1
));
seq
:=
csqTabSpa
;
//Indica secuencia
ReplaceSeq
:=
copy
(
ReplaceSeq
,
p
+
2
,
length
(
ReplaceSeq
));
end
;
end
;
procedure
TFaCursorEnviron
.
InsertSequence
(
ed
:
TSynEdit
;
Pos1
,
Pos2
:
TPoint
;
ReplaceSeq
:
string
);
{Inserta una secuencia de reemplazo en el bloque definido por P1 y P2}
function
FindNoWhiteLineUp
(
ed
:
TSynEdit
):
string
;
{Busca hacia arriba, una línea con caracteres diferentes de espacio y que ocupen una posición
más a la derecha de la posición actual del cursor. La búsqueda se hace a partir de la
posición actual del cursor. Si no encuentra, devuelve línea en blanco.}
var
x
,
y
:
Integer
;
lin
:
String
;
begin
y
:=
ed
.
CaretY
-
1
;
//desde la línea anterior
x
:=
ed
.
CaretX
;
while
y
>
0
do
begin
lin
:=
ed
.
Lines
[
y
-
1
];
if
trim
(
copy
(
lin
,
x
,
length
(
lin
)))<>
''
then
exit
(
lin
);
dec
(
y
);
end
;
//no encontró
exit
(
''
);
end
;
var
toRepl
:
String
;
cursorPos
:
TPoint
;
seq
:
TFaCompletSeqType
;
linNoWhite
,
curLin
:
String
;
i
,
dif
:
Integer
;
begin
ed
.
BeginUndoBlock
;
ed
.
TextBetweenPointsEx
[
Pos1
,
Pos2
,
scamEnd
]
:=
''
;
//elimina el contenido y deja cursor al final
cursorPos
.
x
:=
-
1
;
//marca bandera
while
ReplaceSeq
<>
''
do
begin
toRepl
:=
ExtractStaticText
(
ReplaceSeq
,
seq
);
case
seq
of
csqNone
:
begin
//no hay ruptura, es un texto sencillo
//reemplaza y deja cursor al final
Pos1
:=
ed
.
CaretXY
;
ed
.
TextBetweenPointsEx
[
Pos1
,
Pos1
,
scamEnd
]
:=
toRepl
;
//se suepone que esta es la última secuencia
end
;
csqCurPos
:
begin
//hay comando de posicionamiento de cursor
//reemplaza, deja cursor al final y guarda posición
Pos1
:=
ed
.
CaretXY
;
ed
.
TextBetweenPointsEx
[
Pos1
,
Pos1
,
scamEnd
]
:=
toRepl
;
cursorPos
:=
ed
.
CaretXY
;
end
;
csqTabSpa
:
begin
//hay comando de tabulación inteligente
Pos1
:=
ed
.
CaretXY
;
//inserta fragmento
ed
.
TextBetweenPointsEx
[
Pos1
,
Pos1
,
scamEnd
]
:=
toRepl
;
//calcula espaciado
linNoWhite
:=
FindNoWhiteLineUp
(
ed
);
if
linNoWhite
<>
''
then
begin
//hay línea sin blancos, busca posición de caracter no blanco
for
i
:=
ed
.
CaretX
to
length
(
linNoWhite
)
do
begin
//La definición de blanco #1..#32, corresponde al resaltador
if
not
(
linNoWhite
[
i
]
in
[#
1.
.#
32
])
then
begin
//Encontró. Ahora hay que posicionar el cursor en "i".
curLin
:=
ed
.
LineText
;
//línea actual
if
length
(
curLin
)<
i
then
begin
//No hay caracteres, en donde se quiere colocar el cursor.
dif
:=
i
-
length
(
curLin
);
//esto es lo que falta
ed
.
CaretX
:=
length
(
curLin
)+
1
;
//pone cursor al final
ed
.
InsertTextAtCaret
(
Space
(
dif
));
{Completa con espacios. Usa InsertTextAtCaret,
para poder deshacer.}
ed
.
CaretX
:=
i
;
//ahora sí se puede posicionar el cursor.
end
else
begin
//Se puede posicionar directamente
ed
.
CaretX
:=
i
;
end
;
break
;
//sale del FOR
end
;
end
;
{Si llega aquí sin encontrar el caracter buscado, indicaria que el
algoritmo de búsqueda de FindNoWhiteLineUp() no es consistente con este código.}
end
;
end
;
end
;
end
;
if
cursorPos
.
x
<>-
1
then
begin
//ha habido posicionamiento de cursor
ed
.
CaretXY
:=
cursorPos
;
end
;
ed
.
EndUndoBlock
;
end
;
procedure
TFaCursorEnviron
.
ReplaceLastTok
(
ed
:
TSynEdit
;
ReplaceSeq
:
string
);
{Reemplaza el último token}
var
Pos1
,
Pos2
:
TPoint
;
begin
if
not
HaveLastTok
then
exit
;
Pos1
:=
Point
(
tok_1
^.
posIni
+
1
,
CurY
);
Pos2
:=
Point
(
tok_1
^.
posIni
+
tok_1
^.
length
+
1
,
CurY
);
//Realiza el reemplazo del texto, con procesamiento
InsertSequence
(
ed
,
Pos1
,
Pos2
,
ReplaceSeq
);
end
;
procedure
TFaCursorEnviron
.
ReplaceLastIden
(
ed
:
TSynEdit
;
ReplaceSeq
:
string
);
{Reemplaza el último identificador}
var
Pos1
,
Pos2
:
TPoint
;
i
:
Integer
;
begin
if
not
HaveLastIdent
then
exit
;
Pos1
:=
Point
(
StartIdentif
,
CurY
);
i
:=
CurX
;
while
(
i
<=
length
(
curLine
))
and
(
curLine
[
i
]
in
CHAR_BODY_IDEN
)
do
inc
(
i
);
Pos2
:=
Point
(
i
,
CurY
);
InsertSequence
(
ed
,
Pos1
,
Pos2
,
ReplaceSeq
);
end
;
procedure
TFaCursorEnviron
.
Insert
(
ed
:
TSynEdit
;
ReplaceSeq
:
string
);
{Reemplaza un texto en la posición actual del cursor}
var
Pos1
:
TPoint
;
begin
Pos1
:=
Point
(
CurX
,
CurY
);
InsertSequence
(
ed
,
Pos1
,
Pos1
,
ReplaceSeq
);
end
;
{ TFaOpenEvent }
procedure
TFaOpenEvent
.
DoAction
(
ed
:
TSynEdit
;
env
:
TFaCursorEnviron
;
ReplaceSeq
:
string
);
{Ejecuta la acción que se tenga definido para el evento de apertura}
begin
case
Action
of
pac_None
:;
//no ahce nada
pac_Default
:
//acción por defecto
case
Filter
of
fil_None
:
;
//no hay elemento de selcción
fil_LastTok
,
fil_LastTokPart
:
//trabaja con el último token
env
.
ReplaceLastTok
(
ed
,
ReplaceSeq
);
fil_LastIdent
,
fil_LastIdentPart
:
//trabaja con el úmtimo identificador
env
.
ReplaceLastIden
(
ed
,
ReplaceSeq
);
end
;
pac_Insert
:
//inserta
env
.
Insert
(
ed
,
ReplaceSeq
);
pac_Rep_LastTok
:
env
.
ReplaceLastTok
(
ed
,
ReplaceSeq
);
{Se pueden completar más acciones}
else
env
.
ReplaceLastTok
(
ed
,
ReplaceSeq
);
end
;
end
;
procedure
TFaOpenEvent
.
FillFilteredIn
(
const
env
:
TFaCursorEnviron
;
lst
:
TStrings
);
{Filtra los ítems que contiene (usando "env") y los pone en la lista indicada}
procedure
FilterBy
(
const
str
:
string
);
//Llena el menú de completado a partir de "Avails", filtrando solo las
//palabras que coincidan con "str"
var
l
:
Integer
;
it
:
TFaCompletItem
;
str2
:
String
;
begin
l
:=
length
(
str
);
//Genera la lista que coincide
if
env
.
caseSen
then
begin
for
it
in
Avails
do
begin
//esta no es la forma más eficiente de comparar, pero sirve por ahora.
if
str
=
copy
(
it
.
fCaption
,
1
,
l
)
then
// lst.Add(Avails[i]^.text);
lst
.
AddObject
(
it
.
fCaption
,
it
);
end
;
end
else
begin
//ignora la caja
str2
:=
UpCase
(
str
);
for
it
in
Avails
do
begin
if
str2
=
upcase
(
copy
(
it
.
fCaption
,
1
,
l
))
then
begin
// lst.Add(Avails[i]^.text);
lst
.
AddObject
(
it
.
fCaption
,
it
);
end
;
end
;
end
;
end
;
var
it
:
TFaCompletItem
;
begin
case
Filter
of
fil_None
:
begin
//agrega todos
for
it
in
Avails
do
begin
//agrega sus ítems
lst
.
AddObject
(
it
.
fCaption
,
it
);
end
;
end
;
fil_LastTok
:
begin
//último token
if
env
.
HaveLastTok
then
FilterBy
(
env
.
LastTok
);
end
;
fil_LastTokPart
:
begin
//último token hasta el cursor
if
env
.
HaveLastTok
then
FilterBy
(
env
.
LastTokPart
);
end
;
fil_LastIdent
:
begin
//último token
if
env
.
HaveLastIdent
then
FilterBy
(
env
.
LastIdent
);
end
;
fil_LastIdentPart
:
begin
//último token hasta el cursor
if
env
.
HaveLastIdent
then
FilterBy
(
env
.
LastIdentPart
);
end
;
end
;
end
;
//manejo de elementos de patrones
procedure
TFaOpenEvent
.
ClearBeforePatt
;
begin
elem
[-
3
].
patKind
:=
pak_none
;
elem
[-
2
].
patKind
:=
pak_none
;
elem
[-
1
].
patKind
:=
pak_none
;
nBef
:=
0
;
//no hay elementos válidos
end
;
procedure
TFaOpenEvent
.
ClearAfterPatt
;
begin
elem
[
0
].
patKind
:=
pak_none
;
nAft
:=
0
;
//no hay
end
;
procedure
TFaOpenEvent
.
ExtractElementIn
(
var
befPat
:
string
;
patEle
:
TFaPatternElementPtr
;
var
ErrStr
:
string
);
{Extrae un elemento de un patrón de tokens que viene en cadena y lo almacena en "patEle".
La cadena puede ser algo así como "Identifier,'.',AllIdentifiers".
Si encuentra error, devuelve el mensaje en "ErrStr".}
function
ExtractIdentifier
(
var
befPat
:
string
):
string
;
{Extrae un identificador de la cadena "befPat"}
var
i
:
Integer
;
begin
i
:=
1
;
while
(
i
<=
length
(
befPat
))
and
(
befPat
[
i
]
in
WORD_CHARS
)
do
begin
inc
(
i
);
end
;
Result
:=
copy
(
befPat
,
1
,
i
-
1
);
//extrae cadena
befPat
:=
copy
(
befPat
,
i
,
length
(
befPat
));
//recorta
end
;
function
ExtractString
(
var
befPat
:
string
;
var
ErrStr
:
string
):
string
;
{Extrae una cadena de "befPat". Si hay error devuelve mensaje en "ErrStr"}
var
i
:
Integer
;
ci
:
Char
;
begin
ci
:=
befPat
[
1
];
//caracter inicial
i
:=
2
;
while
(
i
<=
length
(
befPat
))
and
(
befPat
[
i
]
<>
ci
)
do
begin
inc
(
i
);
end
;
if
i
>
length
(
befPat
)
then
begin
ErrStr
:=
ERR_PAT_EXP_ENDSTR
;
exit
;
end
;
Result
:=
copy
(
befPat
,
1
,
i
);
//extrae cadena
befPat
:=
copy
(
befPat
,
i
+
1
,
length
(
befPat
));
//recorta
end
;
procedure
ExtractChar
(
var
befPat
:
string
);
begin
befPat
:=
copy
(
befPat
,
2
,
length
(
befPat
));
end
;
procedure
ExtractComma
(
var
befPat
:
string
);
begin
befPat
:=
TrimLeft
(
befPat
);
//quita espacios
//quita posible coma final
if
(
befPat
<>
''
)
and
(
befPat
[
1
]
=
','
)
then
befPat
:=
copy
(
befPat
,
2
,
length
(
befPat
));
end
;
var
strElem
:
String
;
begin
if
befPat
[
1
]
in
WORD_CHARS
then
begin
//Es un identificador: tipo de token o la cadena especial "AllIdentifiers"
strElem
:=
ExtractIdentifier
(
befPat
);
if
upcase
(
strElem
)
=
upcase
(
ALL_IDENTIF
)
then
begin
//es de tipo "Todos los identificadores"
patEle
^.
patKind
:=
pak_Identif
;
end
else
if
hlt
.
IsAttributeName
(
strElem
)
then
begin
//es
//Es nombre de tipo de token
patEle
^.
patKind
:=
pak_TokTyp
;
patEle
^.
toktyp
:=
hlt
.
GetAttribIDByName
(
strElem
);
//tipo de atributo
end
else
begin
//no es, debe haber algún error
ErrStr
:=
Format
(
ERR_PAT_INVALID_
,[
strElem
]);
exit
;
end
;
ExtractComma
(
befpat
);
end
else
if
befPat
[
1
]
=
'!'
then
begin
//debe ser de tipo "No es ..."
ExtractChar
(
befPat
);
strElem
:=
ExtractIdentifier
(
befPat
);
if
upcase
(
strElem
)
=
upcase
(
ALL_IDENTIF
)
then
begin
//es de tipo "Todos los identificadores"
patEle
^.
patKind
:=
pak_NoIdentif
;
end
else
if
hlt
.
IsAttributeName
(
strElem
)
then
begin
//Es nombre de tipo de token
patEle
^.
patKind
:=
pak_NoTokTyp
;
patEle
^.
toktyp
:=
hlt
.
GetAttribIDByName
(
strElem
);
//tipo de atributo
end
else
begin
//no es, debe haber algún error
ErrStr
:=
Format
(
ERR_PAT_INVALID_
,[
strElem
]);
exit
;
end
;
ExtractComma
(
befpat
);
end
else
if
befPat
[
1
]
in
STR_DELIM
then
begin
//es un literal cadena
strElem
:=
ExtractString
(
befPat
,
ErrStr
);
if
ErrStr
<>
''
then
exit
;
patEle
^.
patKind
:=
pak_String
;
patEle
^.
str
:=
copy
(
strElem
,
2
,
length
(
strElem
)-
2
);
ExtractComma
(
befpat
);
end
else
begin
ErrStr
:=
ERR_PAT_SYNTAX_ERR
;
exit
;
end
;
end
;
procedure
TFaOpenEvent
.
AddBeforeElement
(
var
befPat
:
string
;
out
ErrStr
:
string
);
{Agrega un elemento al patrón anterior. Si encuentra error devuelve el mensaje en ErrStr}
var
patEle
:
^
TFaPatternElement
;
begin
ErrStr
:=
''
;
befPat
:=
TrimLeft
(
befPat
);
//quita espacios
if
befPat
=
''
then
exit
;
//no hay elementos
//Hay algo que agregar
if
nBef
=
3
then
begin
//validación
ErrStr
:=
ERR_PAT_TOO_MAN_EL
;
//no hay espacio
exit
;
end
;
ShiftBeforePattern
;
//hace espacio
patEle
:=
@
elem
[-
1
];
//fija puntero
ExtractElementIn
(
befPat
,
patEle
,
ErrStr
);
//puede salir con mensaje de error en "ErrStr"
end
;
procedure
TFaOpenEvent
.
AddAfterElement
(
var
aftPat
:
string
;
var
ErrStr
:
string
);
{Agrega un elemento al patrón siguiente. Si encuentra error devuelve el mensaje en ErrStr}
var
patEle
:
^
TFaPatternElement
;
begin
ErrStr
:=
''
;
aftPat
:=
TrimLeft
(
aftPat
);
//quita espacios
if
aftPat
=
''
then
exit
;
//no hay elementos
//Hay algo que agregar
if
nAft
=
1
then
begin
//validación
ErrStr
:=
ERR_PAT_TOO_MAN_EL
;
//no hay espacio
exit
;
end
;
inc
(
nAft
);
//lleva la cuenta
patEle
:=
@
elem
[
0
];
//fija puntero
ExtractElementIn
(
aftPat
,
patEle
,
ErrStr
);
//puede salir con mensaje de error en "ErrStr"
end
;
procedure
TFaOpenEvent
.
ShiftBeforePattern
;
{Desplaza los elementos del patrón anterior, a la izquierda, dejando el de la derecha
libre para usarlo. No toca al patrón siguiente}
begin
elem
[-
3
]
:=
elem
[-
2
];
elem
[-
2
]
:=
elem
[-
1
];
elem
[-
1
].
patKind
:=
pak_none
;
Inc
(
nBef
);
//actualiza elementos anteriores válidos
end
;
procedure
TFaOpenEvent
.
AddAvail
(
txt
:
string
);
{Agrega un ítem a la lista Avails[]}
var
it
:
TFaCompletItem
;
begin
it
:=
TFaCompletItem
.
Create
;
it
.
Caption
:=
txt
;
it
.
Replac
:=
txt
;
it
.
idxIcon
:=-
1
;
Avails
.
Add
(
it
);
end
;
procedure
TFaOpenEvent
.
Clear
;
begin
ClearAvails
;
ClearItems
;
end
;
//manejo de ítems
procedure
TFaOpenEvent
.
FilterByChar
(
curEnv
:
TFaCursorEnviron
;
const
c
:
char
);
{Filtra la lista Items[], usando un caracter. Se define como público, para poder usarla
como utilidad, si es que se necesita.}
var
it
:
TFaCompletItem
;
lst
:
TFaCompletionList
;
cu
:
Char
;
begin
Avails
.
Clear
;
if
curEnv
.
caseSen
then
begin
for
it
in
Items
do
begin
if
it
.
StartWith
(
c
)
then
Avails
.
Add
(
it
);
//copia las referencias
end
;
//copia ítens de las listas
for
lst
in
Lists
do
begin
for
it
in
lst
.
Items
do
begin
if
it
.
StartWith
(
c
)
then
Avails
.
Add
(
it
);
//copia las referencias
end
;
end
;
end
else
begin
cu
:=
UpCase
(
c
);
for
it
in
Items
do
begin
if
it
.
StartWithU
(
cu
)
then
Avails
.
Add
(
it
);
//copia las referencias
end
;
//copia ítens de las listas
for
lst
in
Lists
do
begin
for
it
in
lst
.
Items
do
begin
if
it
.
StartWithU
(
cu
)
then
Avails
.
Add
(
it
);
//copia las referencias
end
;
end
;
end
;
end
;
procedure
TFaOpenEvent
.
LoadItems
(
curEnv
:
TFaCursorEnviron
);
{Carga todos los ítems con los que se va a trabajar en Avails[]. Los que se usarán para
posteriormente filtrarse y cargarse al menú de completado.}
var
it
:
TFaCompletItem
;
lst
:
TFaCompletionList
;
Cancel
:
boolean
;
begin
if
OnLoadItems
<>
nil
then
begin
//Hay evento configruado para llenar dinámciamente los ítems
OnLoadItems
(
self
,
curEnv
,
Cancel
);
{$IFDEF Verbose}
debugln
(
' LLenado dinámico de ítems con %d elem.'
,
[
items
.
Count
]);
{$ENDIF}
if
Cancel
then
exit
;
end
;
case
filter
of
fil_None
:
begin
//no hay filtro
Avails
.
Assign
(
Items
);
//copia todas las referencias
//Agrega, también las referencias de las listas que pueda contener.
for
lst
in
Lists
do
begin
for
it
in
lst
.
Items
do
begin
Avails
.
Add
(
it
);
//copia las referencias
end
;
end
;
startX
:=
curEnv
.
CurX
;
//como no hay palabra de trabajo
end
;
fil_LastTok
,
fil_LastTokPart
:
begin
//se usará el último token
if
curEnv
.
HaveLastTok
then
begin
//hay un token anterior
startX
:=
curEnv
.
tok_1
^.
posIni
;
//inicio del token
FilterByChar
(
curEnv
,
curEnv
.
LastTok
[
1
]);
//primer caracter como filtro (peor caso)
end
;
end
;
fil_LastIdent
,
fil_LastIdentPart
:
begin
//se usará el último identif.
if
curEnv
.
HaveLastIdent
then
begin
//hay un token anterior
startX
:=
curEnv
.
StartIdentif
;
//es fácil sacar el inicio del identificador
FilterByChar
(
curEnv
,
curEnv
.
LastIdentPart
[
1
]);
//primer caracter como filtro (peor caso)
end
;
end
;
else
//no debería pasar
Avails
.
Clear
;
startX
:=
curEnv
.
CurX
;
end
;
{$IFDEF Verbose}
debugln
(
' Cargados en Avail: '
+
IntToStr
(
Avails
.
Count
)+
' ítems.'
)
{$ENDIF}
end
;
function
TFaOpenEvent
.
MatchPatternElement
(
nPe
:
integer
;
tokX
:
TFaTokInfoPtr
;
CaseSens
:
boolean
):
boolean
;
{Verifica el elemento de un patrón, coincide con un token de tokens[]. }
var
pe
:
TFaPatternElement
;
begin
Result
:=
false
;
//por defecto
pe
:=
elem
[
nPe
];
//no hay validación. Por velocidad, podría ser mejor un puntero.
if
tokX
=
nil
then
exit
(
false
);
//no existe este token
case
pe
.
patKind
of
pak_none
:
//*** No definido.
exit
(
true
);
//No debería llegar aquí.
pak_String
:
begin
//*** Es una cadena
if
CaseSens
then
begin
//comparación con caja
if
tokX
^.
txt
=
pe
.
str
then
exit
(
true
)
else
exit
(
false
);
end
else
begin
//comparación sin caja
if
UpCase
(
tokX
^.
txt
)
=
UpCase
(
pe
.
str
)
then
exit
(
true
)
else
exit
(
false
);
end
;
end
;
pak_Identif
:
begin
//*** Es identificador
Result
:=
tokX
^.
IsIDentif
;
end
;
pak_NoIdentif
:
begin
Result
:=
not
tokX
^.
IsIDentif
;
end
;
pak_TokTyp
:
begin
//*** Es un tipo específico de token
Result
:=
pe
.
toktyp
=
tokX
^.
TokTyp
;
end
;
pak_NoTokTyp
:
begin
//*** Es un tipo específico de token
Result
:=
not
(
pe
.
toktyp
=
tokX
^.
TokTyp
);
end
;
end
;
end
;
function
TFaOpenEvent
.
MatchPatternBefore
(
const
curEnv
:
TFaCursorEnviron
):
boolean
;
{Verifica si el patrón indicado, cumple con las condiciones actuales (before)}
begin
Result
:=
false
;
//por defecto
case
nBef
of
0
:
begin
//no hay elementos, siempre cumple |
exit
(
true
);
end
;
1
:
begin
//hay elem[-1]
Result
:=
MatchPatternElement
(-
1
,
curEnv
.
tok_1
,
curEnv
.
caseSen
);
end
;
2
:
begin
//hay elem[-2],elem[-1]
Result
:=
MatchPatternElement
(-
1
,
curEnv
.
tok_1
,
curEnv
.
caseSen
)
and
MatchPatternElement
(-
2
,
curEnv
.
tok_2
,
curEnv
.
caseSen
);
end
;
3
:
begin
//hay elem[-3],elem[-2],elem[-1]
Result
:=
MatchPatternElement
(-
1
,
curEnv
.
tok_1
,
curEnv
.
caseSen
)
and
MatchPatternElement
(-
2
,
curEnv
.
tok_2
,
curEnv
.
caseSen
)
and
MatchPatternElement
(-
3
,
curEnv
.
tok_3
,
curEnv
.
caseSen
);
end
;
end
;
end
;
function
TFaOpenEvent
.
MatchPatternAfter
(
const
curEnv
:
TFaCursorEnviron
):
boolean
;
{Verifica si el patrón indicado, cumple con las condiciones actuales (after)}
begin
Result
:=
false
;
//por defecto
case
nAft
of
0
:
begin
//no hay elementos, siempre cumple
exit
(
true
);
end
;
1
:
begin
//hay elem[0]
//es independiente de "inMidTok"
Result
:=
MatchPatternElement
(
0
,
curEnv
.
tok0
,
curEnv
.
caseSen
);
end
;
end
;
end
;
function
TFaOpenEvent
.
MatchPattern
(
const
curEnv
:
TFaCursorEnviron
):
boolean
;
function
ItemInBlock
:
boolean
;
inline
;
begin
Result
:=
(
block
=
nil
)
or
//es válido para todos los bloques
(
block
=
curEnv
.
curBlock
);
end
;
begin
Result
:=
MatchPatternBefore
(
curEnv
)
and
MatchPatternAfter
(
curEnv
)
and
ItemInBlock
;
{$IFDEF Verbose}
if
Result
then
debugln
(
' -> Aplicable Pat: %s con %d elem.'
,
[
name
,
items
.
Count
]);
{$ENDIF}
end
;
procedure
TFaOpenEvent
.
AddItem
(
txt
:
string
;
idxIcon
:
integer
);
{Agrega un ítem al evento de apertura. Versión simplificada}
var
it
:
TFaCompletItem
;
begin
it
:=
TFaCompletItem
.
Create
;
it
.
Caption
:=
txt
;
it
.
idxIcon
:=
idxIcon
;
Items
.
Add
(
it
);
end
;
procedure
TFaOpenEvent
.
AddItems
(
lst
:
TStringList
;
idxIcon
:
integer
);
{Agrega una lista de ítems al evento de apertura, desde un TStringList}
var
it
:
TFaCompletItem
;
i
:
Integer
;
begin
for
i
:=
0
to
lst
.
Count
-
1
do
begin
it
:=
TFaCompletItem
.
Create
;
it
.
Caption
:=
lst
[
i
];
it
.
idxIcon
:=
idxIcon
;
Items
.
Add
(
it
);
end
;
end
;
procedure
TFaOpenEvent
.
AddItems
(
list
:
string
;
idxIcon
:
integer
);
{Agrega una lista de ítems al evento de apertura, desde una cadena }
var
lst
:
TStringList
;
begin
lst
:=
TStringList
.
Create
;
//troza
lst
.
Delimiter
:=
' '
;
lst
.
DelimitedText
:=
list
;
//agrega
AddItems
(
lst
,
idxIcon
);
lst
.
Destroy
;
end
;
procedure
TFaOpenEvent
.
AddList
(
Alist
:
TFaCompletionList
;
OnlyRef
:
boolean
);
{Agrega los datos de una lista o la referencia.}
begin
if
OnlyRef
then
begin
Lists
.
Add
(
Alist
);
//solo guarda la referencia
end
else
begin
//No implementado. Es problemático incluir referencias de objetos no administrados.
//Lo mejor sería usar una lista adicional, sin administración de objetos.
// for it in Alist.Items do begin
// Items.Add(it);
// end;
end
;
end
;
procedure
TFaOpenEvent
.
ClearAvails
;
begin
Avails
.
Clear
;
end
;
procedure
TFaOpenEvent
.
ClearItems
;
begin
Items
.
Clear
;
end
;
constructor
TFaOpenEvent
.
Create
(
hlt0
:
TSynFacilSyn
);
begin
Items
:=
TFaCompletItems
.
Create
(
true
);
//Lista con administración
Avails
:=
TFaCompletItems
.
Create
(
false
);
//referecnias sin administración
Lists
:=
TFaCompletionLists
.
Create
(
false
);
hlt
:=
hlt0
;
end
;
destructor
TFaOpenEvent
.
Destroy
;
begin
Lists
.
Destroy
;
Avails
.
Destroy
;
Items
.
Destroy
;
inherited
Destroy
;
end
;
{ TSynCompletionF }
function
TSynCompletionF
.
OnSynCompletionPaintItem
(
const
AKey
:
string
;
ACanvas
:
TCanvas
;
X
,
Y
:
integer
;
IsSelected
:
boolean
;
Index
:
integer
):
boolean
;
var
// MaxX: Integer;
// hl: TSynCustomHighlighter;
Capt
:
String
;
idIcon
:
Integer
;
obj
:
TObject
;
begin
{
ACanvas.Font.Style:=[];
if not IsSelected then
ACanvas.Font.Color := FActiveEditDefaultFGColor
else
ACanvas.Font.Color := FActiveEditSelectedFGColor;
MaxX:=TheForm.ClientWidth;}
{ hl := nil;
if Editor <> nil then
hl := Editor.Highlighter;}
Capt
:=
ItemList
[
Index
];
if
IconList
<>
nil
then
begin
obj
:=
ItemList
.
Objects
[
Index
];
if
obj
=
nil
then
begin
//Puede pasar cuando no se ha asignado un objeto, sino solo texto
idIcon
:=
-
1
end
else
begin
idIcon
:=
TFaCompletItem
(
obj
).
idxIcon
;
end
;
IconList
.
Draw
(
ACanvas
,
X
+
2
,
Y
,
idIcon
);
ACanvas
.
TextOut
(
X
+
20
,
Y
,
Capt
);
end
else
begin
ACanvas
.
TextOut
(
X
+
2
,
Y
,
Capt
);
// PaintCompletionItem(AKey, ACanvas, X, Y, MaxX, IsSelected, Index, self);
end
;
Result
:=
true
;
//para indicar que lo intercepta
end
;
procedure
TSynCompletionF
.
Refresh
;
begin
if
ItemList
.
Count
=
0
then
begin
//cierra por no tener elementos
Deactivate
;
end
else
begin
//hay elementos
Position
:=
0
;
//selecciona el primero
end
;
TheForm
.
Invalidate
;
//para que se actualice
end
;
constructor
TSynCompletionF
.
Create
(
AOwner
:
TComponent
);
begin
inherited
Create
(
AOwner
);
TheForm
.
ShowSizeDrag
:=
True
;
//intercepta este evento
OnPaintItem
:=@
OnSynCompletionPaintItem
;
end
;
{ TSynFacilComplet }
procedure
TSynFacilComplet
.
ReadSpecialIdentif
;
//Hace una exploración para leer todos los identificadores especiales en la tabla
//SpecIdentifiers[].
var
met
:
TFaProcMetTable
;
p
:
TPtrATokEspec
;
c
:
Char
;
i
:
Integer
;
n
:
Integer
;
begin
setlength
(
SpecIdentifiers
,
0
);
if
CaseSensitive
then
begin
for
c
in
[
'A'
..
'Z'
,
'a'
..
'z'
]
do
begin
TableIdent
(
c
,
p
,
met
);
if
p
<>
nil
then
begin
for
i
:=
0
to
high
(
p
^)
do
begin
n
:=
high
(
SpecIdentifiers
)+
1
;
//tamaño de matriz
setlength
(
SpecIdentifiers
,
n
+
1
);
SpecIdentifiers
[
n
]
:=
p
^[
i
];
end
;
end
;
end
;
end
else
begin
//no es sensible a la caja
for
c
in
[
'A'
..
'Z'
]
do
begin
TableIdent
(
c
,
p
,
met
);
if
p
<>
nil
then
begin
for
i
:=
0
to
high
(
p
^)
do
begin
n
:=
high
(
SpecIdentifiers
)+
1
;
//tamaño de matriz
setlength
(
SpecIdentifiers
,
n
+
1
);
SpecIdentifiers
[
n
]
:=
p
^[
i
];
end
;
end
;
end
;
end
;
end
;
function
TSynFacilComplet
.
LoadSyntaxFromPath
(
SourceFile
:
string
;
path
:
string
;
CaseSens
:
boolean
=
false
):
string
;
//Carga un archivo de sintaxis, buscando el resaltador apropiado en un directorio.
//Si encuentra el archivo de sintaxis apropiado, devuelve el nombre del archivo usado
//(sin incluir la ruta), de otra forma, devuelve una cadena vacía.
var
ext
:
String
;
Hay
:
Boolean
;
SR
:
TSearchRec
;
rut
:
String
;
begin
Result
:=
''
;
ext
:=
ExtractFileExt
(
SourceFile
);
if
ext
<>
''
then
ext
:=
copy
(
ext
,
2
,
10
);
//quita el punto
//explora los lenguajes para encontrar alguno que soporte la extensión dada
Hay
:=
FindFirst
(
path
+
'\*.xml'
,
faAnyFile
-
faDirectory
,
SR
)
=
0
;
while
Hay
do
begin
//encontró archivo, lee sus extensiones
rut
:=
path
+
'\'
+
SR
.
name
;
if
XMLFileHaveExten
(
rut
,
ext
,
CaseSens
)
then
begin
//encontró
LoadFromFile
(
rut
);
//carga sintaxis
Result
:=
SR
.
name
;
exit
;
end
;
//no encontró extensión, busca siguiente archivo
Hay
:=
FindNext
(
SR
)
=
0
;
end
;
//no encontró
end
;
procedure
TSynFacilComplet
.
SetIconList
(
AValue
:
TImageList
);
begin
// if FIconList=AValue then Exit;
MenuComplet
.
IconList
:=
AValue
;
end
;
function
TSynFacilComplet
.
FindOpenEvent
(
oeName
:
string
):
TFaOpenEvent
;
var
eve
:
TFaOpenEvent
;
begin
for
eve
in
OpenEvents
do
begin
if
eve
.
name
=
oeName
then
exit
(
eve
);
end
;
exit
(
nil
);
end
;
procedure
TSynFacilComplet
.
ProcXMLOpenOn
(
nodo
:
TDOMNode
);
{Procesa el bloque <OpenOn ... >}
procedure
GetItemsFromNode
(
nodo
:
TDOMNode
;
opEve
:
TFaOpenEvent
;
idxIcon
:
integer
);
var
listIden
:
DOMString
;
i
,
j
:
Integer
;
nodo2
:
TDOMNode
;
lst
:
TFaCompletionList
;
tIncAttr
,
tIncList
:
TFaXMLatrib
;
tipTok
:
integer
;
tIncIcnI
:
TFaXMLatrib
;
IncIcnI
:
Integer
;
begin
listIden
:=
nodo
.
TextContent
;
if
listIden
<>
''
then
begin
//Se ha especificado lista de palabras. Los carga
opEve
.
AddItems
(
AnsiString
(
listIden
),
idxIcon
);
end
;
//explora nodos
for
i
:=
0
to
nodo
.
ChildNodes
.
Count
-
1
do
begin
nodo2
:=
nodo
.
ChildNodes
[
i
];
if
UpCAse
(
nodo2
.
NodeName
)=
'INCLUDE'
then
begin
//incluye lista de palabras por atributo
//lee parámetros
tIncAttr
:=
ReadXMLParam
(
nodo2
,
'Attribute'
);
tIncList
:=
ReadXMLParam
(
nodo2
,
'List'
);
tIncIcnI
:=
ReadXMLParam
(
nodo2
,
'IconIndex'
);
CheckXMLParams
(
nodo2
,
'Attribute List IconIndex'
);
//puede generar excepción
if
tIncAttr
.
hay
then
begin
//se pide agregar la lista de identificadores de un atributo en especial
if
IsAttributeName
(
tIncAttr
.
val
)
then
begin
tipTok
:=
GetAttribIDByName
(
tIncAttr
.
val
);
//tipo de atributo
if
tIncIcnI
.
hay
then
IncIcnI
:=
tIncIcnI
.
n
else
IncIcnI
:=-
1
;
//busca los identificadores para agregarlos
for
j
:=
0
to
high
(
SpecIdentifiers
)
do
begin
if
SpecIdentifiers
[
j
].
tTok
=
tipTok
then
begin
opEve
.
AddItem
(
SpecIdentifiers
[
j
].
orig
,
IncIcnI
);
{Agrega a lista por defecto.}
end
;
end
;
end
else
begin
//atributo no existe
raise
ESynFacilSyn
.
Create
(
Format
(
ERR_ATTRIB_NO_EXIST
,[
nodo2
.
NodeValue
]));
end
;
end
;
if
tIncList
.
hay
then
begin
//se pide agregar los ítems de una lista
lst
:=
GetListByName
(
tIncList
.
val
);
if
lst
<>
nil
then
begin
opEve
.
AddList
(
lst
,
true
);
end
else
begin
raise
ESynFacilSyn
.
Create
(
Format
(
ERR_LIST_NO_EXIST
,[
tIncList
.
val
]));
end
;
end
;
end
else
if
nodo2
.
NodeName
=
'#text'
then
begin
//éste nodo aparece siempre que haya espacios, saltos o tabulaciones
end
else
if
LowerCase
(
nodo2
.
NodeName
)
=
'#comment'
then
begin
//solo para evitar que de mensaje de error
end
else
begin
raise
ESynFacilSyn
.
Create
(
Format
(
ERR_INVAL_LAB_OPNON
,[
nodo2
.
NodeName
]));
end
;
end
;
end
;
var
tNamPatt
,
tBefPatt
,
tAftPatt
:
TFaXMLatrib
;
filt
:
TFaFilterList
;
tFilPatt
,
tBlkPatt
:
TFaXMLatrib
;
opEve
:
TFaOpenEvent
;
blk
:
TFaSynBlock
;
success
:
boolean
;
tActPatt
:
TFaXMLatrib
;
tIcnPatt
:
TFaXMLatrib
;
idxIcon
:
Integer
;
begin
tNamPatt
:=
ReadXMLParam
(
nodo
,
'Name'
);
tBefPatt
:=
ReadXMLParam
(
nodo
,
'BeforePattern'
);
tAftPatt
:=
ReadXMLParam
(
nodo
,
'AfterPattern'
);
tFilPatt
:=
ReadXMLParam
(
nodo
,
'FilterBy'
);
tBlkPatt
:=
ReadXMLParam
(
nodo
,
'Block'
);
tActPatt
:=
ReadXMLParam
(
nodo
,
'Action'
);
tIcnPatt
:=
ReadXMLParam
(
nodo
,
'IconIndex'
);
CheckXMLParams
(
nodo
,
'Name BeforePattern AfterPattern FilterBy Block Action IconIndex'
);
//puede generar excepción
if
tFilPatt
.
hay
then
begin
case
UpCase
(
tFilPatt
.
val
)
of
'NONE'
:
filt
:=
fil_None
;
'LASTTOK'
:
filt
:=
fil_LastTok
;
'LASTTOKPART'
:
filt
:=
fil_LastTokPart
;
'LASTIDENT'
:
filt
:=
fil_LastIdent
;
'LASTIDENTPART'
:
filt
:=
fil_LastIdentPart
;
else
raise
ESynFacilSyn
.
Create
(
Format
(
ERR_FILTER_NO_EXIST
,[
tFilPatt
.
val
]));
end
;
end
else
begin
filt
:=
fil_LastTokPart
;
//valro por defecto
end
;
if
not
tAftPatt
.
hay
then
begin
tAftPatt
.
val
:=
'Identifier'
;
end
;
//agrega patrón
opEve
:=
AddOpenEvent
(
tAftPatt
.
val
,
tBefPatt
.
val
,
filt
);
//configrua nombre
if
tNamPatt
.
hay
then
begin
//se especificó nombre
opEve
.
name
:=
tNamPatt
.
val
;
end
else
begin
//se asuem un ombre por defecto
opEve
.
name
:=
'#Pat'
+
IntToStr
(
OpenEvents
.
Count
);
end
;
//configura bloque
if
tBlkPatt
.
hay
then
begin
blk
:=
SearchBlock
(
tBlkPatt
.
val
,
success
);
if
not
success
then
begin
raise
ESynFacilSyn
.
Create
(
ERR_INVAL_BLK_NAME
);
end
;
opEve
.
block
:=
blk
;
end
else
begin
opEve
.
block
:=
nil
;
end
;
//configura acción
if
tActPatt
.
hay
then
begin
case
UpCAse
(
tActPatt
.
val
)
of
'NONE'
:
opEve
.
Action
:=
pac_None
;
'DEFAULT'
:
opEve
.
Action
:=
pac_Default
;
'INSERT'
:
opEve
.
Action
:=
pac_Insert
;
'REPLASTTOK'
:
opEve
.
Action
:=
pac_Rep_LastTok
;
else
raise
ESynFacilSyn
.
Create
(
Format
(
ERR_ACTION_NO_EXIST
,[
tActPatt
.
val
]));
end
;
end
else
begin
opEve
.
Action
:=
pac_Default
;
end
;
//configura ícono
if
tIcnPatt
.
hay
then
idxIcon
:=
tIcnPatt
.
n
else
idxIcon
:=-
1
;
//verifica contenido
GetItemsFromNode
(
nodo
,
opEve
,
idxIcon
);
end
;
procedure
TSynFacilComplet
.
ProcCompletionLabel
(
nodo
:
TDOMNode
);
//Procesa la etiqueta <Completion>, que es el bloque que define todo el sistema de
//completado de código.
var
listIden
:
string
;
i
,
j
:
Integer
;
nodo2
:
TDOMNode
;
tipTok
:
integer
;
hayOpen
:
Boolean
;
tIncAttr
:
TFaXMLatrib
;
tLstName
,
tLstIcnI
:
TFaXMLatrib
;
defPat
:
TFaOpenEvent
;
cmpList
:
TFaCompletionList
;
idxIcon
:
integer
;
tIncIcnI
:
TFaXMLatrib
;
IncIcnI
:
Integer
;
begin
hayOpen
:=
false
;
//inicia bandera
//crea evento de apertura por defecto
defPat
:=
AddOpenEvent
(
'Identifier'
,
''
,
fil_LastTokPart
);
defpat
.
name
:=
'#Def'
;
////////// explora nodos hijos //////////
for
i
:=
0
to
nodo
.
ChildNodes
.
Count
-
1
do
begin
nodo2
:=
nodo
.
ChildNodes
[
i
];
if
UpCAse
(
nodo2
.
NodeName
)=
'INCLUDE'
then
begin
//incluye lista de palabras por atributo
//lee parámetros
tIncAttr
:=
ReadXMLParam
(
nodo2
,
'Attribute'
);
tIncIcnI
:=
ReadXMLParam
(
nodo2
,
'IconIndex'
);
CheckXMLParams
(
nodo2
,
'Attribute IconIndex'
);
//puede generar excepción
if
tIncAttr
.
hay
then
begin
//se pide agregar la lista de identificadores de un atributo en especial
if
IsAttributeName
(
tIncAttr
.
val
)
then
begin
tipTok
:=
GetAttribIDByName
(
tIncAttr
.
val
);
//tipo de atributo
if
tIncIcnI
.
hay
then
IncIcnI
:=
tIncIcnI
.
n
else
IncIcnI
:=-
1
;
//busca los identificadores para agregarlos
for
j
:=
0
to
high
(
SpecIdentifiers
)
do
begin
if
SpecIdentifiers
[
j
].
tTok
=
tipTok
then
begin
defPat
.
AddItem
(
SpecIdentifiers
[
j
].
orig
,
IncIcnI
);
{Agrega a lista por defecto.}
end
;
end
;
end
else
begin
//atributo no existe
raise
ESynFacilSyn
.
Create
(
Format
(
ERR_ATTRIB_NO_EXIST
,[
nodo2
.
NodeValue
]));
end
;
end
;
end
else
if
UpCAse
(
nodo2
.
NodeName
)=
'OPENON'
then
begin
//evento de apertura
//lee parámetros
hayOpen
:=
true
;
//marca para indicar que hay lista
ProcXMLOpenOn
(
nodo2
);
//puede generar excepción.
end
else
if
UpCAse
(
nodo2
.
NodeName
)=
'LIST'
then
begin
//forma alternativa para lista de palabras
//Esta forma de declaración permite definir un orden en la carga de listas
//lee parámetros
tLstName
:=
ReadXMLParam
(
nodo2
,
'Name'
);
tLstIcnI
:=
ReadXMLParam
(
nodo2
,
'IconIndex'
);
CheckXMLParams
(
nodo2
,
'Name IconIndex'
);
//puede generar excepción
if
not
tLstName
.
hay
then
begin
tLstName
.
val
:=
'#list'
+
IntToStr
(
CompletLists
.
Count
);
end
;
cmpList
:=
AddComplList
(
tLstName
.
val
);
//Ve si tiene contenido
listIden
:=
AnsiString
(
nodo2
.
TextContent
);
if
listIden
<>
''
then
begin
if
tLstIcnI
.
hay
then
idxIcon
:=
tLstIcnI
.
n
else
idxIcon
:=
-
1
;
cmpList
.
AddItems
(
listIden
,
idxIcon
);
{Agrega los ítems de la lista en este patrón, por si se llegase a utilizar. Se
hace aquí mismo para mantener el orden, si es que se mezcla con etiquetas <INCLUDE>
o listas de palabras indicadas directamente en <COMPLETION> ... </COMPLETION>}
defPat
.
AddItems
(
listIden
,
idxIcon
);
end
;
end
else
if
nodo2
.
NodeName
=
'#text'
then
begin
//Este nodo aparece siempre que haya espacios, saltos o tabulaciones
//Puede ser la lista de palabras incluidas directamente en <COMPLETION> </COMPLETION>
defPat
.
AddItems
(
AnsiString
(
nodo2
.
NodeValue
),
-
1
);
end
else
if
LowerCase
(
nodo2
.
NodeName
)
=
'#comment'
then
begin
//solo para evitar que de mensaje de error
end
else
begin
raise
ESynFacilSyn
.
Create
(
Format
(
ERR_INVAL_LAB_COMP
,[
nodo2
.
NodeName
]));
end
;
end
;
//verifica las opciones por defecto
if
hayOpen
then
begin
//Se ha especificado patrones de apretura.
OpenEvents
.
Remove
(
defPat
);
//elimina el evento por defecto, porque no se va a usar
end
else
begin
//No se ha especificado ningún evento de apertura
//mantiene el evento por defecto
end
;
end
;
procedure
TSynFacilComplet
.
LoadFromFile
(
const
Filename
:
string
);
var
doc
:
TXMLDocument
;
i
:
Integer
;
nodo
:
TDOMNode
;
nombre
:
WideString
;
tCasSen
:
TFaXMLatrib
;
tOpenKUp
:
TFaXMLatrib
;
tSelOEnt
:
TFaXMLatrib
;
begin
inherited
LoadFromFile
(
Filename
);
{Puede disparar excepción. El mesnajes de error generado
incluye el nombre del archivo}
OpenOnKeyUp
:=
true
;
//por defecto
ReadSpecialIdentif
;
//carga los identificadores especiales
OpenEvents
.
Clear
;
//limpia patrones de apertura
CompletLists
.
Clear
;
try
ReadXMLFile
(
doc
,
Filename
);
//carga archivo
//procede a la carga de la etiqueta <COMPLETION>
for
i
:=
0
to
doc
.
DocumentElement
.
ChildNodes
.
Count
-
1
do
begin
// Lee un Nodo o Registro
nodo
:=
doc
.
DocumentElement
.
ChildNodes
[
i
];
nombre
:=
UpCase
(
nodo
.
NodeName
);
if
nombre
=
'COMPLETION'
then
begin
//carga los parámetros
tCasSen
:=
ReadXMLParam
(
nodo
,
'CaseSensitive'
);
tOpenKUp
:=
ReadXMLParam
(
nodo
,
'OpenOnKeyUp'
);
tSelOEnt
:=
ReadXMLParam
(
nodo
,
'SelectOnEnter'
);
//carga atributos leidos
if
tCasSen
.
hay
then
//si se especifica
CaseSensComp
:=
tCasSen
.
bol
//se lee
else
//si no
CaseSensComp
:=
CaseSensitive
;
//toma el del resaltador
if
tOpenKUp
.
hay
then
OpenOnKeyUp
:=
tOpenKUp
.
bol
;
if
tSelOEnt
.
hay
then
SelectOnEnter
:=
tSelOEnt
.
bol
;
ProcCompletionLabel
(
nodo
);
//Puede generar error
end
;
end
;
doc
.
Free
;
//libera
except
on
e
:
Exception
do
begin
//Completa el mensaje con nombre de archivo, porque esta parte del código
//no lo incluye.
e
.
Message
:=
ERROR_LOADING_
+
Filename
+
#
13
#
10
+
e
.
Message
;
doc
.
Free
;
raise
//genera de nuevo
end
;
end
;
end
;
procedure
TSynFacilComplet
.
SelectEditor
(
ed0
:
TSynEdit
);
//Inicia el motor de ayuda contextual, en el editor indicado
begin
ed
:=
ed0
;
//guarda referencia
if
ed
=
nil
then
begin
showmessage
(
'ERROR: Se requiere un editor para el autocompletado.'
);
ed
:=
nil
;
//para indicar que no es válido
exit
;
end
;
//asigna por si acaso no se había hecho
ed
.
Highlighter
:=
self
;
MenuComplet
:=
TSynCompletionF
.
Create
(
ed
.
Owner
);
//crea menú contextual en el formulario
MenuComplet
.
Editor
:=
ed
;
//asigna editor
MenuComplet
.
Width
:=
200
;
//ancho inicial
MenuComplet
.
OnExecute
:=@
MenuComplet_OnExecute
;
MenuComplet
.
OnCodeCompletion
:=@
OnCodeCompletion
;
//Para evitar que reemplace automáticamente, cuando se abre con un solo elemento en la lista
MenuComplet
.
AutoUseSingleIdent
:=
false
;
//iintercepta eventos de teclado, para cambiar comportamiento
MenuComplet
.
OnKeyDown
:=@
FormKeyDown
;
MenuComplet
.
OnUTF8KeyPress
:=@
FormUTF8KeyPress
;
//eventos del teclado de la ventana de completado
end
;
procedure
TSynFacilComplet
.
UnSelectEditor
;
//Método que quita la ayuda contextual al formulario indicado y al editor.
//Se debería llamar siempre si se ha llamado a SelectEditor().
begin
if
MenuComplet
=
nil
then
exit
;
//nunca se creó
MenuComplet
.
Destroy
;
MenuComplet
:=
nil
;
//lo marca como liberado
end
;
function
TSynFacilComplet
.
CheckForClose
:
boolean
;
{Verifica si se dan las condiciones como para cerrar la ventana de completado, y si se da
el caso, la cierra y devuelve TRUE.}
var
opEve
:
TFaOpenEvent
;
begin
curEnv
.
LookAround
(
ed
,
CaseSensComp
);
//para actualizar el nuevo estado
opEve
:=
FindOpenEventMatching
;
//Busca evento de apertura que aplica
if
opEve
=
nil
then
begin
MenuComplet
.
Deactivate
;
CurOpenEve
:=
nil
;
//como se cierra ya no hay evento activo
exit
(
true
);
//ninguno aplica
end
;
//hay un aptrón que aplica
if
opEve
=
CurOpenEve
then
begin
//Es el mismo
exit
(
false
);
end
else
begin
//Es uno nuevo
{Aquí se puede decidir cerrar la ventana, porque aplica otro patrón, pero yo prefiero
el comportamiento en el que se aplica direcatamente el nuevo patrón, sin necesidad de
esperar a pulsar otra tecla.}
CurOpenEve
:=
opEve
;
//actualiza referencia
CurOpenEve
.
LoadItems
(
curEnv
);
//carga ítems
exit
(
false
);
end
;
end
;
function
TSynFacilComplet
.
AddOpenEvent
(
AfterPattern
,
BeforePattern
:
string
;
filter
:
TFaFilterList
):
TFaOpenEvent
;
{Permite agregar un evento de apertura. Devuelve una referencia al evento agregado.}
var
opEve
:
TFaOpenEvent
;
errStr
:
string
;
begin
opEve
:=
TFaOpenEvent
.
Create
(
self
);
///////analiza AfterPattern
opEve
.
ClearBeforePatt
;
while
AfterPattern
<>
''
do
begin
opEve
.
AddBeforeElement
(
AfterPattern
,
errStr
);
if
errStr
<>
''
then
begin
opEve
.
Destroy
;
//destruye proque no lo va a agregar
raise
ESynFacilSyn
.
Create
(
ERR_PAR_AFT_PATT
+
':'
+
errStr
);
end
;
end
;
///////analiza BeforePattern
opEve
.
ClearAfterPatt
;
while
BeforePattern
<>
''
do
begin
opEve
.
AddAfterElement
(
BeforePattern
,
errStr
);
if
errStr
<>
''
then
begin
opEve
.
Destroy
;
//destruye proque no lo va a agregar
raise
ESynFacilSyn
.
Create
(
ERR_PAR_BEF_PATT
+
':'
+
errStr
);
end
;
end
;
//Hay propiedades que no se inician aquí como el nombre
opEve
.
filter
:=
filter
;
//fija filtro
opEve
.
block
:=
nil
;
opEve
.
Action
:=
pac_Default
;
//acción por defecto
OpenEvents
.
Add
(
opEve
);
//agrega
Result
:=
opEve
;
//devuelve referencia
end
;
function
TSynFacilComplet
.
AddComplList
(
lstName
:
string
):
TFaCompletionList
;
var
lst
:
TFaCompletionList
;
begin
lst
:=
TFaCompletionList
.
Create
;
lst
.
Name
:=
lstName
;
CompletLists
.
Add
(
lst
);
Result
:=
lst
;
end
;
function
TSynFacilComplet
.
GetListByName
(
lstName
:
string
):
TFaCompletionList
;
{Devuelve la referencia a una lista, usando su nombre. Si no enecuentra devuelve NIL}
var
l
:
TFaCompletionList
;
UlstName
:
String
;
begin
UlstName
:=
upcase
(
lstName
);
for
l
in
CompletLists
do
begin
if
Upcase
(
l
.
Name
)
=
UlstName
then
exit
(
l
);
end
;
exit
(
nil
);
end
;
procedure
TSynFacilComplet
.
OnCodeCompletion
(
var
Value
:
string
;
SourceValue
:
string
;
var
SourceStart
,
SourceEnd
:
TPoint
;
KeyChar
:
TUTF8Char
;
Shift
:
TShiftState
);
//Se genera antes de hacer el reemplazo del texto. "Value", es la cadena que se usará
//para reemplazar la palabra en el editor.
begin
//Se puede usar "MenuComplet.Position" para saber el elemento seleccionado de la lista
// value := 'hola';
// showmessage(value);
end
;
function
TSynFacilComplet
.
FindOpenEventMatching
:
TFaOpenEvent
;
{Devuelve el priumer evento que coincide con el entorno actual "curEnv". Si ninguno coincide,
devuelve NIL.}
var
opEve
:
TFaOpenEvent
;
begin
for
opEve
in
OpenEvents
do
begin
if
opEve
.
MatchPattern
(
curEnv
)
then
begin
Result
:=
opEve
;
//devuelve referencia
exit
;
end
;
end
;
exit
(
nil
);
//no enccontró
end
;
function
TSynFacilComplet
.
GetIconList
:
TImageList
;
begin
Exit
(
MenuComplet
.
IconList
);
end
;
procedure
TSynFacilComplet
.
MenuComplet_OnExecute
(
Sender
:
TObject
);
{Este evento se genera antes de abrir el menú de completado.
Se puede abrir al pulsar una tecla común. La otra opción es por un atajo.
Llena la lista "AvailItems", con los ítems que correspondan de acuerdo a la posición
actual del cursor y de la configuración del archivo XML.
Luego llena el menú contextual con los ítems filtrados de acuerdo a la posición actual.}
var
opEve
:
TFaOpenEvent
;
begin
MenuComplet
.
ItemList
.
Clear
;
//inicia menú
//Prepara para llenar la lista de completado
curEnv
.
LookAround
(
ed
,
CaseSensComp
);
//Lee entorno.
CurOpenEve
:=
nil
;
opEve
:=
FindOpenEventMatching
;
//Busca evento de apertura que aplica
if
opEve
<>
nil
then
begin
//Se cumple el evento en la posición actual del cursor
opEve
.
LoadItems
(
curEnv
);
//carga los ítems con los que trabajará.
CurOpenEve
:=
opEve
;
//guarda referencia
end
;
//Llena el menú de completado
FillCompletMenuFiltered
;
//hace el primer filtrado
{$IFDEF Verbose}
debugln
(
'Llenado con %d'
,
[
MenuComplet
.
ItemList
.
Count
]);
{$ENDIF}
end
;
procedure
TSynFacilComplet
.
FormKeyDown
(
Sender
:
TObject
;
var
Key
:
Word
;
Shift
:
TShiftState
);
procedure
SeleccionarPalabra
;
{Selecciona, la palabra actual de la lista de completado. Usamos nuestra propia
rutina en vez de OnValidate(), para poder reemplazar identificadores de acuerdo
a la definición de sintaxis, además de otros tipos de tokens.}
var
NewWord
:
String
;
obj
:
TObject
;
begin
if
CurrentLines
=
nil
then
exit
;
//Reemplaza actual
obj
:=
MenuComplet
.
ItemList
.
Objects
[
MenuComplet
.
Position
];
if
obj
=
nil
then
begin
//Puede pasar cuando no se ha asignado un objeto, sino solo texto
end
else
begin
NewWord
:=
TFaCompletItem
(
obj
).
Replac
;
CurOpenEve
.
DoAction
(
ed
,
curEnv
,
NewWord
);
//realiza la acción programada
end
;
CloseCompletionWindow
;
//cierra
end
;
begin
//debugln(' Form.OnKeyDown Key='+ IntToStr(Key) +':'+IntToStr(ed.CaretX));
case
Key
of
VK_RETURN
:
begin
if
Shift
=
[]
then
begin
if
SelectOnEnter
then
//solo si está permitido reemplazar
SeleccionarPalabra
;
Key
:=
VK_UNKNOWN
;
//marca para que no lo procese SynCompletion
end
else
begin
Key
:=
VK_UNKNOWN
;
//marca para que no lo procese SynCompletion
CloseCompletionWindow
;
//cierra
end
;
end
;
VK_HOME
:
begin
if
Shift
=
[]
then
begin
//envía al editor
ed
.
CommandProcessor
(
ecLineStart
,
#
0
,
nil
);
MenuComplet
.
Deactivate
;
//desactiva
end
else
if
Shift
=
[
ssShift
]
then
begin
ed
.
CommandProcessor
(
ecSelLineStart
,
#
0
,
nil
);
MenuComplet
.
Deactivate
;
//desactiva
end
;
Key
:=
VK_UNKNOWN
;
//marca para que no lo procese SynCompletion
end
;
VK_END
:
begin
if
Shift
=
[]
then
begin
//envía al editor
ed
.
CommandProcessor
(
ecLineEnd
,
#
0
,
nil
);
MenuComplet
.
Deactivate
;
//desactiva
end
else
if
Shift
=
[
ssShift
]
then
begin
ed
.
CommandProcessor
(
ecSelLineEnd
,
#
0
,
nil
);
MenuComplet
.
Deactivate
;
//desactiva
end
;
Key
:=
VK_UNKNOWN
;
//marca para que no lo procese SynCompletion
end
;
VK_BACK
:
begin
if
Shift
=
[]
then
begin
//sin Ctrl o Shift
ed
.
CommandProcessor
(
ecDeleteLastChar
,
#
0
,
nil
);
//envía al editor
if
CheckForClose
then
begin
Key
:=
VK_UNKNOWN
;
exit
end
;
FillCompletMenuFiltered
;
end
;
Key
:=
VK_UNKNOWN
;
//marca para que no lo procese SynCompletion
end
;
VK_DELETE
:
begin
if
Shift
=
[]
then
begin
//sin Ctrl o Shift
ed
.
CommandProcessor
(
ecDeleteChar
,
#
0
,
nil
);
//envía al editor
if
CheckForClose
then
begin
Key
:=
VK_UNKNOWN
;
exit
end
;
FillCompletMenuFiltered
;
end
;
Key
:=
VK_UNKNOWN
;
//marca para que no lo procese SynCompletion
end
;
VK_LEFT
:
begin
if
Shift
=
[]
then
begin
//envía al editor
ed
.
CommandProcessor
(
ecLeft
,
#
0
,
nil
);
if
CheckForClose
then
begin
Key
:=
VK_UNKNOWN
;
exit
end
;
FillCompletMenuFiltered
;
end
else
if
Shift
=
[
ssShift
]
then
begin
ed
.
CommandProcessor
(
ecSelLeft
,
#
0
,
nil
);
MenuComplet
.
Deactivate
;
//desactiva
end
else
if
Shift
=
[
ssCtrl
]
then
begin
ed
.
CommandProcessor
(
ecWordLeft
,
#
0
,
nil
);
MenuComplet
.
Deactivate
;
//desactiva
end
else
if
Shift
=
[
ssShift
,
ssCtrl
]
then
begin
ed
.
CommandProcessor
(
ecSelWordLeft
,
#
0
,
nil
);
MenuComplet
.
Deactivate
;
//desactiva
end
;
Key
:=
VK_UNKNOWN
;
//marca para que no lo procese SynCompletion
end
;
VK_RIGHT
:
begin
if
Shift
=
[]
then
begin
//envía al editor
ed
.
CommandProcessor
(
ecRight
,
#
0
,
nil
);
if
CheckForClose
then
begin
Key
:=
VK_UNKNOWN
;
exit
end
;
FillCompletMenuFiltered
;
end
else
if
Shift
=
[
ssShift
]
then
begin
ed
.
CommandProcessor
(
ecSelRight
,
#
0
,
nil
);
MenuComplet
.
Deactivate
;
//desactiva
end
else
if
Shift
=
[
ssCtrl
]
then
begin
ed
.
CommandProcessor
(
ecWordRight
,
#
0
,
nil
);
MenuComplet
.
Deactivate
;
//desactiva
end
else
if
Shift
=
[
ssShift
,
ssCtrl
]
then
begin
ed
.
CommandProcessor
(
ecSelWordRight
,
#
0
,
nil
);
MenuComplet
.
Deactivate
;
//desactiva
end
;
Key
:=
VK_UNKNOWN
;
//marca para que no lo procese SynCompletion
end
;
VK_TAB
:
begin
if
Shift
=
[]
then
begin
SeleccionarPalabra
;
SearchOnKeyUp
:=
false
;
{para que no intente buscar luego en el evento KeyUp,
porque TAB está configurado como tecla válida para abrir la lista, y si
se abre, (y no se ha isertado el TAB), aparecerá de nuevo el mismo
identificador en la lista}
Key
:=
VK_UNKNOWN
;
//marca para que no lo procese SynCompletion
end
;
end
;
end
;
//si no lo procesó aquí, lo procesará SynCompletion
end
;
procedure
TSynFacilComplet
.
FormUTF8KeyPress
(
Sender
:
TObject
;
var
UTF8Key
:
TUTF8Char
);
//Este evento se dispara cuando ya está visible la ventana de autocompletado y por
//lo tanto no se generará el evento KeyUp
begin
//Como este evento se genera apneas pulsar una tecla, primero pasamos la tecla al
//editor para que lo procese y así tendremos el texto modificado, como si estuviéramos
//después de un KeyUp().
ed
.
CommandProcessor
(
ecChar
,
UTF8Key
,
nil
);
UTF8Key
:=
''
;
//limpiamos para que ya no lo procese SynCompletion
//ahora ya tenemos al editor cambiado
//Las posibles opciones ya se deben haber llenado. Aquí solo filtramos.
if
CheckForClose
then
exit
;
FillCompletMenuFiltered
;
end
;
procedure
TSynFacilComplet
.
UTF8KeyPress
(
Sender
:
TObject
;
var
UTF8Key
:
TUTF8Char
);
{Debe recibir la tecla pulsada aquí, y guardarla para KeyUp, porque allí no se puede
reconocer caracteres ASCII. Se usa UTF para hacerlo más fléxible}
begin
UtfKey
:=
UTF8Key
;
//guarda tecla
end
;
procedure
TSynFacilComplet
.
KeyUp
(
Sender
:
TObject
;
var
Key
:
Word
;
Shift
:
TShiftState
);
{Verifica la tecla pulsada, para determinar si abrir o no el menú de ayuda contextual
Debe llamarse después de que el editor ha procesado el evento, para tener
el estado final del editor
Este evento solo se ejecutará una vez antes de abrir la ventana de autocompletado}
begin
if
not
CompletionOn
then
exit
;
if
not
OpenOnKeyUp
then
exit
;
if
not
SearchOnKeyUp
then
begin
//Se ha inhabilitado este evento para el completado
SearchOnKeyUp
:=
true
;
exit
;
end
;
//verificación principal
if
MenuComplet
.
IsActive
then
begin
//verifica si debe desaparecer la ventana, por mover el cursor a una posición anterior
// if CompareCarets(Pos0, CurrentEditor.CaretXY) < 0 then
// Deactivate;
exit
;
//ya está mostrado
end
;
if
ed
=
NIL
then
exit
;
//no hay editor
if
ed
.
SelectionMode
<>
smNormal
then
exit
;
//para no interferir en modo columna
{Llama a OpenCompletionWindow(), con información del teclado, para que evalúe si
corresponde abrir la ventana de completado. De ser así, llamará a MenuComplet_OnExecute.}
OpenCompletionWindow
(
Key
,
Shift
,
UtfKey
);
//solo se mostrará si hay ítems
UtfKey
:=
''
;
//limpia por si la siguiente tecla pulsada no dispara a UTF8KeyPress()
SearchOnKeyUp
:=
true
;
//limpia bandera
End
;
procedure
TSynFacilComplet
.
FillCompletMenuFiltered
;
{Llena el menú de completado a partir de "AvailItems", aplicando el filtro de "CurOpenEve"
}
begin
//Genera la lista que coincide
{ Este proceso puede ser lento si se actualizan muchas opciones en la lista }
MenuComplet
.
ItemList
.
Clear
;
{Limpia todo aquí porque este método es llamado desde distintos
puntos del programa.}
if
CurOpenEve
<>
nil
then
begin
CurOpenEve
.
FillFilteredIn
(
curEnv
,
MenuComplet
.
ItemList
);
end
;
MenuComplet
.
Refresh
;
end
;
procedure
TSynFacilComplet
.
OpenCompletionWindow
(
vKey
:
word
;
vShift
:
TShiftState
;
vUtfKey
:
TUTF8Char
);
//Abre la ayuda contextual, en la posición del cursor.
var
p
:
TPoint
;
begin
//Verifica si se va a abrir la lista por tecla común. La otra opción es por un atajo
if
(
vKey
in
[
VK_BACK
,
VK_TAB
]
)
and
(
vShift
=[])
then
begin
//Esta tecla es válida
{$IFDEF Verbose}
debugln
(
'--Tecla válida para abrir menú: %d'
,
[
vKey
]);
{$ENDIF}
end
else
if
(
vUtfKey
<>
''
)
and
(
vUtfKey
[
1
]
in
[#
8
,#
9
,
' '
..
'@'
,
'A'
..
'z'
])
then
begin
//Esta tecla es válida
{$IFDEF Verbose}
debugln
(
'--Tecla válida para abrir menú: %d'
,
[
vKey
]);
{$ENDIF}
end
else
begin
//Los otros casos no se consideran que deban explorarse
{$IFDEF Verbose}
debugln
(
'--Tecla no válida para abrir menú: %d'
,
[
vKey
]);
{$ENDIF}
exit
;
end
;
//Calcula posición donde aparecerá el menú de completado
p
:=
Point
(
ed
.
CaretXPix
,
ed
.
CaretYPix
+
ed
.
LineHeight
);
p
.
X
:=
Max
(
0
,
Min
(
p
.
X
,
ed
.
ClientWidth
-
MenuComplet
.
Width
));
p
:=
ed
.
ClientToScreen
(
p
);
//Abre menú contextual, llamando primero a OnExecute(). Solo se mostrará si tiene elementos.
MenuComplet
.
Execute
(
''
,
p
.
x
,
p
.
y
);
//pasa una clave cualquiera para identificación posterior
end
;
procedure
TSynFacilComplet
.
CloseCompletionWindow
;
//Cierra la ventana del menú contextual
begin
MenuComplet
.
Deactivate
;
end
;
constructor
TSynFacilComplet
.
Create
(
AOwner
:
TComponent
);
begin
inherited
Create
(
AOwner
);
curEnv
:=
TFaCursorEnviron
.
Create
(
self
);
OpenEvents
:=
TFaOpenEvents
.
Create
(
True
);
CompletLists
:=
TFaCompletionLists
.
Create
(
true
);
CaseSensComp
:=
false
;
//por defecto
CompletionOn
:=
true
;
//activo por defecto
SelectOnEnter
:=
true
;
UtfKey
:=
''
;
//limpia
SearchOnKeyUp
:=
true
;
//Para iniciar la búsqueda
end
;
destructor
TSynFacilComplet
.
Destroy
;
begin
if
MenuComplet
<>
nil
then
MenuComplet
.
Destroy
;
//por si no lo liberaron
CompletLists
.
Destroy
;
OpenEvents
.
Destroy
;
curEnv
.
Destroy
;
inherited
Destroy
;
end
;
end
.
EvoUMLPlugin/src/SynFacilHighlighter.pas
0 → 100644
View file @
224b6f5f
This source diff could not be displayed because it is too large. You can
view the blob
instead.
EvoUMLPlugin/src/ituExplorerV2.dfm
View file @
224b6f5f
...
...
@@ -11751,6 +11751,8 @@ object ExplorerV2: TExplorerV2
ParentColor = False
ParentFont = False
TabOrder = 0
OnKeyUp = SynEditOQLKeyUp
OnUTF8KeyPress = SynEditOQLUTF8KeyPress
BorderStyle = bsNone
Gutter.Width = 42
Gutter.MouseActions = <>
...
...
@@ -12208,9 +12210,6 @@ object ExplorerV2: TExplorerV2
MouseLinkColor.Foreground = clBlue
LineHighlightColor.Background = clNone
LineHighlightColor.Foreground = clNone
OnCommandProcessed = SynEditOQLCommandProcessed
OnProcessCommand = SynEditOQLProcessCommand
OnProcessUserCommand = SynEditOQLProcessUserCommand
inline SynLeftGutterPartList1: TSynGutterPartList
object SynGutterMarks1: TSynGutterMarks
Width = 30
...
...
@@ -14263,19 +14262,4 @@ object ExplorerV2: TExplorerV2
0000000000000000000000000000
}
end
object scpListCompletion: TSynCompletion
Position = 0
LinesInWindow = 6
SelectedColor = clHighlight
CaseSensitive = False
Width = 262
AutoUseSingleIdent = True
ShortCut = 16416
EndOfTokenChr = '()[].'
ExecCommandID = ecSynCompletionExecute
Editor = SynEditOQL
ToggleReplaceWhole = False
Left = 648
Top = 320
end
end
EvoUMLPlugin/src/ituExplorerV2.pas
View file @
224b6f5f
...
...
@@ -7,7 +7,7 @@ uses
Dialogs
,
ExtCtrls
,
StdCtrls
,
ComCtrls
,
ActnList
,
LCLType
,
acuSQLDialectManager
,
ImgList
,
acuframework
,
utuMessage
,
IntegracaoDelphiSUML
,
acuObjectExplorer
,
Menus
,
Buttons
,
SynEdit
,
SynHighlighterSQL
,
SynCompletion
,
acuModel
,
acuRepositorySQL
,
SynEditKeyCmds
,
SynHighlighterPython
,
Types
;
acuModel
,
acuRepositorySQL
,
SynEditKeyCmds
,
SynHighlighterPython
,
SynFacilHighlighter
,
SynFacilCompletion
,
SynFacilBasic
,
Types
;
const
__WHERE
=
'WHERE'
;
...
...
@@ -45,7 +45,6 @@ type
sbStatusBar
:
TStatusBar
;
spResultXML
:
TSplitter
;
spResultObject
:
TSplitter
;
scpListCompletion
:
TSynCompletion
;
SynEditOQL
:
TSynEdit
;
SynEditSQL
:
TSynEdit
;
SynSQLSyn1
:
TSynSQLSyn
;
...
...
@@ -84,27 +83,15 @@ type
tsError
:
TTabSheet
;
memErrorText
:
TMemo
;
pnlActionBar
:
TPanel
;
procedure
opEveLoadItems
(
opEve
:
TFaOpenEvent
;
curEnv
:
TFaCursorEnviron
;
out
Cancel
:
boolean
);
procedure
actModelPanelExecute
(
Sender
:
TObject
);
procedure
edtClasseChange
(
Sender
:
TObject
);
procedure
FormClose
(
Sender
:
TObject
;
var
Action
:
TCloseAction
);
procedure
FormShow
(
Sender
:
TObject
);
procedure
actResultsPanelExecute
(
Sender
:
TObject
);
procedure
rbSequencialClick
(
Sender
:
TObject
);
procedure
scpAtributoCompletionExecute
(
Sender
:
TObject
);
procedure
scpClasseCompletionCodeCompletion
(
var
Value
:
string
;
SourceValue
:
string
;
var
SourceStart
,
SourceEnd
:
TPoint
;
KeyChar
:
TUTF8Char
;
Shift
:
TShiftState
);
procedure
SynEditOQLChange
(
Sender
:
TObject
);
procedure
SynEditOQLClickLink
(
Sender
:
TObject
;
Button
:
TMouseButton
;
Shift
:
TShiftState
;
X
,
Y
:
Integer
);
procedure
SynEditOQLCommandProcessed
(
Sender
:
TObject
;
var
Command
:
TSynEditorCommand
;
var
AChar
:
TUTF8Char
;
Data
:
pointer
);
procedure
SynEditOQLKeyPress
(
Sender
:
TObject
;
var
Key
:
char
);
procedure
SynEditOQLProcessCommand
(
Sender
:
TObject
;
var
Command
:
TSynEditorCommand
;
var
AChar
:
TUTF8Char
;
Data
:
pointer
);
procedure
SynEditOQLProcessUserCommand
(
Sender
:
TObject
;
var
Command
:
TSynEditorCommand
;
var
AChar
:
TUTF8Char
;
Data
:
pointer
);
procedure
SynEditOQLQuadClick
(
Sender
:
TObject
);
procedure
SynEditOQLKeyUp
(
Sender
:
TObject
;
var
Key
:
Word
;
Shift
:
TShiftState
);
procedure
SynEditOQLUTF8KeyPress
(
Sender
:
TObject
;
var
UTF8Key
:
TUTF8Char
);
procedure
tvModelExplorerGetSelectedIndex
(
Sender
:
TObject
;
Node
:
TTreeNode
);
procedure
tvModelExplorerItemExpanding
(
Sender
:
TObject
;
Node
:
TTreeNode
;
var
AllowExpansion
:
Boolean
);
...
...
@@ -131,6 +118,17 @@ type
fSchema
:
String
;
fUtil
:
TUtil
;
fLastExecErrorFlag
:
Boolean
;
fopEvenSearchSelect
,
fopEvenSearchFrom
,
fopEvenSearchWhere
,
fopEvenSelectAposPonto
,
fopEvenFromAposPonto
,
fopEvenWhereAposPonto
,
fopEvenselectCompleto
,
fopEvenFromCompleto
,
fopEvenWhereCompleto
:
TFaOpenEvent
;
fhlt1
:
TSynFacilComplet
;
procedure
MontarModelExploreSequencial
(
Sender
:
TObject
;
Node
:
TTreeNode
);
procedure
MontarModelExploreHieraquia
(
Sender
:
TObject
;
Node
:
TTreeNode
);
procedure
SetMetaModel
(
const
Value
:
acMetaModel
);
...
...
@@ -143,9 +141,10 @@ type
function
BuscaParcialClasse
(
piCLasse
:
string
;
piFiltrar
:
boolean
):
boolean
;
function
BuscarParcialChildClass
(
piClassTicket
:
acClassTicket
;
piFiltrar
:
boolean
):
boolean
;
function
FindParcialNodeClass
(
piClassTicketPai
:
acClassTicket
;
piFiltrar
:
boolean
):
acClassTicket
;
procedure
ExecutarSybCompletion
(
piSynCompletion
:
TSynCompletion
);
//
procedure ExecutarSybCompletion(piSynCompletion : TSynCompletion);
procedure
LoadListCompletion
(
piLinha
:
string
;
piAceitaAtributo
:
boolean
;
piLista
:
TStrings
);
procedure
PreparaSynCompletion
;
// procedure PreparaSynCompletion;
function
RetirarPalavraChaveOLQ
(
piSynEdit
:
TSynEdit
):
string
;
protected
procedure
CreateParams
(
var
Params
:
TCreateParams
);
override
;
function
Connect
:
acPersistenceSession
;
...
...
@@ -450,71 +449,21 @@ procedure TExplorerV2.rbSequencialClick(Sender: TObject);
begin
end
;
procedure
TExplorerV2
.
scpAtributoCompletionExecute
(
Sender
:
TObject
);
begin
end
;
procedure
TExplorerV2
.
scpClasseCompletionCodeCompletion
(
var
Value
:
string
;
SourceValue
:
string
;
var
SourceStart
,
SourceEnd
:
TPoint
;
KeyChar
:
TUTF8Char
;
procedure
TExplorerV2
.
SynEditOQLKeyUp
(
Sender
:
TObject
;
var
Key
:
Word
;
Shift
:
TShiftState
);
begin
end
;
procedure
TExplorerV2
.
SynEditOQLChange
(
Sender
:
TObject
);
begin
end
;
procedure
TExplorerV2
.
SynEditOQLClickLink
(
Sender
:
TObject
;
Button
:
TMouseButton
;
Shift
:
TShiftState
;
X
,
Y
:
Integer
);
begin
end
;
procedure
TExplorerV2
.
SynEditOQLCommandProcessed
(
Sender
:
TObject
;
var
Command
:
TSynEditorCommand
;
var
AChar
:
TUTF8Char
;
Data
:
pointer
);
begin
if
AChar
=
'.'
then
if
not
(
(
Shift
=
[
ssCtrl
])
and
(
Key
=
86
)
)
then
begin
PreparaSynCompletion
;
ExecutarSybCompletion
(
scpListCompletion
);
fhlt1
.
KeyUp
(
Sender
,
Key
,
Shift
);
end
;
end
;
procedure
TExplorerV2
.
SynEditOQLKeyPress
(
Sender
:
TObject
;
var
Key
:
char
);
begin
end
;
procedure
TExplorerV2
.
SynEditOQLProcessCommand
(
Sender
:
TObject
;
var
Command
:
TSynEditorCommand
;
var
AChar
:
TUTF8Char
;
Data
:
pointer
);
begin
end
;
procedure
TExplorerV2
.
SynEditOQLProcessUserCommand
(
Sender
:
TObject
;
var
Command
:
TSynEditorCommand
;
var
AChar
:
TUTF8Char
;
Data
:
pointer
);
begin
PreparaSynCompletion
;
// ExecutarSybCompletion(scpListCompletion);
end
;
procedure
TExplorerV2
.
SynEditOQLQuadClick
(
Sender
:
TObject
);
begin
end
;
procedure
TExplorerV2
.
SynEditOQLUTF8KeyPress
(
Sender
:
TObject
;
var
UTF8Key
:
TUTF8Char
);
begin
fhlt1
.
UTF8KeyPress
(
Sender
,
UTF8Key
);
end
;
...
...
@@ -717,7 +666,7 @@ begin
end
;
end
;
{
procedure TExplorerV2.ExecutarSybCompletion(piSynCompletion: TSynCompletion);
var apoint,temppoint:tpoint;
begin
...
...
@@ -727,7 +676,7 @@ begin
apoint := SynEditOQL.ClientToScreen(SynEditOQL.RowColumnToPixels(temppoint));
piSynCompletion.Execute('',apoint);
end
;
end;
}
procedure
TExplorerV2
.
LoadListCompletion
(
piLinha
:
string
;
piAceitaAtributo
:
boolean
;
piLista
:
TStrings
);
var
lClassTicketCurrent
,
...
...
@@ -852,7 +801,7 @@ begin
end
;
end
;
{
procedure TExplorerV2.PreparaSynCompletion;
var lAceitaAtributo : boolean;
ls_espaco,
...
...
@@ -898,6 +847,45 @@ begin
end;
LoadListCompletion(ls_aux,lAceitaAtributo,scpListCompletion.ItemList);
end; }
function
TExplorerV2
.
RetirarPalavraChaveOLQ
(
piSynEdit
:
TSynEdit
):
string
;
var
ls_espaco
:
string
;
li_Pos
:
integer
;
p1
,
zeroPoint
:
TPoint
;
begin
p1
:=
piSynEdit
.
CaretXY
;
zeroPoint
.
X
:=
0
;
zeroPoint
.
y
:=
piSynEdit
.
CaretY
;
result
:=
trim
(
piSynEdit
.
TextBetweenPoints
[
zeroPoint
,
p1
]);
ls_espaco
:=
' '
;
li_Pos
:=
Pos
(
__FROM
,
uppercase
(
result
));
if
li_Pos
>
0
then
begin
result
:=
trim
(
Copy
(
result
,
li_Pos
+
Length
(
__FROM
),
Length
(
result
)));
end
;
li_Pos
:=
Pos
(
__SELECT
,
uppercase
(
result
));
if
li_Pos
>
0
then
begin
result
:=
trim
(
Copy
(
result
,
li_Pos
+
Length
(
__SELECT
),
Length
(
result
)));
end
;
li_Pos
:=
Pos
(
__WHERE
,
uppercase
(
result
));
if
li_Pos
>
0
then
begin
result
:=
trim
(
Copy
(
result
,
li_Pos
+
Length
(
__WHERE
),
Length
(
result
)));
end
;
li_Pos
:=
Pos
(
ls_espaco
,
trim
(
result
));
while
li_Pos
>
0
do
begin
result
:=
trim
(
Copy
(
result
,
li_Pos
+
1
,
Length
(
result
)));
li_Pos
:=
Pos
(
ls_espaco
,
result
);
end
;
end
;
procedure
TExplorerV2
.
CreateParams
(
var
Params
:
TCreateParams
);
...
...
@@ -915,6 +903,10 @@ end;
procedure
TExplorerV2
.
FormCreate
(
Sender
:
TObject
);
var
lMetaModelGenOpt
:
TMetaModelGenerationOptions
;
lSelectBlock
,
lSelectSec
,
lFromSec
,
lWhereSec
:
TFaSynBlock
;
begin
lMetaModelGenOpt
.
CheckDBAttributeNameLength
:=
true
;
lMetaModelGenOpt
.
CheckDBTableNameLength
:=
true
;
...
...
@@ -923,7 +915,72 @@ begin
fUtil
.
AlimentarMetamodel
(
lMetaModelGenOpt
,
nil
);
FMetaModelPersistenceMap
:=
fUtil
.
MetaModelPersistenceMap
;
MetaModel
:=
fUtil
.
MetaModel
;
// PreencherSynCompletionClasses(scpListCompletion);
fhlt1
:=
TSynFacilComplet
.
Create
(
self
);
//my highlighter
//Here the syntax is defined
// ...
fhlt1
.
SelectEditor
(
SynEditOQL
);
fhlt1
.
Rebuild
;
fhlt1
.
CompletionOn
:=
true
;
fhlt1
.
OpenOnKeyUp
:=
true
;
fhlt1
.
SelectOnEnter
:=
true
;
fhlt1
.
DefTokIdentif
(
'[$A-Za-z_]'
,
'[A-Za-z0-9_]*'
);
fhlt1
.
KeywordAttribute
.
Foreground
:=
clBlue
;
fhlt1
.
AddKeyword
(
'SELECT'
);
fhlt1
.
AddKeyword
(
'FROM'
);
fhlt1
.
AddKeyword
(
'WHERE'
);
fhlt1
.
AddKeyword
(
'ORDER'
);
fhlt1
.
AddKeyword
(
'BY'
);
fhlt1
.
AddKeyword
(
'JOIN'
);
lSelectSec
:=
fhlt1
.
AddSection
(
__SELECT
);
lFromSec
:=
fhlt1
.
AddSection
(
__FROM
);
lWhereSec
:=
fhlt1
.
AddSection
(
__WHERE
);
fopEvenSearchSelect
:=
fhlt1
.
AddOpenEvent
(
'identifier'
,
''
,
fil_LastIdentPart
);
fopEvenSearchSelect
.
block
:=
lSelectSec
;
fopEvenSearchFrom
:=
fhlt1
.
AddOpenEvent
(
'identifier'
,
''
,
fil_LastIdentPart
);
fopEvenSearchFrom
.
block
:=
lFromSec
;
fopEvenSearchWhere
:=
fhlt1
.
AddOpenEvent
(
'identifier'
,
''
,
fil_LastIdentPart
);
fopEvenSearchWhere
.
block
:=
lWhereSec
;
fopEvenSelectAposPonto
:=
fhlt1
.
AddOpenEvent
(
'identifier,''.'''
,
''
,
fil_None
);
fopEvenSelectAposPonto
.
Action
:=
pac_Insert
;
fopEvenSelectAposPonto
.
block
:=
lSelectSec
;
fopEvenFromAposPonto
:=
fhlt1
.
AddOpenEvent
(
'identifier,''.'''
,
''
,
fil_None
);
fopEvenFromAposPonto
.
Action
:=
pac_Insert
;
fopEvenFromAposPonto
.
block
:=
lFromSec
;
fopEvenWhereAposPonto
:=
fhlt1
.
AddOpenEvent
(
'identifier,''.'''
,
''
,
fil_None
);
fopEvenWhereAposPonto
.
Action
:=
pac_Insert
;
fopEvenWhereAposPonto
.
block
:=
lWhereSec
;
fopEvenSelectCompleto
:=
fhlt1
.
AddOpenEvent
(
''
,
''
,
fil_None
);
fopEvenselectCompleto
.
Action
:=
pac_Insert
;
fopEvenSelectCompleto
.
block
:=
lSelectSec
;
fopEvenFromCompleto
:=
fhlt1
.
AddOpenEvent
(
''
,
''
,
fil_None
);
fopEvenFromCompleto
.
Action
:=
pac_Insert
;
fopEvenFromCompleto
.
block
:=
lFromSec
;
fopEvenWhereCompleto
:=
fhlt1
.
AddOpenEvent
(
''
,
''
,
fil_None
);
fopEvenWhereCompleto
.
Action
:=
pac_Insert
;
fopEvenWhereCompleto
.
block
:=
lWhereSec
;
fopEvenFromAposPonto
.
OnLoadItems
:=
opEveLoadItems
;
fopEvenSelectAposPonto
.
OnLoadItems
:=
opEveLoadItems
;
fopEvenWhereAposPonto
.
OnLoadItems
:=
opEveLoadItems
;
fopEvenselectCompleto
.
OnLoadItems
:=
opEveLoadItems
;
fopEvenFromCompleto
.
OnLoadItems
:=
opEveLoadItems
;
fopEvenWhereCompleto
.
OnLoadItems
:=
opEveLoadItems
;
fopEvenSearchSelect
.
OnLoadItems
:=
opEveLoadItems
;
fopEvenSearchFrom
.
OnLoadItems
:=
opEveLoadItems
;
fopEvenSearchWhere
.
OnLoadItems
:=
opEveLoadItems
;
end
;
...
...
@@ -933,6 +990,43 @@ begin
fUtil
.
Free
;
end
;
procedure
TExplorerV2
.
opEveLoadItems
(
opEve
:
TFaOpenEvent
;
curEnv
:
TFaCursorEnviron
;
out
Cancel
:
boolean
);
var
lLista
:
TStrings
;
lLinha
:
string
;
lAceitaAtributo
:
boolean
;
li
:
integer
;
begin
opEve
.
ClearAvails
;
lAceitaAtributo
:=
false
;
if
opEve
=
fopEvenWhereAposPonto
then
begin
lAceitaAtributo
:=
true
;
end
else
if
opEve
=
fopEvenSelectAposPonto
then
begin
if
XMLQuery
.
Checked
then
begin
lAceitaAtributo
:=
true
;
end
;
end
;
lLista
:=
TStringList
.
Create
;
try
lLinha
:=
RetirarPalavraChaveOLQ
(
SynEditOQL
);
LoadListCompletion
(
lLinha
,
lAceitaAtributo
,
lLista
);
for
li
:=
0
to
lLista
.
Count
-
1
do
begin
opEve
.
AddAvail
(
lLista
.
Strings
[
li
]);
end
finally
lLista
.
Free
;
end
;
Cancel
:=
true
;
end
;
procedure
TExplorerV2
.
MontarModelExploreSequencial
(
Sender
:
TObject
;
Node
:
TTreeNode
);
var
lClassTicket
:
acClassTicket
;
lAttributeTicket
:
acAttributeTicket
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment