
SynEdit, quelques trucs et astuces
SynEdit est un composant logiciel pour Delphi et Free Pascal Lazarus. C'est un éditeur de code pouvant être ajouté à n'importe quelle application Delphi et FPL. Il est doté d'une coloration syntaxique pour la plupart des langages informatiques : HTML, CSS, Javascript, C++, Pascal, assembleur, Perl...
Il ne comporte pas de dépendance telles que des dll, et s'intègre facilement dans un logiciel existant.

Exemple de coloration syntaxique avec SynEdit
Le seul reproche que l'on puisse faire à SynEdit est que la version pour Delphi n'est plus maintenue depuis septembre 2013, alors que la version pour FPL l'est toujours.
Toutefois, la dernière version de SynEdit pour Delphi supporte l'Unicode. De plus, le code source étant fourni, il reste possible d'intervenir sur toutes les parties du composant, ainsi que d'ajouter des highlighters pour de nouveaux langages.
Où télécharger SynEdit ?
Il existe deux versions majeures, l'une sans unicode pour les très anciennes versions de Delphi, l'autre en unicode pour les versions plus récentes :
- SynEdit-2_0_6 : sans unicode, de Delphi 3 jusqu'à Delphi 2006.
- Version SynEdit-2_0_8 : avec unicode, de Delphi 5 jusqu'à XE 5 et plus.
Je vous conseille d'utiliser la version unicode si vous le pouvez.
Liens de téléchargement :
https://sourceforge.net/projects/synedit/files/1%20-%20Stable%20Releases/
SynEdit-2_0_6 (copie de secours)
SynEdit-2_0_8 (copie de secours)
Installation du composant
Dans Delphi, cliquez sur Fichier/Ouvrir, allez dans le dossier SynEdit/Packages et ouvrez le projet correspondant à votre version de Delphi. Il vous restera enfin à compiler et installer le paquet. Sur Free Pascal Lazarus, SynEdit est pré-installé d'origine.
Pour installer SynEdit Unicode dans Delphi 2005 édition personnelle, utilisez SynEdit_D7_PE.dpk dans le dossier SynEdit/Packages.
Lorsque le SynEdit a été correctement installé, il est disponible dans la palette de composants lorsque vous êtes en mode conception.

