Grimoire
======

A source and text editor with syntax colouring, problem marking and
word completion, built on the shared editor (AEditor) and its language
modules (EditLangs).

Grimoire's languages are the drivers in !Languages.Drivers, so the
Filer must have seen !Languages first. Each driver is a Sourcery
project in its own directory there. When Grimoire starts it reads only
each driver's Match file, which gives the language's name, title,
suffixes and filetypes; it comes from the driver's project with its
links file. A driver without one is loaded at once. A driver itself is loaded the first time a
file needs it, or when you choose it from the Language submenu.

Grimoire is also an OLE server: !Boot registers it in OLEServer$Type_XXX
for Text, Obey, TaskObey, XML, HTML, BASIC, Python, CSS, JavaScript,
YAML, TeX, LaTeX, Perl and Makefile files that no other editor has
claimed, and running Grimoire claims them. Applications such as Sourcery
then hand their files to Grimoire for editing; saving the file tells the
application, and closing the window ends the editing session.

!Boot and !Run also set Alias$@EditType_FFF and Alias$@RunType_FFF, so
that Text files are edited and run in Grimoire, unless another editor has
already set them. While Grimoire is the run handler for Text, a running
Grimoire also opens Text files that are double-clicked, rather than the
Filer starting another copy. At the command line, running a Text file
still types it.

Shift-double-click a file in the Filer to edit it rather than run it. A
BASIC, Obey or other file of a type Grimoire edits is loaded with its own
type: it is coloured for its language, and saving it keeps that type.

An OLE client can tell Grimoire where the file's included files are. It
sets Grimoire$IncludePath to a list of paths and directories separated
by commas, such as "C:,OSLib:,ADFS::HD4.$.MyLib.Source", just before it
asks Grimoire to open the file. Grimoire reads it and clears it when the
file opens. Completion, parameter hints and Ctrl-click then look in
those places after the file's own directory, and so do the headers you
open from it with Ctrl-click. Sourcery sets it to the project's include
paths when you open one of the project's source files.

The language is chosen from the file's directory or extension (c, h,
c++, sql, json, xml, html, php, s, py, js, css, pl, lua, sh, p, f,
tex, yaml, diff...) and then its file type. PHP files
are found by their extension (php), as RISC OS has no PHP file type.
When neither says, as with the scraps an OLE client such as Weaver
writes, a Text or HTML file is checked for <?php, a #! line, SQL, CSS,
XML or HTML text and given that language.
If it still gets the wrong language, choose the right one from the
Language submenu of the window's menu, which lists every language
loaded from !Languages with the file's ticked.

In C and C++ files, word completion also offers the functions, types,
macros, variables and members declared in the files the document
#includes, and in the files those include, up to 8 levels deep.
"name.h" is looked for beside the document first, then in C$Path and
OSLib$Path; <name.h> only in C$Path and OSLib$Path. Each file is read
once and read again only when it changes.

For a declared function, the completion list shows its arguments and
type after the name. Typing "(" after the name of a function declared
in those files, or in the document itself, opens a hint above the
caret with the whole declaration, the argument the caret is in
highlighted; it follows the caret as you type commas and closes at
the ")" or with Escape. Macros, and which overload is meant, are not
worked out.

In PHP files it also offers the functions, classes, constants, define()d
names, global variables and class properties from the files the page
includes or requires by a constant name ('lib/db.php', or
__DIR__ . '/lib/db.php'). These are looked for beside the page, then in
PHP$Path, as lib.db/php (or lib.php.db).

The same works for:
  Python     import a.b, from a.b import x: a.b/py, then on Python$Path;
             top-level def, class and NAME =, and methods.
  Lua        require "a.b": a.b/lua, then on Lua$Path; top-level
             function M.name and NAME =.
  Perl       use/require Foo::Bar: Foo.Bar/pm, then on Perl$Path;
             sub, package, our and use constant.
  BASIC      LIBRARY "file" and INSTALL "file", text or tokenised;
             DEF PROC and DEF FN names.
  Assembler  GET file and INCLUDE file; labels in the first column
             and MACRO names.
BASIC and assembler names are used as written; relative names are looked
for beside the document and in its parent directory.

