--- gigedit/trunk/src/gigedit/scripteditor.cpp 2014/06/08 19:09:26 2610 +++ gigedit/trunk/src/gigedit/scripteditor.cpp 2016/05/01 20:20:06 2897 @@ -1,5 +1,5 @@ /* - Copyright (c) 2014 Christian Schoenebeck + Copyright (c) 2014-2016 Christian Schoenebeck This file is part of "gigedit" and released under the terms of the GNU General Public License version 2. @@ -8,6 +8,8 @@ #include "scripteditor.h" #include "global.h" +#if !USE_LS_SCRIPTVM + static const std::string _keywords[] = { "on", "end", "declare", "while", "if", "or", "and", "not", "else", "case", "select", "to", "const", "polyphonic", "mod" @@ -31,27 +33,73 @@ return false; } +#endif // !USE_LS_SCRIPTVM + ScriptEditor::ScriptEditor() : - m_applyButton(Gtk::Stock::APPLY), m_cancelButton(Gtk::Stock::CANCEL) + m_applyButton(_("_Apply"), true), + m_cancelButton(_("_Cancel"), true) { m_script = NULL; +#if USE_LS_SCRIPTVM + m_vm = NULL; +#endif add(m_vbox); m_tagTable = Gtk::TextBuffer::TagTable::create(); + m_keywordTag = Gtk::TextBuffer::Tag::create(); + m_keywordTag->property_foreground() = "#000000"; // black m_keywordTag->property_weight() = PANGO_WEIGHT_BOLD; m_tagTable->add(m_keywordTag); + m_eventTag = Gtk::TextBuffer::Tag::create(); - m_eventTag->property_foreground() = "blue"; + m_eventTag->property_foreground() = "#07c0cf"; // cyan 1 m_eventTag->property_weight() = PANGO_WEIGHT_BOLD; m_tagTable->add(m_eventTag); + + m_variableTag = Gtk::TextBuffer::Tag::create(); + m_variableTag->property_foreground() = "#790cc4"; // magenta + m_tagTable->add(m_variableTag); + + m_functionTag = Gtk::TextBuffer::Tag::create(); + m_functionTag->property_foreground() = "#1ba1dd"; // cyan 2 + m_tagTable->add(m_functionTag); + + m_numberTag = Gtk::TextBuffer::Tag::create(); + m_numberTag->property_foreground() = "#c4950c"; // yellow + m_tagTable->add(m_numberTag); + + m_stringTag = Gtk::TextBuffer::Tag::create(); + m_stringTag->property_foreground() = "#c40c0c"; // red + m_tagTable->add(m_stringTag); + + m_commentTag = Gtk::TextBuffer::Tag::create(); + m_commentTag->property_foreground() = "#9c9c9c"; // gray + m_tagTable->add(m_commentTag); + + m_preprocTag = Gtk::TextBuffer::Tag::create(); + m_preprocTag->property_foreground() = "#2f8a33"; // green + m_tagTable->add(m_preprocTag); + + m_errorTag = Gtk::TextBuffer::Tag::create(); + m_errorTag->property_background() = "#ff9393"; // red + m_tagTable->add(m_errorTag); + + m_warningTag = Gtk::TextBuffer::Tag::create(); + m_warningTag->property_background() = "#fffd7c"; // yellow + m_tagTable->add(m_warningTag); + m_textBuffer = Gtk::TextBuffer::create(m_tagTable); m_textView.set_buffer(m_textBuffer); { Pango::FontDescription fdesc; fdesc.set_family("monospace"); +#if defined(__APPLE__) + fdesc.set_size(12 * PANGO_SCALE); +#else fdesc.set_size(10 * PANGO_SCALE); +#endif #if GTKMM_MAJOR_VERSION < 3 m_textView.modify_font(fdesc); #else @@ -101,6 +149,9 @@ ScriptEditor::~ScriptEditor() { printf("ScriptEditor destruct\n"); +#if USE_LS_SCRIPTVM + if (m_vm) delete m_vm; +#endif } void ScriptEditor::setScript(gig::Script* script) { @@ -119,6 +170,12 @@ } void ScriptEditor::onTextInserted(const Gtk::TextBuffer::iterator& itEnd, const Glib::ustring& txt, int length) { + //printf("onTextInserted()\n"); +#if USE_LS_SCRIPTVM + m_textBuffer->remove_all_tags(m_textBuffer->begin(), m_textBuffer->end()); + updateSyntaxHighlightingByVM(); + updateParserIssuesByVM(); +#else //printf("inserted %d\n", length); Gtk::TextBuffer::iterator itStart = itEnd; itStart.backward_chars(length); @@ -159,10 +216,135 @@ EOF_REACHED: ; + +#endif // USE_LS_SCRIPTVM +} + +#if USE_LS_SCRIPTVM + +LinuxSampler::ScriptVM* ScriptEditor::GetScriptVM() { + if (!m_vm) m_vm = LinuxSampler::ScriptVMFactory::Create("gig"); + return m_vm; +} + +static void getIteratorsForIssue(Glib::RefPtr& txtbuf, const LinuxSampler::ParserIssue& issue, Gtk::TextBuffer::iterator& start, Gtk::TextBuffer::iterator& end) { + start = txtbuf->get_iter_at_line_index(issue.firstLine - 1, issue.firstColumn - 1); + end = start; + end.forward_lines(issue.lastLine - issue.firstLine); + end.forward_chars( + (issue.lastLine != issue.firstLine) + ? issue.lastColumn - 1 + : issue.lastColumn - issue.firstColumn + 1 + ); +} + +static void applyCodeTag(Glib::RefPtr& txtbuf, const LinuxSampler::VMSourceToken& token, Glib::RefPtr& tag) { + Gtk::TextBuffer::iterator itStart = + txtbuf->get_iter_at_line_index(token.firstLine(), token.firstColumn()); + Gtk::TextBuffer::iterator itEnd = itStart; + const int length = token.text().length(); + itEnd.forward_chars(length); + txtbuf->apply_tag(tag, itStart, itEnd); +} + +static void applyCodeTag(Glib::RefPtr& txtbuf, const LinuxSampler::ParserIssue& issue, Glib::RefPtr& tag) { + Gtk::TextBuffer::iterator itStart, itEnd; + getIteratorsForIssue(txtbuf, issue, itStart, itEnd); + txtbuf->apply_tag(tag, itStart, itEnd); } +void ScriptEditor::updateSyntaxHighlightingByVM() { + GetScriptVM(); + const std::string s = m_textBuffer->get_text(); + std::vector tokens = m_vm->syntaxHighlighting(s); + + for (int i = 0; i < tokens.size(); ++i) { + const LinuxSampler::VMSourceToken& token = tokens[i]; + + if (token.isKeyword()) { + applyCodeTag(m_textBuffer, token, m_keywordTag); + } else if (token.isVariableName()) { + applyCodeTag(m_textBuffer, token, m_variableTag); + } else if (token.isIdentifier()) { + if (token.isEventHandlerName()) { + applyCodeTag(m_textBuffer, token, m_eventTag); + } else { // a function ... + applyCodeTag(m_textBuffer, token, m_functionTag); + } + } else if (token.isNumberLiteral()) { + applyCodeTag(m_textBuffer, token, m_numberTag); + } else if (token.isStringLiteral()) { + applyCodeTag(m_textBuffer, token, m_stringTag); + } else if (token.isComment()) { + applyCodeTag(m_textBuffer, token, m_commentTag); + } else if (token.isPreprocessor()) { + applyCodeTag(m_textBuffer, token, m_preprocTag); + } else if (token.isNewLine()) { + } + } +} + +void ScriptEditor::updateParserIssuesByVM() { + GetScriptVM(); + const std::string s = m_textBuffer->get_text(); + LinuxSampler::VMParserContext* parserContext = m_vm->loadScript(s); + m_issues = parserContext->issues(); + + for (int i = 0; i < m_issues.size(); ++i) { + const LinuxSampler::ParserIssue& issue = m_issues[i]; + + if (issue.isErr()) { + applyCodeTag(m_textBuffer, issue, m_errorTag); + } else if (issue.isWrn()) { + applyCodeTag(m_textBuffer, issue, m_warningTag); + } + } + + delete parserContext; +} + +void ScriptEditor::updateIssueTooltip(GdkEventMotion* e) { + int x, y; + m_textView.window_to_buffer_coords(Gtk::TEXT_WINDOW_TEXT, int(e->x), int(e->y), x, y); + + Gtk::TextBuffer::iterator it; + m_textView.get_iter_at_location(it, x, y); + + const int line = it.get_line(); + const int column = it.get_line_offset(); + + //printf("mouse at l%d c%d\n", line, column); + + for (int i = 0; i < m_issues.size(); ++i) { + const LinuxSampler::ParserIssue& issue = m_issues[i]; + const int firstLine = issue.firstLine - 1; + const int firstColumn = issue.firstColumn - 1; + const int lastLine = issue.lastLine - 1; + const int lastColumn = issue.lastColumn - 1; + if (firstLine <= line && line <= lastLine && + (firstLine != line || firstColumn <= column) && + (lastLine != line || lastColumn >= column)) + { + m_textView.set_tooltip_markup( + (issue.isErr() ? "ERROR: " : "Warning: ") + + issue.txt + ); + return; + } + } + + m_textView.set_tooltip_markup(""); +} + +#endif // USE_LS_SCRIPTVM + void ScriptEditor::onTextErased(const Gtk::TextBuffer::iterator& itStart, const Gtk::TextBuffer::iterator& itEnd) { //printf("erased\n"); +#if USE_LS_SCRIPTVM + m_textBuffer->remove_all_tags(m_textBuffer->begin(), m_textBuffer->end()); + updateSyntaxHighlightingByVM(); + updateParserIssuesByVM(); +#else Gtk::TextBuffer::iterator itStart2 = itStart; if (itStart2.inside_word() || itStart2.ends_word()) itStart2.backward_word_start(); @@ -171,6 +353,15 @@ if (itEnd2.inside_word()) itEnd2.forward_word_end(); m_textBuffer->remove_all_tags(itStart2, itEnd2); +#endif // USE_LS_SCRIPTVM +} + +bool ScriptEditor::on_motion_notify_event(GdkEventMotion* e) { +#if USE_LS_SCRIPTVM + //TODO: event throttling would be a good idea here + updateIssueTooltip(e); +#endif + return ManagedWindow::on_motion_notify_event(e); } void ScriptEditor::onModifiedChanged() {