diff --git a/src/ddc/lexer/textsource.d b/src/ddc/lexer/textsource.d index 851fe7f..3d0239d 100644 --- a/src/ddc/lexer/textsource.d +++ b/src/ddc/lexer/textsource.d @@ -89,7 +89,7 @@ class ArraySourceLines : SourceLines { /// source file override @property SourceFile file() { return _file; } /// last read line - override @property uint line() { return _line; } + override @property uint line() { return _line + _firstLine; } /// source encoding //@property EncodingType encoding() { return _encoding; } /// error code diff --git a/src/ddc/lexer/tokenizer.d b/src/ddc/lexer/tokenizer.d index 41cde6e..e20861f 100644 --- a/src/ddc/lexer/tokenizer.d +++ b/src/ddc/lexer/tokenizer.d @@ -928,6 +928,8 @@ class Token { @property Keyword keyword() { return Keyword.NONE; } /// returns true if this is documentation comment token @property bool isDocumentationComment() { return false; } + /// returns true if this is multiline + @property bool isMultilineComment() { return false; } // error handling @@ -1024,7 +1026,9 @@ class OpToken : Token { super(TokenType.OP, file, line, pos); } override public Token clone() { - return new OpToken(_file, _line, _pos); + OpToken res = new OpToken(_file, _line, _pos); + res._op = _op; + return res; } public override @property string toString() { return "Op:" ~ to!string(_op); @@ -1043,7 +1047,9 @@ class KeywordToken : Token { super(TokenType.KEYWORD, file, line, pos); } override public Token clone() { - return new KeywordToken(_file, _line, _pos); + KeywordToken res = new KeywordToken(_file, _line, _pos); + res._keyword = _keyword; + return res; } public override @property string toString() { return "Keyword:" ~ to!string(_keyword); @@ -1054,6 +1060,8 @@ class KeywordToken : Token { class CommentToken : Token { protected dchar[] _text; protected bool _isDocumentationComment; + protected bool _isMultilineComment; + override @property bool isDocumentationComment() { return _isDocumentationComment; @@ -1063,6 +1071,15 @@ class CommentToken : Token { _isDocumentationComment = f; } + /// returns true if this is multiline + override @property bool isMultilineComment() { + return _isMultilineComment; + } + + @property void isMultilineComment(bool f) { + _isMultilineComment = f; + } + @property override dchar[] text() { return _text; } @property void text(dchar[] text) { _text = text; } this() { @@ -1073,7 +1090,10 @@ class CommentToken : Token { _text = text; } override public Token clone() { - return new CommentToken(_file, _line, _pos, _text.dup); + CommentToken res = new CommentToken(_file, _line, _pos, _text.dup); + res._isDocumentationComment = _isDocumentationComment; + res._isMultilineComment = _isMultilineComment; + return res; } public override @property string toString() { return "Comment:" ~ to!string(_text); @@ -1413,7 +1433,7 @@ class Tokenizer init(lineStream); } - void init(SourceLines lineStream) { + void init(SourceLines lineStream, int pos = 0) { _lineStream = lineStream; SourceFile file = _lineStream.file; _sharedWhiteSpaceToken.setFile(file); @@ -1432,6 +1452,7 @@ class Tokenizer _prevLineLength = 0; _lineText = null; nextLine(); + _pos = pos; } this(string code, string filename = "") { @@ -1532,6 +1553,7 @@ class Tokenizer protected Token processMultilineComment() { _sharedCommentToken.setPos(_line, _pos - 1); _sharedCommentToken.isDocumentationComment = _pos + 1 < _lineText.length && _lineText[_pos + 1] == '*'; + _sharedCommentToken.isMultilineComment = true; _commentAppender.reset(); int textStart = _pos + 1; for (;;) { @@ -1566,6 +1588,7 @@ class Tokenizer protected Token processNestedComment() { _sharedCommentToken.setPos(_line, _pos - 1); _sharedCommentToken.isDocumentationComment = _pos + 1 < _lineText.length && _lineText[_pos + 1] == '+'; + _sharedCommentToken.isMultilineComment = true; _commentAppender.reset(); dchar[] text; int textStart = _pos + 1; diff --git a/src/dlangide/ui/dsourceedit.d b/src/dlangide/ui/dsourceedit.d index f8964df..3416f51 100644 --- a/src/dlangide/ui/dsourceedit.d +++ b/src/dlangide/ui/dsourceedit.d @@ -119,6 +119,9 @@ class SimpleDSyntaxHighlighter : SyntaxHighlighter { /// return true if can toggle line comments for specified text range override bool canToggleLineComment(TextRange range) { + TextRange r = content.fullLinesRange(range); + if (isInsideBlockComment(r.start) || isInsideBlockComment(r.end)) + return false; return true; } @@ -176,9 +179,90 @@ class SimpleDSyntaxHighlighter : SyntaxHighlighter { return cast(dstring)res; } + /// searches for neares token start before or equal to position + protected TextPosition tokenStart(TextPosition pos) { + TextPosition p = pos; + for (;;) { + TextPosition prevPos = content.prevCharPos(p); + if (p == prevPos) + return p; // begin of file + TokenProp prop = content.tokenProp(p); + TokenProp prevProp = content.tokenProp(prevPos); + if (prop && prop != prevProp) + return p; + p = prevPos; + } + } + + static struct TokenWithRange { + Token token; + TextRange range; + @property string toString() { + return token.toString ~ range.toString; + } + } + protected TextPosition _lastTokenStart; + protected Token _lastToken; + protected bool initTokenizer(TextPosition startPos) { + const dstring[] lines = content.lines; + _lines.init(cast(dstring[])(lines[startPos.line .. $]), _file, startPos.line); + _tokenizer.init(_lines, startPos.pos); + _lastTokenStart = startPos; + _lastToken = null; + nextToken(); + return true; + } + + protected TokenWithRange nextToken() { + TokenWithRange res; + if (_lastToken && _lastToken.type == TokenType.EOF) { + // end of file + res.range.start = _lastTokenStart; + res.range.end = content.endOfFile(); + res.token = null; + return res; + } + res.range.start = _lastTokenStart; + res.token = _lastToken; + _lastToken = _tokenizer.nextToken(); + if (_lastToken) + _lastToken = _lastToken.clone(); + _lastTokenStart = _lastToken ? TextPosition(_lastToken.line - 1, _lastToken.pos - 1) : content.endOfFile(); + res.range.end = _lastTokenStart; + return res; + } + + protected TokenWithRange getPositionToken(TextPosition pos) { + Log.d("getPositionToken for ", pos); + TextPosition start = tokenStart(pos); + Log.d("token start found: ", start); + initTokenizer(start); + for (;;) { + TokenWithRange tokenRange = nextToken(); + Log.d("read token: ", tokenRange); + if (!tokenRange.token) { + Log.d("end of file"); + return tokenRange; + } + if (pos >= tokenRange.range.start && pos < tokenRange.range.end) { + Log.d("found: ", pos, " in ", tokenRange); + return tokenRange; + } + } + } + + protected bool isInsideBlockComment(TextPosition pos) { + TokenWithRange tokenRange = getPositionToken(pos); + if (tokenRange.token && tokenRange.token.type == TokenType.COMMENT && tokenRange.token.isMultilineComment) + return pos > tokenRange.range.start && pos < tokenRange.range.end; + return false; + } + /// toggle line comments for specified text range override void toggleLineComment(TextRange range, Object source) { TextRange r = content.fullLinesRange(range); + if (isInsideBlockComment(r.start) || isInsideBlockComment(r.end)) + return; int lineCount = r.end.line - r.start.line; bool noEolAtEndOfRange = false; if (lineCount == 0 || r.end.pos > 0) {