UltiSnips expand java package

459 Views Asked by At

I want to be able to write a vim snippet that automatically turns into the required package.

E.g. expanding pkg while inside of .../com/theonlygust/project/Main.java would become

package com.theonlygusti.project;

I think the ways to do this are to either: read up the directory tree until seeing a TLD directory name (com, io, net, etc.) and then use the encountered directory names to build the package string, or to look up the directory tree for the pom.xml and find the package from there.

I learned about python interpolation.

I'm now trying this:

snippet pkg "Create package" b
package `!p

import os
from xml.etree import ElementTree

def get_package_name(pom_file_path):
  namespaces = {'xmlns' : 'http://maven.apache.org/POM/4.0.0'}

  tree = ElementTree.parse(pom_file_path)
  root = tree.getroot()

  groupId = root.find(".//xmlns:groupId", namespaces=namespaces)
  artifactId = root.find(".//xmlns:artifactId", namespaces=namespaces)
  return groupId.text + '.' + artifactId.text

def find_nearest_pom():
  absolute_path = os.path.abspath(os.path.dirname('__file__')).split("/")
  pom_dir_index = -1

  # Find index of 'base_dir_name' element
  while not os.path.isfile('/'.join(absolute_path[:pom_dir_index]) + '/pom.xml'):
    pom_dir_index -= 1

  return '/'.join(absolute_path[:pom_dir_index]) + '/pom.xml'

snip.rv = get_package_name(find_nearest_pom())`;
endsnippet

But I get the error

Name __file__ does not exist

And os.getcwd() doesn't work because that returns the directory from which vim was opened, not the directory that contains the current buffer.

I had a look at the snip object because I know it provides snip.fn to get the filename, but I couldn't find out if it provides the current file's directory.

Nevermind, finally learned that UltiSnips sets a global variable "path"

3

There are 3 best solutions below

0
On BEST ANSWER

I use a combination of the file path and the groupId and the artifactId from the nearest pom.xml (upwards)

global !p
import os
from xml.etree import ElementTree

def get_package_name(pom_file_path):
  namespaces = {'xmlns' : 'http://maven.apache.org/POM/4.0.0'}

  tree = ElementTree.parse(pom_file_path)
  root = tree.getroot()

  groupId = root.find(".//xmlns:groupId", namespaces=namespaces)
  artifactId = root.find(".//xmlns:artifactId", namespaces=namespaces)
  return groupId.text + '.' + artifactId.text

def find_nearest_pom():
  current_file_dir = '/'.join((os.getcwd() + ('/' if os.getcwd()[-1] != '/' else '') + path).split('/')[:-1])
  absolute_path = current_file_dir.split("/")
  pom_dir_index = -1

  if os.path.isfile('/'.join(absolute_path) + '/pom.xml'):
    return '/'.join(absolute_path) + '/pom.xml'

  # Find index of 'base_dir_name' element
  while not os.path.isfile('/'.join(absolute_path[:pom_dir_index]) + '/pom.xml'):
    pom_dir_index -= 1

  return '/'.join(absolute_path[:pom_dir_index]) + '/pom.xml'

def get_file_package():
  current_file_location = '.'.join((os.getcwd() + ('/' if os.getcwd()[-1] != '/' else '') + path).split('/')[:-1])
  package = get_package_name(find_nearest_pom())
  return package + current_file_location.split(package)[1]

endglobal

snippet pkg "package" b
package `!p snip.rv = get_file_package()`;
endsnippet
4
On

UltiSnips stores Java snippets in java.snippets, which on my machine is ~/.vim/bundle/vim-snippets/UltiSnips/java.snippets (I am usinghonza/vim-snippets` as well).

Snippets for Java are implemented using Python, so I have implemented the snippet below using Python as well (you can do it using multiple languages in UltiSnips).

There is already a snippet for package, which adds simply "package" word followed with a placeholder:

snippet pa "package" b
package $0
endsnippet

Let's create a pad snippet that will automatically insert package name, based on the directory chain, instead of the $0 placeholder:

snippet pad "package" b
package `!p

def get_package_string(base_dir_name):
  import os
  # Get absolute path of the package (without filename)
  absolute_path = os.getcwd().split("/")
  src_dir_index = 0

  # Find index of 'base_dir_name' element
  while absolute_path[src_dir_index] != base_dir_name:
    src_dir_index+=1

  # Create a 'package ' string, joining with dots
  package_string = ".".join(absolute_path[src_dir_index+1:])
  return package_string

# snip.rv is UltiSnips' return value we want to paste between ``
snip.rv = get_package_string("java")`

endsnippet

Note that this solution is based on the fact that in many Java projects, there is an src directory with main/java and test/java directories in it and you are editing one of the files in java directory (e.g. for src/main/com/google/common it will return com.google.common). You may need to modify this to be more flexible.

You can find more information about creating snippets in screencasts linked in its README.

0
On

It can't be achieve using regex?

like this:

global !p

def get_file_package():
    input_str = vim.eval('expand("%:p")')
    transformed_str = re.sub(r'^.*/src/(test|main)/java/((.+/)*)(.*?)/.*\.java$', r'\3\4', input_str)
    return re.sub('/', '.', transformed_str)

endglobal

snippet pkg "Declare java package" b
package `!p snip.rv = get_file_package()`;
endsnippet

I using this, tell if I missing something.