Comments (6)
Ok, I have a pull request ready for reviewing. Here's my final array of tests in case anyone in the future tries to fix this:
var fun = () => {}
var x
// Things are correctly indented if their HERE aligns with this one:
//-----------------------HERE
const a = 0 >
1 //-----------------HERE
const b = 0 < 1 && 2 >
3 //-----------------HERE
const c = 0 < 1 || 2 >
3 //-----------------HERE
const d = 0 < 1 ? 2 : 3 + 4 >
5 ? 6 : 7 //---------HERE
const d2 = 0 < 1 ? 2 : 3 * 4 >
5 ? 6 : 7 //---------HERE
const d3 = 0 < 1 ? 2 : 3 / 4 >
5 ? 6 : 7 //---------HERE
const d4 = 0 < 1 ? 2 : 3 % 4 >
5 ? 6 : 7 //---------HERE
const e = [
1 <
2,
3 >
4, //------------HERE
]
const f = [] as Array<number>
fun() //-----------------HERE
x = 1 as number >
2 //-----------------HERE
var g = 1 as number >
2 //-----------------HERE
type dummy1 = number
type dummy2 = dummy1
g = 1 as number < 2 , 2 as number >
2 //-----------------HERE
// Fortunately, TypeScript disallows:
//
// x = 0 as a < b , c >
// f()
//
// when a is not generic (and also is not a built-in type)
type A = Array<number>
fun() //-----------------HERE
type B = Array<Array<number>>
fun() //-----------------HERE
type One<A> = number
type Two<A, B> = number
type C = Two <
number , number[
]
>
fun() //-----------------HERE
type D = Array<number[]>
fun() //-----------------HERE
type Nested = Two < One < number >
, number > //--------HERE
// Unfortunately, something like `foo < bar , baz > \n f(x)` can look syntactically like:
// - a type `foo` with two parameters, then `f(x)` on the next line
// - the sequencing of two comparisons, `foo < bar`, and `baz > f(x)`
var foo
var bar
var beep
type foo<A,B> = {}
type bar = {}
// So the two following examples are hard to distinguish!
beep = foo <
bar , bar >
fun() //-------------HERE
type beep = foo <
bar , bar >
fun() //-----------------HERE
// And we cannot easily rely on the keyword `type` being there, because of those:
type baz = bar
beep = foo <
bar , bar >
fun() //-------------HERE
type bazz
= bar
beep = foo <
bar , bar >
fun() //-------------HERE
type bazzz<A>
= bar //-------------HERE
beep = foo <
bar , bar >
fun() //-------------HERE
type Num<T> = number
const g1 = 1 as Num<A> >
2 //-----------------HERE
They all align correctly after my patch!
from typescript.el.
I can reproduce the problem here. A PR to fix the issue is welcome.
from typescript.el.
Well, I am currently looking into it, but it turns out to be very complicated!
The crux of the problem is to figure out whether a >
symbol at the end of a line stands for the greater-than sign, or for the end tag of a <type argument>
. While this still sounds like a simple problem, the fact that TypeScript accomodates for most of the JavaScript syntax makes this harder than it seems.
Consider:
type a = b < c , d >
f()
a = b < c , d >
f()
The former is a valid type declaration, given that b
accepts two type arguments, followed by a function call.
The latter is a valid assignment, first computing b < c
, throwing away the result, then computing d > f()
and saving this result.
One of my early guesses was to search backwards for the first =
.
From there, you can search backwards for either =
or type
.
If you find =
, that was a greater-than sign.
If you find type
, that was a type argument.
While this solves the problems mentioned, it does not work for the following examples:
const f = [] as Array<number>
f()
So it seems a regexp-based solution might be compromised! At the very least, we'd need to search backwards for the matching <
, which involves counting the opening/closing <
and >
we cross. But even if we had that, we still need to figure out whether we are in a type declaration or not.
I'm still trying to build a decent approximation for the correct behavior, but it'll be hard to be sure it works.
In the meantime, here's a large set of examples to see if a given attempt is misbehaving:
var fun = () => {}
// Things are correctly indented if their HERE aligns with this one:
//-----------------------HERE
const a = 0 >
1 //-----------------HERE
const b = 0 < 1 && 2 >
3 //-----------------HERE
const c = 0 < 1 || 2 >
3 //-----------------HERE
const d = 0 < 1 ? 2 : 3 + 4 >
5 ? 6 : 7 //---------HERE
const d2 = 0 < 1 ? 2 : 3 * 4 >
5 ? 6 : 7 //---------HERE
const d3 = 0 < 1 ? 2 : 3 / 4 >
5 ? 6 : 7 //---------HERE
const d4 = 0 < 1 ? 2 : 3 % 4 >
5 ? 6 : 7 //---------HERE
const e = [
1 <
2,
3 >
4, //------------HERE
]
const f = [] as Array<number>
fun() //-----------------HERE
const g = 1 as number >
2 //-----------------HERE
type A = Array<number>
fun() //-----------------HERE
type B = Array<Array<number>>
fun() //-----------------HERE
type Two<A, B> = number
type C = Two <
number , number[
]
>
fun() //-----------------HERE
type D = Array<number[]>
fun() //-----------------HERE
// Unfortunately, something like `foo < bar , baz > \n f(x)` can look syntactically like:
// - a type `foo` with two parameters, then `f(x)` on the next line
// - the sequencing of two comparisons, `foo < bar`, and `baz > f(x)`
var foo
var bar
var beep
type foo<A,B> = {}
type bar = {}
// So the two following examples are hard to distinguish!
beep = foo <
bar , bar >
fun() //-------------HERE
type beep = foo <
bar , bar >
fun() //-----------------HERE
// And we cannot easily rely on the keyword `type` being there, because of those:
type baz = bar
beep = foo <
bar , bar >
fun() //-------------HERE
type bazz
= bar
beep = foo <
bar , bar >
fun() //-------------HERE
type bazzz<A>
= bar //-------------HERE
beep = foo <
bar , bar >
fun() //-------------HERE
A good implementation should let all those HERE
where they are. The current implementation gives:
var fun = () => {}
// Things are correctly indented if their HERE aligns with this one:
//-----------------------HERE
const a = 0 >
1 //-----------------HERE
const b = 0 < 1 && 2 >
3 //-----------------HERE
const c = 0 < 1 || 2 >
3 //-----------------HERE
const d = 0 < 1 ? 2 : 3 + 4 >
5 ? 6 : 7 //---------HERE
const d2 = 0 < 1 ? 2 : 3 * 4 >
5 ? 6 : 7 //---------HERE
const d3 = 0 < 1 ? 2 : 3 / 4 >
5 ? 6 : 7 //---------HERE
const d4 = 0 < 1 ? 2 : 3 % 4 >
5 ? 6 : 7 //---------HERE
const e = [
1 <
2,
3 >
4, //------------HERE
]
const f = [] as Array<number>
fun() //-----------------HERE (WRONG)
const g = 1 as number >
2 //-----------------HERE
type A = Array<number>
fun() //-----------------HERE (WRONG)
type B = Array<Array<number>>
fun() //-----------------HERE (WRONG)
type Two<A, B> = number
type C = Two <
number , number[
]
>
fun() //-----------------HERE (WRONG)
type D = Array<number[]>
fun() //-----------------HERE (WRONG)
// Unfortunately, something like `foo < bar , baz > \n f(x)` can look syntactically like:
// - a type `foo` with two parameters, then `f(x)` on the next line
// - the sequencing of two comparisons, `foo < bar`, and `baz > f(x)`
var foo
var bar
var beep
type foo<A,B> = {}
type bar = {}
// So the two following examples are hard to distinguish!
beep = foo <
bar , bar >
fun() //-------------HERE
type beep = foo <
bar , bar >
fun() //-----------------HERE (WRONG)
// And we cannot easily rely on the keyword `type` being there, because of those:
type baz = bar
beep = foo <
bar , bar >
fun() //-------------HERE
type bazz
= bar
beep = foo <
bar , bar >
fun() //-------------HERE
type bazzz<A>
= bar //-------------HERE
beep = foo <
bar , bar >
fun() //-------------HERE
from typescript.el.
Yes, the traditional way of doing indentation in Emacs strains under the complexity of TypeScript's syntax. The strategy is to look at the first non-blank character of the line and then, starting from that point, got back in the buffer to figure out what context we're in. For languages with simple syntax, that's fairly easy to do, and results in fairly simple code. By the time you get to languages of TS' complexity, it is much harder to pull off, and results in complicated Elisp logic.
For some languages, the complexity may be such that switching to a system that performs indentation on the basis of an abstract syntax tree (AST) may be the best way, or perhaps the only way, to get valid and consistent results in all cases. If you ever use tide-format
, that's essentially how it works behind the scenes: tide sends the buffer to tsserver
, and tsserver
produces an AST which it uses to indent the file, and sends the result back to tide. (I've omitted from this description details not important to this discussion.)
Indenting code that does not use semicolons to delimit statements may be the proverbial "straw that breaks the camel's back" and requires an AST to pull off correctly.
from typescript.el.
Indenting code that does not use semicolons to delimit statements may be the proverbial "straw that breaks the camel's back" and requires an AST to pull off correctly.
Agreed.
But as we all know, that would be a massive undertaking. For me personally, to justify such an effort I would have to be able to get some pretty massive benefits in returns for that effort.
And as someone using "traditional" TypeScript-syntax with semicolon at the end of my lines, this is definitely not going to be the one who pushes me into implementing this.
If someone else feels like doing it, I'll be happy to review though.
from typescript.el.
I think I came up with a somewhat good heuristic that preserves all good existing behaviors, fixes many of my issues, and only possibly breaks on really weird inputs. I will try and post a PR later today, with an explanation of the heuristic, and a lot of examples of what works and what does not.
Requiring AST, or even worse, type information, for the indentation would indeed be annoying to write, and to maintain! At that point, one would probably just want some language-server type and use tsc
itself. I also don't have a personal justification for putting this much effort into solving my small annoyance! :-)
from typescript.el.
Related Issues (20)
- typescript-beginning-of-defun misses ES2015 forms HOT 2
- can't seem to indent props in JSX expression HOT 1
- Consider migrating CI to something not Travis HOT 5
- Symbol’s value as variable is void: compilation-error-regexp-alist-alist HOT 4
- Indentation hangs in `typescript--backward-to-parameter-list` when previous function has unbalanced parens
- typescript-mode hangs if I enter a newline after a dot character.
- File mode specification error: (void-function -compose) HOT 3
- typescript new keyword 'override' highlight HOT 2
- M-j in /** foo */ comment blocks is broken. Reasonbly easy to fix HOT 3
- Bump Version Tag HOT 8
- const, let formatting with multiple lines HOT 1
- Missing debugger keyword HOT 1
- Freeze on indenting dot after comma HOT 1
- `typescript-mode` is failing to download HOT 4
- Incorrect highlighting of keywords on latest commit HOT 5
- Adding mechanisms in place not to collide with in-tree typescript-mode HOT 7
- typescript 4.9 - satisfies operator HOT 1
- No option to achieve desired formatting HOT 1
- typescript--backward-to-parameter-list can cause multi-second delays in large files HOT 4
- Diagnostics don't show until file is changed HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from typescript.el.