diff --git a/src/main/java/com/squareup/kotlinpoet/CodeWriter.kt b/src/main/java/com/squareup/kotlinpoet/CodeWriter.kt
index f3627336de..35f863e01b 100644
--- a/src/main/java/com/squareup/kotlinpoet/CodeWriter.kt
+++ b/src/main/java/com/squareup/kotlinpoet/CodeWriter.kt
@@ -276,6 +276,12 @@ internal class CodeWriter constructor(
     }
   }
 
+  fun openWrappingGroup() = out.openWrappingGroup()
+
+  fun closeWrappingGroup() {
+    trailingNewline = out.closeWrappingGroup()
+  }
+
   fun emitWrappingSpace() = apply {
     out.wrappingSpace(indentLevel + 2)
   }
diff --git a/src/main/java/com/squareup/kotlinpoet/FunSpec.kt b/src/main/java/com/squareup/kotlinpoet/FunSpec.kt
index c04bfcf725..a917adb538 100644
--- a/src/main/java/com/squareup/kotlinpoet/FunSpec.kt
+++ b/src/main/java/com/squareup/kotlinpoet/FunSpec.kt
@@ -116,7 +116,7 @@ class FunSpec private constructor(builder: Builder) {
       codeWriter.emitCode("%L", escapeIfKeyword(name))
     }
 
