diff --git a/requirements.txt b/requirements.txt
index 07b53d2ad..2d434825d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,4 +3,5 @@ flask-babel
requests
lxml
pyyaml
+pygments
python-dateutil
diff --git a/searx/engines/searchcode_code.py b/searx/engines/searchcode_code.py
index 0f98352c1..655818da2 100644
--- a/searx/engines/searchcode_code.py
+++ b/searx/engines/searchcode_code.py
@@ -10,7 +10,7 @@
from urllib import urlencode
from json import loads
-import cgi
+
# engine dependent config
categories = ['it']
@@ -20,6 +20,12 @@ paging = True
url = 'https://searchcode.com/'
search_url = url+'api/codesearch_I/?{query}&p={pageno}'
+# special code-endings which are not recognised by the file ending
+code_endings = {'cs': 'c#',
+ 'h': 'c',
+ 'hpp': 'cpp',
+ 'cxx': 'cpp'}
+
# do search-request
def request(query, params):
@@ -39,27 +45,24 @@ def response(resp):
for result in search_results['results']:
href = result['url']
title = "" + result['name'] + " - " + result['filename']
- content = result['repo'] + "
"
+ repo = result['repo']
lines = dict()
for line, code in result['lines'].items():
lines[int(line)] = code
- content = content + '
| ' - content = content + str(line) + ' | ' - # Replace every two spaces with ' &nbps;' to keep formatting - # while allowing the browser to break the line if necessary - content = content + cgi.escape(code).replace('\t', ' ').replace(' ', ' ').replace(' ', ' ') - content = content + " |
could not load data!
')})}}$(this).off(a)}),$(".searx_init_map").on("click",function(a){var b=$(this).data("leaflet-target"),c=$(this).data("map-lon"),d=$(this).data("map-lat"),e=$(this).data("map-zoom"),f=$(this).data("map-boundingbox"),g=$(this).data("map-geojson");require(["leaflet-0.7.3.min"],function(){f&&(southWest=L.latLng(f[0],f[2]),northEast=L.latLng(f[1],f[3]),map_bounds=L.latLngBounds(southWest,northEast)),L.Icon.Default.imagePath="./static/themes/oscar/img/map";{var a=L.map(b),h="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",i='Map data © OpenStreetMap contributors',j=new L.TileLayer(h,{minZoom:1,maxZoom:19,attribution:i}),k="http://otile{s}.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg",l='Map data © OpenStreetMap contributors | Tiles Courtesy of MapQuest
',m=new L.TileLayer(k,{minZoom:1,maxZoom:18,subdomains:"1234",attribution:l}),n="http://otile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg",o='Map data © OpenStreetMap contributors | Tiles Courtesy of MapQuest
| Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency';new L.TileLayer(n,{minZoom:1,maxZoom:11,subdomains:"1234",attribution:o})}map_bounds?setTimeout(function(){a.fitBounds(map_bounds,{maxZoom:17})},0):c&&d&&(e?a.setView(new L.LatLng(d,c),e):a.setView(new L.LatLng(d,c),8)),a.addLayer(m);var p={"OSM Mapnik":j,MapQuest:m};L.control.layers(p).addTo(a),g&&L.geoJson(g).addTo(a)}),$(this).off(a)})});
\ No newline at end of file
diff --git a/searx/static/themes/oscar/less/oscar/code.less b/searx/static/themes/oscar/less/oscar/code.less
new file mode 100644
index 000000000..90a2cd60c
--- /dev/null
+++ b/searx/static/themes/oscar/less/oscar/code.less
@@ -0,0 +1,79 @@
+.highlight .hll { background-color: #ffffcc }
+.highlight { background: #f8f8f8; }
+.highlight .c { color: #408080; font-style: italic } /* Comment */
+.highlight .err { border: 1px solid #FF0000 } /* Error */
+.highlight .k { color: #008000; font-weight: bold } /* Keyword */
+.highlight .o { color: #666666 } /* Operator */
+.highlight .cm { color: #408080; font-style: italic } /* Comment.Multiline */
+.highlight .cp { color: #BC7A00 } /* Comment.Preproc */
+.highlight .c1 { color: #408080; font-style: italic } /* Comment.Single */
+.highlight .cs { color: #408080; font-style: italic } /* Comment.Special */
+.highlight .gd { color: #A00000 } /* Generic.Deleted */
+.highlight .ge { font-style: italic } /* Generic.Emph */
+.highlight .gr { color: #FF0000 } /* Generic.Error */
+.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
+.highlight .gi { color: #00A000 } /* Generic.Inserted */
+.highlight .go { color: #888888 } /* Generic.Output */
+.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
+.highlight .gs { font-weight: bold } /* Generic.Strong */
+.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
+.highlight .gt { color: #0044DD } /* Generic.Traceback */
+.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
+.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
+.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
+.highlight .kp { color: #008000 } /* Keyword.Pseudo */
+.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
+.highlight .kt { color: #B00040 } /* Keyword.Type */
+.highlight .m { color: #666666 } /* Literal.Number */
+.highlight .s { color: #BA2121 } /* Literal.String */
+.highlight .na { color: #7D9029 } /* Name.Attribute */
+.highlight .nb { color: #008000 } /* Name.Builtin */
+.highlight .nc { color: #0000FF; font-weight: bold } /* Name.Class */
+.highlight .no { color: #880000 } /* Name.Constant */
+.highlight .nd { color: #AA22FF } /* Name.Decorator */
+.highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */
+.highlight .ne { color: #D2413A; font-weight: bold } /* Name.Exception */
+.highlight .nf { color: #0000FF } /* Name.Function */
+.highlight .nl { color: #A0A000 } /* Name.Label */
+.highlight .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */
+.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
+.highlight .nv { color: #19177C } /* Name.Variable */
+.highlight .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */
+.highlight .w { color: #bbbbbb } /* Text.Whitespace */
+.highlight .mf { color: #666666 } /* Literal.Number.Float */
+.highlight .mh { color: #666666 } /* Literal.Number.Hex */
+.highlight .mi { color: #666666 } /* Literal.Number.Integer */
+.highlight .mo { color: #666666 } /* Literal.Number.Oct */
+.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
+.highlight .sc { color: #BA2121 } /* Literal.String.Char */
+.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
+.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
+.highlight .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */
+.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
+.highlight .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */
+.highlight .sx { color: #008000 } /* Literal.String.Other */
+.highlight .sr { color: #BB6688 } /* Literal.String.Regex */
+.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
+.highlight .ss { color: #19177C } /* Literal.String.Symbol */
+.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
+.highlight .vc { color: #19177C } /* Name.Variable.Class */
+.highlight .vg { color: #19177C } /* Name.Variable.Global */
+.highlight .vi { color: #19177C } /* Name.Variable.Instance */
+.highlight .il { color: #666666 } /* Literal.Number.Integer.Long */
+
+.highlight .lineno {
+ -webkit-touch-callout: none;
+ -webkit-user-select: none;
+ -khtml-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ cursor: default;
+
+ &::selection {
+ background: transparent; /* WebKit/Blink Browsers */
+ }
+ &::-moz-selection {
+ background: transparent; /* Gecko Browsers */
+ }
+}
diff --git a/searx/static/themes/oscar/less/oscar/oscar.less b/searx/static/themes/oscar/less/oscar/oscar.less
index 22f7558ad..6a8478530 100644
--- a/searx/static/themes/oscar/less/oscar/oscar.less
+++ b/searx/static/themes/oscar/less/oscar/oscar.less
@@ -9,3 +9,5 @@
@import "search.less";
@import "cursor.less";
+
+@import "code.less";
diff --git a/searx/static/themes/oscar/less/oscar/results.less b/searx/static/themes/oscar/less/oscar/results.less
index 08500b3ff..cea8f3b41 100644
--- a/searx/static/themes/oscar/less/oscar/results.less
+++ b/searx/static/themes/oscar/less/oscar/results.less
@@ -55,6 +55,11 @@
clear: both;
}
+// code formating of results
+.result-code {
+ clear: both;
+}
+
// suggestion
.suggestion_item {
margin: 2px 5px;
diff --git a/searx/static/themes/oscar/package.json b/searx/static/themes/oscar/package.json
index 945b7943d..7eae9df2b 100644
--- a/searx/static/themes/oscar/package.json
+++ b/searx/static/themes/oscar/package.json
@@ -4,7 +4,8 @@
"grunt-contrib-uglify": "~0.6.0",
"grunt-contrib-watch" : "~0.6.1",
"grunt-contrib-concat" : "~0.5.0",
- "grunt-contrib-jshint" : "~0.10.0"
+ "grunt-contrib-jshint" : "~0.10.0",
+ "grunt-contrib-less" : "~0.11.0"
},
"scripts": {
diff --git a/searx/templates/courgette/result_templates/code.html b/searx/templates/courgette/result_templates/code.html
new file mode 100644
index 000000000..616b7ea62
--- /dev/null
+++ b/searx/templates/courgette/result_templates/code.html
@@ -0,0 +1,9 @@
+{{ result.pretty_url }} cached
+ {% if result.publishedDate %}{{ result.publishedDate }}
{% endif %} +{% if result.img_src %}{% endif %}{% if result.content %}{{ result.content|safe }}
{% endif %}
{{ result.pretty_url }} cached
+ {% if result.publishedDate %}{{ result.publishedDate }}
{% endif %} +{% if result.img_src %}{% endif %}{% if result.content %}{{ result.content|safe }}
{% endif %}
{{ result.pretty_url }}
+{%- endmacro %} diff --git a/searx/templates/oscar/result_templates/code.html b/searx/templates/oscar/result_templates/code.html new file mode 100644 index 000000000..e608bb04f --- /dev/null +++ b/searx/templates/oscar/result_templates/code.html @@ -0,0 +1,12 @@ +{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, icon %} + +{{ result_header(result, favicons) }} +{{ result_sub_header(result) }} + +{% if result.content %}{{ result.content|safe }}
{% endif %} + +{% if result.repository %}{{ icon('file') }} {{ result.repository }}
{% endif %} + +{{ result.codelines|code_highlighter(result.code_language)|safe }} + +{{ result_footer(result) }} diff --git a/searx/templates/oscar/result_templates/default.html b/searx/templates/oscar/result_templates/default.html index 23af61f21..2be06642a 100644 --- a/searx/templates/oscar/result_templates/default.html +++ b/searx/templates/oscar/result_templates/default.html @@ -1,9 +1,7 @@ -{% from 'oscar/macros.html' import icon %} +{% from 'oscar/macros.html' import result_header, result_sub_header, result_footer, icon %} -{{ result.content|safe }}
{% endif %} - - -{{ result.engine }} -{{ result.pretty_url }}
+{{ result_footer(result) }} diff --git a/searx/templates/oscar/result_templates/images.html b/searx/templates/oscar/result_templates/images.html index 94627c9b5..7ad4e2435 100644 --- a/searx/templates/oscar/result_templates/images.html +++ b/searx/templates/oscar/result_templates/images.html @@ -1,3 +1,5 @@ +{% from 'oscar/macros.html' import draw_favicon %} +{{ result.pretty_url }}
+{{ result_footer(result) }} diff --git a/searx/webapp.py b/searx/webapp.py index 8ed4cc7c1..877e40ddc 100644 --- a/searx/webapp.py +++ b/searx/webapp.py @@ -48,6 +48,14 @@ from searx.search import Search from searx.query import Query from searx.autocomplete import searx_bang, backends as autocomplete_backends from searx import logger +try: + from pygments import highlight + from pygments.lexers import get_lexer_by_name + from pygments.formatters import HtmlFormatter +except: + logger.critical("cannot import dependency: pygments") + from sys import exit + exit(1) logger = logger.getChild('webapp') @@ -101,6 +109,55 @@ def get_locale(): return locale +# code-highlighter +@app.template_filter('code_highlighter') +def code_highlighter(codelines, language=None): + if not language: + language = 'text' + + try: + # find lexer by programing language + lexer = get_lexer_by_name(language, stripall=True) + except: + # if lexer is not found, using default one + logger.debug('highlighter cannot find lexer for {0}'.format(language)) + lexer = get_lexer_by_name('text', stripall=True) + + html_code = '' + tmp_code = '' + last_line = None + + # parse lines + for line, code in codelines: + if not last_line: + line_code_start = line + + # new codeblock is detected + if last_line is not None and\ + last_line + 1 != line: + + # highlight last codepart + formatter = HtmlFormatter(linenos='inline', + linenostart=line_code_start) + html_code = html_code + highlight(tmp_code, lexer, formatter) + + # reset conditions for next codepart + tmp_code = '' + line_code_start = line + + # add codepart + tmp_code += code + '\n' + + # update line + last_line = line + + # highlight last codepart + formatter = HtmlFormatter(linenos='inline', linenostart=line_code_start) + html_code = html_code + highlight(tmp_code, lexer, formatter) + + return html_code + + def get_base_url(): if settings['server']['base_url']: hostname = settings['server']['base_url'] diff --git a/setup.py b/setup.py index d976a31f7..1c1a19ddf 100644 --- a/setup.py +++ b/setup.py @@ -44,6 +44,7 @@ setup( 'requests', 'lxml', 'pyyaml', + 'pygments', 'setuptools', 'python-dateutil', ], diff --git a/versions.cfg b/versions.cfg index 2f5dae8ee..7f1734908 100644 --- a/versions.cfg +++ b/versions.cfg @@ -4,6 +4,7 @@ Flask = 0.10.1 Flask-Babel = 0.9 Jinja2 = 2.7.2 MarkupSafe = 0.18 +Pygments = 2.0.1 WebOb = 1.3.1 WebTest = 2.0.11 Werkzeug = 0.9.4