Skip to content

Commit a8f26d3

Browse files
dmmulroypmwals09ceceppa
authored
(release)!: Version 2.0.0 (dmmulroy#35)
* Adds `watch mode` support along with `auto_focus_qflist` and auto_start_watch_mode` as configuration options * Adds `pretty ts errors mode` along with `pretty_errors` configuration option --------- Co-authored-by: Dillon Mulroy <dillon.mulroy@gmail.com> Co-authored-by: pmwals09 <pmwals09@gmail.com> Co-authored-by: Alessandro Senese <senesealessandro@gmail.com>
1 parent 26d3c90 commit a8f26d3

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

72 files changed

+580
-12
lines changed

README.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# tsc.nvim
2+
23
<img width="569" alt="image" src="https://user-images.githubusercontent.com/2755722/233876554-efb9cfe6-c038-46c8-a7cb-b7a4aa9eac5b.png">
34

45
This Neovim plugin provides an asynchronous interface to run project-wide TypeScript type-checking using the TypeScript compiler (`tsc`). It displays the type-checking results in a quickfix list and provides visual notifications about the progress and completion of type-checking.
@@ -65,6 +66,8 @@ require('tsc').setup()
6566

6667
To run TypeScript type-checking, execute the `:TSC` command in Neovim. The plugin will display a progress notification while the type-checking is in progress. When the type-checking is complete, it will show a notification with the results and open a quickfix list if there are any errors.
6768

69+
If `watch` mode is enabled, tsc.nvim will automatically run in the background every time you save in a typescript or tsx file and report the results back to you. In addition, if `auto_start_watch_mode` is enabled, the `:TSC` command will be executed on your behalf when you enter a typescript or tsx files.
70+
6871
## Configuration
6972

7073
By default, the plugin uses the default `tsc` command with the `--noEmit` flag to avoid generating output files during type-checking. It also emulates the default tsc behavior of performing a backward search from the current directory for a `tsconfig` file. The flags option can accept both a string and a table. Here's the default configuration:
@@ -73,28 +76,32 @@ By default, the plugin uses the default `tsc` command with the `--noEmit` flag t
7376
{
7477
auto_open_qflist = true,
7578
auto_close_qflist = false,
79+
auto_focus_qflist = false,
80+
auto_start_watch_mode = false,
7681
bin_path = utils.find_tsc_bin(),
7782
enable_progress_notifications = true,
7883
flags = {
7984
noEmit = true,
8085
project = function()
8186
return utils.find_nearest_tsconfig()
8287
end,
88+
watch = false,
8389
},
8490
hide_progress_notifications_from_history = true,
8591
spinner = { "", "", "", "", "", "", "", "" },
92+
pretty_errors = true,
8693
}
8794
```
8895

89-
With this configuration, you can use keys for flag names and their corresponding values to enable/disable the flag (in the case of `noEmit = true`) or provide a function (as in the case of the `project`). This makes the configuration more explicit and easier to read. Additionally, the flags option is backwards compatible and can accept a string value if you prefer a simpler configuration:
96+
With this configuration, you can use keys for flag names and their corresponding values to enable/disable the flag (in the case of `noEmit = true`), provide a function (as in the case of the `project`) or enable watch mode. This makes the configuration more explicit and easier to read. Additionally, the flags option is backwards compatible and can accept a string value if you prefer a simpler configuration:
9097

9198
```lua
9299
flags = "--noEmit",
93100
```
94101

95102
## FAQs
96103

97-
### I'm using `nvim-notify` and being spammed by progress notifcations, what's going on?
104+
### I'm using `nvim-notify` and being spammed by progress notifications, what's going on?
98105

99106
It's likely that the overwritten default `vim.notify` function isn't returning `nvim-notify`'s notification record, which is used to replace the existing notification. Make sure that you're nvim-notify configuration looks something like this:
100107

lua/tsc/better-messages-test.lua

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
local bm = require("tsc.better-messages")
2+
3+
describe("Does the basics", function()
4+
it("Replaces the original text with the correct md file text", function()
5+
local original_message = "TS7061: A mapped type may not declare properties or methods"
6+
local expected_message = "TS7061: You're trying to create a mapped type with both static and dynamic properties."
7+
assert.equals(expected_message, bm.translate(original_message))
8+
end)
9+
end)
10+
11+
describe("Handles slots", function()
12+
it("Handles a message with one slot", function()
13+
local original_message = "TS2604: JSX element type 'BadComponent' does not have any construct or call signatures."
14+
local expected_message = "TS2604: 'BadComponent' cannot be used as a JSX component because it isn't a function."
15+
assert.equals(expected_message, bm.translate(original_message))
16+
end)
17+
it("Handles a message with multiple slots", function()
18+
local original_message = "TS2551: Property 'foo' does not exist on type 'bar'. Did you mean 'baz'?"
19+
local expected_message =
20+
"TS2551: You're trying to access 'foo' on an object that doesn't contain it. Did you mean 'baz'?"
21+
assert.equals(expected_message, bm.translate(original_message))
22+
end)
23+
end)
24+
25+
describe("Handles links", function()
26+
it("Removes a 'read more' link", function()
27+
local original_message = "TS7006: Parameter 'foo' implicitly has an 'bar' type."
28+
-- check 7006.md to see original pretty message with link
29+
local expected_message =
30+
"TS7006: I don't know what type 'foo' is supposed to be, so I've defaulted it to 'bar'. Your `tsconfig.json` file says I should throw an error here."
31+
assert.equals(expected_message, bm.translate(original_message))
32+
end)
33+
it("Removes a 'this article' link and sentence", function()
34+
local original_message =
35+
"TS7053: Element implicitly has an 'any' type because expression of type 'foo' can't be used to index type 'bar'."
36+
-- check 7053.md to see original pretty message with link
37+
local expected_message = "TS7053: You can't use 'foo' to index into 'bar'."
38+
assert.equals(expected_message, bm.translate(original_message))
39+
end)
40+
it("Handles removes other links href, keeps link text", function()
41+
local original_message =
42+
"TS1268: An index signature parameter type must be 'string', 'number', 'symbol', or a template literal type."
43+
-- check 1268.md to see original pretty message with link
44+
local expected_message =
45+
"TS1268: Objects in TypeScript (and JavaScript!) can only have strings, numbers or symbols as keys. Template literal types are a way of constructing strings."
46+
assert.equals(expected_message, bm.translate(original_message))
47+
end)
48+
end)

lua/tsc/better-messages.lua

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
-- Module M for better error message handling
2+
local M = {}
3+
4+
-- Regex pattern for capturing numbered parameters like {0}, {1}, etc.
5+
local parameter_regex = "({%d})"
6+
7+
-- Extracts parameter placeholders from a given error template.
8+
-- @param error_template string: The template string containing parameter placeholders.
9+
-- @return table: A list of all parameter placeholders found in the template.
10+
local function get_params(error_template)
11+
local params = {}
12+
for param in error_template:gmatch(parameter_regex) do
13+
table.insert(params, param)
14+
end
15+
return params
16+
end
17+
18+
-- Extracts quoted strings from a given message.
19+
-- @param message string: The message string containing quoted parts.
20+
-- @return table: A list of all quoted strings found in the message.
21+
local function get_matches(message)
22+
local matches = {}
23+
24+
for match in string.gmatch(message, "'(.-)'") do
25+
table.insert(matches, match)
26+
end
27+
28+
return matches
29+
end
30+
31+
-- Constructs a better error message by replacing placeholders in the template with actual values.
32+
-- @param error_msg string: The original error message.
33+
-- @param error_template string: The error template with placeholders.
34+
-- @param better_error_template string: The improved error message template.
35+
-- @return string: The improved error message, or original if replacement isn't possible.
36+
local function better_error_message(error_msg, error_template, better_error_template)
37+
local matches = get_matches(error_msg)
38+
local params = get_params(error_template)
39+
40+
if #params ~= #matches then
41+
return error_msg
42+
end
43+
44+
local better_error = better_error_template
45+
46+
for i = 1, #params do
47+
better_error = better_error:gsub(params[i], matches[i])
48+
end
49+
50+
return better_error
51+
end
52+
53+
-- Retrieves a markdown file associated with a specific error number.
54+
-- @param error_num string: The error number identifier.
55+
-- @return file* | nil: The file pointer to the markdown file, if exists.
56+
local function get_error_markdown_file(error_num)
57+
local filename = error_num .. ".md"
58+
local plugin_path = vim.fn.fnamemodify(debug.getinfo(1).source:sub(2), ":p:h")
59+
return io.open(plugin_path .. "/better-messages/" .. filename, "r")
60+
end
61+
62+
-- Parses a markdown file to extract 'original' and 'better' content.
63+
-- @param markdown file*: The markdown file pointer.
64+
-- @return table: A table with 'original' and 'better' keys containing extracted contents.
65+
local function parse_md(markdown)
66+
local contents = markdown:read("*all")
67+
markdown:close()
68+
69+
-- First, remove the leading and trailing '---'
70+
local trimmedMarkdown = contents:gsub("^%-%-%-%s*", ""):gsub("%s*%-%-%-$", "")
71+
72+
-- Split the remaining content at the '---' separator
73+
local originalContent, betterContent = trimmedMarkdown:match("^(.-)%s*%-%-%-%s*(.-)$")
74+
75+
-- Trim whitespace from both contents
76+
originalContent = originalContent:gsub('^original:%s*"(.-)"%s*$', "%1")
77+
betterContent = betterContent:gsub("^%s*(.-)%s*$", "%1")
78+
79+
-- Return the table with the extracted contents
80+
return {
81+
original = originalContent,
82+
better = betterContent,
83+
}
84+
end
85+
86+
-- Attempt to translate a given compiler message into a better one.
87+
-- @param message string: The original compiler message to parse.
88+
-- @return string: The improved or original error message.
89+
M.translate = function(message)
90+
local error_num, original_message = message:match("^.*TS(%d+):%s(.*)")
91+
local improved_text_file = get_error_markdown_file(error_num)
92+
if improved_text_file == nil then
93+
return message
94+
end
95+
96+
local parsed = parse_md(improved_text_file)
97+
98+
local params = get_params(parsed["original"])
99+
100+
if #params == 0 then
101+
return "TS" .. error_num .. ": " .. parsed.body
102+
end
103+
104+
local better_error = better_error_message(original_message, parsed["original"], parsed["better"])
105+
106+
return "TS" .. error_num .. ": " .. better_error
107+
end
108+
109+
-- Returning the module M.
110+
return M

lua/tsc/better-messages/1002.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
original: 'Unterminated string literal.'
3+
---
4+
5+
You've started a string (via a single or double quote) but haven't ended it.

lua/tsc/better-messages/1003.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
original: 'Identifier expected.'
3+
---
4+
5+
I was expecting a name but none was provided.

lua/tsc/better-messages/1006.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
original: 'A file cannot have a reference to itself.'
3+
---
4+
5+
You've got a triple-slash reference inside a file that's referencing itself.

lua/tsc/better-messages/1009.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
original: 'Trailing comma not allowed.'
3+
---
4+
5+
You've added a trailing comma when you're not supposed to add it.

lua/tsc/better-messages/1014.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
original: 'A rest parameter must be last in a parameter list.'
3+
---
4+
5+
A parameter in a function that starts with `...` must be the last one in the list.

lua/tsc/better-messages/1015.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
original: 'Parameter cannot have question mark and initializer.'
3+
---
4+
5+
You can use a question mark or an default value, but not both at once.

lua/tsc/better-messages/1091.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
original: "Only a single variable declaration is allowed in a 'for...in' statement."
3+
---
4+
5+
You can only create a single variable in a 'for...in' statement

0 commit comments

Comments
 (0)