Moved vscode to own repo

This commit is contained in:
thomashii 2021-06-26 19:27:15 +08:00
parent fa25803e50
commit 92d3000033
37 changed files with 0 additions and 9226 deletions

View file

@ -1,4 +0,0 @@
out
node_modules
.vscode-test/
*.vsix

View file

@ -1,36 +0,0 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Extension",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"preLaunchTask": "npm: watch"
},
{
"name": "Extension Tests",
"type": "extensionHost",
"request": "launch",
"runtimeExecutable": "${execPath}",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}",
"--extensionTestsPath=${workspaceFolder}/out/test"
],
"outFiles": [
"${workspaceFolder}/out/test/**/*.js"
],
"preLaunchTask": "npm: watch"
}
]
}

View file

@ -1,9 +0,0 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
}
}

View file

@ -1,20 +0,0 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View file

@ -1,9 +0,0 @@
.vscode/**
.vscode-test/**
out/test/**
out/**/*.map
src/**
.gitignore
tsconfig.json
vsc-extension-quickstart.md
tslint.json

View file

@ -1,7 +0,0 @@
# Change Log
All notable changes to the "angel-dart-vscode" extension will be documented in this file.
Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
## [Unreleased]
- Initial release

View file

@ -1,21 +0,0 @@
MIT License (MIT)
Copyright (c) 2021 dukefirehawk.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,13 +0,0 @@
# Angel Framework support for VSCode
![The Angel Framework](https://angel-dart.github.io/assets/images/logo.png)
This extension provides IDE support within VSCode for the
[Angel](https://angel-dart.github.io)
Web server framework. The goal of this package is to improve
the overall development experience when working with Angel.
## Features
- Dart snippets for Angel framework code
- Syntax highlighting for the Jael templating engine
- **COMING SOON:** Language server support for Jael

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

File diff suppressed because it is too large Load diff

View file

@ -1,85 +0,0 @@
{
"name": "angel-dart-vscode",
"displayName": "Angel",
"description": "Snippets and IDE support for the Angel server framework within VSCode.",
"version": "0.2.0",
"repository": {
"type": "git",
"url": "https://github.com/angel-dart/vscode"
},
"icon": "media/logo.png",
"publisher": "thosakwe0541",
"engines": {
"vscode": "^1.28.0"
},
"categories": [
"Snippets"
],
"keywords": [
"angel",
"angel-dart",
"dart",
"jael",
"template",
"templating",
"flutter",
"fuchsia"
],
"activationEvents": [
"onLanguage:jael"
],
"main": "./out/extension",
"contributes": {
"_commands": [
{
"command": "extension.sayHello",
"title": "Hello World"
}
],
"languages": [
{
"id": "jael",
"aliases": [
"Jael"
],
"extensions": [
".jael"
],
"configuration": "./syntaxes/jael-language-configuration.json"
}
],
"grammars": [
{
"language": "jael",
"scopeName": "source.jael",
"path": "./syntaxes/jael.json"
}
],
"snippets": [
{
"language": "dart",
"path": "./snippets/angel.json"
},
{
"language": "jael",
"path": "./snippets/jael.json"
}
]
},
"scripts": {
"vscode:prepublish": "npm run compile",
"compile": "tsc -p ./",
"watch": "tsc -watch -p ./",
"postinstall": "node ./node_modules/vscode/bin/install",
"test": "npm run compile && node ./node_modules/vscode/bin/test"
},
"devDependencies": {
"@types/mocha": "^2.2.42",
"@types/node": "^7.0.43",
"typescript": "^2.6.1",
"vscode": "^1.1.6"
},
"dependencies": {
"vscode-languageclient": "^5.1.1"
}
}

View file

@ -1,115 +0,0 @@
{
".source.dart": {
"controller": {
"prefix": "angel:controller",
"description": "Generate an Angel Controller.",
"body": [
"import 'package:angel_framework/angel_framework.dart';",
"",
"@Expose('/$1')",
"class $1Controller extends Controller {",
" @Expose('/')",
" String hello() {",
" return 'Hello, world';",
" }",
" $2",
"}"
]
},
"websocket_controller": {
"prefix": "angel:websocket_controller",
"description": "Generate an Angel WebSocketController.",
"body": [
"import 'package:angel_websocket/server.dart';",
"",
"class $1Controller extends WebSocketController {",
" $1Controller(AngelWebSocket ws) : super(ws);",
"",
" @ExposeWs('ping')",
" void hello(WebSocketContext socket) {",
" socket.send('pong', {'message': 'Hello, world!'});",
" }",
" $2",
"}"
]
},
"model": {
"prefix": "angel:model",
"description": "Generate an Angel Model.",
"body": [
"import 'package:angel_model/angel_model.dart';",
"import 'package:angel_serialize/angel_serialize.dart';",
"part '$2.g.dart';",
"part '$2.serializer.g.dart';",
"",
"@serializable",
"abstract class _$1 extends Model {",
" $3",
"}"
]
},
"migration": {
"prefix": "angel:migration",
"description": "Generate an Angel ORM Migration.",
"body": [
"import 'package:angel_migration.dart/angel_migration.dart';",
"",
"class $1Migration extends Migration {",
" @override",
" void up(Schema schema) {",
" schema.create('$2', (table) {",
" table.serial('id').primaryKey();",
" table.date('created_at');",
" table.date('updated_at');",
" $3",
" });",
" }",
"",
" @override",
" void down(Schema schema) {",
" schema.drop('$2');",
" }",
"}"
]
},
"plugin": {
"prefix": "angel:plugin",
"description": "Generate an Angel plugin.",
"body": [
"import 'package:angel_framework/angel_framework.dart';",
"",
"AngelConfigurer $1() {",
" return (Angel app) async {",
" // Work some magic...",
" $2",
" };",
"}"
]
},
"test_driver": {
"prefix": "angel:test",
"description": "Generate an Angel test driver.",
"body": [
"import 'dart:io';",
"import 'package:$1/$1.dart' as $1;",
"import 'package:angel_framework/angel_framework.dart';",
"import 'package:angel_test/angel_test.dart';",
"import 'package:test/test.dart';",
"",
"main() async {",
" TestClient client;",
"",
" setUp(() async {",
" var app = new Angel();",
" await app.configure($1.configureServer);",
" client = await connectTo(app);",
" });",
"",
" tearDown(() => client.close());",
"",
" // Add your tests here...",
"}"
]
}
}
}

View file

@ -1,53 +0,0 @@
{
".source.jael": {
"block": {
"prefix": "block",
"description": "Insert a named <block> tag.",
"body": ["<block name=\"$1\">", " $2", "</block>"]
},
"comment": {
"prefix": "comment",
"description": "Insert a comment.",
"body": ["<!-- $1 -->"]
},
"declare": {
"prefix": "declare",
"description": "Insert a <declare> tag.",
"body": ["<declare $1=$2>", " $3", "</declare>"]
},
"element": {
"prefix": "element",
"description": "Insert a custom <element> tag.",
"body": ["<element name=\"$1\">", " $2", "</element>"]
},
"extend": {
"prefix": "extend",
"description": "Insert an <extend> tag.",
"body": ["<extend src=\"$1\">", " $2", "</extend>"]
},
"for-each": {
"prefix": "for-each",
"description": "Insert a <for-each> tag.",
"body": ["<${1:div} for-each=$2 as=\"$3\">", " $4", "</$1>"]
},
"include": {
"prefix": "include",
"description": "Insert an <include /> tag.",
"body": ["<include src=\"$1\"/>"]
},
"switch": {
"prefix": "switch",
"description": "Insert a <switch> tag.",
"body": [
"<switch value=$1>",
" <case value=$2>",
" $3",
" </case>",
" <default>",
" $4",
" </default>",
"</switch>"
]
}
}
}

View file

@ -1,40 +0,0 @@
"use strict";
import * as vscode from "vscode";
import { workspace } from "vscode";
import {
LanguageClient,
LanguageClientOptions,
ServerOptions,
TransportKind,
Executable
} from "vscode-languageclient";
export function activate(context: vscode.ExtensionContext) {
const runOpts: Executable = {
command: "pub",
args: ["global", "run", "jael_language_server"]
};
const serverOptions: ServerOptions = {
run: runOpts,
debug: runOpts,
transport: TransportKind.stdio
};
const clientOptions: LanguageClientOptions = {
documentSelector: [
{
scheme: "file",
language: "jael"
}
],
synchronize: {
configurationSection: "jael",
fileEvents: workspace.createFileSystemWatcher("**/.jael")
}
};
const lsp = new LanguageClient("jael", "Jael", serverOptions, clientOptions);
context.subscriptions.push(lsp.start());
}
export function deactivate() {}

