Document | TSE_PRO_Undocumented_Features.html |
Author | Carlo Hogeveen |
Origin | Website |
Date | 7 Jul 2023 |
This document describe bugs and undocumented features for the latest TSE Pro version.
A topic title here usually references a same-named topic title in TSE's built-in Help system. A same-named topic only occurs here if there is additional or corrected information.
Note that after you select a topic in the index, you can then copy its URL from the browser's address bar to use it elsewhere as a link.
┌──────────────────────────────────────────────────────────────────────────────┐ │ A B C D E F G H I J K L M N O P Q R S T U V W X Y Z │ └──────────────────────────────────────────────────────────────────────────────┘
It is not possible to conditionally compile only part of a condition.
For example, compiling the following macro reports a syntax error.
proc Main() if FALSE #ifdef WIN32 or TRUE #endif Warn('Yes!') else Warn('No!') endif PurgeMacro(SplitPath(CurrMacroFilename(), _NAME_)) end Main
Bug: In an older TSE version it is not possible to use a not-yet-existing predefined TSE constant from a newer TSE version in an #else clause that should have skipped it.
For example, the TSE constant _STATE_EDITOR_PAUSED_ was introduced in TSE Pro versions after 4.4, but the following fails to compile in TSE Pro 4.4 and lower versions:
#if EDITOR_VERSION < 4500h #define STATE_EDITOR_PAUSED 0x0800 #else #define STATE_EDITOR_PAUSED _STATE_EDITOR_PAUSED_ #endif
A work-around is to reverse the condition, or to use two #if statements, one for each conditional branch.
This is a synonym for _dont_prompt_, and can be used everywhere where _dont_prompt_ can be used.
For an example: Semware uses it in their AutoSave macro.
Advanced macro programming side effect when using Windows' TextOutW API:
If TSE's _USE_3D_CHARS_ and _USE_3D_BUTTONS_ display configuration settings are both OFF, then TextOutW also displays characters the current font does not support, and it displays Unicode's "Combining" characters as combined with their preceding character.
If one of the 3D options is ON and the current font does not support a character, then TextOutW displays the font's "default" character, which for Courier New I see as an empty square.
Note, that the combining/not combining difference impacts the number of characters that are displayed, which might need compensating for.
For an example see the source code of the Uniview extension.
See _USE_3D_BUTTONS_.
This function does not call the _on_exit_called_ hook, but otherwise on the last file implicitly does an Exit() too.
Therefore also see: Process().
"A maximum of 60 external Compiled Macro files may be loaded at any one time."My tests say the actual number is 58.
"Each external Compiled Macro file may have up to 63k of Compiled Macros."But not always:
A control statement and a conditional statement cannot refer to statements that are more than 32 kB away. If violated the compiler will give the error message:
"Range of loop or goto > 32k, please reduce size"This typically occurs in macros that were generated or edited from data files.
White space means a sequence of space and horizontal tab characters.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
Some not explicitly documented behaviour of binary mode.
When loading a file in normal mode, TSE strips line ending characters like the carriage return (CR) and line feed (LF) characters from a text before showing it to us.
When loading a file in binary mode it does not do that stripping and treats the carriage return and line feed characters as any other character.
When you load a file in binary mode, for example by opening it with "-b<N> <filename>" where <N> is a positive number, then editor's shown lines for that file will have a fixed length <N> that will have have nothing to do with the text's actual lines: Line ending characters like a carriage return and line feed will explicitly be shown as characters in the text. (But see Note 2.)
Undocumented feature:
If you load a file with binary mode <N> is -3, then the file is loaded like
with a positive <N>, but with each line's actual length, including its actual
line ending characters like carriage returns and line feeds. Practical!
Note 1:
So binary mode -3 is like the binary modes <positive number>, but handier,
and unlike binary modes -1 and -2, which do something completely different.
Note 2:
In the GUI version of TSE with an ANSI compatible font any control characters,
like the line ending characters carriage return and line feed, are shown as
spaces, which is not practical in binary mode.
The TSE extension "ControlChars" from my website solves that by explicitly
showing each control character as a different character, and by making control
characters stand out for any TSE version and font.
Once you are editing a file in binary mode, you can lengthen and shorten an editor line by inserting characters up to TSE's maximum line length and by deleting characters down to an empty line. When you do not add or delete line ending characters this has no impact on the number of lines in the actual text.
You can even delete and insert empty editor lines without any impact on the file.
When saving a file in binary mode the editor's visual lines are ignored.
The saved file wil only have line endings where explicit carriage return and/or line feed characters occurred in the editor text.
TSE has a problem with searching in binary mode:
While TSE no longer implicitly sees text lines in binary mode,
TSE's search commands are still limited to finding search results
in buffer lines, so they will not find search results which are
(arbitrarily!) split between two buffer lines.
This is what I found so far. It might not be a complete picture.
Bug: If the TSE variable PickFileFlags <> _NONE_ and BuildPickBufferEx() finds nothing, then BuildPickBufferEx() builds a list of directories instead.
The solution is to either temporarily set PickFileFlags to _NONE_ around the call to BuildPickBufferEx(), or to use the code below to clean up such directories.
// Remove directory entries and empty lines after BuildPickBufferEx() // with PickFileFlags <> _NONE_. integer i = 0 for i = 1 to NumLines() GotoLine(i) if (PBAttribute() & _DIRECTORY_) BegLine() KillToEol() endif endfor while lFind('^[ \d009]@$', 'gx') KillLine() endwhile if NumLines() Warn('BuildPickBufferEx() found matches.') else Warn('BuildPickBufferEx() found no matches.') endif
Note, 9 Dec 2022: Do not use syncfg.si as an example to clean up such directories, because that piece of code has many flaws, which go undetected because syncfg.si uses PickFileFlags == _NONE_.
The "when" clause of a "case" statement can not only have a list of values separated by commas and a range of values separated by "..", but also both at the same time.
For Example:
case CurrChar() when 66..68, 70..72, 74..78, 80..84, 86..90 Warn('This is an upper case consonant') endcase
The range of a "when" clause can also be a range of characters.
For example:
case GetText(CurrPos(),1) when 'B'..'D', 'F'..'H', 'J'..'N', 'P'..'T', 'V'..'Z' Warn('This is an upper case consonant') endcase
If ChangeCurrFilename() successfully changes only the case of a file name, then the file is not marked as modified.
For example, given an unchanged file "D:\helloworld.txt", changing its name to "D:\HelloWorld.txt" will not mark the file as modified.
The WordSet and ChrSet() documentation misses a reference to the ClearBit(), GetBit() and SetBit() functions.
Its documentation states not wrongly that it returns a value < 0, == 0 or > 0, but actually it only returns the values -1, 0 or 1.
This enables us to execute the "expensive" CmpiStr() only once using a "case" statement.
For example:
case CmpiStr(string1, string2) when -1 // Usually most common ... // Handle ascending order when 1 ... // Hanlde descending order otherwise ... // Handle same order endcase
The 2nd paragraph of this topic's documentation still states:
"Command-line options are identified by either the "-" or "/" character, immediately followed by the letter that designates the desired option."As of TSE Pro 4.4, only the "-" character is supported.
Tip for macro programmers:
Default you can start a macro from the command line, but not pass it any
parameters other than files to be loaded simultaneously.
By installing the TSE extension
CmdLineParameter
you can pass parameters too.
You cannot debug a macro that contains a config ... endconfig clause.
A constant declaration may be made globally and in a procedure.
There is an undocumented way to initialize multiple constants in one statement. The actual syntax for "constant" is:
constant identifier [= constant_expression] [, identifier [= constant_expression]] ...The difference is, that the constant_expressions may be omitted.
If the constant_expression of the first identifier is omitted, then its value is 1.
If the constant_expression of any next identifier is omitted, then its value is that of its predecessor plus 1.
Examples:
constant a = 1, b, c // a == 1, b == 2, c == 3. constant d, e = 10, f // d == 1, e == 10, f == 11. constant g = -10, h, i // g == -10, h == -9, i == -8.The purpose is to shortly give significant names to consecutive values.
Examples:
constant January, February, March, April, May, June, July, August, September, October, November, December constant smaller_than = -1, equal_to, greater_than
A test showed that CopyBlock() used 33% less memory than Copy() + Paste().
To overwrite the destination file the third parameter must be truthy (any value that a conditional statement would interpret as true).
For example, instead of TRUE we can also use the more readable constant _OVERWRITE_.
The content of the Windows clipboard stops at a NULL character. In TSE that is the character represented by the single byte value 0. In the Windows clipboad text is default character encoded as UTF16-LE and ended with two bytes with value 0.
This is not a TSE bug. It is a property of the Windows clipboard when it contains text.
This might become relevant when copying the contents of a binary file or a file with non-printable characters.
TSE's internal copy commands do not have this problem.
CreateBuffer() has three optional parameters instead of two. Its full syntax is therefore:
CreateBuffer(STRING fn, [INTEGER type [, VAR INTEGER id [, INTEGER flag]]])
fn is the desired buffer name.
flag is _NORMAL_ (the default), _HIDDEN_ or _SYSTEM_.
If id is 0 or not the id of an existing buffer, then a new buffer is created and id returns the id of the newly created buffer, which is TSE's next new id, not the input one.
If id is the id of an existing buffer, then that buffer is reused and returned. The content of the existing buffer is not initialized.
In all of the above cases the function will fail and do nothing if the buffer name already exists as another buffer, unless _FORCE_NAME_ is supplied as the flag parameter, in which case an another buffer with the same same is created.
This setting determines the format of FFDateStr() too.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
Despite what its name might suggest, DelChar() does not add the deleted character to the Deletion Buffer, so it cannot be undeleted, and so there is overhead involved.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
Besides the documented parameters
_DISPLAY_TEXT_ _DISPLAY_HEX_ _DISPLAY_PICKFILE_it has these undocumented ones:
_DISPLAY_FINDS_ _DISPLAY_HELP_ _DISPLAY_USER_
_DISPLAY_FINDS_
This parameter will colour the current file the same as TSE colours the results of a Find() with the "v" option: The current line will be coloured with MenuSelectLtrAttr if the line starts with "File: ", and MenuSelectAttr otherwise. Other lines will be coloured with MenuTextLtrAttr if the line starts with "File: ", and MenuTextAttr otherwise.
But: Browsing to a new line will not change colouring accordingly. Modifying a line will change that line's colouring to that of a current line but will not recolour other lines. It seems necessary to do a new DisplayMode(_DISPLAY_FINDS_) after each user action that therefore might require new colouring of the editing window.
_DISPLAY_HELP_
A valuable source for this section was Chris Antos' GetHelp macro from 2002. Any mistakes in the document you are now reading are mine.
Again, a Unicode warning: This document uses UTF-8 character encoding, which matters to the below text: Read it with a web browser, or install the Unicode macro from my website to read this text in TSE.
DisplayMode(_DISPLAY_HELP_) changes the display of the current file immensely:
The file is displayed with OEM characters, mainly to use their line drawing capability (ANSI does not support this). For example see the Help2txt macro's "to_utf8" proc for Unicode codes and descriptions which are the equivalents of typically used OEM character codes. For users of a GUI version of TSE: Temporarily switch to an OEM font like Terminal, start drawing lines or (partioned) boxes, and switch back to an ANSI font like Courier New to see what you have wrought character-wise.
Default the text is coloured with HelpTextAttr.
Text between the following tags is coloured accordingly.
Text between the tags | Is coloured with |
---|---|
®I¯ and ®/I¯ | HelpItalicsAttr |
®B¯ and ®/B¯ | HelpBoldAttr (aka "Highlighted") |
®L¯ and ®/L¯ | HelpLinkAttr |
®LI¯ and ®/L¯ | HelpInfoAttr |
®T¯ and ®/T¯ | HelpTextAttr |
®S¯ and ®/S¯ | HelpTextAttr |
The tags are not shown: Shown text is left-shifted to the actual text.
Tag colouring exception 1:
If the cursor is on the first character of the opening
tag ®L¯ or ®LI¯, then the text after the tag is coloured
with HelpSelectAttr.
Tag colouring exception 2:
Some tag's colouring can overrule another's.
Text between the tags ®B¯®T¯ and ®/T¯®/B¯,
and text between the tags ®B¯®S¯ and ®/S¯®/B¯
will in both cases colour the text with HelpBoldAttr.
Note that DisplayMode(_DISPLAY_HELP_) only changes how the text of the current file is coloured, nothing more. It has no notion of what the text between tags means, and it performs no actions of any kind. For example, in TSE's Help the tags are used to colour links, but it is TSE's internal Help that acts when you select a link: DisplayMode(_DISPLAY_HELP_) and its tags do not do that.
_DISPLAY_USER_
Intended for use with HookDisplay(). See HookDisplay().
Outside HookDisplay() _DISPLAY_USER_ seems to behave like _DISPLAY_TEXT_.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function's flag parameter also has the flag _START_HIDDEN_, which will cause the command to invisibly start and run in a hidden Windows process.
In newer versions of TSE the _DONT_PROMPT_ and _DONT_CLEAR_ flags can be omitted when using the _START_HIDDEN_ flag, but that makes the macro not backwards compatible, so I advise against it.
This function also has a flag _DONT_WAIT_, which when combined with the _RUN_DETACHED_ flag will make the started command a parallel process.
For example,
#define DOS_SYNC_CALL_FLAGS _DONT_PROMPT_|_DONT_CLEAR_|_START_HIDDEN_|_RETURN_CODE_ #define DOS_ASYNC_CALL_FLAGS _DONT_PROMPT_|_DONT_CLEAR_|_START_HIDDEN_|_RETURN_CODE_|_RUN_DETACHED_|_DONT_WAIT_ if FALSE lDos(LoadDir(TRUE), '', DOS_ASYNC_CALL_FLAGS) else Dos(LoadDir(TRUE), DOS_ASYNC_CALL_FLAGS) endifstarts a second instance of the editor, and you can work in both instances of the editor simultaneously. Here lDos() is better, because Dos() also starts a useless blank window.
Without _RUN_DETACHED_ the Dos() command "eats" any unprocessed keys, making TSE ignore them. In tools that is usually not a problem, in extensions it ususally is.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
Unlike its documentation implies, its return value will only indicate whether
it was capable of creating a new buffer, not whether it succeeded in loading
the supplied file into it.
So if it could not load the file, then it still returns the id of a newly
created empty buffer.
Also unlike its documentation implies, a successfully loaded file will not be marked as a character block like InsertFile() does.
And unlike the documentation implies, a succesfully loaded file will
not create a buffer with a name, like CreateBuffer() does.
EditBuffer() creates a buffer with an empty name, like
CreateTempBuffer() does.
White space means a sequence of space and horizontal tab characters.
The below line from the documentation needed two additional qualifications:
If the RemoveTrailingWhite variable is set ON
and the buffer is not a _SYSTEM_ buffer
and the buffer is not in binary mode
,
then this command will remove any trailing white space found on the
current line.
Definitely see: Process().
Keyboards can have multiple places where you can type the same key. Typically this is the case for the cursor keys and the Home, End, Insert, Delete, PageUp, PageDown, /, *, +, - and Enter keys.
If the EquateEnhancedKbd setting is ON, which is the default, then it becomes irrelevant at which place on the keyboard you type a key.
If the EquateEnhancedKbd setting is OFF, then TSE sees different keys depending on at which place on the keyboard you type a key.
By using the ShowKey macro from TSE's Potpourri menu we can see, that TSE does or does not prefix the key name with "Grey " depending on where on the keyboard a key was typed.
Here is the main undocumented fact:
If EquateEnhancedKbd is OFF and no action is assigned to a "grey" key, then TSE executes the action assigned to the "non-grey" key.
An example of this can be found in the Potpourri's Calendar macro, that even if EquateEnhancedKbd is OFF will act on "grey" cursor keys despite having no definition for them.
This function can also execute a public procedure without its macro filename, if the macro the public procedure occurs in is loaded.
public proc non_existing_macro() Warn('But I do exist!') end non_existing_macro proc Main() ExecMacro('non_existing_macro') Warn('IsMacroLoaded ='; iif(isMacroLoaded('non_existing_macro'), 'TRUE', 'FALSE')) PurgeMacro(CurrMacroFilename()) end Main
Definitely see: Process().
It returns the date in the same encoded format as the Windows API DosDateTimeToFileTime() uses as input:
Bits | Description |
---|---|
0-4 | Day of month (1-31) |
5-8 | Month (1 = January, 2 = February, and so on) |
9-15 | Year offset from 1980 (add 1980 to get actual year) |
Bits are counted right to left, starting at 0.
The returned date is formatted according to the DateFormat setting.
It returns the time in the same encoded format as the Windows API DosDateTimeToFileTime() uses as input:
Bits | Description |
---|---|
0-4 | Second divided by 2 |
5-10 | Minute (0–59) |
11-15 | Hour (0–23 on a 24-hour clock) |
Bits are counted right to left, starting at 0.
Seconds are always even.
Windows does know a more detailed time,
which FFTime() rounds up to the first time
that has an even amount of seconds.
From the Windows command prompt,
this Windows command lists files in the current folder
with a time that can have an odd number of seconds:
forfiles /c "cmd /c echo @file @ftime"
The returned time is formatted according to the TimeFormat setting.
Seconds are always even.
Windows does know a more detailed time,
which FFTime() rounds up to the first time
that has an even amount of seconds.
From the Windows command prompt,
this Windows command lists files in the current folder
with a time that can have an odd number of seconds:
forfiles /c "cmd /c echo @file @ftime"
This is not a topic in TSE's built-in Help system.
To find out which specific folders and files on your computer TSE cannot access, you can execute the DirList macro, and supply it with "C:\ D:\" etcetera as a parameter.
In the result you can then search for "<inaccessible" with options "giv", and optionally use <Alt e> to edit that search result as a file.
Possible annotations to find are "<inaccessible folder>" and "<inaccessible file>".
TSE has several problems with too long paths. For example MkDir()'s specific problem starts after 247 characters. According to Semware the general limit is >= 255 characters.
Luckily, such deep paths are not the norm.
Unluckily, even one such path can make TSE's "-s" parameter return an aborted result or even hang TSE. The "-s" is a command line and EditFile() parameter to list files in all subfolders too.
Reader warning: If you read this file offline with TSE: This is a web browser optimized document that uses Unicode characters, especially in this topic. To read it in TSE you should have TSE's Unicode extension installed. Otherwise use a web browser to read this document.
The Windows file system stores its folder and file names in the UTF-16LE character encoding, which is a fully complient Unicode character encoding, that therefore implements practically all the world's characters, for example containing 137,994 characters as of Unicode 12.1, released in March 2019.
TSE only knows the ANSI (formally Windows-1252) character encoding for the file system. ANSI contains 218 characters.
TSE does no conversion of its own, but uses the standard Windows ANSI APIs to access the file system. For more info see the Microsoft documentation about Unicode in the Windows API and Code Pages .
These Windows ANSI APIs have the nice feature, that (when TSE reads a folder name or file name) they convert the file system's UTF-16LE to ANSI if these character encodings have an equivalent character, and that they convert a UTF-16LE character to a close approximation in ANSI if the character encodings do not have an equivalent character. The other way around (when TSE writes a folder name or file name) the Windows APIs always convert characters exactly, because all ANSI characters have a Unicode equivalent.
For example, this means that a "copyright sign", written as "©", is no problem in a folder or file name, because both UTF-16LE and ANSI have this character, so the Windows ANSI APIs never have a problem converting it.
For another example, this means that when we browse with TSE, we see a "greek small letter alpha" in a folder or file name as an "a", because the greek letter alpha exists in UTF-16LE but not in ANSI, so the Windows ANSI APIs show TSE an approximation. From TSE opening a folder name containing an alpha fails with an error, because the nearest ANSI equivalent of an alpha is an "a", and the folder does not exist with an "a" in its name. From TSE opening a file name containing an alpha fails by opening a new empty file: It does not open the file with an alpha in its name, but it opens a new empty file that has an "a" in the alpha's place.
If you do not use any Apple products in Windows, then the following does not apply to you. But if, for instance, you use Apple's iCloud drive in Windows, or you otherwise have or get access to files created on a Mac or iProduct, then do read on for some dirty details.
In Unicode (and therefore in UTF-16LE) characters with a diacrital mark can be written in two ways:
- As one character. For example, the character "é" can be written as the one Unicode character "latin small letter e with acute".
- As two characters. For example, the character "é" can be written as the two Unicode characters "latin small letter e" and "combining acute accent".
Windows, and thus the Windows ANSI APIs, and thus TSE, all default to the first format. So if you use TSE just with Windows products then life is good.
Enter Apple, who uses the second format when it creates new folders and files.
Both Apple's operating systems and Windows know both formats, and for both formats show the same single character. So far life is still OK.
( Though not perfect: This creates the weird possibility, that in one folder you can have two files with what appears to be the same name without the ability in Apple and Windows to see why they differ. This problem is not caused by Apple or Windows, but by Unicode. )
The Windows ANSI APIs choose to convert the second format to two ANSI characters: The base character followed by a normal accent character. (Because ANSI does not have "combining" accent characters, the APIs again convert to the nearest equivalent; a "normal" accent character. Further on I speculate on why Windows made this choice.)
To continue the previous example, when browsing, TSE sees an Apple folder or file with an "é" character as "e'". Opening a folder with an Apple "é" in its name fails with an error, because TSE tries to open that name with "e'" instead. Opening a file with an Apple "é" in its name opens a new empty file with "e'" in its name instead.
Unlike with the alpha character you can work around the diacritical problem:
- The first work-around is to not use diacritical marks for folder and file names shared between Apple products and Windows.
- The second work-around is to create or once rename such folders and files in Windows: In my experience Apple products understand and do not change back the "non-combining" Windows convention for existing folder and file names with diacritical marks. Note, that you typically only need to do this for folder and file names that need to be accessible from TSE.
Here is my speculation about why Windows' Unicode to ANSI conversion chooses to convert a character with a combining accent to a character following by a separate accent character. A problem arises when such characters occur in a filename. Suppose an ANSI program reads a file with a name that contains a combining accent, and later writes the file back. If Windows had chosen to convert the character with the combining accent to one character, then the ANSI program will write the file back with a single accented character in its name, creating a second, seemingly and indistinguishibly same-named file! Windows' actual choice makes this Unicode problem explicit, and gives the ANSI program the capability to make its own choice on how to deal with it.
Bit 3 is a bit of a mystery. It is documented as the _VOLUME_ bit, but whatever I try FileExists() on, bit 3 is always set to 0.
Bit 4 is not just 1 if the parameter refers to a directory, but also if it contains a wildcard character and matches anything. For example, if a wildcard is used, then bit 4 will also be 1 if the file specification only matches a file.
Bit 7 is 1 if the file specification matches anything. Because it has not been documented, it is formally wrong to use _NORMAL_ for bit 7, but circumstantial documentation and extensive testing imply to me that this is a valid use of _NORMAL_.
It is surprisingly difficult to check if a file specification matches one specific file and is not a directory. Here is an example of how to achieve this. If the file specification contains a wildcard character, then the condition will return FALSE regardless. All the parentheses are necessary!
if (FileExists(file specification) & (_NORMAL_|_DIRECTORY_)) == _NORMAL_
Bug: Unlike its documentation states, the attribute _DIRECTORY_ cannot be used to specifically search for folders. Supplying _DIRECTORY_ has no effect whatsoever, because FindFirstFile() will still find everything when used with a wildcard in the filename.
Bug: Unlike its documentation states, this command will NOT find a folder unless a second parameter _DIRECTORY_ is supplied.
As of TSE Pro v4.2 this function also works for letters with diacritics for non-OEM fonts.
Given that the "for" statement syntax is:
for integer_variable = numeric_expression to/downto numeric_expression2 [by numeric_expression3] statements endforThe "statements" can influence the for-loop by changing the value of "integer_variable", but not by changing "numeric_expression", "numeric_expression2" or "numeric_expression3".
In other words, "numeric_expression", "numeric_expression2" and "numeric_expression3" are evaluated only once at the start of the for-loop.
In some cases this can make a "for" statement (possibly in combination with a "break" statement) more efficient than a "while" statement.
If "by numeric_expression3" comes after "to numeric_expression2", then a positive value for "numeric_expression3" will increase "integer_variable" and a negative value for "numeric_expression3" will decrease "integer_variable".
If "by numeric_expression3" comes after "downto numeric_expression2", then a positive value for "numeric_expression3" will decrease "integer_variable" and a negative value for "numeric_expression3" will increase "integer_variable".
That said, "numeric_expression3" should never be negative, because with it only for-loops can be written that loop zero or infinite times.
It could be argued that this is a bug, since its value is allowed to be negative but is then handled inconsistently.
For example:
for i = 1 to 2 by -1 -> infinite times for i = 1 to 1 by -1 -> infinite times for i = 1 to 0 by -1 -> zero times for i = 1 to -1 by -1 -> zero times for i = 1 to -2 by -1 -> zero times for i = 1 downto 2 by -1 -> zero times for i = 1 downto 1 by -1 -> infinite times for i = 1 downto 0 by -1 -> infinite times for i = 1 downto -1 by -1 -> infinite times for i = 1 downto -2 by -1 -> infinite times
If you use semicolons (";") instead of commas (",") to separate parameters, then the concatanated parameters will be separated by the equal amount of spaces.
For example, after
s1 = Format('a', 'b'; 'c';; 'd';;; 'e';;;; 'f';;;;; 'g')s1 will contain the string
'ab c d e f g'
If you keep reading when the end of the file has been reached, then fRead() keeps returning 0 for the bytes read, not -1 for an error.
Chr(0) bug: SetBufferStr() and GetBufferStr() strip characters with ASCII code 0 from the end of a value, and Warn() strips it and all other characters after one such a character.
Demo macro:
proc Main() integer hash_id = 0 integer org_id = GetBufferId() string s [MAXSTRINGLEN] = 'a' + Chr(0) hash_id = CreateTempBuffer() GotoBufferId(org_id) SetBufferStr('key', s, hash_id) Warn('Bug: "2 2 a ." <> "', Length(s); Length(GetBufferStr('key', hash_id)); s, '."') AbandonFile(hash_id) PurgeMacro(SplitPath(CurrMacroFilename(), _NAME_)) end Main
Beware of this risk! :-)
GetClockTicks() returns
the number of "clockticks" (the number of 1/18ths of a second)
since the start of the TSE session,
and TSE's integer limit is 2,147,483,647,
so your TSE session might destabilize if left open for
more than 3.78 years!
In TSE v4.44 a bug was solved, so that TSE better displays the monospace version of the Unifont font , and makes GetCharWidthHeight() return a more useful width and height for the Unifont font.
Advanced usage: As of TSE v4.44, TSE's GetCharWidthHeight() can also be used for the Unifont font for Windows' TextOut API, but still not for Windows's CreateFont API. The latter might not be a TSE problem, but could be a Unifont, API, or macro problem. See the Uniview extension for an example of how to possibly work around this.
Both the documentation and implementation of the getKeyFlags() function are full of errors.
Here is what I discoverd with the test macro further on:
CONSTANT DOCUMENTED DEFINED WHEN KEY VALUE VALUE PRESSED _RIGHT_SHIFT_KEY_ 1 16 16 _LEFT_SHIFT_KEY_ 2 16 16 _CTRL_KEY_ 4 12 8 _ALT_KEY_ 8 3 2 _SCROLL_LOCK_DEPRESSED_ 16 64 64 _NUM_LOCK_DEPRESSED_ 32 32 32 _CAPS_LOCK_DEPRESSED_ 64 128 128 _INSERT_DEPRESSED_ 128 0 0
Pressing the Insert key or the NumLock key either on or off will temporarily set bit 9 (value 256), but it does not stay set.
GetKeyFlags() always returns 0 in Linux TSE.
Test macro:
integer idle_counter = 0 proc show_key_flags() integer i = 1 integer key_flag_bits = 0 string key_flag_string [MAXSTRINGLEN] = '' string bits [32] = '' key_flag_bits = GetKeyFlags() if key_flag_bits >= 0 key_flag_string = 'KeyFlags: ' + Str(key_flag_bits) + ' (dec), ' i = key_flag_bits while i > 0 bits = Str(i & 1) + bits i = i / 2 endwhile bits = iif(bits == '', '0', bits) key_flag_string = key_flag_string + bits + ' (bin) ' if key_flag_bits & _RIGHT_SHIFT_KEY_ key_flag_string = key_flag_string + ' RightShift' endif if key_flag_bits & _LEFT_SHIFT_KEY_ key_flag_string = key_flag_string + ' LeftShift' endif if key_flag_bits & _CTRL_KEY_ key_flag_string = key_flag_string + ' Ctrl' endif if key_flag_bits & _ALT_KEY_ key_flag_string = key_flag_string + ' Alt' endif if key_flag_bits & _SCROLL_LOCK_DEPRESSED_ key_flag_string = key_flag_string + ' ScrollLock' endif if key_flag_bits & _NUM_LOCK_DEPRESSED_ key_flag_string = key_flag_string + ' NumLock' endif if key_flag_bits & _CAPS_LOCK_DEPRESSED_ key_flag_string = key_flag_string + ' CapsLock' endif if key_flag_bits & _INSERT_DEPRESSED_ key_flag_string = key_flag_string + ' Insert' endif Message(key_flag_string) else Warn('ERROR: GetKeyFlags < 0') PurgeMacro(SplitPath(CurrMacroFilename(), _NAME_)) endif end show_key_flags proc idle() if idle_counter idle_counter = idle_counter - 1 else idle_counter = 9 show_key_flags() endif end idle proc WhenLoaded() Hook(_AFTER_GETKEY_ , show_key_flags) Hook(_BEFORE_GETKEY_, show_key_flags) Hook(_IDLE_ , idle) Hook(_NONEDIT_IDLE_ , idle) end WhenLoaded proc Main() NewFile() AddLine(Format('CONSTANT' :-23; 'DOCUMENTED':10; 'DEFINED' :10; 'WHEN KEY':10)) AddLine(Format('' :-23; 'VALUE' :10; 'VALUE' :10; 'PRESSED':10)) AddLine(Format('_RIGHT_SHIFT_KEY_' :-23; 1:10; _RIGHT_SHIFT_KEY_ :10; 16:10)) AddLine(Format('_LEFT_SHIFT_KEY_' :-23; 2:10; _LEFT_SHIFT_KEY_ :10; 16:10)) AddLine(Format('_CTRL_KEY_' :-23; 4:10; _CTRL_KEY_ :10; 8:10)) AddLine(Format('_ALT_KEY_' :-23; 8:10; _ALT_KEY_ :10; 2:10)) AddLine(Format('_SCROLL_LOCK_DEPRESSED_':-23; 16:10; _SCROLL_LOCK_DEPRESSED_:10; 64:10)) AddLine(Format('_NUM_LOCK_DEPRESSED_' :-23; 32:10; _NUM_LOCK_DEPRESSED_ :10; 32:10)) AddLine(Format('_CAPS_LOCK_DEPRESSED_' :-23; 64:10; _CAPS_LOCK_DEPRESSED_ :10; 128:10)) AddLine(Format('_INSERT_DEPRESSED_' :-23; 128:10; _INSERT_DEPRESSED_ :10; 0:10)) BegFile() end Main
As documented this function has two var parameters, that return the next variable name and variable value from a section of an initialization file (typically with extension ".ini"), the contents of which for example look like:
[section name 1] var name 1=value 1 var name 2=value 2 var name 3=value 3 ...However, sometimes you need to store just a list of values, for example:
[section name 2] value 1 value 2 value 3 ...Undocumented is, that GetNextProfileItem() can handle that as long as the .ini file's data line contains no "=" character. In that case it returns an empty string for the variable name and it returns the whole line for the variable value.
For completeness sake: If a data line contains two or more "=" characters then GetNextProfileItem() returns everything before the first "=" character as the variable name and everything after the first "=" character as the variable value. For example for this data line:
var name 1=value 1=the best value=a great deal=the greatestthe function GetNextProfileItem() returns the two parameter values "var name 1" and "value 1=the best value=a great deal=the greatest".
GetNextProfileItem() will trim spaces from variable names and values. For instance, for a line " variable 1 name = variable 1 value " GetNextProfileItem() will return the parameter values "variable 1 name" and "variable 1 value".
GetNextProfileItem() only ignores empty lines, so there is no way to comment lines in a section that GetNextProfileItem() might be used on. The only safe ways to comment a TSE initialization file is before the first section or to put comments in their own separate section(s). For example:
This is a comment. [section 1] var1=value1 [section 2 comments] The value "value1" may only be the string "true" or "false". [section 2] var1=value1
The parameter "item" stands for the item's name.
Spaces around the item name are irrelevant, both in the parameter and in the initialization file.
If a section in the initialization file contains the same item name twice, then GetProfileStr() will return the value of the first occurrence.
The parameter item name may be an empty string. For example, if the initialization file looks like this:
[file extension descriptions] .s=This is a SAL (Semware Application Language) macro. .txt=This is a text file. =This is an extensionless file. .pl=This is a Perl program.then GetProfileStr('file extension descriptions', '', 'Not Found') will return "This is an extensionless file.".
See GetNextProfileItem() for more info on the initialization file.
To define it more specifically, the second format, GetTime() without parameters, returns the number of hundredths of seconds since the last midnight.
GetToken(string s, string delimiter, integer token_no) works differently if the delimiter is a space character:
- Multiple spaces are treated as one space.
- Leading spaces are ignored.
For example, Gettoken(' a b', ' ', 1) returns "a" and Gettoken(' a b', ' ', 2) returns "b" instead of empty strings.
This command does not change the column position.
Bug: Unlike what its documentation states, Help() always returns FALSE.
TSE has two help files: tsehelp.hlp and compile.hlp.
tsehelp.hlp contains TSE's default Help. Once in the default Help there is no way to access compile.hlp. From a compile.hlp topic you can switch to the default Help using the Contents or Index function. The Search function searches the current Help.
The compile.hlp file contains descriptions for 40 advanced compilation topics related to TSE's Macro -> "Compile Menu" menu and submenus.
The Help() command can access a known topic like "Add Compiler->Command" in the compile.hlp file with this command:
Help('compile|Add Compiler->Command')
compile.hlp does not have an Index, and its topics are not indexed in the default Help.
This function has an optional integer parameter.
If UserHiliteFoundText() is FALSE, then HiLiteFoundText(N) will hilite a string N positions to the right of where it otherwise would. N can be negative to hilite to the left.
According to Semware this parameter was once upon a time created for one user for one macro.
According to Semware, 24 Aug 2021, using the Hook() command, the editor's maximum number of hooks is 168.
According to a test I did on 16 Dec 2022 in Windows GUI TSE v4.48, the editor's maximum number of hooks is 158.
The tool Iused was Test_hooks_free .
For what it is worth, I am a heavy user of macros with hooks, and on 16 Dec 2022 my Windows GUI TSE installation used 76 hooks.
As the syntax implies, you can attach multiple procedures to the same hook. The hook will call these procedures in the reverse order of their hooking.
Here are undocumented properties for these specific hooks:
It was not possible to update the screen from an idle event, but as of the GUI version of TSE Pro v4.4 it is: See the Transparency topic.
This hook exists from the GUI version of TSE Pro v4.2 onwards.
It is also called when TSE closes. A Warn() statement from a _LOSING_FOCUS_ hook, that is called when TSE closes, might leave a hidden TSE session. I am still investigating the details.
Contrary to what its documentation states, the _ON_FIRST_EDIT_ hook is also called when changing an already loaded file's name.
I received this information from Semware:
HookDisplay(currline_offset, before_offset, after_offset, foundtext) currline_offset, before_offset, after_offset, foundtext_offset are macro procs that are called at certain times: currline_offset : called for each line (redrawline) before_offset : called before redraw after_offset : called after redraw foundtext_offset: called when hilited text is found See debug.si, potpourr.s, and whtspc.s for examples.
The following information was deduced and tested.
HookDisplay([proc1], [proc2], [proc3], [proc4]) sets DisplayMode(_DISPLAY_USER_) and optionally hooks each of four types of events to the combination of their corresponding proc name, the current buffer at the time of HookDisplay(), and DisplayMode() being _DISPLAY_USER_.
If afterwards another buffer is current or another DisplayMode() is set, the procs are still hooked but not called. As soon as both the buffer at the time of HookDisplay() is current again, and DisplayMode() is _DISPLAY_USER_ again, the hooks are called again.
Multiple buffers can have their own different HookDisplay() hook(s) simultaneously. Only memory limits the potential number of HookDisplay() hooks.
All three commas are mandatory. If you do not want to use all four hooks then you just leave out a proc name. For example, HookDisplay(,,,MyProc) is legal syntax if proc MyProc() exists.
Only the first proc can optionally be defined with an integer parameter. The integer parameter receives a boolean value that is TRUE when the proc is called for the current line and FALSE otherwise.
For the first proc UpdateDisplay() notes what the current line is,
and then for each screen line temporarily sets the text cursor and the
video output position to their first positions on that screen line
and calls the first proc.
In other words, each time proc1 is called,
its optional parameter is TRUE or FALSE to indicate if it is called for
the current line,
CurrLine() temporarily returns the buffer line proc1 is called for,
Currxoffset() + 1 temporarily returns the buffer's first screen column,
VWhereY() temporarily returns the video output line proc1 is called for,
and VWhereX() returns 1.
proc1 can do without VWhereX() and VWhereX() by just writing to the
screen with PutChar() statements, closing them off with a ClrEol()
statement.
Before proc1 no line is drawn: proc1 does not overwrite the line, it
writes it, and as far as TSE is concerned is its only writer.
For example: The Potpourri menu uses DisplayHook() to colour the first 12 characters of each line differently, but with DisplayMode(_DISPLAY_TEXT_) it temporarily disables that while viewing the help for an item (F1) and while editing the help text for an item (Alt E), and it sets DisplayMode(_DISPLAY_USER_) back when these excursions from the Potpourri menu are done.
UnhookDisplay() unhooks the events, and restores DisplayMode() to the value it had when HookDisplay() was called.
The "in" operator has non-intuitive behaviour.
The "in" operator is correctly implemented as documented, but it deserves an explicit mention because of
"and" and "or" are higher precedence operators than "in" and ",".
For instance, the condition
if (2 in 1, 2, 3) and FALSEevaluates to
FALSE
But the condition
if 2 in 1, 2, 3 and FALSEevaluates as
if 2 in 1, 2, (3 and FALSE)which evaluates as
if 2 in 1, 2, 0which evaluates to
TRUE
"not" is a higher precedence operator than "in" and ",".
For instance, the condition
if not (2 in 4, 5, 6)evaluates to
TRUE
But the condition
if not 2 in 4, 5, 6evaluates as
if (not 2) in 4, 5, 6which evaluates as
if 0 in 4, 5, 6which evaluates to
FALSE
Advice:
Whether (you are in doubt) or (you are not in doubt),
always use parentheses with the "in" operator.
You cannot include a file from an included file. That is, nested includes do not work.
The inserted data is marked as a character block.
Additial note: This command also fails if the supplied file does not exist.
InsertFile() no longer works for an alternate data stream (ADS).
This is not a bug report, but just a write-up of a discovered and
researched TSE change.
Up to and including TSE Beta 4.40.97 (2016 Oct 22) the command
InsertFile("my_text.txt:my_ads")
loaded that ADS if it existed.
Versions from and after TSE Beta v4.40.99 (2018 Apr 28) return
"Open error: <filename>"
for the same command.
It was handy that InsertFile() worked for ADSes, but it is not a
documented or required capability.
The function name and the first line of the official documentation are misleading as to its broader functionality, which is correctly explained in the rest of the topic as recognizing a string that consists of one or more digits.
Another way to define isDigit(s) is, that for every value of string s:
isDigit(s) == StrFind('^[0-9]#$', s, 'x'))Note that the value of isDigit('') is FALSE, and that the value of isDigit() depends on the character under the cursor.
This function is obsolete and always returns 0.
The documentation states it a bit wobbly.
isMacroLoaded() also accepts a public procedure's name as its single argument. See the example below.
public proc non_existing_macro() Warn('But I do exist!') end non_existing_macro proc Main() ExecMacro('non_existing_macro') Warn('IsMacroLoaded ='; iif(isMacroLoaded('non_existing_macro'), 'TRUE', 'FALSE')) PurgeMacro(CurrMacroFilename()) end Main
If a macro uses KeyDef to limit user keys while editing text, then CUAMark and "friends" (other macros that work likewise) do not honour those limits.
That is because CUAMark does not use key definitions for most of its editing keys, but it checks what keys were pressed and then adds it own functionality, whether a key is currently defined or not. Luckily CUAMark and friends are an exception, but on occasion they have to be dealt with.
A work-around for TSE Pro v4.4 upwards is for the "KeyDef-macro" to be lower in TSE's Macro AutoLoad List than CUAMark and friends or to be otherwise loaded later than CUAMark and friends, and for the "KeyDef-macro" to implement this _AFTER_GETKEY_ hook:
proc after_getkey() if not isKeyAssigned(Query(Key)) BreakHookChain() // Nice solution: leaves Key's value unchanged. // Set(Key, -1) // Hard solution: changes Key to dummy value. endif end after_getkey proc WhenLoaded() Hook(_AFTER_GETKEY_, after_getkey) end WhenLoaded
KeyPressed() has a useful side-effect for macros that run a long time.
Probably since an earlier Windows version but definitely since Windows 10, when a macro runs for some time without user interaction, Windows stops updating the screen and shows an hourglass until the macro is finished.
This is not a TSE-specific issue but a general Windows issue.
This typically is a problem if the macro is showing a progress indicator. Any other visual progress on the screen is halted too.
KeyPressed() to the rescue!
By adding a KeyPressed() to the macro inside a loop where it will be called regularly, Windows sees an attempt at user interaction and will not disable screen updates.
Since KeyPressed() has no other side-effects, it will not effect the macro's functionality.
In Linux KeyPressed() waits 0.05 seconds for a key, which is annoyingly slow when used inside a loop.
This value was chosen to give keystrokes through Putty the ability to be processed.
For now the solution is to make our macros compensate for this behavior.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
lDos() also has a flag _START_HIDDEN_, which will cause the command to invisibly start and run in a hidden Windows process.
In newer versions of TSE the _DONT_PROMPT_ and _DONT_CLEAR_ flags can be omitted when using the _START_HIDDEN_ flag, but that makes the macro not backwards compatible, so I advise against it.
lDos() also has a flag _DONT_WAIT_, which when combined with the _RUN_DETACHED_ flag will make the started command a parallel process.
For example,
#define DOS_SYNC_CALL_FLAGS _DONT_PROMPT_|_DONT_CLEAR_|_START_HIDDEN_|_RETURN_CODE_ #define DOS_ASYNC_CALL_FLAGS _DONT_PROMPT_|_DONT_CLEAR_|_START_HIDDEN_|_RETURN_CODE_|_RUN_DETACHED_|_DONT_WAIT_ if TRUE lDos(LoadDir(TRUE), '', DOS_ASYNC_CALL_FLAGS) else Dos(LoadDir(TRUE), DOS_ASYNC_CALL_FLAGS) endifstarts a second instance of the editor, and you can work in both instances of the editor simultaneously.
Here lDos() is better because Dos() also starts a useless blank window.
Without _RUN_DETACHED_ the lDos() command "eats" any unprocessed keys, making TSE ignore them. In tools that is usually not a problem, in extensions it ususally is.
lDos() also has a flag _DONT_CHANGE_TITLE_.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
Bug: The documented parameter _ALWAYS_LOAD_ does not exist, use -1 instead.
The current buffer is emptied when the supplied file does not exist.
This function has an optional integer parameter, which if not zero (not FALSE) will make LoadDir() return the fully qualified name of TSE's executable.
For example for the GUI version of TSE:
exe_string = LoadDir(TRUE) // exe_string == 'C:\Users\Carlo\Tse\g32.exe'
As of TSE Pro v4.2 this function also works for letters with diacritics if the editor uses an ANSI compatible font like Courier New.
If used to execute and terminate a loop within a file (the original Find() or lFind() it was based on did not use the "a" option to search across files), then this command automatically stops.
Bug: If used to execute and terminate a loop across files (the original Find() or lFind() it was based on did use the "a" option to search across files), and the search string occurs in more than one open file, then the loop becomes infinite, i.e. never stops.
White space means a sequence of space and horizontal tab characters.
The built-in documentation states that the available stack space is "approximately 16k". A test says it is approximately 61k.
This function has an optional integer parameter.
The parameter is the block_type, as in: _INCLUSIVE_, _NON_INCLUSIVE_, _LINE_, _COLUMN_.
To be more specific, MakeTempName() only looks at the disk to create a new unique filename, not at TSE's unsaved buffers nor at its own previous call results.
You can supply the optional extension parameter with or without a leading period.
Aside, the numbers MakeTempName() generates are not as random as they seem, because in tests without saved disk files they consistently turned repetitive within 200 tries.
The editors maximum line length is documented as being 16000, while it actually is 32000.
If you use semicolons (";") instead of commas (",") to separate parameters, then the concatanated parameters will be separated by the equal amount of spaces.
For example
Message('a', 'b'; 'c';; 'd';;; 'e';;;; 'f';;;;; 'g')will show the string
'ab c d e f g'
TSE strings can have a maximum length of 255 characters, but based on experiments MkDir's parameter is limited to a maximum length of 247 characters.
To overwrite the destination file the third parameter can be any truthy value, i.e. any value not equal to FALSE.
For example, instead of TRUE we can also use the more readable constant _OVERWRITE_.
When you pass two parameters to MsgBox(), then MsgBox() presents an OK button, but always returns FALSE, even when you select the OK button.
This is a bug, because the documentation states, that FALSE is returned for pressing <Escape> and that for a selection its number is returned.
The "buttons" parameter is limited to 80 characters, including the square brackets and any ampersands.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
NextChar(variable) returns FALSE if the variable has value 0.
NextChar(0) returns an unpredictable integer value that varies with the context in which the command is used. The return value can be 0 too. On the bright side, in practice it makes no sense to use a hardcoded 0 as NextChar()'s parameter, let alone question its return value.
NoOp() has the erroneous side effect, that when it is used in an _AFTER_GETKEY_ hook, then it disables the CUAmark macro's ability to delete a shift-marked block when we start typing.
The logical work-around is to use nothing or a comment instead of NoOp().
The OEM font that TSE internally (*) uses in Windows GUI TSE versions has been broken since TSE v4.40.98 (13 Sep 2017) or v4.40.99 (26 Feb 2018 and 28 Apr 2018).
(*) Here, "internally" mainly means in GUI TSE's help, menus, and borders, and in the macro command PutOemStrXY().
The bug is, that control characters (having character codes below 32) and non-ASCII characters (having character codes above 127) are displayed as spaces or drawing-characters.
The most noticeable effect in GUI TSE itself is, that its built-in help
now displays the often occurring bullet character as a space,
leading to weirdly formatted text.
My
HelpRepair
extension fixes displaying the bullet character.
Otherwise this bug mostly affects non-English users that use the GUI version of TSE with non-Semware/private tools and extensions that use an OEM font, because diacritics are no longer supported.
Semware has reported having reproduced the bug and still having the TSE sources from when the bug started occurring, but has not been able to fix it.
This demo macro displays all internal OEM characters in an 16 x 16 rectangle:
proc Main() integer row = 10 integer c = 0 string s [32] = '' ClrScr() for c = 0 to 255 s = s + Chr(c) if (c + 1) mod 16 == 0 PutOemStrXY(10, row, s, Color(Bright Yellow ON Blue)) s = '' row = row + 1 endif endfor GetKey() UpdateDisplay(_ALL_WINDOWS_REFRESH_) PurgeMacro(SplitPath(CurrMacroFilename(), _NAME_)) end Main
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
Bug: The _OVERWRITE_ parameter does not do anything.
PushUndoMark() and PopUndoMark() are undocumented functions with a serious bug.
Their intended function seems to be, that you can do PushUndoMark() in a normal buffer, do any number of temporary changes, and then restore the buffer with PopUndoMark() Undo().
However, if there were no changes between PushUndoMark() and PopUndoMark(), then PopUndoMark() Undo() restores the buffer to an extra Undo() earlier than that.
Demo of the bug:
/* This macro demonstrates a bug in PushUndoMark() and PopUndoMark(). For UNDO_MARK = 50 it correctly creates a new buffer with numbers 1 to 50. for UNDO_MARK = 100 it incorrectly creates a new buffer with numbers 1 to 91. The bug can be phrased this way: If there were no changes between PushUndoMark() and PopUndoMark(), then PopUndoMark() and Undo() restore the buffer not to its state at PushUndoMark(), but to one extra Undo() before that. */ proc Main() integer i = 0 integer UNDO_MARK = 50 NewFile() for i = 1 to 100 if (i - 1) mod 10 == 0 AddLine(' ' + Str(i)) else EndLine() InsertText(' ' + Str(i), _INSERT_) endif if i == UNDO_MARK PushUndoMark() endif endfor PopUndoMark() Undo() PurgeMacro(SplitPath(CurrMacroFilename(), _NAME_)) end Main
Example of how PushUndoMark() and PopUndoMark() can be used safely:
proc Main() integer old_UndoCount = 0 // ... Get to the point where a current buffer's state needs preserving ... // Save the buffer's current state. old_UndoCount = UndoCount() PushUndoMark() // ... Do stuff to it that needs reversing later ... // Restore the buffer's saved state. PopUndoMark() Undo() if UndoCount() < old_UndoCount Redo() // Correct for the PopUndoMark() bug's extra Undo(). endif // ... Go on with the restored buffer ... PurgeMacro(SplitPath(CurrMacroFilename(), _NAME_)) end Main
Unlike its name suggests and the documentation states, the keyboard is completely bypassed, and no key appears pressed anywhere in any way.
No TSE command is capable of noticing a key was "pressed" by PressKey(), and the passed key is not stored in TSE's "Key" variable.
Instead PressKey() directly executes the commands that are assigned to the key, and any statements after PressKey() are not executed until PressKey()'s assigned commands are finished.
Multiple PressKey() commands are not capable of executing commands that are assigned to a combination of two keys. You can halfway get around that by doing a PushKey() of the second key followed by a PressKey() of the first key.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter.
PrevPosition() only tracks file and line position changes.
PrevPosition(1) also tracks column position changes.
This function has an optional integer parameter.
The intended functionality is this:
The actual functionality is different and much more elaborate, but in its current usage the differences are irrelevant:
Terminology is very important here. Let's follow and build on Semware's terminology as much as possible.
The Process() topic states that it starts a new "editor process".
It would be more specific to state that Process() starts the "editing loop" of a new "editor process".
An "editing loop" is an optional part of an "editor process". For instance at the start of a .ui file's WhenLoaded() procedure there already is an "editor process" that is running the WhenLoaded() procedure but there is no "editing loop" yet. When Process() starts an "editor process" it also starts its "editing loop".
Semware speaks of "the main editing loop" in the "Event", "Hook", and "QueryEditState()" topics.
In TSE's context an "editing loop" is the part of the "editor process" that for the current editing buffer waits for and acts on your key strokes.
QueryEditState() will return zero for an editing loop started by Process() too.
Process() starts a new editor process like a subroutine: The editor process that calls Process() waits until the editing loop of the called editor process is finished before continuing with any next statement.
A called editor process inherits the complete state of the calling editor process.
When the called editor process finishes, the calling editor process inherits any state back that the called editor added with the specific exception of key definitions.
Any called Process() can initiate its own Process(), thus creating a stack of called editors processes.
A called editor process can mainly be exited in two ways:
EndProcess() ends the editor process at the top of the stack of those editors processes that were created with Process(). So it does nothing when executed in the base editor process, because that was not started by a Process().
Exit() possibly ends the whole stack of those editor processes that were created with Process(), and sometimes the base editor process.
For Exit() it matters if the base editor process has an editing loop or not. If the WhenLoaded() procedure in the editor's .ui file starts a Process() and thus the first editing loop, then the base editor process has no editing loop.
At the time Exit() is called
if all the editor processes on the stack were called with Process() or Process(0), then Exit() immediately terminates all editor processes without executing any statements after any Process(), otherwise Exit() will will wait for editing loops to be (re)turned to and terminate them, until no more editing loops exist or a new file is opened.
Note that not all of this is necessarily logical behaviour. For instance the above "if" condition reveals that a non-zero child Process() parameter can negate a zero parent Process() parameter. The described behaviour was revealed by extensively testing commands beyond their current usage.
Additionally to what its documentation states:
Without a parameter the user is presented with a list of loaded macros to purge.
As a parameter you can also supply the macro's filename, with or without its path. Any file extension is ignored.
E.g. these commands makes a macro purge itself when it finishes running:
PurgeMacro(CurrMacroFilename())
PurgeMacro(Splitpath(CurrMacroFilename(), _NAME_) + '.DoesNotExist')
Unlike the documentation states, the key stack is not limited to 64 keys. That was true for TSE v2.8 and below. As of TSE v3.0 the key stack is limited to 512 keys.
The documentation uses the term "stack" to describe PushKey()'s behaviour.
Stack explanation:
A stack implies, that when you put things on a stack in a certain order,
then you have to take them off off the stack in the reverse order.
For PushKey() this stack-like behaviour means for example that
PushKey(<a>) PushKey(<b>) PushKey(<c>)results in TSE's next keyboard reading commands seeing the text "cba".
Terminology:
TSE's documentation contains a pleonasm: It states that its keyboard stack is
a LIFO (Last In First Out) stack. But all stacks are LIFO. That is what the
term "stack" means. The common counterpart of a stack is a queue. A queue is
processed FIFO (First In First Out).
Unlike the documentation states, the key stack is not limited to 64 keys. That was true for TSE v2.8 and below. As of TSE v3.0 the key stack is limited to 512 keys.
For example, the sequence of commands
PushKeyStr('blue') PushKeyStr('pink')results in TSE's next keyboard reading commands seeing the text "pinkblue".
For example, to enter two lines of text, the stack-principle applies too, so push the Enter key before pushing each line of text, and push the lines in reverse order:
PushKey(<Enter>) PushKeyStr('This is the second line.') PushKey(<Enter>) PushKeyStr('This is the first line.')
See PopUndoMark().
In Windows GUI TSE the OEM font bug applies to this command.
Bug: PutStrAttrXY(..., ..., ...., '', Color(Black on Black)) uses Query(Attr) instead of Color(Black on Black).
This bug is specific to the Color(Black on Black) value.
For example, this will display a yellow on blue text:
Set(Attr, Color(bright yellow ON blue)) PutStrAttrXY(5, 5, 'Hello world!', '', Color(black ON black))
A work-around is to Set(Attr, Color(Black on Black)) before using PutStrAttrXY().
QueryEditState() returns a combination of the following flags:
Name | Value | Description |
---|---|---|
_STATE_TWOKEY_ | 0x0004 | Waiting for 2nd key |
_STATE_WARN_ | 0x0008 | At a Warn prompt |
_STATE_EDITOR_PAUSED_ | 0x0010 | Dos or ShowEntryScreen |
_STATE_POPWINDOW_ | 0x0020 | A popwin is active |
_STATE_PROCESS_IN_WINDOW_ | 0x0040 | Editor has entered a recursive level |
_STATE_MENU_ | 0x0080 | Menus are active |
_STATE_PROMPTED_ | 0x0100 | Read in use |
_STATE_BUSY_ | 0x0200 | File is being loaded. (The name "_STATE_BUSY_" was added in TSE 4.48.) |
If QuitFile() closes the last open file, then (without calling the _onexit_called_ hook) implicitly an Exit() is done.
Therefore also see: Process().
TSE's maximum string length is 255 characters. When quoting an unquoted string of 254 or 255 characters, QuotePath() deletes the last 1 or 2 characters of the string respectively, so that it always can add the quotes.
Random()'s bugs for input > 32767 and for lo == hi have been fixed in TSE v4.46.
This function takes a string parameter and returns the string without a trailing slash if it had one.
Examples:
s = RemoveTrailingSlash('a') // s == 'a' t = RemoveTrailingSlash('a\') // t == 'a'
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
White space means a sequence of space and horizontal tab characters.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
Obviously and implicitly an Exit() is done too.
Therefore also see: Process().
As documented in Hook(), this function does not call the _ON_FILE_SAVE_ and _AFTER_FILE_SAVE_ hooks.
As documented in Hook(), this function does not call the _ON_FILE_SAVE_ and _AFTER_FILE_SAVE_ hooks.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
Here is the most insignificant TSE bug I have ever found, but it is somewhat relevant, because it might confuse users who try to delve into the rest:
If you use TSE's regular Find() command to search without the "+" option for a regular expression that matches an empty string, and you do that twice without changing the cursor position, then TSE hilites the current cursor position.
This is a hiliting bug: in this case a character is hilited when an empty string is found.
Examples:
Find('.*', 'x') // Anywhere in or at the end of a line. Find('.@', 'x') // At the end of a line.
The regular expression character "?" is non-greedy. In TSE's terminology: it uses minimal closure. Both mean to say that it prefers to match an empty string. A scary example: if a text contains "book" and you search for "book?" then you will find "boo" (highlighted).
Here is a cool pattern matching hack:
If you create a regular expression with a sequence of tagged patterns AND group them with a tag AND follow that group with "|<something that always matches>", then as long as from left to right the tagged subpatterns match, each match still gets a tag number (that is 1 higher than its occurrence).
For example, if a text contains a line with 3 items like
"(a,b,c)"
and you search that line for 5 subpatterns like with
"{({a},{b},{c},{d},{e})}|{,}"
with the "cgx" option, then for n = 1 to 5 the statement
GetFoundText(n + 1) will return the subpatterns that matched until
a non-matching one is found and an empty string otherwise;
here "a", "b", "c", "", "".
Note 1: The patterns before the "|" must not be able to overlap for this to work. So for example instead of a list of patterns like '"{.*}","{.*}"' use a list of patterns like '"{[~"]*}","{[~"]*}"'. In other words, the list of patterns must be allowed to fail as a whole, and while failing the regular expression will try to match longer strings for a ".*" pattern, which much fail for our purpose.
Note 2: The <something that always matches> part is tricky: not all logical values work; you might have to experiment a bit.
The following is not an undocument feature, but a caveat about implied behaviour that macro programmers tend to stumble upon.
".*" "[0-9]*" "[A-Za-z]?" "{a}|{.*}"
"a@" // If the current character is not an "a". "{a}|{.@}" // Always at the end of a line.
Improved syntax: SearchPath(fn, paths [, subdir])
paths is a list of 1 to N paths separated by ";", and its total length is limited to MAXSTRINGLEN.
When no subdir is supplied, only paths are searched.
What happens when a subdir is supplied, can best be explained by an example.
SearchPath("myfile.txt", "c:\path1;c:\path2", "subdir") searches myfile.txt in these 8 directories in this order: . (the current directory) path1 path1\subdir path2 path2\subdir LoadDir() LoadDir()\subdir SplitPath(LoadDir(TRUE), _DRIVE_|_PATH_) SplitPath(LoadDir(TRUE), _DRIVE_|_PATH_)\subdir
Note that myfile.txt is not searched in subdir in the current directory.
SplitPath(LoadDir(TRUE), _DRIVE_|_PATH_) is the path where TSE's
executable resides.
LoadDir() is default the same, but not if the editor was started with
the "-i" parameter or if an environment variable TSELOADDIR was defined.
See
Augmenting the Editor Load Directory
for a further explanation.
SearchPath with a subdir parameter is dangerous!
In situations where the current directory can be anything,
making it the first search result can lead to unintended outcomes.
Chr(0) bug: SetBufferStr() and GetBufferStr() strip characters with ASCII code 0 from the end of a value, and Warn() strips it and all other characters after one such a character.
Demo macro:
proc Main() integer hash_id = 0 integer org_id = GetBufferId() string s [MAXSTRINGLEN] = 'a' + Chr(0) hash_id = CreateTempBuffer() GotoBufferId(org_id) SetBufferStr('key', s, hash_id) Warn('Bug: "2 2 a ." <> "', Length(s); Length(GetBufferStr('key', hash_id)); s, '."') AbandonFile(hash_id) PurgeMacro(SplitPath(CurrMacroFilename(), _NAME_)) end Main
One:
SetFont() does not always return FALSE when it fails.
My advice is to also follow it up with a GetFont() to determine if it did what you wanted.
Two:
Since TSE v4.40.34 (14 Nov 2007)
the command SetFont('Terminal', <a pointsize>, 0)
no longer changes the font to Terminal, though it does still set the
current font to the new pontsize.
I posed a question to Semware on whether this change was a bug or a repair.
SetFont('Terminal', <a pointsize>, _FONT_OEM_) does still work.
Interactively changing the font to Terminal silently sets it to _FONT_OEM_.
No other pre-installed TSE font has Terminal's now limited capability.
Three:
See OEM font bug.
As the CreateBuffer() help states:
"Undo/Redo information is NOT recorded for _SYSTEM_ buffers."
TSE has "three" sort programs:
The internal Sort() function.
The TSE Help documentation states, that it needs a marked block to sort on, that it only uses the first 80 characters of that block as its sort key, and that it can sort a maximum of 65535 lines.
Tests show the maximum sort key size is actually 282 characters.
The external tsort.com executable.
Start it from the command line with "tsort /?" to see its possible arguments. Where it says "99" you can actually use more than two digits.
Tests show its maximum sort key size is 4095 characters.
Horribly, tests also show that it splits lines that are longer than 8191 characters!
The Sort.s macro, used by TSE's menu.
Based on convoluted rules this tool either calls the internal Sort() function or the external tsort.com executable.
The Sort macro calls the internal Sort() function if one of these conditions applies:
How | When |
---|---|
Silently | If no block is defined. |
Silently | If the block has less than 2000 lines. |
After asking you | If it cannot find the external tsort.com program. |
After asking you | If the current file is in binary mode. |
After asking you | If the current block contains a vertical tab, a carriage return or a line feed character. Note that a TSE buffer normally contains no carriage returns and line feeds when the buffer is not in binary mode. |
If you are asked " ... Use slow sort?", then this refers to the internal Sort function, and if you reply No or Cancel or Escape, then no sort is done at all.
Sad conclusion:
There are files that TSE cannot sort.
The menu's Sort a.k.a. the Sort macro, hides which set of limits
applies and thereby why a sort fails.
Moreover, when any TSE sort fails, it often does so silently,
and possibly even horribly.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
The function StrCount(needle, haystack) counts overlapping needles too.
Examples:
StrCount('aa', 'aaa' ) // Returns 2. StrCount('aa', 'aaaa') // Returns 3.
This function is imperfectly documented, and the 4th parameter is incorrectly implemented.
Abbreviated syntax:
result = StrFind(needle, haystack, options, start, var len)StrFind's return value (further on referred to as "Result") is documented as:
The fourth parameter "start" is weirdly documented as:
The fourth parameter was better named "occurrence", and was better defined as:
Especially for backward searches the "start" parameter is incorrectly implemented; its results are inconsistent.
Below is the output of a test macro that demonstrates the problems. When two separate assesments are given, then the first pertains to the returned result and the second to the returned length. It is acceptable that start 0 is treated as default for start 1. It is acceptable that for result 0 length has no defined value.
StrFind('b', 'abcabc', 'i',-1, len) // Result = 1, Length = 0 WRONG StrFind('b', 'abcabc', 'i', 0, len) // Result = 2, Length = 1 ACCEPTABLE StrFind('b', 'abcabc', 'i', 1, len) // Result = 2, Length = 1 OK StrFind('b', 'abcabc', 'i', 2, len) // Result = 5, Length = 1 OK StrFind('b', 'abcabc', 'i', 3, len) // Result = 0, Length = 1 OK, ACCEPTABLE StrFind('b', 'abcabc', 'bi',-1, len) // Result = 7, Length = 0 WRONG StrFind('b', 'abcabc', 'bi', 0, len) // Result = 5, Length = 1 ACCEPTABLE StrFind('b', 'abcabc', 'bi', 1, len) // Result = 5, Length = 1 OK StrFind('b', 'abcabc', 'bi', 2, len) // Result = 5, Length = 1 WRONG StrFind('b', 'abcabc', 'bi', 3, len) // Result = 2, Length = 1 WRONG StrFind('b', 'abcabc', 'bi', 4, len) // Result = 0, Length = 1 OK, ACCEPTABLE
StrFind() is "slow". This is because it is implemented as pasting the haystack into a helper buffer and then doing an lFind(). Anything that updates a TSE buffer is "slow". This "slowness" is only human noticeable when a macro does massive amounts of StrFind()s. In such cases it pays where possible to use Pos() or FastStrFind() in its stead.
This function does not have the errors of StrFind.
StrReplace('b', 'abcabc', 'B', 'i',-1) // Result = "abcabc" ACCEPTABLE StrReplace('b', 'abcabc', 'B', 'i', 0) // Result = "aBcaBc" ACCEPTABLE StrReplace('b', 'abcabc', 'B', 'i', 1) // Result = "aBcaBc" OK StrReplace('b', 'abcabc', 'B', 'i', 2) // Result = "abcaBc" OK StrReplace('b', 'abcabc', 'B', 'i', 3) // Result = "abcabc" OK StrReplace('b', 'abcabc', 'B', 'bi',-1) // Result = "abcabc" ACCEPTABLE StrReplace('b', 'abcabc', 'B', 'bi', 0) // Result = "aBcaBc" ACCEPTABLE StrReplace('b', 'abcabc', 'B', 'bi', 1) // Result = "aBcaBc" OK StrReplace('b', 'abcabc', 'B', 'bi', 2) // Result = "aBcabc" OK StrReplace('b', 'abcabc', 'B', 'bi', 3) // Result = "abcabc" OK StrReplace('b', 'abcabc', 'B', 'bi', 4) // Result = "abcabc" OK
A string slice with only one parameter has length 1.
For example, the string slices s[3] and s[3:1] work exactly the same, both before and after the assigment operator.
Examples:
string s[9] = 'abcde' string t[9] = '' t = s[3] // t == 'c' s[4] = 'x' // s == 'abcxe'
Info:
Delimiters can be strings with letters: Such delimiters are case
sensitive.
Bug:
In the TSE submenu "SyntaxHilite Mapping Configuration" ->
"Delimiter Tokens" we can define three Quotes and an Escape character
for each Quote.
Testing shows, that each Escape character applies to its matching Quote
only, so the position of the Escape character in the configuration menu
matters.
The bug is, that if a previous Quote has no Escape character in the
configuration menu and an Escape character is applied to a later Quote
in the menu, then initially and visually the menu seems to do so, but
after (!) leaving the menu when the changes are automatically saved to
the .syn file, then the Escape character is moved to the first Quote
that had no Escape character.
So you did not escape the Quote that you thought you did.
Work-around: define a Quote that has an Escape character before a Quote
that has none.
Info:
Examples of what strings are hilited as, assuming default SAL syntax hiliting and "-" being part of the hiliting Keyword WordSet:
String(s) | Hilited as |
---|---|
1b | A (binary) number. |
1a | One text. |
+1 | A keyword and a number. |
-1 | A bug and a number. |
1a+b | Text, a keyword and text. |
1a-b | One text. |
1-ab | A number, a bug and text. |
and or | A keyword, text and a keyword. |
andor | One text. |
Grey+ | One keyword. |
Grey- | One keyword. |
Grey+proc | Two keywords. |
Grey-proc | One text. |
Note, that it is quite common that predefined keywords are not hilited as such.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This setting determines the format of FFTimeStr() too.
Changing this setting only works in the GUI version of TSE.
TSE made the unfortunate implementation choice of making the whole TSE window equally transparent, which makes TSE's menus and text vague. I have seen another tool only making the editing text's background color(s) transparent, which looks awesome and retains the text's readability.
Trick:
Changing the Transparancy setting refreshes the screen.
This can be a work-around in cases where UpdateDisplay() does not work,
for example from an _IDLE_ or _NONEDIT_IDLE_ hook.
For these hooks this needs to be followed by an UpdateDisplay(),
in other cases the screen updates without it.
Example:
Set(Transparency, Query(Transparency)) UpdateDisplay() // Mandatory for _IDLE_ hook, optional in some other cases
White space means a sequence of space and horizontal tab characters.
I tried to reverse engineer the meaning of TSE's history lists data structure, and I think I have got it. This in itself will not solve the GetFreeHistory() bug, but it might help. It will help me if I decide to (automatically?) clean up history list imperfections.
Documented in TSE's Help:
TSE's built-in history lists have a history list number above 127. User/macro history lists have a history list number from 1 to 127. User/macro history lists also have a history list name, that is referenced by the GetFreeHistory() macro statement.
Reverse engineered:
When TSE is closed, its history lists are stored in the file tsehist.dat in binary mode -2. The first line contains a file type verification string starting at column 2. If the byte code value L of the first character of that first line is not 0, then it indicates, that user history list names occur at lines 2 to (L + 1), and that user history list values occur from line (L + 2) onwards.
Each history list has names and values that are each represented by a line: The byte code value N of the first character of the line is the history list number. The rest of the line contains a name or value of the history list. If such a line occurs before line number (L + 2) then it is one of the history list's names, otherwise it is one of the history list's values. Yes, as this implies, technically a user/macro history list can have multiple names: According to TSE's Help this should not be possible, but probably due to a (former?) bug in GetFreeHistory() they can exist. Because the macro language's other history statements work with TSE's fixed history list numbers or with the history list number they get from GetFreeHistory(), they do not have a problem.
When TSE starts, it internally loads tsehist.dat in buffer 6 (using binary mode -2), checks the verification string, reads byte code value L of the first character, and blanks the first line. The latter will frustrate any potential history list maintainers amongst us. Yes, we can retrieve L's initial value from the file, but TSE's autoloaded and start-up macros can immediately make that value outdated.
As elsewhere the CreateBuffer() help states:
"Undo/Redo information is NOT recorded for _SYSTEM_ buffers."
As the CreateBuffer() help states:
"Undo/Redo information is NOT recorded for _SYSTEM_ buffers."
Unhooks the procs hooked with HookDisplay() and restores DisplayMode() to the value it had when HookDisplay() was called.
The exact specification is untested, so the following is just an educated guess.
Syntax: UnloadAllBuffers()
Returns: Unknown.
Does an UnloadBuffer() for all open files.
I do not know a practical use case for this command. If you actually do use this command please let me know what for.
Syntax: UnloadBuffer(integer id)
Returns: TRUE if it did unload the buffer, FALSE if it did not.
Unloads the content of the buffer for which the id is passed,
but only if that buffer:
AND has a _NORMAL_ BufferType(),
AND has no changes,
AND exists as an identically named file on disk.
Note, that it is a TSE feature, that files can be opened and not yet loaded. This typically happens when you open multiple files at once using wildcards; then only the first file will be loaded and the rest will not be until they are accessed.
One possible use of UnloadBuffer() is to simply reduce TSE's memory usage by unloading a file that for now is no longer used.
An extreme(ly useful) example of UnloadBuffer()'s usage is the following. Using wildcards you can open more files than TSE can load into memory. A macro can still process all those files without running out of memory by opening them one at a time and doing an UnloadBuffer() for each file after processing it.
This function has two optional integer parameters:
UnLockCurrentFile([integer keep[, integer file_attribute]])
There are two kinds of file locking - handle and attribute.
Handle locking
In handle locking, the file is sometimes created.
In some cases, when one is unlocking the file, one wants to remove this file, in other cases, not.
This can be indicated by setting "keep" to FALSE or TRUE.
The file_attribute is used in attribute locking (e.g., read-only).
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
In some cases UpdateDisplay() cannot refresh the screen, for example from an _IDLE_ or _NONEDIT_IDLE_ hook.
See the Transparency setting for a work-around trick.
As of TSE Pro v4.2 this function also works for letters with diacritics if the editor uses an ANSI compatible font like Courier New.
This function is undocumented.
So far its observed behaviour is, that it seems to return TRUE immediately after a Find() and FALSE immediately after an lFind().
Trailing spaces are ignored too. For example, Val("123 ") will return the number 123.
Contrary to what the documentation says, you can also set ViewFindsId. The next ViewFinds() will use it.
This function roughly does what its documentation states, but not exactly.
In Windows, smoothly increasing the function's wait-time increases its reponse-time stairs-like, in chunks at a time with a small transitory curve.
There is a pattern, but I have been unable to determine a hard algorithm. For example, this test macro produced this test output .
Note that the shortest time the function waits is 107 milliseconds.
In Linux this function very predictably reacts in chunks of 50 milliseconds to its parameter, with an in-between reaction for values of 50 * N + 1 for N > 0.
Any value from 0 to 50 is interpreted as 50. Any value from 52 to 100 is interpreted as 100. Any value from 102 to 150 is interpreted as 150. Any value from 152 to 200 is interpreted as 200. Etc.
Note that the shortest time the function waits is 50 milliseconds.
If you use semicolons (";") instead of commas (",") to separate parameters, then the concatanated parameters will be separated by the equal amount of spaces.
For example
Warn('a', 'b'; 'c';; 'd';;; 'e';;;; 'f';;;;; 'g')will show the string
'ab c d e f g'
In the console version of TSE Warn() behaves as described.
In the GUI version of TSE this function displays the formatted result string differently than described:
Example:
A GUI version of TSE with a ridiculously small screen width of 40
would display
Warn('He sissed:', Chr(13), '':40:'s')as
He sissed: ssssssssssssssssssssssssssssssssssss ssss
A Console version of TSE would display the single status line:
He sissed:Xssssssssssssss Press <Escape>where X is whatever your console version of TSE displays for a carriage-return character (possibly a music symbol) and where 26 "s" characters are cut off.
Tip:
In the file "Compatibility_downto_tse40.inc" v1.5 or higher a procedure
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Chr(0) bug: SetBufferStr() and GetBufferStr() strip characters with ASCII code 0 from the end of a value, and Warn() strips it and all other characters after one such a character.
Demo macro:
proc Main() integer hash_id = 0 integer org_id = GetBufferId() string s [MAXSTRINGLEN] = 'a' + Chr(0) hash_id = CreateTempBuffer() GotoBufferId(org_id) SetBufferStr('key', s, hash_id) Warn('Bug: "2 2 a ." <> "', Length(s); Length(GetBufferStr('key', hash_id)); s, '."') AbandonFile(hash_id) PurgeMacro(SplitPath(CurrMacroFilename(), _NAME_)) end Main
A WhenPurged() procedure is implicitly forward referenced. This means, that in a macro you can call a WhenPurged() proc that is defined further on in the source without defining a forward reference.
A WhenPurged() procere is not called when the editor is closed. Often this is irrelevant, because WhenPurged() usually just implements cleaning up data in TSE memory. But if a WhenPurged() procedure also cleans up data outside TSE, like for example a temporary disk file, then an easy solution is to add a Hook(_ON_ABANDON_EDITOR_, WhenPurged) to the WhenLoaded() procedure.
The WIN32 compiler directive was named in an era where Linux TSE did not exist. In practice "#ifdef WIN32" was used to distinguish between the 16-bit TSE versions up to and including TSE 2.5 and the 32-bit versions of TSE from TSE v2.6 onwards. Since TSE still ran in a console, and still can, the distinction between Dos and Windows was and is not relevant. All Linux TSE versions are 32-bit, and being "new" they are most compatible with the 32-bit versions of TSE.
For old macros that use "#ifdef WIN32" and "#ifndef WIN32" there is an elegant solution. Make this the first conditional compiler directive:
#ifdef LINUX #define WIN32 FALSE #endif
This changes the meaning of "#ifdef WIN32" to "Is this a 32-bit TSE version?".
This one addition will automagically make all the existing "#ifdef WIN32" and "#ifndef WIN32" work for Linux too, while still being backwards compatible.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
This function has an optional integer parameter that indicates how many times to perform the action. Parameter value 0 is unsafe: the function's action and return value are undefined and can change.
The WordSet and ChrSet() documentation misses a reference to the ClearBit(), GetBit() and SetBit() functions.