จะใส่เครื่องหมายชุดของเส้นที่เลือกในโหมดภาพได้อย่างไร?


35

ฉันจะแสดงความคิดเห็นออกหลายบรรทัดในโหมดภาพที่เลือกได้อย่างไร ฉันจะทำให้เป็นภาษาเฉพาะได้อย่างไร

ตัวอย่างเช่นหากเลือก 4 บรรทัดแรก:

def foo(a,b):
    for each in (a,b):
        print each
    return a+b
print "2"

การดำเนินการของคำสั่ง / แมโครควรส่งผลให้สิ่งนี้ (ในหลาม):

#def foo(a,b):
#    for each in (a,b):
#        print each
#    return a+b
print "2"

คำตอบ:


31

หากคุณต้องการที่เฉพาะเจาะจงภาษาการแสดงความคิดเห็นที่คุณจะต้องปลั๊กอินเช่นnerdcommenter

อีกทางเลือกหนึ่งแม้ว่ามันจะไม่ตอบคำถามที่แท้จริงของคุณ แต่คุณสามารถใช้แอคชั่นแบบบิวอินและรู้ว่าคุณมีความคิดเห็นอักขระแต่ละภาษา ...

ตัวเลือก # 1: บล็อก V

  1. :1 Enter (ไปที่บรรทัด 1)
  2. Ctrl-V (โหมด V-Block)
  3. jjj (ลงอีก 3 บรรทัด)
  4. Shift-I (เข้าสู่โหมดแทรกก่อนบล็อก)
  5. # (แทรก '#')
  6. Esc (กลับสู่โหมดปกติ)

ตัวเลือก # 2: การทดแทน

:1,4s/^/#/

ทำให้พังถล่ม:

  1. : คำสั่ง Ex ติดตาม
  2. 1,4 บนบรรทัดตั้งแต่ 1 ถึง 4
  3. s แทน
  4. /คั่นสำหรับชิ้นส่วนของคำสั่งทดแทน
    (คุณสามารถใช้อักขระอื่นเช่น:)
  5. ^ จุดเริ่มต้นของบรรทัด
  6. / เครื่องสกัด
  7. # ตัวอักษรความคิดเห็นสำหรับงูหลาม
  8. / ตัวคั่นสุดท้าย

