Skip to content

Commit ffaf8d5

Browse files
author
Nathan Hawes
committed
[syntax-coloring] Rework the syntax map to use offset+length rather than start/end line+column and simplify the delta logic
This patch changes the syntax map data structure it uses to be offset based rather than line/col based in order to avoid calling getLineAndColumn for the start and end offset of every token. This removes the 30% of time spent in getLineAndColumn for this request in large files (rdar://problem/28965123). The logic for returning the affected range and the token ranges to highlight following an edit also made several assumptions that no longer hold. This patch changes it to compare the syntax maps from before and after the edit, find the first mismtaching tokens from the start and end of the syntax maps and return the tokens in that range (adjusted to line boundaries). This fixes syntax highlighting issues with interpolated multi-line strings (rdar://problem/32148117) and block comments. With the above changes the per-keystroke time spent for syntax highlighting (with sematic info disabled) dropped from ~80ms to just under 50ms for a 12KLOC file.
1 parent 4834dcc commit ffaf8d5

10 files changed

+623
-221
lines changed

test/SourceKit/DocumentStructure/mark_edit.swift.response

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,6 @@
1313
]
1414
}
1515
{
16-
key.offset: 0,
17-
key.length: 3,
1816
key.diagnostic_stage: source.diagnostic.stage.swift.parse,
1917
key.substructure: [
2018
{
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
2+
let x = /*
3+
4+
*/ 2
5+
func foo() {}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
2+
/// A comment
3+
/// - Note: important things
4+
let x = 42
5+
6+
struct Point {
7+
let x: Int = x
8+
let y: Int = x
9+
10+
func mag2() {
11+
return x*x + y*y;
12+
}
13+
}
14+
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
let x = 421
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
// RUN: %sourcekitd-test -req=open -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift -pos=4:2 -replace=" " -length=1 == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift -pos=4:2 -replace="/" -length=1 == -req=edit -print-raw-response %S/Inputs/syntaxmap-edit-block-comment.swift -pos=1:1 -replace="//" -length=2 | %sed_clean > %t.response
2+
// RUN: %FileCheck -input-file=%t.response %s
3+
4+
// Initial state
5+
6+
// CHECK: {{^}}{
7+
// CHECK-NEXT: key.offset: 0,
8+
// CHECK-NEXT: key.length: 34,
9+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
10+
// CHECK-NEXT: key.syntaxmap: [
11+
// CHECK-NEXT: {
12+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
13+
// CHECK-NEXT: key.offset: 3,
14+
// CHECK-NEXT: key.length: 3
15+
// CHECK-NEXT: },
16+
// CHECK-NEXT: {
17+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
18+
// CHECK-NEXT: key.offset: 7,
19+
// CHECK-NEXT: key.length: 1
20+
// CHECK-NEXT: },
21+
// CHECK-NEXT: {
22+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
23+
// CHECK-NEXT: key.offset: 11,
24+
// CHECK-NEXT: key.length: 6
25+
// CHECK-NEXT: },
26+
// CHECK-NEXT: {
27+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.number,
28+
// CHECK-NEXT: key.offset: 18,
29+
// CHECK-NEXT: key.length: 1
30+
// CHECK-NEXT: },
31+
// CHECK-NEXT: {
32+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
33+
// CHECK-NEXT: key.offset: 20,
34+
// CHECK-NEXT: key.length: 4
35+
// CHECK-NEXT: },
36+
// CHECK-NEXT: {
37+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
38+
// CHECK-NEXT: key.offset: 25,
39+
// CHECK-NEXT: key.length: 3
40+
// CHECK-NEXT: }
41+
// CHECK-NEXT: ],
42+
43+
// After removing the '/' from the comment close
44+
45+
// CHECK: {{^}}{
46+
// CHECK-NEXT: key.offset: 3,
47+
// CHECK-NEXT: key.length: 31,
48+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
49+
// CHECK-NEXT: key.syntaxmap: [
50+
// CHECK-NEXT: {
51+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
52+
// CHECK-NEXT: key.offset: 3,
53+
// CHECK-NEXT: key.length: 3
54+
// CHECK-NEXT: },
55+
// CHECK-NEXT: {
56+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
57+
// CHECK-NEXT: key.offset: 7,
58+
// CHECK-NEXT: key.length: 1
59+
// CHECK-NEXT: },
60+
// CHECK-NEXT: {
61+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
62+
// CHECK-NEXT: key.offset: 11,
63+
// CHECK-NEXT: key.length: 23
64+
// CHECK-NEXT: }
65+
// CHECK-NEXT: ],
66+
67+
// After adding the '/' back to the comment close
68+
69+
// CHECK: {{^}}{
70+
// CHECK-NEXT: key.offset: 3,
71+
// CHECK-NEXT: key.length: 31,
72+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
73+
// CHECK-NEXT: key.syntaxmap: [
74+
// CHECK-NEXT: {
75+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
76+
// CHECK-NEXT: key.offset: 3,
77+
// CHECK-NEXT: key.length: 3
78+
// CHECK-NEXT: },
79+
// CHECK-NEXT: {
80+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
81+
// CHECK-NEXT: key.offset: 7,
82+
// CHECK-NEXT: key.length: 1
83+
// CHECK-NEXT: },
84+
// CHECK-NEXT: {
85+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
86+
// CHECK-NEXT: key.offset: 11,
87+
// CHECK-NEXT: key.length: 6
88+
// CHECK-NEXT: },
89+
// CHECK-NEXT: {
90+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.number,
91+
// CHECK-NEXT: key.offset: 18,
92+
// CHECK-NEXT: key.length: 1
93+
// CHECK-NEXT: },
94+
// CHECK-NEXT: {
95+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.keyword,
96+
// CHECK-NEXT: key.offset: 20,
97+
// CHECK-NEXT: key.length: 4
98+
// CHECK-NEXT: },
99+
// CHECK-NEXT: {
100+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.identifier,
101+
// CHECK-NEXT: key.offset: 25,
102+
// CHECK-NEXT: key.length: 3
103+
// CHECK-NEXT: }
104+
// CHECK-NEXT: ],
105+
106+
// after adding a single-line comment on the line above
107+
108+
// CHECK: {{^}}{
109+
// CHECK-NEXT: key.offset: 0,
110+
// CHECK-NEXT: key.length: 3,
111+
// CHECK-NEXT: key.diagnostic_stage: source.diagnostic.stage.swift.parse,
112+
// CHECK-NEXT: key.syntaxmap: [
113+
// CHECK-NEXT: {
114+
// CHECK-NEXT: key.kind: source.lang.swift.syntaxtype.comment,
115+
// CHECK-NEXT: key.offset: 0,
116+
// CHECK-NEXT: key.length: 3
117+
// CHECK-NEXT: }
118+
// CHECK-NEXT: ],

test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
}
3030
{
3131
key.offset: 1,
32-
key.length: 36,
32+
key.length: 34,
3333
key.diagnostic_stage: source.diagnostic.stage.swift.parse,
3434
key.syntaxmap: [
3535
{

test/SourceKit/SyntaxMapData/syntaxmap-edit-del.swift.response2

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,6 @@
2828
]
2929
}
3030
{
31-
key.offset: 36,
32-
key.length: 0,
3331
key.diagnostic_stage: source.diagnostic.stage.swift.parse,
3432
key.syntaxmap: [
3533
]

0 commit comments

Comments
 (0)