@@ -7,11 +7,14 @@ import std.conv;
77import std.getopt ;
88import std.json ;
99import std.stdio : stderr;
10+ import std.string ;
1011
1112import dfmt.config;
1213import dfmt.editorconfig;
1314import dfmt.formatter : fmt = format;
1415
16+ import dparse.lexer;
17+
1518import core.thread ;
1619
1720import painlessjson;
@@ -141,10 +144,96 @@ class DfmtComponent : ComponentWrapper
141144 else
142145 return code.idup;
143146 }
147+
148+ // / Finds dfmt instruction comments (dfmt off, dfmt on)
149+ // / Returns: a list of dfmt instructions, sorted in appearing (source code)
150+ // / order
151+ DfmtInstruction[] findDfmtInstructions (scope const (char )[] code)
152+ {
153+ LexerConfig config;
154+ config.whitespaceBehavior = WhitespaceBehavior.skip;
155+ config.commentBehavior = CommentBehavior.noIntern;
156+ auto lexer = DLexer(code, config, &workspaced.stringCache);
157+ auto ret = appender! (DfmtInstruction[]);
158+ Search: foreach (token; lexer)
159+ {
160+ if (token.type == tok! " comment" )
161+ {
162+ auto text = dfmtCommentText(token.text);
163+ DfmtInstruction instruction;
164+ switch (text)
165+ {
166+ case " dfmt on" :
167+ instruction.type = DfmtInstruction.Type.dfmtOn;
168+ break ;
169+ case " dfmt off" :
170+ instruction.type = DfmtInstruction.Type.dfmtOff;
171+ break ;
172+ default :
173+ text = text.chompPrefix(" /" ).strip; // make doc comments (///) appear as unknown because only first 2 // are stripped.
174+ if (text.startsWith(" dfmt" , " dmft" , " dftm" )) // include some typos
175+ {
176+ instruction.type = DfmtInstruction.Type.unknown;
177+ break ;
178+ }
179+ continue Search;
180+ }
181+ instruction.index = token.index;
182+ instruction.line = token.line;
183+ instruction.column = token.column;
184+ instruction.length = token.text.length;
185+ ret.put(instruction);
186+ }
187+ else if (token.type == tok! " __EOF__" )
188+ break ;
189+ }
190+ return ret.data;
191+ }
192+ }
193+
194+ // /
195+ struct DfmtInstruction
196+ {
197+ // / Known instruction types
198+ enum Type
199+ {
200+ // / Instruction to turn off formatting from here
201+ dfmtOff,
202+ // / Instruction to turn on formatting again from here
203+ dfmtOn,
204+ // / Starts with dfmt, but unknown contents
205+ unknown,
206+ }
207+
208+ // /
209+ Type type;
210+ // / libdparse Token location (byte based offset)
211+ size_t index;
212+ // / libdparse Token location (byte based, 1-based)
213+ size_t line, column;
214+ // / Comment length in bytes
215+ size_t length;
144216}
145217
146218private :
147219
220+ // from dfmt/formatter.d TokenFormatter!T.commentText
221+ string dfmtCommentText (string commentText)
222+ {
223+ import std.string : strip;
224+
225+ if (commentText[0 .. 2 ] == " //" )
226+ commentText = commentText[2 .. $];
227+ else
228+ {
229+ if (commentText.length > 3 )
230+ commentText = commentText[2 .. $ - 2 ];
231+ else
232+ commentText = commentText[2 .. $];
233+ }
234+ return commentText.strip();
235+ }
236+
148237void tryFetchProperty (T = string )(ref JSONValue json, ref T ret, string name)
149238{
150239 auto ptr = name in json;
@@ -184,3 +273,35 @@ void tryFetchProperty(T = string)(ref JSONValue json, ref T ret, string name)
184273 static assert (false );
185274 }
186275}
276+
277+ unittest
278+ {
279+ scope backend = new WorkspaceD();
280+ auto workspace = makeTemporaryTestingWorkspace;
281+ auto instance = backend.addInstance(workspace.directory);
282+ backend.register! DfmtComponent;
283+ DfmtComponent dfmt = instance.get ! DfmtComponent;
284+
285+ assert (dfmt.findDfmtInstructions(" void main() {}" ).length == 0 );
286+ assert (dfmt.findDfmtInstructions(" void main() {\n\t // dfmt off\n }" ) == [
287+ DfmtInstruction(DfmtInstruction.Type.dfmtOff, 15 , 2 , 2 , 11 )
288+ ]);
289+ assert (dfmt.findDfmtInstructions(` import std.stdio;
290+
291+ // dfmt on
292+ void main()
293+ {
294+ // dfmt off
295+ writeln("hello");
296+ // dmft off
297+ string[string] x = [
298+ "a": "b"
299+ ];
300+ // dfmt on
301+ }` ) == [
302+ DfmtInstruction(DfmtInstruction.Type.dfmtOn, 19 , 3 , 1 , 10 ),
303+ DfmtInstruction(DfmtInstruction.Type.dfmtOff, 45 , 6 , 2 , 11 ),
304+ DfmtInstruction(DfmtInstruction.Type.unknown, 77 , 8 , 2 , 11 ),
305+ DfmtInstruction(DfmtInstruction.Type.dfmtOn, 127 , 12 , 2 , 10 ),
306+ ]);
307+ }
0 commit comments