pascalabcnet/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionParserController.cs

322 lines
14 KiB
C#
Raw Normal View History

// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt)
2015-06-02 22:52:35 +03:00
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections.Generic;
Create generic instances cache (#3259) * First attempt to create generic instances cache * Change TypeScope equality check and add instance cache clearing * Move instance cache clearing call to CodeCompletion Compile funciton Шаблонные типы в instance_cache не должны пересекаться для различных модулей. Для улучшения производительности вызов очистки кэша расположен перед вызовом компиляции всех модулей. * Squashed commit of the following: commit 771ea7b445ec9086a0ac172e24299fa6c8bea0f3 Author: Ivan Bondarev <ibond84@googlemail.com> Date: Sun Apr 6 14:32:17 2025 +0200 #3230 commit 28a1a03e8dbccdff404c133145b5bffc63920cad Author: Ivan Bondarev <ibond84@googlemail.com> Date: Sun Apr 6 11:35:09 2025 +0200 #3251 commit af7abc770b958fe85cf37d4f29f79eb97f3f25ba Author: samuraiGH <87191377+samuraiGH@users.noreply.github.com> Date: Sun Apr 6 08:33:34 2025 +0300 synchronization of namespaces and folders for TeacherControlPlugin (#3262) commit aae723b982e09e5476f89805ee6cfe2b88029dfc Author: AlexanderZemlyak <92867056+AlexanderZemlyak@users.noreply.github.com> Date: Thu Apr 3 12:00:04 2025 +0300 Create default syntax tree converter class (#3216) Для удобства разработчиков простых языков, которым не требуются дополнительные обходы синтаксического дерева. commit f9cc90e50fe4d765ec03b4c685c9ce9b62d66caf Author: Sun Serega <sunserega2@gmail.com> Date: Thu Apr 3 11:59:46 2025 +0300 fix errors (#3260) commit f85bfad60ffee7a9f01f9a95e630388db4cd732b Author: AlexanderZemlyak <92867056+AlexanderZemlyak@users.noreply.github.com> Date: Thu Apr 3 11:58:19 2025 +0300 Intellisense memory leaks fix (#3257) * Fix memory leaks found for PABCSystem.pas Memory leaks of Interface and Implementation Unit Scopes fixed * Change open_files and recomp_files variables type to be Dictionary (not HashTable) * open_files Check simplification * Move TypeTable.Clear call to CodeCompletion Compile method * Fix incorrect dictionary iteration * Add one more dictionary keys copying
2025-04-10 13:14:04 +03:00
using System.Linq;
using PascalABCCompiler.CoreUtils;
2015-05-14 22:35:07 +03:00
namespace VisualPascalABC
{
public delegate void ParseInformationUpdatedDelegate(object obj, string fileName);
2017-05-26 13:29:21 +03:00
public class CodeCompletionParserController : VisualPascalABCPlugins.ICodeCompletionService
2015-05-14 22:35:07 +03:00
{
public static Dictionary<string, bool> filesToParse = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
2015-05-14 22:35:07 +03:00
public VisualEnvironmentCompiler visualEnvironmentCompiler;
private System.Threading.Thread th = null;
private CodeCompletionProvider ccp;
public event ParseInformationUpdatedDelegate ParseInformationUpdated;
public void StopParseThread()
{
try
{
if (th != null)
{
2015-05-14 22:35:07 +03:00
th.Abort();
th.Join(); // Это обязательно. По большому счету вообще Abort надо заменить на современное завершение потоков
}
2015-05-14 22:35:07 +03:00
}
catch
{
}
}
public CodeCompletionParserController()
{
this.th = new System.Threading.Thread(new System.Threading.ThreadStart(ParseInThread));
ccp = new CodeCompletionProvider();
}
2017-02-19 12:58:42 +03:00
public PascalABCCompiler.Parsers.ICodeCompletionDomConverter GetConverter(string fileName)
{
return CodeCompletion.CodeCompletionController.comp_modules[fileName] as PascalABCCompiler.Parsers.ICodeCompletionDomConverter;
}
2015-05-14 22:35:07 +03:00
public static string CurrentTwoLetterISO
{
get
{
return CodeCompletion.CodeCompletionController.currentLanguageISO;
}
set
{
CodeCompletion.CodeCompletionController.currentLanguageISO = value;
}
}
public void Init()
{
New languages engine (#3120) * Implement first version of languages interfaces and classes ParsersController заменен на LanguageProvider. * Update ParserTools.csproj * Update RemoteCompiler.cs * Update TestRunner * Rename LanguageIntegrator project to Languages * Update TestRunner * Rename Parsers folder * Rename PascalABCParser.dll to PascalABCLanguage.dll * Reorganise LanguageIntegrator and rename DocTagsParser * Update Release Generators * Update language loading messages * Update linux version * Move BaseParser fields to ILanguage interface * Revert "Update Release Generators" This reverts commit 26a991c71b81e643d9fbd9a815ddca222768dda9. * Revert "Rename PascalABCParser.dll to PascalABCLanguage.dll" * Clean the mess in parser folders * Organize namespaces properly * Revert "Rename LanguageIntegrator project to Languages" * Add new enclosing folders for standard languages * Organize namespaces of LanguageIntegrator properly * Rename StandardLanguages to Languages * Comment the rest of Visual basic source code * Update OutputPath in pascal parser project * Move BaseParser methods to IParser * Restore Pascal parser project initial structure * Add PascalLanguage project * Move SyntaxTreeConverters project to Languages\Pascal folder * Rename SemanticRules in parser project * Rename Errors1 to Errors in parser project * Delete LambdaConverter dll from installer * Update language integrator to load *Language.dll files * Move lambda converter project to pascal lanuage dir * Revert "Delete LambdaConverter dll from installer" This reverts commit dd56f559ebe4f8c4c5c33752d44e6953001b96a5. * Switch off VBNETParser building * Delete syntax tree converters controller * Delete lambda converter dll from repository * Reorganize syntax tree to semantic tree conversion stage * Add BaseLanguage class to make language initialization more neat * Delete syntax tree post processors entity from ILanguage and refactor ABCStatistics calls * Add new IDocParser interface for documentation comments parser * Clean up folders * Add helper data structures to reduce parameters amount in CompileInterface and CompileImplementation * Add documentation to language classes * Move Union struct in global namespace and project (ParserTools) * Add more comments and a safe select language method * Rename SemanticRules class * Fix directives format null bug * Add System.Linq ref to LanguageIntegrator * Delete SyntaxToSemanticTreeConverter interface * Add BaseSyntaxTreeConverter * Call safe select language method in intellisence * Delete source files providers from parsers * Rename GetSyntaxTree method * Change Prebuild tree to be virtual - not abstract * Refresh documentation a bit * Add temporary language check for ABCHealth button * Add parser reference to parser tools * Move current compilation unit assigning higher to avoid bug with unit check * Delete null DirectiveInfo's and refactor directives' code * Add a few more comments
2024-05-25 12:26:32 +03:00
//LanguageProvider.Instance.SourceFilesProvider = visualEnvironmentCompiler.SourceFilesProvider;
2015-05-14 22:35:07 +03:00
CodeCompletion.CodeCompletionController.currentLanguageISO = PascalABCCompiler.StringResourcesLanguage.CurrentTwoLetterISO;
}
public void RenameFile(string OldFileName, string NewFileName)
{
if (string.Compare(OldFileName, NewFileName, true) != 0)
{
CodeCompletion.CodeCompletionController.comp_modules[NewFileName] = CodeCompletion.CodeCompletionController.comp_modules[OldFileName];
if (CodeCompletion.CodeCompletionController.comp_modules.ContainsKey(OldFileName))
CodeCompletion.CodeCompletionController.comp_modules.Remove(OldFileName);
filesToParse[NewFileName] = filesToParse[OldFileName];
if (filesToParse.ContainsKey(OldFileName))
filesToParse.Remove(OldFileName);
2015-05-14 22:35:07 +03:00
}
}
public void RegisterFileForParsing(string FileName)
{
Деактивация кнопок, связанных с Intellisense, для языков без Intellisense + разделение ILanguageInformation на два интерфейса (#3378) * Refactor language information classes * Implement SimpleLanguageInformation abstract class * Move some properties to SimpleLanguageInformation * Implement format and debug buttons disabling if Intellisense is not supported for current language * Add Intellisense available check in FormsExtensions to deactivate menu items if needed * Change function interfaces in VisibilityService * Replace CodeCompletionController.CurrentParser null checks with IntellisenseAvailable method call * Refactor IParser and BaseParser to deduplicate code * Fix BuildTreeInSpecialMode in SPythonParser * Add SimpleParser class for new languages without Intellisense * Delete duplicate SetDebugButtonsEnabled function * Add Intellisense available check in RunService when attaching debugger * Delete inner check in SetDebugButtonsEnabled to improve architecture * Change CurrentParser property in CodeCompletion to be CurrentLanguage * Divide ILanguageInformation into two interfaces (creating new ILanguageIntellisenseSupport) * Fix LanguageIntellisenseSupport initialization in BaseLanguage * Fix RunService fictive_attach logic * Disable Intellisense parsing for languages without Intellisense support * Add DefaultLanguageInformation abstract class * Add default implementation for SetSemanticConstants in BaseLanguage * Simplify IParser interface * Update developer guide documentation
2026-01-27 22:25:28 +03:00
filesToParse[FileName] = true;
2015-05-14 22:35:07 +03:00
}
public void CloseFile(string FileName)
{
if (CodeCompletion.CodeCompletionController.comp_modules[FileName] != null)
CodeCompletion.CodeCompletionController.comp_modules.Remove(FileName);
filesToParse.Remove(FileName);
2015-05-14 22:35:07 +03:00
}
public void SetAsChanged(string FileName)
{
if (FileName != null && filesToParse.ContainsKey(FileName))
filesToParse[FileName] = true;
2015-05-14 22:35:07 +03:00
}
public void SetAllInProjectChanged()
{
try
{
foreach (string s in filesToParse.Keys.ToArray())
2015-05-14 22:35:07 +03:00
{
if (ProjectFactory.Instance.CurrentProject.ContainsSourceFile(s))
filesToParse[s] = true;
2015-05-14 22:35:07 +03:00
}
}
catch (Exception) { }
2015-05-14 22:35:07 +03:00
}
/// <summary>
/// Запуск потока с Intellisence
/// </summary>
public void SwitchOnIntellisence()
2015-05-14 22:35:07 +03:00
{
th = new System.Threading.Thread(InternalParsing);
th.Priority = System.Threading.ThreadPriority.BelowNormal;
th.IsBackground = true;
th.Start();
}
public void StopParsing()
{
try
{
VisualPABCSingleton.MainForm.StopTimer();
}
catch
{
}
}
private void InternalParsing()
{
while (true)
{
ParseInThread();
System.Threading.Thread.Sleep(2000);
}
}
private long mem_delta = 0;
internal void ParseInThread()
{
try
{
HashSet<string> recomp_files = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
2015-05-14 22:35:07 +03:00
bool is_comp = false;
foreach (string FileName in filesToParse.Keys.ToArray()) // копирование ключей обязательно, иначе будет InvalidOperationException EVA
2015-05-14 22:35:07 +03:00
{
if (filesToParse[FileName])
2015-05-14 22:35:07 +03:00
{
// Попытка компиляции была
filesToParse[FileName] = false;
2015-05-14 22:35:07 +03:00
is_comp = true;
string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string;
if (string.IsNullOrEmpty(text))
text = "begin end.";
if (CompileWatchedFile(FileName, text, true))
2015-05-14 22:35:07 +03:00
{
// успешная компиляция
recomp_files.Add(FileName);
2015-05-14 22:35:07 +03:00
}
}
}
if (recomp_files.Count > 0)
2015-05-14 22:35:07 +03:00
{
foreach (string FileName in filesToParse.Keys.ToArray()) // копирование ключей обязательно, иначе будет InvalidOperationException EVA
2015-05-14 22:35:07 +03:00
{
CodeCompletion.DomConverter dc = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter;
if (dc != null)
{
CodeCompletion.SymScope watchedUnitScope = dc.visitor.entry_scope;
if (watchedUnitScope != null)
2015-05-14 22:35:07 +03:00
{
var usedUnitsTransitive = watchedUnitScope.GetRealUsedUnitsTransitive();
for (int i = 0; i < usedUnitsTransitive.Length; i++)
{
string usedUnitFileName = usedUnitsTransitive[i].file_name;
// Если какая-то из зависимостей была перекомпилирована
if (recomp_files.Contains(usedUnitFileName))
2015-05-14 22:35:07 +03:00
{
// Помечаем нужные модули для будущей перекомпиляции
InvalidateDependentModules(usedUnitsTransitive, usedUnitFileName, filesToParse, FileName);
// Перекомпилируем текущий модуль
is_comp = true;
string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string;
// Здесь третий параметр false, потому что старые данные компиляции еще могут пригодиться до новой перекомпиляции EVA
if (CompileWatchedFile(FileName, text, false))
{
// успешная компиляция
recomp_files.Add(FileName);
}
2015-05-14 22:35:07 +03:00
}
}
}
}
}
}
2017-12-30 17:46:38 +03:00
if (is_comp && mem_delta > 20000000 /*&& mem_delta > 10000000*/)
2015-05-14 22:35:07 +03:00
//postavil delta dlja pamjati, posle kototoj delaetsja sborka musora
{
mem_delta = 0;
GC.Collect();
}
}
catch (Exception) { }
2015-05-14 22:35:07 +03:00
}
/// <summary>
/// Вызов компиляции (интеллисенсом) файла с именем fileName и содержимым fileText.
/// Возвращает true, если компиляция была успешна, false в противном случае.
/// </summary>
private bool CompileWatchedFile(string fileName, string fileText, bool clearOldScope)
{
bool success = false;
CodeCompletion.DomConverter tmp = CodeCompletion.CodeCompletionController.comp_modules[fileName] as CodeCompletion.DomConverter;
long cur_mem = Environment.WorkingSet;
CodeCompletion.CodeCompletionController controller = new CodeCompletion.CodeCompletionController();
CodeCompletion.DomConverter dc = controller.Compile(fileName, fileText);
mem_delta += Environment.WorkingSet - cur_mem;
if (dc.is_compiled)
{
if (clearOldScope && tmp != null)
{
tmp.visitor.entry_scope?.Clear();
tmp.visitor.cur_scope?.Clear();
}
CodeCompletion.CodeCompletionController.comp_modules[fileName] = dc;
success = true;
ParseInformationUpdated?.Invoke(dc.visitor.entry_scope, fileName);
}
else if (tmp == null)
CodeCompletion.CodeCompletionController.comp_modules[fileName] = dc;
return success;
}
/// <summary>
/// Помечает модули из candidateModulesToCheck и watchedFiles для будущей перекомпиляции, если они завивисимы от модуля с именем recompiledDependencyName
/// </summary>
private void InvalidateDependentModules(CodeCompletion.SymScope[] candidateModulesToCheck, string recompiledDependencyName, Dictionary<string, bool> watchedFiles, string currentFileForRecompiling)
{
// Скоупы модулей, соответствующих watchedFiles
var compiledWatchedScopes = watchedFiles.Where(watchedFile => watchedFile.Key != currentFileForRecompiling)
.Select(watchedFile => CodeCompletion.CodeCompletionController.comp_modules[watchedFile.Key])
.SelectMany(converter => converter is CodeCompletion.DomConverter dc ?
new CodeCompletion.SymScope[] { dc.visitor.entry_scope, dc.visitor.impl_scope }
: Enumerable.Empty<CodeCompletion.SymScope>())
.Where(sc => sc != null);
var scopesToCheck = compiledWatchedScopes.Concat(candidateModulesToCheck).Distinct();
foreach (var scope in scopesToCheck)
{
string scopeFileName = scope.file_name;
if (scope is CodeCompletion.ImplementationUnitScope)
scopeFileName = ((CodeCompletion.SymScope)scope.TopScope).file_name;
if (scopeFileName == recompiledDependencyName)
continue;
var usedUnitsForUnit = scope.GetRealUsedUnitsTransitive();
// Если в зависимостях скоупа есть recompiledDependency
if (usedUnitsForUnit.FirstOrDefault(u => u.file_name == recompiledDependencyName) != null)
{
var unitOldConverter = CodeCompletion.CodeCompletionController.comp_modules[scopeFileName];
if (unitOldConverter != null)
{
// Помечаем для перекомпиляции
if (watchedFiles.ContainsKey(scopeFileName))
{
watchedFiles[scopeFileName] = true;
}
else
{
CodeCompletion.CodeCompletionController.comp_modules.Remove(scopeFileName);
}
}
}
}
}
2015-05-14 22:35:07 +03:00
public bool IsParsing()
{
return th != null && th.ThreadState == System.Threading.ThreadState.Running;
}
public void ParseAllFiles()
{
if (visualEnvironmentCompiler.UserOptions.AllowCodeCompletion)// && visualEnvironmentCompiler.compilerLoaded)
{
if (th.ThreadState != System.Threading.ThreadState.Running)
{
th = new System.Threading.Thread(new System.Threading.ThreadStart(this.ParseInThread));
th.Priority = System.Threading.ThreadPriority.BelowNormal;
//th.IsBackground = true;
th.Start();
}
//if (th == null)
// RunParseThread();
}
}
}
}