Skip to content

Commit 85442c3

Browse files
committed
Enable automatic decoding
Adds functionality to more easily decode a *single* adjacent entity/escape char, i.e. * optional settings to automatically decode the last entered entity/escape char, triggered by a space or return * decoding commands will now try to replace the adjacent entity/escape char when there's no active selection; the shortcuts for these commands behave like ALT+X in MS Word *Note* Selections will mostly decode the same as before; multi-byte glyphs can *only* be decoded or encoded when selected (for now) Also improves handling of selections by: * encapsulating logic to clear selections after entity/escape char transformation; this prevents accidental deletion of text ranges by typing over them * only decoding selections of complete entities, i.e. the ';' must be included in the selection, which was not the case before!
1 parent 38a86aa commit 85442c3

7 files changed

Lines changed: 340 additions & 35 deletions

File tree

src/Common/Utf8IniFiles.pas

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
unit Utf8IniFiles;
2+
3+
(*
4+
Copyright (c) 2022 Robert Di Pardo <dipardo.r@gmail.com>
5+
6+
This Source Code Form is subject to the terms of the Mozilla Public
7+
License, v. 2.0. If a copy of the MPL was not distributed with this file,
8+
You can obtain one at https://mozilla.org/MPL/2.0/.
9+
*)
10+
11+
{$IFDEF FPC}{$mode delphi}{$ENDIF}
12+
13+
interface
14+
15+
uses SysUtils, IniFiles;
16+
17+
type
18+
EUtf8IniFileException = class(Exception);
19+
TUtf8IniFile = class(TIniFile)
20+
{$IFNDEF FPC}
21+
constructor Create(const FilePath: string); reintroduce;
22+
{$ELSE}
23+
constructor Create(const FilePath: WideString;
24+
Opts: TIniFileOptions = [ifoEscapeLineFeeds, ifoStripQuotes]);
25+
destructor Destroy; override;
26+
{$ENDIF}
27+
private
28+
procedure MakeIniDir(const iniFileDir: WideString);
29+
end;
30+
31+
implementation
32+
33+
{$IFDEF FPC}
34+
uses Classes;
35+
{$ENDIF}
36+
37+
{$IFNDEF FPC}
38+
constructor TUtf8IniFile.Create(const FilePath: string);
39+
begin
40+
MakeIniDir(ExtractFileDir(FilePath));
41+
inherited Create(FilePath);
42+
end;
43+
44+
{$ELSE}
45+
constructor TUtf8IniFile.Create(const FilePath: WideString; Opts: TIniFileOptions);
46+
var
47+
hIniFile: THandle;
48+
begin
49+
try
50+
MakeIniDir(ExtractFileDir(FilePath));
51+
hIniFile := FileOpen(FilePath, fmOpenReadWrite or fmShareExclusive);
52+
if hIniFile = THandle(-1) then
53+
hIniFile := FileCreate(FilePath);
54+
55+
inherited Create(THandleStream.Create(hIniFile), TEncoding.Utf8, Opts);
56+
except
57+
on E: Exception do
58+
begin
59+
FileClose(hIniFile);
60+
raise EUtf8IniFileException.Create(E.message);
61+
end;
62+
end;
63+
end;
64+
65+
destructor TUtf8IniFile.Destroy;
66+
begin
67+
if Assigned(Self.Stream) then
68+
begin
69+
FileClose(THandleStream(Self.Stream).Handle);
70+
Self.Stream.Free;
71+
end;
72+
73+
inherited;
74+
end;
75+
{$ENDIF ~FPC}
76+
77+
procedure TUtf8IniFile.MakeIniDir(const iniFileDir: WideString);
78+
begin
79+
if (not DirectoryExists(iniFileDir)) and (not CreateDir(iniFileDir)) then
80+
raise EUtf8IniFileException.Create(Format('Directory "%s" is not writable',
81+
[UTF8Encode(iniFileDir)]));
82+
end;
83+
84+
end.

