mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 11:38:21 +02:00
2680 lines
82 KiB
Beef
2680 lines
82 KiB
Beef
using System;
|
|
using System.Collections;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Diagnostics;
|
|
using Beefy;
|
|
using Beefy.gfx;
|
|
using Beefy.events;
|
|
using Beefy.widgets;
|
|
using Beefy.theme.dark;
|
|
using Beefy.geom;
|
|
using Beefy.utils;
|
|
|
|
namespace IDE.ui
|
|
{
|
|
class DocumentationParser
|
|
{
|
|
public String mDocString = new String(256) ~ delete _;
|
|
public String mBriefString ~ delete _;
|
|
public Dictionary<String, String> mParamInfo ~ DeleteDictionaryAndKeysAndItems!(_);
|
|
|
|
public String ShowDocString
|
|
{
|
|
get
|
|
{
|
|
return mBriefString ?? mDocString;
|
|
}
|
|
}
|
|
|
|
public this(StringView info)
|
|
{
|
|
bool atLineStart = true;
|
|
bool lineHadStar = false;
|
|
int blockDepth = 0;
|
|
bool queuedSpace = false;
|
|
bool docStringDone = false;
|
|
bool lineHadContent = false;
|
|
String curDocStr = null;
|
|
|
|
for (int idx = 0; idx < info.Length; idx++)
|
|
{
|
|
char8 c = info[idx];
|
|
char8 nextC = 0;
|
|
if (idx < info.Length - 1)
|
|
nextC = info[idx + 1];
|
|
|
|
if ((c == '/') && (nextC == '*'))
|
|
{
|
|
idx++;
|
|
blockDepth++;
|
|
|
|
if ((idx < info.Length - 1) && (info[idx + 1] == '*'))
|
|
{
|
|
idx++;
|
|
if ((idx < info.Length - 1) && (info[idx + 1] == '<'))
|
|
idx++;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
if ((c == '*') && (nextC == '/'))
|
|
{
|
|
idx++;
|
|
blockDepth--;
|
|
continue;
|
|
}
|
|
|
|
if (c == '\x03') // \n
|
|
{
|
|
if (!lineHadContent)
|
|
{
|
|
if (curDocStr != null)
|
|
curDocStr = null;
|
|
else if (!mDocString.IsEmpty)
|
|
docStringDone = true;
|
|
}
|
|
queuedSpace = false;
|
|
atLineStart = true;
|
|
lineHadStar = false;
|
|
lineHadContent = false;
|
|
continue;
|
|
}
|
|
|
|
if (c == '\x04')
|
|
{
|
|
queuedSpace = false;
|
|
atLineStart = true;
|
|
lineHadStar = false;
|
|
lineHadContent = false;
|
|
mDocString.Append('\n');
|
|
continue;
|
|
}
|
|
|
|
if (atLineStart)
|
|
{
|
|
if ((c == '*') && (blockDepth > 0) && (!lineHadStar))
|
|
{
|
|
lineHadStar = false;
|
|
continue;
|
|
}
|
|
|
|
if ((c == '/') && (!lineHadStar))
|
|
{
|
|
if ((nextC == '<') && (!queuedSpace))
|
|
{
|
|
idx++;
|
|
}
|
|
// Ignore any amount of '/' strings at a line start
|
|
continue;
|
|
}
|
|
|
|
if ((c == '@') || (c == '\\'))
|
|
{
|
|
int pragmaEndPos = info.IndexOf('\x03', idx);
|
|
if (pragmaEndPos == -1)
|
|
pragmaEndPos = info.Length;
|
|
StringView pragma = .(info, idx + 1, pragmaEndPos - idx - 1);
|
|
var splitEnum = pragma.Split(' ');
|
|
if (splitEnum.GetNext() case .Ok(var pragmaName))
|
|
{
|
|
if (pragmaName == "param")
|
|
{
|
|
if (splitEnum.GetNext() case .Ok(var paramName))
|
|
{
|
|
if (mParamInfo == null)
|
|
mParamInfo = new .();
|
|
curDocStr = new String(pragma, Math.Min(splitEnum.MatchPos + 1, pragma.Length));
|
|
mParamInfo[new String(paramName)] = curDocStr;
|
|
lineHadContent = true;
|
|
}
|
|
}
|
|
else if (pragmaName == "brief")
|
|
{
|
|
if (mBriefString == null)
|
|
mBriefString = new String(pragma.Length);
|
|
else if (mBriefString != null)
|
|
{
|
|
if (!mBriefString[mBriefString.Length - 1].IsWhiteSpace)
|
|
mBriefString.Append(" ");
|
|
}
|
|
var briefStr = StringView(pragma, splitEnum.MatchPos + 1);
|
|
briefStr.Trim();
|
|
mBriefString.Append(briefStr);
|
|
curDocStr = mBriefString;
|
|
lineHadContent = true;
|
|
}
|
|
}
|
|
|
|
idx = pragmaEndPos - 1;
|
|
continue;
|
|
}
|
|
|
|
if (c.IsWhiteSpace)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
queuedSpace = true;
|
|
atLineStart = false;
|
|
}
|
|
}
|
|
|
|
if (c.IsWhiteSpace)
|
|
{
|
|
queuedSpace = true;
|
|
continue;
|
|
}
|
|
|
|
if ((curDocStr != null) && (docStringDone))
|
|
continue;
|
|
|
|
String docStr = curDocStr ?? mDocString;
|
|
if (queuedSpace)
|
|
{
|
|
if (!docStr.IsEmpty)
|
|
{
|
|
char8 endC = docStr[docStr.Length - 1];
|
|
if (!endC.IsWhiteSpace)
|
|
docStr.Append(" ");
|
|
}
|
|
queuedSpace = false;
|
|
}
|
|
lineHadContent = true;
|
|
docStr.Append(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
public class AutoComplete
|
|
{
|
|
public class AutoCompleteContent : ScrollableWidget
|
|
{
|
|
public AutoComplete mAutoComplete;
|
|
public bool mIsInitted;
|
|
public int mIgnoreMove;
|
|
public bool mOwnsWindow;
|
|
public float mRightBoxAdjust;
|
|
public float mWantHeight;
|
|
|
|
public this(AutoComplete autoComplete)
|
|
{
|
|
mAutoComplete = autoComplete;
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
//Debug.WriteLine("~this {} {}", this, mIsInitted);
|
|
|
|
if (mIsInitted)
|
|
Cleanup();
|
|
}
|
|
|
|
void LostFocusHandler(BFWindow window, BFWindow newFocus)
|
|
{
|
|
if (gApp.mRunningTestScript)
|
|
return;
|
|
|
|
if ((newFocus != mWidgetWindow) && (newFocus != mAutoComplete.mTargetEditWidget.mWidgetWindow))
|
|
mAutoComplete.Close();
|
|
}
|
|
|
|
void HandleWindowMoved(BFWindow window)
|
|
{
|
|
if (gApp.mRunningTestScript)
|
|
return;
|
|
|
|
if ((mWidgetWindow == null) || (mWidgetWindow.mRootWidget != this))
|
|
return; // We're being replaced as root
|
|
|
|
if (let widgetWindow = window as WidgetWindow)
|
|
{
|
|
if (widgetWindow.mRootWidget is DarkTooltipContainer)
|
|
return;
|
|
}
|
|
|
|
if ((mIgnoreMove == 0) && (mWidgetWindow != null) && (!mWidgetWindow.mHasClosed))
|
|
mAutoComplete.Close();
|
|
}
|
|
|
|
public void Init()
|
|
{
|
|
Debug.Assert(!mIsInitted);
|
|
mIsInitted = true;
|
|
|
|
//Console.WriteLine("AutoCompleteContent Init");
|
|
|
|
//Debug.WriteLine("Init {} {} {} {}", this, mIsInitted, mOwnsWindow, mAutoComplete);
|
|
|
|
if (mOwnsWindow)
|
|
{
|
|
WidgetWindow.sOnWindowLostFocus.Add(new => LostFocusHandler);
|
|
WidgetWindow.sOnMouseDown.Add(new => HandleMouseDown);
|
|
WidgetWindow.sOnMouseWheel.Add(new => HandleMouseWheel);
|
|
WidgetWindow.sOnWindowMoved.Add(new => HandleWindowMoved);
|
|
WidgetWindow.sOnMenuItemSelected.Add(new => HandleSysMenuItemSelected);
|
|
}
|
|
}
|
|
|
|
void HandleMouseWheel(MouseEvent evt)
|
|
{
|
|
if (gApp.mRunningTestScript)
|
|
return;
|
|
if (mWidgetWindow == null)
|
|
return;
|
|
|
|
WidgetWindow widgetWindow = (WidgetWindow)evt.mSender;
|
|
if (!(widgetWindow.mRootWidget is AutoCompleteContent))
|
|
{
|
|
float mouseScreenX = widgetWindow.mClientX + evt.mX;
|
|
float mouseScreenY = widgetWindow.mClientY + evt.mY;
|
|
let windowRect = Rect(mWidgetWindow.mX, mWidgetWindow.mY, mWidgetWindow.mWindowWidth, mWidgetWindow.mWindowHeight);
|
|
if (windowRect.Contains(mouseScreenX, mouseScreenY))
|
|
{
|
|
MouseWheel(evt.mX - mWidgetWindow.mX, evt.mY - mWidgetWindow.mY, evt.mWheelDeltaX, evt.mWheelDeltaY);
|
|
evt.mHandled = true;
|
|
}
|
|
else
|
|
mAutoComplete.Close();
|
|
}
|
|
}
|
|
|
|
void HandleMouseDown(MouseEvent evt)
|
|
{
|
|
WidgetWindow widgetWindow = (WidgetWindow)evt.mSender;
|
|
if (!(widgetWindow.mRootWidget is AutoCompleteContent))
|
|
mAutoComplete.Close();
|
|
}
|
|
|
|
void HandleSysMenuItemSelected(IMenu sysMenu)
|
|
{
|
|
mAutoComplete.Close();
|
|
}
|
|
|
|
public void Cleanup()
|
|
{
|
|
//Debug.WriteLine("Cleanup {} {}", this, mIsInitted);
|
|
|
|
if (!mIsInitted)
|
|
return;
|
|
|
|
//Console.WriteLine("AutoCompleteContent Dispose");
|
|
if (mOwnsWindow)
|
|
{
|
|
WidgetWindow.sOnWindowLostFocus.Remove(scope => LostFocusHandler, true);
|
|
WidgetWindow.sOnMouseDown.Remove(scope => HandleMouseDown, true);
|
|
WidgetWindow.sOnMouseWheel.Remove(scope => HandleMouseWheel, true);
|
|
WidgetWindow.sOnWindowMoved.Remove(scope => HandleWindowMoved, true);
|
|
WidgetWindow.sOnMenuItemSelected.Remove(scope => HandleSysMenuItemSelected, true);
|
|
mIsInitted = false;
|
|
}
|
|
}
|
|
|
|
public override void Draw(Graphics g)
|
|
{
|
|
base.Draw(g);
|
|
|
|
float drawHeight = (mWantHeight != 0) ? mWantHeight : mHeight;
|
|
float boxWidth = mWidth - GS!(2) - mRightBoxAdjust;
|
|
|
|
if (mOwnsWindow)
|
|
{
|
|
using (g.PushColor(0x80000000))
|
|
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.DropShadow), GS!(2), GS!(2), boxWidth, drawHeight - GS!(2));
|
|
|
|
base.Draw(g);
|
|
using (g.PushColor(0xFFFFFFFF))
|
|
g.DrawBox(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.Menu), 0, 0, boxWidth - GS!(6), drawHeight - GS!(8));
|
|
}
|
|
|
|
g.SetFont(IDEApp.sApp.mCodeFont);
|
|
|
|
/*using (g.PushColor(0x80FF0000))
|
|
g.FillRect(0, 0, mWidth, mHeight);*/
|
|
}
|
|
|
|
public override void Resize(float x, float y, float width, float height)
|
|
{
|
|
base.Resize(x, y, width, height);
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
base.Update();
|
|
Debug.Assert((mIgnoreMove >= 0) && (mIgnoreMove <= 4));
|
|
}
|
|
}
|
|
|
|
public class AutoCompleteListWidget : AutoCompleteContent
|
|
{
|
|
BumpAllocator mAlloc = new BumpAllocator(.Ignore) ~ delete _;
|
|
public List<EntryWidget> mFullEntryList = new List<EntryWidget>() ~ delete _;
|
|
public List<EntryWidget> mEntryList = mFullEntryList;
|
|
public float mItemSpacing = GS!(18);
|
|
public int32 mSelectIdx = -1;
|
|
public float mMaxWidth;
|
|
public float mDocWidth;
|
|
public float mDocHeight;
|
|
public int mDocumentationDelay = -1;
|
|
|
|
public ~this()
|
|
{
|
|
Debug.Assert(mParent == null);
|
|
if (mEntryList != mFullEntryList)
|
|
delete mEntryList;
|
|
}
|
|
|
|
public override void MouseEnter()
|
|
{
|
|
base.MouseEnter();
|
|
}
|
|
|
|
public class EntryWidget
|
|
{
|
|
public int32 mShowIdx;
|
|
public AutoCompleteListWidget mAutoCompleteListWidget;
|
|
public String mEntryType;
|
|
public String mEntryDisplay;
|
|
public String mEntryInsert;
|
|
public String mDocumentation;
|
|
public Image mIcon;
|
|
|
|
public float Y
|
|
{
|
|
get
|
|
{
|
|
return GS!(6) + mShowIdx * mAutoCompleteListWidget.mItemSpacing;
|
|
}
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
}
|
|
|
|
public void Draw(Graphics g)
|
|
{
|
|
if (mIcon != null)
|
|
g.Draw(mIcon, 0, 0);
|
|
|
|
g.SetFont(IDEApp.sApp.mCodeFont);
|
|
g.DrawString(mEntryDisplay, GS!(20), 0);
|
|
}
|
|
}
|
|
|
|
class Content : Widget
|
|
{
|
|
AutoCompleteListWidget mAutoCompleteListWidget;
|
|
|
|
public this(AutoCompleteListWidget autoCompleteListWidget)
|
|
{
|
|
mAutoCompleteListWidget = autoCompleteListWidget;
|
|
}
|
|
|
|
public override void Draw(Graphics g)
|
|
{
|
|
base.Draw(g);
|
|
|
|
float absX;
|
|
float absY;
|
|
mParent.SelfToRootTranslate(0, 0, out absX, out absY);
|
|
|
|
float scrollPos = -g.mMatrix.ty + absY;
|
|
int32 startIdx = (int32)(scrollPos / mAutoCompleteListWidget.mItemSpacing);
|
|
int32 endIdx = Math.Min((int32)((scrollPos + mAutoCompleteListWidget.mHeight)/ mAutoCompleteListWidget.mItemSpacing) + 1, (int32)mAutoCompleteListWidget.mEntryList.Count);
|
|
|
|
if (mAutoCompleteListWidget.mSelectIdx != -1)
|
|
{
|
|
var selectedEntry = mAutoCompleteListWidget.mEntryList[mAutoCompleteListWidget.mSelectIdx];
|
|
using (g.PushColor(DarkTheme.COLOR_MENU_FOCUSED))
|
|
{
|
|
let dispWidth = g.mFont.GetWidth(selectedEntry.mEntryDisplay) + GS!(24);
|
|
|
|
float width = mWidth - GS!(16) - mAutoCompleteListWidget.mRightBoxAdjust;
|
|
if (mAutoCompleteListWidget.mVertScrollbar != null)
|
|
width -= GS!(18);
|
|
width = Math.Max(dispWidth, width);
|
|
|
|
g.DrawButton(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.MenuSelect), GS!(4), selectedEntry.Y - GS!(2), width);
|
|
}
|
|
}
|
|
|
|
for (int32 itemIdx = startIdx; itemIdx < endIdx; itemIdx++)
|
|
{
|
|
var entry = (EntryWidget)mAutoCompleteListWidget.mEntryList[itemIdx];
|
|
|
|
float curY = entry.Y;
|
|
using (g.PushTranslate(4, curY))
|
|
entry.Draw(g);
|
|
}
|
|
}
|
|
|
|
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
|
|
{
|
|
base.MouseDown(x, y, btn, btnCount);
|
|
|
|
int32 idx = (int32)((y - GS!(4.0f)) / mAutoCompleteListWidget.mItemSpacing);
|
|
if ((idx >= 0) && (idx < mAutoCompleteListWidget.mEntryList.Count))
|
|
{
|
|
mAutoCompleteListWidget.Select(idx);
|
|
if (!mAutoCompleteListWidget.mOwnsWindow)
|
|
mAutoCompleteListWidget.SetFocus();
|
|
if (btnCount > 1)
|
|
{
|
|
mAutoCompleteListWidget.mAutoComplete.InsertSelection((char8)0);
|
|
mAutoCompleteListWidget.mAutoComplete.Close();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public this(AutoComplete autoComplete) : base(autoComplete)
|
|
{
|
|
mScrollContent = new Content(this);
|
|
mScrollContentContainer.AddWidget(mScrollContent);
|
|
}
|
|
|
|
public void UpdateWidth()
|
|
{
|
|
int firstEntry = (int)(-(int)mScrollContent.mY / mItemSpacing);
|
|
int lastEntry = (int)((-(int)mScrollContent.mY + mScrollContentContainer.mHeight) / mItemSpacing);
|
|
|
|
if (mScrollContentContainer.mHeight == 0)
|
|
{
|
|
firstEntry = Math.Max(mSelectIdx - 3, 0);
|
|
lastEntry = mSelectIdx + 7;
|
|
}
|
|
|
|
lastEntry = Math.Min(lastEntry, mEntryList.Count);
|
|
|
|
float prevMaxWidth = mMaxWidth;
|
|
var font = IDEApp.sApp.mCodeFont;
|
|
|
|
for (int i = firstEntry; i < lastEntry; i++)
|
|
{
|
|
var entry = mEntryList[i];
|
|
float entryWidth = font.GetWidth(entry.mEntryDisplay) + GS!(32);
|
|
mMaxWidth = Math.Max(mMaxWidth, entryWidth);
|
|
}
|
|
|
|
|
|
float docWidth = 0.0f;
|
|
float docHeight = 0;
|
|
if ((mSelectIdx != -1) && (mSelectIdx < mEntryList.Count))
|
|
{
|
|
let selectedEntry = mEntryList[mSelectIdx];
|
|
if (selectedEntry.mDocumentation != null)
|
|
{
|
|
DocumentationParser docParser = scope DocumentationParser(selectedEntry.mDocumentation);
|
|
var showDocString = docParser.ShowDocString;
|
|
|
|
int lineCount = 0;
|
|
docWidth = 0;
|
|
for (var line in showDocString.Split('\n'))
|
|
{
|
|
docWidth = Math.Max(docWidth, font.GetWidth(line) + GS!(24));
|
|
lineCount++;
|
|
}
|
|
|
|
int drawScreenX = (.)(mWidgetWindow.mX + mWidth - mDocWidth);
|
|
gApp.GetWorkspaceRectFrom(drawScreenX, mWidgetWindow.mY, 0, 0, var workspaceX, var workspaceY, var workspaceWidth, var workspaceHeight);
|
|
float maxWidth = workspaceWidth - drawScreenX - GS!(8);
|
|
float newDocWidth = Math.Min(docWidth, workspaceWidth - drawScreenX - GS!(8));
|
|
newDocWidth = Math.Max(newDocWidth, GS!(80));
|
|
if ((docWidth > maxWidth) || (lineCount > 1))
|
|
{
|
|
docWidth = newDocWidth;
|
|
docHeight = font.GetWrapHeight(showDocString, docWidth - GS!(20)) + GS!(17);
|
|
}
|
|
else
|
|
docHeight = GS!(32);
|
|
}
|
|
}
|
|
|
|
if ((mOwnsWindow) && ((prevMaxWidth != mMaxWidth) || (docWidth != mDocWidth) || (docHeight != mDocHeight)) && (mWidgetWindow != null))
|
|
{
|
|
if (mWantHeight == 0)
|
|
mWantHeight = mHeight;
|
|
|
|
mDocWidth = docWidth;
|
|
mDocHeight = docHeight;
|
|
mRightBoxAdjust = docWidth + GS!(16);
|
|
int32 windowWidth = (int32)mMaxWidth;
|
|
windowWidth += (.)mDocWidth;
|
|
windowWidth += GS!(32);
|
|
|
|
if (mVertScrollbar != null)
|
|
{
|
|
windowWidth += GS!(12);
|
|
}
|
|
|
|
int windowHeight = (int)(mWantHeight + Math.Max(0, mDocHeight - GS!(32)));
|
|
|
|
mIgnoreMove++;
|
|
if (mAutoComplete.mInvokeWidget != null)
|
|
mAutoComplete.mInvokeWidget.mIgnoreMove++;
|
|
mWidgetWindow.Resize(mWidgetWindow.mX, mWidgetWindow.mY, windowWidth, windowHeight);
|
|
mScrollContent.mWidth = mWidth;
|
|
//Resize(0, 0, mWidgetWindow.mClientWidth, mWidgetWindow.mClientHeight);
|
|
mIgnoreMove--;
|
|
if (mAutoComplete.mInvokeWidget != null)
|
|
mAutoComplete.mInvokeWidget.mIgnoreMove--;
|
|
ResizeContent(-1, -1, mVertScrollbar != null);
|
|
}
|
|
}
|
|
|
|
/*public override void Resize(float x, float y, float width, float height)
|
|
{
|
|
if (mWantHeight != 0)
|
|
{
|
|
mScrollContentInsets.mBottom = mHeight - mWantHeight;
|
|
mScrollbarInsets.mBottom = mHeight - mWantHeight + 10;
|
|
}
|
|
else
|
|
{
|
|
mScrollContentInsets.mBottom = 0;
|
|
mScrollbarInsets.mBottom = 10;
|
|
}
|
|
|
|
base.Resize(x, y, width, height);
|
|
}*/
|
|
|
|
public void UpdateEntry(EntryWidget entry, int showIdx)
|
|
{
|
|
if (showIdx == -1)
|
|
{
|
|
//entry.mVisible = false;
|
|
entry.mShowIdx = (int32)showIdx;
|
|
return;
|
|
}
|
|
|
|
//entry.mVisible = true;
|
|
//entry.Resize(GS!(4), GS!(6) + showIdx * mItemSpacing, int32.MaxValue, mItemSpacing);
|
|
entry.mShowIdx = (int32)showIdx;
|
|
|
|
/*if (showIdx < 10)
|
|
{
|
|
var font = IDEApp.sApp.mCodeFont;
|
|
float entryWidth = font.GetWidth(entry.mEntryDisplay) + GS!(32);
|
|
mMaxWidth = Math.Max(mMaxWidth, entryWidth);
|
|
}*/
|
|
}
|
|
|
|
public void AddEntry(StringView entryType, StringView entryDisplay, Image icon, StringView entryInsert = default, StringView documentation = default)
|
|
{
|
|
var entryWidget = new:mAlloc EntryWidget();
|
|
entryWidget.mAutoCompleteListWidget = this;
|
|
entryWidget.mEntryType = new:mAlloc String(entryType);
|
|
entryWidget.mEntryDisplay = new:mAlloc String(entryDisplay);
|
|
if (!entryInsert.IsEmpty)
|
|
entryWidget.mEntryInsert = new:mAlloc String(entryInsert);
|
|
if (!documentation.IsEmpty)
|
|
entryWidget.mDocumentation = new:mAlloc String(documentation);
|
|
entryWidget.mIcon = icon;
|
|
|
|
UpdateEntry(entryWidget, mEntryList.Count);
|
|
mEntryList.Add(entryWidget);
|
|
//mScrollContent.AddWidget(entryWidget);
|
|
}
|
|
|
|
public void EnsureSelectionVisible()
|
|
{
|
|
if (mVertScrollbar != null)
|
|
{
|
|
float extraSpacing = mOwnsWindow ? 0 : GS!(4);
|
|
|
|
//int numItemsVisible = (int)((mVertScrollbar.mPageSize - GS!(6)) / mItemSpacing);
|
|
//float usableHeight = numItemsVisible * mItemSpacing;
|
|
float usableHeight = (float)mVertScrollbar.mPageSize;
|
|
|
|
float height = mItemSpacing;
|
|
var selectItem = mEntryList[mSelectIdx];
|
|
if (selectItem.Y - extraSpacing < mVertScrollbar.mContentPos)
|
|
mVertScrollbar.ScrollTo(selectItem.Y - extraSpacing);
|
|
if (selectItem.Y + height > mVertScrollbar.mContentPos + usableHeight)
|
|
mVertScrollbar.ScrollTo(selectItem.Y + height - usableHeight);
|
|
}
|
|
UpdateWidth();
|
|
}
|
|
|
|
public void CenterSelection()
|
|
{
|
|
if (mSelectIdx == -1)
|
|
return;
|
|
if (mVertScrollbar == null)
|
|
return;
|
|
|
|
var selectItem = mEntryList[mSelectIdx];
|
|
VertScrollTo(selectItem.Y + mItemSpacing - mVertScrollbar.mPageSize / 2, true);
|
|
UpdateWidth();
|
|
}
|
|
|
|
public void Select(int32 idx)
|
|
{
|
|
if (mSelectIdx == idx)
|
|
return;
|
|
|
|
MarkDirty();
|
|
mSelectIdx = idx;
|
|
if ((gApp.mSettings.mEditorSettings.mAutoCompleteShowDocumentation) && (!mAutoComplete.mIsDocumentationPass))
|
|
{
|
|
// Show faster when we have a panel to show within
|
|
mDocumentationDelay = mOwnsWindow ? 40 : 20;
|
|
}
|
|
EnsureSelectionVisible();
|
|
}
|
|
|
|
public void SelectDirection(int32 dir)
|
|
{
|
|
if (mEntryList.IsEmpty)
|
|
return;
|
|
int32 newSelection = mSelectIdx + dir;
|
|
if (newSelection < 0)
|
|
{
|
|
if (dir == -1)
|
|
newSelection = (.)mEntryList.Count - 1;
|
|
else
|
|
newSelection = 0;
|
|
}
|
|
else if (newSelection >= mEntryList.Count)
|
|
{
|
|
if (dir == 1)
|
|
newSelection = 0;
|
|
else
|
|
newSelection = (.)mEntryList.Count - 1;
|
|
}
|
|
|
|
if (mEntryList[newSelection].mShowIdx != -1)
|
|
Select(newSelection);
|
|
}
|
|
|
|
public override void ScrollPositionChanged()
|
|
{
|
|
if (mVertScrollbar != null)
|
|
{
|
|
//mVertScrollbar.mContentPos = (float)Math.Round(mVertScrollbar.mContentPos / mItemSpacing) * mItemSpacing;
|
|
|
|
}
|
|
|
|
base.ScrollPositionChanged();
|
|
}
|
|
|
|
public override void Draw(Graphics g)
|
|
{
|
|
base.Draw(g);
|
|
if (mSelectIdx != -1)
|
|
{
|
|
let selectedEntry = mEntryList[mSelectIdx];
|
|
if ((selectedEntry.mDocumentation != null) && (mDocumentationDelay <= 0))
|
|
{
|
|
DocumentationParser docParser = scope .(selectedEntry.mDocumentation);
|
|
if (mOwnsWindow)
|
|
{
|
|
if (mDocWidth > 0)
|
|
{
|
|
float drawX = mWidth - mDocWidth - GS!(22);
|
|
|
|
//float drawX = mRightBoxAdjust + GS!(42);
|
|
float drawY = GS!(4);
|
|
//float drawHeight = GS!(32);
|
|
float drawHeight = mDocHeight;
|
|
|
|
using (g.PushColor(0x80000000))
|
|
g.DrawBox(DarkTheme.sDarkTheme.GetImage(.DropShadow), drawX + GS!(2), drawY + GS!(2), mRightBoxAdjust - GS!(2), drawHeight - GS!(2));
|
|
|
|
using (g.PushColor(0xFFFFFFFF))
|
|
g.DrawBox(DarkTheme.sDarkTheme.GetImage(.Menu), drawX, drawY, mRightBoxAdjust - GS!(8), drawHeight - GS!(8));
|
|
|
|
using (g.PushColor(gApp.mSettings.mUISettings.mColors.mAutoCompleteDocText))
|
|
g.DrawString(docParser.ShowDocString, drawX + GS!(8), drawY + GS!(4), .Left, mDocWidth - GS!(20), .Wrap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
/*float drawX = GS!(8);
|
|
float drawY = mHeight + GS!(2);
|
|
using (g.PushColor(0xFFC0C0C0))
|
|
g.DrawString(docParser.ShowDocString, drawX, drawY, .Left, mWidth - drawX, .Wrap);*/
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void DrawAll(Graphics g)
|
|
{
|
|
base.DrawAll(g);
|
|
/*using (g.PushColor(0x20FF0000))
|
|
g.FillRect(0, 0, mWidth, mHeight);*/
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
base.Update();
|
|
if (mDocumentationDelay > 0)
|
|
--mDocumentationDelay;
|
|
}
|
|
|
|
public void ResizeContent(int32 width, int32 height, bool wantScrollbar)
|
|
{
|
|
InitScrollbars(false, wantScrollbar);
|
|
if ((wantScrollbar) && (mOwnsWindow))
|
|
{
|
|
mVertScrollbar.mScrollIncrement = mItemSpacing;
|
|
mVertScrollbar.mAlignItems = true;
|
|
}
|
|
if (mOwnsWindow)
|
|
{
|
|
mScrollbarInsets.mTop = GS!(2);
|
|
mScrollbarInsets.mBottom = GS!(10);
|
|
mScrollbarInsets.mRight = GS!(10) + mRightBoxAdjust;
|
|
mScrollContentInsets.mBottom = 0;
|
|
|
|
if (mWantHeight != 0)
|
|
{
|
|
mScrollbarInsets.mBottom += mHeight - mWantHeight;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
mScrollbarInsets.mTop = GS!(2);
|
|
mScrollbarInsets.mBottom = GS!(2);
|
|
mScrollbarInsets.mRight = GS!(2);
|
|
}
|
|
|
|
if (width != -1)
|
|
{
|
|
mScrollContent.mWidth = width /*- mRightBoxAdjust*/;
|
|
mScrollContent.mHeight = height;
|
|
}
|
|
UpdateScrollbars();
|
|
}
|
|
|
|
public override void RehupScale(float oldScale, float newScale)
|
|
{
|
|
base.RehupScale(oldScale, newScale);
|
|
mAutoComplete.Close();
|
|
}
|
|
|
|
public override void KeyDown(KeyCode keyCode, bool isRepeat)
|
|
{
|
|
base.KeyDown(keyCode, isRepeat);
|
|
|
|
switch (keyCode)
|
|
{
|
|
case .Up:
|
|
SelectDirection(-1);
|
|
case .Down:
|
|
SelectDirection(1);
|
|
default:
|
|
}
|
|
}
|
|
}
|
|
|
|
public class InvokeWidget : AutoCompleteContent
|
|
{
|
|
public class Entry
|
|
{
|
|
public String mText ~ delete _;
|
|
public String mDocumentation ~ delete _;
|
|
|
|
public int GetParamCount()
|
|
{
|
|
int splitCount = 0;
|
|
for (int32 i = 0; i < mText.Length; i++)
|
|
{
|
|
char8 c = mText[i];
|
|
if (c == '\x01')
|
|
{
|
|
splitCount++;
|
|
i++;
|
|
}
|
|
}
|
|
return splitCount - 1;
|
|
}
|
|
|
|
public bool HasParamsParam()
|
|
{
|
|
int lastSplit = mText.LastIndexOf('\x01');
|
|
if (lastSplit == -1)
|
|
return false;
|
|
lastSplit = mText.LastIndexOf('\x01', lastSplit - 1);
|
|
if (lastSplit == -1)
|
|
return false;
|
|
|
|
StringView sv = .(mText, lastSplit);
|
|
return sv.StartsWith("\x01params ") || sv.StartsWith("\x01 params ");
|
|
}
|
|
}
|
|
|
|
public List<Entry> mEntryList = new List<Entry>() ~ DeleteContainerAndItems!(_);
|
|
public int32 mSelectIdx;
|
|
public float mMaxWidth;
|
|
public int32 mLeftParenIdx;
|
|
public bool mIsAboveText;
|
|
|
|
public this(AutoComplete autoComplete)
|
|
: base(autoComplete)
|
|
{
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
}
|
|
|
|
public void AddEntry(Entry entry)
|
|
{
|
|
mEntryList.Add(entry);
|
|
|
|
var font = IDEApp.sApp.mCodeFont;
|
|
String checkString = scope String(entry.mText);
|
|
checkString.Replace("\x01", "");
|
|
float entryWidth = font.GetWidth(checkString) + GS!(32);
|
|
mMaxWidth = Math.Max(mMaxWidth, entryWidth);
|
|
}
|
|
|
|
public void ResizeContent(bool resizeWindow)
|
|
{
|
|
if (mOwnsWindow)
|
|
{
|
|
int workspaceX;
|
|
int workspaceY;
|
|
int workspaceWidth;
|
|
int workspaceHeight;
|
|
BFApp.sApp.GetWorkspaceRect(out workspaceX, out workspaceY, out workspaceWidth, out workspaceHeight);
|
|
mWidth = workspaceWidth;
|
|
}
|
|
else
|
|
mWidth = gApp.mAutoCompletePanel.mWidth;
|
|
|
|
float extWidth;
|
|
float extHeight;
|
|
DrawInfo(null, out extWidth, out extHeight);
|
|
|
|
mWidth = extWidth;
|
|
mHeight = extHeight;
|
|
|
|
if (resizeWindow)
|
|
{
|
|
if (mOwnsWindow)
|
|
{
|
|
mIgnoreMove++;
|
|
mAutoComplete.UpdateWindow(ref mWidgetWindow, this, mAutoComplete.mInvokeSrcPositions[0], (int32)extWidth, (int32)extHeight);
|
|
mIgnoreMove--;
|
|
}
|
|
else
|
|
{
|
|
gApp.mAutoCompletePanel.ResizeComponents();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Select(int32 idx)
|
|
{
|
|
mSelectIdx = idx;
|
|
ResizeContent(mWidgetWindow != null);
|
|
}
|
|
|
|
public new void Init()
|
|
{
|
|
base.Init();
|
|
|
|
if (mSelectIdx >= mEntryList.Count)
|
|
mSelectIdx = 0;
|
|
}
|
|
|
|
public void SelectDirection(int32 dir)
|
|
{
|
|
int32 newSelection = mSelectIdx + dir;
|
|
if ((newSelection >= 0) && (newSelection < mEntryList.Count))
|
|
Select(newSelection);
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
}
|
|
|
|
void DrawInfo(Graphics g, out float extWidth, out float extHeight)
|
|
{
|
|
var font = IDEApp.sApp.mCodeFont;
|
|
|
|
extWidth = 0;
|
|
|
|
float curX = GS!(8);
|
|
float curY = GS!(5);
|
|
|
|
if (mEntryList.Count > 1)
|
|
{
|
|
String numStr = scope String();
|
|
numStr.AppendF("{0}/{1}", mSelectIdx + 1, mEntryList.Count);
|
|
if (g != null)
|
|
{
|
|
using (g.PushColor(gApp.mSettings.mUISettings.mColors.mAutoCompleteSubText))
|
|
g.DrawString(numStr, curX, curY);
|
|
}
|
|
curX += font.GetWidth(numStr) + GS!(8);
|
|
}
|
|
|
|
var selectedEntry = mEntryList[mSelectIdx];
|
|
|
|
float maxWidth = mWidth;
|
|
|
|
StringView paramName = .();
|
|
List<StringView> textSections = scope List<StringView>(selectedEntry.mText.Split('\x01'));
|
|
|
|
int cursorPos = mAutoComplete.mTargetEditWidget.Content.CursorTextPos;
|
|
int cursorSection = -1;
|
|
for (int sectionIdx = 0; sectionIdx < mAutoComplete.mInvokeSrcPositions.Count - 1; sectionIdx++)
|
|
{
|
|
if (cursorPos > mAutoComplete.mInvokeSrcPositions[sectionIdx])
|
|
cursorSection = sectionIdx + 1;
|
|
}
|
|
|
|
// Just show last section hilighted even if we have too many params.
|
|
// This accounts for variadic cases
|
|
if (cursorSection >= textSections.Count - 1)
|
|
cursorSection = textSections.Count - 2;
|
|
|
|
float paramX = 0;
|
|
for (int sectionIdx = 0; sectionIdx < textSections.Count; sectionIdx++)
|
|
{
|
|
bool isParam = (sectionIdx > 0) && (sectionIdx < textSections.Count - 1);
|
|
if ((isParam) && (paramX == 0))
|
|
paramX = curX;
|
|
|
|
StringView sectionStr = .(textSections[sectionIdx]);
|
|
|
|
float sectionWidth = font.GetWidth(sectionStr);
|
|
if (curX + sectionWidth > maxWidth)
|
|
{
|
|
curX = paramX;
|
|
curY += font.GetLineSpacing();
|
|
while (sectionStr.StartsWith(" "))
|
|
sectionStr.RemoveFromStart(1);
|
|
}
|
|
if (sectionIdx == cursorSection)
|
|
{
|
|
int lastSpace = sectionStr.LastIndexOf(' ');
|
|
if (lastSpace != -1)
|
|
{
|
|
paramName = .(sectionStr, lastSpace + 1);
|
|
if (paramName.EndsWith(','))
|
|
paramName.RemoveFromEnd(1);
|
|
}
|
|
}
|
|
|
|
if (g != null)
|
|
{
|
|
using (g.PushColor(((sectionIdx == cursorSection) && (isParam)) ? gApp.mSettings.mUISettings.mColors.mAutoCompleteActiveText : gApp.mSettings.mUISettings.mColors.mText))
|
|
g.DrawString(sectionStr, curX, curY);
|
|
}
|
|
curX += sectionWidth;
|
|
|
|
extWidth = Math.Max(extWidth, curX);
|
|
}
|
|
|
|
extWidth += GS!(16);
|
|
extHeight = curY + font.GetLineSpacing() + GS!(16);
|
|
|
|
if ((selectedEntry.mDocumentation != null) && (gApp.mSettings.mEditorSettings.mAutoCompleteShowDocumentation))
|
|
{
|
|
DocumentationParser docParser = scope .(selectedEntry.mDocumentation);
|
|
var docString = docParser.mBriefString ?? docParser.mDocString;
|
|
|
|
curX = GS!(32);
|
|
|
|
float docHeight = 0;
|
|
if (mWidgetWindow == null)
|
|
{
|
|
docHeight = font.GetHeight();
|
|
}
|
|
else
|
|
{
|
|
int drawScreenX = (.)(mWidgetWindow.mX + curX);
|
|
gApp.GetWorkspaceRectFrom(drawScreenX, mWidgetWindow.mY, 0, 0, var workspaceX, var workspaceY, var workspaceWidth, var workspaceHeight);
|
|
float maxDocWidth = workspaceWidth - drawScreenX - GS!(8);
|
|
maxDocWidth = Math.Min(maxDocWidth, workspaceWidth - drawScreenX - GS!(8));
|
|
maxDocWidth = Math.Max(maxDocWidth, GS!(80));
|
|
|
|
if (!docString.IsWhiteSpace)
|
|
{
|
|
curY += font.GetLineSpacing() + GS!(4);
|
|
if (g != null)
|
|
{
|
|
using (g.PushColor(gApp.mSettings.mUISettings.mColors.mAutoCompleteDocText))
|
|
docHeight = g.DrawString(docString, curX, curY, .Left, maxDocWidth, .Wrap);
|
|
}
|
|
else
|
|
docHeight = font.GetWrapHeight(docString, maxDocWidth);
|
|
}
|
|
|
|
extWidth = Math.Max(extWidth, Math.Min(font.GetWidth(docString), maxDocWidth) + GS!(48));
|
|
}
|
|
extHeight += docHeight + GS!(4);
|
|
|
|
/*if (docWidth > maxDocWidth)
|
|
{
|
|
docWidth = newDocWidth;
|
|
docHeight = font.GetWrapHeight(showDocString, docWidth - GS!(20)) + GS!(17);
|
|
}
|
|
else
|
|
docHeight = GS!(32);*/
|
|
|
|
|
|
/*curY += font.GetLineSpacing() + GS!(4);
|
|
if (g != null)
|
|
{
|
|
using (g.PushColor(0xFFC0C0C0))
|
|
g.DrawString(docString, curX, curY, .Left, mWidth, .Ellipsis);
|
|
}
|
|
extWidth = Math.Max(extWidth, font.GetWidth(docString) + GS!(48));
|
|
extHeight += font.GetLineSpacing() + GS!(4);*/
|
|
|
|
|
|
if (docParser.mParamInfo != null)
|
|
{
|
|
if (docParser.mParamInfo.TryGetValue(scope String(paramName), var paramDoc))
|
|
{
|
|
curY += font.GetLineSpacing() + GS!(4);
|
|
if (g != null)
|
|
{
|
|
using (g.PushColor(gApp.mSettings.mUISettings.mColors.mText))
|
|
{
|
|
g.DrawString(scope String(paramName.Length + 1)..AppendF("{0}:", paramName), curX, curY, .Left, mWidth, .Ellipsis);
|
|
}
|
|
|
|
using (g.PushColor(gApp.mSettings.mUISettings.mColors.mAutoCompleteDocText))
|
|
{
|
|
g.DrawString(paramDoc, curX + font.GetWidth(paramName) + font.GetWidth(": "), curY, .Left, mWidth, .Ellipsis);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var paramDocKV in docParser.mParamInfo)
|
|
{
|
|
extWidth = Math.Max(extWidth, font.GetWidth(paramDocKV.key) + font.GetWidth(": ") + font.GetWidth(paramDocKV.value) + GS!(48));
|
|
}
|
|
extHeight += font.GetLineSpacing() + GS!(4);
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Draw(Beefy.gfx.Graphics g)
|
|
{
|
|
base.Draw(g);
|
|
|
|
float extWidth;
|
|
float extHeight;
|
|
DrawInfo(g, out extWidth, out extHeight);
|
|
}
|
|
|
|
public override void RehupScale(float oldScale, float newScale)
|
|
{
|
|
base.RehupScale(oldScale, newScale);
|
|
mAutoComplete.Close();
|
|
}
|
|
}
|
|
|
|
public EditWidget mTargetEditWidget;
|
|
public Event<Action> mOnAutoCompleteInserted ~ _.Dispose();
|
|
public Event<Action> mOnClosed ~ _.Dispose();
|
|
public WidgetWindow mListWindow;
|
|
public AutoCompleteListWidget mAutoCompleteListWidget;
|
|
public WidgetWindow mInvokeWindow;
|
|
public InvokeWidget mInvokeWidget;
|
|
public List<InvokeWidget> mInvokeStack = new List<InvokeWidget>() ~ delete _; // Previous invokes (from async)
|
|
public int32 mInsertStartIdx = -1;
|
|
public int32 mInsertEndIdx = -1;
|
|
public String mInfoFilter ~ delete _;
|
|
public List<int32> mInvokeSrcPositions ~ delete _;
|
|
public static int32 sAutoCompleteIdx = 1;
|
|
public static Dictionary<String, int32> sAutoCompleteMRU = new Dictionary<String, int32>() {
|
|
(new String("return"), (int32)1)
|
|
} ~ delete _;
|
|
public bool mIsAsync = true;
|
|
public bool mIsMember;
|
|
public bool mIsFixit;
|
|
public bool mInvokeOnly;
|
|
public bool mUncertain;
|
|
public bool mIsDocumentationPass;
|
|
public bool mIsUserRequested;
|
|
|
|
bool mClosed;
|
|
bool mPopulating;
|
|
float mWantX;
|
|
float mWantY;
|
|
|
|
public this(EditWidget targetEditWidget)
|
|
{
|
|
mTargetEditWidget = targetEditWidget;
|
|
}
|
|
|
|
public ~this()
|
|
{
|
|
Close(false);
|
|
}
|
|
|
|
static ~this()
|
|
{
|
|
for (var key in sAutoCompleteMRU.Keys)
|
|
delete key;
|
|
}
|
|
|
|
public void UpdateWindow(ref WidgetWindow widgetWindow, Widget rootWidget, int textIdx, int width, int height)
|
|
{
|
|
var textIdx;
|
|
|
|
// This makes typing '..' NOT move the window after pressing the second '.'
|
|
if (mTargetEditWidget.Content.SafeGetChar(textIdx - 2) == '.')
|
|
{
|
|
textIdx--;
|
|
}
|
|
|
|
Debug.Assert(textIdx >= 0);
|
|
int line = 0;
|
|
int column = 0;
|
|
if (textIdx >= 0)
|
|
mTargetEditWidget.Content.GetLineCharAtIdx(textIdx, out line, out column);
|
|
|
|
float x;
|
|
float y;
|
|
mTargetEditWidget.Content.GetTextCoordAtLineChar(line, column, out x, out y);
|
|
|
|
mTargetEditWidget.Content.GetTextCoordAtCursor(var cursorX, var cursorY);
|
|
|
|
if (mInvokeWidget?.mIsAboveText != true)
|
|
y = Math.Max(y, cursorY + gApp.mCodeFont.GetHeight() * 0.0f);
|
|
|
|
/*if (cursorY > y + gApp.mCodeFont.GetHeight() * 2.5f)
|
|
y = cursorY;*/
|
|
|
|
float screenX;
|
|
float screenY;
|
|
mTargetEditWidget.Content.SelfToRootTranslate(x, y, out screenX, out screenY);
|
|
|
|
///
|
|
|
|
/*if ((mInvokeSrcPositions != null) && (mInvokeSrcPositions.Count > 0))
|
|
{
|
|
textIdx = mInvokeSrcPositions[mInvokeSrcPositions.Count - 1];
|
|
mTargetEditWidget.Content.GetLineCharAtIdx(textIdx, out line, out column);
|
|
mTargetEditWidget.Content.GetTextCoordAtLineChar(line, column, out x, out y);
|
|
|
|
float endScreenX;
|
|
float endScreenY;
|
|
mTargetEditWidget.Content.SelfToRootTranslate(x, y, out endScreenX, out endScreenY);
|
|
|
|
screenY = endScreenY;
|
|
}*/
|
|
|
|
///
|
|
|
|
int screenWidth = width;
|
|
int screenHeight = height;
|
|
|
|
screenX += mTargetEditWidget.mWidgetWindow.mClientX;
|
|
screenY += mTargetEditWidget.mWidgetWindow.mClientY;
|
|
screenX -= GS!(24);
|
|
screenY += GS!(20);
|
|
|
|
float startScreenY = screenY;
|
|
if (rootWidget == mInvokeWidget)
|
|
{
|
|
if (mInvokeWidget.mIsAboveText)
|
|
screenY -= height + GS!(16);
|
|
}
|
|
|
|
//TODO: Do better positioning
|
|
if ((mInvokeWindow != null) && (widgetWindow == mListWindow))
|
|
{
|
|
if (!mInvokeWidget.mIsAboveText)
|
|
screenY += mInvokeWindow.mWindowHeight - 6;
|
|
}
|
|
|
|
mWantX = screenX;
|
|
mWantY = screenY;
|
|
|
|
int workspaceX;
|
|
int workspaceY;
|
|
int workspaceWidth;
|
|
int workspaceHeight;
|
|
BFApp.sApp.GetWorkspaceRect(out workspaceX, out workspaceY, out workspaceWidth, out workspaceHeight);
|
|
if (screenX + width > workspaceWidth)
|
|
screenX = workspaceWidth - width;
|
|
if (screenX < workspaceX)
|
|
screenX = workspaceX;
|
|
|
|
if (rootWidget == mAutoCompleteListWidget)
|
|
{
|
|
// May clip of bottom?
|
|
if (screenY + GetMaxWindowHeight() >= workspaceHeight)
|
|
{
|
|
screenY = startScreenY - (height + GS!(16));
|
|
}
|
|
}
|
|
|
|
/*if (width > workspaceWidth)
|
|
{
|
|
screenWidth = workspaceWidth;
|
|
var font = IDEApp.sApp.mCodeFont;
|
|
//font.GetWrapHeight()
|
|
}*/
|
|
|
|
if (widgetWindow == null)
|
|
{
|
|
|
|
BFWindow.Flags windowFlags = BFWindow.Flags.ClientSized | BFWindow.Flags.PopupPosition | BFWindow.Flags.NoActivate | BFWindow.Flags.NoMouseActivate | BFWindow.Flags.DestAlpha;
|
|
widgetWindow = new WidgetWindow(mTargetEditWidget.mWidgetWindow,
|
|
"Autocomplete",
|
|
(int32)screenX, (int32)screenY,
|
|
screenWidth, screenHeight,
|
|
windowFlags,
|
|
rootWidget);
|
|
}
|
|
else
|
|
{
|
|
if (widgetWindow.mRootWidget != rootWidget)
|
|
{
|
|
var prevRoot = widgetWindow.mRootWidget;
|
|
//Debug.WriteLine("Setting window {0} to root {1} from root {2}", widgetWindow, rootWidget, prevRoot);
|
|
widgetWindow.SetRootWidget(rootWidget);
|
|
delete prevRoot;
|
|
}
|
|
widgetWindow.Resize((int)screenX, (int)screenY, width, height);
|
|
}
|
|
}
|
|
|
|
public void UpdateAsyncInfo()
|
|
{
|
|
GetAsyncTextPos();
|
|
UpdateData(null, true);
|
|
}
|
|
|
|
public void Update()
|
|
{
|
|
if ((mInvokeWindow != null) && (!mInvokeWidget.mIsAboveText))
|
|
{
|
|
int textIdx = mTargetEditWidget.Content.CursorTextPos;
|
|
int line = 0;
|
|
int column = 0;
|
|
if (textIdx >= 0)
|
|
mTargetEditWidget.Content.GetLineCharAtIdx(textIdx, out line, out column);
|
|
float x;
|
|
float y;
|
|
mTargetEditWidget.Content.GetTextCoordAtLineChar(line, column, out x, out y);
|
|
|
|
float screenX;
|
|
float screenY;
|
|
mTargetEditWidget.Content.SelfToRootTranslate(x, y, out screenX, out screenY);
|
|
|
|
screenX += mTargetEditWidget.mWidgetWindow.mClientX;
|
|
screenY += mTargetEditWidget.mWidgetWindow.mClientY;
|
|
|
|
//if (screenY >= mInvokeWindow.mY - 8)
|
|
|
|
int invokeLine = 0;
|
|
int invokeColumn = 0;
|
|
if (mInvokeSrcPositions != null)
|
|
mTargetEditWidget.Content.GetLineCharAtIdx(mInvokeSrcPositions[0], out invokeLine, out invokeColumn);
|
|
|
|
int insertLine = line;
|
|
if ((insertLine != invokeLine) && ((insertLine - invokeLine) * gApp.mCodeFont.GetHeight() < GS!(40)))
|
|
{
|
|
mInvokeWidget.mIgnoreMove++;
|
|
if (mListWindow != null)
|
|
mAutoCompleteListWidget.mIgnoreMove++;
|
|
mInvokeWidget.mIsAboveText = true;
|
|
mInvokeWidget.ResizeContent(false);
|
|
UpdateWindow(ref mInvokeWindow, mInvokeWidget, mInvokeSrcPositions[0], (int32)mInvokeWidget.mWidth, (int32)mInvokeWidget.mHeight);
|
|
if (mListWindow != null)
|
|
{
|
|
UpdateWindow(ref mListWindow, mAutoCompleteListWidget, mInsertStartIdx, mListWindow.mWindowWidth, mListWindow.mWindowHeight);
|
|
mAutoCompleteListWidget.mIgnoreMove--;
|
|
}
|
|
mInvokeWidget.mIgnoreMove--;
|
|
}
|
|
}
|
|
|
|
if (mAutoCompleteListWidget != null)
|
|
mAutoCompleteListWidget.UpdateWidth();
|
|
|
|
if ((IsShowing()) && (!IsInPanel()))
|
|
{
|
|
bool hasFocus = false;
|
|
if ((mListWindow != null) && (mListWindow.mHasFocus))
|
|
hasFocus = true;
|
|
if (mTargetEditWidget.mHasFocus)
|
|
hasFocus = true;
|
|
if (!hasFocus)
|
|
{
|
|
Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void GetFilter(String outFilter)
|
|
{
|
|
if ((mInsertEndIdx != -1) && (mInsertStartIdx != -1))
|
|
{
|
|
mTargetEditWidget.Content.ExtractString(mInsertStartIdx, Math.Max(mInsertEndIdx - mInsertStartIdx, 0), outFilter);
|
|
}
|
|
}
|
|
|
|
public void GetAsyncTextPos()
|
|
{
|
|
//Debug.WriteLine("GetAsyncTextPos start {0} {1}", mInsertStartIdx, mInsertEndIdx);
|
|
|
|
mInsertEndIdx = (int32)mTargetEditWidget.Content.CursorTextPos;
|
|
while ((mInsertStartIdx != -1) && (mInsertStartIdx < mInsertEndIdx))
|
|
{
|
|
char8 c = (char8)mTargetEditWidget.Content.mData.mText[mInsertStartIdx].mChar;
|
|
if ((c != ' ') && (c != ','))
|
|
break;
|
|
mInsertStartIdx++;
|
|
}
|
|
|
|
/*mInsertStartIdx = mInsertEndIdx;
|
|
while (mInsertStartIdx > 0)
|
|
{
|
|
char8 c = (char8)mTargetEditWidget.Content.mData.mText[mInsertStartIdx - 1].mChar;
|
|
if ((!c.IsLetterOrDigit) && (c != '_'))
|
|
{
|
|
break;
|
|
}
|
|
mInsertStartIdx--;
|
|
}*/
|
|
|
|
if ((mInvokeWidget != null) && (mInvokeWidget.mEntryList.Count > 0))
|
|
{
|
|
var data = mTargetEditWidget.Content.mData;
|
|
|
|
int32 startIdx = mInvokeSrcPositions[0];
|
|
if ((startIdx < data.mTextLength) && (data.mText[startIdx].mChar == '('))
|
|
{
|
|
mInvokeSrcPositions.Clear();
|
|
int32 openDepth = 0;
|
|
int32 checkIdx = startIdx;
|
|
mInvokeSrcPositions.Add(startIdx);
|
|
int32 argCount = 0;
|
|
|
|
void HadContent()
|
|
{
|
|
if (argCount == 0)
|
|
argCount++;
|
|
}
|
|
|
|
bool failed = false;
|
|
while (checkIdx < mTargetEditWidget.Content.mData.mText.Count)
|
|
{
|
|
var char8Data = mTargetEditWidget.Content.mData.mText[checkIdx];
|
|
if (char8Data.mDisplayTypeId == 0)
|
|
{
|
|
if (char8Data.mChar == '{')
|
|
{
|
|
openDepth++;
|
|
failed = true;
|
|
break;
|
|
}
|
|
else if (char8Data.mChar == '}')
|
|
openDepth--;
|
|
else if (char8Data.mChar == '(')
|
|
openDepth++;
|
|
else if (char8Data.mChar == ')')
|
|
{
|
|
openDepth--;
|
|
}
|
|
else if ((char8Data.mChar == ',') && (openDepth == 1))
|
|
{
|
|
mInvokeSrcPositions.Add(checkIdx);
|
|
argCount++;
|
|
}
|
|
else if (!((char8)char8Data.mChar).IsWhiteSpace)
|
|
HadContent();
|
|
|
|
if (openDepth == 0)
|
|
{
|
|
mInvokeSrcPositions.Add(checkIdx);
|
|
break;
|
|
}
|
|
}
|
|
else if (char8Data.mDisplayPassId != (.)SourceElementType.Comment)
|
|
{
|
|
HadContent();
|
|
}
|
|
checkIdx++;
|
|
}
|
|
|
|
bool hasTooFewParams = false;
|
|
if (!failed)
|
|
{
|
|
if (mInvokeWidget.mSelectIdx != -1)
|
|
{
|
|
let entry = mInvokeWidget.mEntryList[mInvokeWidget.mSelectIdx];
|
|
if (!entry.HasParamsParam())
|
|
hasTooFewParams = entry.GetParamCount() < argCount;
|
|
}
|
|
}
|
|
|
|
if (hasTooFewParams)
|
|
{
|
|
// Make sure the current method has enough params to support the args coming in
|
|
for (int checkOffset = 0; checkOffset < mInvokeWidget.mEntryList.Count; checkOffset++)
|
|
{
|
|
int checkEntryIdx = (mInvokeWidget.mSelectIdx + checkOffset) % mInvokeWidget.mEntryList.Count;
|
|
|
|
let entry = mInvokeWidget.mEntryList[checkEntryIdx];
|
|
bool matches = false;
|
|
int paramCount = entry.GetParamCount();
|
|
if (argCount <= paramCount)
|
|
{
|
|
matches = true;
|
|
}
|
|
if (entry.HasParamsParam())
|
|
{
|
|
matches = true;
|
|
}
|
|
|
|
if ((matches) && (mInvokeWidget.mSelectIdx != -1))
|
|
{
|
|
let prevEntry = mInvokeWidget.mEntryList[mInvokeWidget.mSelectIdx];
|
|
int prevMatchDiff = prevEntry.GetParamCount() - argCount;
|
|
int newMatchDiff = entry.GetParamCount() - argCount;
|
|
if ((prevMatchDiff >= 0) && (prevMatchDiff < newMatchDiff))
|
|
matches = false;
|
|
}
|
|
|
|
if (matches)
|
|
{
|
|
mInvokeWidget.mSelectIdx = (int32)checkEntryIdx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//Debug.WriteLine("GetAsyncTextPos end {0} {1}", mInsertStartIdx, mInsertEndIdx);
|
|
}
|
|
|
|
bool SelectEntry(String curString)
|
|
{
|
|
if (mAutoCompleteListWidget == null)
|
|
return false;
|
|
|
|
int32 caseMatchMRUPriority = -1;
|
|
int32 caseNotMatchMRUPriority = -1;
|
|
|
|
int32 selectIdx = mAutoCompleteListWidget.mSelectIdx;
|
|
|
|
bool hadMatch = false;
|
|
for (int32 i = 0; i < mAutoCompleteListWidget.mEntryList.Count; i++)
|
|
{
|
|
var entry = mAutoCompleteListWidget.mEntryList[i];
|
|
if (entry.mEntryDisplay == curString)
|
|
{
|
|
hadMatch = true;
|
|
selectIdx = i;
|
|
break;
|
|
}
|
|
|
|
if (curString.Length > entry.mEntryDisplay.Length)
|
|
continue;
|
|
|
|
if (String.Compare(curString, 0, entry.mEntryDisplay, 0, curString.Length, false) == 0)
|
|
{
|
|
hadMatch = true;
|
|
int32 priority = -1;
|
|
sAutoCompleteMRU.TryGetValue(entry.mEntryDisplay, out priority);
|
|
if (priority > caseMatchMRUPriority)
|
|
{
|
|
selectIdx = i;
|
|
caseMatchMRUPriority = priority;
|
|
}
|
|
}
|
|
else if ((caseMatchMRUPriority == -1) && (String.Compare(curString, 0, entry.mEntryDisplay, 0, curString.Length, true) == 0))
|
|
{
|
|
hadMatch = true;
|
|
int32 priority = -1;
|
|
sAutoCompleteMRU.TryGetValue(entry.mEntryDisplay, out priority);
|
|
if (priority > caseNotMatchMRUPriority)
|
|
{
|
|
selectIdx = i;
|
|
caseNotMatchMRUPriority = priority;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (selectIdx == -1)
|
|
selectIdx = 0;
|
|
|
|
if (!mAutoCompleteListWidget.mEntryList.IsEmpty)
|
|
mAutoCompleteListWidget.Select(selectIdx);
|
|
|
|
return hadMatch;
|
|
}
|
|
|
|
public void SetIgnoreMove(bool ignoreMove)
|
|
{
|
|
if (mAutoCompleteListWidget != null)
|
|
mAutoCompleteListWidget.mIgnoreMove += ignoreMove ? 1 : -1;
|
|
if (mInvokeWidget != null)
|
|
mInvokeWidget.mIgnoreMove += ignoreMove ? 1 : -1;
|
|
}
|
|
|
|
bool DoesFilterMatch(String entry, String filter)
|
|
{
|
|
if (filter.Length == 0)
|
|
return true;
|
|
|
|
char8* entryPtr = entry.Ptr;
|
|
char8* filterPtr = filter.Ptr;
|
|
|
|
int filterLen = (int)filter.Length;
|
|
int entryLen = (int)entry.Length;
|
|
|
|
bool hasUnderscore = false;
|
|
bool checkInitials = filterLen > 1;
|
|
for (int i = 0; i < (int)filterLen; i++)
|
|
{
|
|
char8 c = filterPtr[i];
|
|
if (c == '_')
|
|
hasUnderscore = true;
|
|
else if (filterPtr[i].IsLower)
|
|
checkInitials = false;
|
|
}
|
|
|
|
if (hasUnderscore)
|
|
//return strnicmp(filter, entry, filterLen) == 0;
|
|
return (entryLen >= filterLen) && (String.Compare(entryPtr, filterLen, filterPtr, filterLen, true) == 0);
|
|
|
|
char8[256] initialStr;
|
|
char8* initialStrP = &initialStr;
|
|
|
|
//String initialStr;
|
|
bool prevWasUnderscore = false;
|
|
|
|
for (int entryIdx = 0; entryIdx < entryLen; entryIdx++)
|
|
{
|
|
char8 entryC = entryPtr[entryIdx];
|
|
|
|
if (entryC == '_')
|
|
{
|
|
prevWasUnderscore = true;
|
|
continue;
|
|
}
|
|
|
|
if ((entryIdx == 0) || (prevWasUnderscore) || (entryC.IsUpper) || (entryC.IsDigit))
|
|
{
|
|
/*if (strnicmp(filter, entry + entryIdx, filterLen) == 0)
|
|
return true;*/
|
|
if ((entryLen - entryIdx >= filterLen) && (String.Compare(entryPtr + entryIdx, filterLen, filterPtr, filterLen, true) == 0))
|
|
return true;
|
|
if (checkInitials)
|
|
*(initialStrP++) = entryC;
|
|
}
|
|
prevWasUnderscore = false;
|
|
|
|
if (filterLen == 1)
|
|
break; // Don't check inners for single-character case
|
|
}
|
|
|
|
if (!checkInitials)
|
|
return false;
|
|
int initialLen = initialStrP - (char8*)&initialStr;
|
|
return (initialLen >= filterLen) && (String.Compare(&initialStr, filterLen, filterPtr, filterLen, true) == 0);
|
|
|
|
//*(initialStrP++) = 0;
|
|
//return strnicmp(filter, initialStr, filterLen) == 0;
|
|
}
|
|
|
|
void UpdateData(String selectString, bool changedAfterInfo)
|
|
{
|
|
if ((mInsertEndIdx != -1) && (mInsertEndIdx < mInsertStartIdx))
|
|
{
|
|
mPopulating = false;
|
|
Close();
|
|
return;
|
|
}
|
|
|
|
int visibleCount = 0;
|
|
if (mAutoCompleteListWidget != null)
|
|
visibleCount = mAutoCompleteListWidget.mEntryList.Count;
|
|
if ((mAutoCompleteListWidget != null) && ((mInsertEndIdx != -1) || (selectString != null)))
|
|
{
|
|
String curString;
|
|
if (selectString != null)
|
|
curString = selectString;
|
|
else
|
|
{
|
|
curString = scope:: String();
|
|
mTargetEditWidget.Content.ExtractString(mInsertStartIdx, mInsertEndIdx - mInsertStartIdx, curString);
|
|
}
|
|
|
|
//if (selectString == null)
|
|
if (changedAfterInfo)
|
|
{
|
|
mAutoCompleteListWidget.mSelectIdx = -1;
|
|
|
|
if ((curString.Length == 0) && (!mIsMember) && (mInvokeSrcPositions == null))
|
|
{
|
|
mPopulating = false;
|
|
Close();
|
|
return;
|
|
}
|
|
|
|
// Only show applicable entries
|
|
mAutoCompleteListWidget.mMaxWidth = 0;
|
|
mAutoCompleteListWidget.mDocWidth = 0;
|
|
mAutoCompleteListWidget.mRightBoxAdjust = 0;
|
|
visibleCount = 0;
|
|
if (mAutoCompleteListWidget.mEntryList == mAutoCompleteListWidget.mFullEntryList)
|
|
mAutoCompleteListWidget.mEntryList = new List<AutoCompleteListWidget.EntryWidget>();
|
|
mAutoCompleteListWidget.mEntryList.Clear();
|
|
|
|
int spaceIdx = curString.LastIndexOf(' ');
|
|
if (spaceIdx != -1)
|
|
curString.Remove(0, spaceIdx + 1);
|
|
|
|
curString.Trim();
|
|
if (curString == ".")
|
|
curString.Clear();
|
|
|
|
for (int i < mAutoCompleteListWidget.mFullEntryList.Count)
|
|
{
|
|
var entry = mAutoCompleteListWidget.mFullEntryList[i];
|
|
//if (String.Compare(entry.mEntryDisplay, 0, curString, 0, curString.Length, true) == 0)
|
|
if (DoesFilterMatch(entry.mEntryDisplay, curString))
|
|
{
|
|
mAutoCompleteListWidget.mEntryList.Add(entry);
|
|
mAutoCompleteListWidget.UpdateEntry(entry, visibleCount);
|
|
visibleCount++;
|
|
}
|
|
else
|
|
{
|
|
mAutoCompleteListWidget.UpdateEntry(entry, -1);
|
|
}
|
|
}
|
|
|
|
if ((visibleCount == 0) && (mInvokeSrcPositions == null))
|
|
{
|
|
mPopulating = false;
|
|
Close();
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Only take last part, useful for "overide <methodName>" autocompletes
|
|
for (int32 i = 0; i < curString.Length; i++)
|
|
{
|
|
char8 c = curString[i];
|
|
if ((c == '<') || (c == '('))
|
|
break;
|
|
if (c.IsWhiteSpace)
|
|
{
|
|
curString.Remove(0, i + 1);
|
|
i = 0;
|
|
}
|
|
}
|
|
|
|
if ((!SelectEntry(curString)) && (curString.Length > 0))
|
|
{
|
|
// If we can't find any matches, at least select a string that starts with the right char8acter
|
|
curString.RemoveToEnd(1);
|
|
SelectEntry(curString);
|
|
}
|
|
|
|
if (mAutoCompleteListWidget != null)
|
|
{
|
|
mAutoCompleteListWidget.UpdateWidth();
|
|
}
|
|
}
|
|
else if (selectString == null)
|
|
{
|
|
SelectEntry("");
|
|
}
|
|
|
|
SetIgnoreMove(true);
|
|
|
|
gApp.mAutoCompletePanel.StartBind(this);
|
|
|
|
int32 prevInvokeSelect = 0;
|
|
if (mInvokeWidget != null)
|
|
{
|
|
prevInvokeSelect = mInvokeWidget.mSelectIdx;
|
|
if ((mInvokeWidget.mEntryList.Count > 0) && (!mInvokeSrcPositions.IsEmpty))
|
|
{
|
|
if (IsInPanel())
|
|
{
|
|
mInvokeWidget.mOwnsWindow = false;
|
|
}
|
|
else
|
|
{
|
|
mInvokeWidget.mOwnsWindow = true;
|
|
mInvokeWidget.ResizeContent(false);
|
|
UpdateWindow(ref mInvokeWindow, mInvokeWidget, mInvokeSrcPositions[0], (int32)mInvokeWidget.mWidth, (int32)mInvokeWidget.mHeight);
|
|
mInvokeWidget.ResizeContent(true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((mInvokeWindow == null) || (mInvokeWindow.mRootWidget != mInvokeWidget))
|
|
delete mInvokeWidget;
|
|
if (mInvokeWindow != null)
|
|
{
|
|
mInvokeWindow.Close();
|
|
mInvokeWindow = null;
|
|
}
|
|
mInvokeWidget = null;
|
|
}
|
|
}
|
|
|
|
if (mAutoCompleteListWidget != null)
|
|
{
|
|
if (mAutoCompleteListWidget.mEntryList.Count > 0)
|
|
{
|
|
mAutoCompleteListWidget.mOwnsWindow = !IsInPanel();
|
|
mAutoCompleteListWidget.mAutoFocus = IsInPanel();
|
|
int32 windowWidth = (int32)mAutoCompleteListWidget.mMaxWidth;
|
|
if (mAutoCompleteListWidget.mRightBoxAdjust != 0)
|
|
windowWidth += (int32)mAutoCompleteListWidget.mRightBoxAdjust; // - GS!(16);
|
|
//windowWidth += (int32)mAutoCompleteListWidget.mDocWidth;
|
|
windowWidth += GS!(16);
|
|
|
|
int32 contentHeight = (int32)(visibleCount * mAutoCompleteListWidget.mItemSpacing);
|
|
int32 windowHeight = contentHeight + GS!(20);
|
|
int32 maxWindowHeight = GetMaxWindowHeight();
|
|
bool wantScrollbar = false;
|
|
if (windowHeight > maxWindowHeight)
|
|
{
|
|
windowHeight = maxWindowHeight;
|
|
wantScrollbar = true;
|
|
windowWidth += GS!(12);
|
|
}
|
|
contentHeight += GS!(8);
|
|
mAutoCompleteListWidget.ResizeContent(windowWidth, contentHeight, wantScrollbar);
|
|
if ((mInsertStartIdx != -1) && (!IsInPanel()))
|
|
{
|
|
UpdateWindow(ref mListWindow, mAutoCompleteListWidget, mInsertStartIdx, windowWidth, windowHeight);
|
|
mAutoCompleteListWidget.mWantHeight = windowHeight;
|
|
}
|
|
mAutoCompleteListWidget.UpdateScrollbars();
|
|
mAutoCompleteListWidget.CenterSelection();
|
|
mAutoCompleteListWidget.UpdateWidth();
|
|
}
|
|
else
|
|
{
|
|
if ((mListWindow == null) || (mListWindow.mRootWidget != mAutoCompleteListWidget))
|
|
{
|
|
if (IsInPanel())
|
|
{
|
|
gApp.mAutoCompletePanel.Unbind(this);
|
|
if (mInvokeWidget != null)
|
|
{
|
|
if (mInvokeWidget.mParent != null)
|
|
mInvokeWidget.RemoveSelf();
|
|
delete mInvokeWidget;
|
|
mInvokeWidget = null;
|
|
}
|
|
}
|
|
delete mAutoCompleteListWidget;
|
|
}
|
|
if (mListWindow != null)
|
|
{
|
|
mListWindow.Close();
|
|
mListWindow = null;
|
|
}
|
|
mAutoCompleteListWidget = null;
|
|
}
|
|
}
|
|
gApp.mAutoCompletePanel.FinishBind();
|
|
SetIgnoreMove(false);
|
|
|
|
if ((mAutoCompleteListWidget != null) && (!mAutoCompleteListWidget.mIsInitted))
|
|
mAutoCompleteListWidget.Init();
|
|
if ((mInvokeWidget != null) && (!mInvokeWidget.mIsInitted))
|
|
{
|
|
mInvokeWidget.mSelectIdx = prevInvokeSelect;
|
|
mInvokeWidget.Init();
|
|
}
|
|
}
|
|
|
|
public void UpdateInfo(String info)
|
|
{
|
|
for (var entryView in info.Split('\n'))
|
|
{
|
|
StringView entryType = StringView(entryView);
|
|
int tabPos = entryType.IndexOf('\t');
|
|
StringView entryDisplay = default;
|
|
if (tabPos != -1)
|
|
{
|
|
entryDisplay = StringView(entryView, tabPos + 1);
|
|
entryType = StringView(entryType, 0, tabPos);
|
|
}
|
|
|
|
StringView documentation = default;
|
|
int docPos = entryDisplay.IndexOf('\x03');
|
|
if (docPos != -1)
|
|
{
|
|
documentation = StringView(entryDisplay, docPos + 1);
|
|
entryDisplay = StringView(entryDisplay, 0, docPos);
|
|
}
|
|
|
|
StringView entryInsert = default;
|
|
tabPos = entryDisplay.IndexOf('\t');
|
|
if (tabPos != -1)
|
|
{
|
|
entryInsert = StringView(entryDisplay, tabPos + 1);
|
|
entryDisplay = StringView(entryDisplay, 0, tabPos);
|
|
}
|
|
|
|
int entryIdx = 0;
|
|
switch (entryType)
|
|
{
|
|
case "insertRange":
|
|
case "invoke":
|
|
case "invoke_cur":
|
|
case "isMember":
|
|
case "invokeInfo":
|
|
case "invokeLeftParen":
|
|
case "select":
|
|
default:
|
|
{
|
|
if ((!documentation.IsEmpty) && (mAutoCompleteListWidget != null))
|
|
{
|
|
while (entryIdx < mAutoCompleteListWidget.mEntryList.Count)
|
|
{
|
|
let entry = mAutoCompleteListWidget.mEntryList[entryIdx];
|
|
if ((entry.mEntryDisplay == entryDisplay) && (entry.mEntryType == entryType))
|
|
{
|
|
if (entry.mDocumentation == null)
|
|
entry.mDocumentation = new:(mAutoCompleteListWidget.[Friend]mAlloc) String(documentation);
|
|
break;
|
|
}
|
|
entryIdx++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (mAutoCompleteListWidget != null)
|
|
mAutoCompleteListWidget.UpdateWidth();
|
|
}
|
|
MarkDirty();
|
|
|
|
//Debug.WriteLine("UpdateInfo {0} {1}", mInsertStartIdx, mInsertEndIdx);
|
|
}
|
|
|
|
public void SetInfo(String info, bool clearList = true, int32 textOffset = 0, bool changedAfterInfo = false)
|
|
{
|
|
scope AutoBeefPerf("AutoComplete.SetInfo");
|
|
|
|
DeleteAndNullify!(mInfoFilter);
|
|
|
|
mPopulating = true;
|
|
//defer { mPopulating = false; };
|
|
|
|
Debug.Assert(!mClosed);
|
|
|
|
mIsFixit = false;
|
|
mInsertStartIdx = -1;
|
|
mInsertEndIdx = -1;
|
|
delete mInvokeSrcPositions;
|
|
mInvokeSrcPositions = null;
|
|
mUncertain = false;
|
|
|
|
if (clearList)
|
|
{
|
|
if (mAutoCompleteListWidget != null)
|
|
{
|
|
mAutoCompleteListWidget.mIgnoreMove++;
|
|
if (IsInPanel())
|
|
{
|
|
mAutoCompleteListWidget.RemoveSelf();
|
|
delete mAutoCompleteListWidget;
|
|
}
|
|
else if (mListWindow != null)
|
|
{
|
|
// Will get deleted later...
|
|
Debug.Assert(mListWindow.mRootWidget == mAutoCompleteListWidget);
|
|
}
|
|
else
|
|
delete mAutoCompleteListWidget;
|
|
mAutoCompleteListWidget = null;
|
|
}
|
|
}
|
|
if (mAutoCompleteListWidget == null)
|
|
{
|
|
mAutoCompleteListWidget = new AutoCompleteListWidget(this);
|
|
//Debug.WriteLine("Created mAutoCompleteListWidget {} in {}", mAutoCompleteListWidget, this);
|
|
}
|
|
|
|
bool queueClearInvoke = false;
|
|
if (queueClearInvoke)
|
|
{
|
|
mInvokeSrcPositions = null;
|
|
}
|
|
else
|
|
{
|
|
if (IsInPanel())
|
|
{
|
|
if (mInvokeWidget != null)
|
|
{
|
|
mInvokeWidget.RemoveSelf();
|
|
delete mInvokeWidget;
|
|
}
|
|
}
|
|
|
|
mInvokeWidget = new InvokeWidget(this);
|
|
}
|
|
|
|
InvokeWidget oldInvokeWidget = null;
|
|
String selectString = null;
|
|
for (var entryView in info.Split('\n'))
|
|
{
|
|
|
|
Image entryIcon = null;
|
|
StringView entryType = StringView(entryView);
|
|
int tabPos = entryType.IndexOf('\t');
|
|
StringView entryDisplay = default;
|
|
if (tabPos != -1)
|
|
{
|
|
entryDisplay = StringView(entryView, tabPos + 1);
|
|
entryType = StringView(entryType, 0, tabPos);
|
|
}
|
|
|
|
StringView documentation = default;
|
|
int docPos = entryDisplay.IndexOf('\x03');
|
|
if (docPos != -1)
|
|
{
|
|
documentation = StringView(entryDisplay, docPos + 1);
|
|
entryDisplay = StringView(entryDisplay, 0, docPos);
|
|
}
|
|
|
|
StringView entryInsert = default;
|
|
tabPos = entryDisplay.IndexOf('\t');
|
|
if (tabPos != -1)
|
|
{
|
|
entryInsert = StringView(entryDisplay, tabPos + 1);
|
|
entryDisplay = StringView(entryDisplay, 0, tabPos);
|
|
}
|
|
|
|
if (entryDisplay.Ptr == null)
|
|
{
|
|
if (entryView == "uncertain")
|
|
{
|
|
mUncertain = true;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
switch (entryType)
|
|
{
|
|
case "method":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.Method);
|
|
case "field":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.Field);
|
|
case "property":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.Property);
|
|
case "namespace":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.Namespace);
|
|
case "class":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.Type_Class);
|
|
case "interface":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.Interface);
|
|
case "valuetype":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.Type_ValueType);
|
|
case "object":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.IconObject);
|
|
case "pointer":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.IconPointer);
|
|
case "value":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.IconValue);
|
|
case "payloadEnum":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.IconPayloadEnum);
|
|
case "generic": //TODO: make icon
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.IconValue);
|
|
case "folder":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.ProjectFolder);
|
|
case "file":
|
|
entryIcon = DarkTheme.sDarkTheme.GetImage(.Document);
|
|
}
|
|
|
|
bool isInvoke = false;
|
|
switch (entryType)
|
|
{
|
|
case "insertRange":
|
|
{
|
|
//var infoSections = scope List<StringView>(entryDisplay.Split(' '));
|
|
int spacePos = entryDisplay.IndexOf(' ');
|
|
if (spacePos != -1)
|
|
{
|
|
String str = scope String();
|
|
//infoSections[0].ToString(str);
|
|
str.Append(StringView(entryDisplay, 0, spacePos));
|
|
mInsertStartIdx = int32.Parse(str).Get() + textOffset;
|
|
str.Clear();
|
|
str.Append(StringView(entryDisplay, spacePos + 1));
|
|
//infoSections[1].ToString(str);
|
|
mInsertEndIdx = int32.Parse(str).Get();
|
|
if (mInsertEndIdx != -1)
|
|
mInsertEndIdx += textOffset;
|
|
}
|
|
}
|
|
case "invoke":
|
|
{
|
|
isInvoke = true;
|
|
}
|
|
case "invoke_cur":
|
|
// Only use the "invoke_cur" if we don't already have an invoke widget
|
|
if ((mInvokeWidget == null) || (mInvokeWidget.mEntryList.Count <= 1))
|
|
{
|
|
isInvoke = true;
|
|
}
|
|
else
|
|
{
|
|
for (int32 invokeIdx = 0; invokeIdx < mInvokeWidget.mEntryList.Count; invokeIdx++)
|
|
{
|
|
var invokeEntry = mInvokeWidget.mEntryList[invokeIdx];
|
|
if (invokeEntry.mText == entryDisplay)
|
|
{
|
|
mInvokeWidget.mSelectIdx = invokeIdx;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case "isMember":
|
|
mIsMember = true;
|
|
case "invokeInfo":
|
|
{
|
|
String invokeStr = scope String(entryDisplay);
|
|
|
|
var infoSections = scope List<StringView>(invokeStr.Split(' '));
|
|
var str = scope String();
|
|
infoSections[0].ToString(str);
|
|
mInvokeWidget.mSelectIdx = int32.Parse(str);
|
|
|
|
mInvokeSrcPositions = new List<int32>();
|
|
for (int32 i = 1; i < infoSections.Count; i++)
|
|
{
|
|
str.Clear();
|
|
infoSections[i].ToString(str);
|
|
mInvokeSrcPositions.Add(int32.Parse(str));
|
|
}
|
|
}
|
|
case "invokeLeftParen":
|
|
{
|
|
mInvokeWidget.mLeftParenIdx = int32.Parse(entryDisplay);
|
|
}
|
|
case "select":
|
|
{
|
|
selectString = scope:: String(entryDisplay);
|
|
}
|
|
default:
|
|
{
|
|
if (!mInvokeOnly)
|
|
{
|
|
mIsFixit |= entryType == "fixit";
|
|
mAutoCompleteListWidget.AddEntry(entryType, entryDisplay, entryIcon, entryInsert, documentation);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isInvoke)
|
|
{
|
|
if (queueClearInvoke)
|
|
{
|
|
oldInvokeWidget = mInvokeWidget;
|
|
mInvokeWidget = new InvokeWidget(this);
|
|
queueClearInvoke = false;
|
|
}
|
|
|
|
var invokeEntry = new InvokeWidget.Entry();
|
|
invokeEntry.mText = new String(entryDisplay);
|
|
if (!documentation.IsEmpty)
|
|
invokeEntry.mDocumentation = new String(documentation);
|
|
mInvokeWidget.AddEntry(invokeEntry);
|
|
}
|
|
}
|
|
|
|
if (oldInvokeWidget != null)
|
|
{
|
|
/*if ((!mIsAsync) || (oldInvokeWidget.mLeftParenIdx == mInvokeWidget.mLeftParenIdx))
|
|
{
|
|
// If it's not another embedded invoke then just get rid of it
|
|
delete oldInvokeWidget;
|
|
}
|
|
else
|
|
{
|
|
mInvokeStack.Add(oldInvokeWidget);
|
|
oldInvokeWidget.Cleanup();
|
|
}*/
|
|
}
|
|
|
|
if (changedAfterInfo)
|
|
{
|
|
GetAsyncTextPos();
|
|
}
|
|
|
|
if ((mInvokeWidget != null) && (mInvokeSrcPositions != null))
|
|
{
|
|
int invokeLine = 0;
|
|
int invokeColumn = 0;
|
|
mTargetEditWidget.Content.GetLineCharAtIdx(mInvokeSrcPositions[0], out invokeLine, out invokeColumn);
|
|
int insertLine = 0;
|
|
int insertColumn = 0;
|
|
mTargetEditWidget.Content.GetLineCharAtIdx(mTargetEditWidget.Content.CursorTextPos, out insertLine, out insertColumn);
|
|
|
|
if ((insertLine != invokeLine) && ((insertLine - invokeLine) * gApp.mCodeFont.GetHeight() < GS!(40)))
|
|
mInvokeWidget.mIsAboveText = true;
|
|
}
|
|
|
|
mInfoFilter = new String();
|
|
GetFilter(mInfoFilter);
|
|
UpdateData(selectString, changedAfterInfo);
|
|
mPopulating = false;
|
|
|
|
//Debug.WriteLine("SetInfo {0} {1}", mInsertStartIdx, mInsertEndIdx);
|
|
}
|
|
|
|
public bool HasSelection()
|
|
{
|
|
return mAutoCompleteListWidget != null;
|
|
}
|
|
|
|
public bool IsShowing()
|
|
{
|
|
return (mInvokeWidget != null) || (mAutoCompleteListWidget != null);
|
|
}
|
|
|
|
public bool IsInPanel()
|
|
{
|
|
return (gApp.mAutoCompletePanel != null) && (this == gApp.mAutoCompletePanel.mAutoComplete);
|
|
}
|
|
|
|
public void Close(bool deleteSelf = true)
|
|
{
|
|
Debug.Assert(!mPopulating);
|
|
|
|
if (!mClosed)
|
|
{
|
|
if ((DarkTooltipManager.sTooltip != null) && (DarkTooltipManager.sTooltip.mAllowMouseOutside))
|
|
DarkTooltipManager.CloseTooltip();
|
|
|
|
if (IsInPanel())
|
|
{
|
|
gApp.mAutoCompletePanel.Unbind(this);
|
|
if (mTargetEditWidget.mWidgetWindow != null)
|
|
mTargetEditWidget.SetFocus();
|
|
}
|
|
|
|
if (deleteSelf)
|
|
{
|
|
BFApp.sApp.DeferDelete(this);
|
|
}
|
|
mClosed = true;
|
|
|
|
if (mInvokeStack != null)
|
|
{
|
|
for (var oldInvoke in mInvokeStack)
|
|
delete oldInvoke;
|
|
}
|
|
|
|
if (mOnClosed.HasListeners)
|
|
mOnClosed();
|
|
|
|
if ((mAutoCompleteListWidget != null) && (mAutoCompleteListWidget.mWidgetWindow == null))
|
|
{
|
|
mAutoCompleteListWidget.Cleanup();
|
|
if (mListWindow?.mRootWidget == mAutoCompleteListWidget)
|
|
mListWindow.mRootWidget = null;
|
|
if (IsInPanel())
|
|
gApp.mAutoCompletePanel.Unbind(this);
|
|
delete mAutoCompleteListWidget;
|
|
}
|
|
|
|
if (mListWindow != null)
|
|
{
|
|
mListWindow.Dispose();
|
|
mListWindow = null;
|
|
}
|
|
|
|
if ((mInvokeWidget != null) && (mInvokeWidget.mWidgetWindow == null))
|
|
{
|
|
mInvokeWidget.Cleanup();
|
|
if (mInvokeWindow?.mRootWidget == mInvokeWidget)
|
|
mInvokeWindow.mRootWidget = null;
|
|
delete mInvokeWidget;
|
|
mInvokeWidget = null;
|
|
}
|
|
|
|
if (mInvokeWindow != null)
|
|
{
|
|
mInvokeWindow.Dispose();
|
|
mInvokeWindow = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void CloseListWindow()
|
|
{
|
|
if (mInvokeSrcPositions == null)
|
|
{
|
|
Close();
|
|
return;
|
|
}
|
|
|
|
if (IsInPanel())
|
|
{
|
|
if (mAutoCompleteListWidget != null)
|
|
{
|
|
mAutoCompleteListWidget.RemoveSelf();
|
|
delete mAutoCompleteListWidget;
|
|
mAutoCompleteListWidget = null;
|
|
}
|
|
}
|
|
else if (mListWindow != null)
|
|
{
|
|
mListWindow.Dispose();
|
|
mListWindow = null;
|
|
mAutoCompleteListWidget = null;
|
|
}
|
|
}
|
|
|
|
public void ClearAsyncEdit()
|
|
{
|
|
CloseListWindow();
|
|
if (mInvokeSrcPositions != null)
|
|
UpdateAsyncInfo();
|
|
}
|
|
|
|
public void CloseInvoke()
|
|
{
|
|
if (mInvokeStack.Count > 0)
|
|
{
|
|
delete mInvokeWidget;
|
|
mInvokeWidget = mInvokeStack[mInvokeStack.Count - 1];
|
|
mInvokeStack.RemoveAt(mInvokeStack.Count - 1);
|
|
|
|
UpdateAsyncInfo();
|
|
return;
|
|
}
|
|
|
|
Close();
|
|
}
|
|
|
|
public bool IsInsertEmpty()
|
|
{
|
|
return mInsertStartIdx == mInsertEndIdx;
|
|
}
|
|
|
|
void ApplyFixit(String data)
|
|
{
|
|
int splitIdx = data.IndexOf('\x01');
|
|
if (splitIdx != -1)
|
|
{
|
|
String lhs = scope String(data, 0, splitIdx);
|
|
String rhs = scope String(data, splitIdx + 1);
|
|
ApplyFixit(lhs);
|
|
ApplyFixit(rhs);
|
|
return;
|
|
}
|
|
|
|
var targetSourceEditWidgetContent = mTargetEditWidget.Content as SourceEditWidgetContent;
|
|
var sourceEditWidgetContent = targetSourceEditWidgetContent;
|
|
var prevCursorPosition = sourceEditWidgetContent.CursorTextPos;
|
|
var prevScrollPos = mTargetEditWidget.mVertPos.mDest;
|
|
|
|
UndoBatchStart undoBatchStart = null;
|
|
|
|
var parts = String.StackSplit!(data, '|');
|
|
String fixitKind = parts[0];
|
|
String fixitFileName = parts[1];
|
|
SourceViewPanel sourceViewPanel = IDEApp.sApp.ShowSourceFile(fixitFileName);
|
|
bool focusChange = !fixitKind.StartsWith(".");
|
|
|
|
var historyEntry = targetSourceEditWidgetContent.RecordHistoryLocation();
|
|
historyEntry.mNoMerge = true;
|
|
|
|
if (sourceEditWidgetContent.mSourceViewPanel != sourceViewPanel)
|
|
{
|
|
sourceEditWidgetContent = (SourceEditWidgetContent)sourceViewPanel.GetActivePanel().EditWidget.mEditWidgetContent;
|
|
undoBatchStart = new UndoBatchStart("autocomplete");
|
|
sourceEditWidgetContent.mData.mUndoManager.Add(undoBatchStart);
|
|
}
|
|
|
|
if (!focusChange)
|
|
{
|
|
if (prevScrollPos != 0)
|
|
sourceEditWidgetContent.CheckRecordScrollTop(true);
|
|
}
|
|
|
|
int32 fixitIdx = 0;
|
|
int32 fixitLen = 0;
|
|
StringView fixitLocStr = parts[2];
|
|
int dashPos = fixitLocStr.IndexOf('-');
|
|
if (dashPos != -1)
|
|
{
|
|
fixitLen = int32.Parse(fixitLocStr.Substring(dashPos + 1));
|
|
fixitLocStr.RemoveToEnd(dashPos);
|
|
}
|
|
|
|
if (fixitLocStr.Contains(':'))
|
|
{
|
|
var splitItr = fixitLocStr.Split(':');
|
|
int32 line = int32.Parse(splitItr.GetNext().Value).Value;
|
|
int32 col = int32.Parse(splitItr.GetNext().Value).Value;
|
|
fixitIdx = (.)sourceEditWidgetContent.GetTextIdx(line, col);
|
|
}
|
|
else
|
|
fixitIdx = int32.Parse(fixitLocStr).GetValueOrDefault();
|
|
|
|
int prevTextLength = sourceEditWidgetContent.mData.mTextLength;
|
|
|
|
int insertCount = 0;
|
|
int dataIdx = 3;
|
|
|
|
while (dataIdx < parts.Count)
|
|
{
|
|
int32 lenAdd = 0;
|
|
|
|
String fixitInsert = scope String(parts[dataIdx++]);
|
|
while (dataIdx < parts.Count)
|
|
{
|
|
var insertStr = parts[dataIdx++];
|
|
if (insertStr.StartsWith("`"))
|
|
{
|
|
lenAdd = int32.Parse(StringView(insertStr, 1));
|
|
break;
|
|
}
|
|
|
|
fixitInsert.Append('\n');
|
|
fixitInsert.Append(insertStr);
|
|
}
|
|
|
|
#unwarn
|
|
bool hasMore = dataIdx < parts.Count;
|
|
|
|
if (sourceViewPanel != null)
|
|
{
|
|
if (sourceViewPanel.IsReadOnly)
|
|
{
|
|
gApp.Fail(scope String()..AppendF("The selected fixit cannot be applied to locked file '{}'", sourceViewPanel.mFilePath));
|
|
return;
|
|
}
|
|
|
|
sourceEditWidgetContent.CursorTextPos = fixitIdx;
|
|
if (focusChange)
|
|
sourceEditWidgetContent.EnsureCursorVisible(true, true);
|
|
|
|
sourceEditWidgetContent.mSelection = null;
|
|
if (fixitLen > 0)
|
|
{
|
|
sourceEditWidgetContent.mSelection = EditSelection(fixitIdx, fixitIdx + fixitLen);
|
|
sourceEditWidgetContent.DeleteSelection();
|
|
fixitLen = 0;
|
|
}
|
|
|
|
if (fixitInsert.StartsWith('\n'))
|
|
sourceEditWidgetContent.PasteText(fixitInsert, fixitInsert.StartsWith("\n"));
|
|
else
|
|
InsertImplText(sourceEditWidgetContent, fixitInsert);
|
|
|
|
fixitIdx = (.)sourceEditWidgetContent.CursorTextPos;
|
|
insertCount++;
|
|
}
|
|
}
|
|
|
|
if (!focusChange)
|
|
{
|
|
mTargetEditWidget.VertScrollTo(prevScrollPos, true);
|
|
sourceEditWidgetContent.CursorTextPos = prevCursorPosition;
|
|
int addedSize = sourceEditWidgetContent.mData.mTextLength - prevTextLength;
|
|
sourceEditWidgetContent.[Friend]AdjustCursorsAfterExternalEdit(fixitIdx, addedSize);
|
|
}
|
|
|
|
if (historyEntry != null)
|
|
{
|
|
// Make sure when we go back that we'll go back to the insert position
|
|
int idx = gApp.mHistoryManager.mHistoryList.LastIndexOf(historyEntry);
|
|
if (idx != -1)
|
|
gApp.mHistoryManager.mHistoryIdx = (.)idx;
|
|
}
|
|
|
|
if (undoBatchStart != null)
|
|
sourceEditWidgetContent.mData.mUndoManager.Add(undoBatchStart.mBatchEnd);
|
|
}
|
|
|
|
void InsertImplText(SourceEditWidgetContent sourceEditWidgetContent, String implText)
|
|
{
|
|
String implSect = scope .();
|
|
|
|
int startIdx = 0;
|
|
for (int i < implText.Length)
|
|
{
|
|
char8 c = implText[i];
|
|
if ((c == '\a') || (c == '\t') || (c == '\b') || (c == '\r') || (c == '\f'))
|
|
{
|
|
implSect.Clear();
|
|
implSect.Append(implText, startIdx, i - startIdx);
|
|
if (!implSect.IsEmpty)
|
|
{
|
|
sourceEditWidgetContent.InsertAtCursor(implSect);
|
|
}
|
|
|
|
if (c == '\a') // Ensure we have spacing or an open brace on the previous line
|
|
{
|
|
int lineNum = sourceEditWidgetContent.CursorLineAndColumn.mLine;
|
|
if (lineNum > 0)
|
|
{
|
|
sourceEditWidgetContent.GetLinePosition(lineNum - 1, var lineStart, var lineEnd);
|
|
for (int idx = lineEnd; idx >= lineStart; idx--)
|
|
{
|
|
let charData = sourceEditWidgetContent.mData.mText[idx];
|
|
if (charData.mDisplayTypeId == (.)SourceElementType.Comment)
|
|
continue;
|
|
if (charData.mChar.IsWhiteSpace)
|
|
continue;
|
|
if (charData.mChar == '{')
|
|
break;
|
|
|
|
// Add new line
|
|
sourceEditWidgetContent.InsertAtCursor("\n");
|
|
sourceEditWidgetContent.CursorToLineEnd();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if (c == '\f') // Make sure we're on an empty line
|
|
{
|
|
if (!sourceEditWidgetContent.IsLineWhiteSpace(sourceEditWidgetContent.CursorLineAndColumn.mLine))
|
|
{
|
|
sourceEditWidgetContent.InsertAtCursor("\n");
|
|
}
|
|
if (!sourceEditWidgetContent.IsLineWhiteSpace(sourceEditWidgetContent.CursorLineAndColumn.mLine))
|
|
{
|
|
int prevPos = sourceEditWidgetContent.CursorTextPos;
|
|
sourceEditWidgetContent.InsertAtCursor("\n");
|
|
sourceEditWidgetContent.CursorTextPos = prevPos;
|
|
}
|
|
sourceEditWidgetContent.CursorToLineEnd();
|
|
}
|
|
else if (c == '\t') // Open block
|
|
{
|
|
sourceEditWidgetContent.InsertAtCursor("\n");
|
|
sourceEditWidgetContent.CursorToLineEnd();
|
|
sourceEditWidgetContent.OpenCodeBlock();
|
|
}
|
|
else if (c == '\r') // Newline
|
|
{
|
|
sourceEditWidgetContent.InsertAtCursor("\n");
|
|
sourceEditWidgetContent.CursorToLineEnd();
|
|
}
|
|
else if (c == '\b') // Close block
|
|
{
|
|
int cursorPos = sourceEditWidgetContent.CursorTextPos;
|
|
while (cursorPos < sourceEditWidgetContent.mData.mTextLength)
|
|
{
|
|
char8 checkC = sourceEditWidgetContent.mData.mText[cursorPos].mChar;
|
|
cursorPos++;
|
|
if (checkC == '}')
|
|
break;
|
|
}
|
|
sourceEditWidgetContent.CursorTextPos = cursorPos;
|
|
}
|
|
else
|
|
{
|
|
let lc = sourceEditWidgetContent.CursorLineAndColumn;
|
|
sourceEditWidgetContent.CursorLineAndColumn = .(lc.mLine + 1, 0);
|
|
sourceEditWidgetContent.CursorToLineEnd();
|
|
sourceEditWidgetContent.InsertAtCursor("\n");
|
|
sourceEditWidgetContent.CursorToLineEnd();
|
|
}
|
|
|
|
startIdx = i + 1;
|
|
}
|
|
}
|
|
|
|
implSect.Clear();
|
|
implSect.Append(implText, startIdx, implText.Length - startIdx);
|
|
if (!implSect.IsEmpty)
|
|
{
|
|
sourceEditWidgetContent.InsertAtCursor(implSect);
|
|
}
|
|
}
|
|
|
|
|
|
public void InsertSelection(char32 keyChar, String insertType = null, String insertStr = null)
|
|
{
|
|
//Debug.WriteLine("InsertSelection");
|
|
|
|
EditSelection editSelection = EditSelection();
|
|
editSelection.mStartPos = mInsertStartIdx;
|
|
if (mInsertEndIdx != -1)
|
|
editSelection.mEndPos = mInsertEndIdx;
|
|
else
|
|
editSelection.mEndPos = mInsertStartIdx;
|
|
|
|
var entry = mAutoCompleteListWidget.mEntryList[mAutoCompleteListWidget.mSelectIdx];
|
|
if (keyChar == '!')
|
|
{
|
|
if (!entry.mEntryDisplay.EndsWith("!"))
|
|
{
|
|
// Try to find one that DOES end with a '!'
|
|
for (var checkEntry in mAutoCompleteListWidget.mEntryList)
|
|
{
|
|
if (checkEntry.mEntryDisplay.EndsWith("!"))
|
|
entry = checkEntry;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (insertStr != null)
|
|
insertStr.Append(entry.mEntryInsert ?? entry.mEntryDisplay);
|
|
|
|
if (entry.mEntryType == "fixit")
|
|
{
|
|
if (insertType != null)
|
|
insertType.Append(entry.mEntryType);
|
|
ApplyFixit(entry.mEntryInsert);
|
|
return;
|
|
}
|
|
|
|
String insertText = entry.mEntryInsert ?? entry.mEntryDisplay;
|
|
if ((keyChar == '=') && (insertText.EndsWith("=")))
|
|
insertText.RemoveToEnd(insertText.Length - 1);
|
|
//insertText = insertText.Substring(0, insertText.Length - 1);
|
|
String implText = null;
|
|
int tabIdx = insertText.IndexOf('\t');
|
|
if (tabIdx != -1)
|
|
{
|
|
implText = scope:: String();
|
|
implText.Append(insertText, tabIdx);
|
|
insertText.RemoveToEnd(tabIdx);
|
|
}
|
|
String prevText = scope String();
|
|
mTargetEditWidget.Content.ExtractString(editSelection.mStartPos, editSelection.mEndPos - editSelection.mStartPos, prevText);
|
|
|
|
//sAutoCompleteMRU[insertText] = sAutoCompleteIdx++;
|
|
String* keyPtr;
|
|
int32* valuePtr;
|
|
if (sAutoCompleteMRU.TryAdd(entry.mEntryDisplay, out keyPtr, out valuePtr))
|
|
{
|
|
// Only create new string if this entry doesn't exist already
|
|
*keyPtr = new String(entry.mEntryDisplay);
|
|
}
|
|
*valuePtr = sAutoCompleteIdx++;
|
|
|
|
if (insertText == prevText)
|
|
return;
|
|
|
|
var sourceEditWidgetContent = mTargetEditWidget.Content as SourceEditWidgetContent;
|
|
|
|
PersistentTextPosition[] persistentInvokeSrcPositons = null;
|
|
if (mInvokeSrcPositions != null)
|
|
{
|
|
persistentInvokeSrcPositons = scope:: PersistentTextPosition[mInvokeSrcPositions.Count];
|
|
for (int32 i = 0; i < mInvokeSrcPositions.Count; i++)
|
|
{
|
|
persistentInvokeSrcPositons[i] = new PersistentTextPosition(mInvokeSrcPositions[i]);
|
|
sourceEditWidgetContent.PersistentTextPositions.Add(persistentInvokeSrcPositons[i]);
|
|
}
|
|
}
|
|
|
|
mTargetEditWidget.Content.mSelection = editSelection;
|
|
//bool isMethod = (entry.mEntryType == "method");
|
|
if (insertText.EndsWith("<>"))
|
|
{
|
|
if (keyChar == '\t')
|
|
mTargetEditWidget.Content.InsertCharPair(insertText);
|
|
else if (keyChar == '<')
|
|
{
|
|
String str = scope String();
|
|
str.Append(insertText, 0, insertText.Length - 2);
|
|
mTargetEditWidget.Content.InsertAtCursor(str, .NoRestoreSelectionOnUndo);
|
|
}
|
|
else
|
|
mTargetEditWidget.Content.InsertAtCursor(insertText, .NoRestoreSelectionOnUndo);
|
|
}
|
|
else
|
|
mTargetEditWidget.Content.InsertAtCursor(insertText, .NoRestoreSelectionOnUndo);
|
|
|
|
/*if (mIsAsync)
|
|
UpdateAsyncInfo();*/
|
|
|
|
if (implText != null)
|
|
InsertImplText(sourceEditWidgetContent, implText);
|
|
|
|
if (persistentInvokeSrcPositons != null)
|
|
{
|
|
for (int32 i = 0; i < mInvokeSrcPositions.Count; i++)
|
|
{
|
|
//TEST
|
|
//var persistentTextPositon = persistentInvokeSrcPositons[i + 100];
|
|
var persistentTextPositon = persistentInvokeSrcPositons[i];
|
|
mInvokeSrcPositions[i] = persistentTextPositon.mIndex;
|
|
sourceEditWidgetContent.PersistentTextPositions.Remove(persistentTextPositon);
|
|
delete persistentTextPositon;
|
|
}
|
|
}
|
|
|
|
mTargetEditWidget.Content.EnsureCursorVisible();
|
|
if ((insertType != null) && (insertText.Length > 0))
|
|
insertType.Append(entry.mEntryType);
|
|
}
|
|
|
|
public void MarkDirty()
|
|
{
|
|
if (mInvokeWidget != null)
|
|
mInvokeWidget.MarkDirty();
|
|
if (mAutoCompleteListWidget != null)
|
|
mAutoCompleteListWidget.MarkDirty();
|
|
}
|
|
|
|
int32 GetMaxWindowHeight()
|
|
{
|
|
return (int32)(9 * mAutoCompleteListWidget.mItemSpacing) + GS!(20);
|
|
}
|
|
}
|
|
}
|