Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ Extension for [CSS Modules](https://github.com/css-modules/css-modules), which s
- Autocomplete
- Go to definition

## Additional Features

- **Experimental:** SASS (.sass) file support for class name completion

## Demo

![](https://i.giphy.com/l0EwY2Mk4IBgIholi.gif)
Expand Down
7 changes: 6 additions & 1 deletion src/test/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,9 @@ export const SAMPLE_TS_FILE = path.join(FIXTURES_PATH, "sample.ts");
export const SAMPLE_ASTRO_FILE = path.join(FIXTURES_PATH, "sample.astro");
export const STYLUS_JSX_FILE = path.join(FIXTURES_PATH, "stylus.jsx");
export const JUMP_PRECISE_DEF_FILE = path.join(FIXTURES_PATH, "jumpDef.jsx");
export const SPREAD_SYNTAX_FILE = path.join(FIXTURES_PATH, "spread-syntax", "index.js");
export const SPREAD_SYNTAX_FILE = path.join(
FIXTURES_PATH,
"spread-syntax",
"index.js"
);
export const SASS_JSX_FILE = path.join(FIXTURES_PATH, "sass.jsx");
20 changes: 20 additions & 0 deletions src/test/fixtures/sample.sass
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
.foo
color: red

.bar, .baz
background: blue

.parent
&.active
color: green
& .activeSpace
color: blue
> .child
color: yellow
+ .sibling
color: orange
~ .other
color: purple

.complex, &.compound, >.direct, +.adjacent, ~.general
border: 1px solid black
21 changes: 21 additions & 0 deletions src/test/fixtures/sass.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import styles from "./sample.sass";

const Component = () => {
return (
<div className={styles.foo}>
<div className={styles.bar}>Bar</div>
<div className={styles.baz}>Baz</div>
<div className={styles.active}>Active</div>
<div className={styles.child}>Child</div>
<div className={styles.sibling}>Sibling</div>
<div className={styles.other}>Other</div>
<div className={styles.complex}>Complex</div>
<div className={styles.compound}>Compound</div>
<div className={styles.direct}>Direct</div>
<div className={styles.adjacent}>Adjacent</div>
<div className={styles.general}>General</div>
</div>
);
};

export default Component;
9 changes: 9 additions & 0 deletions src/test/suite/CompletionProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
SAMPLE_TSX_FILE,
SAMPLE_TS_FILE,
STYLUS_JSX_FILE,
SASS_JSX_FILE,
} from "../constant";
import { readOptions } from "../utils";

Expand All @@ -19,6 +20,7 @@ const uri3 = vscode.Uri.file(SAMPLE_JS_FILE);
const uri4 = vscode.Uri.file(SAMPLE_TSX_FILE);
const uri5 = vscode.Uri.file(SAMPLE_TS_FILE);
const uri6 = vscode.Uri.file(SAMPLE_ASTRO_FILE);
const uri7 = vscode.Uri.file(SASS_JSX_FILE);

function testCompletion(
position: vscode.Position,
Expand Down Expand Up @@ -210,3 +212,10 @@ test("support astro", () => {
assert.ok(false, `error in OpenTextDocument ${err}`);
});
});

test("support .sass completion", () => {
const position = new vscode.Position(4, 27);
return Promise.resolve(testCompletion(position, 14, uri7)).catch((err) => {
assert.ok(false, `error in OpenTextDocument ${err}`);
});
});
59 changes: 46 additions & 13 deletions src/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { Position, TextDocument, CompletionItem, CompletionItemKind, TextEdit, Range } from "vscode";
import {
Position,
TextDocument,
CompletionItem,
CompletionItemKind,
TextEdit,
Range,
} from "vscode";
import * as fse from "fs-extra";
import * as _ from "lodash";

Expand All @@ -12,7 +19,10 @@ export function getCurrentLine(
/**
* @TODO Refact by new Tokenizer
*/
export async function getAllClassNames(filePath: string, keyword: string): Promise<string[]> {
export async function getAllClassNames(
filePath: string,
keyword: string
): Promise<string[]> {
// check file exists, if not just return []
const filePathStat = await fse.stat(filePath);
if (!filePathStat.isFile()) {
Expand All @@ -22,10 +32,16 @@ export async function getAllClassNames(filePath: string, keyword: string): Promi
const content = await fse.readFile(filePath, { encoding: "utf8" });
let matchLineRegexp = /.*[,{]/g;

// experimental stylus support
if (filePath.endsWith(".styl") ||filePath.endsWith(".stylus")) {
matchLineRegexp = /\..*/g
// experimental stylus support
if (filePath.endsWith(".styl") || filePath.endsWith(".stylus")) {
matchLineRegexp = /\..*/g;
}

// experimental sass support
if (filePath.endsWith(".sass")) {
matchLineRegexp = /(^|[\s,&>+~])\.[_A-Za-z0-9-]+/gm;
}

const lines = content.match(matchLineRegexp);
if (lines === null) {
return [];
Expand All @@ -36,7 +52,10 @@ export async function getAllClassNames(filePath: string, keyword: string): Promi
return [];
}

const uniqNames = _.uniq(classNames).map((item) => item.slice(1)).filter((item) => !/^[0-9]/.test(item));
const uniqNames = _.uniq(classNames)
.map((item) => item.slice(1))
.filter((item) => !/^[0-9]/.test(item));

return keyword !== ""
? uniqNames.filter((item) => item.indexOf(keyword) !== -1)
: uniqNames;
Expand All @@ -53,19 +72,33 @@ export function dashesCamelCase(str: string): string {
/**
* check kebab-case classname
*/
export function isKebabCaseClassName (className: string): boolean {
return className?.includes('-');
export function isKebabCaseClassName(className: string): boolean {
return className?.includes("-");
}

/**
* BracketCompletionItem Factory
*/
export function createBracketCompletionItem (className: string, position: Position): CompletionItem {
const completionItem = new CompletionItem(className, CompletionItemKind.Variable);
export function createBracketCompletionItem(
className: string,
position: Position
): CompletionItem {
const completionItem = new CompletionItem(
className,
CompletionItemKind.Variable
);
completionItem.detail = `['${className}']`;
completionItem.documentation = "kebab-casing may cause unexpected behavior when trying to access style.class-name as a dot notation. You can still work around kebab-case with bracket notation (eg. style['class-name']) but style.className is cleaner.";
completionItem.documentation =
"kebab-casing may cause unexpected behavior when trying to access style.class-name as a dot notation. You can still work around kebab-case with bracket notation (eg. style['class-name']) but style.className is cleaner.";
completionItem.insertText = `['${className}']`;
completionItem.additionalTextEdits = [new TextEdit(new Range(new Position(position.line, position.character - 1),
new Position(position.line, position.character)), '')];
completionItem.additionalTextEdits = [
new TextEdit(
new Range(
new Position(position.line, position.character - 1),
new Position(position.line, position.character)
),
""
),
];
return completionItem;
}
Loading