private void BtClearAllErrorsOnButtonPressed(object sender, EventArgs buttonPressedEventArgs) { OpenedFilesInfo.ClearAllErrors(Npp.CurrentFileInfo.Path); Sci.GrabFocus(); }
/// <summary> /// This function handles the display of the autocompletion form, create or update it /// </summary> private static void ShowSuggestionList(AutoCompletionForm form) { // we changed the list of items to display if (_needToSetItems) { DoInLock(() => { form.SetItems(CurrentItems.Cast <ListItem>().ToList()); }); _needToSetItems = false; } // only activate certain types if (_needToSetActiveTypes) { switch (CurrentActiveTypes) { case ActiveTypes.All: form.SetUnactiveType(new List <int> { (int)CompletionType.KeywordObject }); break; case ActiveTypes.KeywordObject: form.SetActiveType(new List <int> { (int)CompletionType.KeywordObject }); break; default: form.SetUnactiveType(null); break; } } // the filter uses the current caret line to know which item should be filtered, set it here int nppCurrentLine = Sci.Line.CurrentLine; CompletionFilterClass.Instance.UpdateConditions(nppCurrentLine); // filter with keyword (keyword can be empty) form.SetFilterString(_currentWord); // close if the list ends up empty after the filter if (!_openedFromShortCut && Config.Instance.AutoCompleteOnKeyInputHideIfEmpty && form.GetNbItems() == 0) { Cloak(); return; } // if the form was already visible, don't go further if (IsVisible) { return; } // update form position var lineHeight = Sci.TextHeight(nppCurrentLine); var point = Sci.GetCaretScreenLocation(); point.Y += lineHeight; form.SetPosition(point, lineHeight + 2, WinApi.GetWindowRect(Npp.CurrentSci.Handle)); form.UnCloak(); form.SetSelectedIndex(0); form.SetCaseMode(FilterCaseMode); _shownPosition = Sci.CurrentPosition; }
/// <summary> /// returns the number of chars between two lines in the current document /// </summary> private static int NbExtraCharBetweenLines(int startLine, int endLine) { return((Sci.StartBytePosOfLine(endLine) - Sci.StartBytePosOfLine(startLine)) - Config.Instance.GlobalMaxNbCharInBlock); }
public override string ToString() { var toDisplay = new StringBuilder(); toDisplay.Append(HtmlHelper.FormatRow("Type of keyword", HtmlHelper.FormatSubString(SubText))); // for abbreviations, find the complete keyword first string keyword = DisplayText; if (!string.IsNullOrEmpty(_fullWord)) { toDisplay.Append(HtmlHelper.FormatRow("Abbreviation of", HtmlHelper.FormatSubString(FullWord))); } string keyToFind = string.Join(" ", DisplayText, KeywordType); // for the keywords define and create, we try to match the second keyword that goes with it if (KeywordType == KeywordType.Statement && (keyword.EqualsCi("define") || keyword.EqualsCi("create"))) { var lineStr = Sci.GetLine(Sci.LineFromPosition(Sci.GetPositionFromMouseLocation())).LineText; var listOfSecWords = new List <string> { "ALIAS", "BROWSE", "BUFFER", "BUTTON", "CALL", "CLIENT-PRINCIPAL", "DATA-SOURCE", "DATABASE", "DATASET", "EVENT", "FRAME", "IMAGE", "MENU", "PARAMETER", "PROPERTY", "QUERY", "RECTANGLE", "SAX-ATTRIBUTES", "SAX-READER", "SAX-WRITER", "SERVER", "SERVER-SOCKET", "SOAP-HEADER", "SOAP-HEADER-ENTRYREF", "SOCKET", "STREAM", "SUB-MENU", "TEMP-TABLE", "VARIABLE", "WIDGET-POOL", "WORK-TABLE", "WORKFILE", "X-DOCUMENT", "X-NODEREF" }; foreach (var word in listOfSecWords) { if (lineStr.ContainsFast(word)) { keyToFind = string.Join(" ", keyword, word, KeywordType); break; } } } var dataHelp = Keywords.Instance.GetKeywordHelp(keyToFind); if (dataHelp != null) { toDisplay.Append(HtmlHelper.FormatSubtitle("DESCRIPTION")); toDisplay.Append(dataHelp.Description); // synthax if (dataHelp.Synthax.Count >= 1 && !string.IsNullOrEmpty(dataHelp.Synthax[0])) { toDisplay.Append(HtmlHelper.FormatSubtitle("SYNTAX")); toDisplay.Append(@"<div class='ToolTipcodeSnippet'>"); var i = 0; foreach (var synthax in dataHelp.Synthax) { if (i > 0) { toDisplay.Append(@"<br>"); } toDisplay.Append(synthax); i++; } toDisplay.Append(@"</div>"); } } else { toDisplay.Append(HtmlHelper.FormatSubtitle("404 NOT FOUND")); if (KeywordType == KeywordType.Option) { toDisplay.Append("<i><b>Sorry, this keyword doesn't have any help associated</b><br>Since this keyword is an option, try to hover the first keyword of the statement or refer to the 4GL help</i>"); } else { toDisplay.Append("<i><b>Sorry, this keyword doesn't have any help associated</b><br>Please refer to the 4GL help</i>"); } } return(toDisplay.ToString()); }
/// <summary> /// Updates the CURRENT ITEMS LIST, /// handles the opening or the closing of the auto completion form on key input /// (externally called when a new char is entered or when a char is deleted) /// </summary> public static void UpdateAutocompletion(char c = char.MinValue, int insertPosition = -1) { if (_insertingWord) { return; } var typing = IsCharPartOfWord(c); var isVisible = IsVisible; // currently continuing to type a word in a visible auto completion, return asap if (typing && isVisible) { // the auto completion is already visible, this means the _currentWord is set // we only have to filter the current list even more ShowSuggestionList(_currentWord + c); return; } var nppCurrentPosition = Sci.CurrentPosition; var nppCurrentLine = Sci.Line.CurrentLine; var isNormalContext = SyntaxHighlight.IsCarretInNormalContext(nppCurrentPosition); string strOnLeft = null; //---------------------- // we finished entering a word (we typed a char that is not part of a word, a space of new line or separator...) //---------------------- if (c != char.MinValue && !typing && isNormalContext) { strOnLeft = Sci.GetTextOnLeftOfPos(nppCurrentPosition, 61); var strOnLeftLength = strOnLeft.Length; // we finished entering a word, find the offset at which we can find said word int offset = 1; if (c == '\r' || c == '\n') { offset = nppCurrentPosition - insertPosition; if (offset > 40) { // case of an extremely hard tabbed line strOnLeft = Sci.GetTextOnLeftOfPos(nppCurrentPosition - offset, 61); } } bool textHasChanged = false; if (offset > 0 && strOnLeftLength > offset) { bool hasAtLeastOneLetter = false; bool hasAtLeastOneDigit = false; var checkOffset = offset; while (strOnLeftLength - 1 - checkOffset > 0 && IsCharPartOfWord(strOnLeft[strOnLeftLength - 1 - checkOffset])) { var ch = strOnLeft[strOnLeftLength - 1 - checkOffset]; if (char.IsDigit(ch)) { hasAtLeastOneDigit = true; } if (char.IsLetter(ch)) { hasAtLeastOneLetter = true; break; } checkOffset++; } // See if the char of the "word" we finished entering is actually part of a word // (maybe not if, for instance, we just input 2 spaces consecutively) if (hasAtLeastOneLetter || (hasAtLeastOneDigit && !Npp.CurrentFileInfo.IsProgress && !Config.Instance.NppAutoCompleteIgnoreNumbers)) { // automatically insert selected keyword of the completion list? if (InsertSelectedSuggestionOnWordEnd && isVisible) { InsertSuggestion(_form.GetCurrentCompletionItem(), -offset); textHasChanged = true; } // automatically change the case of the keyword? else if (AutoCase && (nppCurrentPosition - offset) != _positionOfLastInsertion) { var candidates = FindInCompletionData(strOnLeft.Substring(0, strOnLeftLength - offset), nppCurrentLine); // we matched the word in the list, correct the case if (candidates != null && candidates.Count > 0) { InsertSuggestion(candidates.First(), -offset); textHasChanged = true; } } } } // replace semicolon by a point if (c == ';' && Config.Instance.AutoCompleteReplaceSemicolon && Npp.CurrentFileInfo.IsProgress) { _insertingWord = true; _positionOfLastInsertion = Sci.ModifyTextAroundCaret(-1, 0, "."); _insertingWord = false; textHasChanged = true; } // need to update the strOnLeft since we use it again in this method if (textHasChanged) { nppCurrentPosition = Sci.CurrentPosition; strOnLeft = Sci.GetTextOnLeftOfPos(nppCurrentPosition, 61); } } //---------------------- // We are here if the auto completion is hidden or if the user is not continuing to type a word, // So we have the opportunity to change the list of items in the auto completion if needed //---------------------- if (!_openedFromShortCut) { // don't automatically show the auto completion on input? if (!Config.Instance.AutoCompleteOnKeyInputShowSuggestions) { return; } // don't show in string/comments..? if (!isVisible && !isNormalContext && !Config.Instance.AutoCompleteShowInCommentsAndStrings) { return; } } else { // the caret changed line (happens when we trigger the auto comp manually on the first position of a line // and we press backspace, we return to the previous line but the form would still be visible) if (isVisible && nppCurrentLine != _openedFromShortcutLine) { Cloak(); return; } } // get current word if (strOnLeft == null) { strOnLeft = Sci.GetTextOnLeftOfPos(nppCurrentPosition, 61); } int charPos = 0; char?firstSeparator; var firstKeyword = GetWord(strOnLeft, ref charPos, out firstSeparator); if (typing) { // (form is not visible if we are here) Min length is not reached? if (!_openedFromShortCut && firstKeyword.Length < Config.Instance.AutoCompleteStartShowingListAfterXChar) { return; } } if (firstSeparator == null) { // we didn't match a known separator just before the keyword; // this means we want to display the entire list of keywords if (CurrentActiveTypes != ActiveTypes.All) { CurrentActiveTypes = ActiveTypes.All; DoInLock(() => { CurrentItems = _savedAllItems; }); } } else { // return the list of children that should be used in the auto completion, filtered by the previous keywords List <CompletionItem> outList = null; DoInLock(() => { var filterClass = new CompletionFilterClass(); filterClass.UpdateConditions(nppCurrentLine); outList = GetWordsList(_savedAllItems.Where(filterClass.FilterPredicate), strOnLeft, charPos, firstSeparator).ToList(); }); // empty list? if (outList == null || outList.Count == 0) { // if the current word is directly preceded by a :, we are entering an object field/method // for now, we then display the whole list of object keywords if (firstSeparator == ':' && Npp.CurrentFileInfo.IsProgress) { if (CurrentActiveTypes != ActiveTypes.KeywordObject) { CurrentActiveTypes = ActiveTypes.KeywordObject; DoInLock(() => { CurrentItems = _savedAllItems; }); } ShowSuggestionList(firstKeyword); return; } // we should consider the first separator found not as a child separator, display the whole list if (CurrentActiveTypes != ActiveTypes.All) { CurrentActiveTypes = ActiveTypes.All; DoInLock(() => { CurrentItems = _savedAllItems; }); } } else { // we will display the list filtered with all the needed children CurrentActiveTypes = ActiveTypes.Filtered; DoInLock(() => { CurrentItems = outList.ToList(); CurrentItems.Sort(CompletionSortingClass <CompletionItem> .Instance); }); // we want to show the list no matter how long the filter keyword if (Config.Instance.AutoCompleteShowChildrenAfterSeparator) { ShowSuggestionList(firstKeyword); return; } } } // check if the minimum length of word is ok if (isVisible) { // close if (we didn't open from shortcut or we are not at the opening position) and the min length is not reached if ((!_openedFromShortCut || nppCurrentPosition < _shownPosition) && firstKeyword.Length < Config.Instance.AutoCompleteStartShowingListAfterXChar) { Cloak(); return; } // auto completion from shortcut at the end of a word and we press space, we need to close it! if (_openedFromShortCut && nppCurrentPosition > _shownPosition && c != char.MinValue) { Cloak(); return; } } else { // Min length is not reached? if (!_openedFromShortCut && firstKeyword.Length < Config.Instance.AutoCompleteStartShowingListAfterXChar) { return; } } ShowSuggestionList(firstKeyword); }
static public bool NavigateToNextParam() { var indic = Sci.GetIndicator(SnippetContext.IndicatorId); indic.IndicatorStyle = IndicatorStyle.Box; indic.ForeColor = Color.Blue; var indicators = indic.FindRanges().ToArray(); if (!indicators.Any()) { return(false); } if (LocSnippetContext.CurrentParameter != null) { Point currentParam = LocSnippetContext.CurrentParameter.Value; string currentParamOriginalText = LocSnippetContext.CurrentParameterValue; Sci.SetSelection(currentParam.X, currentParam.X); string currentParamDetectedText = Sci.GetWordAtPosition(Sci.CurrentPosition, AutoCompletion.CurrentLangAdditionalChars); if (currentParamOriginalText != currentParamDetectedText) { //current parameter is modified, indicator is destroyed so restore the indicator first indic.Add(currentParam.X, currentParam.X + currentParamDetectedText.Length); indicators = indic.FindRanges().ToArray(); //needs refreshing as the document is modified var paramsInfo = indicators.Select(p => new { Index = indicators.IndexOf(p), Text = Sci.GetTextBetween(p), Range = p, Pos = p.X }) .OrderBy(x => x.Pos) .ToArray(); var paramsToUpdate = paramsInfo.Where(item => item.Text == currentParamOriginalText).ToArray(); foreach (var param in paramsToUpdate) { ReplaceTextAtIndicator(currentParamDetectedText, indicators[param.Index]); indicators = indic.FindRanges().ToArray(); //needs refreshing as the document is modified } } Point?nextParameter = null; int currentParamIndex = indicators.FindIndex(x => x.X >= currentParam.X); //can also be logical 'next' var prevParamsValues = indicators.Take(currentParamIndex).Select(p => Sci.GetTextBetween(p)).ToList(); prevParamsValues.Add(currentParamOriginalText); prevParamsValues.Add(currentParamDetectedText); prevParamsValues.Add(" "); prevParamsValues.Add("|"); foreach (var range in indicators.ToArray()) { if (currentParam.X < range.X && !prevParamsValues.Contains(Sci.GetTextBetween(range))) { nextParameter = range; break; } } if (!nextParameter.HasValue) { nextParameter = indicators.FirstOrDefault(); } LocSnippetContext.CurrentParameter = nextParameter; } if (LocSnippetContext.CurrentParameter.HasValue) { Sci.SetSelection(LocSnippetContext.CurrentParameter.Value.X, LocSnippetContext.CurrentParameter.Value.Y); LocSnippetContext.CurrentParameterValue = Sci.GetTextBetween(LocSnippetContext.CurrentParameter.Value); } return(true); }
public void OrganizeImportsFixtureSetUp() { // Needed for preprocessor directives... Sci.SetProperty("fold", "1"); Sci.SetProperty("fold.preprocessor", "1"); }
internal static void DoPlugStart() { if (OnPlugReady != null) { OnPlugReady(); } ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12; // subscribe to static events ProEnvironment.OnEnvironmentChange += FileExplorer.Instance.RebuildFileList; ProEnvironment.OnEnvironmentChange += DataBase.Instance.UpdateDatabaseInfo; ProEnvironment.OnEnvironmentChange += ParserHandler.ClearStaticData; Keywords.Instance.OnImport += AutoCompletion.SetStaticItems; DataBase.Instance.OnDatabaseUpdate += AutoCompletion.SetStaticItems; AutoCompletion.OnUpdateStaticItems += ParserHandler.UpdateKnownStaticItems; ParserHandler.OnStart += CodeExplorer.Instance.OnStart; ParserHandler.OnEndSendCompletionItems += AutoCompletion.SetDynamicItems; ParserHandler.OnEndSendParserItems += CodeExplorer.Instance.OnParseEndParserItems; ParserHandler.OnEndSendParserItems += SyntaxFolding.OnParseEndParserItems; ParserHandler.OnEndSendCodeExplorerItems += CodeExplorer.Instance.OnParseEndCodeExplorerItems; ParserHandler.OnEnd += CodeExplorer.Instance.OnParseEnd; ProExecutionHandleCompilation.OnEachCompilationOk += OpenedFilesInfo.ProExecutionHandleCompilationOnEachCompilationOk; // Clear the %temp% directory if we didn't do it properly last time Utils.DeleteDirectory(Config.FolderTemp, true); //Snippets.Init(); FileCustomInfo.Import(); DelayedAction.StartNew(100, () => { if (Config.Instance.InstallStep == 0) { Config.Instance.InstallStep++; // we are at the first notepad++ start Npp.ConfXml.FinishPluginInstall(); // will apply npp options and restart npp return; } if (Config.Instance.InstallStep == 1) { Config.Instance.InstallStep++; // global options applied, we are at the second startup UserCommunication.NotifyUnique("welcome", "Thank you for installing 3P, you are awesome!<br><br>If this is your first look at 3P you should probably read the <b>getting started</b> section of the home page by clicking " + "go".ToHtmlLink("on this link right here") + ".<br><br><div align='right'>And as always... Enjoy!</div>", MessageImg.MsgInfo, "Fresh install", "Hello and welcome aboard!", args => { Appli.ToggleView(); UserCommunication.CloseUniqueNotif("welcome"); args.Handled = true; }); } else if (!Config.Instance.NppStoppedCorrectly) { // Npp didn't stop correctly, if the backup mode is activated, inform the user if (Npp.ConfXml.BackupMode > 0) { UserCommunication.Notify("It seems that notepad++ didn't stop correctly.<br>If you lost some modifications, don't forget that you have a backup folder here :<br><br><div>" + Npp.ConfXml.BackupDirectory.ToHtmlLink() + "</div>" + (Npp.ConfXml.BackupUseCustomDir ? "<div>" + Npp.ConfXml.CustomBackupDirectory.ToHtmlLink() + "</div>" : ""), MessageImg.MsgInfo, "Notepad++ crashed", "Backup folder location"); } } Config.Instance.NppStoppedCorrectly = false; // check if an update was done and start checking for new updates Updater <MainUpdaterWrapper> .Instance.CheckForUpdateDoneAndStartCheckingForUpdates(); if (Updater <ProlintUpdaterWrapper> .Instance.LocalVersion.IsHigherVersionThan("v0")) { Updater <ProlintUpdaterWrapper> .Instance.StartCheckingForUpdate(); } if (Updater <ProparseUpdaterWrapper> .Instance.LocalVersion.IsHigherVersionThan("v0")) { Updater <ProparseUpdaterWrapper> .Instance.StartCheckingForUpdate(); } // Try to update the configuration from the distant shared folder ShareExportConf.StartCheckingForUpdates(); }); // check if npp version is meeting current recommended version if (!string.IsNullOrEmpty(Npp.SoftwareStringVersion) && !Npp.SoftwareStringVersion.IsHigherOrEqualVersionThan(Config.RequiredNppVersion)) { if (!Config.Instance.NppOutdatedVersion) { UserCommunication.Notify("This version of 3P has been developed for Notepad++ " + Config.RequiredNppVersion + ", your version (" + Npp.SoftwareStringVersion + ") is outdated.<br><br>Using an outdated version, you might encounter bugs that would not occur otherwise.<br>Try to update your version of Notepad++ as soon as possible, or use 3P at your own risks.<br><br><a href='https://notepad-plus-plus.org/download/'>Download the latest version of Notepad++ here</a>", MessageImg.MsgHighImportance, "Outdated version", "3P requirements are not met"); Config.Instance.NppOutdatedVersion = true; } } else { Config.Instance.NppOutdatedVersion = false; } // ReSharper disable once ObjectCreationAsStatement RecurentAction.StartNew(User.Ping, 1000 * 60 * 120); // Make sure to give the focus to scintilla on startup Sci.GrabFocus(); DelayedAction.StartNew(1000, Config.Save); }
/// <summary> /// handles the notifications send by npp and scintilla to the plugin /// </summary> public static void OnNppNotification(SCNotification nc) { try { uint code = nc.nmhdr.code; // Plugin waiting to be started... if (!PluginIsReady) { switch (code) { case (uint)NppNotif.NPPN_TBMODIFICATION: // this is the event that we want to respond to, it sets the toolbar icons UnmanagedExports.NppFuncItems.RefreshItems(); Plug.DoNppNeedToolbarImages(); return; case (uint)NppNotif.NPPN_READY: // notify plugins that all the procedures of launch of notepad are done ActionsAfterUpdateUi = new Queue <Action>(); Npp.UpdateCurrentSci(); // init current scintilla UiThread.Init(); PluginIsReady = Plug.DoNppReady(); // call OnNppReady then OnPlugReady if it all went ok if (PluginIsReady) { Plug.DoPlugStart(); OnNppNotification(new SCNotification((uint)NppNotif.NPPN_BUFFERACTIVATED)); // simulate buffer activated // set hooks on mouse/keyboard SetHooks(); } return; case (uint)NppNotif.NPPN_SHUTDOWN: // uninstall hooks on mouse/keyboard UninstallHooks(); UiThread.Close(); Plug.DoNppShutDown(); return; case (uint)NppNotif.NPPN_CANCELSHUTDOWN: PluginIsReady = true; return; } } else { // the plugin is fully loaded and ready to do stuff if ((uint)SciNotif.SCN_NOTIF_BEGIN < code && code < (uint)SciNotif.SCN_NOTIF_END) { switch (code) { // -------------------------------------------------------- // Scintilla message // -------------------------------------------------------- case (uint)SciNotif.SCN_CHARADDED: // called each time the user add a char in the current scintilla // It's actually better to use the SCI_MODIFIED instead, this notification // is not always called when it should! (ex not called for /t) return; case (uint)SciNotif.SCN_UPDATEUI: while (ActionsAfterUpdateUi.Any()) { ActionsAfterUpdateUi.Dequeue()(); } Plug.OnSciUpdateUi(nc); return; case (uint)SciNotif.SCN_MODIFIED: // This notification is sent when the text or styling of the document changes or is about to change // (note : this notif isn't sent when the user SWITCHES to tab file (already opened in another tab) ! // But it is sent when the user opens a NEW file) bool deletedText = (nc.modificationType & (int)SciModificationMod.SC_MOD_DELETETEXT) != 0; bool insertedText = (nc.modificationType & (int)SciModificationMod.SC_MOD_INSERTTEXT) != 0; bool undo = (nc.modificationType & (int)SciModificationMod.SC_PERFORMED_UNDO) != 0; bool redo = (nc.modificationType & (int)SciModificationMod.SC_PERFORMED_REDO) != 0; bool singleCharModification = false; if ((insertedText || deletedText) && !ScnModifiedDisabled) { var encoding = Sci.Encoding; Npp.CurrentSci.Lines.OnScnModified(nc, !deletedText, encoding); // register line modifications if (!undo && !redo) { // if the text has changed unsafe { var nbCarets = Sci.Selection.Count; if (_currentCaret > 0) { _currentCaret++; if (_currentCaret <= nbCarets) { return; } // then it is the first caret again, we can handle the char _currentCaret = 0; } if (nbCarets > 1) { _currentCaret++; } // only 1 char appears to be modified var ncLength = nc.length.ToInt32(); if (ncLength <= 2) { // get the char var bytes = (byte *)nc.text; var arrbyte = new byte[ncLength]; int index; for (index = 0; index < ncLength; index++) { arrbyte[index] = bytes[index]; } var c = encoding.GetChars(arrbyte); var cLength = c.Length; // do we really have a 1 char input? if (cLength == 1 || (cLength == 2 && c[0] == '\r')) { if (insertedText) { ActionsAfterUpdateUi.Enqueue(() => Plug.OnCharAdded(c[0], nc.position.ToInt32())); } else { ActionsAfterUpdateUi.Enqueue(() => Plug.OnCharDeleted(c[0], nc.position.ToInt32())); } singleCharModification = true; } } } } ActionsAfterUpdateUi.Enqueue(() => Plug.OnTextModified(nc, insertedText, deletedText, singleCharModification, undo, redo)); } return; case (uint)SciNotif.SCN_STYLENEEDED: // if we use the contained lexer, we will receive this notification and we will have to style the text Plug.OnStyleNeeded(Sci.GetEndStyled(), nc.position.ToInt32()); return; case (uint)SciNotif.SCN_MARGINCLICK: // called each time the user click on a margin Plug.OnSciMarginClick(nc); return; case (uint)SciNotif.SCN_MODIFYATTEMPTRO: // Code a checkout when trying to modify a read-only file return; case (uint)SciNotif.SCN_DWELLSTART: // when the user hover at a fixed position for too long Plug.OnSciDwellStart(); return; case (uint)SciNotif.SCN_DWELLEND: // when he moves his cursor Plug.OnSciDwellEnd(); return; } } else if ((uint)NppNotif.NPPN_NOTIF_BEGIN < code && code < (uint)NppNotif.NPPN_NOTIF_END) { // -------------------------------------------------------- // Npp message // -------------------------------------------------------- switch (code) { case (uint)NppNotif.NPPN_BUFFERACTIVATED: // the user changes the current document (this event is called when the current document is switched (via the tabs) // and also when a new file is opened in npp Npp.UpdateCurrentSci(); // update current scintilla Npp.CurrentSci.Lines.Reset(); // register new lines NppBufferActivated(); return; case (uint)NppNotif.NPPN_FILERENAMED: // the user can open a .txt and rename it as a .p NppBufferActivated(); return; case (uint)NppNotif.NPPN_FILESAVED: // the user can open a .txt and save it as a .p NppBufferActivated(); Plug.DoNppDocumentSaved(); return; case (uint)NppNotif.NPPN_FILEBEFORELOAD: // fire when a file is opened // When loading a new file into NPP, the events fired are (in order) : // NPPN_FILEBEFORELOAD > SCN_MODIFIED > NPPN_FILEBEFOREOPEN > NPPN_FILEOPENED > NPPN_BUFFERACTIVATED // we deactivate the SCN_MODIFIED between NPPN_FILEBEFORELOAD and NPPN_FILEBEFOREOPEN ScnModifiedDisabled = true; Plug.DoNppFileBeforeLoad(); return; case (uint)NppNotif.NPPN_FILEBEFOREOPEN: ScnModifiedDisabled = false; return; case (uint)NppNotif.NPPN_FILEOPENED: // on file opened Plug.OnNppFileOpened(); return; case (uint)NppNotif.NPPN_FILEBEFORECLOSE: // on file closed Plug.OnNppFileBeforeClose(); return; case (uint)NppNotif.NPPN_LANGCHANGED: // on lang type changed Plug.OnLangChanged(); NppBufferActivated(); return; case (uint)NppNotif.NPPN_WORDSTYLESUPDATED: // The styles have been modified Npp.StylersXml.Reload(); // unfortunatly, if the user changed of styler.xml file (he selected another theme) then we // will incorrectly read the styles since we have to wait for the config.xml to be updated // and it only updates on npp shutdown return; case (uint)NppNotif.NPPN_BEFORESHUTDOWN: // prevent the plugin from handling a lot of events when npp is about to shutdown PluginIsReady = false; return; } } } } catch (Exception e) { ErrorHandler.ShowErrors(e, "Error in beNotified : code = " + nc.nmhdr.code); } }
/// <summary> /// Called after the execution of run/compile/check/prolint /// </summary> public static void OnSingleExecutionOk(ProExecutionHandleCompilation lastExec, List <FileToCompile> filesToCompile, List <FileToDeploy> filesToDeploy) { try { var treatedFile = lastExec.Files.First(); CurrentOperation currentOperation; if (!Enum.TryParse(lastExec.ExecutionType.ToString(), true, out currentOperation)) { currentOperation = CurrentOperation.Run; } var isCurrentFile = treatedFile.SourcePath.EqualsCi(Npp.CurrentFileInfo.Path); var otherFilesInError = false; int nbWarnings = 0; int nbErrors = 0; // count number of warnings/errors, loop through files > loop through errors in each file foreach (var fileInError in filesToCompile.Where(file => file.Errors != null)) { foreach (var error in fileInError.Errors) { if (error.Level <= ErrorLevel.StrongWarning) { nbWarnings++; } else { nbErrors++; } } otherFilesInError = otherFilesInError || !treatedFile.SourcePath.EqualsCi(fileInError.SourcePath); } // Prepare the notification content var notifTitle = currentOperation.GetAttribute <CurrentOperationAttr>().Name; var notifImg = (nbErrors > 0) ? MessageImg.MsgError : ((nbWarnings > 0) ? MessageImg.MsgWarning : MessageImg.MsgOk); var notifTimeOut = (nbErrors > 0) ? 0 : ((nbWarnings > 0) ? 10 : 5); var notifSubtitle = lastExec.ExecutionType == ExecutionType.Prolint ? (nbErrors + nbWarnings) + " problem" + ((nbErrors + nbWarnings) > 1 ? "s" : "") + " detected" : (nbErrors > 0) ? nbErrors + " error" + (nbErrors > 1 ? "s" : "") + " found" : ((nbWarnings > 0) ? nbWarnings + " warning" + (nbWarnings > 1 ? "s" : "") + " found" : "Syntax correct"); // when compiling, transferring .r/.lst to compilation dir if (filesToDeploy != null) { filesToDeploy = lastExec.ProEnv.Deployer.DeployFiles(filesToDeploy, null, null); } // Notify the user, or not if (Config.Instance.CompileAlwaysShowNotification || !isCurrentFile || !Sci.GetFocus() || otherFilesInError) { UserCommunication.NotifyUnique(treatedFile.SourcePath, "<div style='padding-bottom: 5px;'>Was " + currentOperation.GetAttribute <CurrentOperationAttr>().ActionText + " :</div>" + ProExecutionCompile.FormatCompilationResultForSingleFile(treatedFile.SourcePath, treatedFile, filesToDeploy), notifImg, notifTitle, notifSubtitle, null, notifTimeOut); } } catch (Exception e) { ErrorHandler.ShowErrors(e, "Error in OnExecutionOk"); } }
/// <summary> /// Displays the errors for the current file (if any) /// display an annotation with the message below the line + display a marker in the margin /// </summary> public static void UpdateErrorsInScintilla() { // Updates the number of errors in the FileExplorer form and the file status UpdateFileStatus(); var currentFilePath = Npp.CurrentFile.Path; var marginError = Sci.GetMargin(ErrorMarginNumber); // need to clear scintilla for this file? if (_sessionInfo.ContainsKey(currentFilePath) && _sessionInfo[currentFilePath].NeedToCleanScintilla) { ClearAnnotationsAndMarkers(); _sessionInfo[currentFilePath].NeedToCleanScintilla = false; } // check if current file is a progress and if we got info on it if (!Npp.CurrentFile.IsProgress || !_sessionInfo.ContainsKey(currentFilePath) || _sessionInfo[currentFilePath].FileErrors == null || _sessionInfo[currentFilePath].FileErrors.Count == 0) { if (marginError.Width > 0) { marginError.Width = 1; marginError.Width = 0; } // reset annotation to default Sci.AnnotationVisible = Plug.AnnotationMode; return; } // activate annotation (if not already done) Plug.AnnotationMode = Annotation.Indented; // show margin if (marginError.Sensitive == false) { marginError.Sensitive = true; } if (marginError.Type != MarginType.Symbol) { marginError.Type = MarginType.Symbol; } if (marginError.Mask != EveryMarkersMask) { marginError.Mask = EveryMarkersMask; } // only show the new errors if (_sessionInfo[currentFilePath].HasErrorsNotDisplayed) { _sessionInfo[currentFilePath].HasErrorsNotDisplayed = false; StylerHelper stylerHelper = new StylerHelper(); int lastLine = -2; StringBuilder lastMessage = new StringBuilder(); foreach (var fileError in _sessionInfo[currentFilePath].FileErrors) { // new line if (lastLine != fileError.Line) { stylerHelper.Clear(); lastMessage.Clear(); // set marker style now (the first error encountered for a given line is the highest anyway) if (!((int)Sci.GetLine(fileError.Line).MarkerGet()).IsBitSet((int)fileError.Level)) { Sci.GetLine(fileError.Line).MarkerAdd((int)fileError.Level); } } else { // append to existing annotation stylerHelper.Style("\n", (byte)fileError.Level); lastMessage.Append("\n"); } lastLine = fileError.Line; var mess = fileError.FromProlint ? "Prolint (level " + fileError.ErrorNumber : ("Compilation " + (fileError.Level == ErrorLevel.Critical ? "error" : "warning") + " (n°" + fileError.ErrorNumber); mess += fileError.FromProlint ? "): " : ", col " + fileError.Column + "): "; stylerHelper.Style(mess, (byte)(Style.ErrorAnnotBoldStyleOffset + fileError.Level)); lastMessage.Append(mess); mess = fileError.Message.BreakText(140); stylerHelper.Style(mess, (byte)(Style.ErrorAnnotStandardStyleOffset + fileError.Level)); lastMessage.Append(mess); if (Config.Instance.GlobalShowDetailedHelpForErrors && !string.IsNullOrEmpty(fileError.Help)) { mess = "\nDetailed help: " + fileError.Help.BreakText(140); stylerHelper.Style(mess, (byte)(Style.ErrorAnnotItalicStyleOffset + fileError.Level)); lastMessage.Append(mess); } if (fileError.Times > 0) { mess = "\nThis message above appeared " + fileError.Times + " times in the compiler log"; stylerHelper.Style(mess, (byte)(Style.ErrorAnnotBoldStyleOffset + fileError.Level)); lastMessage.Append(mess); } // set annotation Sci.GetLine(lastLine).AnnotationText = lastMessage.ToString(); Sci.GetLine(lastLine).AnnotationStyles = stylerHelper.GetStyleArray(); } } marginError.Width = ErrorMarginWidth + 1; marginError.Width = ErrorMarginWidth; }
/// <summary> /// This method allows the user to GOTO a word definition, if a tooltip is opened then it tries to /// go to the definition of the displayed word, otherwise it tries to find the declaration of the parsed word under the /// caret. At last, it tries to find a file in the propath /// </summary> public static void GoToDefinition(bool fromMouseClick) { // if a tooltip is opened, try to execute the "go to definition" of the tooltip first if (InfoToolTip.InfoToolTip.IsVisible) { if (!string.IsNullOrEmpty(InfoToolTip.InfoToolTip.GoToDefinitionFile)) { Npp.Goto(InfoToolTip.InfoToolTip.GoToDefinitionFile, InfoToolTip.InfoToolTip.GoToDefinitionPoint.X, InfoToolTip.InfoToolTip.GoToDefinitionPoint.Y); InfoToolTip.InfoToolTip.Cloak(); return; } InfoToolTip.InfoToolTip.Cloak(); } // try to go to the definition of the selected word var position = fromMouseClick ? Sci.GetPositionFromMouseLocation() : Sci.CurrentPosition; if (fromMouseClick && position <= 0) { return; } var curWord = Sci.GetWordAtPosition(position, AutoCompletion.CurrentLangAdditionalChars); if (string.IsNullOrEmpty(curWord)) { return; } // match a word in the autocompletion? go to definition var listKeywords = AutoCompletion.FindInCompletionData(curWord, Sci.LineFromPosition(position)); if (listKeywords != null) { var listItems = listKeywords.Where(item => item.FromParser && item.ParsedBaseItem is ParsedItem).ToList(); if (listItems.Count > 0) { // only one match, then go to the definition if (listItems.Count == 1) { var pItem = listItems.First().ParsedBaseItem as ParsedItem; if (pItem != null) { Npp.Goto(pItem.FilePath, pItem.Line, pItem.Column); return; } } if (listItems.Count > 1) { // otherwise, list the items and notify the user var output = new StringBuilder(@"Found several matching items, please choose the correct one :<br>"); foreach (var cData in listItems) { var pItem = listItems.First().ParsedBaseItem as ParsedItem; if (pItem != null) { output.Append("<div>" + (pItem.FilePath + "|" + pItem.Line + "|" + pItem.Column).ToHtmlLink("In " + Path.GetFileName(pItem.FilePath) + " (line " + pItem.Line + ")")); cData.DoForEachFlag((s, flag) => { output.Append("<img style='padding-right: 0px; padding-left: 5px;' src='" + s + "' height='15px'>"); }); output.Append("</div>"); } } UserCommunication.NotifyUnique("GoToDefinition", output.ToString(), MessageImg.MsgQuestion, "Question", "Go to the definition", args => { Utils.OpenPathClickHandler(null, args); UserCommunication.CloseUniqueNotif("GoToDefinition"); }, 0, 500); return; } } } // last resort, try to find a matching file in the propath // if in a string, read the whole string // try to read all the . and \ // first look in the propath var fullPaths = ProEnvironment.Current.FindFiles(curWord, Config.Instance.FilesPatternProgress.Replace("*", "")); if (fullPaths.Count > 0) { if (fullPaths.Count > 1) { var output = new StringBuilder(@"Found several files matching this name, please choose the correct one :<br>"); foreach (var fullPath in fullPaths) { output.Append("<div>" + fullPath.ToHtmlLink() + "</div>"); } UserCommunication.NotifyUnique("GoToDefinition", output.ToString(), MessageImg.MsgQuestion, "Question", "Open a file", args => { Npp.Goto(args.Link); UserCommunication.CloseUniqueNotif("GoToDefinition"); args.Handled = true; }, 0, 500); } else { Npp.Goto(fullPaths[0]); } return; } UserCommunication.Notify("Sorry, couldn't go to the definition of <b>" + curWord + "</b>", MessageImg.MsgInfo, "Information", "Failed to find an origin", 5); }
/// <summary> /// Method called when the tooltip is opened from the mouse being inactive on scintilla /// </summary> public static void ShowToolTipFromDwell(bool openTemporary = true) { if (Config.Instance.ToolTipDeactivate) { return; } InitIfneeded(); var position = Sci.GetPositionFromMouseLocation(); if (position < 0) { return; } // check caret context, dont display a tooltip for comments var curContext = (SciStyleId)Sci.GetStyleAt(position); if (curContext == SciStyleId.Comment) { return; } // sets the tooltip content var data = AutoCompletion.FindInCompletionData(Sci.GetWordAtPosition(position, AutoCompletion.CurrentLangAllChars, AutoCompletion.CurrentLangAdditionalChars), Sci.LineFromPosition(position)); if (data != null && data.Count > 0) { _currentCompletionList = data; } else { return; } // in strings, only functions trigger the tooltip if ((curContext == SciStyleId.DoubleQuote || curContext == SciStyleId.SimpleQuote) && _currentCompletionList.First().Type != CompletionType.Function) { return; } SetToolTip(); // update position var point = Sci.GetPointXyFromPosition(position); point.Offset(Sci.GetScintillaRectangle().Location); var lineHeight = Sci.TextHeight(Sci.Line.CurrentLine); point.Y += lineHeight + 5; _form.Location = _form.GetBestAutocompPosition(point, lineHeight + 5); _openedFromDwell = openTemporary; if (!_form.Visible) { _form.UnCloak(); } }
/// <summary> /// Call this method to insert a new piece of code /// </summary> public void InsertCode <T>() where T : ParsedScopeItem { IProCode codeCode; string insertText; string blockDescription; // in case of an incorrect document, warn the user var parserErrors = _parser.ParseErrorsInHtml; if (!string.IsNullOrEmpty(parserErrors)) { if (UserCommunication.Message("The internal parser of 3P has found inconsistencies in your document :<br>" + parserErrors + "<br>You can still insert a new piece of code but the insertion position might not be calculated correctly; take caution of what is generated if you decide to go through with it.", MessageImg.MsgQuestion, "Generate code", "Problems spotted", new List <string> { "Continue", "Abort" }) != 0) { return; } } if (typeof(ParsedImplementation) == typeof(T)) { object input = new ProCodeFunction(); if (UserCommunication.Input(ref input, "Please provide information about the procedure that will be created", MessageImg.MsgQuestion, "Generate code", "Insert a new function") != 0) { return; } codeCode = (IProCode)input; codeCode.Name = codeCode.Name.MakeValidVariableName(); blockDescription = @"_FUNCTION " + codeCode.Name + " Procedure"; insertText = Encoding.Default.GetString(DataResources.FunctionImplementation).Trim(); insertText = insertText.Replace("{&type}", ((ProCodeFunction)codeCode).Type); insertText = insertText.Replace("{&private}", ((ProCodeFunction)codeCode).IsPrivate ? " PRIVATE" : ""); } else if (typeof(ParsedProcedure) == typeof(T)) { object input = new ProCodeProcedure(); if (UserCommunication.Input(ref input, "Please provide information about the procedure that will be created", MessageImg.MsgQuestion, "Generate code", "Insert a new procedure") != 0) { return; } codeCode = (IProCode)input; blockDescription = @"_PROCEDURE " + codeCode.Name + " Procedure"; insertText = Encoding.Default.GetString(DataResources.InternalProcedure).Trim(); insertText = insertText.Replace("{&private}", ((ProCodeProcedure)codeCode).IsPrivate ? " PRIVATE" : ""); } else { return; } if (string.IsNullOrEmpty(codeCode.Name)) { return; } // check if the code already exists if (_parsedItems.Exists(item => item.GetType() == typeof(T) && item.Name.EqualsCi(codeCode.Name))) { UserCommunication.Notify("Sorry, this name is already taken by another existing instance", MessageImg.MsgHighImportance, "Invalid name", "Existing name", 5); return; } insertText = insertText.Replace("{&name}", codeCode.Name); // reposition caret and insert bool insertBefore; int insertPos = GetCaretPositionForInsertion <T>(codeCode.Name, codeCode.InsertPosition, out insertBefore); if (insertPos < 0) { insertPos = Sci.GetPosFromLineColumn(Sci.Line.CurrentLine, 0); } insertText = FormatInsertion(insertText, blockDescription, insertBefore); int internalCaretPos = insertText.IndexOf("|||", StringComparison.Ordinal); insertText = insertText.Replace("|||", ""); Sci.SetSelection(insertPos); Sci.ModifyTextAroundCaret(0, 0, insertText); Sci.GoToLine(Sci.LineFromPosition(insertPos)); Sci.GotoPosition(insertPos + (internalCaretPos > 0 ? internalCaretPos : 0)); // in the case of a new function, update the prototype if needed if (typeof(ParsedImplementation) == typeof(T)) { ParseNow(); UpdateFunctionPrototypes(true); } }
/// <summary> /// returns the best caret position for inserting a new IProNew /// </summary> private int GetCaretPositionForInsertion <T>(string codeName, ProInsertPosition insertPos, out bool insertBefore) where T : ParsedScopeItem { insertBefore = false; // at caret position if (insertPos == ProInsertPosition.CaretPosition) { return(Sci.GetPosFromLineColumn(Sci.Line.CurrentLine, 0)); } T refItem = null; #region set insertBefore and refItem // the following is a little annoying to code and understand... // the idea is to get (or dont get if it doesn't exist) the previous or the next item // of type T in the existing list of said types so we can "anchor" on it to insert // our new stuff... if (typeof(ParsedPrototype) == typeof(T)) { // find the previous/next function implementation with a prototype bool found = false; ParsedImplementation foundImplement = null; foreach (var impl in _parsedItems.Where(item => item is ParsedImplementation).Cast <ParsedImplementation>()) { if (impl != null) { // we didn't match our current function implementation yet if (!found) { // we just did if (impl.Name.Equals(codeName)) { found = true; continue; } // set previous item if (impl.HasPrototype) { foundImplement = impl; } } else { // match first item after we found our implementation if (impl.HasPrototype) { insertBefore = true; foundImplement = impl; break; } } } } // now we need its proto if (foundImplement != null) { refItem = _parsedItems.FirstOrDefault(fun => { var proto = fun as ParsedPrototype; return(proto != null && proto.Name.Equals(foundImplement.Name) && proto.SimpleForward); }) as T; } } else { // list of existing items of the same type var existingList = _parsedItems.Where(item => item.GetType() == typeof(T)).Select(item => (T)item).ToList(); if (existingList.Count > 0) { // alphabetical order if (insertPos == ProInsertPosition.AlphabeticalOrder) { // find the position that would take our new code int index = existingList.Select(item => item.Name).ToList().BinarySearch(codeName); if (index < 0) { index = ~index - 1; // we get the index in which it should be inserted - 1 if (index == -1) { insertBefore = true; refItem = existingList[0]; } else { refItem = existingList[index]; } } // first of its kind } else if (insertPos == ProInsertPosition.First) { refItem = existingList.FirstOrDefault(); insertBefore = true; } else if (insertPos == ProInsertPosition.Last) { refItem = existingList.LastOrDefault(); } } } #endregion string preProcBlockType = null; string typeComment = null; if (typeof(ParsedImplementation) == typeof(T)) { preProcBlockType = @"_FUNCTION"; typeComment = @"Function\s+Implementations"; } else if (typeof(ParsedPrototype) == typeof(T)) { preProcBlockType = @"_FUNCTION-FORWARD"; typeComment = @"Function\s+Prototypes"; } else if (typeof(ParsedProcedure) == typeof(T)) { preProcBlockType = @"_PROCEDURE"; typeComment = @"Internal\s+Procedures"; } // is there already an item existing? if (refItem != null && preProcBlockType != null) { // try to find a &IF DEFINED(EXCLUDE- block or a _UIB_BLOCK that surrounds the prototype var preProcBlock = GetPreProcBlock(refItem, preProcBlockType); if (preProcBlock != null) { return(insertBefore ? preProcBlock.Position : preProcBlock.EndBlockPosition); } // otherwise return the position of the function itself return(insertBefore ? refItem.Position : refItem.EndBlockPosition); } // can we find a comment indicating where the proc should be inserted? if (typeComment != null) { Sci.TargetWholeDocument(); var previousFlags = Sci.SearchFlags; Sci.SearchFlags = SearchFlags.Regex; var streg = @"\/\*\s+[\*]+\s+" + typeComment + @"\s+[\*]+"; var foundPos = Sci.SearchInTarget(streg); Sci.SearchFlags = previousFlags; if (foundPos == -1) { foundPos = new Regex(@"\/\*\s+[\*]+\s+" + typeComment + @"\s+[\*]+").Match(Sci.Text).Index; if (foundPos == 0) { foundPos = -1; } } if (foundPos > -1) { return(Sci.GetPosFromLineColumn(Sci.LineFromPosition(foundPos) + 1, 0)); } } // At last, we find the best position considering the appbuilder blocks if (typeof(ParsedImplementation) == typeof(T)) { // function implementation goes all the way bottom return(Sci.TextLength); } if (typeof(ParsedPrototype) == typeof(T)) { // prototypes go after &ANALYZE-SUSPEND _UIB-PREPROCESSOR-BLOCK var preprocessorBlock = _parsedItems.FirstOrDefault(item => item is ParsedPreProcBlock && ((ParsedPreProcBlock)item).Type == ParsedPreProcBlockType.UibPreprocessorBlock); if (preprocessorBlock != null) { insertBefore = false; return(((ParsedPreProcBlock)preprocessorBlock).EndBlockPosition); } } if (typeof(ParsedProcedure) == typeof(T)) { // new procedure goes before the first function implementation of last var firstFunc = _parsedItems.FirstOrDefault(item => item is ParsedImplementation) as ParsedImplementation; if (firstFunc != null) { insertBefore = true; // try to find a &IF DEFINED(EXCLUDE- block that surrounds the func var preProcBlock = GetPreProcBlock(firstFunc, @"_FUNCTION"); if (preProcBlock != null) { return(preProcBlock.Position); } return(firstFunc.Position); } // otherwise it goes at the end return(Sci.TextLength); } return(-1); }
/// <summary> /// This method checks if the current document contains function prototypes that are not updated /// and correct them if needed /// </summary> /// <remarks>This method is costly because we parse everything potentially X times, but it's much simpler this way...</remarks> private void UpdateFunctionPrototypes(bool silent) { try { List <ParsedImplementation> listOfOutDatedProto; List <ParsedImplementation> listOfSoloImplementation; List <ParsedPrototype> listOfUselessProto; StringBuilder outputMessage = new StringBuilder(); var nbLoop = 0; var nbNotCreated = 0; var nbThingsDone = 0; var nbToDo = GetPrototypesLists(out listOfOutDatedProto, out listOfSoloImplementation, out listOfUselessProto); // if there is at least 1 thing to do if (nbToDo > 0) { Sci.BeginUndoAction(); // Add proto if (listOfSoloImplementation.Count > 0 && string.IsNullOrEmpty(_parser.ParseErrorsInHtml)) { var tempMes = new StringBuilder("The following function prototypes have been created :"); while (listOfSoloImplementation.Count > nbNotCreated && nbLoop < nbToDo) { if (AddPrototypes(ref tempMes, listOfSoloImplementation[nbNotCreated])) { nbThingsDone++; } else { nbNotCreated++; } ParseNow(); GetPrototypesLists(out listOfOutDatedProto, out listOfSoloImplementation, out listOfUselessProto); nbLoop++; } tempMes.Append("<br><br>"); if (nbThingsDone > 0) { outputMessage.Append(tempMes); } } // delete proto if (listOfUselessProto.Count > 0) { outputMessage.Append("The following prototypes have been deleted :"); while (listOfUselessProto.Count > 0 && nbLoop < nbToDo) { if (DeletePrototypes(ref outputMessage, listOfUselessProto[0])) { nbThingsDone++; } ParseNow(); GetPrototypesLists(out listOfOutDatedProto, out listOfSoloImplementation, out listOfUselessProto); nbLoop++; } outputMessage.Append("<br><br>"); } // update proto if (listOfOutDatedProto.Count > 0) { outputMessage.Append("The following functions have had their prototype synchronized :"); while (listOfOutDatedProto.Count > 0 && nbLoop < nbToDo) { if (UpdatePrototypes(ref outputMessage, listOfOutDatedProto[0])) { nbThingsDone++; } ParseNow(); GetPrototypesLists(out listOfOutDatedProto, out listOfSoloImplementation, out listOfUselessProto); nbLoop++; } outputMessage.Append("<br><br>"); } Sci.EndUndoAction(); } if (nbThingsDone == 0) { if (!silent) { if (nbToDo == 0) { UserCommunication.Notify("There was nothing to be done :<br>All the prototypes match their implementation", MessageImg.MsgInfo, "Function prototypes", "Everything is synchronized", 5); } else { UserCommunication.Notify("Failed to find the prototype for " + nbNotCreated + " function implementations<br>Your document is not correctly formatted for 3P to automatically create them :<br><i>The block _UIB-PREPROCESSOR-BLOCK is missing or the procedure can't be opened in the appbuilder!</i><br><br>Please correct your document manually, then they will all be updated correctly" + _parser.ParseErrorsInHtml, MessageImg.MsgHighImportance, "Function prototypes", "Failed to create prototypes"); } } } else { outputMessage.Append("<i>"); outputMessage.Append("CTRL + Z will cancel the above-mentioned modifications<br>"); outputMessage.Append(Npp.CurrentFile.Path.ToHtmlLink("Click here to stop auto-updating the prototypes for this file")); outputMessage.Append("</i>"); UserCommunication.NotifyUnique("Prototype_synchro", outputMessage.ToString(), MessageImg.MsgOk, "Function prototypes", "Synchronization done", args => { var split = args.Link.Split('#'); if (split.Length == 2) { Npp.GotoPos(split[0], int.Parse(split[1])); args.Handled = true; } else { if (!_ignoredFiles.Contains(args.Link)) { _ignoredFiles.Add(args.Link); UserCommunication.NotifyUnique("Prototype_synchro", "Automatic prototype updates stopped for the file :<br>" + Npp.CurrentFile.Path + "<br><br><i>This is effective until you restart Notepad++<br>You can also trigger an update manually to restart the auto-update</i>", MessageImg.MsgInfo, "Function prototypes", "Synchronization stopped", null, 5); args.Handled = true; } } }, 5); } } catch (Exception e) { ErrorHandler.ShowErrors(e, "Error updating prototypes"); } }