[svn] horrid .svns copied accidentally

This commit is contained in:
goatchurch
2009-06-28 21:26:35 +01:00
parent db5e315db0
commit 16b7404d9b
45 changed files with 8750 additions and 0 deletions

View File

@@ -0,0 +1,32 @@
Copyright (c) 2009, Franciszek Wawrzak
All rights reserved.
This software is provided for use in connection with the
CodeMirror suite of modules and utilities, hosted and maintained
at http://marijn.haverbeke.nl/codemirror/.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,59 @@
.editbox {
margin: .4em;
padding: 0;
font-family: monospace;
font-size: 10pt;
color: black;
}
pre.code, .editbox {
color: #666666;
}
.editbox p {
margin: 0;
}
span.lua-comment {
color: #BB9977;
}
span.lua-keyword {
font-weight: bold;
color: blue;
}
span.lua-string {
color: #AA2222;
}
span.lua-stdfunc {
font-weight: bold;
color: #077;
}
span.lua-customfunc {
font-weight: bold;
color: #0AA;
}
span.lua-identifier {
color: black;
}
span.lua-number {
color: #3A3;
}
span.lua-token {
color: #151;
}
span.lua-error {
color: #FFF;
background-color: #F00;
}

View File

@@ -0,0 +1,68 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="../../js/codemirror.js" type="text/javascript"></script>
<title>CodeMirror: Lua demonstration</title>
</head>
<body style="padding: 20px;">
<p>This page demonstrates <a href="../../index.html">CodeMirror</a>'s
Lua parser. Written by <a href="http://francio.pl/">Franciszek
Wawrzak</a>, released under a BSD-style <a
href="LICENSE">license</a>.</p>
<div style="border: 1px solid black; padding: 0px;">
<textarea id="code" cols="120" rows="30">
--[[
example useless code to show lua syntax highlighting
this is multiline comment
]]
function blahblahblah(x)
local table = {
"asd" = 123,
"x" = 0.34,
}
if x ~= 3 then
print( x )
elseif x == "string"
my_custom_function( 0x34 )
else
unknown_function( "some string" )
end
--single line comment
end
function blablabla3()
for k,v in ipairs( table ) do
--abcde..
y=[=[
x=[[
x is a multi line string
]]
but its definition is iside a highest level string!
]=]
print(" \"\" ")
--this marks a parser error:
s = [== asdasdasd]]
s = math.sin( x )
end
end
</textarea>
</div>
<script type="text/javascript">
var editor = CodeMirror.fromTextArea('code', {
height: "350px",
parserfile: "../contrib/lua/js/parselua.js",
stylesheet: "css/luacolors.css",
path: "../../js/"
});
</script>
</body>
</html>

View File

@@ -0,0 +1,253 @@
/*
Simple parser for LUA
Written for Lua 5.1, based on parsecss and other parsers.
features: highlights keywords, strings, comments (no leveling supported! ("[==[")),tokens, basic indenting
to make this parser highlight your special functions pass table with this functions names to parserConfig argument of creator,
parserConfig: ["myfunction1","myfunction2"],
*/
function findFirstRegexp(words) {
return new RegExp("^(?:" + words.join("|") + ")", "i");
}
function matchRegexp(words) {
return new RegExp("^(?:" + words.join("|") + ")$", "i");
}
var luaCustomFunctions= matchRegexp([]);
function configureLUA(parserConfig){
if(parserConfig)
luaCustomFunctions= matchRegexp(parserConfig);
}
//long list of standard functions from lua manual
var luaStdFunctions = matchRegexp([
"_G","_VERSION","assert","collectgarbage","dofile","error","getfenv","getmetatable","ipairs","load","loadfile","loadstring","module","next","pairs","pcall","print","rawequal","rawget","rawset","require","select","setfenv","setmetatable","tonumber","tostring","type","unpack","xpcall",
"coroutine.create","coroutine.resume","coroutine.running","coroutine.status","coroutine.wrap","coroutine.yield",
"debug.debug","debug.getfenv","debug.gethook","debug.getinfo","debug.getlocal","debug.getmetatable","debug.getregistry","debug.getupvalue","debug.setfenv","debug.sethook","debug.setlocal","debug.setmetatable","debug.setupvalue","debug.traceback",
"close","flush","lines","read","seek","setvbuf","write",
"io.close","io.flush","io.input","io.lines","io.open","io.output","io.popen","io.read","io.stderr","io.stdin","io.stdout","io.tmpfile","io.type","io.write",
"math.abs","math.acos","math.asin","math.atan","math.atan2","math.ceil","math.cos","math.cosh","math.deg","math.exp","math.floor","math.fmod","math.frexp","math.huge","math.ldexp","math.log","math.log10","math.max","math.min","math.modf","math.pi","math.pow","math.rad","math.random","math.randomseed","math.sin","math.sinh","math.sqrt","math.tan","math.tanh",
"os.clock","os.date","os.difftime","os.execute","os.exit","os.getenv","os.remove","os.rename","os.setlocale","os.time","os.tmpname",
"package.cpath","package.loaded","package.loaders","package.loadlib","package.path","package.preload","package.seeall",
"string.byte","string.char","string.dump","string.find","string.format","string.gmatch","string.gsub","string.len","string.lower","string.match","string.rep","string.reverse","string.sub","string.upper",
"table.concat","table.insert","table.maxn","table.remove","table.sort"
]);
var luaKeywords = matchRegexp(["and","break","elseif","false","nil","not","or","return",
"true","function", "end", "if", "then", "else", "do",
"while", "repeat", "until", "for", "in", "local" ]);
var luaIndentKeys = matchRegexp(["function", "if","repeat","for","while", "[\(]", "{"]);
var luaUnindentKeys = matchRegexp(["end", "until", "[\)]", "}"]);
var luaUnindentKeys2 = findFirstRegexp(["end", "until", "[\)]", "}"]);
var luaMiddleKeys = findFirstRegexp(["else","elseif"]);
var LUAParser = Editor.Parser = (function() {
var tokenizeLUA = (function() {
function normal(source, setState) {
var ch = source.next();
if (ch == "-" && source.equals("-")) {
source.next();
setState(inSLComment);
return null;
}
else if (ch == "\"" || ch == "'") {
setState(inString(ch));
return null;
}
if (ch == "[" && (source.equals("[") || source.equals("="))) {
var level = 0;
while(source.equals("=")){
level ++;
source.next();
}
if(! source.equals("[") )
return "lua-error";
setState(inMLSomething(level,"lua-string"));
return null;
}
else if (ch == "=") {
if (source.equals("="))
source.next();
return "lua-token";
}
else if (ch == ".") {
if (source.equals("."))
source.next();
if (source.equals("."))
source.next();
return "lua-token";
}
else if (ch == "+" || ch == "-" || ch == "*" || ch == "/" || ch == "%" || ch == "^" || ch == "#" ) {
return "lua-token";
}
else if (ch == ">" || ch == "<" || ch == "(" || ch == ")" || ch == "{" || ch == "}" || ch == "[" ) {
return "lua-token";
}
else if (ch == "]" || ch == ";" || ch == ":" || ch == ",") {
return "lua-token";
}
else if (source.equals("=") && (ch == "~" || ch == "<" || ch == ">")) {
source.next();
return "lua-token";
}
else if (/\d/.test(ch)) {
source.nextWhileMatches(/[\w.%]/);
return "lua-number";
}
else {
source.nextWhileMatches(/[\w\\\-_.]/);
return "lua-identifier";
}
}
function inSLComment(source, setState) {
var start = true;
var count=0;
while (!source.endOfLine()) {
var ch = source.next();
var level = 0;
if ((ch =="[") && start)
while(source.equals("=")){
source.next();
level++;
}
if (source.equals("[")){
setState(inMLSomething(level,"lua-comment"));
return null;
}
start = false;
}
setState(normal);
return "lua-comment";
}
function inMLSomething(level,what) {
//wat sholud be "lua-string" or "lua-comment", level is the number of "=" in opening mark.
return function(source, setState){
var dashes = 0;
while (!source.endOfLine()) {
var ch = source.next();
if (dashes == level+1 && ch == "]" ) {
setState(normal);
break;
}
if (dashes == 0)
dashes = (ch == "]") ? 1:0;
else
dashes = (ch == "=") ? dashes + 1 : 0;
}
return what;
}
}
function inString(quote) {
return function(source, setState) {
var escaped = false;
while (!source.endOfLine()) {
var ch = source.next();
if (ch == quote && !escaped)
break;
escaped = !escaped && ch == "\\";
}
if (!escaped)
setState(normal);
return "lua-string";
};
}
return function(source, startState) {
return tokenizer(source, startState || normal);
};
})();
function indentLUA(indentDepth, base) {
return function(nextChars) {
var closing = (luaUnindentKeys2.test(nextChars) || luaMiddleKeys.test(nextChars));
return base + ( indentUnit * (indentDepth - (closing?1:0)) );
};
}
function parseLUA(source,basecolumn) {
basecolumn = basecolumn || 0;
var tokens = tokenizeLUA(source);
var indentDepth = 0;
var iter = {
next: function() {
var token = tokens.next(), style = token.style, content = token.content;
if (style == "lua-identifier" && luaKeywords.test(content)){
token.style = "lua-keyword";
}
if (style == "lua-identifier" && luaStdFunctions.test(content)){
token.style = "lua-stdfunc";
}
if (style == "lua-identifier" && luaCustomFunctions.test(content)){
token.style = "lua-customfunc";
}
if (luaIndentKeys.test(content))
indentDepth++;
else if (luaUnindentKeys.test(content))
indentDepth--;
if (content == "\n")
token.indentation = indentLUA( indentDepth, basecolumn);
return token;
},
copy: function() {
var _tokenState = tokens.state, _indentDepth = indentDepth;
return function(source) {
tokens = tokenizeLUA(source, _tokenState);
indentDepth = _indentDepth;
return iter;
};
}
};
return iter;
}
return {make: parseLUA, configure:configureLUA, electricChars: "delf})"}; //en[d] els[e] unti[l] elsei[f] // this should be taken from Keys keywords
})();

