View Issue Details

IDProjectCategoryView StatusLast Update
0038196LazarusIDEpublic2021-02-16 15:37
ReporterOkobaPatino Assigned ToJuha Manninen  
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionfixed 
Product Version2.1 (SVN) 
Summary0038196: JCF will move the breakpoints
DescriptionAfter formatting a code with breakpoints, one of the breakpoints will move to the first line of the unit.
Steps To Reproduceprogram Project1;

begin
  b:=a; //<<Line with breakpoint
end.
After format:
program Project1;//<<Line with breakpoint

begin
  b:=a
end.
TagsNo tags attached.
Fixed in Revisionr64347, r64348
LazTarget-
Widgetset
Attached Files

Relationships

related to 0038195 closedJuha Manninen JCF should set the cursor at the line end 
related to 0038125 closedJuha Manninen JCF fails formatting using defines in uses 

Activities

OkobaPatino

2020-12-10 09:08

reporter   ~0127506

It is related to 0038178 or 0038081.

OkobaPatino

2020-12-10 09:09

reporter   ~0127507

@DomingoGP, Can you please check the last changes?

Domingo Galmés

2020-12-10 21:47

reporter   ~0127515

Basically the problem with breakpoints and also with bookmarks is because we replace all the editor text after formatting, which in my opinion is better than the previous version that synchronized badly, replacing almost all the lines from the first difference and adding blank lines throughout the code at "random" positions.

See 0038195 for more complete explanation.

OkobaPatino

2020-12-10 21:51

reporter   ~0127516

I can understand your view, but I can not accept it specifically because of breaking a working behavior for breakpoints and other things because of a not complete implementation.
In the old way at least we could do day to day work with some issues, now and with breakpoints problem, JCF is broken.
Please revert back the working version until your new way is working in all cases.

Domingo Galmés

2020-12-10 22:32

reporter   ~0127519

This patch reverts to the old code.
JCF_Revert.patch (1,429 bytes)   
From 03e11911af0551eb5a561196a44b05dbd1cdda21 Mon Sep 17 00:00:00 2001
From: DomingoGP <dgalmesp@gmail.com>
Date: Thu, 10 Dec 2020 22:23:30 +0100
Subject: [PATCH] revert copy of formated code to ide editor.

---
 components/jcf2/ReadWrite/EditorConverter.pas | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/components/jcf2/ReadWrite/EditorConverter.pas b/components/jcf2/ReadWrite/EditorConverter.pas
index 571ae84595..c0bd3627db 100644
--- a/components/jcf2/ReadWrite/EditorConverter.pas
+++ b/components/jcf2/ReadWrite/EditorConverter.pas
@@ -135,6 +135,9 @@ begin
   Result := pcUnit.Lines.Text;
 end;
 
+{.$DEFINE NEWCODE}
+{$IFDEF NEWCODE}
+// This code don't keep breakpoints.
 procedure TEditorConverter.WriteToIDE(const pcUnit: TSourceEditorInterface; const psText: string);
 var
   lLogicalCaretXY:TPoint;
@@ -165,11 +168,12 @@ begin
   end;
 end;
 
+{$ELSE}
 //BUGGY: inserts empty blank lines in "random" position in the editor.
 // and if only one line es added or deleted after formatting then doesn't syncronize well.
 // i think is better change all text in the editor.
 // TODO: delete
-{
+
 procedure TEditorConverter.WriteToIDE(const pcUnit: TSourceEditorInterface; const psText: string);
 var
   lcSourceLines, lcDestLines: TStrings;
@@ -226,7 +230,7 @@ begin
     lcSameEnd.Free;
    end;
 end;
-}
+{$ENDIF}
 
 procedure TEditorConverter.AfterConvert;
 begin
-- 
2.29.1.windows.1

JCF_Revert.patch (1,429 bytes)   

Domingo Galmés

2021-01-06 18:34

reporter   ~0128128

This big patch merges the changes made by the JCF into the editor, changing only the modified lines, so preserves the breakpoints.

For doing the diff / merge uses the TDiff component from Angus Johnson taken from http://github.com/rickard67/TextDiff.

I have attached another patch adding the author of the component to the acknowledgments of lazarus in case you consider appropriate to apply it.
JCF_diff_merge.patch (103,347 bytes)   
From 7632e540e5e387f5ac1fd32571d8e7c61a31e219 Mon Sep 17 00:00:00 2001
From: DomingoGP <dgalmesp@gmail.com>
Date: Wed, 6 Jan 2021 18:20:16 +0100
Subject: [PATCH] Merges the changes of JCF in the editor. Only modified lines
 are changed.

---
 .../jcf2/IdePlugin/lazarus/jcfidelazarus.lpk  | 1472 +++++++++--------
 .../jcf2/IdePlugin/lazarus/jcfidelazarus.pas  |  110 +-
 .../jcf2/IdePlugin/lazarus/jcfidemain.pas     |    7 +-
 components/jcf2/ReadWrite/Converter.pas       |   11 +-
 components/jcf2/ReadWrite/Diff.pas            |  901 ++++++++++
 components/jcf2/ReadWrite/EditorConverter.pas |   91 +-
 components/jcf2/ReadWrite/diffmerge.pas       |  202 +++
 7 files changed, 1913 insertions(+), 881 deletions(-)
 create mode 100644 components/jcf2/ReadWrite/Diff.pas
 create mode 100644 components/jcf2/ReadWrite/diffmerge.pas