src/LibNppPlugin/NppSimpleObjects.pas

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ TTextRange = class
3737
FStartPos: Sci_Position;
3838
FEndPos: Sci_Position;
3939

40+
function GetAnchor(): Sci_Position;
41+
procedure SetAnchor(const AValue: Sci_Position);
4042
function GetStart(): Sci_Position; virtual;
4143
procedure SetStart(const AValue: Sci_Position); virtual;
4244
function GetEnd(): Sci_Position; virtual;
@@ -57,10 +59,12 @@ TTextRange = class
5759
destructor Destroy; override;
5860

5961
procedure Select();
62+
procedure ClearSelection;
6063
procedure Indent(const Levels: integer = 1);
6164
procedure Mark(const Style: integer; const DurationInMs: cardinal = 0);
6265

6366
property Document: TActiveDocument read FEditor;
67+
property Anchor: Sci_Position read GetAnchor write SetAnchor;
6468
property StartPos: Sci_Position read FStartPos write SetStart;
6569
property EndPos: Sci_Position read FEndPos write SetEnd;
6670
property Length: Sci_Position read GetLength write SetLength;
@@ -75,8 +79,6 @@ TTextRange = class
7579
{ -------------------------------------------------------------------------------------------- }
7680
TSelection = class(TTextRange)
7781
protected
78-
function GetAnchor(): Sci_Position;
79-
procedure SetAnchor(const AValue: Sci_Position);
8082
function GetCurrentPos(): Sci_Position;
8183
procedure SetCurrentPos(const AValue: Sci_Position);
8284
function GetStart(): Sci_Position; override;
@@ -90,7 +92,6 @@ TSelection = class(TTextRange)
9092
public
9193
constructor Create(const AEditor: TActiveDocument);
9294

93-
property Anchor: Sci_Position read GetAnchor write SetAnchor;
9495
property Position: Sci_Position read GetCurrentPos write SetCurrentPos;
9596
property StartPos: Sci_Position read GetStart write SetStart;
9697
property EndPos: Sci_Position read GetEnd write SetEnd;
@@ -143,6 +144,7 @@ TActiveDocument = class(TWindowedObject)
143144
procedure SetText(const AValue: WideString);
144145
function GetLength(): Sci_Position;
145146
function GetLineCount(): Sci_Position;
147+
function GetNextLineStart(): Sci_Position;
146148
function GetLangType(): LangType;
147149
procedure SetLangType(const AValue: LangType);
148150

@@ -174,6 +176,7 @@ TActiveDocument = class(TWindowedObject)
174176
property Text: WideString read GetText write SetText;
175177
property Length: Sci_Position read GetLength;
176178
property LineCount: Sci_Position read GetLineCount;
179+
property NextLineStartPosition: Sci_Position read GetNextLineStart;
177180
property Language: LangType read GetLangType write SetLangType;
178181

179182
property CurrentPosition:Sci_Position read GetCurrentPos write SetCurrentPos;
@@ -301,6 +304,18 @@ destructor TTextRange.Destroy;
301304
inherited;
302305
end;
303306

307+
{ ------------------------------------------------------------------------------------------------ }
308+
function TTextRange.GetAnchor: Sci_Position;
309+
begin
310+
Result := FEditor.SendMessage(SCI_GETANCHOR);
311+
end;
312+
313+
{ ------------------------------------------------------------------------------------------------ }
314+
procedure TTextRange.SetAnchor(const AValue: Sci_Position);
315+
begin
316+
FEditor.SendMessage(SCI_SETANCHOR, AValue);
317+
end;
318+
304319
{ ------------------------------------------------------------------------------------------------ }
305320

306321
function TTextRange.GetStart: Sci_Position;
@@ -467,6 +482,18 @@ procedure TTextRange.Select;
467482
FEditor.SendMessage(SCI_SCROLLCARET);
468483
end;
469484

