Offer-motivated features: :hline defaults, ranged :hpos, @date :days, :bottom none
- @table: a writer's :hline/:vline replaces the default lines; new
boundary name 'none' removes all lines
- @table: ranged :hpos argument overrides :cell_hpos per cell
(\multicolumn{1} in tex; positions a colspan anchor's merged cell)
- @date/@datetime: :days offset argument (sks/date/date.py)
- @document: ":bottom none" suppresses the footer (\pagestyle{empty})
Also carries the escape-system generator/renderer fixes, per-cell-range
:format, and uppercase .TTF/.OTF font recognition from klammertext-dev.
This commit is contained in:
83
mac/.clang-tidy
Normal file
83
mac/.clang-tidy
Normal file
@@ -0,0 +1,83 @@
|
||||
Checks: >
|
||||
*,
|
||||
-boost-*,
|
||||
-fuchsia-*,
|
||||
-google-*,
|
||||
-zircon-*,
|
||||
-abseil-*,
|
||||
-modernize-use-trailing-return-type,
|
||||
-llvmlibc-*,
|
||||
-altera-*,
|
||||
-android-*,
|
||||
-objc-*,
|
||||
-fuchsia-*,
|
||||
-hicpp-*,
|
||||
-misc-*,
|
||||
-performance-*,
|
||||
-portability-*,
|
||||
-readability-*,
|
||||
-bugprone-*,
|
||||
-cert-*,
|
||||
-clang-analyzer-*,
|
||||
-cppcoreguidelines-*,
|
||||
-google-*,
|
||||
-hicpp-*,
|
||||
-llvm-*,
|
||||
-llvmlibc-*,
|
||||
-misc-*,
|
||||
-modernize-*,
|
||||
-performance-*,
|
||||
-portability-*,
|
||||
-readability-*,
|
||||
-zircon-*
|
||||
|
||||
WarningsAsErrors: ''
|
||||
HeaderFilterRegex: '.*'
|
||||
FormatStyle: none
|
||||
CheckOptions:
|
||||
- key: readability-identifier-naming.ClassCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.ClassMemberCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.ConstexprVariableCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.ConstexprVariablePrefix
|
||||
value: k
|
||||
- key: readability-identifier-naming.EnumCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.EnumConstantCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.FunctionCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.GlobalConstantCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.GlobalConstantPrefix
|
||||
value: k
|
||||
- key: readability-identifier-naming.GlobalVariableCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.GlobalVariablePrefix
|
||||
value: g_
|
||||
- key: readability-identifier-naming.LocalConstantCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.LocalVariableCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.MemberCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.MemberPrefix
|
||||
value: m_
|
||||
- key: readability-identifier-naming.NamespaceCase
|
||||
value: lower_case
|
||||
- key: readability-identifier-naming.PrivateMemberPrefix
|
||||
value: m_
|
||||
- key: readability-identifier-naming.StaticConstantCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.StaticConstantPrefix
|
||||
value: k
|
||||
- key: readability-identifier-naming.StaticVariableCase
|
||||
value: camelBack
|
||||
- key: readability-identifier-naming.StaticVariablePrefix
|
||||
value: s_
|
||||
- key: readability-identifier-naming.TemplateParameterCase
|
||||
value: CamelCase
|
||||
- key: readability-identifier-naming.VariableCase
|
||||
value: camelBack
|
||||
14
mac/eval.cpp
14
mac/eval.cpp
@@ -6,6 +6,7 @@
|
||||
#include "show.h"
|
||||
#include "katom.h"
|
||||
#include "file.h"
|
||||
#include <algorithm>
|
||||
#include <unistd.h>
|
||||
|
||||
std::string shell(State state, std::string command, Locator loc)
|
||||
@@ -151,8 +152,19 @@ katom_list Eval::eval(katom_iter begin, katom_iter end)
|
||||
}
|
||||
katom_list result {};
|
||||
Machine M = m_machine;
|
||||
size_t before = M.m_katoms.size();
|
||||
M.read(eval_result);
|
||||
M.apply(M.m_state.value("K_target"), false, false);
|
||||
// If the @eval produced more Klammertext -- the read-back result still
|
||||
// holds klammers to reduce (a "generator", e.g. a Python klammer returning
|
||||
// a @table call with data) -- then its text is writer content: escape it
|
||||
// for the target before applying, exactly as typed input is escaped, so a
|
||||
// "%" in the data becomes "\%". If it produced final target markup (no
|
||||
// klammers left, a "renderer" such as @table itself emitting \begin{tabular})
|
||||
// leave it untouched. ^'...'^ literal spans are skipped by the escape pass,
|
||||
// so a generator can still carry raw target markup.
|
||||
bool has_klammer = std::any_of(
|
||||
M.m_katoms.begin() + before, M.m_katoms.end(), begin_apply);
|
||||
M.apply(M.m_state.value("K_target"), false, has_klammer);
|
||||
result = trim(M.m_katoms);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,18 @@
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
|
||||
// A filesystem extension lowercased for case-insensitive matching, so a
|
||||
// font named Foo.TTF or Foo.OTF is recognized like Foo.ttf / Foo.otf.
|
||||
static std::string lower_extension(const fs::path& path)
|
||||
{
|
||||
std::string ext = path.extension();
|
||||
for (char& c : ext) {
|
||||
c = std::tolower(static_cast<unsigned char>(c));
|
||||
}
|
||||
return ext;
|
||||
}
|
||||
|
||||
|
||||
std::string name_to_dirname(std::string name)
|
||||
{
|
||||
std::string result {};
|
||||
@@ -275,7 +287,10 @@ Font_file classify_font_file(const std::string& path)
|
||||
{
|
||||
Font_file file {};
|
||||
file.path = path;
|
||||
file.extension = fs::path(path).extension();
|
||||
// Normalize to lowercase so the installed filename, its @font-face url,
|
||||
// and the truetype/opentype format detection are uniform regardless of
|
||||
// how the source file's extension was capitalized.
|
||||
file.extension = lower_extension(path);
|
||||
|
||||
std::string d = read_binary_file(path);
|
||||
if (d.size() < 12) {
|
||||
@@ -372,7 +387,7 @@ std::vector<Font_file> classify_font_files(const std::string& directory)
|
||||
"The font directory \"" + directory + "\" does not exist");
|
||||
}
|
||||
for (auto& entry : fs::recursive_directory_iterator(directory)) {
|
||||
std::string ext = entry.path().extension();
|
||||
std::string ext = lower_extension(entry.path());
|
||||
if (entry.is_regular_file() && (ext == ".ttf" || ext == ".otf")) {
|
||||
result.push_back(classify_font_file(entry.path()));
|
||||
}
|
||||
|
||||
@@ -214,6 +214,7 @@ void Klammer::copy_components(
|
||||
}
|
||||
for (auto c : cs) {
|
||||
m_body[c.target] = c.body;
|
||||
m_body_generic[c.target] = (c.target == Target_set::general_name);
|
||||
m_varmap[c.target] = c.varmap;
|
||||
}
|
||||
}
|
||||
@@ -287,6 +288,7 @@ void Klammer::copy_general_klammer_to_undefined(Target_set targets)
|
||||
if (m_body.count(target_name) == 0 && target_name != Target_set::declare_name) {
|
||||
// std::cout << " Copying to " << target_name << "\n";
|
||||
m_body[target_name] = body;
|
||||
m_body_generic[target_name] = true; // general body -> writer content
|
||||
m_defloc[target_name] = loc;
|
||||
m_varmap[target_name] = varmap;
|
||||
}
|
||||
|
||||
@@ -86,6 +86,9 @@ public:
|
||||
std::map<std::string, Locator> m_defloc {}; // target -> Locator
|
||||
std::map<std::string, defmode_t> m_defmode {}; // target -> defmode
|
||||
target_variable_map_t m_varmap {}; // target -> map: variable -> index
|
||||
// target -> true if this target's body came from a general ("*") definition
|
||||
// (writer content, subject to target escaping) vs a target-specific one.
|
||||
std::map<std::string, bool> m_body_generic {};
|
||||
};
|
||||
|
||||
std::string klammer_name_from_katom(std::string s, Locator loc);
|
||||
|
||||
@@ -413,6 +413,66 @@ katom_list Machine::apply_klammer(
|
||||
result[i].m_type = katom_t::text;
|
||||
}
|
||||
}
|
||||
// Escape target-specific characters (e.g. tex "&" -> "\&") in the writer
|
||||
// text of a GENERAL klammer's body. Runs BEFORE process_katoms/apply()
|
||||
// below expand the body, so that target-native markup pulled in by nested
|
||||
// klammers (e.g. nl.tex -> "\newline") is left untouched -- only this
|
||||
// klammer's own literal writer text is escaped here; nested klammers escape
|
||||
// theirs when they are applied in turn. Bodies from target-specific
|
||||
// definitions (m_body_generic[target] == false) are already in target form
|
||||
// and skipped. KTESC markers are idempotent, so text already escaped at the
|
||||
// top level passes through unchanged. Two kinds of body content are NOT
|
||||
// writer text and must be skipped:
|
||||
// * ^'...'^ literal spans -- raw target markup the writer typed directly.
|
||||
// At this point they are typed literal_begin/literal_end with plain-text
|
||||
// content (the literal phase runs in process_katoms, below), so track
|
||||
// span depth rather than testing katom type.
|
||||
// * @eval / @read / @cond argument spans -- code, filenames, and
|
||||
// predicates consumed by the primitive, NOT emitted as target text.
|
||||
// (Escaping an underscore in "offer.Price_list(K)" broke @eval.) The
|
||||
// primitive's KLAMMERTEXT result, produced by process_katoms below, is
|
||||
// klammer output and is likewise never escaped -- it is inserted after
|
||||
// this pass and so is untouched, matching the top-level behavior where
|
||||
// @eval is resolved before the escape pass runs.
|
||||
auto gen = klammer.m_body_generic.find(target);
|
||||
if (gen != klammer.m_body_generic.end() && gen->second) {
|
||||
Target tgt = m_targets.get(target, Locator());
|
||||
if (!tgt.m_escapes.empty()) {
|
||||
int literal_depth = 0;
|
||||
int code_depth = 0; // inside an @eval/@read/@cond span
|
||||
std::vector<bool> apply_is_code; // one entry per open application
|
||||
for (auto& k : result) {
|
||||
if (k.m_type == katom_t::literal_begin) { ++literal_depth; continue; }
|
||||
if (k.m_type == katom_t::literal_end) {
|
||||
if (literal_depth > 0) --literal_depth;
|
||||
continue;
|
||||
}
|
||||
if (k.m_type == katom_t::eval_begin ||
|
||||
k.m_type == katom_t::read_begin ||
|
||||
k.m_type == katom_t::cond_begin) {
|
||||
apply_is_code.push_back(true);
|
||||
++code_depth;
|
||||
continue;
|
||||
}
|
||||
if (k.m_type == katom_t::apply_begin) {
|
||||
apply_is_code.push_back(false);
|
||||
continue;
|
||||
}
|
||||
if (k.m_type == katom_t::apply_end) {
|
||||
if (!apply_is_code.empty()) {
|
||||
if (apply_is_code.back()) --code_depth;
|
||||
apply_is_code.pop_back();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (literal_depth == 0 && code_depth == 0 &&
|
||||
(k.m_type == katom_t::text ||
|
||||
k.m_type == katom_t::word ||
|
||||
k.m_type == katom_t::newline))
|
||||
k.m_text = tgt.escape_text(k.m_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
process_katoms(result, klammer.m_name);
|
||||
apply(m_klammers, result, target);
|
||||
m_state.close_frame();
|
||||
|
||||
Reference in New Issue
Block a user