-
Notifications
You must be signed in to change notification settings - Fork 409
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7968f3d
commit 7552383
Showing
24 changed files
with
4,822 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
from typing import Any, List, Tuple | ||
import click | ||
from pygls.server import LanguageServer | ||
from pygls.workspace import TextDocument | ||
from lsprotocol import types as lsp_types | ||
|
||
from edb.tools.edb import edbcommands | ||
|
||
from edb.edgeql import tokenizer | ||
from edb.edgeql import parser as qlparser | ||
from edb.edgeql.parser.grammar import tokens as qltokens | ||
|
||
import edb._edgeql_parser as rust_parser | ||
|
||
|
||
@edbcommands.command("lsp") | ||
@click.option('--stdio', default=False, is_flag=True) | ||
def main(stdio: bool): | ||
ls = LanguageServer('EdgeDB Language Server', 'v0.1') | ||
|
||
@ls.feature( | ||
lsp_types.INITIALIZE, | ||
) | ||
def init(_params: lsp_types.InitializeParams): | ||
ls.show_message_log('Starting') | ||
qlparser.preload_spec() | ||
ls.show_message_log('Started') | ||
|
||
@ls.feature(lsp_types.TEXT_DOCUMENT_DID_OPEN) | ||
def text_document_did_open(params: lsp_types.DidOpenTextDocumentParams): | ||
ls.show_message_log(f'did open: {params.text_document.uri}') | ||
|
||
document = ls.workspace.get_text_document(params.text_document.uri) | ||
parse_and_report_diagnostics(document, ls) | ||
|
||
@ls.feature(lsp_types.TEXT_DOCUMENT_DID_CHANGE) | ||
def text_document_did_change(params: lsp_types.DidChangeTextDocumentParams): | ||
ls.show_message_log(f'did change: {params.text_document.uri}') | ||
|
||
document = ls.workspace.get_text_document(params.text_document.uri) | ||
parse_and_report_diagnostics(document, ls) | ||
|
||
@ls.feature( | ||
lsp_types.TEXT_DOCUMENT_COMPLETION, | ||
lsp_types.CompletionOptions(trigger_characters=[',']), | ||
) | ||
def completions(params: lsp_types.CompletionParams): | ||
items = [] | ||
|
||
document = ls.workspace.get_text_document(params.text_document.uri) | ||
|
||
if item := parse_and_suggest_keyword(document, params.position): | ||
items.append(item) | ||
|
||
return lsp_types.CompletionList(is_incomplete=False, items=items) | ||
|
||
if stdio: | ||
ls.start_io() | ||
|
||
|
||
def position_in_span(pos: lsp_types.Position, span: Tuple[Any, Any]): | ||
start, end = span | ||
|
||
if pos.line < start.line - 1: | ||
return False | ||
if pos.line > end.line - 1: | ||
return False | ||
if pos.line == start.line - 1 and pos.character < start.column - 1: | ||
return False | ||
if pos.line == end.line - 1 and pos.character > end.column - 1: | ||
return False | ||
return True | ||
|
||
|
||
def parse(doc: TextDocument) -> Tuple[tokenizer.Source, List[Any], Any]: | ||
sdl = False | ||
|
||
try: | ||
source = tokenizer.Source.from_string(doc.source) | ||
except Exception as e: | ||
# TODO | ||
print(e) | ||
return | ||
|
||
start_t = qltokens.T_STARTSDLDOCUMENT if sdl else qltokens.T_STARTBLOCK | ||
start_t_name = start_t.__name__[2:] | ||
tokens = source.tokens() | ||
|
||
result, productions = rust_parser.parse(start_t_name, tokens) | ||
return source, result, productions | ||
|
||
|
||
def parse_and_report_diagnostics(doc: TextDocument, ls: LanguageServer) -> None: | ||
source, result, _productions = parse(doc) | ||
|
||
if result.errors: | ||
diagnostics = [] | ||
for error in result.errors: | ||
message, span, hint, details = error | ||
|
||
if details: | ||
message += f"\n{details}" | ||
if hint: | ||
message += f"\nHint: {hint}" | ||
(start, end) = tokenizer.inflate_span(source.text(), span) | ||
|
||
diagnostics.append( | ||
lsp_types.Diagnostic( | ||
range=lsp_types.Range( | ||
start=lsp_types.Position( | ||
line=start.line - 1, | ||
character=start.column - 1, | ||
), | ||
end=lsp_types.Position( | ||
line=end.line - 1, | ||
character=end.column - 1, | ||
), | ||
), | ||
severity=lsp_types.DiagnosticSeverity.Error, | ||
message=message, | ||
) | ||
) | ||
|
||
ls.publish_diagnostics(doc.uri, diagnostics, doc.version) | ||
return | ||
|
||
ls.publish_diagnostics(doc.uri, [], doc.version) | ||
# parsing successful | ||
|
||
|
||
def parse_and_suggest_keyword(document, position) -> lsp_types.CompletionItem: | ||
source, result, _productions = parse(document) | ||
for error in result.errors: | ||
message: str | ||
message, span, hint, details = error | ||
if not message.startswith('Missing keyword '): | ||
continue | ||
(start, end) = tokenizer.inflate_span(source.text(), span) | ||
|
||
if not position_in_span(position, (start, end)): | ||
continue | ||
|
||
keyword = message.removeprefix('Missing keyword \'')[:-1] | ||
|
||
return lsp_types.CompletionItem( | ||
label=keyword, | ||
kind=lsp_types.CompletionItemKind.Keyword, | ||
) | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
node_modules/** | ||
client/node_modules/** | ||
client/out/** | ||
server/node_modules/** | ||
server/out/** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
/**@type {import('eslint').Linter.Config} */ | ||
// eslint-disable-next-line no-undef | ||
module.exports = { | ||
root: true, | ||
parser: '@typescript-eslint/parser', | ||
plugins: [ | ||
'@typescript-eslint', | ||
], | ||
extends: [ | ||
'eslint:recommended', | ||
'plugin:@typescript-eslint/recommended', | ||
], | ||
rules: { | ||
'semi': [2, "always"], | ||
'@typescript-eslint/no-unused-vars': 0, | ||
'@typescript-eslint/no-explicit-any': 0, | ||
'@typescript-eslint/explicit-module-boundary-types': 0, | ||
'@typescript-eslint/no-non-null-assertion': 0, | ||
} | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
out | ||
node_modules | ||
client/server | ||
.vscode-test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
.vscode/** | ||
**/*.ts | ||
**/*.map | ||
.gitignore | ||
**/tsconfig.json | ||
**/tsconfig.base.json | ||
contributing.md | ||
.travis.yml | ||
client/node_modules/** | ||
!client/node_modules/vscode-jsonrpc/** | ||
!client/node_modules/vscode-languageclient/** | ||
!client/node_modules/vscode-languageserver-protocol/** | ||
!client/node_modules/vscode-languageserver-types/** | ||
!client/node_modules/{minimatch,brace-expansion,concat-map,balanced-match}/** | ||
!client/node_modules/{semver,lru-cache,yallist}/** |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# LSP Example | ||
|
||
Heavily documented sample code for https://code.visualstudio.com/api/language-extensions/language-server-extension-guide | ||
|
||
## Functionality | ||
|
||
This Language Server works for plain text file. It has the following language features: | ||
- Completions | ||
- Diagnostics regenerated on each file change or configuration change | ||
|
||
It also includes an End-to-End test. | ||
|
||
## Structure | ||
|
||
``` | ||
. | ||
├── client // Language Client | ||
│ ├── src | ||
│ │ ├── test // End to End tests for Language Client / Server | ||
│ │ └── extension.ts // Language Client entry point | ||
├── package.json // The extension manifest. | ||
└── server // Language Server | ||
└── src | ||
└── server.ts // Language Server entry point | ||
``` | ||
|
||
## Running the Sample | ||
|
||
- Run `npm install` in this folder. This installs all necessary npm modules in both the client and server folder | ||
- Open VS Code on this folder. | ||
- Press Ctrl+Shift+B to start compiling the client and server in [watch mode](https://code.visualstudio.com/docs/editor/tasks#:~:text=The%20first%20entry%20executes,the%20HelloWorld.js%20file.). | ||
- Switch to the Run and Debug View in the Sidebar (Ctrl+Shift+D). | ||
- Select `Launch Client` from the drop down (if it is not already). | ||
- Press ▷ to run the launch config (F5). | ||
- In the [Extension Development Host](https://code.visualstudio.com/api/get-started/your-first-extension#:~:text=Then%2C%20inside%20the%20editor%2C%20press%20F5.%20This%20will%20compile%20and%20run%20the%20extension%20in%20a%20new%20Extension%20Development%20Host%20window.) instance of VSCode, open a document in 'plain text' language mode. | ||
- Type `j` or `t` to see `Javascript` and `TypeScript` completion. | ||
- Enter text content such as `AAA aaa BBB`. The extension will emit diagnostics for all words in all-uppercase. |
Oops, something went wrong.