Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Screen autocomplete #97

Merged
merged 3 commits into from
Aug 26, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
# All changes Copyright 2022, OpenC3, Inc.
# All Rights Reserved

require 'openc3/config/meta_config_parser'

class ScriptAutocompleteController < ApplicationController
CMD_KEYWORDS = %w(cmd cmd_no_range_check cmd_no_hazardous_check cmd_no_checks
cmd_raw cmd_raw_no_range_check cmd_raw_no_hazardous_check cmd_raw_no_checks)
Expand All @@ -33,7 +35,14 @@ def get_reserved_item_names
end

def get_keywords
keywords = params[:type].upcase == 'TLM' ? TLM_KEYWORDS : CMD_KEYWORDS
keywords = case params[:type].upcase
when 'CMD'
CMD_KEYWORDS
when 'TLM'
TLM_KEYWORDS
when 'SCREEN'
get_screen_keywords()
end
render :json => keywords, :status => 200
end

Expand All @@ -45,13 +54,38 @@ def get_ace_autocomplete_data
end

# private

def get_screen_keywords
OpenC3::MetaConfigParser.load(File.join(OpenC3::PATH, 'data', 'config', 'screen.yaml')).keys.sort
end

def build_autocomplete_data(type, scope)
autocomplete_data = OpenC3::TargetModel.all(scope: scope).flat_map do |target_name, target_info|
OpenC3::TargetModel.packets(target_name, type: type.upcase.intern, scope: scope).flat_map do |packet|
packet_to_autocomplete_hashes(packet, target_info, type)
if type.upcase == 'SCREEN'
yaml = OpenC3::MetaConfigParser.load(File.join(OpenC3::PATH, 'data', 'config', 'screen.yaml'))
yaml.sort.map.each do |keyword, data|
params = []
if data['parameters']
params = data['parameters'].collect { |param| param['name'] }
end
# The snippet is what gets put in the file when you autocomplete
# Thus we put the keyword with all the parameters surround by <>
# e.g. SCREEN <Width> <Height> <Polling Period>
snippet = keyword.dup
snippet << " <#{params.join('> <')}>" unless params.empty?
{
:caption => keyword,
:snippet => snippet,
:meta => 'widget',
}
end
else
autocomplete_data = OpenC3::TargetModel.all(scope: scope).flat_map do |target_name, target_info|
OpenC3::TargetModel.packets(target_name, type: type.upcase.intern, scope: scope).flat_map do |packet|
packet_to_autocomplete_hashes(packet, target_info, type)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't going to scale well to lots of packets.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worked ok for 1000 cmd packets generated by loadsim

end
end
autocomplete_data.sort_by { |packet| packet[:caption] }
end
autocomplete_data.sort_by { |packet| packet[:caption] }
end