ตัวเลือก # 3: ทำซ้ำแอปพลิเคชันของแมโคร ( แหล่งที่มา )

  1. :1 Enter (ไปที่บรรทัด 1)
  2. qa(เริ่มบันทึกลงทะเบียนa)
  3. Shift-I (เข้าสู่โหมดแทรกที่จุดเริ่มต้นของบรรทัด
  4. # (เพิ่ม '#' ที่จุดเริ่มต้นของบรรทัด)
  5. Esc (กลับสู่โหมดปกติ)
  6. q (หยุดการบันทึก)

  7. :2,4 normal @a(เรียกใช้แมโครที่บันทึกใหม่เพื่อลงทะเบียนaบนบรรทัดระหว่าง 2 และ 4)

    หรือ

    คุณสามารถเลือกบรรทัดในโหมดภาพและกด:เพื่อเติมบรรทัด Ex โดยอัตโนมัติด้วย:'<,'>(ช่วงจากจุดเริ่มต้นถึงจุดสิ้นสุดของการเลือกภาพ) จากนั้นพิมพ์normal @aและกดEnter( แหล่งที่มา )

ตอนนี้เมื่อใดก็ตามที่คุณต้องการแสดงความคิดเห็นบางบรรทัดเพียงเรียกใช้แมโครที่บันทึกไว้เพื่อลงทะเบียนaในบรรทัดเหล่านั้น:

:9,22 normal @a (comment out lines 9-22)

1
ตัวเลือก 4: ปลั๊กอิน
โพลสำรวจความคิดเห็น

ฉันไม่เข้าใจว่าทำไมคุณใช้แมโครสำหรับคำสั่งเดียวเมื่อคุณสามารถทำ:9,22 normal I#ตามคำตอบของฉัน
เบ็น

ทำไมคุณถึงใช้: 1 <enter> เมื่อคุณสามารถใช้ gg ได้
Tanath

@Tanath การแสดงความคิดเห็นจากบรรทัดแรกนั้นเฉพาะเจาะจงกับตัวอย่างนี้ หากผู้เขียนต้องการแสดงความคิดเห็นจากบรรทัดที่ 9 ถึง 22 พวกเขาจะไม่สามารถใช้ gg ได้
bsmith89

@Ben ฉันไม่รู้อะไรเกี่ยวกับnormalคำสั่งก่อนที่จะเขียนคำตอบนี้ คุณถูก; :9,22 normal I#จะทำงานเช่นกัน
bsmith89

26

ใช้โหมด Visual Block ( CtrlV) เลือกจุดเริ่มต้นของบรรทัด จากนั้นกดI#(นั่นคืออักษรตัวใหญ่I) เพื่อแทรกอักขระแฮชในแต่ละบรรทัดเหล่านั้น จากนั้นกดEscเพื่อกลับจากโหมดแทรกไปที่โหมดปกติ


มันไม่ทำงานสำหรับฉัน มันแทรกความคิดเห็นเฉพาะในบรรทัดแรก
gon1332

คุณกำลังผลักดันctrl? เพราะเป็นสิ่งที่แตกต่างจากctrl+v v
Cody Poll

@CodyPoll ฉันรู้ Iทุกอย่างขึ้นอยู่ตกลงที่จะ เมื่อฉันกดIจากนั้น#จะถูกวางไว้ที่ด้านหน้าของบรรทัดแรกเท่านั้น
gon1332

@CodyPoll Ok .. ฉันเพิ่งป่วย ฉันไม่ได้กดEscหลังจากขั้นตอนที่อธิบายไว้
gon1332

@ gon1332 เท่าที่ฉันรู้ว่าคุณต้องกดEscในตอนท้าย
Gonçalo Ribeiro

18

หากคุณต้องการวิธีแก้ปัญหาอย่างรวดเร็วสำหรับภาษาใดก็ตามที่คุณอยู่ในขณะนี้และคุณได้เลือกข้อความในโหมดภาพแล้ว

:norm 0i#

ทำงาน (สำหรับแต่ละบรรทัดในโหมดปกติให้ไปที่คอลัมน์แรกและส่วนแทรก#การใช้:norm I#จะแทรกไว้ก่อนอักขระที่ไม่ใช่ช่องว่างตัวแรกซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ) การใช้:norm i#จะใช้ได้เนื่องจาก:normเริ่มต้นที่จุดเริ่มต้นของ บรรทัด แต่ชัดเจนน้อยกว่าและชัดเจนน้อยกว่าถ้าคุณไม่ทราบ

แน่นอนถ้าคุณตั้งใจจะทำเช่นนี้บ่อยครั้งคุณจะต้องตั้งค่าการจับคู่หรือมองหาปลั๊กอิน


1
0 ไม่จำเป็นตั้งแต่เริ่มต้นnormalคำสั่งจะถูกดำเนินการด้วยเคอร์เซอร์ที่จุดเริ่มต้นของบรรทัด
nitishch

1
แน่นอนหมายเลขบรรทัด% เครื่องหมายสามารถนำหน้าด้วยคำสั่งนี้ ตัวอย่าง:: 1,5norm i # (หรือ): 'a,' bnorm i # (หรือ): 10% norm i #
SibiCoder

9

การดำเนินการโดยอัตโนมัติจะทำให้คุณต้องเพิ่มสิ่งต่อไปนี้ในvimrcไฟล์ของคุณ(ที่มา ):

au FileType haskell,vhdl,ada let b:comment_leader = '-- '
au FileType vim let b:comment_leader = '" '
au FileType c,cpp,java let b:comment_leader = '// '
au FileType sh,make let b:comment_leader = '# '
au FileType tex let b:comment_leader = '% '
noremap <silent> ,c :<C-B>sil <C-E>s/^/<C-R>=escape(b:comment_leader,'\/')<CR>/<CR>:noh<CR>
noremap <silent> ,u :<C-B>sil <C-E>s/^\V<C-R>=escape(b:comment_leader,'\/')<CR>//e<CR>:noh<CR>

การใช้,cเพื่อแสดงความคิดเห็นในภูมิภาคและไม่พูด,uถึงภูมิภาค การตั้งค่าสัญลักษณ์ความคิดเห็นสำหรับภาษาต่างๆด้วยตนเอง

ตัวเลือกที่สองคือการใช้ปลั๊กอินเช่นtcomment , กลุ่ม-ความเห็นหรือcomments.vim ฉันใช้ความคิดเห็นของตัวเอง โปรดอ่านคำแนะนำเกี่ยวกับการใช้งานและการติดตั้งที่หน้าเว็บของพวกเขาเนื่องจากฉันเชื่อว่าเกินกว่าคำถาม

ฉันขอแนะนำให้คุณใช้ปลั๊กอิน (หนึ่งในลิงก์ด้านบนหรืออื่น ๆ ) เนื่องจากง่ายกว่าการบำรุงรักษาโค้ดในvimrcไฟล์ของคุณ

แก้ไข: ฉันลบวิธีการด้วยตนเองเมื่อคำถามเปลี่ยนไปและวิธีที่ถูกต้องก็ตอบโดย 200_success


ข้อเสนอแนะเพิ่มเติมปลั๊กอิน: NERD Commenter - vim.org/scripts/script.php?script_id=1218
gaveen

หมายเหตุ: สิ่งนี้สนับสนุนความคิดเห็น linewise เท่านั้น ตัวอย่างเช่น ANSI C ไม่รู้จัก//(เฉพาะ/* */)
wchargin

ในขณะที่ฉันชอบวิธีนี้มีวิธีที่จะสลับความคิดเห็นหรือไม่
ideasman42

1
@ ideasman42 คุณจะต้องทำฟังก์ชั่นแทนและตรวจสอบว่าบรรทัดปัจจุบันเริ่มต้นด้วยความคิดเห็นหรือไม่จากนั้นขึ้นอยู่กับการเรียกนั้นว่า:sคำสั่งใดคำสั่งหนึ่งที่แสดงในข้อความที่ตัดตอนมาในคำตอบ getline('.') =~ "^" . escape(b:comment_leader, '\/')การตรวจสอบตัวเองจะเป็นสิ่งที่ชอบ ถ้ามันเป็นความคิดเห็นจริงมิฉะนั้นแสดงความคิดเห็น สิ่งนี้ไม่ได้ทดสอบและควรทำหน้าที่เป็นตัวอย่างเท่านั้น
tokoyami

5

ฉันใช้scrooloose / nerdcommenterสำหรับสิ่งนี้

ด้วยปลั๊กอินนี้คุณสามารถมองเห็นเส้นของคุณและกดleader+ cเพื่อสลับความคิดเห็น มันจะใช้สัญลักษณ์ที่แตกต่างกันสำหรับการแสดงความคิดเห็นทั้งนี้ขึ้นอยู่กับประเภทของไฟล์


5

หลังจากที่คุณเลือกบรรทัดเพียงพิมพ์

:norm I#

:จะใส่โดยอัตโนมัติ'<,'>ในบรรทัดคำสั่งของคุณซึ่งเป็นช่วงจากจุดเริ่มต้นของการเลือกของคุณไปยังจุดสิ้นสุด; normรันคำสั่งโหมดปกติและจะดำเนินการกับช่วงนั้น I#เป็นคำสั่งโหมดปกติที่แทรก '#' ที่จุดเริ่มต้นของบรรทัด


4

ฉันเป็นแฟนตัวยงของTCommentสำหรับเรื่องนี้; ไม่เพียง แต่ฉันสามารถทำสไตล์ความคิดเห็นเฉพาะประเภทไฟล์ แต่ยังระบุ block vs per-line สำหรับภาษาที่สนับสนุนความคิดเห็นบล็อก

    gc{motion}   :: Toggle comments (for small comments within one line 
                    the &filetype_inline style will be used, if 
                    defined)
    gcc          :: Toggle comment for the current line

Explicit commenting/uncommenting:

    g<{motion}   :: Uncomment region
    g<c          :: Uncomment the current line
    g<b          :: Uncomment the current region as block

    g>{motion}   :: Comment region
    g>c          :: Comment the current line
    g>b          :: Comment the current region as block

In visual mode:

    gc           :: Toggle comments
    gC           :: Comment selected text

ขอบคุณสำหรับคำตอบ! คุณสามารถขยายได้ไหม การให้คำตอบปลั๊กอินเป็นเรื่องปกติ แต่ตอนนี้มันเป็นแค่การเชื่อมโยงไปยังปลั๊กอิน อย่างน้อยที่สุดคำอธิบายพื้นฐานของสิ่งที่มันทำและวิธีการใช้มันคาดว่าในคำตอบ โปรดดูโพสต์เมตานี้ด้วย
Martin Tournoij

ดูเหมือนว่าโง่ที่จะคัดลอก / วางปุ่มลัด แต่ไปที่นั่นแล้ว ฉันได้อธิบายไปแล้วว่ามันทำอะไร
Collin Grady

3

ฉันพบว่าปลั๊กอินเสียงเรียกเข้าเป็นวิธีที่ง่ายที่สุดในการทำสิ่งนี้ gcเลือกช่วงของเส้นแล้วเพียงแค่กด มันจะใช้อักขระการแสดงความคิดเห็นที่เหมาะสมสำหรับประเภทไฟล์ที่คุณเปิด มันเป็นไปได้แม้จะไม่ได้เลือกภาพใด ๆ ที่จะแสดงความคิดเห็นสาย uncomment ที่อยู่ติดกันด้วย หรือgcugcgc


2

สมมติว่าคุณต้องการเพิ่มคำนำหน้าเป็น 5 บรรทัดที่จุดเริ่มต้นของบรรทัดจากนั้นคุณสามารถใช้การค้นหาและแทนที่ :

:.,+5s/^/prefix_/g

หรือในตอนท้ายของบรรทัด:

:.,+5s/$/suffix_/g

หรือใช้โหมดภาพ ( Ctrl+ v) เพื่อเลือกบล็อกข้อความแนวตั้งจากนั้นเข้าสู่โหมดแทรก ( I) และพิมพ์บางอย่างแล้วกดEscเพื่อยืนยันและใช้การเปลี่ยนแปลงกับบรรทัดอื่น

ที่เกี่ยวข้อง:


2

คำตอบนี้อยู่ที่นี่เพื่อ 1) แสดงรหัสที่ถูกต้องเพื่อนำไปวางเป็น.vimrcที่จะได้รับvim 7.4+ในการทำบล็อกแสดงความคิดเห็น / uncommenting ขณะที่การรักษาระดับการเยื้องกับ 1 ทางลัดในโหมดภาพและ 2) เพื่ออธิบายมัน