View file

@ -1,28 +0,0 @@
{
"attribution-notice": {
"author-of-most-of-this-file": "Danny Tuppeny",
"source": "https://github.com/Dart-Code/Dart-Code/blob/master/syntaxes/dart.json"
},
"comments": {
"blockComment": ["<!--", "-->"]
},
"brackets": [["{", "}"], ["[", "]"], ["(", ")"], ["<", ">"]],
"autoClosingPairs": [
{ "open": "{", "close": "}" },
{ "open": "[", "close": "]" },
{ "open": "(", "close": ")" },
{ "open": "'", "close": "'", "notIn": ["string", "comment"] },
{ "open": "\"", "close": "\"", "notIn": ["string"] },
{ "open": "`", "close": "`", "notIn": ["string", "comment"] },
{ "open": "/**", "close": " */", "notIn": ["string"] }
],
"surroundingPairs": [
["{", "}"],
["[", "]"],
["(", ")"],
["<", ">"],
["'", "'"],
["\"", "\""],
["`", "`"]
]
}

View file

@ -1,172 +0,0 @@
{
"fileTypes": ["jael"],
"name": "Jael",
"scopeName": "source.jael",
"patterns": [
{
"include": "#expressions"
},
{
"match": "\\b(DOCTYPE|doctype)\\b",
"name": "keyword.doctype.jael"
},
{
"match": "[:,\\.]",
"name": "punctuation.jael"
},
{
"begin": "{{-?",
"end": "}}",
"beginCaptures": {
"0": {
"name": "keyword.operator.jael"
}
},
"endCaptures": {
"0": {
"name": "keyword.operator.jael"
}
},
"patterns": [{ "include": "#expressions" }, { "include": "#operators" }]
},
{
"begin": "<\\s*(script)[^>]*>",
"end": "(.*)<\\s*/\\s*(script)[^>]*>",
"beginCaptures": {
"1": {
"name": "keyword.tag.embedded.js.jael"
}
},
"endCaptures": {
"1": {
"name": "source.js",
"contentName": "source.js"
},
"2": {
"name": "keyword.tag.embedded.js.jael"
}
},
"contentName": "source.js",
"patterns": [{ "include": "source.js" }]
},
{
"begin": "<\\s*(style)[^>]*>",
"end": "<\\s*/\\s*(style)[^>]*>",
"beginCaptures": {
"1": {
"name": "keyword.tag.embedded.css.jael"
}
},
"endCaptures": {
"1": {
"name": "keyword.tag.embedded.css.jael"
}
},
"contentName": "source.css",
"patterns": [{ "include": "source.css" }]
},
{
"match": "\\b(block|declare|for-each|extend|if|include|switch)\\b",
"name": "keyword.control.jael"
},
{
"match": "<\\s*/?\\s*([A-Za-z_][A-Za-z0-9_-]*)(\\s*[A-Za-z_][A-Za-z0-9_]*(?!=)\\s*)*\\b",
"captures": {
"1": {
"name": "keyword.tag.jael"
},
"2": {
"name": "storage.name.jael"
}
}
},
{
"match": "@[A-Za-z_][A-Za-z0-9_]*",
"name": "storage.argument.jael"
},
{
"match": "\\b(@?[A-Za-z_][A-Za-z0-9_]*)=",
"captures": {
"1": {
"name": "storage.name.jael"
}
}
},
{
"captures": {
"0": {
"name": "punctuation.definition.comment.jael"
}
},
"begin": "<!--",
"end": "-->",
"name": "comment.jael"
},
{
"include": "#operators"
},
{
"include": "source.html"
}
],
"repository": {
"operators": {
"patterns": [
{
"match": "((!?=)|\\+|\\*|-|/|\\?|(\\?\\.)|%|\\[|\\])",
"name": "keyword.operator.jael"
}
]
},
"expressions": {
"patterns": [
{
"match": "\\b[<>]\\b",
"name": "keyword.operator.jael"
},
{
"begin": "'",
"end": "'",
"name": "string.quoted.single.jael",
"patterns": [
{
"name": "constant.character.escape.jael",
"match": "\\\\[bfnrt']"
}
]
},
{
"begin": "\"",
"end": "\"",
"name": "string.quoted.double.jael",
"patterns": [
{
"name": "constant.character.escape.jael",
"match": "\\\\[bfnrt\"]"
}
]
},
{
"match": "\\b(true|false|null)\\b",
"name": "constant.language.jael"
},
{
"match": "\\b-?[0-9]+(\\.[0-9]+)?([Ee][0-9]+)?\\b",
"name": "constant.numeric.jael"
},
{
"match": "\\b0x[A-Fa-f0-9]+\\b",
"name": "constant.numeric.jael"
},
{
"match": "([A-Za-z_][A-Za-z0-9_]*)\\s*\\(",
"captures": {
"1": {
"name": "entity.name.function.jael"
}
}
}
]
}
}
}

View file

@ -1,23 +0,0 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es6",
"outDir": "out",
"lib": [
"es6"
],
"sourceMap": true,
"rootDir": "src",
/* Strict Type-Checking Option */
"strict": true, /* enable all strict type-checking options */
/* Additional Checks */
"noUnusedLocals": true /* Report errors on unused locals. */
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
// "noUnusedParameters": true, /* Report errors on unused parameters. */
},
"exclude": [
"node_modules",
".vscode-test"
]
}

View file

@ -1,33 +0,0 @@
# Welcome to your VS Code Extension
## What's in the folder
* This folder contains all of the files necessary for your extension.
* `package.json` - this is the manifest file in which you declare your extension and command.
The sample plugin registers a command and defines its title and command name. With this information
VS Code can show the command in the command palette. It doesnt yet need to load the plugin.
* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
The file exports one function, `activate`, which is called the very first time your extension is
activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
We pass the function containing the implementation of the command as the second parameter to
`registerCommand`.
## Get up and running straight away
* Press `F5` to open a new window with your extension loaded.
* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
* Find output from your extension in the debug console.
## Make changes
* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
## Explore the API
* You can open the full set of our API when you open the file `node_modules/vscode/vscode.d.ts`.
## Run tests
* Open the debug viewlet (`Ctrl+Shift+D` or `Cmd+Shift+D` on Mac) and from the launch configuration dropdown pick `Launch Tests`.
* Press `F5` to run the tests in a new window with your extension loaded.
* See the output of the test result in the debug console.
* Make changes to `test/extension.test.ts` or create new test files inside the `test` folder.
* By convention, the test runner will only consider files matching the name pattern `**.test.ts`.
* You can create folders inside the `test` folder to structure your tests any way you want.

