1 (*
2 This Source Code Form is subject to the terms of the Mozilla Public
3 License, v. 2.0. If a copy of the MPL was not distributed with this
4 file, You can obtain one at http://mozilla.org/MPL/2.0/.
5 
6 Copyright (c) Alexey Torgashin
7 *)
8 unit proc_globdata;
9 
10 {$mode objfpc}{$H+}
11 {$IOChecks off}
12 {$ModeSwitch advancedrecords}
13 
14 interface
15 
16 uses
17   {$ifdef windows}
18   Windows,
19   {$endif}
20   Classes, SysUtils, Forms, Controls, Menus,
21   Dialogs, Graphics,
22   StrUtils,
23   syncobjs,
24   gqueue,
25   Math,
26   InterfaceBase,
27   LclProc, LclType, LazFileUtils,
28   FileUtil,
29   IniFiles,
30   Process,
31   ATSynEdit,
32   ATSynEdit_Keymap,
33   ATSynEdit_Keymap_Init,
34   ATSynEdit_Adapter_litelexer,
35   ATSynEdit_Commands,
36   ATStringProc,
37   ATStringProc_Separator,
38   ATFlatThemes,
39   ATListbox,
40   ATStatusBar,
41   ATScrollBar,
42   ATTabs,
43   at__jsonconf,
44   proc_cmd,
45   proc_msg,
46   proc_str,
47   ec_LexerList,
48   ec_SyntAnal;
49 
50 type
51 
52   { TAppFileProps }
53 
54   TAppFileProps = record
55     Inited: boolean;
56     Exists: boolean;
57     Size: Int64;
58     Age: LongInt;
59     class operator =(const a, b: TAppFileProps): boolean;
60   end;
61 
62   TAppConsoleQueue = specialize TQueue<UnicodeString>;
63 
64   TAppCommandDelayed = record
65     Code: integer;
66     Tabs: TATTabs;
67     TabIndex: integer;
68     Invoke: TATEditorCommandInvoke;
69     EdAddress: pointer;
70     EdIndex: integer;
71   end;
72 
73   TAppCommandsDelayed = specialize TQueue<TAppCommandDelayed>;
74 
75 var
76   AppActiveForm: TObject = nil;
77   AppThemeStatusbar: TATFlatTheme;
78   AppApiDialogCounter: integer = 0;
79 
80 type
81   TApp3States = (
82     a3sOff,
83     a3sOn,
84     a3sPassive
85     );
86 
87   TAppHistoryElement = (
88     ahhText,
89     ahhCaret,
90     ahhCaretSel,
91     ahhTopLine,
92     ahhTabSize,
93     ahhEncoding,
94     ahhBookmarks,
95     ahhLexer,
96     ahhWordWrap,
97     ahhMinimap,
98     ahhMicromap,
99     ahhRuler,
100     ahhUnprinted,
101     ahhLineNumbers,
102     ahhScale,
103     ahhFolding,
104     ahhMarkers,
105     ahhTabColor,
106     ahhCodeTreeFilter,
107     ahhTabSplit
108     );
109 
110 const
111   cAppSessionDefault = 'history session.json';
112   cAppHistoryElementChar: array[TAppHistoryElement] of char =
113     'tchsTeblwMmrunSfkCFi';
114 
115 const
116   cAppMaxGroup = Pred(6+3); //6 normal groups + 3 floating groups
117 
118 var
119   //ATSynEdit has range for bookmarks 0..63, 0=none
120   AppBookmarkSetup: array[1..63] of
121     record
122       ImageIndex: integer;
123       Color: TColor;
124     end;
125 var
126   AppListRecents: TStringList = nil;
127   AppBookmarkImagelist: TImageList = nil;
128   AppApiFlatTheme: TATFlatTheme;
129   AppAlwaysNewInstance: boolean = false;
130   AppSessionName: string = '';
131   AppServerId: string = 'cudatext.0'; //used by TUniqueInstance (which is used only on Unix)
132 
133 var
134   AppFrameList1: TFPList; //all frames - for main thread
135   AppFrameList2: TFPList; //all frames - for file watcher thread
136   AppFrameListDeleting: TFPList; //frames which need to be Free'd
137                               //we don't free frames instantly, because watcher thread can access them
138 
139   AppEventLister: TEvent; //event set to signaled, when main thread has done AppFrameList2 updating
140   AppEventWatcher: TEvent; //event set to signaled, when watcher thread is not busy
141 
142 type
143   TAppKeyValue = class
144     Key: string;
145     Value: string;
146   end;
147 
148 type
149   { TAppKeyValues }
150 
151   TAppKeyValues = class(TFPList)
152   public
153     procedure Add(const AKey, AValue: string);
GetValuenull154     function GetValue(const AKey, ADefValue: string): string;
155   end;
156 
157 var
158   AppConfig_Detect: TAppKeyValues;
159   AppConfig_DetectLine: TAppKeyValues;
160   AppConfig_PGroups: TAppKeyValues;
161 
162 const
163   AppExtensionThemeUi = '.cuda-theme-ui';
164   AppExtensionThemeSyntax = '.cuda-theme-syntax';
165 
166 type
167   TUiOpsFindCaseSensitive = (
168     ufcsCaseIgnore,
169     ufcsCaseSens,
170     ufcsCaseFromDialog
171     );
172 
173 type
174   TUiOps = record
175     VarFontName: string;
176     VarFontSize: integer;
177     OutputFontName: string;
178     OutputFontSize: integer;
179     StatusbarFontName: string;
180     StatusbarFontSize: integer;
181     DoubleBuffered: boolean;
182 
183     PyLibrary: string;
184     PyChangeSlow: integer;
185     InfoAboutOptionsEditor: boolean;
186     AllowFrameParsing: boolean; //must be set in FormMain.OnShow
187     AllowRunPkExec: boolean;
188 
189     LogPluginIniting: boolean;
190     LogSessions: boolean;
191     LogDebug: boolean;
192     LogConsole: boolean;
193     LogConsoleDetailedStartupTime: boolean;
194 
195     LexerThemes: boolean;
196     LexerMenuGrouped: boolean;
197     LexerPostponeUntilShown: boolean;
198     LexerParsingMinTimeForEvent: integer;
199 
200     ToolBarTheme: string;
201     LangName: string;
202 
203     ThemeUi: string;
204     ThemeSyntax: string;
205     ThemeUi_Loaded: boolean;
206     ThemeSyntax_Loaded: boolean;
207     ThemedMainMenu: boolean;
208     ThemedMainMenuFontName: string;
209     ThemedMainMenuFontSize: integer;
210 
211     SidebarShow: boolean;
212     SidebarOnRight: boolean;
213     SidebarTheme: string;
214     SidepanelOnStart: integer;
215     BottomOnStart: integer;
216     PictureTypes: string;
217     DefaultTabSplitIsHorz: boolean;
218     MaxFileSizeToOpen: integer;
219     MaxFileSizeForLexer: integer;
220     MaxStatusbarMessages: integer;
221 
222     AutocompleteHtml: boolean;
223     AutocompleteHtml_AutoClose: boolean;
224     AutocompleteHtml_Lexers: string;
225     AutocompleteCss: boolean;
226     AutocompleteCss_Lexers: string;
227     AutocompleteFileURI: boolean;
228     AutocompleteInComments: boolean;
229     AutocompleteInCommentsHTML: boolean;
230     AutocompleteInStrings: boolean;
231 
232     HtmlBackgroundColorPair: array[boolean] of TColor;
233 
234     ListboxCentered: boolean;
235     ListboxSizeX: integer;
236     ListboxSizeY: integer;
237     ListboxCompleteSizeX: integer;
238     ListboxCompleteSizeY: integer;
239     ListboxFuzzySearch: boolean;
240     ListboxHotkeyFontSizeDelta: integer;
241 
242     TabsShowFoldersSuffix: boolean;
243     TabsShowFoldersMaxLevels: integer;
244     TabsResetUntitledCounter: boolean;
245     TabsDisabled: boolean;
246     TabVarWidth: boolean;
247     TabMultiline: boolean;
248     TabAngled: boolean;
249     TabFlat: boolean;
250     TabWidth: integer;
251     TabWidthMin: integer;
252     TabWidthMax: integer;
253     TabHeight: integer;
254     TabHeightInner: integer;
255     TabSpacer: integer;
256     TabSpaceBeforeText: integer;
257     TabPosition: integer;
258     TabColorFull: boolean;
259     TabFontScale: integer;
260     TabShowX: integer;
261     TabShowXSize: integer;
262     TabShowXRounded: boolean;
263     TabShowPlus: boolean;
264     TabDblClickClose: boolean;
265     TabNumbers: boolean;
266     TabNewNearCurrent: boolean;
267     TabRecentOnClose: boolean;
268     TabButtonLayout: string;
269     TabPreviewFontStyle: string;
270     TabSwitcherDialog: boolean;
271 
272     MaxHistoryEdits: integer;
273     MaxHistoryMenu: integer;
274     MaxHistoryFiles: integer;
275 
276     CmdPaletteFilterKeep: boolean;
277     CmdPaletteFilterText: string;
278 
279     HistoryDisabledStr: string;
280     HistoryItems: array[TAppHistoryElement] of boolean;
281 
282     FindSuggestSel: boolean;
283     FindSuggestWord: boolean;
284     FindSuggestInSelection: boolean;
285     FindCurrentWordCaseSensitive: TUiOpsFindCaseSensitive;
286     FindShowNoResultsByInputBgColor: boolean;
287 
288     FindHiddenButtons: string;
289     FindShow_FindFirst: boolean;
290     FindShow_FindNext: boolean;
291     FindShow_FindPrev: boolean;
292     FindShow_ReplaceAll: boolean;
293     FindShow_ReplaceGlobal: boolean;
294     FindShow_RegEx: boolean;
295     FindShow_CaseSens: boolean;
296     FindShow_WholeWords: boolean;
297     FindShow_Wrapped: boolean;
298     FindShow_InSel: boolean;
299     FindShow_MultiLine: boolean;
300     FindShow_SyntaxElements: boolean;
301     FindShow_HiAll: boolean;
302     FindShow_ConfirmRep: boolean;
303     FindShow_RegexSubst: boolean;
304 
305     FindIndentVert: integer;
306     FindIndentHorz: integer;
307     FindMultiLineScale: double;
308     FindSeparateForm: boolean;
309     FindHiAll_MaxLines: integer;
310     FindHiAll_TagValue: Int64;
311     //FindHiAll_MoveCaret: boolean;
312     FindOccur_TagValue: Int64;
313 
314     AllowProgramUpdates: boolean;
315     EscapeClose: boolean;
316     EscapeCloseConsole: boolean;
317     ConsoleWordWrap: boolean;
318     InputHeight: integer;
319     InitialDir: string;
320     ConfirmLinksClicks: boolean;
321     ConfirmSaveEmptyUntitledTab: boolean;
322 
323     ExportHtmlNumbers: boolean;
324     ExportHtmlFontName: string;
325     ExportHtmlFontSize: integer;
326 
327     TreeTheme: string;
328     TreeAutoSync: boolean;
329     TreeTimeFill: integer;
330     //TreeTimeCaret: integer;
331     TreeShowIcons: boolean;
332     TreeShowTooltips: boolean;
333     TreeFilterLayout: integer;
334     TreeSublexers: boolean;
335     TreeIconFilenames: string;
336 
337     NewdocLexer: string;
338     NewdocEnc: string;
339     NewdocEnds: integer;
340 
341     DefaultEncUtf8: boolean;
342     ViewerBinaryWidth: integer;
343     ViewerNonPrintable: boolean;
344 
345     StatusNoSel: string;
346     StatusSmallSel: string;
347     StatusStreamSel: string;
348     StatusColSel: string;
349     StatusCarets: string;
350     StatusPanels: string;
351     StatusTime: integer;
352     StatusHeightPercents: integer;
353     StatusHeightMin: integer;
354 
355     AltTooltipTime: integer;
356     AltTooltipTimeMax: integer;
357     AltTooltipPaddingX: integer;
358     AltTooltipPaddingY: integer;
359 
360     ScrollbarWidth: integer;
361     ScrollbarBorderSize: integer;
362     ScrollbarArrowSize: integer;
363 
364     ProgressbarWidth: integer;
365     ProgressbarHeightSmall: integer;
366 
367     ShowMenubar: boolean;
368     ShowStatusbar: boolean;
369     ShowToolbar: boolean;
370     ShowTitlePath: boolean;
371     Scale: integer;
372     ScaleFont: integer;
373 
374     ReopenSession: boolean;
375     ReopenSessionWithCmdLine: boolean;
376     SessionSaveInterval: integer;
377     SessionSaveOnExit: boolean;
378     BackupLastSessions: integer;
379     SaveModifiedTabsOnClose: boolean;
380 
381     ShowFormsOnTop: boolean;
382     ShowMenuDialogsWithBorder: boolean;
383     UndoPersistent: string;
384     AllowSaveOfUnmodifiedFile: boolean;
385 
386     PluginDialogsShowInTaskbar: boolean;
387     PluginDialogsModalFormStyle: TFormStyle;
388     FloatGroupsShowInTaskbar: TShowInTaskbar;
389     OneInstance: boolean;
390     NotificationEnabled: boolean;
391     NotificationTimeSeconds: integer;
392     NotificationConfirmReload: integer;
393     MarkFilesDeletedOutsideAsModified: boolean;
394     NonTextFiles: integer; //0: prompt, 1: open, 2: don't open
395     NonTextFilesBufferKb: integer;
396     ReloadUnsavedConfirm: boolean;
397     ReloadFollowTail: boolean;
398     CheckLowDiskSpace: Int64; //minimal disk free space in bytes; can be 0 to disable the check
399     FullScreen: string;
400     MouseGotoDefinition: string;
401 
402     Emmet_AddSlashToEmptyTags: boolean;
403     Emmet_CommentTags: boolean;
404     Emmet_IndentNested: boolean;
405     Emmet_SingleLine: boolean;
406     Emmet_TrimLineMarkers: boolean;
407     Emmet_WordWrap: boolean;
408 
409     HotkeyFindDialog,
410     HotkeyReplaceDialog,
411     HotkeyFindFirst,
412     HotkeyFindNext,
413     HotkeyFindPrev,
414     HotkeyReplaceAndFindNext,
415     HotkeyReplaceNoFindNext,
416     HotkeyReplaceAll,
417     HotkeyReplaceGlobal,
418     HotkeyCountAll,
419     HotkeyExtractAll,
420     HotkeySelectAll,
421     HotkeyMarkAll,
422     HotkeyToggleRegex,
423     HotkeyToggleCaseSens,
424     HotkeyToggleWords,
425     HotkeyToggleWrapped,
426     HotkeyToggleInSelect,
427     HotkeyToggleMultiline,
428     HotkeyToggleConfirmRep,
429     HotkeyToggleTokens,
430     HotkeyToggleHiAll
431       : string;
432   end;
433 var
434   UiOps: TUiOps;
435 
436 const
437   OpStr_FontName = 'font_name'+cOptionSystemSuffix;
438   OpStr_FontName_i = 'font_name_i'+cOptionSystemSuffix;
439   OpStr_FontName_b = 'font_name_b'+cOptionSystemSuffix;
440   OpStr_FontName_bi = 'font_name_bi'+cOptionSystemSuffix;
441   OpStr_FontSize = 'font_size'+cOptionSystemSuffix;
442   OpStr_FontSize_i = 'font_size_i'+cOptionSystemSuffix;
443   OpStr_FontSize_b = 'font_size_b'+cOptionSystemSuffix;
444   OpStr_FontSize_bi = 'font_size_bi'+cOptionSystemSuffix;
445   OpStr_FontQuality = 'font_quality'+cOptionSystemSuffix;
446   OpStr_FontLigatures = 'font_ligatures'; //+cOptionSystemSuffix;
447   OpStr_UiFontName = 'ui_font_name'+cOptionSystemSuffix;
448   OpStr_UiFontSize = 'ui_font_size'+cOptionSystemSuffix;
449   OpStr_UiFontOutputName = 'ui_font_output_name'+cOptionSystemSuffix;
450   OpStr_UiFontOutputSize = 'ui_font_output_size'+cOptionSystemSuffix;
451   OpStr_UiFontStatusbarName = 'ui_font_statusbar_name'+cOptionSystemSuffix;
452   OpStr_UiFontStatusbarSize = 'ui_font_statusbar_size'+cOptionSystemSuffix;
453   OpStr_UiDoubleBuffered = 'ui_buffered'+cOptionSystemSuffix;
454   OpStr_DefEncodingIsUtf8 = 'def_encoding_utf8'+cOptionSystemSuffix;
455 
456 type
457   TEditorOps = record
458     OpFontName: string;
459     OpFontName_i: string;
460     OpFontName_b: string;
461     OpFontName_bi: string;
462     OpFontSize: integer;
463     OpFontSize_i: integer;
464     OpFontSize_b: integer;
465     OpFontSize_bi: integer;
466     OpFontSize_original: integer;
467     OpFontSize_original_i: integer;
468     OpFontSize_original_b: integer;
469     OpFontSize_original_bi: integer;
470     OpFontQuality: TFontQuality;
471     OpFontLigatures: boolean;
472 
473     OpScrollAnimationSteps: integer;
474     OpScrollAnimationSleep: integer;
475 
476     OpScrollbarsNew: boolean;
477     OpSpacingY: integer;
478     OpTabSize: integer;
479     OpTabSpaces: boolean;
480     OpMaxLineLenForBracketFinder: integer;
481     OpMaxLineLenToTokenize: integer;
482 
483     OpActiveBorderRaw: integer;
484     OpActiveBorderInControls: boolean;
485     OpActiveBorderInEditor: boolean;
486     OpActiveBorderWidth: integer;
487 
488     OpOverwriteSel: boolean;
489     OpOverwriteOnPaste: boolean;
490     OpPasteWithEolAtLineStart: boolean;
491 
492     OpAutoCloseBracketsMultiCarets: boolean;
493     OpAutoCloseBrackets: string;
494     OpAutocompleteAutoshowCharCount: integer;
495     OpAutocompleteTriggerChars: string;
496     OpAutocompleteCommitChars: string;
497     OpAutocompleteCloseChars: string;
498     OpAutocompleteAddOpeningBracket: boolean;
499     OpAutocompleteUpDownAtEdge: integer;
500     OpAutocompleteCommitIfSingleItem: boolean;
501 
502     OpUnderlineColorFiles: string;
503     OpUnderlineColorSize: integer;
504     OpLinks: boolean;
505     OpLinksRegex: string;
506 
507     //view
508     OpGutterShow: boolean;
509     OpGutterFold: boolean;
510     OpGutterFoldAlways: boolean;
511     OpGutterFoldIcons: integer;
512     OpGutterBookmarks: boolean;
513 
514     OpNumbersShow: boolean;
515     OpNumbersStyle: integer;
516     OpNumbersForCarets: boolean;
517     OpNumbersCenter: boolean;
518 
519     OpRulerShow: boolean;
520     OpRulerNumeration: integer;
521     OpRulerMarkCaret: integer;
522 
523     OpMinimapShow: boolean;
524     OpMinimapShowSelAlways: boolean;
525     OpMinimapShowSelBorder: boolean;
526     OpMinimapCharWidth: integer;
527     OpMinimapAtLeft: boolean;
528     OpMinimapScale: integer;
529     OpMinimapTooltipShow: boolean;
530     OpMinimapTooltipLineCount: integer;
531     OpMinimapTooltipWidth: integer;
532     OpMinimapDragImmediately: boolean;
533 
534     OpMicromapShow: boolean;
535     OpMicromapOnScrollbar: boolean;
536     OpMicromapLineStates: boolean;
537     OpMicromapSelections: boolean;
538     OpMicromapBookmarks: boolean;
539     OpMicromapSmallMarkSizePercents: integer;
540     OpMicromapMinMarkHeight: integer;
541 
542     OpMarginFixed: integer;
543     OpMarginString: string;
544 
545     OpMarkerSize: integer;
546     OpStaplesStyle: integer;
547     OpStaplesProps: string;
548     OpStapleIndentConsidersEnd: boolean;
549 
550     //unprinted
551     OpUnprintedShow: boolean;
552     OpUnprintedContent: string;
553 
554     //wrap
555     OpWrapMode: integer;
556     OpWrapIndented: boolean;
557     OpWrapEnabledMaxLines: integer;
558 
559     //undo
560     OpUndoLimit: integer;
561     OpUndoGrouped: boolean;
562     OpUndoAfterSave: boolean;
563     OpUndoMaxCarets: integer;
564     OpUndoIndentVert: integer;
565     OpUndoIndentHorz: integer;
566     OpUndoPause: integer;
567     OpUndoMouseClicks: boolean;
568 
569     //caret
570     OpCaretBlinkTime: integer;
571     OpCaretBlinkEn: boolean;
572     OpCaretViewNormal: string;
573     OpCaretViewOverwrite: string;
574     OpCaretViewReadonly: string;
575     OpCaretVirtual: boolean;
576     OpCaretMulti: boolean;
577     OpCaretAfterPasteColumn: integer;
578     OpCaretsAddedToColumnSel: boolean;
579     OpCaretsPrimitiveColumnSel: boolean;
580     OpCaretKeepVisibleOnScroll: boolean;
581     OpCaretProximityVert: integer;
582 
583     //general
584     OpKeepSelFontColor: boolean;
585     OpShowCurLine: boolean;
586     OpShowCurLineMinimal: boolean;
587     OpShowCurLineOnlyFocused: boolean;
588     OpShowCurCol: boolean;
589     OpShowLastLineOnTop: boolean;
590     OpShowFullBackgroundSel: boolean;
591     OpShowFullBackgroundSyntax: boolean;
592     OpShowMouseSelFrame: boolean;
593     OpCopyLineIfNoSel: boolean;
594     OpCutLineIfNoSel: boolean;
595     OpCopyColumnAlignedBySpaces: boolean;
596     OpSavingTrimSpaces: boolean;
597     OpSavingTrimFinalEmptyLines: boolean;
598     OpSavingForceFinalEol: boolean;
599     OpShowHintOnVertScroll: boolean;
600     OpSmoothScroll: boolean;
601     OpCenteringWidth: integer;
602     OpCenteringForDistractionFree: integer;
603     OpScrollStyleHorz: integer;
604     OpLexerDynamicHiliteEnabled: boolean;
605     OpLexerDynamicHiliteMaxLines: integer;
606     OpLexerLineSeparators: boolean;
607     OpZebra: integer;
608     OpZebraStep: integer;
609     OpDimUnfocused: integer;
610     OpCommandLogMaxCount: integer;
611 
612     OpNonWordChars: UnicodeString;
613     OpFoldStyle: integer;
614     OpFoldTooltipShow: boolean;
615 
616     //indent
617     OpIndentAuto: boolean;
618     OpIndentAutoKind: integer;
619     OpIndentSize: integer;
620     OpIndentAutoRule: string;
621     OpUnIndentKeepsAlign: boolean;
622     OpIndentMakesWholeLineSel: boolean;
623 
624     //mouse
625     OpMouse2ClickDragSelectsWords: boolean;
626     OpMouseDragDrop: boolean;
627     OpMouseMiddleClickAction: integer;
628     OpMouseRightClickMovesCaret: boolean;
629     OpMouseEnableColumnSelection: boolean;
630     OpMouseHideCursorOnType: boolean; //don't work on lin
631     OpMouseGutterClickSelectedLine: boolean;
632     OpMouseWheelZoom: boolean;
633     OpMouseWheelSpeedVert: integer;
634     OpMouseWheelSpeedHorz: integer;
635     OpMouseClickNumberSelectsEol: boolean;
636     OpMouseClickLinks: integer;
637 
638     //keys
639     OpKeyBackspaceUnindent: boolean;
640     OpKeyBackspaceWrap: boolean;
641     OpKeyTabIndents: boolean;
642     OpKeyHomeToNonSpace: boolean;
643     OpKeyHomeEndNavigateWrapped: boolean;
644     OpKeyEndToNonSpace: boolean;
645     OpKeyPageKeepsRelativePos: boolean;
646     OpKeyPageUpDownSize: integer;
647     OpKeyUpDownKeepColumn: boolean;
648     OpKeyUpDownNavigateWrapped: boolean;
649     OpKeyUpDownAllowToEdge: boolean;
650     OpKeyLeftRightGoToNextLineWithCarets: boolean;
651     OpKeyLeftRightSwapSel: boolean;
652     OpKeyLeftRightSwapSelAndSelect: boolean;
653 
654     OpBracketHilite: boolean;
655     OpBracketSymbols: string;
656     OpBracketDistance: integer;
657   end;
658 var
659   EditorOps: TEditorOps;
660 
661 var
662   AppDir_Home: string;
663   AppDir_Settings: string;
664   AppDir_SettingsDefault: string;
665   AppDir_Py: string;
666   AppDir_Data: string;
667   AppDir_Lexers: string;
668   AppDir_LexersLite: string;
669   AppDir_DataThemes: string;
670   AppDir_DataAutocomplete: string;
671   AppDir_DataAutocompleteSpec: string;
672   AppDir_DataLang: string;
673   AppDir_DataSidebarIcons: string;
674   AppDir_DataCodetreeIcons: string;
675   AppDir_DataToolbarIcons: string;
676   AppDir_LastInstalledAddon: string = '';
677   AppFile_OptionsDefault: string;
678   AppFile_OptionsUserInit: string;
679   AppFile_OptionsUser: string;
680   AppFile_History: string;
681   AppFile_HistoryFiles: string;
682   AppFile_Hotkeys: string;
683   AppFile_PluginsIni: string;
684 
GetAppLangFilenamenull685 function GetAppLangFilename: string;
GetAppUndoFilenamenull686 function GetAppUndoFilename(const fn: string; IsRedo: boolean): string;
687 
EscapeLexerFilenamenull688 function EscapeLexerFilename(const ALexName: string): string;
GetAppLexerFilenamenull689 function GetAppLexerFilename(const ALexName: string): string;
GetAppLexerMapFilenamenull690 function GetAppLexerMapFilename(const ALexName: string): string;
GetAppLexerOpsFilenamenull691 function GetAppLexerOpsFilename(const ALexName: string): string;
GetAppLexerAcpFilenamenull692 function GetAppLexerAcpFilename(const ALexName: string): string;
GetAppLexerSpecificConfignull693 function GetAppLexerSpecificConfig(ALexer: string; ADefaultConfig: boolean=false): string;
GetAppFilenameIsIgnoredForSessionnull694 function GetAppFilenameIsIgnoredForSession(const AFilename: string): boolean;
IsDefaultSessionActivenull695 function IsDefaultSessionActive: boolean;
696 
MsgBoxnull697 function MsgBox(const Str: string; Flags: Longint): integer;
698 procedure MsgBadConfig(const fn, msg: string);
699 procedure MsgStdout(const Str: string; AllowMsgBox: boolean = false);
700 procedure MsgLogConsole(const AText: string);
701 
AppScalenull702 function AppScale(AValue: integer): integer;
AppScaleFontnull703 function AppScaleFont(AValue: integer): integer;
704 //procedure AppScaleScrollbar(C: TATScroll);
AppListboxItemHeightnull705 function AppListboxItemHeight(AScale, ADoubleHeight: boolean): integer;
706 procedure AppGetFileProps(const FileName: string; out P: TAppFileProps);
707 procedure AppUpdateWatcherFrames;
708 
709 procedure FixFormPositionToDesktop(F: TForm);
710 procedure FixRectPositionToDesktop(var F: TRect);
711 
712 type
713   { TKeymapHelper }
714 
715   TKeymapHelper = class
716   public
717     class procedure ClearKey(AKeymap: TATKeymap; AItemIndex, AKeyIndex: integer);
718     class procedure ClearKeyInAll(AItemIndex, AKeyIndex: integer);
719 
GetHotkeynull720     class function GetHotkey(AKeymap: TATKeymap; const ACmdString: string): string;
SetHotkeynull721     class function SetHotkey(AKeymap: TATKeymap; const AParams: string; AndSaveFile: boolean): boolean;
722 
CheckDuplicateForCommandnull723     class function CheckDuplicateForCommand(
724       AKeymapItem: TATKeymapItem;
725       const ALexerName: string;
726       AOverwriteAndSave: boolean): integer;
727 
GetForLexernull728     class function GetForLexer(const ALexer: string): TATKeymap;
729     class procedure LoadConfig(AKeymap: TATKeymap; const AFileName: string; AForLexer: boolean);
730 
731     class procedure ItemSaveToConfig(K: TATKeymapItem; const path, ALexerName: string; ALexerSpecific: boolean);
732     class procedure ItemDeleteInConfig(K: TATKeymapItem; const path, ALexerName: string; ALexerSpecific: boolean);
733 
SaveKey_ForPluginnull734     class function SaveKey_ForPlugin(AKeymap: TATKeymap; AOverwriteKey: boolean;
735       const AMenuitemCaption, AModuleName, AMethodName, ALexerName, AHotkey: string): boolean;
736   end;
737 
DoLexerDetectByFilenameOrContentnull738 function DoLexerDetectByFilenameOrContent(const AFilename: string;
739   AChooseFunc: TecLexerChooseFunc): TecSyntAnalyzer;
740 procedure DoLexerEnum(L: TStringList; AlsoDisabled: boolean = false);
741 
DoReadOneStringFromFilenull742 function DoReadOneStringFromFile(const AFilename: string): string;
DoReadContentFromFilenull743 function DoReadContentFromFile(const AFilename: string): string;
744 procedure DoWriteStringToFile(const AFilename, AText: string);
745 
AppCollapseHomeDirInFilenamenull746 function AppCollapseHomeDirInFilename(const fn: string): string;
AppExpandHomeDirInFilenamenull747 function AppExpandHomeDirInFilename(const fn: string): string;
AppExpandFileNameWithDirnull748 function AppExpandFileNameWithDir(const AFileName, ADir: string): string;
AppConfigKeyForBookmarksnull749 function AppConfigKeyForBookmarks(Ed: TATSynEdit): string;
750 procedure AppDiskCheckFreeSpace(const fn: string);
AppKeyIsAllowedAsCustomHotkeynull751 function AppKeyIsAllowedAsCustomHotkey(Key: Word; Shift: TShiftState): boolean;
752 
753 var
754   AppManager: TecLexerList = nil;
755   AppManagerLite: TATLiteLexers = nil;
756   AppShortcutEscape: TShortcut = 0;
757   AppShortcutShiftTab: TShortcut = 0;
758 
759   AppKeymapMain: TATKeymap = nil;
760   AppKeymapInitial: TATKeymap = nil; //inited only by API calls
761   AppKeymapLexers: TStringList = nil;
762 
763 type
764   TStrEvent = procedure(Sender: TObject; const ARes: string) of object;
765   TStrFunction = function(const AStr: string): boolean of object;
766 
767 type
768   TAppEncodingRecord = record
769     Sub,
770     Name,
771     ShortName: string;
772   end;
773 
774 const
775   AppEncodings: array[0..38] of TAppEncodingRecord = (
776     (Sub: ''; Name: cEncNameUtf8_NoBom; ShortName: 'utf8'),
777     (Sub: ''; Name: cEncNameUtf8_WithBom; ShortName: 'utf8_bom'),
778     (Sub: ''; Name: cEncNameUtf16LE_NoBom; ShortName: 'utf16le'),
779     (Sub: ''; Name: cEncNameUtf16LE_WithBom; ShortName: 'utf16le_bom'),
780     (Sub: ''; Name: cEncNameUtf16BE_NoBom; ShortName: 'utf16be'),
781     (Sub: ''; Name: cEncNameUtf16BE_WithBom; ShortName: 'utf16be_bom'),
782     (Sub: ''; Name: cEncNameUtf32LE_NoBom; ShortName: 'utf32le'),
783     (Sub: ''; Name: cEncNameUtf32LE_WithBom; ShortName: 'utf32le_bom'),
784     (Sub: ''; Name: cEncNameUtf32BE_NoBom; ShortName: 'utf32be'),
785     (Sub: ''; Name: cEncNameUtf32BE_WithBom; ShortName: 'utf32be_bom'),
786     (Sub: ''; Name: '-'; ShortName: ''),
787     (Sub: 'eu'; Name: 'cp1250'; ShortName: 'cp1250'),
788     (Sub: 'eu'; Name: 'cp1251'; ShortName: 'cp1251'),
789     (Sub: 'eu'; Name: 'cp1252'; ShortName: 'cp1252'),
790     (Sub: 'eu'; Name: 'cp1253'; ShortName: 'cp1253'),
791     (Sub: 'eu'; Name: 'cp1257'; ShortName: 'cp1257'),
792     (Sub: 'eu'; Name: '-'; ShortName: ''),
793     (Sub: 'eu'; Name: 'cp437'; ShortName: 'cp437'),
794     (Sub: 'eu'; Name: 'cp850'; ShortName: 'cp850'),
795     (Sub: 'eu'; Name: 'cp852'; ShortName: 'cp852'),
796     (Sub: 'eu'; Name: 'cp866'; ShortName: 'cp866'),
797     (Sub: 'eu'; Name: '-'; ShortName: ''),
798     (Sub: 'eu'; Name: 'iso88591'; ShortName: 'iso88591'),
799     (Sub: 'eu'; Name: 'iso88592'; ShortName: 'iso88592'),
800     (Sub: 'eu'; Name: 'iso885915'; ShortName: 'iso885915'),
801     (Sub: 'eu'; Name: 'mac'; ShortName: 'mac'),
802     (Sub: 'mi'; Name: 'cp1254'; ShortName: 'cp1254'),
803     (Sub: 'mi'; Name: 'cp1255'; ShortName: 'cp1255'),
804     (Sub: 'mi'; Name: 'cp1256'; ShortName: 'cp1256'),
805     (Sub: 'mi'; Name: '-'; ShortName: ''),
806     (Sub: 'mi'; Name: 'koi8r'; ShortName: 'koi8r'),
807     (Sub: 'mi'; Name: 'koi8u'; ShortName: 'koi8u'),
808     (Sub: 'mi'; Name: 'koi8ru'; ShortName: 'koi8ru'),
809     (Sub: 'as'; Name: 'cp874'; ShortName:  'cp874'),
810     (Sub: 'as'; Name: 'cp932'; ShortName:  'cp932'),
811     (Sub: 'as'; Name: 'cp936'; ShortName:  'cp936'),
812     (Sub: 'as'; Name: 'cp949'; ShortName:  'cp949'),
813     (Sub: 'as'; Name: 'cp950'; ShortName:  'cp950'),
814     (Sub: 'as'; Name: 'cp1258'; ShortName: 'cp1258')
815   );
816 
817 type
818   TAppPyEventResult = record
819     Val: (evrTrue, evrFalse, evrInt, evrString, evrOther);
820     Int: integer;
821     Str: string;
822   end;
823 
824 type
825   TAppPyEvent = (
826     cEventOnKey,
827     cEventOnKeyUp,
828     cEventOnHotspot,
829     cEventOnInsert,
830     cEventOnChange,
831     cEventOnChangeSlow,
832     cEventOnCaret,
833     cEventOnScroll,
834     cEventOnMouseStop,
835     cEventOnClick,
836     cEventOnClickDbl,
837     cEventOnClickGutter,
838     cEventOnClickGap,
839     cEventOnClickLink,
840     cEventOnState,
841     cEventOnStateEd,
842     cEventOnFocus,
843     cEventOnStart,
844     cEventOnOpen,
845     cEventOnOpenBefore,
846     cEventOnOpenNone,
847     cEventOnClose,
848     cEventOnCloseBefore,
849     cEventOnSaveAfter,
850     cEventOnSaveBefore,
851     cEventOnSaveNaming,
852     cEventOnLexer,
853     cEventOnLexerParsed,
854     cEventOnComplete,
855     cEventOnGotoDef,
856     cEventOnGotoEnter,
857     cEventOnFuncHint,
858     cEventOnTabChange,
859     cEventOnTabMove,
860     cEventOnPaste,
861     cEventOnMessage,
862     cEventOnConsoleNav,
863     cEventOnOutputNav,
864     cEventOnSnippet,
865     cEventOnMacro,
866     cEventOnAppActivate,
867     cEventOnAppDeactivate,
868     cEventOnCLI,
869     cEventOnExit
870     );
871   TAppPyEvents = set of TAppPyEvent;
872   TAppPyEventsPrior = array[TAppPyEvent] of byte;
873     //0: default, 1,2...: higher priority
874   TAppPyEventsLazy = array[TAppPyEvent] of boolean;
875 
876 var
877   AppEventsMaxPriorities: array[TAppPyEvent] of integer;
878 
879 const
880   cAppPyEvent: array[TAppPyEvent] of string = (
881     'on_key',
882     'on_key_up',
883     'on_hotspot',
884     'on_insert',
885     'on_change',
886     'on_change_slow',
887     'on_caret',
888     'on_scroll',
889     'on_mouse_stop',
890     'on_click',
891     'on_click_dbl',
892     'on_click_gutter',
893     'on_click_gap',
894     'on_click_link',
895     'on_state',
896     'on_state_ed',
897     'on_focus',
898     'on_start',
899     'on_open',
900     'on_open_pre',
901     'on_open_none',
902     'on_close',
903     'on_close_pre',
904     'on_save',
905     'on_save_pre',
906     'on_save_naming',
907     'on_lexer',
908     'on_lexer_parsed',
909     'on_complete',
910     'on_goto_def',
911     'on_goto_enter',
912     'on_func_hint',
913     'on_tab_change',
914     'on_tab_move',
915     'on_paste',
916     'on_message',
917     'on_console_nav',
918     'on_output_nav',
919     'on_snippet',
920     'on_macro',
921     'on_app_activate',
922     'on_app_deactivate',
923     'on_cli',
924     'on_exit'
925     );
926 
927 type
928 
929   { TAppCommandInfo }
930 
931   TAppCommandInfo = class
932   public
933     ItemModule: string;
934     ItemProc: string;
935     ItemProcParam: string;
936     ItemCaption: string;
937     ItemLexers: string;
938     ItemInMenu: string;
939     ItemFromApi: boolean;
CommaStrnull940     function CommaStr: string;
941   end;
942 
943 type
944   TAppEventInfo = class
945     ItemModule: string;
946     ItemLexers: string;
947     ItemEvents: TAppPyEvents;
948     ItemEventsPrior: TAppPyEventsPrior;
949     ItemEventsLazy: TAppPyEventsLazy;
950     ItemKeys: string;
951   end;
952 
953 type
954   TAppTreeHelper = class
955     ItemModule: string;
956     ItemProc: string;
957     ItemLexers: string;
958   end;
959 
960 var
961   AppConsoleQueue: TAppConsoleQueue;
962   AppCommandsDelayed: TAppCommandsDelayed;
963   AppCommandList: TFPList;
964   AppEventList: TFPList;
965   AppTreeHelpers: TFPList;
966 
967 type
968   TAppMenuProps = class
969   public
970     CommandCode: integer;
971     CommandString: string;
972     TagString: string;
973   end;
974 
975 type
976   { TPluginHelper }
977 
978   TPluginHelper = class
979   public
CommandCategorynull980     class function CommandCategory(Cmd: integer): TAppCommandCategory;
CommandHasConfigurableHotkeynull981     class function CommandHasConfigurableHotkey(Cmd: integer): boolean;
982     class procedure CommandsClearButKeepApiItems;
CommandGetIndexFromModuleAndMethodnull983     class function CommandGetIndexFromModuleAndMethod(const AText: string): integer;
984     class procedure CommandUpdateSubcommands(const AText: string);
985 
EventIsUsednull986     class function EventIsUsed(AEvent: TAppPyEvent): boolean;
987     class procedure EventStringToEventData(const AEventStr: string;
988       out AEvents: TAppPyEvents;
989       out AEventsPrior: TAppPyEventsPrior;
990       out AEventsLazy: TAppPyEventsLazy);
991     class procedure EventsUpdate(const AModuleName, AEventStr, ALexerStr, AKeyStr: string);
992     class procedure EventsMaxPrioritiesUpdate;
993 
CommandCode_To_HotkeyStringIdnull994     class function CommandCode_To_HotkeyStringId(ACmd: integer): string;
HotkeyStringId_To_CommandCodenull995     class function HotkeyStringId_To_CommandCode(const AId: string): integer;
996 
Debug_PluginCommandsnull997     class function Debug_PluginCommands(const AModule: string): string;
998   end;
999 
1000 
AppEncodingShortnameToFullnamenull1001 function AppEncodingShortnameToFullname(const S: string): string;
AppEncodingFullnameToShortnamenull1002 function AppEncodingFullnameToShortname(const S: string): string;
AppEncodingListAsStringnull1003 function AppEncodingListAsString: string;
1004 
1005 procedure UpdateFormOnTop(F: TForm);
1006 
1007 procedure DoStatusbarTextByTag(AStatus: TATStatus; ATag: PtrInt; const AText: string);
1008 procedure DoStatusbarHintByTag(AStatus: TATStatus; ATag: PtrInt; const AText: string);
1009 procedure DoStatusbarColorByTag(AStatus: TATStatus; ATag: PtrInt; AColor: TColor);
1010 
IsFileTooBigForOpeningnull1011 function IsFileTooBigForOpening(const AFilename: string): boolean;
IsFileTooBigForLexernull1012 function IsFileTooBigForLexer(const AFilename: string): boolean;
IsOsFullPathnull1013 function IsOsFullPath(const S: string): boolean;
1014 procedure DoLexerDetect(const AFilename: string;
1015   out Lexer: TecSyntAnalyzer;
1016   out LexerLite: TATLiteLexer;
1017   out LexerName: string;
1018   AChooseFunc: TecLexerChooseFunc);
1019 procedure DoMenuitemEllipsis(c: TMenuItem);
1020 
1021 procedure AppOnLexerLoaded(Sender: TObject; ALexer: TecSyntAnalyzer);
1022 procedure AppLoadLexers;
1023 
1024 type
1025   { TAppManagerThread }
1026 
1027   TAppManagerThread = class(TThread)
1028   public
1029     procedure Execute; override;
1030   end;
1031 
1032 var
1033   AppManagerThread: TAppManagerThread = nil;
1034 
1035 implementation
1036 
1037 uses
1038   ATSynEdit_LineParts,
1039   ATSynEdit_Adapter_EControl,
1040   ec_syntax_format,
1041   proc_colors,
1042   proc_lexer_styles;
1043 
MsgBoxnull1044 function MsgBox(const Str: string; Flags: Longint): integer;
1045 begin
1046   Result:= Application.MessageBox(PChar(Str), PChar(msgTitle), Flags);
1047 end;
1048 
1049 procedure MsgBadConfig(const fn, msg: string);
1050 begin
1051   //MsgBox(msgCannotReadConfig+#10+fn+#10#10+msg, MB_OK+MB_ICONERROR);
1052   MsgLogConsole('ERROR: '+msgCannotReadConfig+' '+ExtractFileName(fn)+'; '+msg);
1053 end;
1054 
InitPyLibraryPathnull1055 function InitPyLibraryPath: string;
1056 const
1057   cMaxVersion = 10;
1058   cMinVersionUnix = 5;
1059   cMinVersionWindows = 4; //support Python 3.4 for WinXP
1060 {$ifdef windows}
1061 var
1062   N: integer;
1063   S, SFile: string;
1064 {$endif}
1065 {$ifdef darwin}
1066 var
1067   N: integer;
1068   S: string;
1069 {$endif}
1070 begin
1071   Result:= '';
1072 
1073   {$ifdef windows}
1074   //detect latest existing file python3x.dll in app folder
1075   S:= ExtractFilePath(Application.ExeName);
1076   for N:= cMaxVersion downto cMinVersionWindows do
1077   begin
1078     SFile:= Format('python3%d.dll', [N]);
1079     //don't return full filename, this loads DLL with full filename and plugins cannot load
1080     if FileExists(S+SFile) then
1081       exit(SFile);
1082   end;
1083   exit;
1084   {$endif}
1085 
1086   {$ifdef darwin}
1087   for N:= cMaxVersion downto cMinVersionUnix do
1088   begin
1089     S:= Format('/Library/Frameworks/Python.framework/Versions/3.%d/lib/libpython3.%d.dylib',
1090       [N, N]);
1091     if FileExists(S) then exit(S);
1092   end;
1093   exit;
1094   {$endif}
1095 
1096   {$ifdef dragonfly}
1097   exit('/usr/local/lib/libpython3.6m.so');
1098   {$endif}
1099 
1100   {$ifdef netbsd}
1101   exit('/usr/pkg/lib/libpython3.7.so');
1102   {$endif}
1103 
1104   {$ifdef solaris}
1105   exit('/usr/lib/amd64/libpython3.5m.so');
1106   {$endif}
1107 
1108   {$ifdef haiku}
1109   exit('/boot/system/develop/lib/libpython3.6m.so');
1110   {$endif}
1111 
1112   {$ifdef unix}
1113   exit('libpython3.so');
1114   {$endif}
1115 end;
1116 
1117 var
1118   OpFileExe: string = '';
1119   OpDirExe: string = '';
1120   OpDirLocal: string = '';
1121   OpDirPrecopy: string = '';
1122 
1123 function GetDirPrecopy: string;
1124 begin
1125   {$ifdef linux}
1126   exit('/usr/share/cudatext');
1127   {$endif}
1128 
1129   {$ifdef darwin}
1130   exit(ExtractFileDir(OpDirExe)+'/Resources');
1131   {$endif}
1132 
1133   {$ifdef dragonfly}
1134   exit('/usr/local/share/cudatext');
1135   {$endif}
1136 
1137   Result:= '';
1138 end;
1139 
1140 function AppCollapseHomeDirInFilename(const fn: string): string;
1141 {$ifndef windows}
1142 var
1143   S: string;
1144 {$endif}
1145 begin
1146   Result:= fn;
1147   {$ifndef windows}
1148   S:= AppDir_Home;
1149   if SBeginsWith(Result, S) then
1150     Result:= '~'+DirectorySeparator+Copy(Result, Length(S)+1, MaxInt);
1151   {$endif}
1152 end;
1153 
1154 function AppExpandHomeDirInFilename(const fn: string): string;
1155 begin
1156   Result:= fn;
1157   {$ifndef windows}
1158   if SBeginsWith(Result, '~'+DirectorySeparator) then
1159     Result:= AppDir_Home+Copy(Result, 3, MaxInt);
1160   {$endif}
1161 end;
1162 
1163 function AppExpandFileNameWithDir(const AFileName, ADir: string): string;
1164 var
1165   S: string;
1166 begin
1167   S:= GetCurrentDir;
1168   SetCurrentDir(ADir);
1169   Result:= ExpandFileName(AFileName);
1170   SetCurrentDir(S);
1171 end;
1172 
1173 
1174 function IsDistroUpdateNeeded: boolean;
1175 begin
1176   with TIniFile.Create(AppDir_Settings+DirectorySeparator+'packages.ini') do
1177   try
1178     Result:= ReadString('app', 'ver', '')<>cAppExeVersion;
1179     if Result then
1180       WriteString('app', 'ver', cAppExeVersion);
1181   finally
1182     Free
1183   end;
1184 end;
1185 
1186 function AppDirSettingsFromCommandLine: string;
1187 const
1188   cParam = '-s=';
1189 var
1190   S: string;
1191   i: integer;
1192 begin
1193   Result:= '';
1194   for i:= 1{not 0} to ParamCount do
1195   begin
1196     S:= ParamStr(i);
1197     if SBeginsWith(S, cParam) then
1198     begin
1199       Delete(S, 1, Length(cParam));
1200       exit(AppExpandHomeDirInFilename(S));
1201     end;
1202   end;
1203 end;
1204 
1205 function Which(const Executable: string): string;
1206 var
1207   ExeName,FoundExe:string;
1208   //Output: string;
1209 begin
1210   result:='';
1211 
1212   ExeName:=Executable;
1213 
1214   {$ifdef Windows}
1215   if ExtractFileExt(ExeName)='' then ExeName:=ExeName+'.exe';
1216   {$endif}
1217   {$ifdef dragonfly}
1218   OpDirLocal:= AppDir_Home + '.config/' + 'cudatext';
1219   CreateDirUTF8(OpDirLocal);
1220   {$endif}
1221 
1222   if FileExists(ExeName) then
1223     exit(ExeName)
1224   else
1225   begin
1226     FoundExe := ExeSearch(ExeName, '');
1227     if FileExists(FoundExe) then
1228       result:=FoundExe
1229     else
1230       result:=FindDefaultExecutablePath(ExeName);
1231   end;
1232 
1233   (*
1234   {$IFNDEF FREEBSD}
1235   if (NOT FileIsExecutable(result)) then result:='';
1236   {$ENDIF}
1237   *)
1238 
1239   (*
1240   {$IFDEF UNIX}
1241   if (NOT FileExists(result)) then
1242   begin
1243     RunCommand('which',[ExeName],Output,[poUsePipes, poStderrToOutPut]{$IF DEFINED(FPC_FULLVERSION) AND (FPC_FULLVERSION >= 30200)},swoHide{$ENDIF});
1244     Output:=Trim(Output);
1245     if ((Output<>'') and FileExists(Output)) then result:=Output;
1246   end;
1247   {$ENDIF}
1248   *)
1249 end;
1250 
1251 procedure InitDirs_Windows;
1252 begin
1253   AppDir_Home:= '';
1254 end;
1255 
1256 procedure InitDirs_macOS;
1257 begin
1258   //from https://github.com/graemeg/freepascal/blob/master/rtl/unix/sysutils.pp
1259   AppDir_Home:= GetEnvironmentVariable('HOME');
1260   if AppDir_Home<>'' then
1261     AppDir_Home:= IncludeTrailingPathDelimiter(AppDir_Home);
1262   OpDirLocal:= AppDir_Home+'Library/Application Support/CudaText';
1263   CreateDirUTF8(OpDirLocal);
1264 end;
1265 
1266 procedure InitDirs_Haiku;
1267 var
1268   HomeConfig: string;
1269 begin
1270   AppDir_Home:= '/boot/home';
1271   HomeConfig:= AppDir_Home+'/config/settings';
1272   OpDirLocal:= HomeConfig+'/cudatext';
1273   CreateDirUTF8(OpDirLocal);
1274 end;
1275 
1276 procedure InitDirs_UnixCommon;
1277 var
1278   HomeConfig: string;
1279   SPathOrig, SPath: RawByteString;
1280   bAppPortable: boolean;
1281 begin
1282   //from https://github.com/graemeg/freepascal/blob/master/rtl/unix/sysutils.pp
1283   AppDir_Home:= GetEnvironmentVariable('HOME');
1284   if AppDir_Home<>'' then
1285     AppDir_Home:= IncludeTrailingPathDelimiter(AppDir_Home);
1286 
1287   SPathOrig:= Which(OpFileExe);
1288   //MsgStdout('CudaText binary: '+SPathOrig);
1289   {$if FPC_FULLVERSION>30200}
1290   //FileGetSymLinkTarget was added in FPC 3.2
1291   if FileGetSymLinkTarget(SPathOrig, SPath) then
1292   begin
1293     //support relative target of symlink like '../dir/cudatext'
1294     if not SBeginsWith(SPath, '/') then
1295       SPath:= ExtractFilePath(SPathOrig)+SPath;
1296     MsgStdout('CudaText starts via symlink to: '+SPath);
1297     OpDirLocal:= ExtractFileDir(SPath);
1298   end
1299   else
1300   {$endif}
1301   begin
1302     bAppPortable:= DirectoryExists(OpDirExe+'/data/lexlib') and
1303       not SBeginsWith(OpDirExe, '/opt/');
1304     if not bAppPortable then
1305     begin
1306       HomeConfig:= GetEnvironmentVariable('XDG_CONFIG_HOME');
1307       if HomeConfig='' then
1308         HomeConfig:= AppDir_Home + '.config/'
1309       else
1310         HomeConfig:= IncludeTrailingPathDelimiter(HomeConfig);
1311 
1312       OpDirLocal:= HomeConfig+'cudatext';
1313       CreateDirUTF8(OpDirLocal);
1314       //MsgStdout('CudaText starts not portable: '+OpDirLocal);
1315     end;
1316   end;
1317 end;
1318 
1319 procedure InitDirs;
1320 var
1321   S: string;
1322 begin
1323   OpFileExe:= ParamStr(0);
1324   OpDirExe:= ExtractFileDir(OpFileExe);
1325   OpDirPrecopy:= GetDirPrecopy;
1326   OpDirLocal:= OpDirExe;
1327 
1328   {$ifdef windows}
1329   InitDirs_Windows;
1330   {$else}
1331     {$ifdef darwin}
1332     InitDirs_macOS;
1333     {$else}
1334       {$ifdef haiku}
1335       InitDirs_Haiku;
1336       {$else}
1337       InitDirs_UnixCommon;
1338       {$endif}
1339     {$endif}
1340   {$endif}
1341 
1342   //support command line key -s=folder
1343   AppDir_Settings:= AppDirSettingsFromCommandLine;
1344   if AppDir_Settings='' then
1345     AppDir_Settings:= OpDirLocal+DirectorySeparator+'settings';
1346 
1347   if not DirectoryExistsUTF8(AppDir_Settings) then
1348     if not CreateDirUTF8(AppDir_Settings) then
1349     begin
1350       MsgStdout(msgCannotCreateDir+' '+AppDir_Settings, true);
1351       Halt;
1352     end;
1353 
1354   AppDir_SettingsDefault:= OpDirLocal+DirectorySeparator+'settings_default';
1355 
1356   {$ifdef linux}
1357   if OpDirLocal<>OpDirExe then
1358     if IsDistroUpdateNeeded then
1359       if DirectoryExistsUTF8(OpDirPrecopy) then
1360       begin
1361         RunCommand('cp', ['-R', '-u', '-t',
1362           OpDirLocal,
1363           '/usr/share/cudatext/py',
1364           '/usr/share/cudatext/data',
1365           '/usr/share/cudatext/settings_default'
1366           ], S);
1367         //set permissions +w for dir+subdirs
1368         RunCommand('chmod', ['-R', '+w', OpDirLocal], S);
1369       end;
1370   {$endif}
1371   {$ifdef darwin}
1372   if IsDistroUpdateNeeded then
1373     if DirectoryExistsUTF8(OpDirPrecopy) then
1374       //see rsync help. need options:
1375       // -u (update)
1376       // -r (recursive)
1377       // -t (preserve times)
1378       RunCommand('rsync', ['-urt',
1379         OpDirPrecopy+'/',
1380         OpDirLocal
1381         ], S);
1382   {$endif}
1383   {$ifdef dragonfly}
1384     RunCommand('cp', ['-R',
1385         '/usr/local/share/cudatext/py',
1386         '/usr/local/share/cudatext/data',
1387         '/usr/local/share/cudatext/readme',
1388         '/usr/local/share/cudatext/settings_default',
1389         OpDirLocal
1390          ], S);
1391   {$endif}
1392 
1393   AppDir_Py:= OpDirLocal+DirectorySeparator+'py';
1394   AppDir_Data:= OpDirLocal+DirectorySeparator+'data';
1395   AppDir_Lexers:= AppDir_Data+DirectorySeparator+'lexlib';
1396   AppDir_LexersLite:= AppDir_Data+DirectorySeparator+'lexliblite';
1397   AppDir_DataThemes:= AppDir_Data+DirectorySeparator+'themes';
1398   AppDir_DataAutocomplete:= AppDir_Data+DirectorySeparator+'autocomplete';
1399   AppDir_DataAutocompleteSpec:= AppDir_Data+DirectorySeparator+'autocompletespec';
1400   AppDir_DataLang:= AppDir_Data+DirectorySeparator+'lang';
1401   AppDir_DataSidebarIcons:= AppDir_Data+DirectorySeparator+'sideicons';
1402   AppDir_DataCodetreeIcons:= AppDir_Data+DirectorySeparator+'codetreeicons';
1403   AppDir_DataToolbarIcons:= AppDir_Data+DirectorySeparator+'toolbaricons';
1404   AppFile_OptionsDefault:= AppDir_SettingsDefault+DirectorySeparator+'default.json';
1405   AppFile_OptionsUserInit:= AppDir_SettingsDefault+DirectorySeparator+'userinit.json';
1406   AppFile_OptionsUser:= AppDir_Settings+DirectorySeparator+'user.json';
1407   AppFile_History:= AppDir_Settings+DirectorySeparator+'history.json';
1408   AppFile_HistoryFiles:= AppDir_Settings+DirectorySeparator+'history files.json';
1409   AppFile_Hotkeys:= AppDir_Settings+DirectorySeparator+'keys.json';
1410   AppFile_PluginsIni:= AppDir_Settings+DirectorySeparator+'plugins.ini';
1411 
1412   if not FileExists(AppFile_OptionsUser)
1413     and FileExists(AppFile_OptionsUserInit) then
1414     CopyFile(AppFile_OptionsUserInit, AppFile_OptionsUser, [], false);
1415 end;
1416 
1417 const
1418   AppDefaultEdFont: string = '';
1419   AppDefaultEdFonts: array[0..2] of string =
1420     {$ifdef windows}
1421     ('Consolas', 'Courier New', 'Courier');
1422     {$else}
1423       {$ifdef darwin}
1424       ('Monaco', 'Liberation Mono', 'DejaVu Sans Mono');
1425       {$else}
1426         {$ifdef haiku}
1427         ('Noto', 'Monoid Nerd', '');
1428         {$else}
1429         ('DejaVu Sans Mono', 'Liberation Mono', 'Courier New');
1430         {$endif}
1431       {$endif}
1432     {$endif}
1433 
1434 function InitAppDefaultEdFont: string;
1435 var
1436   i: integer;
1437 begin
1438   for i:= 0 to High(AppDefaultEdFonts) do
1439   begin
1440     Result:= AppDefaultEdFonts[i];
1441     if Screen.Fonts.IndexOf(Result)>=0 then exit;
1442   end;
1443 end;
1444 
1445 procedure InitEditorOps(var Op: TEditorOps);
1446 begin
1447   if AppDefaultEdFont='' then
1448     AppDefaultEdFont:= InitAppDefaultEdFont;
1449 
1450   with Op do
1451   begin
1452     OpFontName:= AppDefaultEdFont;
1453     OpFontName_i:= '';
1454     OpFontName_b:= '';
1455     OpFontName_bi:= '';
1456 
1457     OpFontSize:= 9;
1458     OpFontSize_i:= OpFontSize;
1459     OpFontSize_b:= OpFontSize;
1460     OpFontSize_bi:= OpFontSize;
1461     OpFontSize_original:= OpFontSize;
1462     OpFontSize_original_i:= OpFontSize;
1463     OpFontSize_original_b:= OpFontSize;
1464     OpFontSize_original_bi:= OpFontSize;
1465 
1466     OpFontQuality:= fqDefault;
1467     OpFontLigatures:= true;
1468 
1469     OpScrollAnimationSteps:= cInitScrollAnimationSteps;
1470     OpScrollAnimationSleep:= cInitScrollAnimationSleep;
1471 
1472     OpScrollbarsNew:= true;
1473     OpSpacingY:= 1;
1474 
1475     OpTabSize:= 4;
1476     OpTabSpaces:= false;
1477     OpMaxLineLenToTokenize:= 4000;
1478     OpMaxLineLenForBracketFinder:= 1000;
1479 
1480     OpActiveBorderRaw:= 1;
1481     OpActiveBorderInControls:= true;
1482     OpActiveBorderInEditor:= false;
1483     OpActiveBorderWidth:= 1;
1484 
1485     OpOverwriteSel:= true;
1486     OpOverwriteOnPaste:= false;
1487     OpPasteWithEolAtLineStart:= false; //maybe change it later to True (like Sublime, VSCode)
1488 
1489     OpAutoCloseBracketsMultiCarets:= true; //must be True, issue #3235
1490     OpAutoCloseBrackets:= '([{';
1491     OpAutocompleteAutoshowCharCount:= 0;
1492     OpAutocompleteTriggerChars:= '';
1493     OpAutocompleteCommitChars:= ' ,;/\''"';
1494     OpAutocompleteCloseChars:= '<>()[]{}=';
1495     OpAutocompleteAddOpeningBracket:= true;
1496     OpAutocompleteUpDownAtEdge:= 1; //cudWrap
1497     OpAutocompleteCommitIfSingleItem:= false;
1498 
1499     OpUnderlineColorFiles:= '*';
1500     OpUnderlineColorSize:= 3;
1501     OpLinks:= true;
1502     OpLinksRegex:= ATSynEdit.cUrlRegexInitial;
1503 
1504     OpGutterShow:= true;
1505     OpGutterFold:= true;
1506     OpGutterFoldAlways:= true;
1507     OpGutterBookmarks:= true;
1508     OpGutterFoldIcons:= 0;
1509 
1510     OpNumbersShow:= true;
1511     OpNumbersStyle:= Ord(cNumbersAll);
1512     OpNumbersForCarets:= false;
1513     OpNumbersCenter:= true;
1514 
1515     OpRulerShow:= false;
1516     OpRulerNumeration:= 0;
1517     OpRulerMarkCaret:= 1;
1518 
1519     OpMinimapShow:= false;
1520     OpMinimapShowSelAlways:= false;
1521     OpMinimapShowSelBorder:= true;
1522     OpMinimapCharWidth:= 0;
1523     OpMinimapAtLeft:= false;
1524     OpMinimapScale:= 0;
1525     OpMinimapTooltipShow:= false;
1526     OpMinimapTooltipLineCount:= 6;
1527     OpMinimapTooltipWidth:= 60;
1528     OpMinimapDragImmediately:= false;
1529 
1530     OpMicromapShow:= false;
1531     OpMicromapOnScrollbar:= false;
1532     OpMicromapLineStates:= true;
1533     OpMicromapSelections:= true;
1534     OpMicromapBookmarks:= false;
1535     OpMicromapSmallMarkSizePercents:= 50;
1536     OpMicromapMinMarkHeight:= 4;
1537 
1538     OpMarginFixed:= 2000; //hide margin
1539     OpMarginString:= '';
1540 
1541     OpMarkerSize:= 30;
1542     OpStaplesStyle:= 1; //Ord(cLineStyleSolid)
1543     OpStaplesProps:= '-1,40,1,1';
1544     OpStapleIndentConsidersEnd:= true;
1545 
1546     OpUnprintedShow:= false;
1547     OpUnprintedContent:= 'se';
1548 
1549     OpWrapMode:= 0;
1550     OpWrapIndented:= true;
1551     OpWrapEnabledMaxLines:= 60*1000;
1552 
1553     OpUndoLimit:= cInitUndoLimit;
1554     OpUndoGrouped:= true;
1555     OpUndoAfterSave:= true;
1556     OpUndoMaxCarets:= cInitUndoMaxCarets;
1557     OpUndoIndentVert:= -5;
1558     OpUndoIndentHorz:= 10;
1559     OpUndoPause:= 300;
1560     OpUndoMouseClicks:= false;
1561 
1562     OpCaretBlinkTime:= cInitCaretBlinkTime;
1563     OpCaretBlinkEn:= true;
1564     OpCaretViewNormal:= '2,-100';
1565     OpCaretViewOverwrite:= '-100,-100';
1566     OpCaretViewReadonly:= '-100,2';
1567     OpCaretVirtual:= false;
1568     OpCaretMulti:= true;
1569     OpCaretAfterPasteColumn:= Ord(cPasteCaretColumnRight);
1570     OpCaretsAddedToColumnSel:= true;
1571     OpCaretKeepVisibleOnScroll:= true;
1572     OpCaretsPrimitiveColumnSel:= true;
1573     OpCaretProximityVert:= 0;
1574 
1575     OpKeepSelFontColor:= false;
1576     OpShowCurLine:= false;
1577     OpShowCurLineMinimal:= true;
1578     OpShowCurLineOnlyFocused:= false;
1579     OpShowCurCol:= false;
1580     OpShowLastLineOnTop:= true;
1581     OpShowFullBackgroundSel:= false;
1582     OpShowFullBackgroundSyntax:= true;
1583     OpShowMouseSelFrame:= true;
1584     OpCopyLineIfNoSel:= true;
1585     OpCutLineIfNoSel:= false;
1586     OpCopyColumnAlignedBySpaces:= true;
1587     OpSavingTrimSpaces:= false;
1588     OpSavingTrimFinalEmptyLines:= false;
1589     OpSavingForceFinalEol:= false;
1590     OpShowHintOnVertScroll:= false;
1591     OpSmoothScroll:= true;
1592     OpCenteringWidth:= 0;
1593     OpCenteringForDistractionFree:= 0;
1594     OpScrollStyleHorz:= 2; //hide, show, auto
1595     OpLexerDynamicHiliteEnabled:= false;
1596     OpLexerDynamicHiliteMaxLines:= 2000;
1597     OpLexerLineSeparators:= false;
1598     OpZebra:= 0;
1599     OpZebraStep:= 2;
1600     OpDimUnfocused:= 0;
1601     OpCommandLogMaxCount:= 200;
1602 
1603     OpNonWordChars:= cDefaultNonWordChars;
1604     OpFoldStyle:= 1;
1605     OpFoldTooltipShow:= false;
1606 
1607     OpIndentAuto:= true;
1608     OpIndentAutoKind:= Ord(cIndentAsPrevLine);
1609     OpIndentSize:= 0;
1610     OpIndentAutoRule:= '';
1611     OpUnIndentKeepsAlign:= false;
1612     OpIndentMakesWholeLineSel:= false;
1613 
1614     OpMouse2ClickDragSelectsWords:= true;
1615     OpMouseDragDrop:= true;
1616     OpMouseMiddleClickAction:= Ord(TATEditorMiddleClickAction.mcaScrolling);
1617     OpMouseRightClickMovesCaret:= false;
1618     OpMouseEnableColumnSelection:= true;
1619     OpMouseHideCursorOnType:= false;
1620     OpMouseGutterClickSelectedLine:= true;
1621     OpMouseWheelZoom:= true;
1622     OpMouseWheelSpeedVert:= 3;
1623     OpMouseWheelSpeedHorz:= 10;
1624     OpMouseClickNumberSelectsEol:= true;
1625     OpMouseClickLinks:= 2;
1626 
1627     OpKeyBackspaceUnindent:= true;
1628     OpKeyBackspaceWrap:= true;
1629     OpKeyTabIndents:= true;
1630     OpKeyHomeToNonSpace:= true;
1631     OpKeyHomeEndNavigateWrapped:= true;
1632     OpKeyEndToNonSpace:= true;
1633     OpKeyPageKeepsRelativePos:= true;
1634     OpKeyPageUpDownSize:= Ord(cPageSizeFullMinus1);
1635     OpKeyUpDownKeepColumn:= true;
1636     OpKeyUpDownNavigateWrapped:= true;
1637     OpKeyUpDownAllowToEdge:= false;
1638     OpKeyLeftRightGoToNextLineWithCarets:= false;
1639     OpKeyLeftRightSwapSel:= true;
1640     OpKeyLeftRightSwapSelAndSelect:= false;
1641 
1642     OpBracketHilite:= false;
1643     OpBracketSymbols:= '()[]{}';
1644     OpBracketDistance:= 150;
1645   end;
1646 end;
1647 
1648 
1649 function IsDoubleBufferedNeeded: boolean;
1650 begin
1651   {$ifdef linux}
1652   //Qt needs true (else caret dont blink, and tab angled borders paint bad)
1653   Exit(true);
1654   {$endif}
1655 
1656   Result:= WidgetSet.GetLCLCapability(lcCanDrawOutsideOnPaint) = LCL_CAPABILITY_YES;
1657 end;
1658 
1659 {$ifdef windows}
1660 procedure Win32GetUserFont(var AFontName: string; var AFontSize: Integer);
1661 // https://github.com/Alexey-T/CudaText/issues/3039
1662 var
1663   lf: LOGFONT;
1664 begin
1665   ZeroMemory(@lf, SizeOf(lf));
1666   if SystemParametersInfo(SPI_GETICONTITLELOGFONT, SizeOf(lf), @lf, 0) then
1667   begin
1668     AFontName := PChar(Addr(lf.lfFaceName[0]));
1669     AFontSize := MulDiv(-lf.lfHeight, 72, GetDeviceCaps(GetDC(0), LOGPIXELSY));
1670   end;
1671 end;
1672 {$endif}
1673 
1674 procedure InitUiOps(var Op: TUiOps);
1675 var
1676   element: TAppHistoryElement;
1677 begin
1678   with Op do
1679   begin
1680     VarFontName:= 'default';
1681     VarFontSize:= 9;
1682 
1683     OutputFontName:= VarFontName;
1684     OutputFontSize:= VarFontSize;
1685 
1686     DoubleBuffered:= IsDoubleBufferedNeeded;
1687 
1688     LexerThemes:= true;
1689     LexerMenuGrouped:= true;
1690     LexerPostponeUntilShown:= true;
1691     LexerParsingMinTimeForEvent:= 600;
1692 
1693     SidebarShow:= true;
1694     SidebarOnRight:= false;
1695     SidebarTheme:= 'common_20x20';
1696     SidepanelOnStart:= 0;
1697     BottomOnStart:= 0;
1698     TreeTheme:= 'default_16x16';
1699     ToolBarTheme:= 'default_24x24';
1700 
1701     LangName:= '';
1702     ThemeUi:= '';
1703     ThemeSyntax:= '';
1704     ThemeUi_Loaded:= false;
1705     ThemeSyntax_Loaded:= false;
1706 
1707     ThemedMainMenu:= true;
1708     ThemedMainMenuFontName:= 'default';
1709     ThemedMainMenuFontSize:= 9;
1710     {$ifdef windows}
1711     if Win32MajorVersion<6 then //Win XP
1712       ThemedMainMenuFontSize:= 8;
1713     Win32GetUserFont(ThemedMainMenuFontName, ThemedMainMenuFontSize);
1714     {$endif}
1715 
1716     AutocompleteHtml:= true;
1717     AutocompleteHtml_AutoClose:= true;
1718     AutocompleteHtml_Lexers:= '.*HTML.*|\bPHP\b';
1719     AutocompleteCss:= true;
1720     AutocompleteCss_Lexers:= 'CSS';
1721     AutocompleteFileURI:= true;
1722     AutocompleteInComments:= false;
1723     AutocompleteInCommentsHTML:= true;
1724     AutocompleteInStrings:= true;
1725 
1726     HtmlBackgroundColorPair[false]:= $F0F0F0;
1727     HtmlBackgroundColorPair[true]:= $101010;
1728 
1729     PyLibrary:= InitPyLibraryPath;
1730     PictureTypes:= 'bmp,png,jpg,jpeg,gif,ico';
1731 
1732     DefaultTabSplitIsHorz:= false;
1733     MaxFileSizeToOpen:= 500;
1734     MaxFileSizeForLexer:= 2;
1735     MaxStatusbarMessages:= 35; //Linux gtk2 shows maximal ~38 lines in tooltip
1736 
1737     ListboxCentered:= true;
1738     ListboxSizeX:= 450;
1739     ListboxSizeY:= 300;
1740     ListboxCompleteSizeX:= 550;
1741     ListboxCompleteSizeY:= 200;
1742     ListboxFuzzySearch:= true;
1743     ListboxHotkeyFontSizeDelta:= 0; //2 gives too small hotkey font on Lin/Win
1744 
1745     TabsShowFoldersSuffix:= true;
1746     TabsShowFoldersMaxLevels:= 3;
1747     TabsResetUntitledCounter:= true;
1748     TabsDisabled:= false;
1749     TabVarWidth:= false;
1750     TabMultiline:= false;
1751     TabAngled:= false;
1752     TabFlat:= false;
1753     TabWidth:= 170;
1754     TabWidthMin:= 40;
1755     TabWidthMax:= 300;
1756     TabHeight:= 26;
1757     TabHeightInner:= TabHeight-1;
1758     TabSpacer:= 2;
1759     TabSpaceBeforeText:= 6;
1760     TabPosition:= 0;
1761     TabColorFull:= false;
1762     TabFontScale:= 100;
1763     TabShowX:= 1; //show all
1764     TabShowXSize:= 14;
1765     TabShowXRounded:= true;
1766     TabShowPlus:= true;
1767     TabDblClickClose:= false;
1768     TabNumbers:= false;
1769     TabNewNearCurrent:= false;
1770     TabRecentOnClose:= false;
1771     TabButtonLayout:= '<>,v';
1772     TabPreviewFontStyle:= 'iu';
1773     TabSwitcherDialog:= true;
1774 
1775     MaxHistoryEdits:= 20;
1776     MaxHistoryMenu:= 10;
1777     MaxHistoryFiles:= 25;
1778 
1779     CmdPaletteFilterKeep:= false;
1780     CmdPaletteFilterText:= '';
1781 
1782     HistoryDisabledStr:= '';
1783     for element:= Low(element) to High(element) do
1784       HistoryItems[element]:= true;
1785 
1786     FindSuggestSel:= true;
1787     FindSuggestWord:= false;
1788     FindSuggestInSelection:= false;
1789     FindCurrentWordCaseSensitive:= ufcsCaseFromDialog;
1790     FindShowNoResultsByInputBgColor:= true;
1791 
1792     FindHiddenButtons:= '';
1793     FindShow_FindFirst:= true;
1794     FindShow_FindNext:= true;
1795     FindShow_FindPrev:= true;
1796     FindShow_ReplaceAll:= true;
1797     FindShow_ReplaceGlobal:= true;
1798     FindShow_RegEx:= true;
1799     FindShow_CaseSens:= true;
1800     FindShow_WholeWords:= true;
1801     FindShow_Wrapped:= true;
1802     FindShow_InSel:= true;
1803     FindShow_MultiLine:= true;
1804     FindShow_SyntaxElements:= true;
1805     FindShow_HiAll:= true;
1806     FindShow_ConfirmRep:= true;
1807     FindShow_RegexSubst:= true;
1808 
1809     FindIndentVert:= -5;
1810     FindIndentHorz:= 10;
1811     FindMultiLineScale:= 2.5;
1812     FindSeparateForm:= false;
1813     FindHiAll_MaxLines:= 1000;
1814     FindHiAll_TagValue:= 99; //GET_UNIQUE_TAG starts with 120
1815     FindOccur_TagValue:= 98;
1816 
1817     AllowProgramUpdates:= true;
1818     EscapeClose:= false;
1819     EscapeCloseConsole:= true;
1820     ConsoleWordWrap:= true;
1821     InputHeight:= 26;
1822     InitialDir:= '';
1823     ConfirmLinksClicks:= true;
1824     ConfirmSaveEmptyUntitledTab:= false;
1825 
1826     ExportHtmlNumbers:= false;
1827     ExportHtmlFontSize:= 12;
1828     ExportHtmlFontName:= 'Courier New';
1829 
1830     TreeAutoSync:= true;
1831     TreeTimeFill:= 2000;
1832     //TreeTimeCaret:= 300;
1833     TreeShowIcons:= true;
1834     TreeShowTooltips:= {$ifdef LCLQt5} false; {$else} true; {$endif} //solve issue #3642
1835     TreeFilterLayout:= 1;
1836     TreeSublexers:= false;
1837     TreeIconFilenames:= 'dir,st1,st2,st3,box,fx,ar1,ar2,';
1838 
1839     PyChangeSlow:= 2000;
1840     InfoAboutOptionsEditor:= true;
1841     AllowFrameParsing:= false;
1842     AllowRunPkExec:= true;
1843 
1844     LogPluginIniting:= true;
1845     LogSessions:= true;
1846     LogDebug:= false;
1847     LogConsole:= false;
1848     LogConsoleDetailedStartupTime:= false; //true;
1849 
1850     NewdocLexer:= '';
1851     NewdocEnc:= 'utf8';
1852     NewdocEnds:= 0;
1853 
1854     DefaultEncUtf8:= {$ifdef windows} false {$else} true {$endif};
1855     ViewerBinaryWidth:= 100;
1856     ViewerNonPrintable:= false;
1857 
1858     StatusNoSel:= '{_ln} {y}, {_col} {xx}';
1859     StatusSmallSel:= '{_ln} {y}, {_col} {xx}, {_sel}';
1860     StatusStreamSel:= '{_ln} {y}, {_col} {xx}, {sel} {_linesel}';
1861     StatusColSel:= '{sel}x{cols} {_sel}';
1862     StatusCarets:= '{carets} {_carets}, {sel} {_linesel}';
1863 
1864     StatusPanels:= 'caret,C,180|enc,C,125|ends,A,45|lexer,C,140|tabsize,A,75|selmode,A,15|msg,L,4000';
1865     StatusTime:= 5;
1866     StatusHeightPercents:= 180;
1867     StatusHeightMin:= 20;
1868 
1869     AltTooltipTime:= 9;
1870     AltTooltipTimeMax:= 60;
1871     AltTooltipPaddingX:= 6;
1872     AltTooltipPaddingY:= 3;
1873 
1874     ScrollbarWidth:= 14;
1875     ScrollbarBorderSize:= 0;
1876     ScrollbarArrowSize:= 3;
1877 
1878     ProgressbarWidth:= 50;
1879     ProgressbarHeightSmall:= 6;
1880 
1881     ShowMenubar:= true;
1882     ShowStatusbar:= true;
1883     ShowToolbar:= false;
1884     ShowTitlePath:= false;
1885 
1886     Scale:= 100;
1887     ScaleFont:= 100;
1888 
1889     ReopenSession:= true;
1890     ReopenSessionWithCmdLine:= false;
1891     SessionSaveInterval:= 30;
1892     SessionSaveOnExit:= false;
1893     BackupLastSessions:= 0;
1894     SaveModifiedTabsOnClose:= true;
1895 
1896     ShowFormsOnTop:= false;
1897     ShowMenuDialogsWithBorder:= {$ifdef LCLGTK2} true {$else} false {$endif};
1898     UndoPersistent:= '';
1899     AllowSaveOfUnmodifiedFile:= true;
1900 
1901     PluginDialogsShowInTaskbar:= {$ifdef windows} false {$else} true {$endif}; //to fix issue #3078 on Linux
1902     PluginDialogsModalFormStyle:= {$ifdef LCLQT5} fsNormal {$else} fsStayOnTop {$endif};
1903     FloatGroupsShowInTaskbar:= stAlways;
1904     OneInstance:= true;
1905     NotificationEnabled:= true;
1906     NotificationTimeSeconds:= 2;
1907     NotificationConfirmReload:= 1;
1908     MarkFilesDeletedOutsideAsModified:= true;
1909     NonTextFiles:= 0;
1910     NonTextFilesBufferKb:= 64;
1911     ReloadFollowTail:= true;
1912     ReloadUnsavedConfirm:= true;
1913     CheckLowDiskSpace:= 1*1024*1024;
1914     FullScreen:= 'tp';
1915     MouseGotoDefinition:= 'a';
1916 
1917     Emmet_AddSlashToEmptyTags:= true;
1918     Emmet_CommentTags:= false;
1919     Emmet_IndentNested:= true;
1920     Emmet_SingleLine:= false;
1921     Emmet_TrimLineMarkers:= true;
1922     Emmet_WordWrap:= false;
1923 
1924     HotkeyFindDialog:= 'Ctrl+F';
1925     HotkeyReplaceDialog:= 'Ctrl+R';
1926     HotkeyFindFirst:= 'Alt+Enter';
1927     HotkeyFindNext:= '';
1928     HotkeyFindPrev:= 'Shift+Enter';
1929     HotkeyReplaceAndFindNext:= 'Alt+Z';
1930     HotkeyReplaceNoFindNext:= 'Ctrl+Alt+Z';
1931     HotkeyReplaceAll:= 'Alt+A';
1932     HotkeyReplaceGlobal:= '';
1933     HotkeyCountAll:= 'Alt+O';
1934     HotkeyExtractAll:= 'Alt+Q';
1935     HotkeySelectAll:= 'Alt+E';
1936     HotkeyMarkAll:= 'Alt+K';
1937     HotkeyToggleRegex:= 'Alt+R';
1938     HotkeyToggleCaseSens:= 'Alt+C';
1939     HotkeyToggleWords:= 'Alt+W';
1940     HotkeyToggleWrapped:= 'Alt+N';
1941     HotkeyToggleInSelect:= 'Alt+X';
1942     HotkeyToggleMultiline:= 'Alt+M';
1943     HotkeyToggleConfirmRep:= 'Alt+Y';
1944     HotkeyToggleTokens:= '';
1945     HotkeyToggleHiAll:= '';
1946   end;
1947 end;
1948 
1949 
1950 procedure SReplaceSpecialFilenameChars(var S: string);
1951 begin
1952   S:= StringReplace(S, '/', '_', [rfReplaceAll]);
1953   S:= StringReplace(S, '\', '_', [rfReplaceAll]);
1954   S:= StringReplace(S, '*', '_', [rfReplaceAll]);
1955   S:= StringReplace(S, ':', '_', [rfReplaceAll]);
1956   S:= StringReplace(S, '<', '_', [rfReplaceAll]);
1957   S:= StringReplace(S, '>', '_', [rfReplaceAll]);
1958 end;
1959 
1960 function GetAppLexerSpecificConfig(ALexer: string; ADefaultConfig: boolean=false): string;
1961 var
1962   dir: string;
1963 begin
1964   //support none-lexer here
1965   if ALexer='' then
1966     ALexer:= '-';
1967   SReplaceSpecialFilenameChars(ALexer);
1968 
1969   if ADefaultConfig then
1970     dir:= AppDir_SettingsDefault
1971   else
1972     dir:= AppDir_Settings;
1973 
1974   Result:= dir+DirectorySeparator+'lexer '+ALexer+'.json';
1975 end;
1976 
1977 function GetAppFilenameIsIgnoredForSession(const AFilename: string): boolean;
1978 var
1979   SName: string;
1980 begin
1981   SName:= ExtractFileName(AFilename);
1982   Result:= SameFileName(ExtractFileDir(AFilename), AppDir_Settings) and
1983     ( SameFileName(SName, 'history.json') or
1984       SameFileName(SName, 'history session.json') );
1985 end;
1986 
1987 function AppFile_HotkeysForLexer(AName: string): string;
1988 begin
1989   //support none-lexer
1990   if AName='' then
1991     AName:= '-';
1992   SReplaceSpecialFilenameChars(AName);
1993   Result:= AppDir_Settings+DirectorySeparator+'keys lexer '+AName+'.json';
1994 end;
1995 
1996 
1997 class function TPluginHelper.HotkeyStringId_To_CommandCode(const AId: string): integer;
1998 begin
1999   //plugin item 'module,method'
2000   if Pos(',', AId)>0 then
2001   begin
2002     Result:= CommandGetIndexFromModuleAndMethod(AId);
2003     if Result>=0 then
2004       Inc(Result, cmdFirstPluginCommand);
2005   end
2006   else
2007     //usual item
2008     Result:= StrToIntDef(AId, -1);
2009 end;
2010 
2011 class function TPluginHelper.Debug_PluginCommands(const AModule: string): string;
2012 var
2013   CmdItem: TAppCommandInfo;
2014   i: integer;
2015 begin
2016   Result:= '';
2017   for i:= 0 to AppCommandList.Count-1 do
2018   begin
2019     CmdItem:= TAppCommandInfo(AppCommandList[i]);
2020     if CmdItem.ItemModule=AModule then
2021       Result+= CmdItem.CommaStr+#10;
2022   end;
2023 end;
2024 
2025 
2026 function DoReadOneStringFromFile(const AFilename: string): string;
2027 var
2028   f: TextFile;
2029 begin
2030   Result:= '';
2031   Assign(f, AFilename);
2032   Reset(f);
2033   if IOResult=0 then
2034   begin
2035     if not Eof(f) then
2036       Readln(f, Result);
2037     CloseFile(f);
2038   end;
2039 end;
2040 
2041 function DoReadContentFromFile(const AFilename: string): string;
2042 var
2043   L: TStringList;
2044 begin
2045   Result:= '';
2046   if not FileExists(AFilename) then exit;
2047 
2048   L:= TStringList.Create;
2049   try
2050     L.LoadFromFile(AFilename);
2051     L.TextLineBreakStyle:= tlbsLF;
2052     if L.Count>0 then
2053       Result:= L.Text;
2054   finally
2055     FreeAndNil(L);
2056   end;
2057 end;
2058 
2059 procedure DoWriteStringToFile(const AFilename, AText: string);
2060 var
2061   f: TextFile;
2062 begin
2063   Assign(f, AFilename);
2064   Rewrite(f);
2065   if IOResult=0 then
2066   begin
2067     Write(f, AText);
2068     CloseFile(f);
2069   end;
2070 end;
2071 
2072 
2073 function DoLexerDetectByFilenameOrContent(const AFilename: string;
2074   AChooseFunc: TecLexerChooseFunc): TecSyntAnalyzer;
2075 const
2076   cSignUTF8: string = #$EF#$BB#$BF;
2077 var
2078   SNameOnly: string;
2079   Item: TAppKeyValue;
2080   ext, sLine, res: string;
2081   i: integer;
2082 begin
2083   SNameOnly:= ExtractFileName(AFilename);
2084 
2085   //detect by filename
2086   res:= AppConfig_Detect.GetValue(SNameOnly, '');
2087   if res<>'' then
2088     exit(AppManager.FindLexerByName(res));
2089 
2090   //detect by double extention
2091   if SFindCharCount(SNameOnly, '.')>1 then
2092   begin
2093     i:= RPos('.', SNameOnly);
2094     i:= RPosEx('.', SNameOnly, i-1);
2095     ext:= Copy(SNameOnly, i, MaxInt);
2096     if ext<>'' then
2097     begin
2098       res:= AppConfig_Detect.GetValue('*'+ext, '');
2099       if res<>'' then
2100         exit(AppManager.FindLexerByName(res));
2101     end;
2102   end;
2103 
2104   //detect by usual extention
2105   ext:= ExtractFileExt(SNameOnly);
2106   if ext<>'' then
2107   begin
2108     res:= AppConfig_Detect.GetValue('*'+ext, '');
2109     if res<>'' then
2110       exit(AppManager.FindLexerByName(res));
2111   end;
2112 
2113   //detect by first line
2114   if AppConfig_DetectLine.Count>0 then
2115   begin
2116     sLine:= DoReadOneStringFromFile(AFilename);
2117     if sLine<>'' then
2118     begin
2119       //skip UTF8 signature, needed for XMLs
2120       if SBeginsWith(sLine, cSignUTF8) then
2121         Delete(sLine, 1, Length(cSignUTF8));
2122       for i:= 0 to AppConfig_DetectLine.Count-1 do
2123       begin
2124         Item:= TAppKeyValue(AppConfig_DetectLine[i]);
2125         if SRegexMatchesString(sLine, Item.Key, true) then
2126           exit(AppManager.FindLexerByName(Item.Value));
2127       end;
2128     end;
2129   end;
2130 
2131   Result:= AppManager.FindLexerByFilename(AFilename, AChooseFunc);
2132 end;
2133 
2134 class function TPluginHelper.CommandCode_To_HotkeyStringId(ACmd: integer): string;
2135 begin
2136   if CommandCategory(ACmd) in [categ_Plugin, categ_PluginSub] then
2137     Result:= TAppCommandInfo(AppCommandList[ACmd-cmdFirstPluginCommand]).CommaStr
2138   else
2139     Result:= IntToStr(ACmd);
2140 end;
2141 
2142 class procedure TKeymapHelper.ItemSaveToConfig(K: TATKeymapItem; const path, ALexerName: string;
2143   ALexerSpecific: boolean);
2144 var
2145   SFilename: string;
2146   c: TJSONConfig;
2147   sl: TStringList;
2148   i: integer;
2149 begin
2150   if ALexerSpecific then
2151     SFilename:= AppFile_HotkeysForLexer(ALexerName)
2152   else
2153     SFilename:= AppFile_Hotkeys;
2154 
2155   c:= TJSONConfig.Create(nil);
2156   sl:= TStringlist.create;
2157   try
2158     try
2159       c.Formatted:= true;
2160       c.Filename:= SFilename;
2161     except
2162       exit;
2163     end;
2164 
2165     c.SetValue(path+'/name', K.Name);
2166 
2167     sl.Clear;
2168     for i:= 0 to High(TATKeyArray.Data) do
2169       if K.Keys1.Data[i]<>0 then
2170         sl.Add(ShortCutToText(K.Keys1.Data[i]));
2171     c.SetValue(path+'/s1', sl);
2172 
2173     sl.clear;
2174     for i:= 0 to High(TATKeyArray.Data) do
2175       if K.Keys2.Data[i]<>0 then
2176         sl.Add(ShortCutToText(K.Keys2.Data[i]));
2177 
2178     if sl.Count>0 then
2179       c.SetValue(path+'/s2', sl)
2180     else
2181       c.DeleteValue(path+'/s2')
2182   finally
2183     c.Free;
2184     sl.Free;
2185   end;
2186 end;
2187 
2188 
2189 class procedure TKeymapHelper.ItemDeleteInConfig(K: TATKeymapItem; const path, ALexerName: string;
2190   ALexerSpecific: boolean);
2191 var
2192   SFilename: string;
2193   c: TJSONConfig;
2194 begin
2195   if ALexerSpecific then
2196     SFilename:= AppFile_HotkeysForLexer(ALexerName)
2197   else
2198     SFilename:= AppFile_Hotkeys;
2199 
2200   c:= TJSONConfig.Create(nil);
2201   try
2202     try
2203       c.Formatted:= true;
2204       c.Filename:= SFilename;
2205     except
2206       exit;
2207     end;
2208 
2209     if c.GetValue(path+'/name', '')<>'' then
2210       c.DeletePath(path);
2211   finally
2212     c.Free;
2213   end;
2214 end;
2215 
2216 
2217 class function TKeymapHelper.SaveKey_ForPlugin(AKeymap: TATKeymap;
2218   AOverwriteKey: boolean;
2219   const AMenuitemCaption, AModuleName, AMethodName, ALexerName, AHotkey: string): boolean;
2220 const
2221   cKeyComboSeparator = '|';
2222 var
2223   SFilename: string;
2224   c: TJSONConfig;
2225   sl: TStringList;
2226   path, s_item: string;
2227   Sep: TATStringSeparator;
2228 begin
2229   Result:= false;
2230 
2231   if ALexerName<>'' then
2232     SFilename:= AppFile_HotkeysForLexer(ALexerName)
2233   else
2234     SFilename:= AppFile_Hotkeys;
2235 
2236   c:= TJSONConfig.Create(nil);
2237   sl:= TStringlist.create;
2238   try
2239     try
2240       c.Formatted:= true;
2241       c.Filename:= SFilename;
2242     except
2243       exit;
2244     end;
2245 
2246     path:= AModuleName+','+AMethodName;
2247 
2248     //check: this command has already any hotkey?
2249     if not AOverwriteKey then
2250       if c.GetValue(path+'/s1', sl, '') then exit;
2251 
2252     c.SetValue(path+'/name', AMenuitemCaption);
2253 
2254     sl.Clear;
2255     Sep.Init(AHotkey, cKeyComboSeparator);
2256     while Sep.GetItemStr(s_item) do
2257       sl.Add(s_item);
2258 
2259     c.SetValue(path+'/s1', sl);
2260     Result:= true;
2261   finally
2262     c.Free;
2263     sl.Free;
2264   end;
2265 end;
2266 
2267 function AppListboxItemHeight(AScale, ADoubleHeight: boolean): integer;
2268 begin
2269   Result:= UiOps.VarFontSize * 18 div 10 +2;
2270 
2271   {$ifdef windows}
2272   Result:= Result * Screen.PixelsPerInch div 96;
2273   {$endif}
2274 
2275   if ADoubleHeight then
2276     Result:= Result * 185 div 100;
2277   if AScale then
2278     Result:= AppScaleFont(Result);
2279 end;
2280 
2281 
2282 procedure DoLexerEnum(L: TStringList; AlsoDisabled: boolean = false);
2283 var
2284   an: TecSyntAnalyzer;
2285   i: integer;
2286 begin
2287   with AppManager do
2288     for i:= 0 to LexerCount-1 do
2289     begin
2290       an:= Lexers[i];
2291       if an.Deleted then Continue;
2292       if an.Internal and not AlsoDisabled then Continue;
2293       L.Add(an.LexerName);
2294     end;
2295 
2296   with AppManagerLite do
2297     for i:= 0 to LexerCount-1 do
2298       L.Add(Lexers[i].LexerName+msgLiteLexerSuffix);
2299 end;
2300 
2301 {
2302 procedure DoLexerSave(an: TecSyntAnalyzer);
2303 begin
2304   if Assigned(an) then
2305     an.SaveToFile(GetAppLexerFilename(an.LexerName));
2306 end;
2307 }
2308 
2309 class function TPluginHelper.CommandGetIndexFromModuleAndMethod(const AText: string): integer;
2310 var
2311   Sep: TATStringSeparator;
2312   SModule, SProc, SProcParam: string;
2313   AppCmd: TAppCommandInfo;
2314   i: integer;
2315 begin
2316   Result:= -1;
2317 
2318   Sep.Init(AText);
2319   Sep.GetItemStr(SModule);
2320   Sep.GetItemStr(SProc);
2321   Sep.GetItemStr(SProcParam);
2322 
2323   if SModule='' then exit;
2324   if SProc='' then exit;
2325 
2326   for i:= 0 to AppCommandList.Count-1 do
2327   begin
2328     AppCmd:= TAppCommandInfo(AppCommandList[i]);
2329     if (AppCmd.ItemModule=SModule) and
2330       (AppCmd.ItemProc=SProc) and
2331       (AppCmd.ItemProcParam=SProcParam) then
2332       exit(i);
2333   end;
2334 end;
2335 
2336 
2337 class procedure TPluginHelper.CommandUpdateSubcommands(const AText: string);
2338 const
2339   cSepRoot=';';
2340   cSepParams=#10;
2341   cSepNameParam=#9;
2342 var
2343   Sep: TATStringSeparator;
2344   SModule, SProc, SParams, SItem, SItemParam, SItemCaption: string;
2345   CmdItem: TAppCommandInfo;
2346   N: integer;
2347 begin
2348   Sep.Init(AText, cSepRoot);
2349   Sep.GetItemStr(SModule);
2350   Sep.GetItemStr(SProc);
2351   Sep.GetRest(SParams);
2352 
2353   //del items for module/method
2354   for N:= AppCommandList.Count-1 downto 0 do
2355     with TAppCommandInfo(AppCommandList[N]) do
2356       if (ItemModule=SModule) and (ItemProc=SProc) and (ItemProcParam<>'') then
2357         AppCommandList.Delete(N);
2358 
2359   //add items for SParams
2360   Sep.Init(SParams, cSepParams);
2361   repeat
2362     if not Sep.GetItemStr(SItem) then Break;
2363     SSplitByChar(SItem, cSepNameParam, SItemCaption, SItemParam);
2364 
2365     CmdItem:= TAppCommandInfo.Create;
2366     CmdItem.ItemModule:= SModule;
2367     CmdItem.ItemProc:= SProc;
2368     CmdItem.ItemProcParam:= SItemParam;
2369     CmdItem.ItemCaption:= SItemCaption;
2370     CmdItem.ItemFromApi:= true;
2371 
2372     AppCommandList.Add(CmdItem);
2373   until false;
2374 end;
2375 
2376 
2377 function GetAppLangFilename: string;
2378 begin
2379   if UiOps.LangName='' then
2380     Result:= ''
2381   else
2382     Result:= AppDir_DataLang+DirectorySeparator+UiOps.LangName+'.ini';
2383 end;
2384 
2385 function EscapeLexerFilename(const ALexName: string): string;
2386 begin
2387   Result:= ALexName;
2388   if Result<>'' then
2389   begin
2390     Result:= StringReplace(Result, ':', '_', [rfReplaceAll]);
2391     Result:= StringReplace(Result, '/', '_', [rfReplaceAll]);
2392     Result:= StringReplace(Result, '\', '_', [rfReplaceAll]);
2393     Result:= StringReplace(Result, '*', '_', [rfReplaceAll]);
2394   end;
2395 end;
2396 
2397 function GetLexerFilenameWithExt(ALexName, AExt: string): string;
2398 begin
2399   if ALexName<>'' then
2400     Result:= AppDir_Lexers+DirectorySeparator+EscapeLexerFilename(ALexName)+AExt
2401   else
2402     Result:= '';
2403 end;
2404 
2405 function GetAppLexerMapFilename(const ALexName: string): string;
2406 begin
2407   Result:= GetLexerFilenameWithExt(ALexName, '.cuda-lexmap');
2408 end;
2409 
2410 function GetAppLexerFilename(const ALexName: string): string;
2411 begin
2412   Result:= GetLexerFilenameWithExt(ALexName, '.lcf');
2413 end;
2414 
2415 function GetAppLexerOpsFilename(const ALexName: string): string;
2416 begin
2417   Result:= AppDir_Settings+DirectorySeparator+EscapeLexerFilename(ALexName)+'.cuda-lexops';
2418 end;
2419 
2420 function GetAppLexerAcpFilename(const ALexName: string): string;
2421 begin
2422   Result:= AppDir_DataAutocomplete+DirectorySeparator+EscapeLexerFilename(ALexName)+'.acp';
2423 end;
2424 
2425 function GetAppUndoFilename(const fn: string; IsRedo: boolean): string;
2426 const
2427   Ext: array[boolean] of string = ('.undx', '.redx');
2428 begin
2429   Result:= ExtractFileDir(fn)+DirectorySeparator+
2430     '.cudatext'+DirectorySeparator+
2431     ExtractFileName(fn)+Ext[IsRedo];
2432 end;
2433 
2434 class function TKeymapHelper.GetHotkey(AKeymap: TATKeymap; const ACmdString: string): string;
2435 var
2436   NCode, NIndex: integer;
2437 begin
2438   Result:= '';
2439   if Pos(',', ACmdString)=0 then
2440     NCode:= StrToIntDef(ACmdString, 0)
2441   else
2442   begin
2443     NIndex:= TPluginHelper.CommandGetIndexFromModuleAndMethod(ACmdString);
2444     if NIndex<0 then exit;
2445     NCode:= NIndex+cmdFirstPluginCommand;
2446   end;
2447 
2448   NIndex:= AKeymap.IndexOf(NCode);
2449   if NIndex<0 then exit;
2450   with AKeymap[NIndex] do
2451     Result:= Keys1.ToString+'|'+Keys2.ToString;
2452 end;
2453 
2454 
2455 class function TKeymapHelper.SetHotkey(AKeymap: TATKeymap; const AParams: string; AndSaveFile: boolean): boolean;
2456 var
2457   Sep: TATStringSeparator;
2458   NCode, NIndex: integer;
2459   SCmd, SKey1, SKey2: string;
2460 begin
2461   Result:= false;
2462 
2463   Sep.Init(AParams, '|');
2464   Sep.GetItemStr(SCmd);
2465   Sep.GetItemStr(SKey1);
2466   Sep.GetItemStr(SKey2);
2467 
2468   if Pos(',', SCmd)=0 then
2469     NCode:= StrToIntDef(SCmd, 0)
2470   else
2471   begin
2472     NIndex:= TPluginHelper.CommandGetIndexFromModuleAndMethod(SCmd);
2473     if NIndex<0 then exit;
2474     NCode:= NIndex+cmdFirstPluginCommand;
2475   end;
2476 
2477   NIndex:= AKeymap.IndexOf(NCode);
2478   if NIndex<0 then exit;
2479   with AKeymap[NIndex] do
2480   begin
2481     Keys1.SetFromString(SKey1);
2482     Keys2.SetFromString(SKey2);
2483 
2484     //save to keys.json
2485     //Py API: no need lexer-specific
2486     if AndSaveFile then
2487       ItemSaveToConfig(AKeymap[NIndex], SCmd, '', false);
2488   end;
2489   Result:= true;
2490 end;
2491 
2492 
2493 class procedure TKeymapHelper.ClearKey(AKeymap: TATKeymap; AItemIndex, AKeyIndex: integer);
2494 begin
2495   with AKeymap do
2496     if IsIndexValid(AItemIndex) then
2497       with Items[AItemIndex] do
2498         if AKeyIndex=0 then
2499           Keys1.Clear
2500         else
2501           Keys2.Clear;
2502 end;
2503 
2504 class procedure TKeymapHelper.ClearKeyInAll(AItemIndex, AKeyIndex: integer);
2505 var
2506   iMap: integer;
2507 begin
2508   ClearKey(AppKeymapMain, AItemIndex, AKeyIndex);
2509 
2510   for iMap:= 0 to AppKeymapLexers.Count-1 do
2511     ClearKey(
2512       TATKeymap(AppKeymapLexers.Objects[iMap]),
2513       AItemIndex, AKeyIndex);
2514 end;
2515 
2516 class function TKeymapHelper.CheckDuplicateForCommand(
2517   AKeymapItem: TATKeymapItem;
2518   const ALexerName: string;
2519   AOverwriteAndSave: boolean): integer;
2520 var
2521   Map: TATKeymap;
2522   MapItem: TATKeymapItem;
2523   ShortKeys1: TATKeyArray;
2524   ShortKeys2: TATKeyArray;
2525   StrId: string;
2526   NKeyIndex: integer;
2527   iCmd: integer;
2528 begin
2529   Result:= 0;
2530 
2531   Map:= AppKeymapMain;
2532   NKeyIndex:= 0;
2533 
2534   ShortKeys1.Clear;
2535   ShortKeys2.Clear;
2536   if AKeymapItem.Keys1.Length>1 then
2537     ShortKeys1:= AKeymapItem.ShortenedKeys1;
2538   if AKeymapItem.Keys2.Length>1 then
2539     ShortKeys2:= AKeymapItem.ShortenedKeys2;
2540 
2541   for iCmd:= 0 to Map.Count-1 do
2542   begin
2543     MapItem:= Map.Items[iCmd];
2544     if MapItem.Command=AKeymapItem.Command then Continue;
2545 
2546     if (AKeymapItem.Keys1.IsConflictWith(MapItem.Keys1)) or
2547        (AKeymapItem.Keys2.IsConflictWith(MapItem.Keys1)) then
2548        NKeyIndex:= 0
2549     else
2550     if (AKeymapItem.Keys1.IsConflictWith(MapItem.Keys2)) or
2551        (AKeymapItem.Keys2.IsConflictWith(MapItem.Keys2)) then
2552        NKeyIndex:= 1
2553     else
2554       Continue;
2555 
2556     if AOverwriteAndSave then
2557     begin
2558       //clear item in ALL existing keymaps
2559       ClearKeyInAll(iCmd, NKeyIndex);
2560 
2561       StrId:= TPluginHelper.CommandCode_To_HotkeyStringId(MapItem.Command);
2562 
2563       //save to "keys.json"
2564       ItemSaveToConfig(MapItem, StrId, '', false);
2565 
2566       //save to "keys nn.json"
2567       if ALexerName<>'' then
2568         ItemSaveToConfig(MapItem, StrId, ALexerName, true);
2569     end
2570     else
2571       Result:= MapItem.Command;
2572 
2573     Break;
2574   end;
2575 end;
2576 
2577 class procedure TKeymapHelper.LoadConfig(AKeymap: TATKeymap; const AFileName: string; AForLexer: boolean);
2578 var
2579   cfg: TJSONConfig;
2580   slist, skeys: TStringList;
2581   //
2582   procedure DoReadConfigToKeys(const path: string; var keys: TATKeyArray);
2583   var
2584     j: integer;
2585   begin
2586     FillChar(keys, SizeOf(keys), 0);
2587     cfg.GetValue(path, skeys, '');
2588     for j:= 0 to skeys.count-1 do
2589       if skeys[j]<>'' then
2590         keys.Data[j]:= TextToShortCut(skeys[j]);
2591   end;
2592   //
2593 var
2594   StrId: string;
2595   ncmd, nitem, i: integer;
2596 begin
2597   cfg:= TJSONConfig.Create(nil);
2598   slist:= TStringList.Create;
2599   skeys:= TStringList.Create;
2600 
2601   try
2602     try
2603       cfg.Formatted:= true;
2604       cfg.Filename:= AFileName;
2605     except
2606       exit;
2607     end;
2608 
2609     cfg.EnumSubKeys('/', slist);
2610     for i:= 0 to slist.count-1 do
2611     begin
2612       StrId:= slist[i];
2613       ncmd:= TPluginHelper.HotkeyStringId_To_CommandCode(StrId);
2614       if ncmd<0 then Continue;
2615 
2616       nitem:= AKeymap.IndexOf(ncmd);
2617       if nitem<0 then Continue;
2618 
2619       DoReadConfigToKeys(StrId+'/s1', AKeymap[nitem].Keys1);
2620       DoReadConfigToKeys(StrId+'/s2', AKeymap[nitem].Keys2);
2621       AKeymap[nitem].LexerSpecific:= AForLexer;
2622     end;
2623   finally
2624     skeys.Free;
2625     slist.Free;
2626     cfg.Free;
2627   end;
2628 end;
2629 
2630 
2631 class function TKeymapHelper.GetForLexer(const ALexer: string): TATKeymap;
2632 var
2633   Keymap: TATKeymap;
2634   N: integer;
2635 begin
2636   N:= AppKeymapLexers.IndexOf(ALexer);
2637   if N>=0 then
2638   begin
2639     Result:= TATKeymap(AppKeymapLexers.Objects[N]);
2640     exit;
2641   end;
2642 
2643   Keymap:= TATKeymap.Create;
2644   Keymap.Assign(AppKeymapMain);
2645   AppKeymapLexers.AddObject(ALexer, Keymap);
2646 
2647   LoadConfig(Keymap,
2648     AppFile_HotkeysForLexer(ALexer), true);
2649 
2650   Result:= Keymap;
2651 end;
2652 
2653 
2654 procedure MsgStdout(const Str: string; AllowMsgBox: boolean = false);
2655 begin
2656   {$ifdef windows}
2657   if AllowMsgBox then
2658     MsgBox(Str, MB_OK+MB_ICONINFORMATION);
2659   {$else}
2660   System.Writeln(Str);
2661   {$endif}
2662 end;
2663 
2664 procedure MsgLogConsole(const AText: string);
2665 var
2666   Sep: TATStringSeparator;
2667   S: UnicodeString;
2668 begin
2669   if Pos(#10, AText)=0 then
2670     AppConsoleQueue.Push(AText)
2671   else
2672   begin
2673     Sep.Init(AText, #10);
2674     while Sep.GetItemStr(S) do
2675       AppConsoleQueue.Push(S);
2676   end;
2677 end;
2678 
2679 
2680 function AppEncodingShortnameToFullname(const S: string): string;
2681 var
2682   i: integer;
2683 begin
2684   Result:= '';
2685   if S='' then exit;
2686   for i:= Low(AppEncodings) to High(AppEncodings) do
2687     with AppEncodings[i] do
2688       if SameText(S, ShortName) then
2689         Exit(Name);
2690 end;
2691 
2692 function AppEncodingFullnameToShortname(const S: string): string;
2693 var
2694   i: integer;
2695 begin
2696   Result:= '';
2697   if S='' then exit;
2698   for i:= Low(AppEncodings) to High(AppEncodings) do
2699     with AppEncodings[i] do
2700       if SameText(S, Name) then
2701         Exit(LowerCase(ShortName));
2702 end;
2703 
2704 function AppEncodingListAsString: string;
2705 var
2706   i: integer;
2707 begin
2708   Result:= '';
2709   for i:= Low(AppEncodings) to High(AppEncodings) do
2710     with AppEncodings[i] do
2711       if ShortName<>'' then
2712         Result:= Result + LowerCase(ShortName) + #10;
2713 end;
2714 
2715 procedure UpdateFormOnTop(F: TForm);
2716 begin
2717   if UiOps.ShowFormsOnTop then
2718     F.FormStyle:= fsSystemStayOnTop
2719   else
2720     F.FormStyle:= fsNormal;
2721 end;
2722 
2723 procedure DoStatusbarTextByTag(AStatus: TATStatus; ATag: PtrInt; const AText: string);
2724 var
2725   NIndex: integer;
2726 begin
2727   NIndex:= AStatus.FindPanel(ATag);
2728   if NIndex>=0 then
2729     AStatus.Captions[NIndex]:= AText;
2730 end;
2731 
2732 procedure DoStatusbarHintByTag(AStatus: TATStatus; ATag: PtrInt; const AText: string);
2733 var
2734   NIndex: integer;
2735 begin
2736   NIndex:= AStatus.FindPanel(ATag);
2737   if NIndex>=0 then
2738     AStatus.Hints[NIndex]:= AText;
2739 end;
2740 
2741 procedure DoStatusbarColorByTag(AStatus: TATStatus; ATag: PtrInt; AColor: TColor);
2742 var
2743   NIndex: integer;
2744   Data: TATStatusData;
2745 begin
2746   NIndex:= AStatus.FindPanel(ATag);
2747   if NIndex>=0 then
2748   begin
2749     Data:= AStatus.GetPanelData(NIndex);
2750     Data.ColorFont:= AColor;
2751     AStatus.Invalidate;
2752   end;
2753 end;
2754 
2755 function IsFileTooBigForOpening(const AFilename: string): boolean;
2756 begin
2757   Result:= (AFilename<>'') and (FileSize(AFileName) div (1024*1024) >= UiOps.MaxFileSizeToOpen);
2758 end;
2759 
2760 function IsFileTooBigForLexer(const AFilename: string): boolean;
2761 begin
2762   Result:= (AFilename<>'') and (FileSize(AFilename) div (1024*1024) >= UiOps.MaxFileSizeForLexer);
2763 end;
2764 
2765 
2766 procedure DoLexerDetect(const AFilename: string;
2767   out Lexer: TecSyntAnalyzer;
2768   out LexerLite: TATLiteLexer;
2769   out LexerName: string;
2770   AChooseFunc: TecLexerChooseFunc);
2771 begin
2772   LexerName:= '';
2773   Lexer:= nil;
2774   LexerLite:= nil;
2775   if AFilename='' then exit;
2776 
2777   if IsFileTooBigForLexer(AFilename) then
2778   begin
2779     LexerLite:= AppManagerLite.FindLexerByFilename(AFilename);
2780   end
2781   else
2782   begin
2783     Lexer:= DoLexerDetectByFilenameOrContent(AFilename, AChooseFunc);
2784     if Lexer=nil then
2785       LexerLite:= AppManagerLite.FindLexerByFilename(AFilename);
2786   end;
2787 
2788   if Assigned(Lexer) then
2789     LexerName:= Lexer.LexerName
2790   else
2791   if Assigned(LexerLite) then
2792     LexerName:= LexerLite.LexerName+msgLiteLexerSuffix;
2793 end;
2794 
2795 
2796 procedure FixFormPositionToDesktop(F: TForm);
2797 const
2798   cReservePixels = 100;
2799 var
2800   R: TRect;
2801 begin
2802   R:= Screen.DesktopRect;
2803   F.Left:= Max(F.Left, R.Left);
2804   F.Left:= Min(F.Left, R.Right-F.Width);
2805   F.Top:= Min(F.Top, R.Bottom-cReservePixels);
2806 end;
2807 
2808 procedure FixRectPositionToDesktop(var F: TRect);
2809 const
2810   cReservePixels = 200;
2811 var
2812   R: TRect;
2813   w, h: integer;
2814 begin
2815   w:= F.Width;
2816   h:= F.Height;
2817 
2818   R:= Screen.DesktopRect;
2819   F.Left:= Max(F.Left, R.Left);
2820   F.Left:= Min(F.Left, R.Right-F.Width);
2821   F.Top:= Min(F.Top, R.Bottom-cReservePixels);
2822 
2823   F.Right:= F.Left+w;
2824   F.Bottom:= F.Top+h;
2825 end;
2826 
2827 procedure EditorClear(Ed: TATSynEdit);
2828 begin
2829   Ed.Strings.Clear;
2830   Ed.Strings.ActionAddFakeLineIfNeeded;
2831   Ed.DoCaretSingle(0, 0);
2832   Ed.Update(true);
2833   Ed.Modified:= false;
2834 end;
2835 
2836 { TAppManagerThread }
2837 
2838 procedure TAppManagerThread.Execute;
2839 begin
2840   //AppManager.AllowedThreadId:= Self.ThreadID;
2841   AppLoadLexers;
2842   //AppManager.AllowedThreadId:= 0;
2843 end;
2844 
2845 { TAppCommandInfo }
2846 
CommaStrnull2847 function TAppCommandInfo.CommaStr: string;
2848 begin
2849   if ItemModule='' then
2850     Result:= ''
2851   else
2852   begin
2853     Result:= ItemModule+','+ItemProc;
2854     if ItemProcParam<>'' then
2855       Result+= ','+ItemProcParam;
2856   end;
2857 end;
2858 
2859 { TAppFileProps }
2860 
2861 class operator TAppFileProps.= (const a, b: TAppFileProps): boolean;
2862 begin
2863   Result:=
2864     (a.Exists=b.Exists) and
2865     (a.Size=b.Size) and
2866     (a.Age=b.Age);
2867 end;
2868 
2869 
2870 { TAppKeyValues }
2871 
2872 procedure TAppKeyValues.Add(const AKey, AValue: string);
2873 var
2874   Item: TAppKeyValue;
2875 begin
2876   Item:= TAppKeyValue.Create;
2877   Item.Key:= AKey;
2878   Item.Value:= AValue;
2879   inherited Add(Item);
2880 end;
2881 
GetValuenull2882 function TAppKeyValues.GetValue(const AKey, ADefValue: string): string;
2883 var
2884   Item: TAppKeyValue;
2885   i: integer;
2886 begin
2887   for i:= 0 to Count-1 do
2888   begin
2889     Item:= TAppKeyValue(Items[i]);
2890     if Item.Key=AKey then
2891       exit(Item.Value);
2892   end;
2893   Result:= ADefValue;
2894 end;
2895 
2896 function AppScale(AValue: integer): integer; inline;
2897 begin
2898   Result:= AValue * UiOps.Scale div 100;
2899 end;
2900 
2901 function AppScaleFont(AValue: integer): integer;
2902 begin
2903   if UiOps.ScaleFont=0 then
2904     Result:= AppScale(AValue)
2905   else
2906     Result:= AValue * UiOps.ScaleFont div 100;
2907 end;
2908 
2909 procedure DoMenuitemEllipsis(c: TMenuItem);
2910 var
2911   s: string;
2912 begin
2913   if c=nil then exit;
2914   s:= c.Caption;
2915   while (s<>'') and (s[Length(s)]='.') do
2916     SetLength(s, Length(s)-1);
2917   c.Caption:= s+'...';
2918 end;
2919 
2920 
2921 function RemoveWindowsStreamSuffix(const fn: string): string;
2922 var
2923   PosSlash, PosColon: integer;
2924 begin
2925   Result:= fn;
2926   {$ifdef windows}
2927   PosSlash:= RPos('\', fn);
2928   if PosSlash=0 then exit;
2929   PosColon:= Pos(':', fn, PosSlash);
2930   if PosColon>0 then
2931     SetLength(Result, PosColon-1);
2932   {$endif}
2933 end;
2934 
2935 procedure AppGetFileProps(const FileName: string; out P: TAppFileProps);
2936 var
2937   Rec: TSearchRec;
2938 begin
2939   P.Inited:= true;
2940   P.Exists:= FindFirst(RemoveWindowsStreamSuffix(FileName), faAnyFile, Rec)=0;
2941   if P.Exists then
2942   begin
2943     P.Size:= Rec.Size;
2944     P.Age:= Rec.Time;
2945     FindClose(Rec);
2946   end
2947   else
2948   begin
2949     P.Size:= 0;
2950     P.Age:= 0;
2951   end;
2952 end;
2953 
2954 procedure AppUpdateWatcherFrames;
2955 var
2956   i: integer;
2957 begin
2958   //function is called in IdleTimer, so just exit if watcher thread is busy,
2959   //we will try this again on next timer tick
2960   if AppEventWatcher.WaitFor(1)<>wrSignaled then exit;
2961 
2962   AppEventLister.ResetEvent;
2963   try
2964     for i:= 0 to AppFrameListDeleting.Count-1 do
2965       TObject(AppFrameListDeleting[i]).Free;
2966     AppFrameListDeleting.Clear;
2967 
2968     AppFrameList2.Assign(AppFrameList1);
2969   finally
2970     AppEventLister.SetEvent;
2971   end;
2972 end;
2973 
2974 
2975 class function TPluginHelper.CommandCategory(Cmd: integer): TAppCommandCategory;
2976 var
2977   N: integer;
2978 begin
2979   case Cmd of
2980     cmdFirstPluginCommand..cmdLastPluginCommand:
2981       begin
2982         Result:= categ_Plugin;
2983         N:= Cmd-cmdFirstPluginCommand;
2984         if N<AppCommandList.Count then
2985         begin
2986           if TAppCommandInfo(AppCommandList[N]).ItemFromApi then
2987             Result:= categ_PluginSub;
2988         end
2989         else
2990           //we are here when e.g. in plugin Macros user deletes a macro,
2991           //so code detects category of deleted command-code
2992           Result:= categ_PluginSub;
2993       end;
2994     cmdFirstLexerCommand..cmdLastLexerCommand:
2995       Result:= categ_Lexer;
2996     cmdFirstFileCommand..cmdLastFileCommand:
2997       Result:= categ_OpenedFile;
2998     cmdFirstRecentCommand..cmdLastRecentCommand:
2999       Result:= categ_RecentFile;
3000     else
3001       Result:= categ_Normal;
3002   end;
3003 end;
3004 
3005 class function TPluginHelper.CommandHasConfigurableHotkey(Cmd: integer): boolean;
3006 begin
3007   Result:= CommandCategory(Cmd) in [categ_Normal, categ_Plugin, categ_PluginSub];
3008 end;
3009 
3010 procedure InitBasicCommandLineOptions;
3011 var
3012   S: string;
3013   i: integer;
3014 begin
3015   for i:= 1 to ParamCount do
3016   begin
3017     S:= ParamStr(i);
3018 
3019     if S='-n' then
3020     begin
3021       AppAlwaysNewInstance:= true;
3022       Continue;
3023     end;
3024 
3025     if SBeginsWith(S, '-id=') then
3026     begin
3027       Delete(S, 1, Length('-id='));
3028       if S<>'' then
3029         AppServerId:= S;
3030       Continue;
3031     end;
3032   end;
3033 end;
3034 
3035 class function TPluginHelper.EventIsUsed(AEvent: TAppPyEvent): boolean;
3036 var
3037   NPlugin: integer;
3038   Plugin: TAppEventInfo;
3039 begin
3040   Result:= false;
3041   for NPlugin:= 0 to AppEventList.Count-1 do
3042   begin
3043     Plugin:= TAppEventInfo(AppEventList[NPlugin]);
3044     if AEvent in Plugin.ItemEvents then
3045       exit(true);
3046   end;
3047 end;
3048 
3049 class procedure TPluginHelper.EventStringToEventData(const AEventStr: string;
3050   out AEvents: TAppPyEvents;
3051   out AEventsPrior: TAppPyEventsPrior;
3052   out AEventsLazy: TAppPyEventsLazy);
3053 const
3054   MaxPriority = 4;
3055 var
3056   Sep: TATStringSeparator;
3057   S: string;
3058   event: TAppPyEvent;
3059   nPrior: byte;
3060   bLazy: boolean;
3061 begin
3062   AEvents:= [];
3063   FillChar(AEventsPrior, SizeOf(AEventsPrior), 0);
3064   FillChar(AEventsLazy, SizeOf(AEventsLazy), 0);
3065 
3066   Sep.Init(AEventStr);
3067   while Sep.GetItemStr(S) do
3068   begin
3069     nPrior:= 0;
3070     while S[Length(S)]='+' do
3071     begin
3072       Inc(nPrior);
3073       SetLength(S, Length(S)-1);
3074     end;
3075 
3076     if nPrior>MaxPriority then
3077       nPrior:= MaxPriority;
3078 
3079     bLazy:= false;
3080     if S[Length(S)]='~' then
3081     begin
3082       bLazy:= true;
3083       SetLength(S, Length(S)-1);
3084     end;
3085 
3086     for event in TAppPyEvent do
3087       if S=cAppPyEvent[event] then
3088       begin
3089         Include(AEvents, event);
3090         AEventsPrior[event]:= nPrior;
3091         AEventsLazy[event]:= bLazy;
3092         Break
3093       end;
3094   end;
3095 end;
3096 
3097 
3098 class procedure TPluginHelper.EventsUpdate(const AModuleName, AEventStr, ALexerStr, AKeyStr: string);
3099 var
3100   EventItem: TAppEventInfo;
3101   i: integer;
3102 begin
3103   //find index of plugin (get first empty index if not listed)
3104   EventItem:= nil;
3105   for i:= 0 to AppEventList.Count-1 do
3106     with TAppEventInfo(AppEventList[i]) do
3107       if (ItemModule=AModuleName) then
3108       begin
3109         EventItem:= TAppEventInfo(AppEventList[i]);
3110         Break
3111       end;
3112 
3113   if EventItem=nil then
3114   begin
3115     EventItem:= TAppEventInfo.Create;
3116     AppEventList.Add(EventItem);
3117   end;
3118 
3119   //update item
3120   with EventItem do
3121   begin
3122     if ItemModule='' then
3123       ItemModule:= AModuleName;
3124     EventStringToEventData(AEventStr, ItemEvents, ItemEventsPrior, ItemEventsLazy);
3125     ItemLexers:= ALexerStr;
3126     ItemKeys:= AKeyStr;
3127   end;
3128 
3129   EventsMaxPrioritiesUpdate;
3130 end;
3131 
3132 class procedure TPluginHelper.CommandsClearButKeepApiItems;
3133 var
3134   i: integer;
3135 begin
3136   for i:= AppCommandList.Count-1 downto 0 do
3137     with TAppCommandInfo(AppCommandList[i]) do
3138       if (ItemModule<>'') and (not ItemFromApi) then
3139         AppCommandList.Delete(i);
3140 end;
3141 
3142 class procedure TPluginHelper.EventsMaxPrioritiesUpdate;
3143 var
3144   ev: TAppPyEvent;
3145   Plugin: TAppEventInfo;
3146   Value, i: integer;
3147 begin
3148   for ev in TAppPyEvent do
3149   begin
3150     Value:= -1;
3151     for i:= 0 to AppEventList.Count-1 do
3152     begin
3153       Plugin:= TAppEventInfo(AppEventList[i]);
3154       if ev in Plugin.ItemEvents then
3155         Value:= Max(Value, Plugin.ItemEventsPrior[ev]);
3156     end;
3157     AppEventsMaxPriorities[ev]:= Value;
3158   end;
3159 end;
3160 
3161 
3162 function IsOsFullPath(const S: string): boolean;
3163 begin
3164   {$ifdef windows}
3165   //'D:\path'
3166   //'\\UNCpath'
3167   Result:=
3168     (Length(S)>2) and
3169     ((S[2]=':') or ((S[1]='\') and (S[2]='\')));
3170   {$else}
3171   Result:= SBeginsWith(S, '/');
3172   {$endif}
3173 end;
3174 
3175 
3176 procedure AppOnLexerLoaded(Sender: TObject; ALexer: TecSyntAnalyzer);
3177 var
3178   fn_ops: string;
3179 begin
3180   //load *.cuda-lexops
3181   fn_ops:= GetAppLexerOpsFilename(ALexer.LexerName);
3182   if FileExists(fn_ops) then
3183     DoLoadLexerStylesFromFile_JsonLexerOps(ALexer, fn_ops, UiOps.LexerThemes);
3184 end;
3185 
3186 procedure AppLoadLexers;
3187 var
3188   cfg: TJsonConfig;
3189   SErrorLines: string;
3190   SErrorItem: string;
3191   Sep: TATStringSeparator;
3192 {
3193 var
3194   NCountNormal, NCountLite: integer;
3195   NTickNormal, NTickLite: QWord;
3196   }
3197 begin
3198   //must read UiOps.LexerThemes here, AppLoadLexers runs in a thread
3199   //before loading all options, and we need this option already
3200   cfg:= TJsonConfig.Create(nil);
3201   try
3202     try
3203       cfg.Filename:= AppFile_OptionsUser;
3204       UiOps.LexerThemes:= cfg.GetValue('ui_lexer_themes', UiOps.LexerThemes);
3205     except
3206     end;
3207   finally
3208     cfg.Free;
3209   end;
3210 
3211   //1) load lite lexers
3212   //NTickLite:= GetTickCount64;
3213 
3214   AppManagerLite.Clear;
3215   AppManagerLite.LoadFromDir(AppDir_LexersLite);
3216 
3217   {
3218   NTickLite:= GetTickCount64-NTickLite;
3219   NCountLite:= AppManagerLite.LexerCount;
3220   if NCountLite=0 then
3221     MsgLogConsole(Format(msgCannotFindLexers, [AppDir_LexersLite]));
3222     }
3223 
3224   //2) load EControl lexers
3225   //NTickNormal:= GetTickCount64;
3226 
3227   AppManager.OnLexerLoaded:= @AppOnLexerLoaded;
3228   AppManager.InitLibrary(AppDir_Lexers, SErrorLines);
3229 
3230   if SErrorLines<>'' then
3231   begin
3232     Sep.Init(SErrorLines, #10);
3233     while Sep.GetItemStr(SErrorItem) do
3234       MsgLogConsole('NOTE: '+SErrorItem);
3235   end;
3236 
3237   {
3238   NTickNormal:= GetTickCount64-NTickNormal;
3239   NCountNormal:= AppManager.LexerCount;
3240   if NCountNormal=0 then
3241     MsgLogConsole(Format(msgCannotFindLexers, [AppDir_Lexers]));
3242     }
3243 end;
3244 
3245 
3246 function LiteLexer_GetStyleHash(const AStyleName: string): integer;
3247 var
3248   iStyle: TAppThemeStyleId;
3249 begin
3250   Result:= -1;
3251   for iStyle:= Low(iStyle) to High(iStyle) do
3252     if SameStr(AStyleName, AppTheme.Styles[iStyle].DisplayName) then
3253       exit(Ord(iStyle));
3254 end;
3255 
3256 procedure LiteLexer_ApplyStyle(AStyleHash: integer; var APart: TATLinePart);
3257 var
3258   st: TecSyntaxFormat;
3259 begin
3260   if AStyleHash<0 then exit;
3261   st:= AppTheme.Styles[TAppThemeStyleId(AStyleHash)];
3262   ApplyPartStyleFromEcontrolStyle(APart, st);
3263 end;
3264 
3265 function IsDefaultSessionActive: boolean;
3266 begin
3267   Result:=
3268     (AppSessionName='') or
3269     (AppSessionName=cAppSessionDefault);
3270 end;
3271 
3272 
3273 function AppConfigKeyForBookmarks(Ed: TATSynEdit): string;
3274 begin
3275   if Ed.FileName<>'' then
3276     Result:= '/bookmarks/'+SMaskFilenameSlashes(AppCollapseHomeDirInFilename(Ed.FileName))
3277   else
3278     Result:= '';
3279 end;
3280 
3281 function AppDiskGetFreeSpace(const fn: string): Int64;
3282 begin
3283   {$ifdef linux}
3284   //this crashes on FreeBSD 12 x64
3285   exit(SysUtils.DiskFree(SysUtils.AddDisk(ExtractFileDir(fn))));
3286   {$endif}
3287 
3288   {$ifdef windows}
3289   exit(SysUtils.DiskFree(SysUtils.GetDriveIDFromLetter(ExtractFileDrive(fn))));
3290   {$endif}
3291 
3292   //cannot detect
3293   exit(-1);
3294 end;
3295 
3296 procedure AppDiskCheckFreeSpace(const fn: string);
3297 var
3298   NSpace: Int64;
3299 begin
3300   if UiOps.CheckLowDiskSpace<=0 then exit;
3301   repeat
3302     NSpace:= AppDiskGetFreeSpace(fn);
3303     if NSpace<0 then exit; //cannot detect free space
3304     if NSpace>=UiOps.CheckLowDiskSpace then exit;
3305     if MsgBox(
3306       Format(msgErrorLowDiskSpaceMb, [NSpace div (1024*1024)]),
3307       MB_RETRYCANCEL or MB_ICONWARNING) = ID_CANCEL then exit;
3308   until false;
3309 end;
3310 
3311 function AppKeyIsAllowedAsCustomHotkey(Key: Word; Shift: TShiftState): boolean;
3312 begin
3313   Result:= true;
3314 
3315   //don't allow to reassign system keys: Alt/Ctrl/Shift/Win
3316   if (Key=VK_MENU) or
3317      (Key=VK_LMENU) or
3318      (Key=VK_RMENU) or
3319      (Key=VK_CONTROL) or
3320      (Key=VK_LCONTROL) or
3321      (Key=VK_RCONTROL) or
3322      (Key=VK_SHIFT) or
3323      (Key=VK_LSHIFT) or
3324      (Key=VK_RSHIFT) or
3325      (Key=VK_LWIN) or
3326      (Key=VK_RWIN) then
3327     exit(false);
3328 
3329   //don't allow to reassign these
3330   if (Key in [VK_SPACE, VK_RETURN, VK_TAB, VK_BACK]) and (Shift=[]) then
3331     exit(false);
3332 end;
3333 
3334 initialization
3335 
3336   InitDirs;
3337   InitEditorOps(EditorOps);
3338   InitUiOps(UiOps);
3339   InitBasicCommandLineOptions;
3340 
3341   AppConsoleQueue:= TAppConsoleQueue.Create;
3342   AppCommandsDelayed:= TAppCommandsDelayed.Create;
3343   AppCommandList:= TFPList.Create;
3344   AppEventList:= TFPList.Create;
3345   AppTreeHelpers:= TFPList.Create;
3346 
3347   AppKeymapMain:= TATKeymap.Create;
3348   InitKeymapFull(AppKeymapMain);
3349   Keymap_AddCudatextItems(AppKeymapMain);
3350 
3351   AppKeymapLexers:= TStringList.Create;
3352   AppKeymapLexers.Sorted:= true;
3353   AppKeymapLexers.OwnsObjects:= true;
3354 
3355   FillChar(AppEventsMaxPriorities, SizeOf(AppEventsMaxPriorities), 0);
3356   FillChar(AppBookmarkSetup, SizeOf(AppBookmarkSetup), 0);
3357   AppBookmarkImagelist:= TImageList.Create(nil);
3358 
3359   AppShortcutEscape:= ShortCut(VK_ESCAPE, []);
3360   AppShortcutShiftTab:= ShortCut(VK_TAB, [ssShift]);
3361 
3362   Mouse.DragImmediate:= false;
3363   Mouse.DragThreshold:= 12;
3364 
3365   AppConfig_Detect:= TAppKeyValues.Create;
3366   AppConfig_DetectLine:= TAppKeyValues.Create;
3367   AppConfig_PGroups:= TAppKeyValues.Create;
3368 
3369   ////detection of Shell files
3370   ////disabled: it detects Python files with shebang
3371   //AppConfig_DetectLine_Keys.Add('\#!.+');
3372   //AppConfig_DetectLine_Values.Add('Bash script');
3373 
3374   //detection of XML
3375   AppConfig_DetectLine.Add('<\?xml .+', 'XML');
3376 
3377   AppFrameList1:= TFPList.Create;
3378   AppFrameList2:= TFPList.Create;
3379   AppFrameListDeleting:= TFPList.Create;
3380   AppEventLister:= TEvent.Create(nil, true, true, '');
3381   AppEventWatcher:= TEvent.Create(nil, true, true, '');
3382 
3383   AppApiFlatTheme:= ATFlatTheme;
3384   AppListRecents:= TStringList.Create;
3385 
3386   ATSynEdit_Commands.cCommand_GotoDefinition:= cmd_GotoDefinition;
3387 
3388   AppManager:= TecLexerList.Create(nil);
3389   AppManagerLite:= TATLiteLexers.Create(nil);
3390   AppManagerLite.OnGetStyleHash:= @LiteLexer_GetStyleHash;
3391   AppManagerLite.OnApplyStyle:= @LiteLexer_ApplyStyle;
3392   AppManagerThread:= TAppManagerThread.Create(false);
3393 
3394 finalization
3395 
3396   FreeAndNil(AppManagerThread);
3397   FreeAndNil(AppManagerLite);
3398   FreeAndNil(AppManager);
3399 
3400   FreeAndNil(AppListRecents);
3401   FreeAndNil(AppEventWatcher);
3402   FreeAndNil(AppEventLister);
3403   FreeAndNil(AppFrameListDeleting);
3404   FreeAndNil(AppFrameList2);
3405   FreeAndNil(AppFrameList1);
3406 
3407   FreeAndNil(AppConfig_PGroups);
3408   FreeAndNil(AppConfig_DetectLine);
3409   FreeAndNil(AppConfig_Detect);
3410   FreeAndNil(AppKeymapLexers);
3411   FreeAndNil(AppKeymapMain);
3412   FreeAndNil(AppBookmarkImagelist);
3413 
3414   FreeAndNil(AppTreeHelpers);
3415   FreeAndNil(AppEventList);
3416   FreeAndNil(AppCommandList);
3417   FreeAndNil(AppConsoleQueue);
3418   FreeAndNil(AppCommandsDelayed);
3419 
3420 end.
3421 
3422