Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
5945c04cce | |||
56e27a2d14 | |||
3f8f515262 | |||
3656d71e6d | |||
1ce0b2e4e2 | |||
9ab72c9ccb | |||
6f5357ea5c | |||
b2014942b6 | |||
e549845d70 | |||
7e5b68116a | |||
10850c9d98 | |||
e8ce9b73ae | |||
fc7e3b431f | |||
0a2a6c4db6 | |||
6e143cd4d0 | |||
f094584dee | |||
c01c04699a | |||
e6fea932d8 | |||
e90524e367 | |||
c117337883 | |||
da08fb3979 | |||
a15429d964 | |||
36ed3328d1 | |||
f8083b26d2 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
client/.antlr
|
||||||
test.bf
|
test.bf
|
||||||
out
|
out
|
||||||
dist
|
dist
|
||||||
|
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@@ -1,9 +1,8 @@
|
|||||||
{
|
{
|
||||||
"editor.insertSpaces": false,
|
|
||||||
"tslint.enable": true,
|
"tslint.enable": true,
|
||||||
"typescript.tsc.autoDetect": "off",
|
"typescript.tsc.autoDetect": "off",
|
||||||
"typescript.preferences.quoteStyle": "single",
|
"typescript.preferences.quoteStyle": "single",
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": "explicit"
|
||||||
}
|
}
|
||||||
}
|
}
|
201
LICENSE
Normal file
201
LICENSE
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright 2022 Atreya Bain
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
58
README.md
58
README.md
@@ -1,21 +1,55 @@
|
|||||||
# BF extension
|
# BF extension
|
||||||
|
|
||||||
A simple language server based VSCode Extension for the ~~Brainfuck~~ ~~Branflakes~~ BF language. You can also execute your code and see its output.
|
A simple language server based VSCode Extension for the (Branflakes?) (BrainFuck?) BF language. You can also execute your code and see its output.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||

|
|
||||||
## Functionality
|
## Functionality
|
||||||
|
|
||||||
- [X] Syntax
|
- Syntax Highlighting
|
||||||
- [X] Bracket matching
|
- Execution
|
||||||
- [X] Autocomplete suggestions
|
- Autocomplete suggestions
|
||||||
- [X] Extension icon
|
|
||||||
- [X] Execution
|
|
||||||
- [ ] Timeout
|
|
||||||
|
|
||||||
### Execution
|
### Execution
|
||||||
|
|
||||||
Use the command to execute the code.
|
Use the BF execute task to execute the code.
|
||||||
Issue is, because BF is a *turing complete* language, there is no way to know if the program will terminate or not. Hence for now, the command may lead to infinite execution.
|
Either run the "current file" task, or create a customized task with the required file.
|
||||||
Additionally, all input is done before program execution (If the program requests for input that hasn't been provided, `zero` is fed to it).
|
I/O is done on the created terminal window.
|
||||||
|
|
||||||
TODO: Implement a timeout.
|
|
||||||
|
There is also an older command to run the code, where output is shown as a status message. Here, if the program requires input, it will be requested as a prompt.
|
||||||
|
|
||||||
|
### Changelog
|
||||||
|
|
||||||
|
#### 0.3.0
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
- Added a task for execution
|
||||||
|
- Press Control C to halt it while its waiting for input
|
||||||
|
- Close task to abort execution
|
||||||
|
- Detail: The program will halt between loop iterations.
|
||||||
|
- Migrated the run command to `bf.execute.old`
|
||||||
|
|
||||||
|
#### 0.2.1
|
||||||
|
|
||||||
|
- Change category
|
||||||
|
- Small bugfix for brackets validation
|
||||||
|
|
||||||
|
#### 0.2.0
|
||||||
|
|
||||||
|
- Cycle input pointer on overflow/underflow
|
||||||
|
- Refactoring code
|
||||||
|
|
||||||
|
#### 0.1.0
|
||||||
|
|
||||||
|
- Request input as required during execution
|
||||||
|
- Using array-based indexing. This implies that only positive indices upto 30k are supported.
|
||||||
|
|
||||||
|
|
||||||
|
### Building it
|
||||||
|
|
||||||
|
1. `npm i` - Install all dependencies
|
||||||
|
2. `npm i -g @vscode/vsce` - Install VSCode Command line CLI
|
||||||
|
3. `vsce package` - Package to VSIX
|
BIN
assets/command.gif
Normal file
BIN
assets/command.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 540 KiB |
BIN
assets/screenshot.png
Normal file
BIN
assets/screenshot.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 28 KiB |
20
client/src/BranFlakesExecutorGen.ts
Normal file
20
client/src/BranFlakesExecutorGen.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { EventEmitter } from 'vscode';
|
||||||
|
import InputStrategy from './input/InputStrategy';
|
||||||
|
import {BranFlakesExecutorVisitor} from './exec/BranFlakesExecutorVisitor';
|
||||||
|
|
||||||
|
export class BranFlakesStreamingExecutor {
|
||||||
|
|
||||||
|
constructor(private fileData: string, private fileName: string = 'fileName:dummy', private emitter: EventEmitter<string>,
|
||||||
|
private inputStrategy: InputStrategy
|
||||||
|
) { }
|
||||||
|
|
||||||
|
|
||||||
|
async run() {
|
||||||
|
const finalOutput = await BranFlakesExecutorVisitor.run(
|
||||||
|
this.fileData,
|
||||||
|
this.fileName,
|
||||||
|
this.inputStrategy,
|
||||||
|
async (str) => { this.emitter.fire(str); }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@@ -16,9 +16,9 @@ export interface TranslationError {
|
|||||||
export function getTree(str: string, fn: string) {
|
export function getTree(str: string, fn: string) {
|
||||||
const charStreams = CharStreams.fromString(str, fn);
|
const charStreams = CharStreams.fromString(str, fn);
|
||||||
const lexer = new bfLexer(charStreams);
|
const lexer = new bfLexer(charStreams);
|
||||||
// const errorListener = this.errorManager.newErrorListener();
|
|
||||||
const issues: TranslationError[] = [];
|
const issues: TranslationError[] = [];
|
||||||
// remove the error listener. We want to put our own
|
// remove the error listener. We want to put our own
|
||||||
|
|
||||||
lexer.removeErrorListeners();
|
lexer.removeErrorListeners();
|
||||||
lexer.addErrorListener({
|
lexer.addErrorListener({
|
||||||
syntaxError(source, o, line, charPositionInLine, msg, error) {
|
syntaxError(source, o, line, charPositionInLine, msg, error) {
|
@@ -1,117 +0,0 @@
|
|||||||
import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor';
|
|
||||||
import { LoopStmtContext, ProgramContext } from '../../client/src/generated/bfParser';
|
|
||||||
import { bfVisitor } from '../../client/src/generated/bfVisitor';
|
|
||||||
import {getTree} from './bfGrammar';
|
|
||||||
import { DiagnosticSeverity } from 'vscode-languageserver-types';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run BF code
|
|
||||||
* @param text
|
|
||||||
* @param fn
|
|
||||||
* @param input
|
|
||||||
* @returns
|
|
||||||
*/
|
|
||||||
export async function runBF(text:string,fn:string,input:string){
|
|
||||||
//get tree and issues
|
|
||||||
const {tree,issues} = getTree(text,fn);
|
|
||||||
|
|
||||||
//get only errors
|
|
||||||
const x = issues.filter(e=>e.type===DiagnosticSeverity.Error);
|
|
||||||
//if any error, drop
|
|
||||||
if(x.length>0) {
|
|
||||||
throw Error('Errors exist');
|
|
||||||
}
|
|
||||||
// make visitor
|
|
||||||
const vis = new BFExecutor(input);
|
|
||||||
|
|
||||||
//visit the tree
|
|
||||||
vis.visit(tree);
|
|
||||||
|
|
||||||
//get output
|
|
||||||
return vis.outputStr;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class BFExecutor
|
|
||||||
extends AbstractParseTreeVisitor<void>
|
|
||||||
implements bfVisitor<void> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param input Input string
|
|
||||||
* @param inputPtr Input pointer to start from
|
|
||||||
*/
|
|
||||||
constructor(protected input: string='', protected inputPtr: number = 0) {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* The memory cells (Can work with negative cells this way)
|
|
||||||
*/
|
|
||||||
protected cells: Map<number, number> = new Map();
|
|
||||||
/**
|
|
||||||
* Pointer
|
|
||||||
*/
|
|
||||||
protected ptr: number = 0;
|
|
||||||
/** Output string */
|
|
||||||
protected outputStrArray:string[]=[];
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Output string (Available only after visiting)
|
|
||||||
*/
|
|
||||||
public get outputStr(){
|
|
||||||
return this.outputStrArray.join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
defaultResult() {}
|
|
||||||
|
|
||||||
visitLoopStmt(ctx:LoopStmtContext){
|
|
||||||
while((this.cells.get(this.ptr)??0)!==0){
|
|
||||||
this.visitChildren(ctx);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
visitPtrLeft() {
|
|
||||||
|
|
||||||
--this.ptr;
|
|
||||||
}
|
|
||||||
visitPtrRight() {
|
|
||||||
++this.ptr;
|
|
||||||
}
|
|
||||||
visitPtrIncr() {
|
|
||||||
|
|
||||||
const val = this.cells.get(this.ptr);
|
|
||||||
if (val === undefined) {
|
|
||||||
this.cells.set(this.ptr, 1);
|
|
||||||
} else if(val===255){
|
|
||||||
this.cells.delete(this.ptr);
|
|
||||||
}else{
|
|
||||||
this.cells.set(this.ptr, val + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
visitPtrDecr() {
|
|
||||||
// console.log('down',this.ptr,this.cells);
|
|
||||||
|
|
||||||
const val = this.cells.get(this.ptr);
|
|
||||||
if (val === undefined || val === 0) {
|
|
||||||
this.cells.set(this.ptr, 255);
|
|
||||||
} else if(val===1){
|
|
||||||
this.cells.delete(this.ptr);
|
|
||||||
}else{
|
|
||||||
this.cells.set(this.ptr, val - 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
visitOutputStmt() {
|
|
||||||
const val = this.cells.get(this.ptr)??0;
|
|
||||||
const str = String.fromCharCode(val);
|
|
||||||
// console.log('op',str);
|
|
||||||
this.outputStrArray.push(str);
|
|
||||||
}
|
|
||||||
|
|
||||||
visitInputStmt(){
|
|
||||||
//get char
|
|
||||||
const char = this.input.charCodeAt(this.inputPtr)??0;
|
|
||||||
//increment the input pointer after this
|
|
||||||
this.inputPtr++;
|
|
||||||
this.cells.set(this.ptr,char);
|
|
||||||
}
|
|
||||||
}
|
|
4
client/src/command/BranFlakesCommand.ts
Normal file
4
client/src/command/BranFlakesCommand.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export interface BranFlakesCommand {
|
||||||
|
getCommandName(): string;
|
||||||
|
getCommandHandler(): (...args: any) => Promise<any>;
|
||||||
|
}
|
29
client/src/command/CompileBranFlakesCommand.ts
Normal file
29
client/src/command/CompileBranFlakesCommand.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { window } from 'vscode';
|
||||||
|
import type { BranFlakesCommand } from './BranFlakesCommand';
|
||||||
|
import { VSCodePromptInputStrategy } from '../input/VSCodePromptInputStrategy';
|
||||||
|
|
||||||
|
export class CompileBranFlakesCommand implements BranFlakesCommand {
|
||||||
|
getCommandName() {
|
||||||
|
return 'bf.execute.old';
|
||||||
|
}
|
||||||
|
getCommandHandler() {
|
||||||
|
return async () => {
|
||||||
|
const text = window.activeTextEditor.document.getText();
|
||||||
|
const fn = window.activeTextEditor.document.fileName;
|
||||||
|
const inputStrategy = new VSCodePromptInputStrategy(
|
||||||
|
window.showInputBox
|
||||||
|
);
|
||||||
|
const { BranFlakesExecutorVisitor } = await import(
|
||||||
|
'../exec/BranFlakesExecutorVisitor'
|
||||||
|
);
|
||||||
|
|
||||||
|
const output = await BranFlakesExecutorVisitor.run(
|
||||||
|
text,
|
||||||
|
fn,
|
||||||
|
inputStrategy,
|
||||||
|
window.showInformationMessage
|
||||||
|
);
|
||||||
|
await window.showInformationMessage(`Output: ${output}`);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
11
client/src/exec/AbortRequestor.ts
Normal file
11
client/src/exec/AbortRequestor.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
|
||||||
|
export class AbortClassRequestor{
|
||||||
|
private abortRequest=false;
|
||||||
|
|
||||||
|
requestAbort(){
|
||||||
|
this.abortRequest=true;
|
||||||
|
}
|
||||||
|
isAborted(){
|
||||||
|
return this.abortRequest;
|
||||||
|
}
|
||||||
|
}
|
147
client/src/exec/BranFlakesExecutorVisitor.ts
Normal file
147
client/src/exec/BranFlakesExecutorVisitor.ts
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
import { AbstractParseTreeVisitor } from 'antlr4ts/tree/AbstractParseTreeVisitor';
|
||||||
|
import { LoopStmtContext } from '../generated/bfParser';
|
||||||
|
import { bfVisitor } from '../generated/bfVisitor';
|
||||||
|
import { DiagnosticSeverity } from 'vscode-languageclient';
|
||||||
|
import { getTree } from '../BranFlakesParseRunner';
|
||||||
|
import { RuleNode } from 'antlr4ts/tree/RuleNode';
|
||||||
|
import type InputStrategy from '../input/InputStrategy';
|
||||||
|
import { AbortClassRequestor } from './AbortRequestor';
|
||||||
|
|
||||||
|
export class BranFlakesExecutorVisitor
|
||||||
|
extends AbstractParseTreeVisitor<Promise<void>>
|
||||||
|
implements bfVisitor<Promise<void>> {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param input Input string
|
||||||
|
* @param inputPtr Input pointer to start from
|
||||||
|
*/
|
||||||
|
constructor(
|
||||||
|
private inputStrategy: InputStrategy,
|
||||||
|
private logger: (val: string) => Thenable<void>,
|
||||||
|
private abortRequestor?: AbortClassRequestor,
|
||||||
|
private inputPtr: number = 0
|
||||||
|
) {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
// /**
|
||||||
|
// * The memory cells (Can work with negative cells this way)
|
||||||
|
// */
|
||||||
|
// private cells: Map<number, number> = new Map();
|
||||||
|
|
||||||
|
private byteArraySize: number = 30000;
|
||||||
|
private byteArray: Int8Array = new Int8Array(this.byteArraySize);
|
||||||
|
/**
|
||||||
|
* Pointer
|
||||||
|
*/
|
||||||
|
private ptr: number = 0;
|
||||||
|
/** Output string */
|
||||||
|
private outputStr: string = '';
|
||||||
|
|
||||||
|
|
||||||
|
defaultResult() {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Run a file
|
||||||
|
* @param text
|
||||||
|
* @param fn
|
||||||
|
* @param inputStrategy
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
static async run(
|
||||||
|
text: string,
|
||||||
|
fn: string,
|
||||||
|
inputStrategy: InputStrategy,
|
||||||
|
logger: (str: string) => Thenable<void>,
|
||||||
|
aborter?: AbortClassRequestor
|
||||||
|
) {
|
||||||
|
//get tree and issues
|
||||||
|
const { tree, issues } = getTree(text, fn);
|
||||||
|
|
||||||
|
//get only errors
|
||||||
|
const x = issues.filter(e => e.type === DiagnosticSeverity.Error);
|
||||||
|
//if any error, drop
|
||||||
|
if (x.length > 0) {
|
||||||
|
throw Error('Errors exist');
|
||||||
|
}
|
||||||
|
// make visitor
|
||||||
|
const vis = new BranFlakesExecutorVisitor(inputStrategy, logger, aborter);
|
||||||
|
//visit the tree
|
||||||
|
await vis.visit(tree);
|
||||||
|
|
||||||
|
//get output
|
||||||
|
return vis.outputStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
getCell(pointerIndex: number) {
|
||||||
|
return this.byteArray[pointerIndex];
|
||||||
|
}
|
||||||
|
setCell(pointerIndex: number, value: number): void {
|
||||||
|
this.byteArray[pointerIndex] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
async visitLoopStmt(ctx: LoopStmtContext) {
|
||||||
|
while ((this.getCell(this.ptr) ?? 0) !== 0) {
|
||||||
|
await this.visitChildren(ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async visitPtrLeft() {
|
||||||
|
this.ptr = (this.ptr + this.byteArraySize - 1) % this.byteArraySize;
|
||||||
|
}
|
||||||
|
async visitPtrRight() {
|
||||||
|
this.ptr = (this.ptr + this.byteArraySize + 1) % this.byteArraySize;
|
||||||
|
}
|
||||||
|
async visitPtrIncr() {
|
||||||
|
const val = this.getCell(this.ptr);
|
||||||
|
this.setCell(this.ptr, (val + 1) % 256);
|
||||||
|
}
|
||||||
|
async visitPtrDecr() {
|
||||||
|
const val = this.getCell(this.ptr);
|
||||||
|
this.setCell(this.ptr, (val + 255) % 256);
|
||||||
|
}
|
||||||
|
async visitOutputStmt() {
|
||||||
|
const val = this.getCell(this.ptr) ?? 0;
|
||||||
|
const str = String.fromCharCode(val);
|
||||||
|
|
||||||
|
this.outputStr += str;
|
||||||
|
await this.logger(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
async visitInputStmt() {
|
||||||
|
//get char
|
||||||
|
const char = (await this.inputStrategy.getInput()) ?? 0;
|
||||||
|
if (char === 3) { throw Error('Halt input wait'); }
|
||||||
|
//increment the input pointer after this
|
||||||
|
this.inputPtr++;
|
||||||
|
this.setCell(this.ptr, char);
|
||||||
|
}
|
||||||
|
|
||||||
|
// override for maintaining async
|
||||||
|
async visitChildren(node: RuleNode): Promise<void> {
|
||||||
|
let result = this.defaultResult();
|
||||||
|
await result;
|
||||||
|
let n = node.childCount;
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
if (!this.shouldVisitNextChild(node, result)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let c = node.getChild(i);
|
||||||
|
let childResult = c.accept(this);
|
||||||
|
result = this.aggregateResult(result, childResult);
|
||||||
|
await result;
|
||||||
|
}
|
||||||
|
// break for any close requests
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
|
||||||
|
setTimeout(() => { if (this.abortRequestor?.isAborted()??false) {rej('aborted');} else {res(undefined);} }, 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// override for maintaining async
|
||||||
|
protected async aggregateResult(
|
||||||
|
aggregate: Promise<void>,
|
||||||
|
nextResult: Promise<void>
|
||||||
|
): Promise<void> {
|
||||||
|
await aggregate;
|
||||||
|
return nextResult;
|
||||||
|
}
|
||||||
|
}
|
@@ -4,18 +4,21 @@
|
|||||||
* ------------------------------------------------------------------------------------------ */
|
* ------------------------------------------------------------------------------------------ */
|
||||||
|
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { workspace, ExtensionContext,commands, window } from 'vscode';
|
import { commands, tasks, workspace,window } from 'vscode';
|
||||||
import {runBF} from './bfExecutor';
|
import type { Disposable, ExtensionContext } from 'vscode';
|
||||||
import {
|
import {
|
||||||
LanguageClient,
|
LanguageClient,
|
||||||
LanguageClientOptions,
|
LanguageClientOptions,
|
||||||
ServerOptions,
|
ServerOptions,
|
||||||
TransportKind
|
TransportKind,
|
||||||
} from 'vscode-languageclient';
|
} from 'vscode-languageclient';
|
||||||
|
import { CompileBranFlakesCommand } from './command/CompileBranFlakesCommand';
|
||||||
|
import { CustomExecutionTaskProvider } from './task/CustomExecutionTerminal';
|
||||||
|
|
||||||
let client: LanguageClient;
|
let client: LanguageClient;
|
||||||
|
|
||||||
|
let bfRunTaskProvider: Disposable;
|
||||||
|
|
||||||
export function activate(context: ExtensionContext) {
|
export function activate(context: ExtensionContext) {
|
||||||
// The server is implemented in node
|
// The server is implemented in node
|
||||||
let serverModule = context.asAbsolutePath(
|
let serverModule = context.asAbsolutePath(
|
||||||
@@ -32,26 +35,28 @@ export function activate(context: ExtensionContext) {
|
|||||||
debug: {
|
debug: {
|
||||||
module: serverModule,
|
module: serverModule,
|
||||||
transport: TransportKind.ipc,
|
transport: TransportKind.ipc,
|
||||||
options: debugOptions
|
options: debugOptions,
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
// Options to control the language client
|
// Options to control the language client
|
||||||
let clientOptions: LanguageClientOptions = {
|
let clientOptions: LanguageClientOptions = {
|
||||||
// Register the server for plain text documents
|
// Register the server for plain text documents
|
||||||
documentSelector: [{ scheme: 'file', language: 'bf' }]
|
documentSelector: [{ scheme: 'file', language: 'bf' }],
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = 'bf.execute';
|
const branFlakesCommands = [new CompileBranFlakesCommand()];
|
||||||
const commandHandler = async()=>{
|
for (let branFlakesCommand of branFlakesCommands) {
|
||||||
const text= window.activeTextEditor.document.getText();
|
context.subscriptions.push(
|
||||||
const fn = window.activeTextEditor.document.fileName;
|
commands.registerCommand(
|
||||||
const input = await window.showInputBox({prompt:'Enter input (If not enough, program will assume 0)'});
|
branFlakesCommand.getCommandName(),
|
||||||
const output = await runBF(text,fn,input);
|
branFlakesCommand.getCommandHandler()
|
||||||
await window.showInformationMessage(`Output: ${output}`);
|
),
|
||||||
};
|
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
context.subscriptions.push(commands.registerCommand(command,commandHandler));
|
|
||||||
|
|
||||||
// Create the language client and start the client.
|
// Create the language client and start the client.
|
||||||
client = new LanguageClient(
|
client = new LanguageClient(
|
||||||
@@ -63,11 +68,17 @@ export function activate(context: ExtensionContext) {
|
|||||||
|
|
||||||
// Start the client. This will also launch the server
|
// Start the client. This will also launch the server
|
||||||
client.start();
|
client.start();
|
||||||
|
|
||||||
|
const workspaceRoot = (workspace.workspaceFolders && (workspace.workspaceFolders.length > 0))
|
||||||
|
? workspace.workspaceFolders[0].uri.fsPath : undefined;
|
||||||
|
|
||||||
|
bfRunTaskProvider = tasks.registerTaskProvider(CustomExecutionTaskProvider.type, new CustomExecutionTaskProvider(workspaceRoot));
|
||||||
}
|
}
|
||||||
|
|
||||||
export function deactivate(): Thenable<void> | undefined {
|
export function deactivate(): Thenable<void> | undefined {
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return client.stop();
|
bfRunTaskProvider?.dispose();
|
||||||
|
client?.stop();
|
||||||
}
|
}
|
||||||
|
4
client/src/input/InputStrategy.ts
Normal file
4
client/src/input/InputStrategy.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
export default interface InputStrategy {
|
||||||
|
getInput(): Promise<number>;
|
||||||
|
};
|
33
client/src/input/VSCodePromptInputStrategy.ts
Normal file
33
client/src/input/VSCodePromptInputStrategy.ts
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
import type { CancellationToken, InputBoxOptions } from 'vscode';
|
||||||
|
import type InputStrategy from './InputStrategy';
|
||||||
|
|
||||||
|
export class VSCodePromptInputStrategy implements InputStrategy {
|
||||||
|
private inputQueue: string = '';
|
||||||
|
constructor(
|
||||||
|
private requestor: (
|
||||||
|
promptOptions?: InputBoxOptions,
|
||||||
|
cancelToken?: CancellationToken
|
||||||
|
) => Thenable<string>
|
||||||
|
) { }
|
||||||
|
|
||||||
|
async getInput(): Promise<number> {
|
||||||
|
while (this.inputQueue.length === 0) {
|
||||||
|
await this.requestInputFromPrompt();
|
||||||
|
}
|
||||||
|
const character = this.popInputFromQueue();
|
||||||
|
|
||||||
|
return character;
|
||||||
|
}
|
||||||
|
private popInputFromQueue() {
|
||||||
|
const character = this.inputQueue.charCodeAt(0);
|
||||||
|
this.inputQueue = this.inputQueue.substring(1);
|
||||||
|
return character;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async requestInputFromPrompt() {
|
||||||
|
const inputPrompt = await this.requestor({
|
||||||
|
prompt: 'More input is required. Please provide input:',
|
||||||
|
});
|
||||||
|
this.inputQueue += inputPrompt;
|
||||||
|
}
|
||||||
|
}
|
152
client/src/task/CustomExecutionTerminal.ts
Normal file
152
client/src/task/CustomExecutionTerminal.ts
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
import * as vscode from 'vscode';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { BranFlakesExecutorVisitor } from '../exec/BranFlakesExecutorVisitor';
|
||||||
|
import { AbortClassRequestor } from '../exec/AbortRequestor';
|
||||||
|
|
||||||
|
interface BFRunTaskDefinition extends vscode.TaskDefinition {
|
||||||
|
file?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CustomExecutionTaskProvider implements vscode.TaskProvider {
|
||||||
|
static type: string = 'bf-run';
|
||||||
|
tasks: vscode.Task[] | undefined;
|
||||||
|
|
||||||
|
constructor(private workspaceRoot: string | undefined) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
provideTasks(token?: vscode.CancellationToken): vscode.ProviderResult<vscode.Task[]> {
|
||||||
|
if (this.tasks !== undefined) { return this.tasks; }
|
||||||
|
|
||||||
|
this.tasks = [this.getTaskFromDefinition(undefined)];
|
||||||
|
return this.tasks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
getTaskFromDefinition(fileName: string | undefined): vscode.Task {
|
||||||
|
const definition: BFRunTaskDefinition = {
|
||||||
|
type: CustomExecutionTaskProvider.type,
|
||||||
|
file: fileName
|
||||||
|
};
|
||||||
|
return new vscode.Task(definition, vscode.TaskScope.Workspace, `bf: run: current file`, CustomExecutionTaskProvider.type,
|
||||||
|
new vscode.CustomExecution(async () => {
|
||||||
|
return new CustomBuildTaskTerminal(definition.file);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
resolveTask(_task: vscode.Task, token?: vscode.CancellationToken): vscode.ProviderResult<vscode.Task> {
|
||||||
|
const definition: BFRunTaskDefinition = <any>_task.definition;
|
||||||
|
|
||||||
|
const fileNameRecovered = definition.file;
|
||||||
|
const taskName = `bf: run: ` + (fileNameRecovered ?? 'current file');
|
||||||
|
return new vscode.Task(definition, vscode.TaskScope.Workspace, taskName, CustomExecutionTaskProvider.type,
|
||||||
|
new vscode.CustomExecution(async () => {
|
||||||
|
return new CustomBuildTaskTerminal(definition.file);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceLFWithCRLF(data: string) {
|
||||||
|
return data.replace(/(?<!\r)\n/gm, '\r\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CustomBuildTaskTerminal implements vscode.Pseudoterminal {
|
||||||
|
private writeEmitter = new vscode.EventEmitter<string>();
|
||||||
|
private closeEmitter = new vscode.EventEmitter<number>();
|
||||||
|
|
||||||
|
private readEmitter = new vscode.EventEmitter<void>();
|
||||||
|
inputQueue: number[] = [];
|
||||||
|
|
||||||
|
private openDocument: vscode.TextDocument | undefined;
|
||||||
|
onDidWrite: vscode.Event<string> = this.writeEmitter.event;
|
||||||
|
onDidClose?: vscode.Event<number> = this.closeEmitter.event;
|
||||||
|
abortRequestor = new AbortClassRequestor();
|
||||||
|
|
||||||
|
handleInput(data: string): void {
|
||||||
|
// this.writeEmitter.fire(`Echo(${data.length})` + data);
|
||||||
|
const newData = [...data].map(e => e.charCodeAt(0));
|
||||||
|
console.log('new input', newData);
|
||||||
|
this.inputQueue.push(...newData);
|
||||||
|
this.writeEmitter.fire(replaceLFWithCRLF(data));
|
||||||
|
this.readEmitter.fire();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
constructor(private fileName?: string) {
|
||||||
|
}
|
||||||
|
|
||||||
|
open(_initialDimensions: vscode.TerminalDimensions | undefined): void {
|
||||||
|
// At this point we can start using the terminal.
|
||||||
|
this.openDocumentForTask().then(this.doExecution.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
getPath(fileLocationString: string | undefined, fileName: string) {
|
||||||
|
if (fileLocationString === undefined) { return vscode.Uri.file(fileName); }
|
||||||
|
return vscode.Uri.file(path.resolve(fileLocationString, fileName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async openDocumentForTask() {
|
||||||
|
let openDocument: vscode.TextDocument;
|
||||||
|
if (this.fileName !== undefined) {
|
||||||
|
try {
|
||||||
|
const fileLocationPathString = vscode.workspace.workspaceFolders?.[0]?.uri?.fsPath;
|
||||||
|
const finalPath = this.getPath(fileLocationPathString, this.fileName);
|
||||||
|
openDocument = await vscode.workspace.openTextDocument(finalPath);
|
||||||
|
} catch (e) {
|
||||||
|
vscode.window.showErrorMessage('Failed to open file ' + this.fileName);
|
||||||
|
this.closeEmitter.fire(2);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
openDocument = vscode.window.activeTextEditor.document;
|
||||||
|
}
|
||||||
|
this.openDocument = openDocument;
|
||||||
|
}
|
||||||
|
|
||||||
|
close(): void {
|
||||||
|
// The terminal has been closed. Shutdown the build.
|
||||||
|
console.log('Forced close');
|
||||||
|
this.abortRequestor.requestAbort();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async doExecution(): Promise<void> {
|
||||||
|
this.writeEmitter.fire('[bf] Requested execution of ' + (this.fileName ?? 'active file') + '\r\n');
|
||||||
|
const cus = this;
|
||||||
|
try {
|
||||||
|
|
||||||
|
await BranFlakesExecutorVisitor.run(this.openDocument.getText(),
|
||||||
|
this.openDocument.uri.fsPath,
|
||||||
|
{
|
||||||
|
getInput() {
|
||||||
|
return new Promise((res, rej) => {
|
||||||
|
if (cus.inputQueue.length > 0) {
|
||||||
|
const char = cus.inputQueue.shift();
|
||||||
|
res(char);
|
||||||
|
} else {
|
||||||
|
const dispose: vscode.Disposable[] = [];
|
||||||
|
cus.readEmitter.event(e => {
|
||||||
|
const char = cus.inputQueue.shift();
|
||||||
|
//clear the earliest disposable
|
||||||
|
dispose.shift()?.dispose();
|
||||||
|
res(char);
|
||||||
|
}, null, dispose);
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async (data) => {
|
||||||
|
this.writeEmitter.fire(replaceLFWithCRLF(data));
|
||||||
|
},this.abortRequestor
|
||||||
|
);
|
||||||
|
this.closeEmitter.fire(0);
|
||||||
|
} catch (e) {
|
||||||
|
this.closeEmitter.fire(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@@ -16,7 +16,8 @@ const config = {
|
|||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
filename: '[name].js',
|
filename: '[name].js',
|
||||||
libraryTarget: 'commonjs2',
|
libraryTarget: 'commonjs2',
|
||||||
devtoolModuleFilenameTemplate: '../[resource-path]'
|
sourceMapFilename: '[name].js.map',
|
||||||
|
devtoolModuleFilenameTemplate: '../[resource-path]',
|
||||||
},
|
},
|
||||||
// devtool: 'source-map',
|
// devtool: 'source-map',
|
||||||
externals: {
|
externals: {
|
||||||
@@ -27,6 +28,7 @@ const config = {
|
|||||||
innerGraph:true,
|
innerGraph:true,
|
||||||
usedExports:true
|
usedExports:true
|
||||||
},
|
},
|
||||||
|
|
||||||
resolve: {
|
resolve: {
|
||||||
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
|
// support reading TypeScript and JavaScript files, 📖 -> https://github.com/TypeStrong/ts-loader
|
||||||
extensions: ['.ts', '.js'],
|
extensions: ['.ts', '.js'],
|
||||||
|
8211
package-lock.json
generated
8211
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
19
package.json
19
package.json
@@ -5,9 +5,9 @@
|
|||||||
"author": "Atreya Bain",
|
"author": "Atreya Bain",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"publisher": "atreyabain",
|
"publisher": "atreyabain",
|
||||||
"version": "0.0.4",
|
"version": "0.3.1",
|
||||||
"icon": "assets/128.png",
|
"icon": "assets/128.png",
|
||||||
"categories": [],
|
"categories": ["Programming Languages","Linters"],
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"multi-root ready",
|
"multi-root ready",
|
||||||
"brainfuck",
|
"brainfuck",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/chrisvrose/bf-server"
|
"url": "https://github.com/chrisvrose/bf-server"
|
||||||
},
|
},
|
||||||
"main": "./client/dist/extension",
|
"main": "./client/dist/extension.js",
|
||||||
"contributes": {
|
"contributes": {
|
||||||
"languages": [
|
"languages": [
|
||||||
{
|
{
|
||||||
@@ -57,7 +57,7 @@
|
|||||||
],
|
],
|
||||||
"commands": [
|
"commands": [
|
||||||
{
|
{
|
||||||
"command": "bf.execute",
|
"command": "bf.execute.old",
|
||||||
"title": "BF: Execute",
|
"title": "BF: Execute",
|
||||||
"when": "editorLangId == bf",
|
"when": "editorLangId == bf",
|
||||||
"enablement": "editorLangId == bf"
|
"enablement": "editorLangId == bf"
|
||||||
@@ -85,7 +85,18 @@
|
|||||||
"description": "Traces the communication between VS Code and the language server."
|
"description": "Traces the communication between VS Code and the language server."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"taskDefinitions": [
|
||||||
|
{
|
||||||
|
"type": "bf-run",
|
||||||
|
"properties": {
|
||||||
|
"file":{
|
||||||
|
"type":"string",
|
||||||
|
"description": "The BF file to be executed. Can be omitted to run current file"
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"vscode:prepublish": "npm run compile",
|
"vscode:prepublish": "npm run compile",
|
||||||
|
128
server/src/connection.ts
Normal file
128
server/src/connection.ts
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import { CompletionItem, CompletionItemKind, Connection, DidChangeConfigurationNotification, DidChangeConfigurationParams, InitializeParams, InitializeResult, TextDocumentPositionParams, TextDocuments, TextDocumentSyncKind } from 'vscode-languageserver';
|
||||||
|
import { validateTextDocument } from './validation';
|
||||||
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||||
|
import { BranFlakesSettings, defaultSettings, SettingsManager } from './settings';
|
||||||
|
|
||||||
|
export class BranFlakesConnectionManager {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(protected connection: Connection, private validator:typeof validateTextDocument, private documents:TextDocuments<TextDocument>, private settingsManager:SettingsManager) {
|
||||||
|
connection.onInitialize(this.initConnection.bind(this));
|
||||||
|
connection.onDidChangeConfiguration(this.onDidChangeConfiguration.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
initConnection(params: InitializeParams) {
|
||||||
|
let capabilities = params.capabilities;
|
||||||
|
|
||||||
|
// Does the client support the `workspace/configuration` request?
|
||||||
|
// If not, we will fall back using global settings
|
||||||
|
this.settingsManager.hasConfigurationCapability = !!(
|
||||||
|
capabilities.workspace && !!capabilities.workspace.configuration
|
||||||
|
);
|
||||||
|
this.settingsManager.hasWorkspaceFolderCapability = !!(
|
||||||
|
capabilities.workspace && !!capabilities.workspace.workspaceFolders
|
||||||
|
);
|
||||||
|
this.settingsManager.hasDiagnosticRelatedInformationCapability = !!(
|
||||||
|
capabilities.textDocument &&
|
||||||
|
capabilities.textDocument.publishDiagnostics &&
|
||||||
|
capabilities.textDocument.publishDiagnostics.relatedInformation
|
||||||
|
);
|
||||||
|
|
||||||
|
const result: InitializeResult = {
|
||||||
|
capabilities: {
|
||||||
|
textDocumentSync: TextDocumentSyncKind.Incremental,
|
||||||
|
// Tell the client that the server supports code completion
|
||||||
|
completionProvider: {
|
||||||
|
resolveProvider: false,
|
||||||
|
triggerCharacters: ['.'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (this.settingsManager.hasWorkspaceFolderCapability) {
|
||||||
|
result.capabilities.workspace = {
|
||||||
|
workspaceFolders: {
|
||||||
|
supported: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
onDidChangeConfiguration(change:DidChangeConfigurationParams){
|
||||||
|
if (this.settingsManager.hasConfigurationCapability) {
|
||||||
|
// Reset all cached document settings
|
||||||
|
this.settingsManager.clearDocumentSettings();
|
||||||
|
} else {
|
||||||
|
this.settingsManager.updateSettings(
|
||||||
|
(change.settings.languageServerExample || defaultSettings)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Revalidate all open text documents
|
||||||
|
Promise.all(this.documents.all().map(validateTextDocument)).catch(e => {
|
||||||
|
this.connection.console.log('Failed to validate text documents');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
onInit() {
|
||||||
|
if (this.settingsManager.hasConfigurationCapability) {
|
||||||
|
// Register for all configuration changes.
|
||||||
|
this.connection.client.register(
|
||||||
|
DidChangeConfigurationNotification.type,
|
||||||
|
undefined
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (this.settingsManager.hasWorkspaceFolderCapability) {
|
||||||
|
this.connection.workspace.onDidChangeWorkspaceFolders(_event => {
|
||||||
|
// connection.console.log('Workspace folder change event received.');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnCompletion() {
|
||||||
|
this.connection.onCompletion((_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
|
||||||
|
const completions: CompletionItem[] = [
|
||||||
|
{
|
||||||
|
label: '+',
|
||||||
|
detail: 'Addition',
|
||||||
|
documentation: 'Add one to cell',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '-',
|
||||||
|
detail: 'Subtraction',
|
||||||
|
documentation: 'Subtract one from cell',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: ',',
|
||||||
|
detail: 'Input',
|
||||||
|
documentation: 'Ask for input (Stored in the ASCII format)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '.',
|
||||||
|
detail: 'Output',
|
||||||
|
documentation: 'Output the equivalent ASCII character',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '>',
|
||||||
|
detail: 'Right Shift',
|
||||||
|
documentation: 'Shift the pointer one cell to the right',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '<',
|
||||||
|
detail: 'Left Shift',
|
||||||
|
documentation: 'Shift the pointer one cell to the Left',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return completions.map(e => {
|
||||||
|
e.kind = CompletionItemKind.Operator;
|
||||||
|
return e;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
listen() {
|
||||||
|
this.connection.listen();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -5,119 +5,33 @@
|
|||||||
import {
|
import {
|
||||||
createConnection,
|
createConnection,
|
||||||
TextDocuments,
|
TextDocuments,
|
||||||
Diagnostic,
|
|
||||||
DiagnosticSeverity,
|
|
||||||
ProposedFeatures,
|
ProposedFeatures,
|
||||||
InitializeParams,
|
|
||||||
DidChangeConfigurationNotification,
|
|
||||||
CompletionItem,
|
|
||||||
CompletionItemKind,
|
|
||||||
TextDocumentPositionParams,
|
|
||||||
TextDocumentSyncKind,
|
|
||||||
InitializeResult,
|
|
||||||
Position,
|
|
||||||
} from 'vscode-languageserver';
|
} from 'vscode-languageserver';
|
||||||
|
|
||||||
// import * as path from 'path';
|
|
||||||
|
|
||||||
import { TextDocument } from 'vscode-languageserver-textdocument';
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||||
// import { getTree } from './bfGrammar';
|
import { BranFlakesSettings, defaultSettings, SettingsManager } from './settings';
|
||||||
|
import { BranFlakesConnectionManager } from './connection';
|
||||||
|
import { validateTextDocument } from './validation';
|
||||||
|
|
||||||
// Create a connection for the server. The connection uses Node's IPC as a transport.
|
// Create a connection for the server. The connection uses Node's IPC as a transport.
|
||||||
// Also include all preview / proposed LSP features.
|
// Also include all preview / proposed LSP features.
|
||||||
let connection = createConnection(ProposedFeatures.all);
|
export let connection = createConnection(ProposedFeatures.all);
|
||||||
|
|
||||||
|
|
||||||
// Create a simple text document manager. The text document manager
|
// Create a simple text document manager. The text document manager
|
||||||
// supports full document sync only
|
// supports full document sync only
|
||||||
let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
|
let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
|
||||||
|
|
||||||
let hasConfigurationCapability: boolean = false;
|
|
||||||
let hasWorkspaceFolderCapability: boolean = false;
|
|
||||||
let hasDiagnosticRelatedInformationCapability: boolean = false;
|
|
||||||
|
|
||||||
connection.onInitialize((params: InitializeParams) => {
|
let globalSettings: BranFlakesSettings = defaultSettings;
|
||||||
let capabilities = params.capabilities;
|
|
||||||
|
|
||||||
// Does the client support the `workspace/configuration` request?
|
|
||||||
// If not, we will fall back using global settings
|
|
||||||
hasConfigurationCapability = !!(
|
|
||||||
capabilities.workspace && !!capabilities.workspace.configuration
|
|
||||||
);
|
|
||||||
hasWorkspaceFolderCapability = !!(
|
|
||||||
capabilities.workspace && !!capabilities.workspace.workspaceFolders
|
|
||||||
);
|
|
||||||
hasDiagnosticRelatedInformationCapability = !!(
|
|
||||||
capabilities.textDocument &&
|
|
||||||
capabilities.textDocument.publishDiagnostics &&
|
|
||||||
capabilities.textDocument.publishDiagnostics.relatedInformation
|
|
||||||
);
|
|
||||||
|
|
||||||
const result: InitializeResult = {
|
|
||||||
capabilities: {
|
|
||||||
textDocumentSync: TextDocumentSyncKind.Incremental,
|
|
||||||
// Tell the client that the server supports code completion
|
|
||||||
completionProvider: {
|
|
||||||
resolveProvider: false,
|
|
||||||
triggerCharacters: ['.'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
if (hasWorkspaceFolderCapability) {
|
|
||||||
result.capabilities.workspace = {
|
|
||||||
workspaceFolders: {
|
|
||||||
supported: true,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
|
|
||||||
connection.onInitialized(() => {
|
|
||||||
if (hasConfigurationCapability) {
|
|
||||||
// Register for all configuration changes.
|
|
||||||
connection.client.register(
|
|
||||||
DidChangeConfigurationNotification.type,
|
|
||||||
undefined
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if (hasWorkspaceFolderCapability) {
|
|
||||||
connection.workspace.onDidChangeWorkspaceFolders(_event => {
|
|
||||||
connection.console.log('Workspace folder change event received.');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// The example settings
|
|
||||||
interface ExampleSettings {
|
|
||||||
maxNumberOfProblems: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The global settings, used when the `workspace/configuration` request is not supported by the client.
|
|
||||||
// Please note that this is not the case when using this server with the client provided in this example
|
|
||||||
// but could happen with other clients.
|
|
||||||
const defaultSettings: ExampleSettings = { maxNumberOfProblems: 5 };
|
|
||||||
let globalSettings: ExampleSettings = defaultSettings;
|
|
||||||
|
|
||||||
// Cache the settings of all open documents
|
// Cache the settings of all open documents
|
||||||
let documentSettings: Map<string, Thenable<ExampleSettings>> = new Map();
|
let documentSettings: Map<string, Thenable<BranFlakesSettings>> = new Map();
|
||||||
|
let settingsManager = new SettingsManager();
|
||||||
|
let cm = new BranFlakesConnectionManager(connection, validateTextDocument,documents,settingsManager);
|
||||||
|
|
||||||
connection.onDidChangeConfiguration(change => {
|
|
||||||
if (hasConfigurationCapability) {
|
|
||||||
// Reset all cached document settings
|
|
||||||
documentSettings.clear();
|
|
||||||
} else {
|
|
||||||
globalSettings = <ExampleSettings>(
|
|
||||||
(change.settings.languageServerExample || defaultSettings)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Revalidate all open text documents
|
export function getDocumentSettings(resource: string): Thenable<BranFlakesSettings> {
|
||||||
documents.all().forEach(validateTextDocument);
|
if (!settingsManager.hasConfigurationCapability) {
|
||||||
});
|
|
||||||
|
|
||||||
function getDocumentSettings(resource: string): Thenable<ExampleSettings> {
|
|
||||||
if (!hasConfigurationCapability) {
|
|
||||||
return Promise.resolve(globalSettings);
|
return Promise.resolve(globalSettings);
|
||||||
}
|
}
|
||||||
let result = documentSettings.get(resource);
|
let result = documentSettings.get(resource);
|
||||||
@@ -133,13 +47,12 @@ function getDocumentSettings(resource: string): Thenable<ExampleSettings> {
|
|||||||
|
|
||||||
// Only keep settings for open documents
|
// Only keep settings for open documents
|
||||||
documents.onDidClose(e => {
|
documents.onDidClose(e => {
|
||||||
documentSettings.delete(e.document.uri);
|
settingsManager.closeDocument(e.document.uri);
|
||||||
});
|
});
|
||||||
|
|
||||||
// The content of a text document has changed. This event is emitted
|
// The content of a text document has changed. This event is emitted
|
||||||
// when the text document first opened or when its content has changed.
|
// when the text document first opened or when its content has changed.
|
||||||
documents.onDidChangeContent(change => {
|
documents.onDidChangeContent(change => {
|
||||||
//change.contentChanges;
|
|
||||||
validateTextDocument(change.document);
|
validateTextDocument(change.document);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -148,112 +61,12 @@ documents.onDidSave(change => {
|
|||||||
validateTextDocument(change.document);
|
validateTextDocument(change.document);
|
||||||
});
|
});
|
||||||
|
|
||||||
const validateBrackets = (text: string) => {
|
|
||||||
let count = 0, lp: number[] = [],issues:number[]=[];
|
|
||||||
const textsplit = text.split('');
|
|
||||||
textsplit.forEach((x, i) => {
|
|
||||||
if (x === '[' || x === ']') {
|
|
||||||
|
|
||||||
if (x === '[') {lp.push(i);}
|
|
||||||
if (x === ']') {if(lp.length===0) {issues.push(i);}lp.pop();}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
return [...lp,...issues];
|
|
||||||
};
|
|
||||||
|
|
||||||
async function validateTextDocument(textDocument: TextDocument): Promise<void> {
|
|
||||||
// In this simple example we get the settings for every validate run.
|
|
||||||
let settings = await getDocumentSettings(textDocument.uri);
|
|
||||||
// The validator creates diagnostics for all uppercase words length 2 and more
|
|
||||||
const text = textDocument.getText();
|
|
||||||
|
|
||||||
let problems = 0;
|
|
||||||
let diagnostics: Diagnostic[] = [];
|
|
||||||
const issues = validateBrackets(text);
|
|
||||||
|
|
||||||
diagnostics.push(...issues.map<Diagnostic>(e => ({
|
|
||||||
message: 'Brackets unmatched',
|
|
||||||
range:{
|
|
||||||
start: textDocument.positionAt(e),
|
|
||||||
end: textDocument.positionAt(e+1),
|
|
||||||
},
|
|
||||||
severity:DiagnosticSeverity.Error,
|
|
||||||
code:'[ and ]',
|
|
||||||
})));
|
|
||||||
|
|
||||||
// diagnostics.push({
|
|
||||||
// message: 'Brackets not matched',
|
|
||||||
// range: {
|
|
||||||
// start: { line: 0, character: 0 },
|
|
||||||
// end: { line: 0, character: 0 },
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
|
|
||||||
// diagnostics.push(<Diagnostic>{
|
|
||||||
// severity: DiagnosticSeverity.Information,
|
|
||||||
// range: {
|
|
||||||
// start: textDocument.positionAt(0),
|
|
||||||
// end: textDocument.positionAt(1),
|
|
||||||
// },
|
|
||||||
// // message:`HI:(${text})(${result.hasErrors()},${result.hasWarnings()})`,
|
|
||||||
// message: `Parsing Failed`,
|
|
||||||
// source: 'test',
|
|
||||||
// });
|
|
||||||
|
|
||||||
// Send the computed diagnostics to VSCode.
|
|
||||||
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
|
|
||||||
}
|
|
||||||
|
|
||||||
// This handler provides the initial list of the completion items.
|
// This handler provides the initial list of the completion items.
|
||||||
connection.onCompletion(
|
cm.setOnCompletion();
|
||||||
(_textDocumentPosition: TextDocumentPositionParams): CompletionItem[] => {
|
|
||||||
const completions: CompletionItem[] = [
|
|
||||||
{
|
|
||||||
label: '+',
|
|
||||||
detail: 'Addition',
|
|
||||||
documentation: 'Add one to cell',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '-',
|
|
||||||
detail: 'Subtraction',
|
|
||||||
documentation: 'Subtract one from cell',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: ',',
|
|
||||||
detail: 'Input',
|
|
||||||
documentation: 'Ask for input (Stored in the ASCII format)',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '.',
|
|
||||||
detail: 'Output',
|
|
||||||
documentation: 'Output the equivalent ASCII character',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '>',
|
|
||||||
detail: 'Right Shift',
|
|
||||||
documentation: 'Shift the pointer one cell to the right',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '<',
|
|
||||||
detail: 'Left Shift',
|
|
||||||
documentation: 'Shift the pointer one cell to the Left',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
return completions.map(e => {
|
|
||||||
e.kind = CompletionItemKind.Operator;
|
|
||||||
return e;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
// Make the text document manager listen on the connection
|
// Make the text document manager listen on the connection
|
||||||
// for open, change and close text document events
|
// for open, change and close text document events
|
||||||
documents.listen(connection);
|
documents.listen(connection);
|
||||||
|
|
||||||
// Listen on the connection
|
// Listen on the connection
|
||||||
connection.listen();
|
cm.listen();
|
35
server/src/settings.ts
Normal file
35
server/src/settings.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// The example settings
|
||||||
|
export interface BranFlakesSettings {
|
||||||
|
maxNumberOfProblems: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// The global settings, used when the `workspace/configuration` request is not supported by the client.
|
||||||
|
// Please note that this is not the case when using this server with the client provided in this example
|
||||||
|
// but could happen with other clients.
|
||||||
|
export const defaultSettings: BranFlakesSettings = {
|
||||||
|
maxNumberOfProblems: 5
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export class SettingsManager {
|
||||||
|
hasConfigurationCapability: boolean = false;
|
||||||
|
hasWorkspaceFolderCapability: boolean = false;
|
||||||
|
hasDiagnosticRelatedInformationCapability: boolean = false;
|
||||||
|
|
||||||
|
documentSettings: Map<string, Thenable<BranFlakesSettings>> = new Map();
|
||||||
|
|
||||||
|
#settings = defaultSettings;
|
||||||
|
|
||||||
|
updateSettings(newSettings: BranFlakesSettings) {
|
||||||
|
this.#settings = newSettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
closeDocument(doc: string) {
|
||||||
|
this.documentSettings.delete(doc);
|
||||||
|
|
||||||
|
}
|
||||||
|
clearDocumentSettings(){
|
||||||
|
this.documentSettings.clear();
|
||||||
|
}
|
||||||
|
}
|
54
server/src/validation.ts
Normal file
54
server/src/validation.ts
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
import { Diagnostic, DiagnosticSeverity } from 'vscode-languageserver';
|
||||||
|
import { TextDocument } from 'vscode-languageserver-textdocument';
|
||||||
|
import { getDocumentSettings, connection } from './server';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const validateBrackets = (text: string) => {
|
||||||
|
let count = 0, lp: number[] = [], issues: number[] = [];
|
||||||
|
const textsplit = text.split('');
|
||||||
|
textsplit.forEach((x, i) => {
|
||||||
|
if (x === '[' || x === ']') {
|
||||||
|
if (x === '[') {
|
||||||
|
lp.push(i);
|
||||||
|
}
|
||||||
|
if (x === ']') {
|
||||||
|
if (lp.length === 0) {
|
||||||
|
issues.push(i);
|
||||||
|
}
|
||||||
|
lp.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [...lp, ...issues];
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export async function validateTextDocument(textDocument: TextDocument): Promise<void> {
|
||||||
|
// In this simple example we get the settings for every validate run.
|
||||||
|
let settings = await getDocumentSettings(textDocument.uri);
|
||||||
|
// The validator creates diagnostics for all uppercase words length 2 and more
|
||||||
|
const text = textDocument.getText();
|
||||||
|
|
||||||
|
let problems = 0;
|
||||||
|
let diagnostics: Diagnostic[] = [];
|
||||||
|
const issues = validateBrackets(text);
|
||||||
|
|
||||||
|
diagnostics.push(
|
||||||
|
...issues.map<Diagnostic>(e => ({
|
||||||
|
message: 'Brackets unmatched',
|
||||||
|
range: {
|
||||||
|
start: textDocument.positionAt(e),
|
||||||
|
end: textDocument.positionAt(e + 1),
|
||||||
|
},
|
||||||
|
severity: DiagnosticSeverity.Error,
|
||||||
|
code: '[ and ]',
|
||||||
|
}))
|
||||||
|
);
|
||||||
|
|
||||||
|
// Send the computed diagnostics to VSCode.
|
||||||
|
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
|
||||||
|
}
|
||||||
|
|
@@ -5,7 +5,8 @@
|
|||||||
"lib": ["ES2019"],
|
"lib": ["ES2019"],
|
||||||
"outDir": "out",
|
"outDir": "out",
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"sourceMap": true
|
"sourceMap": true,
|
||||||
|
"strict": true
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src"
|
"src"
|
||||||
|
Reference in New Issue
Block a user