pascalabcnet/CodeCompletion/CodeCompletion.cs
Александр Земляк b23f9c96d0
Деактивация кнопок, связанных с 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

344 lines
13 KiB
C#

// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt)
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using PascalABCCompiler.SyntaxTree;
using PascalABCCompiler.Parsers;
using PascalABCCompiler.Errors;
using System.IO;
using Languages.Facade;
using System.Linq;
namespace CodeCompletion
{
public class CodeCompletionController
{
private static readonly LanguageProvider LanguageProvider = LanguageProvider.Instance;
public Dictionary<syntax_tree_node, string> docs = new Dictionary<syntax_tree_node, string>();
// static bool parsers_loaded=false;
static CodeCompletionController()
{
//ParsersController.Reload();
}
public CodeCompletionController()
{
/*if (!parsers_loaded)
{
//ParsersController.Reload();
parsers_loaded = true;
}*/
//dconv = new DomConverter(this);
}
public static PascalABCCompiler.Compiler comp;// = new PascalABCCompiler.Compiler();
public static Dictionary<string, string> StandartDirectories = new Dictionary<string, string>();
public static Hashtable comp_modules = new Hashtable(StringComparer.OrdinalIgnoreCase);
// public static Hashtable parsers = new Hashtable(StringComparer.OrdinalIgnoreCase);
public static Dictionary<string, InterfaceUnitScope> pabcNamespaces = new Dictionary<string, InterfaceUnitScope>();
public static string currentLanguageISO;
// public static PascalABCCompiler.Parsers.IParser currentParser;
// static string cur_ext = ".pas";
private static ILanguage currentLanguage;
/// <summary>
/// Запоминает язык текущего открытого файла.
/// Если язык файла не поддерживается в системе, то ошибка не выбрасывается,
/// текущему языку присваивается null.
/// </summary>
public static void SetLanguage(string fileName)
{
currentLanguage = LanguageProvider.SelectLanguageByExtensionSafe(fileName);
}
public void ResetNamespaces()
{
pabcNamespaces.Clear();
}
public static ILanguage CurrentLanguage
{
get
{
return currentLanguage;
}
}
/// <summary>
/// Поддерживается ли Intellisense для текущего языка
/// </summary>
public static bool IntellisenseAvailable()
{
return CurrentLanguage != null && CurrentLanguage.LanguageIntellisenseSupport != null;
}
public DomConverter Compile(string FileName, string Text)
{
List<Error> ErrorsList = new List<Error>();
List<CompilerWarning> Warnings = new List<CompilerWarning>();
compilation_unit cu = null;
ILanguage currentLanguage = LanguageProvider.SelectLanguageByExtension(FileName);
try
{
cu = currentLanguage.Parser.GetCompilationUnit(FileName, Text, ErrorsList, Warnings, ParseMode.Normal, false);
ErrorsList.Clear();
if (currentLanguage.DocParser != null)
BuildDocs(Text, cu, currentLanguage.DocParser);
}
catch (Exception e)
{
#if DEBUG
File.AppendAllText("log.txt", e.Message + Environment.NewLine + e.StackTrace + Environment.NewLine);
#endif
}
TypeScope.instance_cache.Clear();
// очистка кэша и данных от старых компиляций, чтобы при новой компиляции не появились ссылки на старые данные
TypeTable.Clear();
DomConverter dconv = new DomConverter(this);
if (cu != null)
{
PascalABCCompiler.NetHelper.NetHelper.reset();
dconv.ConvertToDom(cu);
}
// Попытка поменять текст программы ниже сработает только для PascalABC.NET
else if (currentLanguage == LanguageProvider.MainLanguage)
{
ErrorsList.Clear();
Warnings.Clear();
try
{
string tmp = ParsersHelper.GetModifiedProgramm(Text);
if (tmp != null)
{
cu = currentLanguage.Parser.GetCompilationUnit(FileName, Text, ErrorsList, Warnings, ParseMode.Special, false);
}
ErrorsList.Clear();
Warnings.Clear();
if (currentLanguage.DocParser != null)
BuildDocs(Text, cu, currentLanguage.DocParser);
}
catch (Exception e)
{
#if DEBUG
File.AppendAllText("log.txt", e.Message + Environment.NewLine + e.StackTrace + Environment.NewLine);
#endif
}
if (cu != null)
{
PascalABCCompiler.NetHelper.NetHelper.reset();
dconv.ConvertToDom(cu);
}
}
if (docs != null) docs.Clear();
//if (dconv.is_compiled) comp_modules[file_name]=dconv;
return dconv;
//ConvertToDom(cu);
}
private void BuildDocs(string Text, compilation_unit cu, IDocParser docParser)
{
documentation_comment_list dt = docParser.BuildTree(Text);
PascalABCCompiler.DocumentationConstructor docconst = new PascalABCCompiler.DocumentationConstructor();
if (cu != null)
docs = docconst.Construct(cu, dt);
}
public compilation_unit ParseOnlySyntaxTree(string FileName, string Text)
{
var currentLanguage = LanguageProvider.SelectLanguageByExtensionSafe(FileName);
if (currentLanguage == null)
return null;
var cu = currentLanguage.Parser.GetCompilationUnit(FileName, Text, new List<Error>(), new List<CompilerWarning>(), ParseMode.Normal, false);
return cu;
}
public DomConverter CompileAllIfNeed(string FileName, bool parse_only_interface=false)
{
string Text = comp.GetSourceFileText(FileName);
List<Error> ErrorsList = new List<Error>();
List<CompilerWarning> Warnings = new List<CompilerWarning>();
compilation_unit cu = null;
DomConverter dconv = new DomConverter(this);
ILanguage currentLanguage = LanguageProvider.SelectLanguageByExtensionSafe(FileName);
if (currentLanguage == null)
return dconv;
if (Text != null)
{
cu = currentLanguage.Parser.GetCompilationUnit(FileName, Text, ErrorsList, Warnings, ParseMode.Normal, true);
}
ErrorsList.Clear();
Warnings.Clear();
if (currentLanguage.DocParser != null)
BuildDocs(Text, cu, currentLanguage.DocParser);
dconv.visitor.parse_only_interface = parse_only_interface;
if (CodeCompletionTools.XmlDoc.LookupLocalizedXmlDocForUnitWithSources(FileName, CodeCompletionController.currentLanguageISO) != null)
{
dconv.visitor.add_doc_from_text = false;
}
if (cu != null)
{
dconv.ConvertToDom(cu);
}
// Попытка поменять текст программы ниже сработает только для PascalABC.NET
else if (currentLanguage == LanguageProvider.MainLanguage)
{
ErrorsList.Clear();
Warnings.Clear();
if (comp_modules[FileName] == null)
{
string tmp = ParsersHelper.GetModifiedProgramm(Text);
if (tmp != null)
{
cu = currentLanguage.Parser.GetCompilationUnit(FileName, Text, ErrorsList, Warnings, ParseMode.Special, true);
}
ErrorsList.Clear();
Warnings.Clear();
}
if (currentLanguage.DocParser != null)
BuildDocs(Text, cu, currentLanguage.DocParser);
if (CodeCompletionTools.XmlDoc.LookupLocalizedXmlDocForUnitWithSources(FileName, CodeCompletionController.currentLanguageISO) != null)
{
dconv.visitor.add_doc_from_text = false;
}
if (cu != null)
{
dconv.ConvertToDom(cu);
}
}
if (dconv.is_compiled) comp_modules[FileName] = dconv;
if (docs != null) docs.Clear();
//comp_modules[file_name] = dconv;
// GC.Collect();
return dconv;
}
}
public class CodeCompletionNameHelper
{
public static readonly string system_unit_file_name = PascalABCCompiler.StringConstants.pascalSystemUnitName;
public static string system_unit_file_full_name;
private static CodeCompletionNameHelper helper;
private CodeCompletionNameHelper()
{
}
public PascalABCCompiler.Parsers.KeywordKind GetKeywordKind(string name)
{
if (CodeCompletionController.IntellisenseAvailable())
return CodeCompletionController.CurrentLanguage.LanguageIntellisenseSupport.GetKeywordKind(name);
return PascalABCCompiler.Parsers.KeywordKind.None;
}
public bool IsKeyword(string name)
{
if (CodeCompletionController.IntellisenseAvailable())
{
return CodeCompletionController.CurrentLanguage.LanguageIntellisenseSupport.IsKeyword(name);
}
return false;
}
public List<string> GetKeywords()
{
if (CodeCompletionController.IntellisenseAvailable())
return CodeCompletionController.CurrentLanguage.LanguageInformation.KeywordsStorage.KeywordsForIntellisenseList;
return new List<string>();
}
public List<string> GetTypeKeywords()
{
if (CodeCompletionController.IntellisenseAvailable())
return CodeCompletionController.CurrentLanguage.LanguageInformation.KeywordsStorage.TypeKeywords;
return new List<string>();
}
const string LibSourceDirectoryIdent = "%LIBSOURCEDIRECTORY%";
public static string FindSourceFileName(string unit_name, out int found_dir_ind, ILanguage currentUnitLanguage, params string[] ddirs)
{
// TODO: check error in older version
List<string> Dirs = new List<string>();
Dirs.AddRange(ddirs);
if (CodeCompletionController.comp != null)
Dirs.AddRange(CodeCompletionController.comp.GetCurrentSearchDirectories(currentUnitLanguage));
// Надо как-то проверять, что мы не в инсталированной версии EVA
if (CodeCompletionController.StandartDirectories.ContainsKey(LibSourceDirectoryIdent) && Directory.Exists(CodeCompletionController.StandartDirectories[LibSourceDirectoryIdent]))
Dirs.AddRange(GetLibSourceDirectories(currentUnitLanguage));
return CodeCompletionController.comp.FindSourceFileNameInDirs(unit_name, out found_dir_ind, currentUnitLanguage, Dirs.ToArray());
}
private static string[] GetLibSourceDirectories(ILanguage currentUnitLanguage)
{
return LanguageProvider.Instance.Languages
.Where(lang => lang == currentUnitLanguage)
.Concat(LanguageProvider.Instance.Languages.Where(lang => lang != currentUnitLanguage))
.Select(lang => Path.Combine(CodeCompletionController.StandartDirectories[LibSourceDirectoryIdent],
lang.Name.Replace(PascalABCCompiler.StringConstants.pascalLanguageName, ""))) // для PascalABC.NET прямо в LibSource, остальные во внутренних папках
.Where(dir => Directory.Exists(dir))
.ToArray();
}
public static CodeCompletionNameHelper Helper
{
get
{
if (helper == null) helper = new CodeCompletionNameHelper();
return helper;
}
}
}
public class ParsersHelper
{
string src;
static StringBuilder sb = new StringBuilder();
public static string GetModifiedProgramm(string src)
{
sb.Remove(0,sb.Length);
if (!src.TrimEnd().EndsWith("end."))
{
sb.AppendLine(src);
sb.AppendLine();
sb.Append("end.");
return sb.ToString();
}
return null;
}
}
}