Commit 198965a2 authored by Christian Hergert's avatar Christian Hergert
Browse files

vala-pack: port plugin to Python

Now that we only have an indenter and error regex pipeline
addin we can easily port this to Python3 and drop all of our
usage of Vala.

The gvls plugin will continue to provide IDE features for Vala
but done from out-of-process.
parent 63595c73
/* ide-vala-indenter.vala
*
* Copyright 2015 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using Gtk;
using Ide;
namespace Ide
{
public class ValaIndenter: Ide.Object, Ide.Indenter
{
public bool is_trigger (Gdk.EventKey evkey)
{
switch (evkey.keyval) {
/* newline indents */
case Gdk.Key.Return:
case Gdk.Key.KP_Enter:
return true;
/* close multiline comment */
case Gdk.Key.slash:
return true;
default:
return false;
}
}
public string? format (Gtk.TextView text_view,
Gtk.TextIter begin,
Gtk.TextIter end,
out int cursor_offset,
Gdk.EventKey evkey)
{
Gtk.SourceView source_view = (text_view as Gtk.SourceView);
bool was_newline = is_newline_keyval (evkey.keyval);
Gtk.TextIter copy = end;
cursor_offset = 0;
/* Move us back to the just inserted character */
copy.backward_char ();
/* If we are in a comment, continue the indentation. */
if (in_comment (text_view, copy)) {
/* maybe close a multiline comment */
if (copy.get_char () == '/') {
Gtk.TextIter close = copy;
if (close.backward_char () && close.get_char () == ' ' &&
close.backward_char () && close.get_char () == '*') {
begin.backward_char ();
begin.backward_char ();
return "/";
}
}
if (was_newline)
return indent_comment (text_view, copy);
}
if (is_newline_in_braces (copy)) {
string prefix = copy_indent (text_view, copy);
string indent;
if (source_view.insert_spaces_instead_of_tabs)
indent = " ";
else
indent = "\t";
cursor_offset = -prefix.length - 1;
return (prefix + indent + "\n" + prefix);
}
if (was_newline)
return copy_indent (text_view, copy);
return null;
}
string? copy_indent (Gtk.TextView text_view,
Gtk.TextIter iter)
{
Gtk.TextIter begin = iter;
Gtk.TextIter end;
begin.set_line_offset (0);
end = begin;
while (!end.ends_line () && end.get_char ().isspace () && end.forward_char ()) {
/* Do nothing */
}
return begin.get_slice (end);
}
string get_line_text (Gtk.TextIter iter)
{
Gtk.TextIter begin = iter;
Gtk.TextIter end = iter;
begin.set_line_offset (0);
if (!end.ends_line ())
end.forward_to_line_end ();
return begin.get_slice (end);
}
string? indent_comment (Gtk.TextView text_view,
Gtk.TextIter iter)
{
string line = get_line_text (iter).strip ();
/* continue with another single line comment */
if (line.has_prefix ("//"))
return copy_indent (text_view, iter) + "// ";
/* comment is closed, copy indent, possibly trimming extra space */
if (line.has_suffix ("*/")) {
if (line.has_prefix ("*")) {
var str = new GLib.StringBuilder (copy_indent (text_view, iter));
if (str.str.has_suffix (" "))
str.truncate (str.len - 1);
return str.str;
}
}
if (line.has_prefix ("/*") && !line.has_suffix ("*/"))
return copy_indent (text_view, iter) + " * ";
else if (line.has_prefix ("*"))
return copy_indent (text_view, iter) + "* ";
return copy_indent (text_view, iter);
}
bool in_comment (Gtk.TextView text_view,
Gtk.TextIter iter)
{
Gtk.SourceBuffer buffer = text_view.buffer as Gtk.SourceBuffer;
Gtk.TextIter copy = iter;
copy.backward_char ();
return buffer.iter_has_context_class (copy, "comment");
}
bool is_newline_keyval (uint keyval)
{
switch (keyval) {
case Gdk.Key.Return:
case Gdk.Key.KP_Enter:
return true;
default:
return false;
}
}
bool is_newline_in_braces (Gtk.TextIter iter)
{
Gtk.TextIter prev = iter;
Gtk.TextIter next = iter;
prev.backward_char ();
next.forward_char ();
return (prev.get_char () == '{') && (iter.get_char () == '\n') && (next.get_char () == '}');
}
}
}
/* ide-vala-pipeline-addin.vala
*
* Copyright 2017 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using GLib;
using Gtk;
using Ide;
namespace Ide
{
public class ValaPipelineAddin: Ide.Object, Ide.PipelineAddin
{
// main.vala:24.30-24.30: error: initializer list used for `Gtk.WindowType', which is neither array nor struct
const string ERROR_FORMAT_REGEX =
"(?<filename>[a-zA-Z0-9\\-\\.\\/_]+.vala):" +
"(?<line>\\d+).(?<column>\\d+)-(?<line2>\\d+).(?<column2>\\d+): " +
"(?<level>[\\w\\s]+): " +
"(?<message>.*)";
uint error_format = 0;
public void prepare (Ide.Pipeline pipeline)
{
}
public void load (Ide.Pipeline pipeline)
{
this.error_format = pipeline.add_error_format (ERROR_FORMAT_REGEX,
GLib.RegexCompileFlags.OPTIMIZE | GLib.RegexCompileFlags.CASELESS);
}
public void unload (Ide.Pipeline pipeline)
{
pipeline.remove_error_format (this.error_format);
}
}
}
if get_option('plugin_vala')
add_languages('vala')
valac = meson.get_compiler('vala')
libvala_version = run_command(valac.cmd_array()[0], '--api-version').stdout().strip()
libvala = dependency('libvala-@0@'.format(libvala_version))
vala_sources = [
'valaconfig.vapi',
'ide-vala-indenter.vala',
'ide-vala-pipeline-addin.vala',
'vala-pack-plugin.vala',
]
vala_deps = [
libpeas_dep,
libide_vapi,
libgtksource_dep,
libvte_dep,
libdazzle_dep,
libtemplate_glib_dep,
libjsonrpc_glib_dep,
libgiounix_dep,
]
shared_module('plugin-vala-pack', vala_sources,
dependencies: vala_deps,
install: true,
install_dir: plugindir,
install_rpath: pkglibdir_abs,
include_directories: [include_directories('.')] + libide_include_directories,
vala_args: [ '--target-glib=2.52',
'--pkg=posix',
'--pkg=libpeas-1.0',
'--pkg=gtksourceview-4',
'--pkg=gio-2.0',
'--pkg=gio-unix-2.0',
'--pkg=libdazzle-1.0',
'--pkg=template-glib-1.0',
'--pkg=vte-2.91',
'--pkg=jsonrpc-glib-1.0',
],
c_args: [ '-DVALA_VERSION="@0@"'.format(libvala_version),
'-DLOG_DOMAIN="vala-pack"',
'-DGETTEXT_PACKAGE="gnome-builder"',
'-DPACKAGE_DATADIR="@0@"'.format(join_paths(get_option('prefix'), get_option('datadir'), 'gnome-builder')),
'-DPACKAGE_LIBEXECDIR="@0@"'.format(join_paths(get_option('prefix'), get_option('libexecdir'))),
'-Wno-incompatible-pointer-types',
],
)
install_data('vala_pack_plugin.py', install_dir: plugindir)
configure_file(
input: 'vala-pack.plugin',
......
/* vala-pack-plugin.vala
*
* Copyright 2015 Christian Hergert <christian@hergert.me>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using GLib;
using Ide;
using Peas;
[ModuleInit]
public void peas_register_types (GLib.TypeModule module)
{
Peas.ObjectModule peas = (Peas.ObjectModule)module;
peas.register_extension_type (typeof (Ide.PipelineAddin), typeof (Ide.ValaPipelineAddin));
peas.register_extension_type (typeof (Ide.Indenter), typeof (Ide.ValaIndenter));
}
[Plugin]
Authors=Christian Hergert <christian@hergert.me>
Builtin=true
Module=plugin-vala-pack
Copyright=Copyright © 2015-2020 Christian Hergert
Description=Provides various language features for Vala
Loader=python3
Module=vala_pack_plugin
Name=Vala Language Pack
X-Builder-ABI=@PACKAGE_ABI@
X-Indenter-Languages-Priority=0
......
#!/usr/bin/env python3
#
# vala_pack_plugin.py
#
# Copyright 2020 Christian Hergert <christian@hergert.me>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
from gi.repository import GLib
from gi.repository import Gdk
from gi.repository import Ide
_ERROR_REGEX = (
"(?<filename>[a-zA-Z0-9\\-\\.\\/_]+.vala):" +
"(?<line>\\d+).(?<column>\\d+)-(?<line2>\\d+).(?<column2>\\d+): " +
"(?<level>[\\w\\s]+): " +
"(?<message>.*)"
)
class ValaPipelineAddin(Ide.Object, Ide.PipelineAddin):
error_format = 0
def do_load(self, pipeline):
self.error_format = pipeline.add_error_format(_ERROR_REGEX, GLib.RegexCompileFlags.OPTIMIZE)
def do_unload(self, pipeline):
pipeline.remove_error_format(self.error_format)
def copy_indent(iter):
begin = iter.copy()
begin.set_line_offset(0)
end = begin.copy()
while not end.ends_line() and end.get_char().isspace() and end.forward_char():
# Do nothing
pass
return begin.get_slice(end)
def get_line_text(iter):
begin = iter.copy()
end = iter.copy()
begin.set_line_offset(0)
if not end.ends_line():
end.forward_to_line_end()
return begin.get_slice(end)
def is_newline_in_braces(iter):
prev = iter.copy()
next = iter.copy()
prev.backward_char()
next.forward_char()
return prev.get_char() == '{' and iter.get_char() == '\n' and next.get_char() == '}'
def is_newline_keyval(keyval):
return keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter)
def in_comment(iter):
buffer = iter.get_buffer()
copy = iter.copy()
copy.backward_char()
return buffer.iter_has_context_class(copy, 'comment')
def indent_comment(iter):
line = get_line_text(iter).strip()
# Continue with another single line comment
if line.startswith('//'):
return copy_indent(iter) + '// '
# Comment is closed, copy indent, possibly trimming extra space
if line.endswith('*/'):
if line.startswith('*'):
str = copy_indent(iter)
if str.endswith(' '):
str = str[:-1]
return str
if line.startswith('/*') and not line.endswith('*/'):
return copy_indent(iter) + ' * '
elif line.startswith('*'):
return copy_indent(iter) + '* '
return copy_indent(iter)
class ValaIndenter(Ide.Object, Ide.Indenter):
def do_is_trigger(self, key):
return key.keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter, Gdk.KEY_slash)
def do_format(self, text_view, begin, end, key):
was_newline = is_newline_keyval(key.keyval)
copy = end.copy()
# Move us back to the jsut inserted character
copy.backward_char()
# If we are in a comment, continue the indentation
if in_comment(copy):
# Maybe close a multi-line comment
if copy.get_char() == '/':
close = copy.copy()
if close.backward_char() and close.get_char() == ' ' and close.backward_char() and close.get_char() == '*' :
begin.backward_char()
begin.backward_char()
return ('/', 0)
if was_newline:
return (indent_comment(copy), 0)
if is_newline_in_braces(copy):
prefix = copy_indent(copy)
if text_view.get_insert_spaces_instead_of_tabs():
indent = " "
else:
indent = '\t'
return (prefix + indent + "\n" + prefix, -(len(prefix) + 1))
if was_newline:
return (copy_indent(copy), 0)
return (None, 0)
[CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "")]
namespace ValaConfig {
public const string PACKAGE_DATADIR;
public const string PACKAGE_LIBEXECDIR;
public const string VALA_VERSION;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment