diff --git a/lib/adapters/code-action-adapter.ts b/lib/adapters/code-action-adapter.ts index bfbc745b..2e6c2bc0 100644 --- a/lib/adapters/code-action-adapter.ts +++ b/lib/adapters/code-action-adapter.ts @@ -1,5 +1,8 @@ import type * as atomIde from "atom-ide-base" +import * as linter from "atom/linter" import LinterPushV2Adapter from "./linter-push-v2-adapter" +/* eslint-disable import/no-deprecated */ +import IdeDiagnosticAdapter from "./diagnostic-adapter" import assert = require("assert") import Convert from "../convert" import ApplyEditAdapter from "./apply-edit-adapter" @@ -7,12 +10,12 @@ import { CodeAction, CodeActionParams, Command, + Diagnostic, LanguageClientConnection, ServerCapabilities, WorkspaceEdit, } from "../languageclient" import { Range, TextEditor } from "atom" -import CommandExecutionAdapter from "./command-execution-adapter" export default class CodeActionAdapter { /** @returns A {Boolean} indicating this adapter can adapt the server based on the given serverCapabilities. */ @@ -28,24 +31,24 @@ export default class CodeActionAdapter { * @param serverCapabilities The {ServerCapabilities} of the language server that will be used. * @param editor The Atom {TextEditor} containing the diagnostics. * @param range The Atom {Range} to fetch code actions for. - * @param diagnostics An {Array} to fetch code actions for. This is typically a list of - * diagnostics intersecting `range`. + * @param linterMessages An {Array} to fetch code actions for. This is typically a list of messages + * intersecting `range`. * @returns A {Promise} of an {Array} of {atomIde$CodeAction}s to display. */ public static async getCodeActions( connection: LanguageClientConnection, serverCapabilities: ServerCapabilities, - linterAdapter: LinterPushV2Adapter | undefined, + linterAdapter: LinterPushV2Adapter | IdeDiagnosticAdapter | undefined, editor: TextEditor, range: Range, - diagnostics: atomIde.Diagnostic[] + linterMessages: linter.Message[] | atomIde.Diagnostic[] ): Promise { if (linterAdapter == null) { return [] } assert(serverCapabilities.codeActionProvider, "Must have the textDocument/codeAction capability") - const params = CodeActionAdapter.createCodeActionParams(linterAdapter, editor, range, diagnostics) + const params = createCodeActionParams(linterAdapter, editor, range, linterMessages) const actions = await connection.codeAction(params) if (actions === null) { return [] @@ -81,34 +84,44 @@ export default class CodeActionAdapter { private static async executeCommand(command: any, connection: LanguageClientConnection): Promise { if (Command.is(command)) { - await CommandExecutionAdapter.executeCommand(connection, command.command, command.arguments) + await connection.executeCommand({ + command: command.command, + arguments: command.arguments, + }) } } +} - private static createCodeActionParams( - linterAdapter: LinterPushV2Adapter, - editor: TextEditor, - range: Range, - diagnostics: atomIde.Diagnostic[] - ): CodeActionParams { - return { - textDocument: Convert.editorToTextDocumentIdentifier(editor), - range: Convert.atomRangeToLSRange(range), - context: { - diagnostics: diagnostics.map((diagnostic) => { - // Retrieve the stored diagnostic code if it exists. - // Until the Linter API provides a place to store the code, - // there's no real way for the code actions API to give it back to us. - const converted = Convert.atomIdeDiagnosticToLSDiagnostic(diagnostic) - if (diagnostic.range != null && diagnostic.text != null) { - const code = linterAdapter.getDiagnosticCode(editor, diagnostic.range, diagnostic.text) - if (code != null) { - converted.code = code - } - } - return converted - }), - }, - } +function createCodeActionParams( + linterAdapter: LinterPushV2Adapter | IdeDiagnosticAdapter, + editor: TextEditor, + range: Range, + linterMessages: linter.Message[] | atomIde.Diagnostic[] +): CodeActionParams { + let diagnostics: Diagnostic[] + if (linterMessages.length === 0) { + diagnostics = [] + } else { + // TODO compile time dispatch using function names + diagnostics = areLinterMessages(linterMessages) + ? linterAdapter.getLSDiagnosticsForMessages(linterMessages as linter.Message[]) + : (linterAdapter as IdeDiagnosticAdapter).getLSDiagnosticsForIdeDiagnostics( + linterMessages as atomIde.Diagnostic[], + editor + ) + } + return { + textDocument: Convert.editorToTextDocumentIdentifier(editor), + range: Convert.atomRangeToLSRange(range), + context: { + diagnostics, + }, + } +} + +function areLinterMessages(linterMessages: linter.Message[] | atomIde.Diagnostic[]): boolean { + if ("excerpt" in linterMessages[0]) { + return true } + return false } diff --git a/lib/adapters/diagnostic-adapter.ts b/lib/adapters/diagnostic-adapter.ts new file mode 100644 index 00000000..49ed85c6 --- /dev/null +++ b/lib/adapters/diagnostic-adapter.ts @@ -0,0 +1,117 @@ +import * as atomIde from "atom-ide-base" +import * as atom from "atom" +import * as ls from "../languageclient" +import Convert from "../convert" +import LinterPushV2Adapter from "./linter-push-v2-adapter" + +/** @deprecated Use Linter V2 service */ +export type DiagnosticCode = number | string + +/** @deprecated Use Linter V2 service */ +export default class IdeDiagnosticAdapter extends LinterPushV2Adapter { + private _diagnosticCodes: Map> = new Map() + + /** + * Public: Capture the diagnostics sent from a langguage server, convert them to the Linter V2 format and forward them + * on to any attached {V2IndieDelegate}s. + * + * @deprecated Use Linter V2 service + * @param params The {PublishDiagnosticsParams} received from the language server that should be captured and + * forwarded on to any attached {V2IndieDelegate}s. + */ + public captureDiagnostics(params: ls.PublishDiagnosticsParams): void { + const path = Convert.uriToPath(params.uri) + const codeMap = new Map() + const messages = params.diagnostics.map((d) => { + const linterMessage = this.diagnosticToV2Message(path, d) + codeMap.set(getCodeKey(linterMessage.location.position, d.message), d.code) + return linterMessage + }) + this._diagnosticMap.set(path, messages) + this._diagnosticCodes.set(path, codeMap) + this._indies.forEach((i) => i.setMessages(path, messages)) + } + + /** + * Public: get diagnostics for the given linter messages + * + * @deprecated Use Linter V2 service + * @param linterMessages An array of linter {V2Message} + * @param editor + * @returns An array of LS {Diagnostic[]} + */ + public getLSDiagnosticsForIdeDiagnostics( + diagnostics: atomIde.Diagnostic[], + editor: atom.TextEditor + ): ls.Diagnostic[] { + return diagnostics.map((diagnostic) => this.getLSDiagnosticForIdeDiagnostic(diagnostic, editor)) + } + + /** + * Public: Get the {Diagnostic} that is associated with the given {atomIde.Diagnostic}. + * + * @deprecated Use Linter V2 service + * @param diagnostic The {atomIde.Diagnostic} object to fetch the {Diagnostic} for. + * @param editor + * @returns The associated {Diagnostic}. + */ + public getLSDiagnosticForIdeDiagnostic(diagnostic: atomIde.Diagnostic, editor: atom.TextEditor): ls.Diagnostic { + // Retrieve the stored diagnostic code if it exists. + // Until the Linter API provides a place to store the code, + // there's no real way for the code actions API to give it back to us. + const converted = atomIdeDiagnosticToLSDiagnostic(diagnostic) + if (diagnostic.range != null && diagnostic.text != null) { + const code = this.getDiagnosticCode(editor, diagnostic.range, diagnostic.text) + if (code != null) { + converted.code = code + } + } + return converted + } + + /** + * Private: Get the recorded diagnostic code for a range/message. Diagnostic codes are tricky because there's no + * suitable place in the Linter API for them. For now, we'll record the original code for each range/message + * combination and retrieve it when needed (e.g. for passing back into code actions) + */ + private getDiagnosticCode(editor: atom.TextEditor, range: atom.Range, text: string): DiagnosticCode | null { + const path = editor.getPath() + if (path != null) { + const diagnosticCodes = this._diagnosticCodes.get(path) + if (diagnosticCodes != null) { + return diagnosticCodes.get(getCodeKey(range, text)) || null + } + } + return null + } +} + +/** @deprecated Use Linter V2 service */ +export function atomIdeDiagnosticToLSDiagnostic(diagnostic: atomIde.Diagnostic): ls.Diagnostic { + // TODO: support diagnostic codes and codeDescriptions + // TODO!: support data + return { + range: Convert.atomRangeToLSRange(diagnostic.range), + severity: diagnosticTypeToLSSeverity(diagnostic.type), + source: diagnostic.providerName, + message: diagnostic.text || "", + } +} + +/** @deprecated Use Linter V2 service */ +export function diagnosticTypeToLSSeverity(type: atomIde.DiagnosticType): ls.DiagnosticSeverity { + switch (type) { + case "Error": + return ls.DiagnosticSeverity.Error + case "Warning": + return ls.DiagnosticSeverity.Warning + case "Info": + return ls.DiagnosticSeverity.Information + default: + throw Error(`Unexpected diagnostic type ${type}`) + } +} + +function getCodeKey(range: atom.Range, text: string): string { + return ([] as any[]).concat(...range.serialize(), text).join(",") +} diff --git a/lib/adapters/linter-push-v2-adapter.ts b/lib/adapters/linter-push-v2-adapter.ts index bdae8217..9d99f70b 100644 --- a/lib/adapters/linter-push-v2-adapter.ts +++ b/lib/adapters/linter-push-v2-adapter.ts @@ -1,22 +1,30 @@ import * as linter from "atom/linter" -import * as atom from "atom" import Convert from "../convert" import { Diagnostic, - DiagnosticCode, DiagnosticSeverity, + DiagnosticRelatedInformation, LanguageClientConnection, PublishDiagnosticsParams, } from "../languageclient" /** * Public: Listen to diagnostics messages from the language server and publish them to the user by way of the Linter - * Push (Indie) v2 API supported by Atom IDE UI. + * Push (Indie) v2 API provided by the Base Linter package. */ export default class LinterPushV2Adapter { - private _diagnosticMap: Map = new Map() - private _diagnosticCodes: Map> = new Map() - private _indies: Set = new Set() + /* + * A map from file path calculated using the LS diagnostic uri to an array of linter messages {linter.Message[]} + */ + protected _diagnosticMap: Map = new Map() + /** + * A map from file path {linter.Message["location"]["file"]} to a Map of all Message keys to Diagnostics + * ${Map} It has to be stored separately because a {Message} object cannot hold all + * of the information that a {Diagnostic} provides, thus we store the original {Diagnostic} object. + */ + protected _lsDiagnosticMap: Map> = + new Map() + protected _indies: Set = new Set() /** * Public: Create a new {LinterPushV2Adapter} that will listen for diagnostics via the supplied {LanguageClientConnection}. @@ -60,14 +68,14 @@ export default class LinterPushV2Adapter { */ public captureDiagnostics(params: PublishDiagnosticsParams): void { const path = Convert.uriToPath(params.uri) - const codeMap = new Map() + const codeMap = new Map() const messages = params.diagnostics.map((d) => { - const linterMessage = this.diagnosticToV2Message(path, d) - codeMap.set(getCodeKey(linterMessage.location.position, d.message), d.code) + const linterMessage = lsDiagnosticToV2Message(path, d) + codeMap.set(getMessageKey(linterMessage), d) return linterMessage }) this._diagnosticMap.set(path, messages) - this._diagnosticCodes.set(path, codeMap) + this._lsDiagnosticMap.set(path, codeMap) this._indies.forEach((i) => i.setMessages(path, messages)) } @@ -91,6 +99,31 @@ export default class LinterPushV2Adapter { } } + /** + * Public: get diagnostics for the given linter messages + * + * @param linterMessages An array of linter {V2Message} + * @returns An array of LS {Diagnostic[]} + */ + public getLSDiagnosticsForMessages(linterMessages: linter.Message[]): Diagnostic[] { + return ( + linterMessages + .map(this.getLSDiagnosticForMessage) + // filter out undefined + .filter((diagnostic) => diagnostic !== undefined) as Diagnostic[] + ) + } + + /** + * Public: Get the {Diagnostic} that is associated with the given Base Linter v2 {Message}. + * + * @param message The {Message} object to fetch the {Diagnostic} for. + * @returns The associated {Diagnostic}. + */ + public getLSDiagnosticForMessage(message: linter.Message): Diagnostic | undefined { + return this._lsDiagnosticMap.get(message.location.file)?.get(getMessageKey(message)) + } + /** * Public: Convert a diagnostic severity number obtained from the language server into the textual equivalent for a * Linter {V2Message}. @@ -110,24 +143,128 @@ export default class LinterPushV2Adapter { return "info" } } +} - /** - * Private: Get the recorded diagnostic code for a range/message. Diagnostic codes are tricky because there's no - * suitable place in the Linter API for them. For now, we'll record the original code for each range/message - * combination and retrieve it when needed (e.g. for passing back into code actions) - */ - public getDiagnosticCode(editor: atom.TextEditor, range: atom.Range, text: string): DiagnosticCode | null { - const path = editor.getPath() - if (path != null) { - const diagnosticCodes = this._diagnosticCodes.get(path) - if (diagnosticCodes != null) { - return diagnosticCodes.get(getCodeKey(range, text)) || null - } - } - return null +/** + * Public: Convert a single {Diagnostic} received from a language server into a single {Message} expected by the Linter V2 API. + * + * @param path A string representing the path of the file the diagnostic belongs to. + * @param diagnostics A {Diagnostic} object received from the language server. + * @returns A {Message} equivalent to the {Diagnostic} object supplied by the language server. + */ +function lsDiagnosticToV2Message(path: string, diagnostic: Diagnostic): linter.Message { + return { + location: { + file: path, + position: Convert.lsRangeToAtomRange(diagnostic.range), + }, + reference: relatedInformationToReference(diagnostic.relatedInformation), + url: diagnostic.codeDescription?.href, + icon: iconForLSSeverity(diagnostic.severity ?? DiagnosticSeverity.Error), + excerpt: diagnostic.message, + linterName: diagnostic.source, + severity: lsSeverityToV2MessageSeverity(diagnostic.severity ?? DiagnosticSeverity.Error), + // BLOCKED: on steelbrain/linter#1722 + solutions: undefined, } } -function getCodeKey(range: atom.Range, text: string): string { - return ([] as any[]).concat(...range.serialize(), text).join(",") +/** + * Convert a severity level of an LSP {Diagnostic} to that of a Base Linter v2 {Message}. Note: this conversion is lossy + * due to the v2 Message not being able to represent hints. + * + * @param severity A severity level of of an LSP {Diagnostic} to be converted. + * @returns A severity level a Base Linter v2 {Message}. + */ +function lsSeverityToV2MessageSeverity(severity: DiagnosticSeverity): linter.Message["severity"] { + switch (severity) { + case DiagnosticSeverity.Error: + return "error" + case DiagnosticSeverity.Warning: + return "warning" + case DiagnosticSeverity.Information: + case DiagnosticSeverity.Hint: + return "info" + default: + throw Error(`Unexpected diagnostic severity '${severity}'`) + } +} + +/** + * Convert a diagnostic severity number obtained from the language server into an Octicon icon. + * + * @param severity A number representing the severity of the diagnostic. + * @returns An Octicon name. + */ +function iconForLSSeverity(severity: DiagnosticSeverity): string | undefined { + switch (severity) { + case DiagnosticSeverity.Error: + return "stop" + case DiagnosticSeverity.Warning: + return "warning" + case DiagnosticSeverity.Information: + return "info" + case DiagnosticSeverity.Hint: + return "light-bulb" + default: + return undefined + } +} + +/** + * Convert the related information from a diagnostic into a reference point for a Linter {V2Message}. + * + * @param relatedInfo Several related information objects (only the first is used). + * @returns A value that is suitable for using as {V2Message}.reference. + */ +function relatedInformationToReference( + relatedInfo: DiagnosticRelatedInformation[] | undefined +): linter.Message["reference"] { + if (relatedInfo === undefined || relatedInfo.length === 0) { + return undefined + } + + const location = relatedInfo[0].location + return { + file: Convert.uriToPath(location.uri), + position: Convert.lsRangeToAtomRange(location.range).start, + } +} + +/** + * Get a unique key for a Linter v2 Message + * + * @param message A {Message} object + * @returns ${string} a unique key + */ +function getMessageKey(message: linter.Message): string { + if (typeof message.key !== "string") { + updateMessageKey(message) + } + return message.key as string // updateMessageKey adds message.key string +} + +/** + * Construct an unique key for a Linter v2 Message and store it in `Message.key` + * + * @param message A {Message} object to serialize. + * @returns ${string} a unique key + */ +function updateMessageKey(message: linter.Message): void { + // From https://github.com/steelbrain/linter/blob/fadd462914ef0a8ed5b73a489f662a9393bdbe9f/lib/helpers.ts#L50-L64 + const { reference, location } = message + const nameStr = `$LINTER:${message.linterName}` + const locationStr = `$LOCATION:${location.file}$${location.position.start.row}$${location.position.start.column}$${location.position.end.row}$${location.position.end.column}` + const referenceStr = reference + ? `$REFERENCE:${reference.file}$${ + reference.position ? `${reference.position.row}$${reference.position.column}` : "" + }` + : "$REFERENCE:null" + const excerptStr = `$EXCERPT:${message.excerpt}` + const severityStr = `$SEVERITY:${message.severity}` + const iconStr = message.icon ? `$ICON:${message.icon}` : "$ICON:null" + const urlStr = message.url ? `$URL:${message.url}` : "$URL:null" + const descriptionStr = + typeof message.description === "string" ? `$DESCRIPTION:${message.description}` : "$DESCRIPTION:null" + message.key = `${nameStr}${locationStr}${referenceStr}${excerptStr}${severityStr}${iconStr}${urlStr}${descriptionStr}` } diff --git a/lib/convert.ts b/lib/convert.ts index ab500792..0b62d7a5 100644 --- a/lib/convert.ts +++ b/lib/convert.ts @@ -2,6 +2,9 @@ import type * as atomIde from "atom-ide-base" import * as ls from "./languageclient" import { Point, FilesystemChange, Range, TextEditor } from "atom" +// eslint-disable-next-line import/no-deprecated +import { diagnosticTypeToLSSeverity, atomIdeDiagnosticToLSDiagnostic } from "./adapters/diagnostic-adapter" + /** * Public: Class that contains a number of helper methods for general conversions between the language server protocol * and Atom/Atom packages. @@ -177,28 +180,16 @@ export default class Convert { } } + /** @deprecated Use Linter V2 service */ public static atomIdeDiagnosticToLSDiagnostic(diagnostic: atomIde.Diagnostic): ls.Diagnostic { - // TODO: support diagnostic codes and codeDescriptions - // TODO!: support data - return { - range: Convert.atomRangeToLSRange(diagnostic.range), - severity: Convert.diagnosticTypeToLSSeverity(diagnostic.type), - source: diagnostic.providerName, - message: diagnostic.text || "", - } + // eslint-disable-next-line import/no-deprecated + return atomIdeDiagnosticToLSDiagnostic(diagnostic) } + /** @deprecated Use Linter V2 service */ public static diagnosticTypeToLSSeverity(type: atomIde.DiagnosticType): ls.DiagnosticSeverity { - switch (type) { - case "Error": - return ls.DiagnosticSeverity.Error - case "Warning": - return ls.DiagnosticSeverity.Warning - case "Info": - return ls.DiagnosticSeverity.Information - default: - throw Error(`Unexpected diagnostic type ${type}`) - } + // eslint-disable-next-line import/no-deprecated + return diagnosticTypeToLSSeverity(type) } /** diff --git a/lib/languageclient.ts b/lib/languageclient.ts index d83b83e3..18855208 100644 --- a/lib/languageclient.ts +++ b/lib/languageclient.ts @@ -419,12 +419,23 @@ export class LanguageClientConnection extends EventEmitter { * Public: Send a `textDocument/codeAction` request. * * @param params The {CodeActionParams} identifying the document, range and context for the code action. - * @returns A {Promise} containing an {Array} of {Commands}s that can be performed against the given documents range. + * @returns A {Promise} containing an {Array} of {Command}s or {CodeAction}s that can be performed against the given + * documents range. */ public codeAction(params: lsp.CodeActionParams): Promise | null> { return this._sendRequest(lsp.CodeActionRequest.type, params) } + /** + * Public: Send a `codeAction/resolve` request. + * + * @param params The {CodeAction} whose properties (e.g. `edit`) are to be resolved. + * @returns A resolved {CodeAction} that can be applied immediately. + */ + public codeActionResolve(params: lsp.CodeAction): Promise { + return this._sendRequest(lsp.CodeActionResolveRequest.type, params) + } + /** * Public: Send a `textDocument/codeLens` request. * @@ -586,8 +597,6 @@ export class LanguageClientConnection extends EventEmitter { } } -export type DiagnosticCode = number | string - /** Contains additional information about the context in which a completion request is triggered. */ export interface CompletionContext { /** How the completion was triggered. */ diff --git a/test/adapters/code-action-adapter.test.ts b/test/adapters/code-action-adapter.test.ts index edddd0de..b7e959ce 100644 --- a/test/adapters/code-action-adapter.test.ts +++ b/test/adapters/code-action-adapter.test.ts @@ -1,7 +1,8 @@ import { Range } from "atom" import * as ls from "../../lib/languageclient" import CodeActionAdapter from "../../lib/adapters/code-action-adapter" -import LinterPushV2Adapter from "../../lib/adapters/linter-push-v2-adapter" +/* eslint-disable import/no-deprecated */ +import IdeDiagnosticAdapter from "../../lib/adapters/diagnostic-adapter" import { createSpyConnection, createFakeEditor } from "../helpers.js" describe("CodeActionAdapter", () => { @@ -20,7 +21,7 @@ describe("CodeActionAdapter", () => { }) describe("getCodeActions", () => { - it("fetches code actions from the connection", async () => { + it("fetches code actions from the connection when IdeDiagnosticAdapter is used", async () => { const connection = createSpyConnection() const languageClient = new ls.LanguageClientConnection(connection) const testCommand: ls.Command = { @@ -31,7 +32,9 @@ describe("CodeActionAdapter", () => { spyOn(languageClient, "codeAction").and.returnValue(Promise.resolve([testCommand])) spyOn(languageClient, "executeCommand").and.callThrough() - const linterAdapter = new LinterPushV2Adapter(languageClient) + const linterAdapter = new IdeDiagnosticAdapter(languageClient) + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore private method spyOn(linterAdapter, "getDiagnosticCode").and.returnValue("test code") const testPath = "/test.txt" diff --git a/test/adapters/diagnostics-adapter-test.ts b/test/adapters/diagnostics-adapter-test.ts new file mode 100644 index 00000000..90335492 --- /dev/null +++ b/test/adapters/diagnostics-adapter-test.ts @@ -0,0 +1,41 @@ +/* eslint-disable import/no-deprecated */ +import { Range } from "atom" +import * as path from "path" +import IdeDiagnosticAdapter from "../../lib/adapters/diagnostic-adapter" +import Convert from "../../lib/convert" +import * as ls from "../../lib/languageclient" +import { createFakeEditor, createSpyConnection } from "../helpers.js" + +describe("IdeDiagnosticAdapter", () => { + describe("captureDiagnostics", () => { + it("stores diagnostic codes and allows their retrival", () => { + const languageClient = new ls.LanguageClientConnection(createSpyConnection()) + const adapter = new IdeDiagnosticAdapter(languageClient) + const testPath = path.join(__dirname, "test.txt") + adapter.captureDiagnostics({ + uri: Convert.pathToUri(testPath), + diagnostics: [ + { + message: "Test message", + range: { + start: { line: 1, character: 2 }, + end: { line: 3, character: 4 }, + }, + source: "source", + code: "test code", + severity: ls.DiagnosticSeverity.Information, + }, + ], + }) + + const mockEditor = createFakeEditor(testPath) + + // using private method + // eslint-disable-next-line dot-notation + const getDiagnosticCode = adapter["getDiagnosticCode"] + expect(getDiagnosticCode(mockEditor, new Range([1, 2], [3, 4]), "Test message")).toEqual("test code") + expect(getDiagnosticCode(mockEditor, new Range([1, 2], [3, 4]), "Test message2")).toBeUndefined() + expect(getDiagnosticCode(mockEditor, new Range([1, 2], [3, 5]), "Test message")).toBeUndefined() + }) + }) +}) diff --git a/test/adapters/linter-push-v2-adapter.test.ts b/test/adapters/linter-push-v2-adapter.test.ts index ab4695ed..991a758d 100644 --- a/test/adapters/linter-push-v2-adapter.test.ts +++ b/test/adapters/linter-push-v2-adapter.test.ts @@ -1,9 +1,7 @@ import LinterPushV2Adapter from "../../lib/adapters/linter-push-v2-adapter" -import Convert from "../../lib/convert" import * as ls from "../../lib/languageclient" -import * as path from "path" import { Point, Range } from "atom" -import { createSpyConnection, createFakeEditor } from "../helpers.js" +import { createSpyConnection } from "../helpers.js" describe("LinterPushV2Adapter", () => { describe("constructor", () => { @@ -62,32 +60,4 @@ describe("LinterPushV2Adapter", () => { expect(severity).toBe("info") }) }) - - describe("captureDiagnostics", () => { - it("stores diagnostic codes and allows their retrival", () => { - const languageClient = new ls.LanguageClientConnection(createSpyConnection()) - const adapter = new LinterPushV2Adapter(languageClient) - const testPath = path.join(__dirname, "test.txt") - adapter.captureDiagnostics({ - uri: Convert.pathToUri(testPath), - diagnostics: [ - { - message: "Test message", - range: { - start: { line: 1, character: 2 }, - end: { line: 3, character: 4 }, - }, - source: "source", - code: "test code", - severity: ls.DiagnosticSeverity.Information, - }, - ], - }) - - const mockEditor = createFakeEditor(testPath) - expect(adapter.getDiagnosticCode(mockEditor, new Range([1, 2], [3, 4]), "Test message")).toBe("test code") - expect(adapter.getDiagnosticCode(mockEditor, new Range([1, 2], [3, 4]), "Test message2")).toBe(null) - expect(adapter.getDiagnosticCode(mockEditor, new Range([1, 2], [3, 5]), "Test message")).toBe(null) - }) - }) }) diff --git a/typings/atom/index.d.ts b/typings/atom/index.d.ts index 3462220d..6635b82d 100644 --- a/typings/atom/index.d.ts +++ b/typings/atom/index.d.ts @@ -27,3 +27,9 @@ declare module "atom/src/config" { get(key: T): string } } + +declare module "atom/linter" { + interface Message { + key?: string + } +}