Skip to content

Commit

Permalink
Script editor: Basic hints for unknown modes (#800)
Browse files Browse the repository at this point in the history
(Slightly) less basic hints for Python

Make "add from textual definition" & sitemap code editor
splits resizable.

Signed-off-by: Yannick Schaus <github@schaus.net>
  • Loading branch information
ghys authored Jan 13, 2021
1 parent 60e35a1 commit 49c8fb2
Show file tree
Hide file tree
Showing 5 changed files with 248 additions and 113 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import 'codemirror/addon/edit/closebrackets.js'
// for autocomplete
import 'codemirror/addon/hint/show-hint.js'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/hint/anyword-hint.js'
import 'codemirror/addon/dialog/dialog.js'
import 'codemirror/addon/dialog/dialog.css'
import 'codemirror/addon/tern/tern.js'
Expand Down Expand Up @@ -94,6 +95,7 @@ import OpenhabDefs from '@/assets/openhab-tern-defs.json'
import componentsHint from '../editor/hint-components'
import rulesHint from '../editor/hint-rules'
import thingsHint from '../editor/hint-things'
import pythonHint from '../editor/hint-python'
// Adapted from https://github.com/lkcampbell/brackets-indent-guides (MIT)
var indentGuidesOverlay = {
Expand Down Expand Up @@ -257,13 +259,18 @@ export default {
if (this.hintContext) cm.state.hintContext = Object.assign({}, this.hintContext)
cm.setOption('hintOptions', {
closeOnUnfocus: false,
completeSingle: self.mode && self.mode.indexOf('yaml') > 0,
hint (cm, option) {
if (self.mode.indexOf('application/vnd.openhab.uicomponent') === 0) {
return componentsHint(cm, option, self.mode)
} else if (self.mode === 'application/vnd.openhab.rule+yaml') {
return rulesHint(cm, option, self.mode)
} else if (self.mode === 'application/vnd.openhab.thing+yaml') {
return thingsHint(cm, option, self.mode)
} else if (self.mode === 'application/python') {
return pythonHint(cm, option, self.mode)
} else {
return _CodeMirror.hint.anyword(cm, option, self.mode)
}
}
})
Expand Down
103 changes: 103 additions & 0 deletions bundles/org.openhab.ui/web/src/components/config/editor/hint-python.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/* This code is adapted from https://github.com/mildsunrise/CodeMirror/blob/master/addon/hint/python-hint.js */

import CodeMirror from 'codemirror'

function forEach (arr, f) {
for (var i = 0, e = arr.length; i < e; ++i) f(arr[i])
}

function arrayContains (arr, item) {
if (!Array.prototype.indexOf) {
var i = arr.length
while (i--) {
if (arr[i] === item) {
return true
}
}
return false
}
return arr.indexOf(item) !== -1
}

function scriptHint (editor, _keywords, getToken) {
// Find the token at the cursor
var cur = editor.getCursor(), token = getToken(editor, cur), tprop = token
// If it's not a 'word-style' token, ignore the token.

if (!/^[\w$_]*$/.test(token.string)) {
token = tprop = { start: cur.ch,
end: cur.ch,
string: '',
state: token.state,
className: token.string === ':' ? 'python-type' : null }
}

let context
if (!context) context = []
context.push(tprop)

var completionList = getCompletions(token, context)
completionList = completionList.sort()
// prevent autocomplete for last word, instead show dropdown with one word
if (completionList.length === 1) {
completionList.push(' ')
}

return { list: completionList,
from: CodeMirror.Pos(cur.line, token.start),
to: CodeMirror.Pos(cur.line, token.end) }
}

CodeMirror.pythonHint = function (editor) {
const scriptHints = scriptHint(editor, pythonKeywordsU, function (e, cur) { return e.getTokenAt(cur) })
const otherHints = CodeMirror.hint.anyword(editor)
return {
from: otherHints.from,
to: otherHints.to,
list: Array.from(new Set([...scriptHints.list, ...otherHints.list])).sort()
}
}

export default CodeMirror.pythonHint

var pythonKeywords = 'and del from not while as elif global or with assert else if pass yield' +
'break except import print class exec in raise continue finally is return def for lambda try'
var pythonKeywordsL = pythonKeywords.split(' ')
var pythonKeywordsU = pythonKeywords.toUpperCase().split(' ')

var pythonBuiltins = 'abs divmod input open staticmethod all enumerate int ord str ' +
'any eval isinstance pow sum basestring execfile issubclass print super' +
'bin file iter property tuple bool filter len range type' +
'bytearray float list raw_input unichr callable format locals reduce unicode' +
'chr frozenset long reload vars classmethod getattr map repr xrange' +
'cmp globals max reversed zip compile hasattr memoryview round __import__' +
'complex hash min set apply delattr help next setattr buffer' +
'dict hex object slice coerce dir id oct sorted intern '
var pythonBuiltinsL = pythonBuiltins.split(' ').join('() ').split(' ')
var pythonBuiltinsU = pythonBuiltins.toUpperCase().split(' ').join('() ').split(' ')

function getCompletions (token, context) {
var found = [], start = token.string
function maybeAdd (str) {
if (str.indexOf(start) === 0 && !arrayContains(found, str)) found.push(str)
}

function gatherCompletions (_obj) {
forEach(pythonBuiltinsL, maybeAdd)
forEach(pythonBuiltinsU, maybeAdd)
forEach(pythonKeywordsL, maybeAdd)
forEach(pythonKeywordsU, maybeAdd)
}

if (context) {
// If this is a property, see if it belongs to some object we can
// find in the current environment.
var obj = context.pop(), base

if (obj.type === 'variable') { base = obj.string } else if (obj.type === 'variable-3') { base = ':' + obj.string }

while (base != null && context.length) { base = base[context.pop().string] }
if (base != null) gatherCompletions(base)
}
return found
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,49 @@
<template>
<div class="row">
<div class="col">
<editor class="sitemap-parser" :value="sitemapDsl" @input="updateSitemap" />
<div v-if="parsedSitemap.error" class="sitemap-results error">
<pre><code class="text-color-red">{{parsedSitemap.error}}</code></pre>
<f7-block class="sitemap-code">
<div class="row sitemap-parser resizable">
<div class="col">
<editor :value="sitemapDsl" @input="updateSitemap" mode="application/vnd.openhab.sitemap+dsl" />
</div>
<div v-else class="sitemap-results">
<pre><code class="text-color-teal">Your sitemap definition looks valid.</code></pre>
<pre><code>{{parsedSitemap}}</code></pre>
<span class="resize-handler"></span>
</div>
<div class="row sitemap-results resizable">
<div class="col">
<div v-if="parsedSitemap.error" class="error">
<pre><code class="text-color-red">{{parsedSitemap.error}}</code></pre>
</div>
<div v-else>
<pre><code class="text-color-teal">Your sitemap definition looks valid.</code></pre>
<pre><code>{{parsedSitemap}}</code></pre>
</div>
</div>
<span class="resize-handler"></span>
</div>
</div>
</f7-block>
</template>

<style lang="stylus">
.sitemap-parser.vue-codemirror
display block
top calc(var(--f7-navbar-height) + var(--f7-tabbar-height))
height calc(50% - 2*var(--f7-navbar-height))
width 100%
.sitemap-results
position absolute
top 50%
height 50%
overflow-y auto
width 100%
&.error
.sitemap-code
margin-top 0 !important
margin-bottom 0 !important
padding 0
z-index auto !important
top 0
height calc(100%)
.sitemap-parser
height 50%
width 100%
.vue-codemirror
top 0
height calc(100% - var(--f7-grid-gap))
.sitemap-results
height 50%
width 100%
overflow-y auto
.error
pre
padding 0 1rem
pre
padding 0 1rem
pre
padding 0 1rem
</style>

<script>
Expand Down
Loading

0 comments on commit 49c8fb2

Please sign in to comment.