Navigation

    Community

    • Login
    • Search
    • Recent
    • Tags
    • Popular
    • Users
    • Groups
    • Search

    Linked text to open other files into Notepad++

    General Discussion
    2
    5
    296
    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.
    • Alan Kilborn
      Alan Kilborn last edited by

      Let’s pick up where THIS POSTING left off. While that posting showed how to obtain clickable text, it didn’t really show a complete example of a good usage for it. Hopefully this current posting will. So let’s get going…

      Notepad++ by default makes it possible to embed links to other text files in your text by using a special syntax, e.g.:

      file://w:\junk\test.txt

      which will appear like this in a Notepad++ tab:

      Imgur

      (double-click that text, and–maybe–the file specified after file:// will open)

      This might be useful if I want to embed links to other files in a Notepad++ tab file, for easy opening of those files into Notepad++.

      However, there are some limitations to the file:// technique:

      • I don’t “associate” .txt files with Notepad++ at the OS level (and I don’t want to), so double-clicking the file: link I show above opens the test.txt file in Notepad.exe

      • If an extension on a file: linked file is “unknown” to Windows, it won’t know how to open it when the link is double-clicked

      • File paths with spaces in them are problematic, e.g. file://w:\junk\test but I have spaces.txt would look for this file: w:\junk\test instead of the desired file…and the link text just doesn’t give one any confidence:

      Imgur

      • Even if the file is opened into Notepad++, there isn’t a way to move the caret to a specific line automatically

      I’ll present a technique that addresses all of the identified issues.

      Imagine writing such file links in any of the following ways (note that the “lead-in” is edit:, not file:):

      1. edit:w:\junk\test.txt
      2. edit:test.txt
      3. edit:…\junk\test.txt
      4. edit:w:\junk\test.txt(L3,C2)
      5. edit:w:\junk\test.txt(L-1)
      6. edit:w:\junk\test%20but%20I%20have%20spaces.txt
      7. edit:w:/junk/test.txt

      After implementing the total solution, the links appear like this in your Notepad++ tab and are “followed” (meaning that the specified file will be opened by Notepad++) by pressing Alt+LeftClick with the mouse (NOT double-clicking):

      Imgur

      Here are some corresponding notes, point by point:

      1. typical, general use case
      2. test.txt to be located in the same folder as the currently active Notepad++ tab file
      3. test.txt to be located using a relative path from the currently active Notepad++ tab file
      4. after loading test.txt into Notepad++, move the caret to line 3 column 2
      5. after loading test.txt into Notepad++, move the caret to the last line of the file
      6. load a file with spaces in its path/name (note that spaces are replaced by %20 in the link text)
      7. same as point 1 but using forward slashes rather than backslashes as pathname component separators

      To achieve the goal, we need a PythonScript, shown below, and a configuration change.

      The configuration change is:

      • add edit to the URI customized schemes: box on the Cloud & Link tab in the Preferences:

      Imgur

      Somewhat obviously, you’ll also need Clickable Link Settings’s Enable box ticked, as is also shown above.

      Some limitations of the approach:

      • custom syntax, i.e., edit:
      • Alt+Lclick link text instead of double-Lclick
      • (obviously) requires some effort in the setup

      Anyway, enough is enough, let’s present the code, I call the script UriIndicatorAltClick.py:

      # -*- coding: utf-8 -*-
      
      from Npp import *
      import os
      import re
      
      class UIAC(object):
      
          def __init__(self):
              self.URL_INDIC = 8  # URL_INDIC is used in N++ source code
              self.ALT_MODIFIER = 4
              self.backslash = '\\' ; self.two_backslashes = self.backslash * 2
              self.alt_held_at_click = False
              self.installed = False
              self.install()
      
          def install(self):
              if not self.installed:
                  # https://www.scintilla.org/ScintillaDoc.html#SCN_INDICATORCLICK
                  editor.callback(self.indicator_click_callback, [SCINTILLANOTIFICATION.INDICATORCLICK])
                  # https://www.scintilla.org/ScintillaDoc.html#SCN_INDICATORRELEASE
                  editor.callback(self.indicator_release_callback, [SCINTILLANOTIFICATION.INDICATORRELEASE])
                  self.installed = True
      
          def uninstall(self):
              if self.installed:
                  editor.clearCallbacks(self.indicator_click_callback)
                  editor.clearCallbacks(self.indicator_release_callback)
                  self.installed = False
      
          def is_installed(self):
              return self.installed
      
          def mb(self, msg, flags=0, title=''):
              return notepad.messageBox(msg, title, flags)
      
          def get_indicator_range(self, indic_number):
              # similar to ScintillaEditView::getIndicatorRange() in N++ source
              # https://github.com/notepad-plus-plus/notepad-plus-plus/blob/8f38707d33d869a5b8f5014dbb18619b166486a0/PowerEditor/src/ScitillaComponent/ScintillaEditView.h#L562
              curr_pos = editor.getCurrentPos()
              indic_mask = editor.indicatorAllOnFor(curr_pos)
              if (indic_mask & (1 << indic_number)) != 0:
                  start_pos = editor.indicatorStart(indic_number, curr_pos)
                  end_pos = editor.indicatorEnd(indic_number, curr_pos)
                  if curr_pos >= start_pos and curr_pos <= end_pos:
                      return (start_pos, end_pos)
              return (0, 0)
      
          def indicator_click_callback(self, args):
              # example: INDICATORCLICK: {'position': 12294, 'idFrom': 0, 'modifiers': 4, 'code': 2023, 'hwndFrom': 1577146}
              #print('UriIndicatorAltClick indicator click callback')
              self.alt_held_at_click = (args['modifiers'] & self.ALT_MODIFIER) != 0
      
          def indicator_release_callback(self, args):
      
              # example: INDICATORRELEASE: {'position': 12294, 'idFrom': 0, 'modifiers': 0, 'code': 2024, 'hwndFrom': 1577146}
      
              #print('UriIndicatorAltClick indicator release callback')
      
              if not self.alt_held_at_click: return
              self.alt_held_at_click = False
      
              (start_pos, end_pos) = self.get_indicator_range(self.URL_INDIC)
              if start_pos == end_pos:  return  # if click on indicator that is not URL_INDIC
      
              uri_text = editor.getTextRange(start_pos, end_pos)
      
              (uri_scheme, _, uri_path) = uri_text.partition(':')
      
              uri_path = uri_path.replace('%20', ' ').replace('%24', '$').replace('/', self.backslash)
      
              # check for optional syntax at end:   edit:....txt(L127,C12)
              goto_line = goto_col = 0
              m = re.search(r'\(L(-?\d+)(?:,C(\d+))?\)$', uri_path)
              if m:
                  uri_path = uri_path[:-len(m.group())]
                  goto_line = int(m.group(1))
                  if m.group(2): goto_col = int(m.group(2))
      
              if not os.path.isfile(uri_path):
                  # look for a relative path, relative to currently active document
                  try:
                      (valid_dir_of_active_doc, _) = notepad.getCurrentFilename().rsplit(os.sep, 1)
                  except ValueError:
                      # we started out in a "new 1" file, no path on that whatsoever
                      self.mb('Cannot find file:\r\n\r\n{}'.format(uri_path))
                      return
                  test_path_in_active_doc_dir = valid_dir_of_active_doc + os.sep + uri_path
                  if os.path.isfile(test_path_in_active_doc_dir):
                      uri_path = test_path_in_active_doc_dir
                  else:
                      (test_dir, test_filename) = test_path_in_active_doc_dir.rsplit(os.sep, 1)
                      if os.path.isdir(test_dir):
                          expanded_test_dir = os.path.abspath(test_dir)
                          if expanded_test_dir != test_dir:
                              self.mb('Cannot find file:\r\n\r\n{}\r\n\r\nLooked in this dir:\r\n\r\n{}'.format(test_filename, expanded_test_dir))
                              return
                      self.mb('Cannot find file:\r\n\r\n{}'.format(uri_path))
                      return
      
              notepad.open(uri_path)
      
              if goto_line != 0:
                  if goto_line == -1: goto_line = editor.getLineCount()
                  goto_line -= 1
                  if goto_col != 0:
                      goto_col_pos = editor.findColumn(goto_line, goto_col)
                      editor.gotoPos(goto_col_pos)
                  else:
                      editor.gotoLine(goto_line)
      
      if __name__ == '__main__':
      
          if 'uiac' not in globals():
              uiac = UIAC()  # will automatically "install" it
          else:
              # each running the script toggles install/uninstall:
              uiac.uninstall() if uiac.is_installed() else uiac.install()
              print('uiac installed?:', uiac.is_installed())
      

      It can be set up to run automatically from user startup.py by adding the following two lines there:

      import UriIndicatorAltClick
      uiac = UriIndicatorAltClick.UIAC()
      
      PeterJones 1 Reply Last reply Reply Quote 3
      • PeterJones
        PeterJones @Alan Kilborn last edited by PeterJones

        @alan-kilborn ,

        Your screenshot shows edit in the config box, instead of the edit: I expected. It works whether I do either, but if I have edit, then text like editing:code will show up as underlined,
        ad13dcd3-e0d7-4fa7-8214-b8f060ee6cf8-image.png
        whereas if I have edit: in the box, editing:code won’t think it’s a link
        88c436bd-ff30-4a54-9a22-bada97a86730-image.png

        But other than that, pretty cool implementation. I’ve added it, and I’ll see if it helps my workflow…

        Alan Kilborn 1 Reply Last reply Reply Quote 3
        • Alan Kilborn
          Alan Kilborn @PeterJones last edited by

          @peterjones said in Linked text to open other files into Notepad++:

          edit in the config box, instead of the edit:

          That’s a good point, not sure why I used the colonless version when I composed the post. My real implemenation uses the colon. :-P

          1 Reply Last reply Reply Quote 2
          • Alan Kilborn
            Alan Kilborn last edited by

            As a side note, if you work a lot with pathnames that contain spaces and you want to use the above technique, it could be helpful to record a macro for the following Replace All operation:

            228e7973-67fb-46dc-abb5-2bd490e11435-image.png

            The little blue rectangle in the Find what: box is a single space.

            After recording and saving the macro, when you are dealing with a pathname with a space and it is problematic as a link because of the spaces, e.g.:

            31c74096-b009-41cd-8447-d572ef64c211-image.png

            Just select it:

            39260356-70e1-4bc5-afc0-e00e0ab42c45-image.png

            And run the macro, to obtain:

            fdd19d4a-5cb4-4bfb-9475-55ab85facf6e-image.png

            Kinda basic stuff, but maybe worth explicitly pointing out…

            1 Reply Last reply Reply Quote 3
            • Alan Kilborn
              Alan Kilborn last edited by

              I should also mention that I didn’t show an example of network paths, but they work fine with this technique as well.

              For example, the text for this cheesy network path:

              edit:\\127.0.0.1\w$\junk\test.txt

              becomes a nice alt+clickable link that works and appears as you’d expect in a N++ tab:

              1b1a8003-c2c9-4cbf-9a8c-656fcc24b88d-image.png

              1 Reply Last reply Reply Quote 3
              • First post
                Last post
              Copyright © 2014 NodeBB Forums | Contributors