Editing keys:
  F4            Find and replace
  F5            Go to line
  Ctrl-click    Go to the declaration of the name clicked on
  F7            List the file's functions
  Ctrl-L        Show or hide line numbers
  Ctrl-B        Jump to the matching bracket
  Tab/Shift-Tab Indent or outdent the selected lines
  Return        New line, keeping the indent of the line above
  Insert        Switch between inserting and overwriting
  F6            Set or clear a bookmark on this line
  Shift-F6      Go to the next bookmark
  Shift-F9      Start or stop recording a keystroke macro
  F9            Play the macro (stopping recording first)
  Ctrl-F        Fold (hide) the more indented lines below this one,
                or show them again

Double-click selects a word, and a third click selects the whole line.
Dragging the selection moves it.
Shift-drag a file from the Filer into a window to insert its full
pathname where you drop it, rather than its contents.

A keystroke macro records the keys typed into any window between
Shift-F9 and Shift-F9 (up to 1024), including cursor and editing keys,
and F9 types them again into the window with the caret. Toolbar buttons
and menu choices are not recorded. The macro is kept until the next
recording or until Grimoire quits.

Alt-drag selects a column: a rectangle of text, which can go past the
ends of short lines. Typing, Delete and Backspace then change every line
of it at once (it becomes a thin bar after the first key, so you can
carry on typing down the column). Ctrl-C and Ctrl-X copy or cut it a
line at a time; Ctrl-V puts one line of text on every line of the
column, or several lines one under another. Any other key, or a click,
ends it. Column selection is off when Wrap is on.

The Find window's Regex option searches with regular expressions:
  .              any character except a line end
  [abc] [^a-z]   any of a set, or any not in it
  \d \w \s       a digit, word character or space (\D \W \S: not)
  * + ? {n,m}    repeat; add ? to match as little as possible
  ^ $            start and end of a line
  \b             a word boundary
  a|b            either
  ( )            a group, \1 to \9 later in the pattern; (?: ) isn't one
In Replace, \1 to \9 put back a group, \0 the whole match, \n a new
line and \t a tab. Ignore case applies to regular expressions too.