File diff suppressed because it is too large Load diff

View file

@ -1,21 +0,0 @@
# See https://www.dartlang.org/guides/libraries/private-files
# Files and directories created by pub
.dart_tool/
.packages
build/
# If you're building an application, you may want to check-in your pubspec.lock
pubspec.lock
# Directory created by dartdoc
# If you don't generate documentation locally you can remove this line.
doc/api/
# Avoid committing generated Javascript files:
*.dart.js
*.info.json # Produced by the --dump-info flag.
*.js # When generated by dart2js. Don't specify *.js if your
# project includes source files written in JavaScript.
*.js_
*.js.deps
*.js.map

View file

@ -1,12 +0,0 @@
Primary Authors
===============
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
Thomas is the current maintainer of the code base. He has refactored and migrated the
code base to support NNBD.
* __[Tobe O](thosakwe@gmail.com)__
Tobe has written much of the original code prior to NNBD migration. He has moved on and
is no longer involved with the project.

View file

@ -1,5 +0,0 @@
# 2.0.0
* Migrated to support Dart SDK 2.12.x NNBD
# 1.0.0
* First release.

View file

@ -1,21 +0,0 @@
MIT License
Copyright (c) 2021 dukefirehawk.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1 +0,0 @@
# Jael Language Server for Vs Code

View file

@ -1,4 +0,0 @@
include: package:pedantic/analysis_options.yaml
analyzer:
strong-mode:
implicit-casts: false

View file

@ -1,69 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'package:args/args.dart';
import 'package:io/ansi.dart';
import 'package:io/io.dart';
//import 'package:dart_language_server/dart_language_server.dart';
import 'package:vs_jael_language_server/jael_language_server.dart';
import 'package:vs_jael_language_server/src/protocol/language_server/server.dart';
void main(List<String> args) async {
var argParser = ArgParser()
..addFlag('help',
abbr: 'h', negatable: false, help: 'Print this help information.')
..addOption('log-file', help: 'A path to which to write a log file.');
void printUsage() {
print('usage: jael_language_server [options...]\n\nOptions:');
print(argParser.usage);
}
try {
var argResults = argParser.parse(args);
if (argResults['help'] as bool) {
printUsage();
return;
} else {
var jaelServer = JaelLanguageServer();
if (argResults.wasParsed('log-file')) {
var f = File(argResults['log-file'] as String);
await f.create(recursive: true);
jaelServer.logger.onRecord.listen((rec) async {
var sink = f.openWrite(mode: FileMode.append);
sink.writeln(rec);
if (rec.error != null) sink.writeln(rec.error);
if (rec.stackTrace != null) sink.writeln(rec.stackTrace);
await sink.close();
});
} else {
jaelServer.logger.onRecord.listen((rec) async {
var sink = stderr;
sink.writeln(rec);
if (rec.error != null) sink.writeln(rec.error);
if (rec.stackTrace != null) sink.writeln(rec.stackTrace);
});
}
var spec = ZoneSpecification(
handleUncaughtError: (self, parent, zone, error, stackTrace) {
jaelServer.logger.severe('Uncaught', error, stackTrace);
},
print: (self, parent, zone, line) {
jaelServer.logger.info(line);
},
);
var zone = Zone.current.fork(specification: spec);
await zone.run(() async {
var stdio = StdIOLanguageServer.start(jaelServer);
await stdio.onDone;
});
}
} on ArgParserException catch (e) {
print('${red.wrap('error')}: ${e.message}\n');
printUsage();
exitCode = ExitCode.usage.code;
}
}

View file

@ -1 +0,0 @@
export 'src/server.dart';

View file

@ -1,154 +0,0 @@
import 'package:jael/jael.dart';
import 'package:logging/logging.dart';
import 'package:symbol_table/symbol_table.dart';
import 'object.dart';
class Analyzer extends Parser {
final Logger logger;
Analyzer(Scanner scanner, this.logger) : super(scanner);
@override
final errors = <JaelError>[];
SymbolTable<JaelObject>? _scope = SymbolTable<JaelObject>();
var allDefinitions = <Variable<JaelObject>>[];
SymbolTable<JaelObject>? get parentScope =>
_scope!.isRoot ? _scope : _scope!.parent;
SymbolTable<JaelObject>? get scope => _scope;
bool ensureAttributeIsPresent(Element element, String name) {
if (element.getAttribute(name)?.value == null) {
addError(JaelError(JaelErrorSeverity.error,
'Missing required attribute `$name`.', element.span));
return false;
}
return true;
}
void addError(JaelError e) {
errors.add(e);
logger.severe(e.message, e.span.highlight());
}
bool ensureAttributeIsConstantString(Element element, String name) {
var a = element.getAttribute(name);
if (a?.value is! StringLiteral || a?.value == null) {
var e = JaelError(
JaelErrorSeverity.warning,
'`$name` attribute should be a constant string literal.',
a?.span ?? element.tagName.span);
addError(e);
return false;
}
return true;
}
@override
Element? parseElement() {
try {
_scope = _scope!.createChild();
var element = super.parseElement();
if (element == null) return null;
// Check if any custom element exists.
_scope!
.resolve(element.tagName.name)
?.value
?.usages
.add(SymbolUsage(SymbolUsageType.read, element.span));
// Validate attrs
var forEach = element.getAttribute('for-each');
if (forEach != null) {
var asAttr = element.getAttribute('as');
if (asAttr != null) {
if (ensureAttributeIsConstantString(element, 'as')) {
var asName = asAttr.string!.value;
_scope!.create(asName,
value: JaelVariable(asName, asAttr.span), constant: true);
}
}
if (forEach.value != null) {
addError(JaelError(JaelErrorSeverity.error,
'Missing value for `for-each` directive.', forEach.span));
}
}
var iff = element.getAttribute('if');
if (iff != null) {
if (iff.value != null) {
addError(JaelError(JaelErrorSeverity.error,
'Missing value for `iff` directive.', iff.span));
}
}
// Validate the tag itself
if (element is RegularElement) {
if (element.tagName.name == 'block') {
ensureAttributeIsConstantString(element, 'name');
//logger.info('Found <block> at ${element.span.start.toolString}');
} else if (element.tagName.name == 'case') {
ensureAttributeIsPresent(element, 'value');
//logger.info('Found <case> at ${element.span.start.toolString}');
} else if (element.tagName.name == 'declare') {
if (element.attributes.isEmpty) {
addError(JaelError(
JaelErrorSeverity.warning,
'`declare` directive does not define any new symbols.',
element.tagName.span));
} else {
for (var attr in element.attributes) {
_scope!
.create(attr.name, value: JaelVariable(attr.name, attr.span));
}
}
} else if (element.tagName.name == 'element') {
if (ensureAttributeIsConstantString(element, 'name')) {
var nameCtx = element.getAttribute('name')!.value as StringLiteral;
var name = nameCtx.value;
//logger.info(
// 'Found custom element $name at ${element.span.start.toolString}');
try {
var symbol = parentScope!.create(name,
value: JaelCustomElement(name, element.tagName.span),
constant: true);
allDefinitions.add(symbol);
} on StateError catch (e) {
addError(JaelError(
JaelErrorSeverity.error, e.message, element.tagName.span));
}
}
} else if (element.tagName.name == 'extend') {
ensureAttributeIsConstantString(element, 'src');
//logger.info('Found <extend> at ${element.span.start.toolString}');
}
} else if (element is SelfClosingElement) {
if (element.tagName.name == 'include') {
//logger.info('Found <include> at ${element.span.start.toolString}');
ensureAttributeIsConstantString(element, 'src');
}
}
return element;
} finally {
_scope = _scope!.parent;
return null;
}
}
@override
Expression? parseExpression(int precedence) {
var expr = super.parseExpression(precedence);
if (expr == null) return null;
if (expr is Identifier) {
var ref = _scope!.resolve(expr.name);
ref?.value?.usages.add(SymbolUsage(SymbolUsageType.read, expr.span));
}
return expr;
}
}