-    parameters.emit(codeWriter) { param ->
+    parameters.emit(codeWriter, wrappable = true) { param ->
       param.emit(codeWriter, includeType = name != SETTER)
     }
 
diff --git a/src/main/java/com/squareup/kotlinpoet/LineWrapper.kt b/src/main/java/com/squareup/kotlinpoet/LineWrapper.kt
index 02dd106bc9..d685bf5f55 100644
--- a/src/main/java/com/squareup/kotlinpoet/LineWrapper.kt
+++ b/src/main/java/com/squareup/kotlinpoet/LineWrapper.kt
@@ -26,71 +26,185 @@ internal class LineWrapper(
 ) {
   private var closed = false
 
-  /** Characters written since the last wrapping space that haven't yet been flushed.  */
-  private val buffer = StringBuilder()
-
   /** The number of characters since the most recent newline. Includes both out and the buffer.  */
   private var column = 0
 
-  /** -1 if we have no buffering; otherwise the number of spaces to write after wrapping.  */
-  private var indentLevel = -1
+  private var helper: BufferedLineWrapperHelper = DefaultLineWrapperHelper()
 
   /** Emit `s`. This may be buffered to permit line wraps to be inserted.  */
   fun append(s: String) {
     check(!closed) { "closed" }
 
-    if (indentLevel != -1) {
+    if (helper.isBuffering) {
       val nextNewline = s.indexOf('\n')
 
       // If s doesn't cause the current line to cross the limit, buffer it and return. We'll decide
       // whether or not we have to wrap it later.
       if (nextNewline == -1 && column + s.length <= columnLimit) {
-        buffer.append(s)
+        helper.buffer(s)
         column += s.length
         return
       }
 
       // Wrap if appending s would overflow the current line.
       val wrap = nextNewline == -1 || column + nextNewline > columnLimit
-      flush(wrap)
+      helper.flush(wrap)
     }
 
-    out.append(s)
+    helper.append(s)
     val lastNewline = s.lastIndexOf('\n')
     column = if (lastNewline != -1)
       s.length - lastNewline - 1 else
       column + s.length
   }
 
+  fun openWrappingGroup() {
+    check(!closed) { "closed" }
+
+    helper = GroupLineWrapperHelper()
+  }
+
   /** Emit either a space or a newline character.  */
   fun wrappingSpace(indentLevel: Int) {
     check(!closed) { "closed" }
 
-    if (this.indentLevel != -1) flush(false)
+    helper.wrappingSpace(indentLevel)
     this.column++
-    this.indentLevel = indentLevel
+  }
+
+  fun closeWrappingGroup(): Boolean {
+    check(!closed) { "closed" }
+
+    val wrapped = helper.close()
+    helper = DefaultLineWrapperHelper()
+    return wrapped
   }
 
   /** Flush any outstanding text and forbid future writes to this line wrapper.  */
   fun close() {
-    if (indentLevel != -1) flush(false)
+    helper.close()
     closed = true
   }
 
   /** Write the space followed by any buffered text that follows it.  */
-  private fun flush(wrap: Boolean) {
+  private fun flush(buffered: String, wrap: Boolean) {
     if (wrap) {
       out.append('\n')
-      for (i in 0 until indentLevel) {
+      for (i in 0 until helper.indentLevel) {
         out.append(indent)
       }
-      column = indentLevel * indent.length
-      column += buffer.length
+      column = helper.indentLevel * indent.length
+      column += buffered.length
     } else {
       out.append(' ')
     }
-    out.append(buffer)
-    buffer.delete(0, buffer.length)
-    indentLevel = -1
+    out.append(buffered)
+  }
+
+  /**
+   * Contract for helpers that handle buffering, post-processing and flushing of the input.
+   */
+  internal interface BufferedLineWrapperHelper {
+
+    val indentLevel: Int
+
+    val isBuffering get() = indentLevel != -1
+
+    /** Append to out, bypassing the buffer */
+    fun append(s: String): Appendable
+
+    /** Append to buffer */
+    fun buffer(s: String): Appendable
+
+    /**
+     * Indicates that a new wrapping space occurred in input.
+     *
+     * @param indentLevel Indentation level for the new line
+     */
+    fun wrappingSpace(indentLevel: Int)
+
+    /**
+     * Flush any buffered text.
+     *
+     * @param wrap `true` if buffer contents should be flushed a on new line
+     * */
+    fun flush(wrap: Boolean)
+
+    /**
+     * Flush and clear the buffer.
+     *
+     * @return `true` if input wrapped to new line
+     */
+    fun close(): Boolean
+  }
+
+  /** Flushes the buffer each time the wrapping space is encountered */
+  internal inner class DefaultLineWrapperHelper : BufferedLineWrapperHelper {
+
+    private val buffer = StringBuilder()
+
+    private var _indentLevel = -1
+
+    override val indentLevel get() = _indentLevel
+
+    override fun append(s: String): Appendable = out.append(s)
+
+    override fun buffer(s: String): Appendable = buffer.append(s)
+
+    override fun wrappingSpace(indentLevel: Int) {
+      if (isBuffering) flush(false)
+      _indentLevel = indentLevel
+    }
+
+    override fun flush(wrap: Boolean) {
+      flush(buffer.toString(), wrap)
+      buffer.delete(0, buffer.length)
+      _indentLevel = -1
+    }
+
+    override fun close(): Boolean {
+      if (isBuffering) flush(false)
+      return false
+    }
+  }
+
+  /**
+   * Holds multiple buffers and only flushes when the group is closed. If wrapping happened within
+   * a group - each buffer will be flushed on a new line.
+   */
+  internal inner class GroupLineWrapperHelper : BufferedLineWrapperHelper {
+
+    private val buffer = mutableListOf(StringBuilder())
+    private var wrapped = false
+
+    private var _indentLevel = -1
+
+    override val indentLevel get() = _indentLevel
+
+    override fun append(s: String): Appendable = buffer.last().append(s)
+
+    override fun buffer(s: String): Appendable = buffer.last().append(s)
+
+    override fun wrappingSpace(indentLevel: Int) {
+      _indentLevel = indentLevel
+      buffer += StringBuilder()
+    }
+
+    override fun flush(wrap: Boolean) {
+      wrapped = wrap
+    }
+
+    override fun close(): Boolean {
+      if (wrapped) buffer.last().append('\n')
+      buffer.forEachIndexed { index, segment ->
+        if (index == 0 && !wrapped) {
+          out.append(segment)
+        } else {
+          flush(segment.toString(), wrapped)
+        }
+      }
+      _indentLevel = -1
+      return wrapped
+    }
   }
 }
diff --git a/src/main/java/com/squareup/kotlinpoet/ParameterSpec.kt b/src/main/java/com/squareup/kotlinpoet/ParameterSpec.kt
index f6695f14d4..527f68f400 100644
--- a/src/main/java/com/squareup/kotlinpoet/ParameterSpec.kt
+++ b/src/main/java/com/squareup/kotlinpoet/ParameterSpec.kt
@@ -146,28 +146,15 @@ class ParameterSpec private constructor(builder: ParameterSpec.Builder) {
 
 internal fun List<ParameterSpec>.emit(
     codeWriter: CodeWriter,
+    wrappable: Boolean = false,
     emitBlock: (ParameterSpec) -> Unit = { it.emit(codeWriter) }
 ) = with(codeWriter) {
-  val params = this@emit
   emit("(")
-  when (size) {
-    0 -> emit("")
-    1 -> emitBlock(params[0])
-    2 -> {
-      emitBlock(params[0])
-      emit(", ")
-      emitBlock(params[1])
-    }
-    else -> {
-      emit("\n")
-      indent(2)
-      forEachIndexed { index, parameter ->
-        if (index > 0) emit(",\n")
-        emitBlock(parameter)
-      }
-      unindent(2)
-      emit("\n")
-    }
+  if (wrappable) codeWriter.openWrappingGroup()
+  forEachIndexed { index, parameter ->
+    if (index > 0) if (wrappable) emitCode(",%W") else emit(", ")
+    emitBlock(parameter)
   }
+  if (wrappable) codeWriter.closeWrappingGroup()
   emit(")")
 }
diff --git a/src/main/java/com/squareup/kotlinpoet/TypeSpec.kt b/src/main/java/com/squareup/kotlinpoet/TypeSpec.kt
index af7f11781e..133a342d81 100644
--- a/src/main/java/com/squareup/kotlinpoet/TypeSpec.kt
+++ b/src/main/java/com/squareup/kotlinpoet/TypeSpec.kt
@@ -115,7 +115,7 @@ class TypeSpec private constructor(builder: TypeSpec.Builder) {
             codeWriter.emit("constructor")
           }
 
-          it.parameters.emit(codeWriter) { param ->
+          it.parameters.emit(codeWriter, wrappable = true) { param ->
             val property = constructorProperties[param.name]
             if (property != null) {
               property.emit(codeWriter, setOf(PUBLIC), withInitializer = false, inline = true)
diff --git a/src/test/java/com/squareup/kotlinpoet/KotlinPoetTest.kt b/src/test/java/com/squareup/kotlinpoet/KotlinPoetTest.kt
index 596de82ae6..9e0f7215fb 100644
--- a/src/test/java/com/squareup/kotlinpoet/KotlinPoetTest.kt
+++ b/src/test/java/com/squareup/kotlinpoet/KotlinPoetTest.kt
@@ -17,6 +17,7 @@ package com.squareup.kotlinpoet
 
 import com.google.common.truth.Truth.assertThat
 import org.junit.Test
+import java.io.Serializable
 
 class KotlinPoetTest {
   private val tacosPackage = "com.squareup.tacos"
@@ -107,11 +108,7 @@ class KotlinPoetTest {
         |import kotlin.Boolean
         |import kotlin.String
         |
-        |class Taco(
-        |    val cheese: String,
-        |    var cilantro: String,
-        |    lettuce: String
-        |) {
+        |class Taco(val cheese: String, var cilantro: String, lettuce: String) {
         |  val lettuce: String = lettuce.trim()
         |
         |  val onion: Boolean = true
@@ -365,11 +362,7 @@ class KotlinPoetTest {
       |import kotlin.String
       |import kotlin.Unit
       |
-      |fun ((
-      |    name: String,
-      |    Int,
-      |    age: Long
-      |) -> Unit).whatever(): Unit = Unit
+      |fun ((name: String, Int, age: Long) -> Unit).whatever(): Unit = Unit
       |""".trimMargin())
   }
 
@@ -552,4 +545,50 @@ class KotlinPoetTest {
         |fun addA(s: String): String = s + "a"
         |""".trimMargin())
   }
+
+  @Test fun longParameterListWrapping() {
+    val source = FunSpec.builder("sum")
+        .addParameter(ParameterSpec.builder("a", Int::class).build())
+        .addParameter(ParameterSpec.builder("b", Int::class).build())
+        .addParameter(ParameterSpec.builder("c", Int::class).build())
+        .addParameter(ParameterSpec.builder("d", Int::class).build())
+        .addParameter(ParameterSpec.builder("e", Int::class).build())
+        .addParameter(ParameterSpec.builder("f", Int::class).build())
+        .addParameter(ParameterSpec.builder("g", Int::class).build())
+        .addStatement("return a + b + c")
+        .build()
+    assertThat(source.toString()).isEqualTo("""
+      |fun sum(
+      |    a: kotlin.Int,
+      |    b: kotlin.Int,
+      |    c: kotlin.Int,
+      |    d: kotlin.Int,
+      |    e: kotlin.Int,
+      |    f: kotlin.Int,
+      |    g: kotlin.Int
+      |) = a + b + c
+      |""".trimMargin())
+  }
+
+  @Test fun longLambdaParameterListWrapping() {
+    val source = FunSpec.builder("veryLongFunctionName")
+        .addParameter(ParameterSpec.builder(
+            "veryLongParameterName",
+            LambdaTypeName.get(
+                parameters = listOf(
+                    ParameterSpec.unnamed(Serializable::class),
+                    ParameterSpec.unnamed(Appendable::class),
+                    ParameterSpec.unnamed(Cloneable::class)),
+                returnType = Unit::class.asTypeName()))
+            .build())
+        .addParameter("i", Int::class)
+        .addStatement("return %T", Unit::class)
+        .build()
+    assertThat(source.toString()).isEqualTo("""
+      |fun veryLongFunctionName(
+      |    veryLongParameterName: (java.io.Serializable, java.lang.Appendable, kotlin.Cloneable) -> kotlin.Unit,
+      |    i: kotlin.Int
+      |) = kotlin.Unit
+      |""".trimMargin())
+  }
 }
diff --git a/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt b/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt
index 318b38f36f..67288c38b5 100644
--- a/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt
+++ b/src/test/java/com/squareup/kotlinpoet/TypeSpecTest.kt
@@ -579,11 +579,7 @@ class TypeSpecTest {
         |
         |  override fun compareTo(p: P): Int = 0
         |
-        |  fun <T, P : Number> of(
-        |      label: T,
-        |      x: P,
-        |      y: P
-        |  ): Location<T, P> {
+        |  fun <T, P : Number> of(label: T, x: P, y: P): Location<T, P> {
         |    throw new UnsupportedOperationException("TODO")
         |  }
         |}
@@ -1100,11 +1096,7 @@ class TypeSpecTest {
         |import kotlin.Int
         |
         |class Taqueria {
-        |  fun prepare(
-        |      workers: Int,
-        |      vararg jobs: Runnable,
-        |      start: Boolean
-        |  ) {
+        |  fun prepare(workers: Int, vararg jobs: Runnable, start: Boolean) {
         |  }
         |}
         |""".trimMargin())
@@ -2579,11 +2571,7 @@ class TypeSpecTest {
         |import kotlin.String
         |import kotlin.collections.Map
         |
-        |class Taco(
-        |    val a: String?,
-        |    val b: String?,
-        |    val c: String?
-        |) {
+        |class Taco(val a: String?, val b: String?, val c: String?) {
         |  constructor(map: Map<String, String>) : this(map["a"], map["b"], map["c"])
         |}
         |""".trimMargin())
@@ -2668,11 +2656,7 @@ class TypeSpecTest {
       |import kotlin.Int
       |import kotlin.String
       |
-      |data class Person(
-      |    override val id: Int,
-      |    override val name: String,
-      |    override val surname: String
-      |)
+      |data class Person(override val id: Int, override val name: String, override val surname: String)
       |""".trimMargin())
   }