mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 19:48:20 +02:00
6100 lines
204 KiB
Beef
6100 lines
204 KiB
Beef
![]() |
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Text;
|
||
|
using System.Threading.Tasks;
|
||
|
using System.Threading;
|
||
|
using System.Diagnostics;
|
||
|
using System.IO;
|
||
|
using Beefy;
|
||
|
using Beefy.widgets;
|
||
|
using Beefy.theme;
|
||
|
using Beefy.gfx;
|
||
|
using Beefy.events;
|
||
|
using Beefy.theme.dark;
|
||
|
using Beefy.utils;
|
||
|
using Beefy.geom;
|
||
|
using IDE.Debugger;
|
||
|
using IDE.Compiler;
|
||
|
using System.Security.Cryptography;
|
||
|
using IDE.util;
|
||
|
|
||
|
namespace IDE.ui
|
||
|
{
|
||
|
public enum SourceElementType
|
||
|
{
|
||
|
Normal,
|
||
|
Keyword,
|
||
|
Literal,
|
||
|
Identifier,
|
||
|
Type,
|
||
|
Comment,
|
||
|
Method,
|
||
|
TypeRef,
|
||
|
Namespace,
|
||
|
|
||
|
Disassembly_Text,
|
||
|
Disassembly_FileName,
|
||
|
|
||
|
Error,
|
||
|
|
||
|
BuildError,
|
||
|
BuildWarning,
|
||
|
|
||
|
VisibleWhiteSpace
|
||
|
}
|
||
|
|
||
|
//[Flags]
|
||
|
public enum SourceElementFlags
|
||
|
{
|
||
|
Error = 1,
|
||
|
Warning = 2,
|
||
|
IsAfter = 4,
|
||
|
Skipped = 8,
|
||
|
CompilerFlags_Mask = 0x0F,
|
||
|
|
||
|
SpellingError = 0x10,
|
||
|
Find_Matches = 0x20,
|
||
|
SymbolReference = 0x40,
|
||
|
EditorFlags_Mask = 0xF0,
|
||
|
|
||
|
MASK = 0xFF
|
||
|
}
|
||
|
|
||
|
public class SourceEditWidget : DarkEditWidget
|
||
|
{
|
||
|
public TextPanel mPanel;
|
||
|
|
||
|
public this(SourceViewPanel panel, SourceEditWidget refEditWidget = null)
|
||
|
: base(new SourceEditWidgetContent((refEditWidget != null) ? refEditWidget.mEditWidgetContent : null))
|
||
|
{
|
||
|
mPanel = panel;
|
||
|
}
|
||
|
|
||
|
public this(SourceViewPanel panel, SourceEditWidgetContent content)
|
||
|
: base(content)
|
||
|
{
|
||
|
mPanel = panel;
|
||
|
}
|
||
|
|
||
|
public override void DrawAll(Graphics g)
|
||
|
{
|
||
|
base.DrawAll(g);
|
||
|
}
|
||
|
|
||
|
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
|
||
|
{
|
||
|
base.MouseDown(x, y, btn, btnCount);
|
||
|
}
|
||
|
|
||
|
public override void GotFocus()
|
||
|
{
|
||
|
//Debug.WriteLine("SourceViewPanel.GotFocus {0}", this);
|
||
|
|
||
|
if (var tabbedView = mParent.mParent as IDETabbedView)
|
||
|
{
|
||
|
if (tabbedView.mIsFillWidget)
|
||
|
gApp.mActiveDocumentsTabbedView = tabbedView;
|
||
|
else
|
||
|
{
|
||
|
NOP!();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidgetContent;
|
||
|
// Just got focus, flip this on until we get a KeyDown
|
||
|
sourceEditWidgetContent.mIgnoreKeyChar = true;
|
||
|
|
||
|
if (mPanel != null)
|
||
|
{
|
||
|
mPanel.mLastFocusTick = IDEApp.sApp.mUpdateCnt;
|
||
|
mPanel.EditGotFocus();
|
||
|
}
|
||
|
base.GotFocus();
|
||
|
}
|
||
|
|
||
|
public override void LostFocus()
|
||
|
{
|
||
|
if (mPanel != null)
|
||
|
mPanel.EditLostFocus();
|
||
|
base.LostFocus();
|
||
|
}
|
||
|
|
||
|
protected override bool WantsUnfocus()
|
||
|
{
|
||
|
if ((mWidgetWindow != null) && (mWidgetWindow.mOverWidget is PanelSplitter))
|
||
|
return false;
|
||
|
|
||
|
return base.WantsUnfocus();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class TrackedTextElementView
|
||
|
{
|
||
|
public TrackedTextElement mTrackedElement;
|
||
|
public PersistentTextPosition mTextPosition;
|
||
|
public int32 mLastBoundLine = -1;
|
||
|
public int32 mLastMoveIdx; // Compare to mTrackedElement.mMoveIdx
|
||
|
|
||
|
public this(TrackedTextElement trackedEntry)
|
||
|
{
|
||
|
mTrackedElement = trackedEntry;
|
||
|
mTrackedElement.AddRef();
|
||
|
}
|
||
|
|
||
|
public ~this()
|
||
|
{
|
||
|
mTrackedElement.Deref();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public class SourceFindTask
|
||
|
{
|
||
|
public WaitEvent mDoneEvent = new WaitEvent() ~ delete _;
|
||
|
public SourceViewPanel mSourceViewPanel;
|
||
|
public bool mCancelling;
|
||
|
public String mFilePath ~ delete _;
|
||
|
public String mFileName ~ delete _;
|
||
|
public String mBackupFileName ~ delete _; // Didn't match hash
|
||
|
public String mFoundPath ~ delete _;
|
||
|
public Dictionary<String, String> mRemapMap = new .() ~ DeleteDictionyAndKeysAndItems!(_);
|
||
|
|
||
|
public ~this()
|
||
|
{
|
||
|
Cancel();
|
||
|
WaitFor();
|
||
|
}
|
||
|
|
||
|
public void Cancel()
|
||
|
{
|
||
|
mCancelling = true;
|
||
|
}
|
||
|
|
||
|
public bool WaitFor(int waitMS = -1)
|
||
|
{
|
||
|
return mDoneEvent.WaitFor(waitMS);
|
||
|
}
|
||
|
|
||
|
void CheckFile(String filePath)
|
||
|
{
|
||
|
bool hashMatches = true;
|
||
|
if (!(mSourceViewPanel.mLoadedHash case .None))
|
||
|
{
|
||
|
SourceHash.Kind hashKind = mSourceViewPanel.mLoadedHash.GetKind();
|
||
|
|
||
|
var buffer = scope String();
|
||
|
SourceHash hash = ?;
|
||
|
if (gApp.LoadTextFile(filePath, buffer, true, scope [&] () => { hash = SourceHash.Create(hashKind, buffer); }) case .Ok)
|
||
|
{
|
||
|
if (hash != mSourceViewPanel.mLoadedHash)
|
||
|
hashMatches = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!hashMatches)
|
||
|
{
|
||
|
if (mBackupFileName == null)
|
||
|
mBackupFileName = new String(filePath);
|
||
|
}
|
||
|
else if (mFoundPath == null)
|
||
|
mFoundPath = new String(filePath);
|
||
|
}
|
||
|
|
||
|
void SearchPath(String dirPath)
|
||
|
{
|
||
|
if (!dirPath.EndsWith('*'))
|
||
|
{
|
||
|
for (var file in Directory.EnumerateFiles(dirPath))
|
||
|
{
|
||
|
var fileName = scope String();
|
||
|
file.GetFileName(fileName);
|
||
|
if (fileName.Equals(mFileName, .OrdinalIgnoreCase))
|
||
|
{
|
||
|
if (mFoundPath == null)
|
||
|
{
|
||
|
var filePath = scope String();
|
||
|
file.GetFilePath(filePath);
|
||
|
CheckFile(filePath);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
String dirSearchStr = scope String();
|
||
|
dirSearchStr.Append(dirPath);
|
||
|
if (!dirSearchStr.EndsWith('*'))
|
||
|
dirSearchStr.Append("/*");
|
||
|
for (var dir in Directory.Enumerate(dirSearchStr, .Directories))
|
||
|
{
|
||
|
var subPath = scope String();
|
||
|
dir.GetFilePath(subPath);
|
||
|
SearchPath(subPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void Run()
|
||
|
{
|
||
|
let dir = scope String();
|
||
|
Path.GetDirectoryPath(mFilePath, dir);
|
||
|
IDEUtils.FixFilePath(dir);
|
||
|
if (!Environment.IsFileSystemCaseSensitive)
|
||
|
dir.ToUpper();
|
||
|
|
||
|
// Check for files relative to manually-specified alias paths
|
||
|
for (var (origDir, localDir) in mRemapMap)
|
||
|
{
|
||
|
if ((mFilePath.Length > origDir.Length) && (dir.StartsWith(origDir)) && (mFilePath[origDir.Length] == Path.DirectorySeparatorChar))
|
||
|
{
|
||
|
let localPath = scope String();
|
||
|
localPath.Append(localDir);
|
||
|
localPath.Append(mFilePath, origDir.Length);
|
||
|
CheckFile(localPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mFoundPath == null)
|
||
|
{
|
||
|
for (let searchPath in gApp.mSettings.mDebuggerSettings.mAutoFindPaths)
|
||
|
{
|
||
|
SearchPath(searchPath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mDoneEvent.Set(true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public enum SourceHash
|
||
|
{
|
||
|
public enum Kind
|
||
|
{
|
||
|
None,
|
||
|
MD5,
|
||
|
SHA256
|
||
|
}
|
||
|
|
||
|
case None;
|
||
|
case MD5(MD5Hash hash);
|
||
|
case SHA256(SHA256Hash hash);
|
||
|
|
||
|
public Kind GetKind()
|
||
|
{
|
||
|
switch (this)
|
||
|
{
|
||
|
case .MD5:
|
||
|
return .MD5;
|
||
|
case .SHA256:
|
||
|
return .SHA256;
|
||
|
default:
|
||
|
return .None;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static SourceHash Create(Kind kind, StringView str)
|
||
|
{
|
||
|
switch (kind)
|
||
|
{
|
||
|
case .MD5:
|
||
|
return .MD5(Security.Cryptography.MD5.Hash(.((uint8*)str.Ptr, str.Length)));
|
||
|
case .SHA256:
|
||
|
return .SHA256(Security.Cryptography.SHA256.Hash(.((uint8*)str.Ptr, str.Length)));
|
||
|
default:
|
||
|
return .None;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static bool operator==(SourceHash lhs, SourceHash rhs)
|
||
|
{
|
||
|
switch (lhs)
|
||
|
{
|
||
|
case .MD5(let lhsMD5):
|
||
|
if (rhs case .MD5(let rhsMD5))
|
||
|
return lhsMD5 == rhsMD5;
|
||
|
case .SHA256(let lhsSHA256):
|
||
|
if (rhs case .SHA256(let rhsSHA256))
|
||
|
return lhsSHA256 == rhsSHA256;
|
||
|
default:
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
class QueuedAutoComplete
|
||
|
{
|
||
|
public char32 mKeyChar;
|
||
|
public SourceEditWidgetContent.AutoCompleteOptions mOptions;
|
||
|
}
|
||
|
|
||
|
struct LinePointerDrawData
|
||
|
{
|
||
|
public Image mImage;
|
||
|
public int32 mLine;
|
||
|
public int32 mUpdateCnt;
|
||
|
public int32 mDebuggerContinueIdx;
|
||
|
}
|
||
|
|
||
|
public class SourceViewPanel : TextPanel
|
||
|
{
|
||
|
enum SourceDisplayId
|
||
|
{
|
||
|
Cleared,
|
||
|
AutoComplete,
|
||
|
SpellCheck,
|
||
|
FullClassify,
|
||
|
SkipResult
|
||
|
}
|
||
|
|
||
|
public bool mAsyncAutocomplete = true;
|
||
|
|
||
|
public SourceEditWidget mEditWidget;
|
||
|
public ProjectSource mProjectSource;
|
||
|
public FileEditData mEditData;
|
||
|
public List<TrackedTextElementView> mTrackedTextElementViewList ~ DeleteContainerAndItems!(_);
|
||
|
public List<BfPassInstance.BfError> mErrorList = new List<BfPassInstance.BfError>() ~ DeleteContainerAndItems!(_);
|
||
|
public List<ResolveParams> mDeferredResolveResults = new .() ~ DeleteContainerAndItems!(_);
|
||
|
public bool mTrackedTextElementViewListDirty;
|
||
|
public String mFilePath ~ delete _;
|
||
|
public bool mIsBinary;
|
||
|
public String mAliasFilePath ~ delete _;
|
||
|
public SourceHash mLoadedHash;
|
||
|
#if IDE_C_SUPPORT
|
||
|
public ProjectSource mClangSource; // For headers, an implementing .cpp file
|
||
|
#endif
|
||
|
public int32 mLastMRUVersion;
|
||
|
public int32 mLastTextVersionId;
|
||
|
public int32 mAutocompleteTextVersionId;
|
||
|
public int32 mClassifiedTextVersionId;
|
||
|
public bool mLoadFailed;
|
||
|
String mOldVerLoadCmd ~ delete _;
|
||
|
IDEApp.ExecutionInstance mOldVerLoadExecutionInstance ~ { if (_ != null) _.mAutoDelete = true; };
|
||
|
SourceFindTask mSourceFindTask ~ delete _;
|
||
|
bool mWantsFastClassify;
|
||
|
bool mWantsFullClassify; // This triggers a classify
|
||
|
bool mWantsFullRefresh; // If mWantsFullClassify is set, mWantsFullRefresh makes the whole thing refresh
|
||
|
bool mRefireMouseOverAfterRefresh;
|
||
|
bool mWantsBackgroundAutocomplete;
|
||
|
QueuedAutoComplete mQueuedAutoComplete ~ delete _;
|
||
|
public bool mWantsSpellCheck;
|
||
|
int32 mTicksSinceTextChanged;
|
||
|
int32 mErrorLookupTextIdx = -1;
|
||
|
LinePointerDrawData mLinePointerDrawData;
|
||
|
#if IDE_C_SUPPORT
|
||
|
public String mClangHoverErrorData ~ delete mClangHoverErrorData;
|
||
|
#endif
|
||
|
|
||
|
public EditWidgetContent.CharData[] mProcessSpellCheckCharData ~ delete _;
|
||
|
public IdSpan mProcessSpellCheckCharIdSpan ~ _.Dispose();
|
||
|
//public int mBackgroundCursorIdx;
|
||
|
|
||
|
String mQueuedLocation ~ delete _;
|
||
|
bool mWantsClassifyAutocomplete;
|
||
|
bool mWantsCurrentLocation;
|
||
|
public bool mIsPerformingBackgroundClassify;
|
||
|
public bool mClassifyPaused = false;
|
||
|
public bool mUseDebugKeyboard;
|
||
|
//public int32 mLastFileTextVersion;
|
||
|
public bool mHasChangedSinceLastCompile;
|
||
|
public bool mIsSourceCode;
|
||
|
public bool mIsBeefSource;
|
||
|
public bool mIsClang;
|
||
|
#if IDE_C_SUPPORT
|
||
|
public bool mClangSourceChanged;
|
||
|
//public ClangCompiler mClangHelper;
|
||
|
bool mDidClangSource;
|
||
|
#endif
|
||
|
public bool mJustShown;
|
||
|
public double mDesiredVertPos;
|
||
|
public float mLockFlashPct;
|
||
|
ResolveType mBackgroundResolveType;
|
||
|
SourceViewPanel mOldVersionPanel;
|
||
|
SourceViewPanel mCurrentVersionPanel;
|
||
|
EditWidgetContent.LineAndColumn? mRequestedLineAndColumn;
|
||
|
|
||
|
SourceViewPanel mSplitTopPanel; // This is only set if we are controlling a top panel
|
||
|
PanelSplitter mSplitter;
|
||
|
SourceViewPanel mSplitBottomPanel; // The primary owning panel is the bottom panel, this only set if we are the bottom panel
|
||
|
|
||
|
int mHotFileIdx = -1;
|
||
|
bool mIsOldCompiledVersion;
|
||
|
static bool sPreviousVersionWarningShown;
|
||
|
PanelHeader mPanelHeader;
|
||
|
NavigationBar mNavigationBar;
|
||
|
public SymbolReferenceHelper mRenameSymbolDialog;
|
||
|
public int32 mBackgroundDelay = 0;
|
||
|
public Monitor mMonitor = new Monitor() ~ delete _;
|
||
|
bool mDidSpellCheck;
|
||
|
int32 mSpellCheckJobCount;
|
||
|
int32 mResolveJobCount;
|
||
|
bool mWantsParserCleanup;
|
||
|
bool mInPostRemoveUpdatePanels;
|
||
|
|
||
|
// For multi-view files...
|
||
|
PersistentTextPosition mTrackCursorPos;
|
||
|
PersistentTextPosition mTrackSelStart;
|
||
|
PersistentTextPosition mTrackSelEnds;
|
||
|
|
||
|
public override float TabWidthOffset
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return 30;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool NeedsPostRemoveUpdate
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return !mDeferredResolveResults.IsEmpty || (mResolveJobCount > 0) || (mSpellCheckJobCount > 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override SourceEditWidget EditWidget
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return mEditWidget;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public CompilerBase ResolveCompiler
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
#if IDE_C_SUPPORT
|
||
|
if (mIsBeefSource)
|
||
|
return IDEApp.sApp.mBfResolveCompiler;
|
||
|
return IDEApp.sApp.mResolveClang;
|
||
|
#else
|
||
|
return IDEApp.sApp.mBfResolveCompiler;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public BfCompiler BfResolveCompiler
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
#if IDE_C_SUPPORT
|
||
|
if (mIsBeefSource)
|
||
|
return IDEApp.sApp.mBfResolveCompiler;
|
||
|
return null;
|
||
|
#else
|
||
|
return IDEApp.sApp.mBfResolveCompiler;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if IDE_C_SUPPORT
|
||
|
public ClangCompiler ClangResolveCompiler
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (mIsClang)
|
||
|
return IDEApp.sApp.mResolveClang;
|
||
|
return null;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
public BfSystem BfResolveSystem
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
#if IDE_C_SUPPORT
|
||
|
if (!mIsClang)
|
||
|
return IDEApp.sApp.mBfResolveSystem;
|
||
|
return null;
|
||
|
#else
|
||
|
return IDEApp.sApp.mBfResolveSystem;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public NavigationBar PrimaryNavigationBar
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (mSplitBottomPanel != null)
|
||
|
return mSplitBottomPanel.mNavigationBar;
|
||
|
return mNavigationBar;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool IsReadOnly
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (gApp.mSettings.mEditorSettings.mLockEditing)
|
||
|
return true;
|
||
|
if ((mProjectSource != null) && (mProjectSource.mProject.mLocked))
|
||
|
return true;
|
||
|
|
||
|
if (gApp.mDebugger.mIsRunning)
|
||
|
{
|
||
|
switch (gApp.mSettings.mEditorSettings.mLockEditingWhenDebugging)
|
||
|
{
|
||
|
case .Always:
|
||
|
return true;
|
||
|
case .Never:
|
||
|
break;
|
||
|
case .WhenNotHotSwappable:
|
||
|
if ((!mIsBeefSource) || (!gApp.mDebugger.mIsRunningCompiled))
|
||
|
{
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ProjectSource FilteredProjectSource
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
if (mProjectSource == null)
|
||
|
return null;
|
||
|
|
||
|
if (!gApp.IsProjectSourceEnabled(mProjectSource))
|
||
|
return null;
|
||
|
|
||
|
return mProjectSource;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public this()
|
||
|
{
|
||
|
DebugManager debugManager = IDEApp.sApp.mDebugger;
|
||
|
debugManager.mBreakpointsChangedDelegate.Add(new => BreakpointsChanged);
|
||
|
|
||
|
mNavigationBar = new NavigationBar(this);
|
||
|
AddWidget(mNavigationBar);
|
||
|
}
|
||
|
|
||
|
public ~this()
|
||
|
{
|
||
|
if (mInPostRemoveUpdatePanels)
|
||
|
{
|
||
|
//Debug.WriteLine("Removing sourceViewPanel from mPostRemoveUpdatePanel {0} in ~this ", this);
|
||
|
gApp.mPostRemoveUpdatePanels.Remove(this);
|
||
|
}
|
||
|
|
||
|
if (!mDisposed)
|
||
|
Dispose();
|
||
|
|
||
|
/*if (mOldVersionPanel != null)
|
||
|
{
|
||
|
Widget.RemoveAndDelete(mOldVersionPanel);
|
||
|
mOldVersionPanel = null;
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
SourceEditWidgetContent Content
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DoAutoComplete(char32 keyChar, SourceEditWidgetContent.AutoCompleteOptions options)
|
||
|
{
|
||
|
var editWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
|
||
|
/*bool isValid = false;
|
||
|
|
||
|
|
||
|
int cursorPos = editWidgetContent.mCursorTextPos;
|
||
|
if (cursorPos > 0)
|
||
|
{
|
||
|
char8 c = editWidgetContent.mData.mText[cursorPos].mChar;
|
||
|
if ((c.IsLetterOrDigit) || (c == '')
|
||
|
}
|
||
|
|
||
|
if (!isValid)
|
||
|
{
|
||
|
Debug.WriteLine("DoAutoComplete ignored");
|
||
|
return;
|
||
|
}*/
|
||
|
|
||
|
|
||
|
if (ResolveCompiler.mThreadWorkerHi.mThreadRunning)
|
||
|
{
|
||
|
//Debug.WriteLine("Deferred DoAutoComplete");
|
||
|
DeleteAndNullify!(mQueuedAutoComplete);
|
||
|
mQueuedAutoComplete = new .();
|
||
|
mQueuedAutoComplete.mKeyChar = keyChar;
|
||
|
mQueuedAutoComplete.mOptions = options;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//Debug.WriteLine("DoAutoComplete");
|
||
|
|
||
|
if (editWidgetContent.mAutoComplete != null)
|
||
|
{
|
||
|
editWidgetContent.mAutoComplete.mInvokeOnly = options.HasFlag(.OnlyShowInvoke);
|
||
|
//Debug.WriteLine("Setting invokeonly {0} {1}", editWidgetContent.mAutoComplete, editWidgetContent.mAutoComplete.mInvokeOnly);
|
||
|
}
|
||
|
|
||
|
//Classify(options.HasFlag(.HighPriority) ? ResolveType.Autocomplete_HighPri : ResolveType.Autocomplete);
|
||
|
|
||
|
ResolveParams resolveParams = new ResolveParams();
|
||
|
resolveParams.mIsUserRequested = options.HasFlag(.UserRequested);
|
||
|
Classify(.Autocomplete, resolveParams);
|
||
|
if (!resolveParams.mInDeferredList)
|
||
|
delete resolveParams;
|
||
|
|
||
|
if (mIsClang)
|
||
|
{
|
||
|
// We classify afterwards so we can attempt to detect any bound functions
|
||
|
mWantsFullClassify = true;
|
||
|
mWantsClassifyAutocomplete = options.HasFlag(.UserRequested);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void CancelResolve(ResolveType resolveType)
|
||
|
{
|
||
|
for (var resolveResults in mDeferredResolveResults)
|
||
|
{
|
||
|
if (resolveResults.mResolveType == resolveType)
|
||
|
{
|
||
|
resolveResults.mCancelled = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void CancelAutocomplete()
|
||
|
{
|
||
|
CancelResolve(.Autocomplete);
|
||
|
DeleteAndNullify!(mQueuedAutoComplete);
|
||
|
}
|
||
|
|
||
|
void SetupEditWidget()
|
||
|
{
|
||
|
if (mEditWidget.mParent == null)
|
||
|
AddWidget(mEditWidget);
|
||
|
|
||
|
mEditWidget.mPanel = this;
|
||
|
var editWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
delete editWidgetContent.mOnGenerateAutocomplete;
|
||
|
editWidgetContent.mOnGenerateAutocomplete = new => DoAutoComplete;
|
||
|
editWidgetContent.mSourceViewPanel = this;
|
||
|
|
||
|
delete editWidgetContent.mOnEscape;
|
||
|
editWidgetContent.mOnEscape = new () => EscapeHandler();
|
||
|
delete editWidgetContent.mOnFinishAsyncAutocomplete;
|
||
|
editWidgetContent.mOnFinishAsyncAutocomplete = new () =>
|
||
|
{
|
||
|
scope AutoBeefPerf("editWidgetContent.mOnFinishAsyncAutocomplete");
|
||
|
|
||
|
ProcessDeferredResolveResults(-1, true);
|
||
|
if (mQueuedAutoComplete != null)
|
||
|
{
|
||
|
DoAutoComplete(mQueuedAutoComplete.mKeyChar, mQueuedAutoComplete.mOptions);
|
||
|
DeleteAndNullify!(mQueuedAutoComplete);
|
||
|
ProcessDeferredResolveResults(-1, true);
|
||
|
}
|
||
|
};
|
||
|
delete editWidgetContent.mOnCancelAsyncAutocomplete;
|
||
|
editWidgetContent.mOnCancelAsyncAutocomplete = new () =>
|
||
|
{
|
||
|
CancelAutocomplete();
|
||
|
};
|
||
|
}
|
||
|
|
||
|
AutoComplete GetAutoComplete()
|
||
|
{
|
||
|
return ((SourceEditWidgetContent)mEditWidget.Content).mAutoComplete;
|
||
|
}
|
||
|
|
||
|
public override void Serialize(StructuredData data)
|
||
|
{
|
||
|
base.Serialize(data);
|
||
|
if (mFilePath == null)
|
||
|
return;
|
||
|
|
||
|
data.Add("Type", "SourceViewPanel");
|
||
|
data.Add("FilePath", mFilePath);
|
||
|
if (mAliasFilePath != null)
|
||
|
data.Add("AliasFilePath", mAliasFilePath);
|
||
|
data.ConditionalAdd("CursorPos", mEditWidget.Content.CursorTextPos, 0);
|
||
|
data.ConditionalAdd("VertPos", mEditWidget.mVertScrollbar.mContentPos, 0);
|
||
|
if (mProjectSource != null)
|
||
|
data.Add("ProjectName", mProjectSource.mProject.mProjectName);
|
||
|
}
|
||
|
|
||
|
public override bool Deserialize(StructuredData data)
|
||
|
{
|
||
|
base.Deserialize(data);
|
||
|
|
||
|
String filePath = scope String();
|
||
|
data.GetString("FilePath", filePath);
|
||
|
String projectName = scope String();
|
||
|
data.GetString("ProjectName", projectName);
|
||
|
|
||
|
var aliasFilePath = scope String();
|
||
|
data.GetString("AliasFilePath", aliasFilePath);
|
||
|
if (!aliasFilePath.IsEmpty)
|
||
|
mAliasFilePath = new String(aliasFilePath);
|
||
|
|
||
|
bool foundProjectSource = false;
|
||
|
if (projectName != null)
|
||
|
{
|
||
|
var project = IDEApp.sApp.FindProjectByName(projectName);
|
||
|
if (project != null)
|
||
|
{
|
||
|
String relPath = scope String();
|
||
|
project.GetProjectRelPath(filePath, relPath);
|
||
|
var projectItem = IDEApp.sApp.FindProjectItem(project.mRootFolder, relPath);
|
||
|
if (projectItem != null)
|
||
|
{
|
||
|
if (!Show(projectItem, true))
|
||
|
return false;
|
||
|
foundProjectSource = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!foundProjectSource)
|
||
|
{
|
||
|
if (!Show(filePath, true))
|
||
|
return false;
|
||
|
}
|
||
|
int32 cursorPos = data.GetInt("CursorPos");
|
||
|
mEditWidget.Content.mCursorTextPos = Math.Min(cursorPos, mEditWidget.mEditWidgetContent.mData.mTextLength);
|
||
|
mDesiredVertPos = data.GetFloat("VertPos");
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public void QueueFullRefresh(bool configMayHaveChanged)
|
||
|
{
|
||
|
#if IDE_C_SUPPORT
|
||
|
if ((mIsClang) && (configMayHaveChanged))
|
||
|
{
|
||
|
// Force file to be recreated with potentially new Clang settings
|
||
|
using (mMonitor.Enter())
|
||
|
{
|
||
|
var clangCompiler = ClangResolveCompiler;
|
||
|
clangCompiler.QueueFileRemoved(mFilePath);
|
||
|
if (mClangSource != null)
|
||
|
mClangSource = null;
|
||
|
mDidClangSource = false;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ((configMayHaveChanged) && (mSplitTopPanel != null))
|
||
|
mSplitTopPanel.QueueFullRefresh(configMayHaveChanged);
|
||
|
|
||
|
mWantsFullClassify = true;
|
||
|
mWantsFullRefresh = true;
|
||
|
|
||
|
// We do this every time we autocomplete, so we don't want to set mDidClangSource to false here. Why did we have that?
|
||
|
//if (mIsClang)
|
||
|
//mDidClangSource = false;
|
||
|
}
|
||
|
|
||
|
public override bool EscapeHandler()
|
||
|
{
|
||
|
if (IDEApp.sApp.mSymbolReferenceHelper != null)
|
||
|
{
|
||
|
IDEApp.sApp.mSymbolReferenceHelper.Cancel();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
if (base.EscapeHandler())
|
||
|
return true;
|
||
|
|
||
|
if (HasFocus(true))
|
||
|
{
|
||
|
if ((mSplitTopPanel != null) && (mSplitTopPanel.EscapeHandler()))
|
||
|
return true;
|
||
|
if ((mSplitBottomPanel != null) && (mSplitBottomPanel.EscapeHandler()))
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// Remove F4-selection from output panels
|
||
|
IDEApp.sApp.mOutputPanel.EscapeHandler();
|
||
|
IDEApp.sApp.mFindResultsPanel.EscapeHandler();
|
||
|
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public bool IsControllingEditData()
|
||
|
{
|
||
|
// We are controlling if we have focus or, if none have focus, if we're the most recently added
|
||
|
var users = Content.mData.mUsers;
|
||
|
for (var user in users)
|
||
|
{
|
||
|
if (user.mEditWidget.mHasFocus)
|
||
|
return mEditWidget == user.mEditWidget;
|
||
|
}
|
||
|
return mEditWidget == users[users.Count - 1].mEditWidget;
|
||
|
}
|
||
|
|
||
|
public bool HasUnsavedChanges()
|
||
|
{
|
||
|
//return mLastFileTextVersion != mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
if (mFilePath == null)
|
||
|
return true;
|
||
|
if (mEditData == null)
|
||
|
return false;
|
||
|
if (mEditData.mFileDeleted)
|
||
|
return true;
|
||
|
return mEditData.mLastFileTextVersion != mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
}
|
||
|
|
||
|
public bool IsLastViewOfData()
|
||
|
{
|
||
|
int32 expectCount = 1;
|
||
|
if (mSplitTopPanel != null)
|
||
|
expectCount = 2;
|
||
|
return Content.Data.mUsers.Count == expectCount;
|
||
|
}
|
||
|
|
||
|
public bool HasTextChangedSinceCompile(int defLineStart, int defLineEnd, int compileInstanceIdx)
|
||
|
{
|
||
|
/*if ((mProjectSource != null) && (!mProjectSource.mHasChangedSinceLastSuccessfulCompile))
|
||
|
return false;*/
|
||
|
|
||
|
if ((compileInstanceIdx == -1) && (!mHasChangedSinceLastCompile))
|
||
|
return false;
|
||
|
|
||
|
let projectSource = FilteredProjectSource;
|
||
|
if (projectSource == null)
|
||
|
return false;
|
||
|
|
||
|
int32 char8IdStart;
|
||
|
int32 char8IdEnd;
|
||
|
IdSpan char8IdData = IDEApp.sApp.mWorkspace.GetProjectSourceSelectionCharIds(projectSource, compileInstanceIdx, defLineStart, defLineEnd, out char8IdStart, out char8IdEnd);
|
||
|
if (char8IdData.IsEmpty)
|
||
|
return false;
|
||
|
|
||
|
bool doDebug = false;
|
||
|
if (doDebug)
|
||
|
{
|
||
|
char8IdData.Dump();
|
||
|
mEditWidget.mEditWidgetContent.mData.mTextIdData.Dump();
|
||
|
}
|
||
|
|
||
|
|
||
|
return !char8IdData.IsRangeEqual(mEditWidget.mEditWidgetContent.mData.mTextIdData.GetPrepared(), char8IdStart, char8IdEnd);
|
||
|
}
|
||
|
|
||
|
public void FileSaved()
|
||
|
{
|
||
|
ClearLoadFailed();
|
||
|
#if IDE_C_SUPPORT
|
||
|
mClangSourceChanged = false;
|
||
|
#endif
|
||
|
//mLastFileTextVersion = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
if (mEditData != null)
|
||
|
{
|
||
|
mEditData.mLastFileTextVersion = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
mEditData.mHadRefusedFileChange = false;
|
||
|
}
|
||
|
if (mProjectSource != null)
|
||
|
{
|
||
|
//mProjectSource.ClearUnsavedData();
|
||
|
var editText = scope String();
|
||
|
mEditWidget.GetText(editText);
|
||
|
using (gApp.mMonitor.Enter())
|
||
|
{
|
||
|
mEditData.SetSavedData(editText, mEditWidget.mEditWidgetContent.mData.mTextIdData.GetPrepared());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
gApp.FileChanged(mFilePath);
|
||
|
}
|
||
|
|
||
|
void ClassifyThreadDone()
|
||
|
{
|
||
|
/*if (mProcessingPassInstance != null)
|
||
|
{
|
||
|
//Debug.WriteLine("Pass Instance {0} Done. Dispose? {1}", mProcessingPassInstance.mId, mWidgetWindow == null);
|
||
|
|
||
|
// If we've closed the window by the time our pass instance is completed then just drop the results
|
||
|
if (mWidgetWindow == null)
|
||
|
{
|
||
|
delete mProcessingPassInstance;
|
||
|
mProcessingPassInstance = null;
|
||
|
}
|
||
|
}*/
|
||
|
mIsPerformingBackgroundClassify = false;
|
||
|
mResolveJobCount--;
|
||
|
}
|
||
|
|
||
|
void BackgroundResolve(ThreadStart threadStart)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
public bool Classify(ResolveType resolveType, ResolveParams resolveParams = null)
|
||
|
{
|
||
|
if (resolveType == .Autocomplete)
|
||
|
{
|
||
|
NOP!();
|
||
|
}
|
||
|
|
||
|
// Don't allow other classify calls interrupt a symbol rename
|
||
|
if ((IDEApp.sApp.mSymbolReferenceHelper != null) && (IDEApp.sApp.mSymbolReferenceHelper.HasStarted) && (IDEApp.sApp.mSymbolReferenceHelper.mKind == SymbolReferenceHelper.Kind.Rename))
|
||
|
return false;
|
||
|
|
||
|
//Debug.WriteLine("Classify({0})", resolveType);
|
||
|
|
||
|
if (!mIsSourceCode)
|
||
|
return true;
|
||
|
|
||
|
if (resolveParams != null)
|
||
|
resolveParams.mResolveType = resolveType;
|
||
|
|
||
|
/*if (mWidgetWindow.IsKeyDown(.Control))
|
||
|
{
|
||
|
NOP!();
|
||
|
}*/
|
||
|
//Debug.WriteLine("Classify {0} {1}", this, resolveType);
|
||
|
|
||
|
scope AutoBeefPerf("SourceViewPanel.Classify");
|
||
|
|
||
|
/*if ((mIsClang) && (!mDidClangSource))
|
||
|
{
|
||
|
mDidClangSource = true;
|
||
|
var clangCompiler = ClangResolveCompiler;
|
||
|
if (mProjectSource != null)
|
||
|
{
|
||
|
clangCompiler.QueueProjectSource(mProjectSource);
|
||
|
// Return because now we need to wait for this to finish
|
||
|
return false;
|
||
|
}
|
||
|
}*/
|
||
|
|
||
|
var useResolveType = resolveType;
|
||
|
var compiler = ResolveCompiler;
|
||
|
var bfCompiler = BfResolveCompiler;
|
||
|
BfSystem bfSystem = null;
|
||
|
if (bfCompiler != null)
|
||
|
bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
/*if ((!mIsBeefSource) || (!bfSystem.HasParser(mFilePath)))
|
||
|
{
|
||
|
//DoFastClassify();
|
||
|
//return;
|
||
|
}*/
|
||
|
|
||
|
if (bfSystem == null)
|
||
|
return false;
|
||
|
|
||
|
if (bfSystem != null)
|
||
|
bfSystem.PerfZoneStart("Classify");
|
||
|
|
||
|
let projectSource = FilteredProjectSource;
|
||
|
|
||
|
//Debug.WriteLine("Classify {0}", doAutocomplete);
|
||
|
//bfSystem.PerfZoneStart("CancelBackground");
|
||
|
|
||
|
bool hasValidProjectSource = false;
|
||
|
if (projectSource != null)
|
||
|
{
|
||
|
if (mIsBeefSource)
|
||
|
{
|
||
|
var bfProject = bfSystem.GetBfProject(projectSource.mProject);
|
||
|
if (!bfProject.mDisabled)
|
||
|
hasValidProjectSource = true;
|
||
|
}
|
||
|
else
|
||
|
hasValidProjectSource = true;
|
||
|
}
|
||
|
|
||
|
mClassifyPaused = false;
|
||
|
|
||
|
if (mIsClang)
|
||
|
{
|
||
|
if (resolveType == .Autocomplete)
|
||
|
return true; // Don't do anything
|
||
|
|
||
|
// Clang always just does a 'fast classify'
|
||
|
DoFastClassify();
|
||
|
}
|
||
|
|
||
|
if (useResolveType == ResolveType.Autocomplete)
|
||
|
mAutocompleteTextVersionId = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
|
||
|
bool wasHighPri = false;
|
||
|
if (useResolveType == ResolveType.Autocomplete_HighPri)
|
||
|
{
|
||
|
wasHighPri = true;
|
||
|
if (mIsClang)
|
||
|
{
|
||
|
compiler.CancelBackground();
|
||
|
//delete mQueuedAutocompleteInfo;
|
||
|
//mQueuedAutocompleteInfo = null;
|
||
|
mWantsBackgroundAutocomplete = false;
|
||
|
}
|
||
|
useResolveType = ResolveType.Autocomplete;
|
||
|
}
|
||
|
|
||
|
bool doBackground = (useResolveType == ResolveType.Classify) || (useResolveType == ResolveType.ClassifyFullRefresh);
|
||
|
if (mAsyncAutocomplete)
|
||
|
{
|
||
|
if ((useResolveType == .Autocomplete) || (useResolveType == .GetCurrentLocation) || (useResolveType == .GetSymbolInfo))
|
||
|
doBackground = true;
|
||
|
}
|
||
|
|
||
|
if (mIsClang)
|
||
|
{
|
||
|
#if !IDE_C_SUPPORT
|
||
|
return true;
|
||
|
#else
|
||
|
if (useResolveType == ResolveType.GetCurrentLocation)
|
||
|
doBackground = (useResolveType == ResolveType.GetCurrentLocation);
|
||
|
|
||
|
if (useResolveType == ResolveType.Autocomplete)
|
||
|
{
|
||
|
mWantsClassifyAutocomplete = GetAutoComplete() != null;
|
||
|
var autocomplete = GetAutoComplete();
|
||
|
if (autocomplete != null)
|
||
|
autocomplete.UpdateAsyncInfo();
|
||
|
|
||
|
if ((compiler.IsPerformingBackgroundOperation()) || (mQueuedAutocompleteInfo != null))
|
||
|
{
|
||
|
if ((mBackgroundResolveType == ResolveType.Autocomplete) && (!mIgnorePendingAsyncAutocomplete))
|
||
|
{
|
||
|
// We already have a relevant pending autocomplete
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
Debug.Assert(!wasHighPri);
|
||
|
mWantsBackgroundAutocomplete = true;
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
mIgnorePendingAsyncAutocomplete = false;
|
||
|
doBackground = true;
|
||
|
}
|
||
|
else if (useResolveType == ResolveType.GetNavigationData)
|
||
|
{
|
||
|
#if IDE_C_SUPPORT
|
||
|
DoClangClassify(useResolveType, resolveParams);
|
||
|
doBackground = false;
|
||
|
#else
|
||
|
return false;
|
||
|
#endif
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (doBackground)
|
||
|
{
|
||
|
ProcessDeferredResolveResults(0);
|
||
|
|
||
|
var resolveParams;
|
||
|
if (resolveParams == null)
|
||
|
{
|
||
|
resolveParams = new ResolveParams();
|
||
|
}
|
||
|
|
||
|
resolveParams.mResolveType = resolveType;
|
||
|
resolveParams.mWaitEvent = new WaitEvent();
|
||
|
resolveParams.mInDeferredList = true;
|
||
|
mDeferredResolveResults.Add(resolveParams);
|
||
|
|
||
|
var autoComplete = GetAutoComplete();
|
||
|
if ((autoComplete != null) && (autoComplete.mIsDocumentationPass))
|
||
|
{
|
||
|
let ewc = (SourceEditWidgetContent)mEditWidget.mEditWidgetContent;
|
||
|
Debug.Assert(ewc.mAutoComplete != null);
|
||
|
let selectedEntry = ewc.mAutoComplete.mAutoCompleteListWidget.mEntryList[ewc.mAutoComplete.mAutoCompleteListWidget.mSelectIdx];
|
||
|
resolveParams.mDocumentationName = new String(selectedEntry.mEntryDisplay);
|
||
|
}
|
||
|
resolveParams.mTextVersion = Content.mData.mCurTextVersionId;
|
||
|
|
||
|
compiler.CheckThreadDone(); // Process any pending thread done callbacks
|
||
|
|
||
|
bool isHi = (resolveType != .ClassifyFullRefresh) && (resolveType != .Classify);
|
||
|
if (isHi)
|
||
|
Debug.Assert(!bfCompiler.mThreadWorkerHi.mThreadRunning);
|
||
|
else
|
||
|
Debug.Assert(!bfCompiler.mThreadWorker.mThreadRunning);
|
||
|
|
||
|
if (bfSystem != null)
|
||
|
bfSystem.PerfZoneStart("DoBackground");
|
||
|
//ProcessResolveData();
|
||
|
|
||
|
//Debug.Assert(mProcessResolveCharData == null);
|
||
|
DuplicateEditState(out resolveParams.mCharData, out resolveParams.mCharIdSpan);
|
||
|
//Debug.WriteLine("Edit State: {0}", mProcessResolveCharData);
|
||
|
|
||
|
if ((useResolveType == .Autocomplete) || (useResolveType == .GetSymbolInfo) || (mIsClang))
|
||
|
{
|
||
|
resolveParams.mOverrideCursorPos = (.)mEditWidget.Content.CursorTextPos;
|
||
|
/*if (useResolveType == .Autocomplete)
|
||
|
resolveParams.mOverrideCursorPos--;*/
|
||
|
}
|
||
|
|
||
|
//Debug.Assert(mCurParser == null);
|
||
|
|
||
|
if ((mIsBeefSource) && (hasValidProjectSource) && (!isHi))
|
||
|
resolveParams.mParser = bfSystem.FindParser(projectSource);
|
||
|
//if (mCurParser != null)
|
||
|
{
|
||
|
if (!isHi)
|
||
|
Debug.Assert(!mIsPerformingBackgroundClassify);
|
||
|
|
||
|
mBackgroundResolveType = useResolveType;
|
||
|
mIsPerformingBackgroundClassify = true;
|
||
|
mResolveJobCount++;
|
||
|
if (useResolveType == .Autocomplete)
|
||
|
compiler.DoBackgroundHi(new () => { DoClassify(.Autocomplete, resolveParams, true); }, new => ClassifyThreadDone);
|
||
|
//BackgroundResolve(new () => { DoClassify(.Autocomplete, resolveParams); });
|
||
|
else if (useResolveType == .ClassifyFullRefresh)
|
||
|
compiler.DoBackground(new () => { DoClassify(.ClassifyFullRefresh, resolveParams, false); },
|
||
|
new () =>
|
||
|
{
|
||
|
ClassifyThreadDone();
|
||
|
});
|
||
|
else if (useResolveType == .GetCurrentLocation)
|
||
|
compiler.DoBackgroundHi(new () => { DoClassify(.GetCurrentLocation, resolveParams, true); }, new => ClassifyThreadDone);
|
||
|
else if (useResolveType == .GetSymbolInfo)
|
||
|
compiler.DoBackgroundHi(new () => { DoClassify(.GetSymbolInfo, resolveParams, true); }, new => ClassifyThreadDone);
|
||
|
else
|
||
|
compiler.DoBackgroundHi(new () => { DoFullClassify(resolveParams); }, new => ClassifyThreadDone);
|
||
|
}
|
||
|
/*else
|
||
|
{
|
||
|
// Not part of project
|
||
|
DoFastClassify();
|
||
|
}*/
|
||
|
|
||
|
if (bfSystem != null)
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
}
|
||
|
else if ((mIsBeefSource) && (hasValidProjectSource))
|
||
|
{
|
||
|
/*if (useResolveType == ResolveType.Autocomplete)
|
||
|
{
|
||
|
Profiler.StartSampling();
|
||
|
DoClassify(useResolveType, resolveParams);
|
||
|
Profiler.StopSampling();
|
||
|
}
|
||
|
else*/
|
||
|
DoClassify(useResolveType, resolveParams, true);
|
||
|
MarkDirty();
|
||
|
}
|
||
|
|
||
|
if (bfSystem != null)
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public void DoParserCleanup()
|
||
|
{
|
||
|
var bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
bfSystem.Lock(0);
|
||
|
//bfSystem.RemoveDeletedParsers();
|
||
|
bfSystem.RemoveOldData();
|
||
|
bfSystem.Unlock();
|
||
|
}
|
||
|
|
||
|
public void DoFullClassify(ResolveParams resolveParams)
|
||
|
{
|
||
|
var bfCompiler = BfResolveCompiler;
|
||
|
//var bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
//bfCompiler.StartTiming();
|
||
|
DoClassify(ResolveType.Classify, resolveParams);
|
||
|
//bfCompiler.StopTiming();
|
||
|
if (bfCompiler != null)
|
||
|
bfCompiler.QueueDeferredResolveAll();
|
||
|
}
|
||
|
|
||
|
//[StdCall, CLink]
|
||
|
//static extern char8* BfDiff_DiffText(char8* text1, char8* text2);
|
||
|
|
||
|
public void DoFastClassify()
|
||
|
{
|
||
|
if (!mIsSourceCode)
|
||
|
return;
|
||
|
|
||
|
//Debug.WriteLine("DoFastClassify");
|
||
|
|
||
|
scope AutoBeefPerf("SourceViewPanel.DoFastClassify");
|
||
|
|
||
|
let projectSource = FilteredProjectSource;
|
||
|
|
||
|
var bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
if (bfSystem == null)
|
||
|
return;
|
||
|
//var compiler = ResolveCompiler;
|
||
|
|
||
|
var char8Data = mEditWidget.Content.mData.mText;
|
||
|
int char8Len = Math.Min(char8Data.Count, mEditWidget.Content.mData.mTextLength);
|
||
|
|
||
|
char8[] chars = new char8[char8Len];
|
||
|
defer delete chars;
|
||
|
for (int32 i = 0; i < char8Len; i++)
|
||
|
chars[i] = (char8)char8Data[i].mChar;
|
||
|
|
||
|
String text = scope String();
|
||
|
text.Append(chars, 0, chars.Count);
|
||
|
|
||
|
BfProject bfProject = null;
|
||
|
if ((projectSource != null) && (mIsBeefSource))
|
||
|
{
|
||
|
bfProject = bfSystem.GetBfProject(projectSource.mProject);
|
||
|
}
|
||
|
var parser = bfSystem.CreateEmptyParser(bfProject);
|
||
|
defer delete parser;
|
||
|
var resolvePassData = parser.CreateResolvePassData();
|
||
|
defer delete resolvePassData;
|
||
|
var passInstance = bfSystem.CreatePassInstance("DoFastClassify");
|
||
|
defer delete passInstance;
|
||
|
parser.SetSource(text, mFilePath);
|
||
|
parser.SetIsClassifying();
|
||
|
parser.Parse(passInstance, !mIsBeefSource);
|
||
|
if (mIsBeefSource)
|
||
|
parser.Reduce(passInstance);
|
||
|
parser.ClassifySource(char8Data, !mIsBeefSource);
|
||
|
mWantsParserCleanup = true;
|
||
|
}
|
||
|
|
||
|
public void DoFastClassify(int start, int length)
|
||
|
{
|
||
|
if (!mIsSourceCode)
|
||
|
return;
|
||
|
|
||
|
var bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
if (bfSystem == null)
|
||
|
return;
|
||
|
//var compiler = ResolveCompiler;
|
||
|
|
||
|
//var char8Data = mEditWidget.Content.mData.mText;
|
||
|
|
||
|
let projectSource = FilteredProjectSource;
|
||
|
|
||
|
int32 char8Len = mEditWidget.Content.mData.mTextLength;
|
||
|
var char8Data = new EditWidgetContent.CharData[char8Len];
|
||
|
defer delete char8Data;
|
||
|
var curCharData = mEditWidget.Content.mData.mText;
|
||
|
|
||
|
char8[] chars = new char8[char8Len];
|
||
|
defer delete chars;
|
||
|
for (int32 i = 0; i < char8Len; i++)
|
||
|
{
|
||
|
char8Data[i] = curCharData[i];
|
||
|
chars[i] = (char8)char8Data[i].mChar;
|
||
|
}
|
||
|
|
||
|
String text = scope String(chars, 0, chars.Count);
|
||
|
|
||
|
BfProject bfProject = null;
|
||
|
if ((projectSource != null) && (mIsBeefSource))
|
||
|
{
|
||
|
bfProject = bfSystem.GetBfProject(projectSource.mProject);
|
||
|
}
|
||
|
var parser = bfSystem.CreateEmptyParser(bfProject);
|
||
|
defer delete parser;
|
||
|
var resolvePassData = parser.CreateResolvePassData();
|
||
|
defer delete resolvePassData;
|
||
|
var passInstance = bfSystem.CreatePassInstance("DoFastClassify");
|
||
|
defer delete passInstance;
|
||
|
parser.SetSource(text, mFilePath);
|
||
|
parser.SetIsClassifying();
|
||
|
parser.Parse(passInstance, !mIsBeefSource);
|
||
|
if (mIsBeefSource)
|
||
|
parser.Reduce(passInstance);
|
||
|
parser.ClassifySource(char8Data, !mIsBeefSource);
|
||
|
mWantsParserCleanup = true;
|
||
|
|
||
|
for (int i = start; i < start + length; i++)
|
||
|
{
|
||
|
curCharData[i] = char8Data[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void HandleAutocompleteInfo(ResolveType resolveType, String autocompleteInfo, bool clearList, bool changedAfterInfo)
|
||
|
{
|
||
|
var editWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
|
||
|
bool wantOpen = (autocompleteInfo.Length > 0);
|
||
|
if ((editWidgetContent.mAutoComplete != null) && (editWidgetContent.mAutoComplete.mIsAsync) &&
|
||
|
(editWidgetContent.mAutoComplete.mInvokeWidget != null))
|
||
|
{
|
||
|
// Leave the existing invoke widget open
|
||
|
wantOpen = true;
|
||
|
}
|
||
|
|
||
|
if (wantOpen)
|
||
|
{
|
||
|
if (editWidgetContent.mAutoComplete == null)
|
||
|
{
|
||
|
editWidgetContent.mAutoComplete = new AutoComplete(mEditWidget);
|
||
|
//if (mIsClang)
|
||
|
//editWidgetContent.mAutoComplete.mIsAsync = true;
|
||
|
editWidgetContent.mAutoComplete.mOnAutoCompleteInserted.Add(new () => { Classify(resolveType); });
|
||
|
editWidgetContent.mAutoComplete.mOnClosed.Add(new () =>
|
||
|
{
|
||
|
//Debug.WriteLine("Autocomplete Closed");
|
||
|
if (editWidgetContent.mAutoComplete != null)
|
||
|
{
|
||
|
editWidgetContent.mAutoComplete = null;
|
||
|
}
|
||
|
// Any pending autocomplete is no longer valid
|
||
|
CancelAutocomplete();
|
||
|
});
|
||
|
}
|
||
|
if (editWidgetContent.mAutoComplete.mIsDocumentationPass)
|
||
|
editWidgetContent.mAutoComplete.UpdateInfo(autocompleteInfo);
|
||
|
else
|
||
|
editWidgetContent.mAutoComplete.SetInfo(autocompleteInfo, clearList, 0, changedAfterInfo);
|
||
|
}
|
||
|
else if ((editWidgetContent.mAutoComplete != null) && (!editWidgetContent.mAutoComplete.mIsDocumentationPass))
|
||
|
editWidgetContent.mAutoComplete.Close();
|
||
|
}
|
||
|
|
||
|
#if IDE_C_SUPPORT
|
||
|
public void DoClangClassify(ResolveType resolveType, ResolveParams resolveParams = null)
|
||
|
{
|
||
|
var buildClang = IDEApp.sApp.mDepClang;
|
||
|
var resolveClang = (ClangCompiler)ResolveCompiler;
|
||
|
|
||
|
bool isBackground = (resolveType == ResolveType.Classify) || (resolveType == ResolveType.ClassifyFullRefresh) || (resolveType == ResolveType.Autocomplete) ||
|
||
|
(resolveType == ResolveType.Autocomplete_HighPri) || (resolveType == ResolveType.GetCurrentLocation);
|
||
|
|
||
|
if (!isBackground)
|
||
|
{
|
||
|
resolveClang.CancelBackground();
|
||
|
}
|
||
|
|
||
|
if (resolveType == ResolveType.GetCurrentLocation)
|
||
|
{
|
||
|
NOP!();
|
||
|
}
|
||
|
|
||
|
if (isBackground)
|
||
|
Debug.Assert(mIsPerformingBackgroundClassify);
|
||
|
|
||
|
var char8Data = (!isBackground) ? mEditWidget.Content.mData.mText : mProcessResolveCharData;
|
||
|
Debug.Assert(char8Data != null);
|
||
|
int char8Len = 0;
|
||
|
if (char8Data != null)
|
||
|
char8Len = (!isBackground) ? mEditWidget.Content.mData.mTextLength : mProcessResolveCharData.Length;
|
||
|
mBackgroundCursorIdx = (int32)Math.Min(mBackgroundCursorIdx, char8Len);
|
||
|
|
||
|
bool didClangSource = false;
|
||
|
using (mMonitor.Enter())
|
||
|
{
|
||
|
didClangSource = mDidClangSource;
|
||
|
mDidClangSource = true;
|
||
|
}
|
||
|
|
||
|
//bool needsNewClangSource = IDEUtils.IsHeaderFile(mFilePath) && (mClangSource == null);
|
||
|
|
||
|
//if ((!didClangSource) || (needsNewClangSource))
|
||
|
if (!didClangSource)
|
||
|
{
|
||
|
bool handled = false;
|
||
|
|
||
|
//if (needsNewClangSource)
|
||
|
if (IDEUtils.IsHeaderFile(mFilePath))
|
||
|
{
|
||
|
// Wait for buildClang to stop
|
||
|
buildClang.CancelBackground();
|
||
|
// Process the buildClang commands in this thread (if needed)
|
||
|
buildClang.ProcessQueue();
|
||
|
|
||
|
ProjectSource projectSource = null;
|
||
|
|
||
|
String findStr = scope String("/");
|
||
|
Path.GetFileNameWithoutExtension(mFilePath, findStr);
|
||
|
findStr.Append(".");
|
||
|
|
||
|
String findFilePath = scope String(mFilePath);
|
||
|
if (!Utils.IsFileSystemCaseSensitive)
|
||
|
findFilePath.ToUpper;
|
||
|
|
||
|
//foreach (var checkProjectSource in resolveClang.mSourceMRUList)
|
||
|
for (int mruIdx = resolveClang.mSourceMRUList.Count - 1; mruIdx >= 0; mruIdx--)
|
||
|
{
|
||
|
var checkProjectSource = resolveClang.mSourceMRUList[mruIdx];
|
||
|
ClangCompiler.FileEntry fileEntry = null;
|
||
|
buildClang.mProjectFileSet.TryGetValue(checkProjectSource, out fileEntry);
|
||
|
if (fileEntry != null)
|
||
|
{
|
||
|
if (fileEntry.mCDepFileRefs.Contains(findFilePath))
|
||
|
{
|
||
|
projectSource = checkProjectSource;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// First try to find a file with the same base name, then try to
|
||
|
for (int32 pass = 0; pass < 2; pass++)
|
||
|
{
|
||
|
if (projectSource != null)
|
||
|
break;
|
||
|
|
||
|
for (var projectPair in buildClang.mProjectFileSet)
|
||
|
{
|
||
|
var checkProjectSource = projectPair.Key;
|
||
|
var fileData = projectPair.Value;
|
||
|
if ((fileData.mCDepFileRefs != null) &&
|
||
|
(((pass == 1) || (checkProjectSource.mPath.IndexOf(findStr, true) != -1))))
|
||
|
{
|
||
|
if (fileData.mCDepFileRefs.Contains(findFilePath))
|
||
|
projectSource = checkProjectSource;
|
||
|
}
|
||
|
//projectPair.Value.mCDepFileRefs.Contains();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
String headerPrefix = scope String();
|
||
|
if (projectSource != null)
|
||
|
{
|
||
|
mClangSource = projectSource;
|
||
|
IdSpan idSpan;
|
||
|
String fileContent = scope String();
|
||
|
IDEApp.sApp.FindProjectSourceContent(mClangSource, out idSpan, true, fileContent);
|
||
|
idSpan.Dispose();
|
||
|
|
||
|
String clangArgs = scope String();
|
||
|
resolveClang.GetClangArgs(mClangSource, clangArgs);
|
||
|
String fullImportPath = scope String();
|
||
|
mClangSource.GetFullImportPath(fullImportPath);
|
||
|
int32 includePos = resolveClang.CDepGetIncludePosition(fullImportPath, fileContent, mFilePath, clangArgs);
|
||
|
if (includePos != -1)
|
||
|
fileContent.Substring(0, includePos, headerPrefix);
|
||
|
resolveClang.AddTranslationUnit(mFilePath, headerPrefix, clangArgs);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
resolveClang.AddTranslationUnit(mFilePath, "", "");
|
||
|
}
|
||
|
|
||
|
handled = true;
|
||
|
}
|
||
|
|
||
|
if ((!handled) && (!didClangSource))
|
||
|
{
|
||
|
Debug.Assert(char8Data != null);
|
||
|
if (mProjectSource != null)
|
||
|
{
|
||
|
resolveClang.AddTranslationUnit(mProjectSource, null, char8Data, char8Len);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (resolveType == ResolveType.GetNavigationData)
|
||
|
{
|
||
|
resolveClang.CancelBackground();
|
||
|
resolveParams.mNavigationData = new String();
|
||
|
resolveClang.GetNavigationData(mFilePath, resolveParams.mNavigationData);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (resolveType == ResolveType.GetCurrentLocation)
|
||
|
{
|
||
|
String result = new String();
|
||
|
resolveClang.GetCurrentLocation(mFilePath, result, mBackgroundCursorIdx);
|
||
|
delete mQueuedLocation;
|
||
|
mQueuedLocation = result;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//string fileName;
|
||
|
//string headerFileName = null;
|
||
|
if (resolveType == ResolveType.Autocomplete)
|
||
|
{
|
||
|
Debug.Assert(mQueuedAutocompleteInfo == null);
|
||
|
mQueuedAutocompleteInfo = new String();
|
||
|
resolveClang.Autocomplete(mFilePath, char8Data, char8Len, mBackgroundCursorIdx, mQueuedAutocompleteInfo);
|
||
|
if ((mBackgroundCursorIdx != -1) && (mQueuedAutocompleteInfo == null))
|
||
|
mQueuedAutocompleteInfo = "";
|
||
|
if (mIgnorePendingAsyncAutocomplete)
|
||
|
{
|
||
|
delete mQueuedAutocompleteInfo;
|
||
|
mQueuedAutocompleteInfo = null;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bool ignoreErrors = (mProjectSource == null) && (mClangSource == null);
|
||
|
/*if (IDEUtils.IsHeaderFile(mFilePath))
|
||
|
ignoreErrors = mClangSource == null;
|
||
|
else
|
||
|
ignoreErrors = mProjectSource == null;*/
|
||
|
|
||
|
String results = scope String();
|
||
|
resolveClang.Classify(mFilePath, char8Data, char8Len, mBackgroundCursorIdx, mErrorLookupTextIdx, ignoreErrors, results);
|
||
|
if (results != null)
|
||
|
{
|
||
|
String autocompleteResult = scope String();
|
||
|
for (var resultLine in String.StackSplit!(results, '\n'))
|
||
|
{
|
||
|
var resultLineSplit = scope List<StringView>();
|
||
|
resultLine.Split(resultLineSplit, scope char8[] {'\t'}, 2);
|
||
|
if (scope String(resultLineSplit[0]) == "diag")
|
||
|
{
|
||
|
delete mClangHoverErrorData;
|
||
|
mClangHoverErrorData = new String(resultLineSplit[1]);
|
||
|
}
|
||
|
else if (resultLine != "")
|
||
|
{
|
||
|
autocompleteResult.Append(resultLine, "\n");
|
||
|
}
|
||
|
}
|
||
|
if ((mWantsClassifyAutocomplete) && (autocompleteResult.Length > 0))
|
||
|
{
|
||
|
Debug.Assert(mQueuedClassifyInfo == null);
|
||
|
mQueuedClassifyInfo = new String();
|
||
|
autocompleteResult.MoveTo(mQueuedClassifyInfo);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
mErrorLookupTextIdx = -1;
|
||
|
}
|
||
|
|
||
|
var bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
bfSystem.RemoveDeletedParsers();
|
||
|
|
||
|
if (isBackground)
|
||
|
Debug.Assert(mIsPerformingBackgroundClassify);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
void HandleResolveResult(ResolveType resolveType, String autocompleteInfo, ResolveParams resolveParams)
|
||
|
{
|
||
|
if (resolveType == ResolveType.GetSymbolInfo)
|
||
|
{
|
||
|
if (!resolveParams.mCancelled)
|
||
|
gApp.mSymbolReferenceHelper.SetSymbolInfo(autocompleteInfo);
|
||
|
}
|
||
|
else if (resolveType == ResolveType.GoToDefinition)
|
||
|
{
|
||
|
var autocompleteLines = String.StackSplit!(autocompleteInfo, '\n');
|
||
|
for (var autocompleteLine in autocompleteLines)
|
||
|
{
|
||
|
var lineData = String.StackSplit!(autocompleteLine, '\t');
|
||
|
if (scope String(lineData[0]) == "defLoc")
|
||
|
{
|
||
|
resolveParams.mOutLine = int32.Parse(lineData[2]);
|
||
|
resolveParams.mOutLineChar = int32.Parse(lineData[3]);
|
||
|
resolveParams.mOutFileName = new String(lineData[1]);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (resolveType == ResolveType.GetNavigationData)
|
||
|
{
|
||
|
resolveParams.mNavigationData = new String(autocompleteInfo);
|
||
|
}
|
||
|
else if (resolveType == ResolveType.GetVarType)
|
||
|
{
|
||
|
resolveParams.mTypeDef = new String(autocompleteInfo);
|
||
|
}
|
||
|
else if (resolveType == ResolveType.GetCurrentLocation)
|
||
|
{
|
||
|
PrimaryNavigationBar.SetLocation(autocompleteInfo);
|
||
|
}
|
||
|
else if ((resolveType == .Autocomplete) || (resolveType == .GetFixits))
|
||
|
{
|
||
|
if ((resolveParams == null) || (!resolveParams.mCancelled))
|
||
|
{
|
||
|
bool changedAfterInfo = (resolveParams != null) && (resolveParams.mTextVersion != Content.mData.mCurTextVersionId);
|
||
|
|
||
|
var autoComplete = GetAutoComplete();
|
||
|
if ((autoComplete != null) && (resolveParams != null))
|
||
|
autoComplete.mIsDocumentationPass = resolveParams.mDocumentationName != null;
|
||
|
HandleAutocompleteInfo(resolveType, autocompleteInfo, true, changedAfterInfo);
|
||
|
autoComplete = GetAutoComplete();
|
||
|
if (autoComplete != null)
|
||
|
{
|
||
|
autoComplete.mIsDocumentationPass = false;
|
||
|
if (resolveParams != null)
|
||
|
autoComplete.mIsUserRequested = resolveParams.mIsUserRequested;
|
||
|
if (resolveType == .GetFixits)
|
||
|
autoComplete.mIsUserRequested = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// fullRefresh means we rebuild types even if the hashes haven't changed - this is required for
|
||
|
// a classifier pass on a newly-opened file
|
||
|
public void DoClassify(ResolveType resolveType, ResolveParams resolveParams = null, bool isInterrupt = false)
|
||
|
{
|
||
|
scope AutoBeefPerf("SourceViewPanel.DoClassify");
|
||
|
|
||
|
if (gApp.mDbgDelayedAutocomplete)
|
||
|
Thread.Sleep(250);
|
||
|
|
||
|
/*if (resolveType == .Autocomplete)
|
||
|
{
|
||
|
Thread.Sleep(250);
|
||
|
}*/
|
||
|
|
||
|
#if IDE_C_SUPPORT
|
||
|
if (mIsClang)
|
||
|
{
|
||
|
DoClangClassify(resolveType, resolveParams);
|
||
|
return;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
var bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
if (bfSystem == null)
|
||
|
return;
|
||
|
var bfCompiler = BfResolveCompiler;
|
||
|
//var compiler = ResolveCompiler;
|
||
|
|
||
|
bool isBackground = (resolveType == ResolveType.Classify) || (resolveType == ResolveType.ClassifyFullRefresh);
|
||
|
bool fullRefresh = resolveType == ResolveType.ClassifyFullRefresh;
|
||
|
|
||
|
if (!isBackground)
|
||
|
Debug.Assert(isInterrupt);
|
||
|
|
||
|
if (mAsyncAutocomplete)
|
||
|
{
|
||
|
if ((resolveType == .Autocomplete) || (resolveType == .GetCurrentLocation) || (resolveType == .GetSymbolInfo))
|
||
|
{
|
||
|
isBackground = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*if ((mFilePath.Contains("mainA.bf")) && (isBackground))
|
||
|
{
|
||
|
Thread.Sleep(1000);
|
||
|
}*/
|
||
|
|
||
|
// If we think we're not running in the background, make sure
|
||
|
Debug.Assert((Thread.CurrentThread == gApp.mMainThread) == !isBackground);
|
||
|
|
||
|
if (isInterrupt)
|
||
|
bfSystem.NotifyWillRequestLock(1);
|
||
|
|
||
|
ProjectSource projectSource = FilteredProjectSource;
|
||
|
|
||
|
bool isFastClassify = false;
|
||
|
BfParser parser;
|
||
|
if (!isBackground)
|
||
|
{
|
||
|
bfSystem.PerfZoneStart("CreateParser");
|
||
|
parser = bfSystem.CreateParser(projectSource, false);
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
}
|
||
|
else if ((resolveParams != null) && (resolveParams.mParser != null))
|
||
|
{
|
||
|
parser = bfSystem.CreateNewParserRevision(resolveParams.mParser);
|
||
|
}
|
||
|
else if (projectSource != null)
|
||
|
{
|
||
|
bfSystem.PerfZoneStart("CreateParser");
|
||
|
parser = bfSystem.CreateParser(projectSource, false);
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// This only happens when we're editing source that isn't in our project
|
||
|
isFastClassify = true;
|
||
|
parser = bfSystem.CreateEmptyParser((BfProject)null);
|
||
|
}
|
||
|
|
||
|
EditWidgetContent.CharData[] char8Data = null;
|
||
|
int char8Len = 0;
|
||
|
if ((resolveParams != null) && (resolveParams.mCharData != null))
|
||
|
{
|
||
|
char8Data = resolveParams.mCharData;
|
||
|
char8Len = resolveParams.mCharData.Count;
|
||
|
}
|
||
|
if (char8Data == null)
|
||
|
{
|
||
|
Debug.Assert(!isBackground);
|
||
|
char8Data = mEditWidget.Content.mData.mText;
|
||
|
char8Len = mEditWidget.Content.mData.mTextLength;
|
||
|
}
|
||
|
|
||
|
/*var char8Data = (!isBackground) ? mEditWidget.Content.mData.mText : mProcessResolveCharData;
|
||
|
int char8Len = Math.Min(char8Data.Count, mEditWidget.Content.mData.mTextLength);*/
|
||
|
|
||
|
String passInstanceName = scope String("DoClassify ");
|
||
|
resolveType.ToString(passInstanceName);
|
||
|
if (projectSource != null)
|
||
|
passInstanceName.Append(":", projectSource.mName);
|
||
|
var passInstance = bfSystem.CreatePassInstance(passInstanceName);
|
||
|
passInstance.SetClassifierPassId(!isBackground ? (uint8)SourceDisplayId.AutoComplete : (uint8)SourceDisplayId.FullClassify);
|
||
|
if (isBackground)
|
||
|
{
|
||
|
//Debug.Assert(mProcessingPassInstance == null);
|
||
|
//mProcessingPassInstance = passInstance;
|
||
|
Debug.Assert(resolveParams.mPassInstance == null);
|
||
|
resolveParams.mPassInstance = passInstance;
|
||
|
}
|
||
|
|
||
|
if (!isBackground)
|
||
|
bfSystem.PerfZoneStart("DoClassify.CreateChars");
|
||
|
char8[] chars = new char8[char8Len];
|
||
|
defer delete chars;
|
||
|
for (int32 i = 0; i < char8Len; i++)
|
||
|
{
|
||
|
char8Data[i].mDisplayPassId = (int32)SourceDisplayId.Cleared;
|
||
|
chars[i] = (char8)char8Data[i].mChar;
|
||
|
}
|
||
|
|
||
|
String text = scope String();
|
||
|
text.Append(chars, 0, chars.Count);
|
||
|
if (!isBackground)
|
||
|
{
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
bfSystem.PerfZoneStart("SetSource");
|
||
|
}
|
||
|
parser.SetIsClassifying();
|
||
|
parser.SetSource(text, mFilePath);
|
||
|
if (!isBackground)
|
||
|
{
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
bfSystem.PerfZoneStart("DoClassify.DoWork");
|
||
|
}
|
||
|
|
||
|
int cursorPos = mEditWidget.mEditWidgetContent.CursorTextPos;
|
||
|
/*if (resolveType == ResolveType.Autocomplete)
|
||
|
cursorPos--;*/
|
||
|
if ((resolveParams != null) && (resolveParams.mOverrideCursorPos != -1))
|
||
|
cursorPos = resolveParams.mOverrideCursorPos;
|
||
|
|
||
|
if ((resolveType == ResolveType.GetNavigationData) || (resolveType == ResolveType.GetFixits))
|
||
|
parser.SetAutocomplete(-1);
|
||
|
else
|
||
|
{
|
||
|
bool setAutocomplete = ((!isBackground) && (resolveType != ResolveType.RenameSymbol));
|
||
|
if ((resolveType == .Autocomplete) || (resolveType == .GetCurrentLocation) || (resolveType == .GetSymbolInfo))
|
||
|
setAutocomplete = true;
|
||
|
if (setAutocomplete)
|
||
|
parser.SetAutocomplete(Math.Max(0, cursorPos));
|
||
|
}
|
||
|
/*else (!isFullClassify) -- do we ever need to do this?
|
||
|
parser.SetCursorIdx(mEditWidget.mEditWidgetContent.CursorTextPos);*/
|
||
|
|
||
|
var resolvePassData = parser.CreateResolvePassData(resolveType);
|
||
|
if (resolveParams != null)
|
||
|
{
|
||
|
if (resolveParams.mLocalId != -1)
|
||
|
resolvePassData.SetLocalId(resolveParams.mLocalId);
|
||
|
if (resolveParams.mTypeDef != null)
|
||
|
resolvePassData.SetSymbolReferenceTypeDef(resolveParams.mTypeDef);
|
||
|
if (resolveParams.mFieldIdx != -1)
|
||
|
resolvePassData.SetSymbolReferenceFieldIdx(resolveParams.mFieldIdx);
|
||
|
if (resolveParams.mMethodIdx != -1)
|
||
|
resolvePassData.SetSymbolReferenceMethodIdx(resolveParams.mMethodIdx);
|
||
|
if (resolveParams.mPropertyIdx != -1)
|
||
|
resolvePassData.SetSymbolReferencePropertyIdx(resolveParams.mPropertyIdx);
|
||
|
}
|
||
|
|
||
|
if ((resolveParams != null) && (resolveParams.mDocumentationName != null))
|
||
|
resolvePassData.SetDocumentationRequest(resolveParams.mDocumentationName);
|
||
|
parser.Parse(passInstance, !mIsBeefSource);
|
||
|
parser.Reduce(passInstance);
|
||
|
|
||
|
if (isInterrupt)
|
||
|
{
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
bfSystem.PerfZoneStart("Lock");
|
||
|
var sw = scope Stopwatch(true);
|
||
|
sw.Start();
|
||
|
bfSystem.Lock(1);
|
||
|
if (sw.ElapsedMicroseconds > 100)
|
||
|
{
|
||
|
NOP!();
|
||
|
}
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
bfSystem.Lock(0);
|
||
|
}
|
||
|
|
||
|
if (!isFastClassify)
|
||
|
parser.BuildDefs(passInstance, resolvePassData, fullRefresh);
|
||
|
|
||
|
// For Fixits we do want to parse the whole file but we need the cursorIdx bound still to
|
||
|
// locate the correct fixit info
|
||
|
if (resolveType == ResolveType.GetFixits)
|
||
|
parser.SetCursorIdx(Math.Max(0, cursorPos));
|
||
|
|
||
|
if ((resolveType == ResolveType.ClassifyFullRefresh) && (mUseDebugKeyboard))
|
||
|
{
|
||
|
//Debug.WriteLine("Classify Paused...");
|
||
|
mClassifyPaused = true;
|
||
|
while (mClassifyPaused)
|
||
|
Thread.Sleep(20);
|
||
|
//Debug.WriteLine("Classify Continuing.");
|
||
|
}
|
||
|
|
||
|
/*if (resolveType == ResolveType.RenameLocalSymbol)
|
||
|
{
|
||
|
// We do want cursor info for replacing the symbol, we just didn't want it for reducing
|
||
|
// and building defs
|
||
|
parser.SetAutocomplete(Math.Max(0, mEditWidget.mEditWidgetContent.CursorTextPos - 1));
|
||
|
}*/
|
||
|
|
||
|
if ((!isFastClassify) && (bfCompiler != null))
|
||
|
{
|
||
|
if (!bfCompiler.ClassifySource(passInstance, parser, resolvePassData, char8Data))
|
||
|
{
|
||
|
//DeleteAndNullify!(mProcessResolveCharData);
|
||
|
//mProcessResolveCharIdSpan.Dispose();
|
||
|
|
||
|
resolveParams.mCancelled = true;
|
||
|
if (resolveType == ResolveType.ClassifyFullRefresh)
|
||
|
QueueFullRefresh(false);
|
||
|
bfCompiler.QueueDeferredResolveAll();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
parser.ClassifySource(char8Data, !mIsBeefSource);
|
||
|
}
|
||
|
|
||
|
if (resolveType == .Autocomplete)
|
||
|
{
|
||
|
NOP!();
|
||
|
}
|
||
|
|
||
|
if (!isBackground)
|
||
|
{
|
||
|
String autocompleteInfo = scope String();
|
||
|
bfCompiler.GetAutocompleteInfo(autocompleteInfo);
|
||
|
HandleResolveResult(resolveType, autocompleteInfo, resolveParams);
|
||
|
}
|
||
|
else if (resolveParams != null)
|
||
|
{
|
||
|
resolveParams.mAutocompleteInfo = new String();
|
||
|
bfCompiler.GetAutocompleteInfo(resolveParams.mAutocompleteInfo);
|
||
|
}
|
||
|
|
||
|
if (!isBackground)
|
||
|
bfSystem.PerfZoneStart("Cleanup");
|
||
|
delete resolvePassData;
|
||
|
|
||
|
if ((!isBackground) || (isFastClassify))
|
||
|
{
|
||
|
//bool isAutocomplete = (resolveType == ResolveType.Autocomplete) || (resolveType == ResolveType.Autocomplete_HighPri);
|
||
|
//if (isAutocomplete)
|
||
|
if ((!isBackground) && (resolveType == ResolveType.Autocomplete))
|
||
|
InjectErrors(passInstance, mEditWidget.mEditWidgetContent.mData.mText, mEditWidget.mEditWidgetContent.mData.mTextIdData.GetPrepared(), true);
|
||
|
//IDEApp.sApp.ShowPassOutput(passInstance);
|
||
|
|
||
|
delete passInstance;
|
||
|
if ((resolveParams != null) && (resolveParams.mPassInstance == passInstance))
|
||
|
resolveParams.mPassInstance = null;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!isInterrupt)
|
||
|
bfSystem.RemoveOldData();
|
||
|
}
|
||
|
|
||
|
if ((resolveParams == null) || (resolveParams.mParser == null))
|
||
|
{
|
||
|
delete parser;
|
||
|
mWantsParserCleanup = true;
|
||
|
}
|
||
|
|
||
|
bfSystem.Unlock();
|
||
|
|
||
|
if (!isBackground)
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
|
||
|
if ((resolveParams != null) && (resolveParams.mWaitEvent != null))
|
||
|
resolveParams.mWaitEvent.Set(true);
|
||
|
|
||
|
//Debug.WriteLine("Classify Done {0}", !isBackground);
|
||
|
|
||
|
/*for (int i = 0; i < text.Length; i++)
|
||
|
mEditWidget.Content.mText[i].mTypeNum = (ushort)elementTypeArray[i]; */
|
||
|
}
|
||
|
|
||
|
int32 GetBraceNum(EditWidgetContent.CharData c)
|
||
|
{
|
||
|
if (c.mDisplayTypeId != 0)
|
||
|
return 0;
|
||
|
switch ((char8)c.mChar)
|
||
|
{
|
||
|
case '{':
|
||
|
return 1;
|
||
|
case '[':
|
||
|
return 2;
|
||
|
case '(':
|
||
|
return 3;
|
||
|
case '<':
|
||
|
return 4;
|
||
|
case '}':
|
||
|
return -1;
|
||
|
case ']':
|
||
|
return -2;
|
||
|
case ')':
|
||
|
return -3;
|
||
|
case '>':
|
||
|
return -4;
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
public void MatchBrace()
|
||
|
{
|
||
|
var textData = mEditWidget.Content.mData.mText;
|
||
|
int32[] braceCounts = scope int32[4];
|
||
|
|
||
|
int cursorIdx = mEditWidget.Content.CursorTextPos;
|
||
|
int braceIdx = cursorIdx;
|
||
|
int braceNum = GetBraceNum(textData[braceIdx]);
|
||
|
if ((braceNum == 0) && (braceIdx > 0))
|
||
|
{
|
||
|
braceIdx--;
|
||
|
cursorIdx--;
|
||
|
braceNum = GetBraceNum(textData[braceIdx]);
|
||
|
}
|
||
|
if (braceNum != 0)
|
||
|
{
|
||
|
int bracePairNum = -braceNum;
|
||
|
int searchIdx = braceIdx;
|
||
|
int searchDir = braceNum / Math.Abs(braceNum);
|
||
|
int braceDepth = 0;
|
||
|
|
||
|
while ((searchIdx >= 0) && (searchIdx < textData.Count))
|
||
|
{
|
||
|
int32 checkBraceNum = GetBraceNum(textData[searchIdx]);
|
||
|
if ((checkBraceNum != 0) && ((braceNum == 4) || (braceNum == -4)))
|
||
|
{
|
||
|
if (checkBraceNum > 0)
|
||
|
braceCounts[checkBraceNum - 1]++;
|
||
|
else if (checkBraceNum < 0)
|
||
|
{
|
||
|
int32 decBraceNum = -checkBraceNum - 1;
|
||
|
braceCounts[decBraceNum]--;
|
||
|
if ((decBraceNum != 3) && (braceNum != checkBraceNum) && (braceCounts[decBraceNum] < 0))
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (textData[searchIdx].mDisplayTypeId != 0)
|
||
|
checkBraceNum = 0;
|
||
|
if ((checkBraceNum == braceNum) || (checkBraceNum == bracePairNum))
|
||
|
{
|
||
|
braceDepth += checkBraceNum;
|
||
|
if (braceDepth == 0)
|
||
|
{
|
||
|
if (searchDir > 0)
|
||
|
searchIdx++; // Put cursor after close
|
||
|
|
||
|
if ((braceNum == 4) || (braceNum == -4))
|
||
|
{
|
||
|
// If we're matching <>'s then make sure the ) and } counts are zero
|
||
|
if ((braceCounts[0] != 0) || (braceCounts[1] != 0) || (braceCounts[2] != 0))
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
int cursorStartPos = cursorIdx;
|
||
|
if (searchDir < 0)
|
||
|
cursorStartPos++;
|
||
|
mEditWidget.Content.CursorTextPos = searchIdx;
|
||
|
if (mWidgetWindow.IsKeyDown(KeyCode.Shift))
|
||
|
{
|
||
|
mEditWidget.Content.mSelection = EditSelection(cursorStartPos, mEditWidget.Content.CursorTextPos);
|
||
|
}
|
||
|
else
|
||
|
mEditWidget.Content.mSelection = null;
|
||
|
mEditWidget.Content.CursorMoved();
|
||
|
mEditWidget.Content.EnsureCursorVisible();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
searchIdx += searchDir;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool FileNameMatches(String fileName)
|
||
|
{
|
||
|
if (mFilePath == null)
|
||
|
return false;
|
||
|
if ((mAliasFilePath != null) && (Path.Equals(fileName, mAliasFilePath)))
|
||
|
return true;
|
||
|
return Path.Equals(fileName, mFilePath);
|
||
|
}
|
||
|
|
||
|
void HilitePosition(LocatorType hilitePosition, int32 prevLine = -1)
|
||
|
{
|
||
|
if (hilitePosition == LocatorType.None)
|
||
|
return;
|
||
|
|
||
|
if ((hilitePosition == LocatorType.Smart) && (prevLine != -1) && (!mJustShown))
|
||
|
{
|
||
|
int32 newLine = mEditWidget.Content.CursorLineAndColumn.mLine;
|
||
|
if (Math.Abs(prevLine - newLine) < 16)
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float x;
|
||
|
float y;
|
||
|
mEditWidget.Content.GetTextCoordAtCursor(out x, out y);
|
||
|
|
||
|
var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
|
||
|
LocatorAnim.Show(mEditWidget.Content, x, y + sourceEditWidgetContent.mFont.GetLineSpacing() / 2);
|
||
|
}
|
||
|
|
||
|
public void ShowFileLocation(int cursorIdx, LocatorType hilitePosition)
|
||
|
{
|
||
|
var activePanel = GetActivePanel();
|
||
|
|
||
|
if (activePanel != this)
|
||
|
{
|
||
|
activePanel.ShowFileLocation(cursorIdx, hilitePosition);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mEditWidget == null)
|
||
|
return;
|
||
|
|
||
|
int32 prevLine = mEditWidget.Content.CursorLineAndColumn.mLine;
|
||
|
|
||
|
mEditWidget.Content.mSelection = null;
|
||
|
mEditWidget.Content.CursorTextPos = cursorIdx;
|
||
|
mEditWidget.Content.CursorMoved();
|
||
|
mEditWidget.Content.EnsureCursorVisible(true, true);
|
||
|
mEditWidget.Content.mCursorImplicitlyMoved = true;
|
||
|
if (mJustShown) // Jump to whatever position we're scrolling to
|
||
|
{
|
||
|
mEditWidget.mVertPos.mPct = 1.0f;
|
||
|
mEditWidget.UpdateContentPosition();
|
||
|
}
|
||
|
|
||
|
HilitePosition(hilitePosition, prevLine);
|
||
|
}
|
||
|
|
||
|
public void ShowFileLocation(int refHotIdx, int line, int column, LocatorType hilitePosition)
|
||
|
{
|
||
|
mRequestedLineAndColumn = EditWidgetContent.LineAndColumn(line, column);
|
||
|
|
||
|
//int prevLine = mEditWidget.Content.CursorLineAndColumn.mLine;
|
||
|
var content = mEditWidget.Content;
|
||
|
var useLine = line;
|
||
|
|
||
|
ProjectSource projectSource = FilteredProjectSource;
|
||
|
|
||
|
if (projectSource != null)
|
||
|
{
|
||
|
bool allowThrough = false;
|
||
|
bool worked = false;
|
||
|
if (refHotIdx == -1)
|
||
|
{
|
||
|
int char8Idx = content.GetTextIdx(useLine, column);
|
||
|
ShowFileLocation(char8Idx, hilitePosition);
|
||
|
worked = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int32 char8Id = IDEApp.sApp.mWorkspace.GetProjectSourceCharId(projectSource, refHotIdx, useLine, column);
|
||
|
if (char8Id != 0)
|
||
|
{
|
||
|
int char8Idx = content.GetCharIdIdx(char8Id);
|
||
|
if (char8Idx != -1)
|
||
|
{
|
||
|
ShowFileLocation(char8Idx, hilitePosition);
|
||
|
worked = true;
|
||
|
}
|
||
|
else if (IDEApp.sApp.mWorkspace.GetProjectSourceCompileInstance(projectSource, refHotIdx) == null)
|
||
|
{
|
||
|
allowThrough = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mOldVersionPanel != null)
|
||
|
{
|
||
|
float scrollTopDelta = mEditWidget.Content.GetCursorScreenRelY();
|
||
|
mOldVersionPanel.ShowFileLocation(refHotIdx, useLine, column, hilitePosition);
|
||
|
if (mOldVersionPanel.mJustShown)
|
||
|
{
|
||
|
mOldVersionPanel.mEditWidget.Content.SetCursorScreenRelY(scrollTopDelta - mOldVersionPanel.mPanelHeader.mHeight);
|
||
|
mOldVersionPanel.mEditWidget.Content.EnsureCursorVisible(true, true);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!allowThrough)
|
||
|
{
|
||
|
if (!worked)
|
||
|
IDEApp.Beep(IDEApp.MessageBeepType.Error);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
useLine = Math.Min(useLine, content.GetLineCount() - 1);
|
||
|
|
||
|
int cursorIdx = content.GetTextIdx(useLine, column);
|
||
|
ShowFileLocation(cursorIdx, hilitePosition);
|
||
|
|
||
|
/*content.MoveCursorTo(line, column, true);
|
||
|
//content.EnsureCursorVisible(true, true);
|
||
|
if (mJustShown) // Jump to whatever position we're scrolling to
|
||
|
{
|
||
|
mEditWidget.mVertPos.mPct = 1.0f;
|
||
|
mEditWidget.UpdateContentPosition();
|
||
|
}
|
||
|
if (hilitePosition)
|
||
|
HilitePosition(prevLine);*/
|
||
|
}
|
||
|
|
||
|
void RemoveEditWidget()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
public void AttachToProjectSource(ProjectSource projectSource)
|
||
|
{
|
||
|
mProjectSource = projectSource;
|
||
|
if (mEditData != null)
|
||
|
{
|
||
|
if (mProjectSource.mEditData == null)
|
||
|
{
|
||
|
mEditData.Ref();
|
||
|
mProjectSource.mEditData = mEditData;
|
||
|
mEditData.mProjectSources.Add(mProjectSource);
|
||
|
}
|
||
|
}
|
||
|
QueueFullRefresh(true);
|
||
|
}
|
||
|
|
||
|
public void DetachFromProjectItem()
|
||
|
{
|
||
|
if (mProjectSource == null)
|
||
|
return;
|
||
|
|
||
|
ProcessDeferredResolveResults(-1);
|
||
|
|
||
|
//Debug.Assert(mEditData != null);
|
||
|
|
||
|
//gApp.mFileEditData.Add(mEditData);
|
||
|
//mProjectSource.mEditData = null;
|
||
|
mProjectSource = null;
|
||
|
QueueFullRefresh(true);
|
||
|
|
||
|
if (mOldVersionPanel != null)
|
||
|
mOldVersionPanel.DetachFromProjectItem();
|
||
|
if (mSplitTopPanel != null)
|
||
|
mSplitTopPanel.DetachFromProjectItem();
|
||
|
}
|
||
|
|
||
|
public override void Dispose()
|
||
|
{
|
||
|
if (mDisposed)
|
||
|
return;
|
||
|
|
||
|
ProcessDeferredResolveResults(-1);
|
||
|
|
||
|
if (IDEApp.sApp.mLastActiveSourceViewPanel == this)
|
||
|
IDEApp.sApp.mLastActiveSourceViewPanel = null;
|
||
|
|
||
|
if (IDEApp.sApp.mLastActivePanel == this)
|
||
|
IDEApp.sApp.mLastActivePanel = null;
|
||
|
|
||
|
if (Content.Data.mCurQuickFind != null)
|
||
|
{
|
||
|
//Content.Data.mCurQuickFind.Close();
|
||
|
Content.Data.mCurQuickFind = null;
|
||
|
}
|
||
|
|
||
|
mEditWidget.mPanel = null;
|
||
|
|
||
|
if (mFilePath != null)
|
||
|
IDEApp.sApp.mFileWatcher.RemoveWatch(mFilePath);
|
||
|
|
||
|
var editWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
if (editWidgetContent.mAutoComplete != null)
|
||
|
editWidgetContent.mAutoComplete.Close();
|
||
|
DeleteAndNullify!(editWidgetContent.mOnGenerateAutocomplete);
|
||
|
DeleteAndNullify!(editWidgetContent.mOnEscape);
|
||
|
DeleteAndNullify!(editWidgetContent.mOnFinishAsyncAutocomplete);
|
||
|
DeleteAndNullify!(editWidgetContent.mOnCancelAsyncAutocomplete);
|
||
|
editWidgetContent.mSourceViewPanel = null;
|
||
|
mEditWidget.RemoveSelf();
|
||
|
|
||
|
if ((mEditData != null) && (mEditData.mEditWidget == mEditWidget))
|
||
|
{
|
||
|
// if there's another view of this window open, remap the mEditData.mEditWidget to that one
|
||
|
// That allows us to delete our current edit widget
|
||
|
for (var user in mEditData.mEditWidget.mEditWidgetContent.mData.mUsers)
|
||
|
{
|
||
|
if ((user != mEditWidget.mEditWidgetContent) && (var sourceEditWidget = user.mEditWidget as SourceEditWidget))
|
||
|
{
|
||
|
mEditData.mEditWidget = sourceEditWidget;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((mEditData != null) && (mEditData.mEditWidget == mEditWidget))
|
||
|
{
|
||
|
//Debug.WriteLine("Dispose mEditData.mEditWidget = {0}", mEditWidget);
|
||
|
Debug.Assert(mEditWidget.mParent == null);
|
||
|
mEditData.mOwnsEditWidget = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//Debug.WriteLine("Dispose deleting mEditWidget {0}", mEditWidget);
|
||
|
delete mEditWidget;
|
||
|
}
|
||
|
mEditWidget = null;
|
||
|
|
||
|
if ((mProjectSource == null) && (mEditData != null) && (mEditData.mOwnsEditWidget) && (mEditData.mEditWidget.mEditWidgetContent.mData.mUsers.Count == 1))
|
||
|
{
|
||
|
// This isn't needed anymore...
|
||
|
gApp.DeleteEditData(mEditData);
|
||
|
}
|
||
|
|
||
|
/*if (mProjectSource != null)
|
||
|
mProjectSource.ClearUnsavedData();*/
|
||
|
|
||
|
if (mOldVersionPanel != null)
|
||
|
mOldVersionPanel.Dispose();
|
||
|
if (mSplitTopPanel != null)
|
||
|
mSplitTopPanel.Dispose();
|
||
|
|
||
|
DebugManager debugManager = IDEApp.sApp.mDebugger;
|
||
|
debugManager.mBreakpointsChangedDelegate.Remove(scope => BreakpointsChanged, true);
|
||
|
|
||
|
#if IDE_C_SUPPORT
|
||
|
if (mIsClang)
|
||
|
{
|
||
|
var clangCompiler = ClangResolveCompiler;
|
||
|
clangCompiler.QueueFileRemoved(mFilePsath);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (IDEApp.sApp.mBfResolveHelper != null)
|
||
|
IDEApp.sApp.mBfResolveHelper.SourceViewPanelClosed(this);
|
||
|
|
||
|
if (mResolveJobCount > 0)
|
||
|
{
|
||
|
//TODO: Make a way we can cancel only our specific job
|
||
|
ResolveCompiler.CancelBackground();
|
||
|
Debug.Assert(mResolveJobCount == 0);
|
||
|
}
|
||
|
|
||
|
if (mSpellCheckJobCount > 0)
|
||
|
{
|
||
|
IDEApp.sApp.mSpellChecker.CancelBackground();
|
||
|
Debug.Assert(mSpellCheckJobCount == 0);
|
||
|
}
|
||
|
|
||
|
if (mRenameSymbolDialog != null)
|
||
|
mRenameSymbolDialog.Close();
|
||
|
|
||
|
base.Dispose();
|
||
|
}
|
||
|
|
||
|
public void Close()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
SourceViewPanel GetOldVersionPanel()
|
||
|
{
|
||
|
if (mSplitBottomPanel != null)
|
||
|
return mSplitBottomPanel.mOldVersionPanel;
|
||
|
return mOldVersionPanel;
|
||
|
}
|
||
|
|
||
|
public SourceViewPanel GetActivePanel()
|
||
|
{
|
||
|
if (mSplitTopPanel != null)
|
||
|
{
|
||
|
if (mSplitTopPanel.mLastFocusTick > mLastFocusTick)
|
||
|
{
|
||
|
return mSplitTopPanel.GetActivePanel();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mOldVersionPanel != null)
|
||
|
{
|
||
|
return mOldVersionPanel.GetActivePanel();
|
||
|
}
|
||
|
|
||
|
return this;
|
||
|
}
|
||
|
|
||
|
public void FocusEdit()
|
||
|
{
|
||
|
GetActivePanel().mEditWidget.SetFocus();
|
||
|
}
|
||
|
|
||
|
public override void SetFocus()
|
||
|
{
|
||
|
//mEditWidget.SetFocus();
|
||
|
FocusEdit();
|
||
|
}
|
||
|
|
||
|
public bool HasFocus(bool selfOnly = false)
|
||
|
{
|
||
|
if (!selfOnly)
|
||
|
{
|
||
|
if ((mSplitTopPanel != null) && (mSplitTopPanel.mEditWidget.mHasFocus))
|
||
|
return true;
|
||
|
if ((mSplitBottomPanel != null) && (mSplitBottomPanel.mEditWidget.mHasFocus))
|
||
|
return true;
|
||
|
if ((mOldVersionPanel != null) && (mOldVersionPanel.mEditWidget.mHasFocus))
|
||
|
return true;
|
||
|
}
|
||
|
if (mEditWidget.mHasFocus)
|
||
|
return true;
|
||
|
if (mQuickFind != null)
|
||
|
return mQuickFind.HasFocus();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public override void EditGotFocus()
|
||
|
{
|
||
|
if (mFilePath != null)
|
||
|
gApp.AddToRecentDisplayedFilesList(mFilePath);
|
||
|
if (mLoadFailed)
|
||
|
return;
|
||
|
#if IDE_C_SUPPORT
|
||
|
if (mIsClang)
|
||
|
{
|
||
|
if (IDEUtils.IsHeaderFile(mFilePath))
|
||
|
{
|
||
|
// If a different cpp file has gotten focus since we have last been here, then we want to
|
||
|
// re-evaluate the mClangSource so we can potentially switch to using that
|
||
|
//if (mClangSource != null)
|
||
|
{
|
||
|
var resolveClang = IDEApp.sApp.mResolveClang;
|
||
|
using (resolveClang.mMonitor.Enter())
|
||
|
{
|
||
|
if (mLastMRUVersion != resolveClang.mProjectSourceVersion)
|
||
|
{
|
||
|
//mClangSource = null;
|
||
|
QueueFullRefresh(true);
|
||
|
mLastMRUVersion = resolveClang.mProjectSourceVersion;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (mProjectSource != null)
|
||
|
IDEApp.sApp.mResolveClang.UpdateMRU(mProjectSource);
|
||
|
}
|
||
|
#endif
|
||
|
var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
sourceEditWidgetContent.mCursorStillTicks = 0;
|
||
|
sourceEditWidgetContent.mCursorBlinkTicks = 0;
|
||
|
|
||
|
if (mProjectSource != null)
|
||
|
{
|
||
|
var editData = gApp.GetEditData(mProjectSource, true);
|
||
|
editData.mEditWidget = mEditWidget;
|
||
|
}
|
||
|
|
||
|
gApp.mLastActiveSourceViewPanel = this;
|
||
|
gApp.mLastActivePanel = this;
|
||
|
}
|
||
|
|
||
|
public override void EditLostFocus()
|
||
|
{
|
||
|
#if IDE_C_SUPPORT
|
||
|
if (mClangSourceChanged)
|
||
|
{
|
||
|
IDEApp.sApp.mDepClang.FileSaved(mFilePath);
|
||
|
mClangSourceChanged = false;
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
public override void RemovedFromParent(Widget previousParent, WidgetWindow window)
|
||
|
{
|
||
|
if (mHoverWatch != null)
|
||
|
mHoverWatch.Close();
|
||
|
|
||
|
if (mRenameSymbolDialog != null)
|
||
|
mRenameSymbolDialog.Cancel();
|
||
|
|
||
|
if (mEditWidget != null)
|
||
|
{
|
||
|
var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
if (sourceEditWidgetContent.mAutoComplete != null)
|
||
|
sourceEditWidgetContent.mAutoComplete.Close();
|
||
|
}
|
||
|
|
||
|
base.RemovedFromParent(previousParent, window);
|
||
|
CloseOldVersion();
|
||
|
|
||
|
if (NeedsPostRemoveUpdate)
|
||
|
{
|
||
|
//Debug.WriteLine("Adding sourceViewPanel to mPostRemoveUpdatePanel {0}", this);
|
||
|
mInPostRemoveUpdatePanels = true;
|
||
|
gApp.mPostRemoveUpdatePanels.Add(this);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void AddedToParent()
|
||
|
{
|
||
|
base.AddedToParent();
|
||
|
mWantsFullClassify = true;
|
||
|
mWantsFullRefresh = true;
|
||
|
mJustShown = true;
|
||
|
mWantsClassifyAutocomplete = false;
|
||
|
|
||
|
if (mUpdateCnt == 0)
|
||
|
{
|
||
|
//DoFastClassify();
|
||
|
mWantsFastClassify = true;
|
||
|
mWantsFullClassify = true;
|
||
|
}
|
||
|
|
||
|
if (mDesiredVertPos != 0)
|
||
|
{
|
||
|
mEditWidget.Content.RecalcSize();
|
||
|
mEditWidget.mVertPos.Set(mDesiredVertPos, true);
|
||
|
mEditWidget.UpdateContentPosition();
|
||
|
mDesiredVertPos = 0;
|
||
|
}
|
||
|
//IDEApp.sApp.mInDisassemblyView = false;
|
||
|
|
||
|
if ((mProjectSource != null) && (IDEApp.sApp.mWakaTime != null))
|
||
|
{
|
||
|
IDEApp.sApp.mWakaTime.QueueFile(mFilePath, mProjectSource.mProject.mProjectName, false);
|
||
|
}
|
||
|
|
||
|
IDEApp.sApp.mLastActiveSourceViewPanel = this;
|
||
|
IDEApp.sApp.mLastActivePanel = this;
|
||
|
}
|
||
|
|
||
|
void CheckTrackedElementChanges()
|
||
|
{
|
||
|
if (mHotFileIdx != -1)
|
||
|
return;
|
||
|
|
||
|
bool hadBreakpointChanges = false;
|
||
|
|
||
|
// Update tracked positions
|
||
|
if (mTrackedTextElementViewList != null)
|
||
|
{
|
||
|
var editContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
|
||
|
for (var trackedElementView in mTrackedTextElementViewList)
|
||
|
{
|
||
|
int32 startContentIdx = -1;
|
||
|
if (trackedElementView.mTrackedElement.mIsDead)
|
||
|
continue;
|
||
|
var breakpoint = trackedElementView.mTrackedElement as Breakpoint;
|
||
|
var trackedElement = trackedElementView.mTrackedElement;
|
||
|
|
||
|
if (trackedElement.mSnapToLineStart)
|
||
|
{
|
||
|
int32 lineLeft = trackedElementView.mTextPosition.mIndex;
|
||
|
repeat
|
||
|
{
|
||
|
if (!((char8)editContent.mData.mText[lineLeft].mChar).IsWhiteSpace)
|
||
|
startContentIdx = lineLeft;
|
||
|
lineLeft--;
|
||
|
}
|
||
|
while ((lineLeft > 0) && (editContent.mData.mText[lineLeft].mChar != '\n'));
|
||
|
|
||
|
if (startContentIdx == -1)
|
||
|
{
|
||
|
lineLeft = trackedElementView.mTextPosition.mIndex;
|
||
|
repeat
|
||
|
{
|
||
|
if (!((char8)editContent.mData.mText[lineLeft].mChar).IsWhiteSpace)
|
||
|
{
|
||
|
startContentIdx = lineLeft;
|
||
|
break;
|
||
|
}
|
||
|
lineLeft++;
|
||
|
}
|
||
|
while ((lineLeft > 0) && (editContent.mData.mText[lineLeft].mChar != '\n'));
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
startContentIdx = trackedElementView.mTextPosition.mIndex;
|
||
|
|
||
|
if ((startContentIdx == -1) || (trackedElementView.mTextPosition.mWasDeleted))
|
||
|
{
|
||
|
if (breakpoint != null)
|
||
|
{
|
||
|
BfLog.LogDbg("Tracked element deleted. Deleting breakpoint\n");
|
||
|
IDEApp.sApp.mDebugger.DeleteBreakpoint(breakpoint);
|
||
|
continue;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
trackedElementView.mTextPosition.mWasDeleted = false;
|
||
|
if (startContentIdx == -1)
|
||
|
startContentIdx = trackedElementView.mTextPosition.mIndex;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (trackedElementView.mLastMoveIdx != trackedElement.mMoveIdx)
|
||
|
{
|
||
|
UpdateTrackedElementView(trackedElementView);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
trackedElementView.mTextPosition.mIndex = startContentIdx;
|
||
|
|
||
|
int line;
|
||
|
int lineCharIdx;
|
||
|
mEditWidget.Content.GetLineCharAtIdx(trackedElementView.mTextPosition.mIndex, out line, out lineCharIdx);
|
||
|
|
||
|
if ((breakpoint != null) && (breakpoint.mLineNum != line))
|
||
|
hadBreakpointChanges = true;
|
||
|
//RemapActiveToCompiledLine(0, ref line, ref lineCharIdx);
|
||
|
trackedElementView.mTrackedElement.Move(line, lineCharIdx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (hadBreakpointChanges)
|
||
|
IDEApp.sApp.mDebugger.mBreakpointsChangedDelegate();
|
||
|
}
|
||
|
|
||
|
void UpdateTrackedElements()
|
||
|
{
|
||
|
if (mHotFileIdx != -1)
|
||
|
return;
|
||
|
if (!mIsBeefSource)
|
||
|
return;
|
||
|
|
||
|
for (var breakpointView in GetTrackedElementList())
|
||
|
{
|
||
|
var trackedElement = breakpointView.mTrackedElement;
|
||
|
var breakpoint = trackedElement as Breakpoint;
|
||
|
//if ((breakpoint != null) && (breakpoint.mNativeBreakpoint != null))
|
||
|
|
||
|
//TODO: we're only doing this for BOUND breakpoints. Otherwise if we create a new method and set a
|
||
|
// breakpoint on it but the new method hasn't actually been compile yet, then it causes the breakpoint
|
||
|
// to go crazy
|
||
|
if ((breakpoint != null) && (breakpoint.mNativeBreakpoint != null))
|
||
|
{
|
||
|
int32 breakpointLineNum = breakpoint.GetLineNum();
|
||
|
|
||
|
if (breakpointLineNum != breakpointView.mLastBoundLine)
|
||
|
{
|
||
|
breakpointView.mLastBoundLine = breakpointLineNum;
|
||
|
UpdateTrackedElementView(breakpointView);
|
||
|
|
||
|
int compileIdx = IDEApp.sApp.mWorkspace.GetHighestCompileIdx();
|
||
|
if (compileIdx != -1)
|
||
|
{
|
||
|
int column = -1;
|
||
|
breakpoint.mLineNum = breakpointLineNum;
|
||
|
int line = breakpoint.mLineNum;
|
||
|
RemapCompiledToActiveLine(compileIdx, ref line, ref column);
|
||
|
breakpoint.mLineNum = (int32)line;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void BreakpointsChanged()
|
||
|
{
|
||
|
mTrackedTextElementViewListDirty = true;
|
||
|
}
|
||
|
|
||
|
public void EnsureTrackedElementsValid()
|
||
|
{
|
||
|
if (mTrackedTextElementViewListDirty)
|
||
|
{
|
||
|
// Update the old one and then clear it and then update the new one
|
||
|
//TODO: The old one could contain deleted breakpoints and such... Under what circumstances should be do this?
|
||
|
CheckTrackedElementChanges();
|
||
|
GetTrackedElementList();
|
||
|
}
|
||
|
CheckTrackedElementChanges();
|
||
|
}
|
||
|
|
||
|
public void ClearTrackedElements()
|
||
|
{
|
||
|
if (mTrackedTextElementViewList != null)
|
||
|
{
|
||
|
var editWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
for (var breakpoint in mTrackedTextElementViewList)
|
||
|
{
|
||
|
editWidgetContent.PersistentTextPositions.Remove(breakpoint.mTextPosition);
|
||
|
delete breakpoint.mTextPosition;
|
||
|
}
|
||
|
DeleteContainerAndItems!(mTrackedTextElementViewList);
|
||
|
}
|
||
|
mTrackedTextElementViewList = null;
|
||
|
}
|
||
|
|
||
|
public void UpdateTrackedElementView(TrackedTextElementView trackedElementView)
|
||
|
{
|
||
|
var editWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
var trackedElement = trackedElementView.mTrackedElement;
|
||
|
|
||
|
if (trackedElementView.mTextPosition != null)
|
||
|
{
|
||
|
editWidgetContent.PersistentTextPositions.Remove(trackedElementView.mTextPosition);
|
||
|
delete trackedElementView.mTextPosition;
|
||
|
}
|
||
|
|
||
|
int lineStart;
|
||
|
int lineEnd;
|
||
|
mEditWidget.Content.GetLinePosition(trackedElement.mLineNum, out lineStart, out lineEnd);
|
||
|
|
||
|
trackedElementView.mTextPosition = new PersistentTextPosition((int32)Math.Min(editWidgetContent.mData.mTextLength, lineStart + trackedElement.mColumn));
|
||
|
editWidgetContent.PersistentTextPositions.Add(trackedElementView.mTextPosition);
|
||
|
|
||
|
trackedElementView.mLastMoveIdx = trackedElement.mMoveIdx;
|
||
|
}
|
||
|
|
||
|
List<TrackedTextElementView> GetTrackedElementList()
|
||
|
{
|
||
|
if (mTrackedTextElementViewListDirty)
|
||
|
{
|
||
|
ClearTrackedElements();
|
||
|
mTrackedTextElementViewListDirty = false;
|
||
|
}
|
||
|
|
||
|
DebugManager debugManager = IDEApp.sApp.mDebugger;
|
||
|
if (mTrackedTextElementViewList == null)
|
||
|
{
|
||
|
String findFileName = mFilePath;
|
||
|
|
||
|
mTrackedTextElementViewList = new List<TrackedTextElementView>();
|
||
|
if (mFilePath == null)
|
||
|
return mTrackedTextElementViewList;
|
||
|
|
||
|
for (var bookmark in IDEApp.sApp.mBookmarkManager.mBookmarkList)
|
||
|
{
|
||
|
if (Path.Equals(bookmark.mFileName, findFileName))
|
||
|
{
|
||
|
var bookmarkView = new TrackedTextElementView(bookmark);
|
||
|
UpdateTrackedElementView(bookmarkView);
|
||
|
mTrackedTextElementViewList.Add(bookmarkView);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (var breakpoint in debugManager.mBreakpointList)
|
||
|
{
|
||
|
if ((breakpoint.mFileName != null) && (Path.Equals(breakpoint.mFileName, findFileName)))
|
||
|
{
|
||
|
var breakpointView = new TrackedTextElementView(breakpoint);
|
||
|
UpdateTrackedElementView(breakpointView);
|
||
|
mTrackedTextElementViewList.Add(breakpointView);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//////
|
||
|
for (var historyEntry in IDEApp.sApp.mHistoryManager.mHistoryList)
|
||
|
{
|
||
|
if (Path.Equals(historyEntry.mFileName, findFileName))
|
||
|
{
|
||
|
var historyView = new TrackedTextElementView(historyEntry);
|
||
|
UpdateTrackedElementView(historyView);
|
||
|
mTrackedTextElementViewList.Add(historyView);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return mTrackedTextElementViewList;
|
||
|
}
|
||
|
|
||
|
void CloseHeader()
|
||
|
{
|
||
|
if (mPanelHeader != null)
|
||
|
{
|
||
|
mPanelHeader.RemoveSelf();
|
||
|
gApp.DeferDelete(mPanelHeader);
|
||
|
mPanelHeader = null;
|
||
|
ResizeComponents();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ClearLoadFailed()
|
||
|
{
|
||
|
if (mLoadFailed)
|
||
|
{
|
||
|
mLoadFailed = false;
|
||
|
CloseHeader();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void CheckBinary()
|
||
|
{
|
||
|
mIsBinary = false;
|
||
|
|
||
|
let data = mEditWidget.mEditWidgetContent.mData;
|
||
|
for (int i < data.mTextLength)
|
||
|
{
|
||
|
if (data.mText[i].mChar <= '\x08')
|
||
|
{
|
||
|
data.mText[i].mChar = ' ';
|
||
|
mIsBinary = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mIsBinary)
|
||
|
{
|
||
|
for (int i < data.mTextLength)
|
||
|
{
|
||
|
if (data.mText[i].mChar >= '\x80')
|
||
|
{
|
||
|
data.mText[i].mChar = ' ';
|
||
|
mIsBinary = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mIsBinary)
|
||
|
mEditWidget.mEditWidgetContent.mIsReadOnly = true;
|
||
|
}
|
||
|
|
||
|
public bool Show(String filePath, bool silentFail = false, FileEditData fileEditData = null)
|
||
|
{
|
||
|
scope AutoBeefPerf("SourceViewPanel.Show");
|
||
|
|
||
|
ClearLoadFailed();
|
||
|
|
||
|
Debug.Assert(!mDisposed);
|
||
|
|
||
|
String useFilePath = null;
|
||
|
var useFileEditData = fileEditData;
|
||
|
if (filePath != null)
|
||
|
{
|
||
|
Debug.Assert(!filePath.Contains("//"));
|
||
|
|
||
|
useFilePath = scope:: String(filePath);
|
||
|
IDEUtils.FixFilePath(useFilePath);
|
||
|
|
||
|
if (mProjectSource == null)
|
||
|
{
|
||
|
mProjectSource = IDEApp.sApp.FindProjectSourceItem(useFilePath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((useFileEditData == null) && (useFilePath != null))
|
||
|
{
|
||
|
if (mProjectSource != null)
|
||
|
{
|
||
|
useFileEditData = IDEApp.sApp.GetEditData(mProjectSource, true);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SourceHash.Kind hashKind = mLoadedHash.GetKind();
|
||
|
if (hashKind == .None)
|
||
|
hashKind = .MD5;
|
||
|
useFileEditData = gApp.GetEditData(useFilePath, true, true, hashKind);
|
||
|
}
|
||
|
if (useFileEditData.mEditWidget == null)
|
||
|
useFileEditData = null;
|
||
|
}
|
||
|
|
||
|
mEditData = useFileEditData;
|
||
|
if (useFileEditData != null)
|
||
|
{
|
||
|
if (useFileEditData.mEditWidget != null)
|
||
|
{
|
||
|
if (mEditWidget != null)
|
||
|
{
|
||
|
mEditWidget.RemoveSelf();
|
||
|
delete mEditWidget;
|
||
|
mEditWidget = null;
|
||
|
}
|
||
|
|
||
|
//Debug.Assert(projectSourceEditData.mOwnsEditWidget);
|
||
|
if (useFileEditData.mOwnsEditWidget)
|
||
|
{
|
||
|
mEditWidget = useFileEditData.mEditWidget;
|
||
|
useFileEditData.mOwnsEditWidget = false;
|
||
|
|
||
|
//Debug.WriteLine("Taking over EditWidget {0} Parent: {1}", mEditWidget, mEditWidget.mParent);
|
||
|
Debug.Assert(mEditWidget.mParent == null);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mEditWidget = IDEApp.sApp.CreateSourceEditWidget(useFileEditData.mEditWidget);
|
||
|
mEditWidget.Content.RecalcSize();
|
||
|
|
||
|
//Debug.WriteLine("Creating new EditWidget {0}", mEditWidget);
|
||
|
}
|
||
|
}
|
||
|
//mLastFileTextVersion = useFileEditData.mLastFileTextVersion;
|
||
|
}
|
||
|
|
||
|
if (mEditWidget == null)
|
||
|
{
|
||
|
mEditWidget = IDEApp.sApp.CreateSourceEditWidget();
|
||
|
}
|
||
|
SetupEditWidget();
|
||
|
|
||
|
DeleteAndNullify!(mFilePath);
|
||
|
if (useFilePath != null)
|
||
|
{
|
||
|
mFilePath = new String(useFilePath);
|
||
|
IDEApp.sApp.mFileWatcher.WatchFile(mFilePath);
|
||
|
|
||
|
var ext = scope String();
|
||
|
Path.GetExtension(useFilePath, ext);
|
||
|
/*if (ext.Length == 0)
|
||
|
return false;*/
|
||
|
ext.ToLower();
|
||
|
mIsSourceCode = IDEApp.IsSourceCode(useFilePath);
|
||
|
mIsBeefSource = IDEApp.IsBeefFile(useFilePath);
|
||
|
}
|
||
|
|
||
|
//TODO: Find mProjectSource
|
||
|
|
||
|
if (mIsSourceCode && !mIsBeefSource)
|
||
|
{
|
||
|
mIsClang = true;
|
||
|
//((SourceEditWidgetContent)mEditWidget.Content).mAsyncAutocomplete = true;
|
||
|
}
|
||
|
|
||
|
mJustShown = true;
|
||
|
mWantsFullRefresh = true;
|
||
|
mWantsFullClassify = true;
|
||
|
|
||
|
//mCurBfParser = IDEApp.sApp.mBfResolveSystem.CreateParser(mFilePath);
|
||
|
if (useFileEditData == null)
|
||
|
{
|
||
|
var text = scope String();
|
||
|
SourceHash hash;
|
||
|
if (mFilePath == null)
|
||
|
{
|
||
|
// Nothing
|
||
|
}
|
||
|
else if (gApp.LoadTextFile(mFilePath, text, true, scope [&] () => { hash = SourceHash.Create(.MD5, text); } ) case .Err)
|
||
|
{
|
||
|
if (silentFail)
|
||
|
{
|
||
|
Close();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
mEditWidget.mEditWidgetContent.mIsReadOnly = true;
|
||
|
FileOpenFailed();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IDEApp.sApp.mFileWatcher.FileIsValid(mFilePath);
|
||
|
|
||
|
mEditWidget.Content.AppendText(text);
|
||
|
|
||
|
using (gApp.mMonitor.Enter())
|
||
|
{
|
||
|
if ((mEditData != null) && (!mEditData.mSavedCharIdData.IsEmpty))
|
||
|
{
|
||
|
int char8IdDataLength = mEditData.mSavedCharIdData.GetTotalLength();
|
||
|
if (char8IdDataLength == mEditWidget.Content.mData.mTextLength)
|
||
|
{
|
||
|
// The saved char8IdData matches the length of our text, so we can use it
|
||
|
// This is important for text mapping after a tab has been closed and then
|
||
|
// the files is reopened
|
||
|
mEditWidget.Content.mData.mTextIdData.DuplicateFrom(ref mEditData.mSavedCharIdData);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//mLastFileTextVersion = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mLoadedHash = useFileEditData.mLoadedHash;
|
||
|
|
||
|
// Sanity check for when we have the saved data cached already
|
||
|
if (useFileEditData.IsFileDeleted())
|
||
|
{
|
||
|
if (silentFail)
|
||
|
{
|
||
|
Close();
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
FileOpenFailed();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
CheckBinary();
|
||
|
|
||
|
InitSplitter();
|
||
|
|
||
|
if ((mEditData != null) && (mFilePath != null))
|
||
|
Debug.Assert(Path.Equals(mFilePath, mEditData.mFilePath));
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public void RehupAlias()
|
||
|
{
|
||
|
if ((gApp.mDebugger.mIsRunning) && (mAliasFilePath != null))
|
||
|
{
|
||
|
gApp.mDebugger.SetAliasPath(mAliasFilePath, mFilePath);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void BrowseForFile()
|
||
|
{
|
||
|
#if !CLI
|
||
|
var fileDialog = scope System.IO.OpenFileDialog();
|
||
|
|
||
|
|
||
|
var initialDir = scope String(IDEApp.sApp.mWorkspace.mDir);
|
||
|
//initialDir.Replace('/', '\\');
|
||
|
|
||
|
var fileName = scope String();
|
||
|
Path.GetFileName(mFilePath, fileName);
|
||
|
|
||
|
var title = scope String();
|
||
|
title.AppendF("Open {0}", fileName);
|
||
|
fileDialog.Title = title;
|
||
|
fileDialog.Multiselect = false;
|
||
|
|
||
|
var ext = scope String();
|
||
|
Path.GetExtension(mFilePath, ext);
|
||
|
|
||
|
fileDialog.InitialDirectory = initialDir;
|
||
|
fileDialog.ValidateNames = true;
|
||
|
fileDialog.DefaultExt = ext;
|
||
|
|
||
|
var filter = scope String();
|
||
|
filter.AppendF("{0}|{0}|All files (*.*)|*.*", fileName, ext);
|
||
|
|
||
|
fileDialog.SetFilter(filter);
|
||
|
mWidgetWindow.PreModalChild();
|
||
|
if (fileDialog.ShowDialog(gApp.GetActiveWindow()).GetValueOrDefault() == .OK)
|
||
|
{
|
||
|
for (String filePath in fileDialog.FileNames)
|
||
|
{
|
||
|
delete mAliasFilePath;
|
||
|
mAliasFilePath = mFilePath;
|
||
|
mFilePath = new String(filePath);
|
||
|
RehupAlias();
|
||
|
RetryLoad();
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
void FileOpenFailed()
|
||
|
{
|
||
|
mLoadFailed = true;
|
||
|
//mLastFileTextVersion = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
mPanelHeader = new PanelHeader();
|
||
|
String fileName = scope String();
|
||
|
Path.GetFileName(mFilePath, fileName);
|
||
|
String headerStr = scope String();
|
||
|
headerStr.AppendF("Unable to load file '{0}'. Requested path is '{1}'", fileName, mFilePath);
|
||
|
mPanelHeader.Label = headerStr;
|
||
|
if (!sPreviousVersionWarningShown)
|
||
|
{
|
||
|
mPanelHeader.Flash();
|
||
|
sPreviousVersionWarningShown = true;
|
||
|
}
|
||
|
|
||
|
bool hasBookmarks = false;
|
||
|
bool hasBreakpoints = false;
|
||
|
|
||
|
var trackedElements = GetTrackedElementList();
|
||
|
for (var element in trackedElements)
|
||
|
{
|
||
|
hasBookmarks |= element.mTrackedElement is Bookmark;
|
||
|
hasBreakpoints |= element.mTrackedElement is Breakpoint;
|
||
|
}
|
||
|
|
||
|
if (hasBookmarks)
|
||
|
{
|
||
|
var button = mPanelHeader.AddButton("Remove Bookmarks");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
button.mVisible = false;
|
||
|
var trackedElements = GetTrackedElementList();
|
||
|
for (var element in trackedElements)
|
||
|
{
|
||
|
if (var bookmark = element.mTrackedElement as Bookmark)
|
||
|
{
|
||
|
gApp.mBookmarkManager.DeleteBookmark(bookmark);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
var button = mPanelHeader.AddButton("Retry");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
Reload();
|
||
|
});
|
||
|
|
||
|
button = mPanelHeader.AddButton("Auto Find");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
AutoFind();
|
||
|
});
|
||
|
|
||
|
button = mPanelHeader.AddButton("Browse...");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
BrowseForFile();
|
||
|
});
|
||
|
|
||
|
mPanelHeader.Flash();
|
||
|
AddWidget(mPanelHeader);
|
||
|
}
|
||
|
|
||
|
void AutoFind()
|
||
|
{
|
||
|
delete mSourceFindTask;
|
||
|
mSourceFindTask = new SourceFindTask();
|
||
|
mSourceFindTask.mSourceViewPanel = this;
|
||
|
mSourceFindTask.mFilePath = new String(mFilePath);
|
||
|
mSourceFindTask.mFileName = new String();
|
||
|
Path.GetFileName(mFilePath, mSourceFindTask.mFileName);
|
||
|
|
||
|
gApp.WithSourceViewPanels(scope (sourceViewPanel) =>
|
||
|
{
|
||
|
if (sourceViewPanel.mAliasFilePath != null)
|
||
|
{
|
||
|
var origDir = scope String();
|
||
|
Path.GetDirectoryPath(sourceViewPanel.mAliasFilePath, origDir);
|
||
|
IDEUtils.FixFilePath(origDir);
|
||
|
if (!Environment.IsFileSystemCaseSensitive)
|
||
|
origDir.ToUpper();
|
||
|
|
||
|
var localDir = scope String();
|
||
|
Path.GetDirectoryPath(sourceViewPanel.mFilePath, localDir);
|
||
|
|
||
|
if (mSourceFindTask.mRemapMap.TryAdd(origDir, var keyPtr, var valuePtr))
|
||
|
{
|
||
|
*keyPtr = new String(origDir);
|
||
|
*valuePtr = new String(localDir);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
ThreadPool.QueueUserWorkItem(new => mSourceFindTask.Run);
|
||
|
|
||
|
CloseHeader();
|
||
|
|
||
|
mPanelHeader = new PanelHeader();
|
||
|
String fileName = scope String();
|
||
|
Path.GetFileName(mFilePath, fileName);
|
||
|
String headerStr = scope String();
|
||
|
headerStr.AppendF("Finding {0}...", fileName);
|
||
|
mPanelHeader.Label = headerStr;
|
||
|
|
||
|
var button = mPanelHeader.AddButton("Cancel");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
mSourceFindTask.Cancel();
|
||
|
});
|
||
|
|
||
|
mPanelHeader.mButtonsOnBottom = true;
|
||
|
|
||
|
AddWidget(mPanelHeader);
|
||
|
ResizeComponents();
|
||
|
}
|
||
|
|
||
|
void ShowWrongHash()
|
||
|
{
|
||
|
CloseHeader();
|
||
|
|
||
|
mPanelHeader = new PanelHeader();
|
||
|
String fileName = scope String();
|
||
|
Path.GetFileName(mFilePath, fileName);
|
||
|
String headerStr = scope String();
|
||
|
headerStr.AppendF("Warning: This file is not an exact match for the file this program was built with", fileName);
|
||
|
mPanelHeader.Label = headerStr;
|
||
|
|
||
|
var button = mPanelHeader.AddButton("Ok");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
CloseHeader();
|
||
|
});
|
||
|
|
||
|
button = mPanelHeader.AddButton("Browse...");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
BrowseForFile();
|
||
|
});
|
||
|
|
||
|
mPanelHeader.mButtonsOnBottom = true;
|
||
|
|
||
|
AddWidget(mPanelHeader);
|
||
|
ResizeComponents();
|
||
|
}
|
||
|
|
||
|
public void SetLoadCmd(String loadCmd)
|
||
|
{
|
||
|
bool isRepeat = mOldVerLoadCmd != null;
|
||
|
|
||
|
CloseHeader();
|
||
|
|
||
|
mPanelHeader = new PanelHeader();
|
||
|
if (mOldVerLoadCmd == null)
|
||
|
mOldVerLoadCmd = new String(loadCmd);
|
||
|
|
||
|
// For testing a long command...
|
||
|
//mOldVerLoadCmd.Set("/bin/sleep.exe 10");
|
||
|
|
||
|
String fileName = scope String();
|
||
|
Path.GetFileName(mFilePath, fileName);
|
||
|
String headerStr = scope String();
|
||
|
if (isRepeat)
|
||
|
headerStr.AppendF("{0} Failed to retrieve file '{1}'.{2} The following command can be rerun to retry:\n{3}\nWARNING: This is a security risk if this PDB comes from an untrusted source.", Font.EncodeColor(0xFFFF8080), fileName, Font.EncodePopColor(), mOldVerLoadCmd);
|
||
|
else
|
||
|
headerStr.AppendF("The file '{0}' can be loaded from a source server by executing the embedded command:\n{1}\nWARNING: This is a security risk if this PDB comes from an untrusted source.", fileName, mOldVerLoadCmd);
|
||
|
mPanelHeader.Label = headerStr;
|
||
|
mPanelHeader.mTooltipText = new String(mOldVerLoadCmd);
|
||
|
|
||
|
var button = mPanelHeader.AddButton("Run");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
LoadOldVer();
|
||
|
});
|
||
|
button = mPanelHeader.AddButton("Always Run");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
LoadOldVer();
|
||
|
});
|
||
|
|
||
|
let checkbox = new BoundCheckbox(ref gApp.mStepOverExternalFiles);
|
||
|
checkbox.Label = "Step over external files";
|
||
|
mPanelHeader.AddWidget(checkbox);
|
||
|
checkbox.mOnValueChanged.Add(new () => { gApp.RehupStepFilters(); });
|
||
|
|
||
|
mPanelHeader.mOnResized.Add(new (widget) =>
|
||
|
{
|
||
|
checkbox.Resize(GS!(10), GS!(60), checkbox.CalcWidth(), GS!(20));
|
||
|
});
|
||
|
|
||
|
//button = mPanelHeader.AddButton("Run On This ");
|
||
|
//button = mPanelHeader.AddButton("Never Run");
|
||
|
|
||
|
mPanelHeader.mButtonsOnBottom = true;
|
||
|
mPanelHeader.mBaseHeight = 84;
|
||
|
AddWidget(mPanelHeader);
|
||
|
ResizeComponents();
|
||
|
}
|
||
|
|
||
|
void LoadOldVer()
|
||
|
{
|
||
|
Debug.Assert(mOldVerLoadExecutionInstance == null);
|
||
|
mOldVerLoadExecutionInstance = gApp.DoRun(null, mOldVerLoadCmd, gApp.mInstallDir, .None);
|
||
|
mOldVerLoadExecutionInstance.mAutoDelete = false;
|
||
|
|
||
|
CloseHeader();
|
||
|
|
||
|
mPanelHeader = new PanelHeader();
|
||
|
String fileName = scope String();
|
||
|
Path.GetFileName(mFilePath, fileName);
|
||
|
String headerStr = scope String();
|
||
|
headerStr.AppendF("Retrieving {0} via command: {1}", fileName, mOldVerLoadCmd);
|
||
|
mPanelHeader.Label = headerStr;
|
||
|
mPanelHeader.mTooltipText = new String(mOldVerLoadCmd);
|
||
|
|
||
|
var button = mPanelHeader.AddButton("Cancel");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
mOldVerLoadExecutionInstance.Cancel();
|
||
|
});
|
||
|
button = mPanelHeader.AddButton("Always Run");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
LoadOldVer();
|
||
|
});
|
||
|
//button = mPanelHeader.AddButton("Run On This ");
|
||
|
//button = mPanelHeader.AddButton("Never Run");
|
||
|
|
||
|
mPanelHeader.mButtonsOnBottom = true;
|
||
|
//mPanelHeader.mBaseHeight = 72;
|
||
|
AddWidget(mPanelHeader);
|
||
|
ResizeComponents();
|
||
|
}
|
||
|
|
||
|
void InitSplitter()
|
||
|
{
|
||
|
mSplitter = new PanelSplitter(mSplitTopPanel, this);
|
||
|
mSplitter.mSplitAction = new => SplitView;
|
||
|
mSplitter.mUnsplitAction = new => UnsplitView;
|
||
|
AddWidget(mSplitter);
|
||
|
}
|
||
|
|
||
|
public void Reload_Old()
|
||
|
{
|
||
|
var text = scope String();
|
||
|
if (gApp.LoadTextFile(mFilePath, text) case .Err)
|
||
|
{
|
||
|
gApp.Fail(StackStringFormat!("Failed to open file '{0}'", mFilePath));
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//mEditWidget.Content.ClearText();
|
||
|
|
||
|
int line;
|
||
|
int lineChar;
|
||
|
mEditWidget.Content.GetCursorLineChar(out line, out lineChar);
|
||
|
//float vertPos = mEditWidget.mVertPos.v;
|
||
|
|
||
|
text.Replace("\r", "");
|
||
|
|
||
|
var replaceSourceAction = new ReplaceSourceAction(mEditWidget.Content, text, true);
|
||
|
mEditWidget.Content.mData.mUndoManager.Add(replaceSourceAction);
|
||
|
replaceSourceAction.Redo();
|
||
|
|
||
|
//replaceSourceActionClearTrackedElements();
|
||
|
|
||
|
//UndoBatchStart undoBatchStart = new UndoBatchStart("reload");
|
||
|
//mEditWidget.Content.mUndoManager.Add(undoBatchStart);
|
||
|
//mEditWidget.Content.SelectAll();
|
||
|
//mEditWidget.Content.DeleteSelection();
|
||
|
//mEditWidget.Content.InsertAtCursor(text);
|
||
|
|
||
|
|
||
|
/*mEditWidget.Content.MoveCursorTo(line, lineChar, true);
|
||
|
mEditWidget.VertScrollTo(vertPos);
|
||
|
|
||
|
mEditWidget.mVertPos.mPct = 1.0f;
|
||
|
mEditWidget.UpdateContentPosition();*/
|
||
|
|
||
|
QueueFullRefresh(false);
|
||
|
|
||
|
//mLastFileTextVersion = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
}
|
||
|
|
||
|
/*struct TextLineSegment
|
||
|
{
|
||
|
public int mIndex;
|
||
|
public int mLength;
|
||
|
public String mLine;
|
||
|
}*/
|
||
|
|
||
|
public void Reload()
|
||
|
{
|
||
|
//TODO: Why did we ClearTrackedElements here? It caused reloads to not move breakpoints and stuff...
|
||
|
//ClearTrackedElements();
|
||
|
if (mLoadFailed)
|
||
|
{
|
||
|
Show(mFilePath);
|
||
|
ResizeComponents();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
var editWidgetContent = (SourceEditWidgetContent)mEditWidget.mEditWidgetContent;
|
||
|
Debug.Assert(!editWidgetContent.mIgnoreSetHistory);
|
||
|
editWidgetContent.mIgnoreSetHistory = true;
|
||
|
if (mEditData != null)
|
||
|
{
|
||
|
mEditData.Reload();
|
||
|
gApp.FileChanged(mEditData);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
editWidgetContent.Reload(mFilePath);
|
||
|
}
|
||
|
editWidgetContent.mIgnoreSetHistory = false;
|
||
|
QueueFullRefresh(false);
|
||
|
#if IDE_C_SUPPORT
|
||
|
mClangSourceChanged = false;
|
||
|
#endif
|
||
|
//mLastFileTextVersion = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
if (mEditData != null)
|
||
|
mEditData.mLastFileTextVersion = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
|
||
|
CheckBinary();
|
||
|
}
|
||
|
|
||
|
void RetryLoad()
|
||
|
{
|
||
|
var prevHash = mLoadedHash;
|
||
|
|
||
|
Reload();
|
||
|
if (mRequestedLineAndColumn != null)
|
||
|
ShowFileLocation(-1, mRequestedLineAndColumn.mValue.mLine, mRequestedLineAndColumn.mValue.mColumn, .Always);
|
||
|
FocusEdit();
|
||
|
|
||
|
if ((!(prevHash case .None)) && (prevHash != mLoadedHash))
|
||
|
ShowWrongHash();
|
||
|
|
||
|
if (!mLoadFailed)
|
||
|
{
|
||
|
gApp.RehupStepFilters();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void RefusedReload()
|
||
|
{
|
||
|
if (mEditData != null)
|
||
|
{
|
||
|
mEditData.mHadRefusedFileChange = true;
|
||
|
DeleteAndNullify!(mEditData.mQueuedContent);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool Show(ProjectItem projectItem, bool silentFail = false)
|
||
|
{
|
||
|
mProjectSource = (ProjectSource)projectItem;
|
||
|
if (projectItem is ProjectSource)
|
||
|
{
|
||
|
var fullPath = scope String();
|
||
|
mProjectSource.GetFullImportPath(fullPath);
|
||
|
return Show(fullPath, silentFail);
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
public void PathChanged(String path)
|
||
|
{
|
||
|
if (mFilePath != null)
|
||
|
IDEApp.sApp.mFileWatcher.RemoveWatch(mFilePath);
|
||
|
mFilePath.Set(path);
|
||
|
IDEApp.sApp.mFileWatcher.WatchFile(path);
|
||
|
mWantsFullRefresh = true;
|
||
|
|
||
|
bool wasSource = mIsBeefSource;
|
||
|
mIsBeefSource = IDEApp.IsBeefFile(mFilePath);
|
||
|
mIsSourceCode = IDEApp.IsSourceCode(mFilePath);
|
||
|
|
||
|
if ((!mIsSourceCode) && (wasSource))
|
||
|
{
|
||
|
var ewd = mEditWidget.mEditWidgetContent.mData;
|
||
|
for (int i < ewd.mTextLength)
|
||
|
{
|
||
|
ewd.mText[i].mDisplayFlags = 0;
|
||
|
ewd.mText[i].mDisplayTypeId = 0;
|
||
|
}
|
||
|
}
|
||
|
MarkDirty();
|
||
|
}
|
||
|
|
||
|
public void SplitView()
|
||
|
{
|
||
|
if (mPanelHeader != null)
|
||
|
return;
|
||
|
|
||
|
if (mSplitTopPanel != null)
|
||
|
return;
|
||
|
|
||
|
// User requested from menu
|
||
|
if (mSplitter.mSplitPct <= 0)
|
||
|
mSplitter.mSplitPct = 0.3f;
|
||
|
|
||
|
mSplitTopPanel = new SourceViewPanel();
|
||
|
mSplitTopPanel.mSplitBottomPanel = this;
|
||
|
mSplitter.mTopPanel = mSplitTopPanel;
|
||
|
|
||
|
if (mProjectSource != null)
|
||
|
mSplitTopPanel.Show(mProjectSource);
|
||
|
else
|
||
|
mSplitTopPanel.Show(mFilePath, false, mEditData);
|
||
|
mSplitTopPanel.mDesiredVertPos = mEditWidget.mVertPos.v;
|
||
|
mSplitTopPanel.Content.CursorLineAndColumn = Content.CursorLineAndColumn;
|
||
|
|
||
|
QueueFullRefresh(false);
|
||
|
mSplitTopPanel.QueueFullRefresh(false);
|
||
|
|
||
|
AddWidget(mSplitTopPanel);
|
||
|
|
||
|
ResizeComponents();
|
||
|
|
||
|
// Match scroll positions
|
||
|
//mSplitTopPanel.mEditWidget.UpdateScrollbars();
|
||
|
//mSplitTopPanel.mEditWidget.mVertScrollbar.ScrollTo(mEditWidget.mVertPos.v);
|
||
|
//mSplitTopPanel.Content.CursorLineAndColumn = Content.CursorLineAndColumn;
|
||
|
}
|
||
|
|
||
|
public void UnsplitView()
|
||
|
{
|
||
|
//Debug.WriteLine("UnsplitView {0}\n", this);
|
||
|
|
||
|
mSplitter.mTopPanel = null;
|
||
|
mSplitTopPanel.Dispose();
|
||
|
Widget.RemoveAndDelete(mSplitTopPanel);
|
||
|
mSplitTopPanel = null;
|
||
|
|
||
|
ResizeComponents();
|
||
|
}
|
||
|
|
||
|
void ShowOld(SourceViewPanel sourceViewPanel, int hotFileIdx)
|
||
|
{
|
||
|
mEditWidget = IDEApp.sApp.CreateSourceEditWidget();
|
||
|
SetupEditWidget();
|
||
|
|
||
|
var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
|
||
|
sourceEditWidgetContent.SetOldVersionColors(true);
|
||
|
|
||
|
String.NewOrSet!(mFilePath, sourceViewPanel.mFilePath);
|
||
|
// We don't set mProjectSource because we don't want to actually perform a Classify, since
|
||
|
// this old version will reference NEW class info, which is a version mismatch
|
||
|
// mProjectSource = sourceViewPanel.mProjectSource;
|
||
|
mIsSourceCode = sourceViewPanel.mIsSourceCode;
|
||
|
mIsBeefSource = sourceViewPanel.mIsBeefSource;
|
||
|
mJustShown = true;
|
||
|
mWantsFullClassify = true;
|
||
|
mWantsFullClassify = true;
|
||
|
|
||
|
var projectSourceCompileInstance = IDEApp.sApp.mWorkspace.GetProjectSourceCompileInstance(sourceViewPanel.mProjectSource, hotFileIdx);
|
||
|
mEditWidget.Content.AppendText(projectSourceCompileInstance.mSource);
|
||
|
Debug.Assert(mEditWidget.Content.mData.mTextLength == projectSourceCompileInstance.mSource.Length);
|
||
|
mEditWidget.Content.mData.mTextIdData.DuplicateFrom(ref projectSourceCompileInstance.mSourceCharIdData);
|
||
|
mEditWidget.Content.mIsReadOnly = true;
|
||
|
mHotFileIdx = hotFileIdx;
|
||
|
mIsOldCompiledVersion = mHotFileIdx < IDEApp.sApp.mWorkspace.GetHighestCompileIdx();
|
||
|
mCurrentVersionPanel = sourceViewPanel;
|
||
|
|
||
|
mPanelHeader = new PanelHeader();
|
||
|
if (mIsOldCompiledVersion)
|
||
|
mPanelHeader.Label = "A previous version of this method is currently executing. The new version will be used when next called.";
|
||
|
else
|
||
|
mPanelHeader.Label = "This method has changed since compiling. Recompile to hot swap changes.";
|
||
|
if (!sPreviousVersionWarningShown)
|
||
|
{
|
||
|
mPanelHeader.Flash();
|
||
|
sPreviousVersionWarningShown = true;
|
||
|
}
|
||
|
var button = mPanelHeader.AddButton("Show &Current");
|
||
|
button.mOnMouseClick.Add(new (evt) =>
|
||
|
{
|
||
|
ShowCurrent();
|
||
|
/*var app = IDEApp.sApp;
|
||
|
int callStackIdx = app.mDebugger.mSelectedCallStackIdx;
|
||
|
float scrollTopDelta = mEditWidget.Content.GetCursorScreenRelY();
|
||
|
bool isPaused = app.mDebugger.IsPaused();
|
||
|
|
||
|
// Do another setPCLocation to make sure our cursor is at the PC position
|
||
|
if (isPaused)
|
||
|
app.ShowPCLocation(callStackIdx, true);
|
||
|
sourceViewPanel.mJustShown = true;
|
||
|
sourceViewPanel.CloseOldVersion();
|
||
|
if (isPaused)
|
||
|
app.ShowPCLocation(callStackIdx, true);
|
||
|
sourceViewPanel.mEditWidget.Content.SetCursorScreenRelY(scrollTopDelta + mPanelHeader.mHeight);*/
|
||
|
});
|
||
|
AddWidget(mPanelHeader);
|
||
|
|
||
|
InitSplitter();
|
||
|
}
|
||
|
|
||
|
public void ShowCurrent()
|
||
|
{
|
||
|
if (mOldVersionPanel != null)
|
||
|
{
|
||
|
mOldVersionPanel.ShowCurrent();
|
||
|
return;
|
||
|
}
|
||
|
if (mCurrentVersionPanel == null)
|
||
|
return;
|
||
|
|
||
|
var app = IDEApp.sApp;
|
||
|
int32 callStackIdx = app.mDebugger.mActiveCallStackIdx;
|
||
|
float scrollTopDelta = mEditWidget.Content.GetCursorScreenRelY();
|
||
|
bool isPaused = app.mDebugger.IsPaused();
|
||
|
|
||
|
// Do another setPCLocation to make sure our cursor is at the PC position
|
||
|
if (isPaused)
|
||
|
app.ShowPCLocation(callStackIdx, true);
|
||
|
mCurrentVersionPanel.mJustShown = true;
|
||
|
mCurrentVersionPanel.CloseOldVersion();
|
||
|
if (isPaused)
|
||
|
app.ShowPCLocation(callStackIdx, true);
|
||
|
mCurrentVersionPanel.mEditWidget.Content.SetCursorScreenRelY(scrollTopDelta + mPanelHeader.mHeight);
|
||
|
}
|
||
|
|
||
|
void CloseOldVersion()
|
||
|
{
|
||
|
if (mEditWidget == null)
|
||
|
{
|
||
|
// What to do?
|
||
|
}
|
||
|
|
||
|
if (mOldVersionPanel != null)
|
||
|
{
|
||
|
mEditWidget.mVisible = true;
|
||
|
if (mOldVersionPanel.mParent != null)
|
||
|
mOldVersionPanel.RemoveSelf();
|
||
|
mOldVersionPanel.Dispose();
|
||
|
BFApp.sApp.DeferDelete(mOldVersionPanel);
|
||
|
//delete mOldVersionPanel;
|
||
|
mOldVersionPanel = null;
|
||
|
if (mWidgetWindow != null)
|
||
|
FocusEdit();
|
||
|
ResizeComponents();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void ShowHotFileIdx(int hotFileIdx)
|
||
|
{
|
||
|
if (mLoadFailed)
|
||
|
return;
|
||
|
|
||
|
bool isOldCompiledVersion = (hotFileIdx != -1) && (hotFileIdx < IDEApp.sApp.mWorkspace.GetHighestCompileIdx());
|
||
|
if ((mOldVersionPanel != null) && (mOldVersionPanel.mHotFileIdx == hotFileIdx) && (mOldVersionPanel.mIsOldCompiledVersion == isOldCompiledVersion))
|
||
|
return;
|
||
|
CloseOldVersion();
|
||
|
//if (hotFileIdx != -1)
|
||
|
if (isOldCompiledVersion)
|
||
|
{
|
||
|
mEditWidget.mVisible = false;
|
||
|
mOldVersionPanel = new SourceViewPanel();
|
||
|
if (mProjectSource != null)
|
||
|
{
|
||
|
mOldVersionPanel.ShowOld(this, hotFileIdx);
|
||
|
AddWidget(mOldVersionPanel);
|
||
|
mOldVersionPanel.FocusEdit();
|
||
|
ResizeComponents();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public void GetCursorPosition(out int32 line, out int32 column)
|
||
|
{
|
||
|
var lineAndCol = mEditWidget.Content.CursorLineAndColumn;
|
||
|
line = lineAndCol.mLine;
|
||
|
column = lineAndCol.mColumn;
|
||
|
}
|
||
|
|
||
|
int GetDrawLineNum(Breakpoint breakpoint)
|
||
|
{
|
||
|
int breakpointLineNum;
|
||
|
|
||
|
/*if (mIsBeefSource)
|
||
|
breakpointLineNum = breakpoint.GetLineNum();
|
||
|
else
|
||
|
breakpointLineNum = breakpoint.mLineNum;*/
|
||
|
// Why did we have "mIsBeefSource" check? This broke our ability to 'move' the breakpoint down
|
||
|
// onto the actual executable line
|
||
|
breakpointLineNum = breakpoint.GetLineNum();
|
||
|
|
||
|
int drawLineNum = breakpointLineNum;
|
||
|
|
||
|
// We want to use "IsBound" instead of "HasNativeBreakpoint" because otherwise when we hot-create a new method
|
||
|
// and then put a breakpoint on it then it'll remap as if it was bound
|
||
|
if ((mIsBeefSource) && (breakpoint.mNativeBreakpoint != null))
|
||
|
{
|
||
|
int compileIdx = gApp.mWorkspace.GetHighestSuccessfulCompileIdx();
|
||
|
//drawLineNum = RemapCompiledToActiveLine(/*breakpoint.mLineNum*/breakpointLineNum);
|
||
|
if (compileIdx != -1)
|
||
|
{
|
||
|
int drawLineColumn = 0;
|
||
|
RemapCompiledToActiveLine(compileIdx, ref drawLineNum, ref drawLineColumn);
|
||
|
if (mHotFileIdx != -1)
|
||
|
{
|
||
|
drawLineNum = RemapActiveLineToHotLine(drawLineNum);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return drawLineNum;
|
||
|
}
|
||
|
|
||
|
public Breakpoint ToggleBreakpointAtCursor(bool forceSet = false, int threadId = -1)
|
||
|
{
|
||
|
var activePanel = GetActivePanel();
|
||
|
if (activePanel != this)
|
||
|
return activePanel.ToggleBreakpointAtCursor(forceSet, threadId);
|
||
|
|
||
|
if (mOldVersionPanel != null)
|
||
|
{
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
DebugManager debugManager = IDEApp.sApp.mDebugger;
|
||
|
|
||
|
int lineIdx;
|
||
|
int lineCharIdx;
|
||
|
mEditWidget.Content.GetLineCharAtIdx(mEditWidget.Content.CursorTextPos, out lineIdx, out lineCharIdx);
|
||
|
|
||
|
bool hadBreakpoint = false;
|
||
|
if (!forceSet)
|
||
|
{
|
||
|
/*WithTrackedElementsAtCursor<Breakpoint>(IDEApp.sApp.mDebugger.mBreakpointList, scope [&] (breakpoint) =>
|
||
|
{
|
||
|
BfLog.LogDbg("SourceViewPanel.ToggleBreakpointAtCursor deleting breakpoint\n");
|
||
|
debugManager.DeleteBreakpoint(breakpoint);
|
||
|
hadBreakpoint = true;
|
||
|
});*/
|
||
|
|
||
|
for (var breakpointView in GetTrackedElementList())
|
||
|
{
|
||
|
var trackedElement = breakpointView.mTrackedElement;
|
||
|
if (var breakpoint = trackedElement as Breakpoint)
|
||
|
{
|
||
|
int drawLineNum = GetDrawLineNum(breakpoint);
|
||
|
if (drawLineNum == lineIdx)
|
||
|
{
|
||
|
BfLog.LogDbg("SourceViewPanel.ToggleBreakpointAtCursor deleting breakpoint\n");
|
||
|
debugManager.DeleteBreakpoint(breakpoint);
|
||
|
hadBreakpoint = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!hadBreakpoint)
|
||
|
{
|
||
|
RecordHistoryLocation();
|
||
|
|
||
|
var editWidgetContent = mEditWidget.Content;
|
||
|
int textPos = mEditWidget.Content.CursorTextPos - lineCharIdx;
|
||
|
lineCharIdx = 0;
|
||
|
|
||
|
// Find first non-space char8
|
||
|
while ((textPos < editWidgetContent.mData.mTextLength) && (((char8)editWidgetContent.mData.mText[textPos].mChar).IsWhiteSpace))
|
||
|
{
|
||
|
textPos++;
|
||
|
lineCharIdx++;
|
||
|
}
|
||
|
|
||
|
int requestedActiveLineIdx = lineIdx;
|
||
|
int curCompileIdx = IDEApp.sApp.mWorkspace.GetHighestCompileIdx();
|
||
|
bool foundPosition = false;
|
||
|
if (gApp.mDebugger.mIsRunning)
|
||
|
foundPosition = RemapActiveToCompiledLine(curCompileIdx, ref lineIdx, ref lineCharIdx);
|
||
|
bool createNow = foundPosition || !mIsBeefSource; // Only be strict about Beef source
|
||
|
Breakpoint newBreakpoint = debugManager.CreateBreakpoint_Create(mFilePath, lineIdx, lineCharIdx, -1);
|
||
|
newBreakpoint.mThreadId = threadId;
|
||
|
debugManager.CreateBreakpoint_Finish(newBreakpoint, createNow);
|
||
|
int newDrawLineNum = GetDrawLineNum(newBreakpoint);
|
||
|
|
||
|
if (!forceSet)
|
||
|
{
|
||
|
for (int32 breakIdx = 0; breakIdx < IDEApp.sApp.mDebugger.mBreakpointList.Count; breakIdx++)
|
||
|
{
|
||
|
var checkBreakpoint = IDEApp.sApp.mDebugger.mBreakpointList[breakIdx];
|
||
|
if ((checkBreakpoint != newBreakpoint) &&
|
||
|
(checkBreakpoint.mFileName == newBreakpoint.mFileName))
|
||
|
{
|
||
|
int checkDrawLineNum = GetDrawLineNum(checkBreakpoint);
|
||
|
if (checkDrawLineNum == newDrawLineNum)
|
||
|
{
|
||
|
BfLog.LogDbg("SourceViewPanel.ToggleBreakpointAtCursor duplicate breakpoint. Deleting breakpoint\n");
|
||
|
|
||
|
// This ended up on the same line as another breakpoint after binding. Hilite the other breakpoint to show there's already one there.
|
||
|
debugManager.DeleteBreakpoint(newBreakpoint);
|
||
|
newBreakpoint = null;
|
||
|
var ewc = mEditWidget.mEditWidgetContent;
|
||
|
LocatorAnim.Show(mEditWidget, ewc.mX + -GS!(15), ewc.mY + newDrawLineNum * ewc.GetLineHeight(0) + GS!(12));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// If we are hot compiling, and the binding of the breakpoint moves the breakpoint (down a few lines presumably), but the text between
|
||
|
// the requested position and the bound position has had changes that haven't been compiled in yet, then we undo the binding so we can
|
||
|
// rebind when we complete the hot compile
|
||
|
if ((newBreakpoint != null) && (gApp.mDebugger.mIsRunning) && (mIsBeefSource) && (mProjectSource != null))
|
||
|
{
|
||
|
int boundLineNum = newBreakpoint.GetLineNum();
|
||
|
int boundColumn = 0;
|
||
|
RemapCompiledToActiveLine(curCompileIdx, ref boundLineNum, ref boundColumn);
|
||
|
if (requestedActiveLineIdx != boundLineNum)
|
||
|
{
|
||
|
int startCheckIdx = editWidgetContent.GetTextIdx(requestedActiveLineIdx, 0);
|
||
|
int endCheckIdx = editWidgetContent.GetTextIdx(boundLineNum, 0);
|
||
|
|
||
|
var textIdData = editWidgetContent.mData.mTextIdData.GetPrepared();
|
||
|
int32 startId = textIdData.GetIdAtIndex(startCheckIdx);
|
||
|
int32 endId = textIdData.GetIdAtIndex(endCheckIdx);
|
||
|
|
||
|
if (var projectSourceInstance = gApp.mWorkspace.GetProjectSourceCompileInstance(mProjectSource, curCompileIdx))
|
||
|
{
|
||
|
if (!textIdData.IsRangeEqual(projectSourceInstance.mSourceCharIdData, startId, endId))
|
||
|
{
|
||
|
newBreakpoint.DisposeNative();
|
||
|
newBreakpoint.mLineNum = (.)requestedActiveLineIdx;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return newBreakpoint;
|
||
|
}
|
||
|
|
||
|
return null;
|
||
|
}
|
||
|
|
||
|
public void ToggleBookmarkAtCursor()
|
||
|
{
|
||
|
if ((mHotFileIdx != -1) || (mOldVersionPanel != null))
|
||
|
return;
|
||
|
if (mFilePath == null)
|
||
|
return;
|
||
|
|
||
|
var activePanel = GetActivePanel();
|
||
|
if (activePanel != this)
|
||
|
{
|
||
|
activePanel.ToggleBookmarkAtCursor();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
BookmarkManager bookmarkManager = IDEApp.sApp.mBookmarkManager;
|
||
|
|
||
|
int lineIdx;
|
||
|
int lineCharIdx;
|
||
|
mEditWidget.Content.GetLineCharAtIdx(mEditWidget.Content.CursorTextPos, out lineIdx, out lineCharIdx);
|
||
|
|
||
|
bool hadBookmark = false;
|
||
|
|
||
|
WithTrackedElementsAtCursor<Bookmark>(IDEApp.sApp.mBookmarkManager.mBookmarkList, scope [&] (bookmark) =>
|
||
|
{
|
||
|
bookmarkManager.DeleteBookmark(bookmark);
|
||
|
hadBookmark = true;
|
||
|
});
|
||
|
|
||
|
if (!hadBookmark)
|
||
|
{
|
||
|
var editWidgetContent = mEditWidget.Content;
|
||
|
int textPos = mEditWidget.Content.CursorTextPos - lineCharIdx;
|
||
|
lineCharIdx = 0;
|
||
|
|
||
|
// Find first non-space char8
|
||
|
while ((textPos < editWidgetContent.mData.mTextLength) && (((char8)editWidgetContent.mData.mText[textPos].mChar).IsWhiteSpace))
|
||
|
{
|
||
|
textPos++;
|
||
|
lineCharIdx++;
|
||
|
}
|
||
|
|
||
|
bookmarkManager.CreateBookmark(mFilePath, lineIdx, lineCharIdx);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void RemapCompiledToActiveLine(int compileInstanceIdx, ref int lineNum, ref int column)
|
||
|
{
|
||
|
let projectSource = FilteredProjectSource;
|
||
|
if (projectSource != null)
|
||
|
{
|
||
|
int32 char8Id = IDEApp.sApp.mWorkspace.GetProjectSourceCharId(projectSource, compileInstanceIdx, lineNum, column);
|
||
|
int char8Idx = mEditWidget.Content.GetCharIdIdx(char8Id);
|
||
|
if (char8Idx != -1)
|
||
|
{
|
||
|
mEditWidget.Content.GetLineCharAtIdx(char8Idx, out lineNum, out column);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool RemapActiveToCompiledLine(int compileInstanceIdx, ref int lineNum, ref int column)
|
||
|
{
|
||
|
var projectSource = FilteredProjectSource;
|
||
|
if (mIsOldCompiledVersion)
|
||
|
projectSource = ((SourceViewPanel)mParent).FilteredProjectSource;
|
||
|
|
||
|
if (projectSource != null)
|
||
|
{
|
||
|
int char8Id = mEditWidget.Content.GetSourceCharIdAtLineChar(lineNum, column);
|
||
|
if (char8Id == 0)
|
||
|
return false;
|
||
|
return IDEApp.sApp.mWorkspace.GetProjectSourceCharIdPosition(projectSource, compileInstanceIdx, char8Id, ref lineNum, ref column);
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
int RemapActiveLineToHotLine(int line)
|
||
|
{
|
||
|
if (mHotFileIdx == -1)
|
||
|
return line;
|
||
|
|
||
|
var activePanel = (SourceViewPanel)mParent;
|
||
|
int32 char8Id = activePanel.mEditWidget.Content.GetSourceCharIdAtLineChar(line, -1);
|
||
|
if (char8Id == -1)
|
||
|
return -1;
|
||
|
int remapIdx = mEditWidget.Content.GetCharIdIdx(char8Id);
|
||
|
if (remapIdx == -1)
|
||
|
return -1;
|
||
|
int remapLine;
|
||
|
int remapLineChar;
|
||
|
mEditWidget.Content.GetLineCharAtIdx(remapIdx, out remapLine, out remapLineChar);
|
||
|
return remapLine;
|
||
|
}
|
||
|
|
||
|
enum LineFlags : uint8
|
||
|
{
|
||
|
None,
|
||
|
BreakpointCountMask = 0x7F,
|
||
|
Boomkmark = 0x80
|
||
|
}
|
||
|
|
||
|
public override void Draw(Graphics g)
|
||
|
{
|
||
|
base.Draw(g);
|
||
|
|
||
|
// If we're trying to time autocomplete
|
||
|
/*var bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
if (bfSystem.mIsTiming)
|
||
|
{
|
||
|
bfSystem.StopTiming();
|
||
|
bfSystem.DbgPrintTimings();
|
||
|
}*/
|
||
|
|
||
|
if (GetOldVersionPanel() != null)
|
||
|
return;
|
||
|
|
||
|
if (mLoadFailed)
|
||
|
return;
|
||
|
|
||
|
DarkEditWidgetContent darkEditWidgetContent = (DarkEditWidgetContent)mEditWidget.Content;
|
||
|
|
||
|
g.SetFont(IDEApp.sApp.mTinyCodeFont);
|
||
|
|
||
|
using (g.PushClip(0, mEditWidget.mY, mWidth, mEditWidget.mHeight - GS!(20)))
|
||
|
{
|
||
|
using (g.PushTranslate(0, mEditWidget.mY + mEditWidget.Content.Y + GS!(2)))
|
||
|
{
|
||
|
float lineSpacing = darkEditWidgetContent.mFont.GetLineSpacing();
|
||
|
float editX = GetEditX();
|
||
|
|
||
|
int lineStart = (int)((-mEditWidget.Content.Y) / lineSpacing) - 1;
|
||
|
int lineEnd = Math.Min(darkEditWidgetContent.GetLineCount(), lineStart + (int)(mHeight / lineSpacing) + 3);
|
||
|
if (lineEnd <= lineStart)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
LineFlags[] lineFlags = scope LineFlags[lineEnd - lineStart];
|
||
|
|
||
|
for (var breakpointView in GetTrackedElementList())
|
||
|
{
|
||
|
var trackedElement = breakpointView.mTrackedElement;
|
||
|
var breakpoint = trackedElement as Breakpoint;
|
||
|
var bookmark = trackedElement as Bookmark;
|
||
|
if (breakpoint != null)
|
||
|
{
|
||
|
int drawLineNum = GetDrawLineNum(breakpoint);
|
||
|
|
||
|
if ((drawLineNum < lineStart) || (drawLineNum >= lineEnd))
|
||
|
continue;
|
||
|
var curLineFlags = ref lineFlags[drawLineNum - lineStart];
|
||
|
int breakpointCount = (.)(curLineFlags & .BreakpointCountMask);
|
||
|
curLineFlags++;
|
||
|
|
||
|
float iconX = Math.Max(GS!(-2), mEditWidget.mX - GS!(24)) + breakpointCount*-GS!(2);
|
||
|
float iconY = 0 + drawLineNum * lineSpacing + (lineSpacing - DarkTheme.sUnitSize + GS!(5)) / 2;
|
||
|
|
||
|
// Just leave last digit visible
|
||
|
/*using (g.PushColor(0xFF595959))
|
||
|
g.FillRect(4, iconY, editX - 14, 20);*/
|
||
|
|
||
|
using (g.PushColor((breakpointCount % 2 == 0) ? 0xFFFFFFFF : 0xFFC0C0C0))
|
||
|
using (g.PushTranslate(iconX, iconY))
|
||
|
{
|
||
|
breakpoint.Draw(g, mIsOldCompiledVersion);
|
||
|
}
|
||
|
}
|
||
|
else if (bookmark != null)
|
||
|
{
|
||
|
|
||
|
if (mHotFileIdx == -1)
|
||
|
{
|
||
|
int32 drawLineNum = bookmark.mLineNum;
|
||
|
if ((drawLineNum < lineStart) || (drawLineNum >= lineEnd))
|
||
|
continue;
|
||
|
//hadLineIcon[drawLineNum - lineStart] = true;
|
||
|
g.Draw(DarkTheme.sDarkTheme.GetImage(.IconBookmark), Math.Max(GS!(-5), mEditWidget.mX - GS!(30)),
|
||
|
0 + bookmark.mLineNum * lineSpacing);
|
||
|
|
||
|
var curLineFlags = ref lineFlags[drawLineNum - lineStart];
|
||
|
curLineFlags |= .Boomkmark;
|
||
|
//FAIL
|
||
|
}
|
||
|
}
|
||
|
|
||
|
////
|
||
|
/*var historyEntry = trackedElement as HistoryEntry;
|
||
|
if (historyEntry != null)
|
||
|
{
|
||
|
if (mHotFileIdx == -1)
|
||
|
{
|
||
|
float xPos;
|
||
|
float yPos;
|
||
|
darkEditWidgetContent.GetTextCoordAtLineChar(historyEntry.mLineNum, historyEntry.mColumn, out xPos, out yPos);
|
||
|
|
||
|
using (g.PushColor(0x60FFFFFF))
|
||
|
g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.IconBookmark), mEditWidget.mX + xPos,
|
||
|
0 + historyEntry.mLineNum * lineSpacing);
|
||
|
}
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
if (gApp.mSettings.mEditorSettings.mShowLineNumbers)
|
||
|
{
|
||
|
String lineStr = scope String(16);
|
||
|
using (g.PushColor(0x80FFFFFF))
|
||
|
{
|
||
|
for (int lineIdx = lineStart; lineIdx < lineEnd; lineIdx++)
|
||
|
{
|
||
|
lineStr.Clear();
|
||
|
|
||
|
int maxLineChars = Int32.MaxValue;
|
||
|
|
||
|
let curLineFlags = lineFlags[lineIdx - lineStart];
|
||
|
if ((uint8)(curLineFlags & .BreakpointCountMask) > 0)
|
||
|
maxLineChars = 1;
|
||
|
else if (curLineFlags.HasFlag(.Boomkmark))
|
||
|
maxLineChars = 2;
|
||
|
|
||
|
switch (maxLineChars)
|
||
|
{
|
||
|
case 0:
|
||
|
case 1: lineStr.AppendF("{0}", (lineIdx + 1) % 10);
|
||
|
case 2: lineStr.AppendF("{0}", (lineIdx + 1) % 100);
|
||
|
default: lineStr.AppendF("{0}", lineIdx + 1);
|
||
|
}
|
||
|
g.DrawString(lineStr, 0, GS!(2) + lineIdx * lineSpacing, FontAlign.Right, editX - GS!(2));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (IDEApp.sApp.mExecutionPaused)
|
||
|
{
|
||
|
int addr;
|
||
|
String fileName = scope String(Path.MaxPath);
|
||
|
int hotIdx;
|
||
|
int defLineStart;
|
||
|
int defLineEnd;
|
||
|
int lineNum;
|
||
|
int column;
|
||
|
int language;
|
||
|
int stackSize;
|
||
|
DebugManager.FrameFlags flags;
|
||
|
IDEApp.sApp.mDebugger.GetStackFrameInfo(IDEApp.sApp.mDebugger.mActiveCallStackIdx, null, out addr, fileName, out hotIdx, out defLineStart, out defLineEnd, out lineNum, out column, out language, out stackSize, out flags);
|
||
|
IDEUtils.FixFilePath(fileName);
|
||
|
int hashPos = fileName.IndexOf('#');
|
||
|
if (hashPos != -1)
|
||
|
fileName.RemoveToEnd(hashPos);
|
||
|
if (FileNameMatches(fileName))
|
||
|
{
|
||
|
RemapCompiledToActiveLine(hotIdx, ref lineNum, ref column);
|
||
|
Image img;
|
||
|
if (IDEApp.sApp.mDebugger.mActiveCallStackIdx == 0)
|
||
|
{
|
||
|
if (flags.HasFlag(.Optimized))
|
||
|
img = DarkTheme.sDarkTheme.GetImage(.LinePointer_Opt);
|
||
|
else
|
||
|
img = DarkTheme.sDarkTheme.GetImage(.LinePointer);
|
||
|
}
|
||
|
else
|
||
|
img = DarkTheme.sDarkTheme.GetImage(.LinePointer_Prev);
|
||
|
|
||
|
// If our step/continue doesn't actually change the line-pointer position, then we
|
||
|
// want to give just the littlest 'flash' of the cursor to indicate to the user
|
||
|
// that something actually happened. The most common case is a breakpoint that
|
||
|
// gets hit over and over F5 (continue).
|
||
|
bool doDraw = false;
|
||
|
if ((mLinePointerDrawData.mImage != img) ||
|
||
|
(mLinePointerDrawData.mLine != lineNum))
|
||
|
{
|
||
|
mLinePointerDrawData.mImage = img;
|
||
|
mLinePointerDrawData.mLine = (.)lineNum;
|
||
|
doDraw = true;
|
||
|
}
|
||
|
else if ((mLinePointerDrawData.mDebuggerContinueIdx == gApp.mDebuggerContinueIdx) ||
|
||
|
(gApp.mUpdateCnt - mLinePointerDrawData.mUpdateCnt >= 3))
|
||
|
{
|
||
|
doDraw = true;
|
||
|
}
|
||
|
|
||
|
if (doDraw)
|
||
|
{
|
||
|
mLinePointerDrawData.mUpdateCnt = gApp.mUpdateCnt;
|
||
|
mLinePointerDrawData.mDebuggerContinueIdx = gApp.mDebuggerContinueIdx;
|
||
|
g.Draw(img, mEditWidget.mX - GS!(20),
|
||
|
0 + lineNum * lineSpacing);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool drawLock = mSplitBottomPanel == null;
|
||
|
if (drawLock)
|
||
|
{
|
||
|
IDEUtils.DrawLock(g, mEditWidget.mX - GS!(20), mHeight - GS!(20), IsReadOnly, mLockFlashPct);
|
||
|
}
|
||
|
|
||
|
/*using (g.PushColor(0x80FF0000))
|
||
|
g.FillRect(0, 0, mWidth, mHeight);*/
|
||
|
}
|
||
|
|
||
|
/*void UpdateCharData()
|
||
|
{
|
||
|
var bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
bfSystem.PerfZoneStart("UpdateCharData");
|
||
|
|
||
|
// Inject new char8 attributes into text
|
||
|
uint highestSrcCharId = 0;
|
||
|
for (int i = 0; i < mProcessingCharData.Length; i++)
|
||
|
{
|
||
|
uint char8Id = mProcessingCharData[i].mCharId;
|
||
|
if (char8Id > highestSrcCharId)
|
||
|
highestSrcCharId = char8Id;
|
||
|
}
|
||
|
|
||
|
int srcIdx = 0;
|
||
|
int destIdx = 0;
|
||
|
var destText = mEditWidget.Content.mText;
|
||
|
int destTextLength = mEditWidget.Content.mTextLength;
|
||
|
while ((srcIdx < mProcessingCharData.Length) && (destIdx < destTextLength))
|
||
|
{
|
||
|
if (destText[destIdx].mCharId > highestSrcCharId)
|
||
|
{
|
||
|
// This is new text since we did the background compile, skip
|
||
|
destIdx++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (mProcessingCharData[srcIdx].mCharId != destText[destIdx].mCharId)
|
||
|
{
|
||
|
// Id doesn't match, char8acter must have been deleted
|
||
|
srcIdx++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Debug.Assert(destText[destIdx].mChar == mProcessingCharData[srcIdx].mChar);
|
||
|
|
||
|
if (destText[destIdx].mDisplayPassId == (byte)SourceDisplayId.AutoComplete)
|
||
|
{
|
||
|
// Autocomplete beat us to it
|
||
|
destText[destIdx].mDisplayPassId = (byte)SourceDisplayId.Cleared;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
byte prevFlags = destText[destIdx].mDisplayFlags;
|
||
|
destText[destIdx] = mProcessingCharData[srcIdx];
|
||
|
destText[destIdx].mDisplayFlags = (byte)
|
||
|
((prevFlags & (byte)SourceElementFlags.EditorFlags_Mask) |
|
||
|
(destText[destIdx].mDisplayFlags & (byte)SourceElementFlags.CompilerFlags_Mask));
|
||
|
}
|
||
|
srcIdx++;
|
||
|
destIdx++;
|
||
|
}
|
||
|
|
||
|
mProcessingCharData = null;
|
||
|
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
}*/
|
||
|
|
||
|
void UpdateCharData(ref EditWidgetContent.CharData[] charData, ref IdSpan charIdData, uint8 replaceFlags, bool flagsOnly)
|
||
|
{
|
||
|
//Debug.WriteLine("UpdateCharData: {0}", char8Data);
|
||
|
|
||
|
scope AutoBeefPerf("SourceViewPanel.UpdateCharData");
|
||
|
|
||
|
charIdData.Prepare();
|
||
|
var editTextIdData = ref mEditWidget.Content.mData.mTextIdData;
|
||
|
editTextIdData.Prepare();
|
||
|
|
||
|
// Inject new char8 attributes into text
|
||
|
int32 highestSrcCharId = 0;
|
||
|
int32 srcCharId = 1;
|
||
|
|
||
|
//string dbgStr = "";
|
||
|
int srcEncodeIdx = 0;
|
||
|
//dbgStr += "Src: ";
|
||
|
while (true)
|
||
|
{
|
||
|
int32 cmd = Utils.DecodeInt(charIdData.mData, ref srcEncodeIdx);
|
||
|
if (cmd > 0)
|
||
|
{
|
||
|
srcCharId = cmd;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
srcCharId += -cmd;
|
||
|
highestSrcCharId = Math.Max(highestSrcCharId, srcCharId - 1);
|
||
|
if (cmd == 0)
|
||
|
break;
|
||
|
}
|
||
|
//dbgStr += " " + cmd;
|
||
|
}
|
||
|
|
||
|
int destEncodeIdx = 0;
|
||
|
/*dbgStr += " Dest: ";
|
||
|
while (true)
|
||
|
{
|
||
|
int cmd = Utils.DecodeInt(mProcessCharIdData, ref destEncodeIdx);
|
||
|
if (cmd == 0)
|
||
|
break;
|
||
|
dbgStr += " " + cmd;
|
||
|
}*/
|
||
|
//Debug.WriteLine(dbgStr);
|
||
|
|
||
|
int32 srcIdx = 0;
|
||
|
int32 destIdx = 0;
|
||
|
var destText = mEditWidget.Content.mData.mText;
|
||
|
int32 destTextLength = mEditWidget.Content.mData.mTextLength;
|
||
|
|
||
|
srcEncodeIdx = 0;
|
||
|
int32 srcSpanLeft = 0;
|
||
|
srcCharId = 1;
|
||
|
destEncodeIdx = 0;
|
||
|
int32 destSpanLeft = 0;
|
||
|
int32 destCharId = 1;
|
||
|
|
||
|
while ((srcIdx < charData.Count) && (destIdx < destTextLength))
|
||
|
{
|
||
|
while (srcSpanLeft == 0)
|
||
|
{
|
||
|
int32 cmd = Utils.DecodeInt(charIdData.mData, ref srcEncodeIdx);
|
||
|
if (cmd > 0)
|
||
|
srcCharId = cmd;
|
||
|
else
|
||
|
srcSpanLeft = -cmd;
|
||
|
}
|
||
|
|
||
|
while (destSpanLeft == 0)
|
||
|
{
|
||
|
int32 cmd = Utils.DecodeInt(editTextIdData.mData, ref destEncodeIdx);
|
||
|
if (cmd > 0)
|
||
|
destCharId = cmd;
|
||
|
else
|
||
|
destSpanLeft = -cmd;
|
||
|
}
|
||
|
|
||
|
if (destCharId > highestSrcCharId)
|
||
|
{
|
||
|
// This is new text since we did the background compile, skip
|
||
|
destIdx++;
|
||
|
destSpanLeft--;
|
||
|
destCharId++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (srcCharId != destCharId)
|
||
|
{
|
||
|
// Id doesn't match, character must have been deleted
|
||
|
srcIdx++;
|
||
|
srcSpanLeft--;
|
||
|
srcCharId++;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
Debug.Assert(destCharId == srcCharId);
|
||
|
Debug.Assert(destText[destIdx].mChar == charData[srcIdx].mChar);
|
||
|
|
||
|
if (destText[destIdx].mDisplayPassId == (uint8)SourceDisplayId.AutoComplete)
|
||
|
{
|
||
|
// Autocomplete beat us to it
|
||
|
destText[destIdx].mDisplayPassId = (uint8)SourceDisplayId.Cleared;
|
||
|
}
|
||
|
else if (charData[srcIdx].mDisplayTypeId == (uint8)SourceDisplayId.SkipResult)
|
||
|
{
|
||
|
//
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
uint8 prevFlags = destText[destIdx].mDisplayFlags;
|
||
|
if (!flagsOnly)
|
||
|
destText[destIdx] = charData[srcIdx];
|
||
|
destText[destIdx].mDisplayFlags = (uint8)
|
||
|
((prevFlags & ~replaceFlags) |
|
||
|
(charData[srcIdx].mDisplayFlags & replaceFlags));
|
||
|
}
|
||
|
|
||
|
srcIdx++;
|
||
|
srcSpanLeft--;
|
||
|
srcCharId++;
|
||
|
destIdx++;
|
||
|
destSpanLeft--;
|
||
|
destCharId++;
|
||
|
}
|
||
|
|
||
|
delete charData;
|
||
|
charData = null;
|
||
|
charIdData.Dispose();
|
||
|
}
|
||
|
|
||
|
public override void ShowQuickFind(bool isReplace)
|
||
|
{
|
||
|
//RecordHistoryLocation();
|
||
|
var activePanel = GetActivePanel();
|
||
|
if (Content.Data.mCurQuickFind != null)
|
||
|
{
|
||
|
Content.Data.mCurQuickFind.Close();
|
||
|
}
|
||
|
|
||
|
if (activePanel != this)
|
||
|
{
|
||
|
activePanel.ShowQuickFind(isReplace);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*if (mOldVersionPanel != null)
|
||
|
{
|
||
|
mOldVersionPanel.ShowQuickFind(isReplace);
|
||
|
return;
|
||
|
}*/
|
||
|
|
||
|
if (mRenameSymbolDialog != null)
|
||
|
mRenameSymbolDialog.Close();
|
||
|
|
||
|
base.ShowQuickFind(isReplace);
|
||
|
|
||
|
Content.Data.mCurQuickFind = mQuickFind;
|
||
|
}
|
||
|
|
||
|
public override void FindNext(int32 dir)
|
||
|
{
|
||
|
var activePanel = GetActivePanel();
|
||
|
if (activePanel != this)
|
||
|
{
|
||
|
activePanel.FindNext();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (mOldVersionPanel != null)
|
||
|
{
|
||
|
mOldVersionPanel.FindNext(dir);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
base.FindNext(dir);
|
||
|
}
|
||
|
|
||
|
public void ReformatDocument()
|
||
|
{
|
||
|
if (!mIsBeefSource)
|
||
|
return;
|
||
|
|
||
|
var bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
if (bfSystem == null)
|
||
|
return;
|
||
|
var parser = bfSystem.CreateEmptyParser(null);
|
||
|
defer delete parser;
|
||
|
var text = scope String();
|
||
|
mEditWidget.GetText(text);
|
||
|
parser.SetSource(text, mFilePath);
|
||
|
var passInstance = bfSystem.CreatePassInstance();
|
||
|
defer delete passInstance;
|
||
|
parser.Parse(passInstance, false);
|
||
|
parser.Reduce(passInstance);
|
||
|
mWantsParserCleanup = true;
|
||
|
bool performSanityCheck = false;
|
||
|
#if !DEBUG
|
||
|
performSanityCheck = false;
|
||
|
#endif
|
||
|
|
||
|
if (performSanityCheck)
|
||
|
{
|
||
|
int32[] char8Mapping;
|
||
|
var newText = scope String();
|
||
|
parser.Reformat(-1, -1, out char8Mapping, newText); // Just reprint without reformatting first
|
||
|
|
||
|
int32 lineNum = 0;
|
||
|
int32 lineStart = 0;
|
||
|
for (int32 i = 0; i < Math.Min(newText.Length, text.Length); i++)
|
||
|
{
|
||
|
if (text[i] == '\n')
|
||
|
{
|
||
|
lineNum++;
|
||
|
lineStart = i + 1;
|
||
|
}
|
||
|
if (text[i] != newText[i])
|
||
|
{
|
||
|
IDEApp.sApp.OutputLine("Reformat had a difference at line {0}", (lineNum + 1));
|
||
|
|
||
|
int nextCr = text.IndexOf('\n', lineStart);
|
||
|
IDEApp.sApp.OutputLine(" {0}", scope String(text, lineStart, nextCr - lineStart));
|
||
|
|
||
|
nextCr = newText.IndexOf('\n', lineStart);
|
||
|
IDEApp.sApp.OutputLine(" {0}", scope String(newText, lineStart, nextCr - lineStart));
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (mEditWidget.Content.HasSelection())
|
||
|
parser.ReformatInto(mEditWidget, mEditWidget.Content.mSelection.Value.MinPos, mEditWidget.Content.mSelection.Value.MaxPos);
|
||
|
else
|
||
|
parser.ReformatInto(mEditWidget, 0, text.Length);
|
||
|
|
||
|
//mEditWidget.SetText(newText);
|
||
|
}
|
||
|
|
||
|
public void GotoLine()
|
||
|
{
|
||
|
GoToLineDialog aDialog = new GoToLineDialog("Go To Line", StackStringFormat!("Line Number ({0}-{1})", 1, mEditWidget.Content.GetLineCount()));
|
||
|
aDialog.Init(this);
|
||
|
aDialog.PopupWindow(mWidgetWindow);
|
||
|
}
|
||
|
|
||
|
public void GotoMethod()
|
||
|
{
|
||
|
if (mSplitBottomPanel != null)
|
||
|
mSplitBottomPanel.GotoMethod();
|
||
|
else
|
||
|
mNavigationBar.ShowDropdown();
|
||
|
}
|
||
|
|
||
|
public void FixitAtCursor()
|
||
|
{
|
||
|
if (!mIsBeefSource)
|
||
|
return;
|
||
|
|
||
|
//TODO: Make better, do async, etc...
|
||
|
DoClassify(ResolveType.GetFixits, null, true);
|
||
|
}
|
||
|
|
||
|
public void ShowSymbolReferenceHelper(SymbolReferenceHelper.Kind symbolReferenceKind)
|
||
|
{
|
||
|
if (gApp.mSymbolReferenceHelper != null)
|
||
|
{
|
||
|
gApp.mSymbolReferenceHelper.Close();
|
||
|
}
|
||
|
|
||
|
SymbolReferenceHelper symbolReferenceHelper = new SymbolReferenceHelper();
|
||
|
//symbolReferenceHelper.[Friend]GCMarkMembers();
|
||
|
//Debug.WriteLine("SymbolReferenceHelper {0} ResolveParams:{1}", symbolReferenceHelper, symbolReferenceHelper.[Friend]mResolveParams);
|
||
|
|
||
|
mRenameSymbolDialog = symbolReferenceHelper;
|
||
|
gApp.mSymbolReferenceHelper = symbolReferenceHelper;
|
||
|
BfResolveCompiler.mThreadWorkerHi.WaitForBackground(); // We need to finish up anything on the hi thread worker so we can queue this
|
||
|
symbolReferenceHelper.Init(this, symbolReferenceKind);
|
||
|
if (!symbolReferenceHelper.mFailed)
|
||
|
{
|
||
|
AddWidget(symbolReferenceHelper);
|
||
|
ResizeComponents();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mRenameSymbolDialog.Close();
|
||
|
}
|
||
|
|
||
|
if ((symbolReferenceKind == .Rename) && (let autoComplete = GetAutoComplete()))
|
||
|
autoComplete.Close();
|
||
|
}
|
||
|
|
||
|
public void RenameSymbol()
|
||
|
{
|
||
|
ShowSymbolReferenceHelper(SymbolReferenceHelper.Kind.Rename);
|
||
|
}
|
||
|
|
||
|
public void FindAllReferences()
|
||
|
{
|
||
|
ShowSymbolReferenceHelper(SymbolReferenceHelper.Kind.FindAllReferences);
|
||
|
}
|
||
|
|
||
|
public override void SetVisible(bool visible)
|
||
|
{
|
||
|
base.SetVisible(visible);
|
||
|
mWantsFullClassify = true;
|
||
|
mWantsClassifyAutocomplete = false;
|
||
|
}
|
||
|
|
||
|
public override void RecordHistoryLocation(bool ignoreIfClose = false)
|
||
|
{
|
||
|
var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
sourceEditWidgetContent.RecordHistoryLocation(ignoreIfClose);
|
||
|
}
|
||
|
|
||
|
bool CheckLeftMouseover()
|
||
|
{
|
||
|
if (mWidgetWindow == null)
|
||
|
return false;
|
||
|
if (!mWidgetWindow.mHasMouseInside)
|
||
|
return false;
|
||
|
|
||
|
Point mousePos;
|
||
|
bool mouseoverFired = DarkTooltipManager.CheckMouseover(this, 10, out mousePos);
|
||
|
|
||
|
String tooltipStr = scope String();
|
||
|
|
||
|
if ((DarkTooltipManager.sTooltip != null) && (DarkTooltipManager.sTooltip.mRelWidget == this))
|
||
|
mouseoverFired = true;
|
||
|
|
||
|
if (!mouseoverFired)
|
||
|
return false;
|
||
|
|
||
|
float editX = GetEditX();
|
||
|
if ((mousePos.x < editX - 24) || (mousePos.x > editX - 5))
|
||
|
return false;
|
||
|
|
||
|
DarkEditWidgetContent darkEditWidgetContent = (DarkEditWidgetContent)mEditWidget.Content;
|
||
|
float lineSpacing = darkEditWidgetContent.mFont.GetLineSpacing();
|
||
|
float ofsY = mEditWidget.mY + darkEditWidgetContent.mY + (lineSpacing - DarkTheme.sUnitSize + GS!(5)) / 2;
|
||
|
|
||
|
for (var breakpointView in GetTrackedElementList())
|
||
|
{
|
||
|
var trackedElement = breakpointView.mTrackedElement;
|
||
|
var breakpoint = trackedElement as Breakpoint;
|
||
|
//var bookmark = trackedElement as Bookmark;
|
||
|
|
||
|
if (breakpoint != null)
|
||
|
{
|
||
|
int breakpointLineNum;
|
||
|
if (mIsBeefSource)
|
||
|
breakpointLineNum = breakpoint.GetLineNum();
|
||
|
else
|
||
|
breakpointLineNum = breakpoint.mLineNum;
|
||
|
int drawLineNum = breakpointLineNum;
|
||
|
|
||
|
float iconY = ofsY + drawLineNum * lineSpacing;
|
||
|
if ((mousePos.y > iconY) && (mousePos.y < iconY + GS!(20)))
|
||
|
{
|
||
|
if (!tooltipStr.IsEmpty)
|
||
|
tooltipStr.Append("\n\n");
|
||
|
|
||
|
breakpoint.ToString_Location(tooltipStr);
|
||
|
|
||
|
if (breakpoint.mThreadId != -1)
|
||
|
tooltipStr.AppendF("\nThread: {0}", breakpoint.mThreadId);
|
||
|
|
||
|
tooltipStr.Append("\nHits: ");
|
||
|
breakpoint.ToString_HitCount(tooltipStr);
|
||
|
|
||
|
if (breakpoint.mLogging != null)
|
||
|
tooltipStr.Append("\nLog: ", breakpoint.mLogging);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (tooltipStr.IsEmpty)
|
||
|
return false;
|
||
|
|
||
|
if ((DarkTooltipManager.sTooltip != null) && (DarkTooltipManager.sTooltip.mText == tooltipStr))
|
||
|
return true;
|
||
|
|
||
|
DarkTooltipManager.ShowTooltip(tooltipStr, this, mousePos.x, mousePos.y);
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
public void GetDebugExpressionAt(int textIdx, String debugExpr)
|
||
|
{
|
||
|
BfSystem bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
let parser = bfSystem.CreateEmptyParser(null);
|
||
|
|
||
|
var text = scope String();
|
||
|
mEditWidget.GetText(text);
|
||
|
parser.SetSource(text, mFilePath);
|
||
|
parser.SetAutocomplete(textIdx);
|
||
|
let passInstance = bfSystem.CreatePassInstance("Mouseover");
|
||
|
parser.SetCompleteParse();
|
||
|
parser.Parse(passInstance, !mIsBeefSource);
|
||
|
parser.Reduce(passInstance);
|
||
|
if (parser.GetDebugExpressionAt(textIdx, debugExpr))
|
||
|
{
|
||
|
if (debugExpr.StartsWith("`"))
|
||
|
debugExpr[0] = ':';
|
||
|
else if (debugExpr.StartsWith(":"))
|
||
|
debugExpr.Clear();
|
||
|
}
|
||
|
delete passInstance;
|
||
|
delete parser;
|
||
|
}
|
||
|
|
||
|
public void UpdateMouseover()
|
||
|
{
|
||
|
if (mWidgetWindow == null)
|
||
|
return;
|
||
|
|
||
|
if (CheckLeftMouseover())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if ((DarkTooltipManager.sTooltip != null) && (DarkTooltipManager.sTooltip.mRelWidget == this))
|
||
|
DarkTooltipManager.CloseTooltip();
|
||
|
|
||
|
if (!CheckAllowHoverWatch())
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/*if ((mHoverWatch != null) && (mHoverWatch.mCloseDelay > 0))
|
||
|
return;*/
|
||
|
var editWidgetContent = mEditWidget.Content;
|
||
|
Point mousePos;
|
||
|
bool mouseoverFired = DarkTooltipManager.CheckMouseover(editWidgetContent, 10, out mousePos);
|
||
|
|
||
|
#unwarn
|
||
|
CompilerBase compiler = ResolveCompiler;
|
||
|
|
||
|
bool hasClangHoverErrorData = false;
|
||
|
|
||
|
#if IDE_C_SUPPORT
|
||
|
hasClangHoverErrorData = mClangHoverErrorData != null;
|
||
|
#endif
|
||
|
if (((mouseoverFired) || (mHoverWatch != null) || (hasClangHoverErrorData)) &&
|
||
|
(mousePos.x >= 0))
|
||
|
{
|
||
|
int line;
|
||
|
int lineChar;
|
||
|
String debugExpr = null;
|
||
|
|
||
|
BfSystem bfSystem = IDEApp.sApp.mBfResolveSystem;
|
||
|
BfPassInstance passInstance = null;
|
||
|
BfParser parser = null;
|
||
|
int textIdx = -1;
|
||
|
|
||
|
bool isOverMessage = false;
|
||
|
float overflowX;
|
||
|
if (editWidgetContent.GetLineCharAtCoord(mousePos.x, mousePos.y, out line, out lineChar, out overflowX))
|
||
|
{
|
||
|
textIdx = editWidgetContent.GetTextIdx(line, lineChar);
|
||
|
|
||
|
int startIdx = editWidgetContent.GetTextIdx(line, lineChar);
|
||
|
|
||
|
uint8 checkFlags = (uint8)SourceElementFlags.Error;
|
||
|
if (!IDEApp.sApp.mDebugger.mIsRunning)
|
||
|
{
|
||
|
// Prioritize debug info over warning when we are debugging
|
||
|
checkFlags |= (uint8)SourceElementFlags.Warning;
|
||
|
}
|
||
|
if ((editWidgetContent.mData.mText[startIdx].mDisplayFlags & checkFlags) != 0)
|
||
|
isOverMessage = true;
|
||
|
|
||
|
bool doSimpleMouseover = false;
|
||
|
|
||
|
if ((editWidgetContent.mSelection != null) &&
|
||
|
(textIdx >= editWidgetContent.mSelection.Value.MinPos) &&
|
||
|
(textIdx < editWidgetContent.mSelection.Value.MaxPos))
|
||
|
{
|
||
|
debugExpr = scope:: String();
|
||
|
editWidgetContent.GetSelectionText(debugExpr);
|
||
|
}
|
||
|
else if (mIsBeefSource)
|
||
|
{
|
||
|
if (bfSystem != null)
|
||
|
{
|
||
|
parser = bfSystem.CreateEmptyParser(null);
|
||
|
|
||
|
var text = scope String();
|
||
|
mEditWidget.GetText(text);
|
||
|
parser.SetSource(text, mFilePath);
|
||
|
parser.SetAutocomplete(textIdx);
|
||
|
passInstance = bfSystem.CreatePassInstance("Mouseover");
|
||
|
parser.SetCompleteParse();
|
||
|
parser.Parse(passInstance, !mIsBeefSource);
|
||
|
parser.Reduce(passInstance);
|
||
|
debugExpr = scope:: String();
|
||
|
if (parser.GetDebugExpressionAt(textIdx, debugExpr))
|
||
|
{
|
||
|
if (debugExpr.StartsWith("`"))
|
||
|
debugExpr[0] = ':';
|
||
|
else if (debugExpr.StartsWith(":"))
|
||
|
debugExpr = null;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if (mIsClang)
|
||
|
doSimpleMouseover = true;
|
||
|
|
||
|
if (doSimpleMouseover)
|
||
|
SimpleMouseover: do
|
||
|
{
|
||
|
int endIdx = startIdx;
|
||
|
String sb = scope:: String();
|
||
|
bool isInvalid = false;
|
||
|
bool prevWasSpace = false;
|
||
|
|
||
|
if (editWidgetContent.mData.mText[startIdx].mChar.IsWhiteSpace)
|
||
|
break;
|
||
|
|
||
|
startIdx--;
|
||
|
while (startIdx > 0)
|
||
|
{
|
||
|
var char8Data = editWidgetContent.mData.mText[startIdx];
|
||
|
if (char8Data.mDisplayTypeId == (uint8)SourceElementType.Comment)
|
||
|
{
|
||
|
if (startIdx == endIdx - 1)
|
||
|
{
|
||
|
// Inside comment
|
||
|
isInvalid = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
char8 c = (char8)char8Data.mChar;
|
||
|
if ((c == ' ') || (c == '\t'))
|
||
|
{
|
||
|
// Ignore
|
||
|
prevWasSpace = true;
|
||
|
}
|
||
|
else if (c == '\n')
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (c == '>')
|
||
|
{
|
||
|
// Is this "->"?
|
||
|
if ((startIdx > 1) && ((char8)editWidgetContent.mData.mText[startIdx - 1].mChar == '-'))
|
||
|
{
|
||
|
sb.Insert(0, "->");
|
||
|
startIdx--;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
else if (c == ':')
|
||
|
{
|
||
|
// Is this "::"?
|
||
|
if ((startIdx > 1) && ((char8)editWidgetContent.mData.mText[startIdx - 1].mChar == ':'))
|
||
|
{
|
||
|
sb.Insert(0, "::");
|
||
|
startIdx--;
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
}
|
||
|
else if (c == '.')
|
||
|
sb.Insert(0, c);
|
||
|
else if ((c == '_') || (c.IsLetterOrDigit))
|
||
|
{
|
||
|
if (prevWasSpace)
|
||
|
break;
|
||
|
sb.Insert(0, c);
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
|
||
|
prevWasSpace = false;
|
||
|
}
|
||
|
}
|
||
|
startIdx--;
|
||
|
}
|
||
|
|
||
|
prevWasSpace = false;
|
||
|
while ((endIdx < editWidgetContent.mData.mTextLength) && (endIdx > startIdx))
|
||
|
{
|
||
|
var char8Data = editWidgetContent.mData.mText[endIdx];
|
||
|
if (char8Data.mDisplayTypeId == (uint8)SourceElementType.Comment)
|
||
|
{
|
||
|
// Ignore
|
||
|
prevWasSpace = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
char8 c = (char8)char8Data.mChar;
|
||
|
if ((c == ' ') || (c == '\t'))
|
||
|
{
|
||
|
// Ignore
|
||
|
prevWasSpace = true;
|
||
|
}
|
||
|
else if ((c == '_') || (c.IsLetterOrDigit))
|
||
|
{
|
||
|
if (prevWasSpace)
|
||
|
break;
|
||
|
|
||
|
sb.Append(c);
|
||
|
}
|
||
|
else
|
||
|
break;
|
||
|
|
||
|
prevWasSpace = false;
|
||
|
}
|
||
|
endIdx++;
|
||
|
}
|
||
|
|
||
|
if (!isInvalid)
|
||
|
debugExpr = sb;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool triedShow = false;
|
||
|
|
||
|
if (mHoverWatch != null)
|
||
|
{
|
||
|
if (debugExpr != null)
|
||
|
{
|
||
|
if (mHoverWatch.mEvalString != debugExpr)
|
||
|
{
|
||
|
mHoverWatch.Close();
|
||
|
mHoverWatch = null;
|
||
|
}
|
||
|
else
|
||
|
triedShow = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (((mHoverWatch == null) && (mouseoverFired)) || (debugExpr == null) || (hasClangHoverErrorData))
|
||
|
{
|
||
|
float x;
|
||
|
float y;
|
||
|
editWidgetContent.GetTextCoordAtLineChar(line, lineChar, out x, out y);
|
||
|
|
||
|
bool hasHoverWatchOpen = (mHoverWatch != null) && (mHoverWatch.mListView != null);
|
||
|
if (mHoverWatch == null)
|
||
|
mHoverWatch = new HoverWatch();
|
||
|
|
||
|
if (debugExpr != null)
|
||
|
triedShow = true;
|
||
|
|
||
|
if ((debugExpr == null) || (isOverMessage) || (!mHoverWatch.Show(this, x, y, debugExpr)))
|
||
|
{
|
||
|
#if IDE_C_SUPPORT
|
||
|
if ((mIsClang) && (textIdx != -1))
|
||
|
{
|
||
|
bool hasErrorFlag = (mEditWidget.Content.mData.mText[textIdx].mDisplayFlags != 0);
|
||
|
|
||
|
if (hasErrorFlag)
|
||
|
{
|
||
|
if (!compiler.IsPerformingBackgroundOperation())
|
||
|
{
|
||
|
bool hadValidError = false;
|
||
|
if (mClangHoverErrorData != null)
|
||
|
{
|
||
|
String[] stringParts = String.StackSplit!(mClangHoverErrorData, '\t');
|
||
|
int startIdx = (int32)int32.Parse(stringParts[0]);
|
||
|
int endIdx = (int32)int32.Parse(stringParts[1]);
|
||
|
if ((textIdx >= startIdx) && (textIdx < endIdx))
|
||
|
{
|
||
|
hadValidError = true;
|
||
|
triedShow = true;
|
||
|
mHoverWatch.Show(this, x, y, scope String(":", stringParts[2]));
|
||
|
if (debugExpr != null)
|
||
|
mHoverWatch.mEvalString.Set(debugExpr); // Set to old debugStr for comparison
|
||
|
else
|
||
|
mHoverWatch.mEvalString.Clear();
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (!hadValidError)
|
||
|
{
|
||
|
mErrorLookupTextIdx = (int32)textIdx;
|
||
|
Classify(ResolveType.Classify);
|
||
|
triedShow = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
triedShow = false;
|
||
|
delete mClangHoverErrorData;
|
||
|
mClangHoverErrorData = null;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ((parser != null) && (mIsBeefSource))
|
||
|
ErrorScope:
|
||
|
{
|
||
|
//TODO: Needed this?
|
||
|
/*var resolvePassData = parser.CreateResolvePassData();
|
||
|
defer (scope) delete resolvePassData;
|
||
|
bfSystem.NotifyWillRequestLock(1);
|
||
|
bfSystem.Lock(1);
|
||
|
parser.BuildDefs(passInstance, resolvePassData, false);
|
||
|
BfResolveCompiler.ClassifySource(passInstance, parser, resolvePassData, null);*/
|
||
|
|
||
|
BfPassInstance.BfError bestError = scope BfPassInstance.BfError();
|
||
|
|
||
|
for (var bfError in mErrorList)
|
||
|
{
|
||
|
if (bfError.mIsWhileSpecializing)
|
||
|
continue;
|
||
|
|
||
|
if ((textIdx >= bfError.mSrcStart) && (textIdx < bfError.mSrcEnd))
|
||
|
{
|
||
|
if ((bestError.mError == null) || (bestError.mIsWarning) || (bestError.mIsPersistent))
|
||
|
bestError = bfError;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
String showMouseoverString = null;
|
||
|
if (bestError.mError != null)
|
||
|
{
|
||
|
showMouseoverString = scope:: String(":", bestError.mError);
|
||
|
|
||
|
if (bestError.mMoreInfo != null)
|
||
|
{
|
||
|
for (var moreInfo in bestError.mMoreInfo)
|
||
|
{
|
||
|
showMouseoverString.AppendF("\n@{0}\t{1}\t{2}", moreInfo.mFileName, moreInfo.mSrcStart, moreInfo.mError);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
var flags = (SourceElementFlags)editWidgetContent.mData.mText[textIdx].mDisplayFlags;
|
||
|
if ((flags.HasFlag(.Error)) || (flags.HasFlag(.Warning)))
|
||
|
{
|
||
|
mWantsFullRefresh = true;
|
||
|
mRefireMouseOverAfterRefresh = true;
|
||
|
//Debug.WriteLine("Full refresh...");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (showMouseoverString != null)
|
||
|
{
|
||
|
triedShow = true;
|
||
|
mHoverWatch.Show(this, x, y, showMouseoverString);
|
||
|
if (debugExpr != null)
|
||
|
mHoverWatch.mEvalString.Set(debugExpr); // Set to old debugStr for comparison
|
||
|
}
|
||
|
else
|
||
|
triedShow = false;
|
||
|
}
|
||
|
}
|
||
|
if (!hasHoverWatchOpen)
|
||
|
mHoverWatch.mOpenMousePos = DarkTooltipManager.sLastRelMousePos;
|
||
|
|
||
|
if ((debugExpr == "var") || (debugExpr == "let"))
|
||
|
{
|
||
|
let resolveParams = scope ResolveParams();
|
||
|
resolveParams.mOverrideCursorPos = (int32)textIdx;
|
||
|
Classify(ResolveType.GetVarType, resolveParams);
|
||
|
if (resolveParams.mTypeDef != null)
|
||
|
{
|
||
|
debugExpr.Set(resolveParams.mTypeDef);
|
||
|
if (!debugExpr.IsEmpty)
|
||
|
debugExpr.Insert(0, ":");
|
||
|
}
|
||
|
|
||
|
if (!triedShow)
|
||
|
{
|
||
|
mHoverWatch.Show(this, x, y, debugExpr);
|
||
|
triedShow = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Not used?
|
||
|
if ((mHoverWatch != null) && (mHoverWatch.mTextPanel != this))
|
||
|
{
|
||
|
mHoverWatch.Close();
|
||
|
mHoverWatch = null;
|
||
|
}
|
||
|
|
||
|
if (mHoverWatch != null)
|
||
|
{
|
||
|
if ((!triedShow) && (!IDEApp.sApp.HasPopupMenus()))
|
||
|
{
|
||
|
if (mHoverWatch.mCloseDelay > 0)
|
||
|
{
|
||
|
//Debug.WriteLine("mHoverWatch.mCloseCountdown = 20");
|
||
|
mHoverWatch.mCloseDelay--;
|
||
|
mHoverWatch.mCloseCountdown = 20;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mHoverWatch.Close();
|
||
|
mHoverWatch = null;
|
||
|
#if IDE_C_SUPPORT
|
||
|
delete mClangHoverErrorData;
|
||
|
mClangHoverErrorData = null;
|
||
|
#endif
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//Debug.WriteLine("mCloseCountdown = 0");
|
||
|
mHoverWatch.mCloseCountdown = 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (passInstance != null)
|
||
|
delete passInstance;
|
||
|
if (parser != null)
|
||
|
{
|
||
|
delete parser;
|
||
|
mWantsParserCleanup = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if IDE_C_SUPPORT
|
||
|
delete mClangHoverErrorData;
|
||
|
mClangHoverErrorData = null;
|
||
|
#endif
|
||
|
|
||
|
/*if ((mIsClang) && (!compiler.IsPerformingBackgroundOperation()))
|
||
|
{
|
||
|
bool hadValidError = false;
|
||
|
if (mClangHoverErrorData != null)
|
||
|
{
|
||
|
int textIdx = -1;
|
||
|
int line;
|
||
|
int lineChar;
|
||
|
if (editWidgetContent.GetLineCharAtCoord(mousePos.x, mousePos.y, out line, out lineChar))
|
||
|
textIdx = editWidgetContent.GetTextIdx(line, lineChar);
|
||
|
|
||
|
string[] stringParts = mClangHoverErrorData.Split('\t');
|
||
|
int startIdx = int.Parse(stringParts[0]);
|
||
|
int endIdx = int.Parse(stringParts[1]);
|
||
|
if ((textIdx >= startIdx) && (textIdx < endIdx))
|
||
|
{
|
||
|
hadValidError = true;
|
||
|
mHoverWatch.Show(this, x, y, ":" + stringParts[2]);
|
||
|
mHoverWatch.mEvalString.Set(debugExpr); // Set to old debugStr for comparison
|
||
|
}
|
||
|
}
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
void DuplicateEditState(out EditWidgetContent.CharData[] char8Data, out IdSpan char8IdData)
|
||
|
{
|
||
|
var srcCharData = mEditWidget.Content.mData.mText;
|
||
|
char8Data = new EditWidgetContent.CharData[mEditWidget.Content.mData.mTextLength];
|
||
|
var editIdData = ref mEditWidget.Content.mData.mTextIdData;
|
||
|
editIdData.Prepare();
|
||
|
char8IdData = editIdData.Duplicate();
|
||
|
for (int32 i = 0; i < char8Data.Count; i++)
|
||
|
{
|
||
|
srcCharData[i].mDisplayPassId = (uint8)SourceDisplayId.Cleared;
|
||
|
char8Data[i] = srcCharData[i];
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void DoSpellCheck()
|
||
|
{
|
||
|
String sb = scope String();
|
||
|
var spellChecker = IDEApp.sApp.mSpellChecker;
|
||
|
int32 wordStart = -1;
|
||
|
bool skipWord = false;
|
||
|
bool isSectionText = true;
|
||
|
int32 spanSectionIdx = -1;
|
||
|
bool skipNextChar = false;
|
||
|
bool isVerbatimString = false;
|
||
|
bool prevWasLetter = false;
|
||
|
SourceElementType prevElementType = .Normal;
|
||
|
for (int32 i = 0; i < mProcessSpellCheckCharData.Count; i++)
|
||
|
{
|
||
|
mProcessSpellCheckCharData[i].mDisplayFlags = 0;
|
||
|
mProcessSpellCheckCharData[i].mDisplayPassId = (uint8)SourceDisplayId.SpellCheck;
|
||
|
|
||
|
if (skipNextChar)
|
||
|
{
|
||
|
skipNextChar = false;
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (spellChecker == null)
|
||
|
continue;
|
||
|
|
||
|
var char8Data = mProcessSpellCheckCharData[i];
|
||
|
char8 c = (char8)char8Data.mChar;
|
||
|
var elementType = (SourceElementType)char8Data.mDisplayTypeId;
|
||
|
bool endString = false;
|
||
|
if (elementType == .Literal)
|
||
|
{
|
||
|
if (prevElementType != .Literal)
|
||
|
isVerbatimString = (c == '@');
|
||
|
}
|
||
|
else
|
||
|
isVerbatimString = false;
|
||
|
|
||
|
if ((elementType == SourceElementType.Comment) || (elementType == SourceElementType.Literal))
|
||
|
{
|
||
|
if (i >= spanSectionIdx)
|
||
|
{
|
||
|
isSectionText = true;
|
||
|
spanSectionIdx = i;
|
||
|
while (spanSectionIdx < mProcessSpellCheckCharData.Count)
|
||
|
{
|
||
|
var checkCharData = mProcessSpellCheckCharData[spanSectionIdx];
|
||
|
var checkElementType = (SourceElementType)checkCharData.mDisplayTypeId;
|
||
|
if (checkElementType != elementType)
|
||
|
break;
|
||
|
char8 checkC = (char8)checkCharData.mChar;
|
||
|
if (checkC == '\n')
|
||
|
break;
|
||
|
if ((checkC == '*') || (checkC == ';'))
|
||
|
isSectionText = false;
|
||
|
if (checkC >= '\x80') // Don't process high characters
|
||
|
isSectionText = false;
|
||
|
spanSectionIdx++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((c == '\\') && (elementType == SourceElementType.Literal) && (!isVerbatimString))
|
||
|
{
|
||
|
endString = true;
|
||
|
skipNextChar = true;
|
||
|
}
|
||
|
|
||
|
bool isLetter = c.IsLetter;
|
||
|
|
||
|
if ((isLetter) || ((c == '\'') && (prevWasLetter)))
|
||
|
{
|
||
|
if (i > 0)
|
||
|
{
|
||
|
char8 prevC = (char8)mProcessSpellCheckCharData[i - 1].mChar;
|
||
|
if ((prevC == '.') || (prevC.IsNumber))
|
||
|
{
|
||
|
// Looks like extension
|
||
|
skipWord = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((c.IsUpper) && (wordStart != -1))
|
||
|
skipWord = true;
|
||
|
if (wordStart == -1)
|
||
|
wordStart = i;
|
||
|
}
|
||
|
else if (c == '_')
|
||
|
{
|
||
|
skipWord = true;
|
||
|
}
|
||
|
else if (c == '.')
|
||
|
{
|
||
|
if (i < mProcessSpellCheckCharData.Count - 1)
|
||
|
{
|
||
|
char8 nextC = (char8)mProcessSpellCheckCharData[i + 1].mChar;
|
||
|
if (nextC.IsLetter)
|
||
|
{
|
||
|
// Looks like a filename
|
||
|
skipWord = true;
|
||
|
}
|
||
|
}
|
||
|
endString = true;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
endString = true;
|
||
|
}
|
||
|
|
||
|
if (c == '\n')
|
||
|
spanSectionIdx = i;
|
||
|
prevWasLetter = isLetter;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
endString = true;
|
||
|
prevWasLetter = false;
|
||
|
}
|
||
|
|
||
|
if ((i == mProcessSpellCheckCharData.Count - 1) && (!endString))
|
||
|
{
|
||
|
// Process last word
|
||
|
i++;
|
||
|
endString = true;
|
||
|
}
|
||
|
|
||
|
if ((endString) && (wordStart != -1))
|
||
|
{
|
||
|
int32 wordLen = i - wordStart;
|
||
|
if (wordLen <= 1)
|
||
|
skipWord = true;
|
||
|
if (!isSectionText)
|
||
|
skipWord = true;
|
||
|
if (!skipWord)
|
||
|
{
|
||
|
sb.Clear();
|
||
|
|
||
|
if (mProcessSpellCheckCharData[i - 1].mChar == '\'')
|
||
|
i--;
|
||
|
for (int32 wordCharIdx = wordStart; wordCharIdx < i; wordCharIdx++)
|
||
|
sb.Append(mProcessSpellCheckCharData[wordCharIdx].mChar);
|
||
|
String word = sb;
|
||
|
bool hasSpellingError = (word.Length > 1) && (!IDEApp.sApp.mSpellChecker.IsWord(word));
|
||
|
if (hasSpellingError)
|
||
|
{
|
||
|
word.ToLower();
|
||
|
using (spellChecker.mMonitor.Enter())
|
||
|
hasSpellingError = !spellChecker.mIgnoreWordList.Contains(word);
|
||
|
}
|
||
|
|
||
|
if (hasSpellingError)
|
||
|
{
|
||
|
for (int32 wordCharIdx = wordStart; wordCharIdx < i; wordCharIdx++)
|
||
|
{
|
||
|
mProcessSpellCheckCharData[wordCharIdx].mDisplayFlags = (uint8)SourceElementFlags.SpellingError;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
skipWord = false;
|
||
|
wordStart = -1;
|
||
|
}
|
||
|
|
||
|
prevElementType = elementType;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void SpellCheckDone()
|
||
|
{
|
||
|
mSpellCheckJobCount--;
|
||
|
}
|
||
|
|
||
|
void StartSpellCheck()
|
||
|
{
|
||
|
if (gApp.mSpellChecker == null)
|
||
|
{
|
||
|
if (mDidSpellCheck)
|
||
|
{
|
||
|
var data = mEditWidget.Content.mData;
|
||
|
for (int i < data.mTextLength)
|
||
|
{
|
||
|
data.mText[i].mDisplayFlags &= ~((uint8)SourceElementFlags.SpellingError);
|
||
|
}
|
||
|
}
|
||
|
mDidSpellCheck = false;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
DuplicateEditState(out mProcessSpellCheckCharData, out mProcessSpellCheckCharIdSpan);
|
||
|
|
||
|
mDidSpellCheck = true;
|
||
|
mSpellCheckJobCount++;
|
||
|
IDEApp.sApp.mSpellChecker.DoBackground(new => DoSpellCheck, new => SpellCheckDone);
|
||
|
}
|
||
|
|
||
|
void AddHistory()
|
||
|
{
|
||
|
|
||
|
}
|
||
|
|
||
|
/*bool ProcessResolveData()
|
||
|
{
|
||
|
scope AutoBeefPerf("SourceViewPanel.ProcessResolveData");
|
||
|
|
||
|
bool canDoBackground = true;
|
||
|
|
||
|
if ((mProcessResolveCharData != null) && (mProcessingPassInstance != null))
|
||
|
{
|
||
|
MarkDirty();
|
||
|
InjectErrors(mProcessingPassInstance, mProcessResolveCharData, mProcessResolveCharIdSpan, false);
|
||
|
canDoBackground = false;
|
||
|
}
|
||
|
|
||
|
if (mProcessResolveCharData != null)
|
||
|
{
|
||
|
for (int i < mProcessResolveCharData.Count)
|
||
|
{
|
||
|
if (mProcessResolveCharData[i].mDisplayFlags == 1)
|
||
|
{
|
||
|
NOP!();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
MarkDirty();
|
||
|
UpdateCharData(ref mProcessResolveCharData, ref mProcessResolveCharIdSpan, (uint8)SourceElementFlags.CompilerFlags_Mask);
|
||
|
canDoBackground = false;
|
||
|
}
|
||
|
|
||
|
if (mProcessingPassInstance != null)
|
||
|
{
|
||
|
MarkDirty();
|
||
|
if (mProcessingPassInstance.HadSignatureChanges())
|
||
|
mWantsFullRefresh = true;
|
||
|
|
||
|
delete mProcessingPassInstance;
|
||
|
mProcessingPassInstance = null;
|
||
|
}
|
||
|
|
||
|
return canDoBackground;
|
||
|
}*/
|
||
|
|
||
|
public void EnsureReady()
|
||
|
{
|
||
|
if (mWantsFastClassify)
|
||
|
{
|
||
|
DoFastClassify();
|
||
|
mWantsFastClassify = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public bool HasDeferredResolveResults()
|
||
|
{
|
||
|
using (mMonitor.Enter())
|
||
|
{
|
||
|
return !mDeferredResolveResults.IsEmpty;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void ProcessDeferredResolveResults(int waitTime, bool autocompleteOnly = false)
|
||
|
{
|
||
|
//bool canDoBackground = true;
|
||
|
|
||
|
int checkIdx = 0;
|
||
|
|
||
|
while (true)
|
||
|
{
|
||
|
ResolveParams resolveResult = null;
|
||
|
using (mMonitor.Enter())
|
||
|
{
|
||
|
if (checkIdx >= mDeferredResolveResults.Count)
|
||
|
break;
|
||
|
resolveResult = mDeferredResolveResults[checkIdx];
|
||
|
if ((autocompleteOnly) && (resolveResult.mResolveType != .Autocomplete))
|
||
|
{
|
||
|
checkIdx++;
|
||
|
continue;
|
||
|
}
|
||
|
if (!resolveResult.mWaitEvent.WaitFor(waitTime))
|
||
|
{
|
||
|
checkIdx++;
|
||
|
continue;
|
||
|
}
|
||
|
mDeferredResolveResults.RemoveAt(checkIdx);
|
||
|
}
|
||
|
HandleResolveResult(resolveResult.mResolveType, resolveResult.mAutocompleteInfo, resolveResult);
|
||
|
|
||
|
//Debug.WriteLine("ProcessDeferredResolveResults finished {0}", resolveResult.mResolveType);
|
||
|
|
||
|
//bool checkIt = (mFilePath.Contains("Program.bf")) && (mEditWidget.mEditWidgetContent.mData.mCurTextVersionId > 3);
|
||
|
/*var data = ref mEditWidget.Content.mData.mText[10018];
|
||
|
if (checkIt)
|
||
|
{
|
||
|
Debug.Assert(resolveResult.mCharData[10018].mDisplayTypeId == 8);
|
||
|
Debug.Assert(data.mDisplayTypeId == 8);
|
||
|
}
|
||
|
uint8* ptr = &data.mDisplayTypeId;*/
|
||
|
|
||
|
scope AutoBeefPerf("SourceViewPanel.ProcessResolveData");
|
||
|
|
||
|
bool canDoBackground = true;
|
||
|
|
||
|
bool wantsData = (!resolveResult.mCancelled) && (resolveResult.mResolveType != .GetCurrentLocation) && (resolveResult.mResolveType != .GetSymbolInfo);
|
||
|
if (wantsData)
|
||
|
{
|
||
|
if ((resolveResult.mCharData != null) && (resolveResult.mPassInstance != null))
|
||
|
{
|
||
|
bool isAutocomplete = (resolveResult.mResolveType == .Autocomplete) || (resolveResult.mResolveType == .Autocomplete_HighPri);
|
||
|
|
||
|
MarkDirty();
|
||
|
InjectErrors(resolveResult.mPassInstance, resolveResult.mCharData, resolveResult.mCharIdSpan, isAutocomplete);
|
||
|
canDoBackground = false;
|
||
|
}
|
||
|
|
||
|
if (resolveResult.mCharData != null)
|
||
|
{
|
||
|
MarkDirty();
|
||
|
UpdateCharData(ref resolveResult.mCharData, ref resolveResult.mCharIdSpan, (uint8)SourceElementFlags.CompilerFlags_Mask, false);
|
||
|
canDoBackground = false;
|
||
|
}
|
||
|
|
||
|
if (resolveResult.mPassInstance != null)
|
||
|
{
|
||
|
MarkDirty();
|
||
|
if (resolveResult.mPassInstance.HadSignatureChanges())
|
||
|
mWantsFullRefresh = true;
|
||
|
}
|
||
|
}
|
||
|
/*if (checkIt)
|
||
|
Debug.Assert(data.mDisplayTypeId == 8);*/
|
||
|
|
||
|
delete resolveResult;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void Update()
|
||
|
{
|
||
|
base.Update();
|
||
|
|
||
|
scope AutoBeefPerf("SourceViewPanel.Update");
|
||
|
|
||
|
EnsureReady();
|
||
|
|
||
|
if (mProjectSource == null)
|
||
|
{
|
||
|
if (mEditData != null)
|
||
|
Debug.Assert(mEditData.mProjectSources.IsEmpty);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (mEditData != null)
|
||
|
Debug.Assert(mEditData.mProjectSources.Contains(mProjectSource));
|
||
|
}
|
||
|
|
||
|
let projectSource = FilteredProjectSource;
|
||
|
|
||
|
/*if (mWidgetWindow.IsKeyDown(.Control) && mWidgetWindow.IsKeyDown(.Alt))
|
||
|
QueueFullRefresh(false);*/
|
||
|
|
||
|
if (mEditData != null)
|
||
|
Debug.Assert(Path.Equals(mFilePath, mEditData.mFilePath));
|
||
|
|
||
|
|
||
|
bool hasFocus = HasFocus(false);
|
||
|
bool selfHasFocus = HasFocus(true);
|
||
|
|
||
|
if (mSourceFindTask != null)
|
||
|
{
|
||
|
if (mSourceFindTask.WaitFor(0))
|
||
|
{
|
||
|
String foundPath = null;
|
||
|
bool isWrongHash = false;
|
||
|
if (mSourceFindTask.mFoundPath != null)
|
||
|
{
|
||
|
foundPath = mSourceFindTask.mFoundPath;
|
||
|
}
|
||
|
else if (mSourceFindTask.mBackupFileName != null)
|
||
|
{
|
||
|
foundPath = mSourceFindTask.mBackupFileName;
|
||
|
isWrongHash = true;
|
||
|
}
|
||
|
|
||
|
if (foundPath != null)
|
||
|
{
|
||
|
delete mAliasFilePath;
|
||
|
mAliasFilePath = mFilePath;
|
||
|
mFilePath = new String(foundPath);
|
||
|
RehupAlias();
|
||
|
}
|
||
|
|
||
|
DeleteAndNullify!(mSourceFindTask);
|
||
|
|
||
|
RetryLoad();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*if (mEditData != null)
|
||
|
{
|
||
|
Debug.Assert(!mEditData.mOwnsEditWidget);
|
||
|
Debug.Assert(!mEditData.mFilePath.Contains("\\\\"));
|
||
|
if (mEditData.mFilePath.IndexOf("BfModule.cpp", true) != -1)
|
||
|
{
|
||
|
Debug.Assert(mProjectSource != null);
|
||
|
}
|
||
|
}*/
|
||
|
|
||
|
if (GetEditX() != mEditWidget.mX)
|
||
|
ResizeComponents();
|
||
|
|
||
|
if (!IDEApp.sApp.mDebugger.mIsRunning)
|
||
|
CloseOldVersion();
|
||
|
|
||
|
if (mOldVerLoadExecutionInstance != null)
|
||
|
{
|
||
|
if (mOldVerLoadExecutionInstance.mExitCode != null)
|
||
|
{
|
||
|
if ((int)mOldVerLoadExecutionInstance.mExitCode == 0)
|
||
|
{
|
||
|
RetryLoad();
|
||
|
}
|
||
|
else
|
||
|
SetLoadCmd(mOldVerLoadCmd);
|
||
|
delete mOldVerLoadExecutionInstance;
|
||
|
mOldVerLoadExecutionInstance = null;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
UpdateMouseover();
|
||
|
|
||
|
var compiler = ResolveCompiler;
|
||
|
var bfSystem = BfResolveSystem;
|
||
|
var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
|
||
|
if (bfSystem != null)
|
||
|
bfSystem.PerfZoneStart("Update");
|
||
|
|
||
|
if ((compiler != null) && (!compiler.IsPerformingBackgroundOperation()))
|
||
|
mBackgroundResolveType = ResolveType.None;
|
||
|
|
||
|
bool canDoBackground = (compiler != null) && (!compiler.IsPerformingBackgroundOperation()) && (!compiler.HasQueuedCommands());
|
||
|
#if IDE_C_SUPPORT
|
||
|
if (mIsClang)
|
||
|
{
|
||
|
var buildClang = IDEApp.sApp.mDepClang;
|
||
|
if ((buildClang.IsPerformingBackgroundOperation()) && (mWantsFullClassify))
|
||
|
{
|
||
|
// Don't let buildClang get in the way of autocompletion
|
||
|
buildClang.mThreadYieldCount = 20;
|
||
|
canDoBackground = false;
|
||
|
}
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (mResolveJobCount > 0)
|
||
|
{
|
||
|
if (compiler != null)
|
||
|
Debug.Assert((compiler.IsPerformingBackgroundOperation()) || (compiler.mThreadWorker.mOnThreadDone != null) || (compiler.mThreadWorkerHi.mOnThreadDone != null));
|
||
|
}
|
||
|
|
||
|
if (mBackgroundDelay > 0)
|
||
|
{
|
||
|
--mBackgroundDelay;
|
||
|
canDoBackground = false;
|
||
|
}
|
||
|
|
||
|
if (mIsPerformingBackgroundClassify)
|
||
|
{
|
||
|
canDoBackground = false;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Handle finishing old backgrounds
|
||
|
//canDoBackground &= ProcessResolveData();
|
||
|
}
|
||
|
|
||
|
if (canDoBackground)
|
||
|
{
|
||
|
// Check for starting new backgrounds
|
||
|
|
||
|
// Wait longer for Clang since it'll delay autocompletions whereas Beef can be interrupted
|
||
|
int32 classifyDelayTicks = (mIsBeefSource || (mUpdateCnt < 40)) ? 2 : 40;
|
||
|
|
||
|
if (mWantsBackgroundAutocomplete)
|
||
|
{
|
||
|
Classify(ResolveType.Autocomplete);
|
||
|
mWantsBackgroundAutocomplete = false;
|
||
|
canDoBackground = false;
|
||
|
}
|
||
|
else if ((mWantsFullClassify) && (mTicksSinceTextChanged >= classifyDelayTicks)
|
||
|
//TODO: Debug, remove
|
||
|
/*&& (mWidgetWindow.IsKeyDown(KeyCode.Alt))*/)
|
||
|
{
|
||
|
if (IsControllingEditData())
|
||
|
{
|
||
|
if (Classify(mWantsFullRefresh ? ResolveType.ClassifyFullRefresh : ResolveType.Classify))
|
||
|
{
|
||
|
mWantsFullClassify = false;
|
||
|
mWantsFullRefresh = false;
|
||
|
}
|
||
|
canDoBackground = false;
|
||
|
}
|
||
|
}
|
||
|
else if (mWantsParserCleanup)
|
||
|
{
|
||
|
if (!compiler.IsPerformingBackgroundOperation())
|
||
|
{
|
||
|
mWantsParserCleanup = false;
|
||
|
compiler.DoBackground(new => DoParserCleanup);
|
||
|
canDoBackground = false;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else if ((mWantsFullClassify) && (selfHasFocus))
|
||
|
{
|
||
|
// Already changed - cancel and restart
|
||
|
if (compiler != null)
|
||
|
compiler.RequestCancelBackground();
|
||
|
//Debug.WriteLine(String.Format("Cancel From: {0}", mFilePath));
|
||
|
}
|
||
|
|
||
|
if ((IDEApp.sApp.mSpellChecker == null) || (!IDEApp.sApp.mSpellChecker.IsPerformingBackgroundOperation()))
|
||
|
{
|
||
|
if (mProcessSpellCheckCharData != null)
|
||
|
{
|
||
|
UpdateCharData(ref mProcessSpellCheckCharData, ref mProcessSpellCheckCharIdSpan, (uint8)SourceElementFlags.SpellingError, true);
|
||
|
delete mProcessSpellCheckCharData;
|
||
|
mProcessSpellCheckCharData = null;
|
||
|
mProcessSpellCheckCharIdSpan.Dispose();
|
||
|
}
|
||
|
if ((mTicksSinceTextChanged >= 60) && (mWantsSpellCheck))
|
||
|
{
|
||
|
if (IsControllingEditData())
|
||
|
StartSpellCheck();
|
||
|
mWantsSpellCheck = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#if IDE_C_SUPPORT
|
||
|
if ((mAutocompleteTextVersionId != mEditWidget.Content.mData.mCurTextVersionId) && (mIsClang))
|
||
|
{
|
||
|
// We used an edit that didn't fire the autocompletion, and thus didn't do a FastClassify
|
||
|
if (IsControllingEditData())
|
||
|
DoFastClassify();
|
||
|
mAutocompleteTextVersionId = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if ((mLastTextVersionId != mEditWidget.Content.mData.mCurTextVersionId) ||
|
||
|
(mClassifiedTextVersionId != mEditWidget.Content.mData.mCurTextVersionId))
|
||
|
{
|
||
|
if ((mIsBeefSource) && (projectSource != null))
|
||
|
{
|
||
|
// If this file is included in multiple projects then we need to
|
||
|
// reparse these contents in the context of the other projects
|
||
|
if ((IsControllingEditData()) && (IDEApp.sApp.mBfResolveHelper != null))
|
||
|
IDEApp.sApp.mBfResolveHelper.DeferReparse(mFilePath, this);
|
||
|
}
|
||
|
|
||
|
#if IDE_C_SUPPORT
|
||
|
if (mIsClang)
|
||
|
{
|
||
|
mClangSourceChanged = true;
|
||
|
IDEApp.sApp.mResolveClang.mProjectSourceVersion++;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
if (mClassifiedTextVersionId != mEditWidget.Content.mData.mCurTextVersionId)
|
||
|
{
|
||
|
mWantsSpellCheck = true;
|
||
|
mWantsFullClassify = true;
|
||
|
mClassifiedTextVersionId = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
|
||
|
if ((mProjectSource != null) && (IDEApp.sApp.mWakaTime != null) && (IsControllingEditData()))
|
||
|
{
|
||
|
IDEApp.sApp.mWakaTime.QueueFile(mFilePath, mProjectSource.mProject.mProjectName, false);
|
||
|
}
|
||
|
}
|
||
|
mLastTextVersionId = mEditWidget.Content.mData.mCurTextVersionId;
|
||
|
mTicksSinceTextChanged = 0;
|
||
|
if (mProjectSource != null)
|
||
|
mProjectSource.HasChangedSinceLastCompile = true;
|
||
|
|
||
|
EnsureTrackedElementsValid();
|
||
|
UpdateHasChangedSinceLastCompile();
|
||
|
if (mHoverWatch != null)
|
||
|
{
|
||
|
mHoverWatch.Close();
|
||
|
Debug.Assert(mHoverWatch == null);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// If we do a non-autocomple change that modifies a type signature, that will generate a Classify which detects
|
||
|
// the signature change afterward, and then we need to do a full classify after that
|
||
|
if (mWantsFullRefresh)
|
||
|
mWantsFullClassify = true;
|
||
|
if (IDEApp.sApp.mIsUpdateBatchStart)
|
||
|
mTicksSinceTextChanged++;
|
||
|
}
|
||
|
|
||
|
//TODO: This is just a test!
|
||
|
/*if (Rand.Float() <so 0.05f)
|
||
|
Classify(false, true);
|
||
|
if (Rand.Float() < 0.05f)
|
||
|
{
|
||
|
mWantsFullClassify = true;
|
||
|
if (Rand.Float() < 0.5f)
|
||
|
mWantsFullRefresh = true;
|
||
|
}*/
|
||
|
|
||
|
UpdateTrackedElements();
|
||
|
|
||
|
if (bfSystem != null)
|
||
|
bfSystem.PerfZoneEnd();
|
||
|
mJustShown = false;
|
||
|
|
||
|
if (hasFocus)
|
||
|
{
|
||
|
IDEApp.sApp.SetInDisassemblyView(false);
|
||
|
/*if ((IDEApp.sApp.mRenameSymbolDialog != null) && (mRenameSymbolDialog == null))
|
||
|
{
|
||
|
// Someone else has one open, cancel it
|
||
|
IDEApp.sApp.mRenameSymbolDialog.Close();
|
||
|
}*/
|
||
|
}
|
||
|
|
||
|
if (HasUnsavedChanges())
|
||
|
{
|
||
|
var parent = mParent;
|
||
|
while (parent != null)
|
||
|
{
|
||
|
var tabbedView = mParent as TabbedView;
|
||
|
if (tabbedView != null)
|
||
|
{
|
||
|
var activeTab = (DarkTabbedView.DarkTabButton)tabbedView.GetActiveTab();
|
||
|
//This isn't true if we have a split view
|
||
|
// Debug.Assert(activeTab.mContent == this);
|
||
|
if (activeTab.mIsRightTab)
|
||
|
{
|
||
|
// When files changes we move it to the permanent area
|
||
|
IDEApp.sApp.ShowSourceFile(mFilePath, mProjectSource, SourceShowType.ShowExisting, false);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
parent = parent.mParent;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// This potentially closes the symbol reference helper if there's nothing going on
|
||
|
if ((selfHasFocus) /*&& (sourceEditWidgetContent.mCursorStillTicks == 0)*/)
|
||
|
{
|
||
|
var symbolReferenceHelper = IDEApp.sApp.mSymbolReferenceHelper;
|
||
|
if ((symbolReferenceHelper != null) && //(symbolReferenceHelper.HasStarted) && (!symbolReferenceHelper.IsStarting) &&
|
||
|
(symbolReferenceHelper.mKind == SymbolReferenceHelper.Kind.ShowFileReferences))
|
||
|
{
|
||
|
int cursorIdx = sourceEditWidgetContent.CursorTextPos;
|
||
|
|
||
|
bool hasFlag = false;
|
||
|
if (!sourceEditWidgetContent.mVirtualCursorPos.HasValue)
|
||
|
{
|
||
|
for (int32 ofs = -1; ofs <= 0; ofs++)
|
||
|
{
|
||
|
if ((cursorIdx + ofs >= 0) && (cursorIdx + ofs < sourceEditWidgetContent.mData.mTextLength) &&
|
||
|
((sourceEditWidgetContent.mData.mText[cursorIdx + ofs].mDisplayFlags & (uint8)(SourceElementFlags.SymbolReference)) != 0))
|
||
|
hasFlag = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((!hasFlag) /*||
|
||
|
((!mEditWidget.mHasFocus) && (!Utils.FileNameEquals(symbolReferenceHelper.mSourceViewPanel.mFilePath, mFilePath)))*/)
|
||
|
{
|
||
|
if ((symbolReferenceHelper.HasStarted) && (!symbolReferenceHelper.IsStarting))
|
||
|
{
|
||
|
symbolReferenceHelper.Close();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
//symbolReferenceHelper.mWantsClose = true;
|
||
|
//Debug.WriteLine("Queued close...");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((gApp.mSymbolReferenceHelper != null) && (gApp.mSymbolReferenceHelper.mWantsClose) &&
|
||
|
(gApp.mSymbolReferenceHelper.HasStarted) && (!gApp.mSymbolReferenceHelper.IsStarting))
|
||
|
{
|
||
|
gApp.mSymbolReferenceHelper.Close();
|
||
|
}
|
||
|
|
||
|
// Set this to 'false' for debugging to remove the hilighting of all symbol references under the cursor
|
||
|
if (BFApp.sApp.mIsUpdateBatchStart)
|
||
|
sourceEditWidgetContent.mCursorStillTicks++;
|
||
|
|
||
|
if ((gApp.mSettings.mEditorSettings.mHiliteCursorReferences) && (HasFocus(true)) && (mProjectSource != null) /*&& (IDEApp.sApp.mSymbolReferenceHelper == null)*/)
|
||
|
{
|
||
|
if ((mEditWidget.mHasFocus) && (mIsBeefSource) && (sourceEditWidgetContent.mCursorStillTicks == 10) && (!sourceEditWidgetContent.mCursorImplicitlyMoved) && (!sourceEditWidgetContent.mVirtualCursorPos.HasValue))
|
||
|
{
|
||
|
var symbolReferenceHelper = IDEApp.sApp.mSymbolReferenceHelper;
|
||
|
if (symbolReferenceHelper == null)
|
||
|
{
|
||
|
if (!compiler.mThreadWorkerHi.mThreadRunning)
|
||
|
{
|
||
|
ShowSymbolReferenceHelper(SymbolReferenceHelper.Kind.ShowFileReferences);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
sourceEditWidgetContent.mCursorStillTicks--; // Try again later (kindof a hack)
|
||
|
}
|
||
|
}
|
||
|
else if (symbolReferenceHelper.mWantsClose)
|
||
|
{
|
||
|
//Debug.WriteLine("Delayed...");
|
||
|
sourceEditWidgetContent.mCursorStillTicks--; // Try again later (kindof a hack)
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Still trying to show the old location
|
||
|
sourceEditWidgetContent.mCursorStillTicks--; // Try again later (kindof a hack)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (sourceEditWidgetContent.mCursorStillTicks == 5)
|
||
|
{
|
||
|
mWantsCurrentLocation = true;
|
||
|
}
|
||
|
|
||
|
if ((mWantsCurrentLocation) && (!compiler.mThreadWorkerHi.mThreadRunning))
|
||
|
{
|
||
|
bool canClassify = true;
|
||
|
#if IDE_C_SUPPORT
|
||
|
if (mIsClang)
|
||
|
canClassify = canDoBackground;
|
||
|
#endif
|
||
|
if (canClassify)
|
||
|
{
|
||
|
Classify(ResolveType.GetCurrentLocation);
|
||
|
canDoBackground = false;
|
||
|
mWantsCurrentLocation = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((mQueuedLocation != null) && (!compiler.IsPerformingBackgroundOperation()))
|
||
|
{
|
||
|
PrimaryNavigationBar.SetLocation(mQueuedLocation);
|
||
|
DeleteAndNullify!(mQueuedLocation);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((mQueuedAutoComplete != null) & (!compiler.mThreadWorkerHi.mThreadRunning))
|
||
|
{
|
||
|
DoAutoComplete(mQueuedAutoComplete.mKeyChar, mQueuedAutoComplete.mOptions);
|
||
|
DeleteAndNullify!(mQueuedAutoComplete);
|
||
|
}
|
||
|
|
||
|
ProcessDeferredResolveResults(0);
|
||
|
|
||
|
if (mLockFlashPct != 0)
|
||
|
{
|
||
|
mLockFlashPct += 0.02f;
|
||
|
if (mLockFlashPct >= 1.0f)
|
||
|
mLockFlashPct = 0;
|
||
|
MarkDirty();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void InjectErrors(BfPassInstance processingPassInstance, EditWidgetContent.CharData[] processResolveCharData, IdSpan processCharIdSpan, bool keepPersistentErrors)
|
||
|
{
|
||
|
if (keepPersistentErrors)
|
||
|
{
|
||
|
for (int errorIdx = mErrorList.Count - 1; errorIdx >= 0; errorIdx--)
|
||
|
{
|
||
|
var bfError = mErrorList[errorIdx];
|
||
|
if (bfError.mIsPersistent)
|
||
|
{
|
||
|
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
delete bfError;
|
||
|
mErrorList.RemoveAt(errorIdx);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DeleteAndClearItems!(mErrorList);
|
||
|
}
|
||
|
|
||
|
int32 errorCount = processingPassInstance.GetErrorCount();
|
||
|
mErrorList.Capacity = mErrorList.Count + errorCount;
|
||
|
|
||
|
for (int32 errorIdx = 0; errorIdx < errorCount; errorIdx++)
|
||
|
{
|
||
|
BfPassInstance.BfError bfError = new BfPassInstance.BfError();
|
||
|
processingPassInstance.GetErrorData(errorIdx, bfError);
|
||
|
|
||
|
for (int32 moreInfoIdx < bfError.mMoreInfoCount)
|
||
|
{
|
||
|
BfPassInstance.BfError moreInfo = new BfPassInstance.BfError();
|
||
|
processingPassInstance.GetMoreInfoErrorData(errorIdx, moreInfoIdx, moreInfo);
|
||
|
if (bfError.mMoreInfo == null)
|
||
|
bfError.mMoreInfo = new List<BfPassInstance.BfError>();
|
||
|
bfError.mMoreInfo.Add(moreInfo);
|
||
|
}
|
||
|
|
||
|
mErrorList.Add(bfError);
|
||
|
}
|
||
|
|
||
|
if (processResolveCharData != null)
|
||
|
{
|
||
|
IdSpan dupSpan = IdSpan();
|
||
|
|
||
|
for (var bfError in mErrorList)
|
||
|
{
|
||
|
int srcStart = bfError.mSrcStart;
|
||
|
int srcEnd = bfError.mSrcEnd;
|
||
|
|
||
|
if (bfError.mIsWhileSpecializing)
|
||
|
continue;
|
||
|
|
||
|
if (bfError.mIsPersistent)
|
||
|
{
|
||
|
if (bfError.mIdSpan.IsEmpty)
|
||
|
{
|
||
|
// New error - set current span
|
||
|
if (dupSpan.IsEmpty)
|
||
|
{
|
||
|
dupSpan = processCharIdSpan.Duplicate();
|
||
|
bfError.mOwnsSpan = true;
|
||
|
}
|
||
|
|
||
|
bfError.mIdSpan = dupSpan;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Old error - remap position
|
||
|
int32 startId = bfError.mIdSpan.GetIdAtIndex(srcStart);
|
||
|
int32 endId = bfError.mIdSpan.GetIdAtIndex(srcEnd);
|
||
|
|
||
|
srcStart = processCharIdSpan.GetIndexFromId(startId);
|
||
|
srcEnd = processCharIdSpan.GetIndexFromId(endId);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (srcStart == -1)
|
||
|
continue;
|
||
|
srcEnd = Math.Min(srcEnd, processResolveCharData.Count);
|
||
|
|
||
|
uint8 flagsOr = bfError.mIsWarning ? (uint8)SourceElementFlags.Warning : (uint8)SourceElementFlags.Error;
|
||
|
if (bfError.mIsAfter)
|
||
|
flagsOr |= (uint8)SourceElementFlags.IsAfter;
|
||
|
for (int i = srcStart; i < srcEnd; i++)
|
||
|
processResolveCharData[i].mDisplayFlags |= flagsOr;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ((mRefireMouseOverAfterRefresh) && (!mWantsFullRefresh))
|
||
|
{
|
||
|
mRefireMouseOverAfterRefresh = false;
|
||
|
DarkTooltipManager.RefireMouseOver();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void UpdateHasChangedSinceLastCompile()
|
||
|
{
|
||
|
mHasChangedSinceLastCompile = false;
|
||
|
if (mProjectSource == null)
|
||
|
return;
|
||
|
int compileIdx = IDEApp.sApp.mWorkspace.GetHighestSuccessfulCompileIdx();
|
||
|
var projectSourceCompileInstance = IDEApp.sApp.mWorkspace.GetProjectSourceCompileInstance(mProjectSource, compileIdx);
|
||
|
if (projectSourceCompileInstance == null)
|
||
|
return;
|
||
|
//var sourceCharIdData = mEditWidget.Content.mTextIdData;
|
||
|
mEditWidget.Content.mData.mTextIdData.Prepare();
|
||
|
mHasChangedSinceLastCompile = !mEditWidget.Content.mData.mTextIdData.Equals(projectSourceCompileInstance.mSourceCharIdData);
|
||
|
}
|
||
|
|
||
|
float GetEditX()
|
||
|
{
|
||
|
if (!gApp.mSettings.mEditorSettings.mShowLineNumbers)
|
||
|
return GS!(24);
|
||
|
|
||
|
var font = IDEApp.sApp.mTinyCodeFont;
|
||
|
|
||
|
float lineWidth = Math.Max(font.GetWidth(ToStackString!(mEditWidget.Content.GetLineCount())) + GS!(8), GS!(32));
|
||
|
return Math.Max(GS!(24), lineWidth);
|
||
|
}
|
||
|
|
||
|
protected override void ResizeComponents()
|
||
|
{
|
||
|
float topY = 0;
|
||
|
if (mSplitBottomPanel == null)
|
||
|
{
|
||
|
mNavigationBar.Resize(GS!(2), 0, Math.Max(mWidth - GS!(2), 0), GS!(22));
|
||
|
topY = GS!(24);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
mNavigationBar.mVisible = false;
|
||
|
}
|
||
|
|
||
|
float splitterHeight = GS!(3);
|
||
|
|
||
|
float splitTopHeight = 0;
|
||
|
float splitBotHeight = Math.Max(mHeight - topY, 0);
|
||
|
if (mSplitTopPanel != null)
|
||
|
{
|
||
|
float splitTotal = Math.Max(splitBotHeight - splitterHeight, 0);
|
||
|
splitTopHeight = (int32)(splitTotal * mSplitter.mSplitPct);
|
||
|
float minTopSize = 0;
|
||
|
float minBotSize = GS!(64);
|
||
|
splitTopHeight = Math.Clamp(splitTopHeight, minTopSize, Math.Max(splitTotal - minBotSize, 0));
|
||
|
splitBotHeight = Math.Max(splitTotal - splitTopHeight, 0);
|
||
|
}
|
||
|
|
||
|
if (mSplitTopPanel != null)
|
||
|
{
|
||
|
mSplitTopPanel.Resize(0, topY, mWidth, splitTopHeight);
|
||
|
mSplitter.Resize(0, topY + splitTopHeight - 1, mWidth, splitterHeight + 2);
|
||
|
topY += splitTopHeight + splitterHeight;
|
||
|
}
|
||
|
else if (mSplitTopPanel == null)
|
||
|
{
|
||
|
mSplitter.Resize(mWidth - GS!(20), GS!(22 - 1), GS!(20), splitterHeight + GS!(2));
|
||
|
}
|
||
|
|
||
|
mSplitter.SetVisible(mPanelHeader == null);
|
||
|
|
||
|
float editX = GetEditX();
|
||
|
if (mPanelHeader != null)
|
||
|
{
|
||
|
mPanelHeader.Resize(editX, topY, mWidth - editX, GS!(mPanelHeader.mBaseHeight));
|
||
|
mEditWidget.Resize(editX, topY + GS!(mPanelHeader.mBaseHeight), mWidth - editX, splitBotHeight - GS!(mPanelHeader.mBaseHeight));
|
||
|
}
|
||
|
else
|
||
|
mEditWidget.Resize(editX, topY, Math.Max(mWidth - editX, 0), splitBotHeight);
|
||
|
mEditWidget.SetVisible(mEditWidget.mHeight > 0);
|
||
|
mEditWidget.mClipGfx = mEditWidget.mHeight < GS!(20);
|
||
|
|
||
|
float rightCornerY = mEditWidget.mY + GS!(2);
|
||
|
|
||
|
if (mRenameSymbolDialog != null)
|
||
|
{
|
||
|
float renameWidth = GS!(240);
|
||
|
float renameHeight = GS!(56);
|
||
|
mRenameSymbolDialog.Resize(mWidth - renameWidth - GS!(10), mEditWidget.mY + GS!(2), renameWidth, renameHeight);
|
||
|
rightCornerY += renameHeight + GS!(20);
|
||
|
}
|
||
|
|
||
|
if (mQuickFind != null)
|
||
|
{
|
||
|
/*float findWidth = GS!(200);
|
||
|
float findHeight = mQuickFind.mIsReplace ? GS!(84) : GS!(58);
|
||
|
mQuickFind.Resize(mWidth - findWidth - GS!(10), mEditWidget.mY + GS!(2), findWidth, findHeight);*/
|
||
|
mQuickFind.ResizeSelf();
|
||
|
//rightCornerY += findHeight + GS!(20);
|
||
|
}
|
||
|
|
||
|
if (mOldVersionPanel != null)
|
||
|
{
|
||
|
mOldVersionPanel.Resize(0, 0, mWidth, mHeight);
|
||
|
if (mSplitTopPanel != null)
|
||
|
mSplitTopPanel.mVisible = false;
|
||
|
mEditWidget.mVisible = false;
|
||
|
}
|
||
|
else if (mSplitTopPanel != null)
|
||
|
{
|
||
|
mSplitTopPanel.mVisible = true;
|
||
|
mEditWidget.mVisible = true;
|
||
|
|
||
|
//Why did we have this? There were some cases where some text was "peeking through" something, but setting this to false
|
||
|
// breaks the normal split-window functionality
|
||
|
//mEditWidget.mVisible = false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void MouseDown(float x, float y, int32 btn, int32 btnCount)
|
||
|
{
|
||
|
base.MouseDown(x, y, btn, btnCount);
|
||
|
|
||
|
if (mSplitBottomPanel != null)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
float lockX = mEditWidget.mX - GS!(20);
|
||
|
float lockY = mHeight - GS!(20);
|
||
|
|
||
|
if (Rect(lockX, lockY, GS!(20), GS!(20)).Contains(x, y))
|
||
|
{
|
||
|
Menu menu = new Menu();
|
||
|
var menuItem = menu.AddItem("Lock Editing");
|
||
|
if (gApp.mSettings.mEditorSettings.mLockEditing)
|
||
|
menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check);
|
||
|
menuItem.mOnMenuItemSelected.Add(new (evt) =>
|
||
|
{
|
||
|
gApp.mSettings.mEditorSettings.mLockEditing = !gApp.mSettings.mEditorSettings.mLockEditing;
|
||
|
});
|
||
|
|
||
|
if (mProjectSource != null)
|
||
|
{
|
||
|
menuItem = menu.AddItem("Lock Project");
|
||
|
if (mProjectSource.mProject.mLocked)
|
||
|
menuItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check);
|
||
|
menuItem.mOnMenuItemSelected.Add(new (item) =>
|
||
|
{
|
||
|
if (mProjectSource.mProject.mLocked && mProjectSource.mProject.mLockedDefault)
|
||
|
{
|
||
|
let dialog = new DarkDialog("Unlock Project?", "This project is locked because it may be a shared library, and editing shared libraries may have unwanted effects on other programs that use it.\n\nAre you sure you want to unlock it?");
|
||
|
dialog.mWindowFlags |= .Modal;
|
||
|
dialog.AddYesNoButtons(new (dlg) =>
|
||
|
{
|
||
|
mProjectSource.mProject.mLocked = false;
|
||
|
gApp.mWorkspace.SetChanged();
|
||
|
});
|
||
|
dialog.PopupWindow(mWidgetWindow);
|
||
|
return;
|
||
|
}
|
||
|
mProjectSource.mProject.mLocked = !mProjectSource.mProject.mLocked;
|
||
|
gApp.mWorkspace.SetChanged();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
menuItem = menu.AddItem("Lock while Debugging");
|
||
|
void AddLockType(String name, Settings.EditorSettings.LockWhileDebuggingKind kind)
|
||
|
{
|
||
|
var subItem = menuItem.AddItem(name);
|
||
|
if (gApp.mSettings.mEditorSettings.mLockEditingWhenDebugging == kind)
|
||
|
subItem.mIconImage = DarkTheme.sDarkTheme.GetImage(.Check);
|
||
|
subItem.mOnMenuItemSelected.Add(new (evt) =>
|
||
|
{
|
||
|
gApp.mSettings.mEditorSettings.mLockEditingWhenDebugging = kind;
|
||
|
});
|
||
|
}
|
||
|
|
||
|
AddLockType("Never", .Never);
|
||
|
AddLockType("Always", .Always);
|
||
|
AddLockType("When not Hot Swappable", .WhenNotHotSwappable);
|
||
|
|
||
|
MenuWidget menuWidget = DarkTheme.sDarkTheme.CreateMenuWidget(menu);
|
||
|
menuWidget.Init(this, x, y, true);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void MouseClicked(float x, float y, int32 btn)
|
||
|
{
|
||
|
base.MouseClicked(x, y, btn);
|
||
|
|
||
|
if (btn == 1)
|
||
|
{
|
||
|
DarkEditWidgetContent darkEditWidgetContent = (DarkEditWidgetContent)mEditWidget.Content;
|
||
|
float lineSpacing = darkEditWidgetContent.mFont.GetLineSpacing();
|
||
|
|
||
|
HashSet<Breakpoint> selectedBreakpoints = scope HashSet<Breakpoint>();
|
||
|
|
||
|
for (var breakpointView in GetTrackedElementList())
|
||
|
{
|
||
|
var trackedElement = breakpointView.mTrackedElement;
|
||
|
var breakpoint = trackedElement as Breakpoint;
|
||
|
if (breakpoint != null)
|
||
|
{
|
||
|
int drawLineNum = RemapActiveLineToHotLine(breakpoint.mLineNum);
|
||
|
//float breakX = mEditWidget.mX - 20;
|
||
|
float breakY = GS!(3) + drawLineNum * lineSpacing + mEditWidget.mY + mEditWidget.Content.Y;
|
||
|
|
||
|
if ((x <= GS!(40)) && (y >= breakY) && (y < breakY + lineSpacing + GS!(1)))
|
||
|
{
|
||
|
selectedBreakpoints.Add(breakpoint);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (selectedBreakpoints.Count > 0)
|
||
|
{
|
||
|
gApp.mBreakpointPanel.Update();
|
||
|
gApp.mBreakpointPanel.SelectBreakpoints(selectedBreakpoints);
|
||
|
#unwarn
|
||
|
var menuWidget = gApp.mBreakpointPanel.ShowRightClickMenu(this, x, y, true);
|
||
|
//menuWidget.mDeletedHandler.Add(new (widget) => { gApp.mBreakpointPanel.mListView.GetRoot().SelectItemExclusively(null); });
|
||
|
}
|
||
|
|
||
|
//float checkY = y + mEditWidget.mScrollContentContainer.m;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public override void DrawAll(Graphics g)
|
||
|
{
|
||
|
DarkEditWidgetContent darkEditWidgetContent = (DarkEditWidgetContent)mEditWidget.Content;
|
||
|
darkEditWidgetContent.mViewWhiteSpaceColor = gApp.mViewWhiteSpace.Bool ? darkEditWidgetContent.mTextColors[(int)SourceElementType.VisibleWhiteSpace] : 0;
|
||
|
|
||
|
/*using (g.PushColor(0x50FFFFFF))
|
||
|
g.FillRect(0, 0, mWidth, mHeight);*/
|
||
|
|
||
|
//IDEApp.sApp.mBfResolveSystem.PerfZoneStart("SourceViewPanel.DrawAll");
|
||
|
base.DrawAll(g);
|
||
|
|
||
|
if (mHotFileIdx != -1)
|
||
|
{
|
||
|
/*using (g.PushColor(0x50D0D0D0))
|
||
|
g.FillRect(0, 0, mWidth, mHeight);*/
|
||
|
}
|
||
|
|
||
|
//using (g.PushClip(0, 0, mWidth, mHeight))
|
||
|
//{
|
||
|
// using (g.PushTranslate(0, mEditWidget.Content.Y))
|
||
|
// {
|
||
|
// foreach (var breakpointView in GetTrackedElementList())
|
||
|
// {
|
||
|
// /*var breakpoint = breakpointView.mTrackedElement as Breakpoint;
|
||
|
// using (g.PushColor((breakpoint.GetAddress() != 0) ? Color.White : 0x8080FFFF))
|
||
|
// {
|
||
|
// var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
// float breakX;
|
||
|
// float breakY;
|
||
|
// sourceEditWidgetContent.GetTextCoordAtLineChar(breakpoint.mLineNum, breakpoint.mColumn, out breakX, out breakY);
|
||
|
// g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.RedDot), mEditWidget.mX + breakX, mEditWidget.mY + breakY);
|
||
|
// }*/
|
||
|
|
||
|
// var historyEntry = breakpointView.mTrackedElement as HistoryEntry;
|
||
|
// if (historyEntry != null)
|
||
|
// {
|
||
|
// using (g.PushColor(0x8080FFFF))
|
||
|
// {
|
||
|
// var sourceEditWidgetContent = (SourceEditWidgetContent)mEditWidget.Content;
|
||
|
// float breakX;
|
||
|
// float breakY;
|
||
|
// sourceEditWidgetContent.GetTextCoordAtLineChar(historyEntry.mLineNum, historyEntry.mColumn, out breakX, out breakY);
|
||
|
// g.Draw(DarkTheme.sDarkTheme.GetImage(DarkTheme.ImageIdx.IconBookmark), mEditWidget.mX + breakX, mEditWidget.mY + breakY);
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
// }
|
||
|
//}
|
||
|
|
||
|
//IDEApp.sApp.mBfResolveSystem.PerfZoneEnd();
|
||
|
}
|
||
|
|
||
|
|
||
|
public void WithTrackedElementsAtCursor<T>(List<T> trackedElementList, Action<T> func) where T : TrackedTextElement
|
||
|
{
|
||
|
int lineIdx;
|
||
|
int lineCharIdx;
|
||
|
mEditWidget.Content.GetLineCharAtIdx(mEditWidget.Content.CursorTextPos, out lineIdx, out lineCharIdx);
|
||
|
|
||
|
int32 idx = 0;
|
||
|
while (idx < trackedElementList.Count)
|
||
|
{
|
||
|
var trackedElement = trackedElementList[idx];
|
||
|
if (trackedElement.mLineNum == lineIdx)
|
||
|
{
|
||
|
int prevSize = trackedElementList.Count;
|
||
|
func(trackedElement);
|
||
|
if (trackedElementList.Count >= prevSize)
|
||
|
idx++;
|
||
|
}
|
||
|
else
|
||
|
idx++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|