diff --git a/README.md b/README.md
index 3abee55..134dcfc 100644
--- a/README.md
+++ b/README.md
@@ -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

diff --git a/src/test/constant.ts b/src/test/constant.ts
index cff060a..b0220f9 100644
--- a/src/test/constant.ts
+++ b/src/test/constant.ts
@@ -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");
diff --git a/src/test/fixtures/sample.sass b/src/test/fixtures/sample.sass
new file mode 100644
index 0000000..b6fd3d0
--- /dev/null
+++ b/src/test/fixtures/sample.sass
@@ -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
\ No newline at end of file
diff --git a/src/test/fixtures/sass.jsx b/src/test/fixtures/sass.jsx
new file mode 100644
index 0000000..357be4f
--- /dev/null
+++ b/src/test/fixtures/sass.jsx
@@ -0,0 +1,21 @@
+import styles from "./sample.sass";
+
+const Component = () => {
+ return (
+
+
Bar
+
Baz
+
Active
+
Child
+
Sibling
+
Other
+
Complex
+
Compound
+
Direct
+
Adjacent
+
General
+
+ );
+};
+
+export default Component;
\ No newline at end of file
diff --git a/src/test/suite/CompletionProvider.test.ts b/src/test/suite/CompletionProvider.test.ts
index 419bd35..9e028f8 100644
--- a/src/test/suite/CompletionProvider.test.ts
+++ b/src/test/suite/CompletionProvider.test.ts
@@ -10,6 +10,7 @@ import {
SAMPLE_TSX_FILE,
SAMPLE_TS_FILE,
STYLUS_JSX_FILE,
+ SASS_JSX_FILE,
} from "../constant";
import { readOptions } from "../utils";
@@ -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,
@@ -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}`);
+ });
+});
diff --git a/src/utils/index.ts b/src/utils/index.ts
index f352505..1a71eb4 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -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";
@@ -12,7 +19,10 @@ export function getCurrentLine(
/**
* @TODO Refact by new Tokenizer
*/
-export async function getAllClassNames(filePath: string, keyword: string): Promise {
+export async function getAllClassNames(
+ filePath: string,
+ keyword: string
+): Promise {
// check file exists, if not just return []
const filePathStat = await fse.stat(filePath);
if (!filePathStat.isFile()) {
@@ -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 [];
@@ -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;
@@ -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;
}