mirror of
https://github.com/textmate/textmate.git
synced 2026-04-28 03:00:34 -04:00
This solves the problem where we need to estimate the current line’s indent, but the lines above it is a multi-line block comment. Previously we would fetch indent patterns based on the current scope, then find the first line above caret, for which the patterns can be used to estimate the indent. The problem is that the commented lines without comment markers would be treated as code, and used for the indent. With the new approach, we can set different patterns for ‘comment.block’ (the C bundle already does this), which basically ignore all the lines, which will cause TextMate to use the code above the comment to estimate indent. This commit closes textmate/c.tmbundle#3 and also closes textmate/php.tmbundle#24.
208 lines
5.2 KiB
C++
208 lines
5.2 KiB
C++
#include <regexp/indent.h>
|
||
#include <text/format.h>
|
||
#include <oak/oak.h>
|
||
|
||
struct line_t
|
||
{
|
||
size_t indent; std::string content;
|
||
};
|
||
|
||
class IndentTests : public CxxTest::TestSuite
|
||
{
|
||
static std::map<indent::pattern_type, regexp::pattern_t> const& patterns ()
|
||
{
|
||
static std::map<indent::pattern_type, regexp::pattern_t> const res =
|
||
{
|
||
{ indent::pattern_type::kIncrease,
|
||
"(?x)"
|
||
" ^ .* \\{ [^}\"']* $"
|
||
"| ^ \\s* (public|private|protected): \\s* $"
|
||
"| ^ \\s* @(public|private|protected) \\s* $",
|
||
},
|
||
{ indent::pattern_type::kDecrease,
|
||
"(?x)"
|
||
" ^ (.*\\*/)? \\s* \\} ( [^}{\"']* \\{ | \\s* while \\s* \\( .* )? [;\\s]* (//.*|/\\*.*\\*/\\s*)? $"
|
||
" | ^ \\s* (public|private|protected): \\s* $"
|
||
" | ^ \\s* @(public|private|protected) \\s* $",
|
||
},
|
||
{ indent::pattern_type::kIncreaseNext,
|
||
"(?x)^"
|
||
" (?! .* [;:{}] # do not indent when line ends with ;, :, {, or }\n"
|
||
" \\s* (//|/[*] .* [*]/ \\s* $) # …account for potential trailing comment\n"
|
||
" | @(public|private|protected) # do not indent after obj-c data access keywords\n"
|
||
" )"
|
||
" .* [^\\s;:{}] \\s* $ # indent next if this one isn’t\n"
|
||
" # terminated with ;, :, {, or }\n",
|
||
},
|
||
{ indent::pattern_type::kIgnore,
|
||
"^\\s*((/\\*|\\*/|//|#|template\\b.*?>(?!\\(.*\\))|@protocol|@optional|@interface(?!.*\\{)|@implementation|@end).*)?$"
|
||
}
|
||
};
|
||
return res;
|
||
}
|
||
|
||
static size_t const indentSize = 4;
|
||
static size_t const tabSize = 4;
|
||
|
||
public:
|
||
void test_indent ()
|
||
{
|
||
static line_t const lines[] =
|
||
{
|
||
{ 0, "#include <foo>" },
|
||
{ 0, "" },
|
||
{ 0, "int main ()" },
|
||
{ 0, "{" },
|
||
{ 1, "while(true)" },
|
||
{ 2, "putc('x');" },
|
||
{ 1, "" },
|
||
{ 1, "if(true)" },
|
||
{ 1, "{" },
|
||
{ 2, "while(true)" },
|
||
{ 3, "putc('x');" },
|
||
{ 2, "if(true)" },
|
||
{ 3, "if(true)" },
|
||
{ 4, "if(true)" },
|
||
{ 5, "continue;" },
|
||
{ 1, "}" },
|
||
{ 1, "" },
|
||
{ 1, "return 42;" },
|
||
{ 0, "}" },
|
||
};
|
||
|
||
indent::fsm_t fsm(indentSize, tabSize);
|
||
iterate(line, lines)
|
||
{
|
||
TSM_ASSERT_EQUALS(text::format("%td: %s", line - std::begin(lines), line->content.c_str()), fsm.scan_line(line->content, patterns()), indentSize * line->indent);
|
||
}
|
||
}
|
||
|
||
void test_seeding_proper_1 ()
|
||
{
|
||
static std::string const seed_lines[] =
|
||
{
|
||
"void main ()",
|
||
"{",
|
||
" if(true)",
|
||
" if(true)",
|
||
" if(true)",
|
||
};
|
||
|
||
indent::fsm_t fsm(indentSize, tabSize);
|
||
for(size_t i = sizeofA(seed_lines); i-- && !fsm.is_seeded(seed_lines[i], patterns()); )
|
||
continue;
|
||
|
||
static line_t const lines[] =
|
||
{
|
||
{ 4, "continue;" },
|
||
{ 1, "continue;" },
|
||
{ 0, "}" },
|
||
};
|
||
|
||
iterate(line, lines)
|
||
{
|
||
TSM_ASSERT_EQUALS(text::format("%td: %s", line - std::begin(lines), line->content.c_str()), fsm.scan_line(line->content, patterns()), indentSize * line->indent);
|
||
}
|
||
}
|
||
|
||
void test_seeding_proper_2 ()
|
||
{
|
||
static std::string const seed_lines[] =
|
||
{
|
||
"void main ()",
|
||
"{",
|
||
" if(true)",
|
||
" if(true)",
|
||
" if(true)",
|
||
" continue;",
|
||
};
|
||
|
||
indent::fsm_t fsm(indentSize, tabSize);
|
||
for(size_t i = sizeofA(seed_lines); i-- && !fsm.is_seeded(seed_lines[i], patterns()); )
|
||
continue;
|
||
|
||
static line_t const lines[] =
|
||
{
|
||
{ 1, "continue;" },
|
||
{ 0, "}" },
|
||
};
|
||
|
||
iterate(line, lines)
|
||
{
|
||
TSM_ASSERT_EQUALS(text::format("%td: %s", line - std::begin(lines), line->content.c_str()), fsm.scan_line(line->content, patterns()), indentSize * line->indent);
|
||
}
|
||
}
|
||
|
||
void test_seeding_improper ()
|
||
{
|
||
static std::string const seed_lines[] =
|
||
{
|
||
"void main ()",
|
||
"{",
|
||
" if(true)",
|
||
" if(true)",
|
||
" if(true)",
|
||
};
|
||
|
||
indent::fsm_t fsm(indentSize, tabSize);
|
||
for(size_t i = sizeofA(seed_lines); i-- && !fsm.is_seeded(seed_lines[i], patterns()); )
|
||
continue;
|
||
|
||
static line_t const lines[] =
|
||
{
|
||
{ 2, "continue;" },
|
||
{ 1, "continue;" },
|
||
{ 0, "}" },
|
||
};
|
||
|
||
iterate(line, lines)
|
||
{
|
||
TSM_ASSERT_EQUALS(text::format("%td: %s", line - std::begin(lines), line->content.c_str()), fsm.scan_line(line->content, patterns()), indentSize * line->indent);
|
||
}
|
||
}
|
||
|
||
void test_seeding_extra_indent ()
|
||
{
|
||
static std::string const seed_lines[] =
|
||
{
|
||
"void main ()",
|
||
"{",
|
||
" foo(bar,",
|
||
" 42);",
|
||
};
|
||
|
||
indent::fsm_t fsm(indentSize, tabSize);
|
||
for(size_t i = sizeofA(seed_lines); i-- && !fsm.is_seeded(seed_lines[i], patterns()); )
|
||
continue;
|
||
|
||
static line_t const lines[] =
|
||
{
|
||
{ 1, "return 0;" },
|
||
{ 0, "}" },
|
||
};
|
||
|
||
iterate(line, lines)
|
||
{
|
||
TSM_ASSERT_EQUALS(text::format("%td: %s", line - std::begin(lines), line->content.c_str()), fsm.scan_line(line->content, patterns()), indentSize * line->indent);
|
||
}
|
||
}
|
||
|
||
void test_mixing ()
|
||
{
|
||
static line_t const lines[] =
|
||
{
|
||
{ 0, "int main ()" },
|
||
{ 0, "{" },
|
||
{ 1, "str = { foo," },
|
||
{ 2, "bar };" },
|
||
{ 1, "}" },
|
||
};
|
||
|
||
indent::fsm_t fsm(indentSize, tabSize);
|
||
iterate(line, lines)
|
||
{
|
||
TSM_ASSERT_EQUALS(text::format("%td: %s", line - std::begin(lines), line->content.c_str()), fsm.scan_line(line->content, patterns()), indentSize * line->indent);
|
||
}
|
||
}
|
||
};
|