diff --git a/CodeCompletion/CodeCompletion.cs b/CodeCompletion/CodeCompletion.cs index b514e05f9..47c0a937d 100644 --- a/CodeCompletion/CodeCompletion.cs +++ b/CodeCompletion/CodeCompletion.cs @@ -60,12 +60,8 @@ namespace CodeCompletion { pabcNamespaces.Clear(); } - - private static string get_doctagsParserExtension(string ext) - { - return ext + "dt"; - } + // нужно переделать на использование ILanguage EVA public static IParser CurrentParser { get @@ -376,18 +372,18 @@ namespace CodeCompletion return false; } - public string[] GetKeywords() + public List GetKeywords() { if (CodeCompletionController.CurrentParser != null) - return CodeCompletionController.CurrentParser.LanguageInformation.Keywords; - return new string[0]; + return CodeCompletionController.CurrentParser.LanguageInformation.KeywordsStorage.Keywords; + return new List(); } - public string[] GetTypeKeywords() + public List GetTypeKeywords() { if (CodeCompletionController.CurrentParser != null) - return CodeCompletionController.CurrentParser.LanguageInformation.TypeKeywords; - return new string[0]; + return CodeCompletionController.CurrentParser.LanguageInformation.KeywordsStorage.TypeKeywords; + return new List(); } const string LibSourceDirectoryIdent = "%LIBSOURCEDIRECTORY%"; diff --git a/CodeCompletion/DomConverter.cs b/CodeCompletion/DomConverter.cs index 8f93451b1..311000fac 100644 --- a/CodeCompletion/DomConverter.cs +++ b/CodeCompletion/DomConverter.cs @@ -39,6 +39,8 @@ namespace CodeCompletion //TreeConverterSymbolTable tcst; public CodeCompletionController controller; public bool is_compiled = false; + + // TODO: Требуется адаптировать к многоязычности EVA public static SymInfo[] standard_units; private Hashtable cur_used_assemblies; public CompilationUnit unit; diff --git a/LanguageIntegrator/BaseLanguage.cs b/LanguageIntegrator/BaseLanguage.cs index c4610cfde..9b318ca90 100644 --- a/LanguageIntegrator/BaseLanguage.cs +++ b/LanguageIntegrator/BaseLanguage.cs @@ -17,14 +17,16 @@ namespace Languages.Facade /// /// Все параметры должны быть не null (и не пустым массивом), кроме IDocParser в случае, если он не требуется /// - public BaseLanguage(string name, string version, string copyright, IParser parser, IDocParser docParser, + public BaseLanguage(string name, string version, string copyright, ILanguageInformation languageInformation, IParser parser, IDocParser docParser, List syntaxTreeConverters, syntax_tree_visitor syntaxTreeToSemanticTreeConverter, string[] filesExtensions, bool caseSensitive, string[] systemUnitNames) { this.Name = name; this.Version = version; this.Copyright = copyright; + this.LanguageInformation = languageInformation; this.Parser = parser; + this.Parser.LanguageInformation = languageInformation; this.DocParser = docParser; this.SyntaxTreeConverters = syntaxTreeConverters; this.SyntaxTreeToSemanticTreeConverter = syntaxTreeToSemanticTreeConverter; @@ -39,6 +41,8 @@ namespace Languages.Facade public virtual string Copyright { get; protected set; } + public virtual ILanguageInformation LanguageInformation { get; } + public virtual IParser Parser { get; protected set; } public virtual IDocParser DocParser { get; protected set; } diff --git a/LanguageIntegrator/ILanguage.cs b/LanguageIntegrator/ILanguage.cs index d0f27dcbe..c2f48d6ed 100644 --- a/LanguageIntegrator/ILanguage.cs +++ b/LanguageIntegrator/ILanguage.cs @@ -28,6 +28,8 @@ namespace Languages.Facade /// string Copyright { get; } + ILanguageInformation LanguageInformation { get; } + /// /// Основной парсер языка /// diff --git a/ParserTools/ParserTools/BaseParser.cs b/ParserTools/ParserTools/BaseParser.cs index 67050d2c8..d11705cca 100644 --- a/ParserTools/ParserTools/BaseParser.cs +++ b/ParserTools/ParserTools/BaseParser.cs @@ -17,20 +17,7 @@ namespace PascalABCCompiler.Parsers public Func CheckIfParsingUnit { get; set; } - public Dictionary ValidDirectives { get; protected set; } - - private ILanguageInformation languageInformation; - public virtual ILanguageInformation LanguageInformation - { - get - { - if (languageInformation == null) - languageInformation = new DefaultLanguageInformation(this); - return languageInformation; - } - } - - public BaseKeywords Keywords { get; protected set; } + public ILanguageInformation LanguageInformation { get; set; } /// /// Возвращеает синтаксическое дерево модуля diff --git a/ParserTools/ParserTools/ILanguageInformation.cs b/ParserTools/ParserTools/ILanguageInformation.cs index ac593d33a..6ec3f840c 100644 --- a/ParserTools/ParserTools/ILanguageInformation.cs +++ b/ParserTools/ParserTools/ILanguageInformation.cs @@ -15,10 +15,10 @@ namespace PascalABCCompiler.Parsers /// Получить полное описание элемента (в желтой подсказке) /// string GetDescription(IBaseScope scope); - /// - /// Получить краткое описание элемента (без ключевых слов) - /// - string GetSimpleDescription(IBaseScope scope); + /// + /// Получить краткое описание элемента (без ключевых слов) + /// + string GetSimpleDescription(IBaseScope scope); /// /// Получить короткое имя откомпилированного типа /// @@ -129,14 +129,17 @@ namespace PascalABCCompiler.Parsers //char GetParameterDelimiter(); string GetCompiledTypeRepresentation(Type t, System.Reflection.MemberInfo mi, ref int line, ref int col); bool IsKeyword(string value); - string[] Keywords - { - get; - } - string[] TypeKeywords + + BaseKeywords KeywordsStorage { get; } + + /// + /// Данные о всех поддерживаемых директивах компилятора + /// + Dictionary ValidDirectives { get; } + string SystemUnitName { get; diff --git a/ParserTools/ParserTools/IParser.cs b/ParserTools/ParserTools/IParser.cs index 94f9d1e4c..70cf789d8 100644 --- a/ParserTools/ParserTools/IParser.cs +++ b/ParserTools/ParserTools/IParser.cs @@ -1,4 +1,4 @@ -// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt) +// 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.Collections.Generic; using PascalABCCompiler.SyntaxTree; @@ -20,24 +20,11 @@ namespace PascalABCCompiler.Parsers } /// - /// Callback , ( ) + /// Callback для проверки, парсим ли мы сейчас модуль или нет (для языков без ключевых слов у модуля) /// Func CheckIfParsingUnit { get; set; } - /// - /// - /// - Dictionary ValidDirectives { get; } - - ILanguageInformation LanguageInformation - { - get; - } - - /// - /// , - /// - BaseKeywords Keywords { get; } + ILanguageInformation LanguageInformation { get; set; } compilation_unit GetCompilationUnit(string FileName, string Text, List Errors, List Warnings, ParseMode parseMode, List DefinesList = null); @@ -50,7 +37,6 @@ namespace PascalABCCompiler.Parsers expression GetTypeAsExpression(string FileName, string Text, List Errors, List Warnings); void Reset(); - } diff --git a/ParserTools/ParserTools/Keywords/BaseKeywords.cs b/ParserTools/ParserTools/Keywords/BaseKeywords.cs index 80e7a6c78..001d383c4 100644 --- a/ParserTools/ParserTools/Keywords/BaseKeywords.cs +++ b/ParserTools/ParserTools/Keywords/BaseKeywords.cs @@ -12,7 +12,13 @@ namespace PascalABCCompiler.Parsers /// /// Соотвествие ключевых слов токенам /// - protected abstract Dictionary KeywordsToTokens { get; set; } + public Dictionary KeywordsToTokens { get; set; } + + public Dictionary KeywordKinds { get; set; } + + public List Keywords { get; set; } = new List(); + + public List TypeKeywords { get; set; } = new List(); /// /// Словарь соответствий ключевых слов их эквивалентам (задается пользователем в специальном файле) @@ -43,7 +49,7 @@ namespace PascalABCCompiler.Parsers } /// - /// Возвращает само эквивалент ключевого слова, либо его само, если эквивалента нет + /// Возвращает эквивалент ключевого слова, либо его самого, если эквивалента нет /// public string ConvertKeyword(string keyword) { @@ -53,9 +59,13 @@ namespace PascalABCCompiler.Parsers return keymap[keyword]; } - public BaseKeywords() + public BaseKeywords(bool caseSensitive) { ReloadKeyMap(); + + KeywordsToTokens = new Dictionary(caseSensitive ? StringComparer.CurrentCulture : StringComparer.CurrentCultureIgnoreCase); + + KeywordKinds = new Dictionary(caseSensitive ? StringComparer.CurrentCulture : StringComparer.CurrentCultureIgnoreCase); } /// @@ -73,5 +83,21 @@ namespace PascalABCCompiler.Parsers else return GetIdToken(); } + + public void CreateNewKeyword(string name, Enum token, KeywordKind kind = KeywordKind.None, bool isTypeKeyword = false) + { + name = ConvertKeyword(name); + + if (kind != KeywordKind.None) + KeywordKinds[name] = kind; + + Keywords.Add(name); + KeywordsToTokens[name] = (int)(object)token; + + if (isTypeKeyword) + TypeKeywords.Add(name); + + // return new Keyword(name, token, kind, isTypeKeyword); + } } } diff --git a/ParserTools/ParserTools/Keywords/KeywordKind.cs b/ParserTools/ParserTools/Keywords/KeywordKind.cs index cbf9479c3..5aa29e9fc 100644 --- a/ParserTools/ParserTools/Keywords/KeywordKind.cs +++ b/ParserTools/ParserTools/Keywords/KeywordKind.cs @@ -55,33 +55,23 @@ namespace PascalABCCompiler.Parsers CommonExpressionKeyword } - /*public class Keyword + /*public struct Keyword { - string _name; - KeywordKind _kind = KeywordKind.None; - public string Name + public string Name { get; set; } + public KeywordKind Kind { get; set; } + + public bool IsTypeKeyword { get; set; } + + public System.Enum Token { get; set; } + + public Keyword(string name, System.Enum token, KeywordKind kind = KeywordKind.None, bool isTypeKeyword = false) { - get - { - return _name; - } - } - KeywordKind Kind - { - get - { - return _kind; - } - } - public Keyword(string name, KeywordKind kind) - { - _name = name; - _kind = kind; - } - public Keyword(string name) - { - _name = name; + Name = name; + Token = token; + Kind = kind; + IsTypeKeyword = isTypeKeyword; } + public override string ToString() { return Name; diff --git a/Parsers/PascalABCParserNewSaushkin/Keywords.cs b/Parsers/PascalABCParserNewSaushkin/Keywords.cs index 5c027544a..18c8c11a1 100644 --- a/Parsers/PascalABCParserNewSaushkin/Keywords.cs +++ b/Parsers/PascalABCParserNewSaushkin/Keywords.cs @@ -1,126 +1,120 @@ // 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.Generic; -using System.Linq; +using PascalABCCompiler.Parsers; namespace Languages.Pascal.Frontend.Core { - public class PascalABCKeywords : PascalABCCompiler.Parsers.BaseKeywords + public class PascalABCKeywords : BaseKeywords { protected override string FileName => "keywordsmap.pabc"; - protected override Dictionary KeywordsToTokens { get; set; } - - public PascalABCKeywords() : base() + public PascalABCKeywords() : base(false) { - KeywordsToTokens = new Dictionary - { - ["or"] = (int)Tokens.tkOr, - ["xor"] = (int)Tokens.tkXor, - ["and"] = (int)Tokens.tkAnd, - ["div"] = (int)Tokens.tkDiv, - ["mod"] = (int)Tokens.tkMod, - ["shl"] = (int)Tokens.tkShl, - ["shr"] = (int)Tokens.tkShr, - ["not"] = (int)Tokens.tkNot, - ["as"] = (int)Tokens.tkAs, - ["in"] = (int)Tokens.tkIn, - ["is"] = (int)Tokens.tkIs, - ["implicit"] = (int)Tokens.tkImplicit, - ["explicit"] = (int)Tokens.tkExplicit, - ["sizeof"] = (int)Tokens.tkSizeOf, - ["typeof"] = (int)Tokens.tkTypeOf, - ["where"] = (int)Tokens.tkWhere, - ["array"] = (int)Tokens.tkArray, - ["begin"] = (int)Tokens.tkBegin, - ["case"] = (int)Tokens.tkCase, - ["class"] = (int)Tokens.tkClass, - ["const"] = (int)Tokens.tkConst, - ["constructor"] = (int)Tokens.tkConstructor, - ["default"] = (int)Tokens.tkDefault, - ["destructor"] = (int)Tokens.tkDestructor, - ["downto"] = (int)Tokens.tkDownto, - ["do"] = (int)Tokens.tkDo, - ["else"] = (int)Tokens.tkElse, - ["end"] = (int)Tokens.tkEnd, - ["event"] = (int)Tokens.tkEvent, - ["except"] = (int)Tokens.tkExcept, - ["exports"] = (int)Tokens.tkExports, - ["file"] = (int)Tokens.tkFile, - ["finalization"] = (int)Tokens.tkFinalization, - ["finally"] = (int)Tokens.tkFinally, - ["for"] = (int)Tokens.tkFor, - ["foreach"] = (int)Tokens.tkForeach, - ["function"] = (int)Tokens.tkFunction, - ["goto"] = (int)Tokens.tkGoto, - ["if"] = (int)Tokens.tkIf, - ["implementation"] = (int)Tokens.tkImplementation, - ["inherited"] = (int)Tokens.tkInherited, - ["initialization"] = (int)Tokens.tkInitialization, - ["interface"] = (int)Tokens.tkInterface, - ["label"] = (int)Tokens.tkLabel, - ["lock"] = (int)Tokens.tkLock, - ["loop"] = (int)Tokens.tkLoop, - ["nil"] = (int)Tokens.tkNil, - ["procedure"] = (int)Tokens.tkProcedure, - ["of"] = (int)Tokens.tkOf, - ["operator"] = (int)Tokens.tkOperator, - ["property"] = (int)Tokens.tkProperty, - ["raise"] = (int)Tokens.tkRaise, - ["record"] = (int)Tokens.tkRecord, - ["repeat"] = (int)Tokens.tkRepeat, - ["set"] = (int)Tokens.tkSet, - ["try"] = (int)Tokens.tkTry, - ["type"] = (int)Tokens.tkType, - ["then"] = (int)Tokens.tkThen, - ["to"] = (int)Tokens.tkTo, - ["until"] = (int)Tokens.tkUntil, - ["uses"] = (int)Tokens.tkUses, - ["var"] = (int)Tokens.tkVar, - ["while"] = (int)Tokens.tkWhile, - ["with"] = (int)Tokens.tkWith, - ["program"] = (int)Tokens.tkProgram, - ["template"] = (int)Tokens.tkTemplate, - ["resourcestring"] = (int)Tokens.tkResourceString, - ["threadvar"] = (int)Tokens.tkThreadvar, - ["sealed"] = (int)Tokens.tkSealed, - ["partial"] = (int)Tokens.tkPartial, - ["params"] = (int)Tokens.tkParams, - ["unit"] = (int)Tokens.tkUnit, - ["library"] = (int)Tokens.tkLibrary, - ["external"] = (int)Tokens.tkExternal, - ["name"] = (int)Tokens.tkName, - ["private"] = (int)Tokens.tkPrivate, - ["protected"] = (int)Tokens.tkProtected, - ["public"] = (int)Tokens.tkPublic, - ["internal"] = (int)Tokens.tkInternal, - ["read"] = (int)Tokens.tkRead, - ["write"] = (int)Tokens.tkWrite, - ["on"] = (int)Tokens.tkOn, - ["forward"] = (int)Tokens.tkForward, - ["abstract"] = (int)Tokens.tkAbstract, - ["overload"] = (int)Tokens.tkOverload, - ["reintroduce"] = (int)Tokens.tkReintroduce, - ["override"] = (int)Tokens.tkOverride, - ["virtual"] = (int)Tokens.tkVirtual, - ["extensionmethod"] = (int)Tokens.tkExtensionMethod, - ["new"] = (int)Tokens.tkNew, - ["auto"] = (int)Tokens.tkAuto, - ["sequence"] = (int)Tokens.tkSequence, - ["yield"] = (int)Tokens.tkYield, - ["match"] = (int)Tokens.tkMatch, - ["when"] = (int)Tokens.tkWhen, - ["namespace"] = (int)Tokens.tkNamespace, - ["static"] = (int)Tokens.tkStatic, - ["step"] = (int)Tokens.tkStep, - ["index"] = (int)Tokens.tkIndex, - ["async"] = (int)Tokens.tkAsync, - ["await"] = (int)Tokens.tkAwait - } - .ToDictionary(kv => ConvertKeyword(kv.Key), kv => kv.Value, StringComparer.CurrentCultureIgnoreCase); + #region Keywords Initialization + CreateNewKeyword("or", Tokens.tkOr); + CreateNewKeyword("xor", Tokens.tkXor); + CreateNewKeyword("and", Tokens.tkAnd); + CreateNewKeyword("div", Tokens.tkDiv); + CreateNewKeyword("mod", Tokens.tkMod); + CreateNewKeyword("shl", Tokens.tkShl); + CreateNewKeyword("shr", Tokens.tkShr); + CreateNewKeyword("not", Tokens.tkNot); + CreateNewKeyword("as", Tokens.tkAs); + CreateNewKeyword("in", Tokens.tkIn); + CreateNewKeyword("is", Tokens.tkIs); + CreateNewKeyword("implicit", Tokens.tkImplicit); + CreateNewKeyword("explicit", Tokens.tkExplicit); + CreateNewKeyword("sizeof", Tokens.tkSizeOf); + CreateNewKeyword("typeof", Tokens.tkTypeOf); + CreateNewKeyword("where", Tokens.tkWhere); + CreateNewKeyword("array", Tokens.tkArray, isTypeKeyword: true); + CreateNewKeyword("begin", Tokens.tkBegin); + CreateNewKeyword("case", Tokens.tkCase); + CreateNewKeyword("class", Tokens.tkClass, isTypeKeyword: true); + CreateNewKeyword("const", Tokens.tkConst, KeywordKind.Const); + CreateNewKeyword("constructor", Tokens.tkConstructor, KeywordKind.Constructor); + CreateNewKeyword("default", Tokens.tkDefault); + CreateNewKeyword("destructor", Tokens.tkDestructor, KeywordKind.Destructor); + CreateNewKeyword("downto", Tokens.tkDownto); + CreateNewKeyword("do", Tokens.tkDo); + CreateNewKeyword("else", Tokens.tkElse); + CreateNewKeyword("end", Tokens.tkEnd); + CreateNewKeyword("event", Tokens.tkEvent); + CreateNewKeyword("except", Tokens.tkExcept); + CreateNewKeyword("exports", Tokens.tkExports); + CreateNewKeyword("file", Tokens.tkFile, isTypeKeyword : true); + CreateNewKeyword("finalization", Tokens.tkFinalization); + CreateNewKeyword("finally", Tokens.tkFinally); + CreateNewKeyword("for", Tokens.tkFor); + CreateNewKeyword("foreach", Tokens.tkForeach); + CreateNewKeyword("function", Tokens.tkFunction, KeywordKind.Function, true); + CreateNewKeyword("goto", Tokens.tkGoto); + CreateNewKeyword("if", Tokens.tkIf); + CreateNewKeyword("implementation", Tokens.tkImplementation); + CreateNewKeyword("inherited", Tokens.tkInherited, KeywordKind.Inherited); + CreateNewKeyword("initialization", Tokens.tkInitialization); + CreateNewKeyword("interface", Tokens.tkInterface); + CreateNewKeyword("label", Tokens.tkLabel); + CreateNewKeyword("lock", Tokens.tkLock); + CreateNewKeyword("loop", Tokens.tkLoop); + CreateNewKeyword("nil", Tokens.tkNil); + CreateNewKeyword("procedure", Tokens.tkProcedure, KeywordKind.Function, true); + CreateNewKeyword("of", Tokens.tkOf, KeywordKind.Of); + CreateNewKeyword("operator", Tokens.tkOperator); + CreateNewKeyword("property", Tokens.tkProperty); + CreateNewKeyword("raise", Tokens.tkRaise, KeywordKind.Raise); + CreateNewKeyword("record", Tokens.tkRecord, isTypeKeyword: true); + CreateNewKeyword("repeat", Tokens.tkRepeat); + CreateNewKeyword("set", Tokens.tkSet, isTypeKeyword : true); + CreateNewKeyword("try", Tokens.tkTry); + CreateNewKeyword("type", Tokens.tkType, KeywordKind.Type); + CreateNewKeyword("then", Tokens.tkThen); + CreateNewKeyword("to", Tokens.tkTo); + CreateNewKeyword("until", Tokens.tkUntil); + CreateNewKeyword("uses", Tokens.tkUses, KeywordKind.Uses); + CreateNewKeyword("var", Tokens.tkVar, KeywordKind.Var); + CreateNewKeyword("while", Tokens.tkWhile); + CreateNewKeyword("with", Tokens.tkWith); + CreateNewKeyword("program", Tokens.tkProgram, KeywordKind.Program); + CreateNewKeyword("template", Tokens.tkTemplate); + CreateNewKeyword("resourcestring", Tokens.tkResourceString); + CreateNewKeyword("threadvar", Tokens.tkThreadvar); + CreateNewKeyword("sealed", Tokens.tkSealed); + CreateNewKeyword("partial", Tokens.tkPartial); + CreateNewKeyword("params", Tokens.tkParams); + CreateNewKeyword("unit", Tokens.tkUnit, KeywordKind.Unit); + CreateNewKeyword("library", Tokens.tkLibrary); + CreateNewKeyword("external", Tokens.tkExternal); + CreateNewKeyword("name", Tokens.tkName); + CreateNewKeyword("private", Tokens.tkPrivate); + CreateNewKeyword("protected", Tokens.tkProtected); + CreateNewKeyword("public", Tokens.tkPublic); + CreateNewKeyword("internal", Tokens.tkInternal); + CreateNewKeyword("read", Tokens.tkRead); + CreateNewKeyword("write", Tokens.tkWrite); + CreateNewKeyword("on", Tokens.tkOn); + CreateNewKeyword("forward", Tokens.tkForward); + CreateNewKeyword("abstract", Tokens.tkAbstract); + CreateNewKeyword("overload", Tokens.tkOverload); + CreateNewKeyword("reintroduce", Tokens.tkReintroduce); + CreateNewKeyword("override", Tokens.tkOverride); + CreateNewKeyword("virtual", Tokens.tkVirtual); + CreateNewKeyword("extensionmethod", Tokens.tkExtensionMethod); + CreateNewKeyword("new", Tokens.tkNew, KeywordKind.New); + CreateNewKeyword("auto", Tokens.tkAuto); + CreateNewKeyword("sequence", Tokens.tkSequence); + CreateNewKeyword("yield", Tokens.tkYield); + CreateNewKeyword("match", Tokens.tkMatch); + CreateNewKeyword("when", Tokens.tkWhen); + CreateNewKeyword("namespace", Tokens.tkNamespace); + CreateNewKeyword("static", Tokens.tkStatic); + CreateNewKeyword("step", Tokens.tkStep); + CreateNewKeyword("index", Tokens.tkIndex); + CreateNewKeyword("async", Tokens.tkAsync); + CreateNewKeyword("await", Tokens.tkAwait); + #endregion } protected override int GetIdToken() => (int)Tokens.tkIdentifier; diff --git a/Parsers/PascalABCParserNewSaushkin/Parser.cs b/Parsers/PascalABCParserNewSaushkin/Parser.cs index 78ec587f4..2f65437e4 100644 --- a/Parsers/PascalABCParserNewSaushkin/Parser.cs +++ b/Parsers/PascalABCParserNewSaushkin/Parser.cs @@ -5,10 +5,7 @@ using System.IO; using System.Collections.Generic; using PascalABCCompiler.SyntaxTree; using PascalABCCompiler.Parsers; -using PascalABCCompiler; using Languages.Pascal.Frontend.Core; -using PascalABCCompiler.ParserTools.Directives; -using static PascalABCCompiler.ParserTools.Directives.DirectiveHelper; namespace Languages.Pascal.Frontend.Wrapping { @@ -74,12 +71,6 @@ namespace Languages.Pascal.Frontend.Wrapping /// public class PascalABCNewLanguageParser : BaseParser { - - public PascalABCNewLanguageParser() - { - InitializeValidDirectives(); - Keywords = new PascalABCKeywords(); - } public override void Reset() { @@ -87,54 +78,6 @@ namespace Languages.Pascal.Frontend.Wrapping Errors.Clear(); } - - /// - /// Здесь записываются все директивы, поддерживаемые языком, а также правила для проверки их параметров (ограничения, накладываемые со стороны языка) - /// - private void InitializeValidDirectives() - { - #region VALID DIRECTIVES - ValidDirectives = new Dictionary(StringComparer.CurrentCultureIgnoreCase) - { - [StringConstants.compiler_directive_apptype] = new DirectiveInfo(SingleAnyOfCheck("console", "windows", "dll", "pcu")), - [StringConstants.compiler_directive_reference] = new DirectiveInfo(quotesAreSpecialSymbols: true), - [StringConstants.compiler_directive_include_namespace] = new DirectiveInfo(quotesAreSpecialSymbols: true), - [StringConstants.compiler_directive_savepcu] = new DirectiveInfo(SingleAnyOfCheck("true", "false")), - [StringConstants.compiler_directive_zerobasedstrings] = new DirectiveInfo(SingleAnyOfCheck("on", "off"), paramsNums: new int[2] { 0, 1 }), - [StringConstants.compiler_directive_zerobasedstrings_ON] = NoParamsDirectiveInfo(), - [StringConstants.compiler_directive_zerobasedstrings_OFF] = NoParamsDirectiveInfo(), - [StringConstants.compiler_directive_nullbasedstrings_ON] = NoParamsDirectiveInfo(), - [StringConstants.compiler_directive_nullbasedstrings_OFF] = NoParamsDirectiveInfo(), - [StringConstants.compiler_directive_initstring_as_empty_ON] = NoParamsDirectiveInfo(), - [StringConstants.compiler_directive_initstring_as_empty_OFF] = NoParamsDirectiveInfo(), - [StringConstants.compiler_directive_resource] = new DirectiveInfo(quotesAreSpecialSymbols: true), - [StringConstants.compiler_directive_platformtarget] = new DirectiveInfo(SingleAnyOfCheck("x86", "x64", "anycpu", "dotnet5win", "dotnet5linux", "dotnet5macos", "native")), - [StringConstants.compiler_directive_faststrings] = NoParamsDirectiveInfo(), - [StringConstants.compiler_directive_gendoc] = new DirectiveInfo(SingleAnyOfCheck("true", "false")), - [StringConstants.compiler_directive_region] = new DirectiveInfo(checkParamsNumNeeded: false), - [StringConstants.compiler_directive_endregion] = new DirectiveInfo(checkParamsNumNeeded: false), - [StringConstants.compiler_directive_ifdef] = new DirectiveInfo(SingleIsValidIdCheck()), - [StringConstants.compiler_directive_endif] = new DirectiveInfo(SingleIsValidIdCheck(), paramsNums: new int[2] { 0, 1 }), - [StringConstants.compiler_directive_ifndef] = new DirectiveInfo(SingleIsValidIdCheck()), - [StringConstants.compiler_directive_else] = new DirectiveInfo(SingleIsValidIdCheck(), paramsNums: new int[2] { 0, 1 }), - [StringConstants.compiler_directive_undef] = new DirectiveInfo(SingleIsValidIdCheck()), - [StringConstants.compiler_directive_define] = new DirectiveInfo(SingleIsValidIdCheck()), - [StringConstants.compiler_directive_include] = new DirectiveInfo(quotesAreSpecialSymbols: true), - [StringConstants.compiler_directive_targetframework] = new DirectiveInfo(), - [StringConstants.compiler_directive_hidden_idents] = NoParamsDirectiveInfo(), - [StringConstants.compiler_directive_disable_standard_units] = NoParamsDirectiveInfo(), - [StringConstants.compiler_directive_version_string] = new DirectiveInfo(IsValidVersionCheck()), - [StringConstants.compiler_directive_product_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), - [StringConstants.compiler_directive_company_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), - [StringConstants.compiler_directive_trademark_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), - [StringConstants.compiler_directive_main_resource_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), - [StringConstants.compiler_directive_title_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), - [StringConstants.compiler_directive_description_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), - [StringConstants.compiler_directive_omp] = new DirectiveInfo(SingleAnyOfCheck("critical", "parallel"), checkParamsNumNeeded: false) - }; - #endregion - } - protected override void PreBuildTree(string FileName) { CompilerDirectives = new List(); @@ -151,11 +94,11 @@ namespace Languages.Pascal.Frontend.Wrapping #endif #endif - PascalParserTools parserTools = new PascalParserTools(Errors, Warnings, ValidDirectives, + PascalParserTools parserTools = new PascalParserTools(Errors, Warnings, LanguageInformation.ValidDirectives, buildTreeForFormatter, false, Path.GetFullPath(fileName), CompilerDirectives); // контекст сканера и парсера - Scanner scanner = new Scanner(Text, parserTools, Keywords, definesList); + Scanner scanner = new Scanner(Text, parserTools, LanguageInformation.KeywordsStorage, definesList); GPPGParser parser = new GPPGParser(scanner, parserTools); @@ -192,11 +135,12 @@ namespace Languages.Pascal.Frontend.Wrapping syntax_tree_node root = Parse(Text, FileName); - if (root == null && origText != null && origText.Contains("<")) + // убрали эту вторую попытку пока, т.к. Intellisense не должен выдавать подсказку в случае, когда компилятор выдает ошибку в такой ситуации EVA 10.10.2024 + /*if (root == null && origText != null && origText.Contains("<")) { Errors.Clear(); root = Parse(String.Concat("<>", Environment.NewLine, origText.Replace("<", "&<")), FileName); - } + }*/ return root as expression; } diff --git a/PascalABCLanguageInfo/PascalABCLanguage.cs b/PascalABCLanguageInfo/PascalABCLanguage.cs index 6bab780f3..0d340269e 100644 --- a/PascalABCLanguageInfo/PascalABCLanguage.cs +++ b/PascalABCLanguageInfo/PascalABCLanguage.cs @@ -18,6 +18,7 @@ namespace Languages.Pascal version: "1.2", copyright: "Copyright © 2005-2025 by Ivan Bondarev, Stanislav Mikhalkovich", + languageInformation: new Frontend.Data.PascalABCLanguageInformation(), parser: new Frontend.Wrapping.PascalABCNewLanguageParser(), docParser: new Frontend.Documentation.PascalDocTagsLanguageParser(), diff --git a/ParserTools/ParserTools/DefaultLanguageInformation.cs b/PascalABCLanguageInfo/PascalABCLanguageInformation.cs similarity index 93% rename from ParserTools/ParserTools/DefaultLanguageInformation.cs rename to PascalABCLanguageInfo/PascalABCLanguageInformation.cs index 4d263c0d6..9a283ea58 100644 --- a/ParserTools/ParserTools/DefaultLanguageInformation.cs +++ b/PascalABCLanguageInfo/PascalABCLanguageInformation.cs @@ -3,142 +3,89 @@ using System; using System.Text; using System.Linq; -using System.Collections; using System.Collections.Generic; using PascalABCCompiler.SyntaxTree; using System.Reflection; -namespace PascalABCCompiler.Parsers +using PascalABCCompiler.Parsers; +using PascalABCCompiler; +using PascalABCCompiler.ParserTools.Directives; +using static PascalABCCompiler.ParserTools.Directives.DirectiveHelper; + +namespace Languages.Pascal.Frontend.Data { - public class DefaultLanguageInformation : ILanguageInformation + public class PascalABCLanguageInformation : ILanguageInformation { - protected IParser parser; - protected string[] type_keywords_array; - protected string[] keywords_array; - protected Dictionary keywords = new Dictionary(StringComparer.CurrentCultureIgnoreCase); - protected Hashtable ignored_keywords = new Hashtable(StringComparer.CurrentCultureIgnoreCase); - protected Hashtable keyword_kinds = new Hashtable(StringComparer.CurrentCultureIgnoreCase); + protected IParser Parser + { + get + { + // Поле парсера желательно убрать отсюда EVA + return Facade.LanguageProvider.Instance.SelectLanguageByName(StringConstants.pascalLanguageName).Parser; + } + } - public DefaultLanguageInformation() - { - - } - - public DefaultLanguageInformation(IParser p) - { - this.parser = p; - InitKeywords(); - } - - protected virtual void InitKeywords() - { - List keys = new List(); - List type_keys = new List(); - keywords.Add("and", "and"); keys.Add("and"); - keywords.Add("or", "or"); keys.Add("or"); - keywords.Add("xor", "xor"); keys.Add("xor"); - keywords.Add("begin", "begin"); keys.Add("begin"); - keywords.Add("end", "end"); keys.Add("end"); - keywords.Add("for", "for"); keys.Add("for"); - keywords.Add("while", "while"); keys.Add("while"); - keywords.Add("repeat", "repeat"); keys.Add("repeat"); - keywords.Add("until", "until"); keys.Add("until"); - keywords.Add("do", "do"); keys.Add("do"); - keywords.Add("to", "to"); keys.Add("to"); - keywords.Add("downto", "downto"); keys.Add("downto"); - keywords.Add("class", "class"); keys.Add("class"); type_keys.Add("class"); - keywords.Add("record", "record"); keys.Add("record"); type_keys.Add("record"); - keywords.Add("set", "set"); keys.Add("set"); type_keys.Add("set"); - keywords.Add("file", "file"); keys.Add("file"); type_keys.Add("file"); - keywords.Add("type", "type"); keys.Add("type"); keyword_kinds.Add("type", KeywordKind.Type); - keywords.Add("array", "array"); keys.Add("array"); type_keys.Add("array"); - keywords.Add("of", "of"); keys.Add("of"); keyword_kinds.Add("of", KeywordKind.Of); - keywords.Add("private", "private"); keys.Add("private"); - keywords.Add("protected", "protected"); keys.Add("protected"); - keywords.Add("public", "public"); keys.Add("public"); - keywords.Add("inherited", "inherited"); keys.Add("inherited"); keyword_kinds.Add("inherited", KeywordKind.Inherited); - keywords.Add("foreach", "foreach"); keys.Add("foreach"); - keywords.Add("try", "try"); keys.Add("try"); - keywords.Add("except", "except"); keys.Add("except"); - keywords.Add("finally", "finally"); keys.Add("finally"); - keywords.Add("uses", "uses"); keys.Add("uses"); keyword_kinds.Add("uses", KeywordKind.Uses); - keywords.Add("unit", "unit"); keys.Add("unit"); keyword_kinds.Add("unit", KeywordKind.Unit); + public BaseKeywords KeywordsStorage { get; } = new Core.PascalABCKeywords(); + + public Dictionary ValidDirectives { get; private set; } + + public PascalABCLanguageInformation() + { + InitializeValidDirectives(); + } + + /// + /// Здесь записываются все директивы, поддерживаемые языком, а также правила для проверки их параметров (ограничения, накладываемые со стороны языка) + /// + private void InitializeValidDirectives() + { + #region VALID DIRECTIVES + ValidDirectives = new Dictionary(StringComparer.CurrentCultureIgnoreCase) + { + [StringConstants.compiler_directive_apptype] = new DirectiveInfo(SingleAnyOfCheck("console", "windows", "dll", "pcu")), + [StringConstants.compiler_directive_reference] = new DirectiveInfo(quotesAreSpecialSymbols: true), + [StringConstants.compiler_directive_include_namespace] = new DirectiveInfo(quotesAreSpecialSymbols: true), + [StringConstants.compiler_directive_savepcu] = new DirectiveInfo(SingleAnyOfCheck("true", "false")), + [StringConstants.compiler_directive_zerobasedstrings] = new DirectiveInfo(SingleAnyOfCheck("on", "off"), paramsNums: new int[2] { 0, 1 }), + [StringConstants.compiler_directive_zerobasedstrings_ON] = NoParamsDirectiveInfo(), + [StringConstants.compiler_directive_zerobasedstrings_OFF] = NoParamsDirectiveInfo(), + [StringConstants.compiler_directive_nullbasedstrings_ON] = NoParamsDirectiveInfo(), + [StringConstants.compiler_directive_nullbasedstrings_OFF] = NoParamsDirectiveInfo(), + [StringConstants.compiler_directive_initstring_as_empty_ON] = NoParamsDirectiveInfo(), + [StringConstants.compiler_directive_initstring_as_empty_OFF] = NoParamsDirectiveInfo(), + [StringConstants.compiler_directive_resource] = new DirectiveInfo(quotesAreSpecialSymbols: true), + [StringConstants.compiler_directive_platformtarget] = new DirectiveInfo(SingleAnyOfCheck("x86", "x64", "anycpu", "dotnet5win", "dotnet5linux", "dotnet5macos", "native")), + [StringConstants.compiler_directive_faststrings] = NoParamsDirectiveInfo(), + [StringConstants.compiler_directive_gendoc] = new DirectiveInfo(SingleAnyOfCheck("true", "false")), + [StringConstants.compiler_directive_region] = new DirectiveInfo(checkParamsNumNeeded: false), + [StringConstants.compiler_directive_endregion] = new DirectiveInfo(checkParamsNumNeeded: false), + [StringConstants.compiler_directive_ifdef] = new DirectiveInfo(SingleIsValidIdCheck()), + [StringConstants.compiler_directive_endif] = new DirectiveInfo(SingleIsValidIdCheck(), paramsNums: new int[2] { 0, 1 }), + [StringConstants.compiler_directive_ifndef] = new DirectiveInfo(SingleIsValidIdCheck()), + [StringConstants.compiler_directive_else] = new DirectiveInfo(SingleIsValidIdCheck(), paramsNums: new int[2] { 0, 1 }), + [StringConstants.compiler_directive_undef] = new DirectiveInfo(SingleIsValidIdCheck()), + [StringConstants.compiler_directive_define] = new DirectiveInfo(SingleIsValidIdCheck()), + [StringConstants.compiler_directive_include] = new DirectiveInfo(quotesAreSpecialSymbols: true), + [StringConstants.compiler_directive_targetframework] = new DirectiveInfo(), + [StringConstants.compiler_directive_hidden_idents] = NoParamsDirectiveInfo(), + [StringConstants.compiler_directive_disable_standard_units] = NoParamsDirectiveInfo(), + [StringConstants.compiler_directive_version_string] = new DirectiveInfo(IsValidVersionCheck()), + [StringConstants.compiler_directive_product_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), + [StringConstants.compiler_directive_company_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), + [StringConstants.compiler_directive_trademark_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), + [StringConstants.compiler_directive_main_resource_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), + [StringConstants.compiler_directive_title_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), + [StringConstants.compiler_directive_description_string] = new DirectiveInfo(quotesAreSpecialSymbols: true), + [StringConstants.compiler_directive_omp] = new DirectiveInfo(SingleAnyOfCheck("critical", "parallel"), checkParamsNumNeeded: false) + }; + #endregion + } - keywords.Add("procedure", "procedure"); keys.Add("procedure"); keyword_kinds.Add("procedure", KeywordKind.Function); type_keys.Add("procedure"); - keywords.Add("function", "function"); keys.Add("function"); keyword_kinds.Add("function", KeywordKind.Function); type_keys.Add("function"); - keywords.Add("constructor", "constructor"); keys.Add("constructor"); keyword_kinds.Add("constructor", KeywordKind.Constructor); - keywords.Add("destructor", "destructor"); keys.Add("destructor"); keyword_kinds.Add("destructor", KeywordKind.Destructor); - keywords.Add("interface", "interface"); keys.Add("interface"); - keywords.Add("implementation", "implementation"); keys.Add("implementation"); - keywords.Add("initialization", "initialization"); keys.Add("initialization"); - keywords.Add("finalization", "finalization"); keys.Add("finalization"); - keywords.Add("with", "with"); keys.Add("with"); - keywords.Add("abstract","abstract"); keys.Add("abstract"); - keywords.Add("virtual", "virtual"); keys.Add("virtual"); - keywords.Add("override", "override"); keys.Add("override"); - keywords.Add("reintroduce", "reintroduce"); keys.Add("reintroduce"); - keywords.Add("sealed", "sealed"); keys.Add("sealed"); - keywords.Add("case", "case"); keys.Add("case"); - keywords.Add("var", "var"); keys.Add("var"); keyword_kinds.Add("var", KeywordKind.Var); - keywords.Add("const", "const"); keys.Add("const"); keyword_kinds.Add("const", KeywordKind.Const); - keywords.Add("if", "if"); keys.Add("if"); - keywords.Add("then", "then"); keys.Add("then"); - keywords.Add("else", "else"); keys.Add("else"); - keywords.Add("in", "in"); keys.Add("in"); - keywords.Add("operator", "operator"); keys.Add("operator"); - keywords.Add("raise", "raise"); keys.Add("raise"); keyword_kinds.Add("raise", KeywordKind.Raise); - keywords.Add("program", "program"); keys.Add("program"); keyword_kinds.Add("program", KeywordKind.Program); - keywords.Add("new", "new"); keys.Add("new"); keyword_kinds.Add("new", KeywordKind.New); - keywords.Add("nil", "nil"); keys.Add("nil"); - keywords.Add("loop", "loop"); keys.Add("loop"); - keywords.Add("yield", "yield"); keys.Add("yield"); - keywords.Add("sequence", "sequence"); keys.Add("sequence"); - keywords.Add("extensionmethod", "extensionmethod"); keys.Add("extensionmethod"); - keywords.Add("params", "params"); keys.Add("params"); - keywords.Add("implicit", "implicit"); keys.Add("implicit"); - keywords.Add("explicit", "explicit"); keys.Add("explicit"); - keywords.Add("forward", "forward"); keys.Add("forward"); - keywords.Add("break", "break"); keys.Add("break"); - keywords.Add("continue", "continue"); keys.Add("continue"); - keywords.Add("default", "default"); keys.Add("default"); - keywords.Add("label", "label"); keys.Add("label"); - keywords.Add("property", "property"); keys.Add("property"); - keywords.Add("auto", "auto"); keys.Add("auto"); - keywords.Add("external", "external"); keys.Add("external"); - keywords.Add("lock", "lock"); keys.Add("lock"); - keywords.Add("where", "where"); keys.Add("where"); - keywords.Add("library", "library"); keys.Add("library"); - keywords.Add("div", "div"); keys.Add("div"); - keywords.Add("mod", "mod"); keys.Add("mod"); - keywords.Add("shl", "shl"); keys.Add("shl"); - keywords.Add("shr", "shr"); keys.Add("shr"); - keywords.Add("not", "not"); keys.Add("not"); - keywords.Add("as", "as"); keys.Add("as"); - keywords.Add("is", "is"); keys.Add("is"); - keywords.Add("on", "on"); keys.Add("on"); - keywords.Add("goto", "goto"); keys.Add("goto"); - keywords.Add("overload", "overload"); keys.Add("overload"); - keywords.Add("internal", "internal"); keys.Add("internal"); - //keywords.Add("template", "template"); keys.Add("template"); - keywords.Add("partial", "partial"); keys.Add("partial"); - keywords.Add("namespace", "namespace"); keys.Add("namespace"); - keywords.Add("exit", "exit"); keys.Add("exit"); - keywords.Add("event", "event"); keys.Add("event"); - keywords.Add("match", "match"); keys.Add("match"); - keywords.Add("when", "when"); keys.Add("when"); - keywords.Add("static", "static"); keys.Add("static"); - //keywords.Add("typeof", "typeof"); //keys.Add("typeof"); - //keywords.Add("sizeof", "sizeof"); //keys.Add("sizeof"); - keywords_array = new string[keywords.Count + 2]; - keywords_array[0] = "typeof"; - keywords_array[1] = "sizeof"; - keywords.Values.CopyTo(keywords_array, 2); - type_keywords_array = type_keys.ToArray(); - } - public bool IsKeyword(string value) { - return keywords.ContainsKey(value.ToLower()); + // typeof и sizeof воспринимаются Intellisense по другому EVA + return !value.Equals("typeof", StringComparison.CurrentCultureIgnoreCase) && !value.Equals("sizeof", StringComparison.CurrentCultureIgnoreCase) + && KeywordsStorage.KeywordsToTokens.ContainsKey(value); } public virtual string BodyStartBracket @@ -161,23 +108,23 @@ namespace PascalABCCompiler.Parsers { get { - return "PABCSystem"; + return PascalABCCompiler.StringConstants.pascalSystemUnitName; } } - public string[] Keywords + public List Keywords { get { - return keywords_array; + return KeywordsStorage.Keywords; } } - public string[] TypeKeywords + public List TypeKeywords { get { - return type_keywords_array; + return KeywordsStorage.TypeKeywords; } } @@ -631,9 +578,9 @@ namespace PascalABCCompiler.Parsers if (!string.IsNullOrEmpty(doc)) { doc = doc.Trim(' ', '\n', '\t', '\r').Replace(Environment.NewLine, Environment.NewLine + " /// "); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); doc = doc.Replace("", ""); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_PARAMETER")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_PARAMETER")); doc = doc.Replace("", ""); sb.AppendLine(" /// " + doc); } @@ -698,7 +645,7 @@ namespace PascalABCCompiler.Parsers FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (fields.Length > 0) { - sb.AppendLine(" {$region " + StringResources.Get("CODE_COMPLETION_FIELDS") + "}"); + sb.AppendLine(" {$region " + PascalABCCompiler.StringResources.Get("CODE_COMPLETION_FIELDS") + "}"); for (int i = 0; i < fields.Length; i++) { if (fields[i].DeclaringType == t && !fields[i].IsPrivate && !fields[i].IsAssembly) @@ -707,9 +654,9 @@ namespace PascalABCCompiler.Parsers if (!string.IsNullOrEmpty(doc)) { doc = doc.Trim(' ', '\n', '\t', '\r').Replace(Environment.NewLine, Environment.NewLine + " /// "); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); doc = doc.Replace("", ""); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_PARAMETER")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_PARAMETER")); doc = doc.Replace("", ""); sb.AppendLine(" /// " + doc); ln++; @@ -731,7 +678,7 @@ namespace PascalABCCompiler.Parsers EventInfo[] events = t.GetEvents(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (events.Length > 0) { - sb.AppendLine(" {$region " + StringResources.Get("CODE_COMPLETION_EVENTS") + "}"); + sb.AppendLine(" {$region " + PascalABCCompiler.StringResources.Get("CODE_COMPLETION_EVENTS") + "}"); for (int i = 0; i < events.Length; i++) { if (events[i].DeclaringType != t) @@ -743,9 +690,9 @@ namespace PascalABCCompiler.Parsers if (!string.IsNullOrEmpty(doc)) { doc = doc.Trim(' ', '\n', '\t', '\r').Replace(Environment.NewLine, Environment.NewLine + " /// "); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); doc = doc.Replace("", ""); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_PARAMETER")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_PARAMETER")); doc = doc.Replace("", ""); sb.AppendLine(" /// " + doc); } @@ -767,7 +714,7 @@ namespace PascalABCCompiler.Parsers PropertyInfo[] props = t.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (props.Length > 0) { - sb.AppendLine(" {$region " + StringResources.Get("CODE_COMPLETION_PROPERTIES") + "}"); + sb.AppendLine(" {$region " + PascalABCCompiler.StringResources.Get("CODE_COMPLETION_PROPERTIES") + "}"); for (int i = 0; i < props.Length; i++) { if (props[i].DeclaringType != t) @@ -779,9 +726,9 @@ namespace PascalABCCompiler.Parsers if (!string.IsNullOrEmpty(doc)) { doc = doc.Trim(' ', '\n', '\t', '\r').Replace(Environment.NewLine, Environment.NewLine + " /// "); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); doc = doc.Replace("", ""); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_PARAMETER")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_PARAMETER")); doc = doc.Replace("", ""); sb.AppendLine(" /// " + doc); } @@ -803,7 +750,7 @@ namespace PascalABCCompiler.Parsers ConstructorInfo[] constrs = t.GetConstructors(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (constrs.Length > 0) { - sb.AppendLine(" {$region " + StringResources.Get("CODE_COMPLETION_CONSTRUCTORS") + "}"); + sb.AppendLine(" {$region " + PascalABCCompiler.StringResources.Get("CODE_COMPLETION_CONSTRUCTORS") + "}"); for (int i = 0; i < constrs.Length; i++) { if (constrs[i].DeclaringType == t && !constrs[i].IsPrivate && !constrs[i].IsAssembly) @@ -812,9 +759,9 @@ namespace PascalABCCompiler.Parsers if (!string.IsNullOrEmpty(doc)) { doc = doc.Trim(' ', '\n', '\t', '\r').Replace(Environment.NewLine, Environment.NewLine + " /// "); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); doc = doc.Replace("", ""); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_PARAMETER")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_PARAMETER")); doc = doc.Replace("", ""); sb.AppendLine(" /// " + doc); } @@ -835,7 +782,7 @@ namespace PascalABCCompiler.Parsers MethodInfo[] meths = t.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (meths.Length > 0) { - sb.AppendLine(" {$region " + StringResources.Get("CODE_COMPLETION_METHODS") + "}"); + sb.AppendLine(" {$region " + PascalABCCompiler.StringResources.Get("CODE_COMPLETION_METHODS") + "}"); for (int i = 0; i < meths.Length; i++) { if (meths[i].DeclaringType == t && !meths[i].IsPrivate && !meths[i].IsAssembly && !meths[i].Name.StartsWith("get_") && !meths[i].Name.StartsWith("set_") && !meths[i].Name.StartsWith("add_") && !meths[i].Name.StartsWith("remove_")) @@ -844,9 +791,9 @@ namespace PascalABCCompiler.Parsers if (!string.IsNullOrEmpty(doc)) { doc = doc.Trim(' ', '\n', '\t', '\r').Replace(Environment.NewLine, Environment.NewLine + " /// "); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_RETURN_VALUE")); doc = doc.Replace("", ""); - doc = doc.Replace("", StringResources.Get("CODE_COMPLETION_PARAMETER")); + doc = doc.Replace("", PascalABCCompiler.StringResources.Get("CODE_COMPLETION_PARAMETER")); doc = doc.Replace("", ""); sb.AppendLine(" /// " + doc); } @@ -1015,7 +962,7 @@ namespace PascalABCCompiler.Parsers var strrank = rank > 1 ? "[" + new string(',', rank - 1) + "]" : ""; return $"array{strrank}" + " of " + GetShortTypeName(ctn.GetElementType()); } - //if (ctn == Type.GetType("System.Void*")) return StringConstants.pointer_type_name; + //if (ctn == Type.GetType("System.Void*")) return PascalABCCompiler.StringConstants.pointer_type_name; return ctn.Name; } @@ -1851,11 +1798,11 @@ namespace PascalABCCompiler.Parsers { if (extensionType.IndexOf(' ') != -1) { - sb.Append("(" + StringResources.Get("CODE_COMPLETION_EXTENSION") + " " + extensionType + ") "); + sb.Append("(" + PascalABCCompiler.StringResources.Get("CODE_COMPLETION_EXTENSION") + " " + extensionType + ") "); } else { - sb.Append("(" + StringResources.Get("CODE_COMPLETION_EXTENSION") + ") "); + sb.Append("(" + PascalABCCompiler.StringResources.Get("CODE_COMPLETION_EXTENSION") + ") "); extensionType = null; } } @@ -2056,12 +2003,12 @@ namespace PascalABCCompiler.Parsers { if (extensionType != null && extensionType.IndexOf(' ') != -1) { - sb.Append("(" + StringResources.Get("CODE_COMPLETION_EXTENSION") + " " + extensionType + ") "); + sb.Append("(" + PascalABCCompiler.StringResources.Get("CODE_COMPLETION_EXTENSION") + " " + extensionType + ") "); extensionType = null; } else { - sb.Append("(" + StringResources.Get("CODE_COMPLETION_EXTENSION")+ ") "); + sb.Append("(" + PascalABCCompiler.StringResources.Get("CODE_COMPLETION_EXTENSION")+ ") "); } } @@ -2167,7 +2114,7 @@ namespace PascalABCCompiler.Parsers private string prepare_member_name(string s) { - if (keywords.ContainsKey(s)) + if (IsKeyword(s)) return "&" + s; return s; } @@ -2351,9 +2298,10 @@ namespace PascalABCCompiler.Parsers public KeywordKind GetKeywordKind(string name) { - object o = keyword_kinds[name]; - if (o != null) return (KeywordKind)o; - else return KeywordKind.None; + if (KeywordsStorage.KeywordKinds.TryGetValue(name, out var kind)) + return kind; + else + return KeywordKind.None; } @@ -2526,7 +2474,7 @@ namespace PascalABCCompiler.Parsers sb.AppendLine(lineText); sb.AppendLine("begin end;"); sb.AppendLine("begin end."); - program_module node = this.parser.GetCompilationUnit("test.pas", sb.ToString(), null, null, ParseMode.Special) as program_module; + program_module node = this.Parser.GetCompilationUnit("test.pas", sb.ToString(), null, null, ParseMode.Special) as program_module; procedure_header header = (node.program_block.defs.defs[0] as procedure_definition).proc_header; get_procedure_template(header, res, col); return res.ToString(); @@ -2537,7 +2485,7 @@ namespace PascalABCCompiler.Parsers sb.AppendLine(lineText); sb.AppendLine("begin end;"); sb.AppendLine("begin end."); - program_module node = this.parser.GetCompilationUnit("test.pas", sb.ToString(), null, null, ParseMode.Special) as program_module; + program_module node = this.Parser.GetCompilationUnit("test.pas", sb.ToString(), null, null, ParseMode.Special) as program_module; if (node.program_block.defs != null && node.program_block.defs.defs.Count > 0 && node.program_block.defs.defs[0] is procedure_definition) { procedure_header header = (node.program_block.defs.defs[0] as procedure_definition).proc_header; @@ -2718,7 +2666,7 @@ namespace PascalABCCompiler.Parsers { keyword = KeywordKind.Raise; } - else if (keywords.ContainsKey(s)) + else if (IsKeyword(s)) { keyword = KeywordKind.CommonKeyword; } @@ -3115,11 +3063,15 @@ namespace PascalABCCompiler.Parsers public virtual string FindExpressionFromAnyPosition(int off, string Text, int line, int col, out KeywordKind keyw, out string expr_without_brackets) { int i = off - 1; + + // это например вызов метода без параметров expr_without_brackets = null; keyw = KeywordKind.None; if (i < 0) return ""; bool is_char = false; + + // идем в обе стороны от off System.Text.StringBuilder sb = new System.Text.StringBuilder(); if (Text[i] != ' ' && (Char.IsLetterOrDigit(Text[i]) || Text[i] == '_' || Text[i] == '&' || Text[i] == '?' && IsPunctuation(Text, i + 1) || Text[i] == '!')) { @@ -3792,8 +3744,8 @@ namespace PascalABCCompiler.Parsers } } - - public class CLanguageInformation : DefaultLanguageInformation + #region LEGACY - C language + /*public class CLanguageInformation : DefaultLanguageInformation { public CLanguageInformation(IParser p) { @@ -3941,18 +3893,18 @@ namespace PascalABCCompiler.Parsers sb.Append(ctn.Name.Substring(0,ctn.Name.IndexOf('`'))); sb.Append('<'); sb.Append('>'); - /*sb.Append('<'); + *//*sb.Append('<'); for (int i=0; i');*/ + sb.Append('>');*//* return sb.ToString(); } //if (ctn.IsArray) return "array of "+GetTypeName(ctn.GetElementType()); - //if (ctn == Type.GetType("System.Void*")) return StringConstants.pointer_type_name; + //if (ctn == Type.GetType("System.Void*")) return PascalABCCompiler.StringConstants.pointer_type_name; return ctn.Name; } @@ -4199,6 +4151,7 @@ namespace PascalABCCompiler.Parsers sb.Append(')'); return sb.ToString(); } - } + }*/ + #endregion } diff --git a/VisualPascalABCNET/DockContent/CodeFileDocumentTextEditorControl.cs b/VisualPascalABCNET/DockContent/CodeFileDocumentTextEditorControl.cs index dff880582..50fe70eee 100644 --- a/VisualPascalABCNET/DockContent/CodeFileDocumentTextEditorControl.cs +++ b/VisualPascalABCNET/DockContent/CodeFileDocumentTextEditorControl.cs @@ -21,7 +21,7 @@ namespace VisualPascalABC public CodeFileDocumentTextEditorControl() { editactions[Keys.Space | Keys.Control] = new CodeCompletionAllNamesAction(); - editactions[Keys.Space | Keys.Shift] = new CodeCompletionNamesOnlyInModuleAction(); + editactions[Keys.Space | Keys.Shift] = new CodeCompletionShiftSpaceActions(); editactions[Keys.Enter | Keys.Control] = new GotoAction(); editactions[Keys.LButton | Keys.Control] = new GotoAction(); editactions[Keys.C | Keys.Shift | Keys.Control] = new ClassOrMethodRealizationAction(); diff --git a/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionActions.cs b/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionActions.cs index 60e3fc50a..a55ba848f 100644 --- a/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionActions.cs +++ b/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionActions.cs @@ -118,22 +118,39 @@ namespace VisualPascalABC public static List GetDefinitionPosition(TextArea textArea, bool only_check) { - if (CodeCompletion.CodeCompletionController.CurrentParser == null) return new List(); + + IParser parser = CodeCompletion.CodeCompletionController.CurrentParser; + + if (parser == null) + return new List(); + IDocument doc = textArea.Document; string textContent = doc.TextContent; ccp = new CodeCompletionProvider(); string full_expr; string expressionResult = FindFullExpression(textContent, textArea, out ccp.keyword, out full_expr); + if (expressionResult != null) expressionResult = expressionResult.Trim(); - if (expressionResult != full_expr && full_expr.StartsWith("(")) - return new List(); - if (full_expr != null && full_expr.Contains("^")) - full_expr = full_expr.Replace("^",""); + + // Проверка, что компилируем Паскаль временно EVA 10.11.2024 + if (parser == Languages.Facade.LanguageProvider.Instance.SelectLanguageByName(StringConstants.pascalLanguageName).Parser) + { + if (expressionResult != full_expr && full_expr.StartsWith("(")) + return new List(); + + if (full_expr != null && full_expr.Contains("^")) + { + full_expr = full_expr.Replace("^", ""); + } + } + List poses = ccp.GetDefinition(full_expr, textArea.MotherTextEditorControl.FileName, textArea.Caret.Line, textArea.Caret.Column, only_check); + if (poses == null || poses.Count == 0) poses = ccp.GetDefinition(expressionResult, textArea.MotherTextEditorControl.FileName, textArea.Caret.Line, textArea.Caret.Column, only_check); + return poses; } @@ -574,12 +591,93 @@ namespace VisualPascalABC } } - public class CodeCompletionNamesOnlyInModuleAction : CodeCompletionAllNamesAction + /// + /// Включает в себя три различных действия по shift + space, что не очень хорошо EVA + /// + public class CodeCompletionShiftSpaceActions : ICSharpCode.TextEditor.Actions.AbstractEditAction { + PABCNETCodeCompletionWindow codeCompletionWindow; + public static TextArea textArea = CodeCompletionAllNamesAction.textArea; + public static Hashtable comp_windows = CodeCompletionAllNamesAction.comp_windows; + public static bool is_begin = false; + + private string get_prev_text(string text, int off) + { + StringBuilder sb = new StringBuilder(); + while (off >= 0 && char.IsLetterOrDigit(text[off])) + { + sb.Insert(0, text[off--]); + } + return sb.ToString(); + } + public override void Execute(TextArea _textArea) { - key = '\0'; - base.Execute(_textArea); + //base.Execute(_textArea); + + textArea = _textArea; + int off = textArea.Caret.Offset; + string text = textArea.Document.TextContent.Substring(0, textArea.Caret.Offset); + + // автодополнение xml-комментария + if (off > 2 && text[off - 1] == '/' && text[off - 2] == '/' && text[off - 3] == '/') + { + CodeCompletionActionsManager.GenerateCommentTemplate(textArea); + return; + } + // автодополнение некоторого выражения из файла template.pct + else + { + string prev = get_prev_text(text, off - 1); + if (!string.IsNullOrEmpty(prev)) + { + CodeCompletionActionsManager.GenerateTemplate(prev, textArea); + return; + } + } + + // если подсказка по точке выключена дальше не идем + if (!WorkbenchServiceFactory.Workbench.UserOptions.CodeCompletionDot) + return; + + if (CodeCompletion.CodeCompletionController.CurrentParser == null) + return; + + is_begin = true; + + codeCompletionWindow = PABCNETCodeCompletionWindow.ShowCompletionWindow( + VisualPABCSingleton.MainForm, // The parent window for the completion window + textArea.MotherTextEditorControl, // The text editor to show the window for + textArea.MotherTextEditorControl.FileName, // Filename - will be passed back to the provider + new CodeCompletionProvider(), // Provider to get the list of possible completions + '\0', // Key pressed - will be passed to the provider + false, + false, + KeywordKind.None + ); + + comp_windows[textArea] = codeCompletionWindow; + + if (codeCompletionWindow != null) + { + // ShowCompletionWindow can return null when the provider returns an empty list + codeCompletionWindow.Closed += new EventHandler(OnCodeCompletionWindowClosed); + } + } + + // такой же метод есть в CodeCompletionKeyHandler EVA + public void OnCodeCompletionWindowClosed(object sender, EventArgs e) + { + if (codeCompletionWindow != null) + { + codeCompletionWindow.Closed -= new EventHandler(OnCodeCompletionWindowClosed); + CodeCompletionProvider.disp.Reset(); + CodeCompletion.AssemblyDocCache.Reset(); + CodeCompletion.UnitDocCache.Reset(); + codeCompletionWindow.Dispose(); + codeCompletionWindow = null; + } + comp_windows[textArea] = null; } } @@ -627,18 +725,6 @@ namespace VisualPascalABC PABCNETCodeCompletionWindow codeCompletionWindow; public static TextArea textArea; public static Hashtable comp_windows = new Hashtable(); - public static bool is_begin = false; - protected char key = '_'; - - private string get_prev_text(string text, int off) - { - StringBuilder sb = new StringBuilder(); - while (off >= 0 && char.IsLetterOrDigit(text[off])) - { - sb.Insert(0, text[off--]); - } - return sb.ToString(); - } public override void Execute(TextArea _textArea) { @@ -647,52 +733,31 @@ namespace VisualPascalABC textArea = _textArea; int off = textArea.Caret.Offset; string text = textArea.Document.TextContent.Substring(0, textArea.Caret.Offset); - if (key == '\0') - if (off > 2 && text[off - 1] == '/' && text[off - 2] == '/' && text[off - 3] == '/') - { - CodeCompletionActionsManager.GenerateCommentTemplate(textArea); - return; - } - else - { - string prev = get_prev_text(text, off - 1); - if (!string.IsNullOrEmpty(prev)) - { - CodeCompletionActionsManager.GenerateTemplate(prev, textArea); - return; - } - } + if (!WorkbenchServiceFactory.Workbench.UserOptions.CodeCompletionDot) return; + if (CodeCompletion.CodeCompletionController.CurrentParser == null) return; CodeCompletionProvider completionDataProvider = new CodeCompletionProvider(); - bool is_pattern = false; + completionDataProvider.preSelection = CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.FindPattern(off, text, out var is_pattern); - - is_begin = true; - - completionDataProvider.preSelection = CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.FindPattern(off, text, out is_pattern); - - if (!is_pattern && off > 0 && text[off - 1] == '.') - key = '$'; codeCompletionWindow = PABCNETCodeCompletionWindow.ShowCompletionWindow( VisualPABCSingleton.MainForm, // The parent window for the completion window textArea.MotherTextEditorControl, // The text editor to show the window for textArea.MotherTextEditorControl.FileName, // Filename - will be passed back to the provider completionDataProvider, // Provider to get the list of possible completions - key, // Key pressed - will be passed to the provider + '_', // Key pressed - will be passed to the provider false, false, - PascalABCCompiler.Parsers.KeywordKind.None + KeywordKind.None ); - key = '_'; - CodeCompletionNamesOnlyInModuleAction.comp_windows[textArea] = codeCompletionWindow; + CodeCompletionShiftSpaceActions.comp_windows[textArea] = codeCompletionWindow; if (codeCompletionWindow != null) { // ShowCompletionWindow can return null when the provider returns an empty list - codeCompletionWindow.Closed += new EventHandler(CloseCodeCompletionWindow); + codeCompletionWindow.Closed += new EventHandler(OnCodeCompletionWindowClosed); } } //catch (Exception e) @@ -701,11 +766,12 @@ namespace VisualPascalABC } } - public void CloseCodeCompletionWindow(object sender, EventArgs e) + // такой же метод есть в CodeCompletionKeyHandler EVA + public void OnCodeCompletionWindowClosed(object sender, EventArgs e) { if (codeCompletionWindow != null) { - codeCompletionWindow.Closed -= new EventHandler(CloseCodeCompletionWindow); + codeCompletionWindow.Closed -= new EventHandler(OnCodeCompletionWindowClosed); CodeCompletionProvider.disp.Reset(); CodeCompletion.AssemblyDocCache.Reset(); CodeCompletion.UnitDocCache.Reset(); diff --git a/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionKeyHandler.cs b/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionKeyHandler.cs index 7e489ef9b..3c860c58e 100644 --- a/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionKeyHandler.cs +++ b/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionKeyHandler.cs @@ -1,4 +1,4 @@ -// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt) +// 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; @@ -40,7 +40,7 @@ namespace VisualPascalABC //editor.ActiveTextAreaControl.TextArea.KeyDown += new System.Windows.Forms.KeyEventHandler(TextArea_KeyDown); //editor.ActiveTextAreaControl.KeyDown += h.TextControlEventHandler; // When the editor is disposed, close the code completion window - editor.Disposed += h.CloseCodeCompletionWindow; + editor.Disposed += h.OnCodeCompletionWindowClosed; return h; } @@ -74,11 +74,11 @@ namespace VisualPascalABC } else { - PABCNETCodeCompletionWindow ccw = CodeCompletionNamesOnlyInModuleAction.comp_windows[editor.ActiveTextAreaControl.TextArea] as PABCNETCodeCompletionWindow; + PABCNETCodeCompletionWindow ccw = CodeCompletionShiftSpaceActions.comp_windows[editor.ActiveTextAreaControl.TextArea] as PABCNETCodeCompletionWindow; - if (ccw != null && CodeCompletionNamesOnlyInModuleAction.is_begin) + if (ccw != null && CodeCompletionShiftSpaceActions.is_begin) { - CodeCompletionNamesOnlyInModuleAction.is_begin = false; + CodeCompletionShiftSpaceActions.is_begin = false; if (key != ' ') ccw.ProcessKeyEvent(key); else @@ -115,10 +115,10 @@ namespace VisualPascalABC true, PascalABCCompiler.Parsers.KeywordKind.None ); - CodeCompletionNamesOnlyInModuleAction.is_begin = true; - CodeCompletionNamesOnlyInModuleAction.comp_windows[editor.ActiveTextAreaControl.TextArea] = codeCompletionWindow; + CodeCompletionShiftSpaceActions.is_begin = true; + CodeCompletionShiftSpaceActions.comp_windows[editor.ActiveTextAreaControl.TextArea] = codeCompletionWindow; if (codeCompletionWindow != null) - codeCompletionWindow.Closed += new EventHandler(CloseCodeCompletionWindow); + codeCompletionWindow.Closed += new EventHandler(OnCodeCompletionWindowClosed); } } else if (key == '(' || key == '[' || key == ',') @@ -131,7 +131,7 @@ namespace VisualPascalABC { insightWindow = new PABCNETInsightWindow(VisualPABCSingleton.MainForm, editor); insightWindow.Font = new Font(Constants.CompletionInsightWindowFontName, insightWindow.Font.Size); - insightWindow.Closed += new EventHandler(CloseInsightWindow); + insightWindow.Closed += new EventHandler(OnInsightWindowClosed); } else { @@ -162,9 +162,9 @@ namespace VisualPascalABC false, keyw ); - CodeCompletionNamesOnlyInModuleAction.comp_windows[editor.ActiveTextAreaControl.TextArea] = codeCompletionWindow; + CodeCompletionShiftSpaceActions.comp_windows[editor.ActiveTextAreaControl.TextArea] = codeCompletionWindow; if (codeCompletionWindow != null) - codeCompletionWindow.Closed += new EventHandler(CloseCodeCompletionWindow); + codeCompletionWindow.Closed += new EventHandler(OnCodeCompletionWindowClosed); //return true; } } @@ -198,8 +198,10 @@ namespace VisualPascalABC if (CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsDefinitionIdentifierAfterKeyword(keyw)) return false; + // если не первый символ выражения (предыдущий тоже буква или "_"), то не открываем новое окно if (editor.ActiveTextAreaControl.TextArea.Caret.Offset > 0 && (char.IsLetterOrDigit(editor.Document.TextContent[editor.ActiveTextAreaControl.TextArea.Caret.Offset - 1]) || editor.Document.TextContent[editor.ActiveTextAreaControl.TextArea.Caret.Offset - 1] == '_')) return false; + completionDataProvider = new CodeCompletionProvider(); codeCompletionWindow = PABCNETCodeCompletionWindow.ShowCompletionWindowWithFirstChar( VisualPABCSingleton.MainForm, // The parent window for the completion window @@ -209,9 +211,9 @@ namespace VisualPascalABC key, // Key pressed - will be passed to the provider keyw ); - CodeCompletionNamesOnlyInModuleAction.comp_windows[editor.ActiveTextAreaControl.TextArea] = codeCompletionWindow; + CodeCompletionShiftSpaceActions.comp_windows[editor.ActiveTextAreaControl.TextArea] = codeCompletionWindow; if (codeCompletionWindow != null) - codeCompletionWindow.Closed += new EventHandler(CloseCodeCompletionWindow); + codeCompletionWindow.Closed += new EventHandler(OnCodeCompletionWindowClosed); } } @@ -253,26 +255,26 @@ namespace VisualPascalABC return false; } - void CloseInsightWindow(object sender, EventArgs e) + void OnInsightWindowClosed(object sender, EventArgs e) { if (insightWindow != null) { - insightWindow.Closed -= new EventHandler(CloseInsightWindow); + insightWindow.Closed -= new EventHandler(OnInsightWindowClosed); insightWindow.Dispose(); insightWindow = null; } } - void CloseCodeCompletionWindow(object sender, EventArgs e) + void OnCodeCompletionWindowClosed(object sender, EventArgs e) { if (codeCompletionWindow != null) { - codeCompletionWindow.Closed -= new EventHandler(CloseCodeCompletionWindow); + codeCompletionWindow.Closed -= new EventHandler(OnCodeCompletionWindowClosed); CodeCompletionProvider.disp.Reset(); CodeCompletion.AssemblyDocCache.Reset(); CodeCompletion.UnitDocCache.Reset(); codeCompletionWindow.Dispose(); - CodeCompletionNamesOnlyInModuleAction.comp_windows[editor.ActiveTextAreaControl.TextArea] = null; + CodeCompletionShiftSpaceActions.comp_windows[editor.ActiveTextAreaControl.TextArea] = null; codeCompletionWindow = null; } } diff --git a/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionProvider.cs b/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionProvider.cs index a1cb49cd6..ad63857a2 100644 --- a/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionProvider.cs +++ b/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionProvider.cs @@ -1,4 +1,4 @@ -// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt) +// 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; @@ -9,6 +9,7 @@ using ICSharpCode.TextEditor; using ICSharpCode.TextEditor.Gui.CompletionWindow; using PascalABCCompiler.Parsers; using Languages.Facade; +using System.Linq; namespace VisualPascalABC { @@ -113,12 +114,6 @@ namespace VisualPascalABC if (VisualPABCSingleton.MainForm.VisualEnvironmentCompiler.compilerLoaded) e = language.Parser.GetExpression("test" + System.IO.Path.GetExtension(fileName), expr, Errors, new List()); - if (e is PascalABCCompiler.SyntaxTree.bin_expr && expr.Contains("<")) - { - expr = expr.Replace("<","&<"); - Errors.Clear(); - e = language.Parser.GetExpression("test" + System.IO.Path.GetExtension(fileName), expr, Errors, new List()); - } if (e == null) return loc; CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[fileName]; @@ -321,251 +316,412 @@ namespace VisualPascalABC return expr; } - public ICompletionData[] GetCompletionDataByFirst(int off, string Text, int line, int col, char charTyped, PascalABCCompiler.Parsers.KeywordKind keyw) + /// + /// Возвращает подсказки определяемые первым символом выражения, введенного пользователем + /// + public ICompletionData[] GetCompletionDataByFirst(int line, int col, char charTyped, KeywordKind keyw) { List resultList = new List(); - List lst = new List(); try { - CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[FileName]; - /*if (dconv == null && CodeCompletion.CodeCompletionNameHelper.system_unit_file_full_name != null - && (keyw == CodeCompletion.KeywordKind.kw_colon || keyw == CodeCompletion.KeywordKind.kw_of)) - { - dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[CodeCompletion.CodeCompletionNameHelper.system_unit_file_full_name]; - special_module = true; - }*/ - string pattern = charTyped.ToString(); - string[] keywords = CodeCompletion.CodeCompletionNameHelper.Helper.GetKeywords(); - if (CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsTypeAfterKeyword(keyw)) + + ILanguageInformation languageInformation = CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation; + + List keywords; + + bool isTypeAfterKeyword = false; + + // если по смыслу должен вводиться тип данных + if (languageInformation.IsTypeAfterKeyword(keyw)) { keywords = CodeCompletion.CodeCompletionNameHelper.Helper.GetTypeKeywords(); + isTypeAfterKeyword = true; } - if (!CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsNamespaceAfterKeyword(keyw)) - foreach (string key in keywords) - { - //if (key.StartsWith(pattern, StringComparison.CurrentCultureIgnoreCase)) - resultList.Add(new UserDefaultCompletionData(key, null, ImagesProvider.IconNumberKeyword, false)); - } - PascalABCCompiler.Parsers.SymInfo[] mis = null; - if (CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsNamespaceAfterKeyword(keyw)) + else { - mis = CodeCompletion.DomConverter.standard_units; + keywords = CodeCompletion.CodeCompletionNameHelper.Helper.GetKeywords(); } - if (dconv != null) - { - //if (keyw == PascalABCCompiler.Parsers.KeywordKind.Colon || keyw == PascalABCCompiler.Parsers.KeywordKind.Of || keyw == PascalABCCompiler.Parsers.KeywordKind.TypeDecl) - if (CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsTypeAfterKeyword(keyw)) - mis = dconv.GetTypeByPattern(pattern, line, col, true, VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); - else if (CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsNamespaceAfterKeyword(keyw) && mis == null) - mis = dconv.GetNamespaces(); - else - mis = dconv.GetNameByPattern(null, line, col, true, VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); - } - Hashtable cache = new Hashtable(); - if (mis != null) - { - bool stop = false; - ICompletionData data = CompletionDataDispatcher.GetLastUsedItem(charTyped); - foreach (PascalABCCompiler.Parsers.SymInfo mi in mis) - { - if (mi.not_include) continue; - if (cache.Contains(mi.name)) - continue; + bool isNamespaceAfterKeyword = false; - UserDefaultCompletionData ddd = new UserDefaultCompletionData(mi.name, mi.description, ImagesProvider.GetPictureNum(mi), false); - if (!stop && data != null && string.Compare(mi.name, data.Text, true) == 0) - { - defaultCompletionElement = ddd; - stop = true; - } - else if (!stop && data == null && mi.name.StartsWith(charTyped.ToString(), StringComparison.CurrentCultureIgnoreCase)) - { - //defaultCompletionElement = ddd; - lst.Add(ddd); - //stop = true; - } - disp.Add(mi, ddd); - resultList.Add(ddd); - cache[mi.name] = mi; - } + // конструкция типа "uses" + if (languageInformation.IsNamespaceAfterKeyword(keyw)) + { + isNamespaceAfterKeyword = true; + } + else + { + resultList.AddRange(keywords.Select(keyword => + new UserDefaultCompletionData(keyword, null, ImagesProvider.IconNumberKeyword, false))); + } + + SymInfo[] symInfos = GetSymInfosForCompletionDataByFirst(line, col, isTypeAfterKeyword, isNamespaceAfterKeyword, charTyped.ToString()); + + if (symInfos != null) + { + bool languageCaseSensitive = LanguageProvider.Instance.SelectLanguageByExtension(FileName).CaseSensitive; + + AddCompletionDatasByFirstForSymInfos(resultList, charTyped, symInfos, languageCaseSensitive); + //resultList.Sort(); //defaultCompletionElement = resultList[0] as DefaultCompletionData; } } - catch (Exception e) - { - - } - - lst.Sort(); - if (lst.Count > 0) defaultCompletionElement = lst[0] as UserDefaultCompletionData; - ICompletionData[] res_arr = resultList.ToArray(); + catch (Exception) { } + this.ByFirstChar = true; - return res_arr; + + return resultList.ToArray(); } - public ICompletionData[] GetCompletionData(int off, string Text, int line, int col, char charTyped, PascalABCCompiler.Parsers.KeywordKind keyw) + /// + /// Добавляет в resultList (итоговый список подсказок) данные, соответствующие переданному массиву типа SymInfo[]. + /// Используется для случая ввода пользователем первого символа выражения + /// + private void AddCompletionDatasByFirstForSymInfos(List resultList, char charTyped, SymInfo[] symInfos, bool languageCaseSensitive) + { + HashSet symbolsAdded = languageCaseSensitive ? new HashSet() : new HashSet(StringComparer.CurrentCultureIgnoreCase); + + List candidatesForDefault = new List(); + + bool stop = false; + ICompletionData lastUsedItem = CompletionDataDispatcher.GetLastUsedItem(charTyped); + + foreach (SymInfo symInfo in symInfos) + { + if (symInfo == null || symInfo.not_include) continue; + + if (symbolsAdded.Contains(symInfo.name)) + continue; + + UserDefaultCompletionData completionData = new UserDefaultCompletionData(symInfo.name, symInfo.description, ImagesProvider.GetPictureNum(symInfo), false); + + StringComparison stringComparison = languageCaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase; + + // если мы выбирали что-то раньше из списка подсказок, то считаем это элементом по умолчанию + if (!stop && lastUsedItem != null && string.Equals(symInfo.name, lastUsedItem.Text, stringComparison)) + { + defaultCompletionElement = completionData; + stop = true; + } + // иначе формируем список подходящих подсказок - "кандидатов" для элемента по умолчанию + else if (!stop && lastUsedItem == null && symInfo.name.StartsWith(charTyped.ToString(), stringComparison)) + { + //defaultCompletionElement = ddd; + candidatesForDefault.Add(completionData); + //stop = true; + } + + disp.Add(symInfo, completionData); + + resultList.Add(completionData); + + symbolsAdded.Add(symInfo.name); + } + + if (candidatesForDefault.Count > 0) + defaultCompletionElement = candidatesForDefault.Min() as UserDefaultCompletionData; // здесь выбирается минимальное по длине + } + + + /// + /// Формирует массив данных из таблицы символов для подсказок в случае введения пользователем первой буквы выражения + /// + private SymInfo[] GetSymInfosForCompletionDataByFirst(int line, int col, bool isTypeAfterKeyword, bool isNamespaceAfterKeyword, string pattern) + { + CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[FileName]; + /*if (dconv == null && CodeCompletion.CodeCompletionNameHelper.system_unit_file_full_name != null + && (keyw == CodeCompletion.KeywordKind.kw_colon || keyw == CodeCompletion.KeywordKind.kw_of)) + { + dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[CodeCompletion.CodeCompletionNameHelper.system_unit_file_full_name]; + special_module = true; + }*/ + + SymInfo[] symInfos = null; + + if (dconv == null) + { + if (isNamespaceAfterKeyword) + { + symInfos = CodeCompletion.DomConverter.standard_units; + } + } + else + { + //if (keyw == PascalABCCompiler.Parsers.KeywordKind.Colon || keyw == PascalABCCompiler.Parsers.KeywordKind.Of || keyw == PascalABCCompiler.Parsers.KeywordKind.TypeDecl) + if (isTypeAfterKeyword) + { + symInfos = dconv.GetTypeByPattern(pattern, line, col, true, VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); + } + else if (isNamespaceAfterKeyword) + { + if (WorkbenchServiceFactory.Workbench.UserOptions.EnableSmartIntellisense) + symInfos = dconv.GetNamespaces(); + else + symInfos = CodeCompletion.DomConverter.standard_units; + } + else + { + // интересно, что передается pattern = null EVA + symInfos = dconv.GetNameByPattern(null, line, col, true, VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); + } + } + + return symInfos; + } + + /// + /// Вспомогательная струтура для метода GetCompletionData, хранит информацию о действиях пользователя + /// + private struct ActionContext + { + public bool dotPressed; + public bool ctrlSpace; + public bool shiftSpace; + public bool spaceAfterNew; + public bool spaceAfterUses; + } + + /// + /// Возвращает массив подсказок для случая нажатия пользователем "триггерной" клавиши + /// + public ICompletionData[] GetCompletionData(int off, string text, int line, int col, char charTyped, KeywordKind keywordKind) { List resultList = new List(); try { - string pattern = null; - string expr = null; - bool ctrl_space = charTyped == '\0' || charTyped == '_'; - bool shift_space = charTyped == '\0'; - bool inside_dot_pattern = false; - bool new_space = keyw == PascalABCCompiler.Parsers.KeywordKind.New; - if (ctrl_space) - { - bool is_pattern = false; - pattern = CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.FindPattern(off, Text, out is_pattern); - if (is_pattern && Text[off - pattern.Length - 1] == '.') - { - inside_dot_pattern = true; - expr = FindExpression(off - pattern.Length - 1, Text, line, col); - } + // поменять на обращение к CodeCompletionController.CurrentLanguage + ILanguage currentLanguage = LanguageProvider.Instance.SelectLanguageByExtension(FileName); - } - else if (new_space) + var context = new ActionContext() { - expr = CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.SkipNew(off - 1, Text, ref keyword); - } - else - if (!new_space && keyw != PascalABCCompiler.Parsers.KeywordKind.Uses) - if (charTyped != '$') - expr = FindExpression(off, Text, line, col); - else - expr = FindExpression(off - 1, Text, line, col); - List Errors = new List(); - PascalABCCompiler.SyntaxTree.expression e = null; - if (ctrl_space && !shift_space && (pattern == null || pattern == "")) + dotPressed = charTyped == '.', + ctrlSpace = charTyped == '_', + shiftSpace = charTyped == '\0', + spaceAfterNew = keywordKind == KeywordKind.New, + spaceAfterUses = keywordKind == KeywordKind.Uses + }; + + string expressionText = GetExpressionTextForCompletionData(off, text, line, col, + currentLanguage.LanguageInformation, in context, out var insidePatternWithDots, out var ctrlOrShiftSpaceAfterDot, out var pattern); + + // добавляем ключевые слова в случае "ctrl + space", нажатых в "пустом" месте + if (!ctrlOrShiftSpaceAfterDot && context.ctrlSpace && string.IsNullOrEmpty(pattern)) { - string[] keywords = CodeCompletion.CodeCompletionNameHelper.Helper.GetKeywords(); - foreach (string key in keywords) - { - //if (key.StartsWith(pattern, StringComparison.CurrentCultureIgnoreCase)) - resultList.Add(new UserDefaultCompletionData(key, null, ImagesProvider.IconNumberKeyword, false)); - } + var keywords = CodeCompletion.CodeCompletionNameHelper.Helper.GetKeywords(); + + resultList.AddRange(keywords.Select(keyword => + new UserDefaultCompletionData(keyword, null, ImagesProvider.IconNumberKeyword, false))); } - ILanguage currentLanguage = LanguageProvider.Instance.SelectLanguageByExtensionSafe(FileName); + PascalABCCompiler.SyntaxTree.expression expr = null; + + // для "ctrl + space" и "shift + space" дерево expression не требуется (кроме случая insidePatternWithDots) + if ((context.dotPressed || context.spaceAfterNew || context.spaceAfterUses || insidePatternWithDots) && expressionText != null) + { + expr = GetExpressionForCompletionData(currentLanguage.Parser, + in context, expressionText, insidePatternWithDots, out var shouldReturnNull); - if (currentLanguage == null) + if (shouldReturnNull) + return null; + } + + SymInfo[] symInfos = GetSymInfosForCompletionData(line, col, in context, currentLanguage.CaseSensitive, + expressionText, ctrlOrShiftSpaceAfterDot, insidePatternWithDots, pattern, expr, out var selectedSymInfo, out var lastUsedMember, out var shouldReturnNull2); + + if (shouldReturnNull2) return null; - if ((!ctrl_space || inside_dot_pattern) && expr != null) + if (symInfos != null) { - e = currentLanguage.Parser.GetTypeAsExpression("test" + System.IO.Path.GetExtension(FileName), expr, Errors, new List()); - if (e == null) - { - Errors.Clear(); - e = currentLanguage.Parser.GetExpression("test" + System.IO.Path.GetExtension(FileName), expr, Errors, new List()); - } - if ((e == null || Errors.Count > 0) && !new_space) return null; - } - SymInfo[] mis = null; - CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[FileName]; - if (dconv == null) - { - if (keyw == PascalABCCompiler.Parsers.KeywordKind.Uses) - mis = CodeCompletion.DomConverter.standard_units; - else if (!ctrl_space) - return new ICompletionData[0]; - } - string fname = FileName; - SymInfo sel_si = null; - string last_used_member = null; - if (dconv != null) - { - if (new_space) - mis = dconv.GetTypes(e, line, col, out sel_si); - else if (keyw == PascalABCCompiler.Parsers.KeywordKind.Uses && mis == null) - { - if (WorkbenchServiceFactory.Workbench.UserOptions.EnableSmartIntellisense) - mis = dconv.GetNamespaces(); - else - mis = CodeCompletion.DomConverter.standard_units; - } - - else - if (!ctrl_space) - { - CodeCompletion.SymScope dot_sc = null; - mis = dconv.GetName(e, expr, line, col, keyword, ref dot_sc); - if (dot_sc != null && VisualPABCSingleton.MainForm.UserOptions.EnableSmartIntellisense) - { - CompletionDataDispatcher.AddMemberBeforeDot(dot_sc); - last_used_member = CompletionDataDispatcher.GetRecentUsedMember(dot_sc); - } - } - else - { - CodeCompletion.SymScope dot_sc = null; - if (inside_dot_pattern) - { - List si_list = new List(); - SymInfo[] from_list = dconv.GetName(e, expr, line, col, keyword, ref dot_sc); - for (int i = 0; i < from_list.Length; i++) - { - if (from_list[i].name.StartsWith(pattern, StringComparison.OrdinalIgnoreCase)) - si_list.Add(from_list[i]); - } - mis = si_list.ToArray(); - } - - else - mis = dconv.GetNameByPattern(pattern, line, col, charTyped == '_', VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); - } - - } - Hashtable cache = null; - if (!currentLanguage.CaseSensitive) - cache = new Hashtable(StringComparer.CurrentCultureIgnoreCase); - else - cache = new Hashtable(); - int num = 0; - if (mis != null) - { - bool stop = false; - ICompletionData data = null; - - foreach (SymInfo mi in mis) - { - if (mi == null || mi.not_include) continue; - if (cache.Contains(mi.name + mi.kind)) - continue; - - UserDefaultCompletionData ddd = new UserDefaultCompletionData(mi.addit_name != null ? mi.addit_name : mi.name, mi.description, ImagesProvider.GetPictureNum(mi), false); - - disp.Add(mi, ddd); - resultList.Add(ddd); - cache[mi.name + mi.kind] = mi; - /*if (VisualPABCSingleton.MainForm.UserOptions.EnableSmartIntellisense && mi.name != null && mi.name != "" && data == null) - { - data = CompletionDataDispatcher.GetLastUsedItem(mi.name[0]); - if (data != null && data.Text == ddd.Text) data = ddd; - }*/ - if (last_used_member != null && last_used_member == mi.name) - { - defaultCompletionElement = ddd; - } - if (sel_si != null && mi == sel_si) - { - defaultCompletionElement = ddd; - stop = true; - } - } - - if (defaultCompletionElement == null && data != null) - defaultCompletionElement = data as UserDefaultCompletionData; + AddCompletionDatasForSymInfos(resultList, currentLanguage.CaseSensitive, symInfos, selectedSymInfo, lastUsedMember); } } - catch (Exception e) + catch (Exception) { } + + return resultList.ToArray(); + } + + /// + /// Добавляет в resultList (итоговый список подсказок) данные, соответствующие переданному массиву типа SymInfo[]. + /// Используется для случая нажатия пользователем "триггерной" клавиши + /// + private void AddCompletionDatasForSymInfos(List resultList, bool languageCaseSensitive, SymInfo[] symInfos, SymInfo selectedSymInfo, string lastUsedMember) + { + // ICompletionData data = null; + + HashSet symbolsAdded = languageCaseSensitive ? new HashSet() : new HashSet(StringComparer.CurrentCultureIgnoreCase); + + foreach (SymInfo symInfo in symInfos) + { + if (symInfo == null || symInfo.not_include) + continue; + + if (symbolsAdded.Contains(symInfo.name + symInfo.kind)) + continue; + + UserDefaultCompletionData completionData = new UserDefaultCompletionData( + symInfo.addit_name != null ? symInfo.addit_name : symInfo.name, + symInfo.description, ImagesProvider.GetPictureNum(symInfo), false + ); + + disp.Add(symInfo, completionData); + + resultList.Add(completionData); + + symbolsAdded.Add(symInfo.name + symInfo.kind); + + /*if (VisualPABCSingleton.MainForm.UserOptions.EnableSmartIntellisense && mi.name != null && mi.name != "" && data == null) + { + data = CompletionDataDispatcher.GetLastUsedItem(mi.name[0]); + if (data != null && data.Text == ddd.Text) data = ddd; + }*/ + + if (lastUsedMember != null && lastUsedMember == symInfo.name || selectedSymInfo != null && symInfo == selectedSymInfo) + { + defaultCompletionElement = completionData; + } + } + + /*if (defaultCompletionElement == null && data != null) + defaultCompletionElement = data as UserDefaultCompletionData;*/ + } + + /// + /// Формирует массив данных из таблицы символов для подсказок в случае нажатия пользователем "триггерной" клавиши + /// (в зависимости от введенного выражения и другого контекста) + /// + private SymInfo[] GetSymInfosForCompletionData(int line, int col, in ActionContext context, bool languageCaseSensitive, string expressionText, bool ctrlOrShiftSpaceAfterDot, bool insidePatternWithDots, string pattern, PascalABCCompiler.SyntaxTree.expression expr, out SymInfo selectedSymInfo, out string lastUsedMember, out bool shouldReturnNull) + { + SymInfo[] symInfos = null; + + shouldReturnNull = false; + + selectedSymInfo = null; + lastUsedMember = null; + + CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[FileName]; + + if (dconv == null) + { + // в данном случае возвращаем пустой массив ICompletionData + if (!context.spaceAfterUses && !context.ctrlSpace) + shouldReturnNull = true; + + if (context.spaceAfterUses) + symInfos = CodeCompletion.DomConverter.standard_units; + } + else + { + if (context.spaceAfterNew) + { + symInfos = dconv.GetTypes(expr, line, col, out selectedSymInfo); + } + else if (context.spaceAfterUses) + { + if (WorkbenchServiceFactory.Workbench.UserOptions.EnableSmartIntellisense) + symInfos = dconv.GetNamespaces(); + else + symInfos = CodeCompletion.DomConverter.standard_units; + } + // нажатие ctrl + space и shift + space сразу после точки приравнивается к нажатию точки + else if (context.dotPressed || ctrlOrShiftSpaceAfterDot) + { + CodeCompletion.SymScope dotScope = null; + symInfos = dconv.GetName(expr, expressionText, line, col, keyword, ref dotScope); + + if (dotScope != null && VisualPABCSingleton.MainForm.UserOptions.EnableSmartIntellisense) + { + CompletionDataDispatcher.AddMemberBeforeDot(dotScope); + lastUsedMember = CompletionDataDispatcher.GetRecentUsedMember(dotScope); + } + } + // ctrl + space после некоторого выражения + else if (context.ctrlSpace) // context.chiftSpace здесь не может быть true, поскольку такие ситуации обрабатываются в ShiftSpaceActions.Execute() + { + CodeCompletion.SymScope dotScope = null; + + // если мы в цепочечном выражении с точками + if (insidePatternWithDots) + { + + symInfos = dconv.GetName(expr, expressionText, line, col, keyword, ref dotScope) + .Where(symInfo => symInfo.name.StartsWith(pattern, + languageCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) + .ToArray(); + } + else + symInfos = dconv.GetNameByPattern(pattern, line, col, context.ctrlSpace, VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); + } + + } + + return symInfos; + } + + /// + /// Возвращает дерево выражения, введенного пользователем перед нажатием "триггерной" клавиши + /// + private PascalABCCompiler.SyntaxTree.expression GetExpressionForCompletionData(IParser parser, in ActionContext context, string expressionText, bool insidePatternWithDots, out bool shouldReturnNull) + { + shouldReturnNull = false; + + List Errors = new List(); + List Warnings = new List(); + + var expr = parser.GetTypeAsExpression("test" + System.IO.Path.GetExtension(FileName), expressionText, Errors, Warnings); + if (expr == null) + { + Errors.Clear(); + expr = parser.GetExpression("test" + System.IO.Path.GetExtension(FileName), expressionText, Errors, Warnings); + } + + if ((expr == null || Errors.Count > 0) && !context.spaceAfterNew) + shouldReturnNull = true; + + return expr; + } + + /// + /// Получение текста выражения, введенного пользователем перед нажатием "триггерной" клавиши + /// + private string GetExpressionTextForCompletionData(int off, string text, int line, int col, ILanguageInformation languageInformation, in ActionContext context, out bool insidePatternWithDots, out bool ctrlOrShiftSpaceAfterDot, out string pattern) + { + + string expressionText = null; + pattern = null; + insidePatternWithDots = false; + ctrlOrShiftSpaceAfterDot = false; + + if (context.ctrlSpace || context.shiftSpace) { + pattern = languageInformation.FindPattern(off, text, out var isPattern); + + // в конце выражения точка + if (!isPattern && text[off - 1] == '.') + { + ctrlOrShiftSpaceAfterDot = true; + } + + // если нужно подсказать все варианты после точки, то поведение как в случае context.dotPressed + if (isPattern && text[off - pattern.Length - 1] == '.' || ctrlOrShiftSpaceAfterDot) + { + insidePatternWithDots = true; + expressionText = FindExpression(off - (pattern?.Length ?? 0) - 1, text, line, col); + } + } - return resultList.ToArray(); + else if (context.spaceAfterNew) + { + expressionText = languageInformation.SkipNew(off - 1, text, ref keyword); + } + else if (context.dotPressed) // keywordKind != KeywordKind.Uses + { + expressionText = FindExpression(off, text, line, col); + } + + return expressionText; } private CodeCompletion.CodeCompletionController controller; @@ -636,11 +792,11 @@ namespace VisualPascalABC public ICompletionData[] GenerateCompletionDataByFirstChar(string fileName, TextArea textArea, char charTyped, PascalABCCompiler.Parsers.KeywordKind keyw) { controller = new CodeCompletion.CodeCompletionController(); - int off = textArea.Caret.Offset; + // int off = textArea.Caret.Offset; string text = textArea.Document.TextContent.Substring(0, textArea.Caret.Offset); //controller.Compile(file_name, text /*+ ")))));end."*/); FileName = fileName; Text = text; - ICompletionData[] data = GetCompletionDataByFirst(off, text, textArea.Caret.Line, textArea.Caret.Column, charTyped, keyw); + ICompletionData[] data = GetCompletionDataByFirst(textArea.Caret.Line, textArea.Caret.Column, charTyped, keyw); CodeCompletion.AssemblyDocCache.CompleteDocumentation(); CodeCompletion.UnitDocCache.CompleteDocumentation(); controller = null; diff --git a/VisualPascalABCNET/IB/CodeCompletion/CodeTemplateManager.cs b/VisualPascalABCNET/IB/CodeCompletion/CodeTemplateManager.cs index 669efba85..04b361174 100644 --- a/VisualPascalABCNET/IB/CodeCompletion/CodeTemplateManager.cs +++ b/VisualPascalABCNET/IB/CodeCompletion/CodeTemplateManager.cs @@ -11,6 +11,7 @@ namespace VisualPascalABC { public Dictionary ht = new Dictionary(); + // TODO: Требуется адаптация к многоязычности EVA public CodeTemplateManager(string filename = "template.pct", bool searchFromLocalDirectory = false) { try diff --git a/VisualPascalABCNET/IB/CodeCompletion/TooltipServiceManager.cs b/VisualPascalABCNET/IB/CodeCompletion/TooltipServiceManager.cs index 6c83e5e22..06e3dd50b 100644 --- a/VisualPascalABCNET/IB/CodeCompletion/TooltipServiceManager.cs +++ b/VisualPascalABCNET/IB/CodeCompletion/TooltipServiceManager.cs @@ -4,14 +4,10 @@ using System; using System.Collections.Generic; using System.Drawing; using System.IO; -using System.Linq; -using System.Text; using System.Windows.Forms; -using ICSharpCode.Core; using ICSharpCode.TextEditor; using ICSharpCode.TextEditor.Document; using ICSharpCode.TextEditor.Gui.CompletionWindow; -using Languages.Integration; using PascalABCCompiler.Parsers; namespace VisualPascalABC @@ -22,65 +18,68 @@ namespace VisualPascalABC private static string GetPopupHintText(TextArea textArea, ToolTipRequestEventArgs e) { - ICSharpCode.TextEditor.TextLocation logicPos = e.LogicalPosition; + TextLocation logicPos = e.LogicalPosition; IDocument doc = textArea.Document; - LineSegment seg = doc.GetLineSegment(logicPos.Y); - string FileName = textArea.MotherTextEditorControl.FileName; - PascalABCCompiler.Parsers.KeywordKind keyw = PascalABCCompiler.Parsers.KeywordKind.None; - if (logicPos.X > seg.Length - 1) + LineSegment lineSegment = doc.GetLineSegment(logicPos.Y); + + string fileName = textArea.MotherTextEditorControl.FileName; + + if (logicPos.X > lineSegment.Length - 1) return null; + //string expr = FindFullExpression(doc.TextContent, seg.Offset + logicPos.X,e.LogicalPosition.Line,e.LogicalPosition.Column); - string expr_without_brackets = null; - string expr = CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.FindExpressionFromAnyPosition(seg.Offset + logicPos.X, doc.TextContent, e.LogicalPosition.Line, e.LogicalPosition.Column, out keyw, out expr_without_brackets); + + IParser parser = CodeCompletion.CodeCompletionController.CurrentParser; + + string expr = parser.LanguageInformation.FindExpressionFromAnyPosition( + lineSegment.Offset + logicPos.X, doc.TextContent, e.LogicalPosition.Line, e.LogicalPosition.Column, out var keyw, out var exprWithoutBrackets); + + // пока непонятно, когда такое может быть EVA + if (expr == null) + expr = exprWithoutBrackets; + if (expr == null) - expr = expr_without_brackets; - if (expr_without_brackets == null) return null; + List Errors = new List(); + List Warnings = new List(); - IParser parser = Languages.Facade.LanguageProvider.Instance.SelectLanguageByExtension(FileName).Parser; + PascalABCCompiler.SyntaxTree.expression expressionTree = parser.GetExpression("test" + Path.GetExtension(fileName), + expr, Errors, Warnings); - PascalABCCompiler.SyntaxTree.expression tree = parser.GetExpression("test" + Path.GetExtension(FileName), expr, Errors, new List()); - bool header = false; - if (Errors.Count > 0) + // Пока добавили проверку, что анализируем Паскаль EVA + if (Errors.Count > 0 && parser == Languages.Facade.LanguageProvider.Instance.SelectLanguageByName(PascalABCCompiler.StringConstants.pascalLanguageName).Parser) { - if (expr.IndexOf('<') != -1 && expr.IndexOf('>') != -1) + string s = expr.TrimStart(); + if (s.Length > 0 && s[0] == '^') { Errors.Clear(); - tree = parser.GetExpression("test" + Path.GetExtension(FileName), expr.Replace("<", "&<"), Errors, new List()); - } - else - { - string s = expr.TrimStart(); - if (s.Length > 0 && s[0] == '^') - { - Errors.Clear(); - expr_without_brackets = expr_without_brackets.TrimStart().Substring(1); - tree = parser.GetExpression("test" + Path.GetExtension(FileName), s.Substring(1), Errors, new List()); - } + exprWithoutBrackets = exprWithoutBrackets.TrimStart().Substring(1); + expressionTree = parser.GetExpression("test" + Path.GetExtension(fileName), s.Substring(1), Errors, Warnings); } } - if (tree == null || Errors.Count > 0) + + List Errors2 = new List(); + + // проверка для выражения без скобок + PascalABCCompiler.SyntaxTree.expression expressionTree2 = parser.GetExpression("test" + Path.GetExtension(fileName), exprWithoutBrackets, Errors2, Warnings); + + // если не получается, то сразу возврат + if (expressionTree2 == null || Errors2.Count > 0) + return null; + + bool header = false; + if (expressionTree == null || Errors.Count > 0) { - Errors.Clear(); - tree = parser.GetExpression("test" + Path.GetExtension(FileName), expr_without_brackets, Errors, new List()); header = true; - if (tree == null || Errors.Count > 0) - return null; + expressionTree = expressionTree2; } - else - { - Errors.Clear(); - PascalABCCompiler.SyntaxTree.expression tree2 = parser.GetExpression("test" + Path.GetExtension(FileName), expr_without_brackets, Errors, new List()); - //header = true; - if (tree2 == null || Errors.Count > 0) - return null; - //if (tree is PascalABCCompiler.SyntaxTree.new_expr && (tree as PascalABCCompiler.SyntaxTree.new_expr).params_list == null) - // tree = tree2; - } - CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[FileName]; - if (dconv == null) return null; - return dconv.GetDescription(tree, FileName, expr_without_brackets, e.LogicalPosition.Line, e.LogicalPosition.Column, keyw, header); + + CodeCompletion.DomConverter domConverter = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[fileName]; + + if (domConverter == null) return null; + + return domConverter.GetDescription(expressionTree, fileName, exprWithoutBrackets, e.LogicalPosition.Line, e.LogicalPosition.Column, keyw, header); } static int _mouse_hint_x = 0, _mouse_hint_y = 0; diff --git a/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionActions.cs b/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionActions.cs index 89983ebf1..0303944c7 100644 --- a/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionActions.cs +++ b/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionActions.cs @@ -118,22 +118,38 @@ namespace VisualPascalABC public static List GetDefinitionPosition(TextArea textArea, bool only_check) { - if (CodeCompletion.CodeCompletionController.CurrentParser == null) return new List(); + IParser parser = CodeCompletion.CodeCompletionController.CurrentParser; + + if (parser == null) + return new List(); + IDocument doc = textArea.Document; string textContent = doc.TextContent; ccp = new CodeCompletionProvider(); string full_expr; string expressionResult = FindFullExpression(textContent, textArea, out ccp.keyword, out full_expr); + if (expressionResult != null) expressionResult = expressionResult.Trim(); - if (expressionResult != full_expr && full_expr.StartsWith("(")) - return new List(); - - if (full_expr != null && full_expr.Contains("^")) - full_expr = full_expr.Replace("^",""); + + + // Проверка, что компилируем Паскаль временно EVA 10.11.2024 + if (parser == Languages.Facade.LanguageProvider.Instance.SelectLanguageByName(StringConstants.pascalLanguageName).Parser) + { + if (expressionResult != full_expr && full_expr.StartsWith("(")) + return new List(); + + if (full_expr != null && full_expr.Contains("^")) + { + full_expr = full_expr.Replace("^", ""); + } + } + List poses = ccp.GetDefinition(full_expr, textArea.MotherTextEditorControl.FileName, textArea.Caret.Line, textArea.Caret.Column, only_check); + if (poses == null || poses.Count == 0) poses = ccp.GetDefinition(expressionResult, textArea.MotherTextEditorControl.FileName, textArea.Caret.Line, textArea.Caret.Column, only_check); + return poses; } diff --git a/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionKeyHandler.cs b/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionKeyHandler.cs index 208e498c6..f4f761ca3 100644 --- a/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionKeyHandler.cs +++ b/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionKeyHandler.cs @@ -1,4 +1,4 @@ -// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt) +// 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; @@ -199,8 +199,10 @@ namespace VisualPascalABC if (CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsDefinitionIdentifierAfterKeyword(keyw)) return false; + // если не первый символ выражения (предыдущий тоже буква или "_"), то не открываем новое окно if (editor.ActiveTextAreaControl.TextArea.Caret.Offset > 0 && (char.IsLetterOrDigit(editor.Document.TextContent[editor.ActiveTextAreaControl.TextArea.Caret.Offset - 1]) || editor.Document.TextContent[editor.ActiveTextAreaControl.TextArea.Caret.Offset - 1] == '_')) return false; + completionDataProvider = new CodeCompletionProvider(); codeCompletionWindow = PABCNETCodeCompletionWindow.ShowCompletionWindowWithFirstChar( VisualPABCSingleton.MainForm, // The parent window for the completion window diff --git a/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionProvider.cs b/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionProvider.cs index 569f6aced..c48bbd881 100644 --- a/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionProvider.cs +++ b/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionProvider.cs @@ -1,8 +1,9 @@ -// Copyright (c) Ivan Bondarev, Stanislav Mikhalkovich (for details please see \doc\copyright.txt) +// 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.Linq; using System.Text; using System.Windows.Forms; using ICSharpCode.TextEditor; @@ -113,12 +114,6 @@ namespace VisualPascalABC if (VisualPABCSingleton.MainForm.VisualEnvironmentCompiler.compilerLoaded) e = language.Parser.GetExpression("test" + System.IO.Path.GetExtension(fileName), expr, Errors, new List()); - if (e is PascalABCCompiler.SyntaxTree.bin_expr && expr.Contains("<")) - { - expr = expr.Replace("<","&<"); - Errors.Clear(); - e = language.Parser.GetExpression("test" + System.IO.Path.GetExtension(fileName), expr, Errors, new List()); - } if (e == null) return loc; CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[fileName]; @@ -321,251 +316,412 @@ namespace VisualPascalABC return expr; } - public ICompletionData[] GetCompletionDataByFirst(int off, string Text, int line, int col, char charTyped, PascalABCCompiler.Parsers.KeywordKind keyw) + /// + /// Возвращает подсказки определяемые первым символом выражения, введенного пользователем + /// + public ICompletionData[] GetCompletionDataByFirst(int line, int col, char charTyped, KeywordKind keyw) { List resultList = new List(); - List lst = new List(); try { - CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[FileName]; - /*if (dconv == null && CodeCompletion.CodeCompletionNameHelper.system_unit_file_full_name != null - && (keyw == CodeCompletion.KeywordKind.kw_colon || keyw == CodeCompletion.KeywordKind.kw_of)) - { - dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[CodeCompletion.CodeCompletionNameHelper.system_unit_file_full_name]; - special_module = true; - }*/ - string pattern = charTyped.ToString(); - string[] keywords = CodeCompletion.CodeCompletionNameHelper.Helper.GetKeywords(); - if (CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsTypeAfterKeyword(keyw)) + + ILanguageInformation languageInformation = CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation; + + List keywords; + + bool isTypeAfterKeyword = false; + + // если по смыслу должен вводиться тип данных + if (languageInformation.IsTypeAfterKeyword(keyw)) { keywords = CodeCompletion.CodeCompletionNameHelper.Helper.GetTypeKeywords(); + isTypeAfterKeyword = true; } - if (!CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsNamespaceAfterKeyword(keyw)) - foreach (string key in keywords) - { - //if (key.StartsWith(pattern, StringComparison.CurrentCultureIgnoreCase)) - resultList.Add(new UserDefaultCompletionData(key, null, ImagesProvider.IconNumberKeyword, false)); - } - PascalABCCompiler.Parsers.SymInfo[] mis = null; - if (CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsNamespaceAfterKeyword(keyw)) + else { - mis = CodeCompletion.DomConverter.standard_units; + keywords = CodeCompletion.CodeCompletionNameHelper.Helper.GetKeywords(); } - if (dconv != null) - { - //if (keyw == PascalABCCompiler.Parsers.KeywordKind.Colon || keyw == PascalABCCompiler.Parsers.KeywordKind.Of || keyw == PascalABCCompiler.Parsers.KeywordKind.TypeDecl) - if (CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsTypeAfterKeyword(keyw)) - mis = dconv.GetTypeByPattern(pattern, line, col, true, VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); - else if (CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.IsNamespaceAfterKeyword(keyw) && mis == null) - mis = dconv.GetNamespaces(); - else - mis = dconv.GetNameByPattern(null, line, col, true, VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); - } - Hashtable cache = new Hashtable(); - if (mis != null) - { - bool stop = false; - ICompletionData data = CompletionDataDispatcher.GetLastUsedItem(charTyped); - foreach (PascalABCCompiler.Parsers.SymInfo mi in mis) - { - if (mi.not_include) continue; - if (cache.Contains(mi.name)) - continue; + bool isNamespaceAfterKeyword = false; + + // конструкция типа "uses" + if (languageInformation.IsNamespaceAfterKeyword(keyw)) + { + isNamespaceAfterKeyword = true; + } + else + { + resultList.AddRange(keywords.Select(keyword => + new UserDefaultCompletionData(keyword, null, ImagesProvider.IconNumberKeyword, false))); + } + + SymInfo[] symInfos = GetSymInfosForCompletionDataByFirst(line, col, isTypeAfterKeyword, isNamespaceAfterKeyword, charTyped.ToString()); + + if (symInfos != null) + { + bool languageCaseSensitive = LanguageProvider.Instance.SelectLanguageByExtension(FileName).CaseSensitive; + + AddCompletionDatasByFirstForSymInfos(resultList, charTyped, symInfos, languageCaseSensitive); - UserDefaultCompletionData ddd = new UserDefaultCompletionData(mi.name, mi.description, ImagesProvider.GetPictureNum(mi), false); - if (!stop && data != null && string.Compare(mi.name, data.Text, true) == 0) - { - defaultCompletionElement = ddd; - stop = true; - } - else if (!stop && data == null && mi.name.StartsWith(charTyped.ToString(), StringComparison.CurrentCultureIgnoreCase)) - { - //defaultCompletionElement = ddd; - lst.Add(ddd); - //stop = true; - } - disp.Add(mi, ddd); - resultList.Add(ddd); - cache[mi.name] = mi; - } //resultList.Sort(); //defaultCompletionElement = resultList[0] as DefaultCompletionData; } } - catch (Exception e) - { + catch (Exception) { } - } - - lst.Sort(); - if (lst.Count > 0) defaultCompletionElement = lst[0] as UserDefaultCompletionData; - ICompletionData[] res_arr = resultList.ToArray(); this.ByFirstChar = true; - return res_arr; + + return resultList.ToArray(); } - public ICompletionData[] GetCompletionData(int off, string Text, int line, int col, char charTyped, PascalABCCompiler.Parsers.KeywordKind keyw) + /// + /// Добавляет в resultList (итоговый список подсказок) данные, соответствующие переданному массиву типа SymInfo[]. + /// Используется для случая ввода пользователем первого символа выражения + /// + private void AddCompletionDatasByFirstForSymInfos(List resultList, char charTyped, SymInfo[] symInfos, bool languageCaseSensitive) + { + HashSet symbolsAdded = languageCaseSensitive ? new HashSet() : new HashSet(StringComparer.CurrentCultureIgnoreCase); + + List candidatesForDefault = new List(); + + bool stop = false; + ICompletionData lastUsedItem = CompletionDataDispatcher.GetLastUsedItem(charTyped); + + foreach (SymInfo symInfo in symInfos) + { + if (symInfo == null || symInfo.not_include) continue; + + if (symbolsAdded.Contains(symInfo.name)) + continue; + + UserDefaultCompletionData completionData = new UserDefaultCompletionData(symInfo.name, symInfo.description, ImagesProvider.GetPictureNum(symInfo), false); + + StringComparison stringComparison = languageCaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase; + + // если мы выбирали что-то раньше из списка подсказок, то считаем это элементом по умолчанию + if (!stop && lastUsedItem != null && string.Equals(symInfo.name, lastUsedItem.Text, stringComparison)) + { + defaultCompletionElement = completionData; + stop = true; + } + // иначе формируем список подходящих подсказок - "кандидатов" для элемента по умолчанию + else if (!stop && lastUsedItem == null && symInfo.name.StartsWith(charTyped.ToString(), stringComparison)) + { + //defaultCompletionElement = ddd; + candidatesForDefault.Add(completionData); + //stop = true; + } + + disp.Add(symInfo, completionData); + + resultList.Add(completionData); + + symbolsAdded.Add(symInfo.name); + } + + if (candidatesForDefault.Count > 0) + defaultCompletionElement = candidatesForDefault.Min() as UserDefaultCompletionData; // здесь выбирается минимальное по длине + } + + + /// + /// Формирует массив данных из таблицы символов для подсказок в случае введения пользователем первой буквы выражения + /// + private SymInfo[] GetSymInfosForCompletionDataByFirst(int line, int col, bool isTypeAfterKeyword, bool isNamespaceAfterKeyword, string pattern) + { + CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[FileName]; + /*if (dconv == null && CodeCompletion.CodeCompletionNameHelper.system_unit_file_full_name != null + && (keyw == CodeCompletion.KeywordKind.kw_colon || keyw == CodeCompletion.KeywordKind.kw_of)) + { + dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[CodeCompletion.CodeCompletionNameHelper.system_unit_file_full_name]; + special_module = true; + }*/ + + SymInfo[] symInfos = null; + + if (dconv == null) + { + if (isNamespaceAfterKeyword) + { + symInfos = CodeCompletion.DomConverter.standard_units; + } + } + else + { + //if (keyw == PascalABCCompiler.Parsers.KeywordKind.Colon || keyw == PascalABCCompiler.Parsers.KeywordKind.Of || keyw == PascalABCCompiler.Parsers.KeywordKind.TypeDecl) + if (isTypeAfterKeyword) + { + symInfos = dconv.GetTypeByPattern(pattern, line, col, true, VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); + } + else if (isNamespaceAfterKeyword) + { + if (WorkbenchServiceFactory.Workbench.UserOptions.EnableSmartIntellisense) + symInfos = dconv.GetNamespaces(); + else + symInfos = CodeCompletion.DomConverter.standard_units; + } + else + { + // интересно, что передается pattern = null EVA + symInfos = dconv.GetNameByPattern(null, line, col, true, VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); + } + } + + return symInfos; + } + + /// + /// Вспомогательная струтура для метода GetCompletionData, хранит информацию о действиях пользователя + /// + private struct ActionContext + { + public bool dotPressed; + public bool ctrlSpace; + public bool shiftSpace; + public bool spaceAfterNew; + public bool spaceAfterUses; + } + + /// + /// Возвращает массив подсказок для случая нажатия пользователем "триггерной" клавиши + /// + public ICompletionData[] GetCompletionData(int off, string text, int line, int col, char charTyped, KeywordKind keywordKind) { List resultList = new List(); try { - string pattern = null; - string expr = null; - bool ctrl_space = charTyped == '\0' || charTyped == '_'; - bool shift_space = charTyped == '\0'; - bool inside_dot_pattern = false; - bool new_space = keyw == PascalABCCompiler.Parsers.KeywordKind.New; - if (ctrl_space) - { - bool is_pattern = false; - pattern = CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.FindPattern(off, Text, out is_pattern); - if (is_pattern && Text[off - pattern.Length - 1] == '.') - { - inside_dot_pattern = true; - expr = FindExpression(off - pattern.Length - 1, Text, line, col); - } + // поменять на обращение к CodeCompletionController.CurrentLanguage + ILanguage currentLanguage = LanguageProvider.Instance.SelectLanguageByExtension(FileName); - } - else if (new_space) + var context = new ActionContext() { - expr = CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.SkipNew(off - 1, Text, ref keyword); - } - else - if (!new_space && keyw != PascalABCCompiler.Parsers.KeywordKind.Uses) - if (charTyped != '$') - expr = FindExpression(off, Text, line, col); - else - expr = FindExpression(off - 1, Text, line, col); - List Errors = new List(); - PascalABCCompiler.SyntaxTree.expression e = null; - if (ctrl_space && !shift_space && (pattern == null || pattern == "")) + dotPressed = charTyped == '.', + ctrlSpace = charTyped == '_', + shiftSpace = charTyped == '\0', + spaceAfterNew = keywordKind == KeywordKind.New, + spaceAfterUses = keywordKind == KeywordKind.Uses + }; + + string expressionText = GetExpressionTextForCompletionData(off, text, line, col, + currentLanguage.LanguageInformation, in context, out var insidePatternWithDots, out var ctrlOrShiftSpaceAfterDot, out var pattern); + + // добавляем ключевые слова в случае "ctrl + space", нажатых в "пустом" месте + if (!ctrlOrShiftSpaceAfterDot && context.ctrlSpace && string.IsNullOrEmpty(pattern)) { - string[] keywords = CodeCompletion.CodeCompletionNameHelper.Helper.GetKeywords(); - foreach (string key in keywords) - { - //if (key.StartsWith(pattern, StringComparison.CurrentCultureIgnoreCase)) - resultList.Add(new UserDefaultCompletionData(key, null, ImagesProvider.IconNumberKeyword, false)); - } + var keywords = CodeCompletion.CodeCompletionNameHelper.Helper.GetKeywords(); + + resultList.AddRange(keywords.Select(keyword => + new UserDefaultCompletionData(keyword, null, ImagesProvider.IconNumberKeyword, false))); } - ILanguage currentLanguage = LanguageProvider.Instance.SelectLanguageByExtensionSafe(FileName); + PascalABCCompiler.SyntaxTree.expression expr = null; - if (currentLanguage == null) + // для "ctrl + space" и "shift + space" дерево expression не требуется (кроме случая insidePatternWithDots) + if ((context.dotPressed || context.spaceAfterNew || context.spaceAfterUses || insidePatternWithDots) && expressionText != null) + { + expr = GetExpressionForCompletionData(currentLanguage.Parser, + in context, expressionText, insidePatternWithDots, out var shouldReturnNull); + + if (shouldReturnNull) + return null; + } + + SymInfo[] symInfos = GetSymInfosForCompletionData(line, col, in context, currentLanguage.CaseSensitive, + expressionText, ctrlOrShiftSpaceAfterDot, insidePatternWithDots, pattern, expr, out var selectedSymInfo, out var lastUsedMember, out var shouldReturnNull2); + + if (shouldReturnNull2) return null; - if ((!ctrl_space || inside_dot_pattern) && expr != null) + if (symInfos != null) { - e = currentLanguage.Parser.GetTypeAsExpression("test" + System.IO.Path.GetExtension(FileName), expr, Errors, new List()); - if (e == null) - { - Errors.Clear(); - e = currentLanguage.Parser.GetExpression("test" + System.IO.Path.GetExtension(FileName), expr, Errors, new List()); - } - if ((e == null || Errors.Count > 0) && !new_space) return null; - } - SymInfo[] mis = null; - CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[FileName]; - if (dconv == null) - { - if (keyw == PascalABCCompiler.Parsers.KeywordKind.Uses) - mis = CodeCompletion.DomConverter.standard_units; - else if (!ctrl_space) - return new ICompletionData[0]; - } - string fname = FileName; - SymInfo sel_si = null; - string last_used_member = null; - if (dconv != null) - { - if (new_space) - mis = dconv.GetTypes(e, line, col, out sel_si); - else if (keyw == PascalABCCompiler.Parsers.KeywordKind.Uses && mis == null) - { - if (WorkbenchServiceFactory.Workbench.UserOptions.EnableSmartIntellisense) - mis = dconv.GetNamespaces(); - else - mis = CodeCompletion.DomConverter.standard_units; - } - - else - if (!ctrl_space) - { - CodeCompletion.SymScope dot_sc = null; - mis = dconv.GetName(e, expr, line, col, keyword, ref dot_sc); - if (dot_sc != null && VisualPABCSingleton.MainForm.UserOptions.EnableSmartIntellisense) - { - CompletionDataDispatcher.AddMemberBeforeDot(dot_sc); - last_used_member = CompletionDataDispatcher.GetRecentUsedMember(dot_sc); - } - } - else - { - CodeCompletion.SymScope dot_sc = null; - if (inside_dot_pattern) - { - List si_list = new List(); - SymInfo[] from_list = dconv.GetName(e, expr, line, col, keyword, ref dot_sc); - for (int i = 0; i < from_list.Length; i++) - { - if (from_list[i].name.StartsWith(pattern, StringComparison.OrdinalIgnoreCase)) - si_list.Add(from_list[i]); - } - mis = si_list.ToArray(); - } - - else - mis = dconv.GetNameByPattern(pattern, line, col, charTyped == '_', VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); - } - - } - Hashtable cache = null; - if (!currentLanguage.CaseSensitive) - cache = new Hashtable(StringComparer.CurrentCultureIgnoreCase); - else - cache = new Hashtable(); - int num = 0; - if (mis != null) - { - bool stop = false; - ICompletionData data = null; - - foreach (SymInfo mi in mis) - { - if (mi.not_include) continue; - if (cache.Contains(mi.name + mi.kind)) - continue; - - UserDefaultCompletionData ddd = new UserDefaultCompletionData(mi.addit_name != null ? mi.addit_name : mi.name, mi.description, ImagesProvider.GetPictureNum(mi), false); - - disp.Add(mi, ddd); - resultList.Add(ddd); - cache[mi.name + mi.kind] = mi; - /*if (VisualPABCSingleton.MainForm.UserOptions.EnableSmartIntellisense && mi.name != null && mi.name != "" && data == null) - { - data = CompletionDataDispatcher.GetLastUsedItem(mi.name[0]); - if (data != null && data.Text == ddd.Text) data = ddd; - }*/ - if (last_used_member != null && last_used_member == mi.name) - { - defaultCompletionElement = ddd; - } - if (sel_si != null && mi == sel_si) - { - defaultCompletionElement = ddd; - stop = true; - } - } - - if (defaultCompletionElement == null && data != null) - defaultCompletionElement = data as UserDefaultCompletionData; + AddCompletionDatasForSymInfos(resultList, currentLanguage.CaseSensitive, symInfos, selectedSymInfo, lastUsedMember); } } - catch (Exception e) + catch (Exception) { } + + return resultList.ToArray(); + } + + /// + /// Добавляет в resultList (итоговый список подсказок) данные, соответствующие переданному массиву типа SymInfo[]. + /// Используется для случая нажатия пользователем "триггерной" клавиши + /// + private void AddCompletionDatasForSymInfos(List resultList, bool languageCaseSensitive, SymInfo[] symInfos, SymInfo selectedSymInfo, string lastUsedMember) + { + // ICompletionData data = null; + + HashSet symbolsAdded = languageCaseSensitive ? new HashSet() : new HashSet(StringComparer.CurrentCultureIgnoreCase); + + foreach (SymInfo symInfo in symInfos) + { + if (symInfo == null || symInfo.not_include) + continue; + + if (symbolsAdded.Contains(symInfo.name + symInfo.kind)) + continue; + + UserDefaultCompletionData completionData = new UserDefaultCompletionData( + symInfo.addit_name != null ? symInfo.addit_name : symInfo.name, + symInfo.description, ImagesProvider.GetPictureNum(symInfo), false + ); + + disp.Add(symInfo, completionData); + + resultList.Add(completionData); + + symbolsAdded.Add(symInfo.name + symInfo.kind); + + /*if (VisualPABCSingleton.MainForm.UserOptions.EnableSmartIntellisense && mi.name != null && mi.name != "" && data == null) + { + data = CompletionDataDispatcher.GetLastUsedItem(mi.name[0]); + if (data != null && data.Text == ddd.Text) data = ddd; + }*/ + + if (lastUsedMember != null && lastUsedMember == symInfo.name || selectedSymInfo != null && symInfo == selectedSymInfo) + { + defaultCompletionElement = completionData; + } + } + + /*if (defaultCompletionElement == null && data != null) + defaultCompletionElement = data as UserDefaultCompletionData;*/ + } + + /// + /// Формирует массив данных из таблицы символов для подсказок в случае нажатия пользователем "триггерной" клавиши + /// (в зависимости от введенного выражения и другого контекста) + /// + private SymInfo[] GetSymInfosForCompletionData(int line, int col, in ActionContext context, bool languageCaseSensitive, string expressionText, bool ctrlOrShiftSpaceAfterDot, bool insidePatternWithDots, string pattern, PascalABCCompiler.SyntaxTree.expression expr, out SymInfo selectedSymInfo, out string lastUsedMember, out bool shouldReturnNull) + { + SymInfo[] symInfos = null; + + shouldReturnNull = false; + + selectedSymInfo = null; + lastUsedMember = null; + + CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[FileName]; + + if (dconv == null) + { + // в данном случае возвращаем пустой массив ICompletionData + if (!context.spaceAfterUses && !context.ctrlSpace) + shouldReturnNull = true; + + if (context.spaceAfterUses) + symInfos = CodeCompletion.DomConverter.standard_units; + } + else + { + if (context.spaceAfterNew) + { + symInfos = dconv.GetTypes(expr, line, col, out selectedSymInfo); + } + else if (context.spaceAfterUses) + { + if (WorkbenchServiceFactory.Workbench.UserOptions.EnableSmartIntellisense) + symInfos = dconv.GetNamespaces(); + else + symInfos = CodeCompletion.DomConverter.standard_units; + } + // нажатие ctrl + space и shift + space сразу после точки приравнивается к нажатию точки + else if (context.dotPressed || ctrlOrShiftSpaceAfterDot) + { + CodeCompletion.SymScope dotScope = null; + symInfos = dconv.GetName(expr, expressionText, line, col, keyword, ref dotScope); + + if (dotScope != null && VisualPABCSingleton.MainForm.UserOptions.EnableSmartIntellisense) + { + CompletionDataDispatcher.AddMemberBeforeDot(dotScope); + lastUsedMember = CompletionDataDispatcher.GetRecentUsedMember(dotScope); + } + } + // ctrl + space после некоторого выражения + else if (context.ctrlSpace) // context.chiftSpace здесь не может быть true, поскольку такие ситуации обрабатываются в ShiftSpaceActions.Execute() + { + CodeCompletion.SymScope dotScope = null; + + // если мы в цепочечном выражении с точками + if (insidePatternWithDots) + { + + symInfos = dconv.GetName(expr, expressionText, line, col, keyword, ref dotScope) + .Where(symInfo => symInfo.name.StartsWith(pattern, + languageCaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase)) + .ToArray(); + } + else + symInfos = dconv.GetNameByPattern(pattern, line, col, context.ctrlSpace, VisualPABCSingleton.MainForm.UserOptions.CodeCompletionNamespaceVisibleRange); + } + + } + + return symInfos; + } + + /// + /// Возвращает дерево выражения, введенного пользователем перед нажатием "триггерной" клавиши + /// + private PascalABCCompiler.SyntaxTree.expression GetExpressionForCompletionData(IParser parser, in ActionContext context, string expressionText, bool insidePatternWithDots, out bool shouldReturnNull) + { + shouldReturnNull = false; + + List Errors = new List(); + List Warnings = new List(); + + var expr = parser.GetTypeAsExpression("test" + System.IO.Path.GetExtension(FileName), expressionText, Errors, Warnings); + if (expr == null) + { + Errors.Clear(); + expr = parser.GetExpression("test" + System.IO.Path.GetExtension(FileName), expressionText, Errors, Warnings); + } + + if ((expr == null || Errors.Count > 0) && !context.spaceAfterNew) + shouldReturnNull = true; + + return expr; + } + + /// + /// Получение текста выражения, введенного пользователем перед нажатием "триггерной" клавиши + /// + private string GetExpressionTextForCompletionData(int off, string text, int line, int col, ILanguageInformation languageInformation, in ActionContext context, out bool insidePatternWithDots, out bool ctrlOrShiftSpaceAfterDot, out string pattern) + { + + string expressionText = null; + pattern = null; + insidePatternWithDots = false; + ctrlOrShiftSpaceAfterDot = false; + + if (context.ctrlSpace || context.shiftSpace) { + pattern = languageInformation.FindPattern(off, text, out var isPattern); + + // в конце выражения точка + if (!isPattern && text[off - 1] == '.') + { + ctrlOrShiftSpaceAfterDot = true; + } + + // если нужно подсказать все варианты после точки, то поведение как в случае context.dotPressed + if (isPattern && text[off - pattern.Length - 1] == '.' || ctrlOrShiftSpaceAfterDot) + { + insidePatternWithDots = true; + expressionText = FindExpression(off - (pattern?.Length ?? 0) - 1, text, line, col); + } + } - return resultList.ToArray(); + else if (context.spaceAfterNew) + { + expressionText = languageInformation.SkipNew(off - 1, text, ref keyword); + } + else if (context.dotPressed) // keywordKind != KeywordKind.Uses + { + expressionText = FindExpression(off, text, line, col); + } + + return expressionText; } private CodeCompletion.CodeCompletionController controller; @@ -640,7 +796,7 @@ namespace VisualPascalABC string text = textArea.Document.TextContent.Substring(0, textArea.Caret.Offset); //controller.Compile(file_name, text /*+ ")))));end."*/); FileName = fileName; Text = text; - ICompletionData[] data = GetCompletionDataByFirst(off, text, textArea.Caret.Line, textArea.Caret.Column, charTyped, keyw); + ICompletionData[] data = GetCompletionDataByFirst(textArea.Caret.Line, textArea.Caret.Column, charTyped, keyw); CodeCompletion.AssemblyDocCache.CompleteDocumentation(); CodeCompletion.UnitDocCache.CompleteDocumentation(); controller = null; diff --git a/VisualPascalABCNETLinux/IB/CodeCompletion/TooltipServiceManager.cs b/VisualPascalABCNETLinux/IB/CodeCompletion/TooltipServiceManager.cs index ea203f6ae..fba42b1cc 100644 --- a/VisualPascalABCNETLinux/IB/CodeCompletion/TooltipServiceManager.cs +++ b/VisualPascalABCNETLinux/IB/CodeCompletion/TooltipServiceManager.cs @@ -18,65 +18,68 @@ namespace VisualPascalABC private static string GetPopupHintText(TextArea textArea, ToolTipRequestEventArgs e) { - ICSharpCode.TextEditor.TextLocation logicPos = e.LogicalPosition; + TextLocation logicPos = e.LogicalPosition; IDocument doc = textArea.Document; - LineSegment seg = doc.GetLineSegment(logicPos.Y); - string FileName = textArea.MotherTextEditorControl.FileName; - PascalABCCompiler.Parsers.KeywordKind keyw = PascalABCCompiler.Parsers.KeywordKind.None; - if (logicPos.X > seg.Length - 1) + LineSegment lineSegment = doc.GetLineSegment(logicPos.Y); + + string fileName = textArea.MotherTextEditorControl.FileName; + + if (logicPos.X > lineSegment.Length - 1) return null; + //string expr = FindFullExpression(doc.TextContent, seg.Offset + logicPos.X,e.LogicalPosition.Line,e.LogicalPosition.Column); - string expr_without_brackets = null; - string expr = CodeCompletion.CodeCompletionController.CurrentParser.LanguageInformation.FindExpressionFromAnyPosition(seg.Offset + logicPos.X, doc.TextContent, e.LogicalPosition.Line, e.LogicalPosition.Column, out keyw, out expr_without_brackets); + + IParser parser = CodeCompletion.CodeCompletionController.CurrentParser; + + string expr = parser.LanguageInformation.FindExpressionFromAnyPosition( + lineSegment.Offset + logicPos.X, doc.TextContent, e.LogicalPosition.Line, e.LogicalPosition.Column, out var keyw, out var exprWithoutBrackets); + + // пока непонятно, когда такое может быть EVA + if (expr == null) + expr = exprWithoutBrackets; + if (expr == null) - expr = expr_without_brackets; - if (expr_without_brackets == null) return null; + List Errors = new List(); + List Warnings = new List(); - IParser parser = Languages.Facade.LanguageProvider.Instance.SelectLanguageByExtension(FileName).Parser; + PascalABCCompiler.SyntaxTree.expression expressionTree = parser.GetExpression("test" + Path.GetExtension(fileName), + expr, Errors, Warnings); - PascalABCCompiler.SyntaxTree.expression tree = parser.GetExpression("test" + Path.GetExtension(FileName), expr, Errors, new List()); - bool header = false; - if (Errors.Count > 0) + // Пока добавили проверку, что анализируем Паскаль EVA + if (Errors.Count > 0 && parser == Languages.Facade.LanguageProvider.Instance.SelectLanguageByName(PascalABCCompiler.StringConstants.pascalLanguageName).Parser) { - if (expr.IndexOf('<') != -1 && expr.IndexOf('>') != -1) + string s = expr.TrimStart(); + if (s.Length > 0 && s[0] == '^') { Errors.Clear(); - tree = parser.GetExpression("test" + Path.GetExtension(FileName), expr.Replace("<", "&<"), Errors, new List()); - } - else - { - string s = expr.TrimStart(); - if (s.Length > 0 && s[0] == '^') - { - Errors.Clear(); - expr_without_brackets = expr_without_brackets.TrimStart().Substring(1); - tree = parser.GetExpression("test" + Path.GetExtension(FileName), s.Substring(1), Errors, new List()); - } + exprWithoutBrackets = exprWithoutBrackets.TrimStart().Substring(1); + expressionTree = parser.GetExpression("test" + Path.GetExtension(fileName), s.Substring(1), Errors, Warnings); } } - if (tree == null || Errors.Count > 0) + + List Errors2 = new List(); + + // проверка для выражения без скобок + PascalABCCompiler.SyntaxTree.expression expressionTree2 = parser.GetExpression("test" + Path.GetExtension(fileName), exprWithoutBrackets, Errors2, Warnings); + + // если не получается, то сразу возврат + if (expressionTree2 == null || Errors2.Count > 0) + return null; + + bool header = false; + if (expressionTree == null || Errors.Count > 0) { - Errors.Clear(); - tree = parser.GetExpression("test" + Path.GetExtension(FileName), expr_without_brackets, Errors, new List()); header = true; - if (tree == null || Errors.Count > 0) - return null; + expressionTree = expressionTree2; } - else - { - Errors.Clear(); - PascalABCCompiler.SyntaxTree.expression tree2 = parser.GetExpression("test" + Path.GetExtension(FileName), expr_without_brackets, Errors, new List()); - //header = true; - if (tree2 == null || Errors.Count > 0) - return null; - //if (tree is PascalABCCompiler.SyntaxTree.new_expr && (tree as PascalABCCompiler.SyntaxTree.new_expr).params_list == null) - // tree = tree2; - } - CodeCompletion.DomConverter dconv = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[FileName]; - if (dconv == null) return null; - return dconv.GetDescription(tree, FileName, expr_without_brackets, e.LogicalPosition.Line, e.LogicalPosition.Column, keyw, header); + + CodeCompletion.DomConverter domConverter = (CodeCompletion.DomConverter)CodeCompletion.CodeCompletionController.comp_modules[fileName]; + + if (domConverter == null) return null; + + return domConverter.GetDescription(expressionTree, fileName, exprWithoutBrackets, e.LogicalPosition.Line, e.LogicalPosition.Column, keyw, header); } static int _mouse_hint_x = 0, _mouse_hint_y = 0; diff --git a/documentation/Intellisense/Функции Intellisense.md b/documentation/Intellisense/Функции Intellisense.md index e81a9f53f..d7fe404f9 100644 --- a/documentation/Intellisense/Функции Intellisense.md +++ b/documentation/Intellisense/Функции Intellisense.md @@ -1,41 +1,56 @@ #### Подсказка при наведении мыши На верхнем уровне реализуется в классе `TooltipServiceManager`. Основная логика реализована в методе `GetPopupHintText`. Вначале вызывается `FindExpressionFromAnyPosition` для получения нужного `expression` в виде строки. -Затем вызывается нужный парсер для получения дерева выражения (используется часть грамматики, связанная с нетерминалом parts). Если не получается распарсить, то делается попытка с решением неоднозначности, связанной с шаблонными параметрами (&<). -После этого еще попытка с удалением разыменования, пока непонятно зачем это. -Еще удаление разыменования было найдено в методе `GetDefinitionPosition` из `CodeCompletionActions`. -Затем еще попытка с тем же выражением без скобочек. При этом, если без скобочек распарсить не удается, то вне зависимости от успешности парсинга полного `expression` возвращается `null`. +Затем вызывается нужный парсер для получения дерева выражения (используется часть грамматики, связанная с нетерминалом parts). Если не получается распарсить, то делается попытка с удалением символа разыменования в выражении, пока неясно в какой ситуации это требуется. +Еще удаление разыменования присутствует в методе `GetDefinitionPosition` из `CodeCompletionActions`. +Затем делается еще попытка с тем же выражением без скобочек. При этом, если без скобочек распарсить не удается, то вне зависимости от успешности парсинга полного `expression` возвращается `null`. В конце проверяется был ли успешен обход дерева программы визитором (`DomSyntaxTreeVisitor`). Если это так, то в словаре откомпилированных модулей будет данный модуль и соответствующий ему `DomConverter`. -Тогда мы сможем вызвать метод `GetDescription` из этого класса, который выдаст нам описание выражения. +Тогда есть возможность вызвать метод `GetDescription` из этого класса, который выдаст нам описание выражения. #### Подсказка параметров функции На нажатие клавиш (скобки, квадратной скобки или запятой) реагирует `TextAreaKeyEventHandler` из класса `CodeCompletionKeyHandler`. Там создается экземпляр `DefaultInsightDataProvider`, который загружается в стек провайдеров. Информация для подсказки формируется в методе `SetupDataProvider` инсайт-провайдера. После выяснения кода на текущей подстроке вызывается `FindExpressionForMethod` из `LanguageInformation`. Далее делается попытка парсинга стандартным парсером языка и наконец из `DomConverter` вызывается `GetNameOfMethod`, либо `GetIndex` в зависимости от нажатой клавиши. -#### Подсказка по нажатию клавиш (по точке или пробелу) +#### Подсказка по нажатию клавиш `CodeCompletionKeyHandler` реагирует на нажатие вызовом метода `ShowCompletionWindow`. В нем есть обращение к `GenerateCompletionDataWithKeyword` из класса `CodeCompletionProvider`. В этом методе выделяется нужная подстрока и есть обращение к методу `GetCompletionData`. **Логика этого метода следующая:** -1) если был нажат `ctrl + space` (`shift + space` входит в это понятие тоже) -Вызывается `FindPattern` из `LanguageInformation` для получения "токена" до каретки. Если в процессе встретится точка, то вызывается `FindExpression`. Ниже если pattern пустой, то в итоговый список `ICompletionData` добавляются ключевые слова языка. +Вначале выясняется контекст вызова Intellisense, а именно нажатые пользователем клавиши и т. п. Далее вызывается специальный метод для получения текста выражения, введенного пользователем. При этом +1) если был нажат `ctrl + space` или `shift + space` +Вызывается `FindPattern` из `LanguageInformation` для получения "токена" до курсора. Если в процессе встретится точка, то вызывается `FindExpression`. 2) если был нажат пробел после `new` Вызывается SkipNew для пропуска `new` и поиска expression перед ним. -3) иначе, если не было введено `uses` +3) иначе, если пользователем была введена точка вызывается `FindExpression` (c текущей позиции) -Далее, если [**не** `ctrl + space` или была найдена цепочка имен в выражении] и выражение не пусто, то делается попытка вызова функции `GetTypeAsExpression` стандартного парсера. Если это ничего не дало далее попытка вызова `GetExpression`. Если снова ничего не удалось и мы не в случае пробела после `new`, то возврат. +Ниже если был нажат `ctrl + space` не после точки и pattern пустой то в итоговый список `ICompletionData` добавляются ключевые слова языка. -Далее, если Intellisense успешно не обошла текущий модуль, то для случая `uses` мы будем рассматривать SymInfo стандартных модулей (взятых из DomConverter). Если же у нас не `uses`случай и не случай ctrl + space, мы возвращаем пустой массив. +Далее, если выражение не пусто, то строится дерево требуемого expression (это не требуется для `ctrl + space` и `shift + space`, кроме случая, когда они нажаты в цепочечном выражении с точками). Внутри метода построения дерева делается попытка вызова функции `GetTypeAsExpression`, а затем `GetExpression`. Если ничего не удалось построить и мы не в случае пробела после `new`, то нужно выходить из внешнего метода `GetCompletionData`. + +Далее получаем необходимую информацию о выражениях для нужного expression из таблицы символов (в специальном методе). Если визитор Intellisense успешно не обошел текущий модуль (тогда мы не получим соотв. экземпляр `DomConverter`), то + - Если у нас не случай `ctrl + space` или `shift + space` , нужно вернуть пустой массив `ICompletionData`. + - Для случая `uses` сразу добавляем массив стандартных модулей языка Иначе, если у нас есть `DomConverter`: - Если пробел после `new`, то вызываем `GetTypes` - Иначе, если `uses`, то работаем со списком стандартных модулей + пр-в имен в случае семантического Intellisense. - - Иначе, если не `ctrl + space` (случай, когда мы поставили точку), то вызываем `GetName` для получения всех возможных имен после точки - - Иначе, если `ctrl + space`, то либо ищем имена после точки - `GetName` (если точка была раньше), либо ищем имя по его началу - `GetNameByPattern` + - Иначе, если была нажата точка, либо `ctrl + space` или `shift + space` сразу после точки, то вызываем `GetName` для получения всех возможных имен после точки + - Иначе, если `ctrl + space`, то либо ищем имена, подходящие для помещения после точки, начинающиеся на `pattern` - `GetName` + `StartsWith`, либо просто ищем имя по его началу - `GetNameByPattern` -> [!NOTE] Замечание ->Также на нажатие реагирует Execute из CodeCompletionAllNamesAction. key подменяется на доллар если это было нажатие `ctrl + space` сразу после точки. - В конце по полученным SymInfo строится результирующий список CompletionData (без повторений). +> [!NOTE] Замечание +>Случаи обработки `ctrl + space` и `shift + space` перед вызовом `GetCompletionData` обрабатываются не в классе `CodeCompletionKeyHandler`, а в отдельных классах файла CodeCompletionActions. У `ctrl + space` класс называется `CodeCompletionAllNamesAction`, а у `shift + space` - `CodeCompletionShiftSpaceActions`. При этом для `shift + space` в вышеупомянутом классе предварительно проверяются еще два сценария его применения помимо подсказки локальных имен, реализованной в `GetCompletionData`. Эти сценарии - это автодополнение xml-комментария и автодополнение выражений из файла template.pct языка. + +#### Подсказка по первому символу выражения +Данная ситуация обрабатывается в последней ветке метода `TextAreaKeyEventHandler` класса `CodeCompletionKeyHandler`. Оттуда цепочка вызовов приходит к методу `GetCompletionDataByFirst`. В этом методе мы так же как в `GetCompletionData` вначале выясняем контекст, а именно должен ли в данной позиции быть введен тип данных, либо мы находимся в секции "uses". Если мы не в "uses", то можно добавить в результирующий список подсказок нужные ключевые слова. + +Далее получаем нужную информацию из таблицы символов в специальном методе по следующим правилам: +Если `DomConverter` данного модуля получить невозможно, то в случае "uses" можно добавить список стандартных модулей. +Если же `DomConverter` получен, то + - если нужно подсказать типы данных вызывается `GetTypeByPattern` + - если секция "uses", то либо вызывается `GetNamespaces` когда включен семантический Intellisense, либо просто добавляется список стандартных модулей + - во всех остальных ситуациях вызывается `GetNameByPattern`, причем ему передается пустой `pattern` + +После этого в специальном методе по полученным SymInfo строим результирующий список подсказок без повторений. При этом назначается значение `defaultCompletionElement` (подсказки, которая выбирается по умолчанию). Это будет либо прошлый выбранный вариант подсказки, либо если такого не найдется, то минимальный элемент по длине. #### Переход к определению символа -В `CodeCompletionActoins` есть специальный класс GotoAction, в котором вызывается метод `GoToDefinition`. В этом методе используется метод GetDefinitionPosition, который в свою очередь (тоже косвенно) использует `FindExpressionFromAnyPosition` из `LanguageInformation` и `GetDefinition` из `CodeCompletionProvider`. Последний вышеупомянутый метод является оберткой на `GetDefinition` из DomConverter. \ No newline at end of file +В `CodeCompletionActions` есть специальный класс `GotoAction`, в котором вызывается метод `GoToDefinition`. В этом методе используется метод `GetDefinitionPosition`, который в свою очередь (тоже косвенно) использует `FindExpressionFromAnyPosition` из `LanguageInformation` и `GetDefinition` из `CodeCompletionProvider`. Последний вышеупомянутый метод является оберткой над `GetDefinition` из `DomConverter`. \ No newline at end of file