485+
{ ------------------------------------------------------------------------------------------------ }
486+
procedure TTextRange.ClearSelection();
487+
var
488+
SciMsg: Cardinal;
489+
begin
490+
if (Self.Anchor > FEditor.CurrentPosition) then
491+
SciMsg := SCI_POSITIONBEFORE
492+
else
493+
SciMsg := SCI_POSITIONAFTER;
494+
FEditor.CurrentPosition := FEditor.SendMessage(SciMsg, FEditor.CurrentPosition);
495+
end;
496+
470497
{ ================================================================================================ }
471498
{ TSelection }
472499

@@ -477,18 +504,6 @@ constructor TSelection.Create(const AEditor: TActiveDocument);
477504

478505
{ ------------------------------------------------------------------------------------------------ }
479506

480-
function TSelection.GetAnchor: Sci_Position;
481-
begin
482-
Result := FEditor.SendMessage(SCI_GETANCHOR);
483-
end;
484-
{ ------------------------------------------------------------------------------------------------ }
485-
procedure TSelection.SetAnchor(const AValue: Sci_Position);
486-
begin
487-
FEditor.SendMessage(SCI_SETANCHOR, AValue);
488-
end;
489-
490-
{ ------------------------------------------------------------------------------------------------ }
491-
492507
function TSelection.GetCurrentPos: Sci_Position;
493508
begin
494509
Result := FEditor.SendMessage(SCI_GETCURRENTPOS);
@@ -864,6 +879,16 @@ function TActiveDocument.GetLineCount: Sci_Position;
864879

865880
{ ------------------------------------------------------------------------------------------------ }
866881

882+
function TActiveDocument.GetNextLineStart(): Sci_Position;
883+
var
884+
LineEndCurrent: Sci_Position;
885+
begin
886+
LineEndCurrent := SendMessage(SCI_GETLINEENDPOSITION, SendMessage(SCI_LINEFROMPOSITION, CurrentPosition));
887+
Result := SendMessage(SCI_POSITIONAFTER, LineEndCurrent);
888+
end;
889+
890+
{ ------------------------------------------------------------------------------------------------ }
891+
867892
function TActiveDocument.GetLines(const FirstLine: Sci_Position; const Count: Sci_Position): TTextRange;
868893
var
869894
LineCount: Sci_Position;

src/LibNppPlugin/nppplugin.pas

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,13 @@ interface
4242

4343
TNppPlugin = class(TObject)
4444
private
45-
FuncArray: array of _TFuncItem;
4645
FClosingBufferID: THandle;
4746
FConfigDir: string;
4847
function IsDarkModeEnabled: Boolean;
4948
function MinSubsystemIsVista: Boolean;
5049
protected
5150
PluginName: nppString;
51+
FuncArray: array of _TFuncItem;
5252
function SupportsBigFiles: Boolean;
5353
function HasV5Apis: Boolean;
5454
function HasFullRangeApis: Boolean;
@@ -81,6 +81,7 @@ TNppPlugin = class(TObject)
8181
procedure DoNppnShutdown; virtual;
8282
procedure DoNppnBufferActivated(const BufferID: THandle); virtual;
8383
procedure DoNppnFileClosed(const BufferID: THandle); virtual;
84+
procedure DoCharAdded(const hwnd: HWND; const ch: Integer); virtual;
8485
procedure DoUpdateUI(const hwnd: HWND; const updated: Integer); virtual;
8586
procedure DoModified(const hwnd: HWND; const modificationType: Integer); virtual;
8687

@@ -214,6 +215,10 @@ procedure TNppPlugin.BeNotified(sn: PSCNotification);
214215
end;
215216
end else begin
216217
case sn.nmhdr.code of
218+
SCN_CHARADDED: begin
219+
if (sn.characterSource = SC_CHARACTERSOURCE_DIRECT_INPUT) then
220+
Self.DoCharAdded(HWND(sn.nmhdr.hwndFrom), sn.ch);
221+
end;
217222
SCN_MODIFIED: begin
218223
Self.DoModified(HWND(sn.nmhdr.hwndFrom), sn.modificationType);
219224
end;
@@ -260,6 +265,10 @@ procedure TNppPlugin.DoNppnFileClosed(const BufferID: THandle);
260265
// override these
261266
end;
262267

