Microsoft Language Server Protocol (LSP): A possible revolution for Notepad++?
-
@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. -
@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.
-
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 -
Now I’m thinking that, if I remember correctly, you have to use python 3.8. PS3 started with this version.
-
@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.
-
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 theprefer installed python libraries
from PS configuration dialog. -
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.
-
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 themjedi.preload_module(['os','sys', ...])
-
@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.
-
:-D - the same would happen to me if I try to write perl :-D
-
@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.:
У меня есть немного для php, js, vbs, 1C, MySql.
https://github.com/trdm/jn-npp-scripts -
@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.
-
let me have a look and play with it a bit.
-
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. -
@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.
-
@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. -
@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!
-
A plugin implementing an LSP client in Notepad++ has finally been released.
https://github.com/Ekopalypse/NppLspClient
Credits to @Ekopalypse for that.
I encourage everyone to give it a try and report back for any found issue.
-
-