2
2
3
3
import com .google .gson .JsonObject ;
4
4
import com .google .gson .JsonParser ;
5
+ import com .google .gson .JsonSyntaxException ;
5
6
import com .intellij .lang .annotation .AnnotationHolder ;
6
7
import com .intellij .lang .annotation .ExternalAnnotator ;
7
8
import com .intellij .lang .annotation .HighlightSeverity ;
9
+ import com .intellij .openapi .diagnostic .Logger ;
8
10
import com .intellij .openapi .editor .Document ;
9
- import com .intellij .openapi .project . DumbAware ;
10
- import com .intellij .openapi .project . Project ;
11
+ import com .intellij .openapi .editor . Editor ;
12
+ import com .intellij .openapi .util . Pair ;
11
13
import com .intellij .openapi .util .TextRange ;
14
+ import com .intellij .openapi .vfs .LocalFileSystem ;
12
15
import com .intellij .openapi .vfs .VirtualFile ;
13
- import com .intellij .psi .PsiDocumentManager ;
14
16
import com .intellij .psi .PsiFile ;
15
- import com .intellij .psi .PsiManager ;
16
- import org .intellij .sdk .language .psi .YakshaFile ;
17
17
import org .intellij .sdk .language .tw .YakshaToolWindow ;
18
18
import org .jetbrains .annotations .NotNull ;
19
19
import org .jetbrains .annotations .Nullable ;
20
20
21
+ import java .io .BufferedWriter ;
21
22
import java .io .File ;
22
23
import java .io .IOException ;
24
+ import java .nio .charset .StandardCharsets ;
25
+ import java .nio .file .Files ;
26
+ import java .nio .file .Path ;
27
+ import java .nio .file .Paths ;
23
28
import java .util .ArrayList ;
24
29
import java .util .List ;
25
30
import java .util .concurrent .TimeUnit ;
26
31
27
- public class YakshaCompilerAnnotator extends ExternalAnnotator <YakshaFile , List <YakshaCompilerAnnotator .CompilerError >> implements DumbAware {
32
+ public class YakshaCompilerAnnotator extends ExternalAnnotator <Pair <Editor , PsiFile >, List <YakshaCompilerAnnotator .CompilerError >> {
33
+
34
+ private static final Logger LOGGER = Logger .getInstance (YakshaCompilerAnnotator .class );
35
+ public static final int MAX_DEPTH = 5 ;
36
+
37
+ private static void log (final String s ) {
38
+ // System.out.println(s);
39
+ LOGGER .info (s );
40
+ }
41
+
28
42
@ Override
29
- public @ Nullable YakshaFile collectInformation (@ NotNull PsiFile file ) {
30
- if (file instanceof YakshaFile ) {
31
- return (YakshaFile ) file ;
32
- }
33
- return null ;
43
+ public @ Nullable Pair <Editor , PsiFile > collectInformation (@ NotNull PsiFile file , @ NotNull Editor editor , boolean hasErrors ) {
44
+ return Pair .create (editor , file );
34
45
}
35
46
36
47
@ Override
37
- public @ NotNull List <CompilerError > doAnnotate (@ Nullable YakshaFile collectedInfo ) {
48
+ public @ NotNull List <CompilerError > doAnnotate (@ Nullable Pair < Editor , PsiFile > collectedInfo ) {
38
49
if (collectedInfo == null ) {
39
50
return List .of ();
40
51
}
41
- final var project = collectedInfo .getProject ();
42
- final var virtualFile = collectedInfo .getVirtualFile ();
43
- File mainFile = findMainFile (new File (virtualFile .getPath ()), 5 );
44
- if (mainFile == null ) {
52
+ final var editor = collectedInfo .first ;
53
+ final var psiFile = collectedInfo .second ;
54
+ final var virtualFile = psiFile .getVirtualFile ();
55
+
56
+ File f = new File (virtualFile .getPath ());
57
+ final File cleanup ;
58
+ final File directory = f .getParentFile ();
59
+
60
+ if (f .getName ().startsWith ("_." )) {
61
+ log ("You are editing a scratch file: " + f .getPath ());
45
62
return List .of ();
46
63
}
47
64
65
+ if (f .getName ().equals ("main.yaka" )) {
66
+ final var scratch = Paths .get (directory .getAbsolutePath (), "_.main.yaka" );
67
+ log ("Wrote scratch file:" + scratch );
68
+ final var temp = createSyncedFile (editor .getDocument (), scratch );
69
+ if (temp == null ) {
70
+ f = Paths .get (directory .getAbsolutePath (), "_.main.yaka" ).toFile ();
71
+ } else {
72
+ f = new File (temp .getPath ());
73
+ }
74
+ cleanup = f ;
75
+ } else {
76
+ final var name = "_." + f .getName ();
77
+ final var scratch = Paths .get (f .getAbsoluteFile ().getParent (), name );
78
+ cleanup = scratch .toFile ();
79
+ createSyncedFile (editor .getDocument (), scratch );
80
+ log ("Wrote scratch file:" + scratch );
81
+ f = findMainFile (new File (directory .getPath ()));
82
+ if (f == null ) {
83
+ deleteScratch (cleanup );
84
+ return List .of ();
85
+ }
86
+ }
87
+
88
+ final var result = runCompiler (f );
89
+ deleteScratch (cleanup );
90
+ return result ;
91
+ }
92
+
93
+ private static void deleteScratch (@ NotNull File f ) {
94
+ try {
95
+ final var success = f .delete ();
96
+ log ("Deleted " + f .getPath () + " success = " + success );
97
+ } catch (Exception ignored ) {
98
+ log ("Failed to delete " + f .getPath ());
99
+ }
100
+ }
48
101
49
- return runCompiler (mainFile );
102
+ private static VirtualFile createSyncedFile (Document doc , Path tmp ) {
103
+ try {
104
+ try (BufferedWriter out = Files .newBufferedWriter (tmp , StandardCharsets .UTF_8 )) {
105
+ out .write (doc .getText ());
106
+ }
107
+ File f = tmp .toFile ();
108
+ return LocalFileSystem .getInstance ().refreshAndFindFileByIoFile (f );
109
+ } catch (IOException ex ) {
110
+ throw new RuntimeException (ex );
111
+ }
50
112
}
51
113
52
114
@ Override
@@ -59,6 +121,13 @@ public void apply(@NotNull PsiFile file, List<CompilerError> annotationResult, @
59
121
if (document == null ) {
60
122
return ;
61
123
}
124
+ final var f = new File (file .getVirtualFile ().getPath ());
125
+ if (f .getName ().startsWith ("_." )) {
126
+ return ;
127
+ }
128
+
129
+ final var givenFile = f .getAbsolutePath ();
130
+ final var givenFileScratch = Paths .get (Paths .get (givenFile ).getParent ().toString (), "_." + f .getName ()).toFile ().getAbsolutePath ();
62
131
63
132
for (CompilerError error : annotationResult ) {
64
133
var line = error .line - 1 ;
@@ -67,35 +136,33 @@ public void apply(@NotNull PsiFile file, List<CompilerError> annotationResult, @
67
136
var startOffset = document .getLineStartOffset (line ) + error .column - 1 ;
68
137
var endOffset = startOffset + error .length ;
69
138
70
- holder .newAnnotation (HighlightSeverity .ERROR , error .message ).range (new TextRange (startOffset , endOffset )).create ();
71
- }
72
- }
139
+ final var absPath = new File (error .file ).getAbsolutePath ();
73
140
74
- private @ Nullable Document getDoc (Project project , VirtualFile file ) {
75
- final var psiFile = PsiManager .getInstance (project ).findFile (file );
76
- if (psiFile == null ) {
77
- return null ;
141
+ if (absPath .equals (givenFile ) || absPath .equals (givenFileScratch )) {
142
+ holder .newAnnotation (HighlightSeverity .ERROR , error .message ).range (new TextRange (startOffset , endOffset )).create ();
143
+ }
78
144
}
79
- return PsiDocumentManager .getInstance (project ).getDocument (psiFile );
80
145
}
81
146
82
147
public static class CompilerError {
148
+ final String file ;
83
149
final String message ;
84
150
final int line ;
85
151
final int column ;
86
152
final int length ;
87
153
88
- private CompilerError (final String message , final int line , final int column , final int length ) {
154
+ private CompilerError (final String file , final String message , final int line , final int column , final int length ) {
155
+ this .file = file ;
89
156
this .message = message ;
90
157
this .line = line ;
91
158
this .column = column ;
92
159
this .length = length ;
93
160
}
94
161
}
95
162
96
- private File findMainFile (File dir , int maxDepth ) {
163
+ private File findMainFile (final File dir ) {
97
164
File currentDir = dir ;
98
- for (int i = 0 ; i <= maxDepth ; i ++) {
165
+ for (int i = 0 ; i <= YakshaCompilerAnnotator . MAX_DEPTH ; i ++) {
99
166
File mainCFile = new File (currentDir , "main.yaka" );
100
167
if (mainCFile .exists () && mainCFile .isFile ()) {
101
168
return mainCFile ;
@@ -107,31 +174,28 @@ private File findMainFile(File dir, int maxDepth) {
107
174
}
108
175
109
176
private List <CompilerError > runCompiler (File mainFile ) {
110
- List <CompilerError > errors = new ArrayList <>();
177
+ final List <CompilerError > errors = new ArrayList <>();
111
178
final var compiler = YakshaToolWindow .YAKSHA_EXE_PATH ;
112
179
if (compiler .isBlank ()) {
113
- System . out . println ("------ yaksha compiler path is not set -----" );
180
+ log ("------ yaksha compiler path is not set -----" );
114
181
return errors ;
115
182
}
116
183
117
- System . out . println ("----- yaksha compiler: " + compiler );
184
+ log ("----- yaksha compiler: " + compiler );
118
185
119
- List <String > command = List .of (compiler , "compile" , "-d" , mainFile .getAbsolutePath ());
186
+ final List <String > command = List .of (compiler , "compile" , "-d" , "-e " , mainFile .getAbsolutePath ());
120
187
121
188
try {
122
- Process process = new ProcessBuilder (command )
123
- .directory (mainFile .getParentFile ())
124
- .redirectErrorStream (true )
125
- .start ();
189
+ final Process process = new ProcessBuilder (command ).directory (mainFile .getParentFile ()).redirectErrorStream (true ).start ();
126
190
127
191
process .waitFor (60 , TimeUnit .SECONDS );
128
192
129
193
String output = new String (process .getInputStream ().readAllBytes ());
130
- int exitCode = process . exitValue ( );
131
-
132
- errors .addAll (parseCompilerErrors ( output , mainFile . getAbsolutePath ()) );
133
- } catch (IOException | InterruptedException e ) {
134
- e . printStackTrace ( );
194
+ final var result = parseCompilerErrors ( output , mainFile . getAbsolutePath () );
195
+ log ( "Found errors " + result . size ());
196
+ errors .addAll (result );
197
+ } catch (IOException | InterruptedException ignored ) {
198
+ log ( "-------------- error " );
135
199
}
136
200
137
201
return errors ;
@@ -143,16 +207,19 @@ private List<CompilerError> parseCompilerErrors(String output, String mainFilePa
143
207
144
208
for (String line : lines ) {
145
209
if (!line .trim ().isEmpty ()) {
146
- JsonObject json = JsonParser .parseString (line ).getAsJsonObject ();
210
+ final JsonObject json ;
211
+ try {
212
+ json = JsonParser .parseString (line ).getAsJsonObject ();
213
+ } catch (JsonSyntaxException exception ) {
214
+ continue ;
215
+ }
147
216
String file = json .has ("file" ) ? json .get ("file" ).getAsString () : mainFilePath ;
148
217
int lineNumber = json .has ("line" ) ? json .get ("line" ).getAsInt () : 0 ;
149
218
int columnNumber = json .has ("pos" ) ? json .get ("pos" ).getAsInt () : 0 ;
150
219
String message = json .get ("message" ).getAsString ();
151
220
int tokenLength = json .has ("token" ) ? json .get ("token" ).getAsString ().length () : 1 ;
152
221
153
- if (mainFilePath .equals (file )) {
154
- errors .add (new CompilerError (message , lineNumber , columnNumber , tokenLength ));
155
- }
222
+ errors .add (new CompilerError (file , message , lineNumber , columnNumber , tokenLength ));
156
223
}
157
224
}
158
225
0 commit comments