Problèmes divers de SynEdit pour Delphi
Voici les solutions de quelques soucis que j'ai rencontré dans l'utilisation de SynEdit Unicode pour Delphi, avec Delphi 2005 édition personnelle.
Problème d'ouverture de fichiers UTF8 sans BOM
Si l'on veut ouvrir un fichier UTF8 sans Byte Order Mark, il sera ouvert en tant que fichier Ansi, ce qui provoquera l'affichage de « signes bizarres ». Ce défaut pourra être facilement corrigé en rectifiant l'unité SynUnicode.Pas dans la procédure LoadFromStream, comme indiqué ci-dessous.
procedure TUnicodeStrings.LoadFromStream(Stream: TStream);
// usual loader routine, but enhanced to handle byte order marks in stream
var
Size,
BytesRead: Integer;
ByteOrderMask: array[0..5] of Byte; // BOM size is max 5 bytes (cf: wikipedia)
// but it is easier to implement with a multiple of 2
Loaded: Boolean;
SW: UnicodeString;
SA: AnsiString;
begin
BeginUpdate;
try
Loaded := False;
Size := Stream.Size - Stream.Position;
BytesRead := Stream.Read(ByteOrderMask[0], SizeOf(ByteOrderMask));
// UTF16 LSB = Unicode LSB/LE
if (BytesRead >= 2) and (ByteOrderMask[0] = UTF16BOMLE[0])
and (ByteOrderMask[1] = UTF16BOMLE[1]) then
begin
FSaveFormat := sfUTF16LSB;
SetLength(SW, (Size - 2) div SizeOf(WideChar));
Assert((Size and 1) <> 1, 'Number of chars must be a multiple of 2');
if BytesRead > 2 then
begin
System.Move(ByteOrderMask[2], SW[1], BytesRead - 2); // max 4 bytes = 2 widechars
if Size > BytesRead then
// first 2 chars (maximum) were copied by System.Move
Stream.Read(SW[3], Size - BytesRead);
end;
SetTextStr(SW);
Loaded := True;
end;
// UTF16 MSB = Unicode MSB/BE
if (BytesRead >= 2) and (ByteOrderMask[0] = UTF16BOMBE[0])
and (ByteOrderMask[1] = UTF16BOMBE[1]) then
begin
FSaveFormat := sfUTF16MSB;
SetLength(SW, (Size - 2) div SizeOf(WideChar));
Assert((Size and 1) <> 1, 'Number of chars must be a multiple of 2');
if BytesRead > 2 then
begin
System.Move(ByteOrderMask[2], SW[1] ,BytesRead - 2); // max 4 bytes = 2 widechars
if Size > BytesRead then
// first 2 chars (maximum) were copied by System.Move
Stream.Read(SW[3], Size - BytesRead);
StrSwapByteOrder(PWideChar(SW));
end;
SetTextStr(SW);
Loaded := True;
end;
// UTF8
if (BytesRead >= 3) and (ByteOrderMask[0] = UTF8BOM[0])
and (ByteOrderMask[1] = UTF8BOM[1]) and (ByteOrderMask[2] = UTF8BOM[2]) then
begin
FSaveFormat := sfUTF8;
SetLength(SA, (Size - 3) div SizeOf(AnsiChar));
if BytesRead > 3 then
begin
System.Move(ByteOrderMask[3], SA[1], BytesRead - 3); // max 3 bytes = 3 chars
if Size > BytesRead then
// first 3 chars were copied by System.Move
Stream.Read(SA[4], Size - BytesRead);
SW := UTF8Decode(SA);
end;
SetTextStr(SW);
Loaded := True;
end;
// UTF8 no-BOM or Ansi ----- Bug fixed, not origin of SynEdit -----
if not Loaded then
begin
FSaveFormat := sfAnsi;
SetLength(SA, Size div SizeOf(AnsiChar));
if BytesRead > 0 then
begin
System.Move(ByteOrderMask[0], SA[1], BytesRead); // max 6 bytes = 6 chars
if Size > BytesRead then
Stream.Read(SA[7], Size - BytesRead); // first 6 chars were copied by System.Move
SW := UTF8Decode(SA);
if SW <> '' then begin //UTF8 no-BOM
FSaveFormat := sfUTF8;
SetTextStr(SW);
Loaded := True;
end;
end;
if not Loaded then //Ansi
SetTextStr(SA);
end;
// default case (Ansi) ----- Replaced -----
{if not Loaded then
begin
FSaveFormat := sfAnsi;
SetLength(SA, Size div SizeOf(AnsiChar));
if BytesRead > 0 then
begin
System.Move(ByteOrderMask[0], SA[1], BytesRead); // max 6 bytes = 6 chars
if Size > BytesRead then
Stream.Read(SA[7], Size - BytesRead); // first 6 chars were copied by System.Move
end;
SetTextStr(SA);
end; }
finally
EndUpdate;
end;
end;
J'ai trouvé cette solution ici : https://forum.mh-nexus.de/viewtopic.php?t=371. A noter la prise de bec entre deux intervenants dans les commentaires, mais qui n'avait pas de raison d'être, puisque j'ai personnellement testé ce code et il donne satisfaction.
Il y a un petit ralentissement si le fichier est au format Ansi, mais ce ralentissement est négligeable et ne gêne pas. Cette solution est donc tout à fait applicable.
Problème de sélection d'un mot entier contenant des lettres accentuées
Par exemple, le mot « carrière » est sélectionné du début jusqu'à la lettre accentuée, ou bien après la lettre accentuée et jusqu'à la fin, alors qu'il devrait être sélectionné en totalité.
En cherchant avec Google, j'ai trouvé ces deux solutions :
- https://sourceforge.net/p/synedit/bugs/445/
- https://sourceforge.net/p/synedit/mailman/synedit-devel/?viewmonth=200508 (Voir Unit SyneditHighlighter.PAS)
L'alternative ci-dessus ne m'a pas donné entière satisfaction. Malgré tout, je remercie les deux contributeurs qui m'ont mis sur la bonne piste, ce qui m'a fait gagner beaucoup de temps pour trouver une meilleure solution, que j'ai indiqué ci-dessous.
Rectifiez dans l'unité SynHighlighterHtml.pas à l'intérieur de la procédure IsIdentChar.
function TSynHTMLSyn.IsIdentChar(AChar: WideChar): Boolean;
begin
case AChar of
'_', '/', '0'..'9', 'A'..'Z', 'a'..'z',
'À'..'Ö', 'Ù'..'Ý', 'à'..'ö', 'ù'..'ý', 'ÿ', 'ƒ': //Christian Feron 08/05/2016
Result := True;
else
Result := False;
end;
end;
Cette modification devrait, normalement, être appliquée sur tous les highlighters qui acceptent les commentaires. Mais, si vous n'utilisez qu'un ou deux highlighters seulement, vous pouvez vous contenter de modifier uniquement ceux-là.
Comment détecter l'encodage d'un fichier avec SynEditUnicode
Vous voulez ouvrir un fichier texte, et savoir quel est son format : Ansi, UTF8 sans BOM, UTF8 avec BOM, UTF16 Little Endian, UTF16 Big Endian. Dans ce cas, utilisez la fonction GetEncoding de l'unité SynUnicode.pas, comme dans l'exemple ci-dessous.
procedure TForm1.GetFileEncoding(aFileName: String);
var
aEncoding : TSynEncoding;
aBOM : Boolean;
begin
aEncoding := GetEncoding(aFileName, aBOM);//Identifies file encoding
case aSynEncoding of
seAnsi : Label1.Caption := 'Ansi';
seUTF8 : begin
if not aBOM then Label1.Caption := 'UTF8';
if ABOM then Label1.Caption := 'UTF8Bom';
end;
seUTF16LE : Label1.Caption := 'LittleEndian';
seUTF16BE : Label1.Caption := 'BigEndian';
end;//End case...
end;
Pour utiliser plusieurs highlighters dans un même document
Un document HTML peut contenir à la fois du HTML, du CSS et du Javascript. Comment faire pour obtenir une coloration syntaxique sur les trois à la fois ?
Pour cela, il faut utiliser le TSynMultiSyn, disponible dans la palette principale des composants SynEdit (pas celle où se trouvent tous les highlighters, mais l'autre).
Lorsque vos différents highlighters ont été placés sur votre TForm, vous pouvez les initialiser dans une procédure, par exemple comme ceci :
procedure TMainForm.InitMultiHighlighters;
begin
// HTML complex
with SynMultiSyn1 do begin //----- Multi-Highlighters ----------------------
DefaultHighlighter := SynHTMLSyn1;
DefaultLanguageName:= 'HTML complex';
DefaultFilter := 'HTML complex (*.html; *.htm)|*.html; *.htm';
with Schemes do begin
Add.Index:= 0; //----- Javascript -----
with Items[0] do begin
SchemeName := 'Sch_Javascript';
Highlighter := SynJScriptSyn1;
StartExpr := '<script';
EndExpr := '</script>';
with MarkerAttri do begin
Background := $00CDEBFF;//PeachPuff
Foreground := clNavy;
end;
end;//End with Items[0]...
Add.Index:= 1; //----- CSS -----
with Items[1] do begin
SchemeName := 'Sch_CSS';
Highlighter := SynCssSyn1;
StartExpr := '<style';
EndExpr := '</style>';
with MarkerAttri do begin
Background := $00CDEBFF;//PeachPuff
Foreground := clNavy;
end;
end;//End with Items[1]...
end;//End with Schemes...
end;//End with SynMultiSyn1...
end;
Vous pouvez aussi regarder l'exemple visible ici :
https://github.com/SynEdit/SynEdit/tree/master/Demos/MultiSynDemo
Pour créer un nouveau highlighter dans SynEdit
Regardez dans le dossier SynEdit/SysGen, vous trouverez le code-source du générateur de highlighters ainsi que son mode d'emploi en HTML.
Si vous n'arrivez pas à compiler le générateur, utilisez celui-ci, que j'ai compilé avec Delphi 2005 PE : SynGen.exe

Interface de SynGen.exe
Utiliser ce générateur suppose que vous ayez déjà créé un fichier de grammaire pour votre nouveau highlighter (*.msg) bien évidemment.