def target_packet_name(packet)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
},
"dependencies": {
"@openc3/tool-common": "5.0.9-beta0",
"ace-builds": "1.8.1",
"date-fns": "2.29.1",
"lodash": "4.17.21",
"axios": "0.26.1",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

<template>
<!-- Edit dialog -->
<v-dialog v-model="show" width="600">
<v-dialog v-model="show" width="75vw">
<v-card>
<v-system-bar>
<div class="mx-2">
Expand Down Expand Up @@ -77,19 +77,14 @@
</v-col>
</v-row>
<v-row> Edit the screen definition. </v-row>
<v-row no-gutters>
<!-- TODO: Consider putting this in the Ace Editor for line number support -->
<v-textarea
v-model="currentDefinition"
rows="12"
:rules="[rules.required]"
data-test="screen-text-input"
/>
<v-row class="mb-2">
<pre id="editor"></pre>
</v-row>
<v-row v-for="(error, index) in editErrors" :key="index" class="my-3">
<span class="red--text" v-text="error"></span>
</v-row>
<v-row>
<span>Ctrl-space brings up autocomplete</span>
<v-spacer />
<v-btn
@click="$emit('cancel')"
Expand All @@ -100,7 +95,7 @@
Cancel
</v-btn>
<v-btn
@click="$emit('save', currentDefinition)"
@click="$emit('save', editor.getValue())"
class="mx-2"
color="primary"
data-test="editScreenSubmitBtn"
Expand All @@ -114,6 +109,13 @@
</template>

<script>
import * as ace from 'ace-builds'
import 'ace-builds/src-min-noconflict/mode-text'
import 'ace-builds/src-min-noconflict/theme-twilight'
import 'ace-builds/src-min-noconflict/ext-language_tools'
import 'ace-builds/src-min-noconflict/ext-searchbox'
import { ScreenCompleter } from '@/tools/TlmViewer/autocomplete'

export default {
props: {
value: Boolean, // value is the default prop when using v-model
Expand All @@ -129,6 +131,10 @@ export default {
type: String,
default: '',
},
keywords: {
type: Array,
default: () => [],
},
errors: {
type: Array,
default: () => [],
Expand Down Expand Up @@ -176,7 +182,72 @@ export default {
},
},
},
mounted: function () {
this.editor = ace.edit('editor')
this.editor.setTheme('ace/theme/twilight')
const screenMode = this.buildScreenMode()
this.editor.session.setMode(new screenMode())
this.editor.session.setTabSize(2)
this.editor.session.setUseWrapMode(true)
this.editor.$blockScrolling = Infinity
this.editor.setOption('enableBasicAutocompletion', true)
this.editor.setOption('enableLiveAutocompletion', true)
this.editor.completers = [new ScreenCompleter()]
this.editor.setHighlightActiveLine(false)
this.editor.setValue(this.currentDefinition)
this.editor.clearSelection()
this.editor.focus()
},
beforeDestroy() {
this.editor.destroy()
this.editor.container.remove()
},
methods: {
buildScreenMode() {
var oop = ace.require('ace/lib/oop')
var TextHighlightRules = ace.require(
'ace/mode/text_highlight_rules'
).TextHighlightRules

let list = this.keywords.join('|')
var OpenC3HighlightRules = function () {
this.$rules = {
start: [
{
token: 'comment',
regex: '#.*$',
},
{
token: 'string',
regex: '".*?"',
},
{
token: 'string',
regex: "'.*?'",
},
{
token: 'constant.numeric',
regex: '\\b\\d+(?:\\.\\d+)?\\b',
},
{
token: 'keyword',
regex: new RegExp(`^\\s*(${list})\\b`),
},
],
}
this.normalizeRules()
}
oop.inherits(OpenC3HighlightRules, TextHighlightRules)
var Mode = function () {
this.HighlightRules = OpenC3HighlightRules
}
var TextMode = ace.require('ace/mode/text').Mode
oop.inherits(Mode, TextMode)
;(function () {
this.$id = 'ace/mode/openc3'
}.call(Mode.prototype))
return Mode
},
downloadScreen: function () {
const blob = new Blob([this.currentDefinition], {
type: 'text/plain',
Expand Down Expand Up @@ -214,6 +285,11 @@ export default {
</script>

<style scoped>
#editor {
height: 50vh;
width: 75vw;
font-size: 16px;
}
.v-card {
background-color: var(--v-tertiary-darken2);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@
:target="target"
:screen="screen"
:definition="currentDefinition"
:keywords="keywords"
:errors="errors"
@save="saveEdit($event)"
@cancel="cancelEdit()"
Expand Down Expand Up @@ -185,6 +186,10 @@ export default {
type: String,
default: '',
},
keywords: {
type: Array,
default: () => [],
},
},
data() {
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
:target="def.target"
:screen="def.screen"
:definition="def.definition"
:keywords="keywords"
@close-screen="closeScreen(def.id)"
@min-max-screen="refreshLayout"
@add-new-screen="($event) => showScreen(...$event)"
Expand Down Expand Up @@ -129,6 +130,7 @@ export default {
newScreenDialog: false,
grid: null,
api: null,
keywords: [],
menus: [
{
label: 'File',
Expand Down Expand Up @@ -169,6 +171,9 @@ export default {
}
this.updateScreens()
})
Api.get('/openc3-api/autocomplete/keywords/screen').then((response) => {
this.keywords = response.data
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about autocomplete for target,packet,item in valueWidgets?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I punted on that for now. Might be a way to incorporate the existing stuff in there.

})
},
mounted() {
this.grid = new Muuri('.grid', {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
# Copyright 2022 Ball Aerospace & Technologies Corp.
# All Rights Reserved.
#
# This program is free software; you can modify and/or redistribute it
# under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation; version 3 with
# attribution addendums as found in the LICENSE.txt
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.

# Modified by OpenC3, Inc.
# All changes Copyright 2022, OpenC3, Inc.
# All Rights Reserved
*/

import ScreenCompleter from './screenCompleter.js'

export { ScreenCompleter }
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
# Copyright 2022 Ball Aerospace & Technologies Corp.
# All Rights Reserved.
#
# This program is free software; you can modify and/or redistribute it
# under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation; version 3 with
# attribution addendums as found in the LICENSE.txt
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.

# Modified by OpenC3, Inc.
# All changes Copyright 2022, OpenC3, Inc.
# All Rights Reserved
*/

import Api from '@openc3/tool-common/src/services/api'

export default class ScreenCompleter {
constructor() {
Api.get(`/openc3-api/autocomplete/data/screen`).then(
(response) => (this.autocompleteData = response.data)
)
}
getCompletions = function (editor, session, position, prefix, callback) {
callback(null, [...this.autocompleteData])
}
}