View File

@@ -0,0 +1,37 @@
Copyright (c) 2008-2009, Yahoo! Inc.
All rights reserved.
This software is provided for use in connection with the
CodeMirror suite of modules and utilities, hosted and maintained
at http://marijn.haverbeke.nl/codemirror/.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
* Neither the name of Yahoo! Inc. nor the names of its
contributors may be used to endorse or promote products
derived from this software without specific prior
written permission of Yahoo! Inc.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,110 @@
/*
Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed by
Yahoo! Inc. under the BSD (revised) open source license
@author Dan Vlad Dascalescu <dandv@yahoo-inc.com>
*/
.editbox {
margin: .4em;
padding: 0;
font-family: monospace;
font-size: 10pt;
}
/*We should define specific styles for every element of the syntax.
the setting below will cause some annoying color to show through if we missed
defining a style for a token. This is also the "color" of the whitespace and
of the cursor.
*/
pre.code, .editbox {
color: red;
}
.editbox p {
margin: 0;
}
span.php-punctuation {
color: blue;
}
span.php-keyword {
color: #770088;
font-weight: bold;
}
span.php-operator {
color: blue;
}
/* __FILE__ etc.; http://php.net/manual/en/reserved.php */
span.php-compile-time-constant {
color: #776088;
font-weight: bold;
}
/* output of get_defined_constants(). Differs from http://php.net/manual/en/reserved.constants.php */
span.php-predefined-constant {
color: darkgreen;
font-weight: bold;
}
/* PHP reserved "language constructs"... echo() etc.; http://php.net/manual/en/reserved.php */
span.php-reserved-language-construct {
color: green;
font-weight: bold;
}
/* PHP built-in functions: glob(), chr() etc.; output of get_defined_functions()["internal"] */
span.php-predefined-function {
color: green;
}
/* PHP predefined classes: PDO, Exception etc.; output of get_declared_classes() and different from http://php.net/manual/en/reserved.classes.php */
span.php-predefined-class {
color: green;
}
span.php-atom {
color: #228811;
}
/* class, interface, namespace or function names, but not $variables */
span.php-t_string {
color: black;
}
span.php-variable {
color: black;
font-weight: bold;
}
span.js-localvariable {
color: #004499;
}
span.php-comment {
color: #AA7700;
font-stretch: condensed;
/* font-style: italic; This causes line height to slightly change, getting line numbers out of sync */
}
span.php-string-single-quoted {
color: #AA2222;
}
/* double quoted strings allow interpolation */
span.php-string-double-quoted {
color: #AA2222;
font-weight: bold;
}
span.syntax-error {
background-color: red;
}
span.deprecated {
font-size: smaller;
}

View File