View file

@ -1,117 +0,0 @@
import 'package:jael/jael.dart';
class JaelFormatter {
final num? tabSize;
final bool? insertSpaces;
final _buffer = StringBuffer();
int _level = 0;
String? _spaces;
static String _spaceString(int tabSize) {
var b = StringBuffer();
for (var i = 0; i < tabSize; i++) {
b.write(' ');
}
return b.toString();
}
JaelFormatter(this.tabSize, this.insertSpaces) {
_spaces = insertSpaces! ? _spaceString(tabSize!.toInt()) : '\t';
}
void _indent() {
_level++;
}
void _outdent() {
if (_level > 0) _level--;
}
void _applySpacing() {
for (var i = 0; i < _level; i++) {
_buffer.write(_spaces);
}
}
String apply(Document? document) {
if (document?.doctype != null) {
_buffer.write('<!doctype');
if (document!.doctype!.html != null) _buffer.write(' html');
if (document.doctype!.public != null) _buffer.write(' public');
if (document.doctype!.url != null) {
_buffer.write('${document.doctype!.url}');
}
_buffer.writeln();
}
_formatChild(document?.root);
return _buffer.toString();
}
void _formatChild(ElementChild? child) {
if (child == null) return;
_applySpacing();
if (child is Text) {
_buffer.write(child.text.span.text);
} else if (child is TextNode) {
_buffer.write(child.text.span.text);
} else if (child is Element) _formatElement(child);
}
void _formatElement(Element element) {
_applySpacing();
_buffer.write('<${element.tagName.name}');
for (var attr in element.attributes) {
_buffer.write(' ${attr.name}');
if (attr.value != null) {
if (attr.value is Identifier) {
var id = attr.value as Identifier;
if (id.name == 'true') {
_buffer.write(id.name);
} else if (id.name != 'false') {
if (attr.nequ != null) _buffer.write('!=');
if (attr.equals != null) _buffer.write('=');
_buffer.write(id.name);
}
} else {
if (attr.nequ != null) _buffer.write('!=');
if (attr.equals != null) _buffer.write('=');
_buffer.write(attr.value!.span.text);
}
}
}
if (element is SelfClosingElement) {
_buffer.writeln('/>');
} else if (element is RegularElement) {
if (element.children.length == 1 &&
(element.children.first is Text ||
element.children.first is TextNode)) {
_buffer.write('>');
_buffer.write(element.children.first.span.text);
} else {
_buffer.writeln('>');
_indent();
element.children.forEach(_formatChild);
_outdent();
}
if (element.children.isNotEmpty &&
(element.children.last is Text ||
element.children.last is TextNode)) {
_buffer.writeln();
}
_applySpacing();
_buffer.writeln('</${element.tagName.name}>');
} else {
throw ArgumentError();
}
}
}

View file

@ -1,34 +0,0 @@
import 'dart:collection';
import 'package:source_span/source_span.dart';
abstract class JaelObject {
final FileSpan span;
final usages = <SymbolUsage>[];
String get name;
JaelObject(this.span);
}
class JaelCustomElement extends JaelObject {
@override
final String name;
final attributes = SplayTreeSet<String>();
JaelCustomElement(this.name, FileSpan span) : super(span);
}
class JaelVariable extends JaelObject {
@override
final String name;
JaelVariable(this.name, FileSpan span) : super(span);
}
class SymbolUsage {
final SymbolUsageType type;
final FileSpan span;
SymbolUsage(this.type, this.span);
}
enum SymbolUsageType { definition, read }

View file

@ -1,64 +0,0 @@
import 'dart:async';
import 'package:json_rpc_2/json_rpc_2.dart';
import 'messages.dart';
abstract class LanguageServer {
final _onDone = Completer<void>();
Future<void> get onDone => _onDone.future;
Future<void> shutdown() async {}
void exit() {
_onDone.complete();
}
Future<ServerCapabilities> initialize(int? clientPid, String? rootUri,
ClientCapabilities clientCapabilities, String? trace) async =>
ServerCapabilities((b) => b);
void initialized() {}
void textDocumentDidOpen(TextDocumentItem document) {}
void textDocumentDidChange(VersionedTextDocumentIdentifier documentId,
List<TextDocumentContentChangeEvent> changes) {}
void textDocumentDidClose(TextDocumentIdentifier documentId) {}
Future<CompletionList> textDocumentCompletion(
TextDocumentIdentifier documentId, Position position) async =>
CompletionList((b) => b);
Future<Location?> textDocumentDefinition(
TextDocumentIdentifier documentId, Position position) async =>
null;
Future<List<Location>> textDocumentReferences(
TextDocumentIdentifier documentId,
Position position,
ReferenceContext context) async =>
[];
Future<List<Location>> textDocumentImplementation(
TextDocumentIdentifier documentId, Position position) async =>
[];
Future<List<DocumentHighlight>> textDocumentHighlight(
TextDocumentIdentifier documentId, Position position) async =>
[];
Future<List<SymbolInformation>> textDocumentSymbols(
TextDocumentIdentifier documentId) async =>
[];
Future<List<SymbolInformation>> workspaceSymbol(String? query) async => [];
Future<dynamic> textDocumentHover(
TextDocumentIdentifier documentId, Position position) async =>
null;
Future<List<dynamic /*Command|CodeAction*/ >> textDocumentCodeAction(
TextDocumentIdentifier documentId,
Range range,
CodeActionContext context) async =>
[];
Future<void> workspaceExecuteCommand(
String? command, List<dynamic>? arguments) async {}
Future<WorkspaceEdit?> textDocumentRename(TextDocumentIdentifier documentId,
Position position, String? newName) async =>
null;
Stream<Diagnostics> get diagnostics => Stream.empty();
Stream<ApplyWorkspaceEditParams> get workspaceEdits => Stream.empty();
Stream<ShowMessageParams> get showMessages => Stream.empty();
Stream<ShowMessageParams> get logMessages => Stream.empty();
void setupExtraMethods(Peer peer) {}
}

View file