Throwback: Grimoire registers with DDEUtils to receive the errors and
warnings that compilers, AMU and ObjAsm report when run with -throwback
(as Sourcery's builds are). They are listed in the Throwback window,
grouped by file, with the number of errors and warnings in its title;
each build replaces the list. Click an error to open its file, or bring
its window to the front, with the caret on the error's line. If
DDEUtils is loaded after Grimoire, as when a build loads it, Grimoire
registers when the next task starts, so the build's errors still come
here. Only one task can receive throwback: if another editor registered
first, quit it and restart Grimoire.

The toolbar's top row is the same for every file: Find, Go to, Indent,
Outdent, Lines (line numbers), Mark and Next (bookmarks), Undo, Redo,
Wrap, Fold, Funcs and Comment. The second row has the buttons of the file's language;
files without any (plain text) show just the top row.
Rest the pointer on a button for half a second to see its name and keys.

Funcs (F7) opens a list of the functions, procedures and types the file
declares (labels in assembler), with their line numbers. Click one to go
to it. The list is refreshed each time you open it.

Comment comments out the selected lines (or the caret's line) in the
file's language: // in C, REM in BASIC, # in Python, Perl and shell
scripts, -- in SQL and Lua, | in Obey, ; in assembler, and so on. If
every selected line is already commented, it uncomments them. HTML, XML,
Markdown, CSS and Pascal, which have no line comments, put the selection
in a block comment (<!-- -->, /* */ or { }) instead. Plain text, JSON and
diffs have no comments.

Autosave and backups: every 5 minutes Grimoire saves a copy of each file
with unsaved changes in <Choices$Write>.Grimoire.Recover. The copies are
deleted when you save or close the file, or quit. If Grimoire stops
without closing them (an error, or the machine is reset), it offers to
open them the next time it starts; they open with their original names,
marked as modified. The first time you save over a file, the file as it
was is kept as the same name with ~ added. Set how many minutes apart
the copies are kept (0 for none), and whether backups are kept, in
Choices. Grimoire passes them on in Grimoire$Autosave and
Grimoire$Backups; if you set those before Grimoire starts, Choices starts
with them.

Compare: click Compare on the top toolbar row and choose another open
file from the menu. Its window opens to the right of this one, and the
lines that differ are coloured in both, using the problem background
colour; a message says if the files are the same. Editing a file clears
its colouring; click Compare again to clear both.

Snips (Shift-F7) puts in a snippet: type its name, such as "for", then
press Shift-F7 to replace the name with the snippet, indented like the
line; or press it anywhere else to list the language's snippets and
click one. Each language's snippets are in a text file named after the
language in Grimoire's Snippets directory; a file of the same name in
Choices:Grimoire.Snippets is used instead. In a file, a line starting
@@ begins a snippet, giving its name and a description; the snippet's
text follows. $| marks where the caret goes and $$ is a $.

If another program changes a file you have open, Grimoire notices when
you next click in its window and asks whether to reload it (warning you
if you have unsaved changes). Reloading replaces the text with the
file's, keeps the caret where it was, and clears the undo history.
Saving from Grimoire itself does not count as a change.

Ctrl-click a function, type, macro or variable to go to where it is
declared: in the file itself, or in a header it includes, which is opened
if it isn't already. BBC BASIC's PROC and FN names go to their DEF.
While you hold down Ctrl, a name or included file name that Ctrl-click
can go to is underlined when the pointer is over it.

Save and Save all, at the left of the top toolbar row, save this file,
or every file with unsaved changes, where it was last saved. Save opens
the Save box for a file that hasn't been saved yet; Save all leaves it.

Run on the top toolbar row runs the file as a script in a task window:
BBC BASIC, Obey, Lua, Perl and Python files, or files of any language
when a command is set for it in Choices: choose the language from the
menu beside Run and type its command (%f in it is the file; without %f
the file goes at the end), for example bash %f for Shell. Leave it empty
for the built-in command. Choices sets Grimoire$Run_<language> to the
command, so it can also be set with *Set Grimoire$Run_Shell "bash %f"
before Grimoire starts. Save the file first. The
output is listed in the Throwback window, and a line that names a line
of the script ("at line 20") is an error: click it to go to that line.
Click Run again while the script is running to stop it.

Files on the top toolbar row opens Find in files: give a directory
(it starts as the current file's) and the text to find, with Ignore case
and Regex options. Every text file in the directory and the directories
inside it is searched, and the matching lines are listed in the
Throwback window; click one to open the file at that line.

The status line along the bottom of each window shows the caret's line
and column, INS or OVR (Insert toggles it), the file's language and its
line ends.

The Text menu has Find, Go to line, line numbers, change case, sort
lines, trim trailing spaces, line ending conversion (LF, CR LF, CR),
bookmarks, spaces for tabs, overwrite, word wrap, fold and unfold all.
New view opens a second window on the same file.

Misc > Class diagram... lists the classes and structs defined in the
file, the files it includes and, for a file in a c, c++ or h directory,
the headers in the h directory beside it. Click one to open a new window
holding a Mermaid class diagram (in the Mermaid language, so it can be
saved and viewed in !Mermaid) of that class, its base classes, the
classes derived from it and the classes its members hold: --> for a
pointer or reference, *-- for a value. Destructors are left out, because
Mermaid reads ~ as the start of a generic type.
File > Print prints the text through the printer driver, and the icon
bar menu's Recent files lists files opened or saved recently.

Choices... on the icon bar menu sets the editor font (Font Manager name,
typed or chosen from the font menu on the arrow beside it, size in
points and extra line spacing), the tab width (how many columns
apart the tab stops are, 1 to 16, 4 to start with), a Light or Dark
theme, and the colour of each kind of token for each theme. Type a
colour as six hex digits RRGGBB, or click the arrow beside it to choose
it in the colour picker; the swatch beside the field shows the colour.
Set uses them until Grimoire quits; Save also keeps them in
Choices:Grimoire.Choices. Default puts back the built-in font and colours.
These choices only affect Grimoire, not the other editors.

Typing an opening bracket also types its closing one, and so does a
quote (only " in BASIC and Obey; not in plain text, Markdown or TeX),
unless the caret is just before a word, or just after one for a quote.
Typing the closing character then steps over it, and Delete straight
after the opening one removes both. Overwrite mode turns this off.

