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

Experimental support for Javascript and Typescript #34

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
24 changes: 12 additions & 12 deletions moatless/codeblocks/codeblocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -558,6 +558,7 @@ def to_tree(
include_block_delimiters: bool = False,
include_references: bool = False,
include_merge_history: bool = False,
use_colors: bool = True,
):
if not include_merge_history and self.type == CodeBlockType.BLOCK_DELIMITER:
return ""
Expand All @@ -579,8 +580,9 @@ def to_tree(
)

if show_spans:
color = Colors.WHITE if highlighted else Colors.GRAY
child_tree += f"{indent_str} {indent} {color}Span: {current_span}{Colors.RESET}\n"
color = Colors.WHITE if highlighted and use_colors else Colors.GRAY if use_colors else ""
reset = Colors.RESET if use_colors else ""
child_tree += f"{indent_str} {indent} {color}Span: {current_span}{reset}\n"

if (
exclude_not_highlighted
Expand All @@ -605,6 +607,7 @@ def to_tree(
include_block_delimiters=include_block_delimiters,
include_references=include_references,
include_merge_history=include_merge_history,
use_colors=use_colors,
)

is_visible = not highlight_spans or self.belongs_to_any_span(highlight_spans)
Expand All @@ -618,25 +621,21 @@ def to_tree(
)

content = (
Colors.YELLOW
if is_visible
else Colors.GRAY
+ (self.content.strip().replace("\n", "\\n") or "")
+ Colors.RESET
)
(Colors.YELLOW if is_visible else Colors.GRAY) if use_colors else ""
) + (self.content.strip().replace("\n", "\\n") or "") + (Colors.RESET if use_colors else "")

if self.identifier:
if only_identifiers:
content = ""
content += Colors.GREEN if is_visible else Colors.GRAY
content += (Colors.GREEN if is_visible else Colors.GRAY) if use_colors else ""
if include_parameters and self.parameters:
content += f"{self.identifier}({', '.join([param.identifier for param in self.parameters])})"
elif show_full_path:
content += f" ({self.path_string()})"
else:
content += f" ({self.identifier})"

content += Colors.RESET
content += Colors.RESET if use_colors else ""

if include_line_numbers:
extra += f" {self.start_line}-{self.end_line}"
Expand All @@ -649,8 +648,9 @@ def to_tree(
[str(action) for action in self.merge_history]
)

type_color = Colors.BLUE if is_visible else Colors.GRAY
return f"{indent_str} {indent} {type_color}{self.type.value}{Colors.RESET} `{content}`{extra}{Colors.RESET}\n{child_tree}"
type_color = (Colors.BLUE if is_visible else Colors.GRAY) if use_colors else ""
reset = Colors.RESET if use_colors else ""
return f"{indent_str} {indent} {type_color}{self.type.value}{reset} `{content}`{extra}{reset}\n{child_tree}"

def _to_prompt_string(
self,
Expand Down
12 changes: 11 additions & 1 deletion moatless/codeblocks/parser/create.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
from moatless.codeblocks.parser.javascript import JavaScriptParser
from moatless.codeblocks.parser.parser import CodeParser
from moatless.codeblocks.parser.python import PythonParser
from moatless.codeblocks.parser.java import JavaParser
from moatless.codeblocks.parser.typescript import TypeScriptParser


def is_supported(language: str) -> bool:
return language and language in ["python", "java"]
return language and language in ["python", "java", "javascript", "jsx", "typescript", "tsx"]


def create_parser_by_ext(ext: str, **kwargs) -> CodeParser | None:
if ext == ".py":
return PythonParser(**kwargs)
elif ext == ".java":
return JavaParser(**kwargs)
elif ext in [".js", ".jsx"]:
return JavaScriptParser(**kwargs)
elif ext in [".ts", ".tsx"]:
return TypeScriptParser(**kwargs)

raise NotImplementedError(f"Extension {ext} is not supported.")

Expand All @@ -21,5 +27,9 @@ def create_parser(language: str, **kwargs) -> CodeParser | None:
return PythonParser(**kwargs)
elif language == "java":
return JavaParser(**kwargs)
elif language in ["javascript", "jsx"]:
return JavaScriptParser(**kwargs)
elif language in ["typescript", "tsx"]:
return TypeScriptParser(**kwargs)

raise NotImplementedError(f"Language {language} is not supported.")
61 changes: 61 additions & 0 deletions moatless/codeblocks/parser/javascript.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import logging

from tree_sitter import Language, Node
import tree_sitter_javascript as javascript
from tree_sitter_typescript import language_tsx

from moatless.codeblocks.codeblocks import CodeBlockType, CodeBlock, Relationship, ReferenceScope, RelationshipType
from moatless.codeblocks.parser.parser import CodeParser, NodeMatch

block_delimiters = [
"{",
"}",
";",
"(",
")"
]

logger = logging.getLogger(__name__)

class JavaScriptParser(CodeParser):

def __init__(self, language: str = "javascript", lang=Language(javascript.language()), **kwargs):
if language == "javascript":
lang = Language(javascript.language())
elif language in ["tsx", "typescript"]:
lang = Language(language_tsx())
else:
raise ValueError(f"Language {language} not supported")
super().__init__(lang, **kwargs)
self.queries = []

if language in ["tsx", "typescript"]:
self.queries.extend(self._build_queries("typescript.scm"))
else:
self.queries.extend(self._build_queries("javascript_only.scm"))

self.queries.extend(self._build_queries("javascript.scm"))

if self.apply_gpt_tweaks:
self.queries.extend(self._build_queries("javascript_gpt.scm"))

def process_match(self, node_match: NodeMatch, node: Node, content_bytes: bytes):
if node.type in block_delimiters: # TODO: Move to query
node_match = NodeMatch(block_type=CodeBlockType.BLOCK_DELIMITER)

return node_match

def pre_process(self, codeblock: CodeBlock, node_match: NodeMatch):
if codeblock.type == CodeBlockType.FUNCTION and codeblock.identifier == "constructor":
codeblock.type = CodeBlockType.CONSTRUCTOR

if codeblock.type == CodeBlockType.COMMENT and "..." in codeblock.content:
codeblock.type = CodeBlockType.COMMENTED_OUT_CODE

# Expect class on components with an identifier starting with upper case
if codeblock.type == CodeBlockType.FUNCTION and codeblock.identifier and codeblock.identifier[0].isupper():
codeblock.type = CodeBlockType.CLASS

# Handle JSX closing elements as block delimiters
if codeblock.content in ["/", ">"]:
codeblock.type = CodeBlockType.BLOCK_DELIMITER
Loading
Loading