@ -1,321 +0,0 @@
TextDocumentItem:
uri: String
text: String
languageId: String
version: int
TextDocumentIdentifier:
uri: String
VersionedTextDocumentIdentifier:
uri: String
version: int
TextDocumentContentChangeEvent:
range: Range
rangeLength: int
text: String
Range:
start: Position
end: Position
Position:
line: int
character: int
Diagnostics:
uri: String
diagnostics:
listType: Diagnostic
Diagnostic:
range: Range
severity: int
code: dynamic
source: String
message: String
CompletionList:
isIncomplete: bool
items:
listType: CompletionItem
CompletionItem:
label: String
kind: CompletionItemKind
detail: String
documentation: String
sortText: String
filterText: String
insertText: String
insertTextFormat: InsertTextFormat
textEdit: TextEdit
additionalTextEdits:
listType: TextEdit
command: Command
data: dynamic
CompletionItemKind:
enumValues:
text: 1
method: 2
function: 3
constructor: 4
field: 5
variable: 6
classKind: 7
interface: 8
module: 9
property: 10
unit: 11
value: 12
enumKind: 13
keyword: 14
snippet: 15
color: 16
file: 17
reference: 18
wireType: int
InsertTextFormat:
enumValues:
plainText: 1
snippet: 2
wireType: int
TextEdit:
range: Range
newText: String
Command:
title: String
command: String
arguments:
listType: dynamic
Location:
uri: String
range: Range
DynamicRegistrationCapability:
dynamicRegistration: bool
WorkspaceClientCapabilities:
applyEdit: bool
didChangeConfiguration: DynamicRegistrationCapability
didChangeWatchedFiles: DynamicRegistrationCapability
symbol: DynamicRegistrationCapability
executeCommand: DynamicRegistrationCapability
SynchronizationCapabilities:
dynamicRegistration: bool
willSave: bool
willSaveWaitUntil: bool
didSave: bool
CompletionItemCapabilities:
snippetSupport: bool
CompletionCapabilities:
dynamicRegistration: bool
completionItem: CompletionItemCapabilities
HoverCapabilities:
dynamicRegistration: bool
contentFormat:
listType: String
CodeActionCapabilities:
dynamicRegistration: bool
codeActionLiteralSupport: CodeActionLiteralSupport
CodeActionLiteralSupport:
codeActionKind: CodeActionKinds
CodeActionKinds:
valueSet:
listType: String # open ended enum
TextDocumentClientCapabilities:
codeAction: CodeActionCapabilities
completion: CompletionCapabilities
hover: HoverCapabilities
synchronization: SynchronizationCapabilities
codeLens: DynamicRegistrationCapability
definition: DynamicRegistrationCapability
documentHighlight: DynamicRegistrationCapability
documentLink: DynamicRegistrationCapability
documentSymbol: DynamicRegistrationCapability
formatting: DynamicRegistrationCapability
onTypeFormatting: DynamicRegistrationCapability
references: DynamicRegistrationCapability
rename: DynamicRegistrationCapability
ClientCapabilities:
workspace: WorkspaceClientCapabilities
textDocument: TextDocumentClientCapabilities
TextDocumentSyncKind:
enumValues:
none: 0
full: 1
incremental: 2
wireType: int
CompletionOptions:
resolveProvider: bool
triggerCharacters:
listType: String
SignatureHelpOptions:
triggerCharacters:
listType: String
CodeLensOptions:
resolveProvider: bool
DocumentOnTypeFormattingOptions:
firstTriggerCharacter: String
moreTriggerCharacter:
listType: String
DocumentLinkOptions:
resolveProvider: bool
ExecuteCommandOptions:
commands:
listType: String
SaveOptions:
includeText: bool
TextDocumentSyncOptions:
openClose: bool
change: TextDocumentSyncKind
willSave: bool
willSaveWaitUntil: bool
save: SaveOptions
ServerCapabilities:
codeActionProvider: bool
codeLensProvider: CodeLensOptions
completionProvider: CompletionOptions
definitionProvider: bool
documentFormattingProvider: bool
documentHighlightProvider: bool
documentLinkProvider: DocumentLinkOptions
documentOnTypeFormattingProvider: DocumentOnTypeFormattingOptions
documentRangeFormattingProvider: bool
documentSymbolProvider: bool
executeCommandProvider: ExecuteCommandOptions
hoverProvider: bool
implementationProvider: bool
referencesProvider: bool
renameProvider: bool
signatureHelpProvider: SignatureHelpOptions
textDocumentSync: TextDocumentSyncOptions
workspaceSymbolProvider: bool
ReferenceContext:
includeDeclaration: bool
Hover:
contents: String
range: Range
HoverMarkup:
contents: MarkupContent
range: Range
CodeActionContext:
diagnostics:
listType: Diagnostic
CodeAction:
title: String
kind: String
diagnostics:
listType: Diagnostic
edit: WorkspaceEdit
command: Command
ApplyWorkspaceEditParams:
label: String
edit: WorkspaceEdit
WorkspaceEdit:
# Not using `documentChanges` since there is no reasonable way to support text
# document version
changes:
mapType:
listType: TextEdit
DocumentHighlight:
range: Range
kind: DocumentHighlightKind
DocumentHighlightKind:
enumValues:
text: 1
read: 2
write: 3
wireType: int
SymbolInformation:
name: String
kind: SymbolKind
location: Location
containerName: String
SymbolKind:
enumValues:
file: 1
module: 2
namespace: 3
package: 4
classSymbol: 5
method: 6
property: 7
field: 8
constructor: 9
enumSymbol: 10
interface: 11
function: 12
variable: 13
constant: 14
string: 15
number: 16
boolean: 17
array: 18
object: 19
key: 20
nullSymbol: 21
enumMember: 22
struct: 23
event: 24
operator: 25
typeParameter: 26
wireType: int
MarkupContentKind:
enumValues:
plaintext: 'plaintext'
markdown: 'markdown'
wireType: String
MarkupContent:
kind: MarkupContentKind
value: String
MessageType:
enumValues:
error: 1
warning: 2
info: 3
log: 4
wireType: int
ShowMessageParams:
type: MessageType
message: String

View file