นี่คือรหัส:

let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.[ch]    let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.cpp    let b:commentChar='//'
autocmd BufNewFile,BufReadPost *.py    let b:commentChar='#'
autocmd BufNewFile,BufReadPost *.*sh    let b:commentChar='#'
function! Docomment ()
  "make comments on all the lines we've grabbed
  execute '''<,''>s/^\s*/&'.escape(b:commentChar, '\/').' /e'
endfunction
function! Uncomment ()
  "uncomment on all our lines
  execute '''<,''>s/\v(^\s*)'.escape(b:commentChar, '\/').'\v\s*/\1/e'
endfunction
function! Comment ()
  "does the first line begin with a comment?
  let l:line=getpos("'<")[1]
  "if there's a match
  if match(getline(l:line), '^\s*'.b:commentChar)>-1
    call Uncomment()
  else
    call Docomment()
  endif
endfunction
vnoremap <silent> <C-r> :<C-u>call Comment()<cr><cr>

มันทำงานอย่างไร:

  • let b:commentChar='//': สิ่งนี้สร้างตัวแปรเป็นกลุ่ม bที่นี่หมายถึงขอบเขตซึ่งในกรณีนี้มีการบัฟเฟอร์หมายถึงไฟล์ที่เปิดอยู่ในปัจจุบัน อักขระความคิดเห็นของคุณเป็นสตริงและจำเป็นต้องใส่ในเครื่องหมายคำพูดเครื่องหมายคำพูดไม่ได้เป็นส่วนหนึ่งของสิ่งที่จะถูกแทนที่เมื่อสลับความคิดเห็น

  • autocmd BufNewFile,BufReadPost *...: ทริกเกอร์คำสั่งอัตโนมัติในสิ่งต่าง ๆ ในกรณีนี้สิ่งเหล่านี้จะทริกเกอร์เมื่อไฟล์ใหม่หรือไฟล์อ่านจบลงด้วยนามสกุลที่แน่นอน เมื่อมีการเรียกใช้คำสั่ง execute ต่อไปนี้ซึ่งทำให้เราสามารถเปลี่ยนcommentCharชนิดไฟล์ที่ขึ้นอยู่กับ มีวิธีอื่นในการทำเช่นนี้ แต่พวกเขาสับสนกับมือใหม่ (เช่นฉัน) มากกว่า

  • function! Docomment(): ฟังก์ชั่นมีการประกาศโดยเริ่มต้นด้วยและลงท้ายด้วยfunction endfunctionฟังก์ชั่นจะต้องเริ่มต้นด้วยทุน !เพื่อให้แน่ใจว่าฟังก์ชั่นนี้จะเขียนทับฟังก์ชั่นใด ๆ ก่อนกำหนดเป็นกับรุ่นนี้Docomment() Docomment()ถ้าไม่มี!ฉันก็มีข้อผิดพลาด แต่นั่นอาจเป็นเพราะฉันกำหนดฟังก์ชั่นใหม่ผ่านทางบรรทัดคำสั่ง vim

  • execute '''<,''>s/^\s*/&'.escape(b:commentChar, '\/').' /e': เรียกใช้คำสั่ง ในกรณีนี้เรากำลังดำเนินการsubstituteซึ่งสามารถใช้ช่วง (โดยค่าเริ่มต้นนี่คือบรรทัดปัจจุบัน) เช่น%สำหรับบัฟเฟอร์ทั้งหมดหรือ'<,'>สำหรับส่วนที่ไฮไลต์ ^\s*คือ regex เพื่อให้ตรงกับจุดเริ่มต้นของบรรทัดแล้วตามด้วยช่องว่างจำนวนใด ๆ ซึ่งต่อท้ายไปแล้ว (เนื่องจาก&) .ที่นี่จะใช้สำหรับสตริงเนื่องจากescape()ไม่สามารถห่อในเครื่องหมายคำพูด escape()ช่วยให้คุณสามารถที่จะหลบหนีตัวละครในcommentCharที่ตรงกับข้อโต้แย้ง (ในกรณีนี้\และ/) โดย prepending \พวกเขาด้วย หลังจากนี้เราต่อกันอีกครั้งกับจุดจบของเราsubstituteซึ่งมีeธง. การตั้งค่าสถานะนี้ช่วยให้เราล้มเหลวอย่างเงียบ ๆ ซึ่งหมายความว่าหากเราไม่พบการแข่งขันในบรรทัดที่กำหนดเราจะไม่ตะโกนเกี่ยวกับมัน โดยรวมแล้วบรรทัดนี้ช่วยให้เราใส่อักขระความคิดเห็นตามด้วยช่องว่างหน้าข้อความแรกซึ่งหมายความว่าเรารักษาระดับการเยื้องไว้

  • execute '''<,''>s/\v(^\s*)'.escape(b:commentChar, '\/').'\v\s*/\1/e': นี่คล้ายกับคำสั่งอันยาวนานของเรา ที่ไม่ซ้ำกับคนนี้เรามี\vซึ่งจะทำให้แน่ใจว่าเราไม่ได้มีการหลบหนีของเรา()และซึ่งหมายถึงกลุ่มที่เราทำกับเรา1 ()โดยพื้นฐานแล้วเรากำลังจับคู่บรรทัดที่ขึ้นต้นด้วยช่องว่างจำนวนหนึ่งแล้วตามด้วยอักขระความคิดเห็นของเราตามด้วยช่องว่างจำนวนหนึ่งและเราจะรักษาช่องว่างชุดแรกเท่านั้น อีกครั้งeให้เราล้มเหลวอย่างเงียบ ๆ หากเราไม่มีตัวอักษรแสดงความคิดเห็นในบรรทัดนั้น

  • let l:line=getpos("'<")[1]: ชุดนี้ตัวแปรเหมือนที่เราทำกับตัวละครความคิดเห็นของเรา แต่lหมายถึงขอบเขตท้องถิ่น (ท้องถิ่นฟังก์ชั่นนี้) getpos()รับตำแหน่งในกรณีนี้จุดเริ่มต้นของการเน้นของเราและ[1]วิธีการที่เราสนใจเฉพาะหมายเลขบรรทัดไม่ใช่สิ่งอื่น ๆ เช่นหมายเลขคอลัมน์

  • if match(getline(l:line), '^\s*'.b:commentChar)>-1: คุณรู้วิธีการifทำงาน match()ตรวจสอบว่าสิ่งแรกมีสิ่งที่สองดังนั้นเราจึงคว้าเส้นที่เราเริ่มเน้นของเราและตรวจสอบว่ามันเริ่มต้นด้วยช่องว่างตามด้วยตัวละครความคิดเห็นของเรา match()ส่งคืนดัชนีซึ่งเป็นจริงและ-1หากไม่พบรายการที่ตรงกัน เนื่องจากifประเมินตัวเลขที่ไม่ใช่ศูนย์ทั้งหมดให้เป็นจริงเราต้องเปรียบเทียบผลลัพธ์ของเราเพื่อดูว่ามันมากกว่า -1 หรือไม่ การเปรียบเทียบvimส่งคืน 0 หากเป็นเท็จและ 1 ถ้าเป็นจริงซึ่งเป็นสิ่งที่ifต้องการดูเพื่อประเมินอย่างถูกต้อง

  • vnoremap <silent> <C-r> :<C-u>call Comment()<cr><cr>: vnoremapหมายถึงแมปคำสั่งต่อไปนี้ในโหมดภาพ แต่ไม่ต้องแมปคำสั่งซ้ำ (หมายถึงอย่าเปลี่ยนคำสั่งอื่น ๆ ที่อาจใช้ในวิธีอื่น) โดยทั่วไปถ้าคุณเป็นกลุ่มมือใหม่ควรใช้noremapเพื่อให้แน่ใจว่าคุณจะไม่ทำสิ่งต่าง ๆ <silent>หมายถึง "ฉันไม่ต้องการคำพูดของคุณเพียงแค่การกระทำของคุณ" และบอกว่าจะไม่พิมพ์อะไรไปยังบรรทัดคำสั่ง <C-r>เป็นสิ่งที่เรากำลังทำแผนที่ซึ่งเป็น ctrl + r ในกรณีนี้ (โปรดทราบว่าคุณยังคงสามารถใช้ Cr ตามปกติสำหรับ "ทำซ้ำ" ในโหมดปกติด้วยการทำแผนที่นี้) C-uค่อนข้างจะสับสน แต่โดยพื้นฐานแล้วมันทำให้แน่ใจว่าคุณไม่หลงลืมการเน้นภาพของคุณ (ตามคำตอบนี้มันทำให้คำสั่งของคุณเริ่มต้นด้วย'<,'>สิ่งที่เราต้องการ)callที่นี่เพียงแค่บอกเป็นกลุ่มเพื่อดำเนินการฟังก์ชั่นที่เราตั้งชื่อและ<cr>หมายถึงการกดenterปุ่ม เราต้องกดปุ่มหนึ่งครั้งเพื่อเรียกใช้ฟังก์ชั่นจริง ๆ (ไม่อย่างนั้นเราแค่พิมพ์call function()บนบรรทัดคำสั่งและเราต้องกดมันอีกครั้งเพื่อให้ตัวแทนของเราผ่านไปได้ตลอดทาง (ไม่แน่ใจจริงๆว่าทำไม

อย่างไรก็ตามหวังว่านี่จะช่วยได้ นี้จะเอาอะไรกับไฮไลต์v, VหรือC-vตรวจสอบว่าสายแรกคือการให้ความเห็นว่าถ้าใช่ลอง uncomment เส้นไฮไลต์ทั้งหมดและหากไม่ได้เพิ่มชั้นพิเศษของตัวละครความคิดเห็นที่แต่ละบรรทัด นี่คือพฤติกรรมที่ฉันต้องการ ฉันไม่ได้แค่อยากจะสลับว่าแต่ละบรรทัดในบล็อกถูกคอมเม้นต์หรือไม่ดังนั้นมันจึงทำงานได้อย่างสมบูรณ์แบบสำหรับฉันหลังจากถาม คำถามหลาย ๆข้อในหัวข้อ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.