How to implement Notetab outline files?
-
I’m moving to NPP from Notetab Pro because it doesn’t handle Unicode. So far, the only feature I’m having trouble with is Notetab Outline files (with an extension of .otl), which have an index of named entries, and text for each entry. An Outline file displays one entry at a time, hiding all the others. It is a way of combining many files of related text into one text file. I want to implement Outline files as a User Defined Language, so I can use Code Folding to hide all but one entry at a time.
Here is an example of a Notetab outline file, showing its actual rather than displayed content:
= V4 Outline MultiLine NoSorting TabWidth=30 H="A" Text for heading A H="B" Text for heading B
The NPP XML file is quite long, so I won’t include it here, but all I’ve defined is in the Menu > Language > Define your language > Folder & Default tab > “Folding in code 1 style” section.
Open contains H=" and Close contains ((EOL)).
This almost works, but when I close (hide) entry (heading) A, it also closes heading B and all the rest of the headings, if any. Perhaps it’s doing a greedy regular expression match instead of ungreedy.
Can anyone suggest an improvement?
-
UDL doesn’t do regex at all. It goes through each char and checks the condition.
UDL works best if it has unique tags like let’s say open
H=
and close!H
but not sure if this can be done in your case. Maybe a fake comment
can be used to define the closing tag? -
@David-Spector said,
Close contains ((EOL)).
I think the problem is that
((EOL))
doesn’t mean quite what you think it means. If you look at https://ivan-radic.github.io/udl-documentation/delimiters/, where((EOL))
has the most examples, it’s always ending the match on the same line that the match started. It’s not matching the first blank line – it’s matching the first end-of-line after the start.However, in Folding rules, that doesn’t make sense, because you cannot fold from the start of a line to the end of that same line. It appears the Folding rules ignore
((EOL))
completely.I did verify that you can use
(( blah1 blah2 ))
to get it to close on eitherblah1
orblah2
, so it’s not completely igorning the((...))
notation.Can anyone suggest an improvement?
I thought of setting Open and Middle to
H="
, and leaving Close empty; unfortunately, that’s got two problems: 1) it never closes; 2) if there were some way to close it, it’s actually nesting those, rather than treating the middleH="
as a Middle. :-(I think @Ekopalypse has the best idea.
Either that, or find/write/contract-out-for a true lexer plugin that properly handles the structure. (Or play with the UDL 2.1 code, and see if you can implement a fix, like a
(( ))
notation that indicates blank line … maybe((EOLEOL))
, and then put in a Pull Request.)Unfortunately, UDL – though nice – is not meant to replace all the possibilities inherent in a true lexer plugin.
-
I substituted \n for ((EOL)) and the behavior was the same: you can’t fold and unfold each entry separately.
The UDL parser is working fine, but not defining each entry as a separate item.
I’ll try the “end of entry” suggestion soon.
-
I’ve got this task done. It should be useful to others moving from NoteTab to Notepad++.
I’ve added an end-of-outline-entry symbol “End=Entry” that should appear at the end of each entry. I’ve also written a migration program in PHP to insert these end-of-outline-entry symbols automatically into outline files (.otl). I’m using the “.out” extension for the output of this program. These .out files will be “outline” files in Notepad++.
Here is the migration program:
<?php //--------------------------------------------------------------------// // Program to migrate .otl files to .out files // Springtime Software, David Spector, 10/3/19, public domain. //--------------------------------------------------------------------// define("NL"," "); $FileName=GetGetArg(); $PathIn="$FileName.otl"; $PathOut="$FileName.out"; $C=file_get_contents($PathIn,true); $Lines=explode(NL,$C); // Delete two header lines array_shift($Lines); array_shift($Lines); $firstLine=true; $bloat=0; foreach ($Lines as $n=>$Line) { $R=preg_match('@^H=@',$Line,$F); if ($R && !$firstLine) { array_splice($Lines,$n+$bloat,0,"End=Entry"); ++$bloat; } $firstLine=false; } // Terminate last entry array_splice($Lines,$n+$bloat+1,0,"End=Entry"); $C=implode("\n",$Lines); $BytesOrFalse=file_put_contents($PathOut,$C); function GetGetArg() { foreach ($_GET as $ArgName=>$ArgVal) { return $ArgName; } // Each expected arg } // GetGetArg ?>