@ -1,201 +0,0 @@
import 'dart:async';
import 'dart:io';
import 'package:json_rpc_2/json_rpc_2.dart';
import 'interface.dart';
import 'messages.dart';
import 'wireformat.dart';
/// A Language Server communicating over stdin and stdout.
class StdIOLanguageServer {
final LanguageServer _server;
Future<void>? onDone;
/// Wrap [_server] and register RPC methods using the LSP wire protocol.
///
/// Methods are guarded against being called before the server is initialized.
StdIOLanguageServer.start(this._server) {
final peer = Peer(lspChannel(stdin, stdout));
_lifecycleMethods(peer);
_fileHandlingMethods(peer);
_notifications(peer);
_completionMethods(peer);
_referenceMethods(peer);
_codeActionMethods(peer);
_server.setupExtraMethods(peer);
peer.listen();
onDone = _server.onDone.then((_) => peer.close()).then((_) => null);
}
bool _isInitialized = false;
void _lifecycleMethods(Peer peer) {
peer
..registerMethod('initialize', (params) async {
final serverCapabilities = await _server.initialize(
params['processId'].valueOr(0) as int?,
params['rootUri'].valueOr('') as String?,
ClientCapabilities.fromJson(params['capabilities'].value as Map),
params['trace'].valueOr('off') as String?);
_isInitialized = true;
return {'capabilities': serverCapabilities.toJson()};
})
..registerMethod('initialized', (params) => _server.initialized())
..registerMethod('shutdown', _server.shutdown)
..registerMethod('exit', _server.exit);
}
/// Register a request that will throw if throw if used before initialization.
void _registerRequest(Peer peer, String methodName, Function callback) {
peer.registerMethod(methodName, (params) {
if (!_isInitialized) {
throw RpcException(-32003, 'The server has not been initialized');
}
return callback(params);
});
}
/// Notifications are ignored until after initialization.
void _registerNotification(Peer peer, String methodName, Function callback) {
peer.registerMethod(methodName, (params) {
if (_isInitialized) return callback(params);
});
}
void _fileHandlingMethods(Peer peer) {
_registerNotification(peer, 'textDocument/didOpen', (params) {
_server.textDocumentDidOpen(_documentItem(params));
});
_registerNotification(peer, 'textDocument/didChange', (params) {
_server.textDocumentDidChange(
_versionedDocument(params), _contentChanges(params));
});
_registerNotification(peer, 'textDocument/didClose', (params) {
_server.textDocumentDidClose(_document(params));
});
}
void _notifications(Peer peer) {
_server
..diagnostics.map((d) => d.toJson()).forEach((diagnostics) =>
peer.sendNotification('textDocument/publishDiagnostics', diagnostics))
..workspaceEdits.map((e) => e.toJson()).forEach((edit) {
// Ignore response?
peer.sendRequest('workspace/applyEdit', edit);
})
..logMessages.map((e) => e.toJson()).forEach(
(message) => peer.sendNotification('window/logMessage', message))
..showMessages.map((e) => e.toJson()).forEach(
(message) => peer.sendNotification('window/showMessage', message));
}
void _completionMethods(Peer peer) {
_registerRequest(
peer,
'textDocument/completion',
(params) => _server
.textDocumentCompletion(_document(params), _position(params))
.then((r) => r.toJson()));
}
void _referenceMethods(Peer peer) {
_registerRequest(
peer,
'textDocument/definition',
(params) => _server
.textDocumentDefinition(_document(params), _position(params))
.then((r) => r?.toJson()));
_registerRequest(
peer,
'textDocument/hover',
(params) => _server
.textDocumentHover(_document(params), _position(params))
.then((r) => r?.toJson()));
_registerRequest(
peer,
'textDocument/references',
(params) => _server
.textDocumentReferences(
_document(params), _position(params), _referenceContext(params))
.then((r) => r.map((e) => e.toJson()).toList()));
_registerRequest(
peer,
'textDocument/implementation',
(params) => _server
.textDocumentImplementation(_document(params), _position(params))
.then((r) => r.map((e) => e.toJson()).toList()));
_registerRequest(
peer,
'textDocument/documentHighlight',
(params) => _server
.textDocumentHighlight(_document(params), _position(params))
.then((r) => r.map((e) => e.toJson()).toList()));
_registerRequest(
peer,
'textDocument/documentSymbol',
(params) => _server
.textDocumentSymbols(_document(params))
.then((r) => r.map((e) => e.toJson()).toList()));
_registerRequest(
peer,
'workspace/symbol',
(params) => _server
.workspaceSymbol(_query(params))
.then((r) => r.map((e) => e.toJson()).toList()));
}
void _codeActionMethods(Peer peer) {
_registerRequest(
peer,
'textDocument/codeAction',
(params) => _server
.textDocumentCodeAction(
_document(params), _range(params), _codeActionContext(params))
.then((r) => r.map((e) => e.toJson()).toList()));
_registerRequest(
peer,
'workspace/executeCommand',
(params) => _server.workspaceExecuteCommand(
params['command'].value as String?,
params['arguments']?.value as List?));
_registerRequest(
peer,
'textDocument/rename',
(params) async => (await _server.textDocumentRename(_document(params),
_position(params), params['newName'].value as String?))!
.toJson());
}
}
TextDocumentItem _documentItem(params) =>
TextDocumentItem.fromJson(params['textDocument'].value as Map);
VersionedTextDocumentIdentifier _versionedDocument(params) =>
VersionedTextDocumentIdentifier.fromJson(
params['textDocument'].value as Map);
TextDocumentIdentifier _document(params) =>
TextDocumentIdentifier.fromJson(params['textDocument'].value as Map);
Range _range(params) => Range.fromJson(params['range'].value as Map);
Position _position(params) =>
Position.fromJson(params['position'].value as Map);
CodeActionContext _codeActionContext(params) =>
CodeActionContext.fromJson(params['context'].value as Map);
ReferenceContext _referenceContext(params) =>
ReferenceContext.fromJson(params['context'].value as Map);
List<TextDocumentContentChangeEvent> _contentChanges(params) =>
(params['contentChanges'].value as Iterable)
.map((change) => TextDocumentContentChangeEvent.fromJson(change as Map))
.toList();
String? _query(params) => params['query'].value as String?;

View file

@ -1,98 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'package:stream_channel/stream_channel.dart';
import 'package:async/async.dart';
StreamChannel<String> lspChannel(
Stream<List<int>> stream, StreamSink<List<int>> sink) {
final parser = _Parser(stream);
final outSink = StreamSinkTransformer.fromHandlers(
handleData: _serialize,
handleDone: (sink) {
sink.close();
parser.close();
}).bind(sink);
return StreamChannel.withGuarantees(parser.stream, outSink);
}
void _serialize(String data, EventSink<List<int>> sink) {
final message = utf8.encode(data);
final header = 'Content-Length: ${message.length}\r\n\r\n';
sink.add(ascii.encode(header));
for (var chunk in _chunks(message, 1024)) {
sink.add(chunk);
}
}
class _Parser {
final _streamCtl = StreamController<String>();
Stream<String> get stream => _streamCtl.stream;
final _buffer = <int>[];
bool _headerMode = true;
int _contentLength = -1;
late StreamSubscription _subscription;
_Parser(Stream<List<int>> stream) {
_subscription =
stream.expand((bytes) => bytes).listen(_handleByte, onDone: () {
_streamCtl.close();
});
}
Future<void> close() => _subscription.cancel();
void _handleByte(int byte) {
_buffer.add(byte);
if (_headerMode && _headerComplete) {
_contentLength = _parseContentLength();
_buffer.clear();
_headerMode = false;
} else if (!_headerMode && _messageComplete) {
_streamCtl.add(utf8.decode(_buffer));
_buffer.clear();
_headerMode = true;
}
}
/// Whether the entire message is in [_buffer].
bool get _messageComplete => _buffer.length >= _contentLength;
/// Decodes [_buffer] into a String and looks for the 'Content-Length' header.
int _parseContentLength() {
final asString = ascii.decode(_buffer);
final headers = asString.split('\r\n');
final lengthHeader =
headers.firstWhere((h) => h.startsWith('Content-Length'));
final length = lengthHeader.split(':').last.trim();
return int.parse(length);
}
/// Whether [_buffer] ends in '\r\n\r\n'.
bool get _headerComplete {
final l = _buffer.length;
return l > 4 &&
_buffer[l - 1] == 10 &&
_buffer[l - 2] == 13 &&
_buffer[l - 3] == 10 &&
_buffer[l - 4] == 13;
}
}
Iterable<List<T>> _chunks<T>(List<T> data, int chunkSize) sync* {
if (data.length <= chunkSize) {
yield data;
return;
}
var low = 0;
while (low < data.length) {
if (data.length > low + chunkSize) {
yield data.sublist(low, low + chunkSize);
} else {
yield data.sublist(low);
}
low += chunkSize;
}
}

View file