@@ -0,0 +1,292 @@
<!--
Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed by
Yahoo! Inc. under the BSD (revised) open source license
@author Dan Vlad Dascalescu <dandv@yahoo-inc.com>
-->
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="../../js/codemirror.js" type="text/javascript"></script>
<title>CodeMirror: PHP+HTML+JavaScript+CSS mixed-mode demonstration</title>
<link rel="stylesheet" type="text/css" href="../../css/docs.css"/>
</head>
<body style="padding: 20px;">
<p>This is a complex demonstration of the <b>PHP+HTML+JavaScript+CSS mixed-mode
syntax highlight</b> capabilities of <a href="../../index.html">CodeMirror</a>.
&lt;?php ... ?> tags use the PHP parser, &lt;script> tags use the JavaScript
parser, and &lt;style> tags use the CSS parser. The rest of the content is
parsed using the XML parser in HTML mode.</p>
<p>Features of the PHP parser:
<ul>
<li>special "deprecated" style for PHP4 keywords like 'var'
<li>support for PHP 5.3 keywords: 'namespace', 'use'
<li>911 predefined constants, 1301 predefined functions, 105 predeclared classes
from a typical PHP installation in a LAMP environment
<li>new feature: syntax error flagging, thus enabling strict parsing of:
<ol>
<li>function definitions with explicitly or implicitly typed arguments and default values
<li>modifiers (public, static etc.) applied to method and member definitions
<li>foreach(array_expression as $key [=> $value]) loops
</ol>
<li>differentiation between single-quoted strings and double-quoted interpolating strings
</ul>
</p>
<div style="border: 1px solid black; padding: 3px; background-color: #F8F8F8">
<textarea id="code" cols="120" rows="30">
The "root" parser is XML in HTML mode.
Next, we can switch into PHP mode, for example. This is
<?php echo 'text output by';
?>
PHP. </b>
On the line above, we just had an XML syntax error due to the </b> tag not being opened.
<?xml version='1.0' encoding='UTF-8' standalone='yes'?> HTML text will follow
<html>
<head>
<title>Similarly, the 'script' tag will switch to the JavaScript parser:</title>
<script type="text/javascript">
// Press enter inside the object and your new line will be suitably
// indented.
var keyBindings = {
enter: "newline-and-indent",
tab: "reindent-selection",
ctrl_enter: "reparse-buffer",
ctrl_z: "undo",
ctrl_y: "redo",
ctrl_backspace: "undo-for-safari-which-stupidly-enough-blocks-ctrl-z"
};
// Press tab on the next line and the wrong indentation will be fixed.
var regex = /foo|bar/i;
function example(x) {
// Local variables get a different colour than global ones.
var y = 44.4;
return x + y - z;
}
</script>
<style>
/* Some example CSS */
@import url("something.css");
body {
margin: 0;
padding: 3em 6em;
font-family: tahoma, arial, sans-serif;
color: #000;
}
#navigation a {
font-weight: bold;
text-decoration: none !important;
}
h1 {
font-size: 2.5em;
}
h1:before, h2:before {
content: "::";
}
code {
font-family: courier, monospace;
font-size: 80%;
color: #418A8A;
}
</style>
</head>
<body>
The PHP code below contains some deliberate errors. Play with the editor by fixing them
and observing how the highlight changes.
<?php
namespace A;
namespace A::B::C;
namespace A::::B;
namespace A::B::C::;
namespace A::B::C::D x;
self::range($row['lft'], $row['rgt'])); // error: extra ')'
$a = (b() + 4) 5 foo; // error: missing operators
self::$var;
$parent = self::range($max + 1, $max + 1);
$row[attributes][$attribute_name] = $attribute_value;
$row[attributes()][$attribute_name] = $attribute_value;
$row[attributes(5)][$attribute_name] = $attribute_value;
$row[$attributes()][$attribute_name] = $attribute_value;
abstract class 5 extends foo implements echo {
private function domainObjectBuilder() {
return $this->use_domain_object_builder
? $this->domain()->objectBuilder()
: null;
}
const $myconst = 'some string';
$array[myconst] = 4;
// this is a single-line C++-style comment
# this is a single-line shell-style comment
private var $a = __FILE__;
protected static $b = timezone_transitions_get('some parameter here');
global $g = isset("string");
static $s = hash_update_file; // warning: predefined function non-call
function mike ($var) $foo;
mike(A::func(param));
func($b $c); // error: function parameters must be comma-separated
foo bar; // error: no operator
$baz $quux; // error: no operator
public abstract function loadPageXML(util_FilePath $filename, $merge=0+$foo, $x, $y=3) {
$newrow[$key] = $val;
$newresult[] = $row;
$state = $row['c'] == 1;
$attribute_values[$attribute_name] = null;
$row['attributes'][$attribute_name] = $attribute_value;
$result[$row['element']][$row['attribute']] = $row['value'];
$sql = "multiline string
line2 is special - it'll interpolate variables like $state and method calls
{$this->cache->add($key, 5)} and maybe \"more\"
line5";
$sql = 'multiline string
single quoting means no \'interpolation\' like "$start" or method call
{$this->cache->add($key, 5)} will happen
line5';
$bitpattern = 1 << 2;
$bitpattern <<= 3;
$incorrect = <<< 5 EOSTRING // FIXME: CodeMirror update bug: add a letter before 5 and notice that syntax is not updated until EOF, even with continuousScanning: 500
error: the identifier must conform to the identifier rules
EOSTRING;
$sql = <<< EOSQL
SELECT attribute, element, value
FROM attribute_values
WHERE dimension = ?
EOSQL;
$this->lr_cache->add($key, self::range($row['lft'], $row['rgt']));
$composite_string = <<<EOSTRING
some lines here
EOSTRING
. 'something extra';
$page_lft = ($domain->name() == 'page') ? $start + 1 : $page_start + 1;
echo "This is class foo";
echo "a = ".$this ->a[2+3*$array["foo"]]."";
echo "b = {$this->b}"; // FIXME: highlight interpolation in strings
}
final function makecoffee error($types = array("cappuccino"), $coffeeMaker = NULL) {
$out_of_way_amount = $max - $child->left() + 1;
$absolute_pos = $child->left() - $move->width();
$varfunc(1, 'x');
$varfunc(1, 'x') + foo() - 5;
$funcarray[$i]('param1', $param2);
$lr[$domain_name] = $this->get_left_and_right($domain,
$dimension_name,
$element_name);
$domain_list = is_null($domain) ?
r3_Domain::names() :
array($domain->name());
foreach (r3_Domain::names() as $domain_name) {
$placeholders = 'distance LIKE '
. implode(array_fill(1, $num_distances, '?'),
' OR distance LIKE ');
}
return $this->target*$this->trans+myfunc(__METHOD__);
/*
echo 'This is a test'; /* This comment will cause a problem */
*/
}
switch( $type ) {
case "r3core_AddTemplateToTargetEvent":
$this->notifyAddTemplateToTarget( $genevent );
break;
case "r3core_GenerateTargetEvent" $this:
for($i=0; $i<=this->method(); $i++) {
echo 'Syntax "highlighting"';
}
try {
foreach($array xor $loader->parse_fn($filename) as $key => value) {
namespace r3;
}
} catch( Exception $e ) {
/** restore the backup
*/
$this->loadAll($tmp, $event, true);
// `php -l` doesn't complain at all at this (it assumes string constants):
this + makes * no - sense;
}
break;
default moo:
throw new r3_util_Exception( get_class( $genevent ) . " does not map" );
}
};
?>
<r3:cphp>
php("works", $here, 2);
</r3:cphp>
<r4:cphp>
class foo {
// a comment
var $a;
var $b;
};
</r4:cphp>
<h1>This is an <?php # echo 'simple';?> example.</h1>
<p>The header above will say 'This is an example'.</p>
<h1>This is an <?php // echo 'simple';?> example.</h1>
<?php echo; ?>
<body>
<?php echo "<html>
<head>
<script>
var foo = 'bar';
</script>
<style>
span.test {font-family: arial, 'lucida console', sans-serif}
</style>
</head>
<body>
<!-- comment -->
</body>
</html>"; ?>
</body>
</html>
</textarea>
</div>
<script type="text/javascript">
var editor = CodeMirror.fromTextArea('code', {
height: "350px",
parserfile: ["parsexml.js", "parsecss.js", "tokenizejavascript.js", "parsejavascript.js",
"../contrib/php/js/tokenizephp.js", "../contrib/php/js/parsephp.js",
"../contrib/php/js/parsephphtmlmixed.js"],
stylesheet: ["../../css/xmlcolors.css", "../../css/jscolors.css", "../../css/csscolors.css", "css/phpcolors.css"],
path: "../../js/",
continuousScanning: 500
});
</script>
</body>
</html>

