Can clickable links contain line numbers?



  • I have log files that have clickable links in them like:

    ____________________________________________
    (2)file:///C:/Scot/config/BaseTBAutomationPosMgr.xml#3686
    

    I can click this link and it opens the file in Notepad++ like I intended. However, it doesn’t go to line 3686. Is there any way I can make this link or another format link open the file in Notepad++ and go to that line?



  • @David-Miller229

    I don’t think it is possible using native npp functions.
    What I can think of is using a scripting language like python script or lua script
    and using the hotspotclick notification send by scintilla.
    We would receive the location/position of this click event and could get the text
    from that line. If your format is always the same I assume we could prepare such a script.
    Let us know if you wanna go this way.

    Cheers
    Claudia



  • Python would be great! I already have it installed. I have never used these kinds of scripts with NPP so that would be a great learning experience for me.



  • @David-Miller229

    because the latest python script version tends to fail if installed by plugin manager
    just double check if it has correctly installed on your side. Plugins->PythonScript->Show Console should report something like

    Python 2.7.6-notepad++ r2 (default, Apr 21 2014, 19:26:54) [MSC v.1600 32 bit (Intel)]
    Initialisation took 145ms
    Ready.
    

    If this doesn’t appear or you get an error download the MSI package and reinstall it.

    Once done, create a new script by Plugin->PythonScript-New Script
    name the file startup.py and paste the following content into it.

    import os
    
    file_identifier = 'file:///'
    position_seperator = '#'
    file_to_load = None
    line_in_file = 0
     
    def callbackBUFFERACTIVATED(args):
        global file_to_load
        if file_to_load is not None:
            current_file = notepad.getBufferFilename(args['bufferID'])
            if file_to_load.lower() == os.path.normpath(current_file).lower():
                editor.gotoLine(int(line_in_file))
                editor.verticalCentreCaret()
                file_to_load = None
    
    
    def callbackHOTSPOTCLICK(args):
        line_of_link = editor.lineFromPosition(args['position'])
        link_text = editor.getLine(line_of_link)
        if file_identifier in link_text and position_seperator in link_text:
            global file_to_load
            global line_in_file
            file_to_load, line_in_file = link_text.split(position_seperator)
            file_to_load = file_to_load.replace(file_identifier, '')
            file_to_load = os.path.normpath(file_to_load)
            notepad.open(file_to_load)
    
    editor.callback(callbackHOTSPOTCLICK, [SCINTILLANOTIFICATION.HOTSPOTCLICK])
    notepad.callback(callbackBUFFERACTIVATED, [NOTIFICATION.BUFFERACTIVATED])
    

    Take care about indention, python is strict about it (don’t know if you familar with python language).

    One additional step -> Plugins->PythonScript->Configuration
    change Initialisation from LAZY to ATSTARTUP.

    Done - every time you start npp this script gets executed automatically.

    What does the script?
    It registers a callback for hotspotclick and bufferactivated notification. Once you click
    on a link the callback gets executed and tries to get the text from the link.
    If it starts with file:/// and has # in the line it is supposed to be a file link and gets
    the details, file_to_load and line_in_file. Then it asks notepad to open that file.
    If this is successful, npp sends a notification (bufferactivated) once the buffer has been loaded. Now the second part starts - it gets checked if this is the file we want
    and if so we goto the line in question.
    Currently I have no windows environment to test but under Linux(wine)
    I do have a strange behavior. When the goto line is called some text before that
    line gets selected. I hope this isn’t an issue under windows.

    Cheers
    Claudia



  • @David-Miller229

    ok, found the culprit, we need to use HOTSPOTRELEASECLICK instead HOTSPOTCLICK. The following should work.

    import os
    
    file_identifier = 'file:///'
    position_seperator = '#'
    file_to_load = None
    line_in_file = 0
     
    def callbackBUFFERACTIVATED(args):
        global file_to_load
        if file_to_load is not None:
            current_file = notepad.getBufferFilename(args['bufferID'])
            if file_to_load.lower() == os.path.normpath(current_file).lower():
                editor.gotoLine(int(line_in_file))
                editor.verticalCentreCaret()
                file_to_load = None
    
    
    def callbackHOTSPOTRELEASECLICK(args):
        line_of_link = editor.lineFromPosition(args['position'])
        link_text = editor.getLine(line_of_link)
        if file_identifier in link_text and position_seperator in link_text:
            global file_to_load
            global line_in_file
            file_to_load, line_in_file = link_text.split(position_seperator)
            file_to_load = file_to_load.replace(file_identifier, '')
            file_to_load = os.path.normpath(file_to_load)
            notepad.open(file_to_load)
    
    editor.callback(callbackHOTSPOTRELEASECLICK, [SCINTILLANOTIFICATION.HOTSPOTRELEASECLICK])
    notepad.callback(callbackBUFFERACTIVATED, [NOTIFICATION.BUFFERACTIVATED])
    

    Cheers
    Claudia



  • @David-Miller229

    and if your program starts counting lines from 1 instead of 0 you need to
    change the line

    editor.gotoLine(int(line_in_file))
    

    to

    editor.gotoLine(int(line_in_file)-1)
    

    So, hopefully this was the last update today ;-)

    Cheers
    Claudia



  • @David-Miller229

    ok, one last change before going to bed.
    Maybe there is text before and after the link, in such a case
    it would break the logic. Here a solution which gets the link text only.
    Replace callbackHOTSPOT… with this one

    def callbackHOTSPOTRELEASECLICK(args):
        __pos = args['position']
        __style_id = editor.getStyleAt(__pos)
        __word_start = None
        __word_end = None
        
        _p = __pos
        while True:
            __word_start = _p
            if editor.getStyleAt(_p) != __style_id:
                break
            _p -= 1
    
        _p = __pos
        while True:
            if editor.getStyleAt(_p) != __style_id:
                __word_end = _p
                break
            _p += 1
        
        link_text = editor.getTextRange(__word_start, __word_end+1) 
        
        if file_identifier in link_text and position_seperator in link_text:
            global file_to_load
            global line_in_file
            file_to_load, line_in_file = link_text[link_text.find(file_identifier):].split(position_seperator)
            file_to_load = file_to_load.replace(file_identifier, '')
            file_to_load = os.path.normpath(file_to_load)
            notepad.open(file_to_load)
    

    Good Night.

    Cheers
    Claudia



  • Thank you! The last works perfect.


Log in to reply