Skip to content
This repository was archived by the owner on Jun 8, 2023. It is now read-only.

Commit

Permalink
refactor language server
Browse files Browse the repository at this point in the history
  • Loading branch information
saw-jan committed Mar 17, 2022
1 parent c295db3 commit db1d77e
Show file tree
Hide file tree
Showing 5 changed files with 73 additions and 72 deletions.
11 changes: 0 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,17 +57,6 @@
"type": "number",
"default": 1000,
"description": "Controls the maximum number of problems produced by the server."
},
"gherlint.trace.server": {
"scope": "window",
"type": "string",
"enum": [
"off",
"message",
"verbose"
],
"default": "off",
"description": "Traces the communication between VS Code and the language server."
}
}
}
Expand Down
5 changes: 5 additions & 0 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"author": "Sawjan G.",
"license": "MIT",
"dependencies": {
"vscode-languageserver": "^7.0.0"
"vscode-languageserver": "^7.0.0",
"vscode-languageserver-textdocument": "^1.0.4"
}
}
48 changes: 39 additions & 9 deletions server/src/linter.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,41 @@
const { DiagnosticSeverity } = require('vscode-languageserver/node');

module.exports.validateSteps = async function (document) {
const keywordsRegex =
/(Feature:|Rule:|Background:|Scenario( (Outline|Template))?:|Given|When|Then|And|But|Example(s)?:|Scenarios:)/;

const stepKeywords = ['Given', 'When', 'Then', 'And', 'But'];
const exampleKeywords = ['Background', 'Scenario', 'Example', 'Scenario Outline', 'Scenario Template'];

module.exports.validateDocument = function (document, docConfig) {
const diagnostics = [];
diagnostics.push(...checkBeginningStep(document, diagnostics));
diagnostics.push(...checkRepeatedSteps(document, diagnostics));
return diagnostics;
};

function checkBeginningStep(document, diagnostics) {
let text = document.getText();
const regex = /(Background:|Scenario( (Outline|Template))?:|Example:)/;
let noOfIssue = 0;
while ((match = regex.exec(text)) && noOfIssue < docConfig.maxNumberOfProblems) {
noOfIssue++;
diagnostics.push({
severity: DiagnosticSeverity.Warning,
range: {
start: document.positionAt(match.index),
end: document.positionAt(match.index + match[0].length),
},
message: `Starting step must be "Given" or "When" step`,
});
}
return diagnostics;
}

function checkRepeatedSteps(document, diagnostics) {
let text = document.getText();
const lines = text.split('\n');
const regex = /[GWTAB]{1}[ivenhdut]{2,4}\s/;
const regex = /[GWTAB]{1}[ivenhdut]{2,4}/;

let diagnostics = [];
let prevStep = '';
let index = 0;
lines.forEach((line) => {
Expand All @@ -18,7 +48,7 @@ module.exports.validateSteps = async function (document) {
}
const match = regex.exec(line);
if (Boolean(match)) {
const matchStep = match[0].trim();
const matchStep = match[0].trim().trim(':');
if (prevStep === matchStep) {
const rangeEnd = index + match.index + matchStep.length;
let diagnostic = {
Expand All @@ -27,18 +57,18 @@ module.exports.validateSteps = async function (document) {
start: document.positionAt(index + match.index),
end: document.positionAt(rangeEnd),
},
message: `Replace '${matchStep}' with 'And'`,
message: `Replace '${matchStep}' with 'And' or 'But'`,
};
diagnostics.push(diagnostic);
}
if (matchStep !== 'And') {
prevStep = matchStep;
}
} else {
prevStep = '';
// send invalid step error
if (!stepKeywords.includes(matchStep)) {
prevStep = '';
}
}
index += lineLength;
});
return diagnostics;
};
}
78 changes: 27 additions & 51 deletions server/src/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ const {
DidChangeConfigurationNotification,
TextDocumentSyncKind,
} = require('vscode-languageserver/node');
const { TextDocument } = require('vscode-languageserver-textdocument');

const { validateSteps } = require('./linter');
const { formatDocument } = require('./formatter');
const { validateDocument } = require('./linter');

// Create a connection for the server, using Node's IPC as a transport.
// Also include all preview / proposed LSP features.
const connection = createConnection(ProposedFeatures.all);
// Text document manager
const documents = new TextDocuments();
const documents = new TextDocuments(TextDocument);

let hasConfigurationCapability = false;
let hasWorkspaceFolderCapability = false;
Expand All @@ -29,11 +29,6 @@ connection.onInitialize((params) => {
const result = {
capabilities: {
textDocumentSync: TextDocumentSyncKind.Full,
completionProvider: {
resolveProvider: true,
},
documentFormattingProvider: true,
documentRangeFormattingProvider: true,
},
};

Expand Down Expand Up @@ -73,27 +68,15 @@ connection.onDidChangeConfiguration((change) => {
// Reset all cached document settings
documentSettings.clear();
} else {
globalSettings = change.settings.languageServerExample || defaultSettings;
globalSettings = change.settings.gherlint || defaultSettings;
}
// Revalidate all open text documents
documents.all().forEach(validateSteps);
// Revalidate all open documents
documents.all().forEach((document) => {
const docConfig = getDocumentConfig(document);
validateDocument(document, docConfig);
});
});

function getDocumentSettings(resource) {
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((_event) => {
documentSettings.delete(_event.document.uri);
Expand All @@ -102,39 +85,32 @@ documents.onDidClose((_event) => {
// 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(async ({ document }) => {
// check for errors
const diagnostics = await validateSteps(document);
const docConfig = getDocumentConfig(document);
// Revalidate the document
const diagnostics = await validateDocument(document, docConfig);

// Send the computed diagnostics to VS Code.
connection.sendDiagnostics({ uri: document.uri, diagnostics });
});

connection.onDidChangeWatchedFiles(() => {
connection.console.log('We received a file change event');
});

connection.onDocumentFormatting((params) => {
const result = formatDocument(documents, params);
return Promise.resolve(result);
});

// This handler provides the initial list of the completion items.
connection.onCompletion(() => {
// The pass parameter contains the position of the text document in
// which code complete got requested. For the example we ignore this
// info and always provide the same completion items.
return [];
});

// This handler resolves additional information for the item selected in
// the completion list.
connection.onCompletionResolve((item) => {
return item;
});

// 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();

function getDocumentConfig(document) {
if (!hasConfigurationCapability) {
return Promise.resolve(globalSettings);
}
let result = documentSettings.get(document);
if (!result) {
result = connection.workspace.getConfiguration({
scopeUri: document,
section: 'gherlint',
});
documentSettings.set(document, result);
}
return result;
}

0 comments on commit db1d77e

Please sign in to comment.