View File

@@ -0,0 +1,371 @@
/*
Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed by
Yahoo! Inc. under the BSD (revised) open source license
@author Dan Vlad Dascalescu <dandv@yahoo-inc.com>
Parse function for PHP. Makes use of the tokenizer from tokenizephp.js.
Based on parsejavascript.js by Marijn Haverbeke.
Features:
+ special "deprecated" style for PHP4 keywords like 'var'
+ support for PHP 5.3 keywords: 'namespace', 'use'
+ 911 predefined constants, 1301 predefined functions, 105 predeclared classes
from a typical PHP installation in a LAMP environment
+ new feature: syntax error flagging, thus enabling strict parsing of:
+ function definitions with explicitly or implicitly typed arguments and default values
+ modifiers (public, static etc.) applied to method and member definitions
+ foreach(array_expression as $key [=> $value]) loops
+ differentiation between single-quoted strings and double-quoted interpolating strings
*/
// add the Array.indexOf method for JS engines that don't support it (e.g. IE)
// code from https://developer.mozilla.org/En/Core_JavaScript_1.5_Reference/Global_Objects/Array/IndexOf
if (!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(elt /*, from*/)
{
var len = this.length;
var from = Number(arguments[1]) || 0;
from = (from < 0)
? Math.ceil(from)
: Math.floor(from);
if (from < 0)
from += len;
for (; from < len; from++)
{
if (from in this &&
this[from] === elt)
return from;
}
return -1;
};
};
var PHPParser = Editor.Parser = (function() {
// Token types that can be considered to be atoms, part of operator expressions
var atomicTypes = {
"atom": true, "number": true, "variable": true, "string": true
};
// Constructor for the lexical context objects.
function PHPLexical(indented, column, type, align, prev, info) {
// indentation at start of this line
this.indented = indented;
// column at which this scope was opened
this.column = column;
// type of scope ('stat' (statement), 'form' (special form), '[', '{', or '(')
this.type = type;
// '[', '{', or '(' blocks that have any text after their opening
// character are said to be 'aligned' -- any lines below are
// indented all the way to the opening character.
if (align != null)
this.align = align;
// Parent scope, if any.
this.prev = prev;
this.info = info;
};
// PHP indentation rules
function indentPHP(lexical) {
return function(firstChars) {
var firstChar = firstChars && firstChars.charAt(0), type = lexical.type;
var closing = firstChar == type;
if (type == "form" && firstChar == "{")
return lexical.indented;
else if (type == "stat" || type == "form")
return lexical.indented + indentUnit;
else if (lexical.info == "switch" && !closing)
return lexical.indented + (/^(?:case|default)\b/.test(firstChars) ? indentUnit : 2 * indentUnit);
else if (lexical.align)
return lexical.column - (closing ? 1 : 0);
else
return lexical.indented + (closing ? 0 : indentUnit);
};
};
// The parser-iterator-producing function itself.
function parsePHP(input, basecolumn) {
// Wrap the input in a token stream
var tokens = tokenizePHP(input);
// The parser state. cc is a stack of actions that have to be
// performed to finish the current statement. For example we might
// know that we still need to find a closing parenthesis and a
// semicolon. Actions at the end of the stack go first. It is
// initialized with an infinitely looping action that consumes
// whole statements.
var cc = [statements];
// The lexical scope, used mostly for indentation.
var lexical = new PHPLexical((basecolumn || 0) - indentUnit, 0, "block", false);
// Current column, and the indentation at the start of the current
// line. Used to create lexical scope objects.
var column = 0;
var indented = 0;
// Variables which are used by the mark, cont, and pass functions
// below to communicate with the driver loop in the 'next' function.
var consume, marked;
// The iterator object.
var parser = {next: next, copy: copy};
// parsing is accomplished by calling next() repeatedly
function next(){
// Start by performing any 'lexical' actions (adjusting the
// lexical variable), or the operations below will be working
// with the wrong lexical state.
while(cc[cc.length - 1].lex)
cc.pop()();
// Fetch the next token.
var token = tokens.next();
// Adjust column and indented.
if (token.type == "whitespace" && column == 0)
indented = token.value.length;
column += token.value.length;
if (token.content == "\n"){
indented = column = 0;
// If the lexical scope's align property is still undefined at
// the end of the line, it is an un-aligned scope.
if (!("align" in lexical))
lexical.align = false;
// Newline tokens get an indentation function associated with
// them.
token.indentation = indentPHP(lexical);
}
// No more processing for meaningless tokens.
if (token.type == "whitespace" || token.type == "comment"
|| token.type == "string_not_terminated" )
return token;
// When a meaningful token is found and the lexical scope's
// align is undefined, it is an aligned scope.
if (!("align" in lexical))
lexical.align = true;
// Execute actions until one 'consumes' the token and we can
// return it. 'marked' is used to change the style of the current token.
while(true) {
consume = marked = false;
// Take and execute the topmost action.
var action = cc.pop();
action(token);
if (consume){
if (marked)
token.style = marked;
// Here we differentiate between local and global variables.
return token;
}
}
return 1; // Firebug workaround for http://code.google.com/p/fbug/issues/detail?id=1239#c1
}
// This makes a copy of the parser state. It stores all the
// stateful variables in a closure, and returns a function that
// will restore them when called with a new input stream. Note
// that the cc array has to be copied, because it is contantly
// being modified. Lexical objects are not mutated, so they can
// be shared between runs of the parser.
function copy(){
var _lexical = lexical, _cc = cc.concat([]), _tokenState = tokens.state;
return function copyParser(input){
lexical = _lexical;
cc = _cc.concat([]); // copies the array
column = indented = 0;
tokens = tokenizePHP(input, _tokenState);
return parser;
};
}
// Helper function for pushing a number of actions onto the cc
// stack in reverse order.
function push(fs){
for (var i = fs.length - 1; i >= 0; i--)
cc.push(fs[i]);
}
// cont and pass are used by the action functions to add other
// actions to the stack. cont will cause the current token to be
// consumed, pass will leave it for the next action.
function cont(){
push(arguments);
consume = true;
}
function pass(){
push(arguments);
consume = false;
}
// Used to change the style of the current token.
function mark(style){
marked = style;
}
// Add a lyer of style to the current token, for example syntax-error
function mark_add(style){
marked = marked + ' ' + style;
}
// Push a new lexical context of the given type.
function pushlex(type, info) {
var result = function pushlexing() {
lexical = new PHPLexical(indented, column, type, null, lexical, info)
};
result.lex = true;
return result;
}
// Pop off the current lexical context.
function poplex(){
lexical = lexical.prev;
}
poplex.lex = true;
// The 'lex' flag on these actions is used by the 'next' function
// to know they can (and have to) be ran before moving on to the
// next token.
// Creates an action that discards tokens until it finds one of
// the given type. This will ignore (and recover from) syntax errors.
function expect(wanted){
return function expecting(token){
if (token.type == wanted) cont(); // consume the token
else {
cont(arguments.callee); // continue expecting() - call itself
}
};
}
// Require a specific token type, or one of the tokens passed in the 'wanted' array
// Used to detect blatant syntax errors. 'execute' is used to pass extra code
// to be executed if the token is matched. For example, a '(' match could
// 'execute' a cont( compasep(funcarg), require(")") )
function require(wanted, execute){
return function requiring(token){
var ok;
var type = token.type;
if (typeof(wanted) == "string")
ok = (type == wanted) -1;
else
ok = wanted.indexOf(type);
if (ok >= 0) {
if (execute && typeof(execute[ok]) == "function")
execute[ok](token);
cont(); // just consume the token
}
else {
if (!marked) mark(token.style);
mark_add("syntax-error");
cont(arguments.callee);
}
};
}
// Looks for a statement, and then calls itself.
function statements(token){
return pass(statement, statements);
}
// Dispatches various types of statements based on the type of the current token.
function statement(token){
var type = token.type;
if (type == "keyword a") cont(pushlex("form"), expression, statement, poplex);
else if (type == "keyword b") cont(pushlex("form"), statement, poplex);
else if (type == "{") cont(pushlex("}"), block, poplex);
else if (type == "function") funcdef();
// technically, "class implode {...}" is correct, but we'll flag that as an error because it overrides a predefined function
else if (type == "class") cont(require("t_string"), expect("{"), pushlex("}"), block, poplex);
else if (type == "foreach") cont(pushlex("form"), require("("), pushlex(")"), expression, require("as"), require("variable"), /* => $value */ expect(")"), poplex, statement, poplex);
else if (type == "for") cont(pushlex("form"), require("("), pushlex(")"), expression, require(";"), expression, require(";"), expression, require(")"), poplex, statement, poplex);
// public final function foo(), protected static $bar;
else if (type == "modifier") cont(require(["modifier", "variable", "function"], [null, null, funcdef]));
else if (type == "switch") cont(pushlex("form"), require("("), expression, require(")"), pushlex("}", "switch"), require([":", "{"]), block, poplex, poplex);
else if (type == "case") cont(expression, require(":"));
else if (type == "default") cont(require(":"));
else if (type == "catch") cont(pushlex("form"), require("("), require("t_string"), require("variable"), require(")"), statement, poplex);
else if (type == "const") cont(require("t_string")); // 'const static x=5' is a syntax error
// technically, "namespace implode {...}" is correct, but we'll flag that as an error because it overrides a predefined function
else if (type == "namespace") cont(namespacedef, require(";"));
// $variables may be followed by operators, () for variable function calls, or [] subscripts
else pass(pushlex("stat"), expression, require(";"), poplex);
}
// Dispatch expression types.
function expression(token){
var type = token.type;
if (atomicTypes.hasOwnProperty(type)) cont(maybeoperator);
else if (type == "<<<") cont(require("string"), maybeoperator); // heredoc/nowdoc
else if (type == "t_string") cont(maybe_double_colon, maybeoperator);
else if (type == "keyword c") cont(expression);
// function call or parenthesized expression: $a = ($b + 1) * 2;
else if (type == "(") cont(pushlex(")"), commasep(expression), require(")"), poplex, maybeoperator);
else if (type == "operator") cont(expression);
}
// Called for places where operators, function calls, or subscripts are
// valid. Will skip on to the next action if none is found.
function maybeoperator(token){
var type = token.type;
if (type == "operator") {
if (token.content == "?") cont(expression, require(":"), expression); // ternary operator
else cont(expression);
}
else if (type == "(") cont(pushlex(")"), expression, commasep(expression), require(")"), poplex, maybeoperator /* $varfunc() + 3 */);
else if (type == "[") cont(pushlex("]"), expression, require("]"), maybeoperator /* for multidimensional arrays, or $func[$i]() */, poplex);
}
// A regular use of the double colon to specify a class, as in self::func() or myclass::$var;
// Differs from `namespace` or `use` in that only one class can be the parent; chains (A::B::$var) are a syntax error.
function maybe_double_colon(token) {
if (token.type == "t_double_colon")
// A::$var, A::func(), A::const
cont(require(["t_string", "variable"]), maybeoperator);
else {
// a t_string wasn't followed by ::, such as in a function call: foo()
pass(expression)
}
}
// the declaration or definition of a function
function funcdef() {
cont(require("t_string"), require("("), pushlex(")"), commasep(funcarg), require(")"), poplex, block);
}
// Parses a comma-separated list of the things that are recognized
// by the 'what' argument.
function commasep(what){
function proceed(token) {
if (token.type == ",") cont(what, proceed);
};
return function commaSeparated() {
pass(what, proceed);
};
}
// Look for statements until a closing brace is found.
function block(token) {
if (token.type == "}") cont();
else pass(statement, block);
}
function maybedefaultparameter(token){
if (token.content == "=") cont(expression);
}
// support for default arguments: http://us.php.net/manual/en/functions.arguments.php#functions.arguments.default
function funcarg(token){
// function foo(myclass $obj) {...}
if (token.type == "t_string") cont(require("variable"), maybedefaultparameter);
// function foo($string) {...}
else if (token.type == "variable") cont(maybedefaultparameter);
}
// A namespace definition or use
function maybe_double_colon_def(token) {
if (token.type == "t_double_colon")
cont(namespacedef);
}
function namespacedef(token) {
pass(require("t_string"), maybe_double_colon_def);
}
return parser;
}
return {make: parsePHP, electricChars: "{}:"};
})();

