Windows: product names, the editors, WSL; the Linux page's image step

Assembled from development commit c3c40f6c8fca. Since 830b193: on Windows the
products carry Windows names (ktext.exe, klammertext.dll, document.dll,
reflect.dll) and the console's output code page is UTF-8 for a run; the
editor tooling works on Windows (VS Code, Sublime Text, Vim, with the
interpreter's name chosen by platform) and Notepad++ joins it
(doc/edit/notepadpp: a User Defined Language and two Run-menu commands);
a sixth install page, doc/install/windows_wsl_install.md, for Linux
inside WSL; the Windows page opens with the three routes and gains its
editor section, the PowerShell quoting and Path notes; the Linux page
installs OpenImageIO from the python3-openimageio package and verifies
with kdiag --machine; the README lists all six install pages. Linux and
macOS behaviour is unchanged.
This commit is contained in:
ack committed 2026-09-25 18:43:22 +02:00
1 parent 830b19394d
commit ee8bcf5300
33 files changed
+741 -122

No files matched your search

+3 -1
View File
@@ -1,7 +1,9 @@
# Build artifacts # Build artifacts (Windows: *.dll, *.pyd; the commands are under bin/)
*.o *.o
*.d *.d
*.so *.so
*.dll
*.pyd
__pycache__/ __pycache__/
# Editor / OS cruft # Editor / OS cruft
+3 -1
View File
@@ -11,6 +11,8 @@ Installation guides are in [`doc/install/`](doc/install/):
- Linux, from source — `doc/install/linux_source_install.md` - Linux, from source — `doc/install/linux_source_install.md`
- macOS, from source — `doc/install/macos_source_install.md` - macOS, from source — `doc/install/macos_source_install.md`
- Windows, from source (native) — `doc/install/windows_source_install.md`
- Windows, Linux inside WSL — `doc/install/windows_wsl_install.md`
- Linux, container — `doc/install/linux_container_install.md` - Linux, container — `doc/install/linux_container_install.md`
- macOS, container — `doc/install/macos_container_install.md` - macOS, container — `doc/install/macos_container_install.md`
@@ -51,7 +53,7 @@ are regenerated on each release — patches cannot be merged directly.
Report problems (or send patches) to the author; accepted changes are Report problems (or send patches) to the author; accepted changes are
applied to the development tree and appear in a following snapshot. applied to the development tree and appear in a following snapshot.
This snapshot was assembled from development commit `f344a37d772c`. This snapshot was assembled from development commit `c3c40f6c8fca`.
## License ## License
+3 -2
View File
@@ -1,5 +1,6 @@
# bin # bin
This directory holds the built Klammertext command executables (`ktext`, This directory holds the built Klammertext command executables (`ktext`,
`kdesc`, `kdiag`) after you build Klammertext with `make -C com`. `kdesc`, `kdiag`) after you build Klammertext with `make -C com`. On
It is empty in the repository. Windows the library and the compiler's runtime DLLs are copied here too,
so `bin\` alone needs to be on the `Path`. It is empty in the repository.
+15 -12
View File
@@ -13,8 +13,10 @@ COMMANDS := kdiag kdesc ktext
# Build output directory: commands are linked directly into ../bin, so there is # Build output directory: commands are linked directly into ../bin, so there is
# exactly one copy of each (no redundant executables in com/) and make still # exactly one copy of each (no redundant executables in com/) and make still
# tracks them by path for incremental builds. # tracks them by path for incremental builds.
# The targets carry the system's executable suffix (makefile.env's EXE) so
# that on Windows the file gcc writes, ktext.exe, is the target itself.
BINDIR := ../bin BINDIR := ../bin
BINCOMMANDS := $(addprefix $(BINDIR)/,$(COMMANDS)) BINCOMMANDS := $(addprefix $(BINDIR)/,$(addsuffix $(EXE),$(COMMANDS)))
# System install location (out-of-tree). Override on the command line, e.g. # System install location (out-of-tree). Override on the command line, e.g.
# make install PREFIX=/opt DESTDIR=/tmp/stage # make install PREFIX=/opt DESTDIR=/tmp/stage
@@ -23,7 +25,7 @@ DESTDIR ?=
# Shared library location # Shared library location
LIBDIR := ../lib LIBDIR := ../lib
LIBRARY := $(LIBDIR)/libklammertext.so LIBRARY := $(LIBDIR)/$(KLAMMERTEXT_LIBFILE)
# Linker flags for commands # Linker flags for commands
COM_LDFLAGS := $(EXPORT_DYNAMIC) -Wl,-rpath,'$(ORIGIN)/../lib' -L$(LIBDIR) COM_LDFLAGS := $(EXPORT_DYNAMIC) -Wl,-rpath,'$(ORIGIN)/../lib' -L$(LIBDIR)
@@ -36,7 +38,7 @@ ifneq (,$(filter MINGW% MSYS%,$(UNAME_S)))
MANIFEST_OBJ := manifest.o MANIFEST_OBJ := manifest.o
# The DLLs beside the commands (makefile.env explains); with them, bin/ on # The DLLs beside the commands (makefile.env explains); with them, bin/ on
# PATH is all a Windows user needs besides Python. # PATH is all a Windows user needs besides Python.
RUNTIME_DLLS := $(addprefix $(BINDIR)/,libklammertext.so $(GCC_RUNTIME_DLLS)) RUNTIME_DLLS := $(addprefix $(BINDIR)/,$(KLAMMERTEXT_LIBFILE) $(GCC_RUNTIME_DLLS))
else else
MANIFEST_OBJ := MANIFEST_OBJ :=
RUNTIME_DLLS := RUNTIME_DLLS :=
@@ -46,8 +48,9 @@ endif
DEPFILES := $(addsuffix .d,$(COMMANDS)) DEPFILES := $(addsuffix .d,$(COMMANDS))
# Compiler flags for dependency generation ($(@F) keeps the .d files in com/, # Compiler flags for dependency generation ($(@F) keeps the .d files in com/,
# not in the ../bin output directory). # not in the ../bin output directory; $(basename) drops the .exe so the file
DEPFLAGS = -MMD -MP -MF $(@F).d # is ktext.d on every system, the name DEPFILES includes and clean removes).
DEPFLAGS = -MMD -MP -MF $(basename $(@F)).d
.PHONY: all clean redo clang mac sks install .PHONY: all clean redo clang mac sks install
@@ -65,7 +68,7 @@ commands : $(BINCOMMANDS) $(RUNTIME_DLLS)
ifneq (,$(filter MINGW% MSYS%,$(UNAME_S))) ifneq (,$(filter MINGW% MSYS%,$(UNAME_S)))
manifest.o : klammertext.rc klammertext.manifest manifest.o : klammertext.rc klammertext.manifest
windres $< $@ windres $< $@
$(BINDIR)/libklammertext.so : $(LIBRARY) | $(BINDIR) $(BINDIR)/$(KLAMMERTEXT_LIBFILE) : $(LIBRARY) | $(BINDIR)
cp $< $@ cp $< $@
$(BINDIR)/%.dll : $(GCC_BIN)%.dll | $(BINDIR) $(BINDIR)/%.dll : $(GCC_BIN)%.dll | $(BINDIR)
cp $< $@ cp $< $@
@@ -86,18 +89,18 @@ sks : | mac
$(MAKE) -C $(KS)/document $(MAKE) -C $(KS)/document
# Explicit rules for each command - link against shared library, output to bin/ # Explicit rules for each command - link against shared library, output to bin/
$(BINDIR)/kdiag : kdiag.cpp $(LIBRARY) $(MANIFEST_OBJ) | $(BINDIR) $(BINDIR)/kdiag$(EXE) : kdiag.cpp $(LIBRARY) $(MANIFEST_OBJ) | $(BINDIR)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEPFLAGS) $(COM_LDFLAGS) $(LDFLAGS) $< $(MANIFEST_OBJ) -o $@ $(KLAMMERTEXT_LIB) $(LDLIBS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEPFLAGS) $(COM_LDFLAGS) $(LDFLAGS) $< $(MANIFEST_OBJ) -o $@ $(KLAMMERTEXT_LIB) $(LDLIBS)
$(BINDIR)/kdesc : kdesc.cpp $(LIBRARY) $(MANIFEST_OBJ) | $(BINDIR) $(BINDIR)/kdesc$(EXE) : kdesc.cpp $(LIBRARY) $(MANIFEST_OBJ) | $(BINDIR)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEPFLAGS) $(COM_LDFLAGS) $(LDFLAGS) $< $(MANIFEST_OBJ) -o $@ $(KLAMMERTEXT_LIB) $(LDLIBS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEPFLAGS) $(COM_LDFLAGS) $(LDFLAGS) $< $(MANIFEST_OBJ) -o $@ $(KLAMMERTEXT_LIB) $(LDLIBS)
$(BINDIR)/ktext : ktext.cpp $(LIBRARY) $(MANIFEST_OBJ) | $(BINDIR) $(BINDIR)/ktext$(EXE) : ktext.cpp $(LIBRARY) $(MANIFEST_OBJ) | $(BINDIR)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEPFLAGS) $(COM_LDFLAGS) $(LDFLAGS) $< $(MANIFEST_OBJ) -o $@ $(KLAMMERTEXT_LIB) $(LDLIBS) $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(DEPFLAGS) $(COM_LDFLAGS) $(LDFLAGS) $< $(MANIFEST_OBJ) -o $@ $(KLAMMERTEXT_LIB) $(LDLIBS)
# Out-of-tree system install (copies; the build tree stays intact). The # Out-of-tree system install (copies; the build tree stays intact). The
# installed commands still need KLAMMERTEXT_HOME pointing at a Klammertext tree # installed commands still need KLAMMERTEXT_HOME pointing at a Klammertext tree
# for sks/ and the Python modules; their rpath finds libklammertext.so in # for sks/ and the Python modules; their rpath finds the library in
# $(PREFIX)/lib. # $(PREFIX)/lib.
install : all install : all
install -d $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(PREFIX)/lib install -d $(DESTDIR)$(PREFIX)/bin $(DESTDIR)$(PREFIX)/lib
@@ -115,9 +118,9 @@ else
$(MAKE) -C $(KS)/kutil clean $(MAKE) -C $(KS)/kutil clean
$(MAKE) -C $(KS)/target clean $(MAKE) -C $(KS)/target clean
$(MAKE) -C $(KS)/document clean $(MAKE) -C $(KS)/document clean
# ref/reflect.so links against libklammertext.so and is dlopened, so # ref/reflect$(DSO) links against the library and is dlopened, so
# it shares the class layouts: a stale one after a header change is # it shares the class layouts: a stale one after a header change is
# the same memory-corruption hazard as a stale sks/document.so. # the same memory-corruption hazard as a stale sks/document$(DSO).
$(MAKE) -C $(K)/ref clean $(MAKE) -C $(K)/ref clean
$(MAKE) -C $(K)/kpy clean $(MAKE) -C $(K)/kpy clean
$(MAKE) clean $(MAKE) clean
+10 -6
View File
@@ -1,7 +1,7 @@
# Klammertext editor support # Klammertext editor support
Editing support for Klammertext files (`.kt` documents and `.k` klammer Editing support for Klammertext files (`.kt` documents and `.k` klammer
definitions) in four editors, built around one shared implementation. definitions) in five editors, built around one shared implementation.
| Directory | Contents | | Directory | Contents |
|---|---| |---|---|
@@ -10,6 +10,7 @@ definitions) in four editors, built around one shared implementation.
| `sublime/` | Sublime Text package. Syntax file plus thin plugin wrappers that import the shared core directly (Sublime's plugin host is Python). | | `sublime/` | Sublime Text package. Syntax file plus thin plugin wrappers that import the shared core directly (Sublime's plugin host is Python). |
| `vim/` | Vim plugin. Syntax/ftplugin files plus commands that run the shared core's CLI; with `+python3`, the core also runs in-process (`=` operator, live match highlighting). | | `vim/` | Vim plugin. Syntax/ftplugin files plus commands that run the shared core's CLI; with `+python3`, the core also runs in-process (`=` operator, live match highlighting). |
| `vscode/` | Visual Studio Code extension. TextMate grammar plus a dependency-free extension that spawns the language server and speaks LSP to it. | | `vscode/` | Visual Studio Code extension. TextMate grammar plus a dependency-free extension that spawns the language server and speaks LSP to it. |
| `notepadpp/` | Notepad++ (Windows). A User Defined Language for the highlighting, and two Run-menu commands that pass the current file through the shared core's CLI (delimiter check, reindent). No plugin host, so nothing structural runs in-process. |
## The architecture ## The architecture
@@ -41,7 +42,8 @@ document/range formatting (reindentation), documentHighlight and
definition (the matcher), and a `klammertext.alignTable` command. The definition (the matcher), and a `klammertext.alignTable` command. The
VS Code extension is its first client; any LSP client works — Neovim's VS Code extension is its first client; any LSP client works — Neovim's
built-in LSP, Emacs eglot, Sublime's LSP package — with a one-line built-in LSP, Emacs eglot, Sublime's LSP package — with a one-line
configuration pointing at `python3 klammertext_ls.py`. configuration pointing at `python3 klammertext_ls.py` (on Windows
`python klammertext_ls.py`).
Syntax highlighting and cursor-latency features stay native in each editor Syntax highlighting and cursor-latency features stay native in each editor
(a tokenizer or a per-keystroke matcher cannot round-trip to Python), so (a tokenizer or a per-keystroke matcher cannot round-trip to Python), so
@@ -68,10 +70,12 @@ Two kinds of artifact cannot read it and restate parts of it by hand:
defcustoms, checked byte-for-byte against the shared core by defcustoms, checked byte-for-byte against the shared core by
`tst/editor_test.sh`. `tst/editor_test.sh`.
2. **The static syntax files** — the literal-klammer set (`@code`) appears 2. **The static syntax files** — the literal-klammer set (`@code`) appears
in the Sublime `.sublime-syntax`, the Vim `syntax/klammertext.vim`, and in the Sublime `.sublime-syntax`, the Vim `syntax/klammertext.vim`, the
the VS Code `tmLanguage.json` grammar (and the Emacs defcustom). When VS Code `tmLanguage.json` grammar, and the Notepad++ `Klammertext.udl.xml`
you add a literal klammer, change them together; each file's header (and the Emacs defcustom). When you add a literal klammer, change them
carries the same SYNC note. together; each file's header carries the same SYNC note. The UDL can
hold only names that are not prefixes of other klammer names (it has
no word boundary), so it carries `code` and not `c`.
## Testing ## Testing
+114
View File
@@ -0,0 +1,114 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Klammertext.udl.xml - Notepad++ User Defined Language for Klammertext
(.kt documents and .k klammer definitions). Highlighting only: Notepad++
has no Python plugin host and no built-in language-server client, so the
structural tools of the shared core (doc/edit/shared/klammertext_edit.py)
are reached through Run-menu commands instead; README.md has them.
What it colours, and how (a UDL is a fixed set of token classes, not a
grammar; the choices below are the closest fit):
@@@name, @@@ system commands Keywords1, prefix match, orange
@@name, @@ klammer definitions Keywords2, prefix match, green
@name, @ klammer applications Keywords3, prefix match, blue
The groups are checked in order, longest run first,
so @@name is not taken for an @ followed by a name.
A named close (name@) is coloured only at its @.
^X ^UUUU^ ^' escapes Keywords4, prefix match, the
default colour: consumed so the character after the
caret is never read as a delimiter, shown as text
(as the Emacs mode does).
# ... removal to end of line line comment, grey; only
when the # is the first thing on its line
(forcePureLC), because a UDL comment cannot tell
"# comment" from the whitespace operators #- #+ #/,
which would otherwise grey the rest of their line.
#[ ... ]# removal of a region block comment, grey, folds.
## #- #+ #/ the other # forms Operators2, marker red.
| : bars and option marks Operators1, marker red.
@code ... code@ verbatim interior Delimiters1, muted, nothing
inside it is coloured. The other literal klammer,
@c ... c@, is NOT here: a UDL delimiter has no word
boundary, so "@c" would also open at @caption,
@cond and every other c-klammer.
SYNC: the literal-klammer set's source of truth is LITERAL_KLAMMERS in
doc/edit/shared/klammertext_edit.py. When a klammer is added there, add
"@NAME" / "NAME@" as a further delimiter pair here (pairs 2 to 8 are free:
items 03/05, 06/08, ... in the "Delimiters" list) - if its name is not a
prefix of other klammer names.
Colours are the palette's light-ground values (notes/klammertext_palette.md:
application 528599, definition 758B55, system 996743, markers 994040,
removed text 9A9A9A); colorStyle="1" keeps every background transparent, so
a dark Notepad++ theme keeps its own ground (the hues still read there).
Install: copy this file into %AppData%\Notepad++\userDefineLangs\ and
restart Notepad++ (or Language > User Defined Language > Define your
language... > Import...). README.md has the details and the Run commands.
-->
<NotepadPlus>
<UserLang name="Klammertext" ext="kt k" udlVersion="2.1">
<Settings>
<Global caseIgnored="no" allowFoldOfComments="yes" foldCompact="no" forcePureLC="1" decimalSeparator="0" />
<Prefix Keywords1="yes" Keywords2="yes" Keywords3="yes" Keywords4="yes" Keywords5="no" Keywords6="no" Keywords7="no" Keywords8="no" />
</Settings>
<KeywordLists>
<Keywords name="Comments">00# 01 02 03#[ 04]#</Keywords>
<Keywords name="Numbers, prefix1"></Keywords>
<Keywords name="Numbers, prefix2"></Keywords>
<Keywords name="Numbers, extras1"></Keywords>
<Keywords name="Numbers, extras2"></Keywords>
<Keywords name="Numbers, suffix1"></Keywords>
<Keywords name="Numbers, suffix2"></Keywords>
<Keywords name="Numbers, range"></Keywords>
<Keywords name="Operators1">| :</Keywords>
<Keywords name="Operators2">## #- #+ #/</Keywords>
<Keywords name="Folders in code1, open"></Keywords>
<Keywords name="Folders in code1, middle"></Keywords>
<Keywords name="Folders in code1, close"></Keywords>
<Keywords name="Folders in code2, open"></Keywords>
<Keywords name="Folders in code2, middle"></Keywords>
<Keywords name="Folders in code2, close"></Keywords>
<Keywords name="Folders in comment, open">#[</Keywords>
<Keywords name="Folders in comment, middle"></Keywords>
<Keywords name="Folders in comment, close">]#</Keywords>
<Keywords name="Keywords1">@@@</Keywords>
<Keywords name="Keywords2">@@</Keywords>
<Keywords name="Keywords3">@</Keywords>
<Keywords name="Keywords4">^</Keywords>
<Keywords name="Keywords5"></Keywords>
<Keywords name="Keywords6"></Keywords>
<Keywords name="Keywords7"></Keywords>
<Keywords name="Keywords8"></Keywords>
<Keywords name="Delimiters">00@code 01 02code@ 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23</Keywords>
</KeywordLists>
<Styles>
<WordsStyle name="DEFAULT" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="COMMENTS" fgColor="9A9A9A" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="LINE COMMENTS" fgColor="9A9A9A" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="NUMBERS" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS1" fgColor="996743" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="1" nesting="0" />
<WordsStyle name="KEYWORDS2" fgColor="758B55" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="1" nesting="0" />
<WordsStyle name="KEYWORDS3" fgColor="528599" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="1" nesting="0" />
<WordsStyle name="KEYWORDS4" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS5" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS6" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS7" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="KEYWORDS8" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="OPERATORS" fgColor="994040" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN CODE1" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN CODE2" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="FOLDER IN COMMENT" fgColor="994040" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS1" fgColor="5A5A5A" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS2" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS3" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS4" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS5" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS6" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS7" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
<WordsStyle name="DELIMITERS8" fgColor="000000" bgColor="FFFFFF" colorStyle="1" fontName="" fontStyle="0" nesting="0" />
</Styles>
</UserLang>
</NotepadPlus>
+114
View File
@@ -0,0 +1,114 @@
# Klammertext support for Notepad++
Notepad++ support for editing Klammertext files (`.kt` documents and `.k`
klammer definitions): syntax highlighting through a **User Defined
Language**, and the shared editor core's delimiter check and
reindentation through two **Run-menu commands**. Notepad++ is Windows
only, and so is this package.
Notepad++ has no Python plugin host and no built-in language-server
client, so it cannot import the shared core the way Sublime Text does or
talk to the language server the way VS Code does. What it has is a
declarative highlighter and a Run menu that can invoke any program on the
current file, and this package uses exactly those two things.
## Files
| File | Purpose |
|---|---|
| `Klammertext.udl.xml` | The User Defined Language: highlighting for the three `@`-tiers, escapes, text removal, and the `@code` verbatim interior. Its header lists what it colours and the limits of a UDL. |
| `klammertext_edit.py` | The shared editor core (in the editing zip it sits here; in the Klammertext repository it is `doc\edit\shared\klammertext_edit.py`). The Run commands call it. |
| `README.md` | This file. |
## Requirements
- Notepad++ 7.9 or later (the `userDefineLangs` folder; any recent
version qualifies).
- For the Run commands: `python` on the `Path`, which the Klammertext
installation already needs.
## Install the highlighting
Copy `Klammertext.udl.xml` into Notepad++'s user-defined-language folder
and restart Notepad++:
```powershell
$d = "$env:AppData\Notepad++\userDefineLangs"
New-Item -ItemType Directory -Force $d | Out-Null
Copy-Item Klammertext.udl.xml $d
```
Notepad++ loads every file in that folder at start. `.kt` and `.k` files
then open as "Klammertext" (the language shows in the status bar and at
the bottom of the **Language** menu). The alternative is the dialog:
**Language → User Defined Language → Define your language… → Import…**,
which copies the file to the same place.
What you see: klammer applications (`@name … @`) in blue, definitions
(`@@name … @@`) in green, system commands (`@@@name … @@@`) in orange;
a `#` at the start of a line greys the rest of the line, `#[ … ]#` greys
and folds a region; `##`, `#-`, `#+`, `#/`, `|` and `:` in the marker
red; the interior of `@code … code@` muted and uninterpreted. The
colours are the palette's light-ground values with transparent
backgrounds, so a dark theme keeps its own ground.
Two limits, both properties of a UDL rather than of Klammertext, are
documented in the file's header: a `#` that is not the first thing on
its line is not treated as a comment (so that the whitespace operators
`#-`, `#+`, `#/` never grey the rest of their line), and `@c … c@` is not
made verbatim (a UDL delimiter has no word boundary, so `@c` would also
open at `@caption` and `@cond`).
## The Run commands
The shared core's command-line interface reads a file on standard input
and writes the result on standard output; Notepad++'s **Run → Run…**
dialog (`F5`) runs a command line with the current file's path filled in
as `$(FULL_CURRENT_PATH)`. Two commands cover the structural tools.
Replace `C:\Users\<you>\klammertext\doc\edit\shared` with the folder that
holds `klammertext_edit.py` (with the editing zip, the folder this
README is in).
**Check the delimiters** — lists every unclosed opening, extra close,
name or tier mismatch, and unclosed literal span or `#[` block, as
`line:column: message`; an empty window means the file is balanced.
The `/k` keeps the window open to read:
```
cmd /k python "C:\Users\<you>\klammertext\doc\edit\shared\klammertext_edit.py" check < "$(FULL_CURRENT_PATH)"
```
**Reindent the file** — rewrites the file with the canonical
indentation (2 spaces per nesting level, bars and closes at their
opener's column, `@document` transparent, verbatim and removed text
untouched):
```
cmd /c python "C:\Users\<you>\klammertext\doc\edit\shared\klammertext_edit.py" indent < "$(FULL_CURRENT_PATH)" > "$(FULL_CURRENT_PATH).tmp" && move /y "$(FULL_CURRENT_PATH).tmp" "$(FULL_CURRENT_PATH)"
```
Paste each line into the Run dialog once, press **Save…**, give it a
name ("Klammertext check", "Klammertext reindent") and a shortcut
(`Ctrl+Alt+K`, `Ctrl+Alt+I` are free); they then appear at the bottom of
the **Run** menu with those keys.
Two things to know about the reindent command. It works on the file
**on disk**, so save first (`Ctrl+S`); Notepad++ does not save before
running. And after it runs, Notepad++ notices the changed file: with the
default setting it asks whether to reload, which you answer Yes; under
**Settings → Preferences → MISC. → File Status Auto-Detection** the
choice "Update silently" makes the reload automatic. Undo history is
lost on a reload, as for any external edit.
Table alignment and jump-to-match need a caret position the Run dialog
cannot pass, so they are not offered here. They are available in every
other supported editor, and for Notepad++ through the PythonScript
plugin, which embeds a Python that can import the shared core and reach
the caret; a script for it is a possible addition, not part of this
package.
## Keeping things in sync
The UDL restates the literal-klammer set (`@code`) that the shared core
owns in `LITERAL_KLAMMERS`; its header says how to add one. The editor
support's overview is `doc/edit/README.md` in the Klammertext repository.
+11 -1
View File
@@ -56,7 +56,10 @@ import sys
# --- policy ---------------------------------------------------------------- # --- policy ----------------------------------------------------------------
# Klammer names whose content is a literal argument (verbatim interior, # Klammer names whose content is a literal argument (verbatim interior,
# closed by a named NAME@ delimiter). # closed by a named NAME@ delimiter). The static syntax artifacts restate
# this set by hand (Emacs defcustom, .sublime-syntax, vim syntax, tmLanguage,
# the Notepad++ UDL -- which can hold only "code"); doc/edit/README.md,
# "Keeping things in sync".
LITERAL_KLAMMERS = set(["code", "c"]) LITERAL_KLAMMERS = set(["code", "c"])
# Klammers that contribute no indentation level (a @document's paragraphs # Klammers that contribute no indentation level (a @document's paragraphs
@@ -1037,6 +1040,13 @@ def _cli(argv):
if len(argv) < 1: if len(argv) < 1:
return usage() return usage()
mode = argv[0] mode = argv[0]
# Klammertext writes LF on every system. Python's text-mode stdout turns
# every "\n" into "\r\n" on Windows, and an editor -- or the test suite --
# comparing the result with an LF buffer then sees every line changed.
# stdin keeps universal newlines: a CRLF buffer arrives as "\n" lines,
# which is what the algorithms expect.
if hasattr(sys.stdout, 'reconfigure'): # 3.7+; the floor is 3.8
sys.stdout.reconfigure(newline='\n')
s = sys.stdin.read() s = sys.stdin.read()
if mode == 'indent': if mode == 'indent':
+2 -2
View File
@@ -203,8 +203,8 @@ elsewhere) are not part of this package, but they exist: the **Klammertext
language server** (`doc/edit/shared/klammertext_ls.py`, the same program the language server** (`doc/edit/shared/klammertext_ls.py`, the same program the
VS Code extension uses) serves them to Sublime through the community LSP VS Code extension uses) serves them to Sublime through the community LSP
package. Install "LSP" from Package Control and add a client with package. Install "LSP" from Package Control and add a client with
`command: ["python3", "/path/to/klammertext_ls.py"]` for the `command: ["python3", "/path/to/klammertext_ls.py"]` (on Windows
`text.klammertext` selector. Everything in this package works the same `"python"`) for the `text.klammertext` selector. Everything in this package works the same
with or without it. with or without it.
## Troubleshooting ## Troubleshooting
+4 -3
View File
@@ -14,7 +14,8 @@ sync.
## Requirements ## Requirements
- Vim 8+ (or Neovim) with `+eval` — any normal Vim; only `vim-tiny` lacks it. - Vim 8+ (or Neovim) with `+eval` — any normal Vim; only `vim-tiny` lacks it.
- `python3` on `PATH` (Klammertext itself already requires Python). - `python3` on `PATH` — on Windows `python`, the name python.org installs
(Klammertext itself already requires Python).
- The shared core, found automatically in this order: - The shared core, found automatically in this order:
1. `g:klammertext_edit_py`, if you set it (a full path to 1. `g:klammertext_edit_py`, if you set it (a full path to
`klammertext_edit.py`); `klammertext_edit.py`);
@@ -81,7 +82,7 @@ in-process: the `=` operator reindents through `'indentexpr'` (`==`, `gg=G`),
and the matching delimiter is highlighted live as the cursor sits on one — and the matching delimiter is highlighted live as the cursor sits on one —
the show-paren equivalent, with a mismatched or unbalanced delimiter shown the show-paren equivalent, with a mismatched or unbalanced delimiter shown
in red plus a message. Without `+python3` the commands above still work; in red plus a message. Without `+python3` the commands above still work;
they shell out to `python3`. they shell out to `python3` (on Windows `python`).
**Comment toggling** — `'commentstring'` is set to `# %s`, so Vim 9.1's **Comment toggling** — `'commentstring'` is set to `# %s`, so Vim 9.1's
built-in commenting and Neovim's `gcc`/`gc` (or the commentary plugin) built-in commenting and Neovim's `gcc`/`gc` (or the commentary plugin)
@@ -92,7 +93,7 @@ toggle `#` line removal.
| Variable | Meaning (default) | | Variable | Meaning (default) |
|---|---| |---|---|
| `g:klammertext_edit_py` | full path to `klammertext_edit.py` (auto-detected) | | `g:klammertext_edit_py` | full path to `klammertext_edit.py` (auto-detected) |
| `g:klammertext_python` | Python interpreter (`python3`) | | `g:klammertext_python` | Python interpreter (`python3`; on Windows `python`) |
| `g:klammertext_no_mappings` | set to define no default mappings | | `g:klammertext_no_mappings` | set to define no default mappings |
## Neovim and the language server ## Neovim and the language server
+4 -2
View File
@@ -11,7 +11,8 @@
" Configuration: " Configuration:
" g:klammertext_edit_py full path to klammertext_edit.py (overrides the " g:klammertext_edit_py full path to klammertext_edit.py (overrides the
" search described below) " search described below)
" g:klammertext_python the Python interpreter (default "python3") " g:klammertext_python the Python interpreter (default "python3";
" on Windows "python", the name python.org installs)
" --- locating the shared core --------------------------------------------- " --- locating the shared core ---------------------------------------------
" Search order: an explicit g:klammertext_edit_py; a vendored copy at this " Search order: an explicit g:klammertext_edit_py; a vendored copy at this
@@ -37,7 +38,8 @@ function! s:EditScript() abort
endfunction endfunction
function! s:Python() abort function! s:Python() abort
return exists('g:klammertext_python') ? g:klammertext_python : 'python3' return exists('g:klammertext_python') ? g:klammertext_python
\ : (has('win32') ? 'python' : 'python3')
endfunction endfunction
" Run the core CLI over the whole buffer. ARGS is the argument string " Run the core CLI over the whole buffer. ARGS is the argument string
+9 -7
View File
@@ -18,8 +18,9 @@ language's structure, everywhere.
- VS Code 1.75 or later — a minimum, not a target: VS Code's monthly - VS Code 1.75 or later — a minimum, not a target: VS Code's monthly
releases count 1.75, 1.76, … (1.75 is from January 2023), so any releases count 1.75, 1.76, … (1.75 is from January 2023), so any
version from the last few years qualifies. version from the last few years qualifies.
- `python3` on `PATH` (or set `klammertext.pythonPath`); Klammertext itself - `python3` on `PATH` — on Windows `python`, the name python.org installs
already requires Python. (or set `klammertext.pythonPath`); Klammertext itself already requires
Python.
- The language server, found automatically in this order: - The language server, found automatically in this order:
1. the `klammertext.serverPath` setting, if set; 1. the `klammertext.serverPath` setting, if set;
2. `klammertext_ls.py` vendored next to `extension.js` (the layout the 2. `klammertext_ls.py` vendored next to `extension.js` (the layout the
@@ -32,8 +33,9 @@ language's structure, everywhere.
## Install ## Install
Package the extension as a `.vsix` and install it (the builder needs only Package the extension as a `.vsix` and install it (the builder needs only
bash and python3; the editing zip from the Klammertext website ships a bash and Python — on Windows, run it in the MSYS2 shell the Klammertext
prebuilt `klammertext.vsix`, so there the first step is already done): build uses; the editing zip from the Klammertext website ships a prebuilt
`klammertext.vsix`, so there the first step is already done):
./make_vsix.sh ./make_vsix.sh
code --install-extension klammertext.vsix code --install-extension klammertext.vsix
@@ -129,7 +131,7 @@ Shortcuts** (`Ctrl+K Ctrl+S`), search "klammertext", and rebind.
## Settings ## Settings
A normal installation needs neither setting: the extension runs `python3` A normal installation needs neither setting: the extension runs `python3`
from `PATH` and finds the language server automatically (the copy vendored (on Windows `python`) from `PATH` and finds the language server automatically (the copy vendored
next to `extension.js`, then `../shared/`, then next to `extension.js`, then `../shared/`, then
`$KLAMMERTEXT_HOME/doc/edit/shared/`). They exist for unusual setups. `$KLAMMERTEXT_HOME/doc/edit/shared/`). They exist for unusual setups.
Set them in the Settings UI (`Ctrl+,`, search "klammertext") or in Set them in the Settings UI (`Ctrl+,`, search "klammertext") or in
@@ -138,10 +140,10 @@ reload the window after changing either.
| Setting | Meaning (default) | | Setting | Meaning (default) |
|---|---| |---|---|
| `klammertext.pythonPath` | Python interpreter for the server (`python3`) | | `klammertext.pythonPath` | Python interpreter for the server (`python3`; on Windows `python`) |
| `klammertext.serverPath` | full path to `klammertext_ls.py` (auto-located) | | `klammertext.serverPath` | full path to `klammertext_ls.py` (auto-located) |
`pythonPath` matters when `python3` is not on the `PATH` VS Code sees — a `pythonPath` matters when the interpreter is not on the `PATH` VS Code sees — a
VS Code launched from the desktop inherits a different environment than VS Code launched from the desktop inherits a different environment than
your shell — or when a specific interpreter is wanted. `serverPath` your shell — or when a specific interpreter is wanted. `serverPath`
matters only when the server file lives outside the search chain above. matters only when the server file lives outside the search chain above.
+5 -1
View File
@@ -165,7 +165,11 @@ function activate(context) {
'diagnostics, formatting, matching and alignment need the server.'); 'diagnostics, formatting, matching and alignment need the server.');
return; return;
} }
const python = vscode.workspace.getConfiguration('klammertext').get('pythonPath') || 'python3'; // The setting's declared default is empty so that this fallback is what
// decides: python3 everywhere but Windows, where python.org installs
// "python" only (the Klammertext commands there use the same name).
const python = vscode.workspace.getConfiguration('klammertext').get('pythonPath') ||
(process.platform === 'win32' ? 'python' : 'python3');
client = new LspClient(python, [serverPath], log); client = new LspClient(python, [serverPath], log);
client.proc.on('error', (err) => { client.proc.on('error', (err) => {
vscode.window.showWarningMessage( vscode.window.showWarningMessage(
+9 -3
View File
@@ -9,7 +9,7 @@
# code --install-extension klammertext.vsix # code --install-extension klammertext.vsix
# #
# A .vsix is a zip holding a two-file manifest plus the extension payload # A .vsix is a zip holding a two-file manifest plus the extension payload
# under extension/. This script builds it with bash + python3 only — no # under extension/. This script builds it with bash + Python only — no
# npm, no vsce — matching the extension's no-dependency design. The shared # npm, no vsce — matching the extension's no-dependency design. The shared
# core and language server are vendored into the payload: from this folder # core and language server are vendored into the payload: from this folder
# if present (the editing-zip layout), else from ../shared (the repository # if present (the editing-zip layout), else from ../shared (the repository
@@ -18,6 +18,12 @@
set -e set -e
cd "$(dirname "$0")" cd "$(dirname "$0")"
# The interpreter: $PYTHON if the environment names one (env/runtime.env
# does), else python3, else python -- the only name python.org installs on
# Windows, where this runs in the MSYS2 shell.
PYTHON="${PYTHON:-$(command -v python3 || command -v python)}"
[ -n "$PYTHON" ] || { echo "make_vsix.sh: no python3 or python on PATH" >&2; exit 1; }
OUT="$PWD/klammertext.vsix" OUT="$PWD/klammertext.vsix"
STAGE="$(mktemp -d)" STAGE="$(mktemp -d)"
trap 'rm -rf "$STAGE"' EXIT trap 'rm -rf "$STAGE"' EXIT
@@ -36,7 +42,7 @@ for f in klammertext_edit.py klammertext_ls.py; do
fi fi
done done
VERSION=$(python3 -c "import json; print(json.load(open('package.json'))['version'])") VERSION=$("$PYTHON" -c "import json; print(json.load(open('package.json'))['version'])")
cat > "$STAGE/[Content_Types].xml" <<'EOF' cat > "$STAGE/[Content_Types].xml" <<'EOF'
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
@@ -75,7 +81,7 @@ cat > "$STAGE/extension.vsixmanifest" <<EOF
</PackageManifest> </PackageManifest>
EOF EOF
python3 - "$STAGE" "$OUT" <<'EOF' "$PYTHON" - "$STAGE" "$OUT" <<'EOF'
import os, sys, zipfile import os, sys, zipfile
stage, out = sys.argv[1], sys.argv[2] stage, out = sys.argv[1], sys.argv[2]
with zipfile.ZipFile(out, 'w', zipfile.ZIP_DEFLATED) as z: with zipfile.ZipFile(out, 'w', zipfile.ZIP_DEFLATED) as z:
+2 -2
View File
@@ -83,8 +83,8 @@
"properties": { "properties": {
"klammertext.pythonPath": { "klammertext.pythonPath": {
"type": "string", "type": "string",
"default": "python3", "default": "",
"description": "Python interpreter used to run the Klammertext language server." "description": "Python interpreter used to run the Klammertext language server. Leave blank for python3 (on Windows: python)."
}, },
"klammertext.serverPath": { "klammertext.serverPath": {
"type": "string", "type": "string",
+13 -7
View File
@@ -7,6 +7,8 @@ including `:haskell` and `:shell` commands that depend on locally installed
software. software.
For the container installation on Linux, see `linux_container_install.md`. For the container installation on Linux, see `linux_container_install.md`.
On Windows, this page also applies unchanged inside the Windows Subsystem
for Linux; `windows_wsl_install.md` says what is specific to WSL.
## Prerequisites ## Prerequisites
@@ -21,15 +23,19 @@ sudo apt-get install g++ make python3-dev
The C++ compiler must support C++20. GCC 11 or later is required (Ubuntu 22.04 The C++ compiler must support C++20. GCC 11 or later is required (Ubuntu 22.04
and later include GCC 12+). and later include GCC 12+).
The SKS `@image` klammer requires OpenImageIO Python bindings. These must match The SKS `@image` klammer requires the OpenImageIO Python bindings, for the
the Python version that ktext is built against (check with same Python that ktext is built against (the system `python3`). Ubuntu
`python3.XX -c "import OpenImageIO"`). For example, if ktext links against packages them:
Python 3.12:
```bash ```bash
pip3.12 install OpenImageIO sudo apt-get install python3-openimageio
python3 -c "import OpenImageIO"
``` ```
(A `pip` install works too, but a fresh Ubuntu has no `pip` until
`python3-pip` is installed, and since 24.04 it refuses to install into the
system Python without `--break-system-packages`; the package avoids both.)
Verify: Verify:
```bash ```bash
@@ -284,8 +290,8 @@ ktext -s '@eval 1 + 1 @' -d
# Shell evaluation # Shell evaluation
ktext -s '@eval :shell date @' -d ktext -s '@eval :shell date @' -d
# Show machine state (SKS is loaded by default) # Show the machine's initial state: targets, argument types, the primitive klammers
ktext -s '' -m kdiag --machine
# HTML and PDF output (uses the hello.kt from the Build section) # HTML and PDF output (uses the hello.kt from the Build section)
ktext hello.kt -t html ktext hello.kt -t html
+88 -13
View File
@@ -13,6 +13,14 @@ The installation uses three things a Windows user installs once:
building; **Python** from python.org, which Klammertext embeds; and **TeX building; **Python** from python.org, which Klammertext embeds; and **TeX
Live**, for PDF output. Everyday use needs only PowerShell. Live**, for PDF output. Everyday use needs only PowerShell.
This is one of three ways to have Klammertext on Windows. If you are at
home in Linux, the Windows Subsystem for Linux gives you the Linux
installation unchanged, with the Windows editors and browser alongside:
`windows_wsl_install.md`. If you want no build at all, the prebuilt
container images run on Windows through Docker Desktop, which that page
also describes. This page is the native route: Windows programs, no
Linux layer.
## Prerequisites ## Prerequisites
@@ -119,10 +127,11 @@ optimizing compiler may run out of memory and report `out of memory
allocating`. If so, run `make -C com` without `-j`: slower, and it allocating`. If so, run `make -C com` without `-j`: slower, and it
finishes. finishes.
This builds the library, the Standard Klammer Set's components and the This builds the library (`klammertext.dll`), the Standard Klammer Set's
three commands into `bin\`, and copies the library and the compiler's components and the three commands (`ktext.exe`, `kdesc.exe`, `kdiag.exe`)
runtime DLLs there too, so `bin\` is self-contained. The Python binding is into `bin\`, and copies the library and the compiler's runtime DLLs there
built into `kpy\`. Verify: too, so `bin\` is self-contained. The Python binding is built into `kpy\`.
Verify:
```bash ```bash
ktext -s '@eval 1 + 1 @' -d # prints 2 ktext -s '@eval 1 + 1 @' -d # prints 2
@@ -221,21 +230,37 @@ ktext hello.kt -t pdf # writes hello.pdf
## Using the commands from PowerShell ## Using the commands from PowerShell
Building needs the UCRT64 shell; using Klammertext does not. For everyday Building needs the UCRT64 shell; using Klammertext does not. For everyday
use from PowerShell, put three directories on the `Path` and set use from PowerShell, put two directories on the `Path` and set
`KLAMMERTEXT_HOME`, once, as user environment variables (Settings ▸ `KLAMMERTEXT_HOME`, once, as user environment variables (Settings ▸
System ▸ About ▸ Advanced system settings ▸ Environment Variables, or with System ▸ About ▸ Advanced system settings ▸ Environment Variables, or
`setx`): from PowerShell):
```powershell ```powershell
setx KLAMMERTEXT_HOME "C:\Users\<you>\klammertext" [Environment]::SetEnvironmentVariable("KLAMMERTEXT_HOME", "C:\Users\<you>\klammertext", "User")
setx Path "$env:Path;C:\Users\<you>\klammertext\bin;C:\Users\<you>\external\texlive\2026\bin\windows" $p = [Environment]::GetEnvironmentVariable("Path", "User")
[Environment]::SetEnvironmentVariable("Path", "$p;C:\Users\<you>\klammertext\bin;C:\Users\<you>\external\texlive\2026\bin\windows", "User")
``` ```
Python is already on the `Path` from its installer. Open a new PowerShell (Not `setx Path "$env:Path;…"`: `$env:Path` is the machine and user
values merged, so that copies every machine entry into your user `Path`,
and `setx` silently cuts a value over 1024 characters.) Python is already
on the `Path` from its installer. Open a new PowerShell
window and `ktext`, `kdesc` and `kdiag` work there directly. Paths window and `ktext`, `kdesc` and `kdiag` work there directly. Paths
Klammertext prints use forward slashes on every system; paths you type may Klammertext prints use forward slashes on every system; paths you type may
use either. use either.
One PowerShell rule to know when you pass Klammertext text with `-s`: an
apostrophe inside a single-quoted string ends the string, so `It's` must
be written `It''s` (two apostrophes), or the whole text put in double
quotes. The error you get otherwise mentions a "here-string header",
because PowerShell reads the next `@'` as one; it is PowerShell's message,
not Klammertext's.
```powershell
ktext -s '@i It''s a Pyrrhic victory! @' -d
ktext -s "@i It's a Pyrrhic victory! @" -d
```
## Verifying the installation ## Verifying the installation
@@ -250,12 +275,12 @@ ktext hello.kt -t html # html
ktext hello.kt -t pdf # pdf, requires TeX Live ktext hello.kt -t pdf # pdf, requires TeX Live
make -C $KLAMMERTEXT_HOME/tst test # the engine's test suites make -C $KLAMMERTEXT_HOME/tst test # the engine's test suites
make -C $KLAMMERTEXT_HOME/sks/tst test # the Standard Klammer Set's suites (needs TeX Live)
``` ```
A few suite cases report `SKIP` on Windows for things the MSYS2 shell A few suite cases report `SKIP` on Windows for things the MSYS2 shell
cannot provide, such as a real terminal for the colour tests or a cannot provide, such as a real terminal for the colour tests or a
symbolic link; that is expected. symbolic link; that is expected. (The Standard Klammer Set's own suites
are part of the development repository, not of this distribution.)
## Updating ## Updating
@@ -269,6 +294,56 @@ make -C com -j
``` ```
## Editor support
Klammertext's editor support (`doc\edit\`, one shared implementation
behind VS Code, Sublime Text, Vim and Emacs integrations) works on Windows
with the Python already installed. Each editor's README in `doc\edit\`
has the details; the Windows-specific steps are these.
**Visual Studio Code.** Build the extension package in the UCRT64 shell,
then install it from PowerShell (or from any window where `code` is on the
`Path`):
```bash
cd ~/klammertext/doc/edit/vscode && ./make_vsix.sh
```
```powershell
code --install-extension C:\Users\<you>\klammertext\doc\edit\vscode\klammertext.vsix
```
Reload VS Code. The extension runs `python` from the `Path` and finds the
language server inside the package; nothing needs configuring.
**Sublime Text 4.** Copy the package and the shared core into Sublime's
`Packages` directory (**Preferences → Browse Packages…** opens it):
```powershell
$p = "$env:AppData\Sublime Text\Packages\Klammertext"
New-Item -ItemType Directory -Force $p | Out-Null
Copy-Item C:\Users\<you>\klammertext\doc\edit\sublime\* $p
Copy-Item C:\Users\<you>\klammertext\doc\edit\shared\klammertext_edit.py $p
```
Sublime loads the package live.
**Notepad++.** Highlighting comes from a User Defined Language file, the
delimiter check and reindentation from two Run-menu commands
(`doc\edit\notepadpp\README.md` has both):
```powershell
$d = "$env:AppData\Notepad++\userDefineLangs"
New-Item -ItemType Directory -Force $d | Out-Null
Copy-Item C:\Users\<you>\klammertext\doc\edit\notepadpp\Klammertext.udl.xml $d
```
Restart Notepad++; `.kt` and `.k` files open as Klammertext.
**Vim.** As in `doc\edit\vim\README.md`; the plugin runs the shared core
through `python` on Windows automatically.
## Troubleshooting ## Troubleshooting
**The command window closes at once, or "The code execution cannot proceed **The command window closes at once, or "The code execution cannot proceed
@@ -282,7 +357,7 @@ runtime, which nearly every Windows machine already has but a freshly
installed one may not. Install it from installed one may not. Install it from
https://aka.ms/vs/17/release/vc_redist.x64.exe and try the import again. https://aka.ms/vs/17/release/vc_redist.x64.exe and try the import again.
**"libklammertext.so was not found" or "libstdc++-6.dll was not found".** **"klammertext.dll was not found" or "libstdc++-6.dll was not found".**
`bin\` is not on the `Path`, or the build did not finish: `make -C com -j` `bin\` is not on the `Path`, or the build did not finish: `make -C com -j`
places the library and the runtime DLLs in `bin\` beside the commands. places the library and the runtime DLLs in `bin\` beside the commands.
+153
View File
@@ -0,0 +1,153 @@
# Klammertext on Windows through the Windows Subsystem for Linux
This page is for someone who works in Linux by habit and has a Windows
machine to work on. The Windows Subsystem for Linux (WSL 2) runs a real
Linux kernel and an ordinary Ubuntu inside Windows, and Klammertext's
Linux installation applies there unchanged: the same compiler, the same
Python, the same TeX Live, the same `runtime.env`. So this page is short.
It gets you to an Ubuntu prompt, sends you to the Linux page, and then
says what is specific to living in WSL: where files belong, how to look
at the output from Windows, and how the Windows editors reach the Linux
side. The steps were followed on a Windows 11 machine with WSL 2.7 and
the Ubuntu it installs today, 26.04.
There are two other ways to have Klammertext on Windows: as native
Windows programs built from source (`windows_source_install.md`), and
from the prebuilt container images, which this page covers at the end,
since Docker runs inside WSL as it runs on Linux.
## Getting Linux
In a PowerShell window:
```powershell
wsl --install
```
This turns on the subsystem, installs the WSL layer and the current
Ubuntu, and may ask you to restart Windows; do that before going on.
Then open a terminal and type `wsl` (or start "Ubuntu" from the Start
menu). The first start asks for a Linux username and a password; these
are separate from your Windows account. Ubuntu may also ask whether to
send platform metrics; you can say no. You are then at a bash prompt in
your Linux home directory, and `wsl -l -v` in PowerShell shows Ubuntu as
version 2.
Two things worth knowing about the Ubuntu you now have: `sudo` works with
the password you just chose, and the machine's Windows `Path` is appended
to the Linux `PATH`, so Windows programs can be run from the Linux prompt
by name (`explorer.exe`, `code`). That is convenient below and harmless
otherwise; a Windows Python or TeX Live on the machine is not picked up
by Linux, since Linux does not resolve `.exe` names.
## Installing Klammertext
Follow `linux_source_install.md` at the Ubuntu prompt, from its first
line. Everything on it applies: the packages by `apt-get`, the clone, the
line in `~/.bashrc` that sources `runtime.env`, the build, TeX Live by the
bundled script, the verification. Nothing on that page is different
inside WSL. On the machine this page was written on, the build took under
a minute and TeX Live nine, and the engine's test suite passed as it does
on Linux.
Keep the clone and your documents in the Linux file system, under your
Linux home, not under `/mnt/c`. The Windows drives are mounted there and
work, but every file operation crosses between the two systems, and a
build or a PDF run on `/mnt/c` is several times slower.
## Seeing the output from Windows
WSL has no browser or PDF viewer of its own, and it needs none. Windows
sees the Linux file system as a network path, `\\wsl.localhost\Ubuntu\`,
and Windows programs can open files there. From the Linux prompt, after
`ktext hello.kt -t html` and `ktext hello.kt -t pdf`:
```bash
# at the Ubuntu prompt (type wsl in PowerShell to get there), not in PowerShell
explorer.exe "$(wslpath -w hello/index.html)"
explorer.exe "$(wslpath -w hello.pdf)"
```
`wslpath -w` turns a Linux path into the Windows form of the same file,
and `explorer.exe` opens it with whatever Windows opens that kind of file
with: your browser, your PDF viewer. The first time, Windows may ask
which application to use. `explorer.exe .` opens the current Linux
directory in Explorer, and from the Windows side the same files are in
Explorer under "Linux" in the sidebar. (`explorer.exe` reports exit
status 1 even when it succeeds; a script that needs a true status can use
`powershell.exe -NoProfile -Command "Start-Process \"$(wslpath -w hello.pdf)\""`
instead.)
## Editors
The Windows editors reach the Linux side, and the Linux editors are
simply there.
**Visual Studio Code**, the case this page was written for. Install VS
Code on Windows and its "WSL" extension (Microsoft's
`ms-vscode-remote.remote-wsl`). Then, at the Linux prompt, in the
directory you want to work in:
```bash
code .
```
The first time, this installs VS Code's Linux-side server into your
Linux home and opens a VS Code window on Windows connected to it. The
Klammertext extension is installed on that Linux side, once, from the
package built in the clone:
```bash
cd ~/klammertext/doc/edit/vscode && ./make_vsix.sh && code --install-extension klammertext.vsix
```
Run from the Linux prompt, `code --install-extension` installs to the
WSL side, where the extension then starts the Klammertext language
server with the Linux `python3`; everything in `doc/edit/vscode/README.md`
applies as on Linux. (Extensions installed on the Windows side of VS Code
are a separate list and do not run in WSL windows.)
**Emacs and Vim** are Linux programs: `sudo apt-get install emacs` or
`vim`, then the Emacs and Vim packages under `doc/edit/` as on Linux.
**Sublime Text and Notepad++** on Windows can open the Linux files
through `\\wsl.localhost\Ubuntu\...` and highlight them with their
Klammertext packages (`doc/edit/sublime/`, `doc/edit/notepadpp/`); their
commands that run the shared core would run the Windows `python`, so for
those two editors the native Windows installation is the better fit.
## The container route
If you would rather not build anything, the prebuilt Klammertext images
run inside WSL exactly as `linux_container_install.md` describes, since
the Ubuntu WSL installs today runs systemd and therefore Docker's
service: follow that page at the Ubuntu prompt, from `apt-get install
docker.io` on. That was done on the machine this page was written on:
the image, 2.4 GB with its TeX Live, pulled in about a minute, and its
`ktext` made the page's web page and PDF, owned by the Linux user. Docker
Desktop for Windows is the other way to run the same images; its engine
is WSL 2 as well, and it has its own licence terms for larger companies.
## Troubleshooting
**`wsl --install` says virtualization is not enabled.** WSL 2 needs the
CPU's virtualization support turned on in the machine's firmware
settings; the name varies (Intel VT-x, AMD-V, SVM). Turn it on and run
the command again.
**`code .` says the command is not found.** VS Code's `bin` directory is
put on the Windows `Path` by its installer; open a new WSL terminal after
installing VS Code, so the Linux `PATH` picks it up.
**A build or a PDF run is slow.** The clone or the document is under
`/mnt/c`. Move it under your Linux home.
**The Ubuntu window asks for a username again.** The distribution was
reset or reinstalled; the setup creates a fresh Linux home, and
Klammertext must be installed again inside it.
+26 -6
View File
@@ -63,14 +63,16 @@ SHARED = -dynamiclib
SONAME = -install_name @rpath/$(notdir $@) SONAME = -install_name @rpath/$(notdir $@)
ORIGIN := @loader_path ORIGIN := @loader_path
EXPORT_DYNAMIC = -Wl,-export_dynamic EXPORT_DYNAMIC = -Wl,-export_dynamic
EXE :=
DSO := .so
KLAMMERTEXT_LIBFILE := libklammertext.so
else ifneq (,$(filter MINGW% MSYS%,$(UNAME_S))) else ifneq (,$(filter MINGW% MSYS%,$(UNAME_S)))
# ---------- Windows (MSYS2, UCRT64 environment; native PE binaries) ---------- # ---------- Windows (MSYS2, UCRT64 environment; native PE binaries) ----------
# uname says MINGW64_NT-... under UCRT64 too. The compilers come from the # uname says MINGW64_NT-... under UCRT64 too. The compilers come from the
# environment's PATH (/ucrt64/bin). libdl.a is the mingw-w64-dlfcn shim over # environment's PATH (/ucrt64/bin). libdl.a is the mingw-w64-dlfcn shim over
# LoadLibrary, linked statically so no libdl.dll travels with the products. # LoadLibrary, linked statically so no libdl.dll travels with the products.
# PE has no soname and no rpath; a DLL is found through PATH or # PE has no soname and no rpath; a DLL is found through PATH or
# the executable's own directory, and GNU ld's -l search list on PE does not # the executable's own directory.
# include ".so", hence the exact-name form below.
GCC := g++ GCC := g++
CLANG := clang++ CLANG := clang++
LDFLAGS = LDFLAGS =
@@ -79,15 +81,20 @@ SHARED = -shared
SONAME = SONAME =
ORIGIN := $$ORIGIN ORIGIN := $$ORIGIN
EXPORT_DYNAMIC = -Wl,--export-all-symbols EXPORT_DYNAMIC = -Wl,--export-all-symbols
KLAMMERTEXT_LIB = -l:libklammertext.so EXE := .exe
DSO := .dll
KLAMMERTEXT_LIBFILE := klammertext.dll
# A Windows program finds a DLL through PATH or its own directory, never an # A Windows program finds a DLL through PATH or its own directory, never an
# rpath. So every directory that holds a program or a Python extension also # rpath. So every directory that holds a program or a Python extension also
# holds the DLLs it needs: libklammertext.so and the three gcc runtime DLLs # holds the DLLs it needs: klammertext.dll and the three gcc runtime DLLs
# the toolchain links (from the compiler's own bin/). com/ and kpy/ copy them. # the toolchain links (from the compiler's own bin/). com/ and kpy/ copy them.
GCC_BIN := $(dir $(shell cygpath -m "$$(command -v g++)")) GCC_BIN := $(dir $(shell cygpath -m "$$(command -v g++)"))
GCC_RUNTIME_DLLS := libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll GCC_RUNTIME_DLLS := libstdc++-6.dll libgcc_s_seh-1.dll libwinpthread-1.dll
else else
# ---------- Linux ---------- # ---------- Linux ----------
EXE :=
DSO := .so
KLAMMERTEXT_LIBFILE := libklammertext.so
GCC := /usr/bin/g++ GCC := /usr/bin/g++
CLANG := $(shell command -v clang++-18 2>/dev/null || command -v clang++ 2>/dev/null) CLANG := $(shell command -v clang++-18 2>/dev/null || command -v clang++ 2>/dev/null)
LDFLAGS = -L/usr/lib/x86_64-linux-gnu LDFLAGS = -L/usr/lib/x86_64-linux-gnu
@@ -97,8 +104,21 @@ SONAME = -Wl,-soname,$(notdir $@)
ORIGIN := $$ORIGIN ORIGIN := $$ORIGIN
EXPORT_DYNAMIC = -rdynamic EXPORT_DYNAMIC = -rdynamic
endif endif
# How a link line names libklammertext (set above for Windows). # Product names (decided 2026-09-25: Windows users get Windows names). The
KLAMMERTEXT_LIB ?= -lklammertext # three variables above are set per platform: EXE is the executable suffix
# (".exe" on Windows, none elsewhere), DSO the suffix of a shared object
# loaded at run time (".dll" / ".so": document$(DSO), reflect$(DSO)), and
# KLAMMERTEXT_LIBFILE the library's file name (klammertext.dll /
# libklammertext.so). A make target carries the suffix so that the file the
# compiler writes IS the target: bin/ktext never existed on Windows -- gcc
# wrote ktext.exe -- and every make relinked the three commands. The engine's
# own copy of the DSO suffix is shared_library_suffix in mac/file.h.
#
# One link form everywhere: GNU ld's -l search list on PE ends with
# "<name>.dll", so -lklammertext finds klammertext.dll as it finds
# libklammertext.so on Linux (it did NOT find a DLL called libklammertext.so,
# which needed the exact-name -l: form).
KLAMMERTEXT_LIB = -lklammertext
# Resolve the compiler choice (gcc by default; clang with COMPILER=clang). # Resolve the compiler choice (gcc by default; clang with COMPILER=clang).
CXX := $(if $(filter clang,$(COMPILER)),$(CLANG),$(GCC)) CXX := $(if $(filter clang,$(COMPILER)),$(CLANG),$(GCC))
+6 -6
View File
@@ -1,8 +1,8 @@
# kpy/ -- the Python binding of the Klammermachine. # kpy/ -- the Python binding of the Klammermachine.
# #
# klammertext.cpp is a CPython extension module (see its header comment). # klammertext.cpp is a CPython extension module (see its header comment).
# Like ref/reflect.so and sks/document/document.so it links against # Like ref/reflect$(DSO) and sks/document/document$(DSO) it links against
# libklammertext.so and shares the engine's class layouts, so it is rebuilt # the shared library and shares the engine's class layouts, so it is rebuilt
# whenever they are (mac/Makefile's "all" runs "make -C kpy"; com/Makefile's # whenever they are (mac/Makefile's "all" runs "make -C kpy"; com/Makefile's
# "redo" and dev/rebuild.sh clean it -- a clean that misses a directory is a # "redo" and dev/rebuild.sh clean it -- a clean that misses a directory is a
# mixed-object hazard, not a tidiness one). # mixed-object hazard, not a tidiness one).
@@ -19,7 +19,7 @@ K := $(KLAMMERTEXT_HOME)
include $(K)/env/makefile.env include $(K)/env/makefile.env
LIBDIR := $(K)/lib LIBDIR := $(K)/lib
LIBRARY := $(LIBDIR)/libklammertext.so LIBRARY := $(LIBDIR)/$(KLAMMERTEXT_LIBFILE)
EXT_SUFFIX := $(shell $(PYTHON) -c 'import sysconfig; print(sysconfig.get_config_var("EXT_SUFFIX"))') EXT_SUFFIX := $(shell $(PYTHON) -c 'import sysconfig; print(sysconfig.get_config_var("EXT_SUFFIX"))')
MODULE := klammertext$(EXT_SUFFIX) MODULE := klammertext$(EXT_SUFFIX)
@@ -38,7 +38,7 @@ DEPFLAGS = -MMD -MP -MF $(@:.o=.d)
# extension's dependencies from the extension's own directory, never from # extension's dependencies from the extension's own directory, never from
# PATH or an rpath -- there is none on PE); see makefile.env. # PATH or an rpath -- there is none on PE); see makefile.env.
ifneq (,$(filter MINGW% MSYS%,$(UNAME_S))) ifneq (,$(filter MINGW% MSYS%,$(UNAME_S)))
BESIDE_DLLS := libklammertext.so $(GCC_RUNTIME_DLLS) BESIDE_DLLS := $(KLAMMERTEXT_LIBFILE) $(GCC_RUNTIME_DLLS)
else else
BESIDE_DLLS := BESIDE_DLLS :=
endif endif
@@ -54,14 +54,14 @@ $(MODULE) : $(OBJECTS) $(LIBRARY)
-o $@ $(OBJECTS) $(KLAMMERTEXT_LIB) $(LDLIBS) -o $@ $(OBJECTS) $(KLAMMERTEXT_LIB) $(LDLIBS)
ifneq (,$(filter MINGW% MSYS%,$(UNAME_S))) ifneq (,$(filter MINGW% MSYS%,$(UNAME_S)))
libklammertext.so : $(LIBRARY) $(KLAMMERTEXT_LIBFILE) : $(LIBRARY)
cp $< $@ cp $< $@
%.dll : $(GCC_BIN)%.dll %.dll : $(GCC_BIN)%.dll
cp $< $@ cp $< $@
endif endif
clean : clean :
rm -f $(OBJECTS) $(DEPFILES) klammertext.*.so klammertext.*.pyd libklammertext.so *.dll *~ rm -f $(OBJECTS) $(DEPFILES) klammertext.*.so klammertext.*.pyd *.dll *~
rm -rf __pycache__ rm -rf __pycache__
-include $(DEPFILES) -include $(DEPFILES)
+2 -2
View File
@@ -1,5 +1,5 @@
# lib # lib
This directory holds the compiled Klammermachine shared library This directory holds the compiled Klammermachine shared library
(`libklammertext.so`) after you build Klammertext with `make -C com`. (`libklammertext.so`; on Windows `klammertext.dll`) after you build
It is empty in the repository. Klammertext with `make -C com`. It is empty in the repository.
+1 -1
View File
@@ -21,7 +21,7 @@ DEPFILES := $(addsuffix .d,$(BASENAMES))
# Shared library # Shared library
LIBDIR := ../lib LIBDIR := ../lib
LIBRARY := $(LIBDIR)/libklammertext.so LIBRARY := $(LIBDIR)/$(KLAMMERTEXT_LIBFILE)
# Compiler flags for dependency generation # Compiler flags for dependency generation
DEPFLAGS = -MMD -MP -MF $(@:.o=.d) DEPFLAGS = -MMD -MP -MF $(@:.o=.d)
+31
View File
@@ -1,7 +1,11 @@
#ifdef _WIN32 #ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <windows.h>
#include <fcntl.h> #include <fcntl.h>
#include <io.h> #include <io.h>
#endif #endif
#include <cstdlib>
#include <tuple> #include <tuple>
#include "argv.h" #include "argv.h"
@@ -39,6 +43,26 @@ void set_verbose_level(int argc, char* argv[])
// byte. The C runtime's text mode would append it to every line. // byte. The C runtime's text mode would append it to every line.
_setmode(_fileno(stdout), _O_BINARY); _setmode(_fileno(stdout), _O_BINARY);
_setmode(_fileno(stderr), _O_BINARY); _setmode(_fileno(stderr), _O_BINARY);
// A console decodes what a program writes in the CONSOLE's output code
// page, which on a localized Windows is an OEM page (850 on a German one)
// and never UTF-8, whatever the process's own code page (the manifest
// makes that UTF-8). The commands write UTF-8 -- kdesc --katoms's
// ⟨name⟩, an error report's ⟨katom⟩ -- so a console showed three cp850
// characters per symbol while a file or a pipe received the right bytes
// (which is why no suite saw it; Andy did, 2026-09-25). Switch the
// console to UTF-8 while a command runs, when a stream is a console, and
// put it back at exit: the page belongs to the console, not the process,
// and would outlive it.
static UINT console_output_cp = 0;
static UINT console_output_cp_now = 0;
if (_isatty(_fileno(stdout)) || _isatty(_fileno(stderr))) {
console_output_cp = GetConsoleOutputCP();
if (console_output_cp != 0 && console_output_cp != CP_UTF8) {
SetConsoleOutputCP(CP_UTF8);
console_output_cp_now = GetConsoleOutputCP();
std::atexit([] { SetConsoleOutputCP(console_output_cp); });
}
}
#endif #endif
//command_name = absolute_pathname(argv[0]); //command_name = absolute_pathname(argv[0]);
command_name = fs::path(argv[0]).generic_string(); command_name = fs::path(argv[0]).generic_string();
@@ -49,6 +73,13 @@ void set_verbose_level(int argc, char* argv[])
args.opt("v", "'verbosity'", "level", "0", "'verbosity'"); args.opt("v", "'verbosity'", "level", "0", "'verbosity'");
args.parse(argc, argv, false); args.parse(argc, argv, false);
verbose_level = args.as_verbosity("v"); verbose_level = args.as_verbosity("v");
#ifdef _WIN32
if (console_output_cp != 0 && console_output_cp != CP_UTF8) {
K::log(1, "Console output code page set to UTF-8 for this run (was "
+ std::to_string(console_output_cp) + ", now "
+ std::to_string(console_output_cp_now) + ")");
}
#endif
} }
bool show_usage(int argc, char* argv[]) bool show_usage(int argc, char* argv[])
+1 -1
View File
@@ -320,7 +320,7 @@ std::string Eval::eval_command(katom_iter begin, katom_iter end)
if (!khome.empty()) { if (!khome.empty()) {
lib_text = string_replace(lib_text, "*KLAMMERTEXT_HOME*", khome); lib_text = string_replace(lib_text, "*KLAMMERTEXT_HOME*", khome);
} }
fs::path libpath(lib_text + ".so"); fs::path libpath(lib_text + shared_library_suffix);
// A relative library name not found from the cwd is searched in the // A relative library name not found from the cwd is searched in the
// directories of the files the Machine has read (same rule as the // directories of the files the Machine has read (same rule as the
// Python module path: the library lives next to the file using it). // Python module path: the library lives next to the file using it).
+6 -3
View File
@@ -72,14 +72,17 @@ size_t count_substrings(const std::string& text, const std::string& substring) {
std::string home_directory() std::string home_directory()
{ {
if (const char* home = std::getenv("HOME"); home && *home) {
return home;
}
#ifdef _WIN32 #ifdef _WIN32
// USERPROFILE first: an MSYS2 shell may set HOME to its own /home/<user>
// (C:\msys64\home\<user>), and a Windows user's ~/.klammertext must be
// the same directory from PowerShell and from that shell.
if (const char* profile = std::getenv("USERPROFILE"); profile && *profile) { if (const char* profile = std::getenv("USERPROFILE"); profile && *profile) {
return profile; return profile;
} }
#endif #endif
if (const char* home = std::getenv("HOME"); home && *home) {
return home;
}
return {}; return {};
} }
+11 -3
View File
@@ -25,9 +25,17 @@ constexpr char path_list_separator = ';';
#else #else
constexpr char path_list_separator = ':'; constexpr char path_list_separator = ':';
#endif #endif
// The user's home directory as the system names it: HOME, and on Windows // The suffix of a shared library loaded at run time (the library a
// USERPROFILE when HOME is unset (a plain PowerShell or cmd sets no HOME; // "@eval :cpp" names): ".dll" on Windows, ".so" everywhere else -- the same
// an MSYS2 shell does). Empty when neither is set. // choice makefile.env's DSO makes when it builds one.
#ifdef _WIN32
constexpr const char* shared_library_suffix = ".dll";
#else
constexpr const char* shared_library_suffix = ".so";
#endif
// The user's home directory as the system names it: HOME; on Windows
// USERPROFILE first, so the directory is the same from PowerShell (no HOME)
// and from an MSYS2 shell (which may set HOME to its own /home/<user>).
std::string home_directory(); std::string home_directory();
// The first PROGRAM on PATH (split on path_list_separator; on Windows the // The first PROGRAM on PATH (split on path_list_separator; on Windows the
// name also tried with ".exe"), or "" when none: what "which" answers, without // name also tried with ".exe"), or "" when none: what "which" answers, without
+9 -7
View File
@@ -4,7 +4,8 @@
# character tables and return Klammertext (a "@table" call and such), which # character tables and return Klammertext (a "@table" call and such), which
# the .kt fragments in this directory then render for whichever target is # the .kt fragments in this directory then render for whichever target is
# in force. They are reached through "@eval :cpp", so this builds a DSO # in force. They are reached through "@eval :cpp", so this builds a DSO
# loaded at run time, not part of libklammertext.so. # loaded at run time, not part of the shared library: reflect.so, on
# Windows reflect.dll (makefile.env's DSO).
# #
# Adapted from sks/document/Makefile. Two things differ and both matter: # Adapted from sks/document/Makefile. Two things differ and both matter:
# ref/ has no sks dependencies (a generator returns Klammertext, so it # ref/ has no sks dependencies (a generator returns Klammertext, so it
@@ -19,10 +20,11 @@ include $(K)/env/makefile.env
# reflect.cpp includes (machine, character, error, show). No local -I. # reflect.cpp includes (machine, character, error, show). No local -I.
LIBDIR := $(K)/lib LIBDIR := $(K)/lib
LIBRARY := $(LIBDIR)/libklammertext.so LIBRARY := $(LIBDIR)/$(KLAMMERTEXT_LIBFILE)
REFLECT := reflect$(DSO)
# "../lib", not "../../lib": reflect.so sits in $(K)/ref, so one level up # "../lib", not "../../lib": the DSO sits in $(K)/ref, so one level up
# reaches $(K)/lib. (sks/document.so needs two, which is what this was # reaches $(K)/lib. (sks/document's needs two, which is what this was
# copied from -- a wrong rpath here shows up only at dlopen time, and only # copied from -- a wrong rpath here shows up only at dlopen time, and only
# where the loading process does not already have the library.) # where the loading process does not already have the library.)
REF_LDFLAGS := $(SHARED) $(EXPORT_DYNAMIC) -Wl,-rpath,'$(ORIGIN)/../lib' -L$(LIBDIR) REF_LDFLAGS := $(SHARED) $(EXPORT_DYNAMIC) -Wl,-rpath,'$(ORIGIN)/../lib' -L$(LIBDIR)
@@ -34,7 +36,7 @@ DEPFLAGS = -MMD -MP -MF $(@:.o=.d)
.PHONY: all clean .PHONY: all clean
all : reflect.so all : $(REFLECT)
%.o : %.cpp %.o : %.cpp
@echo Compiling $< @echo Compiling $<
@@ -46,11 +48,11 @@ all : reflect.so
# for the same file -- compiling reflect.cpp inline AND linking reflect.o -- # for the same file -- compiling reflect.cpp inline AND linking reflect.o --
# defines every symbol in it twice. Building through the pattern rule also # defines every symbol in it twice. Building through the pattern rule also
# gets reflect.d, so a header change rebuilds it. # gets reflect.d, so a header change rebuilds it.
reflect.so : $(OBJECTS) $(LIBRARY) $(REFLECT) : $(OBJECTS) $(LIBRARY)
$(CXX) $(CPPFLAGS) $(CXXFLAGS) $(REF_LDFLAGS) $(LDFLAGS) \ $(CXX) $(CPPFLAGS) $(CXXFLAGS) $(REF_LDFLAGS) $(LDFLAGS) \
-o $@ $(OBJECTS) $(KLAMMERTEXT_LIB) $(LDLIBS) -o $@ $(OBJECTS) $(KLAMMERTEXT_LIB) $(LDLIBS)
clean : clean :
rm -f $(OBJECTS) $(DEPFILES) *.so *~ rm -f $(OBJECTS) $(DEPFILES) *.so *.dll *~
-include $(DEPFILES) -include $(DEPFILES)
+14 -11
View File
@@ -1,31 +1,34 @@
# Klammertext sks/ Makefile # Klammertext sks/ Makefile
# Builds all .so files in sks/ subdirectories # Builds all the DSOs (document$(DSO): .so, on Windows .dll) in sks/
# subdirectories
K := $(KLAMMERTEXT_HOME) K := $(KLAMMERTEXT_HOME)
KS := $(K)/sks KS := $(K)/sks
KM := $(K)/mac KM := $(K)/mac
# Shared library that all .so files depend on include $(K)/env/makefile.env
LIBRARY := $(K)/lib/libklammertext.so
# Shared library that all the DSOs depend on
LIBRARY := $(K)/lib/$(KLAMMERTEXT_LIBFILE)
# Support directories (build .o files used by other sks/ components) # Support directories (build .o files used by other sks/ components)
SUPPORT_DIRS := kutil target SUPPORT_DIRS := kutil target
# Excluded directories: # Excluded directories:
# kutil, target - support directories that build .o files, not .so # kutil, target - support directories that build .o files, not a DSO
# book, phase - obsolete, not updated for libklammertext.so # book, phase - obsolete, not updated for the shared library
# Directories that build .so files (excluding support and obsolete directories) # Directories that build a DSO (excluding support and obsolete directories):
# Find all subdirectories with Makefiles that have .so build targets # every subdirectory whose Makefile names its product with makefile.env's
# (look for lines like "xyz.so :" or "all : xyz.so") # $(DSO) suffix ("document$(DSO) :", "all : document$(DSO)").
SO_DIRS := $(shell for dir in $(KS)/*/; do \ SO_DIRS := $(shell for dir in $(KS)/*/; do \
if [ -f "$${dir}Makefile" ] && grep -qE '^[a-z]+\.so\s*:|all\s*:.*\.so' "$${dir}Makefile" 2>/dev/null; then \ if [ -f "$${dir}Makefile" ] && grep -qE '\$$\(DSO\)' "$${dir}Makefile" 2>/dev/null; then \
basename "$$dir"; \ basename "$$dir"; \
fi; done | grep -v -E '^(kutil|target|book|phase)$$') fi; done | grep -v -E '^(kutil|target|book|phase)$$')
.PHONY: all clean support $(SUPPORT_DIRS) $(SO_DIRS) .PHONY: all clean support $(SUPPORT_DIRS) $(SO_DIRS)
# Default target: build support first, then all .so files # Default target: build support first, then every DSO
all : support $(SO_DIRS) all : support $(SO_DIRS)
# Build support directories # Build support directories
@@ -37,7 +40,7 @@ kutil :
target : kutil target : kutil
$(MAKE) -j -C $(KS)/target $(MAKE) -j -C $(KS)/target
# Pattern rule for .so directories # Pattern rule for the DSO directories
# Each depends on the library and support directories # Each depends on the library and support directories
$(SO_DIRS) : support $(LIBRARY) $(SO_DIRS) : support $(LIBRARY)
$(MAKE) -j -C $(KS)/$@ $(MAKE) -j -C $(KS)/$@
+10 -7
View File
@@ -12,16 +12,19 @@ LOCAL_CPPFLAGS := -I$(KM) -I$(KS)/kutil -I$(KS)/target
# Shared library location # Shared library location
LIBDIR := $(K)/lib LIBDIR := $(K)/lib
LIBRARY := $(LIBDIR)/libklammertext.so LIBRARY := $(LIBDIR)/$(KLAMMERTEXT_LIBFILE)
# Linker flags for document.so # The product: document.so, on Windows document.dll (makefile.env's DSO)
DOCUMENT := document$(DSO)
# Linker flags for the DSO
DOC_LDFLAGS := $(SHARED) $(EXPORT_DYNAMIC) -Wl,-rpath,'$(ORIGIN)/../../lib' -L$(LIBDIR) DOC_LDFLAGS := $(SHARED) $(EXPORT_DYNAMIC) -Wl,-rpath,'$(ORIGIN)/../../lib' -L$(LIBDIR)
# Local object files # Local object files
LOCAL_OBJECTS := document_class.o document_html.o document_latex.o heading.o reference.o LOCAL_OBJECTS := document_class.o document_html.o document_latex.o heading.o reference.o
LOCAL_DEPFILES := document_class.d document_html.d document_latex.d heading.d reference.d document.d LOCAL_DEPFILES := document_class.d document_html.d document_latex.d heading.d reference.d document.d
# External object files from sks/ (mac/*.o now in libklammertext.so) # External object files from sks/ (mac/*.o now in the shared library)
SKS_OBJECTS := $(KS)/kutil/kutil.o $(KS)/kutil/klammer_base.o \ SKS_OBJECTS := $(KS)/kutil/kutil.o $(KS)/kutil/klammer_base.o \
$(KS)/target/html_util.o $(KS)/target/latex_util.o $(KS)/target/html_util.o $(KS)/target/latex_util.o
@@ -33,7 +36,7 @@ DEPFLAGS = -MMD -MP -MF $(@:.o=.d)
.PHONY: all clean deps .PHONY: all clean deps
# Default target # Default target
all : document.so all : $(DOCUMENT)
# Build external dependencies first # Build external dependencies first
deps : deps :
@@ -45,13 +48,13 @@ deps :
@echo Compiling $< @echo Compiling $<
$(CXX) -c $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(CXXFLAGS) $(DEPFLAGS) $< -o $@ $(CXX) -c $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(CXXFLAGS) $(DEPFLAGS) $< -o $@
# Shared object - link against libklammertext.so # Shared object - link against the library
document.so : $(LOCAL_OBJECTS) $(SKS_OBJECTS) $(LIBRARY) document.cpp | deps $(DOCUMENT) : $(LOCAL_OBJECTS) $(SKS_OBJECTS) $(LIBRARY) document.cpp | deps
$(CXX) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(CXXFLAGS) $(DOC_LDFLAGS) $(LDFLAGS) \ $(CXX) $(CPPFLAGS) $(LOCAL_CPPFLAGS) $(CXXFLAGS) $(DOC_LDFLAGS) $(LDFLAGS) \
document.cpp -o $@ $(ALL_OBJECTS) $(KLAMMERTEXT_LIB) $(LDLIBS) document.cpp -o $@ $(ALL_OBJECTS) $(KLAMMERTEXT_LIB) $(LDLIBS)
clean : clean :
rm -f $(LOCAL_OBJECTS) $(LOCAL_DEPFILES) *.so *~ rm -f $(LOCAL_OBJECTS) $(LOCAL_DEPFILES) *.so *.dll *~
# document_txt.py is imported at render time (2026-09-18), so this # document_txt.py is imported at render time (2026-09-18), so this
# directory now exists here; rm -f refused it and failed every clean. # directory now exists here; rm -f refused it and failed every clean.
rm -rf __pycache__ rm -rf __pycache__
+3 -1
View File
@@ -62,7 +62,9 @@ def main():
out = run_cli(script, ['align', str(line), str(col)], s) out = run_cli(script, ['align', str(line), str(col)], s)
else: else:
sys.exit("editor_driver.py: unknown mode: " + mode) sys.exit("editor_driver.py: unknown mode: " + mode)
with open(outfile, 'w') as f: # newline='': the core's "\n" reaches the file untranslated on every
# system (text mode would write CRLF on Windows, against LF fixtures).
with open(outfile, 'w', newline='') as f:
f.write(out) f.write(out)
# Error and diagnostics paths. # Error and diagnostics paths.
+3 -1
View File
@@ -81,7 +81,9 @@ const vscodeStub = {
Uri: { parse: (s) => ({ toString: () => s }) }, Uri: { parse: (s) => ({ toString: () => s }) },
DiagnosticSeverity: { Error: 0, Warning: 1 }, DiagnosticSeverity: { Error: 0, Warning: 1 },
workspace: { workspace: {
getConfiguration: () => ({ get: (k) => (k === 'pythonPath' ? 'python3' : '') }), // Both settings blank: the extension's own defaults are what is tested
// (python3, on Windows python; the server located by the search chain).
getConfiguration: () => ({ get: () => '' }),
textDocuments: [fakeDoc], textDocuments: [fakeDoc],
onDidOpenTextDocument: (fn) => { listeners.open.push(fn); return { dispose() {} }; }, onDidOpenTextDocument: (fn) => { listeners.open.push(fn); return { dispose() {} }; },
onDidChangeTextDocument: (fn) => { listeners.change.push(fn); return { dispose() {} }; }, onDidChangeTextDocument: (fn) => { listeners.change.push(fn); return { dispose() {} }; },
+50 -8
View File
@@ -59,6 +59,17 @@ FIX="$HERE/editor"
OUT="$(mktemp -d)" OUT="$(mktemp -d)"
trap 'rm -rf "$OUT"' EXIT trap 'rm -rf "$OUT"' EXIT
# A path written INSIDE a Python string must be one the interpreter can open.
# Under MSYS2 the shell's /c/... form is converted only for standalone
# arguments to a native program, never inside a string, so those get the
# Windows form (forward slashes; a backslash in a Python literal is an escape).
native() {
if command -v cygpath >/dev/null 2>&1; then cygpath -m "$1"; else printf '%s' "$1"; fi
}
SHARED_NATIVE="$(native "$SHARED_DIR")"
SUBLIME_NATIVE="$(native "$SUBLIME_DIR")"
OUT_NATIVE="$(native "$OUT")"
INDENT_FIXTURES="indent_list indent_document indent_table indent_untouched INDENT_FIXTURES="indent_list indent_document indent_table indent_untouched
indent_defs indent_escapes indent_named_close indent_literal_c" indent_defs indent_escapes indent_named_close indent_literal_c"
ALIGN_FIXTURES="align_mixed align_empty_cells align_boundary align_colspan ALIGN_FIXTURES="align_mixed align_empty_cells align_boundary align_colspan
@@ -105,10 +116,10 @@ fi
# --- the Sublime adapters resolve to the shared core ----------------------- # --- the Sublime adapters resolve to the shared core -----------------------
if PYTHONDONTWRITEBYTECODE=1 "$PYTHON" -c " if PYTHONDONTWRITEBYTECODE=1 "$PYTHON" -c "
import sys import os, sys
sys.path.insert(0, '$SUBLIME_DIR') sys.path.insert(0, '$SUBLIME_NATIVE')
import Klammertext, Klammertext_indent, Klammertext_align import Klammertext, Klammertext_indent, Klammertext_align
assert Klammertext.KE.__file__.startswith('$SHARED_DIR') assert os.path.normpath(Klammertext.KE.__file__).startswith(os.path.normpath('$SHARED_NATIVE'))
" 2>"$OUT/sublime.log"; then " 2>"$OUT/sublime.log"; then
echo "${green}PASS${reset} sublime adapters import the shared core" echo "${green}PASS${reset} sublime adapters import the shared core"
PASS=$((PASS + 1)) PASS=$((PASS + 1))
@@ -118,6 +129,25 @@ else
FAIL=$((FAIL + 1)) FAIL=$((FAIL + 1))
fi fi
# --- the Notepad++ UDL: well-formed, and it restates the literal klammer -----
# (a static artifact of the SYNC set; nothing else can check it)
if PYTHONDONTWRITEBYTECODE=1 "$PYTHON" -c "
import sys, xml.etree.ElementTree as ET
u = ET.parse('$(native "$EDIT_DIR")/notepadpp/Klammertext.udl.xml').getroot().find('UserLang')
assert u.get('name') == 'Klammertext' and set(u.get('ext').split()) == {'kt', 'k'}, u.attrib
lists = {k.get('name'): (k.text or '') for k in u.find('KeywordLists')}
assert '00@code' in lists['Delimiters'].split() and '02code@' in lists['Delimiters'].split(), lists['Delimiters']
assert lists['Keywords1'].strip() == '@@@' and lists['Keywords2'].strip() == '@@' and lists['Keywords3'].strip() == '@'
assert '03#[' in lists['Comments'].split() and '04]#' in lists['Comments'].split()
" 2>"$OUT/udl.log"; then
echo "${green}PASS${reset} notepad++ UDL is well-formed and restates the literal klammer"
PASS=$((PASS + 1))
else
echo "${red}FAIL${reset} notepad++ UDL is well-formed and restates the literal klammer"
cat "$OUT/udl.log"
FAIL=$((FAIL + 1))
fi
# --- the language server protocol test ------------------------------------- # --- the language server protocol test -------------------------------------
if PYTHONDONTWRITEBYTECODE=1 "$PYTHON" "$FIX/ls_test.py" "$SHARED_DIR" "$FIX" >"$OUT/ls.log" 2>&1; then if PYTHONDONTWRITEBYTECODE=1 "$PYTHON" "$FIX/ls_test.py" "$SHARED_DIR" "$FIX" >"$OUT/ls.log" 2>&1; then
echo "${green}PASS${reset} language server protocol ($(grep -c '^PASS' "$OUT/ls.log") checks)" echo "${green}PASS${reset} language server protocol ($(grep -c '^PASS' "$OUT/ls.log") checks)"
@@ -230,17 +260,29 @@ else
fi fi
# --- the VS Code extension (optional: needs a Node runtime) ---------------- # --- the VS Code extension (optional: needs a Node runtime) ----------------
# node itself, or VS Code's Electron binary run as Node. # node itself, or VS Code's Electron binary run as Node, wherever an
# installed VS Code keeps it: the Linux package, the macOS app bundle, the
# Windows per-user install (LOCALAPPDATA is a Windows variable, so the shell
# is MSYS2 there and cygpath exists).
CODE_BIN=""
if [ -x /usr/share/code/code ]; then
CODE_BIN=/usr/share/code/code
elif [ -x "/Applications/Visual Studio Code.app/Contents/MacOS/Electron" ]; then
CODE_BIN="/Applications/Visual Studio Code.app/Contents/MacOS/Electron"
elif [ -n "$LOCALAPPDATA" ] && command -v cygpath >/dev/null 2>&1; then
c="$(cygpath -u "$LOCALAPPDATA")/Programs/Microsoft VS Code/Code.exe"
[ -x "$c" ] && CODE_BIN="$c"
fi
run_node() { run_node() {
if command -v node >/dev/null 2>&1; then if command -v node >/dev/null 2>&1; then
node "$@" node "$@"
elif [ -x /usr/share/code/code ]; then elif [ -n "$CODE_BIN" ]; then
ELECTRON_RUN_AS_NODE=1 /usr/share/code/code "$@" ELECTRON_RUN_AS_NODE=1 "$CODE_BIN" "$@"
else else
return 127 return 127
fi fi
} }
if command -v node >/dev/null 2>&1 || [ -x /usr/share/code/code ]; then if command -v node >/dev/null 2>&1 || [ -n "$CODE_BIN" ]; then
if run_node "$FIX/vscode_ext_test.js" "$EDIT_DIR/vscode" "$FIX" >"$OUT/vscode.log" 2>&1; then if run_node "$FIX/vscode_ext_test.js" "$EDIT_DIR/vscode" "$FIX" >"$OUT/vscode.log" 2>&1; then
echo "${green}PASS${reset} vscode extension ($(grep -c '^PASS' "$OUT/vscode.log") checks)" echo "${green}PASS${reset} vscode extension ($(grep -c '^PASS' "$OUT/vscode.log") checks)"
PASS=$((PASS + 1)) PASS=$((PASS + 1))
@@ -260,7 +302,7 @@ cp -R "$EDIT_DIR/vscode" "$EDIT_DIR/shared" "$OUT/vsix/"
if ( cd "$OUT/vsix/vscode" && ./make_vsix.sh >/dev/null 2>&1 ) && \ if ( cd "$OUT/vsix/vscode" && ./make_vsix.sh >/dev/null 2>&1 ) && \
PYTHONDONTWRITEBYTECODE=1 "$PYTHON" -c " PYTHONDONTWRITEBYTECODE=1 "$PYTHON" -c "
import sys, zipfile import sys, zipfile
names = set(zipfile.ZipFile('$OUT/vsix/vscode/klammertext.vsix').namelist()) names = set(zipfile.ZipFile('$OUT_NATIVE/vsix/vscode/klammertext.vsix').namelist())
need = {'extension.vsixmanifest', '[Content_Types].xml', need = {'extension.vsixmanifest', '[Content_Types].xml',
'extension/package.json', 'extension/extension.js', 'extension/package.json', 'extension/extension.js',
'extension/language-configuration.json', 'extension/README.md', 'extension/language-configuration.json', 'extension/README.md',
+6 -2
View File
@@ -86,7 +86,10 @@ check_contains() {
FAIL=$((FAIL + 1)) FAIL=$((FAIL + 1))
return return
fi fi
if printf '%s' "$output" | grep -qF "$needle"; then # Wrap-insensitive: error text is justified to 80 columns, and where the
# needle breaks across a line depends on the installation's path length
# (found in Windows Sandbox, whose user name is long: "was not \nfound").
if printf '%s' "$output" | tr '\n' ' ' | tr -s ' ' | grep -qF "$needle"; then
echo "${green}PASS${reset} $test_name" echo "${green}PASS${reset} $test_name"
PASS=$((PASS + 1)) PASS=$((PASS + 1))
else else
@@ -114,7 +117,8 @@ check_fails() {
FAIL=$((FAIL + 1)) FAIL=$((FAIL + 1))
return return
fi fi
if printf '%s' "$output" | grep -qF "$needle"; then # Wrap-insensitive, like the other helper above.
if printf '%s' "$output" | tr '\n' ' ' | tr -s ' ' | grep -qF "$needle"; then
echo "${green}PASS${reset} $test_name" echo "${green}PASS${reset} $test_name"
PASS=$((PASS + 1)) PASS=$((PASS + 1))
else else