Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
19e9eb1af8 | ||
|
|
e57044800c | ||
|
|
ae8ff7cb7f | ||
|
|
c05914f222 | ||
|
|
24179655b8 | ||
|
|
d27b419e68 | ||
|
|
9fc7a32a24 |
@@ -1,6 +1,6 @@
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.176
|
||||
rev: v0.0.177
|
||||
hooks:
|
||||
- id: ruff
|
||||
|
||||
|
||||
8
Cargo.lock
generated
8
Cargo.lock
generated
@@ -724,7 +724,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.176-dev.0"
|
||||
version = "0.0.177-dev.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
@@ -1821,7 +1821,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.176"
|
||||
version = "0.0.177"
|
||||
dependencies = [
|
||||
"annotate-snippets 0.9.1",
|
||||
"anyhow",
|
||||
@@ -1874,7 +1874,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.176"
|
||||
version = "0.0.177"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap 4.0.29",
|
||||
@@ -1892,7 +1892,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.176"
|
||||
version = "0.0.177"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
||||
@@ -6,7 +6,7 @@ members = [
|
||||
|
||||
[package]
|
||||
name = "ruff"
|
||||
version = "0.0.176"
|
||||
version = "0.0.177"
|
||||
edition = "2021"
|
||||
rust-version = "1.65.0"
|
||||
|
||||
@@ -41,7 +41,7 @@ quick-junit = { version = "0.3.2" }
|
||||
rayon = { version = "1.5.3" }
|
||||
regex = { version = "1.6.0" }
|
||||
ropey = { version = "1.5.0", features = ["cr_lines", "simd"], default-features = false }
|
||||
ruff_macros = { version = "0.0.176", path = "ruff_macros" }
|
||||
ruff_macros = { version = "0.0.177", path = "ruff_macros" }
|
||||
rustc-hash = { version = "1.1.0" }
|
||||
rustpython-ast = { features = ["unparse"], git = "https://github.com/RustPython/RustPython.git", rev = "2edd0d264c50c7807bcff03a52a6509e8b7f187f" }
|
||||
rustpython-common = { git = "https://github.com/RustPython/RustPython.git", rev = "2edd0d264c50c7807bcff03a52a6509e8b7f187f" }
|
||||
|
||||
67
README.md
67
README.md
@@ -155,7 +155,7 @@ Ruff also works with [pre-commit](https://pre-commit.com):
|
||||
```yaml
|
||||
repos:
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: v0.0.176
|
||||
rev: v0.0.177
|
||||
hooks:
|
||||
- id: ruff
|
||||
```
|
||||
@@ -778,7 +778,7 @@ For more, see [flake8-simplify](https://pypi.org/project/flake8-simplify/0.19.3/
|
||||
|
||||
| Code | Name | Message | Fix |
|
||||
| ---- | ---- | ------- | --- |
|
||||
| SIM118 | KeyInDict | Use 'key in dict' instead of 'key in dict.keys() | 🛠 |
|
||||
| SIM118 | KeyInDict | Use `key in dict` instead of `key in dict.keys()` | 🛠 |
|
||||
|
||||
### flake8-tidy-imports (TID)
|
||||
|
||||
@@ -852,6 +852,37 @@ For more, see [Pylint](https://pypi.org/project/pylint/2.15.7/) on PyPI.
|
||||
|
||||
Download the [Ruff VS Code extension](https://marketplace.visualstudio.com/items?itemName=charliermarsh.ruff).
|
||||
|
||||
### Language Server Protocol
|
||||
|
||||
Ruff is available as a [Language Server Protocol (LSP)](https://microsoft.github.io/language-server-protocol/)
|
||||
server, distributed as the [`python-lsp-ruff`](https://github.com/python-lsp/python-lsp-ruff) plugin
|
||||
for [`python-lsp-server`](https://github.com/python-lsp/python-lsp-server), both of which are
|
||||
installable via [PyPI](https://pypi.org/project/python-lsp-ruff/):
|
||||
|
||||
```shell
|
||||
pip install python-lsp-server python-lsp-ruff
|
||||
```
|
||||
|
||||
The LSP server can be used with any editor that supports the Language Server Protocol. For example,
|
||||
to use it with Neovim, you would add something like the following to your `init.lua`:
|
||||
|
||||
```lua
|
||||
require'lspconfig'.pylsp.setup {
|
||||
settings = {
|
||||
pylsp = {
|
||||
plugins = {
|
||||
ruff = {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
[`ruffd`](https://github.com/Seamooo/ruffd) is another implementation of the Language Server
|
||||
Protocol (LSP) for Ruff, written in Rust.
|
||||
|
||||
### PyCharm
|
||||
|
||||
Ruff can be installed as an [External Tool](https://www.jetbrains.com/help/pycharm/configuring-third-party-tools.html)
|
||||
@@ -864,10 +895,13 @@ Ruff should then appear as a runnable action:
|
||||
|
||||

|
||||
|
||||
### Vim & Neovim (Unofficial)
|
||||
### Vim & Neovim
|
||||
|
||||
Ruff is available as part of the [coc-pyright](https://github.com/fannheyward/coc-pyright) extension
|
||||
for coc.nvim.
|
||||
Ruff can be integrated into any editor that supports the Language Server Protocol (LSP) (see:
|
||||
[Language Server Protocol](#language-server-protocol)).
|
||||
|
||||
Ruff is also available as part of the [coc-pyright](https://github.com/fannheyward/coc-pyright)
|
||||
extension for `coc.nvim`.
|
||||
|
||||
<details>
|
||||
<summary>Ruff can also be integrated via <a href="https://github.com/neovim/nvim-lspconfig/blob/master/doc/server_configurations.md#efm"><code>efm</code></a> in just a <a href="https://github.com/JafarAbdi/myconfigs/blob/6f0b6b2450e92ec8fc50422928cd22005b919110/efm-langserver/config.yaml#L14-L20">few lines</a>.</summary>
|
||||
@@ -923,11 +957,6 @@ null_ls.setup({
|
||||
|
||||
</details>
|
||||
|
||||
### Language Server Protocol (Unofficial)
|
||||
|
||||
[`ruffd`](https://github.com/Seamooo/ruffd) is a Rust-based language server for Ruff that implements
|
||||
the Language Server Protocol (LSP).
|
||||
|
||||
### GitHub Actions
|
||||
|
||||
GitHub Actions has everything you need to run Ruff out-of-the-box:
|
||||
@@ -975,9 +1004,8 @@ Under those conditions, Ruff implements every rule in Flake8.
|
||||
Ruff also re-implements some of the most popular Flake8 plugins and related code quality tools
|
||||
natively, including:
|
||||
|
||||
- [`isort`](https://pypi.org/project/isort/)
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||
- [`eradicate`](https://pypi.org/project/eradicate/)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
@@ -995,12 +1023,13 @@ natively, including:
|
||||
- [`flake8-return`](https://pypi.org/project/flake8-return/)
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`isort`](https://pypi.org/project/isort/)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
- [`yesqa`](https://github.com/asottile/yesqa)
|
||||
- [`eradicate`](https://pypi.org/project/eradicate/)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33)
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`pygrep-hooks`](https://github.com/pre-commit/pygrep-hooks) (1/10)
|
||||
- [`autoflake`](https://pypi.org/project/autoflake/) (1/7)
|
||||
- [`pyupgrade`](https://pypi.org/project/pyupgrade/) (16/33)
|
||||
- [`yesqa`](https://github.com/asottile/yesqa)
|
||||
|
||||
Note that, in some cases, Ruff uses different error code prefixes than would be found in the
|
||||
originating Flake8 plugins. For example, Ruff uses `TID252` to represent the `I252` rule from
|
||||
@@ -1031,8 +1060,6 @@ Pylint parity is being tracked in [#689](https://github.com/charliermarsh/ruff/i
|
||||
|
||||
Today, Ruff can be used to replace Flake8 when used with any of the following plugins:
|
||||
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`flake8-2020`](https://pypi.org/project/flake8-2020/)
|
||||
- [`flake8-annotations`](https://pypi.org/project/flake8-annotations/)
|
||||
- [`flake8-bandit`](https://pypi.org/project/flake8-bandit/) (6/40)
|
||||
@@ -1051,6 +1078,8 @@ Today, Ruff can be used to replace Flake8 when used with any of the following pl
|
||||
- [`flake8-super`](https://pypi.org/project/flake8-super/)
|
||||
- [`flake8-tidy-imports`](https://pypi.org/project/flake8-tidy-imports/) (1/3)
|
||||
- [`mccabe`](https://pypi.org/project/mccabe/)
|
||||
- [`pep8-naming`](https://pypi.org/project/pep8-naming/)
|
||||
- [`pydocstyle`](https://pypi.org/project/pydocstyle/)
|
||||
|
||||
Ruff can also replace [`isort`](https://pypi.org/project/isort/),
|
||||
[`yesqa`](https://github.com/asottile/yesqa), [`eradicate`](https://pypi.org/project/eradicate/),
|
||||
|
||||
4
flake8_to_ruff/Cargo.lock
generated
4
flake8_to_ruff/Cargo.lock
generated
@@ -771,7 +771,7 @@ checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flake8_to_ruff"
|
||||
version = "0.0.176"
|
||||
version = "0.0.177"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"clap",
|
||||
@@ -1975,7 +1975,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "ruff"
|
||||
version = "0.0.176"
|
||||
version = "0.0.177"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bincode",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "flake8-to-ruff"
|
||||
version = "0.0.176-dev.0"
|
||||
version = "0.0.177-dev.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -39,3 +39,27 @@ if True:
|
||||
import collections
|
||||
import typing
|
||||
def f(): pass
|
||||
|
||||
|
||||
import os
|
||||
|
||||
# Comment goes here.
|
||||
|
||||
def f():
|
||||
pass
|
||||
|
||||
|
||||
import os
|
||||
|
||||
# Comment goes here.
|
||||
def f():
|
||||
pass
|
||||
|
||||
|
||||
import os
|
||||
|
||||
# Comment goes here.
|
||||
|
||||
# And another.
|
||||
def f():
|
||||
pass
|
||||
|
||||
@@ -39,3 +39,27 @@ if True:
|
||||
import collections
|
||||
import typing
|
||||
def f(): pass
|
||||
|
||||
|
||||
import os
|
||||
|
||||
# Comment goes here.
|
||||
|
||||
def f():
|
||||
pass
|
||||
|
||||
|
||||
import os
|
||||
|
||||
# Comment goes here.
|
||||
def f():
|
||||
pass
|
||||
|
||||
|
||||
import os
|
||||
|
||||
# Comment goes here.
|
||||
|
||||
# And another.
|
||||
def f():
|
||||
pass
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_dev"
|
||||
version = "0.0.176"
|
||||
version = "0.0.177"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "ruff_macros"
|
||||
version = "0.0.176"
|
||||
version = "0.0.177"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
|
||||
@@ -37,7 +37,7 @@ pub fn check_imports(
|
||||
autofix: bool,
|
||||
path: &Path,
|
||||
) -> Vec<Check> {
|
||||
let mut tracker = ImportTracker::new(directives, path);
|
||||
let mut tracker = ImportTracker::new(locator, directives, path);
|
||||
for stmt in python_ast {
|
||||
tracker.visit_stmt(stmt);
|
||||
}
|
||||
|
||||
@@ -2334,7 +2334,7 @@ impl CheckKind {
|
||||
}
|
||||
// flake8-simplify
|
||||
CheckKind::KeyInDict(key, dict) => {
|
||||
format!("Use '{key} in {dict}' instead of '{key} in {dict}.keys()")
|
||||
format!("Use `{key} in {dict}` instead of `{key} in {dict}.keys()`")
|
||||
}
|
||||
// pyupgrade
|
||||
CheckKind::TypeOfPrimitive(primitive) => {
|
||||
|
||||
55
src/isort/helpers.rs
Normal file
55
src/isort/helpers.rs
Normal file
@@ -0,0 +1,55 @@
|
||||
use rustpython_ast::Stmt;
|
||||
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
/// Return `true` if a `Stmt` is preceded by a "comment break"
|
||||
pub fn has_comment_break(stmt: &Stmt, locator: &SourceCodeLocator) -> bool {
|
||||
// Starting from the `Stmt` (`def f(): pass`), we want to detect patterns like
|
||||
// this:
|
||||
//
|
||||
// import os
|
||||
//
|
||||
// # Detached comment.
|
||||
//
|
||||
// def f(): pass
|
||||
|
||||
// This should also be detected:
|
||||
//
|
||||
// import os
|
||||
//
|
||||
// # Detached comment.
|
||||
//
|
||||
// # Direct comment.
|
||||
// def f(): pass
|
||||
|
||||
// But this should not:
|
||||
//
|
||||
// import os
|
||||
//
|
||||
// # Direct comment.
|
||||
// def f(): pass
|
||||
let mut seen_blank = false;
|
||||
for line in locator
|
||||
.slice_source_code_until(&stmt.location)
|
||||
.lines()
|
||||
.rev()
|
||||
{
|
||||
let line = line.trim();
|
||||
if seen_blank {
|
||||
if line.starts_with('#') {
|
||||
return true;
|
||||
} else if !line.is_empty() {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if line.is_empty() {
|
||||
seen_blank = true;
|
||||
} else if line.starts_with('#') || line.starts_with('@') {
|
||||
continue;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -18,6 +18,7 @@ use crate::isort::types::{
|
||||
mod categorize;
|
||||
mod comments;
|
||||
pub mod format;
|
||||
mod helpers;
|
||||
pub mod plugins;
|
||||
pub mod settings;
|
||||
mod sorting;
|
||||
|
||||
@@ -49,32 +49,17 @@ expression: checks
|
||||
column: 0
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 33
|
||||
row: 52
|
||||
column: 0
|
||||
end_location:
|
||||
row: 35
|
||||
row: 54
|
||||
column: 0
|
||||
fix:
|
||||
content: " import collections\n import typing\n\n"
|
||||
content: "import os\n\n\n"
|
||||
location:
|
||||
row: 33
|
||||
row: 52
|
||||
column: 0
|
||||
end_location:
|
||||
row: 35
|
||||
column: 0
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 39
|
||||
column: 0
|
||||
end_location:
|
||||
row: 41
|
||||
column: 0
|
||||
fix:
|
||||
content: " import collections\n import typing\n\n"
|
||||
location:
|
||||
row: 39
|
||||
column: 0
|
||||
end_location:
|
||||
row: 41
|
||||
row: 54
|
||||
column: 0
|
||||
|
||||
|
||||
@@ -47,19 +47,4 @@ expression: checks
|
||||
end_location:
|
||||
row: 16
|
||||
column: 0
|
||||
- kind: UnsortedImports
|
||||
location:
|
||||
row: 33
|
||||
column: 0
|
||||
end_location:
|
||||
row: 35
|
||||
column: 0
|
||||
fix:
|
||||
content: " import collections\n import typing\n\n"
|
||||
location:
|
||||
row: 33
|
||||
column: 0
|
||||
end_location:
|
||||
row: 35
|
||||
column: 0
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ expression: checks
|
||||
row: 6
|
||||
column: 13
|
||||
fix:
|
||||
content: " import os\n import sys\n\n"
|
||||
content: " import os\n import sys\n"
|
||||
location:
|
||||
row: 5
|
||||
column: 0
|
||||
|
||||
@@ -8,20 +8,24 @@ use rustpython_ast::{
|
||||
|
||||
use crate::ast::visitor::Visitor;
|
||||
use crate::directives::IsortDirectives;
|
||||
use crate::isort::helpers;
|
||||
use crate::source_code_locator::SourceCodeLocator;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Trailer {
|
||||
Sibling,
|
||||
ClassDef,
|
||||
FunctionDef,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
#[derive(Debug, Default)]
|
||||
pub struct Block<'a> {
|
||||
pub imports: Vec<&'a Stmt>,
|
||||
pub trailer: Option<Trailer>,
|
||||
}
|
||||
|
||||
pub struct ImportTracker<'a> {
|
||||
locator: &'a SourceCodeLocator<'a>,
|
||||
directives: &'a IsortDirectives,
|
||||
pyi: bool,
|
||||
blocks: Vec<Block<'a>>,
|
||||
@@ -30,8 +34,13 @@ pub struct ImportTracker<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ImportTracker<'a> {
|
||||
pub fn new(directives: &'a IsortDirectives, path: &'a Path) -> Self {
|
||||
pub fn new(
|
||||
locator: &'a SourceCodeLocator<'a>,
|
||||
directives: &'a IsortDirectives,
|
||||
path: &'a Path,
|
||||
) -> Self {
|
||||
Self {
|
||||
locator,
|
||||
directives,
|
||||
pyi: path.extension().map_or(false, |ext| ext == "pyi"),
|
||||
blocks: vec![Block::default()],
|
||||
@@ -46,31 +55,46 @@ impl<'a> ImportTracker<'a> {
|
||||
}
|
||||
|
||||
fn trailer_for(&self, stmt: &'a Stmt) -> Option<Trailer> {
|
||||
if self.pyi {
|
||||
// Black treats interface files differently, limiting to one newline
|
||||
// (`Trailing::Sibling`), and avoiding inserting any newlines in nested function
|
||||
// blocks.
|
||||
if self.nested
|
||||
&& matches!(
|
||||
stmt.node,
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. }
|
||||
)
|
||||
{
|
||||
None
|
||||
} else {
|
||||
Some(Trailer::Sibling)
|
||||
}
|
||||
} else if self.nested {
|
||||
Some(Trailer::Sibling)
|
||||
} else {
|
||||
Some(match &stmt.node {
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
Trailer::FunctionDef
|
||||
}
|
||||
StmtKind::ClassDef { .. } => Trailer::ClassDef,
|
||||
_ => Trailer::Sibling,
|
||||
})
|
||||
// No need to compute trailers if we won't be finalizing anything.
|
||||
let index = self.blocks.len() - 1;
|
||||
if self.blocks[index].imports.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Similar to isort, avoid enforcing any newline behaviors in nested blocks.
|
||||
if self.nested {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(if self.pyi {
|
||||
// Black treats interface files differently, limiting to one newline
|
||||
// (`Trailing::Sibling`).
|
||||
Trailer::Sibling
|
||||
} else {
|
||||
// If the import block is followed by a class or function, we want to enforce
|
||||
// two blank lines. The exception: if, between the import and the class or
|
||||
// function, we have at least one commented line, followed by at
|
||||
// least one blank line. In that case, we treat it as a regular
|
||||
// sibling (i.e., as if the comment is the next statement, as
|
||||
// opposed to the class or function).
|
||||
match &stmt.node {
|
||||
StmtKind::FunctionDef { .. } | StmtKind::AsyncFunctionDef { .. } => {
|
||||
if helpers::has_comment_break(stmt, self.locator) {
|
||||
Trailer::Sibling
|
||||
} else {
|
||||
Trailer::FunctionDef
|
||||
}
|
||||
}
|
||||
StmtKind::ClassDef { .. } => {
|
||||
if helpers::has_comment_break(stmt, self.locator) {
|
||||
Trailer::Sibling
|
||||
} else {
|
||||
Trailer::ClassDef
|
||||
}
|
||||
}
|
||||
_ => Trailer::Sibling,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn finalize(&mut self, trailer: Option<Trailer>) {
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct Options {
|
||||
`RUF002`, and `RUF003`.
|
||||
"#,
|
||||
default = r#"[]"#,
|
||||
value_type = "Vec<a test>",
|
||||
value_type = "Vec<char>",
|
||||
example = r#"
|
||||
# Allow minus-sign (U+2212), greek-small-letter-rho (U+03C1), and the asterisk-operator (U+2217),
|
||||
# which could be confused for "-", "p", and "*", respectively.
|
||||
|
||||
@@ -31,6 +31,12 @@ impl<'a> SourceCodeLocator<'a> {
|
||||
Cow::from(rope.slice(offset..))
|
||||
}
|
||||
|
||||
pub fn slice_source_code_until(&self, location: &Location) -> Cow<'_, str> {
|
||||
let rope = self.get_or_init_rope();
|
||||
let offset = rope.line_to_char(location.row() - 1) + location.column();
|
||||
Cow::from(rope.slice(..offset))
|
||||
}
|
||||
|
||||
pub fn slice_source_code_range(&self, range: &Range) -> Cow<'_, str> {
|
||||
let rope = self.get_or_init_rope();
|
||||
let start = rope.line_to_char(range.location.row() - 1) + range.location.column();
|
||||
|
||||
Reference in New Issue
Block a user