View File

@@ -0,0 +1,90 @@
/*
Copyright (c) 2008-2009 Yahoo! Inc. All rights reserved.
The copyrights embodied in the content of this file are licensed by
Yahoo! Inc. under the BSD (revised) open source license
@author Dan Vlad Dascalescu <dandv@yahoo-inc.com>
Based on parsehtmlmixed.js by Marijn Haverbeke.
*/
var PHPHTMLMixedParser = Editor.Parser = (function() {
if (!(PHPParser && CSSParser && JSParser && XMLParser))
throw new Error("PHP, CSS, JS, and XML parsers must be loaded for PHP+HTML mixed mode to work.");
XMLParser.configure({useHTMLKludges: true});
function parseMixed(stream) {
var htmlParser = XMLParser.make(stream), localParser = null, inTag = false;
var iter = {next: top, copy: copy};
function top() {
var token = htmlParser.next();
if (token.content == "<")
inTag = true;
else if (token.style == "xml-tagname" && inTag === true)
inTag = token.content.toLowerCase();
else if (token.type == "xml-processing") {
// dispatch on PHP
if (token.content == "<?php")
iter.next = local(PHPParser, "?>");
}
// "xml-processing" tokens are ignored, because they should be handled by a specific local parser
else if (token.content == ">") {
if (inTag == "script")
iter.next = local(JSParser, "</script");
else if (inTag == "style")
iter.next = local(CSSParser, "</style");
inTag = false;
}
return token;
}
function local(parser, tag) {
var baseIndent = htmlParser.indentation();
localParser = parser.make(stream, baseIndent + indentUnit);
return function() {
if (stream.lookAhead(tag, false, false, true)) {
localParser = null;
iter.next = top;
return top(); // pass the ending tag to the enclosing parser
}
var token = localParser.next();
var lt = token.value.lastIndexOf("<"), sz = Math.min(token.value.length - lt, tag.length);
if (lt != -1 && token.value.slice(lt, lt + sz).toLowerCase() == tag.slice(0, sz) &&
stream.lookAhead(tag.slice(sz), false, false, true)) {
stream.push(token.value.slice(lt));
token.value = token.value.slice(0, lt);
}
if (token.indentation) {
var oldIndent = token.indentation;
token.indentation = function(chars) {
if (chars == "</")
return baseIndent;
else
return oldIndent(chars);
}
}
return token;
};
}
function copy() {
var _html = htmlParser.copy(), _local = localParser && localParser.copy(),
_next = iter.next, _inTag = inTag;
return function(_stream) {
stream = _stream;
htmlParser = _html(_stream);
localParser = _local && _local(_stream);
iter.next = _next;
inTag = _inTag;
return iter;
};
}
return iter;
}
return {make: parseMixed, electricChars: "{}/:"};
})();

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,32 @@
Copyright (c) 2009, Timothy Farrell
All rights reserved.
This software is provided for use in connection with the
CodeMirror suite of modules and utilities, hosted and maintained
at http://marijn.haverbeke.nl/codemirror/.
Redistribution and use of this software in source and binary forms,
with or without modification, are permitted provided that the
following conditions are met:
* Redistributions of source code must retain the above
copyright notice, this list of conditions and the
following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other
materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,54 @@
.editbox {
padding: .4em;
margin: 0;
font-family: monospace;
font-size: 10pt;
line-height: 1.1em;
color: black;
}
pre.code, .editbox {
color: #666666;
}
.editbox p {
margin: 0;
}
span.py-delimiter, span.py-special {
color: #666666;
}
span.py-operator {
color: #666666;
}
span.py-error {
background-color: #660000;
color: #FFFFFF;
}
span.py-keyword {
color: #770088;
font-weight: bold;
}
span.py-literal {
color: #228811;
}
span.py-identifier, span.py-func {
color: black;
}
span.py-type, span.py-decorator {
color: #0000FF;
}
span.py-comment {
color: #AA7700;
}
span.py-string, span.py-bytes, span.py-raw, span.py-unicode {
color: #AA2222;
}

