save progress
This commit is contained in:
parent
f34407ce31
commit
388ec2f1cd
|
|
@ -0,0 +1,533 @@
|
|||
module dsymbol.coloredlogger;
|
||||
|
||||
import std.logger;
|
||||
import std.stdio;
|
||||
import std.concurrency;
|
||||
import std.datetime;
|
||||
import std.stdio;
|
||||
|
||||
|
||||
import std.algorithm : map, filter, joiner;
|
||||
import std.array : join, split;
|
||||
import std.conv : to;
|
||||
import std.format : format;
|
||||
import std.functional : not;
|
||||
import std.range : ElementType, empty, front, popFront;
|
||||
import std.regex : ctRegex, Captures, replaceAll;
|
||||
import std.string : toUpper;
|
||||
import std.traits : EnumMembers;
|
||||
|
||||
version (unittest)
|
||||
{
|
||||
import core.thread : Thread;
|
||||
import core.time : msecs;
|
||||
import std.conv : to;
|
||||
import std.process : environment;
|
||||
import std.stdio : writeln, write;
|
||||
import unit_threaded;
|
||||
}
|
||||
/// Available Colors
|
||||
enum AnsiColor
|
||||
{
|
||||
black = 30,
|
||||
red = 31,
|
||||
green = 32,
|
||||
yellow = 33,
|
||||
blue = 34,
|
||||
magenta = 35,
|
||||
cyan = 36,
|
||||
lightGray = 37,
|
||||
defaultColor = 39,
|
||||
darkGray = 90,
|
||||
lightRed = 91,
|
||||
lightGreen = 92,
|
||||
lightYellow = 93,
|
||||
lightBlue = 94,
|
||||
lightMagenta = 95,
|
||||
lightCyan = 96,
|
||||
white = 97
|
||||
}
|
||||
|
||||
/// Available Styles
|
||||
enum Style
|
||||
{
|
||||
bold = 1,
|
||||
dim = 2,
|
||||
underlined = 4,
|
||||
blink = 5,
|
||||
reverse = 7,
|
||||
hidden = 8
|
||||
}
|
||||
|
||||
/// Internal structure to style a string
|
||||
struct StyledString
|
||||
{
|
||||
private string unformatted;
|
||||
private int[] befores;
|
||||
private int[] afters;
|
||||
/// Create a styled string
|
||||
public this(string unformatted)
|
||||
{
|
||||
this.unformatted = unformatted;
|
||||
}
|
||||
|
||||
private StyledString addPair(int before, int after)
|
||||
{
|
||||
befores ~= before;
|
||||
afters ~= after;
|
||||
return this;
|
||||
}
|
||||
|
||||
StyledString setForeground(int color)
|
||||
{
|
||||
return addPair(color, 0);
|
||||
}
|
||||
|
||||
StyledString setBackground(int color)
|
||||
{
|
||||
return addPair(color + 10, 0);
|
||||
}
|
||||
|
||||
/// Add styling to a string
|
||||
StyledString addStyle(int style)
|
||||
{
|
||||
return addPair(style, 0);
|
||||
}
|
||||
|
||||
string toString() const @safe
|
||||
{
|
||||
auto prefix = befores.map!(a => "\033[%dm".format(a)).join("");
|
||||
auto suffix = afters.map!(a => "\033[%dm".format(a)).join("");
|
||||
return "%s%s%s".format(prefix, unformatted, suffix);
|
||||
}
|
||||
|
||||
/// Concatenate with another string
|
||||
string opBinary(string op : "~")(string rhs) @safe
|
||||
{
|
||||
return toString ~ rhs;
|
||||
}
|
||||
}
|
||||
|
||||
/// Truecolor string
|
||||
struct RGBString
|
||||
{
|
||||
private string unformatted;
|
||||
/// Colorinformation
|
||||
struct RGB
|
||||
{
|
||||
/// Red component 0..256
|
||||
ubyte r;
|
||||
/// Green component 0..256
|
||||
ubyte g;
|
||||
/// Blue component 0..256
|
||||
ubyte b;
|
||||
}
|
||||
|
||||
private RGB* foreground;
|
||||
private RGB* background;
|
||||
/// Create RGB String
|
||||
this(string unformatted)
|
||||
{
|
||||
this.unformatted = unformatted;
|
||||
}
|
||||
|
||||
/// Set color
|
||||
auto rgb(ubyte r, ubyte g, ubyte b)
|
||||
{
|
||||
this.foreground = new RGB(r, g, b);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// Set background color
|
||||
auto onRgb(ubyte r, ubyte g, ubyte b)
|
||||
{
|
||||
this.background = new RGB(r, g, b);
|
||||
return this;
|
||||
}
|
||||
|
||||
string toString() @safe
|
||||
{
|
||||
auto res = "";
|
||||
if (foreground != null)
|
||||
{
|
||||
res = "\033[38;2;%s;%s;%sm".format(foreground.r, foreground.g, foreground.b) ~ res;
|
||||
}
|
||||
if (background != null)
|
||||
{
|
||||
res = "\033[48;2;%s;%s;%sm".format(background.r, background.g, background.b) ~ res;
|
||||
}
|
||||
res ~= unformatted;
|
||||
if (foreground != null || background != null)
|
||||
{
|
||||
res ~= "\033[0m";
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/// Convinient helper function
|
||||
string rgb(string s, ubyte r, ubyte g, ubyte b)
|
||||
{
|
||||
return RGBString(s).rgb(r, g, b).toString;
|
||||
}
|
||||
|
||||
/// Convinient helper function
|
||||
string onRgb(string s, ubyte r, ubyte g, ubyte b)
|
||||
{
|
||||
return RGBString(s).onRgb(r, g, b).toString;
|
||||
}
|
||||
|
||||
@system @("rgb") unittest
|
||||
{
|
||||
import std.experimental.color : RGBA8, convertColor;
|
||||
import std.experimental.color.hsx : HSV;
|
||||
|
||||
writeln("red: ", "r".rgb(255, 0, 0).onRgb(0, 255, 0));
|
||||
writeln("green: ", "g".rgb(0, 255, 0).onRgb(0, 0, 255));
|
||||
writeln("blue: ", "b".rgb(0, 0, 255).onRgb(255, 0, 0));
|
||||
writeln("mixed: ", ("withoutColor" ~ "red".red.to!string ~ "withoutColor").bold);
|
||||
for (int r = 0; r <= 255; r += 10)
|
||||
{
|
||||
for (int g = 0; g <= 255; g += 3)
|
||||
{
|
||||
write(" ".onRgb(cast(ubyte) r, cast(ubyte) g, cast(ubyte)(255 - r)));
|
||||
}
|
||||
writeln;
|
||||
}
|
||||
|
||||
int delay = environment.get("DELAY", "0").to!int;
|
||||
for (int j = 0; j < 255; j += 1)
|
||||
{
|
||||
for (int i = 0; i < 255; i += 3)
|
||||
{
|
||||
auto c = HSV!ubyte(cast(ubyte)(i - j), 0xff, 0xff);
|
||||
auto rgb = convertColor!RGBA8(c).tristimulus;
|
||||
write(" ".onRgb(rgb[0].value, rgb[1].value, rgb[2].value));
|
||||
}
|
||||
Thread.sleep(delay.msecs);
|
||||
write("\r");
|
||||
}
|
||||
writeln;
|
||||
}
|
||||
|
||||
@system @("styledstring") unittest
|
||||
{
|
||||
foreach (immutable color; [EnumMembers!AnsiColor])
|
||||
{
|
||||
auto colorName = "%s".format(color);
|
||||
writeln(StyledString(colorName).setForeground(color));
|
||||
}
|
||||
foreach (immutable color; [EnumMembers!AnsiColor])
|
||||
{
|
||||
auto colorName = "bg%s".format(color);
|
||||
writeln(StyledString(colorName).setBackground(color));
|
||||
}
|
||||
foreach (immutable style; [EnumMembers!Style])
|
||||
{
|
||||
auto styleName = "%s".format(style);
|
||||
writeln(StyledString(styleName).addStyle(style));
|
||||
}
|
||||
|
||||
writeln("boldUnderlined".bold.underlined);
|
||||
writeln("redOnGreenReverse".red.onGreen.reverse);
|
||||
}
|
||||
|
||||
@system @("styledstring ~") unittest
|
||||
{
|
||||
("test".red ~ "blub").should == "\033[31mtest\033[0mblub";
|
||||
}
|
||||
|
||||
/// Create `color` and `onColor` functions for all enum members. e.g. "abc".green.onRed
|
||||
auto colorMixin(T)()
|
||||
{
|
||||
string res = "";
|
||||
foreach (immutable color; [EnumMembers!T])
|
||||
{
|
||||
auto t = typeof(T.init).stringof;
|
||||
auto c = "%s".format(color);
|
||||
res ~= "auto %1$s(string s) { return StyledString(s).setForeground(%2$s.%1$s); }\n".format(c,
|
||||
t);
|
||||
res ~= "auto %1$s(StyledString s) { return s.setForeground(%2$s.%1$s); }\n".format(c, t);
|
||||
string name = c[0 .. 1].toUpper ~ c[1 .. $];
|
||||
res ~= "auto on%3$s(string s) { return StyledString(s).setBackground(%2$s.%1$s); }\n".format(c,
|
||||
t, name);
|
||||
res ~= "auto on%3$s(StyledString s) { return s.setBackground(%2$s.%1$s); }\n".format(c,
|
||||
t, name);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Create `style` functions for all enum mebers, e.g. "abc".bold
|
||||
auto styleMixin(T)()
|
||||
{
|
||||
string res = "";
|
||||
foreach (immutable style; [EnumMembers!T])
|
||||
{
|
||||
auto t = typeof(T.init).stringof;
|
||||
auto s = "%s".format(style);
|
||||
res ~= "auto %1$s(string s) { return StyledString(s).addStyle(%2$s.%1$s); }\n".format(s, t);
|
||||
res ~= "auto %1$s(StyledString s) { return s.addStyle(%2$s.%1$s); }\n".format(s, t);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
mixin(colorMixin!AnsiColor);
|
||||
mixin(styleMixin!Style);
|
||||
|
||||
@system @("api") unittest
|
||||
{
|
||||
"redOnGreen".red.onGreen.writeln;
|
||||
"redOnYellowBoldUnderlined".red.onYellow.bold.underlined.writeln;
|
||||
"bold".bold.writeln;
|
||||
"test".writeln;
|
||||
}
|
||||
|
||||
/// Calculate length of string excluding all formatting escapes
|
||||
ulong unformattedLength(string s)
|
||||
{
|
||||
enum State
|
||||
{
|
||||
NORMAL,
|
||||
ESCAPED,
|
||||
}
|
||||
|
||||
auto state = State.NORMAL;
|
||||
ulong count = 0;
|
||||
foreach (c; s)
|
||||
{
|
||||
switch (state)
|
||||
{
|
||||
case State.NORMAL:
|
||||
if (c == 0x1b)
|
||||
{
|
||||
state = State.ESCAPED;
|
||||
}
|
||||
else
|
||||
{
|
||||
count++;
|
||||
}
|
||||
break;
|
||||
case State.ESCAPED:
|
||||
if (c == 'm')
|
||||
{
|
||||
state = State.NORMAL;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Illegal state");
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
/++ Range to work with ansi escapes. The ESC[ parts and m must be
|
||||
+ already removed and the numbers need to be converted to uints.
|
||||
+ See https://en.wikipedia.org/wiki/ANSI_escape_code
|
||||
+/
|
||||
auto tokenize(Range)(Range parts)
|
||||
{
|
||||
struct TokenizeResult(Range)
|
||||
{
|
||||
Range parts;
|
||||
ElementType!(Range)[] next;
|
||||
this(Range parts)
|
||||
{
|
||||
this.parts = parts;
|
||||
tokenizeNext();
|
||||
}
|
||||
|
||||
private void tokenizeNext()
|
||||
{
|
||||
next = [];
|
||||
if (parts.empty)
|
||||
{
|
||||
return;
|
||||
}
|
||||
switch (parts.front)
|
||||
{
|
||||
case 38:
|
||||
case 48:
|
||||
next ~= 38;
|
||||
parts.popFront;
|
||||
switch (parts.front)
|
||||
{
|
||||
case 2:
|
||||
next ~= 2;
|
||||
parts.popFront;
|
||||
next ~= parts.front;
|
||||
parts.popFront;
|
||||
next ~= parts.front;
|
||||
parts.popFront;
|
||||
next ~= parts.front;
|
||||
parts.popFront;
|
||||
break;
|
||||
case 5:
|
||||
next ~= 5;
|
||||
parts.popFront;
|
||||
next ~= parts.front;
|
||||
parts.popFront;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Only [38,48];[2,5] are supported but got %s;%s".format(next[0],
|
||||
parts.front));
|
||||
}
|
||||
break;
|
||||
case 0: .. case 37:
|
||||
case 39: .. case 47:
|
||||
case 49:
|
||||
case 51:
|
||||
.. case 55:
|
||||
case 60: .. case 65:
|
||||
case 90: .. case 97:
|
||||
case 100: .. case 107:
|
||||
next ~= parts.front;
|
||||
parts.popFront;
|
||||
break;
|
||||
default:
|
||||
throw new Exception("Only colors are supported");
|
||||
}
|
||||
}
|
||||
|
||||
auto front()
|
||||
{
|
||||
return next;
|
||||
}
|
||||
|
||||
bool empty()
|
||||
{
|
||||
return next == null;
|
||||
}
|
||||
|
||||
void popFront()
|
||||
{
|
||||
tokenizeNext();
|
||||
}
|
||||
}
|
||||
|
||||
return TokenizeResult!(Range)(parts);
|
||||
}
|
||||
|
||||
@system @("ansi tokenizer") unittest
|
||||
{
|
||||
[38, 5, 2, 38, 2, 1, 2, 3, 36, 1, 2, 3, 4].tokenize.should == ([
|
||||
[38, 5, 2], [38, 2, 1, 2, 3], [36], [1], [2], [3], [4]
|
||||
]);
|
||||
}
|
||||
|
||||
/++ Remove classes of ansi escapes from a styled string.
|
||||
+/
|
||||
string filterAnsiEscapes(alias predicate)(string s)
|
||||
{
|
||||
string withFilters(Captures!string c)
|
||||
{
|
||||
auto parts = c[1].split(";").map!(a => a.to!uint)
|
||||
.tokenize
|
||||
.filter!(p => predicate(p));
|
||||
if (parts.empty)
|
||||
{
|
||||
return "";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "\033[" ~ parts.joiner.map!(a => "%d".format(a)).join(";") ~ "m";
|
||||
}
|
||||
}
|
||||
|
||||
alias r = ctRegex!"\033\\[(.*?)m";
|
||||
return s.replaceAll!(withFilters)(r);
|
||||
}
|
||||
|
||||
/// Predicate to select foreground color ansi escapes
|
||||
bool foregroundColor(uint[] token)
|
||||
{
|
||||
return token[0] >= 30 && token[0] <= 38;
|
||||
}
|
||||
|
||||
/// Predicate to select background color ansi escapes
|
||||
bool backgroundColor(uint[] token)
|
||||
{
|
||||
return token[0] >= 40 && token[0] <= 48;
|
||||
}
|
||||
|
||||
/// Predicate to select style ansi escapes
|
||||
bool style(uint[] token)
|
||||
{
|
||||
return token[0] >= 1 && token[0] <= 29;
|
||||
}
|
||||
|
||||
/// Predicate select nothing
|
||||
bool none(uint[])
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Predicate to select all
|
||||
bool all(uint[])
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@system @("configurable strip") unittest
|
||||
{
|
||||
import unit_threaded;
|
||||
import std.functional : not;
|
||||
|
||||
"test".red.onGreen.bold.toString.filterAnsiEscapes!(foregroundColor).should == "\033[31mtest";
|
||||
"test".red.onGreen.bold.toString.filterAnsiEscapes!(not!foregroundColor)
|
||||
.should == "\033[42m\033[1mtest\033[0m\033[0m\033[0m";
|
||||
"test".red.onGreen.bold.toString.filterAnsiEscapes!(style).should == "\033[1mtest";
|
||||
"test".red.onGreen.bold.toString.filterAnsiEscapes!(none).should == "test";
|
||||
"test".red.onGreen.bold.toString.filterAnsiEscapes!(all)
|
||||
.should == "\033[31m\033[42m\033[1mtest\033[0m\033[0m\033[0m";
|
||||
"test".red.onGreen.bold.toString.filterAnsiEscapes!(backgroundColor).should == "\033[42mtest";
|
||||
}
|
||||
|
||||
/// Add fillChar to the right of the string until width is reached
|
||||
auto leftJustifyFormattedString(string s, ulong width, dchar fillChar = ' ')
|
||||
{
|
||||
auto res = s;
|
||||
const currentWidth = s.unformattedLength;
|
||||
for (long i = currentWidth; i < width; ++i)
|
||||
{
|
||||
res ~= fillChar;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@system @("leftJustifyFormattedString") unittest
|
||||
{
|
||||
import unit_threaded;
|
||||
|
||||
"test".red.toString.leftJustifyFormattedString(10).should == "\033[31mtest\033[0m ";
|
||||
}
|
||||
|
||||
/// Add fillChar to the left of the string until width is reached
|
||||
auto rightJustifyFormattedString(string s, ulong width, char fillChar = ' ')
|
||||
{
|
||||
auto res = s;
|
||||
const currentWidth = s.unformattedLength;
|
||||
for (long i = currentWidth; i < width; ++i)
|
||||
{
|
||||
res = fillChar ~ res;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@system @("rightJustifyFormattedString") unittest
|
||||
{
|
||||
"test".red.toString.rightJustifyFormattedString(10).should == (" \033[31mtest\033[0m");
|
||||
}
|
||||
|
||||
/// Force a style on possible preformatted text
|
||||
auto forceStyle(string text, Style style) {
|
||||
return "\033[%d".format(style.to!int) ~ "m" ~ text.split("\033[0m").join("\033[0;%d".format(style.to!int) ~"m") ~ "\033[0m";
|
||||
}
|
||||
|
||||
@("forceStyle") unittest
|
||||
{
|
||||
auto splitt = "1es2eses3".split("es").filter!(not!(empty));
|
||||
splitt.should == ["1", "2", "3"];
|
||||
string s = "noformatting%snoformatting".format("red".red).forceStyle(Style.reverse);
|
||||
writeln(s);
|
||||
s.should == "\033[7mnoformatting\033[31mred\033[0;7mnoformatting\033[0m";
|
||||
}
|
||||
|
|
@ -245,6 +245,24 @@ final class FirstPass : ASTVisitor
|
|||
}
|
||||
}
|
||||
|
||||
void processTypeIdentifierPart(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, VariableContext.TypeInstance* current, TypeIdentifierPart tip)
|
||||
{
|
||||
|
||||
auto newArg = GCAllocator.instance.make!(VariableContext.TypeInstance)();
|
||||
newArg.parent = current;
|
||||
current.args ~= newArg;
|
||||
|
||||
if (tip.identifierOrTemplateInstance)
|
||||
{
|
||||
processIdentifierOrTemplate(symbol, lookup, ctx, newArg, tip.identifierOrTemplateInstance);
|
||||
}
|
||||
|
||||
if (tip.typeIdentifierPart)
|
||||
{
|
||||
error("i should probably handle this");
|
||||
}
|
||||
}
|
||||
|
||||
void processTemplateInstance(SemanticSymbol* symbol, TypeLookup* lookup, VariableContext* ctx, VariableContext.TypeInstance* current, TemplateInstance ti)
|
||||
{
|
||||
if (ti.identifier != tok!"")
|
||||
|
|
@ -264,6 +282,8 @@ final class FirstPass : ASTVisitor
|
|||
if (targ.type.type2 is null) continue;
|
||||
|
||||
auto part = targ.type.type2.typeIdentifierPart;
|
||||
if (part is null) continue;
|
||||
|
||||
auto newArg = GCAllocator.instance.make!(VariableContext.TypeInstance)();
|
||||
newArg.parent = current;
|
||||
current.args ~= newArg;
|
||||
|
|
@ -278,6 +298,11 @@ final class FirstPass : ASTVisitor
|
|||
{
|
||||
processIdentifierOrTemplate(symbol, lookup, ctx, newArg, part.typeIdentifierPart.identifierOrTemplateInstance);
|
||||
}
|
||||
|
||||
if (part.typeIdentifierPart)
|
||||
{
|
||||
error("i should probably handle this");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -373,47 +398,15 @@ final class FirstPass : ASTVisitor
|
|||
// TODO: remove this cast. See the note on structFieldTypes
|
||||
structFieldTypes.insert(cast() dec.type);
|
||||
}
|
||||
if (dec.type.type2 && dec.type.type2.typeIdentifierPart)
|
||||
|
||||
auto lookup = symbol.typeLookups.front;
|
||||
|
||||
if (dec.type && dec.type.type2 && dec.type.type2.typeIdentifierPart)
|
||||
{
|
||||
auto typeIdentifierPart = dec.type.type2.typeIdentifierPart;
|
||||
if (typeIdentifierPart && typeIdentifierPart.identifierOrTemplateInstance &&
|
||||
typeIdentifierPart.identifierOrTemplateInstance.templateInstance)
|
||||
{
|
||||
auto templateInstance = typeIdentifierPart.identifierOrTemplateInstance.templateInstance;
|
||||
TypeIdentifierPart typeIdentifierPart = cast(TypeIdentifierPart) dec.type.type2.typeIdentifierPart;
|
||||
|
||||
// if template
|
||||
// allocate symbol
|
||||
// set kind to TypeArg
|
||||
// set index
|
||||
// for each argument
|
||||
// create child symbol
|
||||
// set kind to TypeArg
|
||||
// set index
|
||||
|
||||
// to resolve:
|
||||
// get item.type
|
||||
// copy symbol
|
||||
// traverse and replace parts
|
||||
|
||||
if (!templateInstance.templateArguments)
|
||||
{}
|
||||
else if (templateInstance.templateArguments.templateArgumentList)
|
||||
{
|
||||
foreach(i, targ; templateInstance.templateArguments.templateArgumentList.items)
|
||||
{
|
||||
if (targ.type is null) continue;
|
||||
// TODO: support template with multiple arguments
|
||||
auto tokens = targ.type.type2.tokens;
|
||||
warning(" tokens: ", tokens[0].text);
|
||||
}
|
||||
|
||||
}
|
||||
else if (templateInstance.templateArguments.templateSingleArgument)
|
||||
{
|
||||
auto singleArg = typeIdentifierPart.identifierOrTemplateInstance.templateInstance.templateArguments.templateSingleArgument;
|
||||
symbol.acSymbol.tmplArgNames ~= istring(singleArg.token.text);
|
||||
}
|
||||
}
|
||||
lookup.ctx.root = GCAllocator.instance.make!(VariableContext.TypeInstance)();
|
||||
processTypeIdentifierPart(symbol, lookup, &lookup.ctx, lookup.ctx.root, typeIdentifierPart);
|
||||
}
|
||||
}
|
||||
if (dec.autoDeclaration !is null)
|
||||
|
|
@ -462,6 +455,9 @@ final class FirstPass : ASTVisitor
|
|||
else if (FunctionCallExpression fc = unary.functionCallExpression)
|
||||
{
|
||||
warning("functioncall expression ", fc.type, " ", fc.unaryExpression," ", fc.templateArguments);
|
||||
//if (fc.unaryExpression)
|
||||
// traverseUnaryExpression(symbol, lookup, &lookup.ctx, unary);
|
||||
unary = fc.unaryExpression;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -498,7 +494,6 @@ final class FirstPass : ASTVisitor
|
|||
lookup.breadcrumbs.insert(istring(tic.text));
|
||||
|
||||
lookup.ctx.root = GCAllocator.instance.make!(VariableContext.TypeInstance)();
|
||||
assert(lookup.ctx.root != null);
|
||||
processTemplateInstance(symbol, lookup, &lookup.ctx, lookup.ctx.root, iot.templateInstance);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ ScopeSymbolPair generateAutocompleteTrees(const(Token)[] tokens,
|
|||
|
||||
secondPass(first.rootSymbol, first.moduleScope, cache);
|
||||
|
||||
thirdPass(first.moduleScope, cache, cursorPosition);
|
||||
thirdPass(first.rootSymbol, first.moduleScope, cache, cursorPosition);
|
||||
|
||||
auto r = move(first.rootSymbol.acSymbol);
|
||||
typeid(SemanticSymbol).destroy(first.rootSymbol);
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import dsymbol.type_lookup;
|
|||
import dsymbol.deferred;
|
||||
import dsymbol.import_;
|
||||
import dsymbol.modulecache;
|
||||
import dsymbol.coloredlogger;
|
||||
import std.experimental.allocator;
|
||||
import std.experimental.allocator.gc_allocator : GCAllocator;
|
||||
import std.experimental.logger;
|
||||
|
|
@ -62,10 +63,11 @@ void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCac
|
|||
if (lookup.ctx.root)
|
||||
{
|
||||
auto type = currentSymbol.acSymbol.type;
|
||||
if (type.kind == structName || type.kind == className)
|
||||
if (type.kind == structName || type.kind == className && lookup.ctx.root.args.length > 0)
|
||||
{
|
||||
int depth;
|
||||
resolveTemplate(currentSymbol.acSymbol, type, lookup, lookup.ctx.root, moduleScope, cache, depth);
|
||||
//DSymbol*[string] mapping;
|
||||
//int depth;
|
||||
//resolveTemplate(currentSymbol.acSymbol, type, lookup, lookup.ctx.root, moduleScope, cache, depth, mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -120,155 +122,6 @@ void secondPass(SemanticSymbol* currentSymbol, Scope* moduleScope, ref ModuleCac
|
|||
}
|
||||
|
||||
|
||||
DSymbol* createTypeWithTemplateArgs(DSymbol* type, TypeLookup* lookup, VariableContext.TypeInstance* ti, ref ModuleCache cache, Scope* moduleScope, ref int depth, DSymbol*[string] m = null)
|
||||
{
|
||||
warning("processing type: ", type.name, " ", ti.chain, " ", ti.args);
|
||||
DSymbol* newType = GCAllocator.instance.make!DSymbol("", CompletionKind.dummy, null);
|
||||
newType.name = type.name;
|
||||
newType.kind = type.kind;
|
||||
newType.qualifier = type.qualifier;
|
||||
newType.protection = type.protection;
|
||||
newType.symbolFile = type.symbolFile;
|
||||
newType.doc = type.doc;
|
||||
newType.callTip = type.callTip;
|
||||
DSymbol*[string] mapping;
|
||||
|
||||
if (m)
|
||||
foreach(k,v; m)
|
||||
{
|
||||
mapping[k] = v;
|
||||
}
|
||||
|
||||
int[string] mapping_index;
|
||||
int count = 0;
|
||||
if (ti.args.length > 0)
|
||||
{
|
||||
warning("hard args, build mapping");
|
||||
foreach(part; type.opSlice())
|
||||
{
|
||||
if (part.kind == CompletionKind.typeTmpParam)
|
||||
{
|
||||
scope(exit) count++;
|
||||
|
||||
warning("building mapping for: ", part.name, " chain: ", ti.args[count].chain);
|
||||
auto key = part.name;
|
||||
|
||||
DSymbol* first;
|
||||
foreach(i, crumb; ti.args[count].chain)
|
||||
{
|
||||
if (i == 0)
|
||||
{
|
||||
auto result = moduleScope.getSymbolsAtGlobalScope(istring(crumb));
|
||||
if (result.length == 0)
|
||||
{
|
||||
error("can't find symbol: ", crumb);
|
||||
break;
|
||||
}
|
||||
first = result[0];
|
||||
}
|
||||
else {
|
||||
first = first.getFirstPartNamed(istring(crumb));
|
||||
}
|
||||
}
|
||||
|
||||
mapping_index[key] = count;
|
||||
if (first is null)
|
||||
{
|
||||
error("can't find type for mapping: ", part.name);
|
||||
continue;
|
||||
}
|
||||
warning(" map: ", key ,"->", first.name);
|
||||
|
||||
mapping[key] = createTypeWithTemplateArgs(first, lookup, ti.args[count], cache, moduleScope, depth, m ? m : mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
string[] T_names;
|
||||
|
||||
foreach(part; type.opSlice())
|
||||
{
|
||||
if (part.kind == CompletionKind.typeTmpParam)
|
||||
{
|
||||
warning(" #", count, " ", part.name);
|
||||
T_names ~= part.name;
|
||||
}
|
||||
else if (part.type && part.type.kind == CompletionKind.typeTmpParam)
|
||||
{
|
||||
DSymbol* newPart = GCAllocator.instance.make!DSymbol(part.name, part.kind, null);
|
||||
newPart.qualifier = part.qualifier;
|
||||
newPart.protection = part.protection;
|
||||
newPart.symbolFile = part.symbolFile;
|
||||
newPart.doc = part.doc;
|
||||
newPart.callTip = part.callTip;
|
||||
newPart.ownType = true;
|
||||
|
||||
if (part.type.name in mapping)
|
||||
{
|
||||
newPart.type = mapping[part.type.name];
|
||||
warning(" mapping found: ", part.type.name," -> ", newPart.type.name);
|
||||
}
|
||||
else
|
||||
if (m && part.type.name in m)
|
||||
{
|
||||
newPart.type = m[part.type.name];
|
||||
warning(" mapping found: ", part.type.name," -> ", newPart.type.name);
|
||||
}
|
||||
else
|
||||
error(" mapping not found: ", part.type.name);
|
||||
|
||||
newType.addChild(newPart, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (depth < 50)
|
||||
if (part.type && part.kind == CompletionKind.variableName)
|
||||
foreach(partPart; part.type.opSlice())
|
||||
{
|
||||
if (partPart.kind == CompletionKind.typeTmpParam)
|
||||
{
|
||||
|
||||
warning("go agane ", part.name, " ", part.type.name, " with arg: ", ti.chain," Ts: ", T_names);
|
||||
|
||||
foreach(arg; ti.args)
|
||||
{
|
||||
warning(" >", arg.chain, " ", arg.args);
|
||||
}
|
||||
//resolveTemplate(part, part.type, lookup, ti, moduleScope, cache, depth, mapping);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
newType.addChild(part, true);
|
||||
}
|
||||
}
|
||||
return newType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve template arguments
|
||||
*/
|
||||
void resolveTemplate(DSymbol* variableSym, DSymbol* type, TypeLookup* lookup, VariableContext.TypeInstance* current, Scope* moduleScope, ref ModuleCache cache, ref int depth, DSymbol*[string] mapping = null)
|
||||
{
|
||||
depth += 1;
|
||||
|
||||
|
||||
warning("resolving template for var: ", variableSym.name, " type: ", type.name, "depth: ", depth);
|
||||
warning("current args: ");
|
||||
foreach(i, arg; current.args)
|
||||
warning(" i: ", i, " ", arg.chain);
|
||||
DSymbol* newType = createTypeWithTemplateArgs(type, lookup, current, cache, moduleScope, depth, mapping);
|
||||
|
||||
|
||||
|
||||
|
||||
variableSym.type = newType;
|
||||
variableSym.ownType = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void resolveImport(DSymbol* acSymbol, ref TypeLookups typeLookups,
|
||||
ref ModuleCache cache)
|
||||
|
|
|
|||
|
|
@ -24,8 +24,13 @@ import dsymbol.semantic;
|
|||
import dsymbol.symbol;
|
||||
import dsymbol.string_interning;
|
||||
import dsymbol.deferred;
|
||||
import dsymbol.type_lookup;
|
||||
|
||||
import containers.hashset;
|
||||
import dsymbol.coloredlogger;
|
||||
import std.logger;
|
||||
import std.experimental.allocator;
|
||||
import std.experimental.allocator.gc_allocator;
|
||||
|
||||
|
||||
/**
|
||||
|
|
@ -34,12 +39,231 @@ import containers.hashset;
|
|||
* If it is, then it'll set its type
|
||||
* If the symbol is not found, then it'll do nothing
|
||||
*/
|
||||
void thirdPass(Scope* mscope, ref ModuleCache cache, size_t cursorPosition)
|
||||
void thirdPass(SemanticSymbol* symbol, Scope* mscope, ref ModuleCache cache, size_t cursorPosition)
|
||||
{
|
||||
auto desired = mscope.getScopeByCursor(cursorPosition);
|
||||
tryResolve(desired, cache);
|
||||
|
||||
// now as our final step, let's try to resolve templates
|
||||
|
||||
tryResolveTemplates(symbol, mscope, cache);
|
||||
}
|
||||
|
||||
void tryResolveTemplates(SemanticSymbol* currentSymbol, Scope* mscope, ref ModuleCache cache)
|
||||
{
|
||||
with (CompletionKind) switch (currentSymbol.acSymbol.kind)
|
||||
{
|
||||
case variableName:
|
||||
case memberVariableName:
|
||||
if (currentSymbol.acSymbol.type && currentSymbol.typeLookups.length > 0)
|
||||
{
|
||||
TypeLookup* lookup = currentSymbol.typeLookups.front;
|
||||
if (lookup.ctx.root)
|
||||
{
|
||||
auto type = currentSymbol.acSymbol.type;
|
||||
if (type.kind == structName || type.kind == className && lookup.ctx.root.args.length > 0)
|
||||
{
|
||||
DSymbol*[string] mapping;
|
||||
int depth;
|
||||
resolveTemplate(currentSymbol.acSymbol, type, lookup, lookup.ctx.root, mscope, cache, depth, mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
warning("no type: ", currentSymbol.acSymbol.name," ", currentSymbol.acSymbol.kind);
|
||||
}
|
||||
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
|
||||
foreach (child; currentSymbol.children)
|
||||
tryResolveTemplates(child, mscope, cache);
|
||||
}
|
||||
|
||||
|
||||
DSymbol* createTypeWithTemplateArgs(DSymbol* type, TypeLookup* lookup, VariableContext.TypeInstance* ti, ref ModuleCache cache, Scope* moduleScope, ref int depth, DSymbol*[string] m)
|
||||
{
|
||||
assert(type);
|
||||
warning("processing type: ", type.name, " ", ti.chain, " ", ti.args);
|
||||
DSymbol* newType = GCAllocator.instance.make!DSymbol("dummy", CompletionKind.dummy, null);
|
||||
newType.name = type.name;
|
||||
newType.kind = type.kind;
|
||||
newType.qualifier = type.qualifier;
|
||||
newType.protection = type.protection;
|
||||
newType.symbolFile = type.symbolFile;
|
||||
newType.doc = type.doc;
|
||||
newType.callTip = type.callTip;
|
||||
newType.type = type.type;
|
||||
DSymbol*[string] mapping;
|
||||
|
||||
|
||||
|
||||
if (m)
|
||||
foreach(k,v; m)
|
||||
{
|
||||
warning("store mapping: ".yellow, k, " ", v.name);
|
||||
mapping[k] = v;
|
||||
}
|
||||
|
||||
int[string] mapping_index;
|
||||
int count = 0;
|
||||
if (ti.args.length > 0)
|
||||
{
|
||||
warning("hard args, build mapping");
|
||||
foreach(part; type.opSlice())
|
||||
{
|
||||
if (part.kind == CompletionKind.typeTmpParam)
|
||||
{
|
||||
scope(exit) count++;
|
||||
|
||||
warning("building mapping for: ", part.name, " chain: ", ti.args[count].chain);
|
||||
auto key = part.name;
|
||||
|
||||
DSymbol* first;
|
||||
foreach(i, crumb; ti.args[count].chain)
|
||||
{
|
||||
auto argName = crumb;
|
||||
if (i == 0)
|
||||
{
|
||||
|
||||
if (key in mapping)
|
||||
{
|
||||
//first = mapping[argName];
|
||||
//continue;
|
||||
argName = mapping[key].name;
|
||||
}
|
||||
|
||||
auto result = moduleScope.getSymbolsAtGlobalScope(istring(argName));
|
||||
if (result.length == 0)
|
||||
{
|
||||
error("can't find symbol: ".red, argName);
|
||||
|
||||
foreach(k, v; mapping)
|
||||
{
|
||||
warning("k: ", k, " v: ", v);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
first = result[0];
|
||||
}
|
||||
else {
|
||||
first = first.getFirstPartNamed(istring(argName));
|
||||
}
|
||||
}
|
||||
|
||||
mapping_index[key] = count;
|
||||
if (first is null)
|
||||
{
|
||||
error("can't find type for mapping: ".red, part.name);
|
||||
continue;
|
||||
}
|
||||
warning(" map: ", key ,"->", first.name);
|
||||
|
||||
warning(" creating type: ".blue, first.name);
|
||||
|
||||
auto ca = ti.args[count];
|
||||
if (ca.chain.length > 0)
|
||||
mapping[key] = createTypeWithTemplateArgs(first, lookup, ca, cache, moduleScope, depth, mapping);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
assert(newType);
|
||||
warning("process parts..");
|
||||
string[] T_names;
|
||||
foreach(part; type.opSlice())
|
||||
{
|
||||
if (part.kind == CompletionKind.typeTmpParam)
|
||||
{
|
||||
warning(" #", count, " ", part.name);
|
||||
T_names ~= part.name;
|
||||
}
|
||||
else if (part.type && part.type.kind == CompletionKind.typeTmpParam)
|
||||
{
|
||||
DSymbol* newPart = GCAllocator.instance.make!DSymbol(part.name, part.kind, null);
|
||||
newPart.qualifier = part.qualifier;
|
||||
newPart.protection = part.protection;
|
||||
newPart.symbolFile = part.symbolFile;
|
||||
newPart.doc = part.doc;
|
||||
newPart.callTip = part.callTip;
|
||||
newPart.ownType = false;
|
||||
|
||||
if (part.type.name in mapping)
|
||||
{
|
||||
newPart.type = mapping[part.type.name];
|
||||
warning(" mapping found: ", part.type.name," -> ", newPart.type.name);
|
||||
}
|
||||
else
|
||||
if (m && part.type.name in m)
|
||||
{
|
||||
newPart.type = m[part.type.name];
|
||||
warning(" mapping in m found: ", part.type.name," -> ", newPart.type.name);
|
||||
}
|
||||
else
|
||||
error(" mapping not found: ".red, part.type.name," type: ", type.name, " cur: ", ti.chain, "args: ", ti.args);
|
||||
|
||||
newType.addChild(newPart, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if (depth < 50)
|
||||
//if (part.type && part.kind == CompletionKind.variableName)
|
||||
//foreach(partPart; part.type.opSlice())
|
||||
//{
|
||||
// if (partPart.kind == CompletionKind.typeTmpParam)
|
||||
// {
|
||||
// foreach(arg; ti.args)
|
||||
// {
|
||||
// warning(" >", arg.chain, " ", arg.args);
|
||||
// }
|
||||
// warning("go agane ".blue, part.name, " ", part.type.name, " with arg: ", ti.chain," Ts: ", T_names);
|
||||
// //resolveTemplate(part, part.type, lookup, ti, moduleScope, cache, depth, mapping);
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
warning("adding untouched: ", part.name, "into: ", newType);
|
||||
newType.addChild(part, false);
|
||||
}
|
||||
}
|
||||
return newType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Resolve template arguments
|
||||
*/
|
||||
void resolveTemplate(DSymbol* variableSym, DSymbol* type, TypeLookup* lookup, VariableContext.TypeInstance* current, Scope* moduleScope, ref ModuleCache cache, ref int depth, DSymbol*[string] mapping = null)
|
||||
{
|
||||
depth += 1;
|
||||
|
||||
if (variableSym is null || type is null) return;
|
||||
|
||||
|
||||
warning("resolving template for var: ", variableSym.name, " type: ", type.name, "depth: ", depth);
|
||||
warning("current args: ");
|
||||
foreach(i, arg; current.args)
|
||||
warning(" i: ", i, " ", arg.chain);
|
||||
warning("current chain: ", current.chain, " name: ", current.name);
|
||||
if (current.chain.length == 0) return; // TODO: should not be empty, happens for simple stuff Inner inner;
|
||||
|
||||
DSymbol* newType = createTypeWithTemplateArgs(type, lookup, current, cache, moduleScope, depth, mapping);
|
||||
|
||||
|
||||
|
||||
|
||||
variableSym.type = newType;
|
||||
variableSym.ownType = true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Used to resolve missing symbols within a scope
|
||||
*/
|
||||
|
|
|
|||
Loading…
Reference in New Issue