Skip to content

Commit

Permalink
feat: add support for python
Browse files Browse the repository at this point in the history
  • Loading branch information
kkoomen committed Oct 4, 2020
1 parent 2958330 commit 027216a
Show file tree
Hide file tree
Showing 10 changed files with 140 additions and 46 deletions.
7 changes: 4 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ RUN npm install -g node-gyp

ENV NODE_PATH="/usr/lib/node_modules"
RUN npm -g config set user root
RUN npm install -g tree-sitter \
tree-sitter-typescript \
tree-sitter-php
RUN npm install -g tree-sitter
RUN npm install -g tree-sitter-php
RUN npm install -g tree-sitter-typescript
RUN npm install -g tree-sitter-python

# Install addition python modules.
RUN pip3 install --upgrade pip vim-vint==0.3.15 setuptools
Expand Down
57 changes: 15 additions & 42 deletions ftplugin/python.vim
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ if !exists('g:doge_python_settings')
\}
endif


let b:doge_pattern_single_line_comment = '\m#.\{-}$'
let b:doge_pattern_multi_line_comment = '\m\(""".\{-}"""\|' . "'''.\\{-}'''" . '\)'
let b:doge_parser = 'python'
let b:doge_insert = 'below'

let b:doge_supported_doc_standards = doge#buffer#get_supported_doc_standards([
\ 'reST',
Expand All @@ -28,45 +27,16 @@ let b:doge_supported_doc_standards = doge#buffer#get_supported_doc_standards([
let b:doge_doc_standard = doge#buffer#get_doc_standard('python')
let b:doge_patterns = doge#buffer#get_patterns()

" ==============================================================================
"
" Define our base for every pattern.
"
" ==============================================================================
let s:pattern_base = {
\ 'parameters': {
\ 'match': '\m\([[:alnum:]_]\+\)\%(\s*:\s*\([[:alnum:]_.]\+\%(\[[[:alnum:]_[\],[:space:]]*\]\)\?\)\)\?\%(\s*=\s*\([^,]\+\)\)\?',
\ 'tokens': ['name', 'type', 'default'],
\ },
\ 'insert': 'below',
\}

" ==============================================================================
"
" Define the pattern types.
"
" ==============================================================================

" ------------------------------------------------------------------------------
" Matches regular function expressions and class methods.
" ------------------------------------------------------------------------------
" def __init__(self: MyClass):
" def myMethod(self: MyClass, p1: Sequence[T]) -> Generator[int, float, str]:
" def call(self, *args: str, **kwargs: str) -> str:
" def myFunc(p1: Callable[[int], None] = False, p2: Callable[[int, Exception], None]) -> Sequence[T]:
" ------------------------------------------------------------------------------
let s:function_and_class_method_pattern = doge#helpers#deepextend(s:pattern_base, {
\ 'match': '\m^\%(async\s\+\)\?def\s\+\%([^(]\+\)\s*(\(.\{-}\))\%(\s*->\s*\(.\{-}\)\)\?\s*:',
\ 'tokens': ['parameters', 'returnType'],
\})

" ==============================================================================
"
" Define the doc standards.
"
" ==============================================================================

call doge#buffer#register_doc_standard('reST', [
\ doge#helpers#deepextend(s:function_and_class_method_pattern, {
\ {
\ 'node_types': ['function_definition'],
\ 'parameters': {
\ 'format': ':param {name} {type|!type}: !description',
\ },
Expand All @@ -78,11 +48,12 @@ call doge#buffer#register_doc_standard('reST', [
\ '%(returnType|:rtype {returnType}: !description)%',
\ '"""',
\ ],
\ }),
\ },
\])

call doge#buffer#register_doc_standard('sphinx', [
\ doge#helpers#deepextend(s:function_and_class_method_pattern, {
\ {
\ 'node_types': ['function_definition'],
\ 'parameters': {
\ 'format': [
\ ':param {name}: !description%(default|, defaults to {default})%',
Expand All @@ -98,11 +69,12 @@ call doge#buffer#register_doc_standard('sphinx', [
\ '%(returnType|:rtype: {returnType})%',
\ '"""',
\ ],
\ }),
\ },
\])

call doge#buffer#register_doc_standard('numpy', [
\ doge#helpers#deepextend(s:function_and_class_method_pattern, {
\ {
\ 'node_types': ['function_definition'],
\ 'parameters': {
\ 'format': [
\ '{name} : {type|!type}',
Expand All @@ -125,11 +97,12 @@ call doge#buffer#register_doc_standard('numpy', [
\ '%(returnType|\t!description)%',
\ '"""',
\ ],
\ }),
\ },
\])

call doge#buffer#register_doc_standard('google', [
\ doge#helpers#deepextend(s:function_and_class_method_pattern, {
\ {
\ 'node_types': ['function_definition'],
\ 'parameters': {
\ 'format': '{name} ({type|!type}%(default|, optional)%): !description',
\ },
Expand All @@ -145,7 +118,7 @@ call doge#buffer#register_doc_standard('google', [
\ '%(returnType|\t{returnType}: !description)%',
\ '"""',
\ ],
\ }),
\ },
\])

