feat(vscode): support lint vue file (#1842)

![CleanShot 2023-12-27 at 22 56 34@2x](https://github.com/oxc-project/oxc/assets/33973865/189cc7e5-91e9-4f25-bd9c-14d663387870)
This commit is contained in:
Wenzhe Wang 2023-12-31 22:31:26 +08:00 committed by GitHub
parent 3d4163795e
commit 665f818217
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 67 additions and 18 deletions

View file

@ -8,12 +8,17 @@ use std::{
}, },
}; };
use crate::options::LintOptions;
use crate::walk::Walk; use crate::walk::Walk;
use crate::{options::LintOptions, walk::Extensions};
use miette::NamedSource; use miette::NamedSource;
use oxc_allocator::Allocator; use oxc_allocator::Allocator;
use oxc_diagnostics::{miette, Error, Severity}; use oxc_diagnostics::{miette, Error, Severity};
use oxc_linter::{LintContext, LintSettings, Linter}; use oxc_linter::{
partial_loader::{
vue_partial_loader::VuePartialLoader, PartialLoader, LINT_PARTIAL_LOADER_EXT,
},
LintContext, LintSettings, Linter,
};
use oxc_linter_plugin::{make_relative_path_parts, LinterPlugin}; use oxc_linter_plugin::{make_relative_path_parts, LinterPlugin};
use oxc_parser::Parser; use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder; use oxc_semantic::SemanticBuilder;
@ -223,8 +228,8 @@ impl IsolatedLintHandler {
} }
fn is_wanted_ext(path: &Path) -> bool { fn is_wanted_ext(path: &Path) -> bool {
path.extension() let extensions = get_extensions();
.map_or(false, |ext| VALID_EXTENSIONS.contains(&ext.to_string_lossy().as_ref())) path.extension().map_or(false, |ext| extensions.contains(&ext.to_string_lossy().as_ref()))
} }
fn process_paths( fn process_paths(
@ -234,7 +239,7 @@ impl IsolatedLintHandler {
) { ) {
let (tx_path, rx_path) = mpsc::channel::<Box<Path>>(); let (tx_path, rx_path) = mpsc::channel::<Box<Path>>();
let walk = Walk::new(&self.options); let walk = Walk::new(&self.options).with_extensions(Extensions(get_extensions()));
let number_of_files = Arc::clone(number_of_files); let number_of_files = Arc::clone(number_of_files);
rayon::spawn(move || { rayon::spawn(move || {
let mut count = 0; let mut count = 0;
@ -276,19 +281,37 @@ impl IsolatedLintHandler {
.collect() .collect()
} }
fn get_source_type_and_text(
path: &Path,
source_text: Option<String>,
) -> Option<(SourceType, String)> {
let read_file = |path: &Path| -> String {
if let Some(source_text) = source_text {
return source_text;
}
fs::read_to_string(path).unwrap_or_else(|_| panic!("Failed to read {path:?}"))
};
if let Ok(source_type) = SourceType::from_path(path) {
return Some((source_type, read_file(path)));
}
let ext = path.extension().and_then(std::ffi::OsStr::to_str)?;
let partial_loader = if ext == "vue" { Some(PartialLoader::Vue) } else { None };
let partial_loader = partial_loader?;
let source_text = read_file(path);
let ret = partial_loader.parse(&source_text);
Some((ret.source_type, ret.source_text))
}
fn lint_path( fn lint_path(
linter: &Linter, linter: &Linter,
path: &Path, path: &Path,
plugin: Plugin, plugin: Plugin,
source_text: Option<String>, source_text: Option<String>,
) -> Option<(PathBuf, Vec<ErrorWithPosition>)> { ) -> Option<(PathBuf, Vec<ErrorWithPosition>)> {
let source_text = source_text.unwrap_or_else(|| { let (source_type, source_text) = Self::get_source_type_and_text(path, source_text)?;
fs::read_to_string(path).unwrap_or_else(|_| panic!("Failed to read {path:?}"))
});
let allocator = Allocator::default(); let allocator = Allocator::default();
let source_type =
SourceType::from_path(path).unwrap_or_else(|_| panic!("Incorrect {path:?}"));
let ret = Parser::new(&allocator, &source_text, source_type) let ret = Parser::new(&allocator, &source_text, source_type)
.allow_return_outside_function(true) .allow_return_outside_function(true)
.parse(); .parse();
@ -389,6 +412,14 @@ impl IsolatedLintHandler {
} }
} }
fn get_extensions() -> Vec<&'static str> {
VALID_EXTENSIONS
.iter()
.chain(LINT_PARTIAL_LOADER_EXT.iter())
.copied()
.collect::<Vec<&'static str>>()
}
#[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_possible_truncation)]
fn offset_to_position(offset: usize, source_text: &str) -> Option<Position> { fn offset_to_position(offset: usize, source_text: &str) -> Option<Position> {
let rope = Rope::from_str(source_text); let rope = Rope::from_str(source_text);

View file

@ -4,9 +4,17 @@ use ignore::{overrides::OverrideBuilder, DirEntry, WalkBuilder};
use oxc_span::VALID_EXTENSIONS; use oxc_span::VALID_EXTENSIONS;
use crate::options::LintOptions; use crate::options::LintOptions;
pub struct Extensions(pub Vec<&'static str>);
impl Default for Extensions {
fn default() -> Self {
Self(VALID_EXTENSIONS.to_vec())
}
}
pub struct Walk { pub struct Walk {
inner: ignore::Walk, inner: ignore::Walk,
extensions: Extensions,
} }
impl Walk { impl Walk {
@ -36,17 +44,26 @@ impl Walk {
// * following symlinks is a really slow syscall // * following symlinks is a really slow syscall
// * it is super rare to have symlinked source code // * it is super rare to have symlinked source code
let inner = inner.ignore(false).git_global(false).follow_links(false).build(); let inner = inner.ignore(false).git_global(false).follow_links(false).build();
Self { inner } Self { inner, extensions: Extensions::default() }
}
pub fn with_extensions(mut self, extensions: Extensions) -> Self {
self.extensions = extensions;
self
} }
pub fn iter(self) -> impl Iterator<Item = Box<Path>> { pub fn iter(self) -> impl Iterator<Item = Box<Path>> {
self.inner let extensions = self.extensions;
.filter_map(Result::ok) self.inner.filter_map(Result::ok).filter_map(move |dir_entry| {
.filter(Self::is_wanted_entry) if Self::is_wanted_entry(&dir_entry, &extensions) {
.map(|entry| entry.path().to_path_buf().into_boxed_path()) Some(dir_entry.path().to_path_buf().into_boxed_path())
} else {
None
}
})
} }
pub fn is_wanted_entry(dir_entry: &DirEntry) -> bool { pub fn is_wanted_entry(dir_entry: &DirEntry, extensions: &Extensions) -> bool {
let Some(file_type) = dir_entry.file_type() else { return false }; let Some(file_type) = dir_entry.file_type() else { return false };
if file_type.is_dir() { if file_type.is_dir() {
return false; return false;
@ -56,6 +73,6 @@ impl Walk {
return false; return false;
} }
let Some(extension) = dir_entry.path().extension() else { return false }; let Some(extension) = dir_entry.path().extension() else { return false };
VALID_EXTENSIONS.contains(&extension.to_string_lossy().as_ref()) extensions.0.contains(&extension.to_string_lossy().as_ref())
} }
} }

View file

@ -125,6 +125,7 @@ export async function activate(context: ExtensionContext) {
"javascript", "javascript",
"typescriptreact", "typescriptreact",
"javascriptreact", "javascriptreact",
"vue"
].map((lang) => ({ ].map((lang) => ({
language: lang, language: lang,
scheme: "file", scheme: "file",