View File

@@ -0,0 +1,141 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<script src="../../js/codemirror.js" type="text/javascript"></script>
<title>CodeMirror: Python demonstration</title>
<style type="text/css">
.CodeMirror-line-numbers {
width: 2.2em;
color: #aaa;
background-color: #eee;
text-align: right;
padding: .4em;
margin: 0;
font-family: monospace;
font-size: 10pt;
line-height: 1.1em;
}
</style>
</head>
<body style="padding: 20px;">
<p>
This is a simple demonstration of the Python syntax highlighting module
for <a href="index.html">CodeMirror</a>.
</p>
<p>
Features of this parser include:
</p>
<ul>
<li>Token-based syntax highlighting - currently very little lexical analysis happens. Few lexical errors will be detected.</li>
<li>Use the normal indentation mode to enforce regular indentation, otherwise the "shift" indentation mode will give you more flexibility.</li>
<li>Parser Options:
<ul>
<li>pythonVersion (Integer) - 2 or 3 to indicate which version of Python to parse. Default = 2</li>
<li>strictErrors (Bool) - true to highlight errors that may not be Python errors but cause confusion for this parser. Default = true</li>
</ul>
</li>
</ul>
<p>Written by Timothy Farrell (<a href="LICENSE">license</a>). Special
thanks to Adam Brand and Marijn Haverbeke for their help in debugging
and providing for this parser.</p>
<div style="border: 1px solid black; padding: 0px;">
<textarea id="code" cols="100" rows="20" style="width:100%">
# Literals
1234
0.0e101
.123
0b01010011100
0o01234567
0x0987654321abcdef
# Error Literals
.0b000
0.0e
0e
# String Literals
'For\''
"God\""
"""so loved
the world"""
'''that he gave
his only begotten\' '''
'that whosoever believeth \
in him'
''
# Identifiers
__a__
a.b
a.b.c
# Error Identifiers
a.
# Operators
+ - * / % & | ^ ~ < >
== != <= >= <> << >> // **
and or not in is
# Delimiters
() [] {} , : ` = ; @ . # At-signs and periods require context
+= -= *= /= %= &= |= ^=
//= >>= <<= **=
# Keywords
as assert break class continue def del elif else except
finally for from global if import lambda pass raise
return try while with yield
# Python 2 Keywords (otherwise Identifiers)
exec print
# Python 3 Keywords (otherwise Identifiers)
nonlocal
# Types
bool classmethod complex dict enumerate float frozenset int list object
property reversed set slice staticmethod str super tuple type
# Python 2 Types (otherwise Identifiers)
basestring buffer file long unicode xrange
# Python 3 Types (otherwise Identifiers)
bytearray bytes filter map memoryview open range zip
# Example Strict Errors
def doesNothing():
pass # indentUnit is set to 4 but this line is indented 3
# Some Example code
import os
from package import ParentClass
@nonsenseDecorator
def doesNothing():
pass
class ExampleClass(ParentClass):
@staticmethod
def example(inputStr):
a = list(inputStr)
a.reverse()
return ''.join(a)
def __init__(self, mixin = 'Hello'):
self.mixin = mixin
</textarea>
</div>
<script type="text/javascript">
var editor = CodeMirror.fromTextArea('code', {
parserfile: ["../contrib/python/js/parsepython.js"],
stylesheet: "css/pythoncolors.css",
path: "../../js/",
lineNumbers: true,
textWrapping: false,
indentUnit: 4,
parserConfig: {'pythonVersion': 2, 'strictErrors': true}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,544 @@
var PythonParser = Editor.Parser = (function() {
function wordRegexp(words) {
return new RegExp("^(?:" + words.join("|") + ")$");
}
var DELIMITERCLASS = 'py-delimiter';
var LITERALCLASS = 'py-literal';
var ERRORCLASS = 'py-error';
var OPERATORCLASS = 'py-operator';
var IDENTIFIERCLASS = 'py-identifier';
var STRINGCLASS = 'py-string';
var BYTESCLASS = 'py-bytes';
var UNICODECLASS = 'py-unicode';
var RAWCLASS = 'py-raw';
var NORMALCONTEXT = 'normal';
var STRINGCONTEXT = 'string';
var singleOperators = '+-*/%&|^~<>';
var doubleOperators = wordRegexp(['==', '!=', '\\<=', '\\>=', '\\<\\>',
'\\<\\<', '\\>\\>', '\\/\\/', '\\*\\*']);
var singleDelimiters = '()[]{}@,:.`=;';
var doubleDelimiters = ['\\+=', '\\-=', '\\*=', '/=', '%=', '&=', '\\|=',
'\\^='];
var tripleDelimiters = wordRegexp(['//=','\\>\\>=','\\<\\<=','\\*\\*=']);
var singleStarters = singleOperators + singleDelimiters + '=!';
var doubleStarters = '=<>*/';
var identifierStarters = /[_A-Za-z]/;
var wordOperators = wordRegexp(['and', 'or', 'not', 'is', 'in']);
var commonkeywords = ['as', 'assert', 'break', 'class', 'continue',
'def', 'del', 'elif', 'else', 'except', 'finally',
'for', 'from', 'global', 'if', 'import',
'lambda', 'pass', 'raise', 'return',
'try', 'while', 'with', 'yield'];
var commontypes = ['bool', 'classmethod', 'complex', 'dict', 'enumerate',
'float', 'frozenset', 'int', 'list', 'object',
'property', 'reversed', 'set', 'slice', 'staticmethod',
'str', 'super', 'tuple', 'type'];
var py2 = {'types': ['basestring', 'buffer', 'file', 'long', 'unicode',
'xrange'],
'keywords': ['exec', 'print'],
'version': 2 };
var py3 = {'types': ['bytearray', 'bytes', 'filter', 'map', 'memoryview',
'open', 'range', 'zip'],
'keywords': ['nonlocal'],
'version': 3};
var py, keywords, types, stringStarters, stringTypes, config;
function configure(conf) {
if (!conf.hasOwnProperty('pythonVersion')) {
conf.pythonVersion = 2;
}
if (!conf.hasOwnProperty('strictErrors')) {
conf.strictErrors = true;
}
if (conf.pythonVersion != 2 && conf.pythonVersion != 3) {
alert('CodeMirror: Unknown Python Version "' +
conf.pythonVersion +
'", defaulting to Python 2.x.');
conf.pythonVersion = 2;
}
if (conf.pythonVersion == 3) {
py = py3;
stringStarters = /[\'\"rbRB]/;
stringTypes = /[rb]/;
doubleDelimiters.push('\\-\\>');
} else {
py = py2;
stringStarters = /['"RUru]/;
stringTypes = /[ru]/;
}
config = conf;
keywords = wordRegexp(commonkeywords.concat(py.keywords));
types = wordRegexp(commontypes.concat(py.types));
doubleDelimiters = wordRegexp(doubleDelimiters);
}
var tokenizePython = (function() {
function normal(source, setState) {
var stringDelim, threeStr, temp, type, word, possible = {};
var ch = source.next();
function filterPossible(token, styleIfPossible) {
if (!possible.style && !possible.content) {
return token;
} else if (typeof(token) == STRINGCONTEXT) {
token = {content: source.get(), style: token};
}
if (possible.style || styleIfPossible) {
token.style = styleIfPossible ? styleIfPossible : possible.style;
}
if (possible.content) {
token.content = possible.content + token.content;
}
possible = {};
return token;
}
// Handle comments
if (ch == '#') {
while (!source.endOfLine()) {
source.next();
}
return 'py-comment';
}
// Handle special chars
if (ch == '\\') {
if (source.peek() != '\n') {
var whitespace = true;
while (!source.endOfLine()) {
if(!(/\s/.test(source.next()))) {
whitespace = false;
}
}
if (!whitespace) {
return ERRORCLASS;
}
}
return 'py-special';
}
// Handle operators and delimiters
if (singleStarters.indexOf(ch) != -1) {
if (doubleStarters.indexOf(source.peek()) != -1) {
temp = ch + source.peek();
// It must be a double delimiter or operator or triple delimiter
if (doubleOperators.test(temp)) {
source.next();
if (tripleDelimiters.test(temp + source.peek())) {
source.next();
return DELIMITERCLASS;
} else {
return OPERATORCLASS;
}
} else if (doubleDelimiters.test(temp)) {
source.next();
return DELIMITERCLASS;
}
}
// It must be a single delimiter or operator
if (singleOperators.indexOf(ch) != -1) {
return OPERATORCLASS;
} else if (singleDelimiters.indexOf(ch) != -1) {
if (ch == '@' && /\w/.test(source.peek())) {
possible = {style:'py-decorator',
content: source.get()};
ch = source.next();
} else if (ch == '.' && /\d/.test(source.peek())) {
possible = {style:LITERALCLASS,
content: source.get()};
ch = source.next();
} else {
return DELIMITERCLASS;
}
} else {
return ERRORCLASS;
}
}
// Handle number literals
if (/\d/.test(ch)) {
if (ch === '0' && !source.endOfLine()) {
switch (source.peek()) {
case 'o':
case 'O':
source.next();
source.nextWhileMatches(/[0-7]/);
return filterPossible(LITERALCLASS, ERRORCLASS);
case 'x':
case 'X':
source.next();
source.nextWhileMatches(/[0-9A-Fa-f]/);
return filterPossible(LITERALCLASS, ERRORCLASS);
case 'b':
case 'B':
source.next();
source.nextWhileMatches(/[01]/);
return filterPossible(LITERALCLASS, ERRORCLASS);
}
}
source.nextWhileMatches(/\d/);
if (source.peek() == '.') {
source.next();
source.nextWhileMatches(/\d/);
}
// Grab an exponent
if (source.peek().toLowerCase() == 'e') {
source.next();
if (source.peek() == '+' || source.peek() == '-') {
source.next();
}
if (/\d/.test(source.peek())) {
source.nextWhileMatches(/\d/);
} else {
return filterPossible(ERRORCLASS);
}
}
// Grab a complex number
if (source.peek().toLowerCase() == 'j') {
source.next();
}
return filterPossible(LITERALCLASS);
}
// Handle strings
if (stringStarters.test(ch)) {
var peek = source.peek();
var stringType = STRINGCLASS;
if ((stringTypes.test(ch)) && (peek == '"' || peek == "'")) {
switch (ch.toLowerCase()) {
case 'b':
stringType = BYTESCLASS;
break;
case 'r':
stringType = RAWCLASS;
break;
case 'u':
stringType = UNICODECLASS;
break;
}
ch = source.next();
stringDelim = ch;
if (source.peek() != stringDelim) {
setState(inString(stringType, stringDelim));
return null;
} else {
source.next();
if (source.peek() == stringDelim) {
source.next();
threeStr = stringDelim + stringDelim + stringDelim;
setState(inString(stringType, threeStr));
return null;
} else {
return stringType;
}
}
} else if (ch == "'" || ch == '"') {
stringDelim = ch;
if (source.peek() != stringDelim) {
setState(inString(stringType, stringDelim));
return null;
} else {
source.next();
if (source.peek() == stringDelim) {
source.next();
threeStr = stringDelim + stringDelim + stringDelim;
setState(inString(stringType, threeStr));
return null;
} else {
return stringType;
}
}
}
}
// Handle Identifier
if (identifierStarters.test(ch)) {
source.nextWhileMatches(/[\w\d]/);
word = source.get();
if (wordOperators.test(word)) {
type = OPERATORCLASS;
} else if (keywords.test(word)) {
type = 'py-keyword';
} else if (types.test(word)) {
type = 'py-type';
} else {
type = IDENTIFIERCLASS;
while (source.peek() == '.') {
source.next();
if (identifierStarters.test(source.peek())) {
source.nextWhileMatches(/[\w\d]/);
} else {
type = ERRORCLASS;
break;
}
}
word = word + source.get();
}
return filterPossible({style: type, content: word});
}
// Register Dollar sign and Question mark as errors. Always!
if (/\$\?/.test(ch)) {
return filterPossible(ERRORCLASS);
}
return filterPossible(ERRORCLASS);
}
function inString(style, terminator) {
return function(source, setState) {
var matches = [];
var found = false;
while (!found && !source.endOfLine()) {
var ch = source.next(), newMatches = [];
// Skip escaped characters
if (ch == '\\') {
if (source.peek() == '\n') {
break;
}
ch = source.next();
ch = source.next();
}
if (ch == terminator.charAt(0)) {
matches.push(terminator);
}
for (var i = 0; i < matches.length; i++) {
var match = matches[i];
if (match.charAt(0) == ch) {
if (match.length == 1) {
setState(normal);
found = true;
break;
} else {
newMatches.push(match.slice(1));
}
}
}
matches = newMatches;
}
return style;
};
}
return function(source, startState) {
return tokenizer(source, startState || normal);
};
})();
function parsePython(source) {
if (!keywords) {
configure({});
}
var tokens = tokenizePython(source);
var lastToken = null;
var column = 0;
var context = {prev: null,
endOfScope: false,
startNewScope: false,
level: 0,
next: null,
type: NORMALCONTEXT
};
function pushContext(level, type) {
type = type ? type : NORMALCONTEXT;
context = {prev: context,
endOfScope: false,
startNewScope: false,
level: level,
next: null,
type: type
};
}
function popContext(remove) {
remove = remove ? remove : false;
if (context.prev) {
if (remove) {
context = context.prev;
context.next = null;
} else {
context.prev.next = context;
context = context.prev;
}
}
}
function indentPython(context) {
var temp;
return function(nextChars, currentLevel, direction) {
if (direction === null || direction === undefined) {
if (nextChars) {
while (context.next) {
context = context.next;
}
}
return context.level;
}
else if (direction === true) {
if (currentLevel == context.level) {
if (context.next) {
return context.next.level;
} else {
return context.level;
}
} else {
temp = context;
while (temp.prev && temp.prev.level > currentLevel) {
temp = temp.prev;
}
return temp.level;
}
} else if (direction === false) {
if (currentLevel > context.level) {
return context.level;
} else if (context.prev) {
temp = context;
while (temp.prev && temp.prev.level >= currentLevel) {
temp = temp.prev;
}
if (temp.prev) {
return temp.prev.level;
} else {
return temp.level;
}
}
}
return context.level;
};
}
var iter = {
next: function() {
var token = tokens.next();
var type = token.style;
var content = token.content;
if (lastToken) {
if (lastToken.content == 'def' && type == IDENTIFIERCLASS) {
token.style = 'py-func';
}
if (lastToken.content == '\n') {
var tempCtx = context;
// Check for a different scope
if (type == 'whitespace' && context.type == NORMALCONTEXT) {
if (token.value.length < context.level) {
while (token.value.length < context.level) {
popContext();
}
if (token.value.length != context.level) {
context = tempCtx;
if (config.strictErrors) {
token.style = ERRORCLASS;
}
} else {
context.next = null;
}
}
} else if (context.level !== 0 &&
context.type == NORMALCONTEXT) {
while (0 !== context.level) {
popContext();
}
if (context.level !== 0) {
context = tempCtx;
if (config.strictErrors) {
token.style = ERRORCLASS;
}
}
}
}
}
// Handle Scope Changes
switch(type) {
case STRINGCLASS:
case BYTESCLASS:
case RAWCLASS:
case UNICODECLASS:
if (context.type !== STRINGCONTEXT) {
pushContext(context.level + 1, STRINGCONTEXT);
}
break;
default:
if (context.type === STRINGCONTEXT) {
popContext(true);
}
break;
}
switch(content) {
case '.':
case '@':
// These delimiters don't appear by themselves
if (content !== token.value) {
token.style = ERRORCLASS;
}
break;
case ':':
// Colons only delimit scope inside a normal scope
if (context.type === NORMALCONTEXT) {
context.startNewScope = context.level+indentUnit;
}
break;
case '(':
case '[':
case '{':
// These start a sequence scope
pushContext(column + content.length, 'sequence');
break;
case ')':
case ']':
case '}':
// These end a sequence scope
popContext(true);
break;
case 'pass':
case 'return':
// These end a normal scope
if (context.type === NORMALCONTEXT) {
context.endOfScope = true;
}
break;
case '\n':
// Reset our column
column = 0;
// Make any scope changes
if (context.endOfScope) {
context.endOfScope = false;
popContext();
} else if (context.startNewScope !== false) {
var temp = context.startNewScope;
context.startNewScope = false;
pushContext(temp, NORMALCONTEXT);
}
// Newlines require an indentation function wrapped in a closure for proper context.
token.indentation = indentPython(context);
break;
}
// Keep track of current column for certain scopes.
if (content != '\n') {
column += token.value.length;
}
lastToken = token;
return token;
},
copy: function() {
var _context = context, _tokenState = tokens.state;
return function(source) {
tokens = tokenizePython(source, _tokenState);
context = _context;
return iter;
};
}
};
return iter;
}
return {make: parsePython,
electricChars: "",
configure: configure};
})();