diff --git a/README.md b/README.md index 9ea274b..0fde8ce 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,18 @@ Converts between `px` and `rem` units in VSCode, And support WXSS. + Move the cursor to `14px`, press `Alt + z` to convert `rem` + CLI: Press `F1`, enter `cssrem` +## Ignoring code + +You can ignore: +- Entire files by setting ignores in `.cssrem` or `.vscode/settings.json` +- The next line by using `// cssrem-disable-next-line` + +```less +// cssrem-disable-next-line +@media (min-width: 768px) {} +``` +> The above code will ignore processing of `768px` + # Support Language html vue css less scss sass stylus tpl(php smarty3) tsx jsx diff --git a/README.zh-CN.md b/README.zh-CN.md index 80cd1be..dd7972a 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -25,6 +25,18 @@ + 光标至 `14px` 上,按下 `Alt + z` 对应转化 `rem` + 通过 `F1` 面板查找:`cssrem` +## 忽略代码 + +你可以忽略: +- 通过 `.cssrem` 或 `.vscode/settings.json` 设置 `ignores` 忽略整个文件 +- 通过使用 `// cssrem-disable-next-line` 表示忽略下一行的处理 + +```less +// cssrem-disable-next-line +@media (min-width: 768px) {} +``` +> 以上代码将忽略 `768px` 的处理 + # 支持语言 html vue css less scss sass stylus tpl(php smarty3) tsx jsx diff --git a/examples/.cssrem b/examples/.cssrem new file mode 100644 index 0000000..7e60e9c --- /dev/null +++ b/examples/.cssrem @@ -0,0 +1,12 @@ +{ + "$schema": "https://raw.githubusercontent.com/cipchk/vscode-cssrem/master/schema.json", + "rootFontSize": 24, + "fixedDigits": 3, + "vw": true, + "currentLine": "show", + "ignores": [ + // "**/*.less", + // "*.tsx" + ], + "ignoresViaCommand": [] +} diff --git a/examples/demo.less b/examples/demo.less index 22cd044..f3a4740 100644 --- a/examples/demo.less +++ b/examples/demo.less @@ -1,5 +1,14 @@ body { - font-size: 1rem; - padding: 10px 12px 1rem 1.2rem; + font-size: 48px; + padding: 10px 12px 24px 28.8px; margin: 7.5px 1vw; } + +// cssrem-disable-next-line +@media (min-width: 768px) { + body { + font-size: 28.8px; + padding: 10px 12px 24px 28.8px; + margin: 7.5px 1vw; + } +} diff --git a/src/completion.ts b/src/completion.ts index f67275a..f3de164 100644 --- a/src/completion.ts +++ b/src/completion.ts @@ -1,6 +1,7 @@ import { CompletionItem, CompletionItemKind, CompletionItemProvider, MarkdownString, Position, Range, TextDocument } from 'vscode'; import { cog, isIngore } from './config'; import { CssRemProcess } from './process'; +import { isDisabledNextLine } from './ignore-comment'; export default class implements CompletionItemProvider { constructor(private _: string, private process: CssRemProcess) {} @@ -9,6 +10,7 @@ export default class implements CompletionItemProvider { if (isIngore(document.uri)) return Promise.resolve([]); return new Promise(resolve => { + if (isDisabledNextLine(document, position.line)) return null; const lineText = document.getText(new Range(position.with(undefined, 0), position)); const res = this.process.convert(lineText); if (res == null || res.length === 0) { diff --git a/src/hover.ts b/src/hover.ts index c3d9840..1016391 100644 --- a/src/hover.ts +++ b/src/hover.ts @@ -1,18 +1,26 @@ -import { Hover, HoverProvider, MarkdownString, Position, ProviderResult, TextDocument } from 'vscode'; -import { cog, isIngore } from './config'; -import { RULES } from './rules'; +import { + Hover, + HoverProvider, + MarkdownString, + Position, + ProviderResult, + TextDocument, +} from "vscode"; +import { cog, isIngore } from "./config"; +import { RULES } from "./rules"; +import { isDisabledNextLine } from "./ignore-comment"; export default class implements HoverProvider { private getText(line: string, pos: Position): string { const point = pos.character; - let text = ''; + let text = ""; line.replace(/[.0-9]+(px|rem|rpx|vw)/g, (a, _, idx) => { const start = idx + 1; const end = idx + a.length + 1; if (!text && point >= start && point <= end) { text = a; } - return ''; + return ""; }); return text; } @@ -24,15 +32,23 @@ export default class implements HoverProvider { if (!text) { return null; } - let results = RULES.filter(w => w.hover && w.hover.test(text) && w.hoverFn != null) - .map(rule => rule.hoverFn!(text)) - .filter(h => h != null && h.documentation); - if (cog.hover === 'onlyMark') { - results = results.filter(w => !line.includes(`/* ${w.type} */`)); + if (isDisabledNextLine(doc, pos.line)) { + return null; + } + let results = RULES.filter( + (w) => w.hover && w.hover.test(text) && w.hoverFn != null + ) + .map((rule) => rule.hoverFn!(text)) + .filter((h) => h != null && h.documentation); + if (cog.hover === "onlyMark") { + results = results.filter((w) => !line.includes(`/* ${w.type} */`)); } if (results.length === 0) return null; - if (results.length === 1) return new Hover(new MarkdownString(results[0].documentation)); + if (results.length === 1) + return new Hover(new MarkdownString(results[0].documentation)); - return new Hover(new MarkdownString(results.map(h => `- ${h.documentation}`).join('\n'))); + return new Hover( + new MarkdownString(results.map((h) => `- ${h.documentation}`).join("\n")) + ); } } diff --git a/src/ignore-comment.ts b/src/ignore-comment.ts new file mode 100644 index 0000000..6366974 --- /dev/null +++ b/src/ignore-comment.ts @@ -0,0 +1,13 @@ +import { TextDocument } from 'vscode'; + +export const DISABLE_NEXT_LINE_COMMAND = 'cssrem-disable-next-line'; + +export function isDisabledNextLineViaText(line: string): boolean { + return line.includes(DISABLE_NEXT_LINE_COMMAND); +} + +export function isDisabledNextLine(doc: TextDocument, lineNumber: number): boolean { + if (lineNumber - 1 < 0) return false; + const line = doc.lineAt(lineNumber - 1).text.trim(); + return isDisabledNextLineViaText(line); +} diff --git a/src/line-annotation.ts b/src/line-annotation.ts index 4be5975..ef4e627 100644 --- a/src/line-annotation.ts +++ b/src/line-annotation.ts @@ -13,6 +13,7 @@ import { import { cog, isIngore } from './config'; import { HoverResult } from './interface'; import { RULES } from './rules'; +import { isDisabledNextLine } from './ignore-comment'; const annotationDecoration = window.createTextEditorDecorationType({ after: { @@ -93,6 +94,7 @@ export class LineAnnotation implements Disposable { private genMessage(doc: TextDocument, lineNumber: number): string | null { const text = doc.lineAt(lineNumber).text.trim(); if (text.length <= 0) return null; + if (isDisabledNextLine(doc, lineNumber)) return null; const values = text.match(/([.0-9]+(px|rem|rpx|vw))/g); if (values == null) return null; diff --git a/src/process.ts b/src/process.ts index 5b2fd76..3ebd015 100644 --- a/src/process.ts +++ b/src/process.ts @@ -1,41 +1,55 @@ -import { ConvertResult, Rule, RuleOPType, Type } from './interface'; -import { RULES } from './rules'; -import { Position, Range, Selection, TextEditor } from 'vscode'; -import { isIngore } from './config'; +import { ConvertResult, Rule, RuleOPType, Type } from "./interface"; +import { RULES } from "./rules"; +import { Position, Range, Selection, TextEditor } from "vscode"; +import { isIngore } from "./config"; +import { isDisabledNextLineViaText } from "./ignore-comment"; export class CssRemProcess { convert(text: string): ConvertResult[] | null { - const res = this.getRule('single', text); + const res = this.getRule("single", text); if (res.length === 0) { return null; } - return res.map(i => i.rule.fn(i.text)!); + return res.map((i) => i.rule.fn(i.text)!); } - convertAll(code: string, ignores: string[], type: Type): string { + private convertAll(code: string, ignores: string[], type: Type): string { if (!code) { return code; } - const rule = RULES.find(w => w.type === type); + const rule = RULES.find((w) => w.type === type); if (!rule) { return code; } - return code.replace(rule.all, (word: string) => { - if (ignores.includes(word)) { - return word; - } - const res = rule.fn(word); - if (res) { - return res.value; - } - return word; - }); + const lines = code.split("\n"); + + const result = lines + .map((line, lineIndex) => { + return line.replace(rule.all, (word) => { + if (ignores.includes(word)) { + return word; + } + if (lineIndex > 0 && isDisabledNextLineViaText(lines[lineIndex - 1])) + return word; + + const res = rule.fn(word); + if (res) { + return res.value; + } + return word; + }); + }) + .join("\n"); + return result; } - private getRule(type: RuleOPType, text: string): { rule: Rule; text: string }[] { + private getRule( + type: RuleOPType, + text: string + ): { rule: Rule; text: string }[] { const result: { rule: Rule; text: string }[] = []; for (const rule of RULES) { const match = text.match((rule as any)[type]); @@ -47,22 +61,29 @@ export class CssRemProcess { } private getWordRange(textEditor: TextEditor, type: Type): Range | null { - const position = new Position(textEditor.selection.start.line, textEditor.selection.start.character); + const position = new Position( + textEditor.selection.start.line, + textEditor.selection.start.character + ); const range = textEditor.document.getWordRangeAtPosition(position); if (!range) return null; const word = textEditor.document.getText(range); if (!word) return null; - const rule = RULES.find(w => w.type === type); + const rule = RULES.find((w) => w.type === type); return rule && rule.all.test(word) ? range : null; } - modifyDocument(textEditor: TextEditor, ignoresViaCommand: string[], type: Type): void { + modifyDocument( + textEditor: TextEditor, + ignoresViaCommand: string[], + type: Type + ): void { const doc = textEditor.document; if (isIngore(doc.uri)) return; let selection: Selection | Range = textEditor.selection; // When the cursor is in the valid range in switch mode - if (selection.isEmpty && type.toLowerCase().includes('switch')) { + if (selection.isEmpty && type.toLowerCase().includes("switch")) { const wordRange = this.getWordRange(textEditor, type); if (wordRange) { selection = wordRange; @@ -70,13 +91,19 @@ export class CssRemProcess { } if (selection.isEmpty) { const start = new Position(0, 0); - const end = new Position(doc.lineCount - 1, doc.lineAt(doc.lineCount - 1).text.length); + const end = new Position( + doc.lineCount - 1, + doc.lineAt(doc.lineCount - 1).text.length + ); selection = new Range(start, end); } const text = doc.getText(selection); - textEditor.edit(builder => { - builder.replace(selection, this.convertAll(text, ignoresViaCommand, type)); + textEditor.edit((builder) => { + builder.replace( + selection, + this.convertAll(text, ignoresViaCommand, type) + ); }); } }