|
| 1 | +""" |
| 2 | +Minimal ProXPL LSP prototype that highlights tokens prefixed with 'taint_' as `tainted`. |
| 3 | +This uses `pygls` as a demonstration. Install with `pip install -r requirements.txt`. |
| 4 | +""" |
| 5 | +from pygls.server import LanguageServer |
| 6 | +from pygls.lsp.types import (DidOpenTextDocumentParams, DidChangeTextDocumentParams, |
| 7 | + InitializeParams, TextDocumentItem, Position, |
| 8 | + Range, SemanticTokens, SemanticTokensLegend, |
| 9 | + SemanticTokensParams) |
| 10 | +from pygls.lsp.types import SemanticTokens, SemanticTokensParams |
| 11 | + |
| 12 | +import re |
| 13 | + |
| 14 | +SERVER = LanguageServer('proxpl-lsp', 'v0.1') |
| 15 | + |
| 16 | +TAINT_TOKEN_TYPE = 100 # custom type index (client must map) |
| 17 | + |
| 18 | +@SERVER.feature('textDocument/didOpen') |
| 19 | +def did_open(ls: LanguageServer, params: DidOpenTextDocumentParams): |
| 20 | + doc = params.text_document |
| 21 | + publish_taint_tokens(ls, doc.text, doc.uri) |
| 22 | + |
| 23 | +@SERVER.feature('textDocument/didChange') |
| 24 | +def did_change(ls: LanguageServer, params: DidChangeTextDocumentParams): |
| 25 | + doc = ls.workspace.get_document(params.text_document.uri) |
| 26 | + publish_taint_tokens(ls, doc.source, doc.uri) |
| 27 | + |
| 28 | +def publish_taint_tokens(ls: LanguageServer, text: str, uri: str): |
| 29 | + tokens = [] # as list of integers per LSP: [line, startChar, length, tokenType, tokenModifiers] |
| 30 | + for lineno, line in enumerate(text.splitlines()): |
| 31 | + for m in re.finditer(r"\btaint_[A-Za-z0-9_]+\b", line): |
| 32 | + start = m.start() |
| 33 | + length = m.end() - m.start() |
| 34 | + tokens.extend([lineno, start, length, TAINT_TOKEN_TYPE, 0]) |
| 35 | + st = SemanticTokens(data=tokens) |
| 36 | + # Semantic token publishing uses a custom notification for simplicity |
| 37 | + ls.show_message_log(f"Publishing {len(tokens)//5} taint tokens for {uri}") |
| 38 | + ls.send_notification('textDocument/semanticTokens', {'uri': uri, 'tokens': tokens}) |
| 39 | + |
| 40 | +def main(): |
| 41 | + import argparse |
| 42 | + parser = argparse.ArgumentParser() |
| 43 | + parser.add_argument('--tcp', action='store_true', help='Run LSP server over stdio (default)') |
| 44 | + args = parser.parse_args() |
| 45 | + SERVER.start_io() |
| 46 | + |
| 47 | +if __name__ == '__main__': |
| 48 | + main() |
0 commit comments