@shopify/theme-language-server-common
Version:
<h1 align="center" style="position: relative;" > <br> <img src="https://github.com/Shopify/theme-check-vscode/blob/main/images/shopify_glyph.png?raw=true" alt="logo" width="141" height="160"> <br> Theme Language Server </h1>
76 lines (67 loc) • 2.49 kB
text/typescript
import { Offense, SourceCodeType, WithRequired } from '@shopify/theme-check-common';
import {
CodeAction,
CodeActionKind,
CodeActionParams,
Command,
Diagnostic,
} from 'vscode-languageserver';
import { applySuggestionCommand } from '../../commands/providers';
import { Anomaly } from '../../diagnostics';
import { BaseCodeActionsProvider } from '../BaseCodeActionsProvider';
import { isInRange, toCodeAction } from './utils';
export class SuggestionProvider extends BaseCodeActionsProvider {
static kind = CodeActionKind.QuickFix;
codeActions(params: CodeActionParams): (Command | CodeAction)[] {
const { uri } = params.textDocument;
const document = this.documentManager.get(uri);
const diagnostics = this.diagnosticsManager.get(uri);
if (!document || !diagnostics) return [];
const { textDocument } = document;
const { anomalies, version } = diagnostics;
const start = textDocument.offsetAt(params.range.start);
const end = textDocument.offsetAt(params.range.end);
const suggestibleAnomalies = anomalies.filter(isSuggestible);
const anomaliesUnderCursor = suggestibleAnomalies.filter((anomaly) =>
isInRange(anomaly, start, end),
);
if (anomaliesUnderCursor.length === 0) return [];
return quickfixCursorActions(uri, version, anomaliesUnderCursor);
}
}
/**
* @returns all Offense.suggest code actions for the offenses under the cursor
* @example Suggestion: Add the `defer` HTML attribute
*/
function quickfixCursorActions(
uri: string,
version: number | undefined,
anomaliesUnderCursor: SuggestibleAnomaly[],
): CodeAction[] {
return anomaliesUnderCursor.flatMap(({ offense, diagnostic, id }) => {
return offense.suggest.map((suggestion, suggestionId) =>
toCodeAction(
`Suggestion: ${suggestion.message}`,
applySuggestionCommand(uri, version, id, suggestionId),
[diagnostic],
SuggestionProvider.kind,
),
);
});
}
/**
* An anomaly is suggestible if the offense has the `suggest` attribute
*
* This type guarantees that Offense.suggest is defined
*/
type SuggestibleAnomaly<S extends SourceCodeType = SourceCodeType> = S extends SourceCodeType
? {
diagnostic: Diagnostic;
offense: WithRequired<Offense<S>, 'suggest'>;
id: number;
}
: never;
function isSuggestible(anomaly: Anomaly): anomaly is SuggestibleAnomaly {
const { offense } = anomaly;
return 'suggest' in offense && offense.suggest !== undefined;
}