282 строки
11 KiB
TypeScript
282 строки
11 KiB
TypeScript
///<reference path='refs.ts'/>
|
|
|
|
module TDev
|
|
{
|
|
export class IntelliItem
|
|
{
|
|
public score = 0;
|
|
public prop:IProperty;
|
|
public decl:AST.Decl;
|
|
public nameOverride:string;
|
|
public imageOverride:string;
|
|
public lowSearch:boolean; // demote in keyboard search
|
|
public descOverride:string;
|
|
public colorOverride:string;
|
|
public tick = Ticks.noEvent;
|
|
public iconOverride:string;
|
|
public cbOverride:(i:IntelliItem)=>void;
|
|
public isWide = false;
|
|
public lastMatchScore = 0;
|
|
public isAttachedTo:Kind = null;
|
|
public usageKey:string;
|
|
public cornerIcon:string;
|
|
public noButton:boolean;
|
|
|
|
public getName()
|
|
{
|
|
if (!!this.nameOverride) return this.nameOverride;
|
|
if (!!this.prop) return this.prop.getName();
|
|
|
|
if (this.decl instanceof AST.SingletonDef && this.decl.getName() == AST.libSymbol)
|
|
return "libs";
|
|
return this.decl.getName();
|
|
}
|
|
|
|
public alphaOverride()
|
|
{
|
|
if (this.cbOverride) return 30;
|
|
if (this.decl instanceof AST.LocalDef) return 20;
|
|
if (this.decl && this.decl.getKind() instanceof ThingSetKind) return 10;
|
|
if (this.prop instanceof AST.LibraryRef) return 5;
|
|
if (this.prop && !Script.canUseProperty(this.prop)) return -10;
|
|
return 0;
|
|
}
|
|
|
|
public getLongName()
|
|
{
|
|
if (!!this.nameOverride) return "\u00a0[" + this.nameOverride + "]";
|
|
var d:any = this.decl;
|
|
if (!d && this.prop) d = this.prop.forwardsTo();
|
|
if (!d) d = this.prop;
|
|
var n = d.getName();
|
|
if (!(d instanceof AST.LibraryRefAction) && d.getNamespace) {
|
|
n = d.getNamespace() + n;
|
|
} else if (d.getNamespaces && d.getNamespaces()[0]) {
|
|
n = d.getNamespaces()[0] + (this.prop ? this.prop.getArrow() : " ") + n;
|
|
} else if (this.isAttachedTo)
|
|
n = this.isAttachedTo.getPropPrefix() + (this.prop ? this.prop.getArrow() : " ") + n;
|
|
else if (this.prop)
|
|
n = this.prop.getArrow() + n;
|
|
return n;
|
|
}
|
|
|
|
private shortName()
|
|
{
|
|
if (!!this.decl && this.decl instanceof AST.SingletonDef)
|
|
return this.decl.getKind().shortName();
|
|
else if (this.prop && this.prop instanceof AST.LibraryRef)
|
|
return AST.libSymbol;
|
|
else if (this.prop && this.prop instanceof AST.GlobalDef)
|
|
return (<AST.GlobalDef>this.prop).isResource ? AST.artSymbol : AST.dataSymbol;
|
|
return null;
|
|
}
|
|
|
|
private getDesc(skip?:boolean)
|
|
{
|
|
if (!!this.descOverride) return this.descOverride;
|
|
return ""
|
|
//if (!!this.prop) return this.prop.getDescription(skip);
|
|
//return this.decl.getDescription(skip);
|
|
}
|
|
|
|
private onClick()
|
|
{
|
|
var calc = TDev.TheEditor.calculator;
|
|
if (this.tick) tick(this.tick)
|
|
calc.searchApi.cancelImplicitInsert();
|
|
if (!!this.cbOverride) this.cbOverride(this);
|
|
else {
|
|
if (!!this.prop) {
|
|
calc.insertProp(this.prop, this.isAttachedTo != null);
|
|
}
|
|
else if (!!this.decl) {
|
|
calc.insertThing(this.decl);
|
|
}
|
|
}
|
|
calc.hideBottomScroller();
|
|
}
|
|
|
|
public mkBox()
|
|
{
|
|
var box:HTMLElement = null;
|
|
if (this.decl) box = DeclRender.mkBox(this.decl);
|
|
else if (this.prop) {
|
|
if (!this.isAttachedTo) {
|
|
box = DeclRender.mkPropBox(this.prop);
|
|
} else {
|
|
this.prop.useFullName = true;
|
|
box = DeclRender.mkPropBox(this.prop);
|
|
this.prop.useFullName = false;
|
|
}
|
|
}
|
|
else {
|
|
var de = new DeclEntry(this.getName());
|
|
de.color = this.getColor();
|
|
de.description = this.getDesc();
|
|
if (this.imageOverride) {
|
|
de.icon = this.imageOverride;
|
|
de.color = 'white';
|
|
} else if (this.iconOverride) de.icon = this.iconOverride;
|
|
box = DeclRender.mkBox(<any> de);
|
|
}
|
|
if (!!this.isAttachedTo)
|
|
box.className += " attachedItem";
|
|
return box.withClick(() => this.onClick());
|
|
}
|
|
|
|
private nameIsOp() {
|
|
var n = this.getName();
|
|
return n == ":=" || (n.length == 1 && !/[a-zA-Z]/.test(n));
|
|
}
|
|
|
|
public apply(c:CalcButton, idx:number) : void
|
|
{
|
|
var par = this.prop ? this.prop.parentKind : null
|
|
var arrow = null
|
|
if (par && !par.shortName() && !this.prop.getInfixPriority())
|
|
arrow = span("calcArrow", this.prop.getArrow())
|
|
var sn = this.shortName();
|
|
var inner:HTMLElement;
|
|
if (sn)
|
|
inner = div("calcOp", [span("symbol", sn + " "), span("", this.getName())])
|
|
else
|
|
inner = div("calcOp", [arrow, text(this.getName())])
|
|
var triangle = idx >= 0;
|
|
inner.style.fontSize = this.nameIsOp() ? "1.2em" : (this.getName().length >= 18 ? "0.6em" : "0.8em");
|
|
var help = this.getDesc()
|
|
if (help.length > 14) help = ""
|
|
var fn = () => {
|
|
if (idx >= 0) {
|
|
tick(Ticks.calcIntelliButton)
|
|
tickN(Ticks.calcIntelliButton0, idx)
|
|
}
|
|
TDev.Browser.EditorSoundManager.intellibuttonClick();
|
|
this.onClick()
|
|
}
|
|
|
|
if (this.imageOverride)
|
|
c.setImage(this.imageOverride, help, Ticks.noEvent, fn)
|
|
else
|
|
c.setHtml(inner, help, fn);
|
|
|
|
var b = c.getButton();
|
|
if (triangle) {
|
|
if (this.cornerIcon) {
|
|
var d = div("calcButtonCornerIcon", SVG.getIconSVG(this.cornerIcon))
|
|
b.appendChild(d)
|
|
} else {
|
|
var cc = this.getColor();
|
|
if (DeclRender.propColor(this.prop)) {
|
|
d = div("calcButtonColorMarker");
|
|
b.appendChild(d);
|
|
d.style.backgroundColor = cc;
|
|
} else {
|
|
d = div("calcButtonTriangle", "");
|
|
b.appendChild(d);
|
|
d.style.borderTopColor = cc;
|
|
d.style.borderRightColor = cc;
|
|
}
|
|
}
|
|
c._theButton.className = "calcButton calcIntelliButton"
|
|
} else {
|
|
c._theButton.className = "calcButton calcStmtButton"
|
|
}
|
|
// display picture art in button
|
|
if (this.prop instanceof TDev.AST.GlobalDef) {
|
|
var gd = <TDev.AST.GlobalDef>this.prop;
|
|
if (gd.isResource && gd.getKind().getName() == "Picture" && Cloud.isArtUrl(gd.url)) {
|
|
c.setBackgroundImage(gd.url);
|
|
inner.style.backgroundColor = 'rgba(238,238,255,0.5)';
|
|
}
|
|
}
|
|
c.intelliItem = this;
|
|
}
|
|
|
|
private getColor()
|
|
{
|
|
var c = "blue";
|
|
if (!!this.colorOverride) return this.colorOverride;
|
|
if (!!this.decl) c = DeclRender.declColor[this.decl.nodeType()](this.decl);
|
|
else if (this.prop instanceof AST.PropertyDecl) c = DeclRender.declColor[(<any>this.prop).nodeType()](this.prop);
|
|
else if (!!this.prop) c = DeclRender.declColor["property"](this.prop);
|
|
return c;
|
|
}
|
|
|
|
static matchString(s:string, terms:string[], begMatch:number, wordMatch:number, match:number)
|
|
{
|
|
if (terms.length == 0) return begMatch;
|
|
|
|
var res = 0;
|
|
for (var i = 0; i < terms.length; ++i) {
|
|
var idx = s.indexOf(terms[i]);
|
|
if (idx < 0) return 0;
|
|
if (idx >= 0) {
|
|
if (idx == 0) {
|
|
res += begMatch;
|
|
} else {
|
|
idx = s.indexOf(" " + terms[i]);
|
|
if (idx >= 0)
|
|
res += wordMatch;
|
|
else
|
|
res += match;
|
|
}
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
static matchProp(p:IProperty, terms:string[], fullName:string)
|
|
{
|
|
if (!p.isBrowsable()) return 0;
|
|
if (terms.length == 0) return 10000;
|
|
var pp:IPropertyWithCache = p.canCacheSearch() ? <IPropertyWithCache>p : null;
|
|
var n:string;
|
|
if (pp && pp.cacheShort) {
|
|
n = pp.cacheShort;
|
|
} else {
|
|
n = (p.getName() + " " + (!p.parentKind ? "" : p.parentKind.toString())).toLowerCase();
|
|
if (pp) pp.cacheShort = n;
|
|
}
|
|
var r = IntelliItem.matchString(n, terms, 10000, 1000, 100);
|
|
if (r > 0) {
|
|
if (p.getName().toLowerCase().replace(/[^a-z0-9]/g, "") == fullName)
|
|
r += 100000;
|
|
return r;
|
|
}
|
|
var lng:string;
|
|
if (pp && pp.cacheLong) {
|
|
lng = pp.cacheLong;
|
|
} else {
|
|
lng = (n + p.getSignature() + " " + p.getDescription(true)).toLowerCase();
|
|
if ((<any>p).forSearch)
|
|
lng += " " + (<any>p).forSearch().toLowerCase()
|
|
if (pp) pp.cacheLong = lng
|
|
}
|
|
return IntelliItem.matchString(lng, terms, 100, 10, 1);
|
|
}
|
|
|
|
static thereIsMore()
|
|
{
|
|
var dotdotdot = new IntelliItem();
|
|
dotdotdot.nameOverride = lf("... there's more ...");
|
|
dotdotdot.descOverride = lf("just keep typing the search terms!");
|
|
return dotdotdot.mkBox();
|
|
}
|
|
|
|
// Returns match quality
|
|
public match(terms:string[], fullName:string)
|
|
{
|
|
if (!!this.prop) return IntelliItem.matchProp(this.prop, terms, fullName);
|
|
if (terms.length == 0) return 1;
|
|
var lowerName = this.getName().toLowerCase();
|
|
var r = IntelliItem.matchString(lowerName, terms, 10000, 1000, 100);
|
|
if (r > 0) {
|
|
if (lowerName.replace(/[^a-z0-9]/g, "") == fullName)
|
|
r += 100000;
|
|
return r;
|
|
}
|
|
return IntelliItem.matchString((this.getName() + " " + this.getDesc(true)).toLowerCase(), terms, 100, 10, 1);
|
|
}
|
|
}
|
|
}
|