let &cpoptions = s:save_cpo
Expand Down
8 changes: 8 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
"dependencies": {
"tree-sitter": "^0.17.0",
"tree-sitter-php": "^0.16.2",
"tree-sitter-python": "^0.16.1",
"tree-sitter-typescript": "^0.16.1",
"typescript": "^4.0.3"
},
Expand Down
2 changes: 1 addition & 1 deletion run-tests
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ set -e
set -u

docker_image=kkoomen/doge
current_docker_image_id=7f28e4874341
current_docker_image_id=c9c37fd778ad
DOGE_DOCKER_IMAGE="$docker_image"
export DOGE_DOCKER_IMAGE

Expand Down
1 change: 1 addition & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export enum Language {
PHP = 'php',
TYPESCRIPT = 'typescript',
PYTHON = 'python',
}
2 changes: 2 additions & 0 deletions src/parser-languages.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import PHP from 'tree-sitter-php';
import Python from 'tree-sitter-python';
import TypeScript from 'tree-sitter-typescript/typescript';
import { Language } from './constants';

export const parserLanguages = {
[Language.PHP]: PHP,
[Language.TYPESCRIPT]: TypeScript,
[Language.PYTHON]: Python,
}
5 changes: 5 additions & 0 deletions src/parsers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,12 @@ import { SyntaxNode } from 'tree-sitter';
import { Language } from '../constants';
import { ValueOf } from '../helpers';
import { PhpParserService } from './php.service';
import { PythonParserService } from './python.service';
import { TypeScriptParserService } from './typescript.service';

export type ParserService = PhpParserService
| TypeScriptParserService
| PythonParserService

export function getParserService(
language: ValueOf<Language>,
Expand All @@ -18,6 +20,9 @@ export function getParserService(
case Language.TYPESCRIPT:
return new TypeScriptParserService(...args);

case Language.PYTHON:
return new PythonParserService(...args);

default:
console.error(`Could not get parser service for unknown language: ${language}`)
break;
Expand Down
102 changes: 102 additions & 0 deletions src/parsers/python.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { SyntaxNode } from 'tree-sitter';
import { CustomParserService } from '../parser-service.interface';
import { BaseParserService } from './base-parser.service';

enum NodeType {
FUNCTION_DEFINITION = 'function_definition',
MODULE = 'module',
}

export class PythonParserService extends BaseParserService implements CustomParserService {
constructor(
private readonly rootNode: SyntaxNode,
private readonly lineNumber: number,
private readonly nodeTypes: string[],
) {
super();
}

public traverse(node: SyntaxNode): void {
if (node.startPosition.row === this.lineNumber && this.nodeTypes.includes(node.type) && this.done === false) {
switch (node.type) {
case NodeType.FUNCTION_DEFINITION: {
this.result = {
name: null,
parameters: [],
returnType: null,
};
this.runNodeParser(this.parseFunction, node);
break;
}

default: {
console.error(`Unable to handle node type: ${node.type}`)
break;
}
}
} else {
if (node.childCount > 0) {
node.children.forEach((childNode: SyntaxNode) => {
this.traverse(childNode);
});
}
}
}

private parseFunction(node: SyntaxNode): void {
node.children.forEach((childNode: SyntaxNode) => {
switch (childNode.type) {
case 'identifier': {
this.result.name = childNode.text;
break;
}
case 'type': {
this.result.returnType = childNode.text;
break;
}
case 'parameters': {
childNode
.children
.filter((n: SyntaxNode) => ['default_parameter', 'typed_parameter', 'typed_default_parameter', 'identifier'].includes(n.type))
.forEach((cn: SyntaxNode) => {
const param: Record<string, any> = { name: null, type: null, default: null };

if (cn.type === 'default_parameter') {
param.name = cn.child(0)?.text;
param.default = cn.child(2)?.text;
}

if (cn.type === 'typed_parameter') {
cn.children.forEach((pn: SyntaxNode) => {
if (pn.type === 'identifier') {
param.name = pn.text;
}

if (['dictionary_splat', 'list_splat'].includes(pn.type)) {
param.name = pn.children.filter((cpn: SyntaxNode) => cpn.type === 'identifier').shift()?.text;
}

if (pn.type === 'type') {
param.type = pn.text;
}
});
}

if (cn.type === 'typed_default_parameter') {
param.name = cn.children.filter((pn: SyntaxNode) => pn.type === 'identifier').shift()?.text;
param.type = cn.children.filter((pn: SyntaxNode) => pn.type === 'type').shift()?.text;
param.default = cn.children.filter((pn: SyntaxNode) => pn.previousSibling?.type === '=').shift()?.text;
}

if (cn.type === 'identifier') {
param.name = cn.text;
}

this.result.parameters.push(param);
})
break;
}
}
});
}
}
1 change: 1 addition & 0 deletions src/vim-doge.d.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
declare module 'tree-sitter-php'
declare module 'tree-sitter-python'
declare module 'tree-sitter-typescript/typescript'

0 comments on commit 027216a

Please sign in to comment.