@ -1,541 +0,0 @@
import 'dart:async';
//import 'package:dart_language_server/src/protocol/language_server/interface.dart';
//import 'package:dart_language_server/src/protocol/language_server/messages.dart';
import 'package:file/file.dart';
import 'package:file/local.dart';
import 'package:file/memory.dart';
import 'package:jael/jael.dart';
import 'package:json_rpc_2/json_rpc_2.dart' as json_rpc_2;
import 'package:logging/logging.dart';
import 'package:path/path.dart' as p;
import 'package:source_span/source_span.dart';
import 'package:string_scanner/string_scanner.dart';
import 'package:symbol_table/symbol_table.dart';
import 'protocol/language_server/interface.dart';
import 'protocol/language_server/messages.dart';
import './analyzer.dart';
import './formatter.dart' as fmt;
import './object.dart';
class JaelLanguageServer extends LanguageServer {
final _diagnostics = StreamController<Diagnostics>();
final _done = Completer();
final _memFs = MemoryFileSystem();
final _localFs = const LocalFileSystem();
Directory? _localRootDir, _memRootDir;
var logger = Logger('jael');
late Uri _rootUri;
final _workspaceEdits = StreamController<ApplyWorkspaceEditParams>();
@override
Stream<Diagnostics> get diagnostics => _diagnostics.stream;
@override
Future<void> get onDone => _done.future;
@override
Stream<ApplyWorkspaceEditParams> get workspaceEdits => _workspaceEdits.stream;
@override
Future<void> shutdown() {
if (!_done.isCompleted) _done.complete();
_diagnostics.close();
_workspaceEdits.close();
return super.shutdown();
}
@override
void setupExtraMethods(json_rpc_2.Peer peer) {
peer.registerMethod('textDocument/formatting',
(json_rpc_2.Parameters params) async {
var documentId =
TextDocumentIdentifier.fromJson(params['textDocument'].asMap);
var formattingOptions =
FormattingOptions.fromJson(params['options'].asMap);
return await textDocumentFormatting(documentId, formattingOptions);
});
}
@override
Future<ServerCapabilities> initialize(int? clientPid, String? rootUri,
ClientCapabilities clientCapabilities, String? trace) async {
// Find our real root dir.
_localRootDir = _localFs.directory(_rootUri = Uri.parse(rootUri!));
_memRootDir = _memFs.directory('/');
await _memRootDir!.create(recursive: true);
_memFs.currentDirectory = _memRootDir;
// Copy all real files that end in *.jael (and *.jl for legacy) into the in-memory filesystem.
await for (var entity in _localRootDir!.list(recursive: true)) {
if (entity is File && p.extension(entity.path) == '.jael') {
logger.info('HEY ${entity.path}');
var file = _memFs.file(entity.absolute.path);
await file.create(recursive: true);
await entity.openRead().pipe(file.openWrite(mode: FileMode.write));
logger.info(
'Found Jael file ${file.path}; copied to ${file.absolute.path}');
// Analyze it
var documentId = TextDocumentIdentifier((b) {
b.uri = _rootUri.replace(path: file.path).toString();
});
await analyzerForId(documentId);
}
}
return ServerCapabilities((b) {
b
..codeActionProvider = false
..completionProvider = CompletionOptions((b) {
b
..resolveProvider = true
..triggerCharacters =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdeghijklmnopqrstuvxwyz'
.codeUnits
.map((c) => String.fromCharCode(c))
.toList();
})
..definitionProvider = true
..documentHighlightProvider = true
..documentRangeFormattingProvider = false
..documentOnTypeFormattingProvider = null
..documentSymbolProvider = true
..documentFormattingProvider = true
..hoverProvider = true
..implementationProvider = true
..referencesProvider = true
..renameProvider = true
..signatureHelpProvider = SignatureHelpOptions((b) {})
..textDocumentSync = TextDocumentSyncOptions((b) {
b
..openClose = true
..change = TextDocumentSyncKind.full
..save = SaveOptions((b) {
b.includeText = false;
})
..willSave = false
..willSaveWaitUntil = false;
})
..workspaceSymbolProvider = true;
});
}
Future<File> fileForId(TextDocumentIdentifier documentId) async {
var uri = Uri.parse(documentId.uri!);
var relativePath = uri.path;
var file = _memFs.directory('/').childFile(relativePath);
/*
logger.info('Searching for $relativePath. All:\n');
await for (var entity in _memFs.directory('/').list(recursive: true)) {
if (entity is File) print(' * ${entity.absolute.path}');
}
*/
if (!await file.exists()) {
await file.create(recursive: true);
await _localFs.file(uri).openRead().pipe(file.openWrite());
logger.info('Opened Jael file ${file.path}');
}
return file;
}
Future<Scanner> scannerForId(TextDocumentIdentifier documentId) async {
var file = await fileForId(documentId);
return scan(await file.readAsString(), sourceUrl: file.uri);
}
Future<Analyzer> analyzerForId(TextDocumentIdentifier documentId) async {
var scanner = await scannerForId(documentId);
var analyzer = Analyzer(scanner, logger)..errors.addAll(scanner.errors);
analyzer.parseDocument();
emitDiagnostics(documentId.uri, analyzer.errors.map(toDiagnostic).toList());
return analyzer;
}
Diagnostic toDiagnostic(JaelError e) {
return Diagnostic((b) {
b
..message = e.message
..range = toRange(e.span)
..severity = toSeverity(e.severity)
..source = e.span.start.sourceUrl.toString();
});
}
int toSeverity(JaelErrorSeverity s) {
switch (s) {
case JaelErrorSeverity.warning:
return DiagnosticSeverity.warning;
default:
return DiagnosticSeverity.error;
}
}
Range toRange(FileSpan span) {
return Range((b) {
b
..start = toPosition(span.start)
..end = toPosition(span.end);
});
}
Range emptyRange() {
return Range((b) => b
..start = b.end = Position((b) {
b
..character = 1
..line = 0;
}));
}
Position toPosition(SourceLocation location) {
return Position((b) {
b
..line = location.line
..character = location.column;
});
}
Location toLocation(String? uri, FileSpan span) {
return Location((b) {
b
..range = toRange(span)
..uri = uri;
});
}
bool isReachable(JaelObject obj, Position position) {
return obj.span.start.line <= position.line! &&
obj.span.start.column <= position.character!;
}
CompletionItem? toCompletion(Variable<JaelObject> symbol) {
var value = symbol.value;
if (value is JaelCustomElement) {
var name = value.name;
return CompletionItem((b) {
b
..kind = CompletionItemKind.classKind
..label = symbol.name
..textEdit = TextEdit((b) {
b
..range = emptyRange()
..newText = '<$name\$1>\n \$2\n</name>';
});
});
} else if (value is JaelVariable) {
return CompletionItem((b) {
b
..kind = CompletionItemKind.variable
..label = symbol.name;
});
}
return null;
}
void emitDiagnostics(String? uri, Iterable<Diagnostic> diagnostics) {
_diagnostics.add(Diagnostics((b) {
logger.info('$uri => ${diagnostics.map((d) => d.message).toList()}');
b
..diagnostics = diagnostics.toList()
..uri = uri.toString();
}));
}
@override
Future textDocumentDidOpen(TextDocumentItem document) async {
await analyzerForId(TextDocumentIdentifier((b) => b..uri = document.uri));
}
@override
Future textDocumentDidChange(VersionedTextDocumentIdentifier documentId,
List<TextDocumentContentChangeEvent> changes) async {
var id = TextDocumentIdentifier((b) => b..uri = documentId.uri);
var file = await fileForId(id);
for (var change in changes) {
if (change.text != null) {
await file.writeAsString(change.text!);
} else if (change.range != null) {
String? contents = await file.readAsString();
int findIndex(Position position) {
var lines = contents!.split('\n');
// Sum the length of the previous lines.
var lineLength = lines
.take(position.line! - 1)
.map((s) => s.length)
.reduce((a, b) => a + b);
return lineLength + position.character! - 1;
}
if (change.range == null) {
contents = change.text;
} else {
var start = findIndex(change.range!.start!),
end = findIndex(change.range!.end!);
contents = contents.replaceRange(start, end, change.text!);
}
logger.info('${file.path} => $contents');
await file.writeAsString(contents!);
}
}
await analyzerForId(id);
}
@override
Future<List> textDocumentCodeAction(TextDocumentIdentifier documentId,
Range range, CodeActionContext context) async {
// TODO: implement textDocumentCodeAction
return [];
}
@override
Future<CompletionList> textDocumentCompletion(
TextDocumentIdentifier documentId, Position position) async {
var analyzer = await analyzerForId(documentId);
var symbols = analyzer.scope!.allVariables;
var reachable = symbols.where((s) => isReachable(s.value!, position));
return CompletionList((b) {
b
..isIncomplete = false
..items = reachable.map(toCompletion).toList();
});
}
final RegExp _id =
RegExp(r'(([A-Za-z][A-Za-z0-9_]*-)*([A-Za-z][A-Za-z0-9_]*))');
Future<String?> currentName(
TextDocumentIdentifier documentId, Position position) async {
// First, read the file.
var file = await fileForId(documentId);
var contents = await file.readAsString();
// Next, find the current index.
var scanner = SpanScanner(contents);
while (!scanner.isDone &&
(scanner.state.line != position.line ||
scanner.state.column != position.character)) {
scanner.readChar();
}
// Next, just read the name.
if (scanner.matches(_id)) {
var longest = scanner.lastSpan!.text;
while (scanner.matches(_id) && scanner.position > 0 && !scanner.isDone) {
longest = scanner.lastSpan!.text;
scanner.position--;
}
return longest;
} else {
return null;
}
}
Future<JaelObject?> currentSymbol(
TextDocumentIdentifier documentId, Position position) async {
var name = await currentName(documentId, position);
if (name == null) return null;
var analyzer = await analyzerForId(documentId);
var symbols = analyzer.allDefinitions; // ?? analyzer.scope!.allVariables;
logger
.info('Current symbols, seeking $name: ${symbols.map((v) => v.name)}');
return analyzer.scope!.resolve(name)?.value;
}
@override
Future<Location?> textDocumentDefinition(
TextDocumentIdentifier documentId, Position position) async {
var symbol = await currentSymbol(documentId, position);
if (symbol != null) {
return toLocation(documentId.uri, symbol.span);
}
return null;
}
@override
Future<List<DocumentHighlight>> textDocumentHighlight(
TextDocumentIdentifier documentId, Position position) async {
var symbol = await currentSymbol(documentId, position);
if (symbol != null) {
return symbol.usages.map((u) {
return DocumentHighlight((b) {
b
..range = toRange(u.span)
..kind = u.type == SymbolUsageType.definition
? DocumentHighlightKind.write
: DocumentHighlightKind.read;
});
}).toList();
}
return [];
}
@override
Future<Hover?> textDocumentHover(
TextDocumentIdentifier documentId, Position position) async {
var symbol = await currentSymbol(documentId, position);
if (symbol != null) {
return Hover((b) {
b
..contents = symbol.span.text
..range = toRange(symbol.span);
});
}
return null;
}
@override
Future<List<Location>> textDocumentImplementation(
TextDocumentIdentifier documentId, Position position) async {
var defn = await textDocumentDefinition(documentId, position);
return defn == null ? [] : [defn];
}
@override
Future<List<Location>> textDocumentReferences(
TextDocumentIdentifier documentId,
Position position,
ReferenceContext context) async {
var symbol = await currentSymbol(documentId, position);
if (symbol != null) {
return symbol.usages.map((u) {
return toLocation(documentId.uri, u.span);
}).toList();
}
return [];
}
@override
Future<WorkspaceEdit> textDocumentRename(TextDocumentIdentifier documentId,
Position position, String? newName) async {
var symbol = await currentSymbol(documentId, position);
if (symbol != null) {
return WorkspaceEdit((b) {
b.changes = {
symbol.name: symbol.usages.map((u) {
return TextEdit((b) {
b
..range = toRange(u.span)
..newText = (symbol is JaelCustomElement &&
u.type == SymbolUsageType.definition)
? '"$newName"'
: newName;
});
}).toList()
};
});
}
return WorkspaceEdit((b) {
b.changes = {};
});
}
@override
Future<List<SymbolInformation>> textDocumentSymbols(
TextDocumentIdentifier documentId) async {
var analyzer = await analyzerForId(documentId);
return analyzer.allDefinitions.map((symbol) {
return SymbolInformation((b) {
b
..kind = SymbolKind.classSymbol
..name = symbol.name
..location = toLocation(documentId.uri, symbol.value!.span);
});
}).toList();
}
@override
Future<void> workspaceExecuteCommand(String? command, List? arguments) async {
// TODO: implement workspaceExecuteCommand
}
@override
Future<List<SymbolInformation>> workspaceSymbol(String? query) async {
var values = <JaelObject?>[];
await for (var file in _memRootDir!.list(recursive: true)) {
if (file is File) {
var id = TextDocumentIdentifier((b) {
b.uri = file.uri.toString();
});
var analyzer = await analyzerForId(id);
values.addAll(analyzer.allDefinitions.map((v) => v.value));
}
}
return values.map((o) {
return SymbolInformation((b) {
b
..name = o!.name
..location = toLocation(o.span.sourceUrl.toString(), o.span)
..containerName = p.basename(o.span.sourceUrl!.path)
..kind = o is JaelCustomElement
? SymbolKind.classSymbol
: SymbolKind.variable;
});
}).toList();
}
Future<List<TextEdit>?> textDocumentFormatting(
TextDocumentIdentifier documentId,
FormattingOptions formattingOptions) async {
try {
var errors = <JaelError>[];
var file = await fileForId(documentId);
var contents = await file.readAsString();
var document =
parseDocument(contents, sourceUrl: file.uri, onError: errors.add);
if (errors.isNotEmpty) return null;
var formatter = fmt.JaelFormatter(
formattingOptions.tabSize, formattingOptions.insertSpaces);
var formatted = formatter.apply(document);
logger.info('Original:$contents\nFormatted:\n$formatted');
if (formatted.isNotEmpty) await file.writeAsString(formatted);
return [
TextEdit((b) {
b
..newText = formatted
..range = document == null ? emptyRange() : toRange(document.span);
})
];
} catch (e, st) {
logger.severe('Formatter error', e, st);
return null;
}
}
}
abstract class DiagnosticSeverity {
static const int error = 0, warning = 1, information = 2, hint = 3;
}
class FormattingOptions {
final num? tabSize;
final bool? insertSpaces;
FormattingOptions(this.tabSize, this.insertSpaces);
factory FormattingOptions.fromJson(Map json) {
return FormattingOptions(
json['tabSize'] as num?, json['insertSpaces'] as bool?);
}
}

View file

@ -1,35 +0,0 @@
name: vs_jael_language_server
version: 2.0.0
description: Language Server Protocol implementation for the Jael templating engine.
homepage: https://github.com/angel-dart/vscode
publish_to: none
environment:
sdk: '>=2.12.0 <3.0.0'
dependencies:
args: ^2.1.1
# dart_language_server: ^0.1.16
file: ^6.1.2
io: ^1.0.0
jael:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/jael/jael
jael_preprocessor:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/jael/jael_preprocessor
json_rpc_2: ^3.0.1
logging: ^1.0.1
path: ^1.8.0
source_span: ^1.8.1
string_scanner: ^1.1.0
symbol_table:
git:
url: https://github.com/dukefirehawk/angel.git
ref: sdk-2.12.x_nnbd
path: packages/symbol_table
pedantic: ^1.11.0
executables:
jael_language_server: jael_language_server