Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

linear-ls

Package Overview
Dependencies
Maintainers
2
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

linear-ls - npm Package Compare versions

Comparing version 0.0.2 to 1.0.0

dist/linear.js

190

dist/server.js

@@ -5,107 +5,129 @@ "use strict";

const vscode_languageserver_textdocument_1 = require("vscode-languageserver-textdocument");
const defaultSettings = { projectPrefixes: [] };
let globalSettings = defaultSettings;
const linear_1 = require("./linear");
const connection = (0, node_1.createConnection)(node_1.ProposedFeatures.all);
const documents = new node_1.TextDocuments(vscode_languageserver_textdocument_1.TextDocument);
const documentSettings = new Map();
let supportsConfiguration = false;
let supportsWorkspaceFolder = false;
connection.onInitialize((params) => {
const capabilities = params.capabilities;
const workspace = capabilities.workspace;
supportsConfiguration = !!workspace?.configuration;
supportsWorkspaceFolder = !!workspace?.workspaceFolders;
const teamKeys = new Set();
const issues = new Map();
const issuePositions = new Map();
// [MOB-201](https://linear.app/eucalyptus/issue/MOB-201/devices-screen-not-paired-state)
connection.onInitialize(async () => {
await (0, linear_1.getTeamKeys)().then((keys) => {
keys.map((k) => {
teamKeys.add(k);
});
});
const result = {
capabilities: {
textDocumentSync: node_1.TextDocumentSyncKind.Incremental,
completionProvider: {
resolveProvider: true,
},
codeActionProvider: {},
hoverProvider: {},
completionProvider: { resolveProvider: true },
},
};
if (supportsWorkspaceFolder) {
result.capabilities.workspace = {
workspaceFolders: {
supported: true,
},
};
}
return result;
});
connection.onInitialized(() => {
if (supportsConfiguration) {
connection.client.register(node_1.DidChangeConfigurationNotification.type, undefined);
connection.onCodeAction((params) => {
if (params.context.triggerKind === node_1.CodeActionTriggerKind.Invoked) {
const textDocument = documents.get(params.textDocument.uri);
if (!textDocument) {
return;
}
const text = textDocument.getText(params.range);
if (!text) {
return;
}
// TODO: Implement command to create ticket
return [{ title: "Create Ticket" }];
}
});
connection.onDidChangeConfiguration((change) => {
if (supportsConfiguration) {
documentSettings.clear();
documents.onDidChangeContent((change) => {
const text = change.document.getText();
const documentPositions = [];
Array.from(teamKeys)
.flatMap((prefix) => {
return Array.from(text.matchAll(new RegExp(`${prefix}-[0-9]*`, "g")));
})
.forEach((m) => {
const issueKey = m[0];
const positionStart = change.document.positionAt(m.index ?? 0);
const positionEnd = change.document.positionAt(issueKey.length + (m.index ?? 0));
// Write down that we've seen the issue, but don't
// fetch the definition just yet.
if (!issues.has(issueKey)) {
issues.set(issueKey, undefined);
}
documentPositions.push({
issueKey,
positionStart,
positionEnd,
offsetStart: change.document.offsetAt(positionStart),
offsetEnd: change.document.offsetAt(positionEnd),
});
});
issuePositions.set(change.document.uri, documentPositions);
});
connection.onHover(async (params) => {
const documentPositions = issuePositions.get(params.textDocument.uri);
if (!documentPositions) {
return;
}
else {
// TODO: Validate this and send notification if cooked.
globalSettings = (change.settings.lls || defaultSettings);
const textDocument = documents.get(params.textDocument.uri);
if (!textDocument) {
return;
}
documents.all().forEach(identifyTickets);
});
function getDocumentSettings(resource) {
if (!supportsConfiguration) {
return Promise.resolve(globalSettings);
const cursorOffset = textDocument.offsetAt(params.position);
const targetIssue = documentPositions.find((dp) => dp.offsetStart <= cursorOffset && dp.offsetEnd > cursorOffset);
if (!targetIssue) {
return;
}
let result = documentSettings.get(resource);
if (!result) {
result = connection.workspace.getConfiguration({
scopeUri: resource,
section: "lls",
});
documentSettings.set(resource, result);
const issue = await (0, linear_1.getIssueByKey)(targetIssue.issueKey);
if (!issue) {
return;
}
return result;
}
documents.onDidClose((e) => {
documentSettings.delete(e.document.uri);
return {
contents: issue.description ?? "Not available",
};
});
documents.onDidChangeContent((change) => {
identifyTickets(change.document);
});
async function identifyTickets(textDocument) {
const settings = await getDocumentSettings(textDocument.uri);
if (settings.projectPrefixes.length === 0) {
return;
connection.onCompletion(async (params) => {
if (teamKeys.size === 0) {
return [];
}
const text = textDocument.getText();
const diagnostics = [];
settings.projectPrefixes.forEach((prefix) => {
Array.from(text.matchAll(new RegExp(`${prefix}-[0-9]*`, "g"))).forEach((m) => {
diagnostics.push({
source: "Linear",
message: m[0],
severity: node_1.DiagnosticSeverity.Hint,
range: {
start: textDocument.positionAt(m.index ?? 0),
end: textDocument.positionAt((m.index ?? 0) + m[0].length),
},
});
});
const document = documents.get(params.textDocument.uri);
if (!document) {
return [];
}
const lineToPosition = document?.getText({
start: { line: params.position.line, character: 0 },
end: { line: params.position.line, character: params.position.character },
});
connection.sendDiagnostics({ uri: textDocument.uri, diagnostics });
}
connection.onCompletion(
// eslint-disable-next-line @typescript-eslint/no-unused-vars
(_textDocumentPosition) => {
return [
{
label: "MOB-1",
data: "MOB-1",
const lastMatch = Array.from(teamKeys)
.flatMap((prefix) => Array.from(lineToPosition.matchAll(new RegExp(`(${prefix})-(.*)`, "g"))))
.at(-1);
if (!lastMatch) {
return [];
}
const [, teamKey, searchString] = lastMatch;
if (!teamKey || !searchString) {
return [];
}
const issues = await (0, linear_1.findIssuesByTitle)([teamKey], searchString);
if (issues === undefined) {
return [];
}
return issues.map((issue) => {
let label = `${issue.identifier}: ${issue.title}`;
if (label.length > 60) {
label = label.substring(0, 60) + "...";
}
return {
data: issue,
detail: issue.description ?? "Not available",
insertText: `[${issue.identifier}](${issue.url})`,
filterText: `${issue.team.key}-${issue.title}`,
label,
kind: node_1.CompletionItemKind.Reference,
},
];
};
});
});
connection.onCompletionResolve((item) => {
if (item.data === "MOB-1") {
item.detail = "Install the application";
item.documentation = "Details details";
}
return item;
});
documents.listen(connection);
connection.listen();
{
"name": "linear-ls",
"version": "0.0.2",
"version": "1.0.0",
"description": "Linear Language Server",

@@ -15,2 +15,5 @@ "bin": "index.js",

"dependencies": {
"@urql/core": "^3.1.1",
"graphql": "^16.6.0",
"node-fetch": "^2.6.7",
"vscode-languageserver": "^8.0.2",

@@ -20,2 +23,6 @@ "vscode-languageserver-textdocument": "^1.0.8"

"devDependencies": {
"@graphql-codegen/cli": "^3.0.0",
"@graphql-codegen/typescript": "^3.0.0",
"@graphql-codegen/typescript-operations": "^3.0.0",
"@types/node-fetch": "^2.6.2",
"@typescript-eslint/eslint-plugin": "^5.50.0",

@@ -39,3 +46,2 @@ "@typescript-eslint/parser": "^5.50.0",

"@typescript-eslint/no-explicit-any": "error",
"@typescript-eslint/explicit-module-boundary-types": "error",
"@typescript-eslint/no-non-null-assertion": "error"

@@ -42,0 +48,0 @@ }

# lls
Linear Language Server
## Installation
```
npm i -g linear-ls
```
## Features
### Hover on Issues
Hovering on ticket identifier shows the ticket description.
<img width="800" alt="image" src="https://user-images.githubusercontent.com/609452/218294991-ef0dfe07-832d-418b-9e7e-8b630f4c2c49.png">
### Issue Completion
Typing team key, hyphen and search term (e.g. `EUC-thing`) triggers issue search. Selecting a result puts in a link to your issue.
<img width="800" alt="image" src="https://user-images.githubusercontent.com/609452/218295062-4a0bbd6c-bb92-44c6-9301-4ab7b1522978.png">
### Create Ticket from Text Selection
TODO
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc