Skip to content

Commit 335a37e

Browse files
added references search
1 parent 6c4750f commit 335a37e

File tree

8 files changed

+145
-7
lines changed

8 files changed

+145
-7
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "css-linter"
3-
version = "1.9.1"
3+
version = "1.9.2"
44
edition = "2021"
55

66
[dependencies]

src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use std::env;
33
use anyhow::Result;
44
use modules::{
55
css_class::get_class_body, defined_classes::get_defined_classes, help::print_help, linter,
6-
styles_imports::get_styles_imports, version::get_version,
6+
styles_imports::get_styles_imports, used_css::get_class_usages, version::get_version,
77
};
88

99
mod config;
@@ -23,6 +23,7 @@ fn main() -> Result<()> {
2323
Some(arg) if arg == "--imports" => get_styles_imports()?,
2424
Some(arg) if arg == "--classes" => get_defined_classes()?,
2525
Some(arg) if arg == "--class" => get_class_body()?,
26+
Some(arg) if arg == "--usages" => get_class_usages()?,
2627
Some(_) => print_help(),
2728
None => print_help(),
2829
};

src/modules/linter.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ pub fn lint() -> Result<()> {
2323
let args: Vec<String> = env::args().collect();
2424

2525
let path = args.get(2).unwrap_or_else(|| {
26-
eprintln!("Path to the file must be provided");
26+
eprintln!("Path to the workplace must be provided");
2727
process::exit(1);
2828
});
2929

src/modules/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@ pub mod defined_classes;
33
pub mod linter;
44
pub mod styles_imports;
55
pub mod version;
6-
pub mod help;
6+
pub mod help;
7+
pub mod used_css;

src/modules/used_css.rs

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
use std::{env, fs, path::Path, process};
2+
3+
use anyhow::Result;
4+
5+
use crate::{
6+
config::get_compiler_options,
7+
parsers::{extract_default_css_imports, extract_used_classes},
8+
utils::{list_files_in_directory, process_relative_import, replace_aliases},
9+
};
10+
11+
pub fn get_class_usages() -> Result<()> {
12+
const COLOR_RED: &str = "\x1b[31m";
13+
const COLOR_RESET: &str = "\u{001B}[0m";
14+
15+
let args: Vec<String> = env::args().collect();
16+
17+
let file = args.get(2).unwrap_or_else(|| {
18+
eprintln!("Path to the file must be provided");
19+
process::exit(1);
20+
});
21+
22+
let workplace_path = env::current_dir()?;
23+
let target_file = match Path::new(file).strip_prefix(workplace_path) {
24+
Ok(f) => f,
25+
Err(_) => {
26+
eprintln!("Path to the file must be relative to the current directory");
27+
process::exit(1);
28+
}
29+
};
30+
31+
let class_name = args.get(3).unwrap_or_else(|| {
32+
eprintln!("Class name must be provided");
33+
process::exit(1);
34+
});
35+
36+
let tsconfig = get_compiler_options().unwrap_or_else(|e| {
37+
eprintln!(
38+
"\n{}Error{}: Could not load tsconfig.json. Is the provided directory a typescript project? ({})",
39+
COLOR_RED, COLOR_RESET, e
40+
);
41+
process::exit(1);
42+
});
43+
44+
let dir = list_files_in_directory(Path::new(".").to_path_buf(), tsconfig.exclude);
45+
for entry in &dir {
46+
let path = entry.replace("\\", "/");
47+
48+
if path.ends_with(".tsx") || path.ends_with(".jsx") {
49+
let code = fs::read_to_string(entry)?;
50+
let imported_css = extract_default_css_imports(&code).unwrap_or_else(|e| {
51+
eprintln!("Could not parse file: {}\n{}", entry, e);
52+
process::exit(1);
53+
});
54+
55+
for (mut style_path, class_names) in imported_css {
56+
process_relative_import(Path::new(entry), &mut style_path)?;
57+
replace_aliases(&mut style_path, tsconfig.compiler_options.paths.clone());
58+
59+
if target_file.to_string_lossy().replace("\\", "/") != style_path.replace("./", "")
60+
{
61+
continue;
62+
}
63+
64+
let used_fields = extract_used_classes(&code, &class_names, path.clone())
65+
.unwrap_or_else(|e| {
66+
eprintln!("Could not parse file: {}\n{}", entry, e);
67+
process::exit(1);
68+
});
69+
70+
for field in used_fields {
71+
if field.class_name == *class_name {
72+
println!(
73+
"{}:{}:{}:{}",
74+
path,
75+
field.line,
76+
field.column,
77+
field.class_name.len()
78+
);
79+
}
80+
}
81+
}
82+
}
83+
}
84+
Ok(())
85+
}

vscode-ext/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "next-css-lint",
33
"displayName": "Next.Js CSS linter",
44
"description": "A Rust-based VSCode extension that adds tools for working with CSS modules in React and Next.Js",
5-
"version": "1.8.2",
5+
"version": "1.8.3",
66
"license": "MIT",
77
"repository": "https://github.com/Andcool-Systems/css-linter",
88
"author": {

vscode-ext/src/extension.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { run_diag } from './diagnostics';
66
import { CssCompletionProvider } from './autocomplete';
77
import { CSSHoverProvider } from './hover';
88
import { convert_css } from './convert';
9+
import { CSSReferenceProvider } from './usages';
910

1011
const fileFilter = [
1112
{ scheme: 'file', language: 'javascriptreact' },
@@ -24,6 +25,7 @@ export function activate(context: vscode.ExtensionContext) {
2425
const css_definition = new CssModuleDefinitionProvider();
2526
const css_completion = new CssCompletionProvider();
2627
const css_hover = new CSSHoverProvider();
28+
const css_references = new CSSReferenceProvider();
2729

2830
let save_evt = vscode.workspace.onDidSaveTextDocument(() => run_diag(diagnosticCollection));
2931
let code_action = vscode.languages.registerCodeActionsProvider(
@@ -42,12 +44,14 @@ export function activate(context: vscode.ExtensionContext) {
4244
);
4345
let hover_provider = vscode.languages.registerHoverProvider(fileFilter, css_hover);
4446
let extractor = vscode.commands.registerCommand('next-css-lint.convert-inline', convert_css);
47+
let css_reference_provider = vscode.languages.registerReferenceProvider('css', css_references);
4548

4649
const enable_command = vscode.commands.registerCommand('next-css-lint.enable', async () => {
4750
await config.update('enabled', true, vscode.ConfigurationTarget.Workspace);
4851
save_evt = vscode.workspace.onDidSaveTextDocument(() => run_diag(diagnosticCollection));
4952
hover_provider = vscode.languages.registerHoverProvider(fileFilter, css_hover);
5053
extractor = vscode.commands.registerCommand('next-css-lint.convert-inline', convert_css);
54+
css_reference_provider = vscode.languages.registerReferenceProvider('css', css_references);
5155
code_action = vscode.languages.registerCodeActionsProvider(
5256
'css',
5357
fix_provider,
@@ -71,7 +75,8 @@ export function activate(context: vscode.ExtensionContext) {
7175
definition_provider,
7276
completion_provider,
7377
hover_provider,
74-
extractor
78+
extractor,
79+
css_reference_provider
7580
);
7681
run_diag(diagnosticCollection);
7782
})
@@ -101,7 +106,8 @@ export function activate(context: vscode.ExtensionContext) {
101106
definition_provider,
102107
completion_provider,
103108
hover_provider,
104-
extractor
109+
extractor,
110+
css_reference_provider
105111
);
106112
run_diag(diagnosticCollection);
107113
})

vscode-ext/src/usages/index.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import * as vscode from 'vscode';
2+
import { execAsync, getExecPath } from '../utils';
3+
import { join } from 'path';
4+
5+
export class CSSReferenceProvider implements vscode.ReferenceProvider {
6+
async provideReferences(
7+
document: vscode.TextDocument,
8+
position: vscode.Position,
9+
context: vscode.ReferenceContext,
10+
token: vscode.CancellationToken
11+
): Promise<vscode.Location[] | null | undefined> {
12+
const wordRange = document.getWordRangeAtPosition(position, /[a-zA-Z0-9_-]+/);
13+
if (!wordRange) return null;
14+
15+
const exec_path = getExecPath();
16+
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
17+
if (!workspacePath) {
18+
return null;
19+
}
20+
21+
const className = document.getText(wordRange);
22+
const used_classes = await execAsync(
23+
`${exec_path} --usages ${document.uri.fsPath} ${className}`,
24+
{
25+
cwd: workspacePath
26+
}
27+
);
28+
console.log(used_classes);
29+
return used_classes
30+
.split('\n')
31+
.filter(Boolean)
32+
.map(line => line.split(':'))
33+
.map(usage => {
34+
let path = usage[0];
35+
if (usage[0].startsWith('.')) {
36+
path = join(workspacePath, usage[0]);
37+
}
38+
const file = vscode.Uri.file(path);
39+
const line = parseInt(usage[1]) - 1;
40+
const col = parseInt(usage[2]) + 1;
41+
const len = parseInt(usage[3]);
42+
return new vscode.Location(file, new vscode.Range(line, col, line, col + len));
43+
});
44+
}
45+
}

0 commit comments

Comments
 (0)