diff --git a/components/jcf2/IdePlugin/lazarus/jcfidelazarus.lpk b/components/jcf2/IdePlugin/lazarus/jcfidelazarus.lpk
index b8527f3b21..f49f0546a1 100644
--- a/components/jcf2/IdePlugin/lazarus/jcfidelazarus.lpk
+++ b/components/jcf2/IdePlugin/lazarus/jcfidelazarus.lpk
@@ -1,732 +1,740 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<CONFIG>
-  <Package Version="5">
-    <PathDelim Value="\"/>
-    <Name Value="jcfidelazarus"/>
-    <Type Value="RunAndDesignTime"/>
-    <AddToProjectUsesSection Value="True"/>
-    <CompilerOptions>
-      <Version Value="11"/>
-      <PathDelim Value="\"/>
-      <SearchPaths>
-        <IncludeFiles Value="..\..\Include;..\..\Ui\Settings"/>
-        <OtherUnitFiles Value="..\..;..\..\Parse;..\..\Parse\PreProcessor;..\..\Parse\UI;..\..\Process;..\..\Process\Align;..\..\Process\Capitalisation;..\..\Process\Indent;..\..\Process\Info;..\..\Process\Obfuscate;..\..\Process\Onceoffs;..\..\Process\Returns;..\..\Process\Spacing;..\..\Process\Transform;..\..\Process\Warnings;..\..\ReadWrite;..\..\Settings;..\..\Settings\Streams;..\..\Ui;..\..\Ui\Settings;..\..\Utils;..\..\Utils\DragDrop"/>
-        <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)\$(LCLWidgetType)"/>
-      </SearchPaths>
-      <Parsing>
-        <SyntaxOptions>
-          <CStyleOperator Value="False"/>
-          <UseAnsiStrings Value="False"/>
-        </SyntaxOptions>
-      </Parsing>
-      <Other>
-        <CustomOptions Value="$(IDEBuildOptions)"/>
-      </Other>
-    </CompilerOptions>
-    <Description Value="JEDI Code Format IDE Plugin for Lazarus"/>
-    <Version Major="2"/>
-    <Files Count="171">
-      <Item1>
-        <Filename Value="jcfidemain.pas"/>
-        <UnitName Value="JcfIdeMain"/>
-      </Item1>
-      <Item2>
-        <Filename Value="jcfideregister.pas"/>
-        <HasRegisterProc Value="True"/>
-        <UnitName Value="JcfIdeRegister"/>
-      </Item2>
-      <Item3>
-        <Filename Value="..\..\Parse\AsmKeywords.pas"/>
-        <UnitName Value="AsmKeywords"/>
-      </Item3>
-      <Item4>
-        <Filename Value="..\..\Parse\BuildParseTree.pas"/>
-        <UnitName Value="BuildParseTree"/>
-      </Item4>
-      <Item5>
-        <Filename Value="..\..\Parse\BuildTokenList.pas"/>
-        <UnitName Value="BuildTokenList"/>
-      </Item5>
-      <Item6>
-        <Filename Value="..\..\Parse\ParseError.pas"/>
-        <UnitName Value="ParseError"/>
-      </Item6>
-      <Item7>
-        <Filename Value="..\..\Parse\ParseTreeNode.pas"/>
-        <UnitName Value="ParseTreeNode"/>
-      </Item7>
-      <Item8>
-        <Filename Value="..\..\Parse\ParseTreeNodeType.pas"/>
-        <UnitName Value="ParseTreeNodeType"/>
-      </Item8>
-      <Item9>
-        <Filename Value="..\..\Parse\PreProcessor\PreProcessorExpressionParser.pas"/>
-        <UnitName Value="PreProcessorExpressionParser"/>
-      </Item9>
-      <Item10>
-        <Filename Value="..\..\Parse\PreProcessor\PreProcessorExpressionTokenise.pas"/>
-        <UnitName Value="PreProcessorExpressionTokenise"/>
-      </Item10>
-      <Item11>
-        <Filename Value="..\..\Parse\PreProcessor\PreProcessorExpressionTokens.pas"/>
-        <UnitName Value="PreProcessorExpressionTokens"/>
-      </Item11>
-      <Item12>
-        <Filename Value="..\..\Parse\PreProcessor\PreProcessorParseTree.pas"/>
-        <UnitName Value="PreProcessorParseTree"/>
-      </Item12>
-      <Item13>
-        <Filename Value="..\..\Parse\SourceToken.pas"/>
-        <UnitName Value="SourceToken"/>
-      </Item13>
-      <Item14>
-        <Filename Value="..\..\Parse\SourceTokenList.pas"/>
-        <UnitName Value="SourceTokenList"/>
-      </Item14>
-      <Item15>
-        <Filename Value="..\..\Parse\Tokens.pas"/>
-        <UnitName Value="Tokens"/>
-      </Item15>
-      <Item16>
-        <Filename Value="..\..\Parse\TokenUtils.pas"/>
-        <UnitName Value="TokenUtils"/>
-      </Item16>
-      <Item17>
-        <Filename Value="..\..\Parse\UI\fShowParseTree.pas"/>
-        <UnitName Value="fShowParseTree"/>
-      </Item17>
-      <Item18>
-        <Filename Value="..\..\Process\Align\AlignAssign.pas"/>
-        <UnitName Value="AlignAssign"/>
-      </Item18>
-      <Item19>
-        <Filename Value="..\..\Process\Align\AlignBase.pas"/>
-        <UnitName Value="AlignBase"/>
-      </Item19>
-      <Item20>
-        <Filename Value="..\..\Process\Align\AlignComment.pas"/>
-        <UnitName Value="AlignComment"/>
-      </Item20>
-      <Item21>
-        <Filename Value="..\..\Process\Align\AlignConst.pas"/>
-        <UnitName Value="AlignConst"/>
-      </Item21>
-      <Item22>
-        <Filename Value="..\..\Process\Align\AlignField.pas"/>
-        <UnitName Value="AlignField"/>
-      </Item22>
-      <Item23>
-        <Filename Value="..\..\Process\Align\AlignTypedef.pas"/>
-        <UnitName Value="AlignTypedef"/>
-      </Item23>
-      <Item24>
-        <Filename Value="..\..\Process\Align\AlignVars.pas"/>
-        <UnitName Value="AlignVars"/>
-      </Item24>
-      <Item25>
-        <Filename Value="..\..\Process\AllProcesses.pas"/>
-        <UnitName Value="AllProcesses"/>
-      </Item25>
-      <Item26>
-        <Filename Value="..\..\Process\BaseVisitor.pas"/>
-        <UnitName Value="BaseVisitor"/>
-      </Item26>
-      <Item27>
-        <Filename Value="..\..\Process\Capitalisation\Capitalisation.pas"/>
-        <UnitName Value="Capitalisation"/>
-      </Item27>
-      <Item28>
-        <Filename Value="..\..\Process\Capitalisation\IdentifierCaps.pas"/>
-        <UnitName Value="IdentifierCaps"/>
-      </Item28>
-      <Item29>
-        <Filename Value="..\..\Process\Capitalisation\SpecificWordCaps.pas"/>
-        <UnitName Value="SpecificWordCaps"/>
-      </Item29>
-      <Item30>
-        <Filename Value="..\..\Process\Capitalisation\UnitNameCaps.pas"/>
-        <UnitName Value="UnitNameCaps"/>
-      </Item30>
-      <Item31>
-        <Filename Value="..\..\Process\FormatFlags.pas"/>
-        <UnitName Value="FormatFlags"/>
-      </Item31>
-      <Item32>
-        <Filename Value="..\..\Process\Indent\IndentAsmParam.pas"/>
-        <UnitName Value="IndentAsmParam"/>
-      </Item32>
-      <Item33>
-        <Filename Value="..\..\Process\Indent\Indenter.pas"/>
-        <UnitName Value="Indenter"/>
-      </Item33>
-      <Item34>
-        <Filename Value="..\..\Process\Info\BasicStats.pas"/>
-        <UnitName Value="BasicStats"/>
-      </Item34>
-      <Item35>
-        <Filename Value="..\..\Process\Nesting.pas"/>
-        <UnitName Value="Nesting"/>
-      </Item35>
-      <Item36>
-        <Filename Value="..\..\Process\Obfuscate\FixCase.pas"/>
-        <UnitName Value="FixCase"/>
-      </Item36>
-      <Item37>
-        <Filename Value="..\..\Process\Obfuscate\RebreakLines.pas"/>
-        <UnitName Value="RebreakLines"/>
-      </Item37>
-      <Item38>
-        <Filename Value="..\..\Process\Obfuscate\ReduceWhiteSpace.pas"/>
-        <UnitName Value="ReduceWhiteSpace"/>
-      </Item38>
-      <Item39>
-        <Filename Value="..\..\Process\Obfuscate\RemoveBlankLine.pas"/>
-        <UnitName Value="RemoveBlankLine"/>
-      </Item39>
-      <Item40>
-        <Filename Value="..\..\Process\Obfuscate\RemoveComment.pas"/>
-        <UnitName Value="RemoveComment"/>
-      </Item40>
-      <Item41>
-        <Filename Value="..\..\Process\Obfuscate\RemoveConsecutiveWhiteSpace.pas"/>
-        <UnitName Value="RemoveConsecutiveWhiteSpace"/>
-      </Item41>
-      <Item42>
-        <Filename Value="..\..\Process\Obfuscate\RemoveReturn.pas"/>
-        <UnitName Value="RemoveReturn"/>
-      </Item42>
-      <Item43>
-        <Filename Value="..\..\Process\Obfuscate\RemoveUnneededWhiteSpace.pas"/>
-        <UnitName Value="RemoveUnneededWhiteSpace"/>
-      </Item43>
-      <Item44>
-        <Filename Value="..\..\Process\Onceoffs\MozComment.pas"/>
-        <UnitName Value="MozComment"/>
-      </Item44>
-      <Item45>
-        <Filename Value="..\..\Process\RemoveEmptyComment.pas"/>
-        <UnitName Value="RemoveEmptyComment"/>
-      </Item45>
-      <Item46>
-        <Filename Value="..\..\Process\Returns\BlockStyles.pas"/>
-        <UnitName Value="BlockStyles"/>
-      </Item46>
-      <Item47>
-        <Filename Value="..\..\Process\Returns\LongLineBreaker.pas"/>
-        <UnitName Value="LongLineBreaker"/>
-      </Item47>
-      <Item48>
-        <Filename Value="..\..\Process\Returns\NoReturnAfter.pas"/>
-        <UnitName Value="NoReturnAfter"/>
-      </Item48>
-      <Item49>
-        <Filename Value="..\..\Process\Returns\NoReturnBefore.pas"/>
-        <UnitName Value="NoReturnBefore"/>
-      </Item49>
-      <Item50>
-        <Filename Value="..\..\Process\Returns\PropertyOnOneLine.pas"/>
-        <UnitName Value="PropertyOnOneLine"/>
-      </Item50>
-      <Item51>
-        <Filename Value="..\..\Process\Returns\RemoveBlankLinesAfterProcHeader.pas"/>
-        <UnitName Value="RemoveBlankLinesAfterProcHeader"/>
-      </Item51>
-      <Item52>
-        <Filename Value="..\..\Process\Returns\RemoveBlankLinesInVars.pas"/>
-        <UnitName Value="RemoveBlankLinesInVars"/>
-      </Item52>
-      <Item53>
-        <Filename Value="..\..\Process\Returns\RemoveConsecutiveReturns.pas"/>
-        <UnitName Value="RemoveConsecutiveReturns"/>
-      </Item53>
-      <Item54>
-        <Filename Value="..\..\Process\Returns\RemoveReturnsAfter.pas"/>
-        <UnitName Value="RemoveReturnsAfter"/>
-      </Item54>
-      <Item55>
-        <Filename Value="..\..\Process\Returns\RemoveReturnsAfterBegin.pas"/>
-        <UnitName Value="RemoveReturnsAfterBegin"/>
-      </Item55>
-      <Item56>
-        <Filename Value="..\..\Process\Returns\RemoveReturnsBeforeEnd.pas"/>
-        <UnitName Value="RemoveReturnsBeforeEnd"/>
-      </Item56>
-      <Item57>
-        <Filename Value="..\..\Process\Returns\ReturnAfter.pas"/>
-        <UnitName Value="ReturnAfter"/>
-      </Item57>
-      <Item58>
-        <Filename Value="..\..\Process\Returns\ReturnBefore.pas"/>
-        <UnitName Value="ReturnBefore"/>
-      </Item58>
-      <Item59>
-        <Filename Value="..\..\Process\Returns\ReturnChars.pas"/>
-        <UnitName Value="ReturnChars"/>
-      </Item59>
-      <Item60>
-        <Filename Value="..\..\Process\Returns\ReturnsAfterFinalEnd.pas"/>
-        <UnitName Value="ReturnsAfterFinalEnd"/>
-      </Item60>
-      <Item61>
-        <Filename Value="..\..\Process\Spacing\MaxSpaces.pas"/>
-        <UnitName Value="MaxSpaces"/>
-      </Item61>
-      <Item62>
-        <Filename Value="..\..\Process\Spacing\NoSpaceAfter.pas"/>
-        <UnitName Value="NoSpaceAfter"/>
-      </Item62>
-      <Item63>
-        <Filename Value="..\..\Process\Spacing\NoSpaceBefore.pas"/>
-        <UnitName Value="NoSpaceBefore"/>
-      </Item63>
-      <Item64>
-        <Filename Value="..\..\Process\Spacing\RemoveSpaceAtLineEnd.pas"/>
-        <UnitName Value="RemoveSpaceAtLineEnd"/>
-      </Item64>
-      <Item65>
-        <Filename Value="..\..\Process\Spacing\SingleSpaceAfter.pas"/>
-        <UnitName Value="SingleSpaceAfter"/>
-      </Item65>
-      <Item66>
-        <Filename Value="..\..\Process\Spacing\SingleSpaceBefore.pas"/>
-        <UnitName Value="SingleSpaceBefore"/>
-      </Item66>
-      <Item67>
-        <Filename Value="..\..\Process\Spacing\SpaceBeforeColon.pas"/>
-        <UnitName Value="SpaceBeforeColon"/>
-      </Item67>
-      <Item68>
-        <Filename Value="..\..\Process\Spacing\SpaceToTab.pas"/>
-        <UnitName Value="SpaceToTab"/>
-      </Item68>
-      <Item69>
-        <Filename Value="..\..\Process\Spacing\TabToSpace.pas"/>
-        <UnitName Value="TabToSpace"/>
-      </Item69>
-      <Item70>
-        <Filename Value="..\..\Process\SwitchableVisitor.pas"/>
-        <UnitName Value="SwitchableVisitor"/>
-      </Item70>
-      <Item71>
-        <Filename Value="..\..\Process\Transform\AddBeginEnd.pas"/>
-        <UnitName Value="AddBeginEnd"/>
-      </Item71>
-      <Item72>
-        <Filename Value="..\..\Process\Transform\AddBlockEndSemicolon.pas"/>
-        <UnitName Value="AddBlockEndSemicolon"/>
-      </Item72>
-      <Item73>
-        <Filename Value="..\..\Process\Transform\FindReplace.pas"/>
-        <UnitName Value="FindReplace"/>
-      </Item73>
-      <Item74>
-        <Filename Value="..\..\Process\Transform\SortUses.pas"/>
-        <UnitName Value="SortUses"/>
-      </Item74>
-      <Item75>
-        <Filename Value="..\..\Process\Transform\SortUsesData.pas"/>
-        <UnitName Value="SortUsesData"/>
-      </Item75>
-      <Item76>
-        <Filename Value="..\..\Process\Transform\UsesClauseFindReplace.pas"/>
-        <UnitName Value="UsesClauseFindReplace"/>
-      </Item76>
-      <Item77>
-        <Filename Value="..\..\Process\Transform\UsesClauseInsert.pas"/>
-        <UnitName Value="UsesClauseInsert"/>
-      </Item77>
-      <Item78>
-        <Filename Value="..\..\Process\Transform\UsesClauseRemove.pas"/>
-        <UnitName Value="UsesClauseRemove"/>
-      </Item78>
-      <Item79>
-        <Filename Value="..\..\Process\TreeWalker.pas"/>
-        <UnitName Value="TreeWalker"/>
-      </Item79>
-      <Item80>
-        <Filename Value="..\..\Process\VisitSetNesting.pas"/>
-        <UnitName Value="VisitSetNesting"/>
-      </Item80>
-      <Item81>
-        <Filename Value="..\..\Process\VisitSetXY.pas"/>
-        <UnitName Value="VisitSetXY"/>
-      </Item81>
-      <Item82>
-        <Filename Value="..\..\Process\VisitStripEmptySpace.pas"/>
-        <UnitName Value="VisitStripEmptySpace"/>
-      </Item82>
-      <Item83>
-        <Filename Value="..\..\Process\Warnings\WarnAssignToFunctionName.pas"/>
-        <UnitName Value="WarnAssignToFunctionName"/>
-      </Item83>
-      <Item84>
-        <Filename Value="..\..\Process\Warnings\WarnCaseNoElse.pas"/>
-        <UnitName Value="WarnCaseNoElse"/>
-      </Item84>
-      <Item85>
-        <Filename Value="..\..\Process\Warnings\WarnDestroy.pas"/>
-        <UnitName Value="WarnDestroy"/>
-      </Item85>
-      <Item86>
-        <Filename Value="..\..\Process\Warnings\WarnEmptyBlock.pas"/>
-        <UnitName Value="WarnEmptyBlock"/>
-      </Item86>
-      <Item87>
-        <Filename Value="..\..\Process\Warnings\Warning.pas"/>
-        <UnitName Value="Warning"/>
-      </Item87>
-      <Item88>
-        <Filename Value="..\..\Process\Warnings\WarnRealType.pas"/>
-        <UnitName Value="WarnRealType"/>
-      </Item88>
-      <Item89>
-        <Filename Value="..\..\Process\Warnings\WarnUnusedParam.pas"/>
-        <UnitName Value="WarnUnusedParam"/>
-      </Item89>
-      <Item90>
-        <Filename Value="..\..\ReadWrite\Converter.pas"/>
-        <UnitName Value="Converter"/>
-      </Item90>
-      <Item91>
-        <Filename Value="..\..\ReadWrite\ConvertTypes.pas"/>
-        <UnitName Value="ConvertTypes"/>
-      </Item91>
-      <Item92>
-        <Filename Value="..\..\ReadWrite\EditorConverter.pas"/>
-        <UnitName Value="EditorConverter"/>
-      </Item92>
-      <Item93>
-        <Filename Value="..\..\ReadWrite\FileConverter.pas"/>
-        <UnitName Value="FileConverter"/>
-      </Item93>
-      <Item94>
-        <Filename Value="..\..\Settings\JcfRegistrySettings.pas"/>
-        <UnitName Value="JcfRegistrySettings"/>
-      </Item94>
-      <Item95>
-        <Filename Value="..\..\Settings\JcfSetBase.pas"/>
-        <UnitName Value="JcfSetBase"/>
-      </Item95>
-      <Item96>
-        <Filename Value="..\..\Settings\JcfSettings.pas"/>
-        <UnitName Value="JcfSettings"/>
-      </Item96>
-      <Item97>
-        <Filename Value="..\..\Settings\SetAlign.pas"/>
-        <UnitName Value="SetAlign"/>
-      </Item97>
-      <Item98>
-        <Filename Value="..\..\Settings\SetAsm.pas"/>
-        <UnitName Value="SetAsm"/>
-      </Item98>
-      <Item99>
-        <Filename Value="..\..\Settings\SetCaps.pas"/>
-        <UnitName Value="SetCaps"/>
-      </Item99>
-      <Item100>
-        <Filename Value="..\..\Settings\SetClarify.pas"/>
-        <UnitName Value="SetClarify"/>
-      </Item100>
-      <Item101>
-        <Filename Value="..\..\Settings\SetComments.pas"/>
-        <UnitName Value="SetComments"/>
-      </Item101>
-      <Item102>
-        <Filename Value="..\..\Settings\SetIndent.pas"/>
-        <UnitName Value="SetIndent"/>
-      </Item102>
-      <Item103>
-        <Filename Value="..\..\Settings\SetObfuscate.pas"/>
-        <UnitName Value="SetObfuscate"/>
-      </Item103>
-      <Item104>
-        <Filename Value="..\..\Settings\SetPreProcessor.pas"/>
-        <UnitName Value="SetPreProcessor"/>
-      </Item104>
-      <Item105>
-        <Filename Value="..\..\Settings\SetReplace.pas"/>
-        <UnitName Value="SetReplace"/>
-      </Item105>
-      <Item106>
-        <Filename Value="..\..\Settings\SetReturns.pas"/>
-        <UnitName Value="SetReturns"/>
-      </Item106>
-      <Item107>
-        <Filename Value="..\..\Settings\SetSpaces.pas"/>
-        <UnitName Value="SetSpaces"/>
-      </Item107>
-      <Item108>
-        <Filename Value="..\..\Settings\SettingsTypes.pas"/>
-        <UnitName Value="SettingsTypes"/>
-      </Item108>
-      <Item109>
-        <Filename Value="..\..\Settings\SetTransform.pas"/>
-        <UnitName Value="SetTransform"/>
-      </Item109>
-      <Item110>
-        <Filename Value="..\..\Settings\SetUses.pas"/>
-        <UnitName Value="SetUses"/>
-      </Item110>
-      <Item111>
-        <Filename Value="..\..\Settings\SetWordList.pas"/>
-        <UnitName Value="SetWordList"/>
-      </Item111>
-      <Item112>
-        <Filename Value="..\..\Settings\Streams\SettingsStream.pas"/>
-        <UnitName Value="SettingsStream"/>
-      </Item112>
-      <Item113>
-        <Filename Value="..\..\Ui\fJcfErrorDisplay.pas"/>
-        <UnitName Value="fJcfErrorDisplay"/>
-      </Item113>
-      <Item114>
-        <Filename Value="..\..\Utils\Delay.pas"/>
-        <UnitName Value="Delay"/>
-      </Item114>
-      <Item115>
-        <Filename Value="..\..\Utils\IntList.pas"/>
-        <UnitName Value="IntList"/>
-      </Item115>
-      <Item116>
-        <Filename Value="..\..\Utils\JcfFontSetFunctions.pas"/>
-        <UnitName Value="JcfFontSetFunctions"/>
-      </Item116>
-      <Item117>
-        <Filename Value="..\..\Utils\JcfHelp.pas"/>
-        <UnitName Value="JcfHelp"/>
-      </Item117>
-      <Item118>
-        <Filename Value="..\..\Utils\JcfLog.pas"/>
-        <UnitName Value="JcfLog"/>
-      </Item118>
-      <Item119>
-        <Filename Value="..\..\Utils\JcfMiscFunctions.pas"/>
-        <UnitName Value="JcfMiscFunctions"/>
-      </Item119>
-      <Item120>
-        <Filename Value="..\..\Ui\fAbout.lfm"/>
-        <Type Value="LFM"/>
-      </Item120>
-      <Item121>
-        <Filename Value="..\..\Ui\fAbout.pas"/>
-        <UnitName Value="fAbout"/>
-      </Item121>
-      <Item122>
-        <Filename Value="..\..\JcfVersionConsts.pas"/>
-        <UnitName Value="JcfVersionConsts"/>
-      </Item122>
-      <Item123>
-        <Filename Value="..\..\Ui\Settings\frFiles.lfm"/>
-        <Type Value="LFM"/>
-      </Item123>
-      <Item124>
-        <Filename Value="..\..\Ui\Settings\frFiles.pas"/>
-        <UnitName Value="frFiles"/>
-      </Item124>
-      <Item125>
-        <Filename Value="..\..\Ui\Settings\frObfuscateSettings.lfm"/>
-        <Type Value="LFM"/>
-      </Item125>
-      <Item126>
-        <Filename Value="..\..\Ui\Settings\frObfuscateSettings.pas"/>
-        <UnitName Value="frObfuscateSettings"/>
-      </Item126>
-      <Item127>
-        <Filename Value="..\..\Ui\Settings\frClarify.lfm"/>
-        <Type Value="LFM"/>
-      </Item127>
-      <Item128>
-        <Filename Value="..\..\Ui\Settings\frClarify.pas"/>
-        <UnitName Value="frClarify"/>
-      </Item128>
-      <Item129>
-        <Filename Value="..\..\Ui\Settings\frClarifySpaces.lfm"/>
-        <Type Value="LFM"/>
-      </Item129>
-      <Item130>
-        <Filename Value="..\..\Ui\Settings\frClarifySpaces.pas"/>
-        <UnitName Value="frClarifySpaces"/>
-      </Item130>
-      <Item131>
-        <Filename Value="..\..\Ui\Settings\frClarifyIndent.lfm"/>
-        <Type Value="LFM"/>
-      </Item131>
-      <Item132>
-        <Filename Value="..\..\Ui\Settings\frClarifyIndent.pas"/>
-        <UnitName Value="frClarifyIndent"/>
-      </Item132>
-      <Item133>
-        <Filename Value="..\..\Ui\Settings\frBlankLines.lfm"/>
-        <Type Value="LFM"/>
-      </Item133>
-      <Item134>
-        <Filename Value="..\..\Ui\Settings\frBlankLines.pas"/>
-        <UnitName Value="frBlankLines"/>
-      </Item134>
-      <Item135>
-        <Filename Value="..\..\Ui\Settings\frClarifyAlign.lfm"/>
-        <Type Value="LFM"/>
-      </Item135>
-      <Item136>
-        <Filename Value="..\..\Ui\Settings\frClarifyAlign.pas"/>
-        <UnitName Value="frClarifyAlign"/>
-      </Item136>
-      <Item137>
-        <Filename Value="..\..\Ui\Settings\frClarifyLongLineBreaker.lfm"/>
-        <Type Value="LFM"/>
-      </Item137>
-      <Item138>
-        <Filename Value="..\..\Ui\Settings\frClarifyLongLineBreaker.pas"/>
-        <UnitName Value="frClarifyLongLineBreaker"/>
-      </Item138>
-      <Item139>
-        <Filename Value="..\..\Ui\Settings\frClarifyReturns.lfm"/>
-        <Type Value="LFM"/>
-      </Item139>
-      <Item140>
-        <Filename Value="..\..\Ui\Settings\frClarifyReturns.pas"/>
-        <UnitName Value="frClarifyReturns"/>
-      </Item140>
-      <Item141>
-        <Filename Value="..\..\Ui\Settings\frCompilerDirectReturns.lfm"/>
-        <Type Value="LFM"/>
-      </Item141>
-      <Item142>
-        <Filename Value="..\..\Ui\Settings\frCompilerDirectReturns.pas"/>
-        <UnitName Value="frCompilerDirectReturns"/>
-      </Item142>
-      <Item143>
-        <Filename Value="..\..\Ui\Settings\frClarifyBlocks.lfm"/>
-        <Type Value="LFM"/>
-      </Item143>
-      <Item144>
-        <Filename Value="..\..\Ui\Settings\frClarifyBlocks.pas"/>
-        <UnitName Value="frClarifyBlocks"/>
-      </Item144>
-      <Item145>
-        <Filename Value="..\..\Ui\Settings\frClarifyCaseBlocks.lfm"/>
-        <Type Value="LFM"/>
-      </Item145>
-      <Item146>
-        <Filename Value="..\..\Ui\Settings\frClarifyCaseBlocks.pas"/>
-        <UnitName Value="frClarifyCaseBlocks"/>
-      </Item146>
-      <Item147>
-        <Filename Value="..\..\Ui\Settings\frComments.lfm"/>
-        <Type Value="LFM"/>
-      </Item147>
-      <Item148>
-        <Filename Value="..\..\Ui\Settings\frComments.pas"/>
-        <UnitName Value="frComments"/>
-      </Item148>
-      <Item149>
-        <Filename Value="..\..\Ui\Settings\frWarnings.lfm"/>
-        <Type Value="LFM"/>
-      </Item149>
-      <Item150>
-        <Filename Value="..\..\Ui\Settings\frWarnings.pas"/>
-        <UnitName Value="frWarnings"/>
-      </Item150>
-      <Item151>
-        <Filename Value="..\..\Ui\Settings\frReservedCapsSettings.lfm"/>
-        <Type Value="LFM"/>
-      </Item151>
-      <Item152>
-        <Filename Value="..\..\Ui\Settings\frReservedCapsSettings.pas"/>
-        <UnitName Value="frReservedCapsSettings"/>
-      </Item152>
-      <Item153>
-        <Filename Value="..\..\Ui\Settings\frAnyCapsSettings.lfm"/>
-        <Type Value="LFM"/>
-      </Item153>
-      <Item154>
-        <Filename Value="..\..\Ui\Settings\frAnyCapsSettings.pas"/>
-        <UnitName Value="frAnyCapsSettings"/>
-      </Item154>
-      <Item155>
-        <Filename Value="..\..\Ui\Settings\frIdentifierCapsSettings.lfm"/>
-        <Type Value="LFM"/>
-      </Item155>
-      <Item156>
-        <Filename Value="..\..\Ui\Settings\frIdentifierCapsSettings.pas"/>
-        <UnitName Value="frIdentifierCapsSettings"/>
-      </Item156>
-      <Item157>
-        <Filename Value="..\..\Ui\Settings\frNotIdentifierCapsSettings.lfm"/>
-        <Type Value="LFM"/>
-      </Item157>
-      <Item158>
-        <Filename Value="..\..\Ui\Settings\frNotIdentifierCapsSettings.pas"/>
-        <UnitName Value="frNotIdentifierCapsSettings"/>
-      </Item158>
-      <Item159>
-        <Filename Value="..\..\Ui\Settings\frUnitCaps.lfm"/>
-        <Type Value="LFM"/>
-      </Item159>
-      <Item160>
-        <Filename Value="..\..\Ui\Settings\frUnitCaps.pas"/>
-        <UnitName Value="frUnitCaps"/>
-      </Item160>
-      <Item161>
-        <Filename Value="..\..\Ui\Settings\frReplace.lfm"/>
-        <Type Value="LFM"/>
-      </Item161>
-      <Item162>
-        <Filename Value="..\..\Ui\Settings\frReplace.pas"/>
-        <UnitName Value="frReplace"/>
-      </Item162>
-      <Item163>
-        <Filename Value="..\..\Ui\Settings\frUses.lfm"/>
-        <Type Value="LFM"/>
-      </Item163>
-      <Item164>
-        <Filename Value="..\..\Ui\Settings\frUses.pas"/>
-        <UnitName Value="frUses"/>
-      </Item164>
-      <Item165>
-        <Filename Value="..\..\Ui\Settings\frTransform.lfm"/>
-        <Type Value="LFM"/>
-      </Item165>
-      <Item166>
-        <Filename Value="..\..\Ui\Settings\frTransform.pas"/>
-        <UnitName Value="frTransform"/>
-      </Item166>
-      <Item167>
-        <Filename Value="..\..\Ui\Settings\frAsm.lfm"/>
-        <Type Value="LFM"/>
-      </Item167>
-      <Item168>
-        <Filename Value="..\..\Ui\Settings\frAsm.pas"/>
-        <UnitName Value="frAsm"/>
-      </Item168>
-      <Item169>
-        <Filename Value="..\..\Ui\Settings\frPreProcessor.lfm"/>
-        <Type Value="LFM"/>
-      </Item169>
-      <Item170>
-        <Filename Value="..\..\Ui\Settings\frPreProcessor.pas"/>
-        <UnitName Value="frPreProcessor"/>
-      </Item170>
-      <Item171>
-        <Filename Value="jcfuiconsts.pas"/>
-        <UnitName Value="JcfUIConsts"/>
-      </Item171>
-    </Files>
-    <CompatibilityMode Value="True"/>
-    <i18n>
-      <EnableI18N Value="True"/>
-      <OutDir Value="languages"/>
-    </i18n>
-    <RequiredPkgs Count="1">
-      <Item1>
-        <PackageName Value="IDEIntf"/>
-      </Item1>
-    </RequiredPkgs>
-    <UsageOptions>
-      <UnitPath Value="$(PkgOutDir)"/>
-    </UsageOptions>
-    <PublishOptions>
-      <Version Value="2"/>
-    </PublishOptions>
-  </Package>
-</CONFIG>
+<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+  <Package Version="5">
+    <PathDelim Value="\"/>
+    <Name Value="jcfidelazarus"/>
+    <Type Value="RunAndDesignTime"/>
+    <AddToProjectUsesSection Value="True"/>
+    <CompilerOptions>
+      <Version Value="11"/>
+      <PathDelim Value="\"/>
+      <SearchPaths>
+        <IncludeFiles Value="..\..\Include;..\..\Ui\Settings"/>
+        <OtherUnitFiles Value="..\..;..\..\Parse;..\..\Parse\PreProcessor;..\..\Parse\UI;..\..\Process;..\..\Process\Align;..\..\Process\Capitalisation;..\..\Process\Indent;..\..\Process\Info;..\..\Process\Obfuscate;..\..\Process\Onceoffs;..\..\Process\Returns;..\..\Process\Spacing;..\..\Process\Transform;..\..\Process\Warnings;..\..\ReadWrite;..\..\Settings;..\..\Settings\Streams;..\..\Ui;..\..\Ui\Settings;..\..\Utils;..\..\Utils\DragDrop"/>
+        <UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)\$(LCLWidgetType)"/>
+      </SearchPaths>
+      <Parsing>
+        <SyntaxOptions>
+          <CStyleOperator Value="False"/>
+          <UseAnsiStrings Value="False"/>
+        </SyntaxOptions>
+      </Parsing>
+      <Other>
+        <CustomOptions Value="$(IDEBuildOptions)"/>
+      </Other>
+    </CompilerOptions>
+    <Description Value="JEDI Code Format IDE Plugin for Lazarus"/>
+    <Version Major="2"/>
+    <Files Count="173">
+      <Item1>
+        <Filename Value="jcfidemain.pas"/>
+        <UnitName Value="JcfIdeMain"/>
+      </Item1>
+      <Item2>
+        <Filename Value="jcfideregister.pas"/>
+        <HasRegisterProc Value="True"/>
+        <UnitName Value="JcfIdeRegister"/>
+      </Item2>
+      <Item3>
+        <Filename Value="..\..\Parse\AsmKeywords.pas"/>
+        <UnitName Value="AsmKeywords"/>
+      </Item3>
+      <Item4>
+        <Filename Value="..\..\Parse\BuildParseTree.pas"/>
+        <UnitName Value="BuildParseTree"/>
+      </Item4>
+      <Item5>
+        <Filename Value="..\..\Parse\BuildTokenList.pas"/>
+        <UnitName Value="BuildTokenList"/>
+      </Item5>
+      <Item6>
+        <Filename Value="..\..\Parse\ParseError.pas"/>
+        <UnitName Value="ParseError"/>
+      </Item6>
+      <Item7>
+        <Filename Value="..\..\Parse\ParseTreeNode.pas"/>
+        <UnitName Value="ParseTreeNode"/>
+      </Item7>
+      <Item8>
+        <Filename Value="..\..\Parse\ParseTreeNodeType.pas"/>
+        <UnitName Value="ParseTreeNodeType"/>
+      </Item8>
+      <Item9>
+        <Filename Value="..\..\Parse\PreProcessor\PreProcessorExpressionParser.pas"/>
+        <UnitName Value="PreProcessorExpressionParser"/>
+      </Item9>
+      <Item10>
+        <Filename Value="..\..\Parse\PreProcessor\PreProcessorExpressionTokenise.pas"/>
+        <UnitName Value="PreProcessorExpressionTokenise"/>
+      </Item10>
+      <Item11>
+        <Filename Value="..\..\Parse\PreProcessor\PreProcessorExpressionTokens.pas"/>
+        <UnitName Value="PreProcessorExpressionTokens"/>
+      </Item11>
+      <Item12>
+        <Filename Value="..\..\Parse\PreProcessor\PreProcessorParseTree.pas"/>
+        <UnitName Value="PreProcessorParseTree"/>
+      </Item12>
+      <Item13>
+        <Filename Value="..\..\Parse\SourceToken.pas"/>
+        <UnitName Value="SourceToken"/>
+      </Item13>
+      <Item14>
+        <Filename Value="..\..\Parse\SourceTokenList.pas"/>
+        <UnitName Value="SourceTokenList"/>
+      </Item14>
+      <Item15>
+        <Filename Value="..\..\Parse\Tokens.pas"/>
+        <UnitName Value="Tokens"/>
+      </Item15>
+      <Item16>
+        <Filename Value="..\..\Parse\TokenUtils.pas"/>
+        <UnitName Value="TokenUtils"/>
+      </Item16>
+      <Item17>
+        <Filename Value="..\..\Parse\UI\fShowParseTree.pas"/>
+        <UnitName Value="fShowParseTree"/>
+      </Item17>
+      <Item18>
+        <Filename Value="..\..\Process\Align\AlignAssign.pas"/>
+        <UnitName Value="AlignAssign"/>
+      </Item18>
+      <Item19>
+        <Filename Value="..\..\Process\Align\AlignBase.pas"/>
+        <UnitName Value="AlignBase"/>
+      </Item19>
+      <Item20>
+        <Filename Value="..\..\Process\Align\AlignComment.pas"/>
+        <UnitName Value="AlignComment"/>
+      </Item20>
+      <Item21>
+        <Filename Value="..\..\Process\Align\AlignConst.pas"/>
+        <UnitName Value="AlignConst"/>
+      </Item21>
+      <Item22>
+        <Filename Value="..\..\Process\Align\AlignField.pas"/>
+        <UnitName Value="AlignField"/>
+      </Item22>
+      <Item23>
+        <Filename Value="..\..\Process\Align\AlignTypedef.pas"/>
+        <UnitName Value="AlignTypedef"/>
+      </Item23>
+      <Item24>
+        <Filename Value="..\..\Process\Align\AlignVars.pas"/>
+        <UnitName Value="AlignVars"/>
+      </Item24>
+      <Item25>
+        <Filename Value="..\..\Process\AllProcesses.pas"/>
+        <UnitName Value="AllProcesses"/>
+      </Item25>
+      <Item26>
+        <Filename Value="..\..\Process\BaseVisitor.pas"/>
+        <UnitName Value="BaseVisitor"/>
+      </Item26>
+      <Item27>
+        <Filename Value="..\..\Process\Capitalisation\Capitalisation.pas"/>
+        <UnitName Value="Capitalisation"/>
+      </Item27>
+      <Item28>
+        <Filename Value="..\..\Process\Capitalisation\IdentifierCaps.pas"/>
+        <UnitName Value="IdentifierCaps"/>
+      </Item28>
+      <Item29>
+        <Filename Value="..\..\Process\Capitalisation\SpecificWordCaps.pas"/>
+        <UnitName Value="SpecificWordCaps"/>
+      </Item29>
+      <Item30>
+        <Filename Value="..\..\Process\Capitalisation\UnitNameCaps.pas"/>
+        <UnitName Value="UnitNameCaps"/>
+      </Item30>
+      <Item31>
+        <Filename Value="..\..\Process\FormatFlags.pas"/>
+        <UnitName Value="FormatFlags"/>
+      </Item31>
+      <Item32>
+        <Filename Value="..\..\Process\Indent\IndentAsmParam.pas"/>
+        <UnitName Value="IndentAsmParam"/>
+      </Item32>
+      <Item33>
+        <Filename Value="..\..\Process\Indent\Indenter.pas"/>
+        <UnitName Value="Indenter"/>
+      </Item33>
+      <Item34>
+        <Filename Value="..\..\Process\Info\BasicStats.pas"/>
+        <UnitName Value="BasicStats"/>
+      </Item34>
+      <Item35>
+        <Filename Value="..\..\Process\Nesting.pas"/>
+        <UnitName Value="Nesting"/>
+      </Item35>
+      <Item36>
+        <Filename Value="..\..\Process\Obfuscate\FixCase.pas"/>
+        <UnitName Value="FixCase"/>
+      </Item36>
+      <Item37>
+        <Filename Value="..\..\Process\Obfuscate\RebreakLines.pas"/>
+        <UnitName Value="RebreakLines"/>
+      </Item37>
+      <Item38>
+        <Filename Value="..\..\Process\Obfuscate\ReduceWhiteSpace.pas"/>
+        <UnitName Value="ReduceWhiteSpace"/>
+      </Item38>
+      <Item39>
+        <Filename Value="..\..\Process\Obfuscate\RemoveBlankLine.pas"/>
+        <UnitName Value="RemoveBlankLine"/>
+      </Item39>
+      <Item40>
+        <Filename Value="..\..\Process\Obfuscate\RemoveComment.pas"/>
+        <UnitName Value="RemoveComment"/>
+      </Item40>
+      <Item41>
+        <Filename Value="..\..\Process\Obfuscate\RemoveConsecutiveWhiteSpace.pas"/>
+        <UnitName Value="RemoveConsecutiveWhiteSpace"/>
+      </Item41>
+      <Item42>
+        <Filename Value="..\..\Process\Obfuscate\RemoveReturn.pas"/>
+        <UnitName Value="RemoveReturn"/>
+      </Item42>
+      <Item43>
+        <Filename Value="..\..\Process\Obfuscate\RemoveUnneededWhiteSpace.pas"/>
+        <UnitName Value="RemoveUnneededWhiteSpace"/>
+      </Item43>
+      <Item44>
+        <Filename Value="..\..\Process\Onceoffs\MozComment.pas"/>
+        <UnitName Value="MozComment"/>
+      </Item44>
+      <Item45>
+        <Filename Value="..\..\Process\RemoveEmptyComment.pas"/>
+        <UnitName Value="RemoveEmptyComment"/>
+      </Item45>
+      <Item46>
+        <Filename Value="..\..\Process\Returns\BlockStyles.pas"/>
+        <UnitName Value="BlockStyles"/>
+      </Item46>
+      <Item47>
+        <Filename Value="..\..\Process\Returns\LongLineBreaker.pas"/>
+        <UnitName Value="LongLineBreaker"/>
+      </Item47>
+      <Item48>
+        <Filename Value="..\..\Process\Returns\NoReturnAfter.pas"/>
+        <UnitName Value="NoReturnAfter"/>
+      </Item48>
+      <Item49>
+        <Filename Value="..\..\Process\Returns\NoReturnBefore.pas"/>
+        <UnitName Value="NoReturnBefore"/>
+      </Item49>
+      <Item50>
+        <Filename Value="..\..\Process\Returns\PropertyOnOneLine.pas"/>
+        <UnitName Value="PropertyOnOneLine"/>
+      </Item50>
+      <Item51>
+        <Filename Value="..\..\Process\Returns\RemoveBlankLinesAfterProcHeader.pas"/>
+        <UnitName Value="RemoveBlankLinesAfterProcHeader"/>
+      </Item51>
+      <Item52>
+        <Filename Value="..\..\Process\Returns\RemoveBlankLinesInVars.pas"/>
+        <UnitName Value="RemoveBlankLinesInVars"/>
+      </Item52>
+      <Item53>
+        <Filename Value="..\..\Process\Returns\RemoveConsecutiveReturns.pas"/>
+        <UnitName Value="RemoveConsecutiveReturns"/>
+      </Item53>
+      <Item54>
+        <Filename Value="..\..\Process\Returns\RemoveReturnsAfter.pas"/>
+        <UnitName Value="RemoveReturnsAfter"/>
+      </Item54>
+      <Item55>
+        <Filename Value="..\..\Process\Returns\RemoveReturnsAfterBegin.pas"/>
+        <UnitName Value="RemoveReturnsAfterBegin"/>
+      </Item55>
+      <Item56>
+        <Filename Value="..\..\Process\Returns\RemoveReturnsBeforeEnd.pas"/>
+        <UnitName Value="RemoveReturnsBeforeEnd"/>
+      </Item56>
+      <Item57>
+        <Filename Value="..\..\Process\Returns\ReturnAfter.pas"/>
+        <UnitName Value="ReturnAfter"/>
+      </Item57>
+      <Item58>
+        <Filename Value="..\..\Process\Returns\ReturnBefore.pas"/>
+        <UnitName Value="ReturnBefore"/>
+      </Item58>
+      <Item59>
+        <Filename Value="..\..\Process\Returns\ReturnChars.pas"/>
+        <UnitName Value="ReturnChars"/>
+      </Item59>
+      <Item60>
+        <Filename Value="..\..\Process\Returns\ReturnsAfterFinalEnd.pas"/>
+        <UnitName Value="ReturnsAfterFinalEnd"/>
+      </Item60>
+      <Item61>
+        <Filename Value="..\..\Process\Spacing\MaxSpaces.pas"/>
+        <UnitName Value="MaxSpaces"/>
+      </Item61>
+      <Item62>
+        <Filename Value="..\..\Process\Spacing\NoSpaceAfter.pas"/>
+        <UnitName Value="NoSpaceAfter"/>
+      </Item62>
+      <Item63>
+        <Filename Value="..\..\Process\Spacing\NoSpaceBefore.pas"/>
+        <UnitName Value="NoSpaceBefore"/>
+      </Item63>
+      <Item64>
+        <Filename Value="..\..\Process\Spacing\RemoveSpaceAtLineEnd.pas"/>
+        <UnitName Value="RemoveSpaceAtLineEnd"/>
+      </Item64>
+      <Item65>
+        <Filename Value="..\..\Process\Spacing\SingleSpaceAfter.pas"/>
+        <UnitName Value="SingleSpaceAfter"/>
+      </Item65>
+      <Item66>
+        <Filename Value="..\..\Process\Spacing\SingleSpaceBefore.pas"/>
+        <UnitName Value="SingleSpaceBefore"/>
+      </Item66>
+      <Item67>
+        <Filename Value="..\..\Process\Spacing\SpaceBeforeColon.pas"/>
+        <UnitName Value="SpaceBeforeColon"/>
+      </Item67>
+      <Item68>
+        <Filename Value="..\..\Process\Spacing\SpaceToTab.pas"/>
+        <UnitName Value="SpaceToTab"/>
+      </Item68>
+      <Item69>
+        <Filename Value="..\..\Process\Spacing\TabToSpace.pas"/>
+        <UnitName Value="TabToSpace"/>
+      </Item69>
+      <Item70>
+        <Filename Value="..\..\Process\SwitchableVisitor.pas"/>
+        <UnitName Value="SwitchableVisitor"/>
+      </Item70>
+      <Item71>
+        <Filename Value="..\..\Process\Transform\AddBeginEnd.pas"/>
+        <UnitName Value="AddBeginEnd"/>
+      </Item71>
+      <Item72>
+        <Filename Value="..\..\Process\Transform\AddBlockEndSemicolon.pas"/>
+        <UnitName Value="AddBlockEndSemicolon"/>
+      </Item72>
+      <Item73>
+        <Filename Value="..\..\Process\Transform\FindReplace.pas"/>
+        <UnitName Value="FindReplace"/>
+      </Item73>
+      <Item74>
+        <Filename Value="..\..\Process\Transform\SortUses.pas"/>
+        <UnitName Value="SortUses"/>
+      </Item74>
+      <Item75>
+        <Filename Value="..\..\Process\Transform\SortUsesData.pas"/>
+        <UnitName Value="SortUsesData"/>
+      </Item75>
+      <Item76>
+        <Filename Value="..\..\Process\Transform\UsesClauseFindReplace.pas"/>
+        <UnitName Value="UsesClauseFindReplace"/>
+      </Item76>
+      <Item77>
+        <Filename Value="..\..\Process\Transform\UsesClauseInsert.pas"/>
+        <UnitName Value="UsesClauseInsert"/>
+      </Item77>
+      <Item78>
+        <Filename Value="..\..\Process\Transform\UsesClauseRemove.pas"/>
+        <UnitName Value="UsesClauseRemove"/>
+      </Item78>
+      <Item79>
+        <Filename Value="..\..\Process\TreeWalker.pas"/>
+        <UnitName Value="TreeWalker"/>
+      </Item79>
+      <Item80>
+        <Filename Value="..\..\Process\VisitSetNesting.pas"/>
+        <UnitName Value="VisitSetNesting"/>
+      </Item80>
+      <Item81>
+        <Filename Value="..\..\Process\VisitSetXY.pas"/>
+        <UnitName Value="VisitSetXY"/>
+      </Item81>
+      <Item82>
+        <Filename Value="..\..\Process\VisitStripEmptySpace.pas"/>
+        <UnitName Value="VisitStripEmptySpace"/>
+      </Item82>
+      <Item83>
+        <Filename Value="..\..\Process\Warnings\WarnAssignToFunctionName.pas"/>
+        <UnitName Value="WarnAssignToFunctionName"/>
+      </Item83>
+      <Item84>
+        <Filename Value="..\..\Process\Warnings\WarnCaseNoElse.pas"/>
+        <UnitName Value="WarnCaseNoElse"/>
+      </Item84>
+      <Item85>
+        <Filename Value="..\..\Process\Warnings\WarnDestroy.pas"/>
+        <UnitName Value="WarnDestroy"/>
+      </Item85>
+      <Item86>
+        <Filename Value="..\..\Process\Warnings\WarnEmptyBlock.pas"/>
+        <UnitName Value="WarnEmptyBlock"/>
+      </Item86>
+      <Item87>
+        <Filename Value="..\..\Process\Warnings\Warning.pas"/>
+        <UnitName Value="Warning"/>
+      </Item87>
+      <Item88>
+        <Filename Value="..\..\Process\Warnings\WarnRealType.pas"/>
+        <UnitName Value="WarnRealType"/>
+      </Item88>
+      <Item89>
+        <Filename Value="..\..\Process\Warnings\WarnUnusedParam.pas"/>
+        <UnitName Value="WarnUnusedParam"/>
+      </Item89>
+      <Item90>
+        <Filename Value="..\..\ReadWrite\Converter.pas"/>
+        <UnitName Value="Converter"/>
+      </Item90>
+      <Item91>
+        <Filename Value="..\..\ReadWrite\ConvertTypes.pas"/>
+        <UnitName Value="ConvertTypes"/>
+      </Item91>
+      <Item92>
+        <Filename Value="..\..\ReadWrite\EditorConverter.pas"/>
+        <UnitName Value="EditorConverter"/>
+      </Item92>
+      <Item93>
+        <Filename Value="..\..\ReadWrite\FileConverter.pas"/>
+        <UnitName Value="FileConverter"/>
+      </Item93>
+      <Item94>
+        <Filename Value="..\..\Settings\JcfRegistrySettings.pas"/>
+        <UnitName Value="JcfRegistrySettings"/>
+      </Item94>
+      <Item95>
+        <Filename Value="..\..\Settings\JcfSetBase.pas"/>
+        <UnitName Value="JcfSetBase"/>
+      </Item95>
+      <Item96>
+        <Filename Value="..\..\Settings\JcfSettings.pas"/>
+        <UnitName Value="JcfSettings"/>
+      </Item96>
+      <Item97>
+        <Filename Value="..\..\Settings\SetAlign.pas"/>
+        <UnitName Value="SetAlign"/>
+      </Item97>
+      <Item98>
+        <Filename Value="..\..\Settings\SetAsm.pas"/>
+        <UnitName Value="SetAsm"/>
+      </Item98>
+      <Item99>
+        <Filename Value="..\..\Settings\SetCaps.pas"/>
+        <UnitName Value="SetCaps"/>
+      </Item99>
+      <Item100>
+        <Filename Value="..\..\Settings\SetClarify.pas"/>
+        <UnitName Value="SetClarify"/>
+      </Item100>
+      <Item101>
+        <Filename Value="..\..\Settings\SetComments.pas"/>
+        <UnitName Value="SetComments"/>
+      </Item101>
+      <Item102>
+        <Filename Value="..\..\Settings\SetIndent.pas"/>
+        <UnitName Value="SetIndent"/>
+      </Item102>
+      <Item103>
+        <Filename Value="..\..\Settings\SetObfuscate.pas"/>
+        <UnitName Value="SetObfuscate"/>
+      </Item103>
+      <Item104>
+        <Filename Value="..\..\Settings\SetPreProcessor.pas"/>
+        <UnitName Value="SetPreProcessor"/>
+      </Item104>
+      <Item105>
+        <Filename Value="..\..\Settings\SetReplace.pas"/>
+        <UnitName Value="SetReplace"/>
+      </Item105>
+      <Item106>
+        <Filename Value="..\..\Settings\SetReturns.pas"/>
+        <UnitName Value="SetReturns"/>
+      </Item106>
+      <Item107>
+        <Filename Value="..\..\Settings\SetSpaces.pas"/>
+        <UnitName Value="SetSpaces"/>
+      </Item107>
+      <Item108>
+        <Filename Value="..\..\Settings\SettingsTypes.pas"/>
+        <UnitName Value="SettingsTypes"/>
+      </Item108>
+      <Item109>
+        <Filename Value="..\..\Settings\SetTransform.pas"/>
+        <UnitName Value="SetTransform"/>
+      </Item109>
+      <Item110>
+        <Filename Value="..\..\Settings\SetUses.pas"/>
+        <UnitName Value="SetUses"/>
+      </Item110>
+      <Item111>
+        <Filename Value="..\..\Settings\SetWordList.pas"/>
+        <UnitName Value="SetWordList"/>
+      </Item111>
+      <Item112>
+        <Filename Value="..\..\Settings\Streams\SettingsStream.pas"/>
+        <UnitName Value="SettingsStream"/>
+      </Item112>
+      <Item113>
+        <Filename Value="..\..\Ui\fJcfErrorDisplay.pas"/>
+        <UnitName Value="fJcfErrorDisplay"/>
+      </Item113>
+      <Item114>
+        <Filename Value="..\..\Utils\Delay.pas"/>
+        <UnitName Value="Delay"/>
+      </Item114>
+      <Item115>
+        <Filename Value="..\..\Utils\IntList.pas"/>
+        <UnitName Value="IntList"/>
+      </Item115>
+      <Item116>
+        <Filename Value="..\..\Utils\JcfFontSetFunctions.pas"/>
+        <UnitName Value="JcfFontSetFunctions"/>
+      </Item116>
+      <Item117>
+        <Filename Value="..\..\Utils\JcfHelp.pas"/>
+        <UnitName Value="JcfHelp"/>
+      </Item117>
+      <Item118>
+        <Filename Value="..\..\Utils\JcfLog.pas"/>
+        <UnitName Value="JcfLog"/>
+      </Item118>
+      <Item119>
+        <Filename Value="..\..\Utils\JcfMiscFunctions.pas"/>
+        <UnitName Value="JcfMiscFunctions"/>
+      </Item119>
+      <Item120>
+        <Filename Value="..\..\Ui\fAbout.lfm"/>
+        <Type Value="LFM"/>
+      </Item120>
+      <Item121>
+        <Filename Value="..\..\Ui\fAbout.pas"/>
+        <UnitName Value="fAbout"/>
+      </Item121>
+      <Item122>
+        <Filename Value="..\..\JcfVersionConsts.pas"/>
+        <UnitName Value="JcfVersionConsts"/>
+      </Item122>
+      <Item123>
+        <Filename Value="..\..\Ui\Settings\frFiles.lfm"/>
+        <Type Value="LFM"/>
+      </Item123>
+      <Item124>
+        <Filename Value="..\..\Ui\Settings\frFiles.pas"/>
+        <UnitName Value="frFiles"/>
+      </Item124>
+      <Item125>
+        <Filename Value="..\..\Ui\Settings\frObfuscateSettings.lfm"/>
+        <Type Value="LFM"/>
+      </Item125>
+      <Item126>
+        <Filename Value="..\..\Ui\Settings\frObfuscateSettings.pas"/>
+        <UnitName Value="frObfuscateSettings"/>
+      </Item126>
+      <Item127>
+        <Filename Value="..\..\Ui\Settings\frClarify.lfm"/>
+        <Type Value="LFM"/>
+      </Item127>
+      <Item128>
+        <Filename Value="..\..\Ui\Settings\frClarify.pas"/>
+        <UnitName Value="frClarify"/>
+      </Item128>
+      <Item129>
+        <Filename Value="..\..\Ui\Settings\frClarifySpaces.lfm"/>
+        <Type Value="LFM"/>
+      </Item129>
+      <Item130>
+        <Filename Value="..\..\Ui\Settings\frClarifySpaces.pas"/>
+        <UnitName Value="frClarifySpaces"/>
+      </Item130>
+      <Item131>
+        <Filename Value="..\..\Ui\Settings\frClarifyIndent.lfm"/>
+        <Type Value="LFM"/>
+      </Item131>
+      <Item132>
+        <Filename Value="..\..\Ui\Settings\frClarifyIndent.pas"/>
+        <UnitName Value="frClarifyIndent"/>
+      </Item132>
+      <Item133>
+        <Filename Value="..\..\Ui\Settings\frBlankLines.lfm"/>
+        <Type Value="LFM"/>
+      </Item133>
+      <Item134>
+        <Filename Value="..\..\Ui\Settings\frBlankLines.pas"/>
+        <UnitName Value="frBlankLines"/>
+      </Item134>
+      <Item135>
+        <Filename Value="..\..\Ui\Settings\frClarifyAlign.lfm"/>
+        <Type Value="LFM"/>
+      </Item135>
+      <Item136>
+        <Filename Value="..\..\Ui\Settings\frClarifyAlign.pas"/>
+        <UnitName Value="frClarifyAlign"/>
+      </Item136>
+      <Item137>
+        <Filename Value="..\..\Ui\Settings\frClarifyLongLineBreaker.lfm"/>
+        <Type Value="LFM"/>
+      </Item137>
+      <Item138>
+        <Filename Value="..\..\Ui\Settings\frClarifyLongLineBreaker.pas"/>
+        <UnitName Value="frClarifyLongLineBreaker"/>
+      </Item138>
+      <Item139>
+        <Filename Value="..\..\Ui\Settings\frClarifyReturns.lfm"/>
+        <Type Value="LFM"/>
+      </Item139>
+      <Item140>
+        <Filename Value="..\..\Ui\Settings\frClarifyReturns.pas"/>
+        <UnitName Value="frClarifyReturns"/>
+      </Item140>
+      <Item141>
+        <Filename Value="..\..\Ui\Settings\frCompilerDirectReturns.lfm"/>
+        <Type Value="LFM"/>
+      </Item141>
+      <Item142>
+        <Filename Value="..\..\Ui\Settings\frCompilerDirectReturns.pas"/>
+        <UnitName Value="frCompilerDirectReturns"/>
+      </Item142>
+      <Item143>
+        <Filename Value="..\..\Ui\Settings\frClarifyBlocks.lfm"/>
+        <Type Value="LFM"/>
+      </Item143>
+      <Item144>
+        <Filename Value="..\..\Ui\Settings\frClarifyBlocks.pas"/>
+        <UnitName Value="frClarifyBlocks"/>
+      </Item144>
+      <Item145>
+        <Filename Value="..\..\Ui\Settings\frClarifyCaseBlocks.lfm"/>
+        <Type Value="LFM"/>
+      </Item145>
+      <Item146>
+        <Filename Value="..\..\Ui\Settings\frClarifyCaseBlocks.pas"/>
+        <UnitName Value="frClarifyCaseBlocks"/>
+      </Item146>
+      <Item147>
+        <Filename Value="..\..\Ui\Settings\frComments.lfm"/>
+        <Type Value="LFM"/>
+      </Item147>
+      <Item148>
+        <Filename Value="..\..\Ui\Settings\frComments.pas"/>
+        <UnitName Value="frComments"/>
+      </Item148>
+      <Item149>
+        <Filename Value="..\..\Ui\Settings\frWarnings.lfm"/>
+        <Type Value="LFM"/>
+      </Item149>
+      <Item150>
+        <Filename Value="..\..\Ui\Settings\frWarnings.pas"/>
+        <UnitName Value="frWarnings"/>
+      </Item150>
+      <Item151>
+        <Filename Value="..\..\Ui\Settings\frReservedCapsSettings.lfm"/>
+        <Type Value="LFM"/>
+      </Item151>
+      <Item152>
+        <Filename Value="..\..\Ui\Settings\frReservedCapsSettings.pas"/>
+        <UnitName Value="frReservedCapsSettings"/>
+      </Item152>
+      <Item153>
+        <Filename Value="..\..\Ui\Settings\frAnyCapsSettings.lfm"/>
+        <Type Value="LFM"/>
+      </Item153>
+      <Item154>
+        <Filename Value="..\..\Ui\Settings\frAnyCapsSettings.pas"/>
+        <UnitName Value="frAnyCapsSettings"/>
+      </Item154>
+      <Item155>
+        <Filename Value="..\..\Ui\Settings\frIdentifierCapsSettings.lfm"/>
+        <Type Value="LFM"/>
+      </Item155>
+      <Item156>
+        <Filename Value="..\..\Ui\Settings\frIdentifierCapsSettings.pas"/>
+        <UnitName Value="frIdentifierCapsSettings"/>
+      </Item156>
+      <Item157>
+        <Filename Value="..\..\Ui\Settings\frNotIdentifierCapsSettings.lfm"/>
+        <Type Value="LFM"/>
+      </Item157>
+      <Item158>
+        <Filename Value="..\..\Ui\Settings\frNotIdentifierCapsSettings.pas"/>
+        <UnitName Value="frNotIdentifierCapsSettings"/>
+      </Item158>
+      <Item159>
+        <Filename Value="..\..\Ui\Settings\frUnitCaps.lfm"/>
+        <Type Value="LFM"/>
+      </Item159>
+      <Item160>
+        <Filename Value="..\..\Ui\Settings\frUnitCaps.pas"/>
+        <UnitName Value="frUnitCaps"/>
+      </Item160>
+      <Item161>
+        <Filename Value="..\..\Ui\Settings\frReplace.lfm"/>
+        <Type Value="LFM"/>
+      </Item161>
+      <Item162>
+        <Filename Value="..\..\Ui\Settings\frReplace.pas"/>
+        <UnitName Value="frReplace"/>
+      </Item162>
+      <Item163>
+        <Filename Value="..\..\Ui\Settings\frUses.lfm"/>
+        <Type Value="LFM"/>
+      </Item163>
+      <Item164>
+        <Filename Value="..\..\Ui\Settings\frUses.pas"/>
+        <UnitName Value="frUses"/>
+      </Item164>
+      <Item165>
+        <Filename Value="..\..\Ui\Settings\frTransform.lfm"/>
+        <Type Value="LFM"/>
+      </Item165>
+      <Item166>
+        <Filename Value="..\..\Ui\Settings\frTransform.pas"/>
+        <UnitName Value="frTransform"/>
+      </Item166>
+      <Item167>
+        <Filename Value="..\..\Ui\Settings\frAsm.lfm"/>
+        <Type Value="LFM"/>
+      </Item167>
+      <Item168>
+        <Filename Value="..\..\Ui\Settings\frAsm.pas"/>
+        <UnitName Value="frAsm"/>
+      </Item168>
+      <Item169>
+        <Filename Value="..\..\Ui\Settings\frPreProcessor.lfm"/>
+        <Type Value="LFM"/>
+      </Item169>
+      <Item170>
+        <Filename Value="..\..\Ui\Settings\frPreProcessor.pas"/>
+        <UnitName Value="frPreProcessor"/>
+      </Item170>
+      <Item171>
+        <Filename Value="jcfuiconsts.pas"/>
+        <UnitName Value="JcfUIConsts"/>
+      </Item171>
+      <Item172>
+        <Filename Value="..\..\ReadWrite\Diff.pas"/>
+        <UnitName Value="Diff"/>
+      </Item172>
+      <Item173>
+        <Filename Value="..\..\ReadWrite\diffmerge.pas"/>
+        <UnitName Value="diffmerge"/>
+      </Item173>
+    </Files>
+    <CompatibilityMode Value="True"/>
+    <i18n>
+      <EnableI18N Value="True"/>
+      <OutDir Value="languages"/>
+    </i18n>
+    <RequiredPkgs Count="1">
+      <Item1>
+        <PackageName Value="IDEIntf"/>
+      </Item1>
+    </RequiredPkgs>
+    <UsageOptions>
+      <UnitPath Value="$(PkgOutDir)"/>
+    </UsageOptions>
+    <PublishOptions>
+      <Version Value="2"/>
+    </PublishOptions>
+  </Package>
+</CONFIG>
diff --git a/components/jcf2/IdePlugin/lazarus/jcfidelazarus.pas b/components/jcf2/IdePlugin/lazarus/jcfidelazarus.pas
index e01392abdf..23669991a5 100644
--- a/components/jcf2/IdePlugin/lazarus/jcfidelazarus.pas
+++ b/components/jcf2/IdePlugin/lazarus/jcfidelazarus.pas
@@ -1,55 +1,55 @@
-{ This file was automatically created by Lazarus. Do not edit!
-  This source is only used to compile and install the package.
- }
-
-unit jcfidelazarus;
-
-{$warn 5023 off : no warning about unused units}
-interface
-
-uses
-  JcfIdeMain, JcfIdeRegister, AsmKeywords, BuildParseTree, BuildTokenList, 
-  ParseError, ParseTreeNode, ParseTreeNodeType, PreProcessorExpressionParser, 
-  PreProcessorExpressionTokenise, PreProcessorExpressionTokens, 
-  PreProcessorParseTree, SourceToken, SourceTokenList, Tokens, TokenUtils, 
-  fShowParseTree, AlignAssign, AlignBase, AlignComment, AlignConst, 
-  AlignField, AlignTypedef, AlignVars, AllProcesses, BaseVisitor, 
-  Capitalisation, IdentifierCaps, SpecificWordCaps, UnitNameCaps, FormatFlags, 
-  IndentAsmParam, Indenter, BasicStats, Nesting, FixCase, RebreakLines, 
-  ReduceWhiteSpace, RemoveBlankLine, RemoveComment, 
-  RemoveConsecutiveWhiteSpace, RemoveReturn, RemoveUnneededWhiteSpace, 
-  MozComment, RemoveEmptyComment, BlockStyles, LongLineBreaker, NoReturnAfter, 
-  NoReturnBefore, PropertyOnOneLine, RemoveBlankLinesAfterProcHeader, 
-  RemoveBlankLinesInVars, RemoveConsecutiveReturns, RemoveReturnsAfter, 
-  RemoveReturnsAfterBegin, RemoveReturnsBeforeEnd, ReturnAfter, ReturnBefore, 
-  ReturnChars, ReturnsAfterFinalEnd, MaxSpaces, NoSpaceAfter, NoSpaceBefore, 
-  RemoveSpaceAtLineEnd, SingleSpaceAfter, SingleSpaceBefore, SpaceBeforeColon, 
-  SpaceToTab, TabToSpace, SwitchableVisitor, AddBeginEnd, 
-  AddBlockEndSemicolon, FindReplace, SortUses, SortUsesData, 
-  UsesClauseFindReplace, UsesClauseInsert, UsesClauseRemove, TreeWalker, 
-  VisitSetNesting, VisitSetXY, VisitStripEmptySpace, WarnAssignToFunctionName, 
-  WarnCaseNoElse, WarnDestroy, WarnEmptyBlock, Warning, WarnRealType, 
-  WarnUnusedParam, Converter, ConvertTypes, EditorConverter, FileConverter, 
-  JcfRegistrySettings, JcfSetBase, JcfSettings, SetAlign, SetAsm, SetCaps, 
-  SetClarify, SetComments, SetIndent, SetObfuscate, SetPreProcessor, 
-  SetReplace, SetReturns, SetSpaces, SettingsTypes, SetTransform, SetUses, 
-  SetWordList, SettingsStream, fJcfErrorDisplay, Delay, IntList, 
-  JcfFontSetFunctions, JcfHelp, JcfLog, JcfMiscFunctions, fAbout, 
-  JcfVersionConsts, frFiles, frObfuscateSettings, frClarify, frClarifySpaces, 
-  frClarifyIndent, frBlankLines, frClarifyAlign, frClarifyLongLineBreaker, 
-  frClarifyReturns, frCompilerDirectReturns, frClarifyBlocks, 
-  frClarifyCaseBlocks, frComments, frWarnings, frReservedCapsSettings, 
-  frAnyCapsSettings, frIdentifierCapsSettings, frNotIdentifierCapsSettings, 
-  frUnitCaps, frReplace, frUses, frTransform, frAsm, frPreProcessor, 
-  jcfuiconsts, LazarusPackageIntf;
-
-implementation
-
-procedure Register;
-begin
-  RegisterUnit('JcfIdeRegister', @JcfIdeRegister.Register);
-end;
-
-initialization
-  RegisterPackage('jcfidelazarus', @Register);
-end.
+{ This file was automatically created by Lazarus. Do not edit!
+  This source is only used to compile and install the package.
+ }
+
+unit jcfidelazarus;
+
+{$warn 5023 off : no warning about unused units}
+interface
+
+uses
+  JcfIdeMain, JcfIdeRegister, AsmKeywords, BuildParseTree, BuildTokenList, 
+  ParseError, ParseTreeNode, ParseTreeNodeType, PreProcessorExpressionParser, 
+  PreProcessorExpressionTokenise, PreProcessorExpressionTokens, 
+  PreProcessorParseTree, SourceToken, SourceTokenList, Tokens, TokenUtils, 
+  fShowParseTree, AlignAssign, AlignBase, AlignComment, AlignConst, 
+  AlignField, AlignTypedef, AlignVars, AllProcesses, BaseVisitor, 
+  Capitalisation, IdentifierCaps, SpecificWordCaps, UnitNameCaps, FormatFlags, 
+  IndentAsmParam, Indenter, BasicStats, Nesting, FixCase, RebreakLines, 
+  ReduceWhiteSpace, RemoveBlankLine, RemoveComment, 
+  RemoveConsecutiveWhiteSpace, RemoveReturn, RemoveUnneededWhiteSpace, 
+  MozComment, RemoveEmptyComment, BlockStyles, LongLineBreaker, NoReturnAfter, 
+  NoReturnBefore, PropertyOnOneLine, RemoveBlankLinesAfterProcHeader, 
+  RemoveBlankLinesInVars, RemoveConsecutiveReturns, RemoveReturnsAfter, 
+  RemoveReturnsAfterBegin, RemoveReturnsBeforeEnd, ReturnAfter, ReturnBefore, 
+  ReturnChars, ReturnsAfterFinalEnd, MaxSpaces, NoSpaceAfter, NoSpaceBefore, 
+  RemoveSpaceAtLineEnd, SingleSpaceAfter, SingleSpaceBefore, SpaceBeforeColon, 
+  SpaceToTab, TabToSpace, SwitchableVisitor, AddBeginEnd, 
+  AddBlockEndSemicolon, FindReplace, SortUses, SortUsesData, 
+  UsesClauseFindReplace, UsesClauseInsert, UsesClauseRemove, TreeWalker, 
+  VisitSetNesting, VisitSetXY, VisitStripEmptySpace, WarnAssignToFunctionName, 
+  WarnCaseNoElse, WarnDestroy, WarnEmptyBlock, Warning, WarnRealType, 
+  WarnUnusedParam, Converter, ConvertTypes, EditorConverter, FileConverter, 
+  JcfRegistrySettings, JcfSetBase, JcfSettings, SetAlign, SetAsm, SetCaps, 
+  SetClarify, SetComments, SetIndent, SetObfuscate, SetPreProcessor, 
+  SetReplace, SetReturns, SetSpaces, SettingsTypes, SetTransform, SetUses, 
+  SetWordList, SettingsStream, fJcfErrorDisplay, Delay, IntList, 
+  JcfFontSetFunctions, JcfHelp, JcfLog, JcfMiscFunctions, fAbout, 
+  JcfVersionConsts, frFiles, frObfuscateSettings, frClarify, frClarifySpaces, 
+  frClarifyIndent, frBlankLines, frClarifyAlign, frClarifyLongLineBreaker, 
+  frClarifyReturns, frCompilerDirectReturns, frClarifyBlocks, 
+  frClarifyCaseBlocks, frComments, frWarnings, frReservedCapsSettings, 
+  frAnyCapsSettings, frIdentifierCapsSettings, frNotIdentifierCapsSettings, 
+  frUnitCaps, frReplace, frUses, frTransform, frAsm, frPreProcessor, 
+  JcfUIConsts, Diff, diffmerge, LazarusPackageIntf;
+
+implementation
+
+procedure Register;
+begin
+  RegisterUnit('JcfIdeRegister', @JcfIdeRegister.Register);
+end;
+
+initialization
+  RegisterPackage('jcfidelazarus', @Register);
+end.
diff --git a/components/jcf2/IdePlugin/lazarus/jcfidemain.pas b/components/jcf2/IdePlugin/lazarus/jcfidemain.pas
index 058399b740..3c151210ff 100644
--- a/components/jcf2/IdePlugin/lazarus/jcfidemain.pas
+++ b/components/jcf2/IdePlugin/lazarus/jcfidemain.pas
@@ -82,6 +82,9 @@ type
 
 implementation
 
