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

Language project scaffolding #31

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
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
4 changes: 2 additions & 2 deletions client/src/services/projectService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,10 +125,10 @@ export class ProjectService implements IProjectService {
ignoreFocusOut: true,
placeHolder: "Enter a project name.",
validateInput: (value: string) => {
if (value && value.trim().length > 0) {
if (value && value.trim().length > 0 && /^[\w\.-]+$/.test(value)) {
return null;
} else {
return "Project name is required.";
return "Project name is required and has to be consisted of letters, numbers, ., -, _ .";
}
},
});
Expand Down
7 changes: 6 additions & 1 deletion textX-LS/core/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,12 @@
packages=packages,
include_package_data=True,
package_data={"": ["*.tx"]},
install_requires=["textX>=2.1.0", "click==7.0", "wheel_inspect==1.3.0"],
install_requires=[
"textX>=2.1.0",
"click==7.0",
"jinja2>=2",
"wheel_inspect==1.3.0",
],
extras_require={
"dev": dev_require,
"test": tests_require,
Expand Down
27 changes: 18 additions & 9 deletions textX-LS/core/textx_ls_core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,6 @@ def __init__(self, project_name, dist_location, detailed_err_msg):
self.detailed_err_msg = detailed_err_msg


class UninstallTextXProjectError(TextXLSError):
"""Indicates an error while uninstalling a textX project."""

def __init__(self, project_name, detailed_err_msg):
super().__init__("Failed to uninstall project: {}".format(project_name))
self.project_name = project_name
self.detailed_err_msg = detailed_err_msg


class LanguageNotRegistered(TextXLSError):
"""Indicates an error if language can't be parsed with any of registered metamodels.
"""
Expand All @@ -71,3 +62,21 @@ def __init__(self, file_name):
file_name
)
)


class ScaffoldTextXProjectError(TextXLSError):
"""Indicates an error while scaffolding a textX project."""

def __init__(self, project_name, detailed_err_msg):
super().__init__("Failed to scaffold project: {}".format(project_name))
self.project_name = project_name
self.detailed_err_msg = detailed_err_msg


class UninstallTextXProjectError(TextXLSError):
"""Indicates an error while uninstalling a textX project."""

def __init__(self, project_name, detailed_err_msg):
super().__init__("Failed to uninstall project: {}".format(project_name))
self.project_name = project_name
self.detailed_err_msg = detailed_err_msg
81 changes: 81 additions & 0 deletions textX-LS/core/textx_ls_core/generators/scaffolding/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import shutil
from functools import partial
from os import rename
from os.path import dirname, exists, join, relpath

import jinja2

from ...exceptions import ScaffoldTextXProjectError

templates_path = join(dirname(__file__), "templates")
textx_language_template_path = join(templates_path, "textx-language")


jinja_env = jinja2.Environment(
loader=jinja2.FileSystemLoader(textx_language_template_path),
autoescape=True,
lstrip_blocks=True,
trim_blocks=True,
)


def _populate_copy(project_name: str, src: str, dest: str) -> str:
"""Populates jinja template.

Args:
project_name: project name
src: source directory path
dest: destination directory path
Returns:
Destination directory path where template is coppied to
Raises:
None

"""
if src.endswith("template"):
template_rel_path = relpath(src, textx_language_template_path)
template = jinja_env.get_template(template_rel_path)
dest = dest.replace(".template", "")
with open(dest, "w") as f:
f.write(template.render(project_name=project_name))
return dest
else:
return shutil.copy2(src, dest)


def scaffold_language_project(project_name: str, dest_dir: str) -> str:
"""Creates a new textX language project in a given destination directory.

Args:
project_name: project name
dest_dir: destination directory path where language project is scaffolded
Returns:
same as `dest_dir`
Raises:
ScaffoldTextXProjectError: If any of `shutil.Error`, `OSError`, `IOError` error
types occurs

"""
project_name = project_name.lower()
dest = join(dest_dir, project_name)

if exists(dest):
raise ScaffoldTextXProjectError(
project_name,
"Project with name {} already exist at {}".format(project_name, dest),
)

try:
# Copy populated project template to dest path
shutil.copytree(
textx_language_template_path,
dest,
copy_function=partial(_populate_copy, project_name),
)
# Rename project name
rename(join(dest, "tx_package_name"), join(dest, "tx_{}".format(project_name)))

return dest
except (shutil.Error, OSError, IOError) as e:
shutil.rmtree(dest)
raise ScaffoldTextXProjectError(project_name, str(e)) from e
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import codecs
import os

from setuptools import find_packages, setup

PACKAGE_NAME = "tx-{{project_name}}"
VERSION = "0.1.0"
AUTHOR = "textX"
DESCRIPTION = "textX extension for language {{project_name}}"
KEYWORDS = "textX DSL python domain specific languages workflow"

setup(
name=PACKAGE_NAME,
version=VERSION,
description=DESCRIPTION,
author=AUTHOR,
keywords=KEYWORDS,
packages=find_packages(),
include_package_data=True,
package_data={"": ["*.tx"]},
install_requires=["textx_ls_core"],
entry_points={"textx_languages": ["{{project_name}} = tx_{{project_name}}:{{project_name}}"]},
classifiers=[
"Development Status :: 2 - Pre-Alpha",
"Intended Audience :: Developers",
"Intended Audience :: Information Technology",
"Topic :: Software Development :: Libraries :: Python Modules",
"License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from os.path import dirname, join

from textx import language, metamodel_from_file


@language("{{project_name}}", "*.{{project_name}}")
def {{project_name}}():
"Language project scaffolded with textX extension."
return metamodel_from_file(join(dirname(__file__), "grammar.tx"))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{{project_name|upper}}:
"{{project_name}}"
;
21 changes: 19 additions & 2 deletions textX-LS/server/textx_ls_server/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
GenerateExtensionError,
GenerateSyntaxHighlightError,
InstallTextXProjectError,
ScaffoldTextXProjectError,
UninstallTextXProjectError,
)
from textx_ls_core.features.generators import (
Expand All @@ -33,6 +34,7 @@
install_project_async,
uninstall_project_async,
)
from textx_ls_core.generators.scaffolding import scaffold_language_project
from textx_ls_core.models import TextXProject
from textx_ls_core.utils import compare_project_names

Expand Down Expand Up @@ -194,8 +196,23 @@ def cmd_project_list(ls: TextXLanguageServer, params) -> List[TextXProject]:


@textx_server.command(TextXLanguageServer.CMD_PROJECT_SCAFFOLD)
def cmd_project_scaffold(ls: TextXLanguageServer, params) -> None:
ls.show_message("Not implemented")
def cmd_scaffold_project(ls: TextXLanguageServer, params) -> None:
"""Command that scaffolds textX project.

Args:
params: project name
Returns:
None
Raises:
None

"""
project_name = params[0]
try:
dest = scaffold_language_project(project_name, ls.workspace.root_path)
ls.show_message("Project successfully scaffolded at {}.".format(str(dest)))
except ScaffoldTextXProjectError as e:
ls.show_errors(str(e))


@textx_server.command(TextXLanguageServer.CMD_PROJECT_UNINSTALL)
Expand Down