[add] indirection

This commit is contained in:
2025-07-19 11:17:53 +01:00
parent e8ce9b73ae
commit 10850c9d98
7 changed files with 897 additions and 667 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
client/.antlr
test.bf
out
dist

View File

@@ -4,6 +4,6 @@
"typescript.tsc.autoDetect": "off",
"typescript.preferences.quoteStyle": "single",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
"source.fixAll.eslint": "explicit"
}
}

1159
package-lock.json generated

File diff suppressed because it is too large Load Diff

108
server/src/connection.ts Normal file
View File

@@ -0,0 +1,108 @@
import { CompletionItem, CompletionItemKind, Connection, DidChangeConfigurationNotification, DidChangeConfigurationParams, InitializeParams, InitializeResult, TextDocumentPositionParams, TextDocumentSyncKind } from 'vscode-languageserver';
export class BranFlakesConnectionManager {
hasConfigurationCapability: boolean = false;
hasWorkspaceFolderCapability: boolean = false;
hasDiagnosticRelatedInformationCapability: boolean = false;
constructor(protected connection: Connection) {
connection.onInitialize(this.initConnection);
}
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.hasConfigurationCapability = !!(
capabilities.workspace && !!capabilities.workspace.configuration
);
this.hasWorkspaceFolderCapability = !!(
capabilities.workspace && !!capabilities.workspace.workspaceFolders
);
this.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.hasWorkspaceFolderCapability) {
result.capabilities.workspace = {
workspaceFolders: {
supported: true,
},
};
}
return result;
}
onInit() {
if (this.hasConfigurationCapability) {
// Register for all configuration changes.
this.connection.client.register(
DidChangeConfigurationNotification.type,
undefined
);
}
if (this.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();
}
}

View File

@@ -3,240 +3,87 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
* ------------------------------------------------------------------------------------------ */
import {
createConnection,
TextDocuments,
Diagnostic,
DiagnosticSeverity,
ProposedFeatures,
InitializeParams,
DidChangeConfigurationNotification,
CompletionItem,
CompletionItemKind,
TextDocumentPositionParams,
TextDocumentSyncKind,
InitializeResult,
createConnection,
TextDocuments,
ProposedFeatures,
} from 'vscode-languageserver';
import { TextDocument } from 'vscode-languageserver-textdocument';
import { BranFlakesSettings, defaultSettings } 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.
// 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
// supports full document sync only
let documents: TextDocuments<TextDocument> = new TextDocuments(TextDocument);
let hasConfigurationCapability: boolean = false;
let hasWorkspaceFolderCapability: boolean = false;
let hasDiagnosticRelatedInformationCapability: boolean = false;
connection.onInitialize((params: InitializeParams) => {
let capabilities = params.capabilities;
let cm = new BranFlakesConnectionManager(connection);
// 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;
let globalSettings: BranFlakesSettings = defaultSettings;
// Cache the settings of all open documents
let documentSettings: Map<string, Thenable<ExampleSettings>> = new Map();
let documentSettings: Map<string, Thenable<BranFlakesSettings>> = new Map();
connection.onDidChangeConfiguration(change => {
if (hasConfigurationCapability) {
// Reset all cached document settings
documentSettings.clear();
} else {
globalSettings = <ExampleSettings>(
if (hasConfigurationCapability) {
// Reset all cached document settings
documentSettings.clear();
} else {
globalSettings = <BranFlakesSettings>(
(change.settings.languageServerExample || defaultSettings)
);
}
}
// Revalidate all open text documents
Promise.all(documents.all().map(validateTextDocument)).catch(e => {
connection.console.log('Failed to validate text documents');
});
// Revalidate all open text documents
Promise.all(documents.all().map(validateTextDocument)).catch(e => {
connection.console.log('Failed to validate text documents');
});
});
function getDocumentSettings(resource: string): Thenable<ExampleSettings> {
if (!hasConfigurationCapability) {
return Promise.resolve(globalSettings);
}
let result = documentSettings.get(resource);
if (!result) {
result = connection.workspace.getConfiguration({
scopeUri: resource,
section: 'languageServerExample',
});
documentSettings.set(resource, result);
}
return result;
export function getDocumentSettings(resource: string): Thenable<BranFlakesSettings> {
if (!hasConfigurationCapability) {
return Promise.resolve(globalSettings);
}
let result = documentSettings.get(resource);
if (!result) {
result = connection.workspace.getConfiguration({
scopeUri: resource,
section: 'languageServerExample',
});
documentSettings.set(resource, result);
}
return result;
}
// Only keep settings for open documents
documents.onDidClose(e => {
documentSettings.delete(e.document.uri);
documentSettings.delete(e.document.uri);
});
// The content of a text document has changed. This event is emitted
// when the text document first opened or when its content has changed.
documents.onDidChangeContent(change => {
validateTextDocument(change.document);
validateTextDocument(change.document);
});
//Run when saved
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 ]',
}))
);
// Send the computed diagnostics to VSCode.
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}
// This handler provides the initial list of the completion items.
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;
});
}
);
cm.setOnCompletion();
// Make the text document manager listen on the connection
// for open, change and close text document events
documents.listen(connection);
// Listen on the connection
connection.listen();
cm.listen();

13
server/src/settings.ts Normal file
View File

@@ -0,0 +1,13 @@
// 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
};

52
server/src/validation.ts Normal file
View File

@@ -0,0 +1,52 @@
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 });
}