268+
procedure TNppPlugin.DoCharAdded(const hwnd: HWND; const ch: Integer);
269+
begin
270+
end;
271+
263272
procedure TNppPlugin.DoModified(const hwnd: HWND; const modificationType: Integer);
264273
begin
265274
// override these

src/U_Entities.pas

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ interface
2323
procedure EncodeEntities(const Scope: TEntityReplacementScope = ersSelection; const Options: TEntityReplacementOptions = []);
2424
function DoEncodeEntities(var Text: WideString; const Entities: TStringList; const Options: TEntityReplacementOptions): Integer;
2525

26-
procedure DecodeEntities(const Scope: TEntityReplacementScope = ersSelection);
26+
function DecodeEntities(const Scope: TEntityReplacementScope = ersSelection): Integer;
2727

2828
////////////////////////////////////////////////////////////////////////////////////////////////////
2929
implementation
@@ -158,6 +158,7 @@ procedure EncodeEntities(const Scope: TEntityReplacementScope; const Options: TE
158158
Text := doc.Selection.Text;
159159
if DoEncodeEntities(Text, FetchEntities, Options) > 0 then begin
160160
doc.Selection.Text := Text;
161+
doc.Selection.ClearSelection;
161162
end;
162163
end;
163164
end{case};
@@ -203,7 +204,7 @@ function DoEncodeEntities(var Text: WideString; const Entities: TStringList; con
203204
end;
204205

205206
{ ------------------------------------------------------------------------------------------------ }
206-
procedure DecodeEntities(const Scope: TEntityReplacementScope = ersSelection);
207+
function DecodeEntities(const Scope: TEntityReplacementScope = ersSelection): Integer;
207208
const
208209
scDigits = '0123456789';
209210
scHexLetters = 'ABCDEFabcdef';
@@ -233,8 +234,12 @@ procedure DecodeEntities(const Scope: TEntityReplacementScope = ersSelection);
233234
Exit;
234235

235236
Text := doc.Selection.Text;
236-
237237
CharIndex := Pos('&', Text);
238+
239+
// make sure the selection includes the semicolon
240+
if not (Pos(';', Text) > CharIndex) then
241+
Exit;
242+
238243
while CharIndex > 0 do begin
239244
FirstPos := CharIndex;
240245
LastPos := FirstPos;
@@ -324,7 +329,10 @@ procedure DecodeEntities(const Scope: TEntityReplacementScope = ersSelection);
324329

325330
if EntitiesReplaced > 0 then begin
326331
doc.Selection.Text := Text;
332+
doc.Selection.ClearSelection;
327333
end;
334+
335+
Result := EntitiesReplaced;
328336
end;
329337

330338
////////////////////////////////////////////////////////////////////////////////////////////////////

src/U_JSEncode.pas

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ function DecodeJS(Scope: TEntityReplacementScope = ersSelection): Integer;
2323
////////////////////////////////////////////////////////////////////////////////////////////////////
2424
implementation
2525
uses
26-
Windows,
2726
SysUtils,
2827
NppPlugin;
2928

@@ -100,6 +99,7 @@ function DoEncodeJS(const Range: TTextRange): Integer; overload;
10099
Result := DoEncodeJS(Text);
101100
if Result > 0 then begin
102101
Range.Text := Text;
102+
Range.ClearSelection;
103103
end;
104104
end{DoEncodeJS};
105105

@@ -162,8 +162,7 @@ function DecodeJS(Scope: TEntityReplacementScope = ersSelection): Integer;
162162
end;
163163
until Match.Length = 0;
164164

165-
if ((Result > 1) and (Target.StartPos > 0)) then
166-
doc.SendMessage(SCI_SETSEL, doc.Selection.StartPos, doc.SendMessage(SCI_POSITIONBEFORE, doc.Selection.EndPos));
165+
if Result > 0 then doc.Selection.ClearSelection;
167166
finally
168167
Target.Free;
169168
Match.Free;

0 commit comments

Comments
 (0)