Community
    • Login

    Microsoft Language Server Protocol (LSP): A possible revolution for Notepad++?

    Scheduled Pinned Locked Moved General Discussion
    38 Posts 7 Posters 9.8k 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.
    • Michael VincentM
      Michael Vincent @dail
      last edited by

      @dail

      Thank you for the feedback!

      EkopalypseE 1 Reply Last reply Reply Quote 0
      • EkopalypseE
        Ekopalypse @Michael Vincent
        last edited by

        I guess the most critical part of changing from 2 to 3 is how python handles strings and this is the
        reason that PS3 is still in alpha because, as far as I understand, this isn’t currently handled in boost::python.

        @Michael-Vincent
        not sure I understand correctly what you are trying to achieve.
        If you want to get auto completion and such stuff to work with python3 scripts in notepad++,
        you normally have to use PS3 plugin.
        Using PS2 plugin would mean you have to start a python3 process, run this in combination with a
        a jedi.py script which then parses your notepad++ .py file and outputs it findings to stdout which then is
        parsed back to PS2. Doesn’t sound desirable.

        Michael VincentM 1 Reply Last reply Reply Quote 2
        • Michael VincentM
          Michael Vincent @Ekopalypse
          last edited by

          @Ekopalypse said in Microsoft Language Server Protocol (LSP): A possible revolution for Notepad++?:

          If you want to get auto completion and such stuff to work with python3 scripts in notepad++,
          you normally have to use PS3 plugin.

          Yes, that’s my desire. I’m not interested in writing Notepad++ PythonScript automation, I’m interested in using Notepad++ to write scripts in version 3 of the Python language to be used for my work projects and while writing those scripts, I’d like to have some “Language Server” features (i.e., autocomplete, etc) above and beyond the standard Notepad++ “function name completion” through the use of a Language Server “plugin” (or in this case, PS3 adapted script capability you speak of).

          I hope that makes sense now in exhaustive detail. I realize the confusion around “PythonScript” and “python scripts” :-)

          Cheers.

          EkopalypseE 1 Reply Last reply Reply Quote 2
          • EkopalypseE
            Ekopalypse @Michael Vincent
            last edited by

            @Michael-Vincent
            ok, this is my “lsp” script (called python_ide.py)

            ''' Basic functionality which one would expect from a full-fledged IDE '''
            import sys
            from Npp import (
                editor, editor1, editor2, notepad,
                SCINTILLANOTIFICATION, NOTIFICATION, LANGTYPE
                )
            
            from pyflakes import api
            from pyflakes import reporter as rep
            import jedi
            jedi.preload_module(['os',])
            
            
            class IDE(rep.Reporter):
            
                def __init__(self):
                    rep.Reporter.__init__(self, sys.stdout, sys.stderr)
                    self.auto_indent = True
                    self.check_types = False
                    self.linting = True
                    self.reporter = None
                    self.is_python = False
                    self.STYLE_WARNING = 55
                    self.excluded_styles = [1, 3, 4, 6, 7, 12, 16, 17, 18, 19]
                    self.prev_completion_list = []
            
            
                def initialize(self):
                    editor1.styleSetFont(self.STYLE_WARNING, 'Consolas')
                    editor1.styleSetItalic(self.STYLE_WARNING,True)
                    editor1.styleSetFore(self.STYLE_WARNING, (200,160,12))
                    editor1.styleSetBack(self.STYLE_WARNING, notepad.getEditorDefaultBackgroundColor())
                    editor2.styleSetFont(self.STYLE_WARNING, 'Consolas')
                    editor2.styleSetItalic(self.STYLE_WARNING,True)
                    editor2.styleSetFore(self.STYLE_WARNING, (200,160,12))
                    editor2.styleSetBack(self.STYLE_WARNING, notepad.getEditorDefaultBackgroundColor())
                    editor.callbackSync(self.on_charadded, [SCINTILLANOTIFICATION.CHARADDED])
                    editor.callbackSync(self.on_modified, [SCINTILLANOTIFICATION.MODIFIED])
                    notepad.callback(self.on_buffer_activated, [NOTIFICATION.BUFFERACTIVATED])
                    notepad.callback(self.on_file_saved, [NOTIFICATION.FILESAVED])
                    self.is_python = notepad.getCurrentLang() == LANGTYPE.PYTHON
            
            
                def on_modified(self, args):
                    if args['modificationType'] & 0x100000 == 0x100000 and args['text'] in ['\r','\n', '\r\n']:
                        self._indent(args['position'], args['text'])
            
            
                def on_buffer_activated(self, args):
                    self.is_python = notepad.getCurrentLang() == LANGTYPE.PYTHON
                    self.path = notepad.getCurrentFilename()
            
            
                def on_file_saved(self, args):
                    if self.is_python:
                        editor.annotationClearAll()
                        self.lint()
            
            
                def on_charadded(self, args):
                    if self.is_python:
                        c = chr(args['ch'])
                        if c in '\r\n:-+*/#=)':
                            return
            
                        pos = editor.getCurrentPos()
                        if editor.getStyleAt(pos) in self.excluded_styles:
                            return
            
                        source = editor.getText() + c
                        line = editor.lineFromPosition(pos) + 1
                        column = editor.getColumn(pos)
            
                        if c == '(':
                            script = jedi.Script(code=source).get_signatures(line, column)
                            self.show_calltip(script, pos)
                        else:
                            if c == '.' and editor.callTipActive():
                                editor.callTipCancel()
                            script = jedi.Script(code=source).complete(line, column)
                            self.autocomplete(script, pos)
            
            
                def unexpectedError(self, filename, msg):
                    # HACK: to avoid encoding declaration in Unicode string error
                    if msg != 'problem decoding source':
                        print(f'File "{filename}", line {0}, unexpected error:{msg}')
            
            
                def syntaxError(self, filename, msg, lineno, offset, text):
                    line = text.splitlines()[-1]
                    if offset is not None:
                        offset -= (len(text) - len(line)) + 1
                    else:
                        offset=''
            
                    print(f'File "{filename}", line {lineno},{offset} Error:{msg}')
                    print(f'    => {line}')
                    self.show_error(filename,lineno, msg)
            
            
                def flake(self, message):
                    msg = message.message % message.message_args
                    for line_ignore_msg in ['# ignore_undefined_name', '# ignore_unused_import',]:
                        if line_ignore_msg in editor.getLine(message.lineno-1 if message.lineno>0 else message.lineno):
                            return
                    self.show_error(message.filename, message.lineno, msg)
            
            
                def show_error(self, filename, line_no, msg):
                    line_no = line_no-1 if line_no>0 else line_no
                    editor.annotationSetText(line_no, f'{msg}')
                    editor.annotationSetVisible(2)
                    editor.annotationSetStyle(line_no, self.STYLE_WARNING)
                    editor.ensureVisible(line_no)
                    editor.gotoLine(line_no)
                    editor.verticalCentreCaret()
            
            
                def lint(self):
                    if self.linting:
                        filename = notepad.getCurrentFilename()
                        if filename.endswith('.py'):
                            api.check(editor.getText(), filename, self)
            
            
                def autocomplete(self, completions, pos):
                    if completions:
                        completion_list = sorted([x.name for x in completions if not x.name.startswith('_')])
                        if completion_list:
                            if self.prev_completion_list != completion_list:
                                self.prev_completion_list = completion_list
                                word_start = editor.wordStartPosition(pos, True)
                                already_typed = pos - word_start
                                already_typed = 0 if already_typed < 0 else already_typed
                                editor.autoCShow(already_typed, ' '.join(completion_list))
            
            
                def show_calltip(self, call_tips, pos):
                    if call_tips:
                        tips = ', '.join([x.name for x in call_tips[0].params])
                        tips += '\n'
                        if tips.strip():
                            editor.callTipShow(pos, f' {tips[:-1]} ')
            
            
                def _indent(self, position, text):
                    if self.is_python and self.auto_indent:
                        indent = editor.getLineIndentation(editor.lineFromPosition(position))
                        if (editor.getCharAt(position-1) == 58 and  # 58 == :
                            editor.getStyleAt(position-1) not in self.excluded_styles):
                            tabwidth = editor.getTabWidth()
                            text += ' '*(indent//tabwidth+1)*tabwidth
                        else:
                            text += ' '*indent
                        editor.changeInsertion(text)  
            

            and this is what I have in user startup.py

            from python_ide import IDE
            __ide = IDE()
            __ide.initialize()
            

            You need to pip-install pyflakes and jedi and use the PS3 alpha version.
            A word of warning, although I don’t think it will affect you.
            I had crashes when using this together with Npp objects.
            As long as you only use standard Python scripts/modules, as you mentioned,
            you should not have this problem.

            Michael VincentM 2 Replies Last reply Reply Quote 2
            • Michael VincentM
              Michael Vincent @Ekopalypse
              last edited by

              @Ekopalypse said in Microsoft Language Server Protocol (LSP): A possible revolution for Notepad++?:

              You need to pip-install pyflakes and jedi and use the PS3 alpha version.

              Sorry for the following probably basic Python questions, but I’m getting this error when running my startup.py:

              Traceback (most recent call last):
                File "C:\usr\bin\npp64\plugins\PythonScript\scripts\startup.py", line 31, in <module>
                  from python_ide import IDE
                File "C:\usr\bin\npp64\plugins\PythonScript\scripts\python_ide.py", line 8, in <module>
                  from pyflakes import api
              ModuleNotFoundError: No module named 'pyflakes'
              

              I’m assuming this is because PyFlakes is not installed in my PythonScript3 distro. I did a:

              python3 -mpip install pyflakes jedi

              and it worked to install to my OS python:

              PS VinsWorldcom C:\usr\bin\npp64\plugins\PythonScript > python3 -V
              Python 3.6.3
              PS VinsWorldcom C:\usr\bin\npp64\plugins\PythonScript > python3
              Python 3.6.3 (v3.6.3:2c5fed8, Oct  3 2017, 18:11:49) [MSC v.1900 64 bit (AMD64)] on win32
              Type "help", "copyright", "credits" or "license" for more information.
              >>> import jedi
              >>> import pyflakes
              

              So I’m guessing I need to have PythonScript3 find my local Python3 libraries or install PyFlakes and Jedi in the PythonScript3 lib/ directory.

              Any tips?

              Cheers.

              EkopalypseE 2 Replies Last reply Reply Quote 0
              • EkopalypseE
                Ekopalypse @Michael Vincent
                last edited by

                @Michael-Vincent

                which PS3 plugin did you install? Depending on it you have to use the correct python version and copy
                the pythonXX.dll to the PS plugin directory.
                Like I have the latest PS, hence I use python 3.8

                0d58770e-7268-4d24-a723-d0f18267a42f-image.png

                Michael VincentM 1 Reply Last reply Reply Quote 1
                • EkopalypseE
                  Ekopalypse @Michael Vincent
                  last edited by

                  @Michael-Vincent

                  Now I’m thinking that, if I remember correctly, you have to use python 3.8. PS3 started with this version.

                  1 Reply Last reply Reply Quote 1
                  • Michael VincentM
                    Michael Vincent @Ekopalypse
                    last edited by

                    @Ekopalypse said in Microsoft Language Server Protocol (LSP): A possible revolution for Notepad++?:

                    which PS3 plugin did you install? Depending on it you have to use the correct python version and copy
                    the pythonXX.dll to the PS plugin directory.

                    I installed v3.0.4 and have the DLL for python 3.8 (that was bundled with the above ZIP) already in place:

                    PS VinsWorldcom C:\usr\bin\npp64\plugins\PythonScript > ls -al
                    total 5680
                    drwxr-x---  1 VinsWorldcom                0 Nov  5 15:00 .\
                    drwxr-x---  1 VinsWorldcom                0 Nov  5 15:00 ..\
                    drwxr-x---  1 VinsWorldcom                0 Sep  7 07:33 doc\
                    drwxr-x---  1 VinsWorldcom                0 Nov  5 14:27 lib\
                    -rw-r----a  1 VinsWorldcom          4209224 Jul 20 16:06 python38.dll
                    -rw-r----a  1 VinsWorldcom          1606144 Sep  7 07:31 PythonScript.dll
                    drwxr-x---  1 VinsWorldcom                0 Nov  5 14:34 scripts\
                    

                    Am I to understand that I need to upgrade my OS Python3.6 version to match the 3.8 for PythonScript?

                    Cheers.

                    EkopalypseE 1 Reply Last reply Reply Quote 0
                    • EkopalypseE
                      Ekopalypse @Michael Vincent
                      last edited by

                      @Michael-Vincent

                      Yes, and copy the pythonXX.dll from the installation to C:\usr\bin\npp64\plugins\PythonScript
                      then PS sees all your pip installed modules.
                      In addition, check the prefer installed python libraries from PS configuration dialog.

                      b83b6798-9b58-47cf-9c5c-ab5bc64325ed-image.png

                      Michael VincentM 1 Reply Last reply Reply Quote 1
                      • Michael VincentM
                        Michael Vincent @Ekopalypse
                        last edited by

                        @Ekopalypse

                        Thanks - I actually got it working by copying PyFlakes, Jedi (and Parso which seemed to be a Jedi dependency) install directories from my system Python3 to the PythonScript/lib folder.

                        Autocompletion now works for “objects”:

                        import socket
                        
                        addrs = socket.getaddrinfo("localhost", 12345, socket.AF_UNSPEC, socket.SOCK_DGRAM)
                        sock = None
                        
                        for addr in addrs:
                            af, socktype, proto, cname, sa = addr
                        
                            try:
                                sock = socket.socket(af, socktype, proto)
                        [...]
                        

                        and then typing:

                        sock. brings up a list of all methods I can call on the socket object - but it’s slow - noticeably slow in populating that autocomplete list.

                        Pretty cool though! I’ll do some more playing with this and thanks for the script and help setting it up.

                        Cheers.

                        EkopalypseE 1 Reply Last reply Reply Quote 0
                        • EkopalypseE
                          Ekopalypse @Michael Vincent
                          last edited by

                          @Michael-Vincent

                          it should be slow only for the first time it discovers an object. If you use certain objects more often
                          then others, you could try to preload them jedi.preload_module(['os','sys', ...])

                          Michael VincentM 1 Reply Last reply Reply Quote 1
                          • Michael VincentM
                            Michael Vincent @Ekopalypse
                            last edited by

                            @Ekopalypse said in Microsoft Language Server Protocol (LSP): A possible revolution for Notepad++?:

                            preload them jedi.preload_module([‘os’,‘sys’, …])

                            Thanks for the tip! Python is obviously not my strength; hence, the want for some “IDE-lite” help from Notepad++.

                            Cheers.

                            EkopalypseE 1 Reply Last reply Reply Quote 1
                            • EkopalypseE
                              Ekopalypse @Michael Vincent
                              last edited by

                              @Michael-Vincent

                              :-D - the same would happen to me if I try to write perl :-D

                              1 Reply Last reply Reply Quote 1
                              • TroshinDVT
                                TroshinDV
                                last edited by

                                @pidgeon777 said in Microsoft Language Server Protocol (LSP): A possible revolution for Notepad++?:

                                Microsoft Language Server Protocol (LSP) defines the protocol used between an editor or IDE and a language server that provides language features like autocomplete, go to definition, find all references etc.:

                                Screenshot_270.png
                                У меня есть немного для php, js, vbs, 1C, MySql.
                                https://github.com/trdm/jn-npp-scripts

                                1 Reply Last reply Reply Quote 0
                                • Michael VincentM
                                  Michael Vincent @Ekopalypse
                                  last edited by

                                  @Ekopalypse said in Microsoft Language Server Protocol (LSP): A possible revolution for Notepad++?:

                                  ok, this is my “lsp” script (called python_ide.py)

                                  I wonder if this pylspclient can be used? It’s supposedly an LSP client written in Python so theoretically PythonScript could run it.

                                  I’ve been playing with it from the command line with my system Python3 and not having much luck with the examples/python-language-server.py script included and having the python-language-server installed.

                                  Cheers.

                                  EkopalypseE 2 Replies Last reply Reply Quote 1
                                  • EkopalypseE
                                    Ekopalypse @Michael Vincent
                                    last edited by

                                    @Michael-Vincent

                                    let me have a look and play with it a bit.

                                    1 Reply Last reply Reply Quote 1
                                    • EkopalypseE
                                      Ekopalypse @Michael Vincent
                                      last edited by Ekopalypse

                                      @Michael-Vincent

                                      It looks like the main problem might be that this lspclient is written for Python2.
                                      Let me see if I can make a working version. Probably in 6-7 hours.

                                      Michael VincentM 1 Reply Last reply Reply Quote 0
                                      • Michael VincentM
                                        Michael Vincent @Ekopalypse
                                        last edited by

                                        @Ekopalypse said in Microsoft Language Server Protocol (LSP): A possible revolution for Notepad++?:

                                        lspclient is written for Python2

                                        Weird - it seems to “run” OK - not throwing any errors, but just not initializing with the language server:

                                        PS VinsWorldcom ~\source\personal\pylspclient\examples > python3 .\python-language-server.py
                                        2020-12-02 10:19:26,251 UTC - WARNING - pyls.config.config - Failed to load pyls entry point 'autopep8': No module named 'pycodestyle'
                                        
                                        2020-12-02 10:19:26,297 UTC - WARNING - pyls.config.config - Failed to load pyls entry point 'pycodestyle': No module named 'pycodestyle'
                                        
                                        2020-12-02 10:19:26,301 UTC - WARNING - pyls.config.config - Failed to load pyls entry point 'pydocstyle': No module named 'pydocstyle'
                                        
                                        2020-12-02 10:19:26,492 UTC - WARNING - pyls.config.config - Failed to load pyls entry point 'rope_completion': No module named 'rope'
                                        
                                        2020-12-02 10:19:26,496 UTC - WARNING - pyls.config.config - Failed to load pyls entry point 'rope_rename': No module named 'rope'
                                        
                                        {'capabilities': {'codeActionProvider': True, 'codeLensProvider': {'resolveProvider': False}, 'completionProvider': {'resolveProvider': False, 'triggerCharacters': ['.']}, 'documentFormattingProvider': True, 'documentHighlightProvider': True, 'documentRangeFormattingProvider': True, 'documentSymbolProvider': True, 'definitionProvider': True, 'executeCommandProvider': {'commands': []}, 'hoverProvider': True, 'referencesProvider': True, 'renameProvider': True, 'foldingRangeProvider': True, 'signatureHelpProvider': {'triggerCharacters': ['(', ',', '=']}, 'textDocumentSync': {'change': 2, 'save': {'includeText': True}, 'openClose': True}, 'workspace': {'workspaceFolders': {'supported': True, 'changeNotifications': True}}, 'experimental': {}}}
                                        init =  None
                                        

                                        NOTE: I made 2 small changes to the the examples/python-language-server.py script:

                                        diff --git a/examples/python-language-server.py b/examples/python-language-server.py
                                        index 72026e5..0e67858 100644
                                        --- a/examples/python-language-server.py
                                        +++ b/examples/python-language-server.py
                                        @@ -19,7 +19,7 @@ class ReadPipe(threading.Thread):
                                                     line = self.pipe.readline().decode('utf-8')
                                        
                                         if __name__ == "__main__":
                                        -    pyls_cmd = ["python", "-m", "pyls"]
                                        +    pyls_cmd = ["python3", "-m", "pyls"]
                                             p = subprocess.Popen(pyls_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                                             read_pipe = ReadPipe(p.stderr)
                                             read_pipe.start()
                                        @@ -143,7 +143,7 @@ if __name__ == "__main__":
                                             root_uri = 'file:///path/to/python/project'
                                             workspace_folders = [{'name': 'python-lsp', 'uri': root_uri}]
                                             print(lsp_client.initialize(p.pid, None, root_uri, None, capabilities, "off", workspace_folders))
                                        -    print(lsp_client.initialized())
                                        +    print("init = ", lsp_client.initialized())
                                        
                                             lsp_client.shutdown()
                                             lsp_client.exit()
                                        

                                        Cheers.

                                        1 Reply Last reply Reply Quote 0
                                        • EkopalypseE
                                          Ekopalypse
                                          last edited by

                                          @Michael-Vincent - I tried to make it work but after three
                                          Npp crashes I decided to make my implementation work with python3.
                                          I’ll keep working on it an updates will be made available via the github repo.

                                          Michael VincentM 1 Reply Last reply Reply Quote 2
                                          • Michael VincentM
                                            Michael Vincent @Ekopalypse
                                            last edited by

                                            @Ekopalypse said in Microsoft Language Server Protocol (LSP): A possible revolution for Notepad++?:

                                            I’ll keep working on it an updates will be made available via the github repo.

                                            I’ll keep checking back then - thanks!

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