+uses
+  diffmerge;
+
 function FileIsAllowedType(const psFileName: string): boolean;
 const
   ALLOWED_FILE_TYPES: array[1..5] of string = ('.pas', '.pp', '.dpr', '.lpr', '.dpk');
@@ -275,7 +278,7 @@ begin
       while (wI > 1) and (fcConverter.OutputCode[wI] in [#10, #13, ' ']) do
         Dec(wI);
       outputstr := Copy(fcConverter.OutputCode, 1, wI);
-      srcEditor.ReplaceLines(BlockBegin.Y, BlockEnd.Y, outputstr, false);
+      DiffMergeEditor(srcEditor,outputstr,BlockBegin.Y,BlockEnd.Y);
     end
     else
     begin    //try formating wrapping selected code in fake unit.
@@ -291,7 +294,7 @@ begin
       fcConverter.GuiMessages := true;
       fcConverter.ConvertUsingFakeUnit;
       if not fcConverter.ConvertError then
-        srcEditor.ReplaceText(BlockBegin, BlockEnd, fcConverter.OutputCode);
+        DiffMergeEditor(srcEditor,fcConverter.OutputCode,BlockBegin.Y,BlockEnd.Y);
     end;
   finally
     fcConverter.Free;
diff --git a/components/jcf2/ReadWrite/Converter.pas b/components/jcf2/ReadWrite/Converter.pas
index 9a3c51bd33..5a7b9c7e1e 100644
--- a/components/jcf2/ReadWrite/Converter.pas
+++ b/components/jcf2/ReadWrite/Converter.pas
@@ -392,14 +392,17 @@ begin
 
   { put markers into the input }
   fsInputCode := StrInsert(FORMAT_END, fsInputCode, liRealInputEnd);
-  fsInputCode := StrInsert(FORMAT_START, fsInputCode, liRealInputStart);
-
+  { add a new line after FORMAT_START, prevents bad formating of first selected line. }
+  fsInputCode := StrInsert(FORMAT_START+#10, fsInputCode, liRealInputStart);
   Convert;
-
   { locate the markers in the output, and replace before and after }
   liOutputStart := Pos(FORMAT_START, fsOutputCode) + Length(FORMAT_START);
+  {remode new line added after FORMAT_START }
+  if (liOutputStart<length(fsOutputCode)) and (fsOutputCode[liOutputStart]=#13) then
+    inc(liOutputStart);
+  if (liOutputStart<length(fsOutputCode)) and (fsOutputCode[liOutputStart]=#10) then
+    inc(liOutputStart);
   liOutputEnd   := PosEx(FORMAT_END, fsOutputCode,liOutputStart);
-
   { splice }
   if aOnlyOutputSelection then
     lsNewOutput := Copy(fsOutputCode, liOutputStart, (liOutputEnd - liOutputStart))
diff --git a/components/jcf2/ReadWrite/Diff.pas b/components/jcf2/ReadWrite/Diff.pas
new file mode 100644
index 0000000000..ddea5f9310
--- /dev/null
+++ b/components/jcf2/ReadWrite/Diff.pas
@@ -0,0 +1,901 @@
+unit Diff;
+
+{$IFDEF FPC}
+  {$mode delphi}{$H+}
+{$ENDIF}
+
+(*******************************************************************************
+* Component         TDiff                                                      *
+* Version:          5.02                                                        *
+* Date:             23 May 2020                                                *
+* Compilers:        Delphi 10.x                                                *
+* Author:           Angus Johnson - angusj-AT-myrealbox-DOT-com                *
+* Copyright:        © 2001-2009 Angus Johnson                                  *
+* Updated by:       Rickard Johansson (RJ TextEd)                              *
+*                                                                              *
+* Licence to use, terms and conditions:                                        *
+*                   The code in the TDiff component is released as freeware    *
+*                   provided you agree to the following terms & conditions:    *
+*                   1. the copyright notice, terms and conditions are          *
+*                   left unchanged                                             *
+*                   2. modifications to the code by other authors must be      *
+*                   clearly documented and accompanied by the modifier's name. *
+*                   3. the TDiff component may be freely compiled into binary  *
+*                   format and no acknowledgement is required. However, a      *
+*                   discrete acknowledgement would be appreciated (eg. in a    *
+*                   program's 'About Box').                                    *
+*                                                                              *
+* Description:      Component to list differences between two integer arrays   *
+*                   using a "longest common subsequence" algorithm.            *
+*                   Typically, this component is used to diff 2 text files     *
+*                   once their individuals lines have been hashed.             *
+*                                                                              *
+* Acknowledgements: The key algorithm in this component is based on:           *
+*                   "An O(NP) Sequence Comparison Algorithm"                   *
+*                   by Sun Wu, Udi Manber & Gene Myers                         *
+*                   and uses a "divide-and-conquer" technique to avoid         *
+*                   using exponential amounts of memory as described in        *
+*                   "An O(ND) Difference Algorithm and its Variations"         *
+*                   By E Myers - Algorithmica Vol. 1 No. 2, 1986, pp. 251-266  *
+*******************************************************************************)
+
+(*******************************************************************************
+* History:                                                                     *
+* 13 December 2001 - Original release (used Myer's O(ND) Difference Algorithm) *
+* 22 April 2008    - Complete rewrite to greatly improve the code and          *
+*                    provide a much simpler view of differences through a new  *
+*                    'Compares' property.                                      *
+* 21 May 2008      - Another complete code rewrite to use Sun Wu et al.'s      *
+*                    O(NP) Sequence Comparison Algorithm which more than       *
+*                    halves times of typical comparisons.                      *
+* 24 May 2008      - Reimplemented "divide-and-conquer" technique (which was   *
+*                    omitted in 21 May release) so memory use is again minimal.*
+* 25 May 2008      - Removed recursion to avoid the possibility of running out *
+*                    of stack memory during massive comparisons.               *
+* 2 June 2008      - Bugfix: incorrect number of appended AddChangeInt() calls *
+*                    in Execute() for integer arrays. (It was OK with Chars)   *
+*                    Added check to prevent repeat calls to Execute() while    *
+*                    already executing.                                        *
+*                    Added extra parse of differences to find occasional       *
+*                    missed matches. (See readme.txt for further discussion)   *
+* 7 November 2009  - Updated so now compiles in newer versions of Delphi.      *
+*                                                                              *
+* 11 November 2018 - Added TList<Cardinal> to store hash values                *
+*                    Made some minor code formatting and code changes          *
+* 19 May 2020        Added Lazarus support                                     *
+* 23 May 2020      - Minor changes and fixed an issue in AddChangeChr()        *
+*******************************************************************************)
+
+interface
+
+uses
+{$IFnDEF FPC}
+  Generics.Collections, Windows,
+{$ELSE}
+  LCLIntf, LCLType, Fgl,
+{$ENDIF}
+  SysUtils,
+  Forms,
+  Classes;
+
+const
+  MAX_DIAGONAL = $FFFFFF; //~16 million
+
+type
+
+  P8Bits = PByte;
+
+  PDiags = ^TDiags;
+  TDiags = array [-MAX_DIAGONAL .. MAX_DIAGONAL] of integer;
+
+  TChangeKind = (ckNone, ckAdd, ckDelete, ckModify);
+
+  PCompareRec = ^TCompareRec;
+  TCompareRec = record
+    Kind      : TChangeKind;
+    oldIndex1 : Integer;
+    oldIndex2 : Integer;
+    case boolean of
+      false   : (chr1, chr2 : Char);
+      true    : (int1, int2 : Cardinal);
+  end;
+
+  PDiffVars = ^TDiffVars;
+  TDiffVars = record
+    offset1 : integer;
+    offset2 : integer;
+    len1    : integer;
+    len2    : integer;
+  end;
+
+  TDiffStats = record
+    matches  : integer;
+    adds     : integer;
+    deletes  : integer;
+    modifies : integer;
+  end;
+
+  {$IFDEF FPC}
+  TIntegerList = TFPGList<Cardinal>;
+  {$ENDIF}
+
+  TDiff = class(TComponent)
+  private
+    FCompareList: TList;
+    FDiffList: TList;      //this TList circumvents the need for recursion
+    FCancelled: boolean;
+    FExecuting: boolean;
+    FCompareInts: boolean; //ie are we comparing integer arrays or char arrays
+    DiagBufferF: pointer;
+    DiagBufferB: pointer;
+    DiagF, DiagB: PDiags;
+    FDiffStats: TDiffStats;
+    FLastCompareRec: TCompareRec;
+    {$IFDEF FPC}
+    FList1: TIntegerList;
+    FList2: TIntegerList;
+    {$ELSE}
+    FList1: TList<Cardinal>;
+    FList2: TList<Cardinal>;
+    {$ENDIF}
+    FStr1: string;
+    FStr2: string;
+    procedure PushDiff(offset1, offset2, len1, len2: integer);
+    function  PopDiff: boolean;
+    procedure InitDiagArrays(len1, len2: integer);
+    procedure DiffInt(offset1, offset2, len1, len2: integer);
+    procedure DiffChr(offset1, offset2, len1, len2: integer);
+    function SnakeChrF(k,offset1,offset2,len1,len2: integer): boolean;
+    function SnakeChrB(k,offset1,offset2,len1,len2: integer): boolean;
+    function SnakeIntF(k,offset1,offset2,len1,len2: integer): boolean;
+    function SnakeIntB(k,offset1,offset2,len1,len2: integer): boolean;
+    procedure AddChangeChr(offset1, range: integer; ChangeKind: TChangeKind);
+    procedure AddChangeInt(offset1, range: integer; ChangeKind: TChangeKind);
+    function GetCompareCount: integer;
+    function GetCompare(index: integer): TCompareRec;
+  public
+    constructor Create(aOwner: TComponent); override;
+    destructor Destroy; override;
+
+    // Compare strings or list of Cardinals ...
+    {$IFDEF FPC}
+    function Execute(const alist1, alist2: TIntegerList): boolean; overload;
+    {$ELSE}
+    function Execute(const alist1, alist2: TList<Cardinal>): boolean; overload;
+    {$ENDIF}
+    function Execute(const s1, s2: string): boolean; overload;
+    // Cancel allows interrupting excessively prolonged comparisons
+    procedure Cancel;
+    procedure Clear;
+    property Cancelled: boolean read FCancelled;
+    property Count: integer read GetCompareCount;
+    property Compares[index: integer]: TCompareRec read GetCompare; default;
+    property DiffStats: TDiffStats read FDiffStats;
+  end;
+
+implementation
+
+procedure Register;
+begin
+  RegisterComponents('Samples', [TDiff]);
+end;
+
+//------------------------------------------------------------------------------
+//------------------------------------------------------------------------------
+
+constructor TDiff.Create(aOwner: TComponent);
+begin
+  inherited;
+  FCompareList := TList.create;
+  FDiffList := TList.Create;
+end;
+//------------------------------------------------------------------------------
+
+destructor TDiff.Destroy;
+begin
+  Clear;
+  FCompareList.free;
+  FDiffList.Free;
+  inherited;
+end;
+//------------------------------------------------------------------------------
+
+{$IFDEF FPC}
+function TDiff.Execute(const alist1, alist2: TIntegerList): boolean;
+{$ELSE}
+function TDiff.Execute(const alist1, alist2: TList<Cardinal>): boolean;
+{$ENDIF}
+var
+  i, Len1Minus1: integer;
+  len1,len2: Integer;
+begin
+  Result := not FExecuting;
+  if not Result then exit;
+  FCancelled := false;
+  FExecuting := true;
+  try
+    FList1 := alist1;
+    FList2 := alist2;
+    len1 := FList1.Count;
+    len2 := FList2.Count;
+
+    Clear;
+
+    Len1Minus1 := len1 -1;
+    FCompareList.Capacity := len1 + len2;
+    FCompareInts := true;
+
+    GetMem(DiagBufferF, sizeof(integer)*(len1+len2+3));
+    GetMem(DiagBufferB, sizeof(integer)*(len1+len2+3));
+    try
+      PushDiff(0, 0, len1, len2);
+      while PopDiff do;
+    finally
+      freeMem(DiagBufferF);
+      freeMem(DiagBufferB);
+    end;
+
+    if FCancelled then
+    begin
+      Result := false;
+      Clear;
+      exit;
+    end;
+
+    //correct the occasional missed match ...
+    for i := 1 to count -1 do
+      with PCompareRec(FCompareList[i])^ do
+        if (Kind = ckModify) and (int1 = int2) then
+        begin
+          Kind := ckNone;
+          Dec(FDiffStats.modifies);
+          Inc(FDiffStats.matches);
+        end;
+        
+    //finally, append any trailing matches onto compareList ...
+    with FLastCompareRec do
+      AddChangeInt(oldIndex1,len1Minus1-oldIndex1, ckNone);
+  finally
+    FExecuting := false;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TDiff.Execute(const s1, s2: string): boolean;
+var
+  i, Len1Minus1: integer;
+  len1,len2: Integer;
+begin
+  Result := not FExecuting;
+  if not Result then exit;
+  FCancelled := false;
+  FExecuting := true;
+  try
+    Clear;
+    len1 := Length(s1);
+    len2 := Length(s2);
+    Len1Minus1 := len1 -1;
+    FCompareList.Capacity := len1 + len2;
+    FDiffList.Capacity := 1024;
+    FCompareInts := false;
+
+    GetMem(DiagBufferF, sizeof(integer)*(len1+len2+3));
+    GetMem(DiagBufferB, sizeof(integer)*(len1+len2+3));
+    FStr1 := s1;
+    FStr2 := s2;
+    try
+      PushDiff(1, 1, len1, len2);
+      while PopDiff do;
+    finally
+      freeMem(DiagBufferF);
+      freeMem(DiagBufferB);
+    end;
+
+    if FCancelled then
+    begin
+      Result := false;
+      Clear;
+      exit;
+    end;
+
+    //correct the occasional missed match ...
+    for i := 1 to count -1 do
+      with PCompareRec(FCompareList[i])^ do
+        if (Kind = ckModify) and (chr1 = chr2) then
+        begin
+          Kind := ckNone;
+          Dec(FDiffStats.modifies);
+          Inc(FDiffStats.matches);
+        end;
+
+    //finally, append any trailing matches onto compareList ...
+    with FLastCompareRec do
+    begin
+      AddChangeChr(oldIndex1,len1Minus1-oldIndex1, ckNone);
+    end;
+  finally
+    FExecuting := false;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TDiff.PushDiff(offset1, offset2, len1, len2: integer);
+var
+  DiffVars: PDiffVars;
+begin
+  new(DiffVars);
+  DiffVars.offset1 := offset1;
+  DiffVars.offset2 := offset2;
+  DiffVars.len1 := len1;
+  DiffVars.len2 := len2;
+  FDiffList.Add(DiffVars);
+end;
+//------------------------------------------------------------------------------
+
+function  TDiff.PopDiff: boolean;
+var
+  DiffVars: PDiffVars;
+  idx: integer;
+begin
+  idx := FDiffList.Count -1;
+  Result := idx >= 0;
+  if not Result then exit;
+  DiffVars := PDiffVars(FDiffList[idx]);
+  with DiffVars^ do
+    if FCompareInts then
+      DiffInt(offset1, offset2, len1, len2) else
+      DiffChr(offset1, offset2, len1, len2);
+  Dispose(DiffVars);
+  FDiffList.Delete(idx);
+end;
+//------------------------------------------------------------------------------
+
+procedure TDiff.InitDiagArrays(len1, len2: integer);
+var
+  i: integer;
+begin
+  //assumes that top and bottom matches have been excluded
+  P8Bits(DiagF) := P8Bits(DiagBufferF) - sizeof(integer)*(MAX_DIAGONAL-(len1+1));
+  for i := - (len1+1) to (len2+1) do
+    DiagF^[i] := -MAXINT;
+  DiagF^[1] := -1;
+
+  P8Bits(DiagB) := P8Bits(DiagBufferB) - sizeof(integer)*(MAX_DIAGONAL-(len1+1));
+  for i := - (len1+1) to (len2+1) do
+    DiagB^[i] := MAXINT;
+  DiagB^[len2-len1+1] := len2;
+end;
+//------------------------------------------------------------------------------
+
+procedure TDiff.DiffInt(offset1, offset2, len1, len2: integer);
+var
+  p, k, delta: integer;
+begin
+  if offset1+len1 > FList1.Count then len1 := FList1.Count - offset1;
+  if offset2+len2 > FList2.Count then len2 := FList2.Count - offset2;
+  //trim matching bottoms ...
+  while (len1 > 0) and (len2 > 0) and (FList1[offset1] = FList2[offset2]) do
+  begin
+    inc(offset1); inc(offset2); dec(len1); dec(len2);
+  end;
+  //trim matching tops ...
+  while (len1 > 0) and (len2 > 0) and (FList1[offset1+len1-1] = FList2[offset2+len2-1]) do
+  begin
+    dec(len1); dec(len2);
+  end;
+
+  //stop diff'ing if minimal conditions reached ...
+  if (len1 = 0) then
+  begin
+    AddChangeInt(offset1 ,len2, ckAdd);
+    exit;
+  end
+  else if (len2 = 0) then
+  begin
+    AddChangeInt(offset1 ,len1, ckDelete);
+    exit;
+  end
+  else if (len1 = 1) and (len2 = 1) then
+  begin
+    AddChangeInt(offset1, 1, ckDelete);
+    AddChangeInt(offset1, 1, ckAdd);
+    exit;
+  end;
+
+  p := -1;
+  delta := len2 - len1;
+  InitDiagArrays(len1, len2);
+  if delta < 0 then
+  begin
+    repeat
+      inc(p);
+      if (p mod 1024) = 1023 then
+      begin
+        Application.ProcessMessages;
+        if FCancelled then exit;
+      end;
+      //nb: the Snake order is important here
+      for k := p downto delta +1 do
+        if SnakeIntF(k,offset1,offset2,len1,len2) then exit;
+      for k := -p + delta to delta-1 do
+        if SnakeIntF(k,offset1,offset2,len1,len2) then exit;
+      for k := delta -p to -1 do
+        if SnakeIntB(k,offset1,offset2,len1,len2) then exit;
+      for k := p downto 1 do
+        if SnakeIntB(k,offset1,offset2,len1,len2) then exit;
+      if SnakeIntF(delta,offset1,offset2,len1,len2) then exit;
+      if SnakeIntB(0,offset1,offset2,len1,len2) then exit;
+    until(false);
+  end else
+  begin
+    repeat
+      inc(p);
+      if (p mod 1024) = 1023 then
+      begin
+        Application.ProcessMessages;
+        if FCancelled then exit;
+      end;
+      //nb: the Snake order is important here
+      for k := -p to delta -1 do
+        if SnakeIntF(k,offset1,offset2,len1,len2) then exit;
+      for k := p + delta downto delta +1 do
+        if SnakeIntF(k,offset1,offset2,len1,len2) then exit;
+      for k := delta + p downto 1 do
+        if SnakeIntB(k,offset1,offset2,len1,len2) then exit;
+      for k := -p to -1 do
+        if SnakeIntB(k,offset1,offset2,len1,len2) then exit;
+      if SnakeIntF(delta,offset1,offset2,len1,len2) then exit;
+      if SnakeIntB(0,offset1,offset2,len1,len2) then exit;
+    until(false);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TDiff.DiffChr(offset1, offset2, len1, len2: integer);
+var
+  p, k, delta: integer;
+begin
+  //trim matching bottoms ...
+  while (len1 > 0) and (len2 > 0) and (FStr1[offset1] = FStr2[offset2]) do
+  begin
+    inc(offset1); inc(offset2); dec(len1); dec(len2);
+  end;
+  //trim matching tops ...
+  while (len1 > 0) and (len2 > 0) and (FStr1[offset1+len1-1] = FStr2[offset2+len2-1]) do
+  begin
+    dec(len1); dec(len2);
+  end;
+
+  //stop diff'ing if minimal conditions reached ...
+  if (len1 = 0) then
+  begin
+    AddChangeChr(offset1 ,len2, ckAdd);
+    exit;
+  end
+  else if (len2 = 0) then
+  begin
+    AddChangeChr(offset1, len1, ckDelete);
+    exit;
+  end
+  else if (len1 = 1) and (len2 = 1) then
+  begin
+    AddChangeChr(offset1, 1, ckDelete);
+    AddChangeChr(offset1, 1, ckAdd);
+    exit;
+  end;
+
+  p := -1;
+  delta := len2 - len1;
+  InitDiagArrays(len1, len2);
+  if delta < 0 then
+  begin
+    repeat
+      inc(p);
+      if (p mod 1024 = 1023) then
+      begin
+        Application.ProcessMessages;
+        if FCancelled then exit;
+      end;
+      //nb: the Snake order is important here
+      for k := p downto delta +1 do
+        if SnakeChrF(k,offset1,offset2,len1,len2) then exit;
+      for k := -p + delta to delta-1 do
+        if SnakeChrF(k,offset1,offset2,len1,len2) then exit;
+      for k := delta -p to -1 do
+        if SnakeChrB(k,offset1,offset2,len1,len2) then exit;
+      for k := p downto 1 do
+        if SnakeChrB(k,offset1,offset2,len1,len2) then exit;
+      if SnakeChrF(delta,offset1,offset2,len1,len2) then exit;
+      if SnakeChrB(0,offset1,offset2,len1,len2) then exit;
+    until(false);
+  end else
+  begin
+    repeat
+      inc(p);
+      if (p mod 1024 = 1023) then
+      begin
+        Application.ProcessMessages;
+        if FCancelled then exit;
+      end;
+      //nb: the Snake order is important here
+      for k := -p to delta -1 do
+        if SnakeChrF(k,offset1,offset2,len1,len2) then exit;
+      for k := p + delta downto delta +1 do
+        if SnakeChrF(k,offset1,offset2,len1,len2) then exit;
+      for k := delta + p downto 1 do
+        if SnakeChrB(k,offset1,offset2,len1,len2) then exit;
+      for k := -p to -1 do
+        if SnakeChrB(k,offset1,offset2,len1,len2) then exit;
+      if SnakeChrF(delta,offset1,offset2,len1,len2) then exit;
+      if SnakeChrB(0,offset1,offset2,len1,len2) then exit;
+    until(false);
+  end;
+end;
+//------------------------------------------------------------------------------
+
+function TDiff.SnakeChrF(k,offset1,offset2,len1,len2: integer): boolean;
+var
+  x,y: integer;
+begin
+  if DiagF[k+1] > DiagF[k-1] then
+    y := DiagF[k+1] else
+    y := DiagF[k-1]+1;
+  x := y - k;
+  while (x < len1-1) and (y < len2-1) and (FStr1[offset1+x+1] = FStr2[offset2+y+1]) do
+  begin
+    inc(x); inc(y);
+  end;
+  DiagF[k] := y;
+  Result := (DiagF[k] >= DiagB[k]);
+  if not Result then exit;
+
+  inc(x); inc(y);
+  PushDiff(offset1+x, offset2+y, len1-x, len2-y);
+  PushDiff(offset1, offset2, x, y);
+end;
+//------------------------------------------------------------------------------
+
+function TDiff.SnakeChrB(k,offset1,offset2,len1,len2: integer): boolean;
+var
+  x,y: integer;
+begin
+  if DiagB[k-1] < DiagB[k+1] then
+    y := DiagB[k-1]
+  else
+    y := DiagB[k+1]-1;
+
+  x := y - k;
+  while (x >= 0) and (y >= 0) and (FStr1[offset1+x] = FStr2[offset2+y]) do
+  begin
+    dec(x); dec(y);
+  end;
+  DiagB[k] := y;
+  Result := DiagB[k] <= DiagF[k];
+  if not Result then exit;
+
+  inc(x); inc(y);
+  PushDiff(offset1+x, offset2+y, len1-x, len2-y);
+  PushDiff(offset1, offset2, x, y);
+end;
+//------------------------------------------------------------------------------
+
+function TDiff.SnakeIntF(k,offset1,offset2,len1,len2: integer): boolean;
+var
+  x,y: integer;
+begin
+  if DiagF^[k+1] > DiagF^[k-1] then
+    y := DiagF^[k+1]
+  else
+    y := DiagF^[k-1]+1;
+  x := y - k;
+  while (x < len1-1) and (y < len2-1) and (FList1[offset1+x+1] = FList2[offset2+y+1]) do
+  begin
+    inc(x); inc(y);
+  end;
+  DiagF^[k] := y;
+  Result := (DiagF^[k] >= DiagB^[k]);
+  if not Result then exit;
+
+  inc(x); inc(y);
+  PushDiff(offset1+x, offset2+y, len1-x, len2-y);
+  PushDiff(offset1, offset2, x, y);
+end;
+//------------------------------------------------------------------------------
+
+function TDiff.SnakeIntB(k,offset1,offset2,len1,len2: integer): boolean;
+var
+  x,y: integer;
+begin
+  if DiagB^[k-1] < DiagB^[k+1] then
+    y := DiagB^[k-1]
+  else
+    y := DiagB^[k+1]-1;
+  x := y - k;
+  while (x >= 0) and (y >= 0) and (FList1[offset1+x] = FList2[offset2+y]) do
+  begin
+    dec(x); dec(y);
+  end;
+  DiagB^[k] := y;
+  Result := DiagB^[k] <= DiagF^[k];
+  if not Result then exit;
+
+  inc(x); inc(y);
+  PushDiff(offset1+x, offset2+y, len1-x, len2-y);
+  PushDiff(offset1, offset2, x, y);
+end;
+//------------------------------------------------------------------------------
+
+procedure TDiff.AddChangeChr(offset1, range: integer; ChangeKind: TChangeKind);
+var
+  i,j: integer;
+  compareRec: PCompareRec;
+begin
+  //first, add any unchanged items into this list ...
+  while (FLastCompareRec.oldIndex1 < offset1 -1) do
+  begin
+    with FLastCompareRec do
+    begin
+      chr1 := #0;
+      chr2 := #0;
+      Kind := ckNone;
+      inc(oldIndex1);
+      inc(oldIndex2);
+      if (oldIndex1 > 0) and (oldIndex1 <= Length(FStr1)) then
+        chr1 := FStr1[oldIndex1];
+      if (oldIndex2 > 0) and (oldIndex2 <= Length(FStr2)) then
+        chr2 := FStr2[oldIndex2];
+    end;
+    New(compareRec);
+    compareRec^ := FLastCompareRec;
+    FCompareList.Add(compareRec);
+    inc(FDiffStats.matches);
+  end;
+
+  case ChangeKind of
+    ckNone:
+      for i := 1 to range do
+      begin
+        with FLastCompareRec do
+        begin
+          Kind := ckNone;
+          inc(oldIndex1);
+          inc(oldIndex2);
+          chr1 := FStr1[oldIndex1];
+          chr2 := FStr2[oldIndex2];
+        end;
+        New(compareRec);
+        compareRec^ := FLastCompareRec;
+        FCompareList.Add(compareRec);
+        inc(FDiffStats.matches);
+      end;
+    ckAdd :
+      begin
+        for i := 1 to range do
+        begin
+          with FLastCompareRec do
+          begin
+
+            //check if a range of adds are following a range of deletes
+            //and convert them to modifies ...
+            if Kind = ckDelete then
+            begin
+              j := FCompareList.Count -1;
+              while (j > 0) and (PCompareRec(FCompareList[j-1]).Kind = ckDelete) do
+                dec(j);
+              PCompareRec(FCompareList[j]).Kind := ckModify;
+              dec(FDiffStats.deletes);
+              inc(FDiffStats.modifies);
+              inc(FLastCompareRec.oldIndex2);
+              PCompareRec(FCompareList[j]).oldIndex2 := FLastCompareRec.oldIndex2;
+              PCompareRec(FCompareList[j]).chr2 := FStr2[oldIndex2];
+              if j = FCompareList.Count-1 then
+                FLastCompareRec.Kind := ckModify;
+              continue;
+            end;
+
+            Kind := ckAdd;
+            chr1 := #0;
+            inc(oldIndex2);
+            chr2 := FStr2[oldIndex2]; //ie what we added
+          end;
+          New(compareRec);
+          compareRec^ := FLastCompareRec;
+          FCompareList.Add(compareRec);
+          inc(FDiffStats.adds);
+        end;
+      end;
+    ckDelete :
+      begin
+        for i := 1 to range do
+        begin
+          with FLastCompareRec do
+          begin
+
+            //check if a range of deletes are following a range of adds
+            //and convert them to modifies ...
+            if Kind = ckAdd then
+            begin
+              j := FCompareList.Count -1;
+              while (j > 0) and (PCompareRec(FCompareList[j-1]).Kind = ckAdd) do
+                dec(j);
+              PCompareRec(FCompareList[j]).Kind := ckModify;
+              dec(FDiffStats.adds);
+              inc(FDiffStats.modifies);
+              inc(FLastCompareRec.oldIndex1);
+              PCompareRec(FCompareList[j]).oldIndex1 := FLastCompareRec.oldIndex1;
+              PCompareRec(FCompareList[j]).chr1 := FStr1[oldIndex1];
+              if j = FCompareList.Count-1 then
+                FLastCompareRec.Kind := ckModify;
+              continue;
+            end;
+
+            Kind := ckDelete;
+            chr2 := #0;
+            inc(oldIndex1);
+            chr1 := FStr1[oldIndex1]; //ie what we deleted
+          end;
+          New(compareRec);
+          compareRec^ := FLastCompareRec;
+          FCompareList.Add(compareRec);
+          inc(FDiffStats.deletes);
+        end;
+      end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TDiff.AddChangeInt(offset1, range: integer; ChangeKind: TChangeKind);
+var
+  i,j: integer;
+  compareRec: PCompareRec;
+begin
+  //first, add any unchanged items into this list ...
+  while (FLastCompareRec.oldIndex1 < offset1 -1) do
+  begin
+    with FLastCompareRec do
+    begin
+      Kind := ckNone;
+      inc(oldIndex1);
+      inc(oldIndex2);
+      if (oldIndex1 >= 0) and (oldIndex1 < FList1.Count) then
+        int1 := FList1[oldIndex1];
+      if (oldIndex2 >= 0) and (oldIndex2 < FList2.Count) then
+        int2 := FList2[oldIndex2];
+    end;
+    New(compareRec);
+    compareRec^ := FLastCompareRec;
+    FCompareList.Add(compareRec);
+    inc(FDiffStats.matches);
+  end;
+
+  case ChangeKind of
+    ckNone:
+      for i := 1 to range do
+      begin
+        with FLastCompareRec do
+        begin
+          Kind := ckNone;
+          inc(oldIndex1);
+          inc(oldIndex2);
+          if (oldIndex1 >= 0) and (oldIndex1 < FList1.Count) then
+            int1 := FList1[oldIndex1];
+          if (oldIndex2 >= 0) and (oldIndex2 < FList2.Count) then
+            int2 := FList2[oldIndex2];
+        end;
+        New(compareRec);
+        compareRec^ := FLastCompareRec;
+        FCompareList.Add(compareRec);
+        inc(FDiffStats.matches);
+      end;
+    ckAdd :
+      begin
+        for i := 1 to range do
+        begin
+          with FLastCompareRec do
+          begin
+
+            //check if a range of adds are following a range of deletes
+            //and convert them to modifies ...
+            if Kind = ckDelete then
+            begin
+              j := FCompareList.Count -1;
+              while (j > 0) and (PCompareRec(FCompareList[j-1]).Kind = ckDelete) do
+                dec(j);
+              PCompareRec(FCompareList[j]).Kind := ckModify;
+              dec(FDiffStats.deletes);
+              inc(FDiffStats.modifies);
+              inc(FLastCompareRec.oldIndex2);
+              PCompareRec(FCompareList[j]).oldIndex2 := FLastCompareRec.oldIndex2;
+              PCompareRec(FCompareList[j]).int2 := FList2[oldIndex2];
+              if j = FCompareList.Count-1 then FLastCompareRec.Kind := ckModify;
+              continue;
+            end;
+
+            Kind := ckAdd;
+            int1 := $0;
+            inc(oldIndex2);
+            if (oldIndex2 >= 0) and (oldIndex2 < FList2.Count) then
+              int2 := FList2[oldIndex2]; //ie what we added
+          end;
+          New(compareRec);
+          compareRec^ := FLastCompareRec;
+          FCompareList.Add(compareRec);
+          inc(FDiffStats.adds);
+        end;
+      end;
+    ckDelete :
+      begin
+        for i := 1 to range do
+        begin
+          with FLastCompareRec do
+          begin
+
+            //check if a range of deletes are following a range of adds
+            //and convert them to modifies ...
+            if Kind = ckAdd then
+            begin
+              j := FCompareList.Count -1;
+              while (j > 0) and (PCompareRec(FCompareList[j-1]).Kind = ckAdd) do
+                dec(j);
+              PCompareRec(FCompareList[j]).Kind := ckModify;
+              dec(FDiffStats.adds);
+              inc(FDiffStats.modifies);
+              inc(FLastCompareRec.oldIndex1);
+              PCompareRec(FCompareList[j]).oldIndex1 := FLastCompareRec.oldIndex1;
+              PCompareRec(FCompareList[j]).int1 := FList1[oldIndex1];
+              if j = FCompareList.Count-1 then FLastCompareRec.Kind := ckModify;
+              continue;
+            end;
+
+            Kind := ckDelete;
+            int2 := $0;
+            inc(oldIndex1);
+            if (oldIndex1 >= 0) and (oldIndex1 < FList1.Count) then
+              int1 := FList1[oldIndex1]; //ie what we deleted
+          end;
+          New(compareRec);
+          compareRec^ := FLastCompareRec;
+          FCompareList.Add(compareRec);
+          inc(FDiffStats.deletes);
+        end;
+      end;
+  end;
+end;
+//------------------------------------------------------------------------------
+
+procedure TDiff.Clear;
+var
+  i: integer;
+begin
+  for i := 0 to FCompareList.Count-1 do
+    dispose(PCompareRec(FCompareList[i]));
+  FCompareList.clear;
+  FLastCompareRec.Kind := ckNone;
+  FLastCompareRec.oldIndex1 := -1;
+  FLastCompareRec.oldIndex2 := -1;
+  FDiffStats.matches := 0;
+  FDiffStats.adds := 0;
+  FDiffStats.deletes :=0;
+  FDiffStats.modifies :=0;
+end;
+//------------------------------------------------------------------------------
+
+function TDiff.GetCompareCount: integer;
+begin
+  Result := FCompareList.count;
+end;
+//------------------------------------------------------------------------------
+
+function TDiff.GetCompare(index: integer): TCompareRec;
+begin
+  Result := PCompareRec(FCompareList[index])^;
+end;
+//------------------------------------------------------------------------------
+
+procedure TDiff.Cancel;
+begin
+  FCancelled := true;
+end;
+//------------------------------------------------------------------------------
+
+end.
diff --git a/components/jcf2/ReadWrite/EditorConverter.pas b/components/jcf2/ReadWrite/EditorConverter.pas
index 571ae84595..7770882512 100644
--- a/components/jcf2/ReadWrite/EditorConverter.pas
+++ b/components/jcf2/ReadWrite/EditorConverter.pas
@@ -34,7 +34,7 @@ See http://www.gnu.org/licenses/gpl.html
 interface
 
 uses
-  Classes, SysUtils, Math,
+  Classes, SysUtils,
   // IdeIntf
   SrcEditorIntf,
   { local }
@@ -85,7 +85,7 @@ implementation
 
 uses
   { local }
-  JcfLog, JcfRegistrySettings, JcfMiscFunctions;
+  JcfLog, JcfRegistrySettings, diffmerge;
 
 constructor TEditorConverter.Create;
 begin
@@ -136,98 +136,13 @@ begin
 end;
 
 procedure TEditorConverter.WriteToIDE(const pcUnit: TSourceEditorInterface; const psText: string);
-var
-  lLogicalCaretXY:TPoint;
-  lStart,lEnd:TPoint;
 begin
   if pcUnit = nil then
     exit;
   if psText <> fcConverter.InputCode then
-  begin
-    try
-      lLogicalCaretXY:=pcUnit.CursorTextXY;
-      pcUnit.BeginUpdate;
-      pcUnit.BeginUndoBlock;
-      lStart.X:=0;  //select all text.
-      lStart.Y:=0;
-      lEnd.X:=0;
-      if pcUnit.LineCount>0 then
-        lEnd.X:=length(pcUnit.Lines[pcUnit.LineCount-1])+1;
-      lEnd.Y:=pcUnit.LineCount;
-      //pcUnit.Lines.Text := psText;    // removes undo history.
-      pcUnit.ReplaceText(lStart,lEnd,psText);
-      pcUnit.CursorTextXY:=lLogicalCaretXY;
-      pcUnit.Modified := True;
-    finally
-      pcUnit.EndUndoBlock;  
-      pcUnit.EndUpdate;
-    end;
-  end;
+    DiffMergeEditor(pcUnit, psText);
 end;
 
-//BUGGY: inserts empty blank lines in "random" position in the editor.
-// and if only one line es added or deleted after formatting then doesn't syncronize well.
-// i think is better change all text in the editor.
-// TODO: delete
-{
-procedure TEditorConverter.WriteToIDE(const pcUnit: TSourceEditorInterface; const psText: string);
-var
-  lcSourceLines, lcDestLines: TStrings;
-  lcSameStart, lcSameEnd: TStrings;
-  lsSourceLine, lsDestLine: string;
-  liStart, liIndex, liMaxIndex: integer;
-  hasSourceLine: Boolean;
-begin
-  if pcUnit = nil then
-    exit;
-  lcSourceLines := TStringList.Create;
-  lcSourceLines.Text := fcConverter.InputCode;
-  lcDestLines := TStringList.Create;
-  lcDestLines.Text := psText;
-  lcSameStart := TStringList.Create;
-  lcSameEnd := TStringList.Create;
-
-  SplitIntoChangeSections(lcSourceLines, lcDestLines, lcSameStart, lcSameEnd);
-  try
-    pcUnit.BeginUpdate;
-    pcUnit.BeginUndoBlock;
-
-    liStart := lcSameStart.Count;
-    liIndex := 0;
-    liMaxIndex := Max(lcSourceLines.Count, lcDestLines.Count);
-    while (liIndex < liMaxIndex) do
-    begin
-      hasSourceLine := liIndex < lcSourceLines.Count;
-      if hasSourceLine then
-        lsSourceLine := lcSourceLines[liIndex]
-      else
-        lsSourceLine := '';
-
-      if liIndex < lcDestLines.Count then
-        lsDestLine := lcDestLines[liIndex]
-      else
-        lsDestLine := '';
-
-      if not hasSourceLine then
-        pcUnit.InsertLine(liStart + liIndex + 1, lsDestLine, True)
-      else
-      if not AnsiSameStr(lsSourceLine, lsDestLine) then
-        // the line is different, replace it
-        pcUnit.ReplaceLines(liStart + liIndex + 1, liStart + liIndex + 1, lsDestLine, True);
-
-       inc(liIndex);
-     end;
-   finally
-    pcUnit.EndUndoBlock;
-    pcUnit.EndUpdate;
-    lcSourceLines.Free;
-    lcDestLines.Free;
-    lcSameStart.Free;
-    lcSameEnd.Free;
-   end;
-end;
-}
-
 procedure TEditorConverter.AfterConvert;
 begin
   FinalSummary;
diff --git a/components/jcf2/ReadWrite/diffmerge.pas b/components/jcf2/ReadWrite/diffmerge.pas
new file mode 100644
index 0000000000..7b3c98685e
--- /dev/null
+++ b/components/jcf2/ReadWrite/diffmerge.pas
@@ -0,0 +1,202 @@
+{
+ /***************************************************************************
+   diffmerge.pas - functions to merge new text with the JCF changes into the
+   editor
+
+ ***************************************************************************/
+
+ ***************************************************************************
+ *                                                                         *
+ *   This source is free software; you can redistribute it and/or modify   *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ *   This code is distributed in the hope that it will be useful, but      *
+ *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
+ *   General Public License for more details.                              *
+ *                                                                         *
+ *   A copy of the GNU General Public License is available on the World    *
+ *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
+ *   obtain it by writing to the Free Software Foundation,                 *
+ *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
+ *                                                                         *
+ ***************************************************************************
+
+  Author: Domingo Galmés
+
+  Abstract:
+    Methods for creating diffs and applying them to the editor.
+
+  Uses TextDiff
+    https://github.com/rickard67/TextDiff
+    http://www.angusj.com/delphi/textdiff.html
+    Author : Angus Johnson - angusj-AT-myrealbox-DOT-com
+    Copyright : © 2001-2008 Angus Johnson
+    Updated by : Rickard Johansson (RJ TextEd)
+}
+unit diffmerge;
+
+{$mode objfpc}{$H+}
+interface
+
+uses
+  Classes, SysUtils, SrcEditorIntf, Diff;
+
+procedure DiffMergeEditor(aEdit: TSourceEditorInterface; const aNewText: string; aFromLine: integer = 1; aToLine: integer = maxint);
+
+implementation
+
+uses
+  crc;
+
+function HashLine(const line: string; aIgnoreFinalSpaces: boolean = False): cardinal;
+var
+  lLen: integer;
+begin
+  lLen := Length(line);
+  if aIgnoreFinalSpaces then
+  begin
+    while (lLen > 0) and (line[lLen] in [' ', #9]) do
+    begin
+      Dec(lLen);
+    end;
+  end;
+  if lLen = 0 then
+    Result := CRC32(0, nil, lLen)
+  else
+    Result := CRC32(0, pbyte(@line[1]), lLen);
+end;
+
+//Line number 1 based as TSourceEditorInterface.
+procedure FillHashList(aList: TIntegerList; aLines: TStrings; aFirstLine: integer = 1; aLastLine: integer = maxint);
+var
+  i: integer;
+begin
+  aList.Clear;
+  Dec(aFirstLine);
+  Dec(aLastLine);
+  if aFirstLine < 0 then
+    aFirstLine := 0;
+  if aLastLine > aLines.Count - 1 then
+    aLastLine := aLines.Count - 1;
+  for i := aFirstLine to aLastLine do
+  begin
+    aList.Add(HashLine(aLines[i]));
+  end;
+end;
+
+function EditorGetLastColumn(aEdit: TSourceEditorInterface; aLineNumber: integer): integer;
+begin
+  Result := length(aEdit.Lines[aLineNumber - 1]) + 1;
+end;
+
+procedure EditorDeleteLine(aEdit: TSourceEditorInterface; aLineNumber: integer);
+var
+  lStartPoint, lEndPoint: Tpoint;
+begin
+  if (aLineNumber < 0) or (aLineNumber > aEdit.Lines.Count) then
+    Exit;
+  lStartPoint.X := 1;
+  lStartPoint.Y := aLineNumber;
+  lEndPoint.X := 1;
+  lEndPoint.Y := aLineNumber + 1;
+  if lEndPoint.Y > aEdit.Lines.Count then
+  begin
+    lEndPoint.Y := aLineNumber;
+    lEndPoint.X := EditorGetLastColumn(aEdit, aLineNumber);
+    if aLineNumber > 1 then
+    begin
+      lStartPoint.X := EditorGetLastColumn(aEdit, aLineNumber - 1);
+      lStartPoint.Y := aLineNumber - 1;
+    end;
+  end;
+  aEdit.ReplaceText(lStartPoint, lEndPoint, '');
+end;
+
+
+procedure EditorInsertLine(aEdit: TSourceEditorInterface; aLineNumber: integer; aText: string);
+begin
+  aEdit.InsertLine(aLineNumber, aText, True);
+end;
+
+procedure EditorReplaceLine(aEdit: TSourceEditorInterface; aLineNumber: integer; aNewText: string);
+begin
+  aEdit.ReplaceLines(aLineNumber, aLineNumber, aNewText, True);
+end;
+
+procedure DiffMergeEditor(aEdit: TSourceEditorInterface; const aNewText: string; aFromLine: integer = 1; aToLine: integer = maxint);
+var
+  lDiff: TDiff;
+  lI: integer;
+  lListOldTextHashes, lListNewTextHashes: TIntegerList;
+  lDeltaLines: integer;
+  lStartLine: integer;
+  lNewText: TStringList;
+  lCursor: TPoint;
+  lCursorAdjusted: boolean;
+  lCursorNeedAdjustX: boolean;
+begin
+  lDiff := nil;
+  lNewText := nil;
+  lListOldTextHashes := nil;
+  lListNewTextHashes := nil;
+  lDeltaLines := 0;
+  lStartLine := aFromLine - 1;  // 0 based offset.
+  lCursor := aEdit.CursorTextXY;
+  lCursorAdjusted := False;
+  lCursorNeedAdjustX := False;
+  try
+    aEdit.BeginUpdate;
+    aEdit.BeginUndoBlock;
+    lDiff := TDiff.Create(nil);
+    lNewText := TStringList.Create;
+    lNewText.Text := aNewText;
+    lListOldTextHashes := TIntegerList.Create;
+    lListNewTextHashes := TIntegerList.Create;
+    FillHashList(lListOldTextHashes, aEdit.Lines, aFromLine, aToLine);
+    FillHashList(lListNewTextHashes, lNewText);
+    lDiff.Execute(lListOldTextHashes, lListNewTextHashes);
+    for lI := 0 to lDiff.Count - 1 do
+    begin
+      with lDiff.Compares[lI] do
+      begin
+        if (lCursorAdjusted = False) and (lCursor.Y = lStartLine + oldIndex1 + 1) then
+        begin
+          lCursor.Y := lCursor.Y + lDeltaLines;
+          lCursorAdjusted := True;
+          lCursorNeedAdjustX := Kind <> ckNone;
+        end;
+        case Kind of
+          ckAdd:
+          begin
+            EditorInsertLine(aEdit, lStartLine + oldIndex2 + 1, lNewText[oldIndex2]);
+            Inc(lDeltaLines);
+          end;
+          ckDelete:
+          begin
+            EditorDeleteLine(aEdit, lStartLine + oldIndex1 + 1 + lDeltaLines);
+            Dec(lDeltaLines);
+          end;
+          ckModify:
+          begin
+            EditorReplaceLine(aEdit, lStartLine + oldIndex2 + 1, lNewText[oldIndex2]);
+          end;
+        end;
+      end;
+    end;
+    if lCursorNeedAdjustX then
+      lCursor.X := EditorGetLastColumn(aEdit, lCursor.Y);
+    aEdit.CursorTextXY := lCursor;
+  finally
+    lDiff.Free;
+    lListOldTextHashes.Free;
+    lListNewTextHashes.Free;
+    lNewText.Free;
+    aEdit.EndUndoBlock;
+    aEdit.EndUpdate;
+  end;
+end;
+
+end.
-- 
2.30.0.windows.1

JCF_diff_merge.patch (103,347 bytes)   
JCF_acknowledgemnt_tdiff.patch (483 bytes)   
diff --git a/docs/acknowledgements.txt b/docs/acknowledgements.txt
index 9d5a5bca53..5c2f9a864b 100644
--- a/docs/acknowledgements.txt
+++ b/docs/acknowledgements.txt
@@ -23,3 +23,7 @@ http://p.yusukekamiyamane.com/
 Created by Carlo Kok / RemObjects Software
 Fixed for Lazarus by Bogusіaw Brandys
 http://www.remobjects.com/ps.aspx
+
+TDiff by Angus Johnson
+Fixed for Freepascal by  Rickard Johansson (RJ TextEd)
+http://github.com/rickard67/TextDiff
\ No newline at end of file

Juha Manninen

2021-01-06 22:11

developer   ~0128133

Could the same diff code be used in Lazarus IDE? Tools menu has a comparison entry. The same code is used elsewhere in the IDE, too.

Domingo Galmés

2021-01-07 14:29

reporter   ~0128140

My first intention was to use the lazarus code to make the comparison, but it gave me inacurate results with blank lines that i was not being able to solve so I tryed to use
the TDiff component.

TDiff could be used in the ide comparison code, but the patch code I provide does not create a "patch" itself how does the comparison tool in the ide, it applies the
changes found with TDiff directly to the editor.

To use TDiff in the comparison, one would have to rewrite all the logic for creating the patch file format generated by the IDE utility.
Practically the IDE comparison tool would have to be rewritten.

Sample of inacurate comparation of files. In the diff only adds a line "+" betwen par1line3 and par2line1 I thig it shoult add 3 "+" and the last lines it marks "!" as modifications
for me they are additions "+"


file1
-----------------
par1line1
par1line2
par1line3

par2line1
par2line2
par2line3

File2
---------------
parnewline1
parnewline2


par1line1
par1line2
par1line3



par2line1
par2line2
par2line3


par3line1
new


Result
---------------------
***************
*** 1,10 ****
  par1line1
  par1line2
  par1line3
  
  par2line1
  par2line2
! par2line3
--- 1,20 ----
+ parnewline1
+ parnewline2
+
+
  par1line1
  par1line2
  par1line3
  
+
  
  par2line1
  par2line2
! par2line3
!
!
! par3line1
! new

Juha Manninen

2021-01-07 16:26

developer   ~0128143

> Practically the IDE comparison tool would have to be rewritten.
A good idea actually. The current code does not scale well. Time grows polynomially with big files.

I changed you new files a little. Please take a look.
LazUtils already has unit IntegerList. In this case type TCardinalList is the right one.
IFDEFs for Delphi are not needed. We removed them from all JCF code.

Unit diffmerge does hashing of text lines. Does it mean that unit Diff cannot be used by itself? A caller must provide hashed data for it.
I would place unit Diff in LazUtils package if it was standalone with a clear API. The Application.ProcessMessages could be refactored out by an event.

The author of unit Diff should accept the same license as JCF2 code has. It is dual Mozilla / GPL.
In LazUtils it would be Modified LGPL2.
In the comment header Angus Johnson practically explains Modified LGPL2 with linking exception but it should be made explicit. There are people who would spot this as a license violation.
diffmerge.pas (6,462 bytes)   
{
 /***************************************************************************
   diffmerge.pas - functions to merge new text with the JCF changes into the
   editor

 ***************************************************************************/

 ***************************************************************************
 *                                                                         *
 *   This source is free software; you can redistribute it and/or modify   *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This code is distributed in the hope that it will be useful, but      *
 *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *   General Public License for more details.                              *
 *                                                                         *
 *   A copy of the GNU General Public License is available on the World    *
 *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
 *   obtain it by writing to the Free Software Foundation,                 *
 *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
 *                                                                         *
 ***************************************************************************

  Author: Domingo Galmés

  Abstract:
    Methods for creating diffs and applying them to the editor.

  Uses TextDiff
    https://github.com/rickard67/TextDiff
    http://www.angusj.com/delphi/textdiff.html
    Author : Angus Johnson - angusj-AT-myrealbox-DOT-com
    Copyright : © 2001-2008 Angus Johnson
    Updated by : Rickard Johansson (RJ TextEd)
}
unit diffmerge;

{$mode objfpc}{$H+}
interface

uses
  Classes, SysUtils, crc,
  // LazUtils
  IntegerList,
  // IdeIntf
  SrcEditorIntf,
  Diff;

procedure DiffMergeEditor(aEdit: TSourceEditorInterface; const aNewText: string; aFromLine: integer = 1; aToLine: integer = maxint);

implementation

function HashLine(const line: string; aIgnoreFinalSpaces: boolean = False): cardinal;
var
  lLen: integer;
begin
  lLen := Length(line);
  if aIgnoreFinalSpaces then
  begin
    while (lLen > 0) and (line[lLen] in [' ', #9]) do
      Dec(lLen);
  end;
  if lLen = 0 then
    Result := CRC32(0, nil, lLen)
  else
    Result := CRC32(0, pbyte(@line[1]), lLen);
end;

//Line number 1 based as TSourceEditorInterface.
procedure FillHashList(aList: TCardinalList; aLines: TStrings; aFirstLine: integer = 1; aLastLine: integer = maxint);
var
  i: integer;
begin
  aList.Clear;
  Dec(aFirstLine);
  Dec(aLastLine);
  if aFirstLine < 0 then
    aFirstLine := 0;
  if aLastLine > aLines.Count - 1 then
    aLastLine := aLines.Count - 1;
  for i := aFirstLine to aLastLine do
    aList.Add(HashLine(aLines[i]));
end;

function EditorGetLastColumn(aEdit: TSourceEditorInterface; aLineNumber: integer): integer;
begin
  Result := length(aEdit.Lines[aLineNumber - 1]) + 1;
end;

procedure EditorDeleteLine(aEdit: TSourceEditorInterface; aLineNumber: integer);
var
  lStartPoint, lEndPoint: Tpoint;
begin
  if (aLineNumber < 0) or (aLineNumber > aEdit.Lines.Count) then
    Exit;
  lStartPoint.X := 1;
  lStartPoint.Y := aLineNumber;
  lEndPoint.X := 1;
  lEndPoint.Y := aLineNumber + 1;
  if lEndPoint.Y > aEdit.Lines.Count then
  begin
    lEndPoint.Y := aLineNumber;
    lEndPoint.X := EditorGetLastColumn(aEdit, aLineNumber);
    if aLineNumber > 1 then
    begin
      lStartPoint.X := EditorGetLastColumn(aEdit, aLineNumber - 1);
      lStartPoint.Y := aLineNumber - 1;
    end;
  end;
  aEdit.ReplaceText(lStartPoint, lEndPoint, '');
end;


procedure EditorInsertLine(aEdit: TSourceEditorInterface; aLineNumber: integer; aText: string);
begin
  aEdit.InsertLine(aLineNumber, aText, True);
end;

procedure EditorReplaceLine(aEdit: TSourceEditorInterface; aLineNumber: integer; aNewText: string);
begin
  aEdit.ReplaceLines(aLineNumber, aLineNumber, aNewText, True);
end;

procedure DiffMergeEditor(aEdit: TSourceEditorInterface; const aNewText: string; aFromLine: integer = 1; aToLine: integer = maxint);
var
  lDiff: TDiff;
  lI: integer;
  lListOldTextHashes, lListNewTextHashes: TCardinalList;
  lDeltaLines: integer;
  lStartLine: integer;
  lNewText: TStringList;
  lCursor: TPoint;
  lCursorAdjusted: boolean;
  lCursorNeedAdjustX: boolean;
begin
  lDiff := nil;
  lNewText := nil;
  lListOldTextHashes := nil;
  lListNewTextHashes := nil;
  lDeltaLines := 0;
  lStartLine := aFromLine - 1;  // 0 based offset.
  lCursor := aEdit.CursorTextXY;
  lCursorAdjusted := False;
  lCursorNeedAdjustX := False;
  try
    aEdit.BeginUpdate;
    aEdit.BeginUndoBlock;
    lDiff := TDiff.Create(nil);
    lNewText := TStringList.Create;
    lNewText.Text := aNewText;
    lListOldTextHashes := TCardinalList.Create;
    lListNewTextHashes := TCardinalList.Create;
    FillHashList(lListOldTextHashes, aEdit.Lines, aFromLine, aToLine);
    FillHashList(lListNewTextHashes, lNewText);
    lDiff.Execute(lListOldTextHashes, lListNewTextHashes);
    for lI := 0 to lDiff.Count - 1 do
    begin
      with lDiff.Compares[lI] do
      begin
        if (lCursorAdjusted = False) and (lCursor.Y = lStartLine + oldIndex1 + 1) then
        begin
          lCursor.Y := lCursor.Y + lDeltaLines;
          lCursorAdjusted := True;
          lCursorNeedAdjustX := Kind <> ckNone;
        end;
        case Kind of
          ckAdd:
          begin
            EditorInsertLine(aEdit, lStartLine + oldIndex2 + 1, lNewText[oldIndex2]);
            Inc(lDeltaLines);
          end;
          ckDelete:
          begin
            EditorDeleteLine(aEdit, lStartLine + oldIndex1 + 1 + lDeltaLines);
            Dec(lDeltaLines);
          end;
          ckModify:
          begin
            EditorReplaceLine(aEdit, lStartLine + oldIndex2 + 1, lNewText[oldIndex2]);
          end;
        end;
      end;
    end;
    if lCursorNeedAdjustX then
      lCursor.X := EditorGetLastColumn(aEdit, lCursor.Y);
    aEdit.CursorTextXY := lCursor;
  finally
    lDiff.Free;
    lListOldTextHashes.Free;
    lListNewTextHashes.Free;
    lNewText.Free;
    aEdit.EndUndoBlock;
    aEdit.EndUpdate;
  end;
end;

end.
diffmerge.pas (6,462 bytes)   
Diff.pas (28,415 bytes)   
unit Diff;

{$mode delphi}{$H+}

(*******************************************************************************
* Component         TDiff                                                      *
* Version:          5.02                                                        *
* Date:             23 May 2020                                                *
* Compilers:        Delphi 10.x                                                *
* Author:           Angus Johnson - angusj-AT-myrealbox-DOT-com                *
* Copyright:        © 2001-2009 Angus Johnson                                  *
* Updated by:       Rickard Johansson (RJ TextEd)                              *
*                                                                              *
* Licence to use, terms and conditions:                                        *
*                   The code in the TDiff component is released as freeware    *
*                   provided you agree to the following terms & conditions:    *
*                   1. the copyright notice, terms and conditions are          *
*                   left unchanged                                             *
*                   2. modifications to the code by other authors must be      *
*                   clearly documented and accompanied by the modifier's name. *
*                   3. the TDiff component may be freely compiled into binary  *
*                   format and no acknowledgement is required. However, a      *
*                   discrete acknowledgement would be appreciated (eg. in a    *
*                   program's 'About Box').                                    *
*                                                                              *
* Description:      Component to list differences between two integer arrays   *
*                   using a "longest common subsequence" algorithm.            *
*                   Typically, this component is used to diff 2 text files     *
*                   once their individuals lines have been hashed.             *
*                                                                              *
* Acknowledgements: The key algorithm in this component is based on:           *
*                   "An O(NP) Sequence Comparison Algorithm"                   *
*                   by Sun Wu, Udi Manber & Gene Myers                         *
*                   and uses a "divide-and-conquer" technique to avoid         *
*                   using exponential amounts of memory as described in        *
*                   "An O(ND) Difference Algorithm and its Variations"         *
*                   By E Myers - Algorithmica Vol. 1 No. 2, 1986, pp. 251-266  *
*******************************************************************************)

(*******************************************************************************
* History:                                                                     *
* 13 December 2001 - Original release (used Myer's O(ND) Difference Algorithm) *
* 22 April 2008    - Complete rewrite to greatly improve the code and          *
*                    provide a much simpler view of differences through a new  *
*                    'Compares' property.                                      *
* 21 May 2008      - Another complete code rewrite to use Sun Wu et al.'s      *
*                    O(NP) Sequence Comparison Algorithm which more than       *
*                    halves times of typical comparisons.                      *
* 24 May 2008      - Reimplemented "divide-and-conquer" technique (which was   *
*                    omitted in 21 May release) so memory use is again minimal.*
* 25 May 2008      - Removed recursion to avoid the possibility of running out *
*                    of stack memory during massive comparisons.               *
* 2 June 2008      - Bugfix: incorrect number of appended AddChangeInt() calls *
*                    in Execute() for integer arrays. (It was OK with Chars)   *
*                    Added check to prevent repeat calls to Execute() while    *
*                    already executing.                                        *
*                    Added extra parse of differences to find occasional       *
*                    missed matches. (See readme.txt for further discussion)   *
* 7 November 2009  - Updated so now compiles in newer versions of Delphi.      *
*                                                                              *
* 11 November 2018 - Added TList<Cardinal> to store hash values                *
*                    Made some minor code formatting and code changes          *
* 19 May 2020        Added Lazarus support                                     *
* 23 May 2020      - Minor changes and fixed an issue in AddChangeChr()        *
*******************************************************************************)

interface

uses
  Classes, SysUtils,
  // LCL
  Forms,
  // LazUtils
  IntegerList;

const
  MAX_DIAGONAL = $FFFFFF; //~16 million

type

  P8Bits = PByte;

  PDiags = ^TDiags;
  TDiags = array [-MAX_DIAGONAL .. MAX_DIAGONAL] of integer;

  TChangeKind = (ckNone, ckAdd, ckDelete, ckModify);

  PCompareRec = ^TCompareRec;
  TCompareRec = record
    Kind      : TChangeKind;
    oldIndex1 : Integer;
    oldIndex2 : Integer;
    case boolean of
      false   : (chr1, chr2 : Char);
      true    : (int1, int2 : Cardinal);
  end;

  PDiffVars = ^TDiffVars;
  TDiffVars = record
    offset1 : integer;
    offset2 : integer;
    len1    : integer;
    len2    : integer;
  end;

  TDiffStats = record
    matches  : integer;
    adds     : integer;
    deletes  : integer;
    modifies : integer;
  end;

  TDiff = class(TComponent)
  private
    FCompareList: TList;
    FDiffList: TList;      //this TList circumvents the need for recursion
    FCancelled: boolean;
    FExecuting: boolean;
    FCompareInts: boolean; //ie are we comparing integer arrays or char arrays
    DiagBufferF: pointer;
    DiagBufferB: pointer;
    DiagF, DiagB: PDiags;
    FDiffStats: TDiffStats;
    FLastCompareRec: TCompareRec;
    FList1: TCardinalList;
    FList2: TCardinalList;
    FStr1: string;
    FStr2: string;
    procedure PushDiff(offset1, offset2, len1, len2: integer);
    function  PopDiff: boolean;
    procedure InitDiagArrays(len1, len2: integer);
    procedure DiffInt(offset1, offset2, len1, len2: integer);
    procedure DiffChr(offset1, offset2, len1, len2: integer);
    function SnakeChrF(k,offset1,offset2,len1,len2: integer): boolean;
    function SnakeChrB(k,offset1,offset2,len1,len2: integer): boolean;
    function SnakeIntF(k,offset1,offset2,len1,len2: integer): boolean;
    function SnakeIntB(k,offset1,offset2,len1,len2: integer): boolean;
    procedure AddChangeChr(offset1, range: integer; ChangeKind: TChangeKind);
    procedure AddChangeInt(offset1, range: integer; ChangeKind: TChangeKind);
    function GetCompareCount: integer;
    function GetCompare(index: integer): TCompareRec;
  public
    constructor Create(aOwner: TComponent); override;
    destructor Destroy; override;

    // Compare strings or list of Cardinals ...
    function Execute(const alist1, alist2: TCardinalList): boolean; overload;
    function Execute(const s1, s2: string): boolean; overload;
    // Cancel allows interrupting excessively prolonged comparisons
    procedure Cancel;
    procedure Clear;
    property Cancelled: boolean read FCancelled;
    property Count: integer read GetCompareCount;
    property Compares[index: integer]: TCompareRec read GetCompare; default;
    property DiffStats: TDiffStats read FDiffStats;
  end;

implementation

procedure Register;
begin
  RegisterComponents('Samples', [TDiff]);
end;

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------

constructor TDiff.Create(aOwner: TComponent);
begin
  inherited;
  FCompareList := TList.create;
  FDiffList := TList.Create;
end;
//------------------------------------------------------------------------------

destructor TDiff.Destroy;
begin
  Clear;
  FCompareList.free;
  FDiffList.Free;
  inherited;
end;
//------------------------------------------------------------------------------

function TDiff.Execute(const alist1, alist2: TCardinalList): boolean;
var
  i, Len1Minus1: integer;
  len1,len2: Integer;
begin
  Result := not FExecuting;
  if not Result then exit;
  FCancelled := false;
  FExecuting := true;
  try
    FList1 := alist1;
    FList2 := alist2;
    len1 := FList1.Count;
    len2 := FList2.Count;

    Clear;

    Len1Minus1 := len1 -1;
    FCompareList.Capacity := len1 + len2;
    FCompareInts := true;

    GetMem(DiagBufferF, sizeof(integer)*(len1+len2+3));
    GetMem(DiagBufferB, sizeof(integer)*(len1+len2+3));
    try
      PushDiff(0, 0, len1, len2);
      while PopDiff do;
    finally
      freeMem(DiagBufferF);
      freeMem(DiagBufferB);
    end;

    if FCancelled then
    begin
      Result := false;
      Clear;
      exit;
    end;

    //correct the occasional missed match ...
    for i := 1 to count -1 do
      with PCompareRec(FCompareList[i])^ do
        if (Kind = ckModify) and (int1 = int2) then
        begin
          Kind := ckNone;
          Dec(FDiffStats.modifies);
          Inc(FDiffStats.matches);
        end;

    //finally, append any trailing matches onto compareList ...
    with FLastCompareRec do
      AddChangeInt(oldIndex1,len1Minus1-oldIndex1, ckNone);
  finally
    FExecuting := false;
  end;
end;
//------------------------------------------------------------------------------

function TDiff.Execute(const s1, s2: string): boolean;
var
  i, Len1Minus1: integer;
  len1,len2: Integer;
begin
  Result := not FExecuting;
  if not Result then exit;
  FCancelled := false;
  FExecuting := true;
  try
    Clear;
    len1 := Length(s1);
    len2 := Length(s2);
    Len1Minus1 := len1 -1;
    FCompareList.Capacity := len1 + len2;
    FDiffList.Capacity := 1024;
    FCompareInts := false;

    GetMem(DiagBufferF, sizeof(integer)*(len1+len2+3));
    GetMem(DiagBufferB, sizeof(integer)*(len1+len2+3));
    FStr1 := s1;
    FStr2 := s2;
    try
      PushDiff(1, 1, len1, len2);
      while PopDiff do;
    finally
      freeMem(DiagBufferF);
      freeMem(DiagBufferB);
    end;

    if FCancelled then
    begin
      Result := false;
      Clear;
      exit;
    end;

    //correct the occasional missed match ...
    for i := 1 to count -1 do
      with PCompareRec(FCompareList[i])^ do
        if (Kind = ckModify) and (chr1 = chr2) then
        begin
          Kind := ckNone;
          Dec(FDiffStats.modifies);
          Inc(FDiffStats.matches);
        end;

    //finally, append any trailing matches onto compareList ...
    with FLastCompareRec do
    begin
      AddChangeChr(oldIndex1,len1Minus1-oldIndex1, ckNone);
    end;
  finally
    FExecuting := false;
  end;
end;
//------------------------------------------------------------------------------

procedure TDiff.PushDiff(offset1, offset2, len1, len2: integer);
var
  DiffVars: PDiffVars;
begin
  new(DiffVars);
  DiffVars.offset1 := offset1;
  DiffVars.offset2 := offset2;
  DiffVars.len1 := len1;
  DiffVars.len2 := len2;
  FDiffList.Add(DiffVars);
end;
//------------------------------------------------------------------------------

function  TDiff.PopDiff: boolean;
var
  DiffVars: PDiffVars;
  idx: integer;
begin
  idx := FDiffList.Count -1;
  Result := idx >= 0;
  if not Result then exit;
  DiffVars := PDiffVars(FDiffList[idx]);
  with DiffVars^ do
    if FCompareInts then
      DiffInt(offset1, offset2, len1, len2) else
      DiffChr(offset1, offset2, len1, len2);
  Dispose(DiffVars);
  FDiffList.Delete(idx);
end;
//------------------------------------------------------------------------------

procedure TDiff.InitDiagArrays(len1, len2: integer);
var
  i: integer;
begin
  //assumes that top and bottom matches have been excluded
  P8Bits(DiagF) := P8Bits(DiagBufferF) - sizeof(integer)*(MAX_DIAGONAL-(len1+1));
  for i := - (len1+1) to (len2+1) do
    DiagF^[i] := -MAXINT;
  DiagF^[1] := -1;

  P8Bits(DiagB) := P8Bits(DiagBufferB) - sizeof(integer)*(MAX_DIAGONAL-(len1+1));
  for i := - (len1+1) to (len2+1) do
    DiagB^[i] := MAXINT;
  DiagB^[len2-len1+1] := len2;
end;
//------------------------------------------------------------------------------

procedure TDiff.DiffInt(offset1, offset2, len1, len2: integer);
var
  p, k, delta: integer;
begin
  if offset1+len1 > FList1.Count then len1 := FList1.Count - offset1;
  if offset2+len2 > FList2.Count then len2 := FList2.Count - offset2;
  //trim matching bottoms ...
  while (len1 > 0) and (len2 > 0) and (FList1[offset1] = FList2[offset2]) do
  begin
    inc(offset1); inc(offset2); dec(len1); dec(len2);
  end;
  //trim matching tops ...
  while (len1 > 0) and (len2 > 0) and (FList1[offset1+len1-1] = FList2[offset2+len2-1]) do
  begin
    dec(len1); dec(len2);
  end;

  //stop diff'ing if minimal conditions reached ...
  if (len1 = 0) then
  begin
    AddChangeInt(offset1 ,len2, ckAdd);
    exit;
  end
  else if (len2 = 0) then
  begin
    AddChangeInt(offset1 ,len1, ckDelete);
    exit;
  end
  else if (len1 = 1) and (len2 = 1) then
  begin
    AddChangeInt(offset1, 1, ckDelete);
    AddChangeInt(offset1, 1, ckAdd);
    exit;
  end;

  p := -1;
  delta := len2 - len1;
  InitDiagArrays(len1, len2);
  if delta < 0 then
  begin
    repeat
      inc(p);
      if (p mod 1024) = 1023 then
      begin
        Application.ProcessMessages;
        if FCancelled then exit;
      end;
      //nb: the Snake order is important here
      for k := p downto delta +1 do
        if SnakeIntF(k,offset1,offset2,len1,len2) then exit;
      for k := -p + delta to delta-1 do
        if SnakeIntF(k,offset1,offset2,len1,len2) then exit;
      for k := delta -p to -1 do
        if SnakeIntB(k,offset1,offset2,len1,len2) then exit;
      for k := p downto 1 do
        if SnakeIntB(k,offset1,offset2,len1,len2) then exit;
      if SnakeIntF(delta,offset1,offset2,len1,len2) then exit;
      if SnakeIntB(0,offset1,offset2,len1,len2) then exit;
    until(false);
  end else
  begin
    repeat
      inc(p);
      if (p mod 1024) = 1023 then
      begin
        Application.ProcessMessages;
        if FCancelled then exit;
      end;
      //nb: the Snake order is important here
      for k := -p to delta -1 do
        if SnakeIntF(k,offset1,offset2,len1,len2) then exit;
      for k := p + delta downto delta +1 do
        if SnakeIntF(k,offset1,offset2,len1,len2) then exit;
      for k := delta + p downto 1 do
        if SnakeIntB(k,offset1,offset2,len1,len2) then exit;
      for k := -p to -1 do
        if SnakeIntB(k,offset1,offset2,len1,len2) then exit;
      if SnakeIntF(delta,offset1,offset2,len1,len2) then exit;
      if SnakeIntB(0,offset1,offset2,len1,len2) then exit;
    until(false);
  end;
end;
//------------------------------------------------------------------------------

procedure TDiff.DiffChr(offset1, offset2, len1, len2: integer);
var
  p, k, delta: integer;
begin
  //trim matching bottoms ...
  while (len1 > 0) and (len2 > 0) and (FStr1[offset1] = FStr2[offset2]) do
  begin
    inc(offset1); inc(offset2); dec(len1); dec(len2);
  end;
  //trim matching tops ...
  while (len1 > 0) and (len2 > 0) and (FStr1[offset1+len1-1] = FStr2[offset2+len2-1]) do
  begin
    dec(len1); dec(len2);
  end;

  //stop diff'ing if minimal conditions reached ...
  if (len1 = 0) then
  begin
    AddChangeChr(offset1 ,len2, ckAdd);
    exit;
  end
  else if (len2 = 0) then
  begin
    AddChangeChr(offset1, len1, ckDelete);
    exit;
  end
  else if (len1 = 1) and (len2 = 1) then
  begin
    AddChangeChr(offset1, 1, ckDelete);
    AddChangeChr(offset1, 1, ckAdd);
    exit;
  end;

  p := -1;
  delta := len2 - len1;
  InitDiagArrays(len1, len2);
  if delta < 0 then
  begin
    repeat
      inc(p);
      if (p mod 1024 = 1023) then
      begin
        Application.ProcessMessages;
        if FCancelled then exit;
      end;
      //nb: the Snake order is important here
      for k := p downto delta +1 do
        if SnakeChrF(k,offset1,offset2,len1,len2) then exit;
      for k := -p + delta to delta-1 do
        if SnakeChrF(k,offset1,offset2,len1,len2) then exit;
      for k := delta -p to -1 do
        if SnakeChrB(k,offset1,offset2,len1,len2) then exit;
      for k := p downto 1 do
        if SnakeChrB(k,offset1,offset2,len1,len2) then exit;
      if SnakeChrF(delta,offset1,offset2,len1,len2) then exit;
      if SnakeChrB(0,offset1,offset2,len1,len2) then exit;
    until(false);
  end else
  begin
    repeat
      inc(p);
      if (p mod 1024 = 1023) then
      begin
        Application.ProcessMessages;
        if FCancelled then exit;
      end;
      //nb: the Snake order is important here
      for k := -p to delta -1 do
        if SnakeChrF(k,offset1,offset2,len1,len2) then exit;
      for k := p + delta downto delta +1 do
        if SnakeChrF(k,offset1,offset2,len1,len2) then exit;
      for k := delta + p downto 1 do
        if SnakeChrB(k,offset1,offset2,len1,len2) then exit;
      for k := -p to -1 do
        if SnakeChrB(k,offset1,offset2,len1,len2) then exit;
      if SnakeChrF(delta,offset1,offset2,len1,len2) then exit;
      if SnakeChrB(0,offset1,offset2,len1,len2) then exit;
    until(false);
  end;
end;
//------------------------------------------------------------------------------

function TDiff.SnakeChrF(k,offset1,offset2,len1,len2: integer): boolean;
var
  x,y: integer;
begin
  if DiagF[k+1] > DiagF[k-1] then
    y := DiagF[k+1] else
    y := DiagF[k-1]+1;
  x := y - k;
  while (x < len1-1) and (y < len2-1) and (FStr1[offset1+x+1] = FStr2[offset2+y+1]) do
  begin
    inc(x); inc(y);
  end;
  DiagF[k] := y;
  Result := (DiagF[k] >= DiagB[k]);
  if not Result then exit;

  inc(x); inc(y);
  PushDiff(offset1+x, offset2+y, len1-x, len2-y);
  PushDiff(offset1, offset2, x, y);
end;
//------------------------------------------------------------------------------

function TDiff.SnakeChrB(k,offset1,offset2,len1,len2: integer): boolean;
var
  x,y: integer;
begin
  if DiagB[k-1] < DiagB[k+1] then
    y := DiagB[k-1]
  else
    y := DiagB[k+1]-1;

  x := y - k;
  while (x >= 0) and (y >= 0) and (FStr1[offset1+x] = FStr2[offset2+y]) do
  begin
    dec(x); dec(y);
  end;
  DiagB[k] := y;
  Result := DiagB[k] <= DiagF[k];
  if not Result then exit;

  inc(x); inc(y);
  PushDiff(offset1+x, offset2+y, len1-x, len2-y);
  PushDiff(offset1, offset2, x, y);
end;
//------------------------------------------------------------------------------

function TDiff.SnakeIntF(k,offset1,offset2,len1,len2: integer): boolean;
var
  x,y: integer;
begin
  if DiagF^[k+1] > DiagF^[k-1] then
    y := DiagF^[k+1]
  else
    y := DiagF^[k-1]+1;
  x := y - k;
  while (x < len1-1) and (y < len2-1) and (FList1[offset1+x+1] = FList2[offset2+y+1]) do
  begin
    inc(x); inc(y);
  end;
  DiagF^[k] := y;
  Result := (DiagF^[k] >= DiagB^[k]);
  if not Result then exit;

  inc(x); inc(y);
  PushDiff(offset1+x, offset2+y, len1-x, len2-y);
  PushDiff(offset1, offset2, x, y);
end;
//------------------------------------------------------------------------------

function TDiff.SnakeIntB(k,offset1,offset2,len1,len2: integer): boolean;
var
  x,y: integer;
begin
  if DiagB^[k-1] < DiagB^[k+1] then
    y := DiagB^[k-1]
  else
    y := DiagB^[k+1]-1;
  x := y - k;
  while (x >= 0) and (y >= 0) and (FList1[offset1+x] = FList2[offset2+y]) do
  begin
    dec(x); dec(y);
  end;
  DiagB^[k] := y;
  Result := DiagB^[k] <= DiagF^[k];
  if not Result then exit;

  inc(x); inc(y);
  PushDiff(offset1+x, offset2+y, len1-x, len2-y);
  PushDiff(offset1, offset2, x, y);
end;
//------------------------------------------------------------------------------

procedure TDiff.AddChangeChr(offset1, range: integer; ChangeKind: TChangeKind);
var
  i,j: integer;
  compareRec: PCompareRec;
begin
  //first, add any unchanged items into this list ...
  while (FLastCompareRec.oldIndex1 < offset1 -1) do
  begin
    with FLastCompareRec do
    begin
      chr1 := #0;
      chr2 := #0;
      Kind := ckNone;
      inc(oldIndex1);
      inc(oldIndex2);
      if (oldIndex1 > 0) and (oldIndex1 <= Length(FStr1)) then
        chr1 := FStr1[oldIndex1];
      if (oldIndex2 > 0) and (oldIndex2 <= Length(FStr2)) then
        chr2 := FStr2[oldIndex2];
    end;
    New(compareRec);
    compareRec^ := FLastCompareRec;
    FCompareList.Add(compareRec);
    inc(FDiffStats.matches);
  end;

  case ChangeKind of
    ckNone:
      for i := 1 to range do
      begin
        with FLastCompareRec do
        begin
          Kind := ckNone;
          inc(oldIndex1);
          inc(oldIndex2);
          chr1 := FStr1[oldIndex1];
          chr2 := FStr2[oldIndex2];
        end;
        New(compareRec);
        compareRec^ := FLastCompareRec;
        FCompareList.Add(compareRec);
        inc(FDiffStats.matches);
      end;
    ckAdd :
      begin
        for i := 1 to range do
        begin
          with FLastCompareRec do
          begin

            //check if a range of adds are following a range of deletes
            //and convert them to modifies ...
            if Kind = ckDelete then
            begin
              j := FCompareList.Count -1;
              while (j > 0) and (PCompareRec(FCompareList[j-1]).Kind = ckDelete) do
                dec(j);
              PCompareRec(FCompareList[j]).Kind := ckModify;
              dec(FDiffStats.deletes);
              inc(FDiffStats.modifies);
              inc(FLastCompareRec.oldIndex2);
              PCompareRec(FCompareList[j]).oldIndex2 := FLastCompareRec.oldIndex2;
              PCompareRec(FCompareList[j]).chr2 := FStr2[oldIndex2];
              if j = FCompareList.Count-1 then
                FLastCompareRec.Kind := ckModify;
              continue;
            end;

            Kind := ckAdd;
            chr1 := #0;
            inc(oldIndex2);
            chr2 := FStr2[oldIndex2]; //ie what we added
          end;
          New(compareRec);
          compareRec^ := FLastCompareRec;
          FCompareList.Add(compareRec);
          inc(FDiffStats.adds);
        end;
      end;
    ckDelete :
      begin
        for i := 1 to range do
        begin
          with FLastCompareRec do
          begin

            //check if a range of deletes are following a range of adds
            //and convert them to modifies ...
            if Kind = ckAdd then
            begin
              j := FCompareList.Count -1;
              while (j > 0) and (PCompareRec(FCompareList[j-1]).Kind = ckAdd) do
                dec(j);
              PCompareRec(FCompareList[j]).Kind := ckModify;
              dec(FDiffStats.adds);
              inc(FDiffStats.modifies);
              inc(FLastCompareRec.oldIndex1);
              PCompareRec(FCompareList[j]).oldIndex1 := FLastCompareRec.oldIndex1;
              PCompareRec(FCompareList[j]).chr1 := FStr1[oldIndex1];
              if j = FCompareList.Count-1 then
                FLastCompareRec.Kind := ckModify;
              continue;
            end;

            Kind := ckDelete;
            chr2 := #0;
            inc(oldIndex1);
            chr1 := FStr1[oldIndex1]; //ie what we deleted
          end;
          New(compareRec);
          compareRec^ := FLastCompareRec;
          FCompareList.Add(compareRec);
          inc(FDiffStats.deletes);
        end;
      end;
  end;
end;
//------------------------------------------------------------------------------

procedure TDiff.AddChangeInt(offset1, range: integer; ChangeKind: TChangeKind);
var
  i,j: integer;
  compareRec: PCompareRec;
begin
  //first, add any unchanged items into this list ...
  while (FLastCompareRec.oldIndex1 < offset1 -1) do
  begin
    with FLastCompareRec do
    begin
      Kind := ckNone;
      inc(oldIndex1);
      inc(oldIndex2);
      if (oldIndex1 >= 0) and (oldIndex1 < FList1.Count) then
        int1 := FList1[oldIndex1];
      if (oldIndex2 >= 0) and (oldIndex2 < FList2.Count) then
        int2 := FList2[oldIndex2];
    end;
    New(compareRec);
    compareRec^ := FLastCompareRec;
    FCompareList.Add(compareRec);
    inc(FDiffStats.matches);
  end;

  case ChangeKind of
    ckNone:
      for i := 1 to range do
      begin
        with FLastCompareRec do
        begin
          Kind := ckNone;
          inc(oldIndex1);
          inc(oldIndex2);
          if (oldIndex1 >= 0) and (oldIndex1 < FList1.Count) then
            int1 := FList1[oldIndex1];
          if (oldIndex2 >= 0) and (oldIndex2 < FList2.Count) then
            int2 := FList2[oldIndex2];
        end;
        New(compareRec);
        compareRec^ := FLastCompareRec;
        FCompareList.Add(compareRec);
        inc(FDiffStats.matches);
      end;
    ckAdd :
      begin
        for i := 1 to range do
        begin
          with FLastCompareRec do
          begin

            //check if a range of adds are following a range of deletes
            //and convert them to modifies ...
            if Kind = ckDelete then
            begin
              j := FCompareList.Count -1;
              while (j > 0) and (PCompareRec(FCompareList[j-1]).Kind = ckDelete) do
                dec(j);
              PCompareRec(FCompareList[j]).Kind := ckModify;
              dec(FDiffStats.deletes);
              inc(FDiffStats.modifies);
              inc(FLastCompareRec.oldIndex2);
              PCompareRec(FCompareList[j]).oldIndex2 := FLastCompareRec.oldIndex2;
              PCompareRec(FCompareList[j]).int2 := FList2[oldIndex2];
              if j = FCompareList.Count-1 then FLastCompareRec.Kind := ckModify;
              continue;
            end;

            Kind := ckAdd;
            int1 := $0;
            inc(oldIndex2);
            if (oldIndex2 >= 0) and (oldIndex2 < FList2.Count) then
              int2 := FList2[oldIndex2]; //ie what we added
          end;
          New(compareRec);
          compareRec^ := FLastCompareRec;
          FCompareList.Add(compareRec);
          inc(FDiffStats.adds);
        end;
      end;
    ckDelete :
      begin
        for i := 1 to range do
        begin
          with FLastCompareRec do
          begin

            //check if a range of deletes are following a range of adds
            //and convert them to modifies ...
            if Kind = ckAdd then
            begin
              j := FCompareList.Count -1;
              while (j > 0) and (PCompareRec(FCompareList[j-1]).Kind = ckAdd) do
                dec(j);
              PCompareRec(FCompareList[j]).Kind := ckModify;
              dec(FDiffStats.adds);
              inc(FDiffStats.modifies);
              inc(FLastCompareRec.oldIndex1);
              PCompareRec(FCompareList[j]).oldIndex1 := FLastCompareRec.oldIndex1;
              PCompareRec(FCompareList[j]).int1 := FList1[oldIndex1];
              if j = FCompareList.Count-1 then FLastCompareRec.Kind := ckModify;
              continue;
            end;

            Kind := ckDelete;
            int2 := $0;
            inc(oldIndex1);
            if (oldIndex1 >= 0) and (oldIndex1 < FList1.Count) then
              int1 := FList1[oldIndex1]; //ie what we deleted
          end;
          New(compareRec);
          compareRec^ := FLastCompareRec;
          FCompareList.Add(compareRec);
          inc(FDiffStats.deletes);
        end;
      end;
  end;
end;
//------------------------------------------------------------------------------

procedure TDiff.Clear;
var
  i: integer;
begin
  for i := 0 to FCompareList.Count-1 do
    dispose(PCompareRec(FCompareList[i]));
  FCompareList.clear;
  FLastCompareRec.Kind := ckNone;
  FLastCompareRec.oldIndex1 := -1;
  FLastCompareRec.oldIndex2 := -1;
  FDiffStats.matches := 0;
  FDiffStats.adds := 0;
  FDiffStats.deletes :=0;
  FDiffStats.modifies :=0;
end;
//------------------------------------------------------------------------------

function TDiff.GetCompareCount: integer;
begin
  Result := FCompareList.count;
end;
//------------------------------------------------------------------------------

function TDiff.GetCompare(index: integer): TCompareRec;
begin
  Result := PCompareRec(FCompareList[index])^;
end;
//------------------------------------------------------------------------------

procedure TDiff.Cancel;
begin
  FCancelled := true;
end;
//------------------------------------------------------------------------------

end.
Diff.pas (28,415 bytes)   

Domingo Galmés

2021-01-07 18:00

reporter   ~0128148

TDiff has two execute functions, one for character strings that looks for the differences in a string by individual characters and another for two lists of integers that are the hashes of the lines. Indeed, for use the caller must pass two lists with hashes of the lines to compare.

In TDiff I had not made any changes to comply with the license that says that the changes made must be clearly indicated.

With the issue of the license I believed that if we did not modify Tdiff.pas we would comply with the author's requirements.
My level of English is not very high as you may have already noticed, I say it to get in touch with the author and explain clearly what we want. It would be better if someone with a better knowledge of the English language requested it.

The changes made in the two files seem correct to me.

Bart Broersma

2021-01-07 22:23

developer   ~0128155

Fixed typo in summary.

Juha Manninen

2021-01-08 00:11

developer   ~0128165

Last edited: 2021-01-08 00:16

View 3 revisions

> With the issue of the license I believed that if we did not modify Tdiff.pas we would comply with the author's requirements.

No, we can easily comply by documenting the changes in the unit's comment header. He writes:
 "modifications to the code by other authors must be clearly documented and accompanied by the modifier's name"
In fact (L)GPL has the same requirement but we break it all the time. Every project breaks it.
Nowadays the same information can be found from revision control more easily.
When GPL was created, revision control was not widely used and SW was copied using floppy disks.
Then the requirement was justified, now it should be updated with an option for revision control.

I applied the changes. New files are in JCF package.
His license resembles Modified LGPL2, it surely is compatible with Mozilla / GPL which is JCF's dual license.
Please test.

P.S. I would like to see a diff tool resembling Kompare or Meld integrated in Lazarus. Maybe you can make such a tool when you have extra time. :)

Issue History

Date Modified Username Field Change
2020-12-10 09:07 OkobaPatino New Issue
2020-12-10 09:08 OkobaPatino Note Added: 0127506
2020-12-10 09:09 OkobaPatino Note Added: 0127507
2020-12-10 21:47 Domingo Galmés Note Added: 0127515
2020-12-10 21:51 OkobaPatino Note Added: 0127516
2020-12-10 22:29 Juha Manninen Relationship added related to 0038195
2020-12-10 22:32 Domingo Galmés Note Added: 0127519
2020-12-10 22:32 Domingo Galmés File Added: JCF_Revert.patch
2021-01-06 18:34 Domingo Galmés Note Added: 0128128
2021-01-06 18:34 Domingo Galmés File Added: JCF_diff_merge.patch
2021-01-06 18:34 Domingo Galmés File Added: JCF_acknowledgemnt_tdiff.patch
2021-01-06 22:11 Juha Manninen Note Added: 0128133
2021-01-07 14:29 Domingo Galmés Note Added: 0128140
2021-01-07 16:26 Juha Manninen Note Added: 0128143
2021-01-07 16:26 Juha Manninen File Added: diffmerge.pas
2021-01-07 16:26 Juha Manninen File Added: Diff.pas
2021-01-07 18:00 Domingo Galmés Note Added: 0128148
2021-01-07 22:23 Bart Broersma Summary JCF will move the braeakpoints => JCF will move the breakpoints
2021-01-07 22:23 Bart Broersma LazTarget => -
2021-01-07 22:23 Bart Broersma Note Added: 0128155
2021-01-07 23:50 Juha Manninen Assigned To => Juha Manninen
2021-01-07 23:50 Juha Manninen Status new => assigned
2021-01-08 00:10 Juha Manninen Assigned To Juha Manninen =>
2021-01-08 00:10 Juha Manninen Assigned To => Juha Manninen
2021-01-08 00:11 Juha Manninen Status assigned => resolved
2021-01-08 00:11 Juha Manninen Resolution open => fixed
2021-01-08 00:11 Juha Manninen Fixed in Revision => r64347, r64348
2021-01-08 00:11 Juha Manninen Note Added: 0128165
2021-01-08 00:15 Juha Manninen Note Edited: 0128165 View Revisions
2021-01-08 00:16 Juha Manninen Note Edited: 0128165 View Revisions
2021-02-16 15:37 Juha Manninen Relationship added related to 0038125