Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
8fd8ad9
A text view keeps carets and selections besides its main one
RyeMutt Oct 3, 2026
156b8cc
Keys act at every caret where a text view has several
RyeMutt Oct 3, 2026
b0dfad8
Carets are added by Alt-click, Control-Alt with an arrow, and Ctrl-D
RyeMutt Oct 3, 2026
7d70438
SLua's forin snippet names its loop's variables k and v
RyeMutt Oct 3, 2026
dc9934c
A function made global at the top is a lint of its own; a for's varia…
RyeMutt Oct 3, 2026
48d3eaf
Apply on previewed fixes says why where it makes none
RyeMutt Oct 3, 2026
1f8b909
Apply on previewed fixes says so where the tab cannot be changed
RyeMutt Oct 3, 2026
d341ff8
SLua's weights name its handlers and count its strings on their lines
RyeMutt Oct 3, 2026
151f6bd
vcpkg installs again when the overlay ports asked for change
RyeMutt Oct 3, 2026
e1cd659
LSL on Luau's weights come to each line
RyeMutt Oct 3, 2026
8516b8b
On Linux what is copied is the viewer's to keep, not SDL's
RyeMutt Oct 3, 2026
5edd96f
SDL keeps the clipboard the viewer set as its own
RyeMutt Oct 3, 2026
81542cc
A column is selected by Shift-Alt-drag and Control-Shift-Alt with an …
RyeMutt Oct 3, 2026
7da187c
A #define goes on over a backslash before CRLF, and past blanks after it
RyeMutt Oct 3, 2026
beb0d73
NexiiLSL's preprocessor use is golden, and every golden file runs wit…
RyeMutt Oct 3, 2026
512e84b
Vim leaves insert mode with one caret, and the live mirrors go
RyeMutt Oct 3, 2026
eb95dca
A caret on a folded line opens it, and folding moves every caret out
RyeMutt Oct 3, 2026
f56fa88
The studio's last refills and holds: shared problems, held tabs, incl…
RyeMutt Oct 3, 2026
aea515e
LSL to SLua: a parameter only read as a truth is a boolean
RyeMutt Oct 3, 2026
0ff8d80
LSL to SLua: a string built in a loop is declared where it is joined
RyeMutt Oct 3, 2026
ff2574b
A global first set inside a function, and named across the script, is…
RyeMutt Oct 3, 2026
a69be2e
A heavy string is a part of its own in a script's weight
RyeMutt Oct 3, 2026
9801790
An estate manager sees each object's script time beside its memory
RyeMutt Oct 3, 2026
f2b36c0
The find bar's Next and Previous let the other carets go, as F3 does
RyeMutt Oct 3, 2026
ab820f8
The offline reference files functions by the region's categories
RyeMutt Oct 3, 2026
573c566
The Weights heading's tip says how each thing it says is counted
RyeMutt Oct 3, 2026
50a71e4
Top Scripts answers are told apart by the owner their rows name
RyeMutt Oct 3, 2026
03cfd2f
LSL to SLua: a counting loop over a list's items walks the items
RyeMutt Oct 3, 2026
df5759d
A snippets notecard can be followed, its snippets offered as it stands
RyeMutt Oct 3, 2026
a311935
A land statistics answer no window waits for builds none
RyeMutt Oct 3, 2026
48d385e
The Snippets tab says why a followed notecard's snippets are not there
RyeMutt Oct 3, 2026
4cd5ff9
A directive read as code says the preprocessor is off
RyeMutt Oct 4, 2026
8439655
A person or group dragged over a notecard box no longer crashes
RyeMutt Oct 4, 2026
d4dd1f7
LSL to SLua: a string two loops build is declared once, and one read …
RyeMutt Oct 4, 2026
7bdafb0
Several carets: Tab, Fold All, Cut, jumps, closers and vim keep to ev…
RyeMutt Oct 4, 2026
13e1274
Top Scripts answers go where they were asked, and say no more than th…
RyeMutt Oct 4, 2026
fe002f3
A followed notecard that cannot be read is not fetched again each second
RyeMutt Oct 4, 2026
150fd8b
SLua weights: a table's written values are placed, and long names cut…
RyeMutt Oct 4, 2026
702289c
A global set in a block of the top is not one set inside a function
RyeMutt Oct 4, 2026
0b7de05
A script with directives is preprocessed whatever the setting, and sa…
RyeMutt Oct 4, 2026
1b2109b
The wide-character column test rounds to an S32
RyeMutt Oct 4, 2026
95c9a43
Merge branch 'develop' into rye/scriptstudio2
RyeMutt Oct 4, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
213 changes: 191 additions & 22 deletions indra/alscript/allsltoslua.cpp

