Skip to content

Commit 203bbb1

Browse files
committed
Update style, add article
1 parent 6c9b9af commit 203bbb1

File tree

2 files changed

+82
-7
lines changed

2 files changed

+82
-7
lines changed
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
---
2+
title: Analyzing typescript imports with neovim and treesitter
3+
---
4+
5+
Ever wanted to do automate a task that requires looking at typescript imports and doing something boring and repetitive
6+
with them?
7+
Neovim offers tooling to do that, and more, out of the box!
8+
9+
There's many use-cases for scanning over parts of the code and automating tasks, but chances are you can imagine at
10+
least a few that would improve your workflow when writing code.
11+
That recently happened to me, when I couldn't find a way to copy imports automatically when copying typescript code
12+
between files (but that's a story for next time).
13+
14+
Neovim uses treesitter for code highlighting, but that's not all it can be used for. Treesitter builds an AST over code
15+
tokens, but instead of using that for highlighting, we'll extract some information from it.
16+
17+
18+
We can inspect the tree by running:
19+
20+
```text
21+
:InspectTree
22+
```
23+
24+
The output depends on the filetype and its contents. For a typescript file, the beginning might look something like:
25+
26+
```ansi
27+
(program ; [0, 0] - [342, 0]
28+
(import_statement ; [0, 0] - [0, 77]
29+
(import_clause ; [0, 7] - [0, 27]
30+
(named_imports ; [0, 7] - [0, 27]
31+
(import_specifier ; [0, 9] - [0, 25]
32+
name: (identifier)))) ; [0, 9] - [0, 25]
33+
34+
[more nodes...]
35+
```
36+
37+
Which is exactly what we were looking for. It breaks down parts of the code into smaller and smaller tokens and gives
38+
us the row/column ranges for each token.
39+
40+
Nodes can then be queried using the treesitter query language. After running the query we can back the matched nodes.
41+
42+
43+
```lua
44+
local import_query = "((import_statement) @node)"
45+
local bufnr = 0
46+
47+
local language_tree = vim.treesitter.get_parser(bufnr)
48+
local root = language_tree:trees()[1]:root()
49+
50+
local query = vim.treesitter.query.parse(language_tree:lang(), import_query)
51+
if query == nil then return end
52+
53+
for _, node in query:iter_captures(root, bufnr) do
54+
-- do something with node
55+
end
56+
```
57+
58+
And that's all there's to it! As long as we know the structure of the AST, parsing out the info we need is pretty
59+
trivial. Do mind that there are several possible ways to import things in typescript:
60+
- named (`import {foo} from "bar"`)
61+
- named with alias (`import {foo as moo} from "bar"`)
62+
- namespace (`import * as foo from "bar"`)
63+
- default (`import foo from "bar"`)
64+
- ambient (`import "bar"`)
65+
66+
We need to write code defensively to breaking our code logic throwing an error.
67+
68+
There's also a handy utility method to get the text at a node range:
69+
70+
```lua
71+
if node:type() == "named_imports" then
72+
local text = vim.treesitter.get_node_text(node, bufnr)
73+
74+
local first_child = node.child(0)
75+
local child_text = vim.treesitter.get_node_text(first_child, bufnr)
76+
end
77+
```
78+
79+
Treesitter knows how to deal with most languages, so there's virtually no limit to what we can use scripting for (i.e.
80+
we could find all variable declarations in a python file, all global function names in a go file, etc.).
81+
82+
Next time we'll write a small plugin that uses treesitter info to do something useful!

src/style/global.style.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import "~/style/fontPreamble.style";
1212
import "~/style/layerPreamble.style";
1313
import "~/style/reset.style";
1414
import "~/style/tw.style";
15-
// import "~/style/styleLoadOrder";
1615
import { css } from "@linaria/core";
1716
import { baseText, smallTextHeight } from "~/style/textStylesTS";
1817

@@ -48,9 +47,6 @@ export const globals = css`
4847
* {
4948
box-sizing: border-box;
5049
}
51-
/* #root * { */
52-
/* position: relative; */
53-
/* } */
5450
5551
body {
5652
min-height: 100vh;
@@ -212,9 +208,6 @@ export const globals = css`
212208
width: 100%;
213209
min-height: ${smallTextHeight}px;
214210
white-space: pre-wrap !important;
215-
&:last-child {
216-
display: none;
217-
}
218211
}
219212
.language-id {
220213
display: none;

0 commit comments

Comments
 (0)