Community
    • Login

    Calltip on mouse-over from UDL xml file (Python)

    Scheduled Pinned Locked Moved Help wanted · · · – – – · · ·
    21 Posts 5 Posters 1.7k Views
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Khundian TwitchK
      Khundian Twitch
      last edited by Khundian Twitch

      I was wondering if there is a way to get the keyword information from a User Defined Language XML file. The script below works to show a calltip on mouse-over, but its kinda useless without a way to get the calltip info from the UDL XML file. Does anyone have experience with this?

      # Calltip on mouse-over script
      from __future__ import print_function
      
      # Clear Callbacks
      editor.clearCallbacks()
      
      # Set Dwell Time
      editor.setMouseDwellTime(500)
      
      # Cancel Calltip
      def dwell_end(args):
          editor.callTipCancel()
          print (editor.callTipCancel)
      
      # Get Word Under Mouse
      def dwell_start(args):
          pos = args["position"]
          dwell_word = editor.getTextRange(
              editor.wordStartPosition(pos, True),
              editor.wordEndPosition(pos, True))
      
          # Show Calltip
          if len(dwell_word):
              print (editor.callTipShow, pos)
              editor.callTipShow(editor.getCurrentPos(), "Test String")
      
      
      console.show()
      print("installing Callbacks")
      show = editor.callback(dwell_start, [SCINTILLANOTIFICATION.DWELLSTART])
      print("Result is ", show)
      cancel = editor.callback(dwell_end, [SCINTILLANOTIFICATION.DWELLEND])
      print("Result is ", cancel)
      
      
      PeterJonesP 1 Reply Last reply Reply Quote 0
      • PeterJonesP
        PeterJones @Khundian Twitch
        last edited by

        @Khundian-Twitch ,

        I don’t know of a way, because Notepad++ does not have an API call that passes they keywords to a plugin.

        And unfortunately, Scintilla has a SCI_SETKEYWORD (pythonscript: editor.setKeyword()) which Notepad++ uses to set the keyword list (both for normal lexers and for UDL languages), but it does not have the corresponding SCI_GETKEYWORD to retrieve the information.

        In theory, you could manually read the UDL XML file yourself – but you’d actually have to read all the UDL files (including the old-style userDefineLangs.xml, since that’s where the UDL GUI still puts them) – because the filenames are independent of the UDL names.

        I might actually recommend reading the autoCompletion file instead (Notepad++ does allow autoCompletion for UDLs – and I recommend defining one for your UDL), because that XML has attributes which might help you with getting extra information which is not carried in the UDL definition file. (I was actually originally going to suggest just making sure of Notepad++'s auto-completion and parameter-hint information, and thus avoiding the scripting plugin: the parameter hints can give info about the active keyword and any arguments… But it’s done through a keystroke rather than hover-over, and it requires a language whose function arguments have a wrapper like parentheses or something, so command arg1, arg2-style languages, without parens, cannot do the hints natively – which means I am guessing it won’t fully meet your needs.)

        Khundian TwitchK 1 Reply Last reply Reply Quote 3
        • Khundian TwitchK
          Khundian Twitch @PeterJones
          last edited by

          @PeterJones Thanks for the reply
          Actually I have an autocomplete file set up already, but the language doesn’t use parentheses, square brackets and curly braces in the conventional way. The best solution would be on a space, but I read on the forum that isn’t natively possible. So I started looking into the PythonScript plugin and the Scintilla callbacks, hoping I could find a solution that way. I figured if I get the mouse-over working, it would be possible on a space too.

          I took your advice and started tinkering a bit myself and made a bit of progress. While far from finished the script below shows its technically possible, Current script iterates through all the functions and when the mouse-over word is the same as the one being checked in the XML file, it shows the return value, name, description and parameters of the keyword. This works with a single or a couple entries in the xml. But my full autocomplete file has close to 3000 entries, would need to be really lucky to see the calltip this way.

          I will add the test.xml too, with the current settings in the script, this file should be in the root notepad folder if anyone wants to tinker with the script.

          # Calltip on mouse-over script
          from __future__ import print_function
          import xml.etree.ElementTree as ET
          
          tree = ET.parse("test.xml")
          root = tree.getroot()
          
          # Clear Callbaks
          editor.clearCallbacks()
          
          # Set Dwell Time
          editor.setMouseDwellTime(500)
          
          
          # Cancel Calltip
          def dwell_end(args):
              editor.callTipCancel()
          
          
          # Get Word Under Mouse
          def dwell_start(args):
              pos = args["position"]
              dwell_word = editor.getTextRange(
                  editor.wordStartPosition(pos, True), editor.wordEndPosition(pos, True)
              )
          
              # Find keyword names and if function or not
              for keyword in root.iter("KeyWord"):
                  func = keyword.get("func")
                  kname = keyword.get("name")
                  # print (name, func)
          
              # Find return values and descriptions
              for overload in root.iter("Overload"):
                  descr = overload.get("descr")
                  retval = overload.get("retVal")
                  # print (retval, descr)
          
              # Find parameter names
              for param in root.iter("Param"):
                  pname = param.get("name")
                  # print (pname)
          
              # Show Calltip
              if dwell_word == kname:
                  editor.callTipShow(editor.getCurrentPos(), (retval, kname, descr, pname))
                  # print (editor.callTipShow, pos)
              else:
                  # Fail message in console when dwell_word doesn't match
                  print("Fail")
          
          
          console.show()
          print("installing callback.")
          show = editor.callback(dwell_start, [SCINTILLANOTIFICATION.DWELLSTART])
          print("Result is ", show)
          cancel = editor.callback(dwell_end, [SCINTILLANOTIFICATION.DWELLEND])
          print("Result is ", cancel)
          
          
          
          <NotepadPlus>
          	<AutoComplete language="">
          		<Environment ignoreCase="yes"/>
          		<KeyWord name="Abs" func="yes">
          			<Overload retVal="Float" descr="When given a number, returns the absolute value of the number.">
          				<Param name="Float:Float"/>
          			</Overload>
          		</KeyWord>
          	</AutoComplete>
          </NotepadPlus>
          
          PeterJonesP 1 Reply Last reply Reply Quote 1
          • PeterJonesP
            PeterJones @Khundian Twitch
            last edited by

            @Khundian-Twitch said in Calltip on mouse-over from UDL xml file (Python):

            This works with a single or a couple entries in the xml. But my full autocomplete file has close to 3000 entries, would need to be really lucky to see the calltip this way.

            I think you are duplicating effort too much in your current script: right now, every time you hover, it iterates over the whole XML, which is hugely inefficient – the UDL’s autoCompletion XML isn’t changing, so you want to just process it once at the beginning. What I would do would be to create a nested dictionary structure, where the key of the top-level dictionary is the word (in this case, the keyword/function name), and then you store the other info in keys under that. That all gets done during script initialization. Then, in the hover-over event, you just need to grab the word you’re hovering over, and then your # Show Calltip if condition would just be if dwell_word in dataStructure

            The datastructure I would use would look something like

            dataStructure = {
                'Abs': [
                    {
                        'retVal': 'Float',
                        'descr': 'When given a number, ...',
                        'params' : [ 'float:float' ]
                    }
                ],
                'FuncWithOptionalParams': [
                    {
                        'retVal': 'default',
                        'descr': 'This overload has 0 params',
                        'params': []
                    },
                    {
                        'retVal': 'alt1',
                        'descr': 'This overload has 1 params',
                        'params': ['in1: integer'],
                    },
                    {
                        'retVal': 'alt2',
                        'descr': 'This overload has 2 params',
                        'params': ['in1: integer', 'in2: float'],
                    }
                ]
            }
            }
            

            Then when you hovered over the first word on each line below, it would run the simpler callback…

            Abs -3.14159
            DoesNotExist
            FuncWithOptionalParams
            

            The middle line would not find ‘DoesNotExist’ in dataStruct, so it wouldn’t tooltip. The first and third lines would find the matching key in your dataStruct dictionary, so could go on to create an appropriate tooltip –

                if dwell_word in dataStruct:
                    str = ""
                    for overload in dataStruct[dwell_word]:
                        str += overload['descr'] + "\n"
                    # ... create tooltip from string here
            
            Khundian TwitchK 1 Reply Last reply Reply Quote 3
            • Khundian TwitchK
              Khundian Twitch @PeterJones
              last edited by Khundian Twitch

              @PeterJones Thanks for the advice!
              I have spent some more time on it, and with your advice got a step closer. I have a searchable dictionary, and got the mouse over working. Still gave to take a look on how to get nicely formatted text instead of the single line with brackets in the calltip.

              I first had a go creating a dictionary with ElementTree because that is standard inside the PythonScript plugin, always nice not having to download extra stuff. But I cannot get the dictionary to respect t the order of the xml, when mouse over the “func” attribute comes before the “name” attribute.

              # Calltip on mouse-over with ElementTree
              from __future__ import print_function
              
              # Import the required modules
              import xml.etree.ElementTree as ET
              from collections import defaultdict
              from pprint import pprint
              
              # Import and parse the data from file
              tree = ET.parse("test.xml")
              root = tree.getroot()
              
              # Create empty dictionary
              data_dict = {}
              
              
              # Imoort the XML data into a Python dictionary
              def etree_to_dict(t):
                  d = {t.tag: {} if t.attrib else None}
                  children = list(t)
                  if children:
                      dd = defaultdict(list)
                      for dc in map(etree_to_dict, children):
                          for k, v in dc.items():
                              dd[k].append(v)
                      d = {t.tag: {k: v[0] if len(v) == 1 else v for k, v in dd.items()}}
                  if t.attrib:
                      d[t.tag].update(("" + k, v) for k, v in t.attrib.items())
                  if t.text:
                      text = t.text.strip()
                      if children or t.attrib:
                          if text:
                              d[t.tag]["#text"] = text
                      else:
                          d[t.tag] = text
                  return d
              
              
              data_dict = etree_to_dict(root)
              
              # Print dictionary to console
              # pprint(data_dict, indent=2)
              
              # Clear callbacks
              editor.clearCallbacks()
              
              # Set dwell time
              editor.setMouseDwellTime(500)
              
              
              # Cancel calltip
              def dwell_end(args):
                  editor.callTipCancel()
              
              
              # Get word under mouse
              def dwell_start(args):
                  pos = args["position"]
                  dwell_word = editor.getTextRange(
                      editor.wordStartPosition(pos, True), editor.wordEndPosition(pos, True)
                  )
                  # Print word under mouse to console
                  print(dwell_word)
                  res = None
                  # Search for the the dwell_word in the dictionary
                  for sub in data_dict["NotepadPlus"]["AutoComplete"]["KeyWord"]:
                      if sub["name"] == dwell_word:
                          res = sub
                          # Show the filter result in console
                          print("The filtered dictionary value is : ", res)
                          # Show dictionary info in calltip
                          editor.callTipShow(editor.getCurrentPos(), res)
                          break
              
              
              console.show()
              print("Calltip on mouse-over with ElementTree installed.")
              
              # Scintilla callback notifications
              show = editor.callback(dwell_start, [SCINTILLANOTIFICATION.DWELLSTART])
              cancel = editor.callback(dwell_end, [SCINTILLANOTIFICATION.DWELLEND])
              
              

              I had a go with xmltodict too, thats a module that has to be downloaded and added to the PythonScript plugin folder. Not ideal, but this module does respect the order of the xml.

              # Calltip on mouse-over with xmltodict
              from __future__ import print_function
              
              # Import the required modules
              import codecs
              from io import open
              import xmltodict
              from pprint import pprint
              
              # Import and parse the data from file
              with open("test.xml", "r", encoding="utf-8") as file:
                  data_xml = file.read()
              
              # Create empty dictionary
              data_dict = {}
              
              # Use xmltodict to parse and convert
              data_dict = xmltodict.parse(data_xml, attr_prefix="")
              
              # Print dictionary to console
              # pprint(data_dict, indent=2)
              
              # Clear callbacks
              editor.clearCallbacks()
              
              # Set dwell time
              editor.setMouseDwellTime(500)
              
              
              # Cancel calltip
              def dwell_end(args):
                  editor.callTipCancel()
              
              
              # Get word under mouse
              def dwell_start(args):
                  pos = args["position"]
                  dwell_word = editor.getTextRange(
                      editor.wordStartPosition(pos, True), editor.wordEndPosition(pos, True)
                  )
                  # Print word under mouse to console
                  print(dwell_word)
                  res = None
                  # Search for the the dwell_word in the dictionary
                  for sub in data_dict["NotepadPlus"]["AutoComplete"]["KeyWord"]:
                      if sub["name"] == dwell_word:
                          res = sub
                          # Show the filter result in console
                          print("The filtered dictionary value is : ", res)
                          # Show dictionary info in calltip
                          editor.callTipShow(editor.getCurrentPos(), res)
                          break
              
              
              console.show()
              print("Calltip on mouse-over with xmltodict installed.")
              
              # Scintilla callback notifications
              show = editor.callback(dwell_start, [SCINTILLANOTIFICATION.DWELLSTART])
              cancel = editor.callback(dwell_end, [SCINTILLANOTIFICATION.DWELLEND])
              
              Alan KilbornA 1 Reply Last reply Reply Quote 1
              • Alan KilbornA
                Alan Kilborn @Khundian Twitch
                last edited by

                @Khundian-Twitch said in Calltip on mouse-over from UDL xml file (Python):

                how to get nicely formatted text instead of the single line with brackets in the calltip.

                Wouldn’t you just insert \r\n into the text string wherever you want the calltip to have a line-break?

                Khundian TwitchK 1 Reply Last reply Reply Quote 2
                • Khundian TwitchK
                  Khundian Twitch @Alan Kilborn
                  last edited by

                  @Alan-Kilborn Thanks for the reply
                  I wouldn’t know to be honest. I haven’t used Python that much before. Its not only line breaks though, at the moment it shows the text in brackets too. Picture below also shows the dictionary created with ElementTree not respecting the order of the XML.

                  2024-01-19 13_34_12-Window.png

                  PeterJonesP 1 Reply Last reply Reply Quote 0
                  • PeterJonesP
                    PeterJones @Khundian Twitch
                    last edited by

                    @Khundian-Twitch ,

                    You shouldn’t be using the default Python language stringification of the data structure element as the popup, if you want the string to be user-friendly. You should be extracting the elements from that sub-object that are useful to you, in the order you want them to appear, using one of Python’s string-formatting techniques. (That’s nothing specific to Notepad++. Any time you want to take data from an object and present it as a string in Python, you need to format the string to make it look like you want – and if you need help with that, it’s not really a question for here, because that’s generic programming help, not Notepad++ specific.)

                    Khundian TwitchK 1 Reply Last reply Reply Quote 2
                    • Khundian TwitchK
                      Khundian Twitch @PeterJones
                      last edited by

                      @PeterJones Thanks for the reply and the advice
                      I understand that my questions after my initial post are not directly related to Notepad++ anymore. But these nudges in the right direction I got from you is all that I needed,

                      I looked into string formatting, and got a step further. Currently it shows only the description of a keyword as calltip, and it only works if there is only 1 Overload entry. I’m going to check how to make it work with multiple hint definitions and the up and down arrows.

                      I included a picture and the test.xml as an example in case someone wants to use the script in its current state. All text will be wrapped at max 90 characters, and as stated in the Notepad++ documentation for line breaks use

                      &#x0a;
                      

                      2024-01-30 23_09_32-Window.png

                      <NotepadPlus>
                      	<AutoComplete language="">
                      		<Environment ignoreCase="yes"/>
                      		<KeyWord name="ActivateAnim" func="yes">
                      			<Overload retVal="" descr='
                      &#x0a;------------------------------------------------------------------------------------------
                      &#x0a;Plays the specified anim at the specified priority and weight, with optional parameters.
                      All arguments except the sequence path are optional.
                      This is a low-level animation function that must be used with care.
                      Use BlendToAnim to deactivate or transition the animation to a different one.
                      &#x0a;------
                      &#x0a;Syntax
                      &#x0a;------
                      &#x0a;actor.ActivateAnim anim sequence path:string
                      &#x0a;bIsFirstPerson:0/1
                      &#x0a;iPriority:integer
                      &#x0a;bStartOver:0/1
                      &#x0a;fWeight:float
                      &#x0a;fEaseInTime:float
                      &#x0a;time sync sequence path:string
                      &#x0a;-------
                      &#x0a;Example
                      &#x0a;-------
                      &#x0a;ActivateAnim "Characters\Recoil.kf"
                      &#x0a;------------------------------------------------------------------------------------------
                      			'>
                      				<Param name=""/>
                      			</Overload>
                      		</KeyWord>
                      	</AutoComplete>
                      </NotepadPlus>
                      
                      
                      # Calltip on mouse-over with ElementTree
                      from __future__ import print_function
                      
                      # Import the required modules
                      import xml.etree.ElementTree as ET
                      from collections import defaultdict
                      from pprint import pprint
                      import textwrap
                      
                      
                      # Import and parse the data from file
                      tree = ET.parse("test.xml")
                      root = tree.getroot()
                      
                      # Create empty dictionary
                      data_dict = {}
                      
                      
                      # Imoort the XML data into a Python dictionary
                      def etree_to_dict(t):
                          d = {t.tag: {} if t.attrib else None}
                          children = list(t)
                          if children:
                              dd = defaultdict(list)
                              for dc in map(etree_to_dict, children):
                                  for k, v in dc.items():
                                      dd[k].append(v)
                              d = {t.tag: {k: v[0] if len(v) == 1 else v for k, v in dd.items()}}
                          if t.attrib:
                              d[t.tag].update(("" + k, v) for k, v in t.attrib.items())
                          if t.text:
                              text = t.text.strip()
                              if children or t.attrib:
                                  if text:
                                      d[t.tag]["#text"] = text
                              else:
                                  d[t.tag] = text
                          return d
                      
                      
                      data_dict = etree_to_dict(root)
                      
                      # Print dictionary to console
                      # pprint(data_dict, indent=4)
                      
                      # Clear callbacks
                      editor.clearCallbacks()
                      
                      # Set dwell time
                      editor.setMouseDwellTime(500)
                      
                      
                      # Cancel calltip
                      def dwell_end(args):
                          editor.callTipCancel()
                      
                      
                      # Get word under mouse
                      def dwell_start(args):
                          pos = args["position"]
                          dwell_word = editor.getTextRange(
                              editor.wordStartPosition(pos, True), editor.wordEndPosition(pos, True)
                          )
                          # Print word under mouse to console
                          # print("Mouse over:", dwell_word)
                      
                          # Search for the the dwell_word in the dictionary
                          for keyword in data_dict["NotepadPlus"]["AutoComplete"]["KeyWord"]:
                              if keyword.get("name") == dwell_word:
                                  nl = "\n"
                                  print(dwell_word, "found in dictionary")
                                  if keyword.get("func") == ("yes"):
                                      # Get name of keyword from dictionary
                                      namevalue = keyword.get("name")
                                      # Get if keyword is a function, yes/no
                                      funcvalue = keyword.get("func")
                                      # Get all overload subs
                                      overloadvalues = keyword.get("Overload")
                                      # Get description of keyword
                                      descrvalue = overloadvalues.get("descr")
                                      # Wrap the description text for calltip
                                      descrvaluewrap = nl.join(
                                          [
                                              nl.join(
                                                  textwrap.wrap(
                                                      line,
                                                      90,
                                                      break_long_words=False,
                                                      replace_whitespace=False,
                                                  )
                                              )
                                              for line in descrvalue.splitlines()
                                              if line.strip() != ""
                                          ]
                                      )
                      
                                      # Print stuff to console
                                      # print(namevalue)
                                      # print(funcvalue)
                                      # pprint(overloadvalues, indent=2)
                                      # print(descrvalue)
                                      # print(descrvaluewrap)
                      
                                      # Show the calltip
                                      editor.callTipShow(editor.getCurrentPos(), descrvaluewrap)
                      
                      
                      # Open console and show install message
                      console.show()
                      print("Calltip on mouse-over with ElementTree installed.")
                      
                      # Scintilla callback notifications
                      show = editor.callback(dwell_start, [SCINTILLANOTIFICATION.DWELLSTART])
                      cancel = editor.callback(dwell_end, [SCINTILLANOTIFICATION.DWELLEND])
                      
                      
                      1 Reply Last reply Reply Quote 2
                      • Khundian TwitchK
                        Khundian Twitch
                        last edited by Khundian Twitch

                        I’ve decided to go with only 1 “Overload” entry per keyword for the time being. I couldn’t find any clear documentation or examples on how to implement them, if anyone has any experience with this… I’m all ears. Unless its to add multiple overloads, this will be the “final” version of the script. A bit funny that it turned out to be “on character” and not “mouse-over”… :)

                        Something weird I noticed while testing with the multiple overloads, when the calltip is showing and I use the “alt up” and “alt down” shortcuts it crashes Notepad++ to desktop. Even with the script below that only has 1 “Overload” entry it CTD, clicking on the calltip doesn’t crash btw. I have no idea why it does this, I disabled the parameter hint shortcuts, and recommend it to anyone who wants to use the script below until the cause is found.

                        I changed the script to work on a “space” instead of mouse-over, but it can easily be changed to any character you want. I added explanations in the comments on how to edit the script to work with any User Defined Language, and how to change the character that triggers the calltip. Formatted the calltip some more and cleaned the script up, if anyone needs help to get it running… don’t hesistate to ask.

                        2024-02-01 22_19_30-Window.png

                        <NotepadPlus>
                        	<AutoComplete language="">
                        		<Environment ignoreCase="yes"/>
                        		<KeyWord name="AddAmmoEffect" func="yes">
                        			<Overload retVal="(successful:0/1)" descr='
                        &#x0a;Adds an ammo effect to an ammo type.
                        &#x0a;------
                        &#x0a;Syntax
                        &#x0a;------
                        &#x0a;(successful:0/1) AddAmmoEffect ammo:ref ammoEffect:ref
                        &#x0a;-------
                        &#x0a;Example
                        &#x0a;-------
                        &#x0a;AddAmmoEffect Ammo12Ga AmmoEffectBeanBagFatigue 
                        			'>
                        				<Param name="Form:AnyForm"/>
                        			</Overload>
                        		</KeyWord>
                        	</AutoComplete>
                        </NotepadPlus>
                        
                        # Calltip on any character
                        from __future__ import print_function
                        
                        # Import the required modules
                        import xml.etree.ElementTree as ET
                        from collections import defaultdict
                        import textwrap
                        
                        # Import and parse the data from file
                        tree = ET.parse("PATH/TO/YOUR/AUTOCOMPLETE.XML")
                        root = tree.getroot()
                        
                        # Create empty dictionary
                        data_dict = {}
                        
                        
                        # Imoort the XML data into a Python dictionary
                        def etree_to_dict(t):
                            d = {t.tag: {} if t.attrib else None}
                            children = list(t)
                            if children:
                                dd = defaultdict(list)
                                for dc in map(etree_to_dict, children):
                                    for k, v in dc.items():
                                        dd[k].append(v)
                                d = {t.tag: {k: v[0] if len(v) == 1 else v for k, v in dd.items()}}
                            if t.attrib:
                                d[t.tag].update(("" + k, v) for k, v in t.attrib.items())
                            if t.text:
                                text = t.text.strip()
                                if children or t.attrib:
                                    if text:
                                        d[t.tag]["#text"] = text
                                else:
                                    d[t.tag] = text
                            return d
                        
                        
                        data_dict = etree_to_dict(root)
                        
                        
                        # Padding for the calltip
                        pad = "----------------------------------------------------------------------"
                        
                        
                        # Clear callbacks
                        editor.clearCallbacks()
                        
                        
                        # Get word before the chosen character
                        def on_char_add(args):
                            # ---------------------------------------------------------------
                            # The line below makes sure that the code below will only run
                            # when a document is active with your chosen language.
                            # Change YOURLANGUAGENAME to the exact name of your User Defined
                            # Language.
                            # ---------------------------------------------------------------
                            if notepad.getLanguageName(LANGTYPE.USER) == "udf - YOURLANGUAGENAME":
                                # -----------------------------------------------------------
                                # Uncomment the line below, and run the script.
                                # When typing text in a file with the chosen language above,
                                # the "ch" number will show in the console.
                                # Use that number to change the added_char value below,
                                # this will change the character that opens the calltip.
                                # -----------------------------------------------------------
                                # print(args)
                                added_char = args["ch"]
                                if (
                                    added_char == 32
                                ):  # Number 32 is the "space" character, change to whatever you want.
                                    pos = editor.getCurrentPos() - 1
                                    search_word = editor.getTextRange(
                                        editor.wordStartPosition(pos, True), editor.wordEndPosition(pos, True)
                                    )
                        
                                    # Search for the the search_word information in the dictionary
                                    for keyword in data_dict["NotepadPlus"]["AutoComplete"]["KeyWord"]:
                                        if keyword.get("name") == search_word:
                                            nl = "\n"
                                            name_value = keyword.get("name")
                                            overload_values = keyword.get("Overload")
                                            retval_value = overload_values.get("retVal")
                                            descr_value = overload_values.get("descr")
                                            param_value = keyword.get("Overload")["Param"]["name"]
                                            retval_text = "RetVal: " + retval_value
                                            param_text = "Param: " + param_value
                        
                                            # Create calltip from all retrieved text
                                            calltip = (
                                                pad
                                                + nl
                                                + name_value
                                                + nl
                                                + pad
                                                + nl
                                                + retval_text
                                                + nl
                                                + pad
                                                + nl
                                                + descr_value
                                                + nl
                                                + pad
                                                + nl
                                                + param_text
                                                + nl
                                                + pad
                                            )
                        
                                            # Wrap the text for calltip
                                            calltip_wrap = nl.join(
                                                [
                                                    nl.join(
                                                        textwrap.wrap(
                                                            line,
                                                            70,
                                                            break_long_words=False,
                                                            replace_whitespace=False,
                                                        )
                                                    )
                                                    for line in calltip.splitlines()
                                                    if line.strip() != ""
                                                ]
                                            )
                        
                                            # Show the calltip
                                            editor.callTipShow(editor.getCurrentPos(), calltip_wrap)
                        
                        
                        # Open console and show install message
                        console.show()
                        print("Calltip on any character installed, its safe to close this console.")
                        
                        
                        # Scintilla callback notifications
                        editor.callback(on_char_add, [SCINTILLANOTIFICATION.CHARADDED])
                        
                        
                        1 Reply Last reply Reply Quote 1
                        • Khundian TwitchK
                          Khundian Twitch
                          last edited by

                          I had some free time and had another go at multiple overloads, but there seems to be a bug with the down arrow in the tooltip / and the alt+down shortcut combo when using PythonScript. Obviously there is nothing wrong with the standard implementation of this feature, only when creating a custom tooltip with PythonScript does this CTD happen.

                          I did a small test with the SCINTILLANOTIFICATION.CALLTIPCLICK and this was the result, position 0 is clicked anywhere but the arrows, position 1 is clicked on the up arrow and position 2 should be clicked on the down arrow… but this results in Notepad++ crashing to desktop.

                          2024-02-02 03_59_55-Window.png

                          Lycan ThropeL mpheathM 2 Replies Last reply Reply Quote 0
                          • Lycan ThropeL
                            Lycan Thrope @Khundian Twitch
                            last edited by

                            @Khundian-Twitch ,
                            Just a note, and I’m not sure if this is relevant with what you’re doing with pythonscript, but the calltip functions are triggered by a delimiter insertion, meaning when you type the keyword, and then click a parens if it is the opening for a function, then the calltip pops up, and you have to use the mouse to click the buttons up or down.

                            What you’re doing, may not make what I just mentioned relevant, since it sounds like you’re past my knowledge, but I thought I might mention in in case it is relevant.

                            1 Reply Last reply Reply Quote 2
                            • mpheathM
                              mpheath @Khundian Twitch
                              last edited by mpheath

                              @Khundian-Twitch said in Calltip on mouse-over from UDL xml file (Python):

                              I did a small test with the SCINTILLANOTIFICATION.CALLTIPCLICK and this was the result, position 0 is clicked anywhere but the arrows, position 1 is clicked on the up arrow and position 2 should be clicked on the down arrow… but this results in Notepad++ crashing to desktop.

                              Exits with

                              Exception Code: c0000094

                              which is a division by zero code. Seems the position key cannot return a value 2 as it fails to get a value.

                              Also a second error

                              Exception Code: c000041d

                              A STATUS_FATAL_USER_CALLBACK_EXCEPTION code.

                              This is what I tested with:

                              from Npp import console, editor, notepad, SCINTILLANOTIFICATION
                              
                              try:
                                  callback_registry
                              except:
                                  callback_registry = []
                              
                              calltip_wrap = ['\x01 one\n\x02 description', '\x01 two\n\x02 description']
                              
                              if not editor.callTipActive():
                                  editor.callTipShow(editor.getCurrentPos(), calltip_wrap[0])
                              
                              def calltip_update(args):
                                  console.write(args)
                                  pos = editor.getCurrentPos()
                              
                                  if editor.callTipActive():
                                      if args['position'] == 2:
                                          editor.callTipCancel()
                                          editor.callTipShow(pos, calltip_wrap[0])
                                      elif args['position'] == 1:
                                          editor.callTipCancel()
                                          editor.callTipShow(pos, calltip_wrap[1])
                              
                                  return True
                              
                              if 'calltip_update' not in callback_registry:
                                  callback_registry.append('calltip_update')
                                  editor.callback(calltip_update, [SCINTILLANOTIFICATION.CALLTIPCLICK])
                              
                              Khundian TwitchK 1 Reply Last reply Reply Quote 2
                              • Khundian TwitchK
                                Khundian Twitch @mpheath
                                last edited by

                                @mpheath Thanks for taking the time to test, reply and clarify the issue.
                                I guess I’m out of luck. I wonder if it’s something with the PythonScript plugin, when I have some time I’m gonna test with the LuaScript plugin and see if the same happens when creating a calltip.

                                1 Reply Last reply Reply Quote 0
                                • Khundian TwitchK
                                  Khundian Twitch
                                  last edited by

                                  I tested with the LuaScript plugin and exactly the same happens, I guess it has nothing to do with either Python or LUA plugin. I guess its a bug in Scintilla or Notepad++. I’ll post the LUA code I used to test below.

                                  function showcalltip(ch)
                                  	calltip = "\001 <Click> \002"
                                  	pos = editor.CurrentPos
                                  	editor:CallTipShow(pos, calltip)
                                      return false
                                  end
                                  
                                  function printclick(flag)
                                      print(flag)
                                      return false
                                  end
                                  
                                  print("Test script running.")
                                   
                                  npp.AddEventHandler("OnChar", showcalltip)
                                  npp.AddEventHandler("OnCallTipClick", printclick)
                                  
                                  Alan KilbornA 1 Reply Last reply Reply Quote 1
                                  • Alan KilbornA
                                    Alan Kilborn @Khundian Twitch
                                    last edited by

                                    @Khundian-Twitch said in Calltip on mouse-over from UDL xml file (Python):

                                    I guess its a bug in Scintilla or Notepad++

                                    A crash is a pretty big deal; someone should open an official bug report on github.

                                    Khundian TwitchK 1 Reply Last reply Reply Quote 2
                                    • Khundian TwitchK
                                      Khundian Twitch @Alan Kilborn
                                      last edited by Khundian Twitch

                                      @Alan-Kilborn said in Calltip on mouse-over from UDL xml file (Python):

                                      @Khundian-Twitch said in Calltip on mouse-over from UDL xml file (Python):

                                      I guess its a bug in Scintilla or Notepad++

                                      A crash is a pretty big deal; someone should open an official bug report on github.

                                      I agree, but I’m not sure if this qualifies as a bug in Notepad++?
                                      The standard implementation of using the arrows to cycle through multiple overloads works fine. Its only when a user starts tinkering with script plugins and creating their own calltips that this crash will occur.

                                      1 Reply Last reply Reply Quote 1
                                      • Khundian TwitchK
                                        Khundian Twitch
                                        last edited by

                                        I created a bug report on github, guess we’ll see if it qualifies as a bug. Link to report

                                        1 Reply Last reply Reply Quote 3
                                        • Khundian TwitchK
                                          Khundian Twitch
                                          last edited by

                                          @mpheath Seems it was something in the Notepad++ code, but solved very quickly by the guys on github. Without a doubt the debug info you provided had something to do with that, Thanks again! :)

                                          mpheathM 1 Reply Last reply Reply Quote 2
                                          • mpheathM
                                            mpheath @Khundian Twitch
                                            last edited by mpheath

                                            @Khundian-Twitch Well done! I tested the artifact in PR #14667 that solves the crash issue and so now position 2 is possible for the down arrow of the calltip.

                                            1 Reply Last reply Reply Quote 3
                                            • First post
                                              Last post
                                            The Community of users of the Notepad++ text editor.
                                            Powered by NodeBB | Contributors