mirror of
https://github.com/beefytech/Beef.git
synced 2025-06-08 11:38:21 +02:00
738 lines
20 KiB
Beef
738 lines
20 KiB
Beef
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using Beefy.widgets;
|
|
using Beefy.utils;
|
|
using Beefy.theme.dark;
|
|
using System.Threading;
|
|
using Beefy;
|
|
using System.IO;
|
|
using System.Diagnostics;
|
|
using IDE.Util;
|
|
|
|
namespace IDE.ui
|
|
{
|
|
public class FindResultsPanel : OutputPanel
|
|
{
|
|
public static String sCurrentDocument = "Current Document";
|
|
public static String sCurrentProject = "Current Project";
|
|
public static String sEntireSolution = "Entire Solution";
|
|
public static String[] sLocationStrings = new .(sCurrentDocument, sCurrentProject, sEntireSolution) ~ delete _;
|
|
|
|
List<String> mPendingLines = new List<String>() ~ DeleteContainerAndItems!(_);
|
|
|
|
int32 mCurLineNum;
|
|
HashSet<String> mFoundPathSet ~ DeleteContainerAndItems!(_);
|
|
List<String> mSearchPaths ~ DeleteContainerAndItems!(_);
|
|
SearchOptions mSearchOptions ~ delete _;
|
|
Thread mSearchThread ~ delete _;
|
|
Monitor mMonitor = new Monitor() ~ delete _;
|
|
bool mCancelling;
|
|
//bool mAbortSearch;
|
|
|
|
public class SearchOptions
|
|
{
|
|
public String mSearchString ~ delete _;
|
|
public String mReplaceString ~ delete _;
|
|
public String mSearchLocation ~ delete _;
|
|
public List<String> mFileTypes ~ DeleteContainerAndItems!(_);
|
|
public bool mMatchCase;
|
|
public bool mMatchWholeWord;
|
|
public bool mRecurseDirectories;
|
|
public bool mDeferredFindFiles;
|
|
}
|
|
|
|
// This is so we can remap to the correct source location even if we add or remove lines above the found line
|
|
class LineSrcInfo
|
|
{
|
|
public FileEditData mEditData;
|
|
public int32 mCharId;
|
|
public int32 mLine;
|
|
public int32 mLineChar;
|
|
}
|
|
List<LineSrcInfo> mLineSrcInfo = new List<LineSrcInfo>() ~ DeleteContainerAndItems!(_);
|
|
bool mHasUnboundLineSrcInfos;
|
|
int32 mLastEditDataRevision;
|
|
|
|
class FindInfo
|
|
{
|
|
public String mContent ~ delete _;
|
|
}
|
|
|
|
Dictionary<String, FindInfo> mFindInfoMap = new Dictionary<String, FindInfo>() ~ { for (var kv in mFindInfoMap) delete kv.value; delete _; };
|
|
List<String> mDeferredReplacePaths = new List<String>() ~ DeleteContainerAndItems!(_);
|
|
|
|
public this()
|
|
{
|
|
((OutputWidgetContent)mOutputWidget.mEditWidgetContent).mGotoReferenceEvent.Add(new => GotoRefrenceAtLine);
|
|
}
|
|
|
|
bool GotoRefrenceAtLine(int line, int lineOfs)
|
|
{
|
|
if (line >= mLineSrcInfo.Count)
|
|
return false;
|
|
|
|
var lineSrcInfo = ref mLineSrcInfo[line];
|
|
if (lineSrcInfo == null)
|
|
return false;
|
|
if (lineSrcInfo.mEditData.mEditWidget == null)
|
|
return false;
|
|
|
|
var editWidgetContent = lineSrcInfo.mEditData.mEditWidget.mEditWidgetContent;
|
|
if (editWidgetContent == null)
|
|
return false;
|
|
|
|
int charIdx = editWidgetContent.mData.mTextIdData.GetPrepared().GetIndexFromId(lineSrcInfo.mCharId);
|
|
if (charIdx == -1)
|
|
return false;
|
|
|
|
int remappedLine;
|
|
int remappedLineChar;
|
|
editWidgetContent.GetLineCharAtIdx(charIdx, out remappedLine, out remappedLineChar);
|
|
|
|
var filePath = lineSrcInfo.mEditData.mFilePath;
|
|
IDEApp.sApp.ShowSourceFileLocation(filePath, -1, -1/*IDEApp.sApp.mWorkspace.GetHighestCompileIdx()*/, remappedLine, remappedLineChar, LocatorType.Always);
|
|
return true;
|
|
}
|
|
|
|
bool IsWordChar(char32 c32)
|
|
{
|
|
return c32.IsLetterOrDigit || (c32 == '_');
|
|
}
|
|
|
|
void SearchThread()
|
|
{
|
|
int32 linesMatched = 0;
|
|
int32 filesMatched = 0;
|
|
int32 filesSearched = 0;
|
|
|
|
if (mSearchOptions.mDeferredFindFiles)
|
|
{
|
|
AddFilesFromDirectory(mSearchOptions.mSearchLocation, mSearchOptions);
|
|
}
|
|
|
|
String line = scope String();
|
|
|
|
for (var filePath in mSearchPaths)
|
|
FileBlock:
|
|
{
|
|
filesSearched++;
|
|
|
|
if (mCancelling)
|
|
break;
|
|
|
|
StreamReader reader = null;
|
|
|
|
String fileText = null;
|
|
|
|
if (mSearchOptions.mSearchString.IsEmpty)
|
|
break;
|
|
|
|
FindInfo findInfo;
|
|
if (mFindInfoMap.TryGetValue(filePath, out findInfo))
|
|
{
|
|
fileText = findInfo.mContent;
|
|
}
|
|
|
|
if (fileText != null)
|
|
{
|
|
let strStream = scope:FileBlock StringStream(fileText, .Reference);
|
|
reader = scope:FileBlock StreamReader(strStream, UTF8Encoding.UTF8, false, 1024);
|
|
}
|
|
else
|
|
{
|
|
let streamReader = new StreamReader();
|
|
if (streamReader.Open(filePath) case .Err(let errCode))
|
|
{
|
|
delete streamReader;
|
|
if (errCode != .NotFound)
|
|
QueueLine(scope String()..AppendF("Failed to open file: {0}", filePath));
|
|
continue;
|
|
}
|
|
|
|
reader = streamReader;
|
|
defer:FileBlock delete reader;
|
|
}
|
|
|
|
bool hasDeferredReplace = false;
|
|
|
|
if (reader != null)
|
|
{
|
|
char8* searchPtr = mSearchOptions.mSearchString.Ptr;
|
|
int searchLength = mSearchOptions.mSearchString.Length;
|
|
|
|
bool hadMatch = false;
|
|
int32 lineNum = 0;
|
|
while (!reader.EndOfStream)
|
|
{
|
|
if (mCancelling)
|
|
break;
|
|
|
|
line.Clear();
|
|
reader.ReadLine(line);
|
|
|
|
char8* linePtr = line.Ptr;
|
|
|
|
bool lineMatched;
|
|
if (mSearchOptions.mMatchWholeWord)
|
|
{
|
|
bool isNewStart = true;
|
|
int lineIdx = 0;
|
|
for (let c32 in line.DecodedChars)
|
|
{
|
|
if ((isNewStart) && (mSearchOptions.mMatchCase ?
|
|
String.[Friend]EqualsHelper(linePtr + lineIdx, searchPtr, searchLength) :
|
|
String.[Friend]EqualsIgnoreCaseHelper(linePtr + lineIdx, searchPtr, searchLength)))
|
|
{
|
|
int checkIdx = lineIdx + searchLength;
|
|
if (checkIdx >= line.Length)
|
|
{
|
|
lineMatched = true;
|
|
break;
|
|
}
|
|
|
|
char32 nextC = line.GetChar32(checkIdx).c;
|
|
if (!IsWordChar(nextC))
|
|
{
|
|
lineMatched = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
isNewStart = !IsWordChar(c32);
|
|
lineIdx = @c32.NextIndex;
|
|
}
|
|
}
|
|
else
|
|
lineMatched = line.IndexOf(mSearchOptions.mSearchString, !mSearchOptions.mMatchCase) != -1;
|
|
|
|
if (lineMatched)
|
|
{
|
|
linesMatched++;
|
|
hadMatch = true;
|
|
if (mSearchOptions.mReplaceString != null)
|
|
{
|
|
hasDeferredReplace = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
const int maxLen = 4096;
|
|
if (line.Length >= maxLen)
|
|
{
|
|
line.RemoveToEnd(maxLen);
|
|
line.Append("...");
|
|
}
|
|
for (var c in ref line.RawChars)
|
|
{
|
|
if (c.IsControl)
|
|
c = ' ';
|
|
}
|
|
QueueLine(filePath, lineNum, 0, line);
|
|
}
|
|
}
|
|
|
|
lineNum++;
|
|
}
|
|
|
|
if (hadMatch)
|
|
filesMatched++;
|
|
}
|
|
|
|
if (hasDeferredReplace)
|
|
{
|
|
mDeferredReplacePaths.Add(filePath);
|
|
}
|
|
}
|
|
|
|
String outLine = scope String();
|
|
if (mSearchOptions.mReplaceString != null)
|
|
outLine.AppendF("Replacing text in {0} file{1}. Total files searched: {2}", filesMatched, (filesMatched == 1) ? "" : "s", filesSearched);
|
|
else
|
|
outLine.AppendF("Matching lines: {0} Matching files: {1} Total files searched: {2}", linesMatched, filesMatched, filesSearched);
|
|
QueueLine(outLine);
|
|
|
|
if (mCancelling)
|
|
QueueLine("Search cancelled");
|
|
}
|
|
|
|
public bool IsSearching
|
|
{
|
|
get
|
|
{
|
|
return mSearchThread != null;
|
|
}
|
|
}
|
|
|
|
public void CancelSearch()
|
|
{
|
|
mCancelling = true;
|
|
}
|
|
|
|
void StopSearch()
|
|
{
|
|
if (mSearchThread != null)
|
|
{
|
|
mSearchThread.Join();
|
|
DeleteAndNullify!(mSearchThread);
|
|
DeleteAndNullify!(mSearchOptions);
|
|
ClearAndDeleteItems(mPendingLines);
|
|
DeleteContainerAndItems!(mSearchPaths);
|
|
mSearchPaths = null;
|
|
DeleteContainerAndItems!(mFoundPathSet);
|
|
mFoundPathSet = null;
|
|
for (var kv in mFindInfoMap)
|
|
delete kv.value;
|
|
mFindInfoMap.Clear();
|
|
mDeferredReplacePaths.Clear();
|
|
mCancelling = false;
|
|
}
|
|
}
|
|
|
|
bool PassesFilter(StringView fileName, SearchOptions searchOptions)
|
|
{
|
|
bool passes = false;
|
|
|
|
if (searchOptions.mFileTypes.IsEmpty)
|
|
passes = true;
|
|
|
|
for (let fileType in searchOptions.mFileTypes)
|
|
{
|
|
if (fileType == "*")
|
|
{
|
|
passes = true;
|
|
continue;
|
|
}
|
|
|
|
if (Path.WildcareCompare(fileName, fileType))
|
|
{
|
|
passes = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return passes;
|
|
}
|
|
|
|
void AddFromFilesFolder(ProjectFolder projectFolder, SearchOptions searchOptions)
|
|
{
|
|
for (var projectEntry in projectFolder.mChildItems)
|
|
{
|
|
var childFolder = projectEntry as ProjectFolder;
|
|
if (childFolder != null)
|
|
AddFromFilesFolder(childFolder, searchOptions);
|
|
|
|
var projectSource = projectEntry as ProjectSource;
|
|
if (projectSource != null)
|
|
{
|
|
var path = scope String();
|
|
projectSource.GetFullImportPath(path);
|
|
|
|
var upperPath = scope String(path);
|
|
IDEUtils.FixFilePath(upperPath);
|
|
if (!Environment.IsFileSystemCaseSensitive)
|
|
upperPath.ToUpper();
|
|
|
|
if ((!mFoundPathSet.Contains(upperPath)) && (PassesFilter(path, searchOptions)))
|
|
{
|
|
mSearchPaths.Add(new String(path));
|
|
mFoundPathSet.Add(new String(upperPath));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void AddFilesFromDirectory(StringView dirPath, SearchOptions searchOptions)
|
|
{
|
|
if (mCancelling)
|
|
return;
|
|
|
|
for (var fileEntry in Directory.EnumerateFiles(dirPath))
|
|
{
|
|
if (mCancelling)
|
|
return;
|
|
|
|
var fileName = scope String();
|
|
fileEntry.GetFileName(fileName);
|
|
if (PassesFilter(fileName, searchOptions))
|
|
{
|
|
var filePath = new String();
|
|
fileEntry.GetFilePath(filePath);
|
|
|
|
var upperPath = new String(filePath);
|
|
IDEUtils.FixFilePath(upperPath);
|
|
if (!Environment.IsFileSystemCaseSensitive)
|
|
upperPath.ToUpper();
|
|
if (mFoundPathSet.Add(upperPath))
|
|
{
|
|
mSearchPaths.Add(filePath);
|
|
}
|
|
else
|
|
{
|
|
delete upperPath;
|
|
delete filePath;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (searchOptions.mRecurseDirectories)
|
|
{
|
|
for (var dirEntry in Directory.EnumerateDirectories(dirPath))
|
|
{
|
|
if (mCancelling)
|
|
return;
|
|
|
|
var newDirPath = scope String();
|
|
dirEntry.GetFilePath(newDirPath);
|
|
AddFilesFromDirectory(newDirPath, searchOptions);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Search(SearchOptions searchOptions)
|
|
{
|
|
if (mSearchThread != null)
|
|
StopSearch();
|
|
|
|
mCancelling = false;
|
|
Clear();
|
|
QueueLine("Searching...");
|
|
|
|
mSearchPaths = new List<String>();
|
|
mFoundPathSet = new HashSet<String>();
|
|
if (searchOptions.mSearchLocation == sEntireSolution)
|
|
{
|
|
for (var project in IDEApp.sApp.mWorkspace.mProjects)
|
|
{
|
|
AddFromFilesFolder(project.mRootFolder, searchOptions);
|
|
}
|
|
}
|
|
else if (searchOptions.mSearchLocation == sCurrentDocument)
|
|
{
|
|
var sourceViewPanel = gApp.GetActiveSourceViewPanel(true);
|
|
if (sourceViewPanel != null)
|
|
{
|
|
mSearchPaths.Add(new String(sourceViewPanel.mFilePath));
|
|
}
|
|
}
|
|
else if (searchOptions.mSearchLocation == sCurrentProject)
|
|
{
|
|
var sourceViewPanel = gApp.GetActiveSourceViewPanel(true);
|
|
if (sourceViewPanel != null)
|
|
{
|
|
if (sourceViewPanel.mProjectSource != null)
|
|
AddFromFilesFolder(sourceViewPanel.mProjectSource.mProject.mRootFolder, searchOptions);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
searchOptions.mDeferredFindFiles = true;
|
|
}
|
|
|
|
for (var filePath in mSearchPaths)
|
|
{
|
|
String fileText = null;
|
|
|
|
var editData = gApp.GetEditData(filePath, false, false);
|
|
if (editData != null)
|
|
{
|
|
if (editData.mEditWidget != null)
|
|
{
|
|
fileText = new String();
|
|
editData.mEditWidget.GetText(fileText);
|
|
}
|
|
else if (editData.mSavedContent != null)
|
|
{
|
|
fileText = new String();
|
|
fileText.Append(editData.mSavedContent);
|
|
}
|
|
}
|
|
|
|
if (fileText != null)
|
|
{
|
|
FindInfo findInfo = new FindInfo();
|
|
findInfo.mContent = fileText;
|
|
mFindInfoMap[filePath] = findInfo;
|
|
}
|
|
}
|
|
|
|
if (searchOptions.mDeferredFindFiles)
|
|
{
|
|
String dirPath = scope String(searchOptions.mSearchLocation);
|
|
IDEUtils.FixFilePath(dirPath);
|
|
if (!dirPath.EndsWith(Path.DirectorySeparatorChar))
|
|
dirPath.Append(Path.DirectorySeparatorChar);
|
|
|
|
using (gApp.mMonitor.Enter())
|
|
{
|
|
for (let kv in gApp.mFileEditData)
|
|
{
|
|
if (kv.key.StartsWith(dirPath, Environment.IsFileSystemCaseSensitive ? .Ordinal : .OrdinalIgnoreCase))
|
|
{
|
|
let editData = kv.value;
|
|
|
|
if (!PassesFilter(editData.mFilePath, searchOptions))
|
|
continue;
|
|
|
|
String fileText = null;
|
|
if (editData.mEditWidget != null)
|
|
{
|
|
fileText = new String();
|
|
editData.mEditWidget.GetText(fileText);
|
|
}
|
|
else if (editData.mSavedContent != null)
|
|
{
|
|
fileText = new String();
|
|
fileText.Append(editData.mSavedContent);
|
|
}
|
|
|
|
if (fileText != null)
|
|
{
|
|
let filePath = new String(editData.mFilePath);
|
|
FindInfo findInfo = new FindInfo();
|
|
findInfo.mContent = fileText;
|
|
mFindInfoMap[filePath] = findInfo;
|
|
mSearchPaths.Add(filePath);
|
|
|
|
mFoundPathSet.Add(new String(kv.key));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
mSearchOptions = searchOptions;
|
|
mSearchThread = new Thread(new => SearchThread);
|
|
mSearchThread.Start(false);
|
|
}
|
|
|
|
public override void Serialize(StructuredData data)
|
|
{
|
|
data.Add("Type", "FindResultsPanel");
|
|
}
|
|
|
|
public override bool Deserialize(StructuredData data)
|
|
{
|
|
return base.Deserialize(data);
|
|
}
|
|
|
|
public void QueueLine(String text)
|
|
{
|
|
mCurLineNum++;
|
|
using (mMonitor.Enter())
|
|
mPendingLines.Add(new String(text));
|
|
}
|
|
|
|
void RecordLineData(int32 ouputLineNum, FileEditData fileEditData)
|
|
{
|
|
|
|
}
|
|
|
|
public void QueueLine(FileEditData fileEditData, int32 line, int32 lineChar, String lineStr)
|
|
{
|
|
RecordLineData(mCurLineNum, fileEditData);
|
|
|
|
int charId = -1;
|
|
if (fileEditData.mEditWidget != null)
|
|
{
|
|
let editWidgetContent = fileEditData.mEditWidget.mEditWidgetContent;
|
|
int textIdx = editWidgetContent.GetTextIdx(line, lineChar);
|
|
charId = editWidgetContent.mData.mTextIdData.GetIdAtIndex(textIdx);
|
|
}
|
|
else
|
|
{
|
|
mHasUnboundLineSrcInfos = true;
|
|
}
|
|
|
|
while (mCurLineNum >= mLineSrcInfo.Count)
|
|
mLineSrcInfo.Add(null);
|
|
var lineSrcInfo = new LineSrcInfo();
|
|
lineSrcInfo.mCharId = (int32)charId;
|
|
lineSrcInfo.mEditData = fileEditData;
|
|
lineSrcInfo.mLine = line;
|
|
lineSrcInfo.mLineChar = lineChar;
|
|
mLineSrcInfo[mCurLineNum] = lineSrcInfo;
|
|
|
|
String outStr = scope String();
|
|
outStr.AppendF("{0}({1}):{2}", fileEditData.mFilePath, line + 1, lineStr);
|
|
gApp.mFindResultsPanel.QueueLine(outStr);
|
|
}
|
|
|
|
public void QueueLine(String fileName, int32 line, int32 column, String text)
|
|
{
|
|
QueueLine(gApp.GetEditData(fileName, true, false), line, column, text);
|
|
}
|
|
|
|
public override void Update()
|
|
{
|
|
base.Update();
|
|
|
|
// Try to bind locations to charIds if new files have been loaded
|
|
if ((mHasUnboundLineSrcInfos) && (mLastEditDataRevision != IDEApp.sApp.mFileDataDataRevision))
|
|
{
|
|
mHasUnboundLineSrcInfos = false;
|
|
for (var lineSrcInfo in mLineSrcInfo)
|
|
{
|
|
if (lineSrcInfo == null)
|
|
continue;
|
|
if (lineSrcInfo.mCharId != -1)
|
|
continue;
|
|
|
|
if (lineSrcInfo.mEditData.mEditWidget == null)
|
|
{
|
|
mHasUnboundLineSrcInfos = true;
|
|
continue;
|
|
}
|
|
|
|
let editWidgetContent = lineSrcInfo.mEditData.mEditWidget.mEditWidgetContent;
|
|
int textIdx = editWidgetContent.GetTextIdx(lineSrcInfo.mLine, lineSrcInfo.mLineChar);
|
|
lineSrcInfo.mCharId = (int32)editWidgetContent.mData.mTextIdData.GetIdAtIndex((int32)textIdx);
|
|
}
|
|
|
|
mLastEditDataRevision = IDEApp.sApp.mFileDataDataRevision;
|
|
}
|
|
|
|
using (mMonitor.Enter())
|
|
{
|
|
if (mPendingLines.Count > 0)
|
|
{
|
|
String sb = scope String();
|
|
|
|
while (mPendingLines.Count > 0)
|
|
{
|
|
var pendingLine = mPendingLines.PopFront();
|
|
sb.Append(pendingLine, "\n");
|
|
delete pendingLine;
|
|
}
|
|
|
|
Write(sb);
|
|
}
|
|
}
|
|
|
|
if ((mSearchThread != null) && (mSearchThread.Join(0)))
|
|
{
|
|
if (!mDeferredReplacePaths.IsEmpty)
|
|
{
|
|
GlobalUndoData globalUndoData = gApp.mGlobalUndoManager.CreateUndoData();
|
|
|
|
int replaceCount = 0;
|
|
|
|
for (var filePath in mDeferredReplacePaths)
|
|
{
|
|
int replaceInFileCount = 0;
|
|
var editData = gApp.GetEditData(filePath);
|
|
|
|
bool hasUnsavedChanges = editData.HasTextChanged();
|
|
|
|
globalUndoData.mFileEditDatas.Add(editData);
|
|
var sourceEditBatchHelper = scope:: SourceEditBatchHelper(editData.mEditWidget, "#renameInFiles", new GlobalUndoAction(editData, globalUndoData));
|
|
|
|
bool matchCase = mSearchOptions.mMatchCase;
|
|
|
|
let editWidgetContent = editData.mEditWidget.mEditWidgetContent;
|
|
let data = editData.mEditWidget.mEditWidgetContent.mData;
|
|
//let searchStrPtr = mSearchOptions.mSearchString.Ptr;
|
|
|
|
bool isNewStart = true;
|
|
//for (int i = 0; i < data.mTextLength - mSearchOptions.mSearchString.Length; i++)
|
|
int i = 0;
|
|
|
|
while (i < data.mTextLength - mSearchOptions.mSearchString.Length)
|
|
{
|
|
if ((isNewStart) || (!mSearchOptions.mMatchWholeWord))
|
|
{
|
|
bool matches = true;
|
|
int searchOfs = 0;
|
|
while (searchOfs < mSearchOptions.mSearchString.Length)
|
|
{
|
|
var (checkC, charBytes) = editWidgetContent.GetChar32(i + searchOfs);
|
|
|
|
char32 searchC = mSearchOptions.mSearchString.GetChar32(searchOfs).c;
|
|
if (!matchCase)
|
|
{
|
|
checkC = checkC.ToUpper;
|
|
searchC = searchC.ToUpper;
|
|
}
|
|
|
|
if (checkC != searchC)
|
|
{
|
|
matches = false;
|
|
break;
|
|
}
|
|
|
|
searchOfs += charBytes;
|
|
}
|
|
|
|
if ((matches) && (mSearchOptions.mMatchWholeWord))
|
|
{
|
|
int nextIdx = i + mSearchOptions.mSearchString.Length;
|
|
if (nextIdx < data.mTextLength)
|
|
{
|
|
var nextC = editWidgetContent.GetChar32(nextIdx).0;
|
|
if (IsWordChar(nextC))
|
|
matches = false;
|
|
}
|
|
}
|
|
|
|
if (matches)
|
|
{
|
|
editWidgetContent.CursorTextPos = i;
|
|
editWidgetContent.mSelection = EditSelection(i, i + mSearchOptions.mSearchString.Length);
|
|
var insertTextAction = new EditWidgetContent.InsertTextAction(editWidgetContent, mSearchOptions.mReplaceString, .None);
|
|
insertTextAction.mMoveCursor = false;
|
|
editWidgetContent.mData.mUndoManager.Add(insertTextAction);
|
|
|
|
editWidgetContent.PhysDeleteSelection(false);
|
|
editWidgetContent.PhysInsertAtCursor(mSearchOptions.mReplaceString, false);
|
|
i += mSearchOptions.mSearchString.Length - 1;
|
|
replaceCount++;
|
|
replaceInFileCount++;
|
|
}
|
|
}
|
|
|
|
var (c32, charBytes) = editWidgetContent.GetChar32(i);
|
|
|
|
if (mSearchOptions.mMatchWholeWord)
|
|
isNewStart = !IsWordChar(c32);
|
|
i += charBytes;
|
|
}
|
|
|
|
sourceEditBatchHelper.Finish();
|
|
|
|
if (!hasUnsavedChanges)
|
|
{
|
|
gApp.SaveFile(editData);
|
|
}
|
|
|
|
if ((IDEApp.IsBeefFile(filePath)) && (gApp.mBfResolveCompiler != null))
|
|
{
|
|
for (var projectSource in editData.mProjectSources)
|
|
gApp.mBfResolveCompiler.QueueProjectSource(projectSource, false);
|
|
gApp.mBfResolveCompiler.QueueDeferredResolveAll();
|
|
}
|
|
|
|
var line = scope String();
|
|
line.AppendF(" {0}: Replaced {1} instance{2}\n", filePath, replaceInFileCount, (replaceInFileCount == 1) ? "" : "s");
|
|
Write(line);
|
|
}
|
|
}
|
|
|
|
StopSearch();
|
|
}
|
|
}
|
|
|
|
public override void Clear()
|
|
{
|
|
base.Clear();
|
|
ClearAndDeleteItems(mLineSrcInfo);
|
|
mHasUnboundLineSrcInfos = false;
|
|
mCurLineNum = 0;
|
|
}
|
|
|
|
public override bool HasAffinity(Widget otherPanel)
|
|
{
|
|
return base.HasAffinity(otherPanel) || (otherPanel is OutputPanel);
|
|
}
|
|
}
|
|
}
|