Large diffs are not rendered by default.

193 changes: 184 additions & 9 deletions indra/alscript/alpreprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -287,12 +287,35 @@ namespace

Tokens run(std::string_view text)
{
// A line ends at CRLF and at a lone CR as at LF (5.1.1.2 phase
// 1), as the studio's own text does: lexed from a copy with them
// made LF, where text from a disk, an external editor or an
// inventory include has them -- else a backslash before CRLF
// joins nothing and its CR is a blank.
if (!mVerbatim && text.find('\r') != std::string_view::npos)
{
mUnixText.clear();
mUnixText.reserve(text.size());
for (size_t i = 0; i < text.size(); ++i)
{
if (text[i] != '\r')
{
mUnixText += text[i];
}
else if (i + 1 >= text.size() || text[i + 1] != '\n')
{
mUnixText += '\n';
}
}
text = mUnixText;
}
mText = text;
mPos = 0;
mLine = 0;
mColumn = 0;
mJoined = 0;
mOut.clear();
mSpacedJoins.clear();
while (mPos < mText.size())
{
if (mLua)
Expand Down Expand Up @@ -341,20 +364,64 @@ namespace

// A backslash and a newline join two lines; both vanish, and the
// newline that ends the joined line stands for them all, so that
// the lines after keep their numbers.
// the lines after keep their numbers. On a directive's line, blanks
// between the two are let by, as GCC and Clang let them by, with a
// warning: they are not seen, and what a #define then left behind
// went on as code. Not elsewhere, where a note ending in a
// backslash and a space would take the line of code after it.
bool continuation()
{
if (!mVerbatim && at(mPos) == '\\' && at(mPos + 1) == '\n')
if (mVerbatim || at(mPos) != '\\')
{
mPos += 2;
++mLine;
mColumn = 0;
++mJoined;
return true;
return false;
}
return false;
size_t newline = mPos + 1;
while (isBlank(at(newline)))
{
++newline;
}
if (at(newline) != '\n')
{
return false;
}
if (newline > mPos + 1)
{
if (!onDirectiveLine())
{
return false;
}
mSpacedJoins.push_back({ mLine, mColumn });
}
mPos = newline + 1;
++mLine;
mColumn = 0;
++mJoined;
return true;
}

// Whether the line being lexed -- since the last newline not
// joined away -- begins, past blanks and comments, with what starts
// a directive.
bool onDirectiveLine() const
{
const Token* first = nullptr;
for (auto it = mOut.rbegin(); it != mOut.rend() && it->kind != Kind::Newline; ++it)
{
if (it->kind != Kind::Space && it->kind != Kind::Comment)
{
first = &*it;
}
}
return first && first->kind == Kind::Punct && first->text == hashOf(mLua);
}

public:
// Where a backslash was joined to its newline over blanks, by its
// line and column, for a warning.
const std::vector<std::pair<S32, S32>>& spacedJoins() const { return mSpacedJoins; }

private:

// Every join where a token goes on: C joins the lines before it
// reads a token (5.1.1.2 phase 2), so one runs on over a join --
// a name, a number, a directive's name, a // comment. LSL's alone:
Expand Down Expand Up @@ -818,6 +885,9 @@ namespace
S32 mJoined = 0;
Token mToken;
Tokens mOut;
// The text with its line ends made LF, where it had others.
std::string mUnixText;
std::vector<std::pair<S32, S32>> mSpacedJoins;
};

// The text of a run of tokens, as written.
Expand Down Expand Up @@ -1266,7 +1336,17 @@ namespace
else
{
f->index = mResult.map.addFile(name, path);
f->lexed = std::make_shared<const Tokens>(Lexer(mOptions.lua, f->index).run(text));
Lexer lexer(mOptions.lua, f->index);
f->lexed = std::make_shared<const Tokens>(lexer.run(text));
for (const auto& [line, column] : lexer.spacedJoins())
{
Token at;
at.file = f->index;
at.line = line;
at.column = column;
at.text = "\\";
problem(ALScriptProblem::Severity::Warning, "PreprocBackslashSpace", "backslash and newline separated by space", {}, at);
}
if (!path.empty())
{
mLexed[path] = Lexed{ f->index, f->lexed };
Expand Down Expand Up @@ -5111,6 +5191,101 @@ void ALPreprocessor::optimize(Result& result, const Options& options)
result.optimized = true;
}

// static
bool ALPreprocessor::usesDirectives(const std::function<std::string_view(S32)>& line, S32 count, bool lua)
{
bool in_comment = false;
for (S32 i = 0; i < count; ++i)
{
const std::string_view text = line(i);
if (!in_comment && directiveName(text, lua) != std::string_view::npos)
{
return true;
}
if (lua)
{
continue;
}
// Where an LSL block comment opens or closes on the line, past its
// strings and a line comment.
bool in_string = false;
for (size_t c = 0; c < text.size(); ++c)
{
const bool pair = c + 1 < text.size();
if (in_comment)
{
if (text[c] == '*' && pair && text[c + 1] == '/')
{
in_comment = false;
++c;
}
}
else if (in_string)
{
if (text[c] == '\\')
{
++c;
}
else if (text[c] == '"')
{
in_string = false;
}
}
else if (text[c] == '"')
{
in_string = true;
}
else if (text[c] == '/' && pair && text[c + 1] == '/')
{
break;
}
else if (text[c] == '/' && pair && text[c + 1] == '*')
{
in_comment = true;
++c;
}
}
}
return false;
}

// static
bool ALPreprocessor::usesDirectives(std::string_view text, bool lua)
{
std::vector<std::string_view> lines;
for (size_t at = 0; at <= text.size();)
{
const size_t end = text.find('\n', at);
lines.push_back(text.substr(at, (end == std::string_view::npos ? text.size() : end) - at));
if (end == std::string_view::npos)
{
break;
}
at = end + 1;
}
return usesDirectives([&lines](S32 i) { return lines[static_cast<size_t>(i)]; }, static_cast<S32>(lines.size()), lua);
}

// static
ALPreprocessor::Wanted ALPreprocessor::wanted(bool enveloped, const std::function<bool()>& directives, bool setting)
{
if (enveloped)
{
return Wanted::Enveloped;
}
if (directives && directives())
{
return Wanted::Directives;
}
return setting ? Wanted::Setting : Wanted::No;
}

// static
ALPreprocessor::Wanted ALPreprocessor::wanted(std::string_view text, bool lua, bool enveloped, bool setting)
{
return wanted(enveloped, [text, lua]() { return usesDirectives(text, lua); }, setting);
}

// static
size_t ALPreprocessor::directiveName(std::string_view line, bool lua)
{
Expand Down
20 changes: 20 additions & 0 deletions indra/alscript/alpreprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,26 @@ class ALPreprocessor
// own name makes `--#` more than a comment. npos where the line holds
// no directive.
static size_t directiveName(std::string_view line, bool lua);
// Whether a script holds a directive: a line that is one, outside an
// LSL block comment. `line` answers a line's text by its index, `count`
// of them.
static bool usesDirectives(const std::function<std::string_view(S32)>& line, S32 count, bool lua);
static bool usesDirectives(std::string_view text, bool lua);

// Why a script is preprocessed as it goes up, or No, in this order: it
// went up in the preprocessor's envelope before, its source kept, and
// goes up as it went; it holds a directive, which the grid could
// compile no other way, whatever the setting; Preprocess on Save is on,
// for every script. `directives` is asked only where it decides.
enum class Wanted : U8
{
No,
Enveloped,
Directives,
Setting
};
static Wanted wanted(bool enveloped, const std::function<bool()>& directives, bool setting);
static Wanted wanted(std::string_view text, bool lua, bool enveloped, bool setting);

// The transform a line of LSL is written for, by the shape of its
// first statement: `switch (` and `case ...:` the switch's; `break;`,
Expand Down
11 changes: 9 additions & 2 deletions indra/alscript/alscriptfixes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -853,9 +853,16 @@ namespace
}
if (!lua && line.front() == '#')
{
// With the lines a backslash carries it on to.
// With the lines a backslash carries it on to, blanks after
// it let by as the preprocessor lets them by on a directive's
// line.
const auto carried = [&lines](S32 at) {
const std::string_view text = lines.line(at);
const size_t last = text.find_last_not_of(" \t\r\f\v");
return last != std::string_view::npos && text[last] == '\\';
};
last_kind = i;
while (!lines.line(last_kind).empty() && lines.line(last_kind).back() == '\\' && last_kind + 1 < lines.count())
while (carried(last_kind) && last_kind + 1 < lines.count())
{
++last_kind;